抽象send方法,添加注释

This commit is contained in:
2026-01-01 17:58:17 +08:00
parent 9146ffbb1a
commit 046dd0860f
9 changed files with 366 additions and 38 deletions

View File

@@ -1,22 +1,25 @@
"""
Echo 插件
提供 /echo 指令,用于原样回复用户输入的内容。
"""
from core.command_manager import matcher
from ..core.ws import WS
from ..models.event import Event
from core.bot import Bot
from models.event import Event
# TODO 把该死的这些给抽象化
@matcher.command("echo")
async def handle_echo(bot: WS, event: Event, args: list[str]):
async def handle_echo(bot: Bot, event: Event, args: list[str]):
"""
处理 echo 指令,原样回复用户输入的内容
:param bot: Bot 实例
:param event: 消息事件对象
:param args: 指令参数列表
"""
if not args:
reply_msg = "请在指令后输入要回复的内容,例如:/echo 你好"
else:
reply_msg = " ".join(args)
if event.message_type == "group":
await bot.call_api(
"send_group_msg", {"group_id": event.group_id, "message": reply_msg}
)
else:
await bot.call_api(
"send_private_msg", {"user_id": event.user_id, "message": reply_msg}
)
await event.reply(reply_msg)

72
core/bot.py Normal file
View File

@@ -0,0 +1,72 @@
"""
Bot 抽象模块
定义了 Bot 类,封装了 OneBot API 的调用逻辑,提供了便捷的消息发送方法。
"""
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from .ws import WS
from ..models.event import Event
class Bot:
"""
Bot 抽象类,封装 API 调用和常用操作
"""
def __init__(self, ws_client: "WS"):
"""
初始化 Bot 实例
:param ws_client: WebSocket 客户端实例,用于底层通信
"""
self.ws = ws_client
async def call_api(self, action: str, params: dict = None) -> dict:
"""
调用 OneBot API
:param action: API 动作名称
:param params: API 参数
:return: API 响应结果
"""
return await self.ws.call_api(action, params)
async def send_group_msg(self, group_id: int, message: str) -> dict:
"""
发送群消息
:param group_id: 群号
:param message: 消息内容
:return: API 响应结果
"""
return await self.call_api(
"send_group_msg", {"group_id": group_id, "message": message}
)
async def send_private_msg(self, user_id: int, message: str) -> dict:
"""
发送私聊消息
:param user_id: 用户 QQ 号
:param message: 消息内容
:return: API 响应结果
"""
return await self.call_api(
"send_private_msg", {"user_id": user_id, "message": message}
)
async def send(self, event: "Event", message: str) -> dict:
"""
智能发送消息,根据事件类型自动选择发送方式
:param event: 触发事件对象
:param message: 消息内容
:return: API 响应结果
"""
if event.message_type == "group" and event.group_id:
return await self.send_group_msg(event.group_id, message)
elif event.user_id:
return await self.send_private_msg(event.user_id, message)
return {"status": "failed", "msg": "Unknown message target"}

View File

