Actualizar server.js

This commit is contained in:
2026-04-03 11:00:53 +00:00
parent 6b212d2e65
commit f42124cb45

View File

@@ -913,6 +913,8 @@ async function procesarConIA(ownerId, mensajeCliente, datosExpediente) {
[ownerId]
);
if (userQ.rowCount === 0) return null;
const userData = userQ.rows[0];
const settings = userData?.wa_settings || {};
const instruccionesExtra = settings.ai_custom_prompt || "";
@@ -936,9 +938,8 @@ async function procesarConIA(ownerId, mensajeCliente, datosExpediente) {
day: "numeric"
}).format(new Date());
// 👇 IMPORTANTE:
// Aquí leemos el historial REAL, ya incluyendo el mensaje actual del cliente
// que el webhook guardará antes de llamar a OpenAI.
// Historial REAL del chat (ya incluye el último mensaje del cliente
// si el webhook lo ha guardado antes de llamar a esta función)
const historyQ = await pool.query(`
SELECT sender_role, message
FROM service_communications
@@ -955,7 +956,7 @@ async function procesarConIA(ownerId, mensajeCliente, datosExpediente) {
content: row.message
}));
// Si solo existe el mensaje actual del cliente, es primer mensaje
// Si el historial solo tiene el mensaje actual del cliente, es primer contacto
const esPrimerMensaje = historialRows.length <= 1;
let agendaOcupadaTexto = "✅ El técnico tiene la agenda libre en horario laboral.";
@@ -988,11 +989,11 @@ async function procesarConIA(ownerId, mensajeCliente, datosExpediente) {
if (r.date && r.time && r.time.includes(':')) {
if (!ocupaciones[r.date]) ocupaciones[r.date] = [];
let [h, m] = r.time.split(':').map(Number);
let dur = parseInt(r.duration || 60, 10);
let endMin = (h * 60 + m) + dur;
let endH = String(Math.floor(endMin / 60) % 24).padStart(2, '0');
let endM = String(endMin % 60).padStart(2, '0');
const [h, m] = r.time.split(':').map(Number);
const dur = parseInt(r.duration || 60, 10);
const endMin = (h * 60 + m) + dur;
const endH = String(Math.floor(endMin / 60) % 24).padStart(2, '0');
const endM = String(endMin % 60).padStart(2, '0');
const tipo = r.provider === 'SYSTEM_BLOCK' ? 'BLOQUEO/AUSENCIA' : 'CITA';
const lugar = r.pob || 'Otra zona';
@@ -1008,6 +1009,7 @@ async function procesarConIA(ownerId, mensajeCliente, datosExpediente) {
day: 'numeric',
month: 'long'
});
return `- Día ${fechaHumana} (${d}):\n * ${ocupaciones[d].join("\n * ")}`;
});
@@ -1021,21 +1023,27 @@ async function procesarConIA(ownerId, mensajeCliente, datosExpediente) {
}
}
const hayCitaPendiente = datosExpediente.appointment_status === 'pending' && datosExpediente.cita_pendiente_fecha;
const tieneCitaConfirmada = datosExpediente.cita && datosExpediente.cita !== 'Ninguna';
const hayCitaPendiente =
datosExpediente.appointment_status === 'pending' &&
datosExpediente.cita_pendiente_fecha;
const tieneCitaConfirmada =
datosExpediente.cita &&
datosExpediente.cita !== 'Ninguna';
const esUrgencia = datosExpediente.is_urgent;
let tramoPendiente = datosExpediente.cita_pendiente_hora || "";
if (tramoPendiente && tramoPendiente.includes(":")) {
let [h, m] = tramoPendiente.split(':');
let hEnd = String((parseInt(h) + 1) % 24).padStart(2, '0');
const [h, m] = tramoPendiente.split(':');
const hEnd = String((parseInt(h, 10) + 1) % 24).padStart(2, '0');
tramoPendiente = `entre las ${h}:${m} y las ${hEnd}:${m} aprox`;
}
let tramoConfirmado = datosExpediente.hora_cita || "";
if (tramoConfirmado && tramoConfirmado.includes(":")) {
let [h, m] = tramoConfirmado.split(':');
let hEnd = String((parseInt(h) + 1) % 24).padStart(2, '0');
const [h, m] = tramoConfirmado.split(':');
const hEnd = String((parseInt(h, 10) + 1) % 24).padStart(2, '0');
tramoConfirmado = `entre las ${h}:${m} y las ${hEnd}:${m} aprox`;
} else {
tramoConfirmado = 'una hora por confirmar';
@@ -1049,7 +1057,9 @@ async function procesarConIA(ownerId, mensajeCliente, datosExpediente) {
if (citaTime < hoyTime) citaYaPaso = true;
}
const esEstadoFinal = datosExpediente.estado && (
const esEstadoFinal =
datosExpediente.estado &&
(
datosExpediente.estado.toLowerCase().includes('finalizado') ||
datosExpediente.estado.toLowerCase().includes('terminado') ||
datosExpediente.estado.toLowerCase().includes('anulado')
@@ -1062,17 +1072,25 @@ async function procesarConIA(ownerId, mensajeCliente, datosExpediente) {
if (esEstadoFinal) {
directivaEstricta = `🛑 ESTADO ACTUAL: SERVICIO CERRADO. Informa al cliente que el servicio por su avería (${datosExpediente.averia}) está finalizado. NO AGENDES NADA.`;
} else if (noTieneTecnico) {
directivaEstricta = `🛑 ESTADO ACTUAL: SIN TÉCNICO ASIGNADO.\nTU ÚNICO OBJETIVO: Informar al cliente que hemos recibido el aviso de su avería (${datosExpediente.averia}) y que estamos coordinando para asignarle un técnico en su zona.\n⛔ PROHIBICIÓN ABSOLUTA: NO ofrezcas citas, NO des horas, NO agendes nada hasta que se le asigne un técnico.`;
directivaEstricta = `🛑 ESTADO ACTUAL: SIN TÉCNICO ASIGNADO.
TU ÚNICO OBJETIVO: Informar al cliente que hemos recibido el aviso de su avería (${datosExpediente.averia}) y que estamos coordinando para asignarle un técnico en su zona.
⛔ PROHIBICIÓN ABSOLUTA: NO ofrezcas citas, NO des horas, NO agendes nada hasta que se le asigne un técnico.`;
} else if (citaYaPaso) {
directivaEstricta = `🛑 ESTADO ACTUAL: LA CITA YA PASÓ (${datosExpediente.cita}). Informa que estamos tramitando su avería (${datosExpediente.averia}). NO AGENDES NADA.`;
} else if (esUrgencia) {
directivaEstricta = `🛑 ESTADO ACTUAL: URGENCIA. Tranquiliza al cliente sobre su avería (${datosExpediente.averia}) y dile que el técnico está avisado. NO PROPONGAS HORAS.`;
} else if (hayCitaPendiente) {
directivaEstricta = `🛑 ESTADO ACTUAL: CITA PENDIENTE DE APROBACIÓN.\n📅 Propuesta actual: El día ${datosExpediente.cita_pendiente_fecha} ${tramoPendiente}.\nTU OBJETIVO: Informar que esperamos confirmación del técnico para reparar su avería (${datosExpediente.averia}).\n⚠️ EXCEPCIÓN: Si el cliente pide CAMBIAR o CANCELAR, ofrécele un hueco nuevo.`;
directivaEstricta = `🛑 ESTADO ACTUAL: CITA PENDIENTE DE APROBACIÓN.
📅 Propuesta actual: El día ${datosExpediente.cita_pendiente_fecha} ${tramoPendiente}.
TU OBJETIVO: Informar que esperamos confirmación del técnico para reparar su avería (${datosExpediente.averia}).
⚠️ EXCEPCIÓN: Si el cliente pide CAMBIAR o CANCELAR, ofrécele un hueco nuevo.`;
} else if (tieneCitaConfirmada) {
directivaEstricta = `🛑 ESTADO ACTUAL: CITA CONFIRMADA para el ${datosExpediente.cita} ${tramoConfirmado}. Recuerda la cita para su avería (${datosExpediente.averia}).\n⚠️ EXCEPCIÓN: Si el cliente pide CAMBIARLA o CANCELARLA, ofrécele un hueco nuevo.`;
directivaEstricta = `🛑 ESTADO ACTUAL: CITA CONFIRMADA para el ${datosExpediente.cita} ${tramoConfirmado}. Recuerda la cita para su avería (${datosExpediente.averia}).
⚠️ EXCEPCIÓN: Si el cliente pide CAMBIARLA o CANCELARLA, ofrécele un hueco nuevo.`;
} else {
directivaEstricta = `🟢 ESTADO ACTUAL: PENDIENTE DE AGENDAR CITA.\nTU OBJETIVO: Acordar fecha y hora para reparar su avería (${datosExpediente.averia}). NUNCA ofrezcas horas ocupadas. Fines de semana solo URGENCIAS.\n⚠️ MUY IMPORTANTE: Cuando el cliente elija un hueco, NO le digas que la cita está confirmada. Dile que le pasas la nota al técnico para que él lo valide.`;
directivaEstricta = `🟢 ESTADO ACTUAL: PENDIENTE DE AGENDAR CITA.
TU OBJETIVO: Acordar fecha y hora para reparar su avería (${datosExpediente.averia}). NUNCA ofrezcas horas ocupadas. Fines de semana solo URGENCIAS.
⚠️ MUY IMPORTANTE: Cuando el cliente elija un hueco, NO le digas que la cita está confirmada. Dile que le pasas la nota al técnico para que él lo valide.`;
}
const promptSistema = `
@@ -1124,6 +1142,7 @@ ${instruccionesExtra ? `6. Instrucción extra de la empresa: ${instruccionesExtr
}
}
// ==========================================
// 📱 OTP PARA PORTAL DEL CLIENTE (ACCESO WEB)
// ==========================================