chore: CRM-NEW baseline
This commit is contained in:
@@ -0,0 +1,64 @@
|
||||
'use strict';
|
||||
|
||||
const crypto = require('crypto');
|
||||
|
||||
// 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 || {};
|
||||
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 };
|
||||
Reference in New Issue
Block a user