@@ -1,3 +1,8 @@
"""
命令管理器模块
提供装饰器用于注册消息指令、通知处理器和请求处理器,并负责事件的分发。
"""
import inspect
from typing import Any, Callable, Dict, List, Tuple
@@ -8,7 +13,16 @@ comm_prefixes = global_config.bot.get("command", ("/",))
class CommandManager:
"""
命令管理器,负责注册和分发指令、通知和请求事件
"""
def __init__(self, prefixes: Tuple[str, ...] = ("/",)):
"""
初始化命令管理器
:param prefixes: 命令前缀元组
"""
self.prefixes = prefixes
self.commands: Dict[str, Callable] = {} # 存储消息指令
self.notice_handlers: List[Dict] = [] # 存储通知处理器
@@ -16,7 +30,12 @@ class CommandManager:
# --- 1. 消息指令装饰器 ---
def command(self, name: str):
"""装饰器:注册消息指令,例如 @matcher.command("echo")"""
"""
装饰器:注册消息指令
:param name: 指令名称(不含前缀)
:return: 装饰器函数
"""
def decorator(func):
self.commands[name] = func
@@ -26,7 +45,12 @@ class CommandManager:
# --- 2. 通知事件装饰器 ---
def on_notice(self, notice_type: str = None):
"""装饰器:注册通知处理器"""
"""
装饰器:注册通知处理器
:param notice_type: 通知类型,如果为 None 则处理所有通知
:return: 装饰器函数
"""
def decorator(func):
self.notice_handlers.append({"type": notice_type, "func": func})
@@ -36,7 +60,12 @@ class CommandManager:
# --- 3. 请求事件装饰器 ---
def on_request(self, request_type: str = None):
"""装饰器:注册请求处理器"""
"""
装饰器:注册请求处理器
:param request_type: 请求类型,如果为 None 则处理所有请求
:return: 装饰器函数
"""
def decorator(func):
self.request_handlers.append({"type": request_type, "func": func})
@@ -46,7 +75,12 @@ class CommandManager:
# --- 消息分发逻辑 ---
async def handle_message(self, bot, event):
"""解析并分发消息指令"""
"""
解析并分发消息指令
:param bot: Bot 实例
:param event: 消息事件对象
"""
if not event.raw_message:
return
@@ -77,21 +111,38 @@ class CommandManager:
# --- 通知分发逻辑 ---
async def handle_notice(self, bot, event):
"""分发通知事件"""
"""
分发通知事件
:param bot: Bot 实例
:param event: 通知事件对象
"""
for handler in self.notice_handlers:
if handler["type"] is None or handler["type"] == event.notice_type:
await self._run_handler(handler["func"], bot, event)
# --- 请求分发逻辑 ---
async def handle_request(self, bot, event):
"""分发请求事件"""
"""
分发请求事件
:param bot: Bot 实例
:param event: 请求事件对象
"""
for handler in self.request_handlers:
if handler["type"] is None or handler["type"] == event.request_type:
await self._run_handler(handler["func"], bot, event)
# --- 通用执行器:自动注入参数 ---
async def _run_handler(self, func, bot, event, args=None):
"""根据函数签名自动注入 bot, event 或 args"""
"""
根据函数签名自动注入 bot, event 或 args
:param func: 目标处理函数
:param bot: Bot 实例
:param event: 事件对象
:param args: 指令参数(仅消息指令有效)
"""
sig = inspect.signature(func)
params = sig.parameters
kwargs = {}

View File

@@ -1,3 +1,8 @@
"""
配置加载模块
负责读取和解析 config.toml 配置文件,提供全局配置对象。
"""
from pathlib import Path
from typing import Any, Dict
@@ -5,12 +10,26 @@ import tomllib
class Config:
"""
配置加载类,负责读取和解析 config.toml 文件
"""
def __init__(self, file_path: str = "config.toml"):
"""
初始化配置加载器
:param file_path: 配置文件路径,默认为 "config.toml"
"""
self.path = Path(file_path)
self._data: Dict[str, Any] = {}
self.load()
def load(self):
"""
加载配置文件
:raises FileNotFoundError: 如果配置文件不存在
"""
if not self.path.exists():
raise FileNotFoundError(f"配置文件 {self.path} 未找到!")
@@ -20,14 +39,29 @@ class Config:
# 通过属性访问配置
@property
def napcat_ws(self) -> dict:
"""
获取 NapCat WebSocket 配置
:return: 配置字典
"""
return self._data.get("napcat_ws", {})
@property
def bot(self) -> dict:
"""
获取 Bot 基础配置
:return: 配置字典
"""
return self._data.get("bot", {})
@property
def features(self) -> dict:
"""
获取功能特性配置
:return: 配置字典
"""
return self._data.get("features", {})

View File

