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: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, 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; // Validate that case number has 9 digits if (element.valor.length !== 9) { console.log('>> Invalid case number - must be 9 digits'); await actualizarCelda( spreadsheetId, `D${element.fila}`, ['Invalid case number - must contain exactly 9 digits'] ); return true; } 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); await delay(1000); await page.keyboard.press('Tab'); await delay(1000); await page.keyboard.press('Tab'); console.log('>> Se diligencio ID de caso'); } catch (error) { console.log('>> No se pudo encontrar el campo de entrada de caso Corte.'); } await delay(1000); try { // await page.waitForSelector('input[type="text"]', { timeout: 10000 }); await page.type('input[type="text"]', element.nacionalidad); console.log('>> Se diligencio nacionalidad'); } catch (error) { console.log('>> No se pudo encontrar el campo nacionalidad'); } await delay(1000); try { const elements = await page.$$('div[id^="react-select-2-option-"]'); for (const element of elements) { await element.click(); } console.log('>> Se dio click a la nacionalidad'); } catch (error) { console.log('>> No se pudo encontrar el campo nacionalidad'); } await delay(1000); 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(3000); try { await delay(3000); // Wait for the 'No case found' message // Check for "No case found" message const result = await page.evaluate(() => { const elements = document.querySelectorAll('div'); for (const el of elements) { if (el.textContent.includes('No case found for this A-Number.') || el.textContent.includes('Case information is unavailable.')) { return true; } } return false; }).catch(() => false); if (result) { console.log(">> No case found for this A-Number"); await actualizarCelda( spreadsheetId, `D${element.fila}`, ['No case found for this A-Number'] ); return true; } else { console.log('>> No error found'); try { const isCaptchaPresent = await page.evaluate(() => { const captchaFrames = document.querySelectorAll('iframe[src*="hcaptcha"]'); return Array.from(captchaFrames).some(frame => { return frame !== null && getComputedStyle(frame).display !== 'none'; }); }); if (isCaptchaPresent) { await delay(5000); console.log('>> hCaptcha detected'); await actualizarCelda( spreadsheetId, `D${element.fila}`, ['Captcha detected - Please try again later'] ); await delay(5000); return false; } else { console.log('>> No captcha detected'); } } catch (error) { console.error('>> Error checking for captcha:', error); } } } catch (error) { console.error('Error during case processing:', error); // Check for hCaptcha presence } 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, `D${element.fila}:G${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 todasLasFilas = datos.slice(1) .map((row, index) => ({ valor: row[0], // Columna A fila: index + 2, // Número de fila real nacionalidad: row[1], // Nacionalidad ultimaConsulta: row[2], // Columna C columnaD: row[3] // Columna D })); // Separate rows into three groups based on priority const filasVaciasD = todasLasFilas.filter(row => !row.columnaD); const filasConCaptcha = todasLasFilas.filter(row => row.columnaD && row.columnaD.includes('Captcha detected - Please try again later')); const filasConFechaB = todasLasFilas.filter(row => { if (!row.columnaD && !row.ultimaConsulta) return false; // Already handled by filasVaciasD or filasVaciasB if (!row.ultimaConsulta) return false; // Handled by filasVaciasB 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(); const diferenciaHoras = (ahora - fechaUltimaConsulta) / (1000 * 60 * 60); if (fechaUltimaConsulta > ahora) return false; // For future dates, return false to skip processing return diferenciaHoras > 1; // Process only if more than 1 hour has passed }); const filasVaciasB = todasLasFilas.filter(row => { return !row.ultimaConsulta && row.columnaD; // Empty F, but G is not empty }); // Sort rows with date in B by oldest date first filasConFechaB.sort((a, b) => { const [dateA, timeA] = a.ultimaConsulta.split(', '); const [dayA, monthA, yearA] = dateA.split('/'); const [hoursA, minutesA, secondsA] = timeA.split(':'); const fechaA = new Date(yearA, monthA - 1, dayA, hoursA, minutesA, secondsA); const [dateB, timeB] = b.ultimaConsulta.split(', '); const [dayB, monthB, yearB] = dateB.split('/'); const [hoursB, minutesB, secondsB] = timeB.split(':'); const fechaB = new Date(yearB, monthB - 1, dayB, hoursB, minutesB, secondsB); return fechaA - fechaB; // Ascending order (oldest first) }); // Combine the arrays in the specified priority order const filasOrdenadas = [...filasVaciasD, ...filasConCaptcha, ...filasConFechaB, ...filasVaciasB]; console.log(filasOrdenadas); console.log(`>> Total de filas a procesar: ${filasOrdenadas.length}`); if (filasOrdenadas.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 filasOrdenadas) { procesadas++; console.log(`\n>> Procesando fila ${element.fila} (${procesadas}/${filasOrdenadas.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, `C${element.fila}`, [fechaHoraActual] ), actualizarCelda( archivo.id, `D${element.fila}:G${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(); });