Files
yawan-workbench/scripts/wechat_bot_adapter.mjs
T

60 lines
2.7 KiB
JavaScript
Raw Normal View History

/**
* 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()