Files
abot/docs/wechat_ipad多版本Server适配路线图.md

499 lines
16 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# wechat_ipad 多版本 Server 适配路线图
## 1. 文档目标
本文档用于指导 ABOT 对接多个 `wechat_ipad server` 版本时的架构收敛方式,重点解决以下问题:
- 当前项目默认绑定 855/859 风格协议,后续接入 864 等新版本时改动面过大
- 不同 server 在登录、心跳、在线状态检测、消息同步方式上存在明显差异
- 希望代码保持精简,不引入过多抽象层,避免阅读成本上升
- 希望未来新增 server 时,尽量不再改动 `robot.py` 主链路
本文档不追求一步到位抽象“所有微信能力”,而是先定义一条适合当前项目的最小演进路线。
## 1.1 当前推进状态
截至当前版本,已完成以下事项:
- 已创建基线 tag`pre_server_adapter_20260507`
- 已新增 [wechat_ipad/gateway.py](/d:/learn/abot/wechat_ipad/gateway.py:1)
- 已新增 [wechat_ipad/provider_base.py](/d:/learn/abot/wechat_ipad/provider_base.py:1)
- 已新增 `providers/legacy_855/` 独立目录,并迁入当前 855/859 协议实现
- 已将 [robot.py](/d:/learn/abot/robot.py:1) 的接入实例化入口切换为 `WechatGateway`
- 已新增 [wechat_ipad/providers/legacy_855/runtime.py](/d:/learn/abot/wechat_ipad/providers/legacy_855/runtime.py:1)
- 已将 855 的登录、历史消息拉取、心跳、长心跳、消息轮询、掉线二次登录恢复迁入 `legacy_855` provider
- 已将 [robot.py](/d:/learn/abot/robot.py:1) 精简为“注册回调 + 业务处理”,不再直接维护 855 的运行时主循环
- 已补上 `Legacy855WechatClient` 的显式初始化入口,避免 provider 多继承构造链不稳定
- 已删除历史 `wechat_ipad/client/` 目录,避免后续误回退到旧实现
- 已为 855 登录流程补充 Dashboard 首页二维码引导态,支持未登录时自动弹窗、倒计时与最近二维码记录展示
- 已新增 `providers/server_864/` 独立目录,用于承接 864 风格 server
- 已为 864 接入补充 `wechat_ipad.server_key` 统一配置项,支持通过 `.env``WECHAT_SERVER_KEY` 注入
- 已在 [wechat_ipad/gateway.py](/d:/learn/abot/wechat_ipad/gateway.py:1) 中注册 `server_864 / 864` 别名
- 已实现 864 第一版登录、初始化等待、HTTP 消息轮询、联系人、群信息、资料与朋友圈基础接口
- 已在 [robot.py](/d:/learn/abot/robot.py:1) 中为 864 增加登录态硬隔离,默认不再回读 855 的历史 `config.toml` 与动态字段
- 已基于真实 864 服务联调修正首批路由差异:二维码返回结构、联系人详情路由、群公告路由、二维码有效期倒计时同步
- 已确认 864 的消息发送类接口在“尚未建立连接对象”时会优先返回“该链接不存在”,这与登录态接口返回“需要重新登录”属于不同阶段
- 已开始把 864 的“等待服务端准备 / 需要重新登录 / 等待扫码”三态显式回传给 Dashboard避免首页把不同问题混成一类未登录提示
当前尚未完成的关键项:
- 855 provider 仍需完成一轮“当前项目实际依赖接口”的可上线回归验证
- 855 provider 仍需继续梳理“项目真实使用到的接口覆盖面”,确认是否还有遗漏能力未纳入 provider 目录
- 864 provider 仍需完成真机联调,尤其是消息字段归一化、视频发送、名片发送等增强能力
- Dashboard 仍需继续做少量多 provider 登录文案收敛,但登录状态结构已具备扩展空间
因此,当前状态可以定义为:
- “接入入口已收口”
- “855 运行时主链路已迁入 provider”
- “未登录场景已有 Dashboard 可视化登录引导”
- “864 第一版 provider 已落地,但还需要真实 server 回归验证”
- “尚未达到 855 / 864 都可直接无差异替换现网上线的最终状态”
## 2. 当前问题概览
### 2.1 当前耦合点
当前微信接入实现仍需关注以下历史耦合点与残留影响:
- 历史版本曾直接依赖 `wechat_ipad/config.toml`,当前已开始切向 `config.yaml + .env`
- `Robot` 的实例化入口虽然已切到 `WechatGateway`,但配置读取与业务初始化仍在主程序中
- 855 的运行时职责已经迁入 provider但 864 尚未接入验证,统一抽象仍需继续收敛
- 公共 `models/` 仍在服务主链路,而多版本协议实现已正式收口到 `providers/*/`
这导致一个结果:
- 如果继续在 `Robot` 主链路里堆版本判断,后续 server 版本变化时改动面仍会再次放大
### 2.2 855 / 864 的核心差异
从现有 855 风格 swagger 与 864 swagger 对比看,差异已经不只是“接口路径不同”,而是“运行模型不同”。
855/859 风格通常具备以下特点:
- 通过 `wxid` 驱动大多数接口调用
- 需要客户端自己执行心跳、长心跳、在线状态维护
- 需要客户端主动轮询同步消息
- 登录和缓存唤醒逻辑由客户端自己串起来
864 风格目前看到的特点包括:
- 更偏向 `key + body` 的请求风格
- 登录接口命名和流程明显不同
- 消息同步同时支持 WS 与 HTTP 轮询
- 在线状态、保活和消息同步的职责边界更多由 server 侧承担
结论:
- 不能只做“路径别名映射”
- 必须把“运行时模型差异”也纳入适配层设计
## 3. 设计原则
后续适配设计建议遵循以下原则:
### 3.1 业务层不感知 server 版本
`Robot`、插件系统、消息归档、后台管理,不应该直接感知:
- 当前是 855 还是 864
- 是否需要心跳
- 是否需要手动轮询消息
- 鉴权参数是 `wxid` 还是 `key`
### 3.2 不做过厚抽象
本项目不建议引入过多层级,例如:
- 不建议再拆 `manager + service + strategy + factory + registry` 多层套娃
- 不建议一开始把所有微信能力抽成几十个接口
- 不建议为每个 API endpoint 单独建类
目标是:
- 层数少
- 入口清晰
- 新人容易顺藤摸瓜读代码
### 3.3 第一阶段先完成 855 全量可上线接入
考虑到当前项目已经依赖 855/859 风格能力在真实环境运行,第一阶段不能只做“主链路最小闭环”,而要做到:
- 855 provider 能完整替代当前 `Robot + 旧接入实现` 的现网接入方式
- 替换后可以直接上线运行
- 业务行为、插件调用、后台功能、消息归档链路不发生明显退化
因此第一阶段目标应定义为:
- 先完成 855 全量接入与运行时收口
- 再在第二阶段接入 864
这比“先抽最小接口、后补功能”更符合当前项目实际。
## 4. 推荐架构
建议采用“两层半”结构:
### 4.1 第一层:业务层
即现有机器人主逻辑:
- `Robot`
- 插件系统
- 消息归档
- 调度系统
- Dashboard
这一层只依赖一个统一微信网关,不直接依赖具体 server 协议。
### 4.2 第二层Gateway 层
建议新增一个轻量入口,例如:
- `wechat_ipad/gateway.py`
职责只保留两项:
1. 根据配置选择 provider
2. 对外暴露统一调用入口
Gateway 不负责:
- 心跳逻辑
- 消息轮询逻辑
- 协议差异判断
- 各版本字段转换细节
### 4.3 第三层Provider 层
建议新增:
- `wechat_ipad/provider_base.py`
- `wechat_ipad/providers/legacy_855/`
- `wechat_ipad/providers/server_864/`
职责:
- 各 server 版本的运行模型适配
- 各 server 版本的协议调用适配
- 各 server 版本的响应结构归一化
说明:
- 不再保留旧 `wechat_ipad/client/` 作为正式实现入口
- 更推荐每个 provider 自带独立目录,内部自行管理登录、消息、联系人、群信息等协议实现
- 这样可以把不同 server 的协议差异彻底隔离,避免为了兼容新版本继续污染历史实现
## 5. 为什么要把“运行模型”抽出来
这是本轮适配设计最关键的一点。
### 5.1 855 的运行模型
855/859 风格更接近“主动轮询型 provider”
- 需要自己发心跳
- 需要自己发长心跳
- 需要自己检查掉线
- 需要自己轮询 `sync_message`
- 需要自己在登录失败时做唤醒或二次恢复
### 5.2 864 的运行模型
864 风格更可能是“轻保活 / 被动事件型 provider”
- 未必需要客户端主动心跳
- 在线状态检查方式不同
- 消息同步方式可能不是旧版的手动轮询
- 可能通过 HTTP 新接口或 WS 模式获取消息
因此,不能要求所有 provider 都暴露完全相同的“内部实现”,只能要求它们提供相同的“对外能力接口”。
## 6. 最小统一接口建议
建议不要统一“所有底层动作”而统一“Robot 真正需要的能力”。
### 6.1 生命周期接口
- `initialize()`
- `start_runtime()`
- `stop_runtime()`
说明:
- `start_runtime()` 是核心抽象
- 855 provider 内部可以在这里启动心跳、长心跳、轮询任务
- 864 provider 内部可以在这里建立消息监听或执行轻量状态检查
### 6.2 登录与状态接口
- `is_logged_in()`
- `ensure_login()`
- `get_login_identity()`
### 6.3 消息入口接口
- `set_message_handler(handler)`
说明:
- 上层统一注册消息处理回调
- provider 自己决定如何拿消息
- 不再强行要求所有 provider 都暴露相同的 `sync_message()` 轮询接口给 `Robot`
### 6.4 主动调用接口
- `send_text()`
- `get_profile()`
- `get_profile_ext()`
- `get_contact_list()`
- `get_contact_detail()`
- `get_chatroom_info()`
- `get_chatroom_member_list()`
### 6.5 855 第一阶段必须覆盖的现网能力
如果目标是“第一阶段即可上线替换”,那么 855 provider 第一版建议直接覆盖当前项目已在使用的能力,而不只是最小主链路。
至少应包含:
- 登录 / 唤醒登录 / 登录缓存读取
- 心跳 / 长心跳 / 二次登录恢复
- 消息同步与消息接收分发
- 文本消息发送
- 图片消息发送
- 语音消息发送
- 视频消息发送
- 表情消息发送
- 链接 / 卡片 / app 消息发送
- 撤回消息
- 联系人列表与联系人详情
- 用户资料与扩展资料
- 群详情、群公告、群成员列表
- 群邀请、拉人等当前项目已在用的群管理能力
- 项目内已经依赖的下载或转发能力
原则:
- 只要当前主程序、插件、后台、调度逻辑已经依赖,就纳入 855 第一阶段
- 不要求第一阶段补齐“server 支持但项目暂未使用”的全部接口
## 7. 消息同步策略如何统一
### 7.1 不统一“消息获取方式”
不要要求所有 provider 都必须靠:
- `sync_message()`
来向上层提供消息。
因为:
- 855 是主动轮询
- 864 可能是新 HTTP 同步接口
- 未来还可能出现 WS 推送模型
### 7.2 统一“消息交付方式”
建议统一成:
- provider 拿到消息后,调用统一的 message handler
即:
- 上层统一消费消息
- 下层各自决定怎么取消息
这样可以把“消息来源差异”完全收敛在 provider 内部。
## 8. 数据归一化建议
为了避免业务层继续面对不同 server 的原始 JSON建议 provider 对以下结果做轻量归一化。
### 8.1 第一阶段先用统一 dict
当前项目不必急着上很多 dataclass。
第一阶段建议直接统一为内部 dict 结构,例如:
```python
{
"wxid": "...",
"nickname": "...",
"alias": "...",
"phone": "...",
"signature": "..."
}
```
消息列表可以统一为:
```python
{
"add_msgs": [...],
"raw": {...}
}
```
这样做的优点:
- 轻量
- 阅读门槛低
- 比强行把原始 server JSON 暴露给 `Robot` 更稳定
### 8.2 后续再考虑更强类型化
只有当 provider 数量继续增加,或者字段归一化变复杂时,再考虑引入:
- `LoginProfile`
- `ChatroomInfo`
- `IncomingMessageEnvelope`
目前不建议一开始做太重。
## 9. 目录结构建议
建议控制在这个粒度:
```text
wechat_ipad/
├── gateway.py
├── provider_base.py
├── providers/
│ ├── legacy_855/
│ │ ├── __init__.py
│ │ ├── provider.py
│ │ ├── login.py
│ │ ├── message.py
│ │ └── contact.py
│ └── server_864/
│ ├── __init__.py
│ ├── provider.py
│ ├── login.py
│ ├── message.py
│ └── contact.py
└── models/
```
说明:
- `gateway.py`:对 `Robot` 提供统一入口
- `provider_base.py`:定义最小约定
- `providers/legacy_855/`855/859 风格协议的独立实现目录
- `providers/server_864/`864 风格协议的独立实现目录
- 每个 provider 目录内按登录、消息、联系人等维度分文件,但只在本 provider 内部使用,不向外暴露协议细节
## 10. 推荐推进路线
### 阶段一:完成 855 全量 provider 化并达到可上线状态
目标:
-`Robot` 依赖 Gateway
-`providers/legacy_855/` 完整承接当前 855/859 接入能力
- 替换后可以直接上线,不依赖历史 `client` 目录
建议步骤:
1. 新增 `gateway.py`
2. 新增 `provider_base.py`
3. 新增 `providers/legacy_855/` 目录与 `provider.py`
4. 将当前项目已使用的 855 功能按模块迁入该目录:
- `login.py`
- `message.py`
- `contact.py`
- `group.py`
- `profile.py`
- `runtime.py`
5.`Robot` 中的登录、心跳、长心跳、消息轮询、恢复逻辑迁入 855 provider
6.`robot.py` 中直接依赖 `WechatAPIClient` 的位置改成依赖 Gateway
7. 跑通人工回归,确认能替换现有接入链路上线
阶段结果:
- 855 provider 完整替代旧接入实现
- `Robot` 主链路与具体 server 实现解耦
- 项目可继续按 855 方案稳定上线
### 阶段二:实现 864 provider 的最小闭环
目标:
- 在不影响 855 现网运行的前提下,引入 864 provider
建议步骤:
1. 新增 `providers/server_864/`
2. 先实现登录状态获取与基础登录能力
3. 实现 864 的消息接收策略
4. 实现文本发送、联系人、群信息等核心能力
5. 在 Gateway 中通过 `server_type` 切换 provider
阶段结果:
- 864 可以先跑主链路
- 高级能力后续再补
当前已完成的第一版范围:
- 已接入固定 `server_key` 配置
- 已实现二维码登录轮询与初始化等待
- 已实现 HTTP 轮询消息同步
- 已实现联系人、群资料、当前账号资料、朋友圈基础接口
- 已保留与 855 尽量一致的对外方法名,便于 `Robot` 无感切换
- 已补一层登录阶段探测,首页可以直接区分“等待服务端准备”“需要重新登录”“等待扫码”
当前仍待补强的范围:
- 真实 864 server 环境下的字段回包核对
- 视频发送、名片发送等低频接口
- 如后续需要更低延迟,可再补 WS 同步消息 runtime
### 阶段三:扩展 864 与后续 provider 的高级能力
后续再逐步统一:
- 图片消息
- 语音消息
- 卡片/链接消息
- 朋友圈
- 群管理高级动作
## 11. 当前最值得优先落地的改造点
如果当前目标是“尽快把架构收口,同时保证可以上线”,建议优先做以下 6 项:
1. 新增 `gateway.py`
2.`providers/legacy_855/`
3. 梳理当前项目实际使用到的 855 接口清单
4. 把运行时逻辑全部迁入 855 provider
5.`Robot` 不再直接 new `WechatAPIClient`
6. 做一套 855 provider 的人工回归清单,确认具备上线条件
## 12. 不建议当前阶段做的事情
为了避免过度设计,当前阶段不建议:
1. 不建议一次抽象所有微信功能
2. 不建议为每个 API endpoint 单独建类
3. 不建议让 `Robot` 持续保留 `if server_type == ...` 的运行逻辑分叉
4. 不建议让 864 回退复用已经移除的旧 client 结构
5. 不建议现在就引入太多类型层、事件总线或复杂工厂模式
## 13. 结论
适配多版本 `wechat_ipad server` 的关键,不是做“更多 URL 配置”,而是把以下两类差异真正隔离开:
- 协议差异:路径、鉴权、请求体、返回结构
- 运行模型差异:登录方式、心跳方式、消息接收方式、掉线恢复方式
对于当前项目,最适合的方向是:
- `Robot` 保持业务中心化
- Gateway 足够薄
- Provider 承担全部 server 差异
- 先统一主链路,不追求一步到位
这条路线既能控制复杂度,也方便未来继续接入 864 或其他版本 server。