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: { CORTE: { URL: 'https://acis.eoir.justice.gov/en/', TYPE: 'corte' }, 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'`, fields: '*' }); console.log(`${res.data.files.length} archivos encontrados`); return res.data.files; } async function obtenerDatosHoja(spreadsheetId) { const response = await sheets.spreadsheets.values.get({ spreadsheetId, range: 'A:G', // 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, 3000)); } catch (error) { console.error('Error al actualizar celda:', error.message); throw error; } } // Funciones de manejo de archivos async function descargarArchivo(fileId, fileName) { const filePath = path.join(CONFIG.PATHS.ARCHIVOS, fileName); const res = await drive.files.get( { fileId, alt: 'media' }, { responseType: 'stream' } ); return new Promise((resolve, reject) => { const dest = createWriteStream(filePath); res.data.pipe(dest); dest.on('finish', () => resolve(filePath)); dest.on('error', reject); }); } async function subirArchivo(filePath, fileId) { const media = { mimeType: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', body: fs.createReadStream(filePath) }; try { const res = await drive.files.update({ fileId: fileId, media: media, fields: 'id, name' }); console.log(`✅ Archivo actualizado: ${res.data.name}`); return res.data.id; } catch (error) { console.error('❌ Error al actualizar archivo:', 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 --user-data-dir="C:\\temp\\chrome_debug_temp" "${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 = 15; // 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, 4000)); // Aumentado a 4 segundos retries--; } } throw new Error('No se pudo conectar a Chrome después de varios intentos'); } // Funciones de procesamiento de Excel async function procesarExcel(filePath, cell, platformType) { const workbook = new ExcelJS.Workbook(); await workbook.xlsx.readFile(filePath); const worksheet = workbook.getWorksheet(1); let dataRows = []; worksheet.eachRow((row, rowNumber) => { if (rowNumber > 1) { const cellF = row.getCell(cell).value; dataRows.push({ valor, coordenada: row.number }); // if (!cellF && cellF !== '') { // const valor = row.getCell(platformType === 'uscis' ? 5 : 1).value; // dataRows.push({ valor, coordenada: row.number }); // } } }); return { dataRows, workbook }; } // 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})`); // Esperar a que el elemento esté disponible try { await page.waitForSelector('#receipt_number', { timeout: 5000 }); } catch (selectorError) { console.log(' ⚠️ No se encontró el campo de recibo, reiniciando navegador...'); if (intentos < MAX_INTENTOS) { // Cerrar el navegador actual const browser = page.browser(); await browser.close(); await cerrarChromeDebugging(); await delay(1000); // Iniciar nuevo navegador const newBrowser = await initBrowser(CONFIG.PLATFORMS.USCIS.URL); const [newPage] = await newBrowser.pages(); // Reintentar con la nueva página return await procesarCasoUSCIS(newPage, element, spreadsheetId, intentos + 1); } else { throw new Error('No se pudo encontrar el campo de recibo después de varios intentos'); } } // Intentar llenar el campo y hacer clic await page.type('#receipt_number', element.valor); await page.click('::-p-text(Verifique Estatus)'); await page.waitForFunction(() => { const input = document.querySelector('#receipt_number'); return input && input.value === ''; }, { timeout: 2000 }); try { await page.waitForSelector('.errorMessage', { timeout: 2000 }); console.log(' ⚠️ Caso no válido'); await actualizarCelda( spreadsheetId, `F${element.fila}`, ['El número de recibo ingresado no es válido, intente nuevamente.'] ); } catch { const titulo = await page.$eval('.conditionalLanding h2', el => el.innerText); const descripcion = await page.$eval('.conditionalLanding p', el => el.innerText); console.log(' ✅ Información obtenida'); console.log(` 📌 Estado: ${titulo}`); await actualizarCelda( spreadsheetId, `F${element.fila}:G${element.fila}`, [titulo, descripcion] ); } } catch (error) { console.error(' ❌ Error procesando caso USCIS:', error); if (intentos < MAX_INTENTOS) { console.log(` 🔄 Reiniciando navegador y reintentando... (${intentos}/${MAX_INTENTOS})`); try { // Cerrar el navegador actual const browser = page.browser(); await browser.close(); await cerrarChromeDebugging(); await delay(2000); // Iniciar nuevo navegador const newBrowser = await initBrowser(CONFIG.PLATFORMS.USCIS.URL); const [newPage] = await newBrowser.pages(); // Reintentar con la nueva página return await procesarCasoUSCIS(newPage, element, spreadsheetId, intentos + 1); } catch (retryError) { console.error(' ❌ Error al reiniciar navegador:', retryError); } } else { console.log(' ⚠️ Se agotaron los intentos o error no recuperable'); await actualizarCelda( spreadsheetId, `F${element.fila}`, ['Error al procesar el caso. Por favor, intente más tarde.'] ); } } } // Modificar la función procesarArchivo para manejar el nuevo browser async function procesarArchivo(archivo) { console.log('\n🔍 Analizando archivo:', archivo.name); const datos = await obtenerDatosHoja(archivo.id); const platform_type = archivo.name.includes('corte') ? 'corte' : 'uscis'; const platform = CONFIG.PLATFORMS[platform_type.toUpperCase()].URL; // const filasAProcesar = datos.slice(1).map((row, index) => ({ // valor: platform_type === 'uscis' ? row[4] : row[0], // fila: index + 2, // tieneValor: platform_type === 'uscis' ? !row[5] : !row[1] // })).filter(row => row.tieneValor); const filasAProcesar = datos.slice(1).map((row, index) => ({ valor: platform_type === 'uscis' ? row[4] : row[0], fila: index + 2, tieneValor: platform_type === 'uscis' ? !row[5] : !row[1] })); console.log(`📊 Total de filas a procesar: ${filasAProcesar.length}`); if (filasAProcesar.length > 0) { console.log('🌐 Iniciando navegador...'); let browser = await initBrowser(platform); const [page] = await browser.pages(); 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}`); if (platform_type === 'uscis') { await procesarCasoUSCIS(page, element, archivo.id); } else { await procesarCasoCorte(page, element, archivo.id); } } 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 esperar y obtener resultados de la Corte async function esperarYObtenerResultadoCorte(page, element, spreadsheetId, intentos = 1) { const MAX_INTENTOS = 3; // Número máximo de intentos try { // Esperar a que aparezca la tarjeta de información await page.waitForSelector('.caseInfoCard', { timeout: 8000 }); console.log(" ✅ Información encontrada"); // Obtener todos los elementos de información const elements = await page.$$eval('div.caseInfoCard', nodes => nodes.map(el => el.innerText.replace(/\n/g, '')) ); // Extraer la información relevante const [box1, box2, box3, box4] = elements; console.log(' 📋 Información obtenida:'); console.log(' Box 1:', box1); console.log(' Box 2:', box2); console.log(' Box 3:', box3); console.log(' Box 4:', box4); return [box1, box2, box3, box4]; } catch (error) { if (error.name === 'TimeoutError') { console.log(` ⚠️ No se encontró información después de 8 segundos (Intento ${intentos}/${MAX_INTENTOS})`); if (intentos < MAX_INTENTOS) { console.log(' 🔄 Reintentando proceso...'); await page.goto(CONFIG.PLATFORMS.CORTE.URL); await delay(2000); // Reiniciar el proceso completo try { await page.waitForSelector('button.btn', { visible: true, timeout: 2000 }); await delay(2000); await page.click('button.btn'); await page.waitForSelector('.codeInput > input:nth-child(1)', { visible: true, timeout: 10000 }); await delay(2000); await page.type('.codeInput > input:nth-child(1)', element.valor); await page.waitForSelector('#btn_submit', { visible: true, timeout: 10000 }); await page.click('#btn_submit'); // Llamada recursiva con incremento de intentos return await esperarYObtenerResultadoCorte(page, element, spreadsheetId, intentos + 1); } catch (retryError) { console.error(' ❌ Error en el reintento:', retryError); } } return ["", "", "", ""]; } throw error; } } // Modificar la función procesarCasoCorte para pasar los parámetros necesarios async function procesarCasoCorte(page, element, spreadsheetId) { try { console.log(' ⌛ Consultando caso Corte...'); try { await page.waitForSelector('button.btn', { visible: true, timeout: 2000 }); await delay(2000); await page.click('button.btn'); } catch (error) { console.log(' ❌ No se pudo encontrar el botón de caso Corte.'); } await page.waitForSelector('.codeInput > input:nth-child(1)', { visible: true, timeout: 10000 }); await delay(2000); await page.type('.codeInput > input:nth-child(1)', element.valor); await page.waitForSelector('#btn_submit', { visible: true, timeout: 10000 }); await page.click('#btn_submit'); // Pasar los parámetros necesarios para el reintento const resultado = await esperarYObtenerResultadoCorte(page, element, spreadsheetId); await actualizarCelda(spreadsheetId, `B${element.fila}:E${element.fila}`, resultado); console.log(' ✅ Información actualizada en la hoja'); await page.goto(CONFIG.PLATFORMS.CORTE.URL); } catch (error) { console.error(' ❌ Error procesando caso Corte:', error); try { console.log(' 🔄 Intentando recargar la página...'); await delay(3000); await page.goto(CONFIG.PLATFORMS.CORTE.URL); await delay(3000); } catch (navError) { console.error(' ❌ Error al recargar la página:', navError); } } } // 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(); } // 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(); });