微信ClawBot Cron定时任务消息,无法推送到微信的解决方案

2026-04-13 13:022阅读0评论SEO基础
  • 内容介绍
  • 文章标签
  • 相关推荐
问题描述: 微信ClawBot Cron任务收不到消息,在web页面可以看到回复,但是无法推送到微信 开发调优
有人用微信ClawBot使用cron任务成功的吗? 在web里可以看到回复内容,但是微信没有收到消息 [image] 我尝试用openclaw主动发送消息给微信,告诉我openclaw-weixin主动发消息需要contextToken,这个 token 是微信对话上下文里的,必须要我先用微信发送消息给openclaw,openclaw才能回复我。 那这不能实现cron,不是瞎扯么

有佬提到是可以的,大概原因应该是我重启了gateway

ST(@hst)佬 说: 一个context_token只能回复10条消息
试了一下,确实是…所以只能临时解决重启gateway后收不到cron消息的问题…
IMG_28651320×2868 497 KB

零、自己提问,自己解决。

在折腾 OpenClaw 的 openclaw-weixin 通道时,

  • 在控制台里主动给微信发消息
  • 用 cron 定时推送到微信
  • gateway 重启后继续给上次聊过的人发消息

报错:

sendWeixinOutbound: contextToken is required

我这边把问题摸清、修好并跑通了。

根因不是 to 写错,也不只是 accountId 没带,而是 Weixin 的主动发送依赖会话 contextToken。这个 token 原本只在内存里,重启就丢,所以需要做一层持久化。


一、问题现象

  • 刚跟机器人聊过,主动发消息有时是成功的
  • 一重启 gateway,突然又不能发了
  • cron 配置看起来没毛病,但投递依然失败

常见报错:

sendWeixinOutbound: contextToken is required

如果你也碰到这个,很大概率就是同一个问题。


二、根因到底是什么

openclaw-weixin 在发送微信消息时,不是只靠:

  • to
  • accountId

就能发出去。

它还需要一个很关键的参数:

  • contextToken

这个 contextToken 是微信在入站消息里带过来的“当前会话上下文令牌”。回复消息时,需要把它原样带回去。

原来的问题

插件原本的逻辑大致是:

  1. 收到微信消息
  2. 从入站消息里拿到 context_token
  3. 放进一个内存 Map
  4. 后续发送时,再从这个 Map 里取出来用

问题就在第 3 步:

它只存在内存里,不落盘。

所以:

  • 进程活着时,可能还能发
  • gateway 一重启,内存清空
  • 之后主动发消息、cron 推送,就拿不到 token 了

这就是为什么它会表现成:

  • “有时候能发”
  • “重启后又不行”
  • “cron 像玄学一样”

三、修复思路

思路其实不复杂:

写入时

收到微信入站消息时:

  • 继续存内存
  • 顺手存一份到磁盘

读取时

发送微信消息时:

  • 先查内存
  • 内存没有就回退查磁盘缓存

这样就能覆盖三种情况:

  1. 实时对话回复:走内存
  2. gateway 重启后继续主动发:走磁盘
  3. cron 定时推送:走磁盘或内存

四、我实际做的改动

1)新增一个持久化文件

新增:

src/storage/context-token.ts

负责把 token 存到:

~/.openclaw/openclaw-weixin/context-tokens/<accountId>.json

也就是说,每个账号一个 token 缓存文件,里面按用户 ID 存最近一次可用的 contextToken


2)修改 src/messaging/inbound.ts

contextToken 的逻辑改成:

  • setContextToken():写内存 + 落盘
  • getContextToken():先查内存,查不到再查磁盘

这样就把“只存在进程内”的临时状态,变成了“可恢复”的状态。


五、修改方式

方式一、新增一个文件,修改一个文件

新增文件:

.openclaw/extensions/openclaw-weixin/src/storage/context-token.ts

