diff --git a/languages/built-in/en.yml b/languages/built-in/en.yml index 02fa5ee..2180e18 100644 --- a/languages/built-in/en.yml +++ b/languages/built-in/en.yml @@ -245,14 +245,14 @@ coin_lost: I lost my coin ## lang lang_des: Switch language. lang_change_to: The language has been switched to -lang_reboot: Restarting. +lang_reboot: Reloading. lang_current_lang: The current language is lang_all_lang: All available languages are ## alias alias_des: Redirect original command. alias_list: 'The following is a list of redirect commands:' alias_no: You have not redirected any original commands. -alias_success: Edit redirection command success,PagerMaid-Pyro is restarting. +alias_success: Edit redirection command success,PagerMaid-Pyro is reloading. alias_exist: Something went wrong, woooo~ The command that is redirected is already occupied. alias_no_exist: Something went wrong, woooo~ The original command is not redirected. #message @@ -309,7 +309,7 @@ apt_processing: Installing plugins... apt_no_py: Something went wrong ~ Failed to get the plug-in file from the attachment. apt_plugin: plugin apt_installed: installed -apt_reboot: PagerMaid-Pyro is restarting. +apt_reboot: PagerMaid-Pyro is reloading. apt_install_success: successful installation apt_not_found: not founded apt_no_update: no need to update @@ -553,3 +553,6 @@ sb_remove: The automatic banned list has been removed this group. sb_exist: This group exists in the automatic banned list. sb_no_exist: This group does't exist in the automatic banned list. sb_channel: Successfully blocked this channel in this group. +#reload +reload_des: Reload the PagerMaid-Pyro instance. +reload_ok: Successfully reloaded. diff --git a/languages/built-in/ja.yml b/languages/built-in/ja.yml index fc5216f..4eb3809 100644 --- a/languages/built-in/ja.yml +++ b/languages/built-in/ja.yml @@ -245,14 +245,14 @@ coin_lost: I lost my coin ## lang lang_des: switchlanguage. lang_change_to: The language has been switchedto -lang_reboot: Restarting. +lang_reboot: リロード中 lang_current_lang: 拡張カードの pubage is lang_all_lang: All available languages are ## alias alias_des: Redirect original command。 alias_list: 'The following is a list of redirect commands:' alias_no: You have not redirected any original commands. -alias_success: Edit redirection command success,PagerMaid-Pyro is restarting. +alias_success: リダイレクトコマンドの編集に成功しました。PagerMaid-Pyro がリロードされています。 alias_exist: Something went wrong, woo~ The commd that is redirected is already occupied。 alias_no_exist: Something went wrong, woo~ The original commd is not redirected. #message @@ -309,7 +309,7 @@ apt_processing: Installing plugins... apt_no_py: Something went wrong ~ Failed to get the plug-in file from the attachment. apt_plugin: plugin apt_installed: installed -apt_reboot: PagerMaid-Pyro is restarting。 +apt_reboot: PagerMaid-Pyroはリロードされています。 apt_install_success: successful installation apt_not_found: not founded apt_no_update: no need to update @@ -553,3 +553,6 @@ sb_remove: The automatic banned list has been removed this group. sb_exist: This group exists in the automatic banned list. sb_no_exist: This group does't exist in the automatic banned list. sb_channel: Successfully blocked this channel in this group. +#reload +reload_des: PagerMaid-Pyroインスタンスをリロードします。 +reload_ok: 再読み込みに成功しました。 diff --git a/languages/built-in/zh-cn.yml b/languages/built-in/zh-cn.yml index f4733af..f231e10 100644 --- a/languages/built-in/zh-cn.yml +++ b/languages/built-in/zh-cn.yml @@ -245,14 +245,14 @@ coin_lost: 我丢了硬币 ## lang lang_des: 切换语言。 lang_change_to: 语言已切换到 -lang_reboot: 正在重新启动。 +lang_reboot: 正在重新加载。 lang_current_lang: 当前语言是 lang_all_lang: 所有可用的语言有: ## alias alias_des: 重定向原始命令。 alias_list: '以下是重定向命令列表:' alias_no: 您还没有重定向任何原始命令。 -alias_success: 编辑重定向命令成功,PagerMaid-Pyro 正在重新启动。 +alias_success: 编辑重定向命令成功,PagerMaid-Pyro 正在重新加载。 alias_exist: 出错了呜呜呜 ~ 重定向指向的命令已被占用。 alias_no_exist: 出错了呜呜呜 ~ 原始命令没有被重定向。 #message @@ -309,7 +309,7 @@ apt_processing: 安装插件中 . . . apt_no_py: 出错了呜呜呜 ~ 无法从附件获取插件文件。 apt_plugin: 插件 apt_installed: 已安装 -apt_reboot: PagerMaid-Pyro 正在重新启动。 +apt_reboot: PagerMaid-Pyro 正在重新加载中。 apt_install_success: 安装成功 apt_not_found: 没有找到 apt_no_update: 无需更新 @@ -553,3 +553,6 @@ sb_remove: 已被移除自动封禁列表。 sb_exist: 此群组存在于自动封禁列表中。 sb_no_exist: 此群组不存在于自动封禁列表中。 sb_channel: 成功在本群封禁此频道。 +#reload +reload_des: 重新加载内置模块和插件。 +reload_ok: 重新加载内置模块和插件成功。 diff --git a/languages/built-in/zh-tw.yml b/languages/built-in/zh-tw.yml index 5a33469..f84c21e 100644 --- a/languages/built-in/zh-tw.yml +++ b/languages/built-in/zh-tw.yml @@ -245,14 +245,14 @@ coin_lost: 我擲了硬幣 ## lang lang_des: 切換語言 lang_change_to: 語言已切換到 -lang_reboot: 正在重新啟動。 +lang_reboot: 正在重新加載。 lang_current_lang: 目前語言是 lang_all_lang: 所有語言有這些: ## alias alias_des: 重定向原始命令。 alias_list: '以下是重定向命令列表:' alias_no: 您還沒有重定向任何原始命令。 -alias_success: 編輯重定向命令成功,PagerMaid-Pyro 正在重新啟動。 +alias_success: 編輯重定向命令成功,PagerMaid-Pyro 正在重新加载。 alias_exist: 出錯了嗚嗚嗚 ~ 重定向指向的命令已被佔用。 alias_no_exist: 出錯了嗚嗚嗚 ~ 原始命令沒有被重定向。 #message @@ -309,7 +309,7 @@ apt_processing: 安裝插件中… apt_no_py: Error!無法獲取插件文件。 apt_plugin: 插件 apt_installed: 已安裝 -apt_reboot: 正在重新啟動 PagerMaid-Pyro。 +apt_reboot: PagerMaid-Pyro 正在重新加載。 apt_install_success: 安裝成功 apt_not_found: Error!沒有找到插件 apt_no_update: 沒必要更新 @@ -553,3 +553,6 @@ sb_remove: 已被移除自動封禁列表。 sb_exist: 此群組存在於自動封禁列表中。 sb_no_exist: 此群組不存在於自動封禁列表中。 sb_channel: 成功在本群封禁此頻道。 +#reload +reload_des: 重新加載內置模塊和插件。 +reload_ok: 重新加載內置模塊和插件成功。 diff --git a/pagermaid/__init__.py b/pagermaid/__init__.py index ac369e1..e0a06f7 100644 --- a/pagermaid/__init__.py +++ b/pagermaid/__init__.py @@ -1,15 +1,16 @@ +import contextlib from coloredlogs import ColoredFormatter +from datetime import datetime, timezone from logging import getLogger, StreamHandler, CRITICAL, INFO, basicConfig, DEBUG -from datetime import datetime from os import getcwd -from apscheduler.schedulers.asyncio import AsyncIOScheduler from pagermaid.config import Config +from pagermaid.scheduler import scheduler import pyromod.listen from pyrogram import Client import sys -pgm_version = "1.0.9" +pgm_version = "1.1.0" CMD_LIST = {} module_dir = __path__[0] working_dir = getcwd() @@ -17,9 +18,7 @@ working_dir = getcwd() read_context = {} help_messages = {} all_permissions = [] -scheduler = AsyncIOScheduler(timezone="Asia/ShangHai") -if not scheduler.running: - scheduler.start() + logs = getLogger(__name__) logging_format = "%(levelname)s [%(asctime)s] [%(name)s] %(message)s" logging_handler = StreamHandler() @@ -27,6 +26,9 @@ logging_handler.setFormatter(ColoredFormatter(logging_format)) root_logger = getLogger() root_logger.setLevel(DEBUG if Config.DEBUG else CRITICAL) root_logger.addHandler(logging_handler) +pyro_logger = getLogger("pyrogram") +pyro_logger.setLevel(CRITICAL) +pyro_logger.addHandler(logging_handler) basicConfig(level=DEBUG if Config.DEBUG else INFO) logs.setLevel(DEBUG if Config.DEBUG else INFO) @@ -38,19 +40,21 @@ elif not Config.API_HASH: logs.error("Api-Hash Not Found!") sys.exit(1) -start_time = datetime.utcnow() +start_time = datetime.now(timezone.utc) -try: - import uvloop +with contextlib.suppress(ImportError): + import uvloop # noqa uvloop.install() -except ImportError: - pass + +if not scheduler.running: + scheduler.start() bot = Client("pagermaid", session_string=Config.STRING_SESSION, api_id=Config.API_ID, api_hash=Config.API_HASH, ipv6=Config.IPV6, proxy=Config.PROXY) +bot.job = scheduler async def log(message): diff --git a/pagermaid/__main__.py b/pagermaid/__main__.py index 5796fd4..9b9416e 100644 --- a/pagermaid/__main__.py +++ b/pagermaid/__main__.py @@ -18,12 +18,12 @@ async def main(): for module_name in module_list: try: - import_module("pagermaid.modules." + module_name) + import_module(f"pagermaid.modules.{module_name}") except BaseException as exception: logs.info(f"{lang('module')} {module_name} {lang('error')}: {type(exception)}: {exception}") for plugin_name in plugin_list: try: - import_module("plugins." + plugin_name) + import_module(f"plugins.{plugin_name}") except BaseException as exception: logs.info(f"{lang('module')} {plugin_name} {lang('error')}: {exception}") plugin_list.remove(plugin_name) diff --git a/pagermaid/group_manager.py b/pagermaid/group_manager.py index 958eda5..a99a40b 100644 --- a/pagermaid/group_manager.py +++ b/pagermaid/group_manager.py @@ -17,7 +17,7 @@ class Permission: self.name = name[1:] if name.startswith("-") else name self.root = self.name.split(".")[0] self.sub = self.name.split(".")[1] if len(self.name.split(".")) > 1 else "" - self.enable: bool = False if name.startswith("-") else True + self.enable: bool = not name.startswith("-") self.act: str = "access" if self.enable else "ejection" @@ -25,12 +25,14 @@ def enforce_permission(user: int, permission: str): data = permission.split(".") if len(data) != 2: raise ValueError("Invalid permission format") - if permissions.enforce(str(user), data[0], "access"): - if not permissions.enforce(str(user), permission, "ejection"): - return True - if permissions.enforce(str(user), permission, "access"): - if not permissions.enforce(str(user), permission, "ejection"): - return True + if permissions.enforce( + str(user), data[0], "access" + ) and not permissions.enforce(str(user), permission, "ejection"): + return True + if permissions.enforce( + str(user), permission, "access" + ) and not permissions.enforce(str(user), permission, "ejection"): + return True return False @@ -43,7 +45,7 @@ def parse_pen(pen: Permission) -> List[Permission]: for i in all_permissions: if pen.root == i.root and len(findall(pen.sub.replace("*", r"([\s\S]*)"), i.sub)) > 0 and i not in datas: datas.append(i) - if len(datas) == 0: + if not datas: raise ValueError("No permission found") return datas @@ -61,40 +63,28 @@ def remove_user_from_group(user: str, group: str): def add_permission_for_group(group: str, permission: Permission): - if "*" in permission.name: - data = parse_pen(permission) - else: - data = [permission] + data = parse_pen(permission) if "*" in permission.name else [permission] for i in data: permissions.add_policy(group, i.name, permission.act, "allow") permissions.save_policy() def remove_permission_for_group(group: str, permission: Permission): - if "*" in permission.name: - data = parse_pen(permission) - else: - data = [permission] + data = parse_pen(permission) if "*" in permission.name else [permission] for i in data: permissions.remove_policy(group, i.name, permission.act, "allow") permissions.save_policy() def add_permission_for_user(user: str, permission: Permission): - if "*" in permission.name: - data = parse_pen(permission) - else: - data = [permission] + data = parse_pen(permission) if "*" in permission.name else [permission] for i in data: permissions.add_permission_for_user(user, i.name, permission.act, "allow") permissions.save_policy() def remove_permission_for_user(user: str, permission: Permission): - if "*" in permission.name: - data = parse_pen(permission) - else: - data = [permission] + data = parse_pen(permission) if "*" in permission.name else [permission] for i in data: permissions.delete_permission_for_user(user, i.name, permission.act, "allow") permissions.save_policy() diff --git a/pagermaid/listener.py b/pagermaid/listener.py index e76df56..ef76fb1 100644 --- a/pagermaid/listener.py +++ b/pagermaid/listener.py @@ -1,3 +1,4 @@ +import contextlib import sys import secrets from asyncio import sleep @@ -5,7 +6,6 @@ from time import strftime, gmtime, time from traceback import format_exc from pyrogram import ContinuePropagation, StopPropagation, filters, Client -from pagermaid.single_utils import Message from pyrogram.errors.exceptions.bad_request_400 import ( MessageIdInvalid, MessageNotModified, @@ -16,7 +16,9 @@ from pyrogram.handlers import MessageHandler, EditedMessageHandler from pagermaid import help_messages, logs, Config, bot, read_context, all_permissions from pagermaid.group_manager import Permission +from pagermaid.single_utils import Message from pagermaid.utils import lang, attach_report, sudo_filter, alias_command, get_permission_name, process_exit +from pagermaid.utils import client as httpx_client secret_generator = secrets.SystemRandom() @@ -97,7 +99,8 @@ def listener(**args): def decorator(function): async def handler(client: Client, message: Message): - message.client = client + message.bot = client + message.request = httpx_client try: try: @@ -116,11 +119,14 @@ def listener(**args): raise ContinuePropagation read_context[(message.chat.id, message.id)] = True - await function(client, message) - except StopPropagation: - raise StopPropagation - except KeyboardInterrupt: - raise KeyboardInterrupt + if function.__code__.co_argcount == 1: + await function(message) + elif function.__code__.co_argcount == 2: + await function(client, message) + except StopPropagation as e: + raise StopPropagation from e + except KeyboardInterrupt as e: + raise KeyboardInterrupt from e except MessageNotModified: pass except MessageIdInvalid: @@ -129,29 +135,21 @@ def listener(**args): ) except UserNotParticipant: pass - except ContinuePropagation: - raise ContinuePropagation + except ContinuePropagation as e: + raise ContinuePropagation from e except SystemExit: await process_exit(start=False, _client=client, message=message) sys.exit(0) except BaseException: exc_info = sys.exc_info()[1] exc_format = format_exc() - try: + with contextlib.suppress(BaseException): await message.edit(lang("run_error"), no_reply=True) # noqa - except BaseException: - pass if not diagnostics: return if Config.ERROR_REPORT: - report = f"# Generated: {strftime('%H:%M %d/%m/%Y', gmtime())}. \n" \ - f"# ChatID: {message.chat.id}. \n" \ - f"# UserID: {message.from_user.id if message.from_user else message.sender_chat.id}. \n" \ - f"# Message: \n-----BEGIN TARGET MESSAGE-----\n" \ - f"{message.text if message.text else message.caption}\n-----END TARGET MESSAGE-----\n" \ - f"# Traceback: \n-----BEGIN TRACEBACK-----\n" \ - f"{str(exc_format)}\n-----END TRACEBACK-----\n" \ - f"# Error: \"{str(exc_info)}\". \n" + report = f"""# Generated: {strftime('%H:%M %d/%m/%Y', gmtime())}. \n# ChatID: {message.chat.id}. \n# UserID: {message.from_user.id if message.from_user else message.sender_chat.id}. \n# Message: \n-----BEGIN TARGET MESSAGE-----\n{message.text or message.caption}\n-----END TARGET MESSAGE-----\n# Traceback: \n-----BEGIN TRACEBACK-----\n{str(exc_format)}\n-----END TRACEBACK-----\n# Error: "{str(exc_info)}". \n""" + await attach_report(report, f"exception.{time()}.pagermaid", None, "Error report generated.") if (message.chat.id, message.id) in read_context: @@ -185,12 +183,18 @@ def raw_listener(filter_s): def decorator(function): async def handler(client, message): + message.bot = client + message.request = httpx_client + try: - await function(client, message) - except StopPropagation: - raise StopPropagation - except ContinuePropagation: - raise ContinuePropagation + if function.__code__.co_argcount == 1: + await function(message) + elif function.__code__.co_argcount == 2: + await function(client, message) + except StopPropagation as e: + raise StopPropagation from e + except ContinuePropagation as e: + raise ContinuePropagation from e except SystemExit: await process_exit(start=False, _client=client, message=message) sys.exit(0) @@ -201,10 +205,8 @@ def raw_listener(filter_s): except BaseException: exc_info = sys.exc_info()[1] exc_format = format_exc() - try: + with contextlib.suppress(BaseException): await message.edit(lang('run_error'), no_reply=True) - except BaseException: - pass if Config.ERROR_REPORT: report = f"# Generated: {strftime('%H:%M %d/%m/%Y', gmtime())}. \n" \ f"# ChatID: {message.chat.id}. \n" \ diff --git a/pagermaid/modules/__init__.py b/pagermaid/modules/__init__.py index d6fb600..080d0a5 100644 --- a/pagermaid/modules/__init__.py +++ b/pagermaid/modules/__init__.py @@ -8,37 +8,38 @@ from pagermaid.utils import lang def __list_modules(): - module_paths = glob(dirname(__file__) + f"{sep}*.py") - result = [ + module_paths = glob(f"{dirname(__file__)}{sep}*.py") + return [ basename(file)[:-3] for file in module_paths - if isfile(file) and file.endswith(".py") and not file.endswith("__init__.py") + if isfile(file) + and file.endswith(".py") + and not file.endswith("__init__.py") ] - return result def __list_plugins(): plugin_paths = glob(f"{getcwd()}{sep}plugins" + f"{sep}*.py") if not exists(f"{getcwd()}{sep}plugins"): makedirs(f"{getcwd()}{sep}plugins") - result = [ + return [ basename(file)[:-3] for file in plugin_paths - if isfile(file) and file.endswith(".py") and not file.endswith("__init__.py") + if isfile(file) + and file.endswith(".py") + and not file.endswith("__init__.py") ] - return result -module_list_string = "" -plugin_list_string = "" - -for module in sorted(__list_modules()): - module_list_string += f"{module}, " +module_list_string = "".join( + f"{module}, " for module in sorted(__list_modules()) +) module_list_string = module_list_string[:-2] -for plugin in sorted(__list_plugins()): - plugin_list_string += f"{plugin}, " +plugin_list_string = "".join( + f"{plugin}, " for plugin in sorted(__list_plugins()) +) plugin_list_string = plugin_list_string[:-2] diff --git a/pagermaid/modules/account.py b/pagermaid/modules/account.py index 584fa87..7aac239 100644 --- a/pagermaid/modules/account.py +++ b/pagermaid/modules/account.py @@ -1,6 +1,5 @@ from os import remove -from pyrogram import Client from pyrogram.errors import UsernameNotOccupied, PeerIdInvalid from pyrogram.types import User @@ -8,11 +7,13 @@ from pagermaid import Config from pagermaid.listener import listener from pagermaid.utils import lang, Message +import contextlib -@listener(is_plugin=False, outgoing=True, command="profile", + +@listener(is_plugin=False, command="profile", description=lang('profile_des'), parameters="") -async def profile(client: Client, message: Message): +async def profile(message: Message): """ Queries profile of a user. """ if len(message.parameter) > 1: await message.edit(f"{lang('error_prefix')}{lang('arg_error')}") @@ -29,7 +30,7 @@ async def profile(client: Client, message: Message): if user.isdigit(): user = int(user) else: - user = await client.get_me() + user = await message.bot.get_me() if message.entities is not None: if message.entities[0].type == "text_mention": user = message.entities[0].user @@ -39,7 +40,7 @@ async def profile(client: Client, message: Message): return await message.edit(f"{lang('error_prefix')}{lang('arg_error')}") if not isinstance(user, User): try: - user = await client.get_users(user) + user = await message.bot.get_users(user) except PeerIdInvalid: return await message.edit(f"{lang('error_prefix')}{lang('profile_e_nof')}") except UsernameNotOccupied: @@ -66,10 +67,10 @@ async def profile(client: Client, message: Message): f"{lang('profile_restricted')}: {restricted} \n" \ f"{lang('profile_type')}: {user_type} \n" \ f"[{first_name}](tg://user?id={user.id})" - photo = await client.download_media(user.photo.big_file_id) + photo = await message.bot.download_media(user.photo.big_file_id) reply_to = message.reply_to_message try: - await client.send_photo( + await message.bot.send_photo( message.chat.id, photo, caption=caption, @@ -85,7 +86,7 @@ async def profile(client: Client, message: Message): need_admin=True, description=lang('block_des'), parameters="(username/uid/reply)") -async def block_user(client: Client, message: Message): +async def block_user(message: Message): """ Block a user. """ if len(message.parameter) > 1: await message.edit(f"{lang('error_prefix')}{lang('arg_error')}") @@ -95,11 +96,9 @@ async def block_user(client: Client, message: Message): user = message.obtain_user() if not user: return await message.edit(f"{lang('error_prefix')}{lang('arg_error')}") - try: - if await client.block_user(user): + with contextlib.suppress(Exception): + if await message.bot.block_user(user): return await message.edit(f"{lang('block_success')} `{user}`") - except Exception: # noqa - pass await message.edit(f"`{user}` {lang('block_exist')}") @@ -107,7 +106,7 @@ async def block_user(client: Client, message: Message): need_admin=True, description=lang('unblock_des'), parameters="") -async def unblock_user(client: Client, message: Message): +async def unblock_user(message: Message): """ Unblock a user. """ if len(message.parameter) > 1: await message.edit(f"{lang('error_prefix')}{lang('arg_error')}") @@ -117,9 +116,7 @@ async def unblock_user(client: Client, message: Message): user = message.obtain_user() if not user: return await message.edit(f"{lang('error_prefix')}{lang('arg_error')}") - try: - if await client.unblock_user(user): + with contextlib.suppress(Exception): + if await message.bot.unblock_user(user): return await message.edit(f"{lang('unblock_success')} `{user}`") - except Exception: # noqa - pass await message.edit(f"`{user}` {lang('unblock_exist')}") diff --git a/pagermaid/modules/avoid.py b/pagermaid/modules/avoid.py index 9b1af3d..3448b7f 100644 --- a/pagermaid/modules/avoid.py +++ b/pagermaid/modules/avoid.py @@ -1 +1,91 @@ -""" PagerMaid module for different ways to avoid users. """ from pyrogram import Client from pagermaid import log from pagermaid.single_utils import sqlite from pagermaid.utils import lang, Message from pagermaid.listener import listener @listener(is_plugin=False, outgoing=True, command="ghost", description=lang('ghost_des'), parameters="") async def ghost(client: Client, message: Message): """ Toggles ghosting of a user. """ if len(message.parameter) != 1: await message.edit(f"{lang('error_prefix')}{lang('arg_error')}") return myself = await client.get_me() self_user_id = myself.id if message.parameter[0] == "true": if message.chat.id == self_user_id: return await message.edit(lang('ghost_e_mark')) sqlite["ghosted.chat_id." + str(message.chat_id)] = True await message.safe_delete() await log(f"{lang('ghost_set_f')} ChatID {str(message.chat_id)} {lang('ghost_set_l')}") elif message.parameter[0] == "false": if message.chat_id == self_user_id: await message.edit(lang('ghost_e_mark')) return try: del sqlite["ghosted.chat_id." + str(message.chat_id)] except KeyError: return await message.edit(lang('ghost_e_noexist')) await message.safe_delete() await log(f"{lang('ghost_set_f')} ChatID {str(message.chat_id)} {lang('ghost_cancel')}") elif message.parameter[0] == "status": if sqlite.get("ghosted.chat_id." + str(message.chat_id), None): await message.edit(lang('ghost_e_exist')) else: await message.edit(lang('ghost_e_noexist')) else: await message.edit(f"{lang('error_prefix')}{lang('arg_error')}") @listener(is_plugin=False, outgoing=True, command="deny", need_admin=True, description=lang('deny_des'), parameters="") async def deny(client: Client, message: Message): """ Toggles denying of a user. """ if len(message.parameter) != 1: await message.edit(f"{lang('error_prefix')}{lang('arg_error')}") return myself = await client.get_me() self_user_id = myself.id if message.parameter[0] == "true": if message.chat.id == self_user_id: return await message.edit(lang('ghost_e_mark')) sqlite["denied.chat_id." + str(message.chat_id)] = True await message.safe_delete() await log(f"ChatID {str(message.chat_id)} {lang('deny_set')}") elif message.parameter[0] == "false": if message.chat_id == self_user_id: await message.edit(lang('ghost_e_mark')) return try: del sqlite["denied.chat_id." + str(message.chat_id)] except KeyError: return await message.edit(lang('deny_e_noexist')) await message.safe_delete() await log(f"ChatID {str(message.chat_id)} {lang('deny_cancel')}") elif message.parameter[0] == "status": if sqlite.get("denied.chat_id." + str(message.chat_id), None): await message.edit(lang('deny_e_exist')) else: await message.edit(lang('deny_e_noexist')) else: await message.edit(f"{lang('error_prefix')}{lang('arg_error')}") @listener(is_plugin=False, incoming=True, outgoing=False, ignore_edited=True) async def set_read_acknowledgement(client: Client, message: Message): """ Event handler to infinitely read ghosted messages. """ if sqlite.get("ghosted.chat_id." + str(message.chat.id), None): await client.read_chat_history(message.chat.id) @listener(is_plugin=False, incoming=True, outgoing=False, ignore_edited=True) async def message_removal(_: Client, message: Message): """ Event handler to infinitely delete denied messages. """ if sqlite.get("denied.chat_id." + str(message.chat.id), None): await message.safe_delete() \ No newline at end of file +""" PagerMaid module for different ways to avoid users. """ + +from pagermaid import log +from pagermaid.single_utils import sqlite +from pagermaid.utils import lang, Message +from pagermaid.listener import listener + + +@listener(is_plugin=False, outgoing=True, command="ghost", + description=lang('ghost_des'), + parameters="") +async def ghost(message: Message): + """ Toggles ghosting of a user. """ + if len(message.parameter) != 1: + await message.edit(f"{lang('error_prefix')}{lang('arg_error')}") + return + myself = await message.bot.get_me() + self_user_id = myself.id + if message.parameter[0] == "true": + if message.chat.id == self_user_id: + return await message.edit(lang('ghost_e_mark')) + sqlite[f"ghosted.chat_id.{str(message.chat_id)}"] = True + await message.safe_delete() + await log(f"{lang('ghost_set_f')} ChatID {str(message.chat_id)} {lang('ghost_set_l')}") + elif message.parameter[0] == "false": + if message.chat_id == self_user_id: + await message.edit(lang('ghost_e_mark')) + return + try: + del sqlite[f"ghosted.chat_id.{str(message.chat_id)}"] + except KeyError: + return await message.edit(lang('ghost_e_noexist')) + await message.safe_delete() + await log(f"{lang('ghost_set_f')} ChatID {str(message.chat_id)} {lang('ghost_cancel')}") + elif message.parameter[0] == "status": + if sqlite.get(f"ghosted.chat_id.{str(message.chat_id)}", None): + await message.edit(lang('ghost_e_exist')) + else: + await message.edit(lang('ghost_e_noexist')) + else: + await message.edit(f"{lang('error_prefix')}{lang('arg_error')}") + + +@listener(is_plugin=False, outgoing=True, command="deny", + need_admin=True, + description=lang('deny_des'), + parameters="") +async def deny(message: Message): + """ Toggles denying of a user. """ + if len(message.parameter) != 1: + await message.edit(f"{lang('error_prefix')}{lang('arg_error')}") + return + myself = await message.bot.get_me() + self_user_id = myself.id + if message.parameter[0] == "true": + if message.chat.id == self_user_id: + return await message.edit(lang('ghost_e_mark')) + sqlite[f"denied.chat_id.{str(message.chat_id)}"] = True + await message.safe_delete() + await log(f"ChatID {str(message.chat_id)} {lang('deny_set')}") + elif message.parameter[0] == "false": + if message.chat_id == self_user_id: + await message.edit(lang('ghost_e_mark')) + return + try: + del sqlite[f"denied.chat_id.{str(message.chat_id)}"] + except KeyError: + return await message.edit(lang('deny_e_noexist')) + await message.safe_delete() + await log(f"ChatID {str(message.chat_id)} {lang('deny_cancel')}") + elif message.parameter[0] == "status": + if sqlite.get(f"denied.chat_id.{str(message.chat_id)}", None): + await message.edit(lang('deny_e_exist')) + else: + await message.edit(lang('deny_e_noexist')) + else: + await message.edit(f"{lang('error_prefix')}{lang('arg_error')}") + + +@listener(is_plugin=False, incoming=True, outgoing=False, ignore_edited=True) +async def set_read_acknowledgement(message: Message): + """ Event handler to infinitely read ghosted messages. """ + if sqlite.get(f"ghosted.chat_id.{str(message.chat.id)}", None): + await message.bot.read_chat_history(message.chat.id) + + +@listener(is_plugin=False, incoming=True, outgoing=False, ignore_edited=True) +async def message_removal(message: Message): + """ Event handler to infinitely delete denied messages. """ + if sqlite.get(f"denied.chat_id.{str(message.chat.id)}", None): + await message.safe_delete() diff --git a/pagermaid/modules/backup.py b/pagermaid/modules/backup.py index 48e2aab..ee7061c 100644 --- a/pagermaid/modules/backup.py +++ b/pagermaid/modules/backup.py @@ -1,10 +1,9 @@ """ Pagermaid backup and recovery plugin. """ import os +import sys import tarfile from traceback import format_exc -from pyrogram import Client - from pagermaid.config import Config from pagermaid.listener import listener from pagermaid.utils import upload_attachment, lang, Message @@ -42,7 +41,7 @@ def un_tar_gz(filename, dirs): @listener(is_plugin=False, outgoing=True, command="backup", description=lang('back_des')) -async def backup(_: Client, message: Message): +async def backup(message: Message): await message.edit(lang('backup_process')) # Remove old backup @@ -66,23 +65,21 @@ async def backup(_: Client, message: Message): @listener(is_plugin=False, outgoing=True, command="recovery", need_admin=True, description=lang('recovery_des')) -async def recovery(_: Client, message: Message): +async def recovery(message: Message): reply = message.reply_to_message - if reply.document: # Overwrite local backup - try: - if ".tar.gz" in reply.document.file_name: # Verify filename - await message.edit(lang('recovery_down')) - # Start download process - pgm_backup_zip_name = await reply.download() # noqa - else: - return await message.edit(lang('recovery_file_error')) - except Exception as e: # noqa - print(e, format_exc()) - return await message.edit(lang('recovery_file_error')) - else: + if not reply.document: return await message.edit(lang('recovery_file_error')) + try: + if ".tar.gz" not in reply.document.file_name: + return await message.edit(lang('recovery_file_error')) + await message.edit(lang('recovery_down')) + # Start download process + pgm_backup_zip_name = await reply.download() # noqa + except Exception as e: # noqa + print(e, format_exc()) + return await message.edit(lang('recovery_file_error')) # Extract backup files await message.edit(lang('recovery_process')) if not os.path.exists(pgm_backup_zip_name): @@ -96,4 +93,4 @@ async def recovery(_: Client, message: Message): os.remove(pgm_backup_zip_name) await message.edit(lang('recovery_success') + " " + lang('apt_reboot')) - exit(1) + sys.exit(1) diff --git a/pagermaid/modules/clock.py b/pagermaid/modules/clock.py index 0e508c2..7acc9b9 100644 --- a/pagermaid/modules/clock.py +++ b/pagermaid/modules/clock.py @@ -2,7 +2,6 @@ from datetime import datetime -from pyrogram import Client from pytz import country_names, country_timezones, timezone from pagermaid.config import Config from pagermaid.listener import listener @@ -12,7 +11,7 @@ from pagermaid.utils import lang, Message @listener(is_plugin=False, outgoing=True, command="time", description=lang('time_des'), parameters=lang('time_parameters')) -async def time(_: Client, message: Message): +async def time(message: Message): """ For querying time. """ if len(message.parameter) == 1: country = message.parameter[0].title() diff --git a/pagermaid/modules/help.py b/pagermaid/modules/help.py index 9d46350..6376ebd 100644 --- a/pagermaid/modules/help.py +++ b/pagermaid/modules/help.py @@ -1,33 +1,21 @@ """ The help module. """ -from pyrogram import Client from json import dump as json_dump from os import listdir, sep from pagermaid import help_messages, Config from pagermaid.group_manager import enforce_permission +from pagermaid.modules.reload import reload_all from pagermaid.utils import lang, Message, from_self, from_msg_get_sudo_uid from pagermaid.listener import listener +import pathlib + @listener(is_plugin=False, command="help", description=lang('help_des'), parameters=f"<{lang('command')}>") -async def help_command(_: Client, message: Message): +async def help_command(message: Message): """ The help new command,""" - support_commands = ['username', 'name', 'pfp', 'bio', 'rmpfp', - 'profile', 'block', 'unblock', 'ghost', 'deny', 'convert', - 'caption', 'ocr', 'highlight', 'time', 'translate', - 'tts', 'google', 'animate', - 'teletype', 'widen', 'owo', 'flip', - 'rng', 'aaa', 'tuxsay', 'coin', 'help', - 'lang', 'alias', 'id', 'uslog', 'log', - 're', 'leave', 'hitokoto', 'apt', 'prune', 'selfprune', - 'yourprune', 'del', 'genqr', 'parseqr', - 'sb', 'sysinfo', 'status', - 'stats', 'speedtest', 'connection', - 'pingdc', 'ping', 'topcloud', - 's', 'sticker', 'sh', 'restart', - 'trace', 'chat', 'update'] if message.arguments: if message.arguments in help_messages: if from_self(message) or \ @@ -39,6 +27,20 @@ async def help_command(_: Client, message: Message): await message.edit(lang('arg_error')) else: result = f"**{lang('help_list')}: \n**" + support_commands = ['username', 'name', 'pfp', 'bio', 'rmpfp', + 'profile', 'block', 'unblock', 'ghost', 'deny', 'convert', + 'caption', 'ocr', 'highlight', 'time', 'translate', + 'tts', 'google', 'animate', + 'teletype', 'widen', 'owo', 'flip', + 'rng', 'aaa', 'tuxsay', 'coin', 'help', + 'lang', 'alias', 'id', 'uslog', 'log', + 're', 'leave', 'hitokoto', 'apt', 'prune', 'selfprune', + 'yourprune', 'del', 'genqr', 'parseqr', + 'sb', 'sysinfo', 'status', + 'stats', 'speedtest', 'connection', + 'pingdc', 'ping', 'topcloud', + 's', 'sticker', 'sh', 'restart', + 'trace', 'chat', 'update'] for command in sorted(help_messages, reverse=False): if str(command) in support_commands: continue @@ -62,7 +64,7 @@ async def help_command(_: Client, message: Message): @listener(is_plugin=False, command="help_raw", description=lang('help_des'), parameters=f"<{lang('command')}>") -async def help_raw_command(_: Client, message: Message): +async def help_raw_command(message: Message): """ The help raw command,""" if message.arguments: if message.arguments in help_messages: @@ -79,29 +81,29 @@ async def help_raw_command(_: Client, message: Message): if from_self(message) or \ enforce_permission(from_msg_get_sudo_uid(message), help_messages[command]["permission"]): result += f"`{command}`, " - await message.edit(result[:-2] + f"\n**{lang('help_send')} \",help <{lang('command')}>\" {lang('help_see')}** " - f"[{lang('help_source')}](https://t.me/PagerMaid_Modify)", - disable_web_page_preview=True) + await message.edit( + f"""{result[:-2]}\n**{lang('help_send')} ",help <{lang('command')}>" {lang('help_see')}** [{lang('help_source')}](https://t.me/PagerMaid_Modify)""", + disable_web_page_preview=True, + ) @listener(is_plugin=False, command="lang", need_admin=True, description=lang('lang_des')) -async def lang_change(_: Client, message: Message): +async def lang_change(message: Message): to_lang = message.arguments from_lang = Config.LANGUAGE dir_, dir__ = listdir('languages/built-in'), [] for i in dir_: - if not i.find('yml') == -1: + if i.find('yml') != -1: dir__.append(i[:-4]) - with open('config.yml') as f: - file = f.read() + file = pathlib.Path('config.yml').read_text() if to_lang in dir__: file = file.replace(f'application_language: "{from_lang}"', f'application_language: "{to_lang}"') with open('config.yml', 'w') as f: f.write(file) await message.edit(f"{lang('lang_change_to')} {to_lang}, {lang('lang_reboot')}") - exit(1) + reload_all() else: await message.edit(f'{lang("lang_current_lang")} {Config.LANGUAGE}\n\n' f'{lang("lang_all_lang")}{",".join(dir__)}') @@ -112,7 +114,7 @@ async def lang_change(_: Client, message: Message): need_admin=True, description=lang('alias_des'), parameters='{list|del|set} ') -async def alias_commands(_: Client, message: Message): +async def alias_commands(message: Message): source_commands = [] to_commands = [] texts = [] @@ -123,9 +125,12 @@ async def alias_commands(_: Client, message: Message): await message.edit(lang('arg_error')) return elif len(message.parameter) == 1: - if not len(source_commands) == 0: - for i in range(0, len(source_commands)): - texts.append(f'`{source_commands[i]}` --> `{to_commands[i]}`') + if source_commands: + texts.extend( + f'`{source_commands[i]}` --> `{to_commands[i]}`' + for i in range(len(source_commands)) + ) + await message.edit(lang('alias_list') + '\n\n' + '\n'.join(texts)) else: await message.edit(lang('alias_no')) @@ -136,7 +141,7 @@ async def alias_commands(_: Client, message: Message): with open(f"data{sep}alias.json", 'w') as f: json_dump(Config.alias_dict, f) await message.edit(lang('alias_success')) - exit(1) + reload_all() except KeyError: await message.edit(lang('alias_no_exist')) return @@ -150,4 +155,4 @@ async def alias_commands(_: Client, message: Message): with open(f"data{sep}alias.json", 'w') as f: json_dump(Config.alias_dict, f) await message.edit(lang('alias_success')) - exit(1) + reload_all() diff --git a/pagermaid/modules/message.py b/pagermaid/modules/message.py index 478ba78..49ec3a5 100644 --- a/pagermaid/modules/message.py +++ b/pagermaid/modules/message.py @@ -1,5 +1,5 @@ """ Pagermaid message plugin. """ -from pyrogram import Client + from pyrogram.errors import Forbidden, FloodWait from pagermaid import log @@ -10,29 +10,29 @@ from pagermaid.utils import lang, Message @listener(is_plugin=False, outgoing=True, command="id", description=lang("id_des")) -async def userid(_: Client, message: Message): +async def userid(message: Message): """ Query the UserID of the sender of the message you replied to. """ reply = message.reply_to_message - text = "Message ID: `" + str(message.id) + "`\n\n" + text = f"Message ID: `{str(message.id)}" + "`\n\n" text += "**Chat**\nid:`" + str(message.chat.id) + "`\n" msg_from = message.chat if msg_from.type == "private": try: - text += "first_name: `" + msg_from.first_name + "`\n" + text += f"first_name: `{msg_from.first_name}" + "`\n" except TypeError: text += "**死号**\n" if msg_from.last_name: - text += "last_name: `" + msg_from.last_name + "`\n" + text += f"last_name: `{msg_from.last_name}" + "`\n" if msg_from.username: - text += "username: @" + msg_from.username + "\n" + text += f"username: @{msg_from.username}" + "\n" if msg_from.type in ["supergroup", "channel"]: - text += "title: `" + msg_from.title + "`\n" + text += f"title: `{msg_from.title}" + "`\n" try: if msg_from.username: - text += "username: @" + msg_from.username + "\n" + text += f"username: @{msg_from.username}" + "\n" except AttributeError: return await message.edit(lang("leave_not_group")) - text += "protected: `" + str(msg_from.has_protected_content) + "`\n" + text += f"protected: `{str(msg_from.has_protected_content)}" + "`\n" if reply: text += "\n" + lang('id_hint') + "\nMessage ID: `" + str(reply.id) + \ "`\n\n**User**\nid: `" + str(reply.from_user.id) + "`" @@ -83,7 +83,7 @@ async def userid(_: Client, message: Message): @listener(is_plugin=False, outgoing=True, command="uslog", description=lang('uslog_des'), parameters="") -async def uslog(_: Client, message: Message): +async def uslog(message: Message): """ Forwards a message into log group """ if Config.LOG: if message.reply_to_message: @@ -101,7 +101,7 @@ async def uslog(_: Client, message: Message): @listener(is_plugin=False, outgoing=True, command="log", description=lang('log_des'), parameters="") -async def logging(_: Client, message: Message): +async def logging(message: Message): """ Forwards a message into log group """ if Config.LOG: if message.reply_to_message: @@ -119,10 +119,9 @@ async def logging(_: Client, message: Message): @listener(is_plugin=False, outgoing=True, command="re", description=lang('re_des'), parameters=lang('re_parameters')) -async def re(_: Client, message: Message): +async def re(message: Message): """ Forwards a message into this group """ - reply = message.reply_to_message - if reply: + if reply := message.reply_to_message: if message.arguments == '': num = 1 else: @@ -130,10 +129,10 @@ async def re(_: Client, message: Message): num = int(message.arguments) if num > 100: await message.edit(lang('re_too_big')) - except: # noqa + except Exception: return await message.edit(lang('re_arg_error')) await message.safe_delete() - for nums in range(0, num): + for _ in range(num): try: if not message.chat.has_protected_content: await reply.forward(reply.chat.id) diff --git a/pagermaid/modules/plugin.py b/pagermaid/modules/plugin.py index 003541f..1d5979a 100644 --- a/pagermaid/modules/plugin.py +++ b/pagermaid/modules/plugin.py @@ -1,30 +1,29 @@ """ PagerMaid module to manage plugins. """ + +import contextlib import json +import importlib + from re import search, I from os import remove, rename, chdir, path, sep from os.path import exists from shutil import copyfile, move from glob import glob -from pyrogram import Client - -from pagermaid import log, working_dir, Config +from pagermaid import log, working_dir, Config, scheduler from pagermaid.listener import listener from pagermaid.single_utils import safe_remove from pagermaid.utils import upload_attachment, lang, Message, client from pagermaid.modules import plugin_list as active_plugins, __list_plugins +from pagermaid.modules.reload import reload_all def remove_plugin(name): plugin_directory = f"{working_dir}{sep}plugins{sep}" - try: + with contextlib.suppress(FileNotFoundError): remove(f"{plugin_directory}{name}.py") - except FileNotFoundError: - pass - try: + with contextlib.suppress(FileNotFoundError): remove(f"{plugin_directory}{name}.py.disabled") - except FileNotFoundError: - pass def move_plugin(file_path): @@ -55,7 +54,7 @@ def update_version(plugin_name, version): diagnostics=False, description=lang('apt_des'), parameters=lang('apt_parameters')) -async def plugin(__: Client, message: Message): +async def plugin(message: Message): if len(message.parameter) == 0: await message.edit(lang('arg_error')) return @@ -81,7 +80,7 @@ async def plugin(__: Client, message: Message): f"{path.basename(file_path)[:-3]} {lang('apt_installed')}," f"{lang('apt_reboot')}") await log(f"{lang('apt_install_success')} {path.basename(file_path)[:-3]}.") - exit(0) + reload_all() elif len(message.parameter) >= 2: process_list = message.parameter message = await message.edit(lang('apt_processing')) @@ -133,24 +132,24 @@ async def plugin(__: Client, message: Message): text += lang('apt_reboot') await message.edit(text) if restart: - exit(0) + reload_all() else: await message.edit(lang('arg_error')) elif message.parameter[0] == "remove": if len(message.parameter) == 2: if exists(f"{plugin_directory}{message.parameter[1]}.py") or \ exists(f"{plugin_directory}{message.parameter[1]}.py.disabled"): - safe_remove(f"{plugin_directory}{message.parameter[1]}.py") - safe_remove(f"{plugin_directory}{message.parameter[1]}.py.disabled") - with open(f"{plugin_directory}version.json", 'r', encoding="utf-8") as f: - version_json = json.load(f) - version_json[message.parameter[1]] = "0.0" - with open(f"{plugin_directory}version.json", 'w') as f: - json.dump(version_json, f) + remove_plugin(message.parameter[1]) + if exists(f"{plugin_directory}version.json"): + with open(f"{plugin_directory}version.json", 'r', encoding="utf-8") as f: + version_json = json.load(f) + version_json[message.parameter[1]] = "0.0" + with open(f"{plugin_directory}version.json", 'w') as f: + json.dump(version_json, f) await message.edit(f"{lang('apt_remove_success')} {message.parameter[1]}, " f"{lang('apt_reboot')} ") await log(f"{lang('apt_remove')} {message.parameter[1]}.") - exit(0) + reload_all() elif "/" in message.parameter[1]: await message.edit(lang('arg_error')) else: @@ -201,7 +200,7 @@ async def plugin(__: Client, message: Message): await message.edit(f"{lang('apt_plugin')} {message.parameter[1]} " f"{lang('apt_enable')},{lang('apt_reboot')}") await log(f"{lang('apt_enable')} {message.parameter[1]}.") - exit(0) + reload_all() else: await message.edit(lang('apt_not_exist')) else: @@ -214,7 +213,7 @@ async def plugin(__: Client, message: Message): await message.edit(f"{lang('apt_plugin')} {message.parameter[1]} " f"{lang('apt_disable')},{lang('apt_reboot')}") await log(f"{lang('apt_disable')} {message.parameter[1]}.") - exit(0) + reload_all() else: await message.edit(lang('apt_not_exist')) else: @@ -262,7 +261,7 @@ async def plugin(__: Client, message: Message): un_need_update += "\n`" + key + "`:Ver " + value else: need_update_list.extend([key]) - need_update += "\n`" + key + "`:Ver " + value + " --> Ver " + i['version'] + need_update += "\n" + key + ":Ver " + value + " > Ver " + i['version'] continue if un_need_update == f"{lang('apt_no_update')}:": un_need_update = "" @@ -288,7 +287,7 @@ async def plugin(__: Client, message: Message): with open(f"{plugin_directory}version.json", "w") as f: json.dump(version_json, f) await message.edit(f"{lang('apt_name')}\n\n" + lang("apt_reading_list") + need_update) - exit(0) + reload_all() elif message.parameter[0] == "search": if len(message.parameter) == 1: await message.edit(lang("apt_search_no_name")) @@ -354,5 +353,9 @@ async def plugin(__: Client, message: Message): await message.edit(lang("apt_why_not_install_a_plugin")) else: await message.edit(",apt install " + " ".join(list_plugin)) + elif message.parameter[0] == "reload": + # bot.dispatcher.remove_all_handlers() # noqa + scheduler.remove_all_jobs() + importlib.reload(importlib.import_module("plugins.sign")) else: await message.edit(lang("arg_error")) diff --git a/pagermaid/modules/prune.py b/pagermaid/modules/prune.py index ed4967a..81d217f 100644 --- a/pagermaid/modules/prune.py +++ b/pagermaid/modules/prune.py @@ -2,17 +2,17 @@ from asyncio import sleep -from pyrogram import Client - from pagermaid import log from pagermaid.listener import listener from pagermaid.utils import lang, Message +import contextlib + @listener(is_plugin=False, outgoing=True, command="prune", need_admin=True, description=lang('prune_des')) -async def prune(client: Client, message: Message): +async def prune(message: Message): """ Purge every single message after the message you replied to. """ if not message.reply_to_message: await message.edit(lang('not_reply')) @@ -20,8 +20,8 @@ async def prune(client: Client, message: Message): input_chat = message.chat.id messages = [] count = 0 - async for msg in client.get_chat_history(input_chat, - limit=message.id - message.reply_to_message.id + 1): + async for msg in message.bot.get_chat_history(input_chat, + limit=message.id - message.reply_to_message.id + 1): if msg.id < message.reply_to_message.id: break messages.append(msg.id) @@ -29,13 +29,13 @@ async def prune(client: Client, message: Message): if msg.reply_to_message: messages.append(msg.reply_to_message.id) if len(messages) == 100: - await client.delete_messages(input_chat, messages) + await message.bot.delete_messages(input_chat, messages) messages = [] if messages: - await client.delete_messages(input_chat, messages) + await message.bot.delete_messages(input_chat, messages) await log(f"{lang('prune_hint1')} {str(count)} {lang('prune_hint2')}") - notification = await send_prune_notify(client, message, count, count) + notification = await send_prune_notify(message, count, count) await sleep(1) await notification.delete() @@ -44,14 +44,14 @@ async def prune(client: Client, message: Message): need_admin=True, description=lang('sp_des'), parameters=lang('sp_parameters')) -async def self_prune(client: Client, message: Message): +async def self_prune(message: Message): """ Deletes specific amount of messages you sent. """ msgs = [] count_buffer = 0 - if not len(message.parameter) == 1: + if len(message.parameter) != 1: if not message.reply_to_message: return await message.edit(lang('arg_error')) - async for msg in client.search_messages( + async for msg in message.bot.search_messages( message.chat.id, from_user="me", offset=message.reply_to_message.id, @@ -59,15 +59,15 @@ async def self_prune(client: Client, message: Message): msgs.append(msg.id) count_buffer += 1 if len(msgs) == 100: - await client.delete_messages(message.chat.id, msgs) + await message.bot.delete_messages(message.chat.id, msgs) msgs = [] if msgs: - await client.delete_messages(message.chat.id, msgs) + await message.bot.delete_messages(message.chat.id, msgs) if count_buffer == 0: await message.delete() count_buffer += 1 await log(f"{lang('prune_hint1')}{lang('sp_hint')} {str(count_buffer)} {lang('prune_hint2')}") - notification = await send_prune_notify(client, message, count_buffer, count_buffer) + notification = await send_prune_notify(message, count_buffer, count_buffer) await sleep(1) await notification.delete() return @@ -77,37 +77,38 @@ async def self_prune(client: Client, message: Message): except ValueError: await message.edit(lang('arg_error')) return - async for msg in client.search_messages(message.chat.id, from_user="me"): + async for msg in message.bot.search_messages(message.chat.id, from_user="me"): if count_buffer == count: break msgs.append(msg.id) count_buffer += 1 if len(msgs) == 100: - await client.delete_messages(message.chat.id, msgs) + await message.bot.delete_messages(message.chat.id, msgs) msgs = [] if msgs: - await client.delete_messages(message.chat.id, msgs) - await log(f"{lang('prune_hint1')}{lang('sp_hint')} {str(count_buffer)} / {str(count)} {lang('prune_hint2')}") - try: - notification = await send_prune_notify(client, message, count_buffer, count) + await message.bot.delete_messages(message.chat.id, msgs) + await log( + f"{lang('prune_hint1')}{lang('sp_hint')} {str(count_buffer)} / {count} {lang('prune_hint2')}" + ) + + with contextlib.suppress(ValueError): + notification = await send_prune_notify(message, count_buffer, count) await sleep(1) await notification.delete() - except ValueError: - pass @listener(is_plugin=False, outgoing=True, command="yourprune", need_admin=True, description=lang('yp_des'), parameters=lang('sp_parameters')) -async def your_prune(client: Client, message: Message): +async def your_prune(message: Message): """ Deletes specific amount of messages someone sent. """ if not message.reply_to_message: return await message.edit(lang('not_reply')) target = message.reply_to_message if not target.from_user: return await message.edit(lang('not_reply')) - if not len(message.parameter) == 1: + if len(message.parameter) != 1: return await message.edit(lang('arg_error')) count = 0 try: @@ -118,13 +119,16 @@ async def your_prune(client: Client, message: Message): except Exception: # noqa pass count_buffer = 0 - async for msg in client.search_messages(message.chat.id, from_user=target.from_user.id): + async for msg in message.bot.search_messages(message.chat.id, from_user=target.from_user.id): if count_buffer == count: break await msg.delete() count_buffer += 1 - await log(f"{lang('prune_hint1')}{lang('yp_hint')} {str(count_buffer)} / {str(count)} {lang('prune_hint2')}") - notification = await send_prune_notify(client, message, count_buffer, count) + await log( + f"{lang('prune_hint1')}{lang('yp_hint')} {str(count_buffer)} / {count} {lang('prune_hint2')}" + ) + + notification = await send_prune_notify(message, count_buffer, count) await sleep(1) await notification.delete() @@ -132,22 +136,19 @@ async def your_prune(client: Client, message: Message): @listener(is_plugin=False, outgoing=True, command="del", need_admin=True, description=lang('del_des')) -async def delete(_: Client, message: Message): +async def delete(message: Message): """ Deletes the message you replied to. """ - target = message.reply_to_message - if target: - try: + if target := message.reply_to_message: + with contextlib.suppress(Exception): await target.delete() - except Exception as e: # noqa - pass await message.delete() await log(lang('del_notification')) else: await message.delete() -async def send_prune_notify(client, message, count_buffer, count): - return await client.send_message( +async def send_prune_notify(message: Message, count_buffer, count): + return await message.bot.send_message( message.chat.id, - "%s %s / %s %s" % (lang('spn_deleted'), str(count_buffer), str(count), lang('prune_hint2')) + f"{lang('spn_deleted')} {str(count_buffer)} / {str(count)} {lang('prune_hint2')}", ) diff --git a/pagermaid/modules/reload.py b/pagermaid/modules/reload.py new file mode 100644 index 0000000..9ffc156 --- /dev/null +++ b/pagermaid/modules/reload.py @@ -0,0 +1,45 @@ +import importlib +import pagermaid.config +import pagermaid.modules + +from pagermaid import bot, logs, help_messages, all_permissions +from pagermaid.listener import listener +from pagermaid.utils import lang, Message + + +def reload_all(): + bot.dispatcher.remove_all_handlers() + bot.job.remove_all_jobs() + loaded_plugins = list(pagermaid.modules.plugin_list) + loaded_plugins.extend(iter(pagermaid.modules.module_list)) + # init + importlib.reload(pagermaid.modules) + importlib.reload(pagermaid.config) + help_messages.clear() + all_permissions.clear() + + importlib.reload(pagermaid.modules) + for module_name in pagermaid.modules.module_list: + try: + module = importlib.import_module(f"pagermaid.modules.{module_name}") + if module_name in loaded_plugins: + importlib.reload(module) + except BaseException as exception: + logs.info(f"{lang('module')} {module_name} {lang('error')}: {type(exception)}: {exception}") + for plugin_name in pagermaid.modules.plugin_list: + try: + plugin = importlib.import_module(f"plugins.{plugin_name}") + if plugin_name in loaded_plugins: + importlib.reload(plugin) + except BaseException as exception: + logs.info(f"{lang('module')} {plugin_name} {lang('error')}: {exception}") + pagermaid.modules.plugin_list.remove(plugin_name) + + +@listener(is_plugin=False, command="reload", + need_admin=True, + description=lang('reload_des')) +async def reload_plugins(message: Message): + """ To reload plugins. """ + reload_all() + await message.edit(lang("reload_ok")) diff --git a/pagermaid/modules/status.py b/pagermaid/modules/status.py index 06ab176..84956a6 100644 --- a/pagermaid/modules/status.py +++ b/pagermaid/modules/status.py @@ -1,9 +1,9 @@ """ PagerMaid module that contains utilities related to system status. """ -from datetime import datetime +from datetime import datetime, timezone from platform import uname, python_version from sys import platform -from pyrogram import Client, __version__ +from pyrogram import __version__ from pyrogram.raw.functions import Ping from pyrogram.enums.parse_mode import ParseMode @@ -21,7 +21,7 @@ from pagermaid.utils import lang, Message, execute @listener(is_plugin=False, command="sysinfo", description=lang('sysinfo_des')) -async def sysinfo(_: Client, message: Message): +async def sysinfo(message: Message): """ Retrieve system information via neofetch. """ if not Config.SILENT: message = await message.edit(lang("sysinfo_loading")) @@ -33,7 +33,7 @@ async def sysinfo(_: Client, message: Message): @listener(is_plugin=False, command="status", description=lang('status_des')) -async def status(_: Client, message: Message): +async def status(message: Message): # database # database = lang('status_online') if redis_status() else lang('status_offline') # uptime https://gist.github.com/borgstrom/936ca741e885a1438c374824efb038b3 @@ -55,7 +55,7 @@ async def status(_: Client, message: Message): time_form = time_form.replace(key, value) return time_form - current_time = datetime.utcnow() + current_time = datetime.now(timezone.utc) uptime_sec = (current_time - start_time).total_seconds() uptime = await human_time_duration(int(uptime_sec)) text = (f"**{lang('status_hint')}** \n" @@ -72,10 +72,10 @@ async def status(_: Client, message: Message): @listener(is_plugin=False, command="ping", description=lang('ping_des')) -async def ping(client: Client, message: Message): +async def ping(message: Message): """ Calculates latency between PagerMaid and Telegram. """ start = datetime.now() - await client.invoke(Ping(ping_id=0)) + await message.bot.invoke(Ping(ping_id=0)) end = datetime.now() ping_duration = (end - start).microseconds / 1000 start = datetime.now() @@ -180,7 +180,8 @@ def neofetch_win(): gpu = [f' {g.strip()}' for g in gpu[1:]][0].strip() ram = get_ram() disks = '\n'.join(partitions()) - text = f'{user_name}@{host_name}\n---------\nOS: {os}\nUptime: {uptime}\n' \ - f'Motherboard: {mboard}\nCPU: {cpu}\nGPU: {gpu}\nMemory: {ram}\n' \ - f'Disk:\n{disks}' - return text + return ( + f'{user_name}@{host_name}\n---------\nOS: {os}\nUptime: {uptime}\n' + f'Motherboard: {mboard}\nCPU: {cpu}\nGPU: {gpu}\nMemory: {ram}\n' + f'Disk:\n{disks}' + ) diff --git a/pagermaid/modules/sudo.py b/pagermaid/modules/sudo.py index 9317fa3..7d71c70 100644 --- a/pagermaid/modules/sudo.py +++ b/pagermaid/modules/sudo.py @@ -1,5 +1,3 @@ -from pyrogram import Client - from pagermaid.single_utils import sqlite from pagermaid.listener import listener from pagermaid.group_manager import add_permission_for_group, Permission, remove_permission_for_group, \ @@ -10,18 +8,17 @@ from pagermaid.single_utils import get_sudo_list def from_msg_get_sudo_id(message: Message) -> int: - reply = message.reply_to_message - if reply: + if reply := message.reply_to_message: return reply.from_user.id if reply.from_user else reply.sender_chat.id else: return message.chat.id -@listener(is_plugin=False, outgoing=True, command="sudo", +@listener(is_plugin=False, command="sudo", need_admin=True, parameters="{on|off|add|remove|gaddp|gaddu|gdelp|gdelu|glist|uaddp|udelp|list}", description=lang('sudo_des')) -async def sudo_change(client: Client, message: Message): +async def sudo_change(message: Message): """ To enable or disable sudo of your userbot. """ input_str = message.arguments sudo = get_sudo_list() @@ -79,28 +76,30 @@ async def sudo_change(client: Client, message: Message): for i in sudo: try: if i > 0: - user = await client.get_users(i) + user = await message.bot.get_users(i) text += f"• {user.mention()} - {' '.join(permissions.get_roles_for_user(str(i)))}\n" else: - chat = await client.get_chat(i) + chat = await message.bot.get_chat(i) text += f"• {chat.title} - {' '.join(permissions.get_roles_for_user(str(i)))}\n" for j in permissions.get_permissions_for_user(str(i)): text += f" • {'-' if j[2] == 'ejection' else ''}{j[1]}\n" - except: + except Exception: text += f"• `{i}` - {' '.join(permissions.get_roles_for_user(str(i)))}\n" await message.edit(text) elif len(message.parameter) > 0: if len(message.parameter) == 2: from_id = from_msg_get_sudo_id(message) if message.parameter[0] == "glist": - data = permissions.get_permissions_for_user(str(message.parameter[1])) - if data: - text = f"**{message.parameter[1]} {lang('sudo_group_list')}**\n\n" - for i in data: - text += f" • `{'-' if i[2] == 'ejection' else ''}{i[1]}`\n" - return await message.edit(text) - else: + if not ( + data := permissions.get_permissions_for_user( + str(message.parameter[1]) + ) + ): return await edit_delete(message, f"__{lang('sudo_group_list')}__") + text = f"**{message.parameter[1]} {lang('sudo_group_list')}**\n\n" + for i in data: + text += f" • `{'-' if i[2] == 'ejection' else ''}{i[1]}`\n" + return await message.edit(text) if from_id not in sudo: return await edit_delete(message, f"__{lang('sudo_no')}__") elif message.parameter[0] == "gaddu": diff --git a/pagermaid/modules/system.py b/pagermaid/modules/system.py index 8687cca..36f7bb8 100644 --- a/pagermaid/modules/system.py +++ b/pagermaid/modules/system.py @@ -6,8 +6,6 @@ from sys import exit from platform import node from getpass import getuser -from pyrogram import Client - from pagermaid import bot from pagermaid.listener import listener from pagermaid.utils import attach_log, execute, Message, lang @@ -17,7 +15,7 @@ from pagermaid.utils import attach_log, execute, Message, lang need_admin=True, description=lang('sh_des'), parameters=lang('sh_parameters')) -async def sh(_: Client, message: Message): +async def sh(message: Message): """ Use the command-line from Telegram. """ user = getuser() command = message.arguments @@ -28,9 +26,9 @@ async def sh(_: Client, message: Message): return message = await message.edit( - f"`{user}`@{hostname} ~" - f"\n> `$` {command}" - ) + f"`{user}`@{hostname} ~" + f"\n> `$` {command}" + ) result = await execute(command) @@ -40,10 +38,10 @@ async def sh(_: Client, message: Message): return await message.edit( - f"`{user}`@{hostname} ~" - f"\n> `#` {command}" - f"\n`{result}`" - ) + f"`{user}`@{hostname} ~" + f"\n> `#` {command}" + f"\n`{result}`" + ) else: return @@ -51,7 +49,7 @@ async def sh(_: Client, message: Message): @listener(is_plugin=False, command="restart", need_admin=True, description=lang('restart_des')) -async def restart(_: Client, message: Message): +async def restart(message: Message): """ To re-execute PagerMaid. """ if not message.text[0].isalpha(): await message.edit(lang('restart_log')) @@ -62,7 +60,7 @@ async def restart(_: Client, message: Message): need_admin=True, description=lang('eval_des'), parameters=lang('eval_parameters')) -async def sh_eval(_: Client, message: Message): +async def sh_eval(message: Message): """ Run python commands from Telegram. """ try: cmd = message.text.split(" ", maxsplit=1)[1] @@ -89,14 +87,9 @@ async def sh_eval(_: Client, message: Message): evaluation = stdout else: evaluation = "Success" - final_output = ( - "**>>>** ```{}``` \n```{}```".format( - cmd, - evaluation, - ) - ) + final_output = f"**>>>** ```{cmd}``` \n```{evaluation}```" if len(final_output) > 4096: - message = await message.edit("**>>>** ```{}```".format(cmd)) + message = await message.edit(f"**>>>** ```{cmd}```") await attach_log(bot, evaluation, message.chat.id, "output.log", message.id) else: await message.edit(final_output) @@ -104,11 +97,14 @@ async def sh_eval(_: Client, message: Message): async def aexec(code, event, client): exec( - f"async def __aexec(e, client): " - + "\n msg = message = e" - + "\n reply = message.reply_to_message" - + "\n chat = e.chat" - + "".join(f"\n {x}" for x in code.split("\n")), + ( + ( + ("async def __aexec(e, client): " + "\n msg = message = e") + + "\n reply = message.reply_to_message" + ) + + "\n chat = e.chat" + ) + + "".join(f"\n {x}" for x in code.split("\n")) ) return await locals()["__aexec"](event, client) diff --git a/pagermaid/modules/update.py b/pagermaid/modules/update.py index d43c7c0..0868829 100644 --- a/pagermaid/modules/update.py +++ b/pagermaid/modules/update.py @@ -1,6 +1,4 @@ -from sys import executable - -from pyrogram import Client +from sys import executable, exit from pagermaid.listener import listener from pagermaid.utils import lang, execute, Message, alias_command @@ -10,7 +8,7 @@ from pagermaid.utils import lang, execute, Message, alias_command need_admin=True, description=lang('update_des'), parameters="") -async def update(_: Client, message: Message): +async def update(message: Message): await execute('git fetch --all') if len(message.parameter) > 0: await execute('git reset --hard origin/master') diff --git a/pagermaid/scheduler.py b/pagermaid/scheduler.py new file mode 100644 index 0000000..ecd70cf --- /dev/null +++ b/pagermaid/scheduler.py @@ -0,0 +1,25 @@ +import contextlib +import datetime + +import pytz +from apscheduler.schedulers.asyncio import AsyncIOScheduler +from pagermaid.single_utils import Message + +scheduler = AsyncIOScheduler(timezone="Asia/ShangHai") + + +async def delete_message(message: Message) -> bool: + with contextlib.suppress(Exception): + await message.delete() + return True + return False + + +def add_delete_message_job(message: Message, delete_seconds: int = 60): + scheduler.add_job( + delete_message, "date", + id=f"{message.chat.id}|{message.id}|delete_message", + name=f"{message.chat.id}|{message.id}|delete_message", + args=[message], + run_date=datetime.datetime.now(pytz.timezone("Asia/Shanghai")) + datetime.timedelta(seconds=delete_seconds), + replace_existing=True) diff --git a/pagermaid/single_utils.py b/pagermaid/single_utils.py index 7e71521..ca8a5e4 100644 --- a/pagermaid/single_utils.py +++ b/pagermaid/single_utils.py @@ -1,6 +1,11 @@ +import contextlib from os import sep, remove, mkdir from os.path import exists from typing import List, Optional +from apscheduler.schedulers.asyncio import AsyncIOScheduler +from httpx import AsyncClient + +from pyrogram import Client from pyrogram.types import Message from sqlitedict import SqliteDict @@ -19,15 +24,19 @@ def _status_sudo(): def safe_remove(name: str) -> None: - try: + with contextlib.suppress(FileNotFoundError): remove(name) - except FileNotFoundError: - pass + + +class Client(Client): # noqa + job: Optional[AsyncIOScheduler] = None class Message(Message): # noqa arguments: str parameter: List + bot: Client + request: Optional[AsyncClient] = None def obtain_message(self) -> Optional[str]: """ Obtains a message from either the reply message or command arguments. """ @@ -37,5 +46,8 @@ class Message(Message): # noqa """ Obtains a user from either the reply message or command arguments. """ return + async def delay_delete(self, delete_seconds: int = 60) -> Optional[bool]: + return + async def safe_delete(self, revoke: bool = True) -> None: return diff --git a/pagermaid/utils.py b/pagermaid/utils.py index 8394cb8..5addadb 100644 --- a/pagermaid/utils.py +++ b/pagermaid/utils.py @@ -1,3 +1,4 @@ +import contextlib import subprocess from importlib.util import find_spec from os.path import exists @@ -18,22 +19,18 @@ from pagermaid.single_utils import _status_sudo, get_sudo_list, Message, sqlite def lang(text: str) -> str: """ i18n """ - result = Config.lang_dict.get(text, text) - return result + return Config.lang_dict.get(text, text) def alias_command(command: str, disallow_alias: bool = False) -> str: """ alias """ - if disallow_alias: - return command - return Config.alias_dict.get(command, command) + return command if disallow_alias else Config.alias_dict.get(command, command) async def attach_report(plaintext, file_name, reply_id=None, caption=None): """ Attach plaintext as logs. """ - file = open(file_name, "w+") - file.write(plaintext) - file.close() + with open(file_name, "w+") as file: + file.write(plaintext) try: await bot.send_document( "PagerMaid_Modify_bot", @@ -48,9 +45,8 @@ async def attach_report(plaintext, file_name, reply_id=None, caption=None): async def attach_log(plaintext, chat_id, file_name, reply_id=None, caption=None): """ Attach plaintext as logs. """ - file = open(file_name, "w+") - file.write(plaintext) - file.close() + with open(file_name, "w+") as file: + file.write(plaintext) await bot.send_document( chat_id, file_name, @@ -141,15 +137,9 @@ async def edit_delete(message: Message, def get_permission_name(is_plugin: bool, need_admin: bool, command: str) -> str: """ Get permission name. """ if is_plugin: - if need_admin: - return f"plugins_root.{command}" - else: - return f"plugins.{command}" + return f"plugins_root.{command}" if need_admin else f"plugins.{command}" else: - if need_admin: - return f"system.{command}" - else: - return f"modules.{command}" + return f"system.{command}" if need_admin else f"modules.{command}" def sudo_filter(permission: str): @@ -196,11 +186,10 @@ async def process_exit(start: int, _client, message=None): if start and data and cid and mid: msg: Message = await _client.get_messages(cid, mid) if msg: - try: - await msg.edit((msg.text if msg.from_user.is_self and msg.text else "") + - f'\n\n> {lang("restart_complete")}') - except Exception as e: # noqa - pass + with contextlib.suppress(Exception): + await msg.edit( + ((msg.text or msg.caption) if msg.from_user.is_self and (msg.text or msg.caption) else "") + + f'\n\n> {lang("restart_complete")}') del sqlite["exit_msg"] if message: sqlite["exit_msg"] = {"cid": message.chat.id, "mid": message.id} diff --git a/pyromod/listen/listen.py b/pyromod/listen/listen.py index 3548b79..c635546 100644 --- a/pyromod/listen/listen.py +++ b/pyromod/listen/listen.py @@ -25,11 +25,10 @@ from typing import Optional, List import pyrogram from pagermaid.single_utils import get_sudo_list, Message +from pagermaid.scheduler import add_delete_message_job from ..utils import patch, patchable -loop = asyncio.get_event_loop() - class ListenerCanceled(Exception): pass @@ -53,14 +52,14 @@ class Client: chat = await self.get_chat(chat_id) chat_id = chat.id - future = loop.create_future() + future = self.loop.create_future() future.add_done_callback( functools.partial(self.clear_listener, chat_id) ) self.listening.update({ chat_id: {"future": future, "filters": filters} }) - return await asyncio.wait_for(future, timeout) + return await asyncio.wait_for(future, timeout, loop=self.loop) @patchable async def ask(self, chat_id, text, filters=None, timeout=None, *args, **kwargs): @@ -144,6 +143,7 @@ class User(pyrogram.types.User): def cancel_listener(self): return self._client.cancel_listener(self.id) + # pagermaid-pyro @@ -163,7 +163,9 @@ class Message(pyrogram.types.Message): @patchable def obtain_message(self) -> Optional[str]: """ Obtains a message from either the reply message or command arguments. """ - return self.arguments if self.arguments else (self.reply_to_message.text if self.reply_to_message else None) + return self.arguments or ( + self.reply_to_message.text if self.reply_to_message else None + ) @patchable def obtain_user(self) -> Optional[int]: @@ -185,6 +187,10 @@ class Message(pyrogram.types.Message): user = self.chat.id return user + @patchable + async def delay_delete(self, delay: int = 60): + add_delete_message_job(self, delay) + @patchable async def edit_text( self, @@ -219,13 +225,12 @@ class Message(pyrogram.types.Message): disable_web_page_preview=disable_web_page_preview, reply_markup=reply_markup ) - else: - if not no_reply: - msg = await self.reply( - text=text, - parse_mode=parse_mode, - disable_web_page_preview=disable_web_page_preview - ) + elif not no_reply: + msg = await self.reply( + text=text, + parse_mode=parse_mode, + disable_web_page_preview=disable_web_page_preview + ) else: try: msg = await self._client.edit_message_text( @@ -238,9 +243,7 @@ class Message(pyrogram.types.Message): reply_markup=reply_markup ) except pyrogram.errors.exceptions.forbidden_403.MessageAuthorRequired: # noqa - if no_reply: - pass - else: + if not no_reply: msg = await self.reply( text=text, parse_mode=parse_mode, @@ -256,11 +259,26 @@ class Message(pyrogram.types.Message): document="output.log", reply_to_message_id=self.id ) - if msg: - msg.parameter = self.parameter - msg.arguments = self.arguments - return msg - else: + if not msg: return self + msg.parameter = self.parameter + msg.arguments = self.arguments + return msg edit = edit_text + + +@patch(pyrogram.dispatcher.Dispatcher) # noqa +class Dispatcher(pyrogram.dispatcher.Dispatcher): # noqa + @patchable + def remove_all_handlers(self): + async def fn(): + for lock in self.locks_list: + await lock.acquire() + + self.groups.clear() + + for lock in self.locks_list: + lock.release() + + self.loop.create_task(fn()) diff --git a/pyromod/nav/pagination.py b/pyromod/nav/pagination.py index 3885de0..607be32 100644 --- a/pyromod/nav/pagination.py +++ b/pyromod/nav/pagination.py @@ -35,12 +35,12 @@ class Pagination: offset = (page-1)*quant_per_page stop = offset+quant_per_page cutted = self.objects[offset:stop] - + total = len(self.objects) pages_range = [*range(1, math.ceil(total/quant_per_page)+1)] # each item is a page last_page = len(pages_range) - - + + nav = [] if page <= 3: for n in [1,2,3]: @@ -57,29 +57,36 @@ class Pagination: (f'{last_page} »' if last_page > 5 else last_page, self.page_data(last_page)) ) elif page >= last_page-2: - nav.extend([ - (f'« 1' if last_page-4 > 1 else 1, self.page_data(1)), - (f'‹ {last_page-3}' if last_page-4 > 1 else last_page-3, self.page_data(last_page-3)) - ]) + nav.extend( + [ + ('« 1' if last_page > 5 else 1, self.page_data(1)), + ( + f'‹ {last_page-3}' if last_page > 5 else last_page - 3, + self.page_data(last_page - 3), + ), + ] + ) + for n in range(last_page-2, last_page+1): text = f"· {n} ·" if n == page else n nav.append( (text, self.page_data(n)) ) else: nav = [ - (f'« 1', self.page_data(1)), - (f'‹ {page-1}', self.page_data(page-1)), + ('« 1', self.page_data(1)), + (f'‹ {page-1}', self.page_data(page - 1)), (f'· {page} ·', "noop"), - (f'{page+1} ›', self.page_data(page+1)), + (f'{page+1} ›', self.page_data(page + 1)), (f'{last_page} »', self.page_data(last_page)), ] - - buttons = [] - for item in cutted: - buttons.append( - (self.item_title(item, page), self.item_data(item, page)) - ) + + + buttons = [ + (self.item_title(item, page), self.item_data(item, page)) + for item in cutted + ] + kb_lines = array_chunk(buttons, columns) if last_page > 1: kb_lines.append(nav) - + return kb_lines \ No newline at end of file diff --git a/pyromod/utils/utils.py b/pyromod/utils/utils.py index 6fc660a..d175144 100644 --- a/pyromod/utils/utils.py +++ b/pyromod/utils/utils.py @@ -25,7 +25,7 @@ def patch(obj): def wrapper(container): for name,func in filter(is_patchable, container.__dict__.items()): old = getattr(obj, name, None) - setattr(obj, 'old'+name, old) + setattr(obj, f'old{name}', old) setattr(obj, name, func) return container return wrapper