diff --git a/config.gen.yml b/config.gen.yml index 5ff7dc2..8f039dc 100644 --- a/config.gen.yml +++ b/config.gen.yml @@ -89,3 +89,6 @@ start_form: "%m/%d %H:%M" # Silent to reduce editing times silent: "True" + +# Eval use pb or not +use_pb: "True" diff --git a/languages/built-in/en.yml b/languages/built-in/en.yml index 893b344..f5dcef7 100644 --- a/languages/built-in/en.yml +++ b/languages/built-in/en.yml @@ -503,6 +503,10 @@ eval_parameters: eval_channel: Something went wrong ~ The current PagerMaid-Pyro configuration prohibits the execution of Python commands in the channel. eval_success: execute Python commands remotely eval_need_dev: '**Please note: This command can directly operate your account** This command is only for developers. If you know what you are doing, please manually configure the `dev` item in the Redis database to any value or create the `dev` file in the `data` folder.' +eval_executing: '🔃 Executing...' +eval_code: '💻 Code:' +eval_result: '✨ Result:' +eval_time: 'Completed in {}s.' ## send_log send_log_des: Send the log file to the specified user. send_log_not_found: The log file does not exist. diff --git a/languages/built-in/ja.yml b/languages/built-in/ja.yml index 149760f..613bbdd 100644 --- a/languages/built-in/ja.yml +++ b/languages/built-in/ja.yml @@ -503,6 +503,10 @@ eval_parameters: eval_channel: Something went wrong ~ The current Maid-Pyro configuration prohibits the execution of Python commands in the channel. eval_success: execute Python commands remotey eval_need_dev: '**Please note: This command can directly operate your account** This command is only for developers. If you know what you are doing, and please manually configure the `dev` item in the Redis database to any value or create the `dev` file in the `data` folder。' +eval_executing: '🔃 実行中...' +eval_code: '💻 コード:' +eval_result: '✨ 結果:' +eval_time: '{}2番 で完了しました。' ## send_log send_log_des: 指定したユーザーにログ ファイルを送信します。 send_log_not_found: ログファイルが存在しません。 diff --git a/languages/built-in/zh-cn.yml b/languages/built-in/zh-cn.yml index f5f16eb..23fed6b 100644 --- a/languages/built-in/zh-cn.yml +++ b/languages/built-in/zh-cn.yml @@ -503,6 +503,10 @@ eval_parameters: <命令> eval_channel: 出错了呜呜呜 ~ 当前 PagerMaid-Pyro 的配置禁止在频道中执行 Python 命令。 eval_success: 远程执行 Python 命令 eval_need_dev: '**请注意:此命令可以直接操作您的账户** 此命令仅适用于开发者,如果您知道您在做什么的话,请手动在 Redis 数据库配置 dev 项为任意值或者在 data 文件夹下创建 dev 文件。' +eval_executing: '🔃 正在执行...' +eval_code: '💻 代码:' +eval_result: '✨ 运行结果:' +eval_time: '耗时 {} 秒' ## send_log send_log_des: 将日志文件发送给指定用户 send_log_not_found: 日志文件不存在。 diff --git a/languages/built-in/zh-tw.yml b/languages/built-in/zh-tw.yml index 975af7f..e18eb47 100644 --- a/languages/built-in/zh-tw.yml +++ b/languages/built-in/zh-tw.yml @@ -503,6 +503,10 @@ eval_parameters: <指令> eval_channel: Error!不能在頻道執行指令。 eval_success: 執行 Python 指令 eval_need_dev: '**請注意:此命令可以直接操作您的賬戶** 此命令僅適用於開發者,如果您知道您在做什麼的話,請手動在 Redis 數據庫配置 dev 項為任意值或者在 data 文件夾下創建 dev 文件。' +eval_executing: '🔃 正在執行...' +eval_code: '💻 代碼:' +eval_result: '✨ 運行結果:' +eval_time: '耗時 {} 秒' ## send_log send_log_des: 將日誌文件發送到指定的用戶 send_log_not_found: 日誌文件不存在 diff --git a/pagermaid/__init__.py b/pagermaid/__init__.py index 447683f..24edf5f 100644 --- a/pagermaid/__init__.py +++ b/pagermaid/__init__.py @@ -22,8 +22,8 @@ from pagermaid.scheduler import scheduler import pyromod.listen from pyrogram import Client -pgm_version = "1.4.8" -pgm_version_code = 1408 +pgm_version = "1.4.9" +pgm_version_code = 1409 CMD_LIST = {} module_dir = __path__[0] working_dir = getcwd() diff --git a/pagermaid/common/system.py b/pagermaid/common/system.py index bda9fed..932a8f6 100644 --- a/pagermaid/common/system.py +++ b/pagermaid/common/system.py @@ -1,11 +1,13 @@ import io import sys import traceback +from typing import Optional from pagermaid import bot +from pagermaid.services import client as httpx_client -async def run_eval(cmd: str, message=None, only_result: bool = False) -> str: +async def run_eval(cmd: str, message=None) -> str: old_stderr = sys.stderr old_stdout = sys.stdout redirected_output = sys.stdout = io.StringIO() @@ -27,7 +29,7 @@ async def run_eval(cmd: str, message=None, only_result: bool = False) -> str: evaluation = stdout else: evaluation = "Success" - return evaluation if only_result else f"**>>>** `{cmd}` \n`{evaluation}`" + return evaluation async def aexec(code, event, client): @@ -43,3 +45,19 @@ async def aexec(code, event, client): ) return await locals()["__aexec"](event, client) + + +async def paste_pb( + content: str, private: bool = True, sunset: int = 3600 +) -> Optional[str]: + data = { + "c": content, + } + if private: + data["p"] = "1" + if sunset: + data["sunset"] = sunset + result = await httpx_client.post("https://fars.ee", data=data) + if result.is_error: + return None + return result.headers.get("location") diff --git a/pagermaid/config.py b/pagermaid/config.py index f183e07..d288904 100644 --- a/pagermaid/config.py +++ b/pagermaid/config.py @@ -17,6 +17,8 @@ def strtobool(val, default=False): are 'n', 'no', 'f', 'false', 'off', and '0'. Raises ValueError if 'val' is anything else. """ + if val is None: + return default val = val.lower() if val in ("y", "yes", "t", "true", "on", "1"): return 1 @@ -143,6 +145,7 @@ class Config: WEB_HOST = os.environ.get("WEB_HOST", web_interface.get("host", "127.0.0.1")) WEB_PORT = int(os.environ.get("WEB_PORT", web_interface.get("port", 3333))) WEB_ORIGINS = web_interface.get("origins", ["*"]) + USE_PB = strtobool(os.environ.get("PGM_USE_PB", config.get("use_pb")), True) except ValueError as e: print(e) sys.exit(1) diff --git a/pagermaid/listener.py b/pagermaid/listener.py index 21a7814..0321cbd 100644 --- a/pagermaid/listener.py +++ b/pagermaid/listener.py @@ -32,6 +32,7 @@ from pagermaid.utils import ( alias_command, get_permission_name, process_exit, + format_exc as format_exc_text, ) from pagermaid.hook import Hook from pagermaid.web import web @@ -212,11 +213,13 @@ def listener(**args): await process_exit(start=False, _client=client, message=message) await Hook.shutdown() web.stop() - except BaseException: + except BaseException as exc: exc_info = sys.exc_info()[1] exc_format = format_exc() with contextlib.suppress(BaseException): - await message.edit(lang("run_error"), no_reply=True) # noqa + exc_text = format_exc_text(exc) + text = f'{lang("run_error")}\n\n{exc_text}' + await message.edit(text, no_reply=True) # noqa if not diagnostics: return 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""" @@ -229,9 +232,7 @@ def listener(**args): None, "PGP Error report generated.", ) - await Hook.process_error_exec( - message, command, exc_info, exc_format - ) + await Hook.process_error_exec(message, command, exc_info, exc_format) if (message.chat.id, message.id) in read_context: del read_context[(message.chat.id, message.id)] if block_process: diff --git a/pagermaid/modules/account.py b/pagermaid/modules/account.py index 65456cd..4ec8fd1 100644 --- a/pagermaid/modules/account.py +++ b/pagermaid/modules/account.py @@ -86,11 +86,7 @@ async def profile(client: Client, message: Message): f"[{first_name}](tg://user?id={user.id})" ) photo = await client.download_media(user.photo.big_file_id) if user.photo else None - reply_to = ( - message.reply_to_message.id - if message.reply_to_message - else None - ) + reply_to = message.reply_to_message.id if message.reply_to_message else None if photo: try: await client.send_photo( diff --git a/pagermaid/modules/message.py b/pagermaid/modules/message.py index 2dac80d..daebf43 100644 --- a/pagermaid/modules/message.py +++ b/pagermaid/modules/message.py @@ -169,7 +169,9 @@ async def re(bot: Client, message: Message): for _ in range(num): try: if not message.chat.has_protected_content: - await reply.forward(reply.chat.id, message_thread_id=reply.message_thread_id) + await reply.forward( + reply.chat.id, message_thread_id=reply.message_thread_id + ) else: await reply.copy( reply.chat.id, diff --git a/pagermaid/modules/mixpanel.py b/pagermaid/modules/mixpanel.py index 895c089..7c83f22 100644 --- a/pagermaid/modules/mixpanel.py +++ b/pagermaid/modules/mixpanel.py @@ -59,7 +59,9 @@ class Mixpanel: await self._request.post(request_url, data=params, timeout=10.0) logs.debug(f"Mixpanel request took {self._now() - start} seconds") - async def people_set(self, distinct_id: str, properties: dict, force_update: bool = False): + async def people_set( + self, distinct_id: str, properties: dict, force_update: bool = False + ): if self.is_people_set and (not force_update): return message = { diff --git a/pagermaid/modules/system.py b/pagermaid/modules/system.py index 2645f53..c2ea7c2 100644 --- a/pagermaid/modules/system.py +++ b/pagermaid/modules/system.py @@ -1,15 +1,24 @@ +import html import sys from getpass import getuser from os.path import exists, sep from platform import node +from time import perf_counter -from pyrogram.enums import ParseMode - -from pagermaid.common.system import run_eval +from pagermaid import Config +from pagermaid.common.system import run_eval, paste_pb from pagermaid.enums import Message from pagermaid.listener import listener from pagermaid.utils import attach_log, execute, lang, upload_attachment +code_result = ( + f"{lang('eval_code')}\n" + '
{}
\n\n' + f'{lang("eval_result")}\n' + "{}\n" + f"{lang('eval_time')}" +) + @listener( is_plugin=False, @@ -28,18 +37,24 @@ async def sh(message: Message): await message.edit(lang("arg_error")) return - message = await message.edit(f"`{user}`@{hostname} ~" f"\n> `$` {command}") + message = await message.edit(f"`{user}`@{hostname} ~\n> `$` {command}") result = await execute(command) if result: - if len(result) > 4096: + final_result = None + if len(result) > 3072: + if Config.USE_PB: + url = await paste_pb(result) + if url: + final_result = html.escape(f"{url}/bash") + else: + final_result = f"{html.escape(result)}" + + if (len(result) > 3072 and not Config.USE_PB) or final_result is None: await attach_log(result, message.chat.id, "output.log", message.id) return - - await message.edit( - f"`{user}`@{hostname} ~" f"\n> `#` {command}" f"\n`{result}`" - ) + await message.edit(f"`{user}`@{hostname} ~\n> `#` {command}\n\n{final_result}") else: return @@ -70,12 +85,29 @@ async def sh_eval(message: Message): cmd = message.text.split(" ", maxsplit=1)[1] except (IndexError, AssertionError): return await message.edit(lang("eval_need_dev")) + message = await message.edit_text(f"{lang('eval_executing')}") + start_time = perf_counter() final_output = await run_eval(cmd, message) - if len(final_output) > 4096: - message = await message.edit(f"**>>>** `{cmd}`", parse_mode=ParseMode.MARKDOWN) - await attach_log(final_output, message.chat.id, "output.log", message.id) + stop_time = perf_counter() + + result = None + if len(final_output) > 3072: + if Config.USE_PB: + url = await paste_pb(final_output) + if url: + result = html.escape(f"{url}/python") else: - await message.edit(final_output) + result = f"{html.escape(final_output)}" + text = code_result.format( + "python", + html.escape(cmd), + result if result else "...", + round(stop_time - start_time, 5), + ) + + await message.edit(text) + if (len(final_output) > 3072 and not Config.USE_PB) or result is None: + await attach_log(final_output, message.chat.id, "output.log", message.id) @listener( diff --git a/pagermaid/utils.py b/pagermaid/utils.py index 729b06a..5d2d1b8 100644 --- a/pagermaid/utils.py +++ b/pagermaid/utils.py @@ -11,6 +11,8 @@ from asyncio import create_subprocess_shell, sleep from asyncio.subprocess import PIPE from pyrogram import filters +from pyrogram.errors import RPCError + from pagermaid.config import Config from pagermaid import bot from pagermaid.group_manager import enforce_permission @@ -53,7 +55,9 @@ async def attach_log(plaintext, chat_id, file_name, reply_id=None, caption=None) remove(file_name) -async def upload_attachment(file_path, chat_id, reply_id, message_thread_id=None, caption=None, thumb=None): +async def upload_attachment( + file_path, chat_id, reply_id, message_thread_id=None, caption=None, thumb=None +): """Uploads a local attachment file.""" if not exists(file_path): return False @@ -207,6 +211,12 @@ async def process_exit(start: int, _client, message=None): sqlite["exit_msg"] = {"cid": message.chat.id, "mid": message.id} +def format_exc(e: BaseException) -> str: + if isinstance(e, RPCError): + return f"API [{e.CODE} {e.ID or e.NAME}] — {e.MESSAGE.format(value=e.value)}" + return f"{e.__class__.__name__}: {e}" + + """ Init httpx client """ # 使用自定义 UA headers = { diff --git a/pagermaid/web/api/status.py b/pagermaid/web/api/status.py index a43a18c..48e9c74 100644 --- a/pagermaid/web/api/status.py +++ b/pagermaid/web/api/status.py @@ -37,7 +37,7 @@ async def run_cmd(token: Optional[str] = Header(...), cmd: str = ""): return "非法请求" async def run_cmd_func(): - result = (await run_eval(cmd, only_result=True)).split("\n") + result = (await run_eval(cmd)).split("\n") for i in result: yield i + "\n" await asyncio.sleep(0.02)