Appearance
OpenClaw 微信公众号接入完全指南:智能客服机器人实战
微信公众号是最大的中文流量平台之一。把 OpenClaw 接入公众号,打造 24 小时智能客服,自动回答用户问题,提升服务效率。
接入架构
┌─────────────────────────────────────────────────────────────┐
│ 微信公众号接入架构 │
└─────────────────────────────────────────────────────────────┘
用户发送消息
│
▼
┌─────────────┐
│ 微信服务器 │
└──────┬──────┘
│ POST 请求
▼
┌─────────────────────────────────────────────────────────────┐
│ 你的服务器 │
│ ┌─────────────┐ ┌─────────────┐ ┌──────────┐ │
│ │ Nginx │──────▶│ OpenClaw │──────▶│ AI 模型 │ │
│ │ 反向代理 │ │ 消息处理 │ │ API │ │
│ └─────────────┘ └─────────────┘ └──────────┘ │
└─────────────────────────────────────────────────────────────┘
│
│ 回复消息
▼
┌─────────────┐
│ 微信服务器 │
└─────────────┘
│
▼
用户收到回复准备工作
所需资源
| 资源 | 要求 | 说明 |
|---|---|---|
| 微信公众号 | 已认证服务号或订阅号 | 未认证功能受限 |
| 服务器 | 公网可访问 | 域名需备案 |
| 域名 | 已备案 | 支持 80/443 端口 |
| SSL 证书 | 可选 | 建议配置 |
公众号类型对比
| 类型 | 消息接口 | 自定义菜单 | 客服消息 | 模板消息 |
|---|---|---|---|---|
| 订阅号(未认证) | ✅ | ❌ | ❌ | ❌ |
| 订阅号(已认证) | ✅ | ✅ | ❌ | ❌ |
| 服务号(未认证) | ✅ | ✅ | ❌ | ❌ |
| 服务号(已认证) | ✅ | ✅ | ✅ | ✅ |
步骤一:配置公众号
获取公众号信息
1. 登录微信公众平台:https://mp.weixin.qq.com
2. 进入「设置与开发」→「基本配置」
3. 记录以下信息:
- AppID: wx1234567890abcdef
- AppSecret: xxxxxxxxxx
- 服务器配置:待填写配置服务器地址
1. 在「基本配置」页面
2. 点击「修改配置」
3. 填写:
- URL: https://your-domain.com/wechat
- Token: your_token_123(自定义,用于验证)
- EncodingAESKey: 随机生成
- 消息加解密方式: 明文模式(测试)/ 安全模式(生产)
4. 点击「提交」步骤二:服务器验证
验证流程
微信服务器发送 GET 请求:
GET /wechat?signature=xxx×tamp=xxx&nonce=xxx&echostr=xxx
验证逻辑:
1. 将 token、timestamp、nonce 排序
2. 拼接后进行 SHA1 加密
3. 对比 signature
4. 返回 echostrOpenClaw 配置
json
// config.json
{
"wechat": {
"appId": "wx1234567890abcdef",
"appSecret": "your_app_secret",
"token": "your_token_123",
"encodingAESKey": "your_aes_key"
}
}验证代码实现
javascript
// routes/wechat.js
const crypto = require('crypto');
// 验证服务器有效性
router.get('/wechat', (req, res) => {
const { signature, timestamp, nonce, echostr } = req.query;
const token = config.wechat.token;
// 1. 排序
const arr = [token, timestamp, nonce].sort();
// 2. 拼接
const str = arr.join('');
// 3. SHA1 加密
const sha1 = crypto.createHash('sha1').update(str).digest('hex');
// 4. 对比
if (sha1 === signature) {
res.send(echostr);
} else {
res.status(403).send('验证失败');
}
});步骤三:消息处理
消息类型
| 类型 | 说明 | 示例场景 |
|---|---|---|
| text | 文本消息 | 用户提问 |
| image | 图片消息 | 图片识别 |
| voice | 语音消息 | 语音转文字 |
| video | 视频消息 | 视频分析 |
| location | 地理位置消息 | 位置服务 |
| event | 事件消息 | 关注/取消关注 |
消息接收处理
javascript
// 处理微信消息
router.post('/wechat', async (req, res) => {
const xml = req.body;
// 解析 XML
const message = parseXML(xml);
// 消息类型判断
switch (message.MsgType) {
case 'text':
await handleTextMessage(message, res);
break;
case 'image':
await handleImageMessage(message, res);
break;
case 'event':
await handleEventMessage(message, res);
break;
default:
res.send('success');
}
});
// 处理文本消息
async function handleTextMessage(message, res) {
const { FromUserName, ToUserName, Content } = message;
// 调用 OpenClaw 处理
const reply = await openclaw.chat({
userId: FromUserName,
message: Content
});
// 返回 XML 格式回复
const xml = buildTextReply({
ToUserName: FromUserName,
FromUserName: ToUserName,
CreateTime: Date.now(),
Content: reply
});
res.set('Content-Type', 'text/xml');
res.send(xml);
}XML 解析与构建
javascript
// 解析微信 XML
function parseXML(xml) {
const result = {};
xml2js.parseString(xml, { explicitArray: false }, (err, parsed) => {
if (!err) {
result = parsed.xml;
}
});
return result;
}
// 构建文本回复 XML
function buildTextReply(data) {
return `<xml>
<ToUserName><![CDATA[${data.ToUserName}]]></ToUserName>
<FromUserName><![CDATA[${data.FromUserName}]]></FromUserName>
<CreateTime>${data.CreateTime}</CreateTime>
<MsgType><![CDATA[text]]></MsgType>
<Content><![CDATA[${data.Content}]]></Content>
</xml>`;
}步骤四:智能回复实现
OpenClaw 技能配置
javascript
// skills/wechat-reply/SKILL.md
---
name: wechat-reply
description: 微信公众号智能回复技能
permissions: [network-access]
---
# 微信公众号智能回复
根据用户消息类型,智能生成回复内容。
## 使用方法
用户发送消息 → 自动回复
## 功能特点
- 意图识别
- 多轮对话
- 上下文记忆
- 知识库查询多轮对话管理
javascript
// 会话管理
class SessionManager {
constructor() {
this.sessions = new Map();
}
getSession(userId) {
if (!this.sessions.has(userId)) {
this.sessions.set(userId, {
messages: [],
context: {},
lastActive: Date.now()
});
}
return this.sessions.get(userId);
}
addMessage(userId, message) {
const session = this.getSession(userId);
session.messages.push(message);
session.lastActive = Date.now();
// 保留最近 20 条消息
if (session.messages.length > 20) {
session.messages.shift();
}
}
clearSession(userId) {
this.sessions.delete(userId);
}
}
// 使用示例
const sessionManager = new SessionManager();
async function handleTextMessage(message, res) {
const userId = message.FromUserName;
const content = message.Content;
// 获取会话历史
const session = sessionManager.getSession(userId);
// 添加用户消息
sessionManager.addMessage(userId, {
role: 'user',
content: content
});
// 调用 AI 生成回复
const reply = await generateReply(session.messages);
// 添加助手消息
sessionManager.addMessage(userId, {
role: 'assistant',
content: reply
});
// 返回回复
res.send(buildTextReply({
ToUserName: userId,
FromUserName: message.ToUserName,
Content: reply
}));
}步骤五:自定义菜单
创建菜单
javascript
// 创建自定义菜单
async function createMenu() {
const menu = {
button: [
{
type: 'click',
name: '产品介绍',
key: 'PRODUCT_INTRO'
},
{
type: 'click',
name: '使用帮助',
key: 'HELP'
},
{
name: '更多',
sub_button: [
{
type: 'view',
name: '官网',
url: 'https://your-website.com'
},
{
type: 'click',
name: '联系客服',
key: 'CONTACT'
}
]
}
]
};
const accessToken = await getAccessToken();
const response = await axios.post(
`https://api.weixin.qq.com/cgi-bin/menu/create?access_token=${accessToken}`,
menu
);
return response.data;
}菜单事件处理
javascript
// 处理菜单点击事件
async function handleEventMessage(message, res) {
const { FromUserName, ToUserName, Event, EventKey } = message;
let reply = '';
switch (Event) {
case 'subscribe':
reply = '欢迎关注!发送任意消息开始对话。';
break;
case 'unsubscribe':
// 清理会话
sessionManager.clearSession(FromUserName);
break;
case 'CLICK':
reply = await handleMenuClick(EventKey);
break;
}
if (reply) {
res.send(buildTextReply({
ToUserName: FromUserName,
FromUserName: ToUserName,
Content: reply
}));
} else {
res.send('success');
}
}
// 处理菜单点击
async function handleMenuClick(key) {
const menuReplies = {
'PRODUCT_INTRO': '我们提供 AI 智能客服解决方案...',
'HELP': '使用方法:直接发送消息即可获得智能回复',
'CONTACT': '客服微信:xxx,工作时间:9:00-18:00'
};
return menuReplies[key] || '功能开发中';
}步骤六:客服消息
发送客服消息
javascript
// 发送客服消息(主动推送)
async function sendCustomMessage(userId, content) {
const accessToken = await getAccessToken();
const response = await axios.post(
`https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=${accessToken}`,
{
touser: userId,
msgtype: 'text',
text: {
content: content
}
}
);
return response.data;
}
// 发送图片消息
async function sendImageMessage(userId, mediaId) {
const accessToken = await getAccessToken();
const response = await axios.post(
`https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=${accessToken}`,
{
touser: userId,
msgtype: 'image',
image: {
media_id: mediaId
}
}
);
return response.data;
}获取 Access Token
javascript
// Access Token 管理
class AccessTokenManager {
constructor(appId, appSecret) {
this.appId = appId;
this.appSecret = appSecret;
this.token = null;
this.expiresAt = 0;
}
async getToken() {
// 如果 token 有效,直接返回
if (this.token && Date.now() < this.expiresAt) {
return this.token;
}
// 获取新 token
const response = await axios.get(
`https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=${this.appId}&secret=${this.appSecret}`
);
this.token = response.data.access_token;
this.expiresAt = Date.now() + (response.data.expires_in - 300) * 1000;
return this.token;
}
}完整示例
主程序入口
javascript
// app.js
const express = require('express');
const wechatRoute = require('./routes/wechat');
const app = express();
// 解析 XML
app.use(express.text({ type: 'text/xml' }));
// 微信路由
app.use('/', wechatRoute);
// 启动服务
app.listen(3000, () => {
console.log('OpenClaw 微信公众号服务已启动');
});Nginx 配置
nginx
# /etc/nginx/sites-available/wechat.conf
server {
listen 80;
server_name your-domain.com;
location /wechat {
proxy_pass http://127.0.0.1:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}常见问题
Q1: 验证失败怎么办?
检查清单:
□ Token 是否一致
□ URL 是否可访问
□ 服务器是否返回 echostr
□ 防火墙是否开放端口Q2: 消息收不到?
排查步骤:
1. 检查公众号后台消息配置
2. 查看服务器日志
3. 确认消息加解密方式
4. 测试 URL 是否正常Q3: 回复超时?
微信服务器要求 5 秒内响应:
解决方案:
1. 快速返回 success
2. 使用客服消息异步回复
3. 优化 AI 响应速度部署上线
部署清单
□ 服务器部署 OpenClaw
□ 配置域名和 SSL
□ 公众号后台配置服务器地址
□ 测试验证流程
□ 测试消息收发
□ 上线运营总结
| 功能 | 实现难度 | 说明 |
|---|---|---|
| 服务器验证 | ★★☆ | 签名校验 |
| 消息收发 | ★★☆ | XML 解析 |
| 智能回复 | ★★★ | AI 集成 |
| 自定义菜单 | ★★☆ | API 调用 |
| 客服消息 | ★★☆ | 主动推送 |
接入核心要点:
- 公众号需认证才能使用完整功能
- 服务器必须公网可访问
- 5 秒内必须响应,否则超时
- 客服消息用于异步回复
微信公众号接入 OpenClaw,让你的公众号变成 24 小时智能客服,自动回答用户问题,提升服务效率。关键是处理好消息格式和响应时间。
