diff --git a/server.js b/server.js index eaffb10..5d0e527 100644 --- a/server.js +++ b/server.js @@ -1812,38 +1812,104 @@ app.post("/providers/credentials", authMiddleware, async (req, res) => { }); // ========================================== -// đŸ“€ OBTENER SERVICIOS PARA EL BUZÓN (ESTO ES LO QUE SE BORRÓ SIN QUERER) +// đŸ“„ RECEPCIÓN DE SERVICIOS (EMBUDO INTELIGENTE DEFINITIVO) // ========================================== -app.get("/providers/scraped", authMiddleware, async (req, res) => { +app.post("/providers/scraped", authMiddleware, async (req, res) => { try { - const q = await pool.query(` - SELECT - s.*, - ap.token as active_token, - EXTRACT(EPOCH FROM (ap.expires_at - CURRENT_TIMESTAMP)) as seconds_left, - u.full_name as current_worker_name, - (SELECT json_agg(json_build_object('name', u2.full_name, 'phone', u2.phone)) - FROM assignment_pings ap2 - JOIN users u2 ON ap2.user_id = u2.id - WHERE ap2.scraped_id = s.id AND ap2.status IN ('expired', 'rejected')) as attempted_workers_data - FROM scraped_services s - LEFT JOIN assignment_pings ap ON s.id = ap.scraped_id AND ap.status = 'pending' - LEFT JOIN users u ON ap.user_id = u.id - WHERE s.owner_id = $1 - ORDER BY s.created_at DESC - `, [req.user.accountId]); + const { provider, services } = req.body; - const services = q.rows.map(row => { - if (row.seconds_left && row.seconds_left > 0) { - row.token_expires_at = new Date(Date.now() + (row.seconds_left * 1000)); - } else if (row.seconds_left <= 0) { - row.token_expires_at = new Date(Date.now() - 1000); + if (!provider || !Array.isArray(services)) { + return res.status(400).json({ ok: false, error: "Formato de datos invĂĄlido" }); + } + + let count = 0; + + const credsQ = await pool.query( + "SELECT auto_dispatch FROM provider_credentials WHERE owner_id = $1 AND provider = $2", + [req.user.accountId, provider] + ); + const autoDispatchEnabled = credsQ.rowCount > 0 && credsQ.rows[0].auto_dispatch === true; + + for (const svc of services) { + + // 1. EXTRAER REFERENCIA A PRUEBA DE BOMBAS (Incluye "SERVICIO" de HomeServe) + const ref = svc['service_ref'] + || svc['SERVICIO'] + || svc['Referencia'] + || svc['NÂș Siniestro'] + || svc['Expediente'] + || svc['expediente'] + || (svc.raw_data && (svc.raw_data['SERVICIO'] || svc.raw_data['Referencia'] || svc.raw_data['NÂș Siniestro'] || svc.raw_data['Expediente'] || svc.raw_data['expediente'])); + + if (!ref) { + console.log("⚠ Se omitiĂł un servicio por no encontrar el nĂșmero de referencia."); + continue; } - delete row.seconds_left; - return row; - }); - res.json({ ok: true, services }); - } catch (e) { res.status(500).json({ ok: false }); } + + // 2. DETECTOR DE URGENCIAS (APLASTAMIENTO TOTAL - CERO FALSOS POSITIVOS) + let esUrgente = false; + + // Juntamos todos los valores del objeto en un solo string + const textoPlano = JSON.stringify(svc).toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, ""); + + // Caso HomeServe: frases mĂĄgicas exactas (cero falsos positivos) + if ( + textoPlano.includes('atencion presencial urgencias') || + textoPlano.includes('atencion de la urgencia') + ) { + esUrgente = true; + } else { + // Caso Multiasistencia u otros: si la columna se llama Urgencia/Urgent y el valor es "si" o "true" + for (const key in svc) { + const col = String(key).toLowerCase(); + const val = String(svc[key]).toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, ""); + if ((col.includes('urgent') || col === 'urgencia') && (val === 'si' || val === 'true' || val === 'si')) { + esUrgente = true; + break; + } + } + } + + const guildId = svc.guild_id || svc['guild_id'] || (svc.raw_data && svc.raw_data.guild_id); + + // 3. GUARDAMOS EN LA BD (CON MEMORIA ETERNA: OR EXCLUDED.is_urgent) + const insertRes = await pool.query(` + INSERT INTO scraped_services (owner_id, provider, service_ref, raw_data, is_urgent) + VALUES ($1, $2, $3, $4, $5) + ON CONFLICT (owner_id, provider, service_ref) + DO UPDATE SET + raw_data = scraped_services.raw_data || EXCLUDED.raw_data, + is_urgent = scraped_services.is_urgent OR EXCLUDED.is_urgent + RETURNING id, automation_status + `, [req.user.accountId, provider, ref, JSON.stringify(svc), esUrgente]); + + const newSvcId = insertRes.rows[0].id; + const autoStatus = insertRes.rows[0].automation_status; + + // 4. AUTO-DESPACHO + if (esUrgente && guildId && autoDispatchEnabled && (autoStatus === 'manual' || autoStatus === 'pending')) { + console.log(`⚡ [AUTO-DISPATCH] Lanzando urgencia ${ref} de ${provider} a la bolsa...`); + + const cpMatch = textoPlano.match(/\b\d{5}\b/); + const cpFinal = cpMatch ? cpMatch[0] : "00000"; + + const port = process.env.PORT || 3000; + fetch(`http://127.0.0.1:${port}/providers/automate/${newSvcId}`, { + method: 'POST', + headers: { 'Content-Type': 'application/json', 'Authorization': req.headers.authorization }, + body: JSON.stringify({ guild_id: guildId, cp: cpFinal, useDelay: false }) + }).catch(e => console.error("Error en auto-despacho:", e.message)); + } + + count++; + } + + res.json({ ok: true, inserted: count }); + + } catch (error) { + console.error("❌ Error recibiendo servicios:", error); + res.status(500).json({ ok: false, error: error.message }); + } }); // ==========================================