diff --git a/plugins/bot_status.py b/plugins/bot_status.py index e29a9a1..8828533 100644 --- a/plugins/bot_status.py +++ b/plugins/bot_status.py @@ -6,6 +6,7 @@ Bot 状态查询插件 import os import psutil import time +import asyncio from datetime import datetime, timedelta from core.bot import Bot @@ -32,15 +33,23 @@ def _get_system_info(): """ 同步函数:使用 psutil 获取系统信息,避免阻塞事件循环。 """ - # interval=1 会阻塞1秒,必须在线程池中运行 - cpu_percent = psutil.cpu_percent(interval=1) - mem_info = psutil.virtual_memory() - bot_mem_mb = PROCESS.memory_info().rss / (1024 * 1024) - return { - "cpu_percent": f"{cpu_percent:.1f}", - "mem_percent": f"{mem_info.percent:.1f}", - "bot_mem_mb": f"{bot_mem_mb:.2f}", - } + try: + # interval=1 会阻塞1秒,必须在线程池中运行 + cpu_percent = psutil.cpu_percent(interval=1) + mem_info = psutil.virtual_memory() + bot_mem_mb = PROCESS.memory_info().rss / (1024 * 1024) + return { + "cpu_percent": f"{cpu_percent:.1f}", + "mem_percent": f"{mem_info.percent:.1f}", + "bot_mem_mb": f"{bot_mem_mb:.2f}", + } + except Exception as e: + logger.error(f"获取系统信息失败: {e}") + return { + "cpu_percent": "N/A", + "mem_percent": "N/A", + "bot_mem_mb": "N/A", + } @matcher.command("status", "状态") async def handle_status(bot: Bot, event: MessageEvent, args: list[str]): @@ -93,26 +102,51 @@ async def handle_status(bot: Bot, event: MessageEvent, args: list[str]): } # 3. 获取统计数据 - msgs_recv = await redis_manager.get("neobot:stats:messages_received") or 0 - msgs_sent = await redis_manager.get("neobot:stats:messages_sent") or 0 - command_stats_raw = await redis_manager.redis.hgetall("neobot:command_stats") - - total_commands = sum(int(v) for v in command_stats_raw.values()) - - stats_data = { - "messages_received": int(msgs_recv), - "messages_sent": int(msgs_sent), - "total_commands": total_commands, - } + try: + msgs_recv = await redis_manager.get("neobot:stats:messages_received") or 0 + msgs_sent = await redis_manager.get("neobot:stats:messages_sent") or 0 + command_stats_raw = await redis_manager.redis.hgetall("neobot:command_stats") + + total_commands = sum(int(v) for v in command_stats_raw.values()) + + stats_data = { + "messages_received": int(msgs_recv), + "messages_sent": int(msgs_sent), + "total_commands": total_commands, + } - command_stats_data = sorted( - [{"name": k, "count": int(v)} for k, v in command_stats_raw.items()], - key=lambda x: x["count"], - reverse=True - ) + command_stats_data = sorted( + [{"name": k, "count": int(v)} for k, v in command_stats_raw.items()], + key=lambda x: x["count"], + reverse=True + ) + except Exception as e: + logger.error(f"获取Redis统计数据失败: {e}") + stats_data = { + "messages_received": 0, + "messages_sent": 0, + "total_commands": 0, + } + command_stats_data = [] # 4. 异步获取系统信息 - system_data = await run_in_thread_pool(_get_system_info) + # 设置超时,防止 psutil 阻塞过久 + try: + system_data = await asyncio.wait_for(run_in_thread_pool(_get_system_info), timeout=5.0) + except asyncio.TimeoutError: + logger.error("获取系统信息超时") + system_data = { + "cpu_percent": "Timeout", + "mem_percent": "Timeout", + "bot_mem_mb": "Timeout", + } + except Exception as e: + logger.error(f"获取系统信息异常: {e}") + system_data = { + "cpu_percent": "Error", + "mem_percent": "Error", + "bot_mem_mb": "Error", + } # 5. 准备模板所需的所有数据 template_data = { @@ -125,18 +159,22 @@ async def handle_status(bot: Bot, event: MessageEvent, args: list[str]): } # 6. 渲染图片 - base64_str = await image_manager.render_template_to_base64( - template_name="status.html", - data=template_data, - output_name="status.png", - image_type="png" - ) + try: + base64_str = await image_manager.render_template_to_base64( + template_name="status.html", + data=template_data, + output_name="status.png", + image_type="png" + ) - if base64_str: - await event.reply(MessageSegment.image(base64_str)) - else: - # 如果渲染失败,image_manager 内部会记录错误,这里给用户一个通用提示 - await event.reply("状态图片生成失败,可能是渲染服务出现问题,请联系管理员。") + if base64_str: + await event.reply(MessageSegment.image(base64_str)) + else: + # 如果渲染失败,image_manager 内部会记录错误,这里给用户一个通用提示 + await event.reply("状态图片生成失败,可能是渲染服务出现问题,请联系管理员。") + except Exception as e: + logger.error(f"渲染图片失败: {e}") + await event.reply("状态图片渲染过程中发生错误。") except Exception as e: logger.exception(f"生成状态图时发生意外错误, 用户: {event.user_id}")