架构概览
Lingchu Bot 是一个以 NoneBot2 为基础的应用侧机器人项目。当前代码将核心插件、命令处理、配置、存储和数据库辅助能力拆分在 src/plugins/nonebot_plugin_lingchu_bot 下。
pyproject.toml:声明插件目录、依赖、适配器和 NoneBot 插件配置,是当前本地插件加载配置的来源。Dockerfile/docker-compose.yml:容器运行入口,构建阶段通过nb-cli生成运行用/tmp/bot.py。nonebot_plugin_lingchu_bot:核心插件包,声明插件元数据并加载共享能力。
文件夹src/plugins/nonebot_plugin_lingchu_bot/
文件夹core/
- async_utils.py
- bot_state.py
- config.py
- handle_config_manager.py
- http_security.py
- menu_config.py
- mutable_settings.py
- schemas.py
文件夹handle_config_defaults/
- …
文件夹subplugins/
文件夹llm_chat/
- …
文件夹novelai_image/
- …
文件夹database/
- _dialect_compat.py
文件夹models/
- …
文件夹orm_crud/
- …
文件夹toml_store/
- …
文件夹handle/
- menu.py
文件夹qq/
文件夹commands/
- triggers.py
- mute.py
- member.py
- block.py
- announcement.py
- remote.py
文件夹adapters/
文件夹onebot11/
文件夹default/
- …
文件夹napcat/
- …
文件夹hooks/
- adapters.py
- interfaces.py
文件夹handlers/
- …
文件夹i18n/
- init .py
- babel.cfg
文件夹locales/
文件夹en_US/
- …
文件夹zh_CN/
- …
文件夹migrations/
- 30f5a01259cd_initial_schema.py
- a1b2c3d4e5f6_permissions_schema.py
- b7c8d9e0f1a2_event_policy_partitions.py
- c3d4e5f6a7b8_scheduler_jobs.py
- cf2c06d51a17_blocklist_unique_constraint.py
文件夹permissions/
- init .py
- admin.py
- bootstrap.py
- config.py
- platforms.py
- service.py
- subject_policy.py
- types.py
文件夹platforms/
- registry.py
文件夹qq/
- permissions.py
文件夹repositories/
- init .py
- blocklist.py
- message_store.py
- permissions.py
- registry.py
- scheduler_jobs.py
文件夹services/
- message_store.py
- protocol_restart_feedback.py
- scheduler.py
文件夹llm/
- …
文件夹start/
- startup.py
文件夹apps/docs/
文件夹content/
- …
文件夹src/
- …
文件夹apps/lingc-cli/
文件夹src/
- …
文件夹tests/
- …
- pyproject.toml
- Taskfile.yml
- turbo.json
- Dockerfile
| 模块 | 职责 |
|---|---|
core/config.py |
仅包含部署字段的 Config pydantic 模型,由 NoneBot get_plugin_config() 解析 |
core/mutable_settings.py |
面向在线可编辑 MutableRuntimeSettings 的 typed localstore 仓库 |
core/handle_config_manager.py |
HandleConfigManager —— 基于 pydantic 校验的 per-command TOML 配置 |
core/handle_config_defaults/ |
通过 register_handle_defaults() 注册的 per-command pydantic BaseModel 默认值 |
core/schemas.py |
Schema 定义与供 CLI 显式调用的安装支持 |
core/bot_state.py |
由 BotStateFile(BaseModel) 支持的 bot_state.toml 持久化 |
core/subplugins/ |
子插件契约、加载器与内置子插件(llm_chat、novelai_image) |
database/toml_store/ |
TOML 文件存储包 |
database/models/ |
ORM 模型包(message、blocklist、registry、identity) |
database/orm_crud/ |
异步 CRUD 辅助(session 首参签名;参见存储与 ORM) |
migrations/ |
Alembic 数据库迁移 |
handle/menu.py |
菜单系统(页面、分区、功能、可用性) |
handle/qq/commands/ |
共享 QQ 命令定义(Alconna 匹配器、触发词) |
handle/qq/adapters/onebot11/ |
OneBot V11 处理器(default、napcat) |
hooks/ |
NoneBot 事件生命周期处理器(lifecycle、bot_connection、message_store、api_audit) |
i18n/__init__.py |
gettext/Babel 翻译辅助、locale 读取和异步 catalog 预热 |
platforms/registry.py |
平台能力与具体适配器的映射、优先级选择和同平台互斥校验 |
platforms/qq/permissions.py |
QQ 默认身份组与运行时平台身份解析 |
permissions/ |
UID 身份、平台账号、身份组成员、命令授权和 SUPERUSERS API |
repositories/blocklist.py |
黑名单数据访问 |
repositories/message_store.py |
消息存储仓库 |
repositories/permissions.py |
权限系统 ORM 仓库 |
services/message_store.py |
消息事件、处理结果、Bot 生命周期和平台 API 调用摘要记录 |
services/llm/ |
LLM 服务抽象(后端、能力、运行时、安全) |
start/ |
启动钩子 |
插件按平台能力声明适配器,而不是把所有导入的具体适配器都视为可用。当前已实现 QQ 平台 profile,启动流程中仅 ~onebot.v11 可用。其他适配器 ID(如 ~milky、~qq、~onebot.v12)未实现;配置其中任何一个会以清晰的 PlatformAdapterUnknownError 退出。未配置时默认启用 OneBot V11。
~onebot.v11 启用中 ~milky~qq~onebot.v12 已移除
同一平台显式配置多个已知适配器会抛出 PlatformAdapterConflictError。Lingchu Bot 不控制 NoneBot 实际导入或注册哪些适配器;它只校验被选中的适配器已经由 NoneBot 加载/注册。未配置时默认选择 ~onebot.v11,如果 OneBot V11 未加载会抛出 PlatformAdapterNotLoadedError。其他同平台适配器即使被导入或注册,也会被视为未启用,不参与平台识别、消息存储或 API 调用记录。LINGCHUAdapter 中显式声明 Lingchu 未实现或无法识别的适配器会启动失败;运行时额外出现的未知适配器不会被 is_adapter_enabled() 判定为已启用,消息存储会把它们归入统一的 unknown 平台。其他平台 profile 和非群管理能力以后续实现和测试为准。
平台权限模块现在通过适配器注册表中的 PlatformProfile.permission_module 字段动态发现,消除了硬编码的模块路径。这实现了平台标识的统一管理。
数据与配置边界
Section titled “数据与配置边界”项目倾向使用 NoneBot 插件配置、localstore 和 ORM 插件提供的能力,而不是在业务代码中硬编码路径或数据库连接。
文件存储、TOML 解析、deepcopy、命令解析和翻译 catalog 加载等潜在阻塞路径会放到 worker thread 执行,避免阻塞 NoneBot 事件循环。
插件启动不会创建部署配置文件,也不会安装 JSON Schema 文件。需要编辑器 schema 时,请手动用 pydantic 的 model_json_schema() 生成,或交由 nonebot 配置管理处理。
Lingchu Bot 以 pydantic 作为部署配置、运行时可变设置、handle 与 bot-state 配置的真源。NoneBot 通过 get_plugin_config(Config) 解析部署字段;在线可编辑设置使用独立的 typed localstore 仓库。
| 场面 | Pydantic 模型 | 备注 |
|---|---|---|
插件 env / .env |
core/config.py::Config(继承 DeploymentSettings) |
NoneBot get_plugin_config() 是部署字段的唯一解析入口。 |
| 在线可编辑设置 | _lingchu_bot_contracts::MutableRuntimeSettings |
由 core/mutable_settings.py 持久化到 localstore 管理的 runtime-overrides.toml。 |
| Per-command handle 配置 | core/handle_config_defaults/<command>.py::<Model>(BaseModel) |
通过 register_handle_defaults() 注册;HANDLE_DEFAULTS_REGISTRY 类型为 dict[str, type[BaseModel]]。HandleConfigManager.get_config() / update_config() 使用 type_validate_python(model_cls, toml_dict)。HandleConfig dataclass 仍持有 dict[str, Any] 以保留 frozen-dataclass 接口;_build_handle_config 通过 model_dump(mode="json") 桥接 pydantic ↔ dict 边界。 |
| Bot 状态 | core/bot_state.py::BotStateFile(BaseModel) |
使用 Field(alias="global") + populate_by_name=True 桥接 global Python 关键字;_save_bot_state() 以 model_dump(mode="json", by_alias=True) 序列化。 |
| JSON Schema 文件 | 手动用 pydantic model_json_schema() 生成 |
pydantic 负责 JSON Schema 输出;启动过程不会安装 schema。 |
新增 handle 配置
Section titled “新增 handle 配置”- 在
core/handle_config_defaults/<command>.py中声明pydantic.BaseModel子类。 - 通过
register_handle_defaults("<command_key>", <Model>)注册。 - 运行
task db:check;需要编辑器 schema 时,再用 pydantic 重新生成。 - 对应的
<command_key>.toml在首次读取时创建;用户可以编辑它,HandleConfigManager.get_config(<command_key>)会通过 pydantic 校验。
不要返回裸 dict 默认值 —— 校验和 JSON Schema 输出由 pydantic 拥有。HandleConfigManager.validate_config() 已移除;非法输入由 pydantic 直接抛错。