rag-whatsapp / src /session.js
DHEIVER's picture
feat: handoff para atendente humano nomeado + confirmação por "sim"
c489e66
Raw
History Blame Contribute Delete
2.52 kB
// src/session.js — Estado de atendimento por contato (jornada).
// Mantém: saudação, memória de conversa, coleta de lead e modo "falar com humano".
// É agnóstico ao backend de IA (HF router ou Ollama) — só guarda estado e grava leads.
import { appendFileSync, mkdirSync, existsSync, readFileSync } from 'node:fs';
import { dirname } from 'node:path';
const TTL_MS = 1000 * 60 * 60 * 6; // 6h sem interação → contato é tratado como "novo"
const MAX_HISTORY = 8; // últimas 8 mensagens (≈4 trocas) lembradas
// Em produção o volume é montado em /data; localmente usa ../data
export const LEADS_PATH = process.env.AUTH_DIR
? '/data/leads.jsonl'
: new URL('../data/leads.jsonl', import.meta.url).pathname;
const sessions = new Map(); // jid -> sessão
export function getSession(jid) {
const now = Date.now();
let s = sessions.get(jid);
if (!s || now - s.lastActivity > TTL_MS) {
s = { greeted: false, stage: 'chat', history: [], lead: {}, paused: false, offeredHuman: false, lastActivity: now };
sessions.set(jid, s);
}
s.lastActivity = now;
return s;
}
export function remember(s, role, content) {
s.history.push({ role, content });
if (s.history.length > MAX_HISTORY) s.history.splice(0, s.history.length - MAX_HISTORY);
}
// Bloco curto do histórico recente, para dar contexto a perguntas de seguimento
// ("e quanto custa?"). Injetado na pergunta enviada ao RAG.
export function historyBlock(s) {
if (!s.history.length) return '';
const recent = s.history.slice(-4);
const lines = recent
.map((m) => `${m.role === 'user' ? 'Cliente' : 'Assistente'}: ${m.content}`)
.join('\n');
return `Conversa anterior (para dar contexto):\n${lines}\n\n`;
}
export function saveLead(jid, lead, extra = {}) {
const rec = {
ts: new Date().toISOString(),
whatsapp: jid.split('@')[0],
nome: lead.name || '',
contato: lead.phone || '',
interesse: lead.interest || '',
...extra,
};
try {
mkdirSync(dirname(LEADS_PATH), { recursive: true });
appendFileSync(LEADS_PATH, JSON.stringify(rec) + '\n');
console.log('🟢 Lead salvo:', rec.nome, rec.contato);
} catch (e) {
console.error('Falha ao salvar lead:', e.message);
}
return rec;
}
export function readLeads() {
if (!existsSync(LEADS_PATH)) return [];
return readFileSync(LEADS_PATH, 'utf8')
.split('\n')
.filter(Boolean)
.map((l) => {
try { return JSON.parse(l); } catch { return null; }
})
.filter(Boolean);
}