
Wechat Channel
- 509 installs
- 82 repo stars
- Updated August 2, 2026
- aaaaqwq/claude-code-skills
wechat-channel is an OpenClaw integration skill that creates a bidirectional WeChat messaging bridge via Wechaty and PadLocal for private chats, group chats, mentions, and file transfers.
About
wechat-channel is an integration skill in aaaaqwq/claude-code-skills that connects WeChat users to an OpenClaw AI agent gateway through a Wechaty bridge using the PadLocal protocol. The architecture routes private chats, group chats, @mention detection, and image or file transfers between WeChat clients and OpenClaw so users can chat with an AI assistant directly inside WeChat. Developers reach for wechat-channel when WeChat messages must trigger AI responses or when OpenClaw needs to send replies back into WeChat conversations. The skill requires Bash, Read, Write, and Edit tool access to configure and run the bridge components.
- Bidirectional WeChat ↔ OpenClaw message channel
- Supports private chat, group chat, @mentions, images and file transfer
- Wechaty + PadLocal bridge with automatic message format conversion
- HTTP webhook receiver and outbound send API for the agent
- Runs as independent Node.js service that forwards WeChat events to your AI agent
Wechat Channel by the numbers
- 509 all-time installs (skills.sh)
- Ranked #1,742 of 16,556 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 3, 2026 (Skillselion catalog sync)
npx skills add https://github.com/aaaaqwq/claude-code-skills --skill wechat-channelAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 509 |
|---|---|
| repo stars | ★ 82 |
| Last updated | August 2, 2026 |
| Repository | aaaaqwq/claude-code-skills ↗ |
How do you connect WeChat messaging to an OpenClaw AI agent?
Create a bidirectional messaging bridge between WeChat and their AI agent so users can chat with the agent directly inside WeChat.
Who is it for?
Developers building OpenClaw agent deployments that must receive and send WeChat messages in private and group conversations.
Skip if: Projects without WeChat user requirements or teams unwilling to run Wechaty with PadLocal protocol dependencies.
When should I use this skill?
WeChat messages need to trigger OpenClaw AI responses or OpenClaw must send messages back into WeChat chats.
What you get
A bidirectional WeChat-to-OpenClaw messaging channel supporting text, mentions, images, and files.
- WeChat messaging bridge
- bidirectional agent channel
Files
微信 Channel 集成
- Author: Daniel Li
- Copyright © Daniel Li. All rights reserved.
将微信接入 OpenClaw,实现双向消息通道。
架构概述
┌─────────────┐ ┌──────────────────┐ ┌─────────────┐
│ 微信用户 │ ←→ │ Wechaty Bridge │ ←→ │ OpenClaw │
│ (私聊/群聊) │ │ (PadLocal协议) │ │ Gateway │
└─────────────┘ └──────────────────┘ └─────────────┘
↓
┌──────────────────┐
│ 消息格式转换 │
│ - 文本/图片/文件 │
│ - @提及检测 │
│ - 群聊/私聊路由 │
└──────────────────┘核心组件
1. Wechaty Bridge (消息桥接服务)
独立运行的 Node.js 服务,负责:
- 微信登录(扫码)
- 消息收发
- 联系人/群组管理
- 与 OpenClaw Gateway 通信
2. OpenClaw Webhook 接收器
接收来自 Wechaty Bridge 的消息,转发给 AI Agent。
3. 消息发送 API
OpenClaw Agent 通过 HTTP API 发送消息到微信。
快速开始
前置条件
- Node.js >= 18
- PadLocal Token(付费服务,约 ¥200/月)
- OpenClaw Gateway 运行中
1. 安装依赖
cd /home/aa/clawd/skills/wechat-channel
npm init -y
npm install wechaty wechaty-puppet-padlocal axios dotenv2. 配置环境变量
cp .env.example .env
# 编辑 .env 填入配置3. 启动服务
node scripts/wechat-bridge.js
# 扫描终端显示的二维码登录配置说明
环境变量 (.env)
# PadLocal Token (必需)
# 获取方式: https://pad-local.com
PADLOCAL_TOKEN=YOUR_PADLOCAL_TOKEN
# OpenClaw Gateway 配置
OPENCLAW_GATEWAY_URL=http://127.0.0.1:18789
OPENCLAW_WEBHOOK_SECRET=your_webhook_secret
# 微信 Bot 配置
WECHAT_BOT_NAME=OpenClaw助手
# 安全配置
# 允许的用户微信ID (逗号分隔,留空允许所有)
ALLOWED_USERS=wxid_xxx,wxid_yyy
# 允许的群聊ID (逗号分隔,留空允许所有)
ALLOWED_GROUPS=xxx@chatroom,yyy@chatroom
# 群聊行为
# 是否需要@才响应群消息
REQUIRE_MENTION_IN_GROUP=true
# 日志级别
LOG_LEVEL=infoOpenClaw 配置 (openclaw.json)
{
"channels": {
"wechat": {
"enabled": true,
"webhookUrl": "http://localhost:3001/webhook",
"webhookSecret": "your_webhook_secret",
"dmPolicy": "allowlist",
"allowFrom": ["wxid_xxx", "wxid_yyy"],
"groups": {
"xxx@chatroom": {
"name": "工作群",
"requireMention": true
}
}
}
}
}消息格式
接收消息 (Webhook Payload)
{
"type": "message",
"channel": "wechat",
"messageId": "msg_123456",
"from": {
"id": "wxid_sender",
"name": "张三",
"alias": "zhangsan"
},
"chat": {
"id": "wxid_sender",
"type": "private"
},
"text": "你好,帮我查一下天气",
"timestamp": 1706745600000,
"mentions": [],
"replyTo": null
}群聊消息
{
"type": "message",
"channel": "wechat",
"messageId": "msg_789012",
"from": {
"id": "wxid_sender",
"name": "张三"
},
"chat": {
"id": "xxx@chatroom",
"type": "group",
"name": "工作群"
},
"text": "@OpenClaw助手 帮我总结一下今天的会议",
"mentions": ["bot_wxid"],
"isMentioned": true
}发送消息 (API)
# 发送文本
curl -X POST http://localhost:3001/api/send \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_SECRET" \
-d '{
"to": "wxid_receiver",
"type": "text",
"content": "收到,正在处理..."
}'
# 发送图片
curl -X POST http://localhost:3001/api/send \
-H "Content-Type: application/json" \
-d '{
"to": "wxid_receiver",
"type": "image",
"url": "https://example.com/image.png"
}'
# 发送文件
curl -X POST http://localhost:3001/api/send \
-d '{
"to": "wxid_receiver",
"type": "file",
"path": "/path/to/file.pdf",
"filename": "report.pdf"
}'安全策略
私聊策略 (dmPolicy)
| 策略 | 说明 |
|---|---|
open | 允许所有人私聊(危险) |
allowlist | 仅允许 allowFrom 列表中的用户 |
pairing | 需要配对审批 |
群聊策略
| 配置 | 说明 |
|---|---|
requireMention: true | 必须@机器人才响应 |
allowFrom | 群内允许触发的用户列表 |
使用场景
1. 个人助手
用户: 帮我查一下明天北京的天气
Bot: 明天北京天气:晴,温度 -5°C ~ 5°C,建议穿羽绒服。2. 群聊助手
用户: @OpenClaw助手 总结一下刚才的讨论
Bot: 刚才讨论的要点:
1. 项目进度需要加快
2. 下周三前完成设计稿
3. 周五进行代码评审3. 自动化通知
// 从 OpenClaw Agent 发送通知
await sendWechatMessage({
to: 'xxx@chatroom',
text: '⚠️ 服务器 CPU 使用率超过 90%,请检查!'
});故障排查
登录问题
问题: 扫码后无法登录 解决: 1. 检查 PadLocal Token 是否有效 2. 确认微信账号未被限制 3. 尝试重新获取 Token
消息收发问题
问题: 消息发送失败 解决: 1. 检查网络连接 2. 确认目标用户/群组 ID 正确 3. 查看日志中的错误信息
连接断开
问题: 服务运行一段时间后断开 解决: 1. 使用 PM2 管理进程,自动重启 2. 检查 PadLocal 服务状态 3. 实现心跳检测和重连机制
限制说明
PadLocal 限制
- 需要付费 Token(约 ¥200/月)
- 单 Token 只能登录一个微信号
- 可能受微信风控影响
微信平台限制
- 发送频率限制(建议间隔 1-2 秒)
- 群聊人数限制
- 文件大小限制(约 100MB)
- 不支持小程序消息
功能限制
- 不支持语音消息转文字(需额外集成)
- 不支持视频号内容
- 红包、转账等敏感功能不可用
相关文件
scripts/wechat-bridge.js- 主服务代码scripts/message-handler.js- 消息处理逻辑.env.example- 环境变量模板references/wechaty-api.md- Wechaty API 参考
TODO
- [ ] 获取 PadLocal Token
- [ ] 配置 OpenClaw Webhook 接收
- [ ] 测试私聊消息收发
- [ ] 测试群聊 @提及
- [ ] 配置安全策略
- [ ] 部署为系统服务
- [ ] 实现断线重连
- [ ] 添加消息队列(高并发场景)
{
"name": "openclaw-wechat-bridge",
"version": "1.0.0",
"description": "WeChat Bridge for OpenClaw - 基于 Wechaty + PadLocal",
"main": "scripts/wechat-bridge.js",
"scripts": {
"start": "node scripts/wechat-bridge.js",
"dev": "LOG_LEVEL=verbose node scripts/wechat-bridge.js"
},
"dependencies": {
"wechaty": "^1.20.2",
"wechaty-puppet-padlocal": "^1.17.0",
"axios": "^1.6.0",
"express": "^4.18.2",
"dotenv": "^16.3.1",
"qrcode-terminal": "^0.12.0",
"file-box": "^1.4.15"
},
"engines": {
"node": ">=18.0.0"
},
"keywords": [
"wechat",
"openclaw",
"wechaty",
"padlocal",
"chatbot"
],
"license": "MIT"
}
Wechaty API 参考
核心概念
Wechaty
Wechaty 是一个开源的对话式 AI SDK,支持多种 IM 平台。
Puppet
Puppet 是 Wechaty 的底层协议实现。PadLocal 是目前最稳定的微信协议实现。
常用 API
Bot 实例
const { WechatyBuilder } = require('wechaty');
const bot = WechatyBuilder.build({
name: 'my-bot',
puppet: new PuppetPadlocal({ token: 'xxx' }),
});
// 启动
await bot.start();
// 停止
await bot.stop();
// 登出
await bot.logout();事件
// 扫码
bot.on('scan', (qrcode, status) => {});
// 登录
bot.on('login', (user) => {});
// 登出
bot.on('logout', (user) => {});
// 消息
bot.on('message', (message) => {});
// 好友请求
bot.on('friendship', (friendship) => {});
// 群邀请
bot.on('room-invite', (roomInvitation) => {});
// 群成员变动
bot.on('room-join', (room, inviteeList, inviter) => {});
bot.on('room-leave', (room, leaverList, remover) => {});
bot.on('room-topic', (room, newTopic, oldTopic, changer) => {});
// 错误
bot.on('error', (error) => {});Message 消息
// 消息类型
message.type(); // Text, Image, Video, Audio, File, Emoticon, etc.
// 发送者
message.talker();
// 所在群聊 (私聊为 null)
message.room();
// 消息文本
message.text();
// 是否自己发的
message.self();
// 消息时间
message.date();
// @提及列表
await message.mentionList();
// 是否@了自己
await message.mentionSelf();
// 转发消息
await message.forward(contact);
// 回复消息
await message.say('回复内容');Contact 联系人
// 查找联系人
const contact = await bot.Contact.find({ id: 'wxid_xxx' });
const contact = await bot.Contact.find({ name: '张三' });
// 所有联系人
const contacts = await bot.Contact.findAll();
// 联系人属性
contact.id;
contact.name();
contact.alias();
contact.type(); // Unknown, Individual, Official
contact.gender(); // Unknown, Male, Female
contact.province();
contact.city();
contact.avatar();
// 发送消息
await contact.say('Hello');
await contact.say(fileBox); // 发送文件/图片Room 群聊
// 查找群聊
const room = await bot.Room.find({ id: 'xxx@chatroom' });
const room = await bot.Room.find({ topic: '工作群' });
// 所有群聊
const rooms = await bot.Room.findAll();
// 群聊属性
room.id;
await room.topic(); // 群名
await room.owner(); // 群主
await room.memberAll(); // 所有成员
await room.member({ name: '张三' }); // 查找成员
// 发送消息
await room.say('Hello');
await room.say('Hello', contact); // @某人
// 群管理
await room.add(contact); // 添加成员
await room.del(contact); // 移除成员
await room.topic('新群名'); // 修改群名
await room.quit(); // 退出群聊FileBox 文件
const { FileBox } = require('file-box');
// 从 URL 创建
const fileBox = FileBox.fromUrl('https://example.com/image.png');
// 从本地文件创建
const fileBox = FileBox.fromFile('/path/to/file.pdf');
// 从 Base64 创建
const fileBox = FileBox.fromBase64(base64String, 'image.png');
// 从 Buffer 创建
const fileBox = FileBox.fromBuffer(buffer, 'file.pdf');
// 发送
await contact.say(fileBox);
await room.say(fileBox);Friendship 好友请求
bot.on('friendship', async (friendship) => {
const type = friendship.type();
if (type === bot.Friendship.Type.Receive) {
// 收到好友请求
await friendship.accept();
}
if (type === bot.Friendship.Type.Confirm) {
// 好友请求已确认
}
});
// 主动添加好友
await bot.Friendship.add(contact, 'Hello, I am OpenClaw');消息类型枚举
const MessageType = {
Unknown: 0,
Attachment: 1,
Audio: 2,
Contact: 3,
ChatHistory: 4,
Emoticon: 5,
Image: 6,
Text: 7,
Location: 8,
MiniProgram: 9,
GroupNote: 10,
Transfer: 11,
RedEnvelope: 12,
Recalled: 13,
Url: 14,
Video: 15,
};最佳实践
消息发送间隔
// 避免发送过快被限制
async function sendWithDelay(target, messages, delayMs = 1000) {
for (const msg of messages) {
await target.say(msg);
await new Promise(r => setTimeout(r, delayMs));
}
}错误重试
async function sendWithRetry(target, content, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
await target.say(content);
return true;
} catch (error) {
console.error(`发送失败 (${i + 1}/${maxRetries}):`, error.message);
await new Promise(r => setTimeout(r, 2000 * (i + 1)));
}
}
return false;
}断线重连
bot.on('error', async (error) => {
console.error('Bot error:', error);
// 等待后重启
await new Promise(r => setTimeout(r, 5000));
try {
await bot.stop();
await bot.start();
} catch (e) {
console.error('重启失败:', e);
}
});参考链接
- Wechaty 官网: https://wechaty.js.org/
- Wechaty GitHub: https://github.com/wechaty/wechaty
- PadLocal: https://pad-local.com/
- Wechaty Puppet 列表: https://wechaty.js.org/docs/puppet-providers/
#!/usr/bin/env node
/**
* WeChat Bridge for OpenClaw
*
* 基于 Wechaty + PadLocal 的微信消息桥接服务
* 将微信消息转发到 OpenClaw Gateway,并接收 OpenClaw 的回复发送到微信
*/
require('dotenv').config();
const { WechatyBuilder, ScanStatus, log } = require('wechaty');
const { PuppetPadlocal } = require('wechaty-puppet-padlocal');
const axios = require('axios');
const express = require('express');
const qrcode = require('qrcode-terminal');
// ============ 配置 ============
const config = {
// PadLocal Token
padlocalToken: process.env.PADLOCAL_TOKEN || 'YOUR_PADLOCAL_TOKEN',
// OpenClaw Gateway
openclawGatewayUrl: process.env.OPENCLAW_GATEWAY_URL || 'http://127.0.0.1:18789',
openclawWebhookSecret: process.env.OPENCLAW_WEBHOOK_SECRET || '',
// Bot 配置
botName: process.env.WECHAT_BOT_NAME || 'OpenClaw助手',
// 安全配置
allowedUsers: process.env.ALLOWED_USERS?.split(',').filter(Boolean) || [],
allowedGroups: process.env.ALLOWED_GROUPS?.split(',').filter(Boolean) || [],
requireMentionInGroup: process.env.REQUIRE_MENTION_IN_GROUP !== 'false',
// 服务端口
apiPort: parseInt(process.env.API_PORT || '3001'),
// 日志级别
logLevel: process.env.LOG_LEVEL || 'info',
};
// ============ 日志 ============
log.level(config.logLevel);
// ============ Wechaty 实例 ============
const puppet = new PuppetPadlocal({
token: config.padlocalToken,
});
const bot = WechatyBuilder.build({
name: 'openclaw-wechat-bridge',
puppet,
});
// Bot 自身信息
let botInfo = null;
// ============ 事件处理 ============
// 扫码登录
bot.on('scan', (qrcodeUrl, status) => {
if (status === ScanStatus.Waiting || status === ScanStatus.Timeout) {
console.log('\n========================================');
console.log('请使用微信扫描下方二维码登录:');
console.log('========================================\n');
qrcode.generate(qrcodeUrl, { small: true });
console.log(`\n或访问: ${qrcodeUrl}\n`);
}
});
// 登录成功
bot.on('login', async (user) => {
botInfo = user;
console.log('\n========================================');
console.log(`✅ 登录成功: ${user.name()}`);
console.log(` 微信ID: ${user.id}`);
console.log('========================================\n');
});
// 登出
bot.on('logout', (user) => {
console.log(`\n❌ 已登出: ${user.name()}\n`);
botInfo = null;
});
// 收到消息
bot.on('message', async (message) => {
try {
await handleMessage(message);
} catch (error) {
console.error('处理消息失败:', error);
}
});
// 错误处理
bot.on('error', (error) => {
console.error('Bot 错误:', error);
});
// ============ 消息处理 ============
async function handleMessage(message) {
// 忽略自己发的消息
if (message.self()) return;
// 只处理文本消息(可扩展支持其他类型)
const msgType = message.type();
if (msgType !== bot.Message.Type.Text) {
log.info('忽略非文本消息:', msgType);
return;
}
const talker = message.talker();
const room = message.room();
const text = message.text();
// 安全检查
if (!await checkPermission(talker, room)) {
log.info('权限检查未通过,忽略消息');
return;
}
// 群聊 @提及检查
if (room && config.requireMentionInGroup) {
const mentionSelf = await message.mentionSelf();
if (!mentionSelf) {
log.verbose('群消息未@机器人,忽略');
return;
}
}
// 构建消息 payload
const payload = await buildMessagePayload(message, talker, room, text);
// 发送到 OpenClaw
await forwardToOpenClaw(payload);
}
async function checkPermission(talker, room) {
const talkerId = talker.id;
// 私聊权限检查
if (!room) {
if (config.allowedUsers.length === 0) return true;
return config.allowedUsers.includes(talkerId);
}
// 群聊权限检查
const roomId = room.id;
if (config.allowedGroups.length === 0) return true;
return config.allowedGroups.includes(roomId);
}
async function buildMessagePayload(message, talker, room, text) {
const mentions = await message.mentionList();
const mentionIds = mentions.map(m => m.id);
// 移除 @xxx 文本,获取纯净消息
let cleanText = text;
for (const mention of mentions) {
const mentionName = mention.name();
cleanText = cleanText.replace(new RegExp(`@${mentionName}\\s*`, 'g'), '');
}
cleanText = cleanText.trim();
const payload = {
type: 'message',
channel: 'wechat',
messageId: message.id,
timestamp: message.date().getTime(),
from: {
id: talker.id,
name: talker.name(),
alias: await talker.alias() || null,
},
text: cleanText,
rawText: text,
mentions: mentionIds,
isMentioned: botInfo ? mentionIds.includes(botInfo.id) : false,
};
if (room) {
payload.chat = {
id: room.id,
type: 'group',
name: await room.topic(),
};
} else {
payload.chat = {
id: talker.id,
type: 'private',
};
}
return payload;
}
async function forwardToOpenClaw(payload) {
try {
const headers = {
'Content-Type': 'application/json',
};
if (config.openclawWebhookSecret) {
headers['Authorization'] = `Bearer ${config.openclawWebhookSecret}`;
}
const response = await axios.post(
`${config.openclawGatewayUrl}/webhook/wechat`,
payload,
{ headers, timeout: 30000 }
);
log.info('消息已转发到 OpenClaw:', response.status);
} catch (error) {
console.error('转发消息到 OpenClaw 失败:', error.message);
}
}
// ============ API 服务 ============
const app = express();
app.use(express.json());
// 健康检查
app.get('/health', (req, res) => {
res.json({
status: 'ok',
loggedIn: !!botInfo,
botName: botInfo?.name() || null,
});
});
// 发送消息 API
app.post('/api/send', async (req, res) => {
try {
// 验证 secret
const authHeader = req.headers.authorization;
if (config.openclawWebhookSecret) {
if (authHeader !== `Bearer ${config.openclawWebhookSecret}`) {
return res.status(401).json({ error: 'Unauthorized' });
}
}
const { to, type, content, url, path, filename } = req.body;
if (!to) {
return res.status(400).json({ error: 'Missing "to" field' });
}
if (!botInfo) {
return res.status(503).json({ error: 'Bot not logged in' });
}
let target;
// 判断是群聊还是私聊
if (to.endsWith('@chatroom')) {
target = await bot.Room.find({ id: to });
} else {
target = await bot.Contact.find({ id: to });
}
if (!target) {
return res.status(404).json({ error: 'Target not found' });
}
// 根据类型发送消息
switch (type || 'text') {
case 'text':
await target.say(content);
break;
case 'image':
if (url) {
const { FileBox } = require('file-box');
const fileBox = FileBox.fromUrl(url);
await target.say(fileBox);
} else if (path) {
const { FileBox } = require('file-box');
const fileBox = FileBox.fromFile(path);
await target.say(fileBox);
}
break;
case 'file':
if (path) {
const { FileBox } = require('file-box');
const fileBox = FileBox.fromFile(path, filename);
await target.say(fileBox);
}
break;
default:
return res.status(400).json({ error: `Unknown message type: ${type}` });
}
res.json({ success: true, message: 'Message sent' });
} catch (error) {
console.error('发送消息失败:', error);
res.status(500).json({ error: error.message });
}
});
// 获取联系人列表
app.get('/api/contacts', async (req, res) => {
try {
if (!botInfo) {
return res.status(503).json({ error: 'Bot not logged in' });
}
const contacts = await bot.Contact.findAll();
const result = contacts.map(c => ({
id: c.id,
name: c.name(),
type: c.type(),
}));
res.json(result);
} catch (error) {
res.status(500).json({ error: error.message });
}
});
// 获取群聊列表
app.get('/api/rooms', async (req, res) => {
try {
if (!botInfo) {
return res.status(503).json({ error: 'Bot not logged in' });
}
const rooms = await bot.Room.findAll();
const result = await Promise.all(rooms.map(async r => ({
id: r.id,
topic: await r.topic(),
})));
res.json(result);
} catch (error) {
res.status(500).json({ error: error.message });
}
});
// ============ 启动 ============
async function main() {
console.log('\n========================================');
console.log('🤖 WeChat Bridge for OpenClaw');
console.log('========================================\n');
// 检查配置
if (config.padlocalToken === 'YOUR_PADLOCAL_TOKEN') {
console.error('❌ 请配置 PADLOCAL_TOKEN 环境变量');
console.error(' 获取 Token: https://pad-local.com');
process.exit(1);
}
// 启动 API 服务
app.listen(config.apiPort, () => {
console.log(`📡 API 服务已启动: http://localhost:${config.apiPort}`);
console.log(` - 健康检查: GET /health`);
console.log(` - 发送消息: POST /api/send`);
console.log(` - 联系人列表: GET /api/contacts`);
console.log(` - 群聊列表: GET /api/rooms`);
});
// 启动 Wechaty
console.log('\n正在启动微信 Bot...\n');
await bot.start();
}
// 优雅退出
process.on('SIGINT', async () => {
console.log('\n正在关闭...');
await bot.stop();
process.exit(0);
});
process.on('SIGTERM', async () => {
await bot.stop();
process.exit(0);
});
main().catch(console.error);
WeChat Channel TODO
必需配置项
1. PadLocal Token
- 获取方式: https://pad-local.com
- 费用: 约 ¥200/月
- 说明: 单 Token 只能登录一个微信号
2. OpenClaw Webhook 配置
- 需要在 OpenClaw Gateway 中配置 Webhook 接收端点
- 当前 OpenClaw 可能不原生支持自定义 channel,需要:
- 方案 A: 开发 OpenClaw 插件 (参考 telegram/whatsapp 插件)
- 方案 B: 使用 Webhook 转发到现有 channel
待解决问题
架构问题
1. OpenClaw Channel 插件开发
- 当前 OpenClaw 的 channel 插件是 TypeScript 编写
- 需要研究如何创建自定义 channel 插件
- 或者使用 Webhook 方式桥接
2. 消息路由
- 如何将微信消息正确路由到 OpenClaw Agent
- 如何将 Agent 回复发送回微信
3. 会话管理
- 如何维护微信用户与 OpenClaw 会话的对应关系
- 多轮对话上下文保持
技术问题
1. 登录状态持久化
- PadLocal 登录状态如何持久化
- 服务重启后是否需要重新扫码
2. 消息可靠性
- 消息发送失败的重试机制
- 消息队列(高并发场景)
3. 媒体消息处理
- 图片/文件的上传下载
- 语音消息转文字(需要额外服务)
安全问题
1. 权限控制
- 私聊白名单
- 群聊白名单
- 群内用户白名单
2. 敏感信息
- Token 安全存储
- 日志脱敏
后续优化方向
功能增强
- [ ] 支持语音消息(转文字)
- [ ] 支持图片识别
- [ ] 支持文件处理
- [ ] 支持表情包
- [ ] 支持小程序卡片解析
稳定性
- [ ] 断线自动重连
- [ ] 心跳检测
- [ ] 消息队列
- [ ] 日志监控告警
部署
- [ ] Docker 容器化
- [ ] PM2 进程管理
- [ ] 系统服务配置
- [ ] 健康检查端点
替代方案
如果 PadLocal 不可用,可考虑:
1. itchat (Python)
- 免费,但不稳定
- 容易被封号
2. WxPusher
- 仅支持发送,不支持接收
- 适合单向通知场景
3. 企业微信
- 官方 API,稳定
- 需要企业认证
- 功能受限
4. 微信公众号
- 官方 API
- 需要认证
- 只能被动回复
参考资源
- Wechaty 文档: https://wechaty.js.org/docs/
- PadLocal 官网: https://pad-local.com/
- OpenClaw 插件开发: 参考
~/.npm-global/lib/node_modules/openclaw/extensions/
Related skills
How it compares
Pick wechat-channel when the agent channel must be WeChat specifically rather than Slack, Discord, or generic webhook integrations.
FAQ
What messaging modes does wechat-channel support?
wechat-channel supports WeChat private chats, group chats, @mention detection, and image or file transfers. Messages route through a Wechaty bridge using the PadLocal protocol to an OpenClaw gateway.
What stack does wechat-channel use for WeChat integration?
wechat-channel builds on Wechaty with the PadLocal protocol to connect WeChat users to OpenClaw. The bridge enables bidirectional message flow so inbound WeChat traffic triggers AI responses and OpenClaw can reply back.