From cb895428095f936e22446b39baf6d8f42ca79ab9 Mon Sep 17 00:00:00 2001 From: ennucore Date: Fri, 24 Jun 2022 18:04:00 +0300 Subject: [PATCH] If a user is a member of a chat, but not a member of the corresponding community, suggest it --- bot.py | 16 ++++++++++++++++ main.py | 7 ++++--- templates.txt | 10 ++++++++++ user.py | 17 ++++++++++++++++- views.py | 32 +++++++++++++++++++++++++------- 5 files changed, 71 insertions(+), 11 deletions(-) diff --git a/bot.py b/bot.py index 85e5812..0a2dc99 100644 --- a/bot.py +++ b/bot.py @@ -205,6 +205,21 @@ class Bot(telebot.TeleBot): self.poll_user_for_community(user, community) self.save_community(community) + def run_suggestions(self): + for user in self.iter_users(): + found_communities = False + try: + for community in self.iter_communities(): + if user.check_chat(community.chat_id, self): + self.send_template(user.user_id, 'community_suggest', community=community) + found_communities = True + except KeyboardInterrupt as e: + raise e + except Exception: + print(f'An exception occured in suggestions: {traceback.format_exc()}') + if found_communities: + self.save_user(user) + def send_meeting_info_in_community(self, community: Community): for meeting in community.scheduled_meetings: person_1, person_2 = self.get_chat(meeting[0]), self.get_chat(meeting[1]) @@ -240,6 +255,7 @@ class Bot(telebot.TeleBot): raise e except Exception: print(f'An exception occurred while iterating through communities: {traceback.format_exc()}') + Thread(target=self.run_suggestions).start() time.sleep(60) def register_next_step_handler(self, message, callback, *args, **kwargs): diff --git a/main.py b/main.py index f6d3fda..413251d 100644 --- a/main.py +++ b/main.py @@ -2,12 +2,13 @@ from bot import Bot from views import views from threading import Thread import traceback +import os config = { - 'mongo': __import__('os').getenv('DB'), - 'name': 'ranteabot', - 'token': __import__('os').getenv('TOKEN'), + 'mongo': os.getenv('DB'), + 'name': 'ranteabot' if not os.getenv('DEBUG') else 'flazhokbot', + 'token': os.getenv('TOKEN'), 'template_files': ['templates.txt'], 'main_keyboard': {'en': [['ℹ️ About']], 'ru': [['ℹ️ О боте']]} } diff --git a/templates.txt b/templates.txt index 3079a74..bdaedc2 100644 --- a/templates.txt +++ b/templates.txt @@ -81,6 +81,16 @@ You have already requested a meeting in the community {{ community.name }}{{ community.name }} недавно. Вы сможете запросить еще одну встречу через несколько дней. +||<| community_suggest |>|| +//| en |// +You are a member of the chat {{ community.name }}. Do you want to add this community in the bot and participate in meetings? +>>| Yes :-: community_add_{{ community.chat_id }} |<< +>>| No :-: dismiss |<< +//| ru |// +Вы являетесь участником чата {{ community.name }}. Хотите добавить его как сообщество в бота и присоединиться к встречам? +>>| Да :-: community_add_{{ community.chat_id }} |<< +>>| Нет :-: dismiss |<< + ||<| canceled |>|| //| en |// Canceled diff --git a/user.py b/user.py index 054f147..854c833 100644 --- a/user.py +++ b/user.py @@ -3,6 +3,7 @@ from dataclasses import dataclass, field, asdict import typing import time from community import Community +from telebot.types import ChatMember if typing.TYPE_CHECKING: from bot import Bot @@ -15,6 +16,20 @@ class User: locale: str = '' start_timestamp: int = field(default_factory=lambda: int(time.time())) last_action_timestamp: int = field(default_factory=lambda: int(time.time())) + # Store the chats which were checked + # (so that we send a message for communities + # where the user is a member of the chat but not a member of the RT community) + checked_chats: typing.List[int] = field(default_factory=list) + + def check_chat(self, chat_id: int, bot: Bot) -> bool: + """ + Check if the user should be notified of this chat + """ + if chat_id in self.checked_chats: + return False + self.checked_chats.append(chat_id) + chat_member_info: ChatMember = bot.get_chat_member(chat_id, self.user_id) + return chat_member_info.is_member or chat_member_info.status in ['creator', 'administrator', 'admin', 'member'] def dict(self) -> dict: data = asdict(self) @@ -25,7 +40,7 @@ class User: def from_dict(cls, data: dict) -> User: data = getattr(data, '__dict__', data) data_ = {key: data[key] for key in data.keys() if - key in ['user_id', 'chat_id', 'locale', 'start_timestamp', 'last_action_timestamp']} + key in ['user_id', 'chat_id', 'locale', 'start_timestamp', 'last_action_timestamp', 'checked_chats']} self = cls(**data_) return self diff --git a/views.py b/views.py index fcb5152..94e97f0 100644 --- a/views.py +++ b/views.py @@ -1,4 +1,5 @@ from datetime import date +from telebot.types import CallbackQuery, Message from bot import Bot from community import Community @@ -6,7 +7,7 @@ from community import Community def views(bot: Bot): @bot.handle_commands(['/start']) - def handle_start(msg, user, args): + def handle_start(msg: Message, user, args): if msg.chat.type in 'supergroup': bot.reply_with_template(msg, 'welcome', community=Community.by_id(msg.chat.id, bot)) return @@ -20,6 +21,7 @@ def views(bot: Bot): bot.poll_user_for_community(user, community) else: bot.reply_with_template(msg, 'err_not_a_member', community=community) + bot.save_community(community) bot.reply_with_template(msg, 'info', start=True) @bot.handle_commands(['/help', 'ℹ️ About', 'ℹ️ О боте']) @@ -27,7 +29,7 @@ def views(bot: Bot): bot.reply_with_template(msg, 'info', start=False) @bot.handle_commands(['/request', '📝 Request a meeting', '📝 Запросить встречу']) - def request_meeting(msg, user, _args): + def request_meeting(msg: Message, user, _args): if msg.chat.type != 'private': bot.reply_with_template(msg, 'err_not_private') return @@ -35,7 +37,7 @@ def views(bot: Bot): bot.reply_with_template(msg, 'request_meeting', comm_list=comm_ids_and_names) @bot.handle_commands(['/join']) - def join_community(msg, user, args): + def join_community(msg: Message, user, args): # check if it really is a community if msg.chat.type == 'private': bot.reply_with_template(msg, 'err_not_community') @@ -45,15 +47,31 @@ def views(bot: Bot): bot.reply_with_template(msg, 'welcome', community=community, join=True) @bot.handle_callback('request_meeting') - def request_meeting_callback(msg, user, args): + def request_meeting_callback(query: CallbackQuery, user, args): community = Community.by_id(int(args), bot) if community.can_user_request_a_meeting(user.user_id): community.polled[user.user_id] = date.today() community.pool.append(user.user_id) - bot.reply_with_template(msg, 'request_meeting_success', community=community) + bot.reply_with_template(query, 'request_meeting_success', community=community) else: - bot.reply_with_template(msg, 'request_meeting_failure', community=community) - bot.answer_callback_query(msg.id) + bot.reply_with_template(query, 'request_meeting_failure', community=community) + bot.answer_callback_query(query.id) + + @bot.handle_callback('dismiss') + def dismiss_callback(query: CallbackQuery, user, _args): + bot.answer_callback_query(query.id, {'en': 'Ok, fine', 'ru': 'ладно'}.get(user.locale, 'Ok')) + bot.delete_message(query.message.chat.id, query.message.message_id) + + @bot.handle_callback('community_add') + def community_add_callback(query: CallbackQuery, user, args): + community = Community.by_id(int(args), bot) + if user.user_id in community.members: + bot.reply_with_template(query, 'community_added', community=community, already_member=True) + elif community.add_member(user.user_id, bot): + bot.reply_with_template(query, 'community_added', community=community, already_member=False) + bot.poll_user_for_community(user, community) + bot.answer_callback_query(query.id) + bot.save_community(community) @bot.handle_commands(['/send_all']) def send_spam(msg, user, _args):