Skip to content

Latest commit

 

History

History
451 lines (366 loc) · 11.8 KB

File metadata and controls

451 lines (366 loc) · 11.8 KB

OpenClaw Weixin Channel Protocol

Protocol documentation for the WeChat OpenClaw iLink messaging channel. This document describes the HTTP JSON API used for WeChat bot communication.

一、概述

OpenClaw Weixin channel 通过 HTTP JSON API 与微信后端网关 (ilinkai.weixin.qq.com) 通信,实现:

  1. 扫码登录 — 获取 bot_token
  2. 长轮询收消息 — getUpdates 模式
  3. 发送消息 — 文本/图片/视频/文件
  4. CDN 媒体传输 — AES-128-ECB 加解密
  5. 输入状态指示 — typing indicator

二、常量与基础配置

常量
API Base URL https://ilinkai.weixin.qq.com
CDN Base URL https://novac2c.cdn.weixin.qq.com/c2c
Bot Type (QR登录) "3"
Long-poll 默认超时 35,000 ms
API 请求默认超时 15,000 ms
Config 请求超时 10,000 ms
Session 过期错误码 -14
Session 暂停时长 1 小时

三、通用请求头

所有 POST 请求携带以下头:

Content-Type: application/json
AuthorizationType: ilink_bot_token
Authorization: Bearer <bot_token>
Content-Length: <body字节长度>
X-WECHAT-UIN: <random_uint32的十进制字符串的base64编码>
SKRouteTag: <可选,路由标签>

X-WECHAT-UIN 生成算法:

1. 生成 4 字节随机数
2. 读取为 uint32 big-endian
3. 转为十进制字符串 (如 "3221225472")
4. 对该字符串做 base64 编码

四、认证流程 (QR Code Login)

4.1 获取二维码

GET {baseUrl}/ilink/bot/get_bot_qrcode?bot_type=3
Headers:
  iLink-App-ClientVersion: 1  (仅在 status 轮询时)
  SKRouteTag: <可选>

响应:

{
  "qrcode": "<qrcode_id>",
  "qrcode_img_content": "<qrcode_url_for_scan>"
}
  • qrcode: 二维码标识符,用于后续轮询状态
  • qrcode_img_content: 二维码图片URL,展示给用户扫描

4.2 轮询二维码状态

GET {baseUrl}/ilink/bot/get_qrcode_status?qrcode=<qrcode_id>
Headers:
  iLink-App-ClientVersion: 1
  SKRouteTag: <可选>

响应:

{
  "status": "wait" | "scaned" | "confirmed" | "expired",
  "bot_token": "<token>",           // confirmed 时返回
  "ilink_bot_id": "<bot_id>",       // confirmed 时返回,如 "hexid@im.bot"
  "baseurl": "<api_base_url>",      // confirmed 时返回(可能更新)
  "ilink_user_id": "<user_id>"      // confirmed 时返回,扫码人的用户ID
}

状态流转: waitscanedconfirmed (或 expired)

  • 长轮询超时 35s,超时返回 {"status": "wait"}
  • QR码过期后可重新获取,最多刷新 3 次
  • 总等待超时 480s (8分钟)

4.3 登录后保存

登录成功后保存:

  • bot_token — 后续所有API请求的认证令牌
  • ilink_bot_id — 账号标识 (normalized: @-, .-)
  • baseurl — 可能更新的API地址
  • ilink_user_id — 绑定的微信用户ID

五、核心API接口

所有接口均为 POST,路径前缀:{baseUrl}/ilink/bot/

每个请求体包含 base_info: { channel_version: "<版本号>" }

5.1 getUpdates — 长轮询消息

POST {baseUrl}/ilink/bot/getupdates

请求体:
{
  "get_updates_buf": "<同步游标,首次传空字符串>",
  "base_info": { "channel_version": "2.0.1" }
}

响应:

{
  "ret": 0,
  "errcode": 0,
  "errmsg": "",
  "msgs": [WeixinMessage, ...],
  "get_updates_buf": "<新游标>",
  "longpolling_timeout_ms": 35000
}
  • ret=0 成功,非0失败
  • errcode=-14 会话过期,需暂停1小时
  • get_updates_buf 必须保存,下次请求回传
  • 客户端超时(AbortError)视为正常,返回空响应重试
  • 连续失败3次后 backoff 30s

5.2 sendMessage — 发送消息

POST {baseUrl}/ilink/bot/sendmessage

