Files
NeoBot/core/managers/command_manager.py
K2cr2O1 5d07a84283 feat: 重构核心架构,增强类型安全与插件管理
本次提交对核心模块进行了深度重构,引入 Pydantic 增强配置管理的类型安全性,并全面优化了插件管理系统。

主要变更详情:

1. 核心架构与配置
   - 重构配置加载模块:引入 Pydantic 模型 (`core/config_models.py`),提供严格的配置项类型检查、验证及默认值管理。
   - 统一模块结构:规范化模块导入路径,移除冗余的 `__init__.py` 文件,提升项目结构的清晰度。
   - 性能优化:集成 Redis 缓存支持 (`RedisManager`),有效降低高频 API 调用开销,提升响应速度。

2. 插件系统升级
   - 实现热重载机制:新增插件文件变更监听功能,支持开发过程中自动重载插件,提升开发效率。
   - 优化生命周期管理:改进插件加载与卸载逻辑,支持精确卸载指定插件及其关联的命令、事件处理器和定时任务。

3. 功能特性增强
   - 新增媒体 API:引入 `MediaAPI` 模块,封装图片、语音等富媒体资源的获取与处理接口。
   - 完善权限体系:重构权限管理系统,实现管理员与操作员的分级控制,支持更细粒度的命令权限校验。

4. 代码质量与稳定性
   - 全面类型修复:解决 `mypy` 静态类型检查发现的大量类型错误(包括 `CommandManager`、`EventFactory` 及 `Bot` API 签名不匹配问题)。
   - 增强错误处理:优化消息处理管道的异常捕获机制,完善关键路径的日志记录,提升系统运行稳定性。
2026-01-08 23:42:53 +08:00

174 lines
5.5 KiB
Python

"""
命令与事件管理器模块
该模块定义了 `CommandManager` 类,它是整个机器人框架事件处理的核心。
它通过装饰器模式,为插件提供了注册消息指令、通知事件处理器和
请求事件处理器的能力。
"""
from typing import Any, Callable, Dict, Optional, Tuple
from ..config_loader import global_config
from ..handlers.event_handler import MessageHandler, NoticeHandler, RequestHandler
# 从配置中获取命令前缀
_config_prefixes = global_config.bot.command
# 确保前缀配置是元组格式
_final_prefixes: Tuple[str, ...]
if isinstance(_config_prefixes, list):
_final_prefixes = tuple(_config_prefixes)
elif isinstance(_config_prefixes, str):
_final_prefixes = (_config_prefixes,)
else:
_final_prefixes = tuple(_config_prefixes)
class CommandManager:
"""
命令管理器,负责注册和分发所有类型的事件。
这是一个单例对象(`matcher`),在整个应用中共享。
它将不同类型的事件处理委托给专门的处理器类。
"""
def __init__(self, prefixes: Tuple[str, ...]):
"""
初始化命令管理器。
Args:
prefixes (Tuple[str, ...]): 一个包含所有合法命令前缀的元组。
"""
self.plugins: Dict[str, Dict[str, Any]] = {}
# 初始化专门的事件处理器
self.message_handler = MessageHandler(prefixes)
self.notice_handler = NoticeHandler()
self.request_handler = RequestHandler()
# 将处理器映射到事件类型
self.handler_map = {
"message": self.message_handler,
"notice": self.notice_handler,
"request": self.request_handler,
}
# 注册内置的 /help 命令
self._register_internal_commands()
def _register_internal_commands(self):
"""
注册框架内置的命令
"""
# Help 命令
self.message_handler.command("help")(self._help_command)
self.plugins["core.help"] = {
"name": "帮助",
"description": "显示所有可用指令的帮助信息",
"usage": "/help",
}
def clear_all_handlers(self):
"""
清空所有已注册的事件处理器。
注意:这也会移除内置的 /help 命令,因此需要重新注册。
"""
self.message_handler.clear()
self.notice_handler.clear()
self.request_handler.clear()
self.plugins.clear()
# 清空后,需要重新注册内置命令
self._register_internal_commands()
def unload_plugin(self, plugin_name: str):
"""
卸载指定插件的所有处理器和命令。
Args:
plugin_name (str): 插件的模块名 (例如 'plugins.bili_parser')
"""
self.message_handler.unregister_by_plugin_name(plugin_name)
self.notice_handler.unregister_by_plugin_name(plugin_name)
self.request_handler.unregister_by_plugin_name(plugin_name)
# 移除插件元信息
plugins_to_remove = [name for name in self.plugins if name.startswith(plugin_name)]
for name in plugins_to_remove:
del self.plugins[name]
# --- 装饰器代理 ---
def on_message(self) -> Callable:
"""
装饰器:注册一个通用的消息处理器。
"""
return self.message_handler.on_message()
def command(
self,
*names: str,
permission: Optional[Any] = None,
override_permission_check: bool = False
) -> Callable:
"""
装饰器:注册一个消息指令处理器。
"""
return self.message_handler.command(
*names,
permission=permission,
override_permission_check=override_permission_check
)
def on_notice(self, notice_type: Optional[str] = None) -> Callable:
"""
装饰器:注册一个通知事件处理器。
"""
return self.notice_handler.register(notice_type=notice_type)
def on_request(self, request_type: Optional[str] = None) -> Callable:
"""
装饰器:注册一个请求事件处理器。
"""
return self.request_handler.register(request_type=request_type)
# --- 事件处理 ---
async def handle_event(self, bot, event):
"""
统一的事件分发入口。
根据事件的 `post_type` 将其分发给对应的处理器。
"""
if event.post_type == 'message' and global_config.bot.ignore_self_message:
if hasattr(event, 'user_id') and hasattr(event, 'self_id') and event.user_id == event.self_id:
return
handler = self.handler_map.get(event.post_type)
if handler:
await handler.handle(bot, event)
# --- 内置命令实现 ---
async def _help_command(self, bot, event):
"""
内置的 `/help` 命令的实现。
"""
help_text = "--- 可用指令列表 ---\n"
for plugin_name, meta in self.plugins.items():
name = meta.get("name", "未命名插件")
description = meta.get("description", "暂无描述")
usage = meta.get("usage", "暂无用法说明")
help_text += f"\n{name}:\n"
help_text += f" 功能: {description}\n"
help_text += f" 用法: {usage}\n"
await bot.send(event, help_text.strip())
# 实例化全局唯一的命令管理器
matcher = CommandManager(prefixes=_final_prefixes)