Actualizar server.js

This commit is contained in:
2026-03-28 12:46:37 +00:00
parent cc2b8f1a32
commit baf0d0a98c

View File

@@ -895,12 +895,12 @@ async function procesarConIA(ownerId, mensajeCliente, datosExpediente) {
if (agendaQ.rowCount > 0) {
const ocupaciones = {};
agendaQ.rows.forEach(r => {
if(r.date && r.time && r.time.includes(':')) {
if(!ocupaciones[r.date]) ocupaciones[r.date] = [];
if (r.date && r.time && r.time.includes(':')) {
if (!ocupaciones[r.date]) ocupaciones[r.date] = [];
// Calculamos la hora de fin exacta sumando la duración (Ej: 60, 120, 180 min)
let [h, m] = r.time.split(':').map(Number);
let dur = parseInt(r.duration || 60);
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');
@@ -912,8 +912,17 @@ async function procesarConIA(ownerId, mensajeCliente, datosExpediente) {
}
});
const lineas = Object.keys(ocupaciones).map(d => `- Día ${d}:\n * ${ocupaciones[d].join("\n * ")}`);
if(lineas.length > 0) {
const lineas = Object.keys(ocupaciones).sort().map(d => {
const [y, m, day] = d.split('-').map(Number);
const fechaHumana = new Date(y, m - 1, day, 12, 0, 0).toLocaleDateString('es-ES', {
weekday: 'long',
day: 'numeric',
month: 'long'
});
return `- Día ${fechaHumana} (${d}):\n * ${ocupaciones[d].join("\n * ")}`;
});
if (lineas.length > 0) {
agendaOcupadaTexto = "Ocupaciones actuales del técnico (Citas confirmadas, pendientes y bloqueos):\n" + lineas.join("\n") +
"\n\n👉 IMPORTANTE: Todas las horas que NO se solapen con esos tramos exactos ESTÁN LIBRES." +
"\n🚨 REGLA LOGÍSTICA ESTRICTA: El técnico necesita tiempo para viajar. Si el cliente actual es de una localidad distinta a la cita anterior o posterior (ej: Algeciras vs La Línea), ES OBLIGATORIO dejar un margen de al menos 45-60 minutos de viaje entre el final de una cita y el inicio de la siguiente. NUNCA ofrezcas horas pegadas si hay desplazamiento.";
@@ -4054,10 +4063,27 @@ app.post("/webhook/evolution", async (req, res) => {
// 🛡️ REGEX BLINDADO: Pilla la etiqueta aunque la IA meta espacios raros
const matchPropuesta = respuestaIA.match(/\[PROPUESTA:\s*(\d{4}-\d{2}-\d{2})\s+(\d{2}:\d{2})\]/i);
// 🧹 BORRAMOS EL TEXTO DEL CÓDIGO PARA QUE EL CLIENTE NO LO VEA NUNCA
let textoLimpio = respuestaIA.replace(/\[PROPUESTA:.*?\]/gi, "").replace(/código:/gi, "").trim();
if (matchPropuesta) {
const fechaSugerida = matchPropuesta[1];
const horaSugerida = matchPropuesta[2];
// 🛡️ ESCUDO ANTI-SOLAPE: comprobamos antes de guardar la propuesta
const disponibilidad = await comprobarDisponibilidad(
ownerId,
service.assigned_to,
fechaSugerida,
horaSugerida,
60,
service.id
);
if (disponibilidad.choca) {
console.log(`⛔ [DOBLE-BOOKING EVITADO] Exp ${service.service_ref} chocaba con ${disponibilidad.ref} a las ${disponibilidad.time}`);
textoLimpio = "Uy, perdona, se me acaban de cruzar los cables y justo me han bloqueado ese hueco por el sistema interno. 😅 ¿Me dices otra hora que te venga bien?";
} else {
// 🚀 GUARDADO COMO PENDIENTE (Espera a que el Técnico la apruebe en la App o en la Oficina)
await pool.query(`
UPDATE scraped_services
@@ -4068,13 +4094,13 @@ app.post("/webhook/evolution", async (req, res) => {
) WHERE id = $3
`, [fechaSugerida, horaSugerida, service.id]);
}
// 🧹 BORRAMOS EL TEXTO DEL CÓDIGO PARA QUE EL CLIENTE NO LO VEA NUNCA
const textoLimpio = respuestaIA.replace(/\[PROPUESTA:.*?\]/gi, "").replace(/código:/gi, "").trim();
}
await sendWhatsAppAuto(telefonoCliente, textoLimpio, instanceName, true);
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", textoLimpio]);
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", textoLimpio]
);
}
} finally {
candadosIA.delete(service.id);