Files
weixin_ClawBot_Bridge/docs/02-ilink-protocol.md
T

257 lines
9.3 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.
# 02 — iLink 协议完整参考
> 本文档是对 iLink Bot API 的完整整理,供实现协议层时对照查阅。
> 来源:腾讯官方包 `@tencent-weixin/openclaw-weixin` 的逆向分析、Java SDK 实现、多个第三方适配器实践。字段级细节以实测为准。
## 1. 基本信息
| 项 | 值 |
|---|---|
| API 根地址 | `https://ilinkai.weixin.qq.com` |
| CDN 地址 | `https://novac2c.cdn.weixin.qq.com/c2c` |
| 协议 | HTTPS + JSON |
| 收消息模式 | HTTP 长轮询(无 Webhook,无需公网 IP) |
| 鉴权 | `Authorization: Bearer <bot_token>` |
## 2. 端点总表
| Endpoint | Method | 功能 | 需要鉴权 |
|---|---|---|---|
| `/ilink/bot/get_bot_qrcode` | GET | 获取登录二维码(`?bot_type=3`) | 否 |
| `/ilink/bot/get_qrcode_status` | GET | 轮询扫码状态(`?qrcode=xxx`) | 否 |
| `/ilink/bot/getupdates` | POST | 长轮询收消息(核心) | 是 |
| `/ilink/bot/sendmessage` | POST | 发送消息(文本/图片/文件/视频/语音) | 是 |
| `/ilink/bot/getconfig` | POST | 获取配置(typing_ticket 等) | 是 |
| `/ilink/bot/sendtyping` | POST | 发送「正在输入」状态 | 是 |
| `/ilink/bot/getuploadurl` | POST | 获取 CDN 预签名上传地址 | 是 |
## 3. 通用请求头
登录后所有请求的固定头:
```http
Content-Type: application/json
AuthorizationType: ilink_bot_token
Authorization: Bearer <bot_token>
X-WECHAT-UIN: <base64(十进制字符串(随机uint32))>
```
- `X-WECHAT-UIN`:每次请求生成一个随机 uint32 → 转十进制字符串 → base64 编码。每次都不一样,起防重放作用。
- 扫码登录阶段(`get_qrcode_status`)有实现带 `iLink-App-ClientVersion: 1` 头。
## 4. 登录流程(一次性扫码绑定)
### 4.1 时序
```
开发者 iLink 服务器 微信用户
│── GET get_bot_qrcode ──▶│ │
│◀── { qrcode, url } ─────│ │
│ │◀──── 用户扫码 ───────│
│── GET get_qrcode_status ─▶│ (长轮询/定时轮询) │
│◀── { status:"confirmed", │ │
│ bot_token, ... } ──│ │
│
▼ 持久化 bot_token,后续所有请求 Bearer 鉴权
```
### 4.2 获取二维码
```http
GET /ilink/bot/get_bot_qrcode?bot_type=3
```
响应:
```json
{
"qrcode": "xxx",
"qrcode_img_content": "https://liteapp.weixin.qq.com/q/..."
}
```
- `qrcode`:QR 标识符,轮询状态时用
- `qrcode_img_content`:二维码图片 URL,可在终端渲染(如 `qrcode-terminal`)或浏览器打开给用户扫
- `bot_type=3` 是官方包源码中硬编码的值,含义未公开,照抄即可
### 4.3 轮询扫码状态
```http
GET /ilink/bot/get_qrcode_status?qrcode=<qrcode>
```
状态流转:`wait` → `scaned` → `confirmed`(注意官方拼写是 `scaned`)。
确认后响应(关键凭据):
```json
{
"status": "confirmed",
"bot_token": "44b9b8ec@im.bot:060000b7...",
"ilink_bot_id": "44b9b8ec@im.bot",
"ilink_user_id": "o9cq809L...@im.wechat",
"baseurl": "https://ilinkai.weixin.qq.com"
}
```
- **`bot_token` 是之后所有 API 调用的鉴权凭据,一次扫码长期有效**(具体有效期官方未公布,社区观察为长期;过期会收到 errcode=-14,需重新扫码)
- 建议把 `bot_token`、`ilink_bot_id`、`ilink_user_id`、`baseurl` 全部持久化
- 轮询建议间隔 1s,整体超时给 5~8 分钟
## 5. 收消息:长轮询
### 5.1 请求
```http
POST /ilink/bot/getupdates
```
```json
{
"get_updates_buf": "<上次返回的游标,首次为空字符串>",
"base_info": { "channel_version": "1.0.2" }
}
```
### 5.2 响应
```json
{
"ret": 0,
"msgs": [ /* WeixinMessage[],见第 7 节 */ ],
"get_updates_buf": "Ck1...",
"longpolling_timeout_ms": 35000
}
```
### 5.3 机制要点
- 服务器 **hold 住连接最多约 30~35 秒**(`longpolling_timeout_ms` 会告知),有新消息立即返回,无消息超时返回空 `msgs`
- HTTP 客户端超时必须设得比 `longpolling_timeout_ms` 大(建议 +10s 余量)
- **`get_updates_buf` 是同步游标**(类似 Telegram getUpdates 的 offset):
- 每次请求带上次返回的值
- **收到响应后立刻持久化**,否则重启后会重复收消息
- 返回中没有该字段时保持旧值
- HTTP 超时/网络错误视为「本轮无新消息」,直接重试,不要中断循环
### 5.4 失败恢复与退避(来自 Java SDK 的实战策略)
| 情况 | 处理 |
|---|---|
| `errcode = -14`(会话过期 / token 失效) | 该账号进入暂停窗口(SDK 默认 1 小时),期间停止轮询和发送;提示用户重新扫码 |
| 其他失败 | 2s 后快速重试;连续失败 3 次后进入 30s 退避 |
| HTTP 超时 | 视为无新消息,立即进入下一轮 |
| 响应带 `longpolling_timeout_ms` | 动态调整下一轮轮询超时 |
## 6. 发消息
```http
POST /ilink/bot/sendmessage
```
```json
{
"msg": {
"to_user_id": "o9cq809L...@im.wechat",
"client_id": "unique-id-123",
"message_type": 2,
"message_state": 2,
"context_token": "CK3E...",
"item_list": [
{ "type": 1, "text_item": { "text": "你好!我是你的 AI。" } }
]
}
}
```
字段说明:
| 字段 | 说明 |
|---|---|
| `to_user_id` | 收件人,取入站消息的 `from_user_id` |
| `client_id` | 客户端生成的去重 ID(如 UUID),防止重发 |
| `message_type` | `2` = Bot 发出的消息(入站用户消息是 `1`) |
| `message_state` | `2` = FINISH 完成态(非流式)。协议还有 NEW(0?) → GENERATING → FINISH 的三段式状态,可用于流式分片发送(Java SDK 的 `sendTextStream` 即分片发送,但注意**那不是 LLM token 流**,而是把长文本切成多条消息) |
| `context_token` | **必填**,从要回复的那条入站消息原样取出 |
| `item_list` | 消息体,文本见上;媒体 item 构造见 03 文档 |
## 7. 消息结构(WeixinMessage)
入站消息核心字段:
```json
{
"from_user_id": "o9cq800kum_xxx@im.wechat",
"to_user_id": "e06c1ceea05e@im.bot",
"message_type": 1,
"message_state": 2,
"context_token": "AARzJWAFAAABAAAAAAAp...",
"item_list": [
{ "type": 1, "text_item": { "text": "你好" } }
]
}
```
### 7.1 ID 格式规律
- 用户 ID:`xxx@im.wechat`
- Bot ID:`xxx@im.bot`
### 7.2 常量表
| 字段 | 值 | 含义 |
|---|---|---|
| `message_type` | 1 | 用户消息(入站) |
| `message_type` | 2 | Bot 消息(出站) |
| `message_state` | 2 | FINISH(完整消息) |
| `item.type` | 1 | 文本(`text_item.text`) |
| `item.type` | 2 | 图片(CDN 加密存储) |
| `item.type` | 3 | 语音(silk 编码,`voice_item.text` 附 ASR 转写文字) |
| `item.type` | 4 | 文件附件 |
| `item.type` | 5 | 视频 |
> 媒体类 item 的详细字段(CDN 引用、aes_key 等)见 [03-messages-and-media.md](03-messages-and-media.md)。
## 8. context_token:对话关联的核心
整个协议里最关键、最容易踩坑的细节:
1. **每条入站消息都带 `context_token`**,回复时必须原样带上,否则消息无法关联到正确的对话窗口
2. 每条入站消息都应刷新本地保存的 token(Java SDK 的做法:`FileContextTokenStore` 按对话持久化,每条入站消息更新),这样主动发消息时也有最新 token 可用
3. 有资料称:**用户最后一条消息超过 24 小时后,bot 的消息可能送达不到**;在窗口内可用 `context_token` 主动追发消息。做定时推送类功能时必须考虑这个窗口(⚠️ 建议实测确认)
## 9. 输入中状态(typing)
```
POST /ilink/bot/getconfig → 获取 typing_ticket
POST /ilink/bot/sendtyping → 发送「正在输入」
```
用途:AI 后端处理耗时较长时,先发个 typing 让用户知道 bot 在干活。非必需,第一版可以不做。
## 10. 错误处理备忘
| 现象 | 含义 | 处理 |
|---|---|---|
| `errcode = -14` | 会话/token 过期 | 暂停轮询与发送,走重新扫码流程 |
| `ret != 0` | 业务错误 | 记录完整响应,按退避策略重试 |
| HTTP 层超时 | 长轮询正常超时或网络抖动 | 直接重试 |
| 重复收到同一条消息 | `get_updates_buf` 没更新/没持久化 | 检查游标保存逻辑;应用层按消息 ID 去重兜底 |
## 11. 协议层 Checklist(实现自查)
- [ ] 扫码登录:`get_bot_qrcode` → 渲染二维码 → 轮询 `get_qrcode_status` 到 `confirmed`
- [ ] 持久化:`bot_token` / `ilink_bot_id` / `ilink_user_id` / `baseurl` / `get_updates_buf` / 每对话 `context_token`
- [ ] 请求头:`Authorization`、`AuthorizationType`、随机 `X-WECHAT-UIN`
- [ ] 长轮询循环:超时 > 服务端 timeout、游标即时保存、异常不中断
- [ ] 回复带 `context_token`、带唯一 `client_id`
- [ ] errcode=-14 的暂停与重新登录路径
- [ ] 退避重试策略
## 参考资料
- [微信 Bot API 技术解析(含裸调 demo.mjs)](https://github.com/hao-ji-xing/openclaw-weixin/blob/main/weixin-bot-api.md)
- [WeChat ClawBot iLink 协议解析](https://www.chihao.li/blog/zh/wechat-clawbot-ilink-protocol)
- [openclaw-weixin-sdk (Java) — 失败恢复与退避策略一节](https://github.com/openclaw-java/openclaw-weixin-sdk)
- [OpenAkita 微信适配器说明](https://github.com/openakita/openakita/blob/main/docs/im-channels.md)