Actualizar server.js

This commit is contained in:
2026-02-20 22:04:27 +00:00
parent f59e079f4e
commit 7cedfc3fe4

View File

@@ -310,8 +310,12 @@ function genCode6() { return String(Math.floor(100000 + Math.random() * 900000))
// 🛡️ MIDDLEWARE DE PLANES (CORREGIDO) // 🛡️ MIDDLEWARE DE PLANES (CORREGIDO)
async function requirePlan(req, res, next, feature) { async function requirePlan(req, res, next, feature) {
try { try {
// Quitamos subscription_status para que no dé error en BD actualizadas
const q = await pool.query("SELECT plan_tier FROM users WHERE id=$1", [req.user.accountId]); const q = await pool.query("SELECT plan_tier FROM users WHERE id=$1", [req.user.accountId]);
const userPlan = 'pro'; // Forzamos PRO para pruebas
// ⚠️ TRUCO TEMPORAL: Forzamos a que el sistema te lea como 'pro' para que puedas probar WhatsApp sin bloqueos
const userPlan = 'pro'; // Cuando quieras restringir planes, cambia esto por: q.rows[0]?.plan_tier || 'free';
const limits = PLAN_LIMITS[userPlan]; const limits = PLAN_LIMITS[userPlan];
if (!limits || !limits[feature]) { if (!limits || !limits[feature]) {
@@ -353,6 +357,7 @@ async function sendWhatsAppAuto(originalPhone, text, instanceName, useDelay = tr
let payloadConEscribiendo; let payloadConEscribiendo;
const typingTimeMs = Math.min(Math.max(text.length * 30, 1500), 8000); const typingTimeMs = Math.min(Math.max(text.length * 30, 1500), 8000);
// Añadimos una cabecera para que sepas a quién iba dirigido realmente el mensaje
const textWithNotice = `*(PRUEBA - Iba para: ${originalPhone})*\n\n` + text; const textWithNotice = `*(PRUEBA - Iba para: ${originalPhone})*\n\n` + text;
if(useDelay) { if(useDelay) {
@@ -635,15 +640,14 @@ async function triggerWhatsAppEvent(ownerId, serviceId, eventType) {
if (!settings[checkSwitch]) return; // Si el botón está apagado, salimos if (!settings[checkSwitch]) return; // Si el botón está apagado, salimos
// 2. Buscamos qué plantilla corresponde a este evento // 2. Buscamos qué plantilla corresponde a este evento
const tplTypeMap = { const tplTypeMap = {
'wa_evt_welcome': 'welcome', 'wa_evt_welcome': 'welcome',
'wa_evt_assigned': 'assigned', // <--- NUEVO: Asignado a operario 'wa_evt_assigned': 'assigned', // NUEVO: Asignado a operario
'wa_evt_date': 'appointment', 'wa_evt_date': 'appointment', // Plantilla: Cita Creada
'wa_evt_update': 'update', 'wa_evt_update': 'update', // Plantilla: Modificación de Servicio
'wa_evt_onway': 'on_way', 'wa_evt_onway': 'on_way',
'wa_evt_survey': 'survey' 'wa_evt_survey': 'survey'
}; };
const tplQ = await pool.query("SELECT content FROM message_templates WHERE owner_id=$1 AND type=$2", [ownerId, tplTypeMap[eventType]]); const tplQ = await pool.query("SELECT content FROM message_templates WHERE owner_id=$1 AND type=$2", [ownerId, tplTypeMap[eventType]]);
if (tplQ.rowCount === 0 || !tplQ.rows[0].content) return; if (tplQ.rowCount === 0 || !tplQ.rows[0].content) return;
let text = tplQ.rows[0].content; let text = tplQ.rows[0].content;
@@ -660,7 +664,7 @@ async function triggerWhatsAppEvent(ownerId, serviceId, eventType) {
const phoneClean = phone.replace('+34', '').trim(); const phoneClean = phone.replace('+34', '').trim();
const clientQ = await pool.query("SELECT portal_token FROM clients WHERE phone LIKE $1 AND owner_id=$2 LIMIT 1", [`%${phoneClean}%`, ownerId]); const clientQ = await pool.query("SELECT portal_token FROM clients WHERE phone LIKE $1 AND owner_id=$2 LIMIT 1", [`%${phoneClean}%`, ownerId]);
const token = clientQ.rowCount > 0 ? clientQ.rows[0].portal_token : "ERROR"; const token = clientQ.rowCount > 0 ? clientQ.rows[0].portal_token : "ERROR";
const linkMagico = `https://portal.integrarepara.es/?token=${token}`; const linkMagico = `https://portal.integrarepara.es/?token=${token}&service=${serviceId}`;
// ========================================== // ==========================================
// 🔄 5. TRADUCTOR DE FECHAS AL FORMATO ESPAÑOL + DÍA // 🔄 5. TRADUCTOR DE FECHAS AL FORMATO ESPAÑOL + DÍA
@@ -691,7 +695,6 @@ async function triggerWhatsAppEvent(ownerId, serviceId, eventType) {
} catch (e) { console.error("Error Motor WA:", e.message); } } catch (e) { console.error("Error Motor WA:", e.message); }
} }
app.get("/providers/credentials", authMiddleware, async (req, res) => { app.get("/providers/credentials", authMiddleware, async (req, res) => {
try { try {
const q = await pool.query("SELECT provider, username, last_sync, status FROM provider_credentials WHERE owner_id=$1", [req.user.accountId]); const q = await pool.query("SELECT provider, username, last_sync, status FROM provider_credentials WHERE owner_id=$1", [req.user.accountId]);
@@ -786,6 +789,8 @@ app.post("/providers/automate/:id", authMiddleware, async (req, res) => {
VALUES ($1, $2, $3, CURRENT_TIMESTAMP + INTERVAL '5 minutes') VALUES ($1, $2, $3, CURRENT_TIMESTAMP + INTERVAL '5 minutes')
`, [id, worker.id, token]); `, [id, worker.id, token]);
// CÁLCULO DE HORA 100% FIABLE: Se lo pedimos a Node forzando a España
// Así siempre saldrá "0:40" en lugar de "23:40" en el texto de WhatsApp
const horaCaducidad = new Date(Date.now() + 5 * 60 * 1000).toLocaleTimeString('es-ES', { const horaCaducidad = new Date(Date.now() + 5 * 60 * 1000).toLocaleTimeString('es-ES', {
hour: '2-digit', hour: '2-digit',
minute: '2-digit', minute: '2-digit',
@@ -807,6 +812,7 @@ app.post("/providers/automate/:id", authMiddleware, async (req, res) => {
🔗 ${link}`; 🔗 ${link}`;
// SAAS: INSTANCIA DE CLIENTE ESPECÍFICA SIN AWAIT PARA NO BLOQUEAR
const instanceName = `cliente_${req.user.accountId}`; const instanceName = `cliente_${req.user.accountId}`;
sendWhatsAppAuto(worker.phone, mensaje, instanceName, useDelay).catch(console.error); sendWhatsAppAuto(worker.phone, mensaje, instanceName, useDelay).catch(console.error);
@@ -966,29 +972,19 @@ app.put("/services/set-appointment/:id", authMiddleware, async (req, res) => {
const statusQ = await pool.query("SELECT name FROM service_statuses WHERE id=$1", [status_operativo]); const statusQ = await pool.query("SELECT name FROM service_statuses WHERE id=$1", [status_operativo]);
const stName = statusQ.rows[0]?.name.toLowerCase() || ""; const stName = statusQ.rows[0]?.name.toLowerCase() || "";
// ... dentro de la lógica del Motor en set-appointment ... // --- MOTOR DE EVENTOS CORREGIDO ---
const stName = statusQ.rows[0]?.name.toLowerCase() || ""; if (stName.includes('asignado')) {
// Si el estado contiene "asignado", disparar plantilla específica
if (stName.includes('asignado')) { triggerWhatsAppEvent(req.user.accountId, id, 'wa_evt_assigned');
// Si el estado contiene "asignado", disparamos el nuevo evento } else if (stName.includes('citado') && newDate !== "") {
triggerWhatsAppEvent(req.user.accountId, id, 'wa_evt_assigned');
} else if (stName.includes('citado') && newDate !== "") {
// ... resto de lógica que ya tenemos ...
// REGLA ESTRICTA: ¿Es estado Citado y tiene fecha?
if (stName.includes('citado') && newDate !== "") {
if (oldDate === "") { if (oldDate === "") {
// No tenía fecha antes -> Dispara "Cita Creada" // Primera vez que se pone fecha
triggerWhatsAppEvent(req.user.accountId, id, 'wa_evt_date'); triggerWhatsAppEvent(req.user.accountId, id, 'wa_evt_date');
} else if (oldDate !== newDate || oldTime !== newTime) { } else if (oldDate !== newDate || oldTime !== newTime) {
// Ya tenía fecha y se ha cambiado -> Dispara "Modificación" // Cambio de fecha u hora
triggerWhatsAppEvent(req.user.accountId, id, 'wa_evt_update'); triggerWhatsAppEvent(req.user.accountId, id, 'wa_evt_update');
} }
} } else if (stName.includes('camino')) {
// Otras reglas
else if (stName.includes('camino')) {
triggerWhatsAppEvent(req.user.accountId, id, 'wa_evt_onway'); triggerWhatsAppEvent(req.user.accountId, id, 'wa_evt_onway');
} else if (stName.includes('finalizado') || stName.includes('terminado')) { } else if (stName.includes('finalizado') || stName.includes('terminado')) {
triggerWhatsAppEvent(req.user.accountId, id, 'wa_evt_survey'); triggerWhatsAppEvent(req.user.accountId, id, 'wa_evt_survey');
@@ -1007,10 +1003,14 @@ app.post("/services/manual-high", authMiddleware, async (req, res) => {
const { phone, name, address, description, guild_id, assigned_to, mode } = req.body; const { phone, name, address, description, guild_id, assigned_to, mode } = req.body;
const serviceRef = "MAN-" + Date.now().toString().slice(-6); const serviceRef = "MAN-" + Date.now().toString().slice(-6);
const rawData = { "Nombre Cliente": name, "Teléfono": phone, "Dirección": address, "Descripción": description, "guild_id": guild_id }; const rawData = { "Nombre Cliente": name, "Teléfono": phone, "Dirección": address, "Descripción": description, "guild_id": guild_id };
await pool.query(` const insert = await pool.query(`
INSERT INTO scraped_services (owner_id, provider, service_ref, raw_data, status, automation_status, assigned_to) INSERT INTO scraped_services (owner_id, provider, service_ref, raw_data, status, automation_status, assigned_to)
VALUES ($1, 'MANUAL', $2, $3, 'pending', $4, $5) VALUES ($1, 'MANUAL', $2, $3, 'pending', $4, $5) RETURNING id
`, [req.user.accountId, serviceRef, JSON.stringify(rawData), mode === 'auto' ? 'manual' : 'completed', mode === 'manual' ? assigned_to : null]); `, [req.user.accountId, serviceRef, JSON.stringify(rawData), mode === 'auto' ? 'manual' : 'completed', mode === 'manual' ? assigned_to : null]);
// Disparar Bienvenida / Alta
triggerWhatsAppEvent(req.user.accountId, insert.rows[0].id, 'wa_evt_welcome');
res.json({ ok: true }); res.json({ ok: true });
} catch (e) { res.status(500).json({ ok: false }); } } catch (e) { res.status(500).json({ ok: false }); }
}); });