From e0c5542f23e476c8d3a7c145ad0ac84ffd3b5c01 Mon Sep 17 00:00:00 2001 From: marsalva Date: Sat, 7 Mar 2026 23:17:00 +0000 Subject: [PATCH] Actualizar server.js --- server.js | 61 ++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 45 insertions(+), 16 deletions(-) diff --git a/server.js b/server.js index b5938db..5afa109 100644 --- a/server.js +++ b/server.js @@ -445,7 +445,7 @@ async function procesarConIA(ownerId, mensajeCliente, datosExpediente) { const ahora = new Date(); const fechaHoyTexto = ahora.toLocaleDateString('es-ES', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' }); - // 🧠 MEMORIA ARREGLADA: Traemos los 8 ÚLTIMOS mensajes (DESC) y luego les damos la vuelta (reverse) + // 🧠 MEMORIA: Traemos los 8 ÚLTIMOS mensajes const historyQ = await pool.query(` SELECT sender_role, message FROM service_communications @@ -453,7 +453,6 @@ async function procesarConIA(ownerId, mensajeCliente, datosExpediente) { ORDER BY created_at DESC LIMIT 8 `, [datosExpediente.dbId]); - // Lo ordenamos cronológicamente para que ChatGPT entienda la charla const historialChat = historyQ.rows.reverse().map(row => ({ role: (row.sender_role === 'ia' || row.sender_role === 'admin' || row.sender_role === 'operario') ? 'assistant' : 'user', content: row.message @@ -461,13 +460,42 @@ async function procesarConIA(ownerId, mensajeCliente, datosExpediente) { const esPrimerMensaje = historialChat.length === 0; + // 🗓️ LECTURA DE AGENDA ANTI-SOLAPAMIENTOS (NUEVO) + let agendaOcupadaTexto = "El técnico tiene la agenda libre. Puedes sugerir cualquier hora dentro del horario laboral."; + if (datosExpediente.worker_id) { + const agendaQ = await pool.query(` + SELECT raw_data->>'scheduled_date' as date, + raw_data->>'scheduled_time' as time, + raw_data->>'Población' as pob + FROM scraped_services + WHERE assigned_to = $1 + AND raw_data->>'scheduled_date' >= CURRENT_DATE::text + AND status != 'archived' + AND id != $2 + ORDER BY date ASC, time ASC + `, [datosExpediente.worker_id, datosExpediente.dbId]); + + if (agendaQ.rowCount > 0) { + const ocupaciones = {}; + agendaQ.rows.forEach(r => { + if(r.date && r.time) { + if(!ocupaciones[r.date]) ocupaciones[r.date] = []; + ocupaciones[r.date].push(`${r.time} (en ${r.pob || 'otra zona'})`); + } + }); + + const lineas = Object.keys(ocupaciones).map(d => `- Día ${d}: Ocupado a las ${ocupaciones[d].join(", ")}`); + if(lineas.length > 0) agendaOcupadaTexto = lineas.join("\n "); + } + } + const promptSistema = ` - Eres el asistente humano de "${empresaNombre}". Hablas de tú, de forma muy natural, corta y directa por WhatsApp. No suenes robótico. + Eres el asistente humano de "${empresaNombre}". Hablas de tú, de forma muy natural, corta y directa por WhatsApp. CONTEXTO DE TRABAJO: - Hoy es: ${fechaHoyTexto}. (Año 2026). - Horario L-V: Mañanas (${horarios.m_start}-${horarios.m_end}) y Tardes (${horarios.a_start}-${horarios.a_end}). - - ⛔ FIN DE SEMANA CERRADO. Si piden cita en fin de semana, recuérdalo y ofrece lunes o viernes. + - ⛔ FIN DE SEMANA CERRADO. Ofrece solo de lunes a viernes. DATOS DEL CLIENTE (Aviso #${datosExpediente.ref}): - Estado: ${datosExpediente.estado} @@ -475,17 +503,18 @@ async function procesarConIA(ownerId, mensajeCliente, datosExpediente) { - Urgencia: ${datosExpediente.is_urgent ? 'SÍ' : 'No'} - Cita actual: ${datosExpediente.cita || 'Ninguna'} - INSTRUCCIONES DE ACTUACIÓN (SIGUE A RAJATABLA): - 1. SI ES URGENCIA: No agendes. Di que el técnico está avisado y contactará urgente. - 2. SI YA TIENE CITA: Recuérdale cuándo es su cita, no le des otra. - 3. PARA AGENDAR (PASO A PASO): - - Si no hay fecha: Pregunta qué día (L-V) y si prefiere mañana o tarde. - - Si dice un día/franja: Sugiere tú una hora concreta (ej: "¿Te viene bien a las 16:30?"). - - ⚠️ CIERRE: Si el cliente ACEPTA tu hora (dice "sí", "vale", "perfecto", "ok"), CIERRA LA CITA. Confírmale que le pasas la nota al técnico y AÑADE OBLIGATORIAMENTE al final el código [PROPUESTA:YYYY-MM-DD HH:mm] con la fecha/hora acordadas. - - EJEMPLO DE CIERRE: "Genial, te dejo anotado para el lunes a las 16:30. Le paso el aviso al técnico. [PROPUESTA:2026-03-09 16:30]" - 4. NUNCA TE PRESENTES si ya estás conversando con el cliente. Ve al grano. - ${esPrimerMensaje ? '5. ÚNICA EXCEPCIÓN: Como es el primer mensaje, preséntate brevemente diciendo de qué empresa eres.' : ''} - 6. Sé extremadamente breve (máximo 1 o 2 frases). + ⚠️ CALENDARIO DEL TÉCNICO ASIGNADO (CRÍTICO): + ${agendaOcupadaTexto} + REGLA DE CALENDARIO: NUNCA sugieras ni aceptes una hora que ya esté ocupada en la lista anterior. Deja un margen de al menos 1 hora entre citas. Si el cliente pide una hora ocupada, dile que está cogida y ofrece una alternativa libre. + + INSTRUCCIONES DE ACTUACIÓN: + 1. SI ES URGENCIA: No agendes. Di que el técnico contactará urgente. + 2. PARA AGENDAR: + - Si no hay fecha: Sugiere tú una hora libre basándote en su calendario. + - Si el cliente ACEPTA una hora libre, CIERRA LA CITA confirmándolo y añade OBLIGATORIAMENTE al final el código [PROPUESTA:YYYY-MM-DD HH:mm]. + 3. NUNCA TE PRESENTES si ya estáis conversando. Ve al grano. + ${esPrimerMensaje ? '4. Como es el primer mensaje, preséntate diciendo de qué empresa eres.' : ''} + 5. Sé breve (máximo 1 o 2 frases). `; const mensajesParaIA = [ @@ -497,7 +526,7 @@ async function procesarConIA(ownerId, mensajeCliente, datosExpediente) { const completion = await openai.chat.completions.create({ model: "gpt-4o-mini", messages: mensajesParaIA, - temperature: 0.2, // Baja creatividad = No divaga y obedece instrucciones + temperature: 0.2, // Mantenemos creatividad baja para que respete el horario estrictamente }); return completion.choices[0].message.content;