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

471 lines
14 KiB
Markdown
Raw 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 多继承构造链不稳定
当前尚未完成的关键项:
- 855 provider 仍需完成一轮“当前项目实际依赖接口”的可上线回归验证
- 855 provider 仍需继续梳理“项目真实使用到的接口覆盖面”,确认是否还有遗漏的旧能力残留在历史目录
- 864 provider 尚未开始接入,当前统一接口仍主要围绕 855 第一阶段目标进行验证
因此,当前状态可以定义为:
- “接入入口已收口”
- “855 运行时主链路已迁入 provider”
- “尚未达到 855 可直接替换现网上线的最终状态”
## 2. 当前问题概览
### 2.1 当前耦合点
当前微信接入实现仍需关注以下历史耦合点与残留影响:
- 历史版本曾直接依赖 `wechat_ipad/config.toml`,当前已开始切向 `config.yaml + .env`
- `Robot` 的实例化入口虽然已切到 `WechatGateway`,但配置读取与业务初始化仍在主程序中
- 855 的运行时职责已经迁入 provider但 864 尚未接入验证,统一抽象仍需继续收敛
- `wechat_ipad/client/*.py` 仍作为历史目录存在,接口路径、请求体、返回结构都面向旧 server 编写
这导致一个结果:
- 如果继续在历史目录或 `Robot` 主链路里堆版本判断,后续 server 版本变化时改动面仍会再次放大
### 2.2 855 / 864 的核心差异
从现有 855 风格 swagger 与 864 swagger 对比看,差异已经不只是“接口路径不同”,而是“运行模型不同”。
855/859 风格通常具备以下特点:
- 通过 `wxid` 驱动大多数接口调用
- 需要客户端自己执行心跳、长心跳、在线状态维护
- 需要客户端主动轮询同步消息
- 登录和缓存唤醒逻辑由客户端自己串起来
864 风格目前看到的特点包括:
- 更偏向 `key + body` 的请求风格
- 登录接口命名和流程明显不同
- 消息同步接口不再与 855 完全一致
- 在线状态、保活和消息同步的职责边界可能由 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 + wechat_ipad/client` 的现网接入方式
- 替换后可以直接上线运行
- 业务行为、插件调用、后台功能、消息归档链路不发生明显退化
因此第一阶段目标应定义为:
- 先完成 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 版本的响应结构归一化
说明:
- 不再要求 855 继续复用现有 `wechat_ipad/client/`
- 更推荐每个 provider 自带独立目录,内部自行管理登录、消息、联系人、群信息等协议实现
- 这样可以把不同 server 的协议差异彻底隔离,避免为了兼容新版本继续污染旧 client
## 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 接入能力
- 替换后可以直接上线,不依赖旧 `wechat_ipad/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 可以先跑主链路
- 高级能力后续再补
### 阶段三:扩展 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/*.py`
5. 不建议现在就引入太多类型层、事件总线或复杂工厂模式
## 13. 结论
适配多版本 `wechat_ipad server` 的关键,不是做“更多 URL 配置”,而是把以下两类差异真正隔离开:
- 协议差异:路径、鉴权、请求体、返回结构
- 运行模型差异:登录方式、心跳方式、消息接收方式、掉线恢复方式
对于当前项目,最适合的方向是:
- `Robot` 保持业务中心化
- Gateway 足够薄
- Provider 承担全部 server 差异
- 先统一主链路,不追求一步到位
这条路线既能控制复杂度,也方便未来继续接入 864 或其他版本 server。