你的龙虾为何能在微信上聊天?深入源码分析
2026年3月22日,腾讯官方宣布微信推出「ClawBot」插件,正式支持接入 OpenClaw(开源 AI Agent 框架,俗称”小龙虾”)。这意味着用户可以通过微信与 AI Agent 直接对话,让 AI 助手帮你完成各种任务。
本文档的目标是从零开始,完整还原整个技术探索过程,包括:
-
如何找到并下载源码 -
源码的整体结构是什么样的 -
登录认证机制是如何工作的 -
消息收发的底层实现 -
媒体文件如何处理 -
整体系统架构是怎样的
二、探索过程:从命令到源码
2.1 从一个安装命令开始
当我们运行以下命令时:
npx -y @tencent-weixin/openclaw-weixin-cli@latest install
这个命令做了什么?背后的实现原理是什么?让我们一步步揭开面纱。
2.2 第一步:搜索包信息
首先,通过 npm 官方包页面和搜索引擎查找相关信息:
|
|
|
|---|---|
|
|
@tencent-weixin/openclaw-weixin-cli |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
关键发现:这个包实际上是一个轻量安装器,它本身不包含微信通道的核心逻辑。真正的微信通道实现藏在另一个包 @tencent-weixin/openclaw-weixin 中。
2.3 第二步:下载并解压源码
通过 npm pack 命令下载 tarball 并解压:
# 下载 CLI 安装器cd /tmp && npm pack @tencent-weixin/openclaw-weixin-cli@latest# 下载核心插件(真正的实现)npm pack @tencent-weixin/openclaw-weixin@latest# 解压tar -xzf tencent-weixin-openclaw-weixin-cli-1.0.2.tgztar -xzf tencent-weixin-openclaw-weixin-1.0.2.tgz
两个包的区别:
|
|
|
|
|---|---|---|
openclaw-weixin-cli |
|
|
openclaw-weixin |
|
|
三、源码结构全览
3.1 轻量安装器:openclaw-weixin-cli
package/├── cli.mjs # 入口脚本(124行)├── package.json # 包定义└── LICENSE # MIT 许可证
cli.mjs 的核心逻辑非常简洁(伪代码):
functioninstall() {// 1. 检查 openclaw 是否已安装if (!which("openclaw")) {error("请先安装 openclaw: npm install -g openclaw"); process.exit(1); }// 2. 安装插件run(`openclaw plugins install "@tencent-weixin/openclaw-weixin"`);// 3. 扫码登录(交互式)run(`openclaw channels login --channel openclaw-weixin`);// 4. 重启 Gatewayrun(`openclaw gateway restart`);}
一句话总结:这个安装器就是一个”一键引导脚本”,它依次调用 openclaw 的标准命令来完成安装和配置。
3.2 核心插件:openclaw-weixin
package/src/├── channel.ts # 插件主入口(380行)├── runtime.ts # 运行时上下文管理├── index.ts # 插件导出定义│├── auth/ # 认证模块│ ├── login-qr.ts # 二维码登录核心逻辑│ ├── accounts.ts # 账号凭证存储│ └── pairing.ts # 配对/白名单管理│├── api/ # 微信 API 调用封装│ ├── api.ts # 底层 HTTP 请求│ ├── types.ts # TypeScript 类型定义│ ├── config-cache.ts # 配置缓存│ └── session-guard.ts # Session 状态守卫│├── monitor/ # 消息监控(核心)│ └── monitor.ts # 长轮询主循环│├── messaging/ # 消息处理│ ├── inbound.ts # 入站消息处理│ ├── process-message.ts # 消息处理流水线│ ├── send.ts # 出站消息发送│ ├── send-media.ts # 媒体发送│ ├── debug-mode.ts # 调试模式│ └── slash-commands.ts # 斜杠命令处理│├── cdn/ # CDN 相关│ ├── upload.ts # 文件上传到微信 CDN│ ├── cdn-upload.ts # CDN 底层传输│ ├── aes-ecb.ts # AES 加密│ └── cdn-url.ts # CDN URL 处理│├── media/ # 媒体处理│ ├── media-download.ts # 媒体下载│ ├── silk-transcode.ts # 语音格式转换│ └── mime.ts # MIME 类型│└── util/ # 工具函数 ├── logger.ts # 日志 ├── random.ts # 随机 ID 生成 └── redact.ts # 敏感信息脱敏
四、核心机制详解
4.1 登录认证机制:二维码扫描
文件:src/auth/login-qr.ts
4.1.1 为什么选择二维码登录?
微信不像其他平台提供简单的 API Key 机制,而是采用了扫码授权的方式。这有几个好处:
- 安全性高
:用户主动授权,不需要暴露密码 - 无需密码
:不存储密码,只存储临时的 Bot Token - 易于撤销
:用户可以随时在微信中取消授权
4.1.2 登录流程图

4.1.3 二维码状态机

4.1.4 关键 API 端点
|
|
|
|
|---|---|---|
ilink/bot/get_bot_qrcode |
|
|
ilink/bot/get_qrcode_status |
|
|
get_bot_qrcode 请求示例:
const url = newURL(`ilink/bot/get_bot_qrcode?bot_type=3`, baseUrl);// bot_type=3 表示这是微信客户端机器人
get_qrcode_status 响应示例:
{"status":"confirmed","bot_token":"xxxxx.xxxxx.verylongtoken","ilink_bot_id":"b0f5860fdecb@im.bot","ilink_user_id":"xxxxx@stranger","baseurl":"https://ilinkai.weixin.qq.com"}
4.1.5 凭证存储
登录成功后,凭证保存在本地文件系统:
// 存储路径const path = `${OPENCLAW_STATE_DIR}/openclaw-weixin/accounts/${accountId}.json`;// 存储内容{"token": "xxxxx.xxxxx.verylongtoken","baseUrl": "https://ilinkai.weixin.qq.com","userId": "xxxxx@stranger","savedAt": "2026-03-22T12:00:00.000Z"}
注意:文件权限设置为 0o600(仅所有者可读写),保证安全性。
4.2 消息收发机制:Long Polling
文件:src/monitor/monitor.ts
4.2.1 什么是 Long Polling?
Long Polling(长轮询) 是一种实现”准实时”通信的技术。相比于 WebSocket,它更简单;相比于普通轮询,它更高效。

4.2.2 OpenClaw 的消息接收循环
// 核心循环while (!abortSignal?.aborted) {// 1. 发送长轮询请求const resp = awaitgetUpdates({ baseUrl, token,get_updates_buf: getUpdatesBuf, // 增量同步游标timeoutMs: 35000, // 35秒超时 });// 2. 处理返回的消息for (const msg of resp.msgs ?? []) {awaitprocessOneMessage(msg, deps); }// 3. 保存同步游标(用于下次增量获取)if (resp.get_updates_buf) {saveGetUpdatesBuf(resp.get_updates_buf); }}
4.2.3 增量同步机制:get_updates_buf
这是理解该系统的关键概念!

4.2.4 ContextToken:消息关联的密钥
这是微信通道的核心安全机制:

4.3 消息处理流水线
文件:src/messaging/process-message.ts

4.4 媒体文件处理
入站媒体(接收图片/视频/文件):

出站媒体(发送图片/视频/文件):

五、”对方正在输入中” 的实现机制
5.1 问题引入
很多用户发现,在微信里给 ClawBot 发消息后,会看到”对方正在输入中…”的提示。这是怎么回事?
答案:这是 OpenClaw 插件主动调用了 sendTyping API 的结果!
5.2 技术原理

5.3 关键代码
文件:src/messaging/process-message.ts
// 创建 typing callbacksconst typingCallbacks = createTypingCallbacks({// 开始输入:发送 TYPING 状态start: hasTypingTicket ? () =>sendTyping({baseUrl: deps.baseUrl,token: deps.token,body: {ilink_user_id: ctx.To,typing_ticket: deps.typingTicket!,status: TypingStatus.TYPING, // 1 = typing }, }) : async () => {},// 停止输入:发送 CANCEL 状态stop: hasTypingTicket ? () =>sendTyping({baseUrl: deps.baseUrl,token: deps.token,body: {ilink_user_id: ctx.To,typing_ticket: deps.typingTicket!,status: TypingStatus.CANCEL, // 2 = cancel }, }) : async () => {},// 错误处理onStartError: (err) => deps.log(`[weixin] typing send error: ${String(err)}`),onStopError: (err) => deps.log(`[weixin] typing cancel error: ${String(err)}`),// 保活间隔:每5秒续一次,避免微信端超时消失keepaliveIntervalMs: 5000,});
5.4 typing_ticket 从哪来?
文件:src/api/config-cache.ts
typing_ticket 是通过 getConfig API 获取的,并且有 24 小时的缓存:
constCONFIG_CACHE_TTL_MS = 24 * 60 * 60 * 1000; // 24小时// 每个用户独立缓存asyncgetForUser(userId: string, contextToken?: string) {// 首次获取或缓存过期时,调用 getConfig API// API 返回: { typing_ticket: "base64_encoded_ticket" }// 存入缓存,24小时内复用}
5.5 为什么要有 keepalive?
微信的”正在输入”提示会在一定时间后自动消失(通常 10-15 秒)。为了让用户持续看到提示,插件会每 5 秒发送一次 TYPING 状态来”续命”。
关键:只有当 hasTypingTicket 为 true(即 getConfig 成功返回了 typing_ticket)时,才会发送 typing 状态。如果获取失败,插件会静默降级——用户看不到”正在输入”提示,但消息处理仍然正常进行。
5.6 流程图:typing 与消息处理的关系

六、API 封装层
文件:src/api/api.ts
微信插件定义了清晰的 API 类型:
// 入站消息获取interfaceGetUpdatesReq { get_updates_buf?: string; // 增量同步游标}interfaceGetUpdatesResp {ret: number; // 0 = 成功 msgs?: WeixinMessage[]; // 消息列表 get_updates_buf?: string; // 新的同步游标 longpolling_timeout_ms?: number; // 服务器建议的下次超时}// 出站消息发送interfaceSendMessageReq {msg: {from_user_id: string;to_user_id: string;client_id: string; // 客户端生成的唯一 IDmessage_type: string;message_state: string;item_list: MessageItem[]; // 消息内容 context_token?: string; // 关键:必须与入站消息对应 };}
统一 HTTP 头:
functionbuildHeaders(token?: string) {return {"Content-Type": "application/json","Authorization": `Bearer ${token?.trim()}`,"AuthorizationType": "ilink_bot_token", // 微信 ilink 认证类型"X-WECHAT-UIN": randomWechatUin(), // 随机客户端标识"iLink-App-ClientVersion": "1","SKRouteTag": loadConfigRouteTag(), // 可选的路由标签 };}
七、整体架构图



八、关键代码解析
8.1 插件主入口
// index.ts - OpenClaw 插件标准导出const plugin = {id: "openclaw-weixin",name: "Weixin",description: "Weixin channel (getUpdates long-poll + sendMessage)",// 注册插件register(api: OpenClawPluginApi) {setWeixinRuntime(api.runtime);// 注册通道(处理消息) api.registerChannel({ plugin: weixinPlugin });// 注册 CLI 命令 api.registerCli(({ program, config }) =>registerWeixinCli({...}), {commands: ["openclaw-weixin"], }); },};
8.2 长轮询主循环核心
// monitor.ts - 消息监听核心exportasyncfunctionmonitorWeixinProvider(opts: MonitorWeixinOpts): Promise<void> {let getUpdatesBuf = loadGetUpdatesBuf(syncFilePath) ?? "";while (!abortSignal?.aborted) {// 发送长轮询请求const resp = awaitgetUpdates({ baseUrl, token,get_updates_buf: getUpdatesBuf,timeoutMs: 35000, // 35秒 });// 保存新的同步游标if (resp.get_updates_buf) { getUpdatesBuf = resp.get_updates_buf;saveGetUpdatesBuf(resp.get_updates_buf); }// 逐条处理消息for (const msg of resp.msgs ?? []) {awaitprocessOneMessage(msg, deps); } }}
8.3 发送消息(必须带 contextToken)
// send.ts - 发送消息exportasyncfunctionsendMessageWeixin(params) {// ⚠️ 关键:必须携带 contextTokenif (!opts.contextToken) {thrownewError("sendMessageWeixin: contextToken is required"); }awaitsendMessageApi({baseUrl: opts.baseUrl,token: opts.token,body: {msg: {to_user_id: to,context_token: opts.contextToken, // ← 必填!item_list: [{ type: "text", text_item: { text } }], }, }, });}
九、安全机制
|
|
|
|
|---|---|---|
| Token 认证 | Authorization: Bearer {token} |
|
| 媒体加密 |
|
|
| 凭证存储 |
0o600 |
|
| Session 守卫 |
SESSION_EXPIRED_ERRCODE |
|
| 增量同步 | get_updates_buf
|
|
| ContextToken |
|
|
十、总结
10.1 技术亮点
- Long Polling 优化
:35秒长轮询 + 增量同步游标,高效且可靠 - ContextToken 机制
:巧妙解决消息上下文关联问题 - CDN + AES 加密
:安全的媒体文件传输 - 二维码扫码登录
:用户友好且安全 - OpenClaw 插件标准接口
:高度解耦,易于扩展
10.2 架构启示

10.3 源码位置
|
|
|
|---|---|
|
|
/tmp/package/ |
|
|
/tmp/package-plugin/package/ |
|
|
src/channel.ts |
|
|
src/auth/login-qr.ts |
|
|
src/monitor/monitor.ts |
|
|
src/messaging/send.ts |
十一、附录:常见问题
Q: 为什么需要 OpenClaw 已安装才能使用?A: 微信插件是 OpenClaw 的”通道插件”,负责对接微信。OpenClaw 是真正的 AI Agent 运行时。
Q: Token 会不会过期?A: 会。代码中有 SESSION_EXPIRED_ERRCODE 检测,过期后会自动暂停一段时间。
Q: 支持发送小程序、链接卡片吗?A: 目前代码中主要支持文本、图片、视频、文件。链接会转为纯文本发送。
Q: 一个 OpenClaw 能接多个微信账号吗?A: 技术上支持,但需要配置多个 accountId。
文档生成时间:2026年3月22日分析工具:npm pack + source code review
夜雨聆风