def process_personal_chat_updates(update: Update): user = get_bot_user(update) if not user: return # check for unfinished posts unfinished_post = cached_post_get(update.effective_user.id) # found an unfinished post if unfinished_post: reply = continue_posting(update, unfinished_post, user) if reply: send_telegram_message(chat=Chat(id=update.effective_chat.id), text=reply) return # parse forwarded posts and links if update.message: reply = parse_forwarded_messages(update) if reply: send_telegram_message(chat=Chat(id=update.effective_chat.id), text=reply) return send_telegram_message(chat=Chat(id=update.effective_chat.id), text="Чот непонятна 🤔")
def process_comment_reply(update: Update): if not update.message.reply_to_message: return user = get_bot_user(update) if not user: return comment_url_entity = [ entity["url"] for entity in update.message.reply_to_message.entities if entity["type"] == "text_link" and COMMENT_URL_RE.match(entity["url"]) ] if not comment_url_entity: log.info( f"Comment url not found in: {update.message.reply_to_message.entities}" ) return comment_id = COMMENT_URL_RE.match(comment_url_entity[0]).group(1) comment = Comment.objects.filter(id=comment_id).first() if not comment: log.info(f"Comment not found: {comment_id}") return is_ok = Comment.check_rate_limits(user) if not is_ok: send_telegram_message( chat=Chat(id=update.effective_chat.id), text= f"🙅♂️ Извините, вы комментировали слишком часто и достигли дневного лимита" ) return text = update.message.text or update.message.caption if not text: send_telegram_message( chat=Chat(id=update.effective_chat.id), text=f"😣 Сорян, я пока умею только в текстовые ответы") return # max 3 levels of comments are allowed reply_to_id = comment.id if comment.reply_to_id and comment.reply_to.reply_to_id: reply_to_id = comment.reply_to_id reply = Comment.objects.create(author=user, post=comment.post, reply_to_id=reply_to_id, text=f"@{comment.author.slug}, {text}", useragent="TelegramBot (like TwitterBot)", metadata={"telegram": update.to_dict()}) new_comment_url = settings.APP_HOST + reverse("show_comment", kwargs={ "post_slug": reply.post.slug, "comment_id": reply.id }) send_telegram_message( chat=Chat(id=update.effective_chat.id), text=f"➜ <a href=\"{new_comment_url}\">Отвечено</a> 👍")
def process_personal_chat_updates(update: Update): user = User.objects.filter(telegram_id=update.effective_user.id).first() if not user: send_telegram_message( chat=Chat(id=update.effective_chat.id), text= f"😐 Извините, мы не знакомы. Привяжите свой аккаунт в профиле на https://vas3k.club" ) return # check for unfinished posts unfinished_post = cached_post_get(update.effective_user.id) # found an unfinished post if unfinished_post: reply = continue_posting(update, unfinished_post, user) if reply: send_telegram_message(chat=Chat(id=update.effective_chat.id), text=reply) return # parse forwarded posts and links if update.message: reply = parse_forwarded_messages(update) if reply: send_telegram_message(chat=Chat(id=update.effective_chat.id), text=reply) return send_telegram_message(chat=Chat(id=update.effective_chat.id), text="Чот непонятна 🤔")
def process_auth(update: Update): user = None if update.message and update.message.text: user = User.objects.filter( secret_hash=str(update.message.text).strip()).first() if not user: send_telegram_message( chat=Chat(id=update.effective_chat.id), text= "Привет. Мы пока не знакомы. Привяжи меня на сайте или пришли мне секретный код 👇" ) return user.telegram_id = update.effective_user.id user.telegram_data = { "id": update.effective_user.id, "username": update.effective_user.username, "first_name": update.effective_user.first_name, "last_name": update.effective_user.last_name, "language_code": update.effective_user.language_code, } user.save() send_telegram_message(chat=Chat(id=update.effective_chat.id), text=f"Отлично! Приятно познакомиться, {user.slug}") cache.delete("bot:telegram_user_ids")
def parse_forwarded_messages(update: Update): started_post = { "title": None, "type": Post.TYPE_POST, "text": update.message.text or update.message.caption, "url": None, "is_visible": True, "is_public": True, } for entity, text in update.message.parse_entities().items(): if entity.type == "url": started_post["url"] = text elif entity.type == "bold": started_post["title"] = text # save it to user cache cached_post_set(update.effective_user.id, started_post) if started_post["url"]: # looks like a link send_telegram_message( chat=Chat(id=update.effective_chat.id), text= f"Выглядит как ссылка. Хотите поделиться ей в Клубе? Как будем постить?", reply_markup=telegram.InlineKeyboardMarkup( [[ telegram.InlineKeyboardButton("🔗 Ссылкой", callback_data=f"link"), telegram.InlineKeyboardButton("📝 Как пост", callback_data=f"post"), ], [ telegram.InlineKeyboardButton("❌ Отмена", callback_data=f"nope"), ]])) else: # looks like a text post if len(started_post["text"] or "") < 120: return "Напиши или форвардни мне нормальный пост или ссылку!" send_telegram_message( chat=Chat(id=update.effective_chat.id), text=f"Хотите поделиться этим в Клубе? Как будем постить?", reply_markup=telegram.InlineKeyboardMarkup( [[ telegram.InlineKeyboardButton("📝 Как пост", callback_data=f"post"), telegram.InlineKeyboardButton("❔ Вопросом", callback_data=f"question"), ], [ telegram.InlineKeyboardButton("❌ Отмена", callback_data=f"nope"), ]]))
def process_comment_reply(update: Update): if not update.message.reply_to_message: return user = User.objects.filter(telegram_id=update.effective_user.id).first() if not user: send_telegram_message( chat=Chat(id=update.effective_user.id), text=f"😐 Извините, мы не знакомы. Привяжите свой аккаунт в профиле на https://vas3k.club" ) return comment_url_entity = [ entity["url"] for entity in update.message.reply_to_message.entities if entity["type"] == "text_link" and COMMENT_URL_RE.match(entity["url"]) ] if not comment_url_entity: log.info(f"Comment url not found in: {update.message.reply_to_message.entities}") return reply_to_id = COMMENT_URL_RE.match(comment_url_entity[0]).group(1) reply = Comment.objects.filter(id=reply_to_id).first() if not reply: log.info(f"Reply not found: {reply_to_id}") return is_ok = Comment.check_rate_limits(user) if not is_ok: send_telegram_message( chat=Chat(id=update.effective_chat.id), text=f"🙅♂️ Извините, вы комментировали слишком часто и достигли дневного лимита" ) return comment = Comment.objects.create( author=user, post=reply.post, reply_to=Comment.find_top_comment(reply), text=update.message.text, useragent="TelegramBot (like TwitterBot)", metadata={ "telegram": update.to_dict() } ) new_comment_url = settings.APP_HOST + reverse("show_comment", kwargs={ "post_slug": comment.post.slug, "comment_id": comment.id }) send_telegram_message( chat=Chat(id=update.effective_chat.id), text=f"➜ [Отвечено]({new_comment_url}) 👍" )
def async_create_or_update_achievement(user_achievement: UserAchievement): user = user_achievement.user achievement = user_achievement.achievement # messages if user.is_club_member and user.telegram_id: if achievement.image: send_telegram_image( chat=Chat(id=user.telegram_id), image_url=achievement.image, text=render_html_message("achievement.html", user=user, achievement=achievement), ) # emails if not user.is_email_unsubscribed: email_template = loader.get_template("emails/achievement.html") send_club_email( recipient=user.email, subject=f"🎖 Вас наградили бейджиком «{achievement.name}»", html=email_template.render({ "user": user, "achievement": achievement }), tags=["achievement"])
def notify_post_author_rejected(post): if post.author.telegram_id: send_telegram_message( chat=Chat(id=post.author.telegram_id), text=render_html_message("post_rejected.html", post=post), parse_mode=telegram.ParseMode.HTML, )
def async_create_or_update_comment(comment, is_created): # notify admins send_telegram_message( chat=ADMIN_CHAT, text=render_html_message("moderator_comment.html", comment=comment), parse_mode=telegram.ParseMode.HTML, ) # notify post author post_author = comment.post.author if post_author.telegram_id and comment.author != post_author: send_telegram_message( chat=Chat(id=comment.post.author.telegram_id), text=render_html_message("comment_to_post.html", comment=comment), parse_mode=telegram.ParseMode.HTML, ) # on reply — notify thread author (do not notify yourself) thread_author = None if comment.reply_to: thread_author = comment.reply_to.author if comment.reply_to_id and thread_author.telegram_id and comment.author != thread_author: send_telegram_message( chat=Chat(id=comment.reply_to.author.telegram_id), text=render_html_message("comment_to_thread.html", comment=comment), parse_mode=telegram.ParseMode.HTML, ) # post first level comments to club chat # if not comment.reply_to: # send_telegram_message( # chat=CLUB_CHAT, # text=render_html_message("comment_to_post_announce.html", comment=comment), # parse_mode=telegram.ParseMode.HTML, # ) # parse @nicknames and notify their users for username in USERNAME_RE.findall(comment.text): user = User.objects.filter(slug=username).first() if user and user.telegram_id and user != post_author and user != thread_author: send_telegram_message( chat=Chat(id=user.telegram_id), text=render_html_message("comment_mention.html", comment=comment), parse_mode=telegram.ParseMode.HTML, )
def notify_user_profile_approved(user): user_profile_url = settings.APP_HOST + reverse( "profile", kwargs={"user_slug": user.slug}) if user.telegram_id: send_telegram_message( chat=Chat(id=user.telegram_id), text=f"🚀 Подравляем, вы прошли модерацию. Добро пожаловать в Клуб!" f"\n\nМожно пойти заполнить другие смешные поля в профиле:" f"\n\n{user_profile_url}")
def async_create_or_update_comment(comment): notified_user_ids = set() # notify post subscribers post_subscribers = PostSubscription.post_subscribers(comment.post) for post_subscriber in post_subscribers: if post_subscriber.user.telegram_id and comment.author != post_subscriber.user: template = "comment_to_post.html" if post_subscriber.user == comment.post.author else "comment_to_post_announce.html" send_telegram_message( chat=Chat(id=post_subscriber.user.telegram_id), text=render_html_message(template, comment=comment), ) notified_user_ids.add(post_subscriber.user.id) # on reply — notify thread author (do not notify yourself) if comment.reply_to: thread_author = comment.reply_to.author if thread_author.telegram_id and comment.author != thread_author and thread_author.id not in notified_user_ids: send_telegram_message( chat=Chat(id=thread_author.telegram_id), text=render_html_message("comment_to_thread.html", comment=comment), ) notified_user_ids.add(thread_author.id) # post top level comments to online channel if not comment.reply_to: send_telegram_message( chat=CLUB_ONLINE, text=render_html_message("comment_to_post_announce.html", comment=comment), ) # parse @nicknames and notify their users for username in USERNAME_RE.findall(comment.text): user = User.objects.filter(slug=username).first() if user and user.telegram_id and user.id not in notified_user_ids: send_telegram_message( chat=Chat(id=user.telegram_id), text=render_html_message("comment_mention.html", comment=comment), ) notified_user_ids.add(user.id)
def get_bot_user(update): user = User.objects.filter(telegram_id=update.effective_user.id).first() if not user: send_telegram_message( chat=Chat(id=update.effective_chat.id), text= f"😐 Извините, мы не знакомы. Привяжите свой аккаунт в профиле на https://vas3k.club" ) return None if user.is_banned: send_telegram_message( chat=Chat(id=update.effective_user.id), text= f"😐 Извините, вы забанены до {user.is_banned_until.strftime('%d %B %Y')} и пока не можете писать" ) return None return user
def notify_user_profile_rejected(user: User, reason: RejectReason): try: text = render_html_message(f"rejected/{reason.value}.html", user=user) except TemplateDoesNotExist: text = render_html_message(f"rejected/intro.html", user=user) if user.telegram_id: send_telegram_message( chat=Chat(id=user.telegram_id), text=text, )
def notify_user_profile_rejected(user): user_profile_url = settings.APP_HOST + reverse("profile", kwargs={"user_slug": user.slug}) if user.telegram_id: send_telegram_message( chat=Chat(id=user.telegram_id), text=f"😐 К сожалению, ваш профиль не прошел модерацию. Но это не конец света и всё можно исправить." f"Вот популярные причины почему так бывает:\n" f"- Плохо написано #intro. Одного предложения обычно мало, нам же надо как-то познакомиться\n" f"- Вымышленное имя или профессия\n" f"- Много незаполненных полей\n" f"\n\nВот ссылка чтобы податься на ревью еще раз: {user_profile_url}" )
def async_create_or_update_comment(comment, is_created): # notify post author post_author = comment.post.author if post_author.telegram_id and comment.author != post_author: send_telegram_message( chat=Chat(id=comment.post.author.telegram_id), text=render_html_message("comment_to_post.html", comment=comment), ) # on reply — notify thread author (do not notify yourself) thread_author = None if comment.reply_to: thread_author = comment.reply_to.author if comment.reply_to_id and thread_author.telegram_id and comment.author != thread_author: send_telegram_message( chat=Chat(id=comment.reply_to.author.telegram_id), text=render_html_message("comment_to_thread.html", comment=comment), ) # post top level comments to online channel if not comment.reply_to: send_telegram_message( chat=CLUB_ONLINE, text=render_html_message("comment_to_post_announce.html", comment=comment), ) # parse @nicknames and notify their users for username in USERNAME_RE.findall(comment.text): user = User.objects.filter(slug=username).first() if user and user.telegram_id and user != post_author and user != thread_author: send_telegram_message( chat=Chat(id=user.telegram_id), text=render_html_message("comment_mention.html", comment=comment), )
def announce_in_club_chats(post): if post.topic and post.topic.chat_id: # announce to the topic chat send_telegram_message( chat=Chat(id=post.topic.chat_id), text=render_html_message("channel_post_announce.html", post=post), parse_mode=telegram.ParseMode.HTML, disable_preview=True, ) else: # announce to public chat send_telegram_message( chat=CLUB_CHAT, text=render_html_message("channel_post_announce.html", post=post), parse_mode=telegram.ParseMode.HTML, disable_preview=True, )
def notify_user_profile_rejected(user): user_profile_url = settings.APP_HOST + reverse("profile", kwargs={"user_slug": user.slug}) if user.telegram_id: send_telegram_message( chat=Chat(id=user.telegram_id), text=f"😐 К сожалению, ваш профиль не прошел модерацию. Вот популярные причины почему так бывает:\n\n" f"- 📝 Маленькое #intro. Допишите еще хотя бы пару абзацев. Для примера посмотрите чужие, " f"там есть ссылочки. <a href=\"https://vas3k.club/docs/about/#rules\">Наши правила</a>, " f"с которыми вы согласились, запрещают анонимусов в Клубе.\n" f"- 🤔 Много незаполненных полей. Мы не поняли кто вы. Профиль без фамилии или компании вряд " f"ли пройдет модерацию.\n" f"- 🤪 Вымышленное имя или профессия (например, Олег).\n" f"- 🙅♀️ Наличие фраз типа «не скажу», «не люблю писать о себе», «потом заполню». " f"Потом так потом, мы не торопимся :)\n" f"- 💨 Душность, глупость или желание обмануть модераторов.\n\n" f"\n\nВот ссылка чтобы исправить недочёты и податься на ревью еще раз: {user_profile_url}" )
def async_create_or_update_post(post, is_created): if not post.is_approved_by_moderator: send_telegram_message( chat=ADMIN_CHAT, text=render_html_message("moderator_new_post_review.html", post=post), reply_markup=telegram.InlineKeyboardMarkup( [[ telegram.InlineKeyboardButton( "👍 Одобрить", callback_data=f"approve_post:{post.id}"), telegram.InlineKeyboardButton( "😕 Так себе", callback_data=f"forgive_post:{post.id}"), ], [ telegram.InlineKeyboardButton( "❌ В черновики", callback_data=f"delete_post:{post.id}"), ]])) # post to online channel send_telegram_message( chat=CLUB_ONLINE, text=render_html_message("channel_post_announce.html", post=post), parse_mode=telegram.ParseMode.HTML, disable_preview=True, ) # parse @nicknames and notify mentioned users (only if post is visible) if post.is_visible and (is_created or "is_visible" in post.changed_fields): notified_user_ids = set() for username in USERNAME_RE.findall(post.text): user = User.objects.filter(slug=username).first() if user and user.telegram_id and user.id not in notified_user_ids: send_telegram_message( chat=Chat(id=user.telegram_id), text=render_html_message("post_mention.html", post=post), ) notified_user_ids.add(user.id)
def continue_posting(update: Update, started_post: dict, user: User): if update.callback_query: if update.callback_query.data == "nope": # cancel posting cached_post_delete(update.effective_user.id) return "Ок, забыли 👌" elif update.callback_query.data in {"post", "link", "question"}: # ask for title started_post["type"] = update.callback_query.data cached_post_set(update.effective_user.id, started_post) return f"Отлично. Теперь надо придумать заголовок, чтобы всем было понятно о чем это. " \ f"Подумайте и пришлите его следующим сообщением 👇" elif update.callback_query.data == "go": # go-go-go, post the post FormClass = POST_TYPE_MAP.get(started_post["type"]) or PostTextForm form = FormClass(started_post) if not form.is_valid(): return f"🤦♂️ Что-то пошло не так. Пришлите нам багрепорт. " \ f"Вот ошибка:\n```{str(form.errors)}```" if Post.check_duplicate(user=user, title=form.cleaned_data["title"]): return "🤔 Выглядит как дубликат вашего прошлого поста. " \ "Проверьте всё ли в порядке и пришлите ниже другой заголовок 👇" is_ok = Post.check_rate_limits(user) if not is_ok: return "🙅♂️ Извините, вы сегодня запостили слишком много постов. Попробуйте попозже" post = form.save(commit=False) post.author = user post.type = started_post["type"] post.meta = {"telegram": update.to_json()} post.save() post_url = settings.APP_HOST + reverse("show_post", kwargs={ "post_type": post.type, "post_slug": post.slug }) cached_post_delete(update.effective_user.id) return f"Запостили 🚀🚀🚀\n\n{post_url}" if update.message: started_post["title"] = str(update.message.text or update.message.caption or "").strip()[:128] if len(started_post["title"]) < 7: send_telegram_message( chat=Chat(id=update.effective_chat.id), text=f"Какой-то короткий заголовок. Пришлите другой, подлинее", reply_markup=telegram.InlineKeyboardMarkup([[ telegram.InlineKeyboardButton("❌ Отменить всё", callback_data=f"nope"), ]])) return cached_post_set(update.effective_user.id, started_post) emoji = Post.TYPE_TO_EMOJI.get(started_post["type"]) or "🔥" send_telegram_message( chat=Chat(id=update.effective_chat.id), text=f"Заголовок принят. Теперь пост выглядит как-то так:\n\n" f"{emoji} <b>{started_post['title']}</b>\n\n" f"{started_post['text'] or ''}\n\n" f"{started_post['url'] or ''}\n\n" f"<b>Будем постить?</b> (после публикации его можно будет подредактировать на сайте)", reply_markup=telegram.InlineKeyboardMarkup([ [ telegram.InlineKeyboardButton("✅ Поехали", callback_data=f"go"), telegram.InlineKeyboardButton("❌ Отмена", callback_data=f"nope"), ], ]))
def notify_user_auth(user, code): if user.telegram_id: send_telegram_message( chat=Chat(id=user.telegram_id), text=f"<b>{code.code}</b> — ваш одноразовый код для входа в Клуб")
def notify_user_ping(user, message): if user.telegram_id: send_telegram_message( chat=Chat(id=user.telegram_id), text=f"👋 <b>Вам письмо от модераторов Клуба:</b> {message}")