Actualizar server.js
This commit is contained in:
61
server.js
61
server.js
@@ -445,7 +445,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 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(`
|
const historyQ = await pool.query(`
|
||||||
SELECT sender_role, message
|
SELECT sender_role, message
|
||||||
FROM service_communications
|
FROM service_communications
|
||||||
@@ -453,7 +453,6 @@ async function procesarConIA(ownerId, mensajeCliente, datosExpediente) {
|
|||||||
ORDER BY created_at DESC LIMIT 8
|
ORDER BY created_at DESC LIMIT 8
|
||||||
`, [datosExpediente.dbId]);
|
`, [datosExpediente.dbId]);
|
||||||
|
|
||||||
// Lo ordenamos cronológicamente para que ChatGPT entienda la charla
|
|
||||||
const historialChat = historyQ.rows.reverse().map(row => ({
|
const historialChat = historyQ.rows.reverse().map(row => ({
|
||||||
role: (row.sender_role === 'ia' || row.sender_role === 'admin' || row.sender_role === 'operario') ? 'assistant' : 'user',
|
role: (row.sender_role === 'ia' || row.sender_role === 'admin' || row.sender_role === 'operario') ? 'assistant' : 'user',
|
||||||
content: row.message
|
content: row.message
|
||||||
@@ -461,13 +460,42 @@ async function procesarConIA(ownerId, mensajeCliente, datosExpediente) {
|
|||||||
|
|
||||||
const esPrimerMensaje = historialChat.length === 0;
|
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 = `
|
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:
|
CONTEXTO DE TRABAJO:
|
||||||
- Hoy es: ${fechaHoyTexto}. (Año 2026).
|
- 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 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}):
|
DATOS DEL CLIENTE (Aviso #${datosExpediente.ref}):
|
||||||
- Estado: ${datosExpediente.estado}
|
- Estado: ${datosExpediente.estado}
|
||||||
@@ -475,17 +503,18 @@ async function procesarConIA(ownerId, mensajeCliente, datosExpediente) {
|
|||||||
- Urgencia: ${datosExpediente.is_urgent ? 'SÍ' : 'No'}
|
- Urgencia: ${datosExpediente.is_urgent ? 'SÍ' : 'No'}
|
||||||
- Cita actual: ${datosExpediente.cita || 'Ninguna'}
|
- Cita actual: ${datosExpediente.cita || 'Ninguna'}
|
||||||
|
|
||||||
INSTRUCCIONES DE ACTUACIÓN (SIGUE A RAJATABLA):
|
⚠️ CALENDARIO DEL TÉCNICO ASIGNADO (CRÍTICO):
|
||||||
1. SI ES URGENCIA: No agendes. Di que el técnico está avisado y contactará urgente.
|
${agendaOcupadaTexto}
|
||||||
2. SI YA TIENE CITA: Recuérdale cuándo es su cita, no le des otra.
|
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.
|
||||||
3. PARA AGENDAR (PASO A PASO):
|
|
||||||
- Si no hay fecha: Pregunta qué día (L-V) y si prefiere mañana o tarde.
|
INSTRUCCIONES DE ACTUACIÓN:
|
||||||
- Si dice un día/franja: Sugiere tú una hora concreta (ej: "¿Te viene bien a las 16:30?").
|
1. SI ES URGENCIA: No agendes. Di que el técnico contactará urgente.
|
||||||
- ⚠️ 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.
|
2. PARA AGENDAR:
|
||||||
- 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]"
|
- Si no hay fecha: Sugiere tú una hora libre basándote en su calendario.
|
||||||
4. NUNCA TE PRESENTES si ya estás conversando con el cliente. Ve al grano.
|
- 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].
|
||||||
${esPrimerMensaje ? '5. ÚNICA EXCEPCIÓN: Como es el primer mensaje, preséntate brevemente diciendo de qué empresa eres.' : ''}
|
3. NUNCA TE PRESENTES si ya estáis conversando. Ve al grano.
|
||||||
6. Sé extremadamente breve (máximo 1 o 2 frases).
|
${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 = [
|
const mensajesParaIA = [
|
||||||
@@ -497,7 +526,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, // 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;
|
return completion.choices[0].message.content;
|
||||||
|
|||||||
Reference in New Issue
Block a user