乐于分享
好东西不私藏

微信龙虾插件源码分析及MCP实现

微信龙虾插件源码分析及MCP实现

目录

1. 项目概述与架构全景

2. 认证接口:QR 码登录流程

3. 核心通信接口:getUpdates 长轮询

4. 下行接口:sendMessage 消息发送

5. 媒体文件接口:CDN 加密上传/下载

6. 配置与打字状态接口

7. 通信协议细节:请求头、错误码、重试机制

8. 数据模型完整字段表

9. Session 生命周期管理

10. 消息处理内部管道

https://github.com/yindex/wx-mcp

1. 项目概述与架构全景

1.2 整体架构图

系统由三层构成:微信 iLink API 服务器、openclaw-weixin 插件层、OpenClaw Core Runtime,通过 HTTPS/JSON 双向通信,另有 CDN 服务器处理加密媒体文件。

图1:系统整体架构

1.3 目录结构

openclaw-weixin/├── index.ts                      # 插件注册入口├── src/│   ├── api/│   │   ├── api.ts               # 所有 HTTP API 封装(核心)│   │   ├── types.ts             # 协议数据类型定义│   │   ├── session-guard.ts     # Session 暂停/恢复保护│   │   └── config-cache.ts      # typing_ticket 缓存管理器│   ├── auth/│   │   ├── accounts.ts          # 账号存储/加载(凭证管理)│   │   └── login-qr.ts          # QR 码登录流程│   ├── cdn/│   │   ├── upload.ts            # 媒体上传编排│   │   ├── cdn-upload.ts        # CDN POST + AES 加密│   │   ├── aes-ecb.ts           # AES-128-ECB 加解密│   │   └── pic-decrypt.ts       # CDN 下载 + 解密│   ├── messaging/│   │   ├── inbound.ts           # 入站消息归一化│   │   ├── send.ts              # 文字/媒体消息发送│   │   ├── send-media.ts        # 媒体类型路由│   │   └── process-message.ts   # 主消息处理器│   ├── monitor/│   │   └── monitor.ts           # 长轮询事件循环(核心)│   └── storage/│       └── sync-buf.ts          # 游标持久化 

2. 认证接口:QR 码登录流程

2.1 流程图

图2:QR 码登录流程

2.2 接口一:获取二维码

GEThttps://ilinkai.weixin.qq.com/ilink/bot/get_bot_qrcode?bot_type=3

请求头:

字段

说明

SKRouteTag

可选数字字符串

路由标签,从 openclaw.json 读取

响应体:

{  "qrcode": "weixin://xxxx...",  "qrcode_img_content": "https://mp.weixin.qq.com/..."} 

字段

类型

说明

qrcode

string

二维码原始数据(用于轮询状态)

qrcode_img_content

string

二维码图片 URL(展示给用户扫码)

2.3 接口二:轮询二维码状态

端点:GET https://ilinkai.weixin.qq.com/ilink/bot/get_qrcode_status?qrcode={qrcode}

客户端超时:35 秒(AbortController),超时返回 { status: “wait” } 继续轮询

字段

类型

说明

status

enum

wait / scaned / confirmed / expired

bot_token

string?

登录成功后的 Bot 凭证 Token

ilink_bot_id

string?

Bot 账号 ID(存为 accountId)

baseurl

string?

服务器分配的 API 地址

ilink_user_id

string?

扫码用户的 ID

2.4 凭证存储

登录成功后,Token 持久化到本地文件系统(权限 0600):

~/.openclaw/openclaw-weixin/

├── accounts.json# 账号 ID 索引

└── accounts/

└──{normalizedAccountId}.json# 每个账号的凭证

账号凭证格式:

{  "token""ilink_bot_token_xxxxx",  "savedAt""2026-03-22T10:00:00.000Z",  "baseUrl""https://ilinkai.weixin.qq.com",  "userId""user123@im.wechat"}

⚠ 注意:账号 ID 规范化:b0f5860fdecb@im.bot → b0f5860fdecb-im-bot(文件系统安全)

3. 核心通信接口:getUpdates 长轮询

3.1 长轮询机制

图3:getUpdates 长轮询状态机

3.2 通用请求头(所有 API 接口共用)

请求头

必填

说明

Content-Type

application/json

固定值

Authorization

Bearer {token}

ilink_bot_token 凭证

AuthorizationType

ilink_bot_token

鉴权类型标识

Content-Length

字节数

UTF-8 编码后的 body 长度

X-WECHAT-UIN

base64(随机uint32)

反指纹随机标识,每次请求重新生成

SKRouteTag

可选数字字符串

