Actualizar server.js
This commit is contained in:
102
server.js
102
server.js
@@ -448,7 +448,7 @@ async function procesarConIA(ownerId, mensajeCliente, datosExpediente) {
|
|||||||
const ahora = new Date();
|
const ahora = new Date();
|
||||||
const fechaHoyTexto = ahora.toLocaleDateString('es-ES', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' });
|
const fechaHoyTexto = ahora.toLocaleDateString('es-ES', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' });
|
||||||
|
|
||||||
// 🧠 MEMORIA: Traemos los 8 ÚLTIMOS mensajes
|
// 🧠 MEMORIA: Traemos los últimos 8 mensajes
|
||||||
const historyQ = await pool.query(`
|
const historyQ = await pool.query(`
|
||||||
SELECT sender_role, message
|
SELECT sender_role, message
|
||||||
FROM service_communications
|
FROM service_communications
|
||||||
@@ -463,19 +463,14 @@ async function procesarConIA(ownerId, mensajeCliente, datosExpediente) {
|
|||||||
|
|
||||||
const esPrimerMensaje = historialChat.length === 0;
|
const esPrimerMensaje = historialChat.length === 0;
|
||||||
|
|
||||||
// 🗓️ LECTURA DE AGENDA ANTI-SOLAPAMIENTOS
|
// 🗓️ LECTURA DE AGENDA (Solo la usamos si realmente necesita agendar)
|
||||||
let agendaOcupadaTexto = "El técnico tiene la agenda libre. Puedes sugerir cualquier hora dentro del horario laboral.";
|
let agendaOcupadaTexto = "El técnico tiene la agenda libre en horario laboral.";
|
||||||
if (datosExpediente.worker_id) {
|
if (datosExpediente.worker_id) {
|
||||||
const agendaQ = await pool.query(`
|
const agendaQ = await pool.query(`
|
||||||
SELECT raw_data->>'scheduled_date' as date,
|
SELECT raw_data->>'scheduled_date' as date, raw_data->>'scheduled_time' as time, raw_data->>'Población' as pob
|
||||||
raw_data->>'scheduled_time' as time,
|
|
||||||
raw_data->>'Población' as pob
|
|
||||||
FROM scraped_services
|
FROM scraped_services
|
||||||
WHERE assigned_to = $1
|
WHERE assigned_to = $1 AND raw_data->>'scheduled_date' >= CURRENT_DATE::text
|
||||||
AND raw_data->>'scheduled_date' >= CURRENT_DATE::text
|
AND status != 'archived' AND id != $2 ORDER BY date ASC, time ASC
|
||||||
AND status != 'archived'
|
|
||||||
AND id != $2
|
|
||||||
ORDER BY date ASC, time ASC
|
|
||||||
`, [datosExpediente.worker_id, datosExpediente.dbId]);
|
`, [datosExpediente.worker_id, datosExpediente.dbId]);
|
||||||
|
|
||||||
if (agendaQ.rowCount > 0) {
|
if (agendaQ.rowCount > 0) {
|
||||||
@@ -486,48 +481,71 @@ async function procesarConIA(ownerId, mensajeCliente, datosExpediente) {
|
|||||||
ocupaciones[r.date].push(`${r.time} (en ${r.pob || 'otra zona'})`);
|
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(", ")}`);
|
const lineas = Object.keys(ocupaciones).map(d => `- Día ${d}: Ocupado a las ${ocupaciones[d].join(", ")}`);
|
||||||
if(lineas.length > 0) agendaOcupadaTexto = lineas.join("\n ");
|
if(lineas.length > 0) agendaOcupadaTexto = lineas.join("\n ");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 🚦 ESTADO PENDIENTE
|
// 🚦 ANÁLISIS DEL ESTADO (LA MAGIA EMPIEZA AQUÍ)
|
||||||
const hayCitaPendiente = datosExpediente.appointment_status === 'pending' && datosExpediente.cita_pendiente_fecha;
|
const hayCitaPendiente = datosExpediente.appointment_status === 'pending' && datosExpediente.cita_pendiente_fecha;
|
||||||
const textoCitaPendiente = hayCitaPendiente
|
const tieneCitaConfirmada = datosExpediente.cita && datosExpediente.cita !== 'Ninguna';
|
||||||
? `SÍ. El cliente solicitó cita para el ${datosExpediente.cita_pendiente_fecha} a las ${datosExpediente.cita_pendiente_hora || ''}. Está PENDIENTE de que el técnico la apruebe.`
|
const esUrgencia = datosExpediente.is_urgent;
|
||||||
: `NO.`;
|
|
||||||
|
|
||||||
|
// 🎯 INSTRUCCIÓN DINÁMICA: Le ponemos la camisa de fuerza a la IA
|
||||||
|
let directivaEstricta = "";
|
||||||
|
|
||||||
|
if (esUrgencia) {
|
||||||
|
directivaEstricta = `
|
||||||
|
🛑 ESTADO ACTUAL: SERVICIO DE URGENCIA.
|
||||||
|
TU ÚNICO OBJETIVO: Tranquilizar al cliente. Dile que al ser una urgencia, el técnico está avisado y contactará/acudirá lo antes posible.
|
||||||
|
PROHIBICIÓN ABSOLUTA: Bajo ningún concepto intentes dar cita, ni preguntes por fechas, ni propongas horarios.
|
||||||
|
`;
|
||||||
|
} else if (hayCitaPendiente) {
|
||||||
|
directivaEstricta = `
|
||||||
|
🛑 ESTADO ACTUAL: CITA PENDIENTE DE APROBACIÓN POR EL TÉCNICO.
|
||||||
|
Datos de la propuesta actual: Día ${datosExpediente.cita_pendiente_fecha} a las ${datosExpediente.cita_pendiente_hora}.
|
||||||
|
TU ÚNICO OBJETIVO: Informar al cliente que ya le hemos pasado su propuesta al técnico y que estamos esperando a que él la valide en su aplicación.
|
||||||
|
PROHIBICIÓN ABSOLUTA: No agendes de nuevo. No ofrezcas más huecos. Si el cliente dice "vale", despídete amablemente y fin.
|
||||||
|
`;
|
||||||
|
} else if (tieneCitaConfirmada) {
|
||||||
|
directivaEstricta = `
|
||||||
|
🛑 ESTADO ACTUAL: CITA 100% CONFIRMADA.
|
||||||
|
Fecha de la cita: ${datosExpediente.cita}.
|
||||||
|
TU ÚNICO OBJETIVO: Resolver cualquier duda del cliente y recordarle que su cita es el ${datosExpediente.cita}.
|
||||||
|
PROHIBICIÓN ABSOLUTA: No intentes agendar. No menciones huecos libres. El trabajo ya está programado.
|
||||||
|
`;
|
||||||
|
} else {
|
||||||
|
directivaEstricta = `
|
||||||
|
🟢 ESTADO ACTUAL: PENDIENTE DE AGENDAR CITA.
|
||||||
|
TU OBJETIVO: Acordar una fecha y hora con el cliente.
|
||||||
|
|
||||||
|
REGLAS DE AGENDAMIENTO:
|
||||||
|
1. OFRECE HUECOS: Mira la "AGENDA DEL TÉCNICO". Nunca ofrezcas horas ocupadas (deja 1 hora de margen).
|
||||||
|
2. RUTAS INTELIGENTES: El cliente está en ${datosExpediente.poblacion || 'su domicilio'}. Si el técnico ya va a esa población un día concreto, ofrécele ese día para aprovechar el viaje.
|
||||||
|
3. FINES DE SEMANA CERRADO. Ofrece solo de L-V.
|
||||||
|
4. CÓDIGO DE CIERRE (VITAL): Si el cliente ACEPTA una propuesta (ej: tú dices "el lunes a las 10" y él dice "sí, perfecto"), confírmale que le pasas la nota al técnico y AÑADE AL FINAL DE TU MENSAJE: [PROPUESTA:YYYY-MM-DD HH:mm]
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 🧠 EL PROMPT MAESTRO
|
||||||
const promptSistema = `
|
const promptSistema = `
|
||||||
Eres el asistente humano de "${empresaNombre}". Hablas de tú, de forma muy natural, corta y directa por WhatsApp.
|
Eres el coordinador humano de "${empresaNombre}". Hablas de tú, de forma muy natural, empática y al grano por WhatsApp. Eres resolutivo y no suenas como un contestador automático.
|
||||||
|
|
||||||
--- CONTEXTO Y HORARIOS ---
|
--- CONTEXTO BÁSICO ---
|
||||||
- Hoy es: ${fechaHoyTexto}.
|
- 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}).
|
- Horario de la empresa: Lunes a Viernes de ${horarios.m_start} a ${horarios.m_end} y de ${horarios.a_start} a ${horarios.a_end}.
|
||||||
- ⛔ FIN DE SEMANA CERRADO. Ofrece solo de Lunes a Viernes.
|
|
||||||
|
|
||||||
--- DATOS DEL AVISO #${datosExpediente.ref} ---
|
--- AGENDA DEL TÉCNICO ASIGNADO ---
|
||||||
- Estado: ${datosExpediente.estado}
|
|
||||||
- Población: ${datosExpediente.poblacion}
|
|
||||||
- Urgencia: ${datosExpediente.is_urgent ? 'SÍ (Prioridad)' : 'No'}
|
|
||||||
- Cita Confirmada: ${datosExpediente.cita || 'Ninguna'}
|
|
||||||
- Cita Solicitada PENDIENTE: ${textoCitaPendiente}
|
|
||||||
|
|
||||||
--- ⚠️ CALENDARIO DEL TÉCNICO Y RUTAS ---
|
|
||||||
${agendaOcupadaTexto}
|
${agendaOcupadaTexto}
|
||||||
- REGLA 1 (ANTI-SOLAPAMIENTO): NUNCA sugieras ni aceptes una hora que ya esté ocupada en la lista anterior. Deja margen de al menos 1 hora entre citas.
|
|
||||||
- REGLA 2 (RUTAS): El cliente actual está en ${datosExpediente.poblacion}. Si el técnico ya va a ${datosExpediente.poblacion} un día concreto, PRIORIZA ofrecerle ese mismo día (en una hora libre) para aprovechar el viaje.
|
|
||||||
|
|
||||||
--- 🛠️ MANUAL DE ACTUACIÓN (SIGUE EL ORDEN) ---
|
--- 🎯 DIRECTIVA ESTRICTA PARA ESTE MENSAJE ---
|
||||||
1. 🚨 SI ES URGENCIA: No agendes. Di que al ser urgente, el técnico contactará y acudirá lo antes posible.
|
${directivaEstricta}
|
||||||
2. ⏳ SI TIENE CITA PENDIENTE: Dile que su propuesta ya ha sido enviada al técnico y que le avisaréis en cuanto él la confirme en su agenda. NO busques otra fecha.
|
|
||||||
3. ✅ SI TIENE CITA CONFIRMADA: Recuérdale cuándo es, no agendes otra.
|
--- REGLAS DE ORO DE COMUNICACIÓN ---
|
||||||
4. 📅 PARA AGENDAR (Si no aplica lo anterior):
|
1. Máximo 2 frases. Los mensajes de WhatsApp deben ser cortos.
|
||||||
- Sugiere un hueco libre (usando la Regla 2 de Rutas si es posible).
|
2. Lee el historial de la conversación. Si el cliente solo responde "Ok" o "Gracias", dile "De nada, aquí estamos para lo que necesites" y cierra la charla. No le des la chapa.
|
||||||
- ⚠️ CIERRE: Si el cliente ACEPTA, dile que la anotas para pasársela al técnico y que cuando él la vea, se confirmará.
|
3. NO TE PRESENTES si ya estáis conversando.
|
||||||
- ⚠️ OBLIGATORIO: En el momento del cierre, añade AL FINAL tu respuesta el código oculto: [PROPUESTA:YYYY-MM-DD HH:mm]
|
${esPrimerMensaje ? '4. Como es el primer mensaje del chat, preséntate brevemente diciendo que eres de ' + empresaNombre + ' y da su número de aviso (#' + datosExpediente.ref + ').' : ''}
|
||||||
5. SALUDO: ${esPrimerMensaje ? 'Preséntate diciendo que eres de ' + empresaNombre + ' y da el nº de aviso.' : 'NO TE PRESENTES. Ya habéis hablado, ve al grano.'}
|
|
||||||
6. FORMATO: Máximo 2 frases. Corto y al pie.
|
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const mensajesParaIA = [
|
const mensajesParaIA = [
|
||||||
@@ -539,7 +557,7 @@ async function procesarConIA(ownerId, mensajeCliente, datosExpediente) {
|
|||||||
const completion = await openai.chat.completions.create({
|
const completion = await openai.chat.completions.create({
|
||||||
model: "gpt-4o-mini",
|
model: "gpt-4o-mini",
|
||||||
messages: mensajesParaIA,
|
messages: mensajesParaIA,
|
||||||
temperature: 0.2, // Creatividad baja para obedecer reglas y horarios
|
temperature: 0.1, // Congelado. No queremos que invente, queremos que obedezca.
|
||||||
});
|
});
|
||||||
|
|
||||||
return completion.choices[0].message.content;
|
return completion.choices[0].message.content;
|
||||||
|
|||||||
Reference in New Issue
Block a user