编程

weixinclaw-proactive-push

试用

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

它能做什么

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

技能文档

📦 本 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 配置不完整按"凭据从哪来"补全字段

相关技能

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

通过 pushplus HTTP API,用 curl 即可向微信、ClawBot、邮箱、webhook、短信、App 等渠道推送消息。

25 次安装1 星标

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

21 次安装1 星标

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

27 次安装

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

1 次安装1 星标

Create and wire a new OpenClaw agent with a fixed workflow. Use when the user asks to create/add a new OpenClaw agent or says “我要创建一个新的 Agent”, automate mult...

31 次安装