import fs from "node:fs"; import path from "node:path"; import { resolveStateDir } from "./state-dir.js"; function resolveContextTokenDir(): string { return path.join(resolveStateDir(), "openclaw-weixin", "context-tokens"); } function resolveContextTokenPath(accountId: string): string { return path.join(resolveContextTokenDir(), `${accountId}.json`); } type ContextTokenMap = Record<string, string>; function readTokenMap(filePath: string): ContextTokenMap { try { if (!fs.existsSync(filePath)) return {}; const raw = fs.readFileSync(filePath, "utf-8"); const parsed = JSON.parse(raw) as Record<string, unknown>; const out: ContextTokenMap = {}; for (const [k, v] of Object.entries(parsed)) { if (typeof k === "string" && k && typeof v === "string" && v) out[k] = v; } return out; } catch { return {}; } } export function loadPersistedContextToken(accountId: string, userId: string): string | undefined { const filePath = resolveContextTokenPath(accountId); const map = readTokenMap(filePath); return map[userId]; } export function savePersistedContextToken(accountId: string, userId: string, token: string): void { const dir = resolveContextTokenDir(); fs.mkdirSync(dir, { recursive: true }); const filePath = resolveContextTokenPath(accountId); const current = readTokenMap(filePath); current[userId] = token; fs.writeFileSync(filePath, JSON.stringify(current, null, 2), "utf-8"); try { fs.chmodSync(filePath, 0o600); } catch { // best-effort } }

修改文件:

.openclaw/extensions/openclaw-weixin/src/messaging/inbound.ts

改动一:顶部增加引用:

import { loadPersistedContextToken, savePersistedContextToken } from "../storage/context-token.js";

改动二:修改 setContextToken()

export function setContextToken(accountId: string, userId: string, token: string): void { const k = contextTokenKey(accountId, userId); logger.debug(`setContextToken: key=${k}`); contextTokenStore.set(k, token); savePersistedContextToken(accountId, userId, token); }

改动三:修改 getContextToken()

export function getContextToken(accountId: string, userId: string): string | undefined { const k = contextTokenKey(accountId, userId); const val = contextTokenStore.get(k); if (val !== undefined) { logger.debug( `getContextToken: key=${k} found=true source=memory storeSize=${contextTokenStore.size}`, ); return val; } const persisted = loadPersistedContextToken(accountId, userId); if (persisted !== undefined) { contextTokenStore.set(k, persisted); logger.debug( `getContextToken: key=${k} found=true source=disk storeSize=${contextTokenStore.size}`, ); return persisted; } logger.debug( `getContextToken: key=${k} found=false storeSize=${contextTokenStore.size}`, ); return undefined; }

方式二、patch

openclaw-weixin-contexttoken-persistence.zip (1.4 KB)

使用方式

openclaw-weixin 插件源码目录 里应用它。

比如你的插件源码如果在:

~/.openclaw/extensions/openclaw-weixin

那就进入这个目录再打 patch。

用法 1:git apply

推荐用这个:

cd ~/.openclaw/extensions/openclaw-weixin git apply ~/openclaw-weixin-contexttoken-persistence.patch

如果没有报错,说明 patch 已经成功应用。

用法 2:patch 命令

如果你不是 git 工作树,也可以试:

cd ~/.openclaw/extensions/openclaw-weixin patch -p1 < ./openclaw-weixin-contexttoken-persistence.patch

打完patch

1、重启 gateway

openclaw gateway restart

2、微信openclaw随便发一条消息

这一步很关键。

因为重启后,插件要先收到一条新的微信入站消息,才能把新的 contextToken 写进磁盘缓存。

比如你在微信里给机器人发一句:

测试消息

生成出来的缓存文件类似这样

{ "XXXXXXX88888888XXXXXXXXXXXXX@im.wechat": "AABBAAAFAAABAAAA..." }


六、后续

看哪的文档好像写说token有效期是24小时,大概就是这个。不知道有什么办法可以续时,各位佬再研究研究。。。

网友解答:
--【壹】--:

谢谢大佬分享,暂时没用微信,观望下


--【贰】--:

我的是非常奇怪,无论如何定时任务都无法发到微信插件,即使我正跟微信插件在聊天中
按楼主文改了后,能生成了缓存,还是不行,不知道哪里出问题


--【叁】--:

感谢大佬了。

问题描述: 微信ClawBot Cron任务收不到消息,在web页面可以看到回复,但是无法推送到微信 开发调优
有人用微信ClawBot使用cron任务成功的吗? 在web里可以看到回复内容,但是微信没有收到消息 [image] 我尝试用openclaw主动发送消息给微信,告诉我openclaw-weixin主动发消息需要contextToken,这个 token 是微信对话上下文里的,必须要我先用微信发送消息给openclaw,openclaw才能回复我。 那这不能实现cron,不是瞎扯么

有佬提到是可以的,大概原因应该是我重启了gateway

ST(@hst)佬 说: 一个context_token只能回复10条消息
试了一下,确实是…所以只能临时解决重启gateway后收不到cron消息的问题…
IMG_28651320×2868 497 KB

零、自己提问,自己解决。

在折腾 OpenClaw 的 openclaw-weixin 通道时,

  • 在控制台里主动给微信发消息
  • 用 cron 定时推送到微信
  • gateway 重启后继续给上次聊过的人发消息

报错:

sendWeixinOutbound: contextToken is required

我这边把问题摸清、修好并跑通了。

根因不是 to 写错,也不只是 accountId 没带,而是 Weixin 的主动发送依赖会话 contextToken。这个 token 原本只在内存里,重启就丢,所以需要做一层持久化。


一、问题现象

  • 刚跟机器人聊过,主动发消息有时是成功的
  • 一重启 gateway,突然又不能发了
  • cron 配置看起来没毛病,但投递依然失败

常见报错:

sendWeixinOutbound: contextToken is required

如果你也碰到这个,很大概率就是同一个问题。


二、根因到底是什么

openclaw-weixin 在发送微信消息时,不是只靠:

  • to
  • accountId

就能发出去。

它还需要一个很关键的参数:

  • contextToken

这个 contextToken 是微信在入站消息里带过来的“当前会话上下文令牌”。回复消息时,需要把它原样带回去。

原来的问题

插件原本的逻辑大致是:

  1. 收到微信消息
  2. 从入站消息里拿到 context_token
  3. 放进一个内存 Map
  4. 后续发送时,再从这个 Map 里取出来用

问题就在第 3 步:

它只存在内存里,不落盘。

所以:

  • 进程活着时,可能还能发
  • gateway 一重启,内存清空
  • 之后主动发消息、cron 推送,就拿不到 token 了

这就是为什么它会表现成:

  • “有时候能发”
  • “重启后又不行”
  • “cron 像玄学一样”

三、修复思路

思路其实不复杂:

写入时

收到微信入站消息时:

  • 继续存内存
  • 顺手存一份到磁盘

读取时

发送微信消息时:

  • 先查内存
  • 内存没有就回退查磁盘缓存

这样就能覆盖三种情况:

  1. 实时对话回复:走内存
  2. gateway 重启后继续主动发:走磁盘
  3. cron 定时推送:走磁盘或内存

四、我实际做的改动

1)新增一个持久化文件

新增:

src/storage/context-token.ts

负责把 token 存到:

~/.openclaw/openclaw-weixin/context-tokens/<accountId>.json

也就是说,每个账号一个 token 缓存文件,里面按用户 ID 存最近一次可用的 contextToken


2)修改 src/messaging/inbound.ts

contextToken 的逻辑改成:

  • setContextToken():写内存 + 落盘
  • getContextToken():先查内存,查不到再查磁盘

这样就把“只存在进程内”的临时状态,变成了“可恢复”的状态。


五、修改方式

方式一、新增一个文件,修改一个文件

新增文件:

.openclaw/extensions/openclaw-weixin/src/storage/context-token.ts

import fs from "node:fs"; import path from "node:path"; import { resolveStateDir } from "./state-dir.js"; function resolveContextTokenDir(): string { return path.join(resolveStateDir(), "openclaw-weixin", "context-tokens"); } function resolveContextTokenPath(accountId: string): string { return path.join(resolveContextTokenDir(), `${accountId}.json`); } type ContextTokenMap = Record<string, string>; function readTokenMap(filePath: string): ContextTokenMap { try { if (!fs.existsSync(filePath)) return {}; const raw = fs.readFileSync(filePath, "utf-8"); const parsed = JSON.parse(raw) as Record<string, unknown>; const out: ContextTokenMap = {}; for (const [k, v] of Object.entries(parsed)) { if (typeof k === "string" && k && typeof v === "string" && v) out[k] = v; } return out; } catch { return {}; } } export function loadPersistedContextToken(accountId: string, userId: string): string | undefined { const filePath = resolveContextTokenPath(accountId); const map = readTokenMap(filePath); return map[userId]; } export function savePersistedContextToken(accountId: string, userId: string, token: string): void { const dir = resolveContextTokenDir(); fs.mkdirSync(dir, { recursive: true }); const filePath = resolveContextTokenPath(accountId); const current = readTokenMap(filePath); current[userId] = token; fs.writeFileSync(filePath, JSON.stringify(current, null, 2), "utf-8"); try { fs.chmodSync(filePath, 0o600); } catch { // best-effort } }

修改文件:

.openclaw/extensions/openclaw-weixin/src/messaging/inbound.ts

改动一:顶部增加引用:

import { loadPersistedContextToken, savePersistedContextToken } from "../storage/context-token.js";

改动二:修改 setContextToken()

export function setContextToken(accountId: string, userId: string, token: string): void { const k = contextTokenKey(accountId, userId); logger.debug(`setContextToken: key=${k}`); contextTokenStore.set(k, token); savePersistedContextToken(accountId, userId, token); }

改动三:修改 getContextToken()

export function getContextToken(accountId: string, userId: string): string | undefined { const k = contextTokenKey(accountId, userId); const val = contextTokenStore.get(k); if (val !== undefined) { logger.debug( `getContextToken: key=${k} found=true source=memory storeSize=${contextTokenStore.size}`, ); return val; } const persisted = loadPersistedContextToken(accountId, userId); if (persisted !== undefined) { contextTokenStore.set(k, persisted); logger.debug( `getContextToken: key=${k} found=true source=disk storeSize=${contextTokenStore.size}`, ); return persisted; } logger.debug( `getContextToken: key=${k} found=false storeSize=${contextTokenStore.size}`, ); return undefined; }

方式二、patch

openclaw-weixin-contexttoken-persistence.zip (1.4 KB)

使用方式

openclaw-weixin 插件源码目录 里应用它。

比如你的插件源码如果在:

~/.openclaw/extensions/openclaw-weixin

那就进入这个目录再打 patch。

用法 1:git apply

推荐用这个:

cd ~/.openclaw/extensions/openclaw-weixin git apply ~/openclaw-weixin-contexttoken-persistence.patch

如果没有报错,说明 patch 已经成功应用。

用法 2:patch 命令

如果你不是 git 工作树,也可以试:

cd ~/.openclaw/extensions/openclaw-weixin patch -p1 < ./openclaw-weixin-contexttoken-persistence.patch

打完patch

1、重启 gateway

openclaw gateway restart

2、微信openclaw随便发一条消息

这一步很关键。

因为重启后,插件要先收到一条新的微信入站消息,才能把新的 contextToken 写进磁盘缓存。

比如你在微信里给机器人发一句:

测试消息

生成出来的缓存文件类似这样

{ "XXXXXXX88888888XXXXXXXXXXXXX@im.wechat": "AABBAAAFAAABAAAA..." }


六、后续

看哪的文档好像写说token有效期是24小时,大概就是这个。不知道有什么办法可以续时,各位佬再研究研究。。。

网友解答:
--【壹】--:

谢谢大佬分享,暂时没用微信,观望下


--【贰】--:

我的是非常奇怪,无论如何定时任务都无法发到微信插件,即使我正跟微信插件在聊天中
按楼主文改了后,能生成了缓存,还是不行,不知道哪里出问题


--【叁】--:

感谢大佬了。