def remove_client(self, client: pyrogram.Client): if self._active_handlers.__contains__(client): for handler_ref in self._active_handlers[client]: client.remove_handler(handler_ref) if not client in self._clients: self._clients.append(client) if is_bot(client): if not client in self._bots: self._bots.append(client) else: if not client in self._users: self._users.append(client)
print(f'id: {update.message_id}') print(f'msg: {update.text}') print(f'is_channel: {is_channel}') print(f'reply_to_message_id: {reply_to_message_id}') print('-----------------') # @tlg.on_deleted_messages() async def onDelete(client, update): print('DELETED') deleted_ids = [] chat_id = update[0].chat.id if update[0].chat else None for message in update: deleted_ids.append(message.message_id) print('deleted ids:') print(deleted_ids) print(f'chat_id: {chat_id}') print(f'is_channel: {chat_id is not None}') print('-----------------') try: print(f'listening to number: {c.phone}') handler = tlg.add_handler(MessageHandler(my_handler)) tlg.start() # my_start() asyncio.get_event_loop().run_until_complete(main()) except KeyboardInterrupt: tlg.remove_handler(*handler) print('\nEvent removed and program finished.')
class TelegramBot(Base): client: Client getConfig: BotConfig is_running: bool prefix: str user: pyrogram.types.User uid: int start_time_us: int def __init__(self: "Bot", **kwargs: Any) -> None: self.loaded = False self.getConfig = BotConfig() self._mevent_handlers = {} super().__init__(**kwargs) async def init_client(self: "Bot") -> None: api_id = self.getConfig.api_id if api_id == 0: raise ValueError("API ID is invalid nor empty.") api_hash = self.getConfig.api_hash if not isinstance(api_hash, str): raise TypeError("API HASH must be a string") string_session = self.getConfig.string_session if isinstance(string_session, str): mode = string_session else: mode = ":memory:" self.client = Client(api_id=api_id, api_hash=api_hash, session_name=mode) async def start(self: "Bot") -> None: self.log.info("Starting") await self.init_client() # Load prefix db = self.get_db("core") try: self.prefix = (await db.find_one({"_id": "Core"}))["prefix"] except TypeError: lock = asyncio.Lock() self.prefix = "." # Default is '.'-dot you can change later async with lock: await db.find_one_and_update( {"_id": "Core"}, { "$set": {"prefix": self.prefix} }, upsert=True ) self.client.add_handler( MessageHandler( self.on_command, filters=( self.command_predicate() & filters.me & filters.outgoing ) ), 0) self.client.add_handler( MessageHandler( self.on_conversation, filters=self.conversation_predicate() ), 0) # Load modules self.load_all_modules() await self.dispatch_event("load") self.loaded = True async with silent(): await self.client.start() user = await self.client.get_me() if not isinstance(user, pyrogram.types.User): raise TypeError("Missing full self user information") self.user = user self.uid = user.id self.start_time_us = time.usec() await self.dispatch_event("start", self.start_time_us) self.log.info("Bot is ready") await self.dispatch_event("started") async def idle(self: "Bot") -> None: def signal_handler(_, __): self.log.info(f"Stop signal received ({_}).") self.is_running = False for name in (signal.SIGINT, signal.SIGTERM, signal.SIGABRT): signal.signal(name, signal_handler) self.is_running = True while self.is_running: await asyncio.sleep(1) async def run(self: "Bot") -> None: try: await self.start() self.log.info("Idling...") await self.idle() finally: if not self.stop_manual: await self.stop() def update_module_event(self: "Bot", name: str, handler_type: Handler, filt: Optional[Filter] = None, group: int = 0) -> None: if name in self.listeners: # Add if there ARE listeners and it's NOT already registered if name not in self._mevent_handlers: async def update_handler(_, event) -> None: await self.dispatch_event(name, event) handler_info = self.client.add_handler( # skipcq: PYL-E1111 handler_type(update_handler, filt), group) self._mevent_handlers[name] = handler_info elif name in self._mevent_handlers: # Remove if there are NO listeners and it's ALREADY registered self.client.remove_handler(*self._mevent_handlers[name]) del self._mevent_handlers[name] def update_module_events(self: "Bot") -> None: self.update_module_event("message", MessageHandler, filters.all & ~filters.edited & ~self.command_predicate() & ~TelegramBot.chat_action(), 3) self.update_module_event("message_edit", MessageHandler, filters.edited, 3) self.update_module_event("message_delete", DeletedMessagesHandler, filters.all, 3) self.update_module_event("chat_action", MessageHandler, TelegramBot.chat_action(), 3) self.update_module_event("user_update", UserStatusHandler, filters.all, 5) @property def events_activated(self: "Bot") -> int: return len(self._mevent_handlers) def redact_message(self: "Bot", text: str) -> str: redacted = "[REDACTED]" api_id = self.getConfig.api_hash api_hash = self.getConfig.api_hash db_uri = self.getConfig.db_uri gdrive_secret = self.getConfig.gdrive_secret string_session = self.getConfig.string_session if api_id in text: text = text.replace(api_id, redacted) if api_hash in text: text = text.replace(api_hash, redacted) if db_uri in text: text = text.replace(db_uri, redacted) if gdrive_secret is not None: client_id = gdrive_secret["installed"].get("client_id") client_secret = gdrive_secret["installed"].get("client_secret") if client_id in text: text = text.replace(client_id, redacted) if client_secret in text: text = text.replace(client_secret, redacted) if string_session in text: text = text.replace(string_session, redacted) return text @staticmethod def chat_action() -> Filter: async def func(__, ___, chat: pyrogram.types.Message): return bool(chat.new_chat_members or chat.left_chat_member) return create(func) async def respond( self: "Bot", msg: pyrogram.types.Message, text: Optional[str] = None, *, input_arg: Optional[str] = None, mode: Optional[str] = None, redact: Optional[bool] = True, response: Optional[pyrogram.types.Message] = None, **kwargs: Any, ) -> pyrogram.types.Message: if text is not None: if redact: text = self.redact_message(text) # send as file if text > 4096 if len(text) > tg.MESSAGE_CHAR_LIMIT: await msg.edit("Sending output as a file.") response = await tg.send_as_document( text, msg, input_arg ) await msg.delete() return response # Default to disabling link previews in responses if "disable_web_page_preview" not in kwargs: kwargs["disable_web_page_preview"] = False # Use selected response mode if not overridden by invoker if mode is None: mode = "edit" if mode == "edit": return await msg.edit(text=text, **kwargs) if mode == "reply": if response is not None: # Already replied, so just edit the existing reply to reduce spam return await response.edit(text=text, **kwargs) # Reply since we haven't done so yet return await msg.reply(text, **kwargs) if mode == "repost": if response is not None: # Already reposted, so just edit the existing reply to reduce spam return await response.edit(text=text, **kwargs) # Repost since we haven't done so yet if kwargs.get("document"): del kwargs["disable_web_page_preview"] response = await msg.reply_document(**kwargs) else: response = await msg.reply(text, reply_to_message_id=msg.message_id, **kwargs) await msg.delete() return response raise ValueError(f"Unknown response mode '{mode}'")
class TelegramBot(Base): getConfig: config.BotConfig client: Client owner: int sudo_users: Set[int] user: pyrogram.types.User uid: int start_time_us: int _is_running: bool def __init__(self: "Bot", **kwargs: Any) -> None: self.loaded = False self.getConfig = BotConfig self._mevent_handlers = {} super().__init__(**kwargs) async def init_client(self: "Bot") -> None: api_id = self.getConfig["api_id"] if api_id == 0: raise ValueError("API ID is invalid nor empty.") api_hash = self.getConfig["api_hash"] if not isinstance(api_hash, str): raise TypeError("API HASH must be a string") bot_token = self.getConfig["bot_token"] if bot_token is not None: if not isinstance(bot_token, str): raise TypeError("Bot token must be a string") self.client = Client(api_id=api_id, api_hash=api_hash, bot_token=bot_token, session_name=":memory:") async def start(self: "Bot") -> None: self.log.info("Starting") await self.init_client() db = self.get_db("core") try: self.owner = (await db.find_one({"_id": "Core"}))["owner"] except (TypeError, KeyError): self.owner = int(self.getConfig["owner"]) await db.find_one_and_update({"_id": "Core"}, {"$set": { "owner": self.owner }}, upsert=True) try: sudo_users = (await db.find_one({"_id": "Core"}))["sudo_users"] except (TypeError, KeyError): self.sudo_users = set() else: self.sudo_users = set(sudo_users) self.client.add_handler( MessageHandler(self.on_command, filters=self.command_predicate()), 0) self.client.add_handler( MessageHandler(self.on_conversation, filters=self.conversation_predicate()), 0) # Load modules self.load_all_modules() await self.dispatch_event("load") self.loaded = True await self.client.start() user = await self.client.get_me() if not isinstance(user, pyrogram.types.User): raise TypeError("Missing full self user information") self.user = user self.uid = user.id self.start_time_us = time.usec() await self.dispatch_event("start", self.start_time_us) self.log.info("Bot is ready") await self.dispatch_event("started") async def idle(self: "Bot") -> None: signals = { k: v for v, k in signal.__dict__.items() if v.startswith("SIG") and not v.startswith("SIG_") } def signal_handler(signum, __): self.log.info(f"Stop signal received ('{signals[signum]}').") self._is_running = False for name in (signal.SIGINT, signal.SIGTERM, signal.SIGABRT): signal.signal(name, signal_handler) self._is_running = True while self._is_running: await asyncio.sleep(1) async def run(self: "Bot") -> None: try: await self.start() self.log.info("Gathering dust") await self.idle() finally: if not self.stop_manual: await self.stop() def update_module_event(self: "Bot", name: str, event_type: Type[handler], flt: Optional[filters.Filter] = None, group: int = 0) -> None: if name in self.listeners: if name not in self._mevent_handlers: async def update_event(_: Client, event: Type[update]) -> None: await self.dispatch_event(name, event) event_info = self.client.add_handler( # skipcq: PYL-E1111 event_type(update_event, flt), group) self._mevent_handlers[name] = event_info elif name in self._mevent_handlers: self.client.remove_handler(*self._mevent_handlers[name]) del self._mevent_handlers[name] def update_module_events(self: "Bot") -> None: self.update_module_event("callback_query", CallbackQueryHandler) self.update_module_event("inline_query", InlineQueryHandler) @property def events_activated(self: "Bot") -> int: return len(self._mevent_handlers) def redact_message(self: "Bot", text: str) -> str: redacted = "[REDACTED]" api_id = str(self.getConfig["api_id"]) api_hash = self.getConfig["api_hash"] bot_token = self.getConfig["bot_token"] db_uri = self.getConfig["db_uri"] gdrive_secret = self.getConfig["gdrive_secret"] if api_id in text: text = text.replace(api_id, redacted) if api_hash in text: text = text.replace(api_hash, redacted) if bot_token is not None and bot_token in text: text = text.replace(bot_token, redacted) if db_uri in text: text = text.replace(db_uri, redacted) if gdrive_secret is not None: client_id = gdrive_secret["installed"].get("client_id") client_secret = gdrive_secret["installed"].get("client_secret") if client_id in text: text = text.replace(client_id, redacted) if client_secret in text: text = text.replace(client_secret, redacted) return text async def respond( self: "Bot", msg: pyrogram.types.Message, text: Optional[str] = None, *, input_arg: Optional[str] = None, mode: Optional[str] = None, redact: Optional[bool] = True, response: Optional[pyrogram.types.Message] = None, **kwargs: Any, ) -> pyrogram.types.Message: if text is not None: if redact: text = self.redact_message(text) # send as file if text > 4096 if len(str(text)) > tg.MESSAGE_CHAR_LIMIT: await msg.edit("Sending output as a file.") response = await tg.send_as_document(text, msg, input_arg) await msg.delete() return response # Default to disabling link previews in responses if "disable_web_page_preview" not in kwargs: kwargs["disable_web_page_preview"] = True # Use selected response mode if not overridden by invoker if mode is None: mode = "edit" if mode == "edit": return await msg.edit(text=text, **kwargs) if mode == "reply": if response is not None: # Already replied, so just edit the existing reply to reduce spam return await response.edit(text=text, **kwargs) # Reply since we haven't done so yet return await msg.reply(text, **kwargs) if mode == "repost": if response is not None: # Already reposted, so just edit the existing reply to reduce spam return await response.edit(text=text, **kwargs) # Repost since we haven't done so yet if kwargs.get("document"): del kwargs["disable_web_page_preview"] response = await msg.reply_document(**kwargs) else: response = await msg.reply(text, reply_to_message_id=msg.message_id, **kwargs) await msg.delete() return response raise ValueError(f"Unknown response mode '{mode}'")