diff --git a/requirements.txt b/requirements.txt index 1633097..fdea869 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,2 @@ -python_telegram_bot==13.5 +python-telegram-bot @ git+https://github.com/python-telegram-bot/python-telegram-bot@3940b531105d6396f368c4959eebee04f47e9931 multicolorcaptcha==1.2.0 diff --git a/sources/join_captcha_bot.py b/sources/join_captcha_bot.py index 295e16f..f0cc9ed 100644 --- a/sources/join_captcha_bot.py +++ b/sources/join_captcha_bot.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- -''' +""" Script: join_captcha_bot.py Description: @@ -15,7 +15,7 @@ 08/05/2022 Version: 1.26.0 -''' +""" ############################################################################### ### Imported modules @@ -23,6 +23,8 @@ from platform import system as os_system from signal import signal, SIGTERM, SIGINT +from typing import Union + if os_system() != "Windows": from signal import SIGUSR1 @@ -42,41 +44,72 @@ from multicolorcaptcha import CaptchaGenerator from telegram import ( - Update, Chat, InputMediaPhoto, InlineKeyboardButton, - InlineKeyboardMarkup, Poll + Bot, + Message, + Update, + Chat, + InputMediaPhoto, + InlineKeyboardButton, + InlineKeyboardMarkup, + Poll, ) from telegram.ext import ( - CallbackContext, Updater, CommandHandler, - ChatMemberHandler, MessageHandler, Filters, - CallbackQueryHandler, PollAnswerHandler, Defaults + CallbackContext, + Updater, + CommandHandler, + ChatMemberHandler, + MessageHandler, + filters, + CallbackQueryHandler, + PollAnswerHandler, + Defaults, + Application, ) -from telegram.utils.helpers import ( - escape_markdown -) +from telegram.helpers import escape_markdown from telegram.error import ( - TelegramError, Unauthorized, BadRequest, - TimedOut, NetworkError + TelegramError, + Forbidden as Unauthorized, + BadRequest, + TimedOut, + NetworkError, ) from commons import ( - printts, is_int, add_lrm, file_exists, file_write, file_read, - list_remove_element, get_unix_epoch, pickle_save, pickle_restore + printts, + is_int, + add_lrm, + file_exists, + file_write, + file_read, + list_remove_element, + get_unix_epoch, + pickle_save, + pickle_restore, ) from tlgbotutils import ( - tlg_send_msg, tlg_send_image, tlg_send_poll, tlg_stop_poll, - tlg_answer_callback_query, tlg_delete_msg, tlg_edit_msg_media, - tlg_ban_user, tlg_kick_user, tlg_user_is_admin, tlg_leave_chat, - tlg_restrict_user, tlg_is_valid_user_id_or_alias, tlg_is_valid_group, - tlg_alias_in_string, tlg_extract_members_status_change + tlg_send_msg, + tlg_send_image, + tlg_send_poll, + tlg_stop_poll, + tlg_answer_callback_query, + tlg_delete_msg, + tlg_edit_msg_media, + tlg_ban_user, + tlg_kick_user, + tlg_user_is_admin, + tlg_leave_chat, + tlg_restrict_user, + tlg_is_valid_user_id_or_alias, + tlg_is_valid_group, + tlg_alias_in_string, + tlg_extract_members_status_change, ) -from constants import ( - SCRIPT_PATH, CONST, TEXT -) +from constants import SCRIPT_PATH, CONST, TEXT ############################################################################### ### Globals @@ -96,14 +129,17 @@ ############################################################################### ### Setup Bot Logger -log_level=logging.INFO -logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=log_level) +log_level = logging.INFO +logging.basicConfig( + format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", level=log_level +) ############################################################################### ### Termination Signals Handler For Program Process -def signal_handler(signal, frame): - '''Termination signals (SIGINT, SIGTERM) handler for program process''' + +def signal_handler(signal, frame): + """Termination signals (SIGINT, SIGTERM) handler for program process""" global force_exit global updater global th_0 @@ -145,43 +181,45 @@ def signal_handler(signal, frame): def th_close_resource_file(file_to_close): - '''Threaded function to close resource files in parallel when closing Bot Script.''' + """Threaded function to close resource files in parallel when closing Bot Script.""" file_to_close.lock.acquire() ### Signals attachment -signal(SIGTERM, signal_handler) # SIGTERM (kill pid) to signal_handler +signal(SIGTERM, signal_handler) # SIGTERM (kill pid) to signal_handler signal(SIGINT, signal_handler) # SIGINT (Ctrl+C) to signal_handler if os_system() != "Windows": - signal(SIGUSR1, signal_handler) # SIGUSR1 (self-send) to signal_handler + signal(SIGUSR1, signal_handler) # SIGUSR1 (self-send) to signal_handler ############################################################################### ### JSON Chat Config File Functions + def get_default_config_data(): - '''Get default config data structure''' + """Get default config data structure""" config_data = OrderedDict( - [ - ("Title", CONST["INIT_TITLE"]), - ("Link", CONST["INIT_LINK"]), - ("Language", CONST["INIT_LANG"]), - ("Enabled", CONST["INIT_ENABLE"]), - ("URL_Enabled", CONST["INIT_URL_ENABLE"]), - ("RM_All_Msg", CONST["INIT_RM_ALL_MSG"]), - ("Captcha_Chars_Mode", CONST["INIT_CAPTCHA_CHARS_MODE"]), - ("Captcha_Time", CONST["INIT_CAPTCHA_TIME"]), - ("Captcha_Difficulty_Level", CONST["INIT_CAPTCHA_DIFFICULTY_LEVEL"]), - ("Restrict_Non_Text", CONST["INIT_RESTRICT_NON_TEXT_MSG"]), - ("Rm_Result_Msg", CONST["INIT_RM_RESULT_MSG"]), - ("Rm_Welcome_Msg", CONST["INIT_RM_WELCOME_MSG"]), - ("Poll_Q", ""), - ("Poll_A", []), - ("Poll_C_A", 0), - ("Welcome_Msg", "-"), - ("Welcome_Time", CONST["T_DEL_WELCOME_MSG"]), - ("Ignore_List", []) - ]) + [ + ("Title", CONST["INIT_TITLE"]), + ("Link", CONST["INIT_LINK"]), + ("Language", CONST["INIT_LANG"]), + ("Enabled", CONST["INIT_ENABLE"]), + ("URL_Enabled", CONST["INIT_URL_ENABLE"]), + ("RM_All_Msg", CONST["INIT_RM_ALL_MSG"]), + ("Captcha_Chars_Mode", CONST["INIT_CAPTCHA_CHARS_MODE"]), + ("Captcha_Time", CONST["INIT_CAPTCHA_TIME"]), + ("Captcha_Difficulty_Level", CONST["INIT_CAPTCHA_DIFFICULTY_LEVEL"]), + ("Restrict_Non_Text", CONST["INIT_RESTRICT_NON_TEXT_MSG"]), + ("Rm_Result_Msg", CONST["INIT_RM_RESULT_MSG"]), + ("Rm_Welcome_Msg", CONST["INIT_RM_WELCOME_MSG"]), + ("Poll_Q", ""), + ("Poll_A", []), + ("Poll_C_A", 0), + ("Welcome_Msg", "-"), + ("Welcome_Time", CONST["T_DEL_WELCOME_MSG"]), + ("Ignore_List", []), + ] + ) # Feed Captcha Poll Options with empty answers for expected max num for _ in range(0, CONST["MAX_POLL_OPTIONS"]): config_data["Poll_A"].append("") @@ -189,7 +227,7 @@ def get_default_config_data(): def save_config_property(chat_id, param, value): - '''Store actual chat configuration in file''' + """Store actual chat configuration in file""" fjson_config = get_chat_config_file(chat_id) config_data = fjson_config.read() if not config_data: @@ -201,7 +239,7 @@ def save_config_property(chat_id, param, value): def get_chat_config(chat_id, param): - '''Get specific stored chat configuration property''' + """Get specific stored chat configuration property""" file = get_chat_config_file(chat_id) if file: config_data = file.read() @@ -215,11 +253,11 @@ def get_chat_config(chat_id, param): def get_all_chat_config(chat_id): - '''Get specific stored chat configuration property''' + """Get specific stored chat configuration property""" file = get_chat_config_file(chat_id) if file: config_data = file.read() - if (not config_data): + if not config_data: config_data = get_default_config_data() else: config_data = get_default_config_data() @@ -227,7 +265,7 @@ def get_all_chat_config(chat_id): def get_chat_config_file(chat_id): - '''Determine chat config file from the list by ID. Get the file if exists or create it if not''' + """Determine chat config file from the list by ID. Get the file if exists or create it if not""" global files_config_list file = OrderedDict([("ID", chat_id), ("File", None)]) found = False @@ -238,56 +276,60 @@ def get_chat_config_file(chat_id): found = True break if not found: - chat_config_file_name = "{}/{}/{}".format(CONST["CHATS_DIR"], chat_id, CONST["F_CONF"]) + chat_config_file_name = "{}/{}/{}".format( + CONST["CHATS_DIR"], chat_id, CONST["F_CONF"] + ) file["ID"] = chat_id file["File"] = TSjson(chat_config_file_name) files_config_list.append(file) else: - chat_config_file_name = "{}/{}/{}".format(CONST["CHATS_DIR"], chat_id, CONST["F_CONF"]) + chat_config_file_name = "{}/{}/{}".format( + CONST["CHATS_DIR"], chat_id, CONST["F_CONF"] + ) file["ID"] = chat_id file["File"] = TSjson(chat_config_file_name) files_config_list.append(file) return file["File"] + ############################################################################### ### Telegram Related Functions -def tlg_send_msg_type_chat(bot, chat_type, chat_id, text, - **kwargs_for_send_message): - '''Send a telegram message normal or schedule to self-destruct depending - of chat type (private chat - normal; group - selfdestruct).''' + +async def tlg_send_msg_type_chat(bot, chat_type, chat_id, text, **kwargs_for_send_message): + """Send a telegram message normal or schedule to self-destruct depending + of chat type (private chat - normal; group - selfdestruct).""" if chat_type == "private": - tlg_send_msg(bot, chat_id, text, **kwargs_for_send_message) + await tlg_send_msg(bot, chat_id, text, **kwargs_for_send_message) else: - tlg_send_selfdestruct_msg(bot, chat_id, text, - **kwargs_for_send_message) + await tlg_send_selfdestruct_msg(bot, chat_id, text, **kwargs_for_send_message) -def tlg_send_selfdestruct_msg(bot, chat_id, message, - **kwargs_for_send_message): - '''tlg_send_selfdestruct_msg_in() with default delete time''' - return tlg_send_selfdestruct_msg_in(bot, chat_id, message, - CONST["T_DEL_MSG"], **kwargs_for_send_message) +def tlg_send_selfdestruct_msg(bot, chat_id, message, **kwargs_for_send_message): + """tlg_send_selfdestruct_msg_in() with default delete time""" + return tlg_send_selfdestruct_msg_in( + bot, chat_id, message, CONST["T_DEL_MSG"], **kwargs_for_send_message + ) -def tlg_send_selfdestruct_msg_in(bot, chat_id, message, time_delete_sec, - **kwargs_for_send_message): - '''Send a telegram message that will be auto-delete in specified time''' - sent_result = tlg_send_msg(bot, chat_id, message, - **kwargs_for_send_message) +async def tlg_send_selfdestruct_msg_in( + bot: Bot, chat_id: Union[str, int], message: Message, time_delete_sec, **kwargs_for_send_message +): + """Send a telegram message that will be auto-delete in specified time""" + sent_result = await tlg_send_msg(bot, chat_id, message, **kwargs_for_send_message) if sent_result["msg"] is None: return None tlg_msg_to_selfdestruct_in(sent_result["msg"], time_delete_sec) return sent_result["msg"].message_id -def tlg_msg_to_selfdestruct(message): - '''tlg_msg_to_selfdestruct_in() with default delete time''' +def tlg_msg_to_selfdestruct(message: Message): + """tlg_msg_to_selfdestruct_in() with default delete time""" tlg_msg_to_selfdestruct_in(message, CONST["T_DEL_MSG"]) -def tlg_msg_to_selfdestruct_in(message, time_delete_sec): - '''Add a telegram message to be auto-delete in specified time''' +def tlg_msg_to_selfdestruct_in(message: Message, time_delete_sec): + """Add a telegram message to be auto-delete in specified time""" global to_delete_in_time_messages_list # Check if provided message has all necessary attributtes if message is None: @@ -305,33 +347,45 @@ def tlg_msg_to_selfdestruct_in(message, time_delete_sec): chat_id = message.chat_id user_id = message.from_user.id msg_id = message.message_id + msg_thread_id = message.message_thread_id t0 = time() # Add sent message data to to-delete messages list - sent_msg_data = OrderedDict([("Chat_id", None), ("User_id", None), - ("Msg_id", None), ("time", None), ("delete_time", None)]) + sent_msg_data = OrderedDict( + [ + ("Chat_id", None), + ("User_id", None), + ("Msg_id", None), + ("Msg_thread_id", None), + ("time", None), + ("delete_time", None), + ] + ) sent_msg_data["Chat_id"] = chat_id sent_msg_data["User_id"] = user_id sent_msg_data["Msg_id"] = msg_id + sent_msg_data["Msg_thread_id"] = msg_thread_id sent_msg_data["time"] = t0 sent_msg_data["delete_time"] = t0 + time_delete_sec to_delete_in_time_messages_list.append(sent_msg_data) return True + ############################################################################### ### General Functions + def save_session(): - '''Backup current execution data''' + """Backup current execution data""" # Update time to kick users for chat_id in new_users: for user_id in new_users[chat_id]: t = time() t_diff = t - new_users[chat_id][user_id]["join_data"]["join_time"] - new_timeout = new_users[chat_id][user_id]["join_data"][ - "captcha_timeout"] - t_diff + new_timeout = ( + new_users[chat_id][user_id]["join_data"]["captcha_timeout"] - t_diff + ) new_users[chat_id][user_id]["join_data"]["join_time"] = t - new_users[chat_id][user_id]["join_data"]["captcha_timeout"] = \ - new_timeout + new_users[chat_id][user_id]["join_data"]["captcha_timeout"] = new_timeout # Update messages delete time before save i = 0 while i < len(to_delete_in_time_messages_list): @@ -346,7 +400,7 @@ def save_session(): data = { "to_delete_in_time_messages_list": to_delete_in_time_messages_list, "new_users": new_users, - "connections": connections + "connections": connections, } if not pickle_save(CONST["F_SESSION"], data): printts("Fail to save current session data") @@ -356,7 +410,7 @@ def save_session(): def restore_session(): - '''Load last execution data''' + """Load last execution data""" global to_delete_in_time_messages_list global new_users global connections @@ -371,8 +425,9 @@ def restore_session(): # Load last session data to current RAM connections = last_session_data["connections"] new_users = last_session_data["new_users"] - to_delete_in_time_messages_list = \ - last_session_data["to_delete_in_time_messages_list"] + to_delete_in_time_messages_list = last_session_data[ + "to_delete_in_time_messages_list" + ] # Renew time to kick users for chat_id in new_users: for user_id in new_users[chat_id]: @@ -392,7 +447,7 @@ def restore_session(): def initialize_resources(): - '''Initialize resources by populating files list with chats found files''' + """Initialize resources by populating files list with chats found files""" global files_config_list # Remove old captcha directory and create it again if path.exists(CONST["CAPTCHAS_DIR"]): @@ -416,9 +471,12 @@ def initialize_resources(): files = listdir(CONST["CHATS_DIR"]) for f_chat_id in files: # Populate config files list - file_path = "{}/{}/{}".format(CONST["CHATS_DIR"], f_chat_id, CONST["F_CONF"]) - files_config_list.append(OrderedDict([("ID", f_chat_id), - ("File", TSjson(file_path))])) + file_path = "{}/{}/{}".format( + CONST["CHATS_DIR"], f_chat_id, CONST["F_CONF"] + ) + files_config_list.append( + OrderedDict([("ID", f_chat_id), ("File", TSjson(file_path))]) + ) # Create default configuration file if it does not exists if not path.exists(file_path): default_conf = get_default_config_data() @@ -431,7 +489,7 @@ def initialize_resources(): def load_urls_regex(file_path): - '''Load URL detection Regex from IANA TLD list text file.''' + """Load URL detection Regex from IANA TLD list text file.""" tlds_str = "" list_file_lines = [] try: @@ -449,21 +507,23 @@ def load_urls_regex(file_path): line = line.replace("\n", "|") list_file_lines.append(line) except Exception as e: - printts("Error opening file \"{}\". {}".format(file_path, str(e))) + printts('Error opening file "{}". {}'.format(file_path, str(e))) if len(list_file_lines) > 0: tlds_str = "".join(list_file_lines) CONST["REGEX_URLS"] = CONST["REGEX_URLS"].format(tlds_str) def load_texts_languages(): - '''Load all texts from each language file.''' + """Load all texts from each language file.""" # Initialize all languages to english texts by default, so if # some language file miss some field, the english text is used lang_file = "{}/{}.json".format(CONST["LANG_DIR"], CONST["INIT_LANG"].lower()) json_init_lang_texts = TSjson(lang_file).read() if (json_init_lang_texts is None) or (json_init_lang_texts == {}): - printts("Error loading language \"{}\" from {}. Language file not found or bad JSON " - "syntax.".format(CONST["INIT_LANG"].lower(), lang_file)) + printts( + 'Error loading language "{}" from {}. Language file not found or bad JSON ' + "syntax.".format(CONST["INIT_LANG"].lower(), lang_file) + ) printts("Exit.\n") exit(0) for lang_iso_code in TEXT: @@ -474,8 +534,10 @@ def load_texts_languages(): json_lang_file = TSjson(lang_file) json_lang_texts = json_lang_file.read() if (json_lang_texts is None) or (json_lang_texts == {}): - printts("Error loading language \"{}\" from {}. Language file not found or bad JSON " - "sintax.".format(lang_iso_code, lang_file)) + printts( + 'Error loading language "{}" from {}. Language file not found or bad JSON ' + "sintax.".format(lang_iso_code, lang_file) + ) printts("Exit.\n") exit(0) for text in json_lang_texts: @@ -488,12 +550,15 @@ def load_texts_languages(): json_lang_texts = json_lang_file.read() for text in json_init_lang_texts: if text not in json_lang_texts: - printts("Warning: text \"{}\" missing from language file \"{}\".json".format( - text, lang_iso_code)) + printts( + 'Warning: text "{}" missing from language file "{}".json'.format( + text, lang_iso_code + ) + ) def create_image_captcha(chat_id, file_name, difficult_level, captcha_mode): - '''Generate an image captcha from pseudo numbers''' + """Generate an image captcha from pseudo numbers""" # If it doesn't exists, create captchas folder to store generated captchas img_dir_path = "{}/{}".format(CONST["CAPTCHAS_DIR"], chat_id) img_file_path = "{}/{}.png".format(img_dir_path, file_name) @@ -512,22 +577,23 @@ def create_image_captcha(chat_id, file_name, difficult_level, captcha_mode): "image": img_file_path, "characters": "", "equation_str": "", - "equation_result": "" + "equation_result": "", } if captcha_mode == "math": captcha = CaptchaGen.gen_math_captcha_image(2, bool(randint(0, 1))) captcha_result["equation_str"] = captcha["equation_str"] captcha_result["equation_result"] = captcha["equation_result"] else: - captcha = CaptchaGen.gen_captcha_image(difficult_level, captcha_mode, - bool(randint(0, 1))) + captcha = CaptchaGen.gen_captcha_image( + difficult_level, captcha_mode, bool(randint(0, 1)) + ) captcha_result["characters"] = captcha["characters"] captcha["image"].save(img_file_path, "png") return captcha_result def num_config_poll_options(poll_options): - '''Check how many poll options are configured.''' + """Check how many poll options are configured.""" configured_options = 0 for i in range(0, CONST["MAX_POLL_OPTIONS"]): if poll_options[i] != "": @@ -536,7 +602,7 @@ def num_config_poll_options(poll_options): def is_user_in_ignored_list(chat_id, user): - '''Check if user is in ignored users list.''' + """Check if user is in ignored users list.""" ignored_users = get_chat_config(chat_id, "Ignore_List") if user.id in ignored_users: return True @@ -548,7 +614,7 @@ def is_user_in_ignored_list(chat_id, user): def is_user_in_allowed_list(user): - '''Check if user is in global allowed list.''' + """Check if user is in global allowed list.""" l_white_users = file_read(CONST["F_ALLOWED_USERS"]) if user.id in l_white_users: return True @@ -560,7 +626,7 @@ def is_user_in_allowed_list(user): def is_group_in_allowed_list(chat_id): - '''Check if group is in allowed list.''' + """Check if group is in allowed list.""" # True if Bot is Public if not CONST["BOT_PRIVATE"]: return True @@ -571,15 +637,15 @@ def is_group_in_allowed_list(chat_id): def is_group_in_banned_list(chat_id): - '''Check if group is in banned list.''' + """Check if group is in banned list.""" l_banned_groups = file_read(CONST["F_BAN_GROUPS"]) if str(chat_id) in l_banned_groups: return True return False -def allowed_in_this_group(bot, chat, member_added_by): - '''Check if Bot is allowed to be used in a Chat.''' +async def allowed_in_this_group(bot, chat, member_added_by): + """Check if Bot is allowed to be used in a Chat.""" if not is_group_in_allowed_list(chat.id): printts("Warning: Bot added to not allowed group.") from_user_name = "" @@ -590,11 +656,11 @@ def allowed_in_this_group(bot, chat, member_added_by): chat_link = "" if chat.username: chat_link = "@{}".format(chat.username) - printts("{}, {}, {}, {}".format(chat.id, from_user_name, chat.title, - chat_link)) - msg_text = CONST["NOT_ALLOW_GROUP"].format(CONST["BOT_OWNER"], chat.id, - CONST["REPOSITORY"]) - tlg_send_msg(bot, chat.id, msg_text) + printts("{}, {}, {}, {}".format(chat.id, from_user_name, chat.title, chat_link)) + msg_text = CONST["NOT_ALLOW_GROUP"].format( + CONST["BOT_OWNER"], chat.id, CONST["REPOSITORY"] + ) + await tlg_send_msg(bot, chat.id, msg_text) return False if is_group_in_banned_list(chat.id): printts("[{}] Warning: Bot added to banned group".format(chat.id)) @@ -603,8 +669,8 @@ def allowed_in_this_group(bot, chat, member_added_by): def get_update_user_lang(update_user_data): - '''Get user language-code from Telegram Update user data and return - Bot supported language (english if not supported).''' + """Get user language-code from Telegram Update user data and return + Bot supported language (english if not supported).""" lang = getattr(update_user_data, "language_code", "EN") if lang is None: lang = "EN" @@ -613,12 +679,14 @@ def get_update_user_lang(update_user_data): lang = "EN" return lang + ############################################################################### ### Received Telegram not-command messages handlers + def chat_bot_status_change(update: Update, context: CallbackContext): - '''Get Bot chats status changes (Bot added to group/channel, - started/stoped conversation in private chat, etc.) event handler.''' + """Get Bot chats status changes (Bot added to group/channel, + started/stoped conversation in private chat, etc.) event handler.""" # Check Bot changes result = tlg_extract_members_status_change(update.my_chat_member) if result is None: @@ -632,12 +700,12 @@ def chat_bot_status_change(update: Update, context: CallbackContext): if chat.type == Chat.PRIVATE: return # Bot private conversation started - #if not was_member and is_member: + # if not was_member and is_member: # # ... # Bot private conversation blocked - #elif was_member and not is_member: + # elif was_member and not is_member: # # ... - #else: + # else: # return # Groups elif chat.type in [Chat.GROUP, Chat.SUPERGROUP]: @@ -673,8 +741,11 @@ def chat_bot_status_change(update: Update, context: CallbackContext): print("[{}] Bot leave the group".format(chat.id)) # Bot removed from group else: - print("[{}] Bot removed from group by {}".format( - chat.id, caused_by_user.username)) + print( + "[{}] Bot removed from group by {}".format( + chat.id, caused_by_user.username + ) + ) return else: return @@ -692,10 +763,10 @@ def chat_bot_status_change(update: Update, context: CallbackContext): return -def chat_member_status_change(update: Update, context: CallbackContext): - '''Get Members chats status changes (user join/leave/added/removed to/from +async def chat_member_status_change(update: Update, context: CallbackContext): + """Get Members chats status changes (user join/leave/added/removed to/from group/channel) event handler. Note: if Bot is not an Admin, "chat_member" - update won't be received.''' + update won't be received.""" global new_users bot = context.bot # Check members changes @@ -730,8 +801,9 @@ def chat_member_status_change(update: Update, context: CallbackContext): # for arabic, hebrew, etc.) user_name_lrm = add_lrm(join_user_name) printts(" ") - printts("[{}] New join detected: {} ({})".format(chat_id, - join_user_name, join_user_id)) + printts( + "[{}] New join detected: {} ({})".format(chat_id, join_user_name, join_user_id) + ) # Get and update chat data chat_title = chat.title if chat_title: @@ -744,14 +816,13 @@ def chat_member_status_change(update: Update, context: CallbackContext): chat_link = "@{}".format(chat_link) save_config_property(chat_id, "Link", chat_link) # Ignore Admins - if tlg_user_is_admin(bot, join_user_id, chat_id): + if await tlg_user_is_admin(bot, join_user_id, chat_id): printts("[{}] User is an administrator.".format(chat_id)) printts("Skipping the captcha process.") return # Ignore Members added by an Admin - if tlg_user_is_admin(bot, member_added_by.id, chat_id): - printts("[{}] User has been added by an administrator.".format( - chat_id)) + if await tlg_user_is_admin(bot, member_added_by.id, chat_id): + printts("[{}] User has been added by an administrator.".format(chat_id)) printts("Skipping the captcha process.") return # Ignore if the member that has been join the group is a Bot @@ -773,7 +844,7 @@ def chat_member_status_change(update: Update, context: CallbackContext): if join_user_id in new_users[chat_id]: if "msg_to_rm" in new_users[chat_id][join_user_id]: for msg in new_users[chat_id][join_user_id]["msg_to_rm"]: - tlg_delete_msg(bot, chat_id, msg) + await tlg_delete_msg(bot, chat_id, msg) new_users[chat_id][join_user_id]["msg_to_rm"].clear() # Ignore if the captcha protection is not enable in this chat captcha_enable = get_chat_config(chat_id, "Enabled") @@ -798,44 +869,85 @@ def chat_member_status_change(update: Update, context: CallbackContext): poll_question = get_chat_config(chat_id, "Poll_Q") poll_options = get_chat_config(chat_id, "Poll_A") poll_correct_option = get_chat_config(chat_id, "Poll_C_A") - if (poll_question == "") or \ - (num_config_poll_options(poll_options) < 2) or \ - (poll_correct_option == 0): + if ( + (poll_question == "") + or (num_config_poll_options(poll_options) < 2) + or (poll_correct_option == 0) + ): captcha_mode = choice(["nums", "math"]) if captcha_mode == "button": # Send a button-only challenge - challenge_text = TEXT[lang]["NEW_USER_BUTTON_MODE"].format(user_name_lrm, - chat_title, timeout_str) + challenge_text = TEXT[lang]["NEW_USER_BUTTON_MODE"].format( + user_name_lrm, chat_title, timeout_str + ) # Prepare inline keyboard button to let user pass - keyboard = [[InlineKeyboardButton(TEXT[lang]["PASS_BTN_TEXT"], - callback_data="button_captcha {}".format(join_user_id))]] + keyboard = [ + [ + InlineKeyboardButton( + TEXT[lang]["PASS_BTN_TEXT"], + callback_data="button_captcha {}".format(join_user_id), + ) + ] + ] reply_markup = InlineKeyboardMarkup(keyboard) - printts("[{}] Sending captcha message to {}: [button]".format(chat_id, join_user_name)) - sent_result = tlg_send_msg(bot, chat_id, challenge_text, - reply_markup=reply_markup, timeout=40) + printts( + "[{}] Sending captcha message to {}: [button]".format( + chat_id, join_user_name + ) + ) + sent_result = await tlg_send_msg( + bot, + chat_id, + challenge_text, + reply_markup=reply_markup, + timeout=40, + message_thread_id=update.effective_message.message_thread_id, + ) if sent_result["msg"] is None: send_problem = True elif captcha_mode == "poll": poll_question = get_chat_config(chat_id, "Poll_Q") poll_options = get_chat_config(chat_id, "Poll_A") poll_correct_option = get_chat_config(chat_id, "Poll_C_A") - if (poll_question == "") or (num_config_poll_options(poll_options) < 2) \ - or (poll_correct_option == 0): - tlg_send_selfdestruct_msg_in(bot, chat_id, TEXT[lang]["POLL_NEW_USER_NOT_CONFIG"], \ - CONST["T_FAST_DEL_MSG"]) + if ( + (poll_question == "") + or (num_config_poll_options(poll_options) < 2) + or (poll_correct_option == 0) + ): + await tlg_send_selfdestruct_msg_in( + bot, + chat_id, + TEXT[lang]["POLL_NEW_USER_NOT_CONFIG"], + CONST["T_FAST_DEL_MSG"], + message_thread_id=update.effective_message.message_thread_id, + ) return # Remove empty strings from options list poll_options = list(filter(None, poll_options)) # Send request to solve the poll text message - poll_request_msg_text = TEXT[lang]["POLL_NEW_USER"].format(user_name_lrm, - chat_title, timeout_str) - sent_result = tlg_send_selfdestruct_msg(bot, chat_id, poll_request_msg_text) + poll_request_msg_text = TEXT[lang]["POLL_NEW_USER"].format( + user_name_lrm, chat_title, timeout_str + ) + sent_result = await tlg_send_selfdestruct_msg( + bot, + chat_id, + poll_request_msg_text, + message_thread_id=update.effective_message.message_thread_id, + ) solve_poll_request_msg_id = None if sent_result is not None: solve_poll_request_msg_id = sent_result # Send the Poll - sent_result = tlg_send_poll(bot, chat_id, poll_question, poll_options, - poll_correct_option-1, captcha_timeout, False, Poll.QUIZ) + sent_result = await tlg_send_poll( + bot, + chat_id, + poll_question, + poll_options, + poll_correct_option - 1, + captcha_timeout, + False, + Poll.QUIZ, + ) if sent_result["msg"] is None: send_problem = True else: @@ -844,43 +956,64 @@ def chat_member_status_change(update: Update, context: CallbackContext): poll_id = sent_result["msg"].poll.id poll_msg_id = sent_result["msg"].message_id poll_data = { - poll_id: - { + poll_id: { "chat_id": chat_id, "poll_msg_id": poll_msg_id, "user_id": join_user_id, - "correct_option": poll_correct_option + "correct_option": poll_correct_option, } } context.bot_data.update(poll_data) - else: # Image captcha + else: # Image captcha # Generate a pseudorandom captcha send it to telegram group and # program message - captcha = create_image_captcha(chat_id, join_user_id, captcha_level, \ - captcha_mode) + captcha = create_image_captcha( + chat_id, join_user_id, captcha_level, captcha_mode + ) if captcha_mode == "math": captcha_num = captcha["equation_result"] - printts("[{}] Sending captcha message to {}: {}={}...".format( \ - chat_id, join_user_name, captcha["equation_str"], \ - captcha["equation_result"])) + printts( + "[{}] Sending captcha message to {}: {}={}...".format( + chat_id, + join_user_name, + captcha["equation_str"], + captcha["equation_result"], + ) + ) # Note: Img caption must be <= 1024 chars - img_caption = TEXT[lang]["NEW_USER_MATH_CAPTION"].format( \ - user_name_lrm, chat_title, timeout_str) + img_caption = TEXT[lang]["NEW_USER_MATH_CAPTION"].format( + user_name_lrm, chat_title, timeout_str + ) else: captcha_num = captcha["characters"] - printts("[{}] Sending captcha message to {}: {}...".format( \ - chat_id, join_user_name, captcha_num)) + printts( + "[{}] Sending captcha message to {}: {}...".format( + chat_id, join_user_name, captcha_num + ) + ) # Note: Img caption must be <= 1024 chars - img_caption = TEXT[lang]["NEW_USER_IMG_CAPTION"].format( \ - user_name_lrm, chat_title, timeout_str) + img_caption = TEXT[lang]["NEW_USER_IMG_CAPTION"].format( + user_name_lrm, chat_title, timeout_str + ) # Prepare inline keyboard button to let user request another catcha - keyboard = [[InlineKeyboardButton(TEXT[lang]["OTHER_CAPTCHA_BTN_TEXT"], - callback_data="image_captcha {}".format(join_user_id))]] + keyboard = [ + [ + InlineKeyboardButton( + TEXT[lang]["OTHER_CAPTCHA_BTN_TEXT"], + callback_data="image_captcha {}".format(join_user_id), + ) + ] + ] reply_markup = InlineKeyboardMarkup(keyboard) # Send the image - sent_result = tlg_send_image(bot, chat_id, \ - open(captcha["image"],"rb"), img_caption, \ - reply_markup=reply_markup) + sent_result = await tlg_send_image( + bot, + chat_id, + open(captcha["image"], "rb"), + img_caption, + reply_markup=reply_markup, + message_thread_id=update.effective_message.message_thread_id, + ) if sent_result["msg"] is None: send_problem = True # Remove sent captcha image file from file system @@ -889,17 +1022,16 @@ def chat_member_status_change(update: Update, context: CallbackContext): if not send_problem: # Add sent captcha message to self-destruct list if sent_result["msg"] is not None: - tlg_msg_to_selfdestruct_in(sent_result["msg"], captcha_timeout+10) + tlg_msg_to_selfdestruct_in(sent_result["msg"], captcha_timeout + 10) # Default user join data - join_data = \ - { + join_data = { "user_name": join_user_name, "captcha_num": captcha_num, "captcha_mode": captcha_mode, "join_time": time(), "captcha_timeout": captcha_timeout, "join_retries": 1, - "kicked_ban": False + "kicked_ban": False, } # Create dict keys for new user if chat_id not in new_users: @@ -917,21 +1049,27 @@ def chat_member_status_change(update: Update, context: CallbackContext): # Check if this user was before in the chat without solve the captcha # and restore previous join_retries if len(new_users[chat_id][join_user_id]["join_data"]) != 0: - join_data["join_retries"] = new_users[chat_id][join_user_id]["join_data"]["join_retries"] + join_data["join_retries"] = new_users[chat_id][join_user_id]["join_data"][ + "join_retries" + ] # Add new user join data and messages to be removed new_users[chat_id][join_user_id]["join_data"] = join_data if update.message: new_users[chat_id][join_user_id]["join_msg"] = update.message.message_id if sent_result["msg"]: - new_users[chat_id][join_user_id]["msg_to_rm"].append(sent_result["msg"].message_id) + new_users[chat_id][join_user_id]["msg_to_rm"].append( + sent_result["msg"].message_id + ) if (captcha_mode == "poll") and (solve_poll_request_msg_id is not None): - new_users[chat_id][join_user_id]["msg_to_rm"].append(solve_poll_request_msg_id) + new_users[chat_id][join_user_id]["msg_to_rm"].append( + solve_poll_request_msg_id + ) printts("[{}] Captcha send process complete.".format(chat_id)) printts(" ") -def msg_user_joined_group(update: Update, context: CallbackContext): - '''New member join the group event handler''' +async def msg_user_joined_group(update: Update, context: CallbackContext): + """New member join the group event handler""" global new_users # Get message data update_msg = getattr(update, "message", None) @@ -959,8 +1097,8 @@ def msg_user_joined_group(update: Update, context: CallbackContext): new_users[chat_id][join_user.id]["join_msg"] = msg_id -def msg_notext(update: Update, context: CallbackContext): - '''All non-text messages handler.''' +async def msg_notext(update: Update, context: CallbackContext): + """All non-text messages handler.""" bot = context.bot # Get message data update_msg = getattr(update, "message", None) @@ -1008,14 +1146,20 @@ def msg_notext(update: Update, context: CallbackContext): # Remove send message and notify that not text messages are not allowed until solve captcha msg_id = update_msg.message_id printts("[{}] Removing non-text message sent by {}".format(chat_id, user_name)) - tlg_delete_msg(bot, chat_id, msg_id) + await tlg_delete_msg(bot, chat_id, msg_id) lang = get_chat_config(chat_id, "Language") bot_msg = TEXT[lang]["NOT_TEXT_MSG_ALLOWED"].format(user_name) - tlg_send_selfdestruct_msg_in(bot, chat_id, bot_msg, CONST["T_FAST_DEL_MSG"]) + await tlg_send_selfdestruct_msg_in( + bot, + chat_id, + bot_msg, + CONST["T_FAST_DEL_MSG"], + message_thread_id=update.effective_message.message_thread_id, + ) -def msg_nocmd(update: Update, context: CallbackContext): - '''Non-command text messages handler''' +async def msg_nocmd(update: Update, context: CallbackContext): + """Non-command text messages handler""" global new_users bot = context.bot # Get message data @@ -1092,7 +1236,7 @@ def msg_nocmd(update: Update, context: CallbackContext): url_enable = get_chat_config(chat_id, "URL_Enabled") if not url_enable: # Ignore if message comes from an Admin - is_admin = tlg_user_is_admin(bot, user_id, chat_id) + is_admin = await tlg_user_is_admin(bot, user_id, chat_id) if is_admin: return # Get Chat configured language @@ -1104,7 +1248,9 @@ def msg_nocmd(update: Update, context: CallbackContext): delete_result = tlg_delete_msg(bot, chat_id, msg_id) if delete_result["error"] == "": bot_msg = TEXT[lang]["URL_MSG_NOT_ALLOWED_DETECTED"].format(user_name) - tlg_send_selfdestruct_msg_in(bot, chat_id, bot_msg, CONST["T_FAST_DEL_MSG"]) + tlg_send_selfdestruct_msg_in( + bot, chat_id, bot_msg, CONST["T_FAST_DEL_MSG"] + ) # Ignore if message is not from a new user that has not completed the captcha yet if chat_id not in new_users: return @@ -1121,21 +1267,35 @@ def msg_nocmd(update: Update, context: CallbackContext): printts("[{}] Spammer detected: {}.".format(chat_id, user_name)) printts("[{}] Removing spam message: {}.".format(chat_id, msg_text)) # Try to remove the message and notify detection - delete_result = tlg_delete_msg(bot, chat_id, msg_id) + delete_result = await tlg_delete_msg(bot, chat_id, msg_id) if delete_result["error"] == "": bot_msg = TEXT[lang]["SPAM_DETECTED_RM"].format(user_name) - tlg_send_selfdestruct_msg_in(bot, chat_id, bot_msg, CONST["T_FAST_DEL_MSG"]) + await tlg_send_selfdestruct_msg_in( + bot, + chat_id, + bot_msg, + CONST["T_FAST_DEL_MSG"], + message_thread_id=update.effective_message.message_thread_id, + ) # Check if message cant be removed due to not delete msg privileges elif delete_result["error"] == "Message can't be deleted": bot_msg = TEXT[lang]["SPAM_DETECTED_NOT_RM"].format(user_name) - tlg_send_selfdestruct_msg_in(bot, chat_id, bot_msg, CONST["T_FAST_DEL_MSG"]) + await tlg_send_selfdestruct_msg_in( + bot, + chat_id, + bot_msg, + CONST["T_FAST_DEL_MSG"], + message_thread_id=update.effective_message.message_thread_id, + ) else: printts("Message can't be deleted.") return # End here if no image captcha mode - if captcha_mode not in { "nums", "hex", "ascii", "math" }: + if captcha_mode not in {"nums", "hex", "ascii", "math"}: return - printts("[{}] Received captcha reply from {}: {}".format(chat_id, user_name, msg_text)) + printts( + "[{}] Received captcha reply from {}: {}".format(chat_id, user_name, msg_text) + ) # Check group config regarding if all messages of user must be removed when kick rm_all_msg = get_chat_config(chat_id, "RM_All_Msg") if rm_all_msg: @@ -1155,20 +1315,45 @@ def msg_nocmd(update: Update, context: CallbackContext): bot_msg = TEXT[lang]["CAPTCHA_SOLVED"].format(user_name) # Send message solve message if rm_result_msg: - tlg_send_selfdestruct_msg_in(bot, chat_id, bot_msg, CONST["T_FAST_DEL_MSG"]) + await tlg_send_selfdestruct_msg_in( + bot, + chat_id, + bot_msg, + CONST["T_FAST_DEL_MSG"], + message_thread_id=update.effective_message.message_thread_id, + ) else: - tlg_send_msg(bot, chat_id, bot_msg) + await tlg_send_msg( + bot, + chat_id, + bot_msg, + message_thread_id=update.effective_message.message_thread_id, + ) # Check for custom welcome message and send it - welcome_msg = get_chat_config(chat_id, "Welcome_Msg").format(escape_markdown(user_name)) + welcome_msg = get_chat_config(chat_id, "Welcome_Msg").format( + escape_markdown(user_name) + ) if welcome_msg != "-": # Send the message as Markdown rm_welcome_msg = get_chat_config(chat_id, "Rm_Welcome_Msg") if rm_welcome_msg: welcome_msg_time = get_chat_config(chat_id, "Welcome_Time") - sent_result = tlg_send_selfdestruct_msg_in(bot, chat_id, welcome_msg, - welcome_msg_time, parse_mode="MARKDOWN") + sent_result = await tlg_send_selfdestruct_msg_in( + bot, + chat_id, + welcome_msg, + welcome_msg_time, + parse_mode="MARKDOWN", + message_thread_id=update.effective_message.message_thread_id, + ) else: - sent_result = tlg_send_msg(bot, chat_id, welcome_msg, "MARKDOWN") + sent_result = await tlg_send_msg( + bot, + chat_id, + welcome_msg, + "MARKDOWN", + message_thread_id=update.effective_message.message_thread_id, + ) if sent_result is None: printts("[{}] Error: Can't send the welcome message.".format(chat_id)) # Check for send just text message option and apply user restrictions @@ -1176,53 +1361,92 @@ def msg_nocmd(update: Update, context: CallbackContext): # Restrict for 1 day if restrict_non_text_msgs == 1: tomorrow_epoch = get_unix_epoch() + CONST["T_RESTRICT_NO_TEXT_MSG"] - tlg_restrict_user(bot, chat_id, user_id, send_msg=True, send_media=False, - send_stickers_gifs=False, insert_links=False, send_polls=False, - invite_members=False, pin_messages=False, change_group_info=False, - until_date=tomorrow_epoch) + await tlg_restrict_user( + bot, + chat_id, + user_id, + send_msg=True, + send_media=False, + send_stickers_gifs=False, + insert_links=False, + send_polls=False, + invite_members=False, + pin_messages=False, + change_group_info=False, + until_date=tomorrow_epoch, + ) # Restrict forever elif restrict_non_text_msgs == 2: - tlg_restrict_user(bot, chat_id, user_id, send_msg=True, send_media=False, - send_stickers_gifs=False, insert_links=False, send_polls=False, - invite_members=False, pin_messages=False, change_group_info=False) + await tlg_restrict_user( + bot, + chat_id, + user_id, + send_msg=True, + send_media=False, + send_stickers_gifs=False, + insert_links=False, + send_polls=False, + invite_members=False, + pin_messages=False, + change_group_info=False, + ) # The provided message doesn't has the valid captcha number else: # Check if the message is for a math equation captcha - if (captcha_mode == "math"): + if captcha_mode == "math": clueless_user = False # Check if message is just 4 numbers if is_int(msg_text) and (len(msg_text) == 4): clueless_user = True # Check if message is "NN+NN" or "NN-NN" - elif (len(msg_text) == 5) and (is_int(msg_text[:2])) and \ - (is_int(msg_text[3:])) and (msg_text[2] in ["+", "-"]): + elif ( + (len(msg_text) == 5) + and (is_int(msg_text[:2])) + and (is_int(msg_text[3:])) + and (msg_text[2] in ["+", "-"]) + ): clueless_user = True # Tell the user that is wrong if clueless_user: - sent_msg_id = tlg_send_selfdestruct_msg_in(bot, chat_id, \ - TEXT[lang]["CAPTCHA_INCORRECT_MATH"], CONST["T_FAST_DEL_MSG"]) + sent_msg_id = await tlg_send_selfdestruct_msg_in( + bot, + chat_id, + TEXT[lang]["CAPTCHA_INCORRECT_MATH"], + CONST["T_FAST_DEL_MSG"], + message_thread_id=update.effective_message.message_thread_id, + ) new_users[chat_id][user_id]["msg_to_rm"].append(sent_msg_id) new_users[chat_id][user_id]["msg_to_rm"].append(msg_id) # If "nums", "hex" or "ascii" captcha else: # Check if the message has 4 chars if len(msg_text) == 4: - sent_msg_id = tlg_send_selfdestruct_msg_in(bot, chat_id, \ - TEXT[lang]["CAPTCHA_INCORRECT_0"], CONST["T_FAST_DEL_MSG"]) + sent_msg_id = await tlg_send_selfdestruct_msg_in( + bot, + chat_id, + TEXT[lang]["CAPTCHA_INCORRECT_0"], + CONST["T_FAST_DEL_MSG"], + message_thread_id=update.effective_message.message_thread_id, + ) new_users[chat_id][user_id]["msg_to_rm"].append(sent_msg_id) new_users[chat_id][user_id]["msg_to_rm"].append(msg_id) # Check if the message was just a 4 numbers msg elif is_int(msg_text): - sent_msg_id = tlg_send_selfdestruct_msg_in(bot, chat_id, \ - TEXT[lang]["CAPTCHA_INCORRECT_1"], CONST["T_FAST_DEL_MSG"]) + sent_msg_id = await tlg_send_selfdestruct_msg_in( + bot, + chat_id, + TEXT[lang]["CAPTCHA_INCORRECT_1"], + CONST["T_FAST_DEL_MSG"], + message_thread_id=update.effective_message.message_thread_id, + ) new_users[chat_id][user_id]["msg_to_rm"].append(sent_msg_id) new_users[chat_id][user_id]["msg_to_rm"].append(msg_id) printts("[{}] Captcha reply process complete.".format(chat_id)) printts(" ") -def receive_poll_answer(update: Update, context: CallbackContext): - '''User poll vote received''' +async def receive_poll_answer(update: Update, context: CallbackContext): + """User poll vote received""" global new_users bot = context.bot active_polls = context.bot_data @@ -1244,7 +1468,7 @@ def receive_poll_answer(update: Update, context: CallbackContext): poll_msg_id = poll_data["poll_msg_id"] poll_correct_option = poll_data["correct_option"] # The vote come from expected user, let's stop the Poll - tlg_stop_poll(bot, chat_id, poll_msg_id) + await tlg_stop_poll(bot, chat_id, poll_msg_id) # Get user name (if has an alias, just use the alias) user_name = from_user.full_name if from_user.username is not None: @@ -1253,7 +1477,9 @@ def receive_poll_answer(update: Update, context: CallbackContext): lang = get_chat_config(chat_id, "Language") rm_result_msg = get_chat_config(chat_id, "Rm_Result_Msg") rm_welcome_msg = get_chat_config(chat_id, "Rm_Welcome_Msg") - welcome_msg = get_chat_config(chat_id, "Welcome_Msg").format(escape_markdown(user_name)) + welcome_msg = get_chat_config(chat_id, "Welcome_Msg").format( + escape_markdown(user_name) + ) restrict_non_text_msgs = get_chat_config(chat_id, "Restrict_Non_Text") # Wait 3s to let poll animation be shown sleep(3) @@ -1266,7 +1492,13 @@ def receive_poll_answer(update: Update, context: CallbackContext): printts("[{}] User {} solved a poll challenge.".format(chat_id, user_name)) bot_msg = TEXT[lang]["CAPTCHA_SOLVED"].format(user_name) if rm_result_msg: - tlg_send_selfdestruct_msg_in(bot, chat_id, bot_msg, CONST["T_FAST_DEL_MSG"]) + await tlg_send_selfdestruct_msg_in( + bot, + chat_id, + bot_msg, + CONST["T_FAST_DEL_MSG"], + message_thread_id=update.effective_message.message_thread_id, + ) else: tlg_send_msg(bot, chat_id, bot_msg) del new_users[chat_id][user_id] @@ -1274,69 +1506,153 @@ def receive_poll_answer(update: Update, context: CallbackContext): if welcome_msg != "-": if rm_welcome_msg: welcome_msg_time = get_chat_config(chat_id, "Welcome_Time") - sent_result = tlg_send_selfdestruct_msg_in(bot, chat_id, welcome_msg, - welcome_msg_time, parse_mode="MARKDOWN") + sent_result = await tlg_send_selfdestruct_msg_in( + bot, + chat_id, + welcome_msg, + welcome_msg_time, + parse_mode="MARKDOWN", + message_thread_id=update.effective_message.message_thread_id, + ) else: - sent_result = tlg_send_msg(bot, chat_id, welcome_msg, "MARKDOWN") + sent_result = await tlg_send_msg( + bot, + chat_id, + welcome_msg, + "MARKDOWN", + message_thread_id=update.effective_message.message_thread_id, + ) if sent_result is None: printts("[{}] Error: Can't send the welcome message.".format(chat_id)) # Check for send just text message option and apply user restrictions - if restrict_non_text_msgs == 1: # Restrict for 1 day + if restrict_non_text_msgs == 1: # Restrict for 1 day tomorrow_epoch = get_unix_epoch() + CONST["T_RESTRICT_NO_TEXT_MSG"] - tlg_restrict_user(bot, chat_id, user_id, send_msg=True, send_media=False, - send_stickers_gifs=False, insert_links=False, send_polls=False, - invite_members=False, pin_messages=False, change_group_info=False, - until_date=tomorrow_epoch) - elif restrict_non_text_msgs == 2: # Restrict forever - tlg_restrict_user(bot, chat_id, user_id, send_msg=True, send_media=False, - send_stickers_gifs=False, insert_links=False, send_polls=False, - invite_members=False, pin_messages=False, change_group_info=False) + await tlg_restrict_user( + bot, + chat_id, + user_id, + send_msg=True, + send_media=False, + send_stickers_gifs=False, + insert_links=False, + send_polls=False, + invite_members=False, + pin_messages=False, + change_group_info=False, + until_date=tomorrow_epoch, + ) + elif restrict_non_text_msgs == 2: # Restrict forever + await tlg_restrict_user( + bot, + chat_id, + user_id, + send_msg=True, + send_media=False, + send_stickers_gifs=False, + insert_links=False, + send_polls=False, + invite_members=False, + pin_messages=False, + change_group_info=False, + ) else: # Notify captcha fail printts("[{}] User {} fail a poll challenge.".format(chat_id, user_name)) bot_msg = TEXT[lang]["CAPTCHA_POLL_FAIL_0"].format(user_name) sent_msg_id = None if rm_result_msg: - sent_result = tlg_send_msg(bot, chat_id, bot_msg) + sent_result = await tlg_send_msg( + bot, + chat_id, + bot_msg, + message_thread_id=update.effective_message.message_thread_id, + ) if sent_result["msg"] is not None: sent_msg_id = sent_result["msg"].message_id else: - tlg_send_msg(bot, chat_id, bot_msg) + await tlg_send_msg( + bot, + chat_id, + bot_msg, + message_thread_id=update.effective_message.message_thread_id, + ) # Wait 10s sleep(10) # Try to kick the user - kick_result = tlg_kick_user(bot, chat_id, user_id) + kick_result = await tlg_kick_user(bot, chat_id, user_id) if kick_result["error"] == "": # Kick success msg_text = TEXT[lang]["CAPTCHA_POLL_FAIL_1"].format(user_name) # Send kicked message if rm_result_msg: - tlg_send_selfdestruct_msg_in(bot, chat_id, msg_text, CONST["T_FAST_DEL_MSG"]) + await tlg_send_selfdestruct_msg_in( + bot, + chat_id, + msg_text, + CONST["T_FAST_DEL_MSG"], + message_thread_id=update.effective_message.message_thread_id, + ) else: - tlg_send_msg(bot, chat_id, msg_text) + await tlg_send_msg( + bot, + chat_id, + msg_text, + message_thread_id=update.effective_message.message_thread_id, + ) else: # Kick fail printts("[{}] Unable to kick".format(chat_id)) - if (kick_result["error"] == "The user has left the group") or \ - (kick_result["error"] == "The user was already kicked"): + if (kick_result["error"] == "The user has left the group") or ( + kick_result["error"] == "The user was already kicked" + ): # The user is not in the chat msg_text = TEXT[lang]["NEW_USER_KICK_NOT_IN_CHAT"].format(user_name) if rm_result_msg: - tlg_send_selfdestruct_msg_in(bot, chat_id, msg_text, CONST["T_FAST_DEL_MSG"]) + await tlg_send_selfdestruct_msg_in( + bot, + chat_id, + msg_text, + CONST["T_FAST_DEL_MSG"], + message_thread_id=update.effective_message.message_thread_id, + ) else: - tlg_send_msg(bot, chat_id, msg_text) - elif kick_result["error"] == "Not enough rights to restrict/unrestrict chat member": + await tlg_send_msg( + bot, + chat_id, + msg_text, + message_thread_id=update.effective_message.message_thread_id, + ) + elif ( + kick_result["error"] + == "Not enough rights to restrict/unrestrict chat member" + ): # Bot has no privileges to kick msg_text = TEXT[lang]["NEW_USER_KICK_NOT_RIGHTS"].format(user_name) # Send no rights for kick message without auto-remove - tlg_send_msg(bot, chat_id, msg_text) + await tlg_send_msg( + bot, + chat_id, + msg_text, + message_thread_id=update.effective_message.message_thread_id, + ) else: # For other reason, the Bot can't ban msg_text = TEXT[lang]["BOT_CANT_KICK"].format(user_name) if rm_result_msg: - tlg_send_selfdestruct_msg_in(bot, chat_id, msg_text, CONST["T_FAST_DEL_MSG"]) + await tlg_send_selfdestruct_msg_in( + bot, + chat_id, + msg_text, + CONST["T_FAST_DEL_MSG"], + message_thread_id=update.effective_message.message_thread_id, + ) else: - tlg_send_msg(bot, chat_id, msg_text) + await tlg_send_msg( + bot, + chat_id, + msg_text, + message_thread_id=update.effective_message.message_thread_id, + ) # Remove user from captcha process del new_users[chat_id][user_id] # Remove fail message @@ -1346,12 +1662,12 @@ def receive_poll_answer(update: Update, context: CallbackContext): printts(" ") -def key_inline_keyboard(update: Update, context: CallbackContext): - '''Inline Keyboard button pressed handler''' +async def key_inline_keyboard(update: Update, context: CallbackContext): + """Inline Keyboard button pressed handler""" bot = context.bot query = update.callback_query # Confirm query received - query_ans_result = tlg_answer_callback_query(bot, query) + query_ans_result = await tlg_answer_callback_query(bot, query) if query_ans_result["error"] != "": return # Convert query provided data into list @@ -1368,8 +1684,8 @@ def key_inline_keyboard(update: Update, context: CallbackContext): button_request_pass(bot, query) -def button_request_captcha(bot, query): - '''Button "Another captcha" pressed handler''' +async def button_request_captcha(bot, query): + """Button "Another captcha" pressed handler""" global new_users # Get query data chat_id = query.message.chat_id @@ -1393,8 +1709,14 @@ def button_request_captcha(bot, query): lang = get_chat_config(chat_id, "Language") printts("[{}] User {} requested a new captcha.".format(chat_id, user_name)) # Prepare inline keyboard button to let user request another catcha - keyboard = [[InlineKeyboardButton(TEXT[lang]["OTHER_CAPTCHA_BTN_TEXT"], - callback_data="image_captcha {}".format(str(query.from_user.id)))]] + keyboard = [ + [ + InlineKeyboardButton( + TEXT[lang]["OTHER_CAPTCHA_BTN_TEXT"], + callback_data="image_captcha {}".format(str(query.from_user.id)), + ) + ] + ] reply_markup = InlineKeyboardMarkup(keyboard) # Get captcha timeout captcha_timeout = get_chat_config(chat_id, "Captcha_Time") @@ -1407,27 +1729,32 @@ def button_request_captcha(bot, query): captcha_level = get_chat_config(chat_id, "Captcha_Difficulty_Level") captcha_mode = new_users[chat_id][user_id]["join_data"]["captcha_mode"] # Use nums mode if captcha_mode was changed while captcha was in progress - if captcha_mode not in { "nums", "hex", "ascii", "math" }: + if captcha_mode not in {"nums", "hex", "ascii", "math"}: captcha_mode = "nums" # Generate a new captcha and edit previous captcha image message - captcha = create_image_captcha(chat_id, user_id, captcha_level, \ - captcha_mode) + captcha = create_image_captcha(chat_id, user_id, captcha_level, captcha_mode) if captcha_mode == "math": captcha_num = captcha["equation_result"] - printts("[{}] Sending new captcha msg: {} = {}...".format(chat_id, \ - captcha["equation_str"], captcha_num)) - img_caption = TEXT[lang]["NEW_USER_MATH_CAPTION"].format(user_name, \ - chat_title, timeout_str) + printts( + "[{}] Sending new captcha msg: {} = {}...".format( + chat_id, captcha["equation_str"], captcha_num + ) + ) + img_caption = TEXT[lang]["NEW_USER_MATH_CAPTION"].format( + user_name, chat_title, timeout_str + ) else: captcha_num = captcha["characters"] - printts("[{}] Sending new captcha msg: {}...".format( \ - chat_id, captcha_num)) - img_caption = TEXT[lang]["NEW_USER_IMG_CAPTION"].format(user_name, \ - chat_title, timeout_str) - input_media = InputMediaPhoto(media=open(captcha["image"], "rb"), \ - caption=img_caption) - edit_result = tlg_edit_msg_media(bot, chat_id, msg_id, media=input_media, \ - reply_markup=reply_markup, timeout=20) + printts("[{}] Sending new captcha msg: {}...".format(chat_id, captcha_num)) + img_caption = TEXT[lang]["NEW_USER_IMG_CAPTION"].format( + user_name, chat_title, timeout_str + ) + input_media = InputMediaPhoto( + media=open(captcha["image"], "rb"), caption=img_caption + ) + edit_result = await tlg_edit_msg_media( + bot, chat_id, msg_id, media=input_media, reply_markup=reply_markup, timeout=20 + ) if edit_result["error"] == "": # Set and modified to new expected captcha number new_users[chat_id][user_id]["join_data"]["captcha_num"] = captcha_num @@ -1438,8 +1765,8 @@ def button_request_captcha(bot, query): printts(" ") -def button_request_pass(bot, query): - '''Button "I'm not a bot" pressed handler''' +async def button_request_pass(bot, query): + """Button "I'm not a bot" pressed handler""" global new_users # Get query data chat_id = query.message.chat_id @@ -1467,21 +1794,46 @@ def button_request_pass(bot, query): printts("[{}] User {} solved a button-only challenge.".format(chat_id, user_name)) bot_msg = TEXT[lang]["CAPTCHA_SOLVED"].format(user_name) if rm_result_msg: - tlg_send_selfdestruct_msg_in(bot, chat_id, bot_msg, CONST["T_FAST_DEL_MSG"]) + await tlg_send_selfdestruct_msg_in( + bot, + chat_id, + bot_msg, + CONST["T_FAST_DEL_MSG"], + message_thread_id=query.effective_message.message_thread_id, + ) else: - tlg_send_msg(bot, chat_id, bot_msg) + await tlg_send_msg( + bot, + chat_id, + bot_msg, + message_thread_id=query.effective_message.message_thread_id, + ) del new_users[chat_id][user_id] # Check for custom welcome message and send it - welcome_msg = get_chat_config(chat_id, "Welcome_Msg").format(escape_markdown(user_name)) + welcome_msg = get_chat_config(chat_id, "Welcome_Msg").format( + escape_markdown(user_name) + ) if welcome_msg != "-": # Send the message as Markdown rm_welcome_msg = get_chat_config(chat_id, "Rm_Welcome_Msg") if rm_welcome_msg: welcome_msg_time = get_chat_config(chat_id, "Welcome_Time") - sent_result = tlg_send_selfdestruct_msg_in(bot, chat_id, welcome_msg, - welcome_msg_time, parse_mode="MARKDOWN") + sent_result = await tlg_send_selfdestruct_msg_in( + bot, + chat_id, + welcome_msg, + welcome_msg_time, + parse_mode="MARKDOWN", + message_thread_id=query.effective_message.message_thread_id, + ) else: - sent_result = tlg_send_msg(bot, chat_id, welcome_msg, "MARKDOWN") + sent_result = await tlg_send_msg( + bot, + chat_id, + welcome_msg, + "MARKDOWN", + message_thread_id=query.effective_message.message_thread_id, + ) if sent_result is None: printts("[{}] Error: Can't send the welcome message.".format(chat_id)) # Check for send just text message option and apply user restrictions @@ -1489,23 +1841,47 @@ def button_request_pass(bot, query): # Restrict for 1 day if restrict_non_text_msgs == 1: tomorrow_epoch = get_unix_epoch() + CONST["T_RESTRICT_NO_TEXT_MSG"] - tlg_restrict_user(bot, chat_id, user_id, send_msg=True, send_media=False, - send_stickers_gifs=False, insert_links=False, send_polls=False, - invite_members=False, pin_messages=False, change_group_info=False, - until_date=tomorrow_epoch) + await tlg_restrict_user( + bot, + chat_id, + user_id, + send_msg=True, + send_media=False, + send_stickers_gifs=False, + insert_links=False, + send_polls=False, + invite_members=False, + pin_messages=False, + change_group_info=False, + until_date=tomorrow_epoch, + ) # Restrict forever elif restrict_non_text_msgs == 2: - tlg_restrict_user(bot, chat_id, user_id, send_msg=True, send_media=False, - send_stickers_gifs=False, insert_links=False, send_polls=False, - invite_members=False, pin_messages=False, change_group_info=False) + await tlg_restrict_user( + bot, + chat_id, + user_id, + send_msg=True, + send_media=False, + send_stickers_gifs=False, + insert_links=False, + send_polls=False, + invite_members=False, + pin_messages=False, + change_group_info=False, + ) printts("[{}] Button-only challenge pass request process complete.".format(chat_id)) printts(" ") + ############################################################################### ### Received Telegram command messages handlers -def cmd_start(update: Update, context: CallbackContext): - '''Command /start message handler''' + +async def cmd_start(update: Update, context: CallbackContext): + printts(update.effective_message.message_thread_id) + + """Command /start message handler""" bot = context.bot # Ignore command if it was a edited message update_msg = getattr(update, "message", None) @@ -1515,21 +1891,26 @@ def cmd_start(update: Update, context: CallbackContext): chat_type = update_msg.chat.type lang = get_update_user_lang(update_msg.from_user) if chat_type == "private": - tlg_send_msg(bot, chat_id, TEXT[lang]["START"]) + await tlg_send_msg(bot, chat_id, TEXT[lang]["START"]) else: # Ignore if not requested by a group Admin user_id = update_msg.from_user.id - is_admin = tlg_user_is_admin(bot, user_id, chat_id) + is_admin = await tlg_user_is_admin(bot, user_id, chat_id) if (is_admin is None) or (is_admin == False): return # Send the response message lang = get_chat_config(chat_id, "Language") tlg_msg_to_selfdestruct(update_msg) - tlg_send_selfdestruct_msg(bot, chat_id, TEXT[lang]["START"]) + await tlg_send_selfdestruct_msg( + bot, + chat_id, + TEXT[lang]["START"], + message_thread_id=update.effective_message.message_thread_id, + ) -def cmd_help(update: Update, context: CallbackContext): - '''Command /help message handler''' +async def cmd_help(update: Update, context: CallbackContext): + """Command /help message handler""" bot = context.bot # Ignore command if it was a edited message update_msg = getattr(update, "message", None) @@ -1539,11 +1920,16 @@ def cmd_help(update: Update, context: CallbackContext): chat_type = update_msg.chat.type lang = get_update_user_lang(update_msg.from_user) if chat_type == "private": - tlg_send_msg(bot, chat_id, TEXT[lang]["HELP"]) + await tlg_send_msg( + bot, + chat_id, + TEXT[lang]["HELP"], + message_thread_id=update.effective_message.message_thread_id, + ) else: # Ignore if not requested by a group Admin user_id = update_msg.from_user.id - is_admin = tlg_user_is_admin(bot, user_id, chat_id) + is_admin = await tlg_user_is_admin(bot, user_id, chat_id) if (is_admin is None) or (is_admin == False): return # Send the response message @@ -1552,8 +1938,8 @@ def cmd_help(update: Update, context: CallbackContext): tlg_send_selfdestruct_msg(bot, chat_id, TEXT[lang]["HELP"]) -def cmd_commands(update: Update, context: CallbackContext): - '''Command /commands message handler''' +async def cmd_commands(update: Update, context: CallbackContext): + """Command /commands message handler""" bot = context.bot # Ignore command if it was a edited message update_msg = getattr(update, "message", None) @@ -1563,21 +1949,26 @@ def cmd_commands(update: Update, context: CallbackContext): chat_type = update_msg.chat.type lang = get_update_user_lang(update_msg.from_user) if chat_type == "private": - tlg_send_msg(bot, chat_id, TEXT[lang]["COMMANDS"]) + await tlg_send_msg(bot, chat_id, TEXT[lang]["COMMANDS"]) else: # Ignore if not requested by a group Admin user_id = update_msg.from_user.id - is_admin = tlg_user_is_admin(bot, user_id, chat_id) + is_admin = await tlg_user_is_admin(bot, user_id, chat_id) if (is_admin is None) or (is_admin == False): return # Send the response message lang = get_chat_config(chat_id, "Language") tlg_msg_to_selfdestruct(update_msg) - tlg_send_selfdestruct_msg(bot, chat_id, TEXT[lang]["COMMANDS"]) + await tlg_send_selfdestruct_msg( + bot, + chat_id, + TEXT[lang]["COMMANDS"], + message_thread_id=update.effective_message.message_thread_id, + ) -def cmd_connect(update: Update, context: CallbackContext): - '''Command /connect message handler''' +async def cmd_connect(update: Update, context: CallbackContext): + """Command /connect message handler""" global connections bot = context.bot args = context.args @@ -1595,44 +1986,68 @@ def cmd_connect(update: Update, context: CallbackContext): # Ignore if command is not in private chat if chat_type != "private": # Ignore if not requested by a group Admin - is_admin = tlg_user_is_admin(bot, user_id, chat_id) + is_admin = await tlg_user_is_admin(bot, user_id, chat_id) if (is_admin is None) or (is_admin == False): return # Send just allowed in private chat message lang = get_chat_config(chat_id, "Language") - tlg_send_msg_type_chat(bot, chat_type, chat_id, - TEXT[lang]["CMD_JUST_IN_PRIVATE"]) + await tlg_send_msg_type_chat( + bot, + chat_type, + chat_id, + TEXT[lang]["CMD_JUST_IN_PRIVATE"], + message_thread_id=update.effective_message.message_thread_id, + ) return # Check for group chat ID if len(args) == 0: - tlg_send_msg_type_chat(bot, chat_type, chat_id, - TEXT[lang]["CONNECT_USAGE"]) + await tlg_send_msg_type_chat( + bot, + chat_type, + chat_id, + TEXT[lang]["CONNECT_USAGE"], + message_thread_id=update.effective_message.message_thread_id, + ) return group_id = args[0] # Add "-" if not present if group_id[0] != "-": group_id = "-{}".format(group_id) if not tlg_is_valid_group(group_id): - tlg_send_msg_type_chat(bot, chat_type, chat_id, - TEXT[lang]["INVALID_GROUP_ID"]) + await tlg_send_msg_type_chat( + bot, + chat_type, + chat_id, + TEXT[lang]["INVALID_GROUP_ID"], + message_thread_id=update.effective_message.message_thread_id, + ) return # Check if requested by the Bot owner or an Admin of the group - if (str(user_id) != CONST["BOT_OWNER"]) and \ - (user_alias != CONST["BOT_OWNER"]): - is_admin = tlg_user_is_admin(bot, user_id, group_id) + if (str(user_id) != CONST["BOT_OWNER"]) and (user_alias != CONST["BOT_OWNER"]): + is_admin = await tlg_user_is_admin(bot, user_id, group_id) if (is_admin is None) or (is_admin == False): - tlg_send_msg_type_chat(bot, chat_type, chat_id, - TEXT[lang]["CONNECT_JUST_ADMIN"]) + await tlg_send_msg_type_chat( + bot, + chat_type, + chat_id, + TEXT[lang]["CONNECT_JUST_ADMIN"], + message_thread_id=update.effective_message.message_thread_id, + ) return # Connection group_lang = get_chat_config(group_id, "Language") - connections[user_id] = { "group_id": group_id, "lang": group_lang } - tlg_send_msg_type_chat(bot, chat_type, chat_id, - TEXT[lang]["CONNECT_OK"].format(group_id)) - - -def cmd_disconnect(update: Update, context: CallbackContext): - '''Command /disconnect message handler''' + connections[user_id] = {"group_id": group_id, "lang": group_lang} + await tlg_send_msg_type_chat( + bot, + chat_type, + chat_id, + TEXT[lang]["CONNECT_OK"].format(group_id), + message_thread_id=update.effective_message.message_thread_id, + ) + + +async def cmd_disconnect(update: Update, context: CallbackContext): + """Command /disconnect message handler""" global connections bot = context.bot # Ignore command if it was a edited message @@ -1646,29 +2061,44 @@ def cmd_disconnect(update: Update, context: CallbackContext): # Ignore if command is not in private chat if chat_type != "private": # Ignore if not requested by a group Admin - is_admin = tlg_user_is_admin(bot, user_id, chat_id) + is_admin = await tlg_user_is_admin(bot, user_id, chat_id) if (is_admin is None) or (is_admin == False): return # Send just allowed in private chat message lang = get_chat_config(chat_id, "Language") - tlg_send_msg_type_chat(bot, chat_type, chat_id, - TEXT[lang]["CMD_JUST_IN_PRIVATE"]) + await tlg_send_msg_type_chat( + bot, + chat_type, + chat_id, + TEXT[lang]["CMD_JUST_IN_PRIVATE"], + message_thread_id=update.effective_message.message_thread_id, + ) return # Check if User is connected to some group if user_id not in connections: - tlg_send_msg_type_chat(bot, chat_type, chat_id, - TEXT[lang]["DISCONNECT_NOT_CONNECTED"]) + await tlg_send_msg_type_chat( + bot, + chat_type, + chat_id, + TEXT[lang]["DISCONNECT_NOT_CONNECTED"], + message_thread_id=update.effective_message.message_thread_id, + ) return # Disconnection lang = connections[user_id]["lang"] group_id = connections[user_id]["group_id"] del connections[user_id] - tlg_send_msg_type_chat(bot, chat_type, chat_id, - TEXT[lang]["DISCONNECT_OK"].format(group_id)) + await tlg_send_msg_type_chat( + bot, + chat_type, + chat_id, + TEXT[lang]["DISCONNECT_OK"].format(group_id), + message_thread_id=update.effective_message.message_thread_id, + ) -def cmd_checkcfg(update: Update, context: CallbackContext): - '''Command /checkcfg message handler''' +async def cmd_checkcfg(update: Update, context: CallbackContext): + """Command /checkcfg message handler""" bot = context.bot # Ignore command if it was a edited message update_msg = getattr(update, "message", None) @@ -1680,13 +2110,18 @@ def cmd_checkcfg(update: Update, context: CallbackContext): lang = get_update_user_lang(update_msg.from_user) if chat_type == "private": if user_id not in connections: - tlg_send_msg_type_chat(bot, chat_type, chat_id, - TEXT[lang]["CMD_NEEDS_CONNECTION"]) + await tlg_send_msg_type_chat( + bot, + chat_type, + chat_id, + TEXT[lang]["CMD_NEEDS_CONNECTION"], + message_thread_id=update.effective_message.message_thread_id, + ) return group_id = connections[user_id]["group_id"] else: # Ignore if not requested by a group Admin - is_admin = tlg_user_is_admin(bot, user_id, chat_id) + is_admin = await tlg_user_is_admin(bot, user_id, chat_id) if (is_admin is None) or (is_admin == False): return # Get Group Chat ID and configured language @@ -1696,13 +2131,18 @@ def cmd_checkcfg(update: Update, context: CallbackContext): # Get all group configs group_cfg = get_all_chat_config(group_id) group_cfg = json_dumps(group_cfg, indent=4, sort_keys=True) - tlg_send_msg_type_chat(bot, chat_type, chat_id, - TEXT[lang]["CHECK_CFG"].format(escape_markdown(group_cfg)), - parse_mode="MARKDOWN") - - -def cmd_language(update: Update, context: CallbackContext): - '''Command /language message handler''' + await tlg_send_msg_type_chat( + bot, + chat_type, + chat_id, + TEXT[lang]["CHECK_CFG"].format(escape_markdown(group_cfg)), + parse_mode="MARKDOWN", + message_thread_id=update.effective_message.message_thread_id, + ) + + +async def cmd_language(update: Update, context: CallbackContext): + """Command /language message handler""" bot = context.bot args = context.args # Ignore command if it was a edited message @@ -1716,13 +2156,17 @@ def cmd_language(update: Update, context: CallbackContext): # Check usage in private chat if chat_type == "private": if user_id not in connections: - tlg_send_msg_type_chat(bot, chat_type, chat_id, - TEXT[lang]["CMD_NEEDS_CONNECTION"]) + await tlg_send_msg_type_chat( + bot, + chat_type, + chat_id, + TEXT[lang]["CMD_NEEDS_CONNECTION"], + ) return group_id = connections[user_id]["group_id"] else: # Ignore if not requested by a group Admin - is_admin = tlg_user_is_admin(bot, user_id, chat_id) + is_admin = await tlg_user_is_admin(bot, user_id, chat_id) if (is_admin is None) or (is_admin == False): return # Get Group Chat ID and configured language @@ -1732,9 +2176,14 @@ def cmd_language(update: Update, context: CallbackContext): tlg_msg_to_selfdestruct(update_msg) # Check if no argument was provided with the command if len(args) == 0: - msg_text = TEXT[lang]["LANG_NOT_ARG"].format( - CONST["SUPPORTED_LANGS_CMDS"]) - tlg_send_msg_type_chat(bot, chat_type, chat_id, msg_text) + msg_text = TEXT[lang]["LANG_NOT_ARG"].format(CONST["SUPPORTED_LANGS_CMDS"]) + await tlg_send_msg_type_chat( + bot, + chat_type, + chat_id, + msg_text, + message_thread_id=update.effective_message.message_thread_id, + ) return # Get and configure chat to provided language lang_provided = args[0].upper() @@ -1746,16 +2195,20 @@ def cmd_language(update: Update, context: CallbackContext): connections[user_id]["lang"] = lang msg_text = TEXT[lang]["LANG_CHANGE"] else: - msg_text = TEXT[lang]["LANG_SAME"].format( - CONST["SUPPORTED_LANGS_CMDS"]) + msg_text = TEXT[lang]["LANG_SAME"].format(CONST["SUPPORTED_LANGS_CMDS"]) else: - msg_text = TEXT[lang]["LANG_BAD_LANG"].format( - CONST["SUPPORTED_LANGS_CMDS"]) - tlg_send_msg_type_chat(bot, chat_type, chat_id, msg_text) - - -def cmd_time(update: Update, context: CallbackContext): - '''Command /time message handler''' + msg_text = TEXT[lang]["LANG_BAD_LANG"].format(CONST["SUPPORTED_LANGS_CMDS"]) + await tlg_send_msg_type_chat( + bot, + chat_type, + chat_id, + msg_text, + message_thread_id=update.effective_message.message_thread_id, + ) + + +async def cmd_time(update: Update, context: CallbackContext): + """Command /time message handler""" bot = context.bot args = context.args # Ignore command if it was a edited message @@ -1769,13 +2222,14 @@ def cmd_time(update: Update, context: CallbackContext): # Check and deny usage in private chat if chat_type == "private": if user_id not in connections: - tlg_send_msg_type_chat(bot, chat_type, chat_id, - TEXT[lang]["CMD_NEEDS_CONNECTION"]) + await tlg_send_msg_type_chat( + bot, chat_type, chat_id, TEXT[lang]["CMD_NEEDS_CONNECTION"] + ) return group_id = connections[user_id]["group_id"] else: # Ignore if not requested by a group Admin - is_admin = tlg_user_is_admin(bot, user_id, chat_id) + is_admin = await tlg_user_is_admin(bot, user_id, chat_id) if (is_admin is None) or (is_admin == False): return # Get Group Chat ID and configured language @@ -1785,13 +2239,23 @@ def cmd_time(update: Update, context: CallbackContext): tlg_msg_to_selfdestruct(update_msg) # Check if no argument was provided with the command if len(args) == 0: - tlg_send_msg_type_chat(bot, chat_type, chat_id, - TEXT[lang]["TIME_NOT_ARG"]) + await tlg_send_msg_type_chat( + bot, + chat_type, + chat_id, + TEXT[lang]["TIME_NOT_ARG"], + message_thread_id=update.effective_message.message_thread_id, + ) return # Check if provided time argument is not a number if not is_int(args[0]): - tlg_send_msg_type_chat(bot, chat_type, chat_id, - TEXT[lang]["TIME_NOT_NUM"]) + await tlg_send_msg_type_chat( + bot, + chat_type, + chat_id, + TEXT[lang]["TIME_NOT_NUM"], + message_thread_id=update.effective_message.message_thread_id, + ) return # Get time arguments new_time = int(args[0]) @@ -1808,28 +2272,49 @@ def cmd_time(update: Update, context: CallbackContext): min_sec = "sec" new_time_str = "{} sec".format(new_time) else: - tlg_send_msg_type_chat(bot, chat_type, chat_id, - TEXT[lang]["TIME_NOT_ARG"]) + await tlg_send_msg_type_chat( + bot, + chat_type, + chat_id, + TEXT[lang]["TIME_NOT_ARG"], + message_thread_id=update.effective_message.message_thread_id, + ) return # Check if time value is out of limits - if new_time < 10: # Lees than 10s - msg_text = TEXT[lang]["TIME_OUT_RANGE"].format( \ - CONST["MAX_CONFIG_CAPTCHA_TIME"]) - tlg_send_msg_type_chat(bot, chat_type, chat_id, msg_text) + if new_time < 10: # Lees than 10s + msg_text = TEXT[lang]["TIME_OUT_RANGE"].format(CONST["MAX_CONFIG_CAPTCHA_TIME"]) + await tlg_send_msg_type_chat( + bot, + chat_type, + chat_id, + msg_text, + message_thread_id=update.effective_message.message_thread_id, + ) return if new_time > CONST["MAX_CONFIG_CAPTCHA_TIME"] * CONST["T_SECONDS_IN_MIN"]: - msg_text = TEXT[lang]["TIME_OUT_RANGE"].format( \ - CONST["MAX_CONFIG_CAPTCHA_TIME"]) - tlg_send_msg_type_chat(bot, chat_type, chat_id, msg_text) + msg_text = TEXT[lang]["TIME_OUT_RANGE"].format(CONST["MAX_CONFIG_CAPTCHA_TIME"]) + await tlg_send_msg_type_chat( + bot, + chat_type, + chat_id, + msg_text, + message_thread_id=update.effective_message.message_thread_id, + ) return # Set the new captcha time save_config_property(group_id, "Captcha_Time", new_time) msg_text = TEXT[lang]["TIME_CHANGE"].format(new_time_str) - tlg_send_msg_type_chat(bot, chat_type, chat_id, msg_text) + await tlg_send_msg_type_chat( + bot, + chat_type, + chat_id, + msg_text, + message_thread_id=update.effective_message.message_thread_id, + ) -def cmd_difficulty(update: Update, context: CallbackContext): - '''Command /difficulty message handler''' +async def cmd_difficulty(update: Update, context: CallbackContext): + """Command /difficulty message handler""" bot = context.bot args = context.args # Ignore command if it was a edited message @@ -1843,13 +2328,14 @@ def cmd_difficulty(update: Update, context: CallbackContext): # Check and deny usage in private chat if chat_type == "private": if user_id not in connections: - tlg_send_msg_type_chat(bot, chat_type, chat_id, - TEXT[lang]["CMD_NEEDS_CONNECTION"]) + await tlg_send_msg_type_chat( + bot, chat_type, chat_id, TEXT[lang]["CMD_NEEDS_CONNECTION"] + ) return group_id = connections[user_id]["group_id"] else: # Ignore if not requested by a group Admin - is_admin = tlg_user_is_admin(bot, user_id, chat_id) + is_admin = await tlg_user_is_admin(bot, user_id, chat_id) if (is_admin is None) or (is_admin == False): return # Get Group Chat ID and configured language @@ -1859,8 +2345,13 @@ def cmd_difficulty(update: Update, context: CallbackContext): tlg_msg_to_selfdestruct(update_msg) # Check if no argument was provided with the command if len(args) == 0: - tlg_send_msg_type_chat(bot, chat_type, chat_id, - TEXT[lang]["DIFFICULTY_NOT_ARG"]) + await tlg_send_msg_type_chat( + bot, + chat_type, + chat_id, + TEXT[lang]["DIFFICULTY_NOT_ARG"], + message_thread_id=update.effective_message.message_thread_id, + ) return # Get and configure chat to provided captcha difficulty if is_int(args[0]): @@ -1869,16 +2360,21 @@ def cmd_difficulty(update: Update, context: CallbackContext): new_difficulty = 1 if new_difficulty > 5: new_difficulty = 5 - save_config_property(group_id, "Captcha_Difficulty_Level", - new_difficulty) + save_config_property(group_id, "Captcha_Difficulty_Level", new_difficulty) bot_msg = TEXT[lang]["DIFFICULTY_CHANGE"].format(new_difficulty) else: bot_msg = TEXT[lang]["DIFFICULTY_NOT_NUM"] - tlg_send_msg_type_chat(bot, chat_type, chat_id, bot_msg) + await tlg_send_msg_type_chat( + bot, + chat_type, + chat_id, + bot_msg, + message_thread_id=update.effective_message.message_thread_id, + ) -def cmd_captcha_mode(update: Update, context: CallbackContext): - '''Command /captcha_mode message handler''' +async def cmd_captcha_mode(update: Update, context: CallbackContext): + """Command /captcha_mode message handler""" bot = context.bot args = context.args # Ignore command if it was a edited message @@ -1892,13 +2388,14 @@ def cmd_captcha_mode(update: Update, context: CallbackContext): # Check and deny usage in private chat if chat_type == "private": if user_id not in connections: - tlg_send_msg_type_chat(bot, chat_type, chat_id, - TEXT[lang]["CMD_NEEDS_CONNECTION"]) + await tlg_send_msg_type_chat( + bot, chat_type, chat_id, TEXT[lang]["CMD_NEEDS_CONNECTION"] + ) return group_id = connections[user_id]["group_id"] else: # Ignore if not requested by a group Admin - is_admin = tlg_user_is_admin(bot, user_id, chat_id) + is_admin = await tlg_user_is_admin(bot, user_id, chat_id) if (is_admin is None) or (is_admin == False): return # Get Group Chat ID and configured language @@ -1908,22 +2405,32 @@ def cmd_captcha_mode(update: Update, context: CallbackContext): tlg_msg_to_selfdestruct(update_msg) # Check if no argument was provided with the command if len(args) == 0: - tlg_send_msg_type_chat(bot, chat_type, chat_id, - TEXT[lang]["CAPTCHA_MODE_NOT_ARG"]) + await tlg_send_msg_type_chat( + bot, + chat_type, + chat_id, + TEXT[lang]["CAPTCHA_MODE_NOT_ARG"], + message_thread_id=update.effective_message.message_thread_id, + ) return # Get and configure chat to provided captcha mode new_captcha_mode = args[0].lower() - if new_captcha_mode in { - "poll", "button", "nums", "hex", "ascii", "math", "random" }: + if new_captcha_mode in {"poll", "button", "nums", "hex", "ascii", "math", "random"}: save_config_property(group_id, "Captcha_Chars_Mode", new_captcha_mode) bot_msg = TEXT[lang]["CAPTCHA_MODE_CHANGE"].format(new_captcha_mode) else: bot_msg = TEXT[lang]["CAPTCHA_MODE_INVALID"] - tlg_send_msg_type_chat(bot, chat_type, chat_id, bot_msg) + await tlg_send_msg_type_chat( + bot, + chat_type, + chat_id, + bot_msg, + message_thread_id=update.effective_message.message_thread_id, + ) -def cmd_welcome_msg(update: Update, context: CallbackContext): - '''Command /welcome_msg message handler''' +async def cmd_welcome_msg(update: Update, context: CallbackContext): + """Command /welcome_msg message handler""" bot = context.bot args = context.args # Ignore command if it was a edited message @@ -1937,13 +2444,14 @@ def cmd_welcome_msg(update: Update, context: CallbackContext): # Check and deny usage in private chat if chat_type == "private": if user_id not in connections: - tlg_send_msg_type_chat(bot, chat_type, chat_id, - TEXT[lang]["CMD_NEEDS_CONNECTION"]) + await tlg_send_msg_type_chat( + bot, chat_type, chat_id, TEXT[lang]["CMD_NEEDS_CONNECTION"] + ) return group_id = connections[user_id]["group_id"] else: # Ignore if not requested by a group Admin - is_admin = tlg_user_is_admin(bot, user_id, chat_id) + is_admin = await tlg_user_is_admin(bot, user_id, chat_id) if (is_admin is None) or (is_admin == False): return # Get Group Chat ID and configured language @@ -1953,26 +2461,37 @@ def cmd_welcome_msg(update: Update, context: CallbackContext): tlg_msg_to_selfdestruct(update_msg) # Check if no argument was provided with the command if len(args) == 0: - tlg_send_msg_type_chat(bot, chat_type, chat_id, - TEXT[lang]["WELCOME_MSG_SET_NOT_ARG"]) + await tlg_send_msg_type_chat( + bot, + chat_type, + chat_id, + TEXT[lang]["WELCOME_MSG_SET_NOT_ARG"], + message_thread_id=update.effective_message.message_thread_id, + ) return # Get welcome message in markdown and remove "/Welcome_msg " text from it welcome_msg = update.message.text_markdown_v2[14:] welcome_msg = welcome_msg.replace("{", "{{") welcome_msg = welcome_msg.replace("}", "}}") welcome_msg = welcome_msg.replace("$user", "{0}") - welcome_msg = welcome_msg[:CONST["MAX_WELCOME_MSG_LENGTH"]] + welcome_msg = welcome_msg[: CONST["MAX_WELCOME_MSG_LENGTH"]] if welcome_msg == "disable": - welcome_msg = '-' + welcome_msg = "-" bot_msg = TEXT[lang]["WELCOME_MSG_UNSET"] else: bot_msg = TEXT[lang]["WELCOME_MSG_SET"] save_config_property(group_id, "Welcome_Msg", welcome_msg) - tlg_send_msg_type_chat(bot, chat_type, chat_id, bot_msg) + await tlg_send_msg_type_chat( + bot, + chat_type, + chat_id, + bot_msg, + message_thread_id=update.effective_message.message_thread_id, + ) -def cmd_welcome_msg_time(update: Update, context: CallbackContext): - '''Command /welcome_msg_time message handler''' +async def cmd_welcome_msg_time(update: Update, context: CallbackContext): + """Command /welcome_msg_time message handler""" bot = context.bot args = context.args # Ignore command if it was a edited message @@ -1986,13 +2505,14 @@ def cmd_welcome_msg_time(update: Update, context: CallbackContext): # Check and deny usage in private chat if chat_type == "private": if user_id not in connections: - tlg_send_msg_type_chat(bot, chat_type, chat_id, - TEXT[lang]["CMD_NEEDS_CONNECTION"]) + await tlg_send_msg_type_chat( + bot, chat_type, chat_id, TEXT[lang]["CMD_NEEDS_CONNECTION"] + ) return group_id = connections[user_id]["group_id"] else: # Ignore if not requested by a group Admin - is_admin = tlg_user_is_admin(bot, user_id, chat_id) + is_admin = await tlg_user_is_admin(bot, user_id, chat_id) if (is_admin is None) or (is_admin == False): return # Get Group Chat ID and configured language @@ -2002,13 +2522,23 @@ def cmd_welcome_msg_time(update: Update, context: CallbackContext): tlg_msg_to_selfdestruct(update_msg) # Check if no argument was provided with the command if len(args) == 0: - tlg_send_msg_type_chat(bot, chat_type, chat_id, - TEXT[lang]["WELCOME_TIME_NOT_ARG"]) + await tlg_send_msg_type_chat( + bot, + chat_type, + chat_id, + TEXT[lang]["WELCOME_TIME_NOT_ARG"], + message_thread_id=update.effective_message.message_thread_id, + ) return # Check if provided time argument is not a number if not is_int(args[0]): - tlg_send_msg_type_chat(bot, chat_type, chat_id, - TEXT[lang]["TIME_NOT_NUM"]) + await tlg_send_msg_type_chat( + bot, + chat_type, + chat_id, + TEXT[lang]["TIME_NOT_NUM"], + message_thread_id=update.effective_message.message_thread_id, + ) return # Get time arguments new_time = int(args[0]) @@ -2025,28 +2555,49 @@ def cmd_welcome_msg_time(update: Update, context: CallbackContext): min_sec = "sec" new_time_str = "{} sec".format(new_time) else: - tlg_send_msg_type_chat(bot, chat_type, chat_id, - TEXT[lang]["WELCOME_TIME_NOT_ARG"]) + await tlg_send_msg_type_chat( + bot, + chat_type, + chat_id, + TEXT[lang]["WELCOME_TIME_NOT_ARG"], + message_thread_id=update.effective_message.message_thread_id, + ) return # Check if time value is out of limits - if new_time < 10: # Lees than 10s - msg_text = TEXT[lang]["TIME_OUT_RANGE"].format( \ - CONST["MAX_CONFIG_CAPTCHA_TIME"]) - tlg_send_msg_type_chat(bot, chat_type, chat_id, msg_text) + if new_time < 10: # Lees than 10s + msg_text = TEXT[lang]["TIME_OUT_RANGE"].format(CONST["MAX_CONFIG_CAPTCHA_TIME"]) + await tlg_send_msg_type_chat( + bot, + chat_type, + chat_id, + msg_text, + message_thread_id=update.effective_message.message_thread_id, + ) return if new_time > CONST["MAX_CONFIG_CAPTCHA_TIME"] * CONST["T_SECONDS_IN_MIN"]: - msg_text = TEXT[lang]["TIME_OUT_RANGE"].format( \ - CONST["MAX_CONFIG_CAPTCHA_TIME"]) - tlg_send_msg_type_chat(bot, chat_type, chat_id, msg_text) + msg_text = TEXT[lang]["TIME_OUT_RANGE"].format(CONST["MAX_CONFIG_CAPTCHA_TIME"]) + await tlg_send_msg_type_chat( + bot, + chat_type, + chat_id, + msg_text, + message_thread_id=update.effective_message.message_thread_id, + ) return # Set the new captcha time save_config_property(group_id, "Welcome_Time", new_time) msg_text = TEXT[lang]["WELCOME_TIME_CHANGE"].format(new_time_str) - tlg_send_msg_type_chat(bot, chat_type, chat_id, msg_text) + await tlg_send_msg_type_chat( + bot, + chat_type, + chat_id, + msg_text, + message_thread_id=update.effective_message.message_thread_id, + ) -def cmd_captcha_poll(update: Update, context: CallbackContext): - '''Command /captcha_poll message handler''' +async def cmd_captcha_poll(update: Update, context: CallbackContext): + """Command /captcha_poll message handler""" bot = context.bot args = context.args # Ignore command if it was a edited message @@ -2060,13 +2611,14 @@ def cmd_captcha_poll(update: Update, context: CallbackContext): # Check and deny usage in private chat if chat_type == "private": if user_id not in connections: - tlg_send_msg_type_chat(bot, chat_type, chat_id, - TEXT[lang]["CMD_NEEDS_CONNECTION"]) + await tlg_send_msg_type_chat( + bot, chat_type, chat_id, TEXT[lang]["CMD_NEEDS_CONNECTION"] + ) return group_id = connections[user_id]["group_id"] else: # Ignore if not requested by a group Admin - is_admin = tlg_user_is_admin(bot, user_id, chat_id) + is_admin = await tlg_user_is_admin(bot, user_id, chat_id) if (is_admin is None) or (is_admin == False): return # Get Group Chat ID and configured language @@ -2076,64 +2628,120 @@ def cmd_captcha_poll(update: Update, context: CallbackContext): tlg_msg_to_selfdestruct(update_msg) # Format command usage text text_cmd_usage = TEXT[lang]["CAPTCHA_POLL_USAGE"].format( - CONST["MAX_POLL_QUESTION_LENGTH"], - CONST["MAX_POLL_OPTION_LENGTH"], - CONST["MAX_POLL_OPTIONS"]) + CONST["MAX_POLL_QUESTION_LENGTH"], + CONST["MAX_POLL_OPTION_LENGTH"], + CONST["MAX_POLL_OPTIONS"], + ) # Check if no argument was provided with the command if len(args) < 2: - tlg_send_msg_type_chat(bot, chat_type, chat_id, text_cmd_usage) + await tlg_send_msg_type_chat( + bot, + chat_type, + chat_id, + text_cmd_usage, + message_thread_id=update.effective_message.message_thread_id, + ) return # Get poll message command poll_cmd = args[0] print("poll_cmd: {}".format(poll_cmd)) if poll_cmd not in ["question", "option", "correct_option"]: - tlg_send_msg_type_chat(bot, chat_type, chat_id, text_cmd_usage) + await tlg_send_msg_type_chat( + bot, + chat_type, + chat_id, + text_cmd_usage, + message_thread_id=update.effective_message.message_thread_id, + ) return if poll_cmd == "question": # get Poll Question poll_question = " ".join(args[1:]) print("poll_question: {}".format(poll_question)) if len(poll_question) > CONST["MAX_POLL_QUESTION_LENGTH"]: - poll_question = poll_question[:CONST["MAX_POLL_QUESTION_LENGTH"]] + poll_question = poll_question[: CONST["MAX_POLL_QUESTION_LENGTH"]] # Save Poll Question save_config_property(group_id, "Poll_Q", poll_question) - tlg_send_msg_type_chat(bot, chat_type, chat_id, - TEXT[lang]["POLL_QUESTION_CONFIGURED"]) + await tlg_send_msg_type_chat( + bot, + chat_type, + chat_id, + TEXT[lang]["POLL_QUESTION_CONFIGURED"], + message_thread_id=update.effective_message.message_thread_id, + ) elif poll_cmd == "correct_option": # get Poll correct option and check if is a number option_num = args[1] if not is_int(option_num): - tlg_send_msg_type_chat(bot, chat_type, chat_id, text_cmd_usage) + await tlg_send_msg_type_chat( + bot, + chat_type, + chat_id, + text_cmd_usage, + message_thread_id=update.effective_message.message_thread_id, + ) return option_num = int(option_num) # Check if correct option number is configured if (option_num < 1) or (option_num > CONST["MAX_POLL_OPTIONS"]): - tlg_send_msg_type_chat(bot, chat_type, chat_id, text_cmd_usage) + await tlg_send_msg_type_chat( + bot, + chat_type, + chat_id, + text_cmd_usage, + message_thread_id=update.effective_message.message_thread_id, + ) return poll_options = get_chat_config(group_id, "Poll_A") if option_num > num_config_poll_options(poll_options): - tlg_send_msg_type_chat(bot, chat_type, chat_id, - TEXT[lang]["POLL_CORRECT_OPTION_NOT_CONFIGURED"].format( - option_num)) + await tlg_send_msg_type_chat( + bot, + chat_type, + chat_id, + TEXT[lang]["POLL_CORRECT_OPTION_NOT_CONFIGURED"].format(option_num), + message_thread_id=update.effective_message.message_thread_id, + ) return # Save Poll correct option number save_config_property(group_id, "Poll_C_A", option_num) - tlg_send_msg_type_chat(bot, chat_type, chat_id, - TEXT[lang]["POLL_CORRECT_OPTION_CONFIGURED"].format( - option_num)) + await tlg_send_msg_type_chat( + bot, + chat_type, + chat_id, + TEXT[lang]["POLL_CORRECT_OPTION_CONFIGURED"].format(option_num), + message_thread_id=update.effective_message.message_thread_id, + ) elif poll_cmd == "option": # Check if option argument is valid if len(args) < 3: - tlg_send_msg_type_chat(bot, chat_type, chat_id, text_cmd_usage) + await tlg_send_msg_type_chat( + bot, + chat_type, + chat_id, + text_cmd_usage, + message_thread_id=update.effective_message.message_thread_id, + ) return option_num = args[1] print("option_num: {}".format(option_num)) if not is_int(option_num): - tlg_send_msg_type_chat(bot, chat_type, chat_id, text_cmd_usage) + await tlg_send_msg_type_chat( + bot, + chat_type, + chat_id, + text_cmd_usage, + message_thread_id=update.effective_message.message_thread_id, + ) return option_num = int(option_num) if (option_num < 1) or (option_num > CONST["MAX_POLL_OPTIONS"]): - tlg_send_msg_type_chat(bot, chat_type, chat_id, text_cmd_usage) + await tlg_send_msg_type_chat( + bot, + chat_type, + chat_id, + text_cmd_usage, + message_thread_id=update.effective_message.message_thread_id, + ) return option_num = option_num - 1 # Resize poll options list if missing options slots @@ -2151,7 +2759,7 @@ def cmd_captcha_poll(update: Update, context: CallbackContext): poll_option = " ".join(args[2:]) print("poll_option: {}".format(poll_option)) if len(poll_option) > CONST["MAX_POLL_OPTION_LENGTH"]: - poll_option = poll_option[:CONST["MAX_POLL_OPTION_LENGTH"]] + poll_option = poll_option[: CONST["MAX_POLL_OPTION_LENGTH"]] # Check if requested an option remove and remove it if poll_option.lower() == "remove": del poll_options[option_num] @@ -2160,12 +2768,17 @@ def cmd_captcha_poll(update: Update, context: CallbackContext): poll_options[option_num] = poll_option # Save Poll option save_config_property(group_id, "Poll_A", poll_options) - tlg_send_msg_type_chat(bot, chat_type, chat_id, - TEXT[lang]["POLL_OPTION_CONFIGURED"].format(option_num+1)) + await tlg_send_msg_type_chat( + bot, + chat_type, + chat_id, + TEXT[lang]["POLL_OPTION_CONFIGURED"].format(option_num + 1), + message_thread_id=update.effective_message.message_thread_id, + ) -def cmd_restrict_non_text(update: Update, context: CallbackContext): - '''Command /restrict_non_text message handler''' +async def cmd_restrict_non_text(update: Update, context: CallbackContext): + """Command /restrict_non_text message handler""" bot = context.bot args = context.args # Ignore command if it was a edited message @@ -2179,13 +2792,14 @@ def cmd_restrict_non_text(update: Update, context: CallbackContext): # Check and deny usage in private chat if chat_type == "private": if user_id not in connections: - tlg_send_msg_type_chat(bot, chat_type, chat_id, - TEXT[lang]["CMD_NEEDS_CONNECTION"]) + await tlg_send_msg_type_chat( + bot, chat_type, chat_id, TEXT[lang]["CMD_NEEDS_CONNECTION"] + ) return group_id = connections[user_id]["group_id"] else: # Ignore if not requested by a group Admin - is_admin = tlg_user_is_admin(bot, user_id, chat_id) + is_admin = await tlg_user_is_admin(bot, user_id, chat_id) if (is_admin is None) or (is_admin == False): return # Get Group Chat ID and configured language @@ -2195,14 +2809,24 @@ def cmd_restrict_non_text(update: Update, context: CallbackContext): tlg_msg_to_selfdestruct(update_msg) # Check if no argument was provided with the command if len(args) == 0: - tlg_send_msg_type_chat(bot, chat_type, chat_id, - TEXT[lang]["RESTRICT_NON_TEXT_MSG_NOT_ARG"]) + await tlg_send_msg_type_chat( + bot, + chat_type, + chat_id, + TEXT[lang]["RESTRICT_NON_TEXT_MSG_NOT_ARG"], + message_thread_id=update.effective_message.message_thread_id, + ) return # Check for valid expected argument values restrict_non_text_msgs = args[0] if restrict_non_text_msgs != "enable" and restrict_non_text_msgs != "disable": - tlg_send_msg_type_chat(bot, chat_type, chat_id, - TEXT[lang]["RESTRICT_NON_TEXT_MSG_NOT_ARG"]) + await tlg_send_msg_type_chat( + bot, + chat_type, + chat_id, + TEXT[lang]["RESTRICT_NON_TEXT_MSG_NOT_ARG"], + message_thread_id=update.effective_message.message_thread_id, + ) return # Check for forever restriction argument restrict_forever = False @@ -2218,11 +2842,17 @@ def cmd_restrict_non_text(update: Update, context: CallbackContext): else: save_config_property(group_id, "Restrict_Non_Text", 0) bot_msg = TEXT[lang]["RESTRICT_NON_TEXT_MSG_DISABLED"] - tlg_send_msg_type_chat(bot, chat_type, chat_id, bot_msg) + await tlg_send_msg_type_chat( + bot, + chat_type, + chat_id, + bot_msg, + message_thread_id=update.effective_message.message_thread_id, + ) -def cmd_add_ignore(update: Update, context: CallbackContext): - '''Command /add_ignore message handler''' +async def cmd_add_ignore(update: Update, context: CallbackContext): + """Command /add_ignore message handler""" bot = context.bot args = context.args # Ignore command if it was a edited message @@ -2236,13 +2866,14 @@ def cmd_add_ignore(update: Update, context: CallbackContext): # Check and deny usage in private chat if chat_type == "private": if user_id not in connections: - tlg_send_msg_type_chat(bot, chat_type, chat_id, - TEXT[lang]["CMD_NEEDS_CONNECTION"]) + await tlg_send_msg_type_chat( + bot, chat_type, chat_id, TEXT[lang]["CMD_NEEDS_CONNECTION"] + ) return group_id = connections[user_id]["group_id"] else: # Ignore if not requested by a group Admin - is_admin = tlg_user_is_admin(bot, user_id, chat_id) + is_admin = await tlg_user_is_admin(bot, user_id, chat_id) if (is_admin is None) or (is_admin == False): return # Get Group Chat ID and configured language @@ -2252,8 +2883,13 @@ def cmd_add_ignore(update: Update, context: CallbackContext): tlg_msg_to_selfdestruct(update_msg) # Check if no argument was provided with the command if len(args) == 0: - tlg_send_msg_type_chat(bot, chat_type, chat_id, - TEXT[lang]["IGNORE_LIST_ADD_NOT_ARG"]) + await tlg_send_msg_type_chat( + bot, + chat_type, + chat_id, + TEXT[lang]["IGNORE_LIST_ADD_NOT_ARG"], + message_thread_id=update.effective_message.message_thread_id, + ) return # Check and add user ID/alias form ignore list user_id_alias = args[0] @@ -2271,11 +2907,17 @@ def cmd_add_ignore(update: Update, context: CallbackContext): bot_msg = TEXT[lang]["IGNORE_LIST_ADD_LIMIT_EXCEEDED"] else: bot_msg = TEXT[lang]["IGNORE_LIST_ADD_INVALID"] - tlg_send_msg_type_chat(bot, chat_type, chat_id, bot_msg) + await tlg_send_msg_type_chat( + bot, + chat_type, + chat_id, + bot_msg, + message_thread_id=update.effective_message.message_thread_id, + ) -def cmd_remove_ignore(update: Update, context: CallbackContext): - '''Command /remove_ignore message handler''' +async def cmd_remove_ignore(update: Update, context: CallbackContext): + """Command /remove_ignore message handler""" bot = context.bot args = context.args # Ignore command if it was a edited message @@ -2289,13 +2931,14 @@ def cmd_remove_ignore(update: Update, context: CallbackContext): # Check and deny usage in private chat if chat_type == "private": if user_id not in connections: - tlg_send_msg_type_chat(bot, chat_type, chat_id, - TEXT[lang]["CMD_NEEDS_CONNECTION"]) + await tlg_send_msg_type_chat( + bot, chat_type, chat_id, TEXT[lang]["CMD_NEEDS_CONNECTION"] + ) return group_id = connections[user_id]["group_id"] else: # Ignore if not requested by a group Admin - is_admin = tlg_user_is_admin(bot, user_id, chat_id) + is_admin = await tlg_user_is_admin(bot, user_id, chat_id) if (is_admin is None) or (is_admin == False): return # Get Group Chat ID and configured language @@ -2305,8 +2948,13 @@ def cmd_remove_ignore(update: Update, context: CallbackContext): tlg_msg_to_selfdestruct(update_msg) # Check if no argument was provided with the command if len(args) == 0: - tlg_send_msg_type_chat(bot, chat_type, chat_id, - TEXT[lang]["IGNORE_LIST_REMOVE_NOT_ARG"]) + await tlg_send_msg_type_chat( + bot, + chat_type, + chat_id, + TEXT[lang]["IGNORE_LIST_REMOVE_NOT_ARG"], + message_thread_id=update.effective_message.message_thread_id, + ) return # Check and remove user ID/alias form ignore list ignore_list = get_chat_config(group_id, "Ignore_List") @@ -2316,11 +2964,17 @@ def cmd_remove_ignore(update: Update, context: CallbackContext): bot_msg = TEXT[lang]["IGNORE_LIST_REMOVE_SUCCESS"] else: bot_msg = TEXT[lang]["IGNORE_LIST_REMOVE_NOT_IN_LIST"] - tlg_send_msg_type_chat(bot, chat_type, chat_id, bot_msg) + await tlg_send_msg_type_chat( + bot, + chat_type, + chat_id, + bot_msg, + message_thread_id=update.effective_message.message_thread_id, + ) -def cmd_ignore_list(update: Update, context: CallbackContext): - '''Command /ignore_list message handler''' +async def cmd_ignore_list(update: Update, context: CallbackContext): + """Command /ignore_list message handler""" bot = context.bot # Ignore command if it was a edited message update_msg = getattr(update, "message", None) @@ -2333,13 +2987,14 @@ def cmd_ignore_list(update: Update, context: CallbackContext): # Check and deny usage in private chat if chat_type == "private": if user_id not in connections: - tlg_send_msg_type_chat(bot, chat_type, chat_id, - TEXT[lang]["CMD_NEEDS_CONNECTION"]) + await tlg_send_msg_type_chat( + bot, chat_type, chat_id, TEXT[lang]["CMD_NEEDS_CONNECTION"] + ) return group_id = connections[user_id]["group_id"] else: # Ignore if not requested by a group Admin - is_admin = tlg_user_is_admin(bot, user_id, chat_id) + is_admin = await tlg_user_is_admin(bot, user_id, chat_id) if (is_admin is None) or (is_admin == False): return # Get Group Chat ID and configured language @@ -2353,11 +3008,17 @@ def cmd_ignore_list(update: Update, context: CallbackContext): bot_msg = TEXT[lang]["IGNORE_LIST_EMPTY"] else: bot_msg = "\n".join([str(x) for x in ignore_list]) - tlg_send_msg_type_chat(bot, chat_type, chat_id, bot_msg) + await tlg_send_msg_type_chat( + bot, + chat_type, + chat_id, + bot_msg, + message_thread_id=update.effective_message.message_thread_id, + ) -def cmd_remove_solve_kick_msg(update: Update, context: CallbackContext): - '''Command /remove_solve_kick_msg message handler''' +async def cmd_remove_solve_kick_msg(update: Update, context: CallbackContext): + """Command /remove_solve_kick_msg message handler""" bot = context.bot args = context.args # Ignore command if it was a edited message @@ -2371,13 +3032,14 @@ def cmd_remove_solve_kick_msg(update: Update, context: CallbackContext): # Check and deny usage in private chat if chat_type == "private": if user_id not in connections: - tlg_send_msg_type_chat(bot, chat_type, chat_id, - TEXT[lang]["CMD_NEEDS_CONNECTION"]) + await tlg_send_msg_type_chat( + bot, chat_type, chat_id, TEXT[lang]["CMD_NEEDS_CONNECTION"] + ) return group_id = connections[user_id]["group_id"] else: # Ignore if not requested by a group Admin - is_admin = tlg_user_is_admin(bot, user_id, chat_id) + is_admin = await tlg_user_is_admin(bot, user_id, chat_id) if (is_admin is None) or (is_admin == False): return # Get Group Chat ID and configured language @@ -2387,8 +3049,13 @@ def cmd_remove_solve_kick_msg(update: Update, context: CallbackContext): tlg_msg_to_selfdestruct(update_msg) # Check if no argument was provided with the command if len(args) == 0: - tlg_send_msg_type_chat(bot, chat_type, chat_id, - TEXT[lang]["RM_SOLVE_KICK_MSG"]) + await tlg_send_msg_type_chat( + bot, + chat_type, + chat_id, + TEXT[lang]["RM_SOLVE_KICK_MSG"], + message_thread_id=update.effective_message.message_thread_id, + ) return # Get remove solve/kick messages config to set yes_or_no = args[0].lower() @@ -2400,11 +3067,17 @@ def cmd_remove_solve_kick_msg(update: Update, context: CallbackContext): bot_msg = TEXT[lang]["RM_SOLVE_KICK_MSG_NO"] else: bot_msg = TEXT[lang]["RM_SOLVE_KICK_MSG"] - tlg_send_msg_type_chat(bot, chat_type, chat_id, bot_msg) + await tlg_send_msg_type_chat( + bot, + chat_type, + chat_id, + bot_msg, + message_thread_id=update.effective_message.message_thread_id, + ) -def cmd_remove_welcome_msg(update: Update, context: CallbackContext): - '''Command /remove_welcome_msg message handler''' +async def cmd_remove_welcome_msg(update: Update, context: CallbackContext): + """Command /remove_welcome_msg message handler""" bot = context.bot args = context.args # Ignore command if it was a edited message @@ -2418,8 +3091,9 @@ def cmd_remove_welcome_msg(update: Update, context: CallbackContext): # Check and deny usage in private chat if chat_type == "private": if user_id not in connections: - tlg_send_msg_type_chat(bot, chat_type, chat_id, - TEXT[lang]["CMD_NEEDS_CONNECTION"]) + await tlg_send_msg_type_chat( + bot, chat_type, chat_id, TEXT[lang]["CMD_NEEDS_CONNECTION"] + ) return group_id = connections[user_id]["group_id"] else: @@ -2434,8 +3108,13 @@ def cmd_remove_welcome_msg(update: Update, context: CallbackContext): tlg_msg_to_selfdestruct(update_msg) # Check if no argument was provided with the command if len(args) == 0: - tlg_send_msg_type_chat(bot, chat_type, chat_id, - TEXT[lang]["RM_WELCOME_MSG"]) + await tlg_send_msg_type_chat( + bot, + chat_type, + chat_id, + TEXT[lang]["RM_WELCOME_MSG"], + message_thread_id=update.effective_message.message_thread_id, + ) return # Get remove welcome messages config to set yes_or_no = args[0].lower() @@ -2447,11 +3126,17 @@ def cmd_remove_welcome_msg(update: Update, context: CallbackContext): bot_msg = TEXT[lang]["RM_WELCOME_MSG_NO"] else: bot_msg = TEXT[lang]["RM_WELCOME_MSG"] - tlg_send_msg_type_chat(bot, chat_type, chat_id, bot_msg) + await tlg_send_msg_type_chat( + bot, + chat_type, + chat_id, + bot_msg, + message_thread_id=update.effective_message.message_thread_id, + ) -def cmd_remove_all_msg_kick_on(update: Update, context: CallbackContext): - '''Command /remove_all_msg_kick_on message handler''' +async def cmd_remove_all_msg_kick_on(update: Update, context: CallbackContext): + """Command /remove_all_msg_kick_on message handler""" bot = context.bot # Ignore command if it was a edited message update_msg = getattr(update, "message", None) @@ -2464,13 +3149,17 @@ def cmd_remove_all_msg_kick_on(update: Update, context: CallbackContext): # Check and deny usage in private chat if chat_type == "private": if user_id not in connections: - tlg_send_msg_type_chat(bot, chat_type, chat_id, - TEXT[lang]["CMD_NEEDS_CONNECTION"]) + await tlg_send_msg_type_chat( + bot, + chat_type, + chat_id, + TEXT[lang]["CMD_NEEDS_CONNECTION"], + ) return group_id = connections[user_id]["group_id"] else: # Ignore if not requested by a group Admin - is_admin = tlg_user_is_admin(bot, user_id, chat_id) + is_admin = await tlg_user_is_admin(bot, user_id, chat_id) if (is_admin is None) or (is_admin == False): return # Get Group Chat ID and configured language @@ -2486,11 +3175,17 @@ def cmd_remove_all_msg_kick_on(update: Update, context: CallbackContext): enable = True save_config_property(group_id, "RM_All_Msg", enable) bot_msg = TEXT[lang]["RM_ALL_MSGS_AFTER_KICK_ON"] - tlg_send_msg_type_chat(bot, chat_type, chat_id, bot_msg) + await tlg_send_msg_type_chat( + bot, + chat_type, + chat_id, + bot_msg, + message_thread_id=update.effective_message.message_thread_id, + ) -def cmd_remove_all_msg_kick_off(update: Update, context: CallbackContext): - '''Command /remove_all_msg_kick_off message handler''' +async def cmd_remove_all_msg_kick_off(update: Update, context: CallbackContext): + """Command /remove_all_msg_kick_off message handler""" bot = context.bot # Ignore command if it was a edited message update_msg = getattr(update, "message", None) @@ -2503,13 +3198,14 @@ def cmd_remove_all_msg_kick_off(update: Update, context: CallbackContext): # Check and deny usage in private chat if chat_type == "private": if user_id not in connections: - tlg_send_msg_type_chat(bot, chat_type, chat_id, - TEXT[lang]["CMD_NEEDS_CONNECTION"]) + await tlg_send_msg_type_chat( + bot, chat_type, chat_id, TEXT[lang]["CMD_NEEDS_CONNECTION"] + ) return group_id = connections[user_id]["group_id"] else: # Ignore if not requested by a group Admin - is_admin = tlg_user_is_admin(bot, user_id, chat_id) + is_admin = await tlg_user_is_admin(bot, user_id, chat_id) if (is_admin is None) or (is_admin == False): return # Get Group Chat ID and configured language @@ -2525,11 +3221,17 @@ def cmd_remove_all_msg_kick_off(update: Update, context: CallbackContext): enable = False save_config_property(group_id, "RM_All_Msg", enable) bot_msg = TEXT[lang]["RM_ALL_MSGS_AFTER_KICK_OFF"] - tlg_send_msg_type_chat(bot, chat_type, chat_id, bot_msg) + await tlg_send_msg_type_chat( + bot, + chat_type, + chat_id, + bot_msg, + message_thread_id=update.effective_message.message_thread_id, + ) -def cmd_url_enable(update: Update, context: CallbackContext): - '''Command /url_enable message handler''' +async def cmd_url_enable(update: Update, context: CallbackContext): + """Command /url_enable message handler""" bot = context.bot # Ignore command if it was a edited message update_msg = getattr(update, "message", None) @@ -2542,13 +3244,14 @@ def cmd_url_enable(update: Update, context: CallbackContext): # Check and deny usage in private chat if chat_type == "private": if user_id not in connections: - tlg_send_msg_type_chat(bot, chat_type, chat_id, - TEXT[lang]["CMD_NEEDS_CONNECTION"]) + await tlg_send_msg_type_chat( + bot, chat_type, chat_id, TEXT[lang]["CMD_NEEDS_CONNECTION"] + ) return group_id = connections[user_id]["group_id"] else: # Ignore if not requested by a group Admin - is_admin = tlg_user_is_admin(bot, user_id, chat_id) + is_admin = await tlg_user_is_admin(bot, user_id, chat_id) if (is_admin is None) or (is_admin == False): return # Get Group Chat ID and configured language @@ -2564,11 +3267,17 @@ def cmd_url_enable(update: Update, context: CallbackContext): enable = True save_config_property(group_id, "URL_Enabled", enable) bot_msg = TEXT[lang]["URL_ENABLE"] - tlg_send_msg_type_chat(bot, chat_type, chat_id, bot_msg) + await tlg_send_msg_type_chat( + bot, + chat_type, + chat_id, + bot_msg, + message_thread_id=update.effective_message.message_thread_id, + ) -def cmd_url_disable(update: Update, context: CallbackContext): - '''Command /url_disable message handler''' +async def cmd_url_disable(update: Update, context: CallbackContext): + """Command /url_disable message handler""" bot = context.bot # Ignore command if it was a edited message update_msg = getattr(update, "message", None) @@ -2581,13 +3290,14 @@ def cmd_url_disable(update: Update, context: CallbackContext): # Check and deny usage in private chat if chat_type == "private": if user_id not in connections: - tlg_send_msg_type_chat(bot, chat_type, chat_id, - TEXT[lang]["CMD_NEEDS_CONNECTION"]) + await tlg_send_msg_type_chat( + bot, chat_type, chat_id, TEXT[lang]["CMD_NEEDS_CONNECTION"] + ) return group_id = connections[user_id]["group_id"] else: # Ignore if not requested by a group Admin - is_admin = tlg_user_is_admin(bot, user_id, chat_id) + is_admin = await tlg_user_is_admin(bot, user_id, chat_id) if (is_admin is None) or (is_admin == False): return # Get Group Chat ID and configured language @@ -2603,11 +3313,17 @@ def cmd_url_disable(update: Update, context: CallbackContext): enable = False save_config_property(group_id, "URL_Enabled", enable) bot_msg = TEXT[lang]["URL_DISABLE"] - tlg_send_msg_type_chat(bot, chat_type, chat_id, bot_msg) + await tlg_send_msg_type_chat( + bot, + chat_type, + chat_id, + bot_msg, + message_thread_id=update.effective_message.message_thread_id, + ) -def cmd_enable(update: Update, context: CallbackContext): - '''Command /enable message handler''' +async def cmd_enable(update: Update, context: CallbackContext): + """Command /enable message handler""" bot = context.bot # Ignore command if it was a edited message update_msg = getattr(update, "message", None) @@ -2619,10 +3335,10 @@ def cmd_enable(update: Update, context: CallbackContext): lang = get_update_user_lang(update_msg.from_user) # Check and deny usage in private chat if chat_type == "private": - tlg_send_msg(bot, chat_id, TEXT[lang]["CMD_NOT_ALLOW_PRIVATE"]) + await tlg_send_msg(bot, chat_id, TEXT[lang]["CMD_NOT_ALLOW_PRIVATE"]) return # Ignore if not requested by a group Admin - is_admin = tlg_user_is_admin(bot, user_id, chat_id) + is_admin = await tlg_user_is_admin(bot, user_id, chat_id) if (is_admin is None) or (is_admin == False): return # Set user command message to be deleted by Bot in default time @@ -2637,11 +3353,16 @@ def cmd_enable(update: Update, context: CallbackContext): enable = True save_config_property(chat_id, "Enabled", enable) bot_msg = TEXT[lang]["ENABLE"] - tlg_send_selfdestruct_msg(bot, chat_id, bot_msg) + await tlg_send_selfdestruct_msg( + bot, + chat_id, + bot_msg, + message_thread_id=update.effective_message.message_thread_id, + ) -def cmd_disable(update: Update, context: CallbackContext): - '''Command /disable message handler''' +async def cmd_disable(update: Update, context: CallbackContext): + """Command /disable message handler""" bot = context.bot # Ignore command if it was a edited message update_msg = getattr(update, "message", None) @@ -2653,10 +3374,10 @@ def cmd_disable(update: Update, context: CallbackContext): lang = get_update_user_lang(update_msg.from_user) # Check and deny usage in private chat if chat_type == "private": - tlg_send_msg(bot, chat_id, TEXT[lang]["CMD_NOT_ALLOW_PRIVATE"]) + await tlg_send_msg(bot, chat_id, TEXT[lang]["CMD_NOT_ALLOW_PRIVATE"]) return # Ignore if not requested by a group Admin - is_admin = tlg_user_is_admin(bot, user_id, chat_id) + is_admin = await tlg_user_is_admin(bot, user_id, chat_id) if (is_admin is None) or (is_admin == False): return # Set user command message to be deleted by Bot in default time @@ -2671,11 +3392,16 @@ def cmd_disable(update: Update, context: CallbackContext): enable = False save_config_property(chat_id, "Enabled", enable) bot_msg = TEXT[lang]["DISABLE"] - tlg_send_selfdestruct_msg(bot, chat_id, bot_msg) + await tlg_send_selfdestruct_msg( + bot, + chat_id, + bot_msg, + message_thread_id=update.effective_message.message_thread_id, + ) -def cmd_chatid(update: Update, context: CallbackContext): - '''Command /chatid message handler''' +async def cmd_chatid(update: Update, context: CallbackContext): + """Command /chatid message handler""" bot = context.bot # Ignore command if it was a edited message update_msg = getattr(update, "message", None) @@ -2685,15 +3411,20 @@ def cmd_chatid(update: Update, context: CallbackContext): chat_type = update_msg.chat.type if chat_type == "private": msg_text = "Your Chat ID:\n—————————\n{}".format(chat_id) - tlg_send_msg(bot, chat_id, msg_text) + await tlg_send_msg(bot, chat_id, msg_text) else: msg_text = "Group Chat ID:\n—————————\n{}".format(chat_id) tlg_msg_to_selfdestruct(update_msg) - tlg_send_selfdestruct_msg(bot, chat_id, msg_text) + await tlg_send_selfdestruct_msg( + bot, + chat_id, + msg_text, + message_thread_id=update.effective_message.message_thread_id, + ) -def cmd_version(update: Update, context: CallbackContext): - '''Command /version message handler''' +async def cmd_version(update: Update, context: CallbackContext): + """Command /version message handler""" bot = context.bot # Ignore command if it was a edited message update_msg = getattr(update, "message", None) @@ -2705,21 +3436,26 @@ def cmd_version(update: Update, context: CallbackContext): lang = get_update_user_lang(update_msg.from_user) if chat_type == "private": msg_text = TEXT[lang]["VERSION"].format(CONST["VERSION"]) - tlg_send_msg(bot, chat_id, msg_text) + await tlg_send_msg(bot, chat_id, msg_text) else: # Ignore if not requested by a group Admin - is_admin = tlg_user_is_admin(bot, user_id, chat_id) + is_admin = await tlg_user_is_admin(bot, user_id, chat_id) if (is_admin is None) or (is_admin == False): return # Send the message lang = get_chat_config(chat_id, "Language") msg_text = TEXT[lang]["VERSION"].format(CONST["VERSION"]) tlg_msg_to_selfdestruct(update_msg) - tlg_send_selfdestruct_msg(bot, chat_id, msg_text) + await tlg_send_selfdestruct_msg( + bot, + chat_id, + msg_text, + message_thread_id=update.effective_message.message_thread_id, + ) -def cmd_about(update: Update, context: CallbackContext): - '''Command /about handler''' +async def cmd_about(update: Update, context: CallbackContext): + """Command /about handler""" bot = context.bot # Ignore command if it was a edited message update_msg = getattr(update, "message", None) @@ -2730,14 +3466,23 @@ def cmd_about(update: Update, context: CallbackContext): lang = get_update_user_lang(update_msg.from_user) if chat_type != "private": lang = get_chat_config(chat_id, "Language") - msg_text = TEXT[lang]["ABOUT_MSG"].format(CONST["DEVELOPER"], CONST["ORIG_DEVELOPER"], - CONST["REPOSITORY"], CONST["DEV_DONATION_ADDR"]) - tlg_send_msg(bot, chat_id, msg_text) - - -def cmd_captcha(update: Update, context: CallbackContext): - '''Command /captcha message handler. Usefull to test. - Just Bot Owner can use it.''' + msg_text = TEXT[lang]["ABOUT_MSG"].format( + CONST["DEVELOPER"], + CONST["ORIG_DEVELOPER"], + CONST["REPOSITORY"], + CONST["DEV_DONATION_ADDR"], + ) + await tlg_send_msg( + bot, + chat_id, + msg_text, + message_thread_id=update.effective_message.message_thread_id, + ) + + +async def cmd_captcha(update: Update, context: CallbackContext): + """Command /captcha message handler. Usefull to test. + Just Bot Owner can use it.""" bot = context.bot # Ignore command if it was a edited message update_msg = getattr(update, "message", None) @@ -2751,9 +3496,13 @@ def cmd_captcha(update: Update, context: CallbackContext): user_alias = "@{}".format(user.username) lang = get_update_user_lang(update_msg.from_user) # Check if command was execute by Bot owner - if (str(user_id) != CONST["BOT_OWNER"]) and \ - (user_alias != CONST["BOT_OWNER"]): - tlg_send_selfdestruct_msg(bot, chat_id, CONST["CMD_JUST_ALLOW_OWNER"]) + if (str(user_id) != CONST["BOT_OWNER"]) and (user_alias != CONST["BOT_OWNER"]): + await tlg_send_selfdestruct_msg( + bot, + chat_id, + CONST["CMD_JUST_ALLOW_OWNER"], + message_thread_id=update.effective_message.message_thread_id, + ) return # Set user command message to be deleted by Bot in default time tlg_msg_to_selfdestruct(update_msg) @@ -2762,23 +3511,31 @@ def cmd_captcha(update: Update, context: CallbackContext): captcha_mode = choice(["nums", "hex", "ascii", "math"]) captcha = create_image_captcha(chat_id, user_id, difficulty, captcha_mode) if captcha_mode == "math": - captcha_code = "{} = {}".format(captcha["equation_str"], \ - captcha["equation_result"]) + captcha_code = "{} = {}".format( + captcha["equation_str"], captcha["equation_result"] + ) else: captcha_code = captcha["characters"] printts("[{}] Sending captcha msg: {}".format(chat_id, captcha_code)) # Note: Img caption must be <= 1024 chars - img_caption = "Captcha Level: {}\nCaptcha Mode: {}\n" \ - "Captcha Code: {}".format(difficulty, captcha_mode, captcha_code) - tlg_send_image(bot, chat_id, open(captcha["image"],"rb"), img_caption) + img_caption = "Captcha Level: {}\nCaptcha Mode: {}\n" "Captcha Code: {}".format( + difficulty, captcha_mode, captcha_code + ) + await tlg_send_image( + bot, + chat_id, + open(captcha["image"], "rb"), + img_caption, + message_thread_id=update.effective_message.message_thread_id, + ) # Remove sent captcha image file from file system if path.exists(captcha["image"]): remove(captcha["image"]) -def cmd_allowuserlist(update: Update, context: CallbackContext): - '''Command /allowuserlist message handler. To Global allowed list blind users. - Just Bot Owner can use it.''' +async def cmd_allowuserlist(update: Update, context: CallbackContext): + """Command /allowuserlist message handler. To Global allowed list blind users. + Just Bot Owner can use it.""" bot = context.bot args = context.args # Ignore command if it was a edited message @@ -2794,7 +3551,12 @@ def cmd_allowuserlist(update: Update, context: CallbackContext): lang = get_update_user_lang(update_msg.from_user) # Check if command was execute by Bot owner if (str(user_id) != CONST["BOT_OWNER"]) and (user_alias != CONST["BOT_OWNER"]): - tlg_send_selfdestruct_msg(bot, chat_id, CONST["CMD_JUST_ALLOW_OWNER"]) + await tlg_send_selfdestruct_msg( + bot, + chat_id, + CONST["CMD_JUST_ALLOW_OWNER"], + message_thread_id=update.effective_message.message_thread_id, + ) return # Check if no argument was provided with the command if len(args) == 0: @@ -2802,43 +3564,93 @@ def cmd_allowuserlist(update: Update, context: CallbackContext): l_white_users = file_read(CONST["F_ALLOWED_USERS"]) bot_msg = "\n".join([str(user) for user in l_white_users]) bot_msg = "Global Allowed Users List:\n--------------------\n{}".format(bot_msg) - tlg_send_msg(bot, chat_id, bot_msg) - tlg_send_msg(bot, chat_id, CONST["ALLOWUSERLIST_USAGE"]) + await tlg_send_msg( + bot, + chat_id, + bot_msg, + message_thread_id=update.effective_message.message_thread_id, + ) + await tlg_send_msg( + bot, + chat_id, + CONST["ALLOWUSERLIST_USAGE"], + message_thread_id=update.effective_message.message_thread_id, + ) return else: if len(args) <= 1: - tlg_send_msg(bot, chat_id, CONST["ALLOWUSERLIST_USAGE"]) + await tlg_send_msg( + bot, + chat_id, + CONST["ALLOWUSERLIST_USAGE"], + message_thread_id=update.effective_message.message_thread_id, + ) return if (args[0] != "add") and (args[0] != "rm"): - tlg_send_msg(bot, chat_id, CONST["ALLOWUSERLIST_USAGE"]) + await tlg_send_msg( + bot, + chat_id, + CONST["ALLOWUSERLIST_USAGE"], + message_thread_id=update.effective_message.message_thread_id, + ) return add_rm = args[0] user = args[1] l_white_users = file_read(CONST["F_ALLOWED_USERS"]) if add_rm == "add": if not tlg_is_valid_user_id_or_alias(user): - tlg_send_msg(bot, chat_id, "Invalid User ID/Alias.") + await tlg_send_msg( + bot, + chat_id, + "Invalid User ID/Alias.", + message_thread_id=update.effective_message.message_thread_id, + ) return if user not in l_white_users: file_write(CONST["F_ALLOWED_USERS"], "{}\n".format(user)) - tlg_send_msg(bot, chat_id, "User added to Global allowed list.") + await tlg_send_msg( + bot, + chat_id, + "User added to Global allowed list.", + message_thread_id=update.effective_message.message_thread_id, + ) else: - tlg_send_msg(bot, chat_id, "The User is already in Global allowed list.") + await tlg_send_msg( + bot, + chat_id, + "The User is already in Global allowed list.", + message_thread_id=update.effective_message.message_thread_id, + ) return if add_rm == "rm": if not tlg_is_valid_user_id_or_alias(user): - tlg_send_msg(bot, chat_id, "Invalid User ID/Alias.") + await tlg_send_msg( + bot, + chat_id, + "Invalid User ID/Alias.", + message_thread_id=update.effective_message.message_thread_id, + ) return if list_remove_element(l_white_users, user): file_write(CONST["F_ALLOWED_USERS"], l_white_users, "w") - tlg_send_msg(bot, chat_id, "User removed from Global allowed list.") + await tlg_send_msg( + bot, + chat_id, + "User removed from Global allowed list.", + message_thread_id=update.effective_message.message_thread_id, + ) else: - tlg_send_msg(bot, chat_id, "The User is not in Global allowed list.") + await tlg_send_msg( + bot, + chat_id, + "The User is not in Global allowed list.", + message_thread_id=update.effective_message.message_thread_id, + ) -def cmd_allowgroup(update: Update, context: CallbackContext): - '''Command /allowgroup message handler. To allow Bot usage in groups when Bot is private. - Just Bot Owner can use it.''' +async def cmd_allowgroup(update: Update, context: CallbackContext): + """Command /allowgroup message handler. To allow Bot usage in groups when Bot is private. + Just Bot Owner can use it.""" bot = context.bot args = context.args # Ignore command if it was a edited message @@ -2854,7 +3666,12 @@ def cmd_allowgroup(update: Update, context: CallbackContext): lang = get_update_user_lang(update_msg.from_user) # Check if command was execute by Bot owner if (str(user_id) != CONST["BOT_OWNER"]) and (user_alias != CONST["BOT_OWNER"]): - tlg_send_selfdestruct_msg(bot, chat_id, CONST["CMD_JUST_ALLOW_OWNER"]) + await tlg_send_selfdestruct_msg( + bot, + chat_id, + CONST["CMD_JUST_ALLOW_OWNER"], + message_thread_id=update.effective_message.message_thread_id, + ) return # Check if no argument was provided with the command if len(args) == 0: @@ -2862,44 +3679,96 @@ def cmd_allowgroup(update: Update, context: CallbackContext): l_allowed_groups = file_read(CONST["F_ALLOWED_GROUPS"]) bot_msg = "\n".join([str(group) for group in l_allowed_groups]) bot_msg = "Allowed Groups:\n--------------------\n{}".format(bot_msg) - tlg_send_msg(bot, chat_id, bot_msg) - tlg_send_msg(bot, chat_id, CONST["ALLOWGROUP_USAGE"]) + await tlg_send_msg( + bot, + chat_id, + bot_msg, + message_thread_id=update.effective_message.message_thread_id, + ) + await tlg_send_msg( + bot, + chat_id, + CONST["ALLOWGROUP_USAGE"], + message_thread_id=update.effective_message.message_thread_id, + ) return else: if len(args) <= 1: - tlg_send_msg(bot, chat_id, CONST["ALLOWGROUP_USAGE"]) + await tlg_send_msg( + bot, + chat_id, + CONST["ALLOWGROUP_USAGE"], + message_thread_id=update.effective_message.message_thread_id, + ) return if (args[0] != "add") and (args[0] != "rm"): - tlg_send_msg(bot, chat_id, CONST["ALLOWGROUP_USAGE"]) + await tlg_send_msg( + bot, + chat_id, + CONST["ALLOWGROUP_USAGE"], + message_thread_id=update.effective_message.message_thread_id, + ) return add_rm = args[0] group = args[1] l_allowed_groups = file_read(CONST["F_ALLOWED_GROUPS"]) if add_rm == "add": if not tlg_is_valid_group(group): - tlg_send_msg(bot, chat_id, "Invalid Group ID.") + await tlg_send_msg( + bot, + chat_id, + "Invalid Group ID.", + message_thread_id=update.effective_message.message_thread_id, + ) return if group not in l_allowed_groups: file_write(CONST["F_ALLOWED_GROUPS"], "{}\n".format(group)) - tlg_send_msg(bot, chat_id, "Group added to allowed list.") + await tlg_send_msg( + bot, + chat_id, + "Group added to allowed list.", + message_thread_id=update.effective_message.message_thread_id, + ) else: - tlg_send_msg(bot, chat_id, "The group is already in the allowed list.") + await tlg_send_msg( + bot, + chat_id, + "The group is already in the allowed list.", + message_thread_id=update.effective_message.message_thread_id, + ) return if add_rm == "rm": if not tlg_is_valid_group(group): - tlg_send_msg(bot, chat_id, "Invalid Group ID.") + await tlg_send_msg( + bot, + chat_id, + "Invalid Group ID.", + message_thread_id=update.effective_message.message_thread_id, + ) return if list_remove_element(l_allowed_groups, group): file_write(CONST["F_ALLOWED_GROUPS"], l_allowed_groups, "w") - tlg_send_msg(bot, chat_id, "Group removed from allowed list.") + await tlg_send_msg( + bot, + chat_id, + "Group removed from allowed list.", + message_thread_id=update.effective_message.message_thread_id, + ) else: - tlg_send_msg(bot, chat_id, "The group is not in allowed list.") + await tlg_send_msg( + bot, + chat_id, + "The group is not in allowed list.", + message_thread_id=update.effective_message.message_thread_id, + ) + ############################################################################### ### Bot automatic remove sent messages thread -def th_selfdestruct_messages(bot): - '''Handle remove messages sent by the Bot with the timed self-delete function''' + +async def th_selfdestruct_messages(bot: Bot): + """Handle remove messages sent by the Bot with the timed self-delete function""" global to_delete_in_time_messages_list while not force_exit: # Thread sleep for each iteration @@ -2918,24 +3787,36 @@ def th_selfdestruct_messages(bot): sleep(0.01) # If actual time is equal or more than the expected sent msg delete time if time() >= sent_msg["delete_time"]: - printts("[{}] Scheduled deletion time for message: {}".format( - sent_msg["Chat_id"], sent_msg["Msg_id"])) - delete_result = tlg_delete_msg(bot, sent_msg["Chat_id"], sent_msg["Msg_id"]) + printts( + "[{}] Scheduled deletion time for message: {}".format( + sent_msg["Chat_id"], sent_msg["Msg_id"] + ) + ) + delete_result = await tlg_delete_msg( + bot, sent_msg["Chat_id"], sent_msg["Msg_id"] + ) # The bot has no privileges to delete messages if delete_result["error"] == "Message can't be deleted": lang = get_chat_config(sent_msg["Chat_id"], "Language") - sent_result = tlg_send_msg(bot, sent_msg["Chat_id"], - TEXT[lang]["CANT_DEL_MSG"], reply_to_message_id=sent_msg["Msg_id"]) + sent_result = await tlg_send_msg( + bot, + sent_msg["Chat_id"], + TEXT[lang]["CANT_DEL_MSG"], + reply_to_message_id=sent_msg["Msg_id"], + message_thread_id=sent_msg["Msg_thread_id"] + ) if sent_result["msg"] is not None: tlg_msg_to_selfdestruct(sent_result["msg"]) list_remove_element(to_delete_in_time_messages_list, sent_msg) sleep(0.01) + ############################################################################### ### Handle time to kick users thread -def th_time_to_kick_not_verify_users(bot): - '''Check if the time for ban new users that has not completed the captcha has arrived''' + +async def th_time_to_kick_not_verify_users(bot: Bot): + """Check if the time for ban new users that has not completed the captcha has arrived""" global new_users while not force_exit: # Thread sleep for each iteration @@ -2964,8 +3845,12 @@ def th_time_to_kick_not_verify_users(bot): if user_id not in new_users[chat_id]: continue try: - user_join_time = new_users[chat_id][user_id]["join_data"]["join_time"] - captcha_timeout = new_users[chat_id][user_id]["join_data"]["captcha_timeout"] + user_join_time = new_users[chat_id][user_id]["join_data"][ + "join_time" + ] + captcha_timeout = new_users[chat_id][user_id]["join_data"][ + "captcha_timeout" + ] if new_users[chat_id][user_id]["join_data"]["kicked_ban"]: # Remove from new users list the remaining kicked users that have not solve # the captcha in 1 hour (user ban just happen if a user try to join the group @@ -2980,55 +3865,99 @@ def th_time_to_kick_not_verify_users(bot): continue # The time has come for this user lang = get_chat_config(chat_id, "Language") - user_name = new_users[chat_id][user_id]["join_data"]["user_name"] - join_retries = new_users[chat_id][user_id]["join_data"]["join_retries"] - printts("[{}] Captcha reply timed out for user {}.".format(chat_id, user_name)) + user_name = new_users[chat_id][user_id]["join_data"][ + "user_name" + ] + join_retries = new_users[chat_id][user_id]["join_data"][ + "join_retries" + ] + printts( + "[{}] Captcha reply timed out for user {}.".format( + chat_id, user_name + ) + ) # Get current group remove kick/ban messages config rm_result_msg = get_chat_config(chat_id, "Rm_Result_Msg") # Check if this "user" has not join this chat more than 5 times (just kick) if join_retries < 5: - printts("[{}] Captcha not solved, kicking {} ({})...".format(chat_id, - user_name, user_id)) + printts( + "[{}] Captcha not solved, kicking {} ({})...".format( + chat_id, user_name, user_id + ) + ) # Try to kick the user - kick_result = tlg_kick_user(bot, chat_id, user_id) + kick_result = await tlg_kick_user(bot, chat_id, user_id) if kick_result["error"] == "": # Kick success msg_text = TEXT[lang]["NEW_USER_KICK"].format(user_name) # Increase join retries join_retries = join_retries + 1 - printts("[{}] Increased join_retries to {}".format(chat_id, join_retries)) + printts( + "[{}] Increased join_retries to {}".format( + chat_id, join_retries + ) + ) # Send kicked message if rm_result_msg: - tlg_send_selfdestruct_msg_in(bot, chat_id, msg_text, CONST["T_FAST_DEL_MSG"]) + await tlg_send_selfdestruct_msg_in( + bot, chat_id, msg_text, CONST["T_FAST_DEL_MSG"] + ) else: tlg_send_msg(bot, chat_id, msg_text) else: # Kick fail printts("[{}] Unable to kick".format(chat_id)) - if (kick_result["error"] == "The user has left the group") or \ - (kick_result["error"] == "The user was already kicked"): + if ( + kick_result["error"] + == "The user has left the group" + ) or ( + kick_result["error"] + == "The user was already kicked" + ): # The user is not in the chat - msg_text = TEXT[lang]["NEW_USER_KICK_NOT_IN_CHAT"].format(user_name) + msg_text = TEXT[lang][ + "NEW_USER_KICK_NOT_IN_CHAT" + ].format(user_name) if rm_result_msg: - tlg_send_selfdestruct_msg_in(bot, chat_id, msg_text, CONST["T_FAST_DEL_MSG"]) + tlg_send_selfdestruct_msg_in( + bot, + chat_id, + msg_text, + CONST["T_FAST_DEL_MSG"], + ) else: tlg_send_msg(bot, chat_id, msg_text) - elif kick_result["error"] == "Not enough rights to restrict/unrestrict chat member": + elif ( + kick_result["error"] + == "Not enough rights to restrict/unrestrict chat member" + ): # Bot has no privileges to kick - msg_text = TEXT[lang]["NEW_USER_KICK_NOT_RIGHTS"].format(user_name) + msg_text = TEXT[lang][ + "NEW_USER_KICK_NOT_RIGHTS" + ].format(user_name) # Send no rights for kick message without auto-remove tlg_send_msg(bot, chat_id, msg_text) else: # For other reason, the Bot can't ban - msg_text = TEXT[lang]["BOT_CANT_KICK"].format(user_name) + msg_text = TEXT[lang]["BOT_CANT_KICK"].format( + user_name + ) if rm_result_msg: - tlg_send_selfdestruct_msg_in(bot, chat_id, msg_text, CONST["T_FAST_DEL_MSG"]) + tlg_send_selfdestruct_msg_in( + bot, + chat_id, + msg_text, + CONST["T_FAST_DEL_MSG"], + ) else: tlg_send_msg(bot, chat_id, msg_text) # The user has join this chat 5 times and never succes to solve the captcha (ban) else: - printts("[{}] Captcha not solved, banning {} ({})...".format(chat_id, - user_name, user_id)) + printts( + "[{}] Captcha not solved, banning {} ({})...".format( + chat_id, user_name, user_id + ) + ) # Try to ban the user and notify Admins ban_result = tlg_ban_user(bot, chat_id, user_id) if ban_result["error"] == "": @@ -3038,13 +3967,22 @@ def th_time_to_kick_not_verify_users(bot): # Ban fail if ban_result["error"] == "User not found": # The user is not in the chat - msg_text = TEXT[lang]["NEW_USER_BAN_NOT_IN_CHAT"].format(user_name) - elif ban_result["error"] == "Not enough rights to restrict/unrestrict chat member": + msg_text = TEXT[lang][ + "NEW_USER_BAN_NOT_IN_CHAT" + ].format(user_name) + elif ( + ban_result["error"] + == "Not enough rights to restrict/unrestrict chat member" + ): # Bot has no privileges to ban - msg_text = TEXT[lang]["NEW_USER_BAN_NOT_RIGHTS"].format(user_name) + msg_text = TEXT[lang][ + "NEW_USER_BAN_NOT_RIGHTS" + ].format(user_name) else: # For other reason, the Bot can't ban - msg_text = TEXT[lang]["BOT_CANT_BAN"].format(user_name) + msg_text = TEXT[lang]["BOT_CANT_BAN"].format( + user_name + ) # Send ban notify message printts("[{}] {}".format(chat_id, msg_text)) if rm_result_msg: @@ -3053,9 +3991,15 @@ def th_time_to_kick_not_verify_users(bot): tlg_send_msg(bot, chat_id, msg_text) # Update user info (join_retries & kick_ban) new_users[chat_id][user_id]["join_data"]["kicked_ban"] = True - new_users[chat_id][user_id]["join_data"]["join_retries"] = join_retries + new_users[chat_id][user_id]["join_data"][ + "join_retries" + ] = join_retries # Remove join messages - printts("[{}] Removing messages from user {}...".format(chat_id, user_name)) + printts( + "[{}] Removing messages from user {}...".format( + chat_id, user_name + ) + ) join_msg = new_users[chat_id][user_id]["join_msg"] if join_msg is not None: tlg_delete_msg(bot, chat_id, join_msg) @@ -3074,11 +4018,13 @@ def th_time_to_kick_not_verify_users(bot): except Exception as e: printts("Error handling kick/ban:\n{}".format(str(e))) + ############################################################################### ### Telegram Errors Callback -def tlg_error_callback(update, context): - '''Telegram errors handler.''' + +async def tlg_error_callback(update, context): + """Telegram errors handler.""" try: raise context.error except Unauthorized: @@ -3092,11 +4038,13 @@ def tlg_error_callback(update, context): except TelegramError as e: printts("TLG Error: {}".format(str(e))) + ############################################################################### ### Main Function + def main(): - '''Main Function''' + """Main Function""" global updater global th_0 global th_1 @@ -3120,79 +4068,180 @@ def main(): # Set messages to be sent silently by default msgs_defaults = Defaults(disable_notification=True) # Create an event handler (updater) for a Bot with the given Token and get the dispatcher - updater = Updater(CONST["TOKEN"], workers=12, defaults=msgs_defaults) - dp = updater.dispatcher + + app = Application.builder().token(CONST["TOKEN"]).defaults(msgs_defaults).build() + + app # Set Telegram errors handler - dp.add_error_handler(tlg_error_callback) + app.add_error_handler(tlg_error_callback) # Set to dispatcher all expected commands messages handler - dp.add_handler(CommandHandler("start", cmd_start)) - dp.add_handler(CommandHandler("help", cmd_help)) - dp.add_handler(CommandHandler("commands", cmd_commands)) - dp.add_handler(CommandHandler("checkcfg", cmd_checkcfg)) - dp.add_handler(CommandHandler("connect", cmd_connect, pass_args=True)) - dp.add_handler(CommandHandler("disconnect", cmd_disconnect)) - dp.add_handler(CommandHandler("language", cmd_language, pass_args=True)) - dp.add_handler(CommandHandler("time", cmd_time, pass_args=True)) - dp.add_handler(CommandHandler("difficulty", cmd_difficulty, pass_args=True)) - dp.add_handler(CommandHandler("captcha_mode", cmd_captcha_mode, pass_args=True)) - dp.add_handler(CommandHandler("welcome_msg", cmd_welcome_msg, pass_args=True)) - dp.add_handler(CommandHandler("welcome_msg_time", cmd_welcome_msg_time, pass_args=True)) - dp.add_handler(CommandHandler("captcha_poll", cmd_captcha_poll, pass_args=True)) - dp.add_handler(CommandHandler("restrict_non_text", cmd_restrict_non_text, pass_args=True)) - dp.add_handler(CommandHandler("add_ignore", cmd_add_ignore, pass_args=True)) - dp.add_handler(CommandHandler("remove_ignore", cmd_remove_ignore, pass_args=True)) - dp.add_handler(CommandHandler("ignore_list", cmd_ignore_list)) - dp.add_handler(CommandHandler("remove_solve_kick_msg", cmd_remove_solve_kick_msg, pass_args=True)) - dp.add_handler(CommandHandler("remove_welcome_msg", cmd_remove_welcome_msg, pass_args=True)) - dp.add_handler(CommandHandler("remove_all_msg_kick_on", cmd_remove_all_msg_kick_on)) - dp.add_handler(CommandHandler("remove_all_msg_kick_off", cmd_remove_all_msg_kick_off)) - dp.add_handler(CommandHandler("url_enable", cmd_url_enable)) - dp.add_handler(CommandHandler("url_disable", cmd_url_disable)) - dp.add_handler(CommandHandler("enable", cmd_enable)) - dp.add_handler(CommandHandler("disable", cmd_disable)) - dp.add_handler(CommandHandler("chatid", cmd_chatid)) - dp.add_handler(CommandHandler("version", cmd_version)) - dp.add_handler(CommandHandler("about", cmd_about)) - if (CONST["BOT_OWNER"] != "XXXXXXXXX"): - dp.add_handler(CommandHandler("captcha", cmd_captcha)) - dp.add_handler(CommandHandler("allowuserlist", cmd_allowuserlist, pass_args=True)) + app.add_handler(CommandHandler("start", cmd_start)) + app.add_handler(CommandHandler("help", cmd_help)) + app.add_handler(CommandHandler("commands", cmd_commands)) + app.add_handler(CommandHandler("checkcfg", cmd_checkcfg)) + app.add_handler( + CommandHandler( + "connect", + cmd_connect, + ) + ) + app.add_handler(CommandHandler("disconnect", cmd_disconnect)) + app.add_handler( + CommandHandler( + "language", + cmd_language, + ) + ) + app.add_handler( + CommandHandler( + "time", + cmd_time, + ) + ) + app.add_handler( + CommandHandler( + "difficulty", + cmd_difficulty, + ) + ) + app.add_handler( + CommandHandler( + "captcha_mode", + cmd_captcha_mode, + ) + ) + app.add_handler( + CommandHandler( + "welcome_msg", + cmd_welcome_msg, + ) + ) + app.add_handler( + CommandHandler( + "welcome_msg_time", + cmd_welcome_msg_time, + ) + ) + app.add_handler( + CommandHandler( + "captcha_poll", + cmd_captcha_poll, + ) + ) + app.add_handler( + CommandHandler( + "restrict_non_text", + cmd_restrict_non_text, + ) + ) + app.add_handler( + CommandHandler( + "add_ignore", + cmd_add_ignore, + ) + ) + app.add_handler( + CommandHandler( + "remove_ignore", + cmd_remove_ignore, + ) + ) + app.add_handler(CommandHandler("ignore_list", cmd_ignore_list)) + app.add_handler( + CommandHandler( + "remove_solve_kick_msg", + cmd_remove_solve_kick_msg, + ) + ) + app.add_handler( + CommandHandler( + "remove_welcome_msg", + cmd_remove_welcome_msg, + ) + ) + app.add_handler( + CommandHandler("remove_all_msg_kick_on", cmd_remove_all_msg_kick_on) + ) + app.add_handler( + CommandHandler("remove_all_msg_kick_off", cmd_remove_all_msg_kick_off) + ) + app.add_handler(CommandHandler("url_enable", cmd_url_enable)) + app.add_handler(CommandHandler("url_disable", cmd_url_disable)) + app.add_handler(CommandHandler("enable", cmd_enable)) + app.add_handler(CommandHandler("disable", cmd_disable)) + app.add_handler(CommandHandler("chatid", cmd_chatid)) + app.add_handler(CommandHandler("version", cmd_version)) + app.add_handler(CommandHandler("about", cmd_about)) + if CONST["BOT_OWNER"] != "XXXXXXXXX": + app.add_handler(CommandHandler("captcha", cmd_captcha)) + app.add_handler( + CommandHandler( + "allowuserlist", + cmd_allowuserlist, + ) + ) if (CONST["BOT_OWNER"] != "XXXXXXXXX") and CONST["BOT_PRIVATE"]: - dp.add_handler(CommandHandler("allowgroup", cmd_allowgroup, pass_args=True)) + app.add_handler( + CommandHandler( + "allowgroup", + cmd_allowgroup, + ) + ) # Set to dispatcher a not-command text messages handler - dp.add_handler(MessageHandler(Filters.text, msg_nocmd, run_async=True)) + app.add_handler(MessageHandler(filters.TEXT, msg_nocmd, block=False)) # Set to dispatcher not text messages handler - dp.add_handler(MessageHandler(Filters.photo | Filters.audio | Filters.voice | - Filters.video | Filters.sticker | Filters.document | Filters.location | - Filters.contact, msg_notext)) + app.add_handler( + MessageHandler( + filters.PHOTO + | filters.AUDIO + | filters.VOICE + | filters.VIDEO + | filters.Sticker.ALL + | filters.Document.ALL + | filters.LOCATION + | filters.CONTACT, + msg_notext, + ) + ) # Set to dispatcher a new member join the group and member left the group events handlers - dp.add_handler(ChatMemberHandler(chat_bot_status_change, ChatMemberHandler.MY_CHAT_MEMBER)) - dp.add_handler(ChatMemberHandler(chat_member_status_change, ChatMemberHandler.ANY_CHAT_MEMBER)) + app.add_handler( + ChatMemberHandler(chat_bot_status_change, ChatMemberHandler.MY_CHAT_MEMBER) + ) + app.add_handler( + ChatMemberHandler(chat_member_status_change, ChatMemberHandler.ANY_CHAT_MEMBER) + ) # Set to dispatcher "USER joined the group" messages event handlers - dp.add_handler(MessageHandler(Filters.status_update.new_chat_members, msg_user_joined_group)) + app.add_handler( + MessageHandler(filters.StatusUpdate.NEW_CHAT_MEMBERS, msg_user_joined_group) + ) # Set to dispatcher inline keyboard callback handler for new captcha request and # button captcha challenge - dp.add_handler(CallbackQueryHandler(key_inline_keyboard)) + app.add_handler(CallbackQueryHandler(key_inline_keyboard)) # Set to dispatcher users poll vote handler - dp.add_handler(PollAnswerHandler(receive_poll_answer, run_async=True)) + app.add_handler(PollAnswerHandler(receive_poll_answer, block=False)) # Launch the Bot ignoring pending messages (clean=True) and get all updates (allowed_uptades=[]) if CONST["WEBHOOK_HOST"] == "None": printts("Setup Bot for Polling.") - updater.start_polling( - drop_pending_updates=True, - allowed_updates=Update.ALL_TYPES - ) + + app.run_polling(drop_pending_updates=True, allowed_updates=Update.ALL_TYPES) else: printts("Setup Bot for Webhook.") - updater.start_webhook( - drop_pending_updates=True, listen="0.0.0.0", port=CONST["WEBHOOK_PORT"], url_path=CONST["TOKEN"], - key=CONST["WEBHOOK_CERT_PRIV_KEY"], cert=CONST["WEBHOOK_CERT"], - webhook_url="https://{}:{}/{}".format(CONST["WEBHOOK_HOST"], CONST["WEBHOOK_PORT"], - CONST["TOKEN"]), allowed_updates=Update.ALL_TYPES + app.run_webhook( + drop_pending_updates=True, + listen="0.0.0.0", + port=CONST["WEBHOOK_PORT"], + url_path=CONST["TOKEN"], + key=CONST["WEBHOOK_CERT_PRIV_KEY"], + cert=CONST["WEBHOOK_CERT"], + webhook_url="https://{}:{}/{}".format( + CONST["WEBHOOK_HOST"], CONST["WEBHOOK_PORT"], CONST["TOKEN"] + ), + allowed_updates=Update.ALL_TYPES, ) printts("Bot setup completed. Bot is now running.") # Launch delete mesages and kick users threads - th_0 = Thread(target=th_selfdestruct_messages, args=(updater.bot,)) - th_1 = Thread(target=th_time_to_kick_not_verify_users, args=(updater.bot,)) + th_0 = Thread(target=th_selfdestruct_messages, args=(app.bot,)) + th_1 = Thread(target=th_time_to_kick_not_verify_users, args=(app.bot,)) th_0.name = "th_selfdestruct_messages" th_1.name = "th_time_to_kick_not_verify_users" th_0.start() @@ -3200,7 +4249,6 @@ def main(): th_1.start() # Set main thread to idle # Using Bot idle() catch external signals instead our signal handler - updater.idle() print("Bot Threads end") if os_system() == "Windows": kill(getpid(), SIGTERM) diff --git a/sources/tlgbotutils.py b/sources/tlgbotutils.py index bac841c..cf8d21c 100644 --- a/sources/tlgbotutils.py +++ b/sources/tlgbotutils.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -''' +""" Script: tlgbotutils.py Description: @@ -13,19 +13,20 @@ 21/08/2021 Version: 1.1.1 -''' +""" ############################################################################### ### Imported modules -from typing import Tuple, Optional +from typing import Tuple, Optional, Union -from telegram import ( - ChatPermissions, TelegramError, ParseMode, Poll, ChatMemberUpdated, - ChatMember -) +from telegram import ChatPermissions, Poll, ChatMemberUpdated, ChatMember, Bot -from telegram.utils.helpers import DEFAULT_NONE +from telegram.constants import ParseMode + +from telegram.error import TelegramError + +from telegram._utils.defaultvalue import DEFAULT_NONE from commons import printts @@ -37,52 +38,67 @@ ############################################################################### ### Functions -def tlg_get_chat(bot, chat_id_or_alias, timeout=None): - '''Telegram get chat data.''' + +async def tlg_get_chat(bot: Bot, chat_id_or_alias: Union[str, int], timeout=None): + """Telegram get chat data.""" chat_result = dict() chat_result["chat_data"] = None chat_result["error"] = "" try: - chat_result["chat_data"] = bot.get_chat(chat_id=chat_id_or_alias, - timeout=timeout) + chat_result["chat_data"] = await bot.get_chat( + chat_id=chat_id_or_alias, timeout=timeout + ) except Exception as e: chat_result["error"] = str(e) printts("[{}] {}".format(chat_id_or_alias, chat_result["error"])) return chat_result -def tlg_get_chat_member(bot, chat_id, user_id, timeout=None): - '''telegram Get Chat member info.''' +async def tlg_get_chat_member( + bot: Bot, chat_id: Union[str, int], user_id: Union[str, int], timeout=None +): + """telegram Get Chat member info.""" result = dict() result["member"] = None result["error"] = "" try: - result["member"] = bot.get_chat_member(chat_id=chat_id, - user_id=user_id, timeout=timeout) + result["member"] = await bot.get_chat_member( + chat_id=chat_id, user_id=user_id, timeout=timeout + ) except Exception as e: result["error"] = str(e) printts("[{}] {}".format(chat_id, result["error"])) return result -def tlg_get_chat_members_count(bot, chat_id, timeout=None): - '''telegram Get number of members in a Chat.''' +async def tlg_get_chat_members_count(bot: Bot, chat_id: Union[str, int], timeout=None): + """telegram Get number of members in a Chat.""" result = dict() result["num_members"] = None result["error"] = "" try: - result["num_members"] = bot.get_chat_members_count(chat_id=chat_id, - timeout=timeout) + result["num_members"] = await bot.get_chat_members_count( + chat_id=chat_id, timeout=timeout + ) except Exception as e: result["error"] = str(e) printts("[{}] {}".format(chat_id, result["error"])) return result -def tlg_send_msg(bot, chat_id, text, parse_mode=None, - disable_web_page_preview=None, disable_notification=True, - reply_to_message_id=None, reply_markup=None, timeout=None): - '''Bot try to send a text message''' +async def tlg_send_msg( + bot: Bot, + chat_id: Union[str, int], + text: str, + parse_mode: str = None, + disable_web_page_preview: bool = None, + disable_notification: bool = True, + reply_to_message_id: int = None, + message_thread_id: int = None, + reply_markup=None, + timeout: float = None, +): + """Bot try to send a text message""" sent_result = dict() sent_result["msg"] = None sent_result["error"] = "" @@ -91,127 +107,209 @@ def tlg_send_msg(bot, chat_id, text, parse_mode=None, elif parse_mode == "MARKDOWN": parse_mode = ParseMode.MARKDOWN_V2 try: - sent_result["msg"] = bot.send_message(chat_id=chat_id, text=text, - parse_mode=parse_mode, reply_markup=reply_markup, + sent_result["msg"] = await bot.send_message( + chat_id=chat_id, + text=text, + parse_mode=parse_mode, + reply_markup=reply_markup, disable_web_page_preview=disable_web_page_preview, disable_notification=disable_notification, - reply_to_message_id=reply_to_message_id, timeout=timeout) + reply_to_message_id=reply_to_message_id, + message_thread_id=message_thread_id, + read_timeout=timeout, + ) except TelegramError as e: sent_result["error"] = str(e) printts("[{}] {}".format(chat_id, sent_result["error"])) return sent_result -def tlg_send_image(bot, chat_id, photo, caption=None, - disable_notification=True, reply_to_message_id=None, - reply_markup=None, timeout=40, parse_mode=None): - '''Bot try to send an image message''' +async def tlg_send_image( + bot: Bot, + chat_id: Union[str, int], + photo, + caption: str = None, + disable_notification=True, + reply_to_message_id: int = None, + reply_markup=None, + timeout: float = 40, + parse_mode: str = None, +): + """Bot try to send an image message""" sent_result = dict() sent_result["msg"] = None sent_result["error"] = "" try: - sent_result["msg"] = bot.send_photo(chat_id=chat_id, photo=photo, - caption=caption, disable_notification=disable_notification, - reply_to_message_id=reply_to_message_id, reply_markup=reply_markup, - timeout=timeout, parse_mode=parse_mode) + sent_result["msg"] = await bot.send_photo( + chat_id=chat_id, + photo=photo, + caption=caption, + disable_notification=disable_notification, + reply_to_message_id=reply_to_message_id, + reply_markup=reply_markup, + timeout=timeout, + parse_mode=parse_mode, + ) except TelegramError as e: sent_result["error"] = str(e) printts("[{}] {}".format(chat_id, sent_result["error"])) return sent_result -def tlg_send_poll(bot, chat_id, question, options, correct_option_id=None, - open_period=None, is_anonymous=True, type=Poll.REGULAR, - explanation=None, allows_multiple_answers=False, - is_closed=None, disable_notification=True, - reply_to_message_id=None, reply_markup=None, - explanation_parse_mode=DEFAULT_NONE, close_date=None, - timeout=40, **kwargs): - '''Bot try to send a Poll message''' +async def tlg_send_poll( + bot: Bot, + chat_id: Union[str, int], + question, + options, + correct_option_id=None, + open_period=None, + is_anonymous=True, + type=Poll.REGULAR, + explanation=None, + allows_multiple_answers=False, + is_closed=None, + disable_notification=True, + reply_to_message_id=None, + reply_markup=None, + explanation_parse_mode=DEFAULT_NONE, + close_date=None, + timeout=40, + **kwargs +): + """Bot try to send a Poll message""" sent_result = dict() sent_result["msg"] = None sent_result["error"] = "" try: - sent_result["msg"] = bot.send_poll(chat_id=chat_id, question=question, - options=options, is_anonymous=is_anonymous, type=type, + sent_result["msg"] = await bot.send_poll( + chat_id=chat_id, + question=question, + options=options, + is_anonymous=is_anonymous, + type=type, allows_multiple_answers=allows_multiple_answers, - correct_option_id=correct_option_id, is_closed=is_closed, + correct_option_id=correct_option_id, + is_closed=is_closed, disable_notification=disable_notification, - reply_to_message_id=reply_to_message_id, reply_markup=reply_markup, - timeout=timeout, explanation=explanation, + reply_to_message_id=reply_to_message_id, + reply_markup=reply_markup, + timeout=timeout, + explanation=explanation, explanation_parse_mode=explanation_parse_mode, - open_period=open_period, close_date=close_date, **kwargs) + open_period=open_period, + close_date=close_date, + **kwargs + ) except TelegramError as e: sent_result["error"] = str(e) printts("[{}] {}".format(chat_id, sent_result["error"])) return sent_result -def tlg_stop_poll(bot, chat_id, message_id, reply_markup=None, - timeout=None, **kwargs): - '''Bot try to stop a Poll.''' +async def tlg_stop_poll( + bot, chat_id, message_id, reply_markup=None, timeout=None, **kwargs +): + """Bot try to stop a Poll.""" result = dict() result["msg"] = None result["error"] = "" try: - result["msg"] = bot.stop_poll(chat_id=chat_id, message_id=message_id, - reply_markup=reply_markup, timeout=timeout, **kwargs) + result["msg"] = await bot.stop_poll( + chat_id=chat_id, + message_id=message_id, + reply_markup=reply_markup, + timeout=timeout, + **kwargs + ) except Exception as e: - result["error"] = str(e) - printts("[{}] {}".format(chat_id, result["error"])) + result["error"] = str(e) + printts("[{}] {}".format(chat_id, result["error"])) return result -def tlg_delete_msg(bot, chat_id, msg_id, timeout=None): - '''Try to remove a telegram message''' +async def tlg_delete_msg( + bot: Bot, chat_id: Union[str, int], msg_id: int, timeout: float = None +): + """Try to remove a telegram message""" delete_result = dict() delete_result["error"] = "" if msg_id is not None: try: - bot.delete_message(chat_id=chat_id, message_id=msg_id, - timeout=timeout) + await bot.delete_message( + chat_id=chat_id, message_id=msg_id, timeout=timeout + ) except Exception as e: delete_result["error"] = str(e) printts("[{}] {}".format(chat_id, delete_result["error"])) return delete_result -def tlg_edit_msg_media(bot, chat_id, msg_id, inline_msg_id=None, - media=None, reply_markup=None, timeout=None): - '''Try to edit a telegram multimedia message''' +async def tlg_edit_msg_media( + bot: Bot, + chat_id: Union[str, int], + msg_id: int, + inline_msg_id: str = None, + media=None, + reply_markup=None, + timeout: float = None, +): + """Try to edit a telegram multimedia message""" edit_result = dict() edit_result["error"] = "" try: - bot.edit_message_media(chat_id=chat_id, message_id=msg_id, - inline_message_id=inline_msg_id, media=media, - reply_markup=reply_markup, timeout=timeout) + await bot.edit_message_media( + chat_id=chat_id, + message_id=msg_id, + inline_message_id=inline_msg_id, + media=media, + reply_markup=reply_markup, + timeout=timeout, + ) except Exception as e: edit_result["error"] = str(e) printts("[{}] {}".format(chat_id, edit_result["error"])) return edit_result -def tlg_answer_callback_query(bot, query, text=None, show_alert=False, - url=None, cache_time=None, timeout=None): - '''Try to send a telegram callback query answer''' +async def tlg_answer_callback_query( + bot: Bot, + query, + text: str = None, + show_alert=False, + url: str = None, + cache_time: int = None, + timeout: float = None, +): + """Try to send a telegram callback query answer""" query_ans_result = dict() query_ans_result["error"] = "" try: - bot.answer_callback_query(callback_query_id=query.id, text=text, - show_alert=show_alert, url=url, cache_time=cache_time, - timeout=timeout) + await bot.answer_callback_query( + callback_query_id=query.id, + text=text, + show_alert=show_alert, + url=url, + cache_time=cache_time, + timeout=timeout, + ) except Exception as e: query_ans_result["error"] = str(e) printts("[{}] {}".format(query.message.chat_id, str(e))) return query_ans_result -def tlg_ban_user(bot, chat_id, user_id, timeout=None, until_date=None): - '''Telegram Ban a user of an specified chat''' +async def tlg_ban_user( + bot: Bot, + chat_id: Union[str, int], + user_id: Union[str, int], + timeout=None, + until_date=None, +): + """Telegram Ban a user of an specified chat""" ban_result = dict() ban_result["error"] = "" # Get chat member info - member_info_result = tlg_get_chat_member(bot, chat_id, user_id) + member_info_result = await tlg_get_chat_member(bot, chat_id, user_id) if member_info_result["member"] is None: ban_result["error"] = member_info_result["error"] return ban_result @@ -224,74 +322,100 @@ def tlg_ban_user(bot, chat_id, user_id, timeout=None, until_date=None): return ban_result # Kick the user try: - bot.kick_chat_member(chat_id=chat_id, user_id=user_id, - timeout=timeout, until_date=until_date) + await bot.kick_chat_member( + chat_id=chat_id, user_id=user_id, timeout=timeout, until_date=until_date + ) except Exception as e: ban_result["error"] = str(e) printts("[{}] {}".format(chat_id, ban_result["error"])) return ban_result -def tlg_kick_user(bot, chat_id, user_id, timeout=None): - '''Telegram Kick a user of an specified chat''' +async def tlg_kick_user( + bot: Bot, chat_id: Union[str, int], user_id: Union[str, int], timeout: float = None +): + """Telegram Kick a user of an specified chat""" kick_result = dict() kick_result["error"] = "" # Ban the user (telegram doesn't have a kick method, so we need first # to ban and then remove ban restrictions of the user) - kick_result = tlg_ban_user(bot, chat_id, user_id) + kick_result = await tlg_ban_user(bot, chat_id, user_id) # If user was already kicked by another Admin, keep restrictions - if (kick_result["error"] == "The user has left the group"): + if kick_result["error"] == "The user has left the group": return kick_result - if (kick_result["error"] == "The user was already kicked"): + if kick_result["error"] == "The user was already kicked": return kick_result # Remove restrictions try: - bot.unban_chat_member(chat_id=chat_id, user_id=user_id, - timeout=timeout) + await bot.unban_chat_member(chat_id=chat_id, user_id=user_id, timeout=timeout) except Exception as e: printts("[{}] {}".format(chat_id, str(e))) return kick_result -def tlg_leave_chat(bot, chat_id, timeout=None): - '''Telegram Bot try to leave a chat.''' +async def tlg_leave_chat(bot: Bot, chat_id: Union[str, int], timeout: float = None): + """Telegram Bot try to leave a chat.""" left = False try: - if bot.leave_chat(chat_id=chat_id, timeout=timeout): + if await bot.leave_chat(chat_id=chat_id, timeout=timeout): left = True except Exception as e: printts("[{}] {}".format(chat_id, str(e))) return left -def tlg_restrict_user(bot, chat_id, user_id, until_date=None, timeout=None, - send_msg=None, send_media=None, send_stickers_gifs=None, - insert_links=None, send_polls=None, invite_members=None, - pin_messages=None, change_group_info=None): - '''Telegram Bot try to restrict user permissions in a group.''' +async def tlg_restrict_user( + bot: Bot, + chat_id: Union[str, int], + user_id: Union[str, int], + until_date=None, + timeout=None, + send_msg=None, + send_media=None, + send_stickers_gifs=None, + insert_links=None, + send_polls=None, + invite_members=None, + pin_messages=None, + change_group_info=None, +): + """Telegram Bot try to restrict user permissions in a group.""" result = False try: - permissions = ChatPermissions(send_msg, send_media, send_polls, - send_stickers_gifs, insert_links, change_group_info, - invite_members, pin_messages) - result = bot.restrict_chat_member(chat_id=chat_id, user_id=user_id, - permissions=permissions, until_date=until_date, timeout=timeout) + permissions = ChatPermissions( + send_msg, + send_media, + send_polls, + send_stickers_gifs, + insert_links, + change_group_info, + invite_members, + pin_messages, + ) + result = await bot.restrict_chat_member( + chat_id=chat_id, + user_id=user_id, + permissions=permissions, + until_date=until_date, + timeout=timeout, + ) except Exception as e: printts("[{}] {}".format(chat_id, str(e))) result = False return result -def tlg_user_is_admin(bot, user_id, chat_id, timeout=None): - '''Check if the specified user is an Administrator of a group given - by IDs''' +async def tlg_user_is_admin( + bot: Bot, user_id: Union[str, int], chat_id: Union[str, int], timeout=None +): + """Check if the specified user is an Administrator of a group given + by IDs""" # Check if it is an Admin with anonymous config enabled if user_id == ANONYMOUS_ADMIN_ID: return True # Get group Admins try: - group_admins = bot.get_chat_administrators(chat_id=chat_id, - timeout=timeout) + group_admins = await bot.get_chat_administrators(chat_id=chat_id, timeout=timeout) except Exception as e: printts("[{}] {}".format(chat_id, str(e))) return None @@ -302,20 +426,20 @@ def tlg_user_is_admin(bot, user_id, chat_id, timeout=None): return False -def tlg_get_chat_type(bot, chat_id_or_alias, timeout=None): - '''Telegram check if a chat exists and what type it is (user, group, ''' - '''channel).''' +async def tlg_get_chat_type(bot, chat_id_or_alias, timeout=None): + """Telegram check if a chat exists and what type it is (user, group,""" + """channel).""" chat_type = None - chat_result = tlg_get_chat(bot, chat_id_or_alias, timeout) + chat_result = await tlg_get_chat(bot, chat_id_or_alias, timeout) if chat_result["chat_data"] is not None: chat_type = getattr(chat_result["chat_data"], "type", None) return chat_type def tlg_is_valid_user_id_or_alias(user_id_alias): - '''Check if given telegram ID or alias has a valid expected format.''' + """Check if given telegram ID or alias has a valid expected format.""" # Check if it is a valid alias (start with @ and have 5 characters or more) - if user_id_alias[0] == '@': + if user_id_alias[0] == "@": if len(user_id_alias) > 5: return True # Check if it is a valid ID (is a number larger than 0) @@ -329,9 +453,9 @@ def tlg_is_valid_user_id_or_alias(user_id_alias): def tlg_is_valid_group(group): - '''Check if given telegram Group ID has a valid expected format.''' + """Check if given telegram Group ID has a valid expected format.""" # Check if it start with '-' - if group[0] != '-': + if group[0] != "-": return False # Check if it is a valid ID (is a number larger than 0) try: @@ -344,9 +468,9 @@ def tlg_is_valid_group(group): def tlg_alias_in_string(str_text): - ''' Check if a string contains an alias.''' + """Check if a string contains an alias.""" for word in str_text.split(): - if (len(word) > 1) and (word[0] == '@'): + if (len(word) > 1) and (word[0] == "@"): return True return False @@ -354,32 +478,24 @@ def tlg_alias_in_string(str_text): def tlg_extract_members_status_change( chat_member_update: ChatMemberUpdated, ) -> Optional[Tuple[bool, bool]]: - '''Takes a ChatMemberUpdated instance and extracts whether the + """Takes a ChatMemberUpdated instance and extracts whether the "old_chat_member" was a member of the chat and whether the "new_chat_member" is a member of the chat. Returns None, if the status - didn't change.''' + didn't change.""" members_diff = chat_member_update.difference() status_change = members_diff.get("status") if status_change is None: return None old_status, new_status = status_change old_is_member, new_is_member = members_diff.get("is_member", (None, None)) - was_member = ( - old_status - in [ - ChatMember.MEMBER, - ChatMember.CREATOR, - ChatMember.ADMINISTRATOR, - ] - or (old_status == ChatMember.RESTRICTED and old_is_member is True) - ) - is_member = ( - new_status - in [ - ChatMember.MEMBER, - ChatMember.CREATOR, - ChatMember.ADMINISTRATOR, - ] - or (new_status == ChatMember.RESTRICTED and new_is_member is True) - ) + was_member = old_status in [ + ChatMember.MEMBER, + ChatMember.CREATOR, + ChatMember.ADMINISTRATOR, + ] or (old_status == ChatMember.RESTRICTED and old_is_member is True) + is_member = new_status in [ + ChatMember.MEMBER, + ChatMember.CREATOR, + ChatMember.ADMINISTRATOR, + ] or (new_status == ChatMember.RESTRICTED and new_is_member is True) return was_member, is_member