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

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

主要变更详情:

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

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

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

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

View File

@@ -1,97 +0,0 @@
from .events.base import OneBotEvent
from .events.factory import EventFactory
from .events.message import (
GroupMessageEvent,
MessageEvent,
MessageSegment,
PrivateMessageEvent,
)
from .events.meta import HeartbeatEvent, HeartbeatStatus, LifeCycleEvent, MetaEvent
from .events.notice import (
ClientStatus,
ClientStatusNoticeEvent,
EssenceNoticeEvent,
FriendAddNoticeEvent,
FriendRecallNoticeEvent,
GroupAdminNoticeEvent,
GroupBanNoticeEvent,
GroupCardNoticeEvent,
GroupDecreaseNoticeEvent,
GroupIncreaseNoticeEvent,
GroupRecallNoticeEvent,
GroupUploadFile,
GroupUploadNoticeEvent,
HonorNotifyEvent,
LuckyKingNotifyEvent,
NoticeEvent,
NotifyNoticeEvent,
OfflineFile,
OfflineFileNoticeEvent,
PokeNotifyEvent,
)
from .events.request import FriendRequestEvent, GroupRequestEvent, RequestEvent
from .objects import (
CurrentTalkative,
EssenceMessage,
FriendInfo,
GroupHonorInfo,
GroupInfo,
GroupMemberInfo,
HonorInfo,
LoginInfo,
Status,
StrangerInfo,
VersionInfo,
)
# Alias for backward compatibility
Event = OneBotEvent
__all__ = [
"MessageSegment",
"Sender",
"OneBotEvent",
"Event",
"MessageEvent",
"PrivateMessageEvent",
"GroupMessageEvent",
"NoticeEvent",
"FriendAddNoticeEvent",
"FriendRecallNoticeEvent",
"GroupRecallNoticeEvent",
"GroupIncreaseNoticeEvent",
"GroupDecreaseNoticeEvent",
"GroupAdminNoticeEvent",
"GroupBanNoticeEvent",
"GroupUploadNoticeEvent",
"GroupUploadFile",
"NotifyNoticeEvent",
"PokeNotifyEvent",
"LuckyKingNotifyEvent",
"HonorNotifyEvent",
"GroupCardNoticeEvent",
"OfflineFileNoticeEvent",
"OfflineFile",
"ClientStatusNoticeEvent",
"ClientStatus",
"EssenceNoticeEvent",
"RequestEvent",
"FriendRequestEvent",
"GroupRequestEvent",
"MetaEvent",
"HeartbeatEvent",
"LifeCycleEvent",
"HeartbeatStatus",
"EventFactory",
"GroupInfo",
"GroupMemberInfo",
"FriendInfo",
"StrangerInfo",
"LoginInfo",
"VersionInfo",
"Status",
"EssenceMessage",
"GroupHonorInfo",
"CurrentTalkative",
"HonorInfo",
]

View File

