Actualizar server.js

This commit is contained in:
2026-03-07 20:58:20 +00:00
parent f7d966fcd1
commit 0c803c2fe1

View File

@@ -2968,51 +2968,57 @@ app.post("/services/:id/chat", authMiddleware, async (req, res) => {
} }
}); });
// 🤖 WEBHOOK PARA RECIBIR MENSAJES DE WHATSAPP Y RESPONDER CON IA // 🤖 WEBHOOK CON ESCUDO DE INTERVENCIÓN HUMANA
app.post("/webhook/evolution", async (req, res) => { app.post("/webhook/evolution", async (req, res) => {
try { try {
const data = req.body; const data = req.body;
// 1. Filtrar solo mensajes de texto entrantes
if (data.event !== "messages.upsert" || data.data.key.fromMe) return res.sendStatus(200); if (data.event !== "messages.upsert" || data.data.key.fromMe) return res.sendStatus(200);
const remoteJid = data.data.key.remoteJid; const remoteJid = data.data.key.remoteJid;
const telefonoCliente = remoteJid.split("@")[0]; // Ej: 34666777888 const telefonoCliente = remoteJid.split("@")[0];
const mensajeTexto = data.data.message?.conversation || data.data.message?.extendedTextMessage?.text; const mensajeTexto = data.data.message?.conversation || data.data.message?.extendedTextMessage?.text;
const instanceName = data.instance; // Ej: cliente_1 const instanceName = data.instance;
if (!mensajeTexto) return res.sendStatus(200); if (!mensajeTexto) return res.sendStatus(200);
console.log(`📩 [WEBHOOK] Mensaje de ${telefonoCliente}: "${mensajeTexto}" (Instancia: ${instanceName})`);
// 2. Identificar al dueño (owner_id)
const ownerId = instanceName.split("_")[1]; const ownerId = instanceName.split("_")[1];
if (!ownerId) {
console.error("❌ [WEBHOOK] No se pudo determinar el ownerId de la instancia:", instanceName);
return res.sendStatus(200);
}
// 3. Buscar siniestro por teléfono (Búsqueda ULTRA-FLEXIBLE en todo el JSON)
// Buscamos los últimos 9 dígitos para ignorar el +34 o 34
const cleanPhone = telefonoCliente.slice(-9); const cleanPhone = telefonoCliente.slice(-9);
// 1. BUSCAMOS EL SINIESTRO
const svcQ = await pool.query(` const svcQ = await pool.query(`
SELECT s.id, s.service_ref, u.full_name as worker_name, SELECT s.id, s.service_ref, u.full_name as worker_name,
st.name as status_name, s.raw_data->>'scheduled_date' as cita st.name as status_name, s.raw_data->>'scheduled_date' as cita
FROM scraped_services s FROM scraped_services s
LEFT JOIN users u ON s.assigned_to = u.id LEFT JOIN users u ON s.assigned_to = u.id
LEFT JOIN service_statuses st ON (s.raw_data->>'status_operativo')::text = st.id::text LEFT JOIN service_statuses st ON (s.raw_data->>'status_operativo')::text = st.id::text
WHERE s.owner_id = $1 WHERE s.owner_id = $1 AND s.status != 'archived' AND s.raw_data::text ILIKE $2
AND s.status != 'archived'
AND s.raw_data::text ILIKE $2
ORDER BY s.created_at DESC LIMIT 1 ORDER BY s.created_at DESC LIMIT 1
`, [ownerId, `%${cleanPhone}%`]); `, [ownerId, `%${cleanPhone}%`]);
if (svcQ.rowCount > 0) { if (svcQ.rowCount > 0) {
const service = svcQ.rows[0]; const service = svcQ.rows[0];
console.log(`🔎 [IA] Siniestro localizado: #${service.service_ref}. Preparando respuesta...`);
// 🛡️ 2. VERIFICAR INTERVENCIÓN HUMANA (NUEVO)
// 4. Llamar a la IA // Miramos si el último mensaje fue de un humano en las últimas 2 horas
const checkHumanQ = await pool.query(`
SELECT sender_role, created_at
FROM service_communications
WHERE scraped_id = $1
ORDER BY created_at DESC LIMIT 1
`, [service.id]);
if (checkHumanQ.rowCount > 0) {
const lastMsg = checkHumanQ.rows[0];
const diffMinutos = (new Date() - new Date(lastMsg.created_at)) / (1000 * 60);
// Si el último que habló fue admin/operario y hace menos de 120 min, la IA no responde
if (['admin', 'superadmin', 'operario'].includes(lastMsg.sender_role) && diffMinutos < 120) {
console.log(`🤫 [IA Silenciada] Un humano ha intervenido hace ${Math.round(diffMinutos)} min en el exp #${service.service_ref}`);
return res.sendStatus(200);
}
}
// 3. SI NO HAY HUMANO RECIENTE, LLAMAMOS A LA IA
const respuestaIA = await procesarConIA(ownerId, mensajeTexto, { const respuestaIA = await procesarConIA(ownerId, mensajeTexto, {
ref: service.service_ref, ref: service.service_ref,
estado: service.status_name || "En proceso", estado: service.status_name || "En proceso",
@@ -3021,24 +3027,12 @@ app.post("/webhook/evolution", async (req, res) => {
}); });
if (respuestaIA) { if (respuestaIA) {
console.log(`🤖 [IA] Respondiendo: "${respuestaIA}"`);
// 5. Enviar respuesta por WhatsApp
await sendWhatsAppAuto(telefonoCliente, respuestaIA, instanceName, true); await sendWhatsAppAuto(telefonoCliente, respuestaIA, instanceName, true);
await pool.query(`INSERT INTO service_communications (scraped_id, owner_id, sender_name, sender_role, message) VALUES ($1, $2, $3, $4, $5)`,
// 6. Registrar en el chat y logs de trazabilidad [service.id, ownerId, "Asistente IA", "ia", respuestaIA]);
await pool.query(` await registrarMovimiento(service.id, null, "Respuesta IA", `ChatGPT respondió automáticamente.`);
INSERT INTO service_communications (scraped_id, owner_id, sender_name, sender_role, message)
VALUES ($1, $2, $3, $4, $5)
`, [service.id, ownerId, "Asistente IA", "ia", respuestaIA]);
await registrarMovimiento(service.id, null, "Respuesta IA", `ChatGPT respondió automáticamente al cliente.`);
} else {
console.log(" [IA] ChatGPT no generó respuesta (Botón OFF o error interno).");
} }
} else {
console.warn(`⚠️ [WEBHOOK] No hay siniestros activos para el teléfono ${cleanPhone} en la cuenta del admin ${ownerId}`);
} }
res.sendStatus(200); res.sendStatus(200);
} catch (e) { } catch (e) {
console.error("❌ [WEBHOOK ERROR]:", e.message); console.error("❌ [WEBHOOK ERROR]:", e.message);