refactor!: 协议帧与业务事件分流,各接入方式配专有协议处理器

BaseProtocol 从 emitter.py 移入新模块 protocol.py,接口由 register(emitter)
改为 async on_frame(payload)(返回需回送的响应体);协议处理器不再注册进
emitter,而由 run_loop 注入对应的 connecter,由适配器逐帧调用。

破坏性变更:只有 op=0 的业务事件进事件队列交给 EventEmitter 分发,
HELLO/READY/INVALID_SESSION/op=13 等协议帧在连接器内就地处理——用户 handler
不再收到协议帧(此前 HELLO/INVALID_SESSION 等会路由到 handler)。
EventEmitter.register_protocol()/handle() 与 emitter 持有协议层的机制删除,
协议类不再用 register() 注册 handler。

附带修掉一处时序耦合:INVALID_SESSION 的会话重置此前依赖分发泵在重连前
消费掉该事件(慢 handler 会推迟它,导致带着已死 session_id 去 RESUME),
现在连接器在 break 重连前同步完成重置。

同时做的事件流优化:
- emit() 先取事件名、无 listener 时不构造 Event(未订阅事件省一次对象与拷贝)
- Event.data 改为惰性拷贝(只用 .typed/.raw 的 handler 不再触发 dict(d))
- ws 重连收尾 await 被取消的 reply/heartbeat 任务,避免异常无人取用
- EventQueue.put_event/put_reply 去掉多余的 return await

测试 80 全绿(新增协议帧处理测试组、ws 集成断言协议帧不触达 handler),
ruff/pyright 干净。文档 ARCHITECTURE/API/GUIDE/README/CHANGELOG 同步。
This commit is contained in:
snowy-yang committed 2026-09-20 21:57:57 +08:00
1 parent 71cbfdaf5d
commit 7accbd1ead
16 files changed
+335 -238

No files matched your search

+3 -2
View File
@@ -116,11 +116,12 @@ handler 参数按标注解析的完整规则:
| payload dataclass(如 `GroupAtMessage`) | 解析后的 dataclass 对象;未收录事件回退原始 dict |
| `Event` | 事件封装对象(`.raw`/`.data`/`.typed`/`.user_id`/`.group_id`/`.content`/`.message_id`/`.event_id`) |
| 组件类型(`BotApi`/`Session`/`Config` 等) | `emitter.services` 中按类型登记的实例;未登记回退传原始 d |
| 无标注 | 原始 d(业务事件即事件 dict;协议事件可能是标量,如 `INVALID_SESSION` 的 `resumable: bool`) |
| 无标注 | 原始 d(即事件 dict) |
注意:
- 业务事件(op=0)处理器的**返回值不会回流**,回复消息请在 handler 里显式调 API。
- 只有**业务事件(op=0)**会分发给你的 handler;协议帧(HELLO/READY/INVALID_SESSION/op=13)由 SDK 的协议处理器就地处理,不触达 handler(`Ready` 是例外,它既是握手结果也是可监听的业务事件)。
- 业务事件处理器的**返回值不会回流**,回复消息请在 handler 里显式调 API。
- 单个 handler 抛异常只记录日志,不影响同事件其他 handler 和主循环。
- 所在分组未订阅(intents.toml 中为 `false`)的事件不会到达 handler。
- 自定义组件也能注入:`ee.services[MyService] = MyService(...)` 登记后,handler 形参标注 `MyService` 即可拿到实例。