@@ -252,9 +252,18 @@ class EventFactory:
card_new=data.get("card_new", ""),
card_old=data.get("card_old", "")
)
elif notice_type == "group_card":
return GroupCardNoticeEvent(
**common_args,
notice_type=notice_type,
group_id=data.get("group_id", 0),
user_id=data.get("user_id", 0),
card_new=data.get("card_new", ""),
card_old=data.get("card_old", "")
)
elif notice_type == "offline_file":
file_data = data.get("file", {})
file = OfflineFile(
offline_file = OfflineFile(
name=file_data.get("name", ""),
size=file_data.get("size", 0),
url=file_data.get("url", "")
@@ -263,7 +272,7 @@ class EventFactory:
**common_args,
notice_type=notice_type,
user_id=data.get("user_id", 0),
file=file
file=offline_file
)
elif notice_type == "client_status":
client_data = data.get("client", {})

View File

@@ -4,9 +4,9 @@
定义了消息相关的事件类,包括 MessageEvent, PrivateMessageEvent, GroupMessageEvent。
"""
from dataclasses import dataclass, field
from typing import List, Optional
from typing import List, Optional, Union
from core.managers.permission_manager import ADMIN, OP, USER
from core.permission import Permission
from models.message import MessageSegment
from models.sender import Sender
from .base import OneBotEvent, EventType
@@ -34,9 +34,9 @@ class MessageEvent(OneBotEvent):
"""
# 权限级别常量,用于装饰器参数
ADMIN = ADMIN
OP = OP
USER = USER
ADMIN = Permission.ADMIN
OP = Permission.OP
USER = Permission.USER
message_type: str
"""消息类型: private (私聊), group (群聊)"""
@@ -70,7 +70,7 @@ class MessageEvent(OneBotEvent):
def post_type(self) -> str:
return EventType.MESSAGE
async def reply(self, message: str, auto_escape: bool = False):
async def reply(self, message: Union[str, "MessageSegment", List["MessageSegment"]], auto_escape: bool = False):
"""
回复消息(抽象方法,由子类实现)
@@ -86,7 +86,7 @@ class PrivateMessageEvent(MessageEvent):
私聊消息事件
"""
async def reply(self, message: str, auto_escape: bool = False):
async def reply(self, message: Union[str, "MessageSegment", List["MessageSegment"]], auto_escape: bool = False):
"""
回复私聊消息
@@ -110,7 +110,7 @@ class GroupMessageEvent(MessageEvent):
anonymous: Optional[Anonymous] = None
"""匿名信息"""
async def reply(self, message: str, auto_escape: bool = False):
async def reply(self, message: Union[str, "MessageSegment", List["MessageSegment"]], auto_escape: bool = False):
"""
回复群聊消息

View File

@@ -63,5 +63,5 @@ class LifeCycleEvent(MetaEvent):
meta_event_type: str = 'lifecycle'
"""元事件类型:生命周期事件"""
sub_type: LifeCycleSubType = LifeCycleSubType.ENABLE
sub_type: str = LifeCycleSubType.ENABLE
"""子类型:启用、禁用、连接"""

View File

@@ -6,7 +6,7 @@
"""
from dataclasses import dataclass
from typing import Any, Dict
from typing import Any, Dict, Optional
@dataclass(slots=True)
@@ -76,7 +76,7 @@ class MessageSegment:
return self.data.get("file", "")
return ""
def is_at(self, user_id: int = None) -> bool:
def is_at(self, user_id: Optional[int] = None) -> bool:
"""
检查当前消息段是否是一个 'at' (提及) 消息段。
@@ -102,7 +102,7 @@ class MessageSegment:
# --- 快捷构造方法 ---
@staticmethod
def text(text: str) -> "MessageSegment": # noqa: F811
def from_text(text: str) -> "MessageSegment":
"""
创建一个文本消息段。
@@ -115,7 +115,7 @@ class MessageSegment:
return MessageSegment(type="text", data={"text": text})
@staticmethod
def at(user_id: int | str, name: str = None) -> "MessageSegment":
def at(user_id: int | str, name: Optional[str] = None) -> "MessageSegment":
"""
创建一个 @某人 的消息段。
@@ -132,7 +132,7 @@ class MessageSegment:
return MessageSegment(type="at", data=data)
@staticmethod
def image(file: str, image_type: str = None, cache: bool = True, proxy: bool = True, timeout: int = None, sub_type: int = None) -> "MessageSegment":
def image(file: str, image_type: Optional[str] = None, cache: bool = True, proxy: bool = True, timeout: Optional[int] = None, sub_type: Optional[int] = None) -> "MessageSegment":
"""
创建一个图片消息段。
@@ -194,7 +194,7 @@ class MessageSegment:
"""
return MessageSegment(type="xml", data={"data": data})
@staticmethod
def share(url: str, title: str, content: str = None, image: str = None) -> "MessageSegment":
def share(url: str, title: str, content: Optional[str] = None, image: Optional[str] = None) -> "MessageSegment":
"""
创建一个分享消息段。
@@ -227,7 +227,7 @@ class MessageSegment:
"""
return MessageSegment(type="music", data={"type": type, "id": id})
@staticmethod
def music_custom(url: str, audio: str, title: str, content: str = None, image: str = None) -> "MessageSegment":
def music_custom(url: str, audio: str, title: str, content: Optional[str] = None, image: Optional[str] = None) -> "MessageSegment":
"""
创建一个自定义音乐消息段。
@@ -248,7 +248,7 @@ class MessageSegment:
data["image"] = image
return MessageSegment(type="music", data={"type": "custom", **data})
@staticmethod
def record(file: str, magic: bool = False, cache: bool = True, proxy: bool = True, timeout: int = None) -> "MessageSegment":
def record(file: str, magic: bool = False, cache: bool = True, proxy: bool = True, timeout: Optional[int] = None) -> "MessageSegment":
"""
创建一个语音消息段。
@@ -267,7 +267,7 @@ class MessageSegment:
data["timeout"] = str(timeout)
return MessageSegment(type="record", data=data)
@staticmethod
def video(file: str, cover: str = None, c: int = 2) -> "MessageSegment":
def video(file: str, cover: Optional[str] = None, c: int = 2) -> "MessageSegment":
"""
创建一个视频消息段。