Coding

Weixinclaw Proactive Push

Try it

通过 WorkBuddy 已连接的 ClawBot 微信 bot 通道(weixinClawBot / ilink bot,非微信客服号)主动向老板微信推送文本/图片/文件/视频。当用户要求"通过 ClawBot 主动推送/给老板发微信/微信主动推送测试/微信独立消息/发图片给老板/发文件给老板"时使用。协议严格对齐腾讯官方插件 @tencent-weixin/openclaw-weixin@2.4.6(src/api + src/messaging + src/cdn)。

What it does

通过 WorkBuddy 已连接的 ClawBot 微信 bot 通道(weixinClawBot / ilink bot,非微信客服号)主动向老板微信推送文本/图片/文件/视频。当用户要求"通过 ClawBot 主动推送/给老板发微信/微信主动推送测试/微信独立消息/发图片给老板/发文件给老板"时使用。协议严格对齐腾讯官方插件 @tencent-weixin/openclaw-weixin@2.4.6(src/api + src/messaging + src/cdn)。

The skill document

📦 本 skill 源码仓库https://github.com/NoahEleven/weixinclaw-proactive-push (公开,欢迎 clone / 提 Issue)

🔒 隐私红线(务必遵守):本 skill 运行时只读本机凭据、不写出任何密钥、日志不打印 token 明文。 分享 / 备份 / 提交时,严禁携带以下文件(含 bot 完整凭证 / 老板微信 id):

  • ~/.workbuddy/settings.json(含完整 botTokenuserId
  • ~/.workbuddy/claw-state/weixin/(cursor 游标内含 bot 凭证镜像 get_updates_buf) 本仓库只含 SKILL.md + send.js,接收方用自己的 settings.json 即可,无需你提供任何密钥。

背景

WorkBuddy 迁移自 OpenClaw。微信主动推送能力其实一直活着,藏在 ~/.workbuddy/settings.jsonclaw/users//channels/weixinClawBot 里—— 它是 ClawBot 的微信 bot 通道(ilink bot,polling 模式),对接 https://ilinkai.weixin.qq.com

⚠️ 这不是微信客服号:微信客服号走公众平台客服消息 API(48 小时会话限制), 而本通道是 WorkBuddy / ClawBot 内置的 bot 通道,走 ilink bot API,可随时主动推送,不受客服号限制。

协议实现严格对齐腾讯官方插件 @tencent-weixin/openclaw-weixin@2.4.6 (npm 包,含 src/api/api.tssrc/api/types.tssrc/messaging/*src/cdn/*)。 关键常量、请求头、item 结构、AES 加密、错误码检查均与官方保持一致。

⚠️ openclaw-weixin-cli 不需要装(那是 OpenClaw 的通道安装器,对 WorkBuddy 无用)。

何时用

  • "通过 ClawBot 主动推一条消息给老板"(文本默认空 context_token,无需激活;详见下方机制专节)
  • "微信主动推送测试""给老板发条微信 bot 消息"
  • "发张图/发个文件/发段视频给老板"(context_token 自动读取,详见机制专节)
  • 定时任务把结果主动推到老板微信(ClawBot bot 消息,非对话气泡)

怎么跑

脚本自动读 settings.json 取凭据与收件人,向老板微信发消息:

NODE="C:/Users//.workbuddy/binaries/node/versions/22.22.2/node.exe"   # 将  换成你的 Windows 用户名
SKILL="C:/Users//.workbuddy/skills/weixinclaw-proactive-push/send.js"  # ⚠️ 必须指到 send.js;只传目录会 MODULE_NOT_FOUND(无 package.json main)

# 文本(默认空 context_token,无需激活;详见下方机制专节)
"$NODE" "$SKILL" "🦐 你的消息内容"

# 从文件读长文本
"$NODE" "$SKILL" --text-file 内容.txt

# 图片 + 可选说明(context_token 自动读取)
"$NODE" "$SKILL" --image 截图.png --caption "看这张图"

# 文件 + 文件名 + 可选说明(context_token 自动读取)
"$NODE" "$SKILL" --file 报告.pdf --name "周报.pdf" --caption "请查阅"

# 视频(context_token 自动读取)
"$NODE" "$SKILL" --video clip.mp4

# 显式指定 token(极端兜底:手动传入一个已知有效的 context_token)
"$NODE" "$SKILL" "回复内容" --context 
  • 不传参数 → 发送默认测试文案(文本)。
  • 跑的时候若因沙箱拦截网络,加 dangerouslyDisableSandbox: true(或在弹窗点允许)。
  • 成功标志:终端打印 [weixinclaw] OK,老板微信里会收到一条 ClawBot bot 消息(不是对话气泡)。

凭据从哪来(settings.json)

claw/users//channels/weixinClawBot = {
  "enabled": true,
  "channelId": "<你的 bot channelId,从你的 settings.json 读取>",
  "botToken":  "<完整 botToken 串,含 accountId: 前缀;脚本自动从你的 settings.json 读取,无需手填>",  // 完整 token = Bearer 凭据
  "baseUrl":   "https://ilinkai.weixin.qq.com",                                // apiBaseUrl
  "accountId": "<你的 bot accountId,从你的 settings.json 读取>",
  "userId":    "<老板的 im.wechat id = to_user_id;脚本自动从你的 settings.json 读取>",                       // 老板的 im.wechat id = to_user_id
  "connectionMode": "polling"
}

🔒 以上字段均为接收方自己settings.json 内容,无需分享者提供。脚本只读取、不写出。

🔴 关键机制:context_token(2026-07-09 定稿)

文本默认空 token;只有媒体才带 cursor buf

  • 文本推送msg.context_token 默认传【空串】即可发送,无需读 cursor、无需激活。 这就是为什么会话闲置十几小时(例如昨晚到今早)文本仍能正常推送——空 token 对文本长期有效。
  • 媒体推送(图片 / 文件 / 视频)必须带非空 context_token, 唯一来源是 cursor 游标里的 get_updates_buf(见下)。空 token 发媒体会 ret:-2
  • 任何被拒(ret:-2)都要激活:文本平时用空 token 就能发,一旦文本也被拒, 大概率是底层 bot id / 凭证已轮换,同样需要先给 ClawBot 发消息激活后重跑(详见排错表)。

媒体的 context_token 来源:cursor 游标里的 get_updates_buf

  • host 实时把 ilink 接收会话游标写进 ~/.workbuddy/claw-state/weixin/_im.bot.cursor.jsonget_updates_buf 字段。
  • 该字段值是 base64 编码的 protobuf;我们把整段 base64 串原样当作 context_token 用 (不是解码出来的 botToken 串,也不是里面的 accountId 段)。

✅❌ 读取铁律(违反任何一条都会 ret:-2,这是"编码读取方式"的要害)

  1. 以 UTF-8 文本cursor.json → ② JSON.parse → ③ 取 get_updates_buf 字段的【字符串值】 → ④ 原样作为 context_token
  2. ❌ 严禁对这串做任何变换:base64 解码、重新编码、trim、URL-decode、转义、换行、截断、大小写改写。 base64 串里全是 A-Za-z0-9+/= 字符,JSON 安全,直接原样塞进 context_token 字段即可
  3. ❌ 不要"聪明地"解析 / 改写 / 重组这个串——它就是个不透明令牌,服务端的校验逻辑你是看不见的,任何本地变换都只会让它失效。
  4. ✅ 直接用本仓库的 send.js(已封装以上全部),不要手工拼请求——手工拼最容易在读取这步翻车。
  • 凭证稳定、不要误判botToken / accountId 段(形如 ``)约 1 天才轮换一次,期间一直有效、不变是正常的,它不是失效信号,别拿它判断 buf 是否过期。
  • 文本无需激活:文本走空 token,会话闲置多久都能发(实测跨十几小时);只有出现 ret:-2 时才需激活。
  • 媒体激活一次可长期使用:媒体带 cursor buf,只要此前已激活过且没收到 ret:-2,就可持续推送,不需要每次推送前都重新发消息
  • 文本被拒 = id 变了,也要激活:文本平时空 token 能发;一旦文本 ret:-2,大概率底层 bot id 轮换,同样先发消息激活再重跑。
  • ⚠️ 未激活时反复直接重跑必定再次 ret:-2——请务必先完成"发消息激活"这个动作,再重跑命令。
  • 实测:媒体用 buf 当 context_token + 完整 botToken 当 Bearer ⇒ {} 成功,且不读 context-token.json、无需轮询。
  • 编码细节:get_updates_buf 解码后为约 78 字节 protobuf,含 botId(varint) + 账号标识 + botToken 明文段(accountId@im.bot:secret)。

🔒 get_updates_buf 内嵌了 bot 凭证副本,绝不要把它粘贴到聊天、Issue、或随仓库提交。

im.bot.cursor.json 文件的更新机制(务必分清"文件刷新"和"buf 轮换"两件事)

文件路径:~/.workbuddy/claw-state/weixin/_im.bot.cursor.json (`` = bot accountId 段,如同一个 bot 换了凭证/会话,文件名的这段会变,会出现新的一份 cursor 文件)。

① 文件何时被创建

  • 该文件由 host(WorkBuddy 后台的 ilink bot 长轮询)维护。通道必须先建立过会话才会有这个文件—— 即老板/用户曾在微信给 ClawBot 发过消息、host 侧建立了接收游标。
  • 若目录里没有任何 *_im.bot.cursor.json:让老板/用户先在微信给 ClawBot 发一条消息,host 才会写出该文件。

② 文件 mtime(修改时间)——host 实时刷新,别拿它当判断依据

  • host 在每一轮长轮询写盘游标时都会更新该文件的 mtime,非常频繁,且与你是否发消息无关(是 host 侧实时行为,不是你触发的)。
  • ⚠️ 因此 绝不能用 mtime("多久没变 / 刚变过")来判断 buf 是否新鲜、是否需要刷新——mtime 一直在变,但 get_updates_buf 的值未必跟着变。这是最容易误判的点。

get_updates_buf 内容(真正的 token)——稳定,靠"激活"刷新,不靠时间猜

  • 这才是我们要的 token。它在 bot 凭证/会话有效期内保持稳定accountId 段约 1 天轮换一次,期间不变属正常)。
    • 判断是否需要刷新,唯一依据是「实际推送是否被服务端拒绝(ret:-2)」
      • 被拒=当前 buf 暂时不可发送 → 让老板/用户在微信给 ClawBot 发一条消息激活(host 随即刷新游标),再重跑 send.js 自动读最新 buf 重发即可。
      • 只要不被拒,激活一次即可在较长时间内反复推送(实测跨十几小时仍能成功),不需要每次推送前都发消息激活。
      • ⚠️ 不要把 mtime 变化、或"buf 值有没有变"当成判断条件——这两者都不决定可发送性,纯属干扰。
  • send.js 收到 -2 会【清晰报错并退出】,不内置轮询等待("等待刷新"退步逻辑已彻底删除);由你手动激活后重跑命令即可。

send.js 的取值优先级

文本:ctx = --context 显式传入 > 【空串】(默认,无需 cursor / 激活)
媒体:ctx = --context 显式传入 > 读最新 _im.bot.cursor.json 的 get_updates_buf(默认走这条)

(媒体的 get_updates_buf 为空、或任何推送被拒 ret:-2(含文本被拒=id 变了)时: 先让老板/用户自己在微信给 ClawBot 发一条消息激活(host 随即刷新游标),再重跑 send.js; 仍失败再走 --context 显式兜底。)

🔴 关键坑(必看,否则必失败)

  1. Bearer token 必须用完整 botToken(含 accountId: 前缀),即 <完整 botToken 串(含 accountId: 前缀),从你的 settings.json 读取>。 只取冒号后的短串会返回 errcode:-14 session timeout,推送失败。
  2. 协议常量对齐官方 @tencent-weixin/openclaw-weixin@2.4.6(不要再用旧值):
    • channel_version = "2.4.6"(早期实现误用 cbc-1.0.0,现已对齐官方)
    • iLink-App-ClientVersion = 132102(= buildClientVersion("2.4.6")
    • iLink-App-Id = "bot"bot_agent = "OpenClaw"
  3. 文本默认空 context_token 即可媒体推送必须带有效 context_token(见上节),否则 ret:-2 拒收。文本若也被拒 ret:-2,大概率 bot id 轮换,需激活。
  • 端点:POST {baseUrl}/ilink/bot/sendmessage
  • 请求头(完整,缺一不可):
    • Authorization: Bearer <完整botToken>
    • AuthorizationType: ilink_bot_token
    • X-WECHAT-UIN: <随机4字节uint32的base64>
    • iLink-App-Id: bot
    • iLink-App-ClientVersion: 132102
    • Content-Type: application/json
  • Body(文本):
{
  "msg": {
    "from_user_id": "",
    "to_user_id": "",
    "client_id": "<16位随机hex>",
    "message_type": 2,
    "message_state": 2,
    "item_list": [{ "type": 1, "text_item": { "text": "消息内容" } }],
    "context_token": "<从 cursor 读取的 get_updates_buf>"
  },
  "base_info": { "channel_version": "2.4.6", "bot_agent": "OpenClaw" }
}
  • item_list 类型:1=文本 / 2=图片 / 4=文件 / 5=视频(text 超 4000 字自动分片)。
  • 成功判定:sendmessage 返回 {}(host 视 ret 缺失为成功);ret 非零(如 -2)即失败。
  • 会话存活自检:POST {baseUrl}/ilink/bot/getupdates 返回无 errcode 即健康。

富媒体流程(图片/文件/视频)

  1. context_token 自动从 cursor 游标读取(详见上方机制专节),无需手动准备。没有会 ret:-2
  2. 生成 16 字节随机 AES 密钥 aesKeyaesKeyHex = aesKey.toString('hex')
  3. POST /ilink/bot/getuploadurl,body:filekey, media_type(1=IMAGE/2=VIDEO/3=FILE), to_user_id, rawsize, rawfilemd5, filesize(密文长), aeskey(aesKeyHex), no_need_thumb
  4. 取响应 upload_full_url(或旧版 upload_paramhttps://novac2c.cdn.weixin.qq.com/c2c/upload?encrypted_query_param=...&filekey=...)。
  5. POST 密文(AES-128-ECB(PKCS7) 加密后的文件)到该 URL,Content-Type: application/octet-stream
  6. 取响应头 x-encrypted-param 作为下载令牌 encrypt_query_param
  7. 构造 item:media = { encrypt_query_param, aes_key: base64(aesKeyHex), encrypt_type: 1 }, 图片加 mid_size,文件加 file_name+len,视频加 video_size
  8. POST /ilink/bot/sendmessagemsg.context_token = 有效 token。

排错

现象原因解决
errcode:-14 session timeout用了短 token改用完整 botToken
FAILED: API ret=-2会话已失效 / 底层 bot id 已轮换。文本平时用空 token 可长期发送,一旦文本也 -2 大概率是 id 变了;媒体则是 cursor buf 失效【必须】先到微信给 ClawBot 发一条任意消息(激活 host 的发送游标),激活后再重跑本命令send.js 自动读最新 buf 重发。⚠️ 未激活时反复直接重跑必定再次 ret:-2,请务必先完成激活动作;不要拿 mtime 或"buf 值变没变"当判断条件;send.js 收到 -2 会清晰报错并退出(不内置轮询);仍失败再显式 --context 兜底
网络超时 / fetch 失败默认沙箱拦截出网dangerouslyDisableSandbox: true 或点允许
通道找不到settings.json 路径/字段变了按上文"凭据从哪来"手动核对路径
走错通道误用 MCP 的 weixin/wecom确认用 weixinClawBot(ClawBot),不是 MCP connector
富媒体上传失败getuploadurl 未返回 url / CDN 缺 x-encrypted-param检查 botToken 完整、网络可达 CDN
文本类文件(.txt/.md)中文乱码源文件 UTF-8 无 BOM,微信/Windows 按 GBK 解码send.js 已自动为文本类文件补 UTF-8 BOM,无需手动处理
文件不存在: ...传入的媒体路径错误检查 --image/--file/--video 指向的文件路径
缺少 botToken / 缺少 userIdsettings.json 的 weixinClawBot 配置不完整按"凭据从哪来"补全字段

Related skills

通过 WorkBuddy 已连接的 ClawBot 微信 bot 通道(weixinClawBot / ilink bot,非微信客服号)主动向老板微信推送文本/图片/文件/视频。当用户要求"通过 ClawBot 主动推送/给老板发微信/微信主动推送测试/微信独立消息/发图片给老板/发文件给老板"时使用。协议严格对齐腾讯官方插件 @tencent-weixin/openclaw-weixin@2.4.6(src/api + src/messaging + src/cdn)。

Push messages to WeChat, ClawBot, email, webhook, SMS, App and more via the pushplus HTTP API using only curl.

25 installs1 stars

企业微信群机器人工具与服务端 API 指南。Use for: (1) 用附带的零依赖 CLI 向企微群发通知——文本/markdown/图片/文件, (2) CI/监控/日报自动化推送, (3) 企业微信服务端 API(通讯录/应用消息/客户联系)开发指导。WeCom (WeChat Work) group-ro...

27 installs

Use when configuring OpenClaw chat channels to send an immediate acknowledgement before the main reply starts. Supports Feishu and DingTalk, includes bilingu...

21 installs1 stars

微信AI助手 — 主动发消息 + 自动回复 / WeChat AI assistant — send messages & auto-reply

1 installs1 stars

WeCom Bot (work.weixin.qq.com). Use this skill for ANY WeCom Bot request — reading, creating, updating, and deleting data. Whenever a task involves WeCom Bot, use this skill instead of calling the API directly.

7 installs