diff --git a/core/handlers/event_handler.py b/core/handlers/event_handler.py index f076564..c447e76 100644 --- a/core/handlers/event_handler.py +++ b/core/handlers/event_handler.py @@ -82,7 +82,6 @@ class MessageHandler(BaseHandler): def command( self, *names: str, - *names: str, permission: Optional[Permission] = None, override_permission_check: bool = False ) -> Callable: diff --git a/core/managers/plugin_manager.py b/core/managers/plugin_manager.py index 0141b8c..a41cb6c 100644 --- a/core/managers/plugin_manager.py +++ b/core/managers/plugin_manager.py @@ -27,7 +27,7 @@ def load_all_plugins(): 加载过程中会提取插件元数据 `__plugin_meta__` 并注册到 CommandManager。 """ plugin_dir = os.path.join( - os.path.dirname(os.path.abspath(__file__)), "..", "plugins" + os.path.dirname(os.path.abspath(__file__)), "..", "..", "plugins" ) package_name = "plugins" diff --git a/plugins/code_py.py b/plugins/code_py.py index 4ef5dbd..fe28984 100644 --- a/plugins/code_py.py +++ b/plugins/code_py.py @@ -1,9 +1,6 @@ # -*- coding: utf-8 -*- import html import textwrap -# -*- coding: utf-8 -*- -import html -import textwrap import asyncio from typing import Dict @@ -16,20 +13,12 @@ __plugin_meta__ = { "name": "Python 代码执行", "description": "在安全的沙箱环境中执行 Python 代码片段,支持单行、多行和转发回复。", "usage": "/py <单行代码>\n/code_py <单行代码>\n/py (进入多行输入模式)", - "name": "Python 代码执行", - "description": "在安全的沙箱环境中执行 Python 代码片段,支持单行、多行和转发回复。", - "usage": "/py <单行代码>\n/code_py <单行代码>\n/py (进入多行输入模式)", } # --- 会话状态管理 --- # 结构: {(user_id, group_id): asyncio.TimerHandle} multi_line_sessions: Dict[tuple, asyncio.TimerHandle] = {} -async def reply_as_forward(event: MessageEvent, input_code: str, output_result: str): -# --- 会话状态管理 --- -# 结构: {(user_id, group_id): asyncio.TimerHandle} -multi_line_sessions: Dict[tuple, asyncio.TimerHandle] = {} - async def reply_as_forward(event: MessageEvent, input_code: str, output_result: str): """ 将输入和输出打包成转发消息进行回复。 @@ -59,38 +48,9 @@ async def reply_as_forward(event: MessageEvent, input_code: str, output_result: # 降级为普通消息回复 await event.reply(f"--- 你的代码 ---\n{input_code}\n--- 执行结果 ---\n{output_result}") -async def execute_code(event: MessageEvent, code: str): - 将输入和输出打包成转发消息进行回复。 - 参考 forward_test.py 的实现,兼容私聊和群聊。 - """ - bot = event.bot - - # 1. 构建消息节点列表 - nodes = [ - bot.build_forward_node( - user_id=event.user_id, - nickname=event.sender.nickname or str(event.user_id), - message=f"--- Your Code ---\n{input_code}" - ), - bot.build_forward_node( - user_id=event.self_id, - nickname="Code Executor", - message=f"--- Execution Result ---\n{output_result}" - ) - ] - - try: - # 2. 发送合并转发消息 - await bot.send_forwarded_messages(event, nodes) - except Exception as e: - logger.error(f"[code_py] 发送转发消息失败: {e}") - # 降级为普通消息回复 - await event.reply(f"--- 你的代码 ---\n{input_code}\n--- 执行结果 ---\n{output_result}") - async def execute_code(event: MessageEvent, code: str): """ 核心代码执行逻辑。 - 核心代码执行逻辑。 """ code_executor = getattr(event.bot, 'code_executor', None) if not code_executor or not code_executor.docker_client: @@ -137,74 +97,15 @@ def normalize_code(code: str) -> str: return code.strip() -@matcher.command("py", "python", "code_py", permission=ADMIN) -async def code_py_main(event: MessageEvent, args: list[str]): - code_executor = getattr(event.bot, 'code_executor', None) - if not code_executor or not code_executor.docker_client: - await event.reply("代码执行服务当前不可用,请检查 Docker 连接配置。") - return - - # 修改 add_task,让它能直接接收回复函数 - await code_executor.add_task( - code, - lambda result: reply_as_forward(event, code, result) - ) - await event.reply("代码已提交至沙箱执行队列,请稍候...") - -def cleanup_session(session_key: tuple): - """ - 清理超时的会话。 - """ - if session_key in multi_line_sessions: - del multi_line_sessions[session_key] - logger.info(f"[code_py] 会话 {session_key} 已超时,自动取消。") - -def normalize_code(code: str) -> str: - """ - 规范化用户输入的 Python 代码字符串。 - - 主要处理两个问题: - 1. 对消息中可能存在的 HTML 实体进行解码 (e.g., [ -> [)。 - 2. 移除整个代码块的公共前导缩进,以修复因复制粘贴导致的多余缩进。 - - :param code: 原始代码字符串。 - :return: 规范化后的代码字符串。 - """ - # 1. 解码 HTML 实体 - code = html.unescape(code) - - # 2. 移除公共前导缩进 - try: - code = textwrap.dedent(code) - except Exception: - # 在某些情况下(例如,不一致的缩进),dedent 可能会失败, - # 但我们不希望因此中断流程,所以捕获异常并继续。 - pass - - return code.strip() - - @matcher.command("py", "python", "code_py", permission=ADMIN) async def code_py_main(event: MessageEvent, args: list[str]): """ /py 命令的主入口。 - 如果有参数,直接执行。 - 如果没有参数,开启多行输入模式。 - /py 命令的主入口。 - - 如果有参数,直接执行。 - - 如果没有参数,开启多行输入模式。 """ code_to_run = " ".join(args) - if code_to_run: - # 单行模式,对代码进行规范化处理 - normalized_code = normalize_code(code_to_run) - if not normalized_code: - await event.reply("代码为空或格式错误,请输入有效的代码。") - return - await execute_code(event, normalized_code) - code_to_run = " ".join(args) - if code_to_run: # 单行模式,对代码进行规范化处理 normalized_code = normalize_code(code_to_run) @@ -231,24 +132,6 @@ async def code_py_main(event: MessageEvent, args: list[str]): session_key ) multi_line_sessions[session_key] = timeout_handler - # 多行模式 - # 使用 getattr 兼容私聊和群聊 - session_key = (event.user_id, getattr(event, 'group_id', 'private')) - - # 如果上一个会话的超时任务还在,先取消它 - if session_key in multi_line_sessions: - multi_line_sessions[session_key].cancel() - - await event.reply("已进入多行代码输入模式,请直接发送你的代码。\n(60秒内无操作将自动取消)") - - # 设置 60 秒超时 - loop = asyncio.get_running_loop() - timeout_handler = loop.call_later( - 60, - cleanup_session, - session_key - ) - multi_line_sessions[session_key] = timeout_handler @matcher.on_message() async def handle_multi_line_code(event: MessageEvent): @@ -265,26 +148,6 @@ async def handle_multi_line_code(event: MessageEvent): # 对多行代码进行规范化处理 normalized_code = normalize_code(event.raw_message) - if not normalized_code: - await event.reply("捕获到的代码为空或格式错误,已取消输入。") - return - - await execute_code(event, normalized_code) - return True # 消费事件,防止其他处理器响应 -async def handle_multi_line_code(event: MessageEvent): - """ - 通用消息处理器,用于捕获多行模式下的代码输入。 - """ - # 使用 getattr 兼容私聊和群聊 - session_key = (event.user_id, getattr(event, 'group_id', 'private')) - if session_key in multi_line_sessions: - # 取消超时任务 - multi_line_sessions[session_key].cancel() - del multi_line_sessions[session_key] - - # 对多行代码进行规范化处理 - normalized_code = normalize_code(event.raw_message) - if not normalized_code: await event.reply("捕获到的代码为空或格式错误,已取消输入。") return