请求体:
{
  "msg": {
    "from_user_id": "",
    "to_user_id": "<目标用户ID>",
    "client_id": "<客户端生成的唯一ID>",
    "message_type": 2,          // BOT=2
    "message_state": 2,         // FINISH=2
    "item_list": [MessageItem, ...],
    "context_token": "<从收到的消息中获取>"
  },
  "base_info": { "channel_version": "2.0.1" }
}

重要: context_token 从收到的用户消息中提取,回复时必须回传。

5.3 getUploadUrl — 获取CDN上传参数

POST {baseUrl}/ilink/bot/getuploadurl

请求体:
{
  "filekey": "<随机16字节hex>",
  "media_type": 1,              // 1=IMAGE, 2=VIDEO, 3=FILE, 4=VOICE
  "to_user_id": "<目标用户ID>",
  "rawsize": 12345,             // 明文字节大小
  "rawfilemd5": "<明文MD5 hex>",
  "filesize": 12352,            // AES-128-ECB加密后密文大小
  "no_need_thumb": true,        // 不需要缩略图
  "aeskey": "<AES密钥hex>",
  "base_info": { ... }
}

响应:

{
  "upload_param": "<上传加密参数>",
  "thumb_upload_param": "<缩略图上传参数>"
}

5.4 getConfig — 获取配置

POST {baseUrl}/ilink/bot/getconfig

请求体:
{
  "ilink_user_id": "<用户ID>",
  "context_token": "<可选>",
  "base_info": { ... }
}

响应:

{
  "ret": 0,
  "typing_ticket": "<base64编码的typing ticket>"
}

5.5 sendTyping — 输入状态

POST {baseUrl}/ilink/bot/sendtyping

请求体:
{
  "ilink_user_id": "<用户ID>",
  "typing_ticket": "<从getConfig获取>",
  "status": 1,                  // 1=正在输入, 2=取消输入
  "base_info": { ... }
}

六、消息数据结构

WeixinMessage

{
  seq?: number;               // 消息序列号
  message_id?: number;        // 消息唯一ID
  from_user_id?: string;      // 发送者ID (如 "xxx@im.wechat")
  to_user_id?: string;        // 接收者ID
  client_id?: string;         // 客户端消息ID
  create_time_ms?: number;    // 创建时间戳(ms)
  session_id?: string;        // 会话ID
  message_type?: number;      // 1=USER, 2=BOT
  message_state?: number;     // 0=NEW, 1=GENERATING, 2=FINISH
  item_list?: MessageItem[];  // 消息内容列表
  context_token?: string;     // 会话上下文令牌(必须回传)
}

MessageItem

{
  type?: number;              // 1=TEXT, 2=IMAGE, 3=VOICE, 4=FILE, 5=VIDEO
  text_item?: { text: string };
  image_item?: ImageItem;
  voice_item?: VoiceItem;
  file_item?: FileItem;
  video_item?: VideoItem;
  ref_msg?: { message_item?: MessageItem; title?: string };
}

CDNMedia (所有媒体共用)

{
  encrypt_query_param?: string;  // CDN下载/上传加密参数
  aes_key?: string;              // base64编码的AES-128密钥
  encrypt_type?: number;         // 0=只加密fileid, 1=打包缩略图等
}

ImageItem

{
  media?: CDNMedia;
  thumb_media?: CDNMedia;
  aeskey?: string;           // hex字符串形式的AES key(优先于media.aes_key)
  mid_size?: number;         // 中图密文大小
  hd_size?: number;          // 高清密文大小
  thumb_size?: number;
  thumb_height?: number;
  thumb_width?: number;
}

VoiceItem

{
  media?: CDNMedia;
  encode_type?: number;      // 6=silk
  sample_rate?: number;
  playtime?: number;         // 毫秒
  text?: string;             // 语音转文字(如有则直接用文字)
}

FileItem

{
  media?: CDNMedia;
  file_name?: string;
  md5?: string;
  len?: string;              // 字节数(字符串)
}

VideoItem

{
  media?: CDNMedia;
  video_size?: number;
  play_length?: number;
  video_md5?: string;
  thumb_media?: CDNMedia;
  thumb_size?: number;
  thumb_height?: number;
  thumb_width?: number;
}

七、CDN 媒体操作

7.1 AES-128-ECB 加密

  • 算法:AES-128-ECB,PKCS7 填充
  • 密文大小计算:ceil((plaintext_size + 1) / 16) * 16
  • 密钥:随机16字节

