* 滚木

* feat: 重构核心架构,增强类型安全与插件管理

本次提交对核心模块进行了深度重构,引入 Pydantic 增强配置管理的类型安全性,并全面优化了插件管理系统。

主要变更详情:

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

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

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

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

* feat: 添加测试用例并优化代码结构

refactor(permission_manager): 调整初始化顺序和逻辑
fix(admin_manager): 修复初始化逻辑和目录创建问题
feat(ws): 优化Bot实例初始化条件
feat(message): 增强MessageSegment功能并添加测试
feat(events): 支持字符串格式的消息解析
test: 添加核心功能测试用例
refactor(plugin_manager): 改进插件路径处理
style: 清理无用导入和代码
chore: 更新依赖项

* refactor(handler): 移除TYPE_CHECKING并直接导入Bot类

简化类型注解,直接导入Bot类而非使用TYPE_CHECKING条件导入,提高代码可读性和维护性

* fix(command_manager): 修复插件卸载时元信息移除不精确的问题

修复 CommandManager 中 unload_plugin 方法移除插件元信息时使用 startswith 导致可能误删其他插件的问题,改为精确匹配
同时调整相关测试用例验证精确匹配行为

* refactor: 清理未使用的导入和更新文档结构

docs: 添加config_models.py到项目结构文档
docs: 调整数据目录位置到core/data下
docs: 更新权限管理器文档描述

* 文档更新

* 更新thpic插件 支持一次返回多张图

---------

Co-authored-by: baby20162016 <2185823427@qq.com>
This commit is contained in:
镀铬酸钾
2026-01-09 23:10:58 +08:00
committed by GitHub
parent 61c8d6b328
commit 5f16c288bf
9 changed files with 87 additions and 23 deletions

View File

@@ -74,12 +74,12 @@
│ └── thpic.py │ └── thpic.py
├── core/ # NEO 框架核心代码,通常无需修改 ├── core/ # NEO 框架核心代码,通常无需修改
│ ├── api/ │ ├── api/
│ ├── data/ # 数据存储目录 (管理员列表, 权限配置)
│ │ ├── admin.json
│ │ └── permissions.json
│ ├── bot.py │ ├── bot.py
│ ├── ... │ ├── ...
│ └── ws.py │ └── ws.py
├── data/ # 数据存储目录 (管理员列表, 权限配置)
│ ├── admin.json
│ └── permissions.json
├── html/ # 静态网页文件 ├── html/ # 静态网页文件
├── plugins/ # 插件目录,所有机器人的功能模块都在这里 ├── plugins/ # 插件目录,所有机器人的功能模块都在这里
│ ├── admin.py │ ├── admin.py

View File

@@ -4,7 +4,6 @@
这个包集中了机器人核心的单例管理器。 这个包集中了机器人核心的单例管理器。
通过从这里导入,可以确保在整个应用中访问到的都是同一个实例。 通过从这里导入,可以确保在整个应用中访问到的都是同一个实例。
""" """
from ..config_loader import global_config
from .admin_manager import AdminManager from .admin_manager import AdminManager
from .command_manager import matcher as command_manager from .command_manager import matcher as command_manager
from .permission_manager import PermissionManager from .permission_manager import PermissionManager

View File

@@ -13,7 +13,7 @@
""" """
import json import json
import os import os
from typing import Dict, Optional from typing import Dict
from ..utils.logger import logger from ..utils.logger import logger
from ..utils.singleton import Singleton from ..utils.singleton import Singleton

View File

