mirror of
https://github.com/snowy-yang/qqbotsdk.git
synced 2026-10-21 07:59:07 +00:00
1552c5207b6f95b326fdc39c3298f17bc0e6a4cc
以「语义可读、逻辑清晰、减少心智负担」为目标,不追求极致速度:
- **移除 pyee 依赖**:EventEmitter 改为内置的 `事件名 → handler 列表` 注册表
(on() 追加、emit() 顺序遍历)。SDK 用不到 pyee 的 once/new_listener/错误
事件机制,去掉后少一个依赖、分发语义一眼可读。注册语义不变(同函数重复
注册只生效一次),并补测试锁定「重复注册只生效一次」「按注册顺序执行」。
破坏性:once/remove_listener 等装饰性 API 不再提供。
- **去掉热路径上的重复计算**(纯缓存,不改语义):
· payloads 按类缓存解析计划(字段名 + 是否嵌套 Author),不再每次事件重跑
dataclasses.fields 与联合类型解包;未收录字段照旧忽略(新增回归测试,
线上 payload 会带 author.union_openid 这类字段)。实测 7.5µs → 2.5µs。
· crypto 按 AppSecret 缓存派生私钥(每个 webhook 请求都要验签,派生一次
约 40µs);公钥派生只差 3µs,不再多缓存一层。
- **命名与接口一致性**:
· WebsocketConnecter.reply_helper → send_helper(它从出站缓冲取帧发送,
原名会让人误以为还有 reply 队列;model.py 的对应注释同步)
· Connecter 由 typing.Protocol 改为 abc.ABC,与 BaseProtocol 风格统一
- **webhook 去重改摊还清理**:过期判定按当前 id 做(O(1),且 TTL 外重发的
同一 id 仍按新事件处理——原先的写法在阈值清理下会误判为重复,已由测试
抓出并修正),整表清过期项只在表长到阈值时执行,不再每请求 O(n) 扫描。
测试 85 全绿(新增未知字段/解析计划缓存/注册语义/去重摊还与 TTL 过期),
ruff check/format + pyright 干净。文档 ARCHITECTURE/API/CHANGELOG 同步。
qqbotsdk
QQ 官方机器人 Python SDK(Python 3.13 / asyncio / aiohttp),支持 WebSocket 与 Webhook 两种接入方式。
使用指南 · API 参考 · 架构 · 示例 · 更新日志
架构
flowchart TB
subgraph 接入层
WS["WebsocketConnecter(ws 长连接 / 重连 / 心跳看门狗)"]
WH["WebhookConnecter(http 回调 / 验签 / 去重)"]
end
subgraph 协议层
WSP["WebsocketProtocol(握手 / 会话 / 序列号)"]
WHP["WebhookProtocol(op=13 验证应答)"]
end
subgraph 分发层
Q[("EventQueue<br/>业务事件 op=0")]
EE["EventEmitter(路由 / 参数注入 / 异常隔离)"]
end
subgraph 业务层
API["BotApi(REST + ApiError)"]
USER["用户 handler @ee.on(...)"]
end
QQ["QQ 开放平台"]
WS -. "逐帧 on_frame" .-> WSP
WH -. "op=13 逐帧 on_frame" .-> WHP
WS -->|"业务事件"| Q
WH -->|"业务事件"| Q
Q --> EE
EE --> USER
USER --> API
API --> QQ
WS --> QQ
组件在 run_loop 显式装配,并按类型登记到 emitter.services 供 handler 参数注入(无全局单例);协议帧由连接适配器交给各自的协议处理器就地处理,只有 op=0 的业务事件经队列进入分发——依赖关系与设计决策详见 docs/ARCHITECTURE.md。
特性
- 内置协议处理器(
ws_protocol.py/webhook_protocol.py):HELLO 鉴权(IDENTIFY/RESUME 自动切换)、READY 会话捕获、INVALID_SESSION 会话失效处理;心跳由连接器按连接生命周期驱动 - 断线自动重连(指数退避 + 心跳看门狗),重连时自动 RESUME 续传
- Webhook 接入自带 Ed25519 验签、op=13 验证应答与 60s TTL 事件去重
- 事件订阅由项目根目录的
intents.toml控制,按大类(Intents 分组)开关,将对应分组改为true即可 - Markdown 与内嵌按钮一等支持:
post_group_message(..., markdown=..., keyboard=...)自动置 msg_type=2,respond_interaction()回应按钮回调 - 组件在
run_loop显式装配(无 DI 框架依赖),全 SDK 共享一个 HTTP 连接池
配置
.env(APPID/APPSECRET 必填,缺失启动即报错):
APPID=你的AppID
APPSECRET=你的AppSecret
BASE_URL=https://api.bot.qq.com # 可选,默认官方地址
TIMEOUT=5000 # 可选,毫秒
CONNECTER=websocket # websocket 或 webhook
# 仅 webhook 接入时
WEBHOOK_HOST=0.0.0.0
WEBHOOK_PORT=8080
WEBHOOK_PATH=/qqbot/webhook
使用
编写入口(如 main.py),把自己构造的 EventEmitter 传给 main():
from qqbotsdk import EventEmitter, main
ee = EventEmitter()
# 方式一:裸参数(d 为原始事件数据)
@ee.on("GROUP_AT_MESSAGE_CREATE")
async def on_group_message(d):
print("收到群消息:", d)
# 方式二:类型化 payload(dataclass,见 qqbotsdk.payloads)
from qqbotsdk.payloads import GroupAtMessage
@ee.on("GROUP_AT_MESSAGE_CREATE")
async def on_group_message(msg: GroupAtMessage):
print(msg.group_openid, msg.user_openid, msg.content)
# 方式三:参数注入(按形参标注解析,`BotApi` 等组件由 SDK 装配)
from qqbotsdk.api import BotApi
from qqbotsdk.events import Event
@ee.on("GROUP_AT_MESSAGE_CREATE")
async def handle(event: Event, api: BotApi):
print(event.user_id, event.group_id, event.content)
# 被动回复:带 msg_id(群聊 5 分钟/单聊 60 分钟内有效,最多分别回复 5/4 次);
# 同一条消息多次回复递增 msg_seq
await api.post_group_message(
event.group_id,
content=f"你说:{event.content}",
msg_id=event.message_id,
) # 失败抛 ApiError;富媒体先 upload_group_file 拿 file_info
# 业务事件(op=0)才进分发,返回值不回流,回复请显式调 BotApi;
# 协议帧(HELLO/READY/INVALID_SESSION/op=13)由各接入方式的协议处理器
# 在连接器内就地处理,不进队列、也不会触达 handler。
# 单个 handler 抛异常不会影响其他 handler 和主循环,异常会被记录。
if __name__ == "__main__":
main(ee)
运行:
uv run python main.py
Webhook 接入:将 .env 中 CONNECTER 改为 webhook,运行后把
http://<你的域名>:8080/qqbot/webhook 配置到 QQ 开放平台回调地址(仅支持 80/443/8080/8443 端口,需公网 HTTPS)。
完整可运行示例见 examples/:群机器人(文本/图片回复、入群欢迎、单聊回复)与 Webhook 最小接入。
开发
uv run pytest # 单元测试
uv run ruff check src tests examples && uv run pyright
文档站点基于 docsify(无需构建,仓库根 index.html + Markdown),本地预览:
npx docsify-cli serve # 打开 http://localhost:3000
待完善
- 更多 OpenAPI 封装(按需在
api.py的BotApi上加薄封装即可)
Languages
Python
97.9%
HTML
2.1%