@@ -1,3 +1,8 @@
"""
WebSocket 核心模块
负责与 OneBot 实现端建立 WebSocket 连接,处理消息接收、事件分发和 API 调用。
"""
import asyncio
import json
import traceback
@@ -8,12 +13,20 @@ import websockets
from models import Event
from .bot import Bot
from .command_manager import matcher
from .config_loader import global_config
class WS:
"""
WebSocket 客户端类,负责与 OneBot 实现端建立连接并处理通信
"""
def __init__(self):
"""
初始化 WebSocket 客户端
"""
# 读取参数
cfg = global_config.napcat_ws
self.url = cfg.get("uri")
@@ -22,9 +35,12 @@ class WS:
self.ws = None
self._pending_requests = {}
self.bot = Bot(self)
async def connect(self):
"""主连接循环"""
"""
主连接循环,负责建立连接和自动重连
"""
headers = {"Authorization": f"Bearer {self.token}"} if self.token else {}
while True:
@@ -50,7 +66,11 @@ class WS:
await asyncio.sleep(self.reconnect_interval)
async def _listen_loop(self, websocket):
"""核心监听循环"""
"""
核心监听循环,处理接收到的 WebSocket 消息
:param websocket: WebSocket 连接对象
"""
async for message in websocket:
try:
data = json.loads(message)
@@ -72,10 +92,15 @@ class WS:
print(f" 解析消息异常: {e}")
async def on_event(self, raw_data: dict):
"""事件分发层:根据 post_type 调用 matcher 对应的处理器"""
"""
事件分发层:根据 post_type 调用 matcher 对应的处理器
:param raw_data: 原始事件数据字典
"""
try:
# 解析为 Event 对象
event = Event.from_dict(raw_data)
event.bot = self.bot
# 格式化时间用于打印
t = datetime.fromtimestamp(event.time).strftime("%H:%M:%S")
@@ -87,19 +112,19 @@ class WS:
print(
f" [{t}] [消息] {event.message_type} | {event.user_id}: {event.raw_message}"
)
await matcher.handle_message(self, event)
await matcher.handle_message(self.bot, event)
# B. 通知事件 (Notice)
elif event.post_type == "notice":
print(
f" [{t}] [通知] {event.notice_type} | 来自: {event.group_id or '私聊'}"
)
await matcher.handle_notice(self, event)
await matcher.handle_notice(self.bot, event)
# C. 请求事件 (Request)
elif event.post_type == "request":
print(f" [{t}] [请求] {event.request_type} | 内容: {event.comment}")
await matcher.handle_request(self, event)
await matcher.handle_request(self.bot, event)
# D. 元事件 (Meta Event) - 通常用来心跳检测,可不处理
elif event.post_type == "meta_event":
@@ -110,7 +135,13 @@ class WS:
traceback.print_exc()
async def call_api(self, action: str, params: dict = None):
"""调用 OneBot API"""
"""
调用 OneBot API
:param action: API 动作名称
:param params: API 参数
:return: API 响应结果
"""
if not self.ws:
return {"status": "failed", "msg": "websocket not initialized"}

View File

@@ -1,10 +1,16 @@
# main.py
"""
NEO Bot 主程序入口
"""
import asyncio
import base_plugins # noqa: F401 别动这里是加载插件的
from core import WS
async def main():
"""
主函数,启动 WebSocket 连接
"""
bot = WS()
await bot.connect()

View File

@@ -1 +1,11 @@
# TODO 数据类型
"""
基础数据模型模块
"""
class BaseModel:
"""
基础模型类
"""
pass

View File