@@ -8,15 +8,51 @@
```mermaid ```mermaid
graph TD graph TD
A[OneBot v11 实现端] -- WebSocket Message --> B(core/ws.py); %% 定义样式
B -- Raw JSON Data --> C(models/events/factory.py); classDef external fill:#e1f5fe,stroke:#01579b,stroke-width:2px;
C -- Event Object --> D(core/ws.py on_event); classDef network fill:#fff9c4,stroke:#fbc02d,stroke-width:2px;
D -- Event Object --> E(core/managers/command_manager.py); classDef core fill:#e8f5e9,stroke:#2e7d32,stroke-width:2px;
E -- Event & Command Match --> F(core/handlers/event_handler.py); classDef plugin fill:#fce4ec,stroke:#c2185b,stroke-width:2px;
F -- Matched Handler --> G(plugins/echo.py);
G -- Call API --> H(core/bot.py); subgraph External [外部环境]
H -- Send Request --> B; OneBot[OneBot v11 实现端<br/>(如 NapCatQQ)]:::external
B -- WebSocket Send --> A; end
subgraph NeoBot [NEO Bot Framework]
direction TB
subgraph Network [网络接入层]
WS[WebSocket 连接<br/>core/ws.py]:::network
end
subgraph Processing [核心处理层]
Factory[事件工厂<br/>models/events/factory.py]:::core
Dispatcher[命令管理器<br/>core/managers/command_manager.py]:::core
Handler[事件处理器<br/>core/handlers/event_handler.py]:::core
BotAPI[Bot API 封装<br/>core/bot.py]:::core
end
subgraph Plugins [业务插件层]
UserPlugin[用户插件<br/>plugins/*.py]:::plugin
end
end
%% 事件上报流程 (实线)
OneBot -- 1. WebSocket 消息 --> WS
WS -- 2. 原始 JSON --> Factory
Factory -- 3. Event 对象 --> WS
WS -- 4. 分发事件 --> Dispatcher
Dispatcher -- 5. 匹配指令/事件 --> Handler
Handler -- 6. 调用处理函数 --> UserPlugin
%% API 调用流程 (虚线)
UserPlugin -. 7. 调用 bot.send() .-> BotAPI
BotAPI -. 8. 封装 API 请求 .-> WS
WS -. 9. 发送 JSON .-> OneBot
%% 链接样式
linkStyle 0,1,2,3,4,5 stroke:#333,stroke-width:2px;
linkStyle 6,7,8 stroke:#666,stroke-width:2px,stroke-dasharray: 5 5;
``` ```
## 详细步骤 ## 详细步骤

View File

@@ -36,7 +36,7 @@
* **核心职责**: * **核心职责**:
* **权限定义与检查**: 定义了 `ADMIN`, `OP`, `USER` 等权限等级,并提供了 `check_permission` 方法来验证用户权限。 * **权限定义与检查**: 定义了 `ADMIN`, `OP`, `USER` 等权限等级,并提供了 `check_permission` 方法来验证用户权限。
* **数据持久化**: 负责从 `core/data/permissions.json` 文件中加载和保存用户权限设置。 * **数据持久化**: 负责从 `core/data/permissions.json` 文件中加载和保存用户权限设置。
* **与 `AdminManager` 联动**: 在检查权限时,会自动机器人管理员(来自 `AdminManager`)识别为最高权限 `ADMIN` * **与 `AdminManager` 联动**: 在检查权限和获取所有用户权限时,会自动合并机器人管理员(来自 `AdminManager`的数据,将其识别为最高权限 `ADMIN`
### 3. `AdminManager` (全局实例: `admin_manager`) ### 3. `AdminManager` (全局实例: `admin_manager`)

View File

@@ -40,6 +40,7 @@
* `utils/`: 提供被广泛使用的工具类,如 `logger` (日志)、`singleton` (单例模式基类)。 * `utils/`: 提供被广泛使用的工具类,如 `logger` (日志)、`singleton` (单例模式基类)。
* `bot.py`: 定义了 `Bot` 类,这是插件开发者最常与之交互的对象,用于调用所有 OneBot API。 * `bot.py`: 定义了 `Bot` 类,这是插件开发者最常与之交互的对象,用于调用所有 OneBot API。
* `config_loader.py`: 负责解析 `config.toml` 文件,并提供一个全局的 `global_config` 对象。 * `config_loader.py`: 负责解析 `config.toml` 文件,并提供一个全局的 `global_config` 对象。
* `config_models.py`: 使用 Pydantic 定义了配置文件的结构和类型验证。
* `ws.py`: 实现了与 OneBot v11 实现端的 WebSocket 连接、心跳、重连和消息收发。 * `ws.py`: 实现了与 OneBot v11 实现端的 WebSocket 连接、心跳、重连和消息收发。
### `docs/` ### `docs/`

View File

@@ -1,4 +1,3 @@
from core.handlers.event_handler import MessageHandler
from core.managers import command_manager, permission_manager from core.managers import command_manager, permission_manager
from core.permission import Permission from core.permission import Permission
from models.events.message import MessageEvent from models.events.message import MessageEvent

View File

@@ -12,7 +12,7 @@ from models.events.message import MessageEvent, MessageSegment
__plugin_meta__ = { __plugin_meta__ = {
"name": "thpic", "name": "thpic",
"description": "来看看东方Project的图片吧", "description": "来看看东方Project的图片吧",
"usage": "/thpic", "usage": "/thpic [nums](1~10)",
} }
@@ -25,7 +25,38 @@ async def handle_echo(bot: Bot, event: MessageEvent, args: list[str]):
:param event: 消息事件对象。 :param event: 消息事件对象。
:param args: 指令参数列表(未使用)。 :param args: 指令参数列表(未使用)。
""" """
try: parts = args
await event.reply(str(MessageSegment.image("https://img.paulzzh.com/touhou/random"))) print(parts)
except Exception as e: if not parts:
await event.reply(f"报错了。。。{e}") try:
await event.reply(
str(MessageSegment.image("https://img.paulzzh.com/touhou/random"))
)
except Exception as e:
await event.reply(f"报错了。。。{e}")
else:
if parts[0].isdigit():
nums = int(parts[0])
if nums <= 0:
await event.reply("请输入一个大于0的整数。")
return
elif nums > 10:
await event.reply("请输入一个不大于10的整数。")
return
try:
nodes = []
for _ in range(nums):
nodes.append(
bot.build_forward_node(
user_id=event.self_id,
nickname="机器人",
message=MessageSegment.image(
"https://img.paulzzh.com/touhou/random"
),
)
)
await bot.send_forwarded_messages(event, nodes)
except Exception as e:
await event.reply(f"报错了。。。{e}")
else:
await event.reply(f"用法不正确。\n\n{__plugin_meta__['usage']}")

View File

@@ -1,6 +1,4 @@
import pytest
from unittest.mock import MagicMock
from core.managers.command_manager import CommandManager from core.managers.command_manager import CommandManager
class TestPluginReloadMeta: class TestPluginReloadMeta: