返回 Skill 列表
extension
分类: 其它无需 API Key

Weixin Send Media

微信发送图片/文件技能 –解决 contextToken 持久化问题

person作者: linzminhubclawhub

微信发媒体技能

本技能解决了微信渠道发送媒体消息的核心问题:contextToken 持久化。

⚠️ 前提条件:微信渠道必须已登录并处于活动状态。


🔒 安全说明(重要)

为什么需要修改核心文件?

本技能需要修改 ~/.openclaw/extensions/openclaw-weixin/src/messaging/inbound.ts,这是因为:

  1. contextToken 的生成和存储逻辑在核心扩展中

    • setContextToken()getContextToken() 是内部函数
    • 没有公开 API 可以外部调用
  2. 这是唯一可行的方案

    • 微信 API 要求发送消息时必须提供 contextToken
    • Token 只在收到用户消息时生成
    • 原始设计只缓存在内存,重启即丢失
  3. 修改是安全可控的

    • 只添加了文件读写逻辑,不改变原有功能
    • 所有数据存储在用户本地 ~/.openclaw/openclaw-weixin/context-tokens/
    • 不会外传任何敏感信息

安全最佳实践

# 1. 限制 token 文件权限
chmod 600 ~/.openclaw/openclaw-weixin/context-tokens/*.json

# 2. 定期清理过期 token
find ~/.openclaw/openclaw-weixin/context-tokens/ -mtime +30 -delete

# 3. 检查 token 文件
cat ~/.openclaw/openclaw-weixin/context-tokens/*.json

ClawHub 安全警告说明

ClawHub 可能显示 Security: SUSPICIOUS,这是因为:

  • 技能修改了 OpenClaw 核心扩展文件
  • 使用了补丁(patch)方式注入

这是误报,本技能:

  • ✅ 不窃取任何数据
  • ✅ 不连接外部服务器
  • ✅ 所有操作在本地完成
  • ✅ 代码完全开源可审计

背景问题

微信渠道的 contextToken(会话上下文令牌)原本只缓存在 gateway 进程内存中,导致:

  • ❌ CLI 命令无法发送媒体消息
  • ❌ 自动化脚本无法调用
  • ❌ 重启 gateway 后 token 丢失

本技能通过持久化 contextToken 到磁盘文件,解决了这个问题。


使用方式

方法 1:CLI 发送图片

openclaw message send \
  --channel openclaw-weixin \
  --account d72d5b576646-im-bot \
  --target o9cq802hhREiOXPlXq_Tgb0MjPTo@im.wechat \
  --media /path/to/image.png \
  --message "图片说明"

方法 2:CLI 发送文件

openclaw message send \
  --channel openclaw-weixin \
  --account d72d5b576646-im-bot \
  --target o9cq802hhREiOXPlXq_Tgb0MjPTo@im.wechat \
  --media /path/to/document.pdf \
  --message "文件说明"

方法 3:CLI 发送网络图片

openclaw message send \
  --channel openclaw-weixin \
  --account d72d5b576646-im-bot \
  --target o9cq802hhREiOXPlXq_Tgb0MjPTo@im.wechat \
  --media https://example.com/image.jpg \
  --message "网络图片"

方法 4:Node.js 脚本调用

const { execSync } = require('child_process');

function sendWeixinMedia(userId, mediaPath, caption = '') {
  const cmd = `openclaw message send \\
    --channel openclaw-weixin \\
    --account <your-account-id> \\
    --target ${userId} \\
    --media ${mediaPath} \\
    --message "${caption}"`;
  
  return execSync(cmd, { encoding: 'utf8' });
}

// 使用示例
sendWeixinMedia(
  'o9cq802hhREiOXPlXq_Tgb0MjPTo@im.wechat',
  '/path/to/qr.png',
  '这是二维码'
);

方法 5:使用封装脚本

# 发送图片
./scripts/send-image.js <user-id> <image-path> [caption]

# 示例
./scripts/send-image.js o9cq802hhREiOXPlXq_Tgb0MjPTo@im.wechat ./qr.png "扫码绑定"

核心修改

修改文件

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

修改内容

在文件开头添加:

import fs from "node:fs";
import path from "node:path";

添加持久化目录和文件路径函数:

// Persist context tokens to disk for CLI access
const CONTEXT_TOKEN_DIR = path.join(process.env.HOME!, ".openclaw/openclaw-weixin/context-tokens");
try { fs.mkdirSync(CONTEXT_TOKEN_DIR, { recursive: true }); } catch {}

function contextTokenFile(accountId: string, userId: string): string {
  const safeId = accountId.replace(/[@:]/g, "_");
  const safeUser = userId.replace(/[@:]/g, "_");
  return path.join(CONTEXT_TOKEN_DIR, `${safeId}__${safeUser}.json`);
}

修改 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);
  
  // Persist to disk
  const file = contextTokenFile(accountId, userId);
  try {
    fs.writeFileSync(file, JSON.stringify({ 
      accountId, 
      userId, 
      token, 
      savedAt: new Date().toISOString() 
    }, null, 2));
    logger.debug(`setContextToken: persisted to ${file}`);
  } catch (e) {
    logger.warn(`setContextToken: failed to persist to disk: ${e}`);
  }
}

修改 getContextToken 函数(添加磁盘回退):

export function getContextToken(accountId: string, userId: string): string | undefined {
  const k = contextTokenKey(accountId, userId);
  const val = contextTokenStore.get(k);
  logger.debug(
    `getContextToken: key=${k} found=${val !== undefined} storeSize=${contextTokenStore.size}`,
  );
  if (val) return val;
  
  // Fallback to disk
  const file = contextTokenFile(accountId, userId);
  try {
    if (fs.existsSync(file)) {
      const data = JSON.parse(fs.readFileSync(file, "utf8"));
      logger.debug(`getContextToken: loaded from disk ${file}`);
      return data.token;
    }
  } catch (e) {
    logger.warn(`getContextToken: failed to load from disk: ${e}`);
  }
  return undefined;
}

文件结构

weixin-send-media/
├── SKILL.md                    # 本文件 - 完整文档
├── README.md                   # 快速入门
├── CHANGELOG.md                # 更新日志
├── scripts/
│   ├── send-image.js           # 发图片脚本
│   ├── send-file.js            # 发文件脚本
│   └── export-context-token.js # 导出 token 工具
├── patches/
│   └── inbound.ts.patch        # 补丁文件
├── references/
│   └── api-docs.md             # API 文档
└── tests/
    └── test-token-persistence.sh # 测试脚本

安装步骤

自动安装(推荐)

npx clawhub@latest install weixin-send-media

手动安装

  1. 应用补丁
cd ~/.openclaw/workspace/skills/weixin-send-media
./install.sh
  1. 重启 gateway
openclaw gateway restart
  1. 发送一条消息触发 token 保存 (任意微信消息即可)

  2. 验证 token 文件

cat ~/.openclaw/openclaw-weixin/context-tokens/*.json

典型工作流

发送二维码给家人

用户 query 示例

  • "把绑定二维码发给我老婆"
  • "发一下配对码"

执行流程

  1. 生成二维码图片(openclaw qr
  2. 保存二维码到文件
  3. 调用 openclaw message send --media 发送
  4. 家人扫码绑定

分享截图

用户 query 示例

  • "把这个截图发给张三"
  • "分享刚才的图片"

执行流程

  1. 确认图片路径
  2. 确认目标用户
  3. 发送图片消息

定时发送日报

脚本示例

#!/bin/bash
# 每天下午 6 点发送日报
IMAGE=/path/to/daily-report.png
openclaw message send \
  --channel openclaw-weixin \
  --account <account-id> \
  --target <user-id> \
  --media $IMAGE \
  --message "📊 今日日报"

❓ 常见问题 FAQ

Q1: 安装后还是提示 "contextToken is required"?

A: 可能是因为:

  1. 还没收到过用户消息(token 还没生成)
  2. gateway 没重启(补丁没生效)

解决:

# 1. 确保微信渠道已登录
openclaw channels status

# 2. 发送一条消息给机器人
(在微信里随便发点什么)

# 3. 重启 gateway
openclaw gateway restart

# 4. 检查 token 文件
ls -la ~/.openclaw/openclaw-weixin/context-tokens/

Q2: token 文件在哪里?安全吗?

A:

  • 位置:~/.openclaw/openclaw-weixin/context-tokens/
  • 安全:建议设置权限 chmod 600

Q3: 可以发送给多人吗?

A: 可以,循环调用即可:

for user in user1 user2 user3; do
  openclaw message send --target $user --media image.png --message "分享"
done

Q4: 支持发送视频吗?

A: 支持,但文件大小不能超过 20MB(微信限制)。

Q5: 发送失败怎么排查?

A: 查看日志:

tail -f ~/.openclaw/logs/gateway.log | grep -i weixin

🔧 故障排查

问题 1: 补丁应用失败

症状:

patch: command not found

解决:

# 安装 patch 工具
sudo apt-get install patch  # Debian/Ubuntu
sudo yum install patch      # CentOS/RHEL
brew install patch          # macOS

问题 2: gateway 启动失败

症状:

Error: Cannot find module 'fs'

原因: TypeScript 编译错误

解决:

# 重新编译扩展
cd ~/.openclaw/extensions/openclaw-weixin
npm run build

# 重启 gateway
openclaw gateway restart

问题 3: token 文件为空

症状:

{}

原因: 还没收到用户消息

解决: 在微信里给机器人发条消息

问题 4: 发送消息超时

症状:

Error: Request timeout

可能原因:

  • 网络问题
  • 微信 API 限流
  • 文件太大

解决:

  1. 检查网络连接
  2. 等待几分钟后重试
  3. 压缩文件到 20MB 以内

错误处理

| 错误 | 原因 | 解决方案 | |------|------|----------| | contextToken is required | 没有收到过用户消息 | 先让用户发一条消息 | | media file not found | 图片路径不存在 | 检查文件路径 | | media too large | 文件超过 20MB | 压缩文件或换小图 | | account not configured | 微信未登录 | 运行 openclaw channels login | | patch command not found | 系统没有 patch 工具 | 安装 patch | | gateway failed to start | TypeScript 编译错误 | 重新编译扩展 |


技术细节

contextToken 是什么?

contextToken 是微信 API 要求的会话上下文令牌:

  • 每次收到用户消息时生成
  • 发送回复时必须原样回传
  • 用于关联会话,防止机器人骚扰

为什么需要持久化?

| 方案 | 优点 | 缺点 | |------|------|------| | 内存缓存(原始) | 安全:重启后失效 | 不便:CLI 无法调用 | | 磁盘持久化(本技能) | 方便:CLI/脚本都能用 | 需注意文件权限 |

文件格式

{
  "accountId": "d72d5b576646-im-bot",
  "userId": "o9cq802hhREiOXPlXq_Tgb0MjPTo@im.wechat",
  "token": "AARzJWAFAAABAAAAAAA7Q7l0VPX7W68yTLPAaSAAAAB+9905Q6UiugPBawU3n3cyzQX+LkN8ofRzsCZYN0mt7t0YJWY1u3fHryDZWu4I8cQKMXA4VECLBA3G8SLW2jKCANbBVXp8",
  "savedAt": "2026-03-23T03:28:13.073Z"
}

Token 生命周期

  1. 生成:用户发送消息时
  2. 存储:内存 + 磁盘双写
  3. 读取:先查内存,没有再查磁盘
  4. 过期:建议 30 天清理一次

相关文件

  • 原始实现:~/.openclaw/extensions/openclaw-weixin/src/messaging/inbound.ts
  • 发送逻辑:~/.openclaw/extensions/openclaw-weixin/src/messaging/send.ts
  • 媒体上传:~/.openclaw/extensions/openclaw-weixin/src/messaging/send-media.ts
  • Token 存储:~/.openclaw/openclaw-weixin/context-tokens/

更新日志

v1.1.0 (2026-03-26)

  • ✅ 添加安全说明章节,解释补丁必要性
  • ✅ 添加完整 FAQ 和故障排查指南
  • ✅ 补全 scripts 目录(send-image.js, send-file.js)
  • ✅ 添加测试脚本 test-token-persistence.sh
  • ✅ 优化文档结构和可读性
  • ✅ 修复硬编码的 account ID 和 user ID

v1.0.0 (2026-03-23)

  • ✅ 实现 contextToken 磁盘持久化
  • ✅ 支持 CLI 发送图片/文件
  • ✅ 支持网络图片 URL
  • ✅ 添加错误处理和日志

作者

🦆 鸭鸭 (Yaya) - OpenClaw 微信渠道增强

许可证

MIT-0 License (Free to use, modify, and redistribute. No attribution required.)