路由标签(多区域部署)

3.3 请求体与响应体

端点:POST https://ilinkai.weixin.qq.com/ilink/bot/getupdates

// 请求体{  "get_updates_buf": "xABC...",    // 上次响应返回的游标,首次传 ""  "base_info": { "channel_version": "1.0.2" }}// 响应体{  "ret": 0,  "errcode": 0,                    // 错误码(-14 = Session 过期)  "errmsg": "",  "msgs": [ /* WeixinMessage[] */ ],  "get_updates_buf": "xDEF...",    // 下次请求必须带上的游标  "longpolling_timeout_ms": 35000  // 服务器建议下次超时毫秒数}

3.4 游标持久化

游标文件:~/.openclaw/openclaw-weixin/accounts/{accountId}.sync.json

进程重启时读取此文件,实现断点续传,不丢失历史消息。

4. 下行接口:sendMessage 消息发送

4.1 发送流程

图4:下行消息发送流程

4.2 文字消息请求体

端点:POST https://ilinkai.weixin.qq.com/ilink/bot/sendmessage

{  "msg": {    "from_user_id""",                               // Bot 发送时固定为空    "to_user_id""user123@im.wechat",    "client_id""openclaw-weixin:1711094400000-a1b2",    "message_type"2,                                // 2=Bot消息(固定)    "message_state"2,                               // 2=完成(固定)    "context_token""ctx_token_from_inbound_msg",    // 必填!会话关联 Token    "item_list": [      { "type"1"text_item": { "text""AI 的回复内容" } }    ]  },  "base_info": { "channel_version""1.0.2" }}

4.3 媒体消息请求体

// 图片消息 item (type=2){  "type"2,  "image_item": {    "media": {      "encrypt_query_param""CDN下载参数",      "aes_key""base64编码的AES密钥",      "encrypt_type"1    },    "mid_size"102400        // 密文大小(字节)  }}// 视频消息 item (type=5)"type"5"video_item": { "media": {...}, "video_size"5120000 } }// 文件附件 item (type=4)"type"4"file_item": { "media": {...}, "file_name""report.pdf""len""204800" } }

4.4 关键字段说明

字段

类型

说明

msg.from_user_id

string

Bot 发送时固定为空字符串

msg.to_user_id

string

目标用户 ID(如 xxx@im.wechat)

msg.client_id

string

唯一ID:openclaw-weixin:{ts}-{8hex}

msg.message_type

number

1=用户消息,2=Bot消息(固定2)

msg.message_state

number

0=新建,1=生成中,2=完成(固定2)

msg.context_token

string

必填!从入站消息回传,关联会话

item_list[].type

number

1=文字 2=图片 3=语音 4=文件 5=视频

⚠ 注意:context_token 仅存内存 Map,进程重启后丢失——重启后需等到用户发消息才能恢复回复能力。

5. 媒体文件接口:CDN 加密上传/下载

5.1 CDN 加密上传流程

图5:CDN 加密上传流程

5.2 getUploadUrl 接口

端点:POST https://ilinkai.weixin.qq.com/ilink/bot/getuploadurl

字段

类型

说明

filekey

string

32位随机 hex,文件唯一标识

media_type

number

1=图片 2=视频 3=文件 4=语音

to_user_id

string

接收方用户 ID

rawsize

number

明文文件字节数

rawfilemd5

string

明文文件 MD5 hex

filesize

number

密文字节数(AES-128-ECB 含 PKCS7)

no_need_thumb

boolean

true 跳过缩略图上传 URL

aeskey

string

16字节 AES key 的 hex 字符串

5.3 AES-128-ECB 加密规格

算法:AES-128-ECB(无 IV)

填充:PKCS7(Node.js crypto 默认)

密钥:每次上传随机生成 16 字节 (crypto.randomBytes(16))

密文大小:ceil((plaintext_size + 1) / 16) * 16

加密:createCipheriv(“aes-128-ecb”, key, null)

解密:createDecipheriv(“aes-128-ecb”, key, null) 

5.4 CDN 加密下载流程

图6:CDN 加密下载解密流程

6. 配置与打字状态接口

6.1 getConfig 接口

端点:POST https://ilinkai.weixin.qq.com/ilink/bot/getconfig

// 请求体

{

“ilink_user_id”:“user123@im.wechat”,

“context_token”:“ctx_token”,

“base_info”:{ “channel_version”: “1.0.2” }

}

// 响应体

{ “ret”: 0, “errmsg”: “”, “typing_ticket”: “base64编码的打字凭证…” }

缓存策略:

场景

行为

缓存命中且未过期

直接返回 typingTicket,不发请求

缓存未命中/已过期

调用 getConfig API

API 成功

缓存 TTL = 随机(0 ~ 24h),重置 retryDelay=2s

API 失败

retryDelay 指数退避,上限 60min

6.2 sendTyping 接口

端点:POST https://ilinkai.weixin.qq.com/ilink/bot/sendtyping

{

“ilink_user_id”:“user123@im.wechat”,

“typing_ticket”:“base64编码的凭证”,

“status”:1,// 1=正在输入, 2=取消输入状态

“base_info”:{ “channel_version”: “1.0.2” }

}

7. 通信协议细节:请求头、错误码、重试机制

7.1 超时设置

接口类型

超时时间

说明

getUpdates

35,000ms

长轮询,超时视为正常(无消息),继续下一次

get_qrcode_status

35,000ms

长轮询,超时返回 { status: “wait” }

sendMessage / getUploadUrl

15,000ms

普通 API

getConfig / sendTyping

10,000ms

轻量 API

7.2 错误码

错误码

含义

处理策略

0

成功

正常处理

-14

Session 过期

pauseSession() 暂停 60 分钟

其他非0

API 错误

计数累加,连续 3 次退避 30s

HTTP 4xx

CDN 客户端错误

立即失败,不重试

HTTP 5xx

CDN 服务器错误

最多重试 3 次

7.3 重试策略总结

getUpdates:第1,2次失败→等2s;第3次→退避30s→重置计数

Session过期(errcode=-14):冷却60min,所有API请求前检查

getConfig失败退避:2s→4s→8s→…→上限60min

CDN上传:HTTP 5xx最多重试3次;HTTP 4xx立即失败

8. 数据模型完整字段表

8.1 WeixinMessage(消息主体)

interface 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;   // 创建时间戳(毫秒)  update_time_ms?: number;   // 更新时间戳(毫秒)  delete_time_ms?: number;   // 删除时间戳(撤回消息)  session_id?: string;       // 会话 ID  group_id?: string;         // 群组 ID(仅群聊)  message_type?: number;     // 1=用户消息,2=Bot消息  message_state?: number;    // 0=新建,1=生成中,2=完成  item_list?: MessageItem[]; // 消息内容列表  context_token?: string;    // 会话关联 Token(回复时必须带上)}

8.2 MessageItem 类型枚举

type 值

类型

对应字段

说明

1

TEXT

text_item.text

纯文本

2

IMAGE

image_item.media / aeskey

图片,含 CDN 引用和 AES key

3

VOICE

voice_item.media / text

语音,含 STT 转文字结果

4

FILE

file_item.media / file_name / len

文件附件

5

VIDEO

video_item.media / video_size

视频

8.3 CDNMedia(CDN 媒体引用)

{  encrypt_query_param?: string// CDN 下载参数(含认证信息)  aes_key?: string;             // base64 编码的 AES 密钥  encrypt_type?: number;        // 0=只加密fileid,1=打包缩略图等信息}

8.4 语音编码类型(VoiceItem.encode_type)

encode_type

编码格式

1

PCM

2

ADPCM

4

Speex

5

AMR

6

Silk(微信私有格式)

7

MP3

8

OGG-Speex

9. Session 生命周期管理

图7:Session 生命周期与 Context Token 管理

⚠ 注意:context_token 存于纯内存 Map,进程重启后丢失。生产环境建议持久化到 Redis(TTL 24h)。

10. 消息处理内部管道

图8:入站消息完整处理管道

10.2 消息类型优先级

当 item_list 包含多种媒体类型时(罕见),处理优先级:

图片(type=2) > 视频(type=5) > 文件(type=4) > 语音(type=3, 文字回退) 

11. 如何剥离 OpenClaw 实现通用消息通知系统

11.1 核心洞察

当前代码的 OpenClaw 依赖仅集中在 3 个接触点,其余 90% 代码与框架完全无关:

openclaw/plugin-sdk 依赖点:

① channelRuntime.dispatchReplyFromConfig(ctx)— AI 处理入站消息

② channelRuntime.recordInboundSession(ctx)— 记录会话

③ stripMarkdown()— Markdown 转纯文本工具 

11.2 通用消息通知系统架构

图9:剥离 OpenClaw 后的通用消息通知系统

11.3 生产级部署架构

图10:生产级微信通知系统部署架构

11.4 自定义应用消息测试

11.5 独立MCP Server实现
https://github.com/yindex/wx-mcp
本站文章均为手工撰写未经允许谢绝转载:夜雨聆风 » 微信龙虾插件源码分析及MCP实现

猜你喜欢

  • 暂无文章