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 ExcelJS from 'exceljs'; 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' } } }; // 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_corte'`, fields: '*' }); console.log(`${res.data.files.length} archivo(s) encontrados con el nombre "base_corte"`); return res.data.files; } async function obtenerDatosHoja(spreadsheetId) { const response = await sheets.spreadsheets.values.get({ spreadsheetId, range: 'A:F', // 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=9224%\'" 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=9224'", () => { resolve(); }); } }); } async function iniciarChrome(url) { return new Promise((resolve) => { const chromeProcess = exec( `start chrome ` + `--remote-debugging-port=9224 ` + `--no-sandbox ` + `--disable-setuid-sandbox ` + `--disable-background-timer-throttling ` + `--disable-renderer-backgrounding ` + `--disable-backgrounding-occluded-windows ` + `--user-data-dir="C:\\temp\\chrome_debug_temp_corte" ` + `"${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:9224/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 CORTE async function procesarCasoCorte(page, element, spreadsheetId, intentos = 1) { const MAX_INTENTOS = 3; try { console.log(` ⌛ Consultando caso Corte... (Intento ${intentos}/${MAX_INTENTOS})`); try { await page.goto(CONFIG.PLATFORMS.CORTE.URL, { waitUntil: 'domcontentloaded', timeout: 20000 }); } catch (navigationError) { console.log(' ⚠️ Error de navegación o campo no encontrado, reintentando...'); if (intentos < MAX_INTENTOS) { return await procesarCasoCorte(page, element, spreadsheetId, intentos + 1); } else { throw new Error('No se pudo acceder a la página después de varios intentos'); } } 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.'); } try { 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); } catch (error) { console.log('❌ No se pudo encontrar el campo de entrada de caso Corte.'); } await delay(2000); try { await page.waitForSelector('#btn_submit', { visible: true, timeout: 10000 }); await page.click('#btn_submit'); } catch (error) { console.log('❌ No se pudo encontrar el botón de consulta Corte.'); } await delay(1000); try { await delay(2000); const noResultsElement = await page.$x("//div[contains(text(), 'No case found for this A-Number.')]"); if (noResultsElement.length > 0) { console.log("⚠️ No case found for this A-Number"); await actualizarCelda( spreadsheetId, `C${element.fila}`, ['No case found for this A-Number'] ); return true; } } catch (error) { console.log('ℹ️ Case found, continuing with data extraction...'); } 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); await actualizarCelda( spreadsheetId, `C${element.fila}:F${element.fila}`, [box1, box2, box3, box4] ); } catch (error) { console.log('❌ No se pudo encontrar la información de la tarjeta.'); } return true; // Indica que el proceso fue exitoso } catch (error) { console.error(' ❌ Error procesando caso CORTE:', error); if (intentos < MAX_INTENTOS) { console.log(` 🔄 Reintentando... (${intentos}/${MAX_INTENTOS})`); return await procesarCasoCorte(page, element, spreadsheetId, intentos + 1); } else { console.log(' ⚠️ Se agotaron los intentos o error no recuperable'); await actualizarCelda( spreadsheetId, `C${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 platform_type = 'corte'; const platform = CONFIG.PLATFORMS[platform_type.toUpperCase()].URL; const datos = await obtenerDatosHoja(archivo.id); const filasAProcesar = datos.slice(1) .map((row, index) => ({ valor: row[0], // Columna A fila: index + 2, // Número de fila real ultimaConsulta: row[1], // Columna B columnaC: row[2] // Columna C })) .filter(row => { if (!row.columnaC) return true; // Si la columna C está vacía, procesar // Si la columna C no está vacía, verificar la fecha de última consulta en la columna B if (!row.ultimaConsulta) return true; // Si no hay fecha en B, procesar (aunque C no esté vacía) // Parsear la fecha en formato español 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(); // Calcular la diferencia en horas const diferenciaHoras = (ahora - fechaUltimaConsulta) / (1000 * 60 * 60); // Para fechas futuras, no procesar if (fechaUltimaConsulta > ahora) return false; return diferenciaHoras > 1; // Procesar solo si ha pasado más de 1 hora }); 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' }); // Update timestamp and clear status columns await Promise.all([ actualizarCelda( archivo.id, `B${element.fila}`, [fechaHoraActual] ), actualizarCelda( archivo.id, `C${element.fila}:F${element.fila}`, ['', '', '', ''] ) ]); const resultado = await procesarCasoCorte(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(); } // 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(); });