@@ -1,23 +1,54 @@
"""
事件模型模块
定义了 Event 类和 MessageSegment 类,用于封装 OneBot 11 的上报事件和消息段。
"""
from dataclasses import dataclass, field
from typing import Any, Dict, List, Optional
from typing import Any, Dict, List, Optional, TYPE_CHECKING
from .sender import Sender
if TYPE_CHECKING:
from core.bot import Bot
@dataclass
class MessageSegment:
"""
消息段,对应 OneBot 11 标准中的消息段对象
"""
type: str
"""消息段类型,如 text, image, at 等"""
data: Dict[str, Any]
"""消息段数据"""
@property
def text(self) -> str:
"""
获取文本内容(仅当 type 为 text 时有效)
:return: 文本内容
"""
return self.data.get("text", "") if self.type == "text" else ""
@property
def image_url(self) -> str:
"""
获取图片 URL仅当 type 为 image 时有效)
:return: 图片 URL
"""
return self.data.get("url", "") if self.type == "image" else ""
def is_at(self, user_id: int = None) -> bool:
"""
判断是否为 @某人
:param user_id: 指定的 QQ 号,如果为 None 则只判断是否为 at 类型
:return: 是否匹配
"""
if self.type != "at":
return False
if user_id is None:
@@ -30,31 +61,93 @@ class MessageSegment:
@dataclass
class Event:
"""
事件类,封装了 OneBot 11 的上报事件
"""
post_type: str
"""上报类型: message, notice, request, meta_event"""
self_id: int
"""收到消息的机器人 QQ 号"""
time: int
"""事件发生的时间戳"""
# --- 消息事件字段 ---
message_type: Optional[str] = None
"""消息类型: group, private"""
sub_type: Optional[str] = None
"""消息子类型"""
message_id: Optional[int] = None
"""消息 ID"""
user_id: Optional[int] = None
"""发送者 QQ 号"""
raw_message: Optional[str] = None
"""原始消息内容"""
message: List[MessageSegment] = field(default_factory=list)
"""消息内容列表"""
sender: Optional[Sender] = None
"""发送者信息"""
group_id: Optional[int] = None
"""群号"""
target_id: Optional[int] = None
"""目标 QQ 号"""
# --- 通知事件字段 ---
notice_type: Optional[str] = None
operator_id: Optional[int] = None
duration: Optional[int] = None
honor_type: Optional[str] = None
"""通知类型"""
operator_id: Optional[int] = None
"""操作者 QQ 号"""
duration: Optional[int] = None
"""时长"""
honor_type: Optional[str] = None
"""荣誉类型"""
# --- 请求事件字段 ---
request_type: Optional[str] = None
"""请求类型"""
flag: Optional[str] = None
"""请求 flag"""
comment: Optional[str] = None
"""验证信息"""
# 注入的 Bot 实例
bot: Optional["Bot"] = field(default=None, init=False)
"""关联的 Bot 实例"""
async def reply(self, message: str) -> dict:
"""
快捷回复消息
:param message: 回复内容
:return: API 响应结果
"""
if not self.bot:
return {"status": "failed", "msg": "Bot instance not attached to event"}
return await self.bot.send(self, message)
@classmethod
def from_dict(cls, data: dict):
"""
从字典创建 Event 对象
:param data: 原始事件数据字典
:return: Event 对象
"""
raw_msg_array = data.get("message")
segments = []
if isinstance(raw_msg_array, list):
@@ -84,12 +177,15 @@ class Event:
# --- 快捷判断工具 ---
@property
def is_message(self) -> bool:
"""是否为消息事件"""
return self.post_type == "message"
@property
def is_notice(self) -> bool:
"""是否为通知事件"""
return self.post_type == "notice"
@property
def is_request(self) -> bool:
"""是否为请求事件"""
return self.post_type == "request"

View File

@@ -1,17 +1,42 @@
"""
发送者模型模块
定义了 Sender 类,用于封装 OneBot 11 的发送者信息。
"""
from dataclasses import dataclass
from typing import Optional
@dataclass
class Sender:
"""
发送者信息类,对应 OneBot 11 标准中的 sender 字段
"""
user_id: int
"""发送者 QQ 号"""
nickname: str
"""昵称"""
sex: str = "unknown"
"""性别male 或 female 或 unknown"""
age: int = 0
"""年龄"""
# 群聊特有字段
card: Optional[str] = None # 群名片
area: Optional[str] = None # 地区
level: Optional[str] = None # 等级
role: Optional[str] = None # 角色: owner/admin/member
title: Optional[str] = None # 专属头衔
card: Optional[str] = None
"""群名片/备注"""
area: Optional[str] = None
"""地区"""
level: Optional[str] = None
"""成员等级"""
role: Optional[str] = None
"""角色owner 或 admin 或 member"""
title: Optional[str] = None
"""专属头衔"""