def connect(): module_id = ModulesLoader.get_module_id("welcome_message") bot_manager = ModulesLoader.load_manager("bot") dispatcher = bot_manager.updater.dispatcher dispatcher.add_handler(MessageHandler( Filters.status_update.new_chat_members, welcome_message_handler), group=module_id)
def connect(): # Connect modules logging.info("Connecting modules...") modules = os.listdir("modules") modules.remove("pytg") for module_name in modules: logging.info("Dynamically connecting module {}...".format(module_name)) ModulesLoader.connect_module(module_name)
def animation_message_handler(update, context): bot = context.bot message = update.message if not message or not message.chat or not message.animation: return chat_id = message.chat.id message_id = message.message_id username = message.from_user.username user_id = message.from_user.id logging.info( "Received animation message update from {} ({}) in chat {}".format( username, user_id, chat_id)) data_manager = ModulesLoader.load_manager("data") # Check if the bot is waiting for a form input if data_manager.has_data("forms", chat_id, module="forms"): current_user_form_id = chat_id forms_manager = ModulesLoader.load_manager("forms") form_data = data_manager.load_data("forms", current_user_form_id, module="forms") if form_data["digested"]: return form_name = form_data["form_name"] form_steps = forms_manager.load_form_steps(form_name) step_data = form_steps[form_data["current_step"]] if step_data["type"] != "animation_field": return animation = message.animation animation_id = animation.file_id animation_url = bot.getFile(animation_id).file_path input_data = { "animation_id": animation_id, "animation_url": animation_url } forms_manager = ModulesLoader.load_manager("forms") forms_manager.handle_input(bot, chat_id, message_id, form_name, form_data["current_step"], input_data)
def initialize(): logging.info("Initializing pytg module...") # Initialize modules logging.info("Dynamically loading all modules...") modules = os.listdir("modules") modules.remove("pytg") for module_name in modules: logging.info("Dynamically loading module {}...".format(module_name)) ModulesLoader.initialize_module(module_name)
def load_form_steps(self, form_name, lang=None): module_folder = ModulesLoader.get_module_content_folder("forms") if not lang: config_manager = ModulesLoader.load_manager("config") lang_settings = config_manager.load_settings_file("forms", "lang") lang = lang_settings["default"] return yaml.safe_load( open("{}/formats/{}/{}/steps.yaml".format(module_folder, lang, form_name), "r", encoding="utf8"))
def start_form(self, bot, chat_id, form_name, form_meta={}): logging.info("Starting form {} for {}".format(form_name, chat_id)) data_manager = ModulesLoader.load_manager("data") # Update user data self.clear_user_form_data(bot, chat_id) form_id = chat_id data_manager.create_data("forms", form_id, module="forms") # Show form to user steps = self.load_form_steps(form_name) first_step = steps["meta"]["first_step"] form_data = data_manager.load_data("forms", form_id, module="forms") form_data["form_name"] = form_name form_data["current_step"] = first_step form_data["form_meta"] = form_meta # Set default entries if "default_entries" in steps["meta"].keys(): default_entries = steps["meta"]["default_entries"] for entry in default_entries.keys(): form_data["form_entries"][entry] = default_entries[entry] data_manager.save_data("forms", form_id, form_data, module="forms") self.show_current_step(bot, chat_id)
def set_next_step(self, bot, chat_id, message_id, next_step=None): data_manager = ModulesLoader.load_manager("data") form_id = chat_id form_data = data_manager.load_data("forms", form_id, module="forms") form_name = form_data["form_name"] form_steps = self.load_form_steps(form_name) current_step_data = form_steps[form_data["current_step"]] # Delete previous message if 'clear' is true if "clear" in current_step_data and current_step_data["clear"]: bot.deleteMessage(chat_id=chat_id, message_id=message_id) if next_step == None: next_step = current_step_data["next_step"] logging.info("Showing next step to {} ({} {})".format( chat_id, form_id, next_step)) if next_step and next_step != "None": if next_step == "_RESET": self.clear_user_form_data(bot, chat_id, False) return form_data["current_step"] = next_step data_manager.save_data("forms", form_id, form_data, module="forms") self.show_current_step(bot, chat_id) else: if "void" in form_steps["meta"] and form_steps["meta"]["void"]: return self.digest_form(bot, chat_id, form_id)
def connect(): logging.info("Connecting forms module...") bot_manager = ModulesLoader.load_manager("bot") load_callback_handlers(bot_manager.updater.dispatcher) load_messages_handlers(bot_manager.updater.dispatcher)
def blacklist_message_handler(update, context): bot = context.bot message = update.message chat_id = message.chat.id user_id = message.from_user.id username = message.from_user.username text = message.text tags_manager = ModulesLoader.load_manager("tags") if username: if tags_manager.has_tag(username, "no_words_filter", "users"): return config_manager = ModulesLoader.load_manager("config") blacklist_settings = config_manager.load_settings_file( "message_filter", "blacklist") text_lower = text.lower() for kickable_word in blacklist_settings["kickable_words"]: if kickable_word in text_lower: logger.info( "Deleting message '{}' from chat {} as it contains the kickable word '{}'" .format(text, chat_id, kickable_word)) bot.deleteMessage(chat_id=chat_id, message_id=message.message_id) bannable_words = 0 for bannable_word in blacklist_settings["bannable_words"]: if bannable_word in text_lower: bannable_words += 1 if bannable_words == 3: logger.info( "Deleting message '{}' from chat {} and kicking its author as it contains enough bannable words" .format(text, chat_id)) bot.deleteMessage(chat_id=chat_id, message_id=message.message_id) bot.kickChatMember(chat_id=chat_id, user_id=user_id)
def save_settings_file(self, settings, module="config", file_name="settings"): module_folder = ModulesLoader.get_module_content_folder(module) yaml.safe_dump( settings, open("{}/config/{}.yaml".format(module_folder, file_name), "w"))
def save_data(self, table, object_id, data, module="data"): module_folder = ModulesLoader.get_module_content_folder(module) yaml.safe_dump( data, open("{}/data/{}/{}.yaml".format(module_folder, table, object_id), "w")) return data
def __init__(self): config_manager = ModulesLoader.load_manager("config") settings = config_manager.load_settings_file("bot", "token") self.bot = telegram.Bot(settings["token"]) self.updater = Updater(settings["token"], use_context=True) return
def download_media(self, media_id, media_url, folder="uncategorized"): module_folder = ModulesLoader.get_module_content_folder("cache") media_data = urllib.request.urlopen(media_url).read() with open("{}/{}/{}".format(module_folder, folder, media_id), "wb") as media_file: media_file.write(media_data) return media_data
def load_message_handlers(dispatcher): module_id = ModulesLoader.get_module_id("message_filter") dispatcher.add_handler(MessageHandler(Filters.text, blacklist_message_handler), group=module_id) dispatcher.add_handler(MessageHandler( Filters.text & (Filters.entity(MessageEntity.URL) | Filters.entity(MessageEntity.TEXT_LINK)), links_block_message_handler), group=module_id + 1)
def start_cmd_handler(update, context): bot = context.bot message = update.message chat_id = message.chat.id username = message.from_user.username user_id = message.from_user.id logging.info("Received start command update from {} ({}) in chat {}".format(username, user_id, chat_id)) forms_manager = ModulesLoader.load_manager("forms") forms_manager.start_form(bot,chat_id,"send_photo")
def main(): logging.basicConfig( format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.INFO, # filename="pytg-bot.log" ) logging.info(" ### Launching Bot... ### ") logging.info(str(datetime.datetime.now())) parser = argparse.ArgumentParser(description='PyTG command line launcher') parser.add_argument("--main-module") args = parser.parse_args() main_module = args.main_module if not main_module: logging.error("No main module specified") return # Initialize PyTG module (initializes all modules) ModulesLoader.initialize_module("pytg") # Connect PyTG module (connects all modules) ModulesLoader.connect_module("pytg") # Launch main module ModulesLoader.launch_main_module(main_module)
def links_block_message_handler(update, context): bot = context.bot message = update.message chat_id = message.chat.id username = message.from_user.username config_manager = ModulesLoader.load_manager("config") settings = config_manager.load_settings_file("message_filter", "links_block") if not settings["block_links"]: return chat_admins = message.chat.get_administrators() if settings["admins_authorized"]: for chat_admin in chat_admins: if message.from_user.id == chat_admin.user.id: logging.info("User is an administrator of the chat") return logger.info("Detected message with link") tags_manager = ModulesLoader.load_manager("tags") if username: if tags_manager.has_tag(username, "can_post_links", "users"): logger.info("User is allowed to post links") return logger.info("Deleting message with link") bot.deleteMessage( chat_id = chat_id, message_id = message.message_id )
def find_entry_by_field(self, table, field, value, module="data"): module_folder = ModulesLoader.get_module_content_folder(module) files = os.listdir("{}/data/{}".format(module_folder, table)) files.remove("__default.yaml") for f in files: entry_id = f.replace(".yaml", "") data = self.load_data(table, entry_id) if data[field] == value: return data return None
def load_messages_handlers(dispatcher): module_id = ModulesLoader.get_module_id("forms") dispatcher.add_handler(MessageHandler(Filters.text & ~Filters.command, text_message_handler), group=module_id) dispatcher.add_handler(MessageHandler(Filters.photo, photo_message_handler), group=module_id) dispatcher.add_handler(MessageHandler(Filters.video, video_message_handler), group=module_id) dispatcher.add_handler(MessageHandler(Filters.animation, animation_message_handler), group=module_id)
def load_table_entries(self, table, module="data"): module_folder = ModulesLoader.get_module_content_folder(module) entries = [] files = os.listdir("{}/data/{}".format(module_folder, table)) files.remove("__default.yaml") for f in files: entry_id = f.replace(".yaml", "") data = self.load_data(table, entry_id) entries.append(data) return entries
def digest_form(self, bot, chat_id, form_id): logging.info("Digesting form for {}".format(chat_id)) data_manager = ModulesLoader.load_manager("data") # Load form's data form_data = data_manager.load_data("forms", form_id, module="forms") form_name = form_data["form_name"] # Update digestion flag form_data["digested"] = True data_manager.save_data("forms", form_id, form_data, module="forms") # Digest the form digester = self.digesters[form_name] digester(bot, chat_id, form_data["form_entries"], form_data["form_meta"])
def clear_user_form_data(self, bot, chat_id, delete_messages=True): logging.info("Clearing form data for user {}".format(chat_id)) data_manager = ModulesLoader.load_manager("data") form_id = chat_id if not data_manager.has_data("forms", form_id, module="forms"): return form_data = data_manager.load_data("forms", form_id, module="forms") # Clear form messages if delete_messages: # (not form_data["digested"]) and delete_messages: for form_message_id in form_data["messages"]: try: bot.deleteMessage(chat_id=chat_id, message_id=form_message_id) except: logging.info( "Unable to delete message {}".format(form_message_id)) # Update form data data_manager.delete_data("forms", form_id, module="forms")
def save_cache(self, cache): module_folder = ModulesLoader.get_module_content_folder("cache") yaml.safe_dump(cache, open("{}/cache.yaml".format(module_folder), "w"))
def load_cache(self): module_folder = ModulesLoader.get_module_content_folder("cache") return yaml.safe_load(open("{}/cache.yaml".format(module_folder), "r"))
def connect(): bot_manager = ModulesLoader.load_manager("bot") dispatcher = bot_manager.updater.dispatcher load_message_handlers(dispatcher)
def forms_callback_handler(update, context): bot = context.bot forms_manager = ModulesLoader.load_manager("forms") query = update.callback_query query_data = query.data.split(",") user_id = query.from_user.id username = query.message.chat.username chat_id = query.message.chat_id message_id = query.message.message_id logging.info("Handling forms callback data from {}: {}".format(chat_id, query_data)) if query_data[1] == "fixed_reply": data_manager = ModulesLoader.load_manager("data") form_data = data_manager.load_data("forms", chat_id, module="forms") step_name = form_data["current_step"] form_name = form_data["form_name"] # step_name = query_data[2] # form_name = query_data[3] input_data = { "action": query_data[2], "output_data": query_data[3] } bot.editMessageReplyMarkup( chat_id = chat_id, message_id = message_id, reply_markup = None ) forms_manager.handle_input(bot, chat_id, message_id, form_name, step_name, input_data) return if query_data[1] == "jump": next_step_name = query_data[2] bot.editMessageReplyMarkup( chat_id = chat_id, message_id = message_id, reply_markup = None ) forms_manager.set_next_step(bot, chat_id, message_id, next_step=next_step_name) return if query_data[1] == "show": form_name = query_data[2] forms_manager.start_form(bot, chat_id, form_name) if query_data[1] == "checkbox_click": entry = query_data[2] step_output = query_data[3] form_id = chat_id data_manager = ModulesLoader.load_manager("data") form_data = data_manager.load_data("forms", form_id, module="forms") if entry in form_data["form_entries"][step_output]: form_data["form_entries"][step_output].remove(entry) else: form_data["form_entries"][step_output].append(entry) data_manager.save_data("forms", form_id, form_data, module="forms") forms_manager.show_current_step(bot, chat_id, message_id)
def load_media(self, media_id, folder="uncategorized"): module_folder = ModulesLoader.get_module_content_folder("cache") return open("{}/{}/{}".format(module_folder, folder, media_id), "rb")
def handle_input(self, bot, chat_id, message_id, form_name, step_name, input_data): logging.info( "Handling input of {} on form {} (step name = {}, input data = {})" .format(chat_id, form_name, step_name, input_data)) data_manager = ModulesLoader.load_manager("data") next_step_name = None # Check if it's an action input if "action" in input_data.keys() and len(input_data["action"]) > 0: actions = input_data["action"].split(";") if actions[0] == "jump": next_step_name = actions[1] form_steps = self.load_form_steps(form_name) step_data = form_steps[step_name] if "output" in step_data.keys(): if step_data["type"] == "text_field": step_output = input_data["text"] elif step_data["type"] == "fixed_reply": step_output = input_data["output_data"] elif step_data["type"] == "keyboard_reply": step_output = input_data["value"] elif step_data["type"] == "image_field": step_output = input_data["image_id"] if step_data["save_in_cache"]: cache_manager = ModulesLoader.load_manager("cache") cache_manager.download_image(input_data["image_id"], input_data["image_url"]) elif step_data["type"] == "video_field": step_output = input_data["video_id"] if step_data["save_in_cache"]: cache_manager = ModulesLoader.load_manager("cache") cache_manager.download_video(input_data["video_id"], input_data["video_url"]) elif step_data["type"] == "animation_field": step_output = input_data["animation_id"] if step_data["save_in_cache"]: cache_manager = ModulesLoader.load_manager("cache") cache_manager.download_animation( input_data["animation_id"], input_data["animation_url"]) current_user_form_id = chat_id form_data = data_manager.load_data("forms", current_user_form_id, module="forms") form_data["form_entries"][step_data["output"]] = step_output data_manager.save_data("forms", current_user_form_id, form_data, module="forms") self.set_next_step(bot, chat_id, message_id, next_step=next_step_name)
def show_current_step(self, bot, chat_id, message_id=None): data_manager = ModulesLoader.load_manager("data") form_id = chat_id form_data = data_manager.load_data("forms", form_id, module="forms") form_name = form_data["form_name"] form_steps = self.load_form_steps(form_name) step_name = form_data["current_step"] current_step_data = form_steps[step_name] if "externs" in current_step_data.keys(): externs = current_step_data["externs"] for extern_key in externs.keys(): extern_data = form_data["form_meta"][externs[extern_key]] current_step_data[extern_key] = extern_data reply_markup = None next_step = "__NULL" # Message if current_step_data["type"] == "message": step_text = current_step_data["text"] if "format" in current_step_data.keys( ) and current_step_data["format"]: form_entries = form_data["form_entries"] step_text = self.format_step_text(step_text, form_entries) # Check if the step requires a back button if "previous_step" in current_step_data.keys(): menu_layout = [] self.append_jump_button(menu_layout, "Back", current_step_data["previous_step"]) reply_markup = InlineKeyboardMarkup(menu_layout) next_step = current_step_data["next_step"] # Text or image field if (current_step_data["type"] == "text_field" or current_step_data["type"] == "image_field" or current_step_data["type"] == "video_field" or current_step_data["type"] == "animation_field"): step_text = current_step_data["text"] if "format" in current_step_data.keys( ) and current_step_data["format"]: form_entries = form_data["form_entries"] step_text = self.format_step_text(step_text, form_entries) reply_markup = None # Check if the step requires a back button if "previous_step" in current_step_data.keys(): menu_layout = [] self.append_jump_button(menu_layout, "Back", current_step_data["previous_step"]) reply_markup = InlineKeyboardMarkup(menu_layout) # Fixed reply if current_step_data["type"] == "fixed_reply": # Format step text (if necessary) step_text = current_step_data["text"] if "format" in current_step_data.keys( ) and current_step_data["format"]: form_entries = form_data["form_entries"] step_text = self.format_step_text(step_text, form_entries) options = current_step_data["options"] reply_markup = self.fixed_reply_reply_markup( options, form_data, current_step_data) # Keyboard reply if current_step_data["type"] == "keyboard_reply": # Format step text (if necessary) step_text = current_step_data["text"] if "format" in current_step_data.keys( ) and current_step_data["format"]: form_entries = form_data["form_entries"] step_text = self.format_step_text(step_text, form_entries) options = current_step_data["options"] reply_markup = self.keyboard_reply_reply_markup( options, form_data, current_step_data) # Checkbox list if current_step_data["type"] == "checkbox_list": step_text = current_step_data["text"] step_output = current_step_data["output"] if "format" in current_step_data.keys( ) and current_step_data["format"]: form_entries = form_data["form_entries"] step_text = self.format_step_text(step_text, form_entries) if step_output not in form_data["form_entries"].keys(): form_data["form_entries"][step_output] = [] data_manager.save_data("forms", form_id, form_data, module="forms") entries = current_step_data["entries"] reply_markup = self.checkbox_list_reply_markup( entries, form_data, current_step_data) # Replace macros in meta for meta in form_data["form_meta"].keys(): macro = "[{}]".format(meta) step_text = step_text.replace(macro, str(form_data["form_meta"][meta])) # Disable web page preview option disable_web_page_preview = True if "disable_web_page_preview" in current_step_data.keys( ) and current_step_data["disable_web_page_preview"]: disable_web_page_preview = current_step_data[ "disable_web_page_preview"] # Send or edit message if message_id: bot.editMessageText( chat_id=chat_id, message_id=message_id, text=step_text, parse_mode=telegram.ParseMode.MARKDOWN, reply_markup=reply_markup, disable_web_page_preview=disable_web_page_preview) else: # Load media data (if any) media_data = None if "media" in current_step_data.keys( ) and current_step_data["media"]: media_data = current_step_data["media"] for meta in form_data["form_meta"].keys(): macro = "[{}]".format(meta) media_data["type"] = media_data["type"].replace( macro, str(form_data["form_meta"][meta])) media_data["path"] = media_data["path"].replace( macro, str(form_data["form_meta"][meta])) if "format" in current_step_data.keys( ) and current_step_data["format"]: for key in form_data["form_entries"].keys(): macro = "[{}]".format(key) media_data["type"] = media_data["type"].replace( macro, str(form_data["form_entries"][key])) media_data["path"] = media_data["path"].replace( macro, str(form_data["form_entries"][key])) # Send complete message if not media_data: sent_message = bot.sendMessage( chat_id=chat_id, text=step_text, parse_mode=telegram.ParseMode.MARKDOWN, reply_markup=reply_markup, disable_web_page_preview=disable_web_page_preview) else: if media_data["type"] == "photo": sent_message = bot.sendPhoto( chat_id=chat_id, caption=step_text, photo=media_data["path"], parse_mode=telegram.ParseMode.MARKDOWN, reply_markup=reply_markup, disable_web_page_preview=disable_web_page_preview) elif media_data["type"] == "video": sent_message = bot.sendVideo( chat_id=chat_id, caption=step_text, video=media_data["path"], parse_mode=telegram.ParseMode.MARKDOWN, reply_markup=reply_markup, disable_web_page_preview=disable_web_page_preview) else: logging.exception("Unknown media type") # Add new message IDs if data_manager.has_data("forms", form_id): form_data = data_manager.load_data("forms", form_id, module="forms") form_data["messages"].append(sent_message.message_id) data_manager.save_data("forms", form_id, form_data, module="forms") if next_step is not "__NULL": self.set_next_step(bot, chat_id, sent_message.message_id, next_step=next_step)
def text_message_handler(update, context): bot = context.bot message = update.message if not message or not message.chat: return chat_id = message.chat.id message_id = message.message_id username = message.from_user.username user_id = message.from_user.id text = message.text logging.info( "Received text message update from {} ({}) in chat {}: {}".format( username, user_id, chat_id, text)) data_manager = ModulesLoader.load_manager("data") # Check if the bot is waiting for a form input if data_manager.has_data("forms", chat_id, module="forms"): current_user_form_id = chat_id forms_manager = ModulesLoader.load_manager("forms") form_data = data_manager.load_data("forms", current_user_form_id, module="forms") if form_data["digested"]: return form_name = form_data["form_name"] form_steps = forms_manager.load_form_steps(form_name) step_data = form_steps[form_data["current_step"]] if step_data["type"] == "text_field": input_data = {"text": text} elif step_data["type"] == "keyboard_reply": replies_map = step_data["map"] if text not in replies_map.keys(): return input_data = {"value": replies_map[text]} # elif step_data["type"] == "image_field": # if not message.photo: # return # photos = message.photo # input_data = { # "image_url": None # } else: return forms_manager.handle_input(bot, chat_id, message_id, form_name, form_data["current_step"], input_data)