feat(web_parser): 新增通用web链接解析插件框架
refactor: 重构B站、抖音、GitHub解析器为模块化结构 fix(executor): 增强docker容器错误处理和回调稳定性 style(templates): 优化帮助页面和代码执行结果的样式 perf(web_parser): 添加API缓存和消息去重机制 docs: 更新插件元信息和注释 chore: 移除旧的独立解析器插件文件
This commit is contained in:
@@ -3,15 +3,19 @@ import html
|
||||
import textwrap
|
||||
import asyncio
|
||||
from typing import Dict
|
||||
import datetime
|
||||
import sys
|
||||
|
||||
from core.managers.command_manager import matcher
|
||||
from models.events.message import MessageEvent
|
||||
from core.permission import Permission
|
||||
from core.utils.logger import logger
|
||||
from core.managers.image_manager import image_manager
|
||||
from models.message import MessageSegment
|
||||
|
||||
__plugin_meta__ = {
|
||||
"name": "Python 代码执行",
|
||||
"description": "在安全的沙箱环境中执行 Python 代码片段,支持单行、多行和转发回复。",
|
||||
"description": "在安全的沙箱环境中执行 Python 代码片段,支持单行、多行和图片输出。",
|
||||
"usage": "/py <单行代码>\n/code_py <单行代码>\n/py (进入多行输入模式)",
|
||||
}
|
||||
|
||||
@@ -19,48 +23,88 @@ __plugin_meta__ = {
|
||||
# 结构: {(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):
|
||||
async def generate_and_send_code_image(event: MessageEvent, input_code: str, output_result: str):
|
||||
"""
|
||||
将输入和输出打包成转发消息进行回复。
|
||||
参考 forward_test.py 的实现,兼容私聊和群聊。
|
||||
生成代码执行结果的图片并发送,如果发送失败则降级为文本消息。
|
||||
|
||||
Args:
|
||||
event (MessageEvent): 消息事件对象
|
||||
input_code (str): 用户输入的代码
|
||||
output_result (str): 代码执行结果
|
||||
"""
|
||||
bot = event.bot
|
||||
|
||||
# 1. 构建消息节点列表
|
||||
nodes = [
|
||||
bot.build_forward_node(
|
||||
user_id=event.user_id,
|
||||
nickname=event.sender.nickname if event.sender else 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)
|
||||
# 准备模板数据
|
||||
user_nickname = event.sender.nickname if event.sender else str(event.user_id)
|
||||
user_id = event.user_id
|
||||
avatar_initial = user_nickname[0] if user_nickname else "U"
|
||||
|
||||
# 构建QQ头像URL
|
||||
qq_avatar_url = f"https://q1.qlogo.cn/g?b=qq&nk={user_id}&s=640"
|
||||
|
||||
template_data = {
|
||||
"user_nickname": user_nickname,
|
||||
"user_id": user_id,
|
||||
"avatar_initial": avatar_initial,
|
||||
"qq_avatar_url": qq_avatar_url,
|
||||
"code": input_code,
|
||||
"result": output_result,
|
||||
"timestamp": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
|
||||
"execution_time": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
|
||||
"python_version": f"{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}",
|
||||
"result_title": "执行成功" if "Traceback" not in output_result and "Error" not in output_result else "执行出错",
|
||||
"result_class": "result-success" if "Traceback" not in output_result and "Error" not in output_result else "result-error"
|
||||
}
|
||||
|
||||
# 渲染模板为图片
|
||||
image_base64 = await image_manager.render_template_to_base64(
|
||||
template_name="code_execution.html",
|
||||
data=template_data,
|
||||
output_name=f"code_execution_{event.user_id}_{int(datetime.datetime.now().timestamp())}.png",
|
||||
quality=90,
|
||||
image_type="png"
|
||||
)
|
||||
|
||||
if image_base64:
|
||||
# 发送图片
|
||||
await event.reply(MessageSegment.image(image_base64))
|
||||
else:
|
||||
# 如果图片生成失败,降级为文本消息
|
||||
await event.reply(f"--- 你的代码 ---\n{input_code}\n--- 执行结果 ---\n{output_result}")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"[code_py] 发送转发消息失败: {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):
|
||||
"""
|
||||
核心代码执行逻辑。
|
||||
|
||||
Args:
|
||||
event (MessageEvent): 消息事件对象
|
||||
code (str): 要执行的Python代码
|
||||
"""
|
||||
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,让它能直接接收回复函数
|
||||
# 定义一个包装回调函数,确保正确处理异步操作和异常
|
||||
async def callback_wrapper(result):
|
||||
try:
|
||||
await generate_and_send_code_image(event, code, result)
|
||||
except Exception as e:
|
||||
logger.error(f"[code_py] 执行回调时发生错误: {e}")
|
||||
# 即使回调失败,也要确保任务被标记为完成
|
||||
# 降级为简单文本回复
|
||||
try:
|
||||
await event.reply(f"代码执行结果:\n{result}")
|
||||
except Exception as reply_error:
|
||||
logger.error(f"[code_py] 发送降级回复时也失败: {reply_error}")
|
||||
|
||||
await code_executor.add_task(
|
||||
code,
|
||||
lambda result: reply_as_forward(event, code, result)
|
||||
callback_wrapper
|
||||
)
|
||||
await event.reply("代码已提交至沙箱执行队列,请稍候...")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user