Actualizar robot.js

This commit is contained in:
2026-02-19 22:14:25 +00:00
parent 63a66a1e35
commit 054b7529df

View File

@@ -77,17 +77,29 @@ function clasificarGremio(descripcion, gremiosActivosDB) {
return null;
}
// Función auxiliar para reintentar la navegación si hay fallo de red
async function gotoWithRetry(page, url, retries = 3) {
for (let i = 0; i < retries; i++) {
try {
// Usamos domcontentloaded para que no se quede colgado esperando imágenes o scripts pesados
await page.goto(url, { waitUntil: 'domcontentloaded', timeout: 45000 });
return; // Si funciona, salimos de la función
} catch (e) {
if (i === retries - 1) throw e; // Si es el último intento, lanza el error
console.log(`⚠️ Fallo de red detectado al ir a ${url}. Reintentando (${i + 1}/${retries})...`);
await page.waitForTimeout(3000); // Esperamos 3 segundos antes de volver a intentar
}
}
}
async function main() {
console.log("🤖 ROBOT MODO: CIRUJANO + ASPIRADORA + CLASIFICADOR PRO");
while (true) {
const client = await pool.connect();
try {
// 1. CARGAMOS LOS GREMIOS ACTUALES DE LA BASE DE DATOS
const gremiosRes = await client.query("SELECT id, name FROM guilds");
const gremiosDB = gremiosRes.rows;
// ACTUALIZACIÓN DE ESTRUCTURA: Aseguramos que exista is_urgent
await client.query(`
DO $$ BEGIN
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name='scraped_services' AND column_name='is_urgent') THEN
@@ -101,7 +113,6 @@ async function main() {
let password = Buffer.from(cred.password_hash, 'base64').toString('utf-8');
console.log(`\n🔄 Procesando ${cred.provider.toUpperCase()}...`);
// 2. PASAMOS LOS GREMIOS AL SCRAPER
if (cred.provider === 'multiasistencia') {
await runMultiasistencia(cred.owner_id, cred.username, password, gremiosDB);
} else if (cred.provider === 'homeserve') {
@@ -119,56 +130,49 @@ async function main() {
}
// ==========================================
// 🏥 MULTIASISTENCIA (CON INTELIGENCIA Y PAGINACIÓN REAL)
// 🏥 MULTIASISTENCIA (CON REINTENTOS ANTI-CAÍDAS)
// ==========================================
async function runMultiasistencia(ownerId, user, pass, gremiosDB) {
// ARGUMENTOS DE SEGURIDAD CONTRA CRASHEOS EN LINUX/DOCKER
const browser = await chromium.launch({
headless: HEADLESS,
args: [
'--no-sandbox',
'--disable-setuid-sandbox',
'--disable-dev-shm-usage', // Evita que se quede sin memoria compartida (causa del SIGSEGV)
'--disable-dev-shm-usage',
'--disable-gpu'
]
});
const context = await browser.newContext();
const page = await context.newPage();
try {
await page.goto('https://web.multiasistencia.com/w3multi/acceso.php', { timeout: 60000 });
// INICIO DE SESIÓN CON REINTENTOS
await gotoWithRetry(page, 'https://web.multiasistencia.com/w3multi/acceso.php');
await page.fill('input[name="usuario"]', user);
await page.fill('input[type="password"]', pass);
await page.click('input[type="submit"]');
await page.waitForTimeout(4000);
await page.goto('https://web.multiasistencia.com/w3multi/frepasos_new.php?refresh=1');
// ENTRAR AL BUZÓN CON REINTENTOS
await gotoWithRetry(page, 'https://web.multiasistencia.com/w3multi/frepasos_new.php?refresh=1');
await page.waitForTimeout(3000);
// --- NUEVO: FORZAR RECARGA DE FORMA SEGURA (SIN HACER CLIC FÍSICO) ---
console.log("🔄 [Multi] Forzando recarga segura mediante script interno...");
try {
await page.evaluate(() => {
// Llamamos a la función nativa de la web para recargar sin reventar el motor gráfico
if (typeof refrescar === 'function') {
refrescar();
}
if (typeof refrescar === 'function') refrescar();
});
// Esperamos a que la red deje de cargar datos
await page.waitForLoadState('networkidle');
await page.waitForTimeout(3000);
} catch (e) {
console.log("⚠️ [Multi] No se pudo ejecutar la recarga (puede que ya estuvieran cargados).");
}
// ----------------------------------------------------------
} catch (e) {}
// --- BUCLE DE PAGINACIÓN INTELIGENTE ---
// --- BUCLE DE PAGINACIÓN ---
let todosExpedientes = new Set();
let paginaActual = 1;
while (true) {
console.log(`📄 [Multi] Escaneando página ${paginaActual}...`);
// 1. Extraer expedientes de la página actual
const expedientesPagina = await page.evaluate(() => {
const links = Array.from(document.querySelectorAll('a[href*="reparacion="]'));
return links.map(a => a.href.match(/reparacion=(\d+)/)?.[1]).filter(Boolean);
@@ -176,15 +180,10 @@ async function runMultiasistencia(ownerId, user, pass, gremiosDB) {
expedientesPagina.forEach(ref => todosExpedientes.add(ref));
// 2. Buscar el botón "Página siguiente"
const hasNextPage = await page.evaluate(() => {
const links = Array.from(document.querySelectorAll('a.lnkheader'));
const nextBtn = links.find(a => a.innerText.trim() === 'Página siguiente');
if (nextBtn) {
nextBtn.click();
return true;
}
if (nextBtn) { nextBtn.click(); return true; }
return false;
});
@@ -192,7 +191,6 @@ async function runMultiasistencia(ownerId, user, pass, gremiosDB) {
await page.waitForLoadState('networkidle');
await page.waitForTimeout(2500);
paginaActual++;
if(paginaActual > 15) {
console.log("⚠️ [Multi] Límite de 15 páginas alcanzado por seguridad.");
break;
@@ -206,14 +204,12 @@ async function runMultiasistencia(ownerId, user, pass, gremiosDB) {
const expedientes = Array.from(todosExpedientes);
console.log(`✅ [Multi] Total expedientes detectados: ${expedientes.length}`);
// --- ARCHIVADO ---
if (expedientes.length > 0) {
await syncAndArchive(ownerId, 'multiasistencia', expedientes);
}
// --- EXTRACCIÓN DE DATOS ---
for (const ref of expedientes) {
await page.goto(`https://web.multiasistencia.com/w3multi/repasos1.php?reparacion=${ref}`, { waitUntil: 'domcontentloaded' });
await gotoWithRetry(page, `https://web.multiasistencia.com/w3multi/repasos1.php?reparacion=${ref}`);
await page.waitForTimeout(1500);
let scrapData = null;
@@ -274,10 +270,9 @@ async function runMultiasistencia(ownerId, user, pass, gremiosDB) {
}
// ==========================================
// 🧹 HOMESERVE (CON INTELIGENCIA)
// 🧹 HOMESERVE
// ==========================================
async function runHomeserve(ownerId, user, pass, gremiosDB) {
// MISMOS ARGUMENTOS DE SEGURIDAD AQUÍ PARA EVITAR CRASHES
const browser = await chromium.launch({
headless: HEADLESS,
args: [
@@ -290,7 +285,7 @@ async function runHomeserve(ownerId, user, pass, gremiosDB) {
const page = await browser.newPage();
try {
console.log("🌍 [HomeServe] Entrando...");
await page.goto('https://www.clientes.homeserve.es/cgi-bin/fccgi.exe?w3exec=PROF_PASS', { timeout: 60000 });
await gotoWithRetry(page, 'https://www.clientes.homeserve.es/cgi-bin/fccgi.exe?w3exec=PROF_PASS');
if (await page.isVisible('input[name="CODIGO"]')) {
await page.fill('input[name="CODIGO"]', user);
@@ -299,7 +294,7 @@ async function runHomeserve(ownerId, user, pass, gremiosDB) {
await page.waitForTimeout(5000);
}
await page.goto('https://www.clientes.homeserve.es/cgi-bin/fccgi.exe?w3exec=lista_servicios_total');
await gotoWithRetry(page, 'https://www.clientes.homeserve.es/cgi-bin/fccgi.exe?w3exec=lista_servicios_total');
await page.waitForTimeout(3000);
const refs = await page.evaluate(() => {
@@ -319,7 +314,7 @@ async function runHomeserve(ownerId, user, pass, gremiosDB) {
console.log(`🔍 [HomeServe] ${refs.length} expedientes detectados.`);
for (const ref of refs) {
await page.goto(`https://www.clientes.homeserve.es/cgi-bin/fccgi.exe?w3exec=ver_servicioencurso&Servicio=${ref}`);
await gotoWithRetry(page, `https://www.clientes.homeserve.es/cgi-bin/fccgi.exe?w3exec=ver_servicioencurso&Servicio=${ref}`);
await page.waitForTimeout(2000);
const scrapData = await page.evaluate(() => {
@@ -364,7 +359,6 @@ async function runHomeserve(ownerId, user, pass, gremiosDB) {
finally { await browser.close(); }
}
// --- NUEVA FUNCIÓN: SINCRONIZACIÓN Y ARCHIVADO ---
async function syncAndArchive(ownerId, provider, currentWebRefs) {
const client = await pool.connect();
try {
@@ -399,11 +393,9 @@ async function syncAndArchive(ownerId, provider, currentWebRefs) {
async function saveServiceToDB(ownerId, provider, ref, data) {
console.log(`💾 Guardando/Actualizando ${provider.toUpperCase()} ${ref}...`);
// CORRECCIÓN URGENCIA: Miramos en el JSON y convertimos a booleano real
const isUrgent = (data['Urgente'] && (data['Urgente'].toLowerCase().trim() === 'sí' || data['Urgente'].toLowerCase().trim() === 'si')) ? true : false;
// Al añadir is_urgent en la consulta, sincronizamos la columna física de la base de datos
// Aquí está la solución al problema del tilde ("sí" vs "si") que te comenté antes
const isUrgent = (data['Urgente'] && (data['Urgente'].toLowerCase().trim() === 'sí' || data['Urgente'].toLowerCase().trim() === 'si')) ? true : false;
await pool.query(`
INSERT INTO scraped_services (owner_id, provider, service_ref, raw_data, status, is_urgent)
VALUES ($1, $2, $3, $4, 'pending', $5)