From b79d37d9551533854b19a90b008b3e1feabb4407 Mon Sep 17 00:00:00 2001 From: marsalva Date: Mon, 16 Feb 2026 20:40:28 +0000 Subject: [PATCH] Actualizar server.js --- server.js | 84 +++++++++++++++++++++++++++++++------------------------ 1 file changed, 47 insertions(+), 37 deletions(-) diff --git a/server.js b/server.js index 9380b9f..2a54a04 100644 --- a/server.js +++ b/server.js @@ -310,14 +310,20 @@ async function sendWhatsAppCode(phone, code) { } catch (e) { console.error("Error envío WA:", e.message); } } -async function sendWhatsAppAuto(phone, text) { - if (!EVOLUTION_BASE_URL || !EVOLUTION_API_KEY || !EVOLUTION_INSTANCE) return; +async function sendWhatsAppAuto(phone, text, instanceName) { + if (!EVOLUTION_BASE_URL || !EVOLUTION_API_KEY || !instanceName) return; try { - await fetch(`${EVOLUTION_BASE_URL.replace(/\/$/, "")}/message/sendText/${EVOLUTION_INSTANCE}`, { + // MATEMÁTICA HUMANA: Tiempo simulado de escritura + const typingTimeMs = Math.min(Math.max(text.length * 30, 1500), 8000); + await fetch(`${EVOLUTION_BASE_URL.replace(/\/$/, "")}/message/sendText/${instanceName}`, { method: "POST", headers: { "Content-Type": "application/json", "apikey": EVOLUTION_API_KEY }, - body: JSON.stringify({ number: phone.replace("+", ""), text }) + body: JSON.stringify({ + number: phone.replace("+", ""), + text: text, + delay: typingTimeMs + }) }); - } catch (e) {} + } catch (e) { console.error("Error envío WA:", e.message); } } async function ensureInstance(instanceName) { @@ -560,7 +566,9 @@ app.post("/providers/automate/:id", authMiddleware, async (req, res) => { 🔗 ${link}`; - await sendWhatsAppAuto(worker.phone, mensaje); + // SAAS: INSTANCIA DE CLIENTE ESPECÍFICA SIN AWAIT PARA NO BLOQUEAR + const instanceName = `cliente_${req.user.accountId}`; + sendWhatsAppAuto(worker.phone, mensaje, instanceName).catch(console.error); res.json({ ok: true, message: "Automatismo iniciado con " + worker.full_name }); } catch (e) { @@ -697,8 +705,7 @@ app.get("/services/active", authMiddleware, async (req, res) => { } catch (e) { res.status(500).json({ ok: false }); } }); -// AÑADIDO: Ruta para fijar la cita o el estado operativo -// AÑADIDO: Ruta para fijar la cita o el estado operativo +// AÑADIDO: Ruta para fijar la cita o el estado operativo (CORREGIDA Y BLINDADA) app.put("/services/set-appointment/:id", authMiddleware, async (req, res) => { try { const { id } = req.params; @@ -841,37 +848,40 @@ app.delete("/services/:id", authMiddleware, async (req, res) => { try { await po // 🕒 EL RELOJ DEL SISTEMA (Ejecutar cada minuto) // ========================================== setInterval(async () => { -    try { -       const expiredPings = await pool.query(` -            SELECT ap.id, ap.scraped_id, ap.user_id, s.owner_id, s.raw_data -            FROM assignment_pings ap -            JOIN scraped_services s ON ap.scraped_id = s.id -            WHERE ap.status = 'pending'  -            AND EXTRACT(EPOCH FROM (ap.expires_at - CURRENT_TIMESTAMP)) <= 0 -            AND s.automation_status = 'in_progress' -        `);  + try { + const expiredPings = await pool.query(` + SELECT ap.id, ap.scraped_id, ap.user_id, s.owner_id, s.raw_data + FROM assignment_pings ap + JOIN scraped_services s ON ap.scraped_id = s.id + WHERE ap.status = 'pending' + AND EXTRACT(EPOCH FROM (ap.expires_at - CURRENT_TIMESTAMP)) <= 0 + AND s.automation_status = 'in_progress' + `); -        for (const ping of expiredPings.rows) { -            await pool.query("UPDATE assignment_pings SET status = 'expired' WHERE id = $1", [ping.id]); -            const nextWorkerQ = await pool.query(` -                SELECT u.id, u.phone, u.full_name  -                FROM users u -                JOIN user_guilds ug ON u.id = ug.user_id -                WHERE u.owner_id = $1 AND u.status = 'active' -                AND u.id NOT IN (SELECT user_id FROM assignment_pings WHERE scraped_id = $2) -                LIMIT 1 -            `, [ping.owner_id, ping.scraped_id]); + for (const ping of expiredPings.rows) { + await pool.query("UPDATE assignment_pings SET status = 'expired' WHERE id = $1", [ping.id]); + const nextWorkerQ = await pool.query(` + SELECT u.id, u.phone, u.full_name + FROM users u + JOIN user_guilds ug ON u.id = ug.user_id + WHERE u.owner_id = $1 AND u.status = 'active' + AND u.id NOT IN (SELECT user_id FROM assignment_pings WHERE scraped_id = $2) + LIMIT 1 + `, [ping.owner_id, ping.scraped_id]); -            if (nextWorkerQ.rowCount > 0) { -                const nextW = nextWorkerQ.rows[0]; -                const newToken = crypto.randomBytes(16).toString('hex'); -                await pool.query(`INSERT INTO assignment_pings (scraped_id, user_id, token, expires_at) VALUES ($1, $2, $3, CURRENT_TIMESTAMP + INTERVAL '5 minutes')`, [ping.scraped_id, nextW.id, newToken]); -                await sendWhatsAppAuto(nextW.phone, `🛠️ *SERVICIO DISPONIBLE*\nEl anterior compañero no respondió. Es tu turno:\n🔗 https://integrarepara.es/aceptar.html?t=${newToken}`); -            } else { -                await pool.query("UPDATE scraped_services SET automation_status = 'failed' WHERE id = $1", [ping.scraped_id]); -            } -        } -    } catch (e) { console.error("Reloj:", e); } + if (nextWorkerQ.rowCount > 0) { + const nextW = nextWorkerQ.rows[0]; + const newToken = crypto.randomBytes(16).toString('hex'); + await pool.query(`INSERT INTO assignment_pings (scraped_id, user_id, token, expires_at) VALUES ($1, $2, $3, CURRENT_TIMESTAMP + INTERVAL '5 minutes')`, [ping.scraped_id, nextW.id, newToken]); + + const mensaje = `🛠️ *SERVICIO DISPONIBLE*\nEl anterior compañero no respondió. Es tu turno:\n🔗 https://integrarepara.es/aceptar.html?t=${newToken}`; + const instanceName = `cliente_${ping.owner_id}`; + sendWhatsAppAuto(nextW.phone, mensaje, instanceName).catch(console.error); + } else { + await pool.query("UPDATE scraped_services SET automation_status = 'failed' WHERE id = $1", [ping.scraped_id]); + } + } + } catch (e) { console.error("Reloj:", e); } }, 60000); const port = process.env.PORT || 3000;