7.2 上传流程

1. 读取文件 → 计算 rawsize, rawfilemd5(hex)
2. 生成 filekey = random(16).hex()
3. 生成 aeskey = random(16)
4. 计算 filesize = aesEcbPaddedSize(rawsize)
5. 调用 getUploadUrl → 获取 upload_param
6. AES-128-ECB(文件内容, aeskey) → ciphertext
7. POST {cdnBaseUrl}/upload?encrypted_query_param={upload_param}&filekey={filekey}
   Content-Type: application/octet-stream
   Body: ciphertext
8. 从响应头 x-encrypted-param 获取 downloadParam
9. 构造 CDNMedia: { encrypt_query_param: downloadParam, aes_key: base64(hex(aeskey)) }

7.3 下载解密流程

1. GET {cdnBaseUrl}/download?encrypted_query_param={encrypt_query_param}
2. 获取密文 bytes
3. 解析 aes_key:
   - base64解码后16字节 → 直接用
   - base64解码后32字节hex字符串 → hex解码为16字节
4. AES-128-ECB 解密 → 明文

7.4 CDN URL 构造

  • 上传: {cdnBaseUrl}/upload?encrypted_query_param={upload_param}&filekey={filekey}
  • 下载: {cdnBaseUrl}/download?encrypted_query_param={encrypt_query_param}

八、SDK 功能清单

核心功能

  1. QR Code 登录 — 获取QR码 + 轮询状态 + 保存token
  2. 长轮询消息接收 — getUpdates 循环,自动重连/退避
  3. 发送文本消息 — sendMessage
  4. 发送图片 — 上传CDN + sendMessage(ImageItem)
  5. 发送视频 — 上传CDN + sendMessage(VideoItem)
  6. 发送文件 — 上传CDN + sendMessage(FileItem)
  7. 接收媒体 — 下载CDN + AES解密
  8. 输入状态 — sendTyping(开始/取消)
  9. 获取配置 — getConfig(typing_ticket)

辅助功能

  1. AES-128-ECB 加解密
  2. X-WECHAT-UIN 生成
  3. context_token 管理 — 自动追踪每个用户的最新token
  4. 同步游标管理 — get_updates_buf 持久化
  5. 会话过期处理 — errcode=-14 自动暂停
  6. 消息回调/事件 — 收到消息时触发回调
  7. 多账号支持 — 支持多个微信号同时在线

九、实战关键发现 (Critical)

9.1 context_token 必须由用户先发消息获取

这是最重要的协议约束,文档中未明确说明:

  • context_token 不是登录时获取的,而是 用户从微信发送第一条消息后,由 getUpdates 响应中的 WeixinMessage.context_token 字段携带。
  • 没有 context_tokensendMessage 调用会被服务端接受 (HTTP 200),但 消息不会投递到微信客户端
  • 因此正确的使用流程是:
1. 扫码绑定 → 获取 bot_token
2. 启动 getUpdates 长轮询
3. 等待用户从微信端发送第一条消息(建立会话通道)
4. 从收到的消息中提取 context_token 并缓存
5. 之后发消息时必须携带该 context_token
6. 每次收到新消息都应更新 context_token(可能会变)

9.2 QR 登录返回的 ilink_user_id

  • get_qrcode_statusconfirmed 状态时返回 ilink_user_id
  • 这是 扫码人的微信用户ID(格式: xxx@im.wechat
  • 可以直接用作后续 sendMessageto_user_id
  • 但仍需等待该用户发来消息获取 context_token 后才能真正投递

9.3 完整使用流程图

[扫码绑定]                       [微信用户]
    |                                |
    |-- get_bot_qrcode ------------> |
    |<-------------- 扫码确认 -------|
    |   (获得 bot_token,             |
    |    ilink_user_id)              |
    |                                |
    |-- 启动 getUpdates 长轮询 ----> |
    |                                |
    |   *** 此时无法发消息给微信 ***   |
    |                                |
    |<-------- 用户发来第一条消息 ----|
    |   (获得 context_token)         |
    |                                |
    |   *** 会话通道已建立 ***        |
    |                                |
    |-- sendMessage + context_token  |
    |          -----> 微信收到消息 -->|
    |                                |
    |<-------- 用户回复消息 ---------|
    |   (更新 context_token)         |

十、关键用户ID格式

  • 微信用户ID: xxx@im.wechat
  • Bot ID: xxx@im.bot (normalized: xxx-im-bot)