weixin_clawbot 0.2.0
weixin_clawbot: ^0.2.0 copied to clipboard
A Flutter package for binding WeChat via QR code and receiving/sending messages through the WeChat iLink Bot API (OpenClaw ClawBot).
weixin_clawbot #
一个 Flutter package,通过微信扫码绑定 ClawBot(基于 OpenClaw iLink Bot API),并在 Flutter 应用中接收/发送微信消息。
源码仓库:https://github.com/ubuntu2204/weixin_clawbot 协议实现参考腾讯官方插件 Tencent/openclaw-weixin(详见文末"参考与致谢")。
灵感来源:@claw-lab/wxclawbot-cli 和 openclaw-weixin-cli。
功能 #
| 功能 | 描述 |
|---|---|
| 🔗 微信扫码绑定 | 获取 QR 码 → 微信扫描 → 自动保存凭证 |
| 📥 接收消息 | HTTP 长轮询 (getupdates),消息以 Stream<WeixinMessage> 形式推送 |
| 📤 发送消息 | 发送文本消息,自动携带 context_token |
| 💾 凭证持久化 | 使用 shared_preferences 跨应用重启保存账号 |
| 🧩 开箱即用 Widget | QrLoginWidget / showQrLoginDialog 直接放入 UI |
快速开始 #
1. 添加依赖 #
dependencies:
weixin_clawbot: ^0.2.0
2. 前置要求 #
使用此 package 前,需要先在 OpenClaw 中安装微信插件并完成一次命令行登录,以确认账号可用:
npx -y @tencent-weixin/openclaw-weixin-cli@latest install
插件会在
https://ilinkai.weixin.qq.com上为你的机器人注册一个账号,本 package 通过该地址的 REST API 工作。
3. 扫码绑定 #
import 'package:weixin_clawbot/weixin_clawbot.dart';
final clawbot = WeixinClawbot();
// 方式 A:对话框(推荐)
final account = await showQrLoginDialog(
context: context,
clawbot: clawbot,
);
if (account != null) {
// account.defaultTo 即绑定时扫码的微信用户 ID,可直接作为 toUserId 使用
clawbot.connect(account).listen((msg) {
print('收到消息:${msg.textContent}');
});
}
// 方式 B:嵌入到自定义页面
QrLoginWidget(
clawbot: clawbot,
onLoggedIn: (account) {
clawbot.connect(account).listen((msg) {
print('收到消息:${msg.textContent}');
});
},
)
4. 接收消息 #
// 从持久化存储中恢复已绑定账号(应用重启后自动重连)
final account = await clawbot.loadAccount();
if (account != null) {
clawbot.connect(account).listen((WeixinMessage msg) {
if (msg.isFromUser) {
print('[${msg.fromUserId}]: ${msg.textContent}');
}
});
}
5. 接收图片 #
图片消息会完整解析 image_item(CDN 下载参数 + AES 密钥)。下载并解密一张图片:
clawbot.connect(account).listen((msg) {
final image = msg.imageItem;
if (image != null) {
final bytes = await clawbot.downloadImage(image); // 已解密的原始图片字节
// 例如:Image.memory(bytes)
}
});
实现与官方 openclaw-weixin 插件一致:优先使用
media.full_url,否则请求{cdn}/download?encrypted_query_param=...(默认 CDN 为https://novac2c.cdn.weixin.qq.com/c2c),再按 AES-128-ECB + PKCS7 解密。image_item.aeskey(hex)与media.aes_key(base64)两种密钥形态均支持。 Flutter Web 上 CDN 请求走<proxyBaseUrl>/cdn前缀,配套代理服务器已支持。
6. 发送消息 #
// 发给绑定时确定的微信用户(account.defaultTo)
final result = await clawbot.sendText(
text: '你好,来自 Flutter!',
toUserId: account.defaultTo, // 扫码绑定时自动写入
);
// 也可以在收到对方消息后,直接回复其 fromUserId
final result = await clawbot.sendText(
text: '已收到你的消息',
toUserId: incomingMsg.fromUserId,
);
if (!result.ok) {
print('发送失败:${result.error}');
}
提示:
context_token由轮询器在收到消息时自动缓存,发送时无需手动传递。 若尚未收到过对方消息(冷启动主动发送),需确保account.defaultTo不为空,否则会返回错误。
Flutter Web(CORS 代理) #
浏览器不允许直接访问跨域 API,需通过本地代理服务器转发:
# 在 example 目录下启动代理
dart run bin/proxy_server.dart
然后在构建/调试时传入代理地址:
flutter run -d chrome --dart-define=PROXY_URL=http://localhost:3001
代码中创建 WeixinClawbot 时设置 proxyBaseUrl:
final clawbot = WeixinClawbot(
proxyBaseUrl: kIsWeb ? 'http://localhost:3001' : null,
);
API 参考 #
WeixinClawbot #
主门面类,管理账号与连接。
| 方法 | 说明 |
|---|---|
loadAccount() |
返回第一个持久化账号,无则返回 null |
loadAllAccounts() |
返回所有持久化账号 |
startQrLogin() |
返回 Stream<QrLoginEvent>,驱动 QR 扫码绑定流程 |
connect(account) |
启动长轮询,返回 Stream<WeixinMessage> |
sendText({text, toUserId?, accountId?}) |
向指定用户发送文本消息 |
disconnect(accountId) |
停止指定账号的长轮询 |
logout({accountId?}) |
删除持久化账号并停止轮询(null 则清除全部) |
dispose() |
停止所有轮询并释放 HTTP 连接池 |
ClawBotAccount #
| 字段 | 说明 |
|---|---|
id / botId |
机器人在 iLink 平台的用户 ID |
token |
Bearer 鉴权令牌 |
baseUrl |
服务器分配的 API 地址(可能与默认地址不同) |
defaultTo |
绑定时扫码的微信用户 ID(扫码登录后自动写入,可直接用作 toUserId) |
contextToken |
最近一条入站消息携带的 context token,用于主动推送通知 |
QrLoginEvent 子类型 #
| 类型 | 说明 |
|---|---|
QrReadyEvent(qrContent) |
QR 码内容就绪,传给 QrImageView 显示 |
QrScannedEvent |
用户已在微信扫码,等待手机端确认 |
QrVerifyCodeRequiredEvent(isRetry) |
手机显示配对数字,需调用 submitVerifyCode 提交 |
QrVerifyCodeBlockedEvent |
配对数字错误次数过多,流程终止 |
QrAlreadyBoundEvent |
该 ClawBot 已绑定(本机已存其凭据),无需重复扫码 |
QrConfirmedEvent(account) |
登录成功,账号已自动持久化 |
QrExpiredEvent |
QR 码超时过期,需重新获取 |
QrErrorEvent(error, stackTrace) |
网络或服务端错误 |
配对数字(need_verifycode):部分账号扫码后微信会在手机上显示一组数字,
需要把它输入回应用(官方 CLI 是终端输入,Flutter 端由 QrLoginWidget 内置输入框处理):
final session = await clawbot.startQrLoginSession();
session.events.listen((event) {
if (event is QrVerifyCodeRequiredEvent) {
// 从输入框拿到数字后提交(QrLoginWidget 已内置此交互)
session.submitVerifyCode(userInput);
}
if (event is QrConfirmedEvent) {
clawbot.connect(event.account);
}
});
WeixinMessage #
| 属性 | 说明 |
|---|---|
fromUserId |
发送方 iLink 用户 ID |
toUserId |
接收方 iLink 用户 ID |
textContent |
第一条文本/语音转文字内容(便捷 getter) |
isFromUser |
true 表示来自真实用户(messageType == 1) |
contextToken |
回复所需的 token,轮询器自动缓存,无需手动管理 |
createTimeMs |
消息创建时间戳(毫秒级 Unix 时间) |
items |
消息体列表,支持文本、图片、语音、文件、视频 |
API 速率限制 #
- 每个机器人账号约 7 条 / 5 分钟,服务端限制,所有客户端共享。
- 错误码
-2:触发频率限制,等待 5–10 秒后重试。 - 错误码
-14:会话已过期,需重新扫码登录(showQrLoginDialog)。
常见问题 #
Q:绑定成功后页面仍显示"未绑定"?
startQrLogin 会在 QrConfirmedEvent 到达监听器之后异步保存账号,
因此 onLoggedIn 触发时账号状态已正确更新。如果遇到此问题,请确认使用的是最新版本。
Q:发送消息返回"No recipient"?
toUserId 必须显式提供,或通过 account.defaultTo(绑定时自动写入)获取。
若 defaultTo 为空,说明服务端未在登录响应中返回 ilink_user_id,
可先让对方发一条消息,轮询器会自动从入站消息中提取并缓存收件人 ID。
Q:Flutter Web 下请求失败(CORS 错误)?
需要启动本地代理服务器(见上方"Flutter Web"章节)。
工作原理 #
Flutter App
│
├─ GET /ilink/bot/get_bot_qrcode → 获取 QR 码内容(bot_type=3)
├─ GET /ilink/bot/get_qrcode_status → 轮询扫码状态(wait / scaned / confirmed)
│ ↑ 微信用户扫码并在手机端确认
├─ POST /ilink/bot/getupdates → 长轮询接收消息(服务端持有连接 ~35s)
└─ POST /ilink/bot/sendmessage → 发送消息(附带 context_token 可触发通知)
Base URL: https://ilinkai.weixin.qq.com (登录后可能切换到账号专属域名)
Auth: Authorization: Bearer <bot_token>
参考与致谢 #
本 package 的协议实现大量参考了腾讯官方的 OpenClaw 微信通道插件:
- Tencent/openclaw-weixin(npm:
@tencent-weixin/openclaw-weixin)——官方 OpenClaw 微信 channel 插件。本项目遵循其消息收发协议(getupdates长轮询、sendmessage、context_token)、CDN 媒体下载参数(encrypt_query_param/full_url)与 AES-128-ECB + PKCS7 媒体加解密逻辑,包括image_item.aeskey(hex)与media.aes_key(base64)两种密钥形态的处理。 - @claw-lab/wxclawbot-cli 与 openclaw-weixin-cli——第三方 CLI 实现,协议细节参考。
感谢以上开源项目。
许可证 #
MIT