From b01d30bf4877c6b89c9fc6123b5357171b326062 Mon Sep 17 00:00:00 2001 From: marsalva Date: Sat, 7 Mar 2026 19:53:25 +0000 Subject: [PATCH] Actualizar server.js --- server.js | 98 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) diff --git a/server.js b/server.js index ea98c0e..57694f6 100644 --- a/server.js +++ b/server.js @@ -4,6 +4,7 @@ import bcrypt from "bcryptjs"; import jwt from "jsonwebtoken"; import pg from "pg"; import crypto from "crypto"; +import OpenAI from "openai"; const { Pool } = pg; const app = express(); @@ -401,6 +402,43 @@ async function registrarMovimiento(serviceId, userId, action, details) { } catch (e) { console.error("Error Robot Notario:", e); } } +// HELPER: Procesar mensaje con IA (ChatGPT) +async function procesarConIA(ownerId, mensajeCliente, datosExpediente) { + try { + const userQ = await pool.query("SELECT wa_settings FROM users WHERE id=$1", [ownerId]); + const settings = userQ.rows[0]?.wa_settings || {}; + if (!settings.wa_ai_enabled) return null; + + const promptSistema = ` + Eres el asistente de IntegraRepara. Responde al cliente de forma profesional y corta. + DATOS DEL EXPEDIENTE #${datosExpediente.ref}: + - Estado: ${datosExpediente.estado} + - Operario: ${datosExpediente.operario || 'Pendiente de asignar'} + - Fecha Cita: ${datosExpediente.cita || 'Pendiente de agendar'} + + REGLAS: + - Si el cliente pregunta cuándo van, dale la fecha si existe. + - Si no hay operario, di que estamos buscando al mejor técnico. + - NO inventes datos. Si no sabes algo, pide que espere a que un humano le atienda. + - Máximo 2 frases. + `; + + const completion = await openai.chat.completions.create({ + model: "gpt-4o-mini", + messages: [ + { role: "system", content: promptSistema }, + { role: "user", content: mensajeCliente } + ], + temperature: 0.7, + }); + + return completion.choices[0].message.content; + } catch (e) { + console.error("❌ Error OpenAI:", e.message); + return null; + } +} + // 🛡️ MIDDLEWARE DE PLANES async function requirePlan(req, res, next, feature) { try { @@ -2901,6 +2939,66 @@ app.post("/services/:id/chat", authMiddleware, async (req, res) => { } }); +// 🤖 WEBHOOK PARA RECIBIR MENSAJES DE WHATSAPP Y RESPONDER CON IA +app.post("/webhook/evolution", async (req, res) => { + try { + const data = req.body; + // Solo procesamos si es un mensaje de texto entrante + if (data.event !== "messages.upsert" || data.data.key.fromMe) return res.sendStatus(200); + + const telefonoCliente = data.data.key.remoteJid.split("@")[0]; + const mensajeTexto = data.data.message?.conversation || data.data.message?.extendedTextMessage?.text; + const instanceName = data.instance; // ej: cliente_1 + + if (!mensajeTexto) return res.sendStatus(200); + + // 1. Identificar al dueño de la instancia (owner_id) + const ownerId = instanceName.split("_")[1]; + + // 2. Buscar si este teléfono tiene un servicio activo + const svcQ = await pool.query(` + SELECT s.id, s.service_ref, s.assigned_to, u.full_name as worker_name, + st.name as status_name, s.raw_data->>'scheduled_date' as cita + FROM scraped_services s + 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 + WHERE s.owner_id = $1 AND s.raw_data->>'Teléfono' ILIKE $2 + ORDER BY s.created_at DESC LIMIT 1 + `, [ownerId, `%${telefonoCliente.slice(-9)}%`]); + + if (svcQ.rowCount > 0) { + const service = svcQ.rows[0]; + + // 3. Llamar a la IA + const respuestaIA = await procesarConIA(ownerId, mensajeTexto, { + ref: service.service_ref, + estado: service.status_name, + operario: service.worker_name, + cita: service.cita + }); + + if (respuestaIA) { + // 4. Enviar respuesta por WhatsApp + await sendWhatsAppAuto(telefonoCliente, respuestaIA, instanceName, true); + + // 5. Registrar en el chat para que la oficina lo vea + await pool.query(` + 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]); + + // 6. Registrar en el historial + await registrarMovimiento(service.id, null, "Respuesta IA", `ChatGPT respondió: ${respuestaIA}`); + } + } + + res.sendStatus(200); + } catch (e) { + console.error("Error Webhook IA:", e.message); + res.sendStatus(500); + } +}); + // ========================================== // 🕒 EL RELOJ DEL SISTEMA (Ejecutar cada minuto) // ==========================================