// 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); }