import path from 'path'; import { exec } from 'child_process'; import puppeteer from 'puppeteer-core'; import fetch from 'node-fetch'; import { google } from 'googleapis'; import { createWriteStream } from 'fs'; import fs from 'fs'; import { app } from 'electron'; console.log(app.getAppPath()); let url_cred = app.getAppPath().replace('app.asar', ''); url_cred = url_cred + '\\credenciales.json'; console.log(url_cred); // Configuración const CONFIG = { PATHS: { BASE_DIR: 'C:\\Users\\Drackxus\\Documents\\NODE\\migracion', ARCHIVOS: 'C:\\Users\\Drackxus\\Documents\\NODE\\migracion\\archivos', CREDENTIALS: url_cred }, DRIVE: { FOLDER_ID: '1bjEaxLFkv4toUt0SeUz_HqrvIWAI2Xv5', SCOPES: [ 'https://www.googleapis.com/auth/drive', 'https://www.googleapis.com/auth/spreadsheets' ] }, PLATFORMS: { USCIS: { URL: 'https://egov.uscis.gov/es', TYPE: 'uscis' } } }; // Inicialización de Google APIs const auth = new google.auth.GoogleAuth({ keyFile: CONFIG.PATHS.CREDENTIALS, scopes: CONFIG.DRIVE.SCOPES }); const sheets = google.sheets({ version: 'v4', auth }); const drive = google.drive({ version: 'v3', auth }); let browser; // Variable global para el navegador // Funciones de Google Sheets async function listarArchivosEnCarpeta(folderId) { const res = await drive.files.list({ q: `'${folderId}' in parents and mimeType='application/vnd.google-apps.spreadsheet' and name='base_uscis'`, fields: '*' }); console.log(`${res.data.files.length} archivo(s) encontrados con el nombre "base_uscis"`); return res.data.files; } async function obtenerDatosHoja(spreadsheetId) { const response = await sheets.spreadsheets.values.get({ spreadsheetId, range: 'A:H', // Ajusta el rango según tus necesidades }); return response.data.values; } async function actualizarCelda(spreadsheetId, range, values) { try { await sheets.spreadsheets.values.update({ spreadsheetId, range, valueInputOption: 'USER_ENTERED', requestBody: { values: [values] } }); await new Promise(resolve => setTimeout(resolve, 1000)); } catch (error) { console.error('Error al actualizar celda:', error.message); throw error; } } // Funciones de manejo del navegador async function cerrarChromeDebugging() { return new Promise((resolve) => { if (process.platform === 'win32') { exec('wmic process where "commandline like \'%--remote-debugging-port=9223%\'" call terminate', (error) => { if (error) { console.log('⚠️ No se encontraron instancias de Chrome debugging'); } else { console.log('🔒 Chrome debugging cerrado correctamente'); } resolve(); }); } else { exec("pkill -f 'chrome.*--remote-debugging-port=9223'", () => { resolve(); }); } }); } async function iniciarChrome(url) { return new Promise((resolve) => { const chromeProcess = exec( `start chrome ` + `--remote-debugging-port=9223 ` + `--no-sandbox ` + `--disable-setuid-sandbox ` + `--disable-background-timer-throttling ` + `--disable-renderer-backgrounding ` + `--disable-backgrounding-occluded-windows ` + `--user-data-dir="C:\\temp\\chrome_debug_temp_uscis" ` + `"${url}"`, (error) => { if (error) { console.log('⚠️ Error al iniciar Chrome:', error); } resolve(); } ); }); } async function initBrowser(url) { try { await cerrarChromeDebugging(); await new Promise(resolve => setTimeout(resolve, 2000)); console.log('🌐 Iniciando Chrome con debugging...'); await iniciarChrome(url); await new Promise(resolve => setTimeout(resolve, 8000)); // Aumentado a 8 segundos console.log('🔍 Intentando conectar con Chrome...'); const webSocketDebuggerUrl = await getWebSocketUrl(); console.log('✅ Conexión establecida con Chrome'); return await puppeteer.connect({ browserWSEndpoint: webSocketDebuggerUrl, defaultViewport: null }); } catch (error) { console.error('❌ Error al inicializar el navegador:', error); throw error; } } async function getWebSocketUrl() { let retries = 10; // Aumentado a 15 intentos while (retries > 0) { try { const response = await fetch('http://localhost:9223/json/version'); if (!response.ok) throw new Error('No se pudo obtener la versión del navegador'); const json = await response.json(); if (json.webSocketDebuggerUrl) { return json.webSocketDebuggerUrl; } throw new Error('URL de WebSocket no encontrada'); } catch (err) { console.log(`🔄 Reintentando conexión a Chrome... (intentos restantes: ${retries})`); await new Promise(resolve => setTimeout(resolve, 2000)); // Aumentado a 4 segundos retries--; } } throw new Error('No se pudo conectar a Chrome después de varios intentos'); } // Funciones de utilidad async function delay(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } // Funciones de procesamiento USCIS async function procesarCasoUSCIS(page, element, spreadsheetId, intentos = 1) { const MAX_INTENTOS = 3; try { console.log(`⌛ Consultando caso USCIS... (Intento ${intentos}/${MAX_INTENTOS})`); try { await page.goto(CONFIG.PLATFORMS.USCIS.URL, { waitUntil: 'domcontentloaded', timeout: 20000 }); await page.waitForSelector('#receipt_number', { timeout: 10000 }); } catch (navigationError) { console.log(' ⚠️ Error de navegación o campo no encontrado, reintentando...'); if (intentos < MAX_INTENTOS) { return await procesarCasoUSCIS(page, element, spreadsheetId, intentos + 1); } else { throw new Error('No se pudo acceder a la página después de varios intentos'); } } // Intentar llenar el campo y hacer clic await page.type('#receipt_number', element.valor); await delay(1000); await page.click('::-p-text(Verifique Estatus)'); await delay(2000); // Esperar a que se complete la acción // page.waitForSelector('.errorMessage', { timeout: 5000 }), // page.waitForSelector('.conditionalLanding', { timeout: 5000 }) // Verificar el resultado const errorMessage = await page.$('.errorMessage', { timeout: 5000 }); if (errorMessage) { console.log(' ⚠️ Caso no válido'); await actualizarCelda( spreadsheetId, `G${element.fila}`, ['El número de recibo ingresado no es válido, intente nuevamente.'] ); } else { try { const [titulo, descripcion] = await Promise.all([ page.$eval('.conditionalLanding h2', el => el.innerText), page.$eval('.conditionalLanding p', el => el.innerText) ]); console.log(' ✅ Información obtenida'); console.log(` 📌 Estado: ${titulo}`); await actualizarCelda( spreadsheetId, `G${element.fila}:H${element.fila}`, [titulo, descripcion] ); } catch (dataError) { console.error(' ❌ Error al obtener datos de la página:', dataError); throw dataError; } } await delay(1000); return true; // Indica que el proceso fue exitoso } catch (error) { console.error(' ❌ Error procesando caso USCIS:', error); if (intentos < MAX_INTENTOS) { console.log(` 🔄 Reintentando... (${intentos}/${MAX_INTENTOS})`); return await procesarCasoUSCIS(page, element, spreadsheetId, intentos + 1); } else { console.log(' ⚠️ Se agotaron los intentos o error no recuperable'); await actualizarCelda( spreadsheetId, `G${element.fila}`, ['Error al procesar el caso. Por favor, intente más tarde.'] ); return false; // Indica que el proceso falló } } } // Modificar la función procesarArchivo para manejar mejor la reconexión de Chrome async function procesarArchivo(archivo) { console.log('\n🔍 Analizando archivo:', archivo.name); const datos = await obtenerDatosHoja(archivo.id); const platform_type = 'uscis'; const platform = CONFIG.PLATFORMS[platform_type.toUpperCase()].URL; // const filasAProcesar = datos.slice(1).map((row, index) => ({ // valor: row[4], // fila: index + 2, // tieneValor: !row[5], // ultimaConsulta: row[5] || '' // })).filter(row => { // if (!row.ultimaConsulta) return true; // const fechaUltimaConsulta = new Date(row.ultimaConsulta); // const ahora = new Date(); // const diferenciaHoras = (ahora - fechaUltimaConsulta) / (1000 * 60 * 60); // return diferenciaHoras > 1; // }); const filasAProcesar = datos.slice(1) .map((row, index) => ({ valor: row[4], // Columna 5 (Valor a consultar) fila: index + 2, // Número de fila real ultimaConsulta: row[5], // Columna 6 (Fecha de última consulta) resultado: row[6] // Columna 7 (Resultado) })) .filter(row => { // Priorizar si la columna G (resultado) está vacía if (!row.resultado) return true; // Si la columna G no está vacía, verificar la fecha en la columna F if (!row.ultimaConsulta) return true; // Si no hay fecha en F, también se procesa (aunque G no esté vacía) // Parse date in Spanish format DD/MM/YYYY const [date, time] = row.ultimaConsulta.split(', '); const [day, month, year] = date.split('/'); const [hours, minutes, seconds] = time.split(':'); const fechaUltimaConsulta = new Date(year, month - 1, day, hours, minutes, seconds); const ahora = new Date(); // Calculate hours difference const diferenciaHoras = (ahora - fechaUltimaConsulta) / (1000 * 60 * 60); // For future dates, return false to skip processing if (fechaUltimaConsulta > ahora) return false; return diferenciaHoras > 1; // Process only if more than 1 hour has passed }); console.log(filasAProcesar); console.log(`📊 Total de filas a procesar: ${filasAProcesar.length}`); if (filasAProcesar.length > 0) { console.log('🌐 Iniciando navegador...'); let browser = await initBrowser(platform); let page = (await browser.pages())[0]; let fallosConsecutivos = 0; const MAX_FALLOS_CONSECUTIVOS = 3; let procesadas = 0; for (const element of filasAProcesar) { procesadas++; console.log(`\n⏳ Procesando fila ${element.fila} (${procesadas}/${filasAProcesar.length})`); console.log(`📝 Valor a consultar: ${element.valor}`); // Guardar la fecha y hora actual const fechaHoraActual = new Date().toLocaleString('es-ES', { year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', second: '2-digit' }); // Actualizar la marca de tiempo en la columna F y limpiar las columnas G y H await Promise.all([ actualizarCelda( archivo.id, `F${element.fila}`, [fechaHoraActual] ), actualizarCelda( archivo.id, `G${element.fila}:H${element.fila}`, ['', ''] ) ]); const resultado = await procesarCasoUSCIS(page, element, archivo.id); if (!resultado) { fallosConsecutivos++; if (fallosConsecutivos >= MAX_FALLOS_CONSECUTIVOS) { console.log('🔄 Reiniciando navegador debido a fallos consecutivos...'); await browser.close(); await cerrarChromeDebugging(); await delay(2000); browser = await initBrowser(platform); page = (await browser.pages())[0]; fallosConsecutivos = 0; } } else { fallosConsecutivos = 0; } } console.log('\n✅ Proceso completado para el archivo:', archivo.name); await browser.close(); } else { console.log('ℹ️ No hay filas para procesar en este archivo'); } } // Función para cerrar el navegador y la aplicación async function cerrarTodo() { if (browser) { await browser.close(); // Cerrar el navegador si está abierto } console.log('🔄 Cerrando Chrome debugging y finalizando...'); await cerrarChromeDebugging(); process.exit(); app.quit(); // Cerrar la aplicación Electron } // Manejar la señal SIGINT (Ctrl + C) process.on('SIGINT', async () => { console.log('Recibida señal de interrupción (Ctrl + C)'); await cerrarTodo(); }); // Función principal async function procesarArchivos() { try { console.log('🚀 Iniciando proceso...'); const archivos = await listarArchivosEnCarpeta(CONFIG.DRIVE.FOLDER_ID); console.log(`📁 Total de archivos encontrados: ${archivos.length}`); let procesados = 0; for (const archivo of archivos) { procesados++; console.log(`\n📌 Procesando archivo ${procesados}/${archivos.length}: ${archivo.name}`); await procesarArchivo(archivo); } console.log('\n🎉 Proceso completado exitosamente!'); } catch (error) { console.error('❌ Error en el proceso:', error); } finally { await cerrarTodo(); // Asegúrate de cerrar todo al final } } // Ejecutar el proceso app.on('ready', () => { console.log('Aplicación Electron está lista y en ejecución sin ventana.'); procesarArchivos(); });