乐于分享
好东西不私藏

你的龙虾为何能在微信上聊天?深入源码分析

你的龙虾为何能在微信上聊天?深入源码分析

一、分析背景与目标

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
最新版本
1.0.2
发布日期
2026年3月22日(刚刚发布!)
许可证
MIT
作者
Tencent
Node.js 要求
>= 22

关键发现:这个包实际上是一个轻量安装器,它本身不包含微信通道的核心逻辑。真正的微信通道实现藏在另一个包 @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
2.4 KB
轻量安装引导程序
openclaw-weixin
47.4 KB
微信通道核心实现

三、源码结构全览

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 机制,而是采用了扫码授权的方式。这有几个好处:

  1. 安全性高
    :用户主动授权,不需要暴露密码
  2. 无需密码
    :不存储密码,只存储临时的 Bot Token
  3. 易于撤销
    :用户可以随时在微信中取消授权

4.1.2 登录流程图

4.1.3 二维码状态机


4.1.4 关键 API 端点

API 端点
方法
用途
ilink/bot/get_bot_qrcode
GET
获取登录二维码
ilink/bot/get_qrcode_status
GET
轮询二维码扫描状态

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,  // 增量同步游标timeoutMs35000,                 // 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!,statusTypingStatus.TYPING,  // 1 = typing          },        })    : async () => {},// 停止输入:发送 CANCEL 状态stop: hasTypingTicket    ? () =>sendTyping({baseUrl: deps.baseUrl,token: deps.token,body: {ilink_user_id: ctx.To,typing_ticket: deps.typingTicket!,statusTypingStatus.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秒续一次,避免微信端超时消失keepaliveIntervalMs5000,});

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 {retnumber;                // 0 = 成功  msgs?: WeixinMessage[];     // 消息列表  get_updates_buf?: string;   // 新的同步游标  longpolling_timeout_ms?: number;  // 服务器建议的下次超时}// 出站消息发送interfaceSendMessageReq {msg: {from_user_idstring;to_user_idstring;client_idstring;         // 客户端生成的唯一 IDmessage_typestring;message_statestring;item_listMessageItem[];   // 消息内容    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,timeoutMs35000,  // 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}
API 调用身份验证
媒体加密
AES-128-ECB + CDN 加密传输
防止媒体内容泄露
凭证存储
文件权限 0o600
防止凭证文件被读取
Session 守卫
检测 SESSION_EXPIRED_ERRCODE
自动处理会话过期
增量同步 get_updates_buf

 游标
防止消息重复/遗漏
ContextToken
消息关联验证
防止消息发错对话

十、总结

10.1 技术亮点

  1. Long Polling 优化
    :35秒长轮询 + 增量同步游标,高效且可靠
  2. ContextToken 机制
    :巧妙解决消息上下文关联问题
  3. CDN + AES 加密
    :安全的媒体文件传输
  4. 二维码扫码登录
    :用户友好且安全
  5. OpenClaw 插件标准接口
    :高度解耦,易于扩展

10.2 架构启示


10.3 源码位置

内容
路径
CLI 安装器
/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

本站文章均为手工撰写未经允许谢绝转载:夜雨聆风 » 你的龙虾为何能在微信上聊天?深入源码分析

猜你喜欢

  • 暂无文章