/** * Wechaty -> 私域 Agent API 适配器(示例) * * 安装:npm i wechaty wechaty-puppet-wechat4u * 启动:YAWAN_API_URL=https://agent.example.com \ * YAWAN_WECHAT_BOT_SECRET=... YAWAN_WECHAT_AGENT_ID=3 node scripts/wechat_bot_adapter.mjs * * 个人微信协议有封号风险。生产环境建议把本适配层替换为腾讯官方 * openclaw-weixin/iLink Bot,只保留下面 callAgent() 的 HTTP 调用。 */ import crypto from 'node:crypto' import { WechatyBuilder, Message } from 'wechaty' const apiUrl = (process.env.YAWAN_API_URL || 'http://127.0.0.1:8010').replace(/\/$/, '') const secret = process.env.YAWAN_WECHAT_BOT_SECRET || '' const agentId = Number(process.env.YAWAN_WECHAT_AGENT_ID || 0) if (!secret || !agentId) throw new Error('请设置 YAWAN_WECHAT_BOT_SECRET 和 YAWAN_WECHAT_AGENT_ID') async function callAgent (conversationId, messageId, text) { const timestamp = String(Math.floor(Date.now() / 1000)) const payload = `${timestamp}.${conversationId}.${messageId}.${text}` const signature = crypto.createHmac('sha256', secret).update(payload).digest('hex') const response = await fetch(`${apiUrl}/api/wechat/bot/reply`, { method: 'POST', headers: { 'content-type': 'application/json', 'x-wechat-bot-timestamp': timestamp, 'x-wechat-bot-signature': signature }, body: JSON.stringify({ agent_id: agentId, conversation_id: conversationId, message_id: messageId, text }) }) const data = await response.json().catch(() => ({})) if (!response.ok) throw new Error(data.detail || `API ${response.status}`) return data.reply || '' } const bot = WechatyBuilder.build({ name: process.env.YAWAN_WECHAT_SESSION || 'yawan-agent', puppet: 'wechaty-puppet-wechat4u', puppetOptions: { uos: true } }) bot.on('scan', (qrcode, status) => console.log('请扫码登录微信:', status, qrcode)) bot.on('login', user => console.log('微信 Bot 已登录:', user.name())) bot.on('logout', user => console.log('微信 Bot 已退出:', user.name())) bot.on('message', async message => { if (message.self() || message.type() !== Message.Type.Text) return const text = message.text().trim() if (!text) return const room = await Promise.resolve(message.room()) const from = await Promise.resolve(message.from()) const conversationId = room?.id || from?.id || 'unknown' const messageId = typeof message.id === 'function' ? message.id() : (message.id || crypto.randomUUID()) try { const reply = await callAgent(conversationId, messageId, text) if (reply) await message.say(reply) } catch (error) { console.error('Agent 对话失败:', error.message) await message.say('系统暂时繁忙,请稍后再试。') } }) await bot.start()