diff --git a/server.js b/server.js index b5422a9..2fc0c02 100644 --- a/server.js +++ b/server.js @@ -1078,11 +1078,51 @@ function extractValidPhone(rawPhone) { let str = String(rawPhone).replace(/\D/g, ""); // Borra todo lo que no sea nĂșmero (espacios, letras, +, -) if (str.startsWith("0034")) str = str.slice(4); if (str.startsWith("34") && str.length >= 11) str = str.slice(2); - - const match = str.match(/[6789]\d{8}/); + + const match = str.match(/[6789]\d{8}/); return match ? match[0] : (str.length >= 9 ? str.slice(-9) : str); } +// 📞 Un servicio puede traer VARIOS telĂ©fonos mezclados en el mismo campo (tĂ­pico de HomeServe: +// "629046101 de 09:00 a 20:00\n659153929 de 09:00 a 20:00..."), fijos y mĂłviles sin ningĂșn orden. +// Esto extrae TODOS los nĂșmeros vĂĄlidos que encuentre (de "TelĂ©fono"/"TELEFONO"/"TELEFONOS"), +// sin duplicados, y los devuelve con los MÓVILES (6xx/7xx) siempre primero. +function extractAllPhones(raw) { + if (!raw) return []; + const sources = [raw['TELEFONOS'], raw['TelĂ©fonos'], raw['TelĂ©fono'], raw['TELEFONO']].filter(Boolean); + const seen = new Set(); + const result = []; + + for (const src of sources) { + const lines = String(src).split(/\n+/); + for (const line of lines) { + const matches = line.match(/[6789]\d{8}/g) || []; + for (const number of matches) { + if (seen.has(number)) continue; + seen.add(number); + const horario = line.match(/de\s*\d{1,2}:\d{2}\s*a\s*\d{1,2}:\d{2}/i)?.[0] || ''; + const contactoMatch = line.match(/Persona de Contacto:\s*([^\n]*?)(?:\s*Observaciones:|$)/i); + const contacto = contactoMatch ? contactoMatch[1].trim() : ''; + result.push({ number, isMobile: /^[67]/.test(number), horario, contacto }); + } + } + } + + result.sort((a, b) => (b.isMobile - a.isMobile)); + return result; +} + +// Igual que extractValidPhone, pero PRIORIZANDO MÓVILES: para automatizaciones de WhatsApp/SMS, +// que solo pueden llegar de verdad a un nĂșmero mĂłvil, no a un fijo. +function extractBestMobile(raw) { + const phones = extractAllPhones(raw); + if (phones.length > 0) return phones[0].number; + // Red de seguridad: si no detectamos ningĂșn nĂșmero con el patrĂłn habitual, mantenemos + // el comportamiento anterior para no perder del todo la posibilidad de contactar. + const fallbackRaw = (raw && (raw["TelĂ©fono"] || raw["TELEFONO"] || raw["TELEFONOS"])) || ""; + return extractValidPhone(fallbackRaw); +} + // 2. Normaliza SIEMPRE a Formato Internacional para la Base de Datos (+34600123456) function normalizePhone(phone) { const clean = extractValidPhone(phone); @@ -1409,8 +1449,7 @@ async function triggerHomeServeRobot(ownerId, serviceId, eventType) { // 3. Traducir variables personalizadas let text = rule.obs || ""; const clientName = raw["Nombre Cliente"] || raw["CLIENTE"] || "Cliente"; - let clientPhone = raw["TelĂ©fono"] || raw["TELEFONO"] || raw["TELEFONOS"] || ""; - clientPhone = extractValidPhone(clientPhone); + let clientPhone = extractBestMobile(raw); // Formateo humano de la fecha para el texto de observaciones let textoFechaCita = "hoy"; @@ -1503,8 +1542,7 @@ async function triggerMultiRobot(ownerId, serviceId, eventType) { let text = rule.obs || ""; const clientName = raw["Nombre Cliente"] || raw["CLIENTE"] || "Cliente"; - let clientPhone = raw["TelĂ©fono"] || raw["TELEFONO"] || raw["TELEFONOS"] || ""; - clientPhone = extractValidPhone(clientPhone); + let clientPhone = extractBestMobile(raw); let textoFechaCita = "hoy"; if (raw.scheduled_date) { @@ -3119,9 +3157,8 @@ async function triggerWhatsAppEvent(ownerId, serviceId, eventType) { const s = svcQ.rows[0]; const raw = s.raw_data || {}; - // 1. EXTRAER TELÉFONO DEL CLIENTE LIMPIO - let rawPhone = raw["TelĂ©fono"] || raw["TELEFONO"] || raw["TELEFONOS"] || ""; - let cleanPhoneToMatch = extractValidPhone(rawPhone); + // 1. EXTRAER TELÉFONO DEL CLIENTE LIMPIO (prioriza mĂłvil sobre fijo) + let cleanPhoneToMatch = extractBestMobile(raw); if (cleanPhoneToMatch.length < 9) return false; // Si no hay telĂ©fono vĂĄlido, cancelamos const finalPhoneToSend = "34" + cleanPhoneToMatch; @@ -3608,12 +3645,13 @@ app.get("/services/active", authMiddleware, async (req, res) => { } query += ` ORDER BY s.created_at DESC`; - + const q = await pool.query(query, params); - res.json({ ok: true, services: q.rows }); - } catch (e) { + const servicesWithPhones = q.rows.map(s => ({ ...s, phones: extractAllPhones(s.raw_data) })); + res.json({ ok: true, services: servicesWithPhones }); + } catch (e) { console.error("Error al cargar /services/active:", e); - res.status(500).json({ ok: false }); + res.status(500).json({ ok: false }); } }); @@ -3770,9 +3808,8 @@ app.post("/services/not-found/:id", authMiddleware, async (req, res) => { // Guardamos en la base de datos await pool.query("UPDATE scraped_services SET raw_data=$1 WHERE id=$2", [JSON.stringify(raw), id]); - // Intentamos enviar el WhatsApp usando la plantilla - let rawPhone = raw["TelĂ©fono"] || raw["TELEFONOS"] || raw["TELEFONO"] || ""; - let cleanPhoneToMatch = extractValidPhone(rawPhone); + // Intentamos enviar el WhatsApp usando la plantilla (prioriza mĂłvil sobre fijo) + let cleanPhoneToMatch = extractBestMobile(raw); if (cleanPhoneToMatch.length >= 9) { // Solo intentamos enviar si hay un nĂșmero vĂĄlido @@ -5245,8 +5282,7 @@ app.post("/services/:id/work-report", authMiddleware, async (req, res) => { const operatorName = userQ.rows[0]?.full_name || "Operario"; const clientName = raw["Nombre Cliente"] || raw["CLIENTE"] || ""; - const clientPhoneRaw = raw["TelĂ©fono"] || raw["TELEFONO"] || raw["TELEFONOS"] || ""; - const clientPhone = extractValidPhone(clientPhoneRaw); + const clientPhone = extractBestMobile(raw); const insert = await pool.query( `INSERT INTO work_reports (scraped_id, owner_id, operator_id, operator_name, client_name, client_phone, raw_text, final_text, signature_data, pdf_base64) @@ -6081,8 +6117,7 @@ async function enviarRecordatorioAutomatizacion(client, svc, tipo, testMode) { if (tplQ.rowCount === 0 || !tplQ.rows[0].content) return; let text = tplQ.rows[0].content; - const rawPhone = raw["TelĂ©fono"] || raw["TELEFONO"] || raw["TELEFONOS"] || ""; - const cleanPhone = extractValidPhone(rawPhone); + const cleanPhone = extractBestMobile(raw); const nombre = raw["Nombre Cliente"] || raw["CLIENTE"] || raw["Nombre"] || "Cliente"; const direccion = raw["DirecciĂłn"] || raw["DOMICILIO"] || "";