67 lines
2.7 KiB
JavaScript
67 lines
2.7 KiB
JavaScript
'use strict';
|
|
|
|
const crypto = require('crypto');
|
|
const prom = require('./prom');
|
|
|
|
// Phase 0:验签 -> 去重 -> 只处理 contact 的 message.created -> 回一条占位消息。
|
|
// Phase 1 会把这里替换成 bridge AI 编排。
|
|
function verifySignature(rawBody, secret, signature) {
|
|
if (!secret || !signature) return false;
|
|
const expected = 'sha256=' + crypto.createHmac('sha256', secret).update(rawBody).digest('hex');
|
|
const a = Buffer.from(String(signature));
|
|
const b = Buffer.from(expected);
|
|
return a.length === b.length && crypto.timingSafeEqual(a, b);
|
|
}
|
|
|
|
function createLibreDeskWebhook(cfg, deps = {}) {
|
|
const client = deps.client || require('./libredesk').createLibreDeskClient(cfg);
|
|
const agent = deps.agent || null;
|
|
const seen = new Map();
|
|
const TTL_MS = 10 * 60 * 1000;
|
|
|
|
function markSeen(uuid) {
|
|
const now = Date.now();
|
|
for (const [key, ts] of seen) if (now - ts > TTL_MS) seen.delete(key);
|
|
if (seen.has(uuid)) return false;
|
|
seen.set(uuid, now);
|
|
return true;
|
|
}
|
|
|
|
async function handle(body) {
|
|
if (!body || body.event !== 'message.created') return;
|
|
const msg = body.payload || {};
|
|
prom.inc('bridge_webhook_events_total', { event: body.event || 'unknown' });
|
|
if (msg.sender_type !== 'contact' || msg.private) return;
|
|
if (!msg.uuid || !msg.conversation_uuid) return;
|
|
if (!markSeen(msg.uuid)) return;
|
|
|
|
const conv = await client.getConversation(msg.conversation_uuid);
|
|
const inboxId = String(conv && conv.inbox_id);
|
|
const allowed = cfg.libredeskWebhookInboxIds || [];
|
|
if (allowed.length && !allowed.includes(inboxId)) {
|
|
console.log(JSON.stringify({ ts: new Date().toISOString(), webhook: 'ignored_inbox', inbox: inboxId, conversation: msg.conversation_uuid }));
|
|
return;
|
|
}
|
|
|
|
const text = String(msg.text_content || msg.content || '').trim();
|
|
console.log(JSON.stringify({ ts: new Date().toISOString(), webhook: 'message.created', inbox: inboxId, conversation: msg.conversation_uuid, message: msg.uuid, text: text.slice(0, 120) }));
|
|
|
|
if (agent) {
|
|
try {
|
|
await agent.run(conv, msg);
|
|
} catch (err) {
|
|
console.error(JSON.stringify({ ts: new Date().toISOString(), webhook: 'agent_error', conversation: msg.conversation_uuid, error: err.message }));
|
|
}
|
|
} else {
|
|
// 没有 agent 时的占位回复(仅用于链路调试)
|
|
const reply = `Bridge Phase 0 已收到:${text || '(空消息)'}`;
|
|
await client.sendMessage(msg.conversation_uuid, reply, { echoId: `bridge-p0-${msg.uuid}` });
|
|
console.log(JSON.stringify({ ts: new Date().toISOString(), webhook: 'replied', conversation: msg.conversation_uuid, reply }));
|
|
}
|
|
}
|
|
|
|
return { verifySignature, handle };
|
|
}
|
|
|
|
module.exports = { createLibreDeskWebhook, verifySignature };
|