# 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 当前耦合点 当前微信接入实现仍需关注以下历史耦合点与残留影响: - [robot.py](/d:/learn/abot/robot.py:221) 直接读取 `wechat_ipad/config.toml` - `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。