diff --git a/corte.mjs b/corte.mjs new file mode 100644 index 0000000..b50841b --- /dev/null +++ b/corte.mjs @@ -0,0 +1,438 @@ +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(); +}); diff --git a/main.mjs b/main.mjs deleted file mode 100644 index 6f0283c..0000000 --- a/main.mjs +++ /dev/null @@ -1,506 +0,0 @@ -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(); -}); diff --git a/package-lock.json b/package-lock.json index c913069..258efda 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,17 +1,17 @@ { - "name": "migracion", - "version": "0.0.10", + "name": "migracion-corte", + "version": "0.0.13", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "migracion", - "version": "0.0.10", + "name": "migracion-corte", + "version": "0.0.13", "license": "ISC", "dependencies": { "googleapis": "^144.0.0", "node-fetch": "^3.3.2", - "puppeteer": "^24.1.1", + "puppeteer": "^24.9.0", "puppeteer-core": "^24.2.0" }, "devDependencies": { @@ -590,18 +590,17 @@ } }, "node_modules/@puppeteer/browsers": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.7.0.tgz", - "integrity": "sha512-bO61XnTuopsz9kvtfqhVbH6LTM1koxK0IlBR+yuVrM2LB7mk8+5o1w18l5zqd5cs8xlf+ntgambqRqGifMDjog==", + "version": "2.10.5", + "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.10.5.tgz", + "integrity": "sha512-eifa0o+i8dERnngJwKrfp3dEq7ia5XFyoqB17S4gK8GhsQE4/P8nxOfQSE0zQHxzzLo/cmF+7+ywEQ7wK7Fb+w==", "license": "Apache-2.0", "dependencies": { - "debug": "^4.4.0", + "debug": "^4.4.1", "extract-zip": "^2.0.1", "progress": "^2.0.3", "proxy-agent": "^6.5.0", - "semver": "^7.6.3", - "tar-fs": "^3.0.6", - "unbzip2-stream": "^1.4.3", + "semver": "^7.7.2", + "tar-fs": "^3.0.8", "yargs": "^17.7.2" }, "bin": { @@ -1280,6 +1279,7 @@ "version": "5.7.1", "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, "funding": [ { "type": "github", @@ -1545,13 +1545,13 @@ } }, "node_modules/chromium-bidi": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-1.1.0.tgz", - "integrity": "sha512-HislCEczCuamWm3+55Lig9XKmMF13K+BGKum9rwtDAzgUAHT4h5jNwhDmD4U20VoVUG8ujnv9UZ89qiIf5uF8w==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-5.1.0.tgz", + "integrity": "sha512-9MSRhWRVoRPDG0TgzkHrshFSJJNZzfY5UFqUMuksg7zL1yoZIZ3jLB0YAgHclbiAxPI86pBnwDX1tbzoiV8aFw==", "license": "Apache-2.0", "dependencies": { - "mitt": "3.0.1", - "zod": "3.24.1" + "mitt": "^3.0.1", + "zod": "^3.24.1" }, "peerDependencies": { "devtools-protocol": "*" @@ -1895,9 +1895,9 @@ } }, "node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", "license": "MIT", "dependencies": { "ms": "^2.1.3" @@ -2041,9 +2041,9 @@ "optional": true }, "node_modules/devtools-protocol": { - "version": "0.0.1380148", - "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1380148.tgz", - "integrity": "sha512-1CJABgqLxbYxVI+uJY/UDUHJtJ0KZTSjNYJYKqd9FRoXT33WDakDHNxRapMEgzeJ/C3rcs01+avshMnPmKQbvA==", + "version": "0.0.1439962", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1439962.tgz", + "integrity": "sha512-jJF48UdryzKiWhJ1bLKr7BFWUQCEIT5uCNbDLqkQJBtkFxYzILJH44WN0PDKMIlGDN7Utb8vyUY85C3w4R/t2g==", "license": "BSD-3-Clause" }, "node_modules/dir-compare": { @@ -3295,6 +3295,7 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, "funding": [ { "type": "github", @@ -4575,17 +4576,17 @@ } }, "node_modules/puppeteer": { - "version": "24.1.1", - "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-24.1.1.tgz", - "integrity": "sha512-fuhceZ5HZuDXVuaMIRxUuDHfCJLmK0pXh8FlzVQ0/+OApStevxZhU5kAVeYFOEqeCF5OoAyZjcWbdQK27xW/9A==", + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-24.9.0.tgz", + "integrity": "sha512-L0pOtALIx8rgDt24Y+COm8X52v78gNtBOW6EmUcEPci0TYD72SAuaXKqasRIx4JXxmg2Tkw5ySKcpPOwN8xXnQ==", "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { - "@puppeteer/browsers": "2.7.0", - "chromium-bidi": "1.1.0", + "@puppeteer/browsers": "2.10.5", + "chromium-bidi": "5.1.0", "cosmiconfig": "^9.0.0", - "devtools-protocol": "0.0.1380148", - "puppeteer-core": "24.1.1", + "devtools-protocol": "0.0.1439962", + "puppeteer-core": "24.9.0", "typed-query-selector": "^2.12.0" }, "bin": { @@ -4596,69 +4597,17 @@ } }, "node_modules/puppeteer-core": { - "version": "24.2.0", - "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-24.2.0.tgz", - "integrity": "sha512-e4A4/xqWdd4kcE6QVHYhJ+Qlx/+XpgjP4d8OwBx0DJoY/nkIRhSgYmKQnv7+XSs1ofBstalt+XPGrkaz4FoXOQ==", + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-24.9.0.tgz", + "integrity": "sha512-HFdCeH/wx6QPz8EncafbCqJBqaCG1ENW75xg3cLFMRUoqZDgByT6HSueiumetT2uClZxwqj0qS4qMVZwLHRHHw==", + "license": "Apache-2.0", "dependencies": { - "@puppeteer/browsers": "2.7.1", - "chromium-bidi": "1.2.0", - "debug": "^4.4.0", - "devtools-protocol": "0.0.1402036", + "@puppeteer/browsers": "2.10.5", + "chromium-bidi": "5.1.0", + "debug": "^4.4.1", + "devtools-protocol": "0.0.1439962", "typed-query-selector": "^2.12.0", - "ws": "^8.18.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/puppeteer-core/node_modules/@puppeteer/browsers": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.7.1.tgz", - "integrity": "sha512-MK7rtm8JjaxPN7Mf1JdZIZKPD2Z+W7osvrC1vjpvfOX1K0awDIHYbNi89f7eotp7eMUn2shWnt03HwVbriXtKQ==", - "dependencies": { - "debug": "^4.4.0", - "extract-zip": "^2.0.1", - "progress": "^2.0.3", - "proxy-agent": "^6.5.0", - "semver": "^7.7.0", - "tar-fs": "^3.0.8", - "yargs": "^17.7.2" - }, - "bin": { - "browsers": "lib/cjs/main-cli.js" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/puppeteer-core/node_modules/chromium-bidi": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-1.2.0.tgz", - "integrity": "sha512-XtdJ1GSN6S3l7tO7F77GhNsw0K367p0IsLYf2yZawCVAKKC3lUvDhPdMVrB2FNhmhfW43QGYbEX3Wg6q0maGwQ==", - "dependencies": { - "mitt": "^3.0.1", - "zod": "^3.24.1" - }, - "peerDependencies": { - "devtools-protocol": "*" - } - }, - "node_modules/puppeteer-core/node_modules/devtools-protocol": { - "version": "0.0.1402036", - "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1402036.tgz", - "integrity": "sha512-JwAYQgEvm3yD45CHB+RmF5kMbWtXBaOGwuxa87sZogHcLCv8c/IqnThaoQ1y60d7pXWjSKWQphPEc+1rAScVdg==" - }, - "node_modules/puppeteer/node_modules/puppeteer-core": { - "version": "24.1.1", - "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-24.1.1.tgz", - "integrity": "sha512-7FF3gq6bpIsbq3I8mfbodXh3DCzXagoz3l2eGv1cXooYU4g0P4mcHQVHuBD4iSZPXNg8WjzlP5kmRwK9UvwF0A==", - "dependencies": { - "@puppeteer/browsers": "2.7.0", - "chromium-bidi": "1.1.0", - "debug": "^4.4.0", - "devtools-protocol": "0.0.1380148", - "typed-query-selector": "^2.12.0", - "ws": "^8.18.0" + "ws": "^8.18.2" }, "engines": { "node": ">=18" @@ -4892,9 +4841,9 @@ "dev": true }, "node_modules/semver": { - "version": "7.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", - "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -5348,12 +5297,6 @@ "b4a": "^1.6.4" } }, - "node_modules/through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", - "license": "MIT" - }, "node_modules/tmp": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz", @@ -5426,16 +5369,6 @@ "node": ">=14.17" } }, - "node_modules/unbzip2-stream": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz", - "integrity": "sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==", - "license": "MIT", - "dependencies": { - "buffer": "^5.2.1", - "through": "^2.3.8" - } - }, "node_modules/undici-types": { "version": "6.20.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", @@ -5631,9 +5564,9 @@ "license": "ISC" }, "node_modules/ws": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", - "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", + "version": "8.18.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.2.tgz", + "integrity": "sha512-DMricUmwGZUVr++AEAe2uiVM7UoO9MAVZMDu05UQOaUII0lp+zOzLLU4Xqh/JvTqklB1T4uELaaPBKyjE1r4fQ==", "license": "MIT", "engines": { "node": ">=10.0.0" diff --git a/package.json b/package.json index 40b9c0a..63d6008 100644 --- a/package.json +++ b/package.json @@ -1,36 +1,37 @@ { - "name": "migracion", - "version": "0.0.12", - "main": "main.mjs", - "scripts": { - "test": "echo \"Error: no test specified\" && exit 1", - "start": "electron .", - "build": "electron-builder" - }, - "keywords": [], - "author": "", - "license": "ISC", - "description": "Migracion", - "dependencies": { - "googleapis": "^144.0.0", - "node-fetch": "^3.3.2", - "puppeteer": "^24.1.1", - "puppeteer-core": "^24.2.0" - }, - "devDependencies": { - "electron": "^34.1.1", - "electron-builder": "^25.1.8" - }, - "type": "module", - "build": { - "appId": "com.migracion.id", - "productName": "Migracion", - "win": { - "target": "nsis", - "icon": "path/to/icon.ico" + "name": "migracion-uscis", + "version": "1.0.0", + "main": "uscis.mjs", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "start": "electron .", + "build": "electron-builder" }, - "extraResources": [ - "credenciales.json" - ] + "keywords": [], + "author": "", + "license": "ISC", + "description": "Migracion Uscis", + "dependencies": { + "googleapis": "^144.0.0", + "node-fetch": "^3.3.2", + "puppeteer": "^24.9.0", + "puppeteer-core": "^24.2.0" + }, + "devDependencies": { + "electron": "^34.1.1", + "electron-builder": "^25.1.8" + }, + "type": "module", + "build": { + "appId": "com.migracion.uscis.id", + "productName": "Migracion Uscis", + "win": { + "target": "nsis", + "icon": "path/to/icon.ico" + }, + "extraResources": [ + "credenciales.json" + ] + } } -} + \ No newline at end of file diff --git a/package23.json b/package23.json new file mode 100644 index 0000000..c6a5226 --- /dev/null +++ b/package23.json @@ -0,0 +1,36 @@ +{ + "name": "migracion-uscis", + "version": "0.0.14", + "main": "uscis.mjs", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "start": "electron .", + "build": "electron-builder" + }, + "keywords": [], + "author": "", + "license": "ISC", + "description": "Migracion Uscis", + "dependencies": { + "googleapis": "^144.0.0", + "node-fetch": "^3.3.2", + "puppeteer": "^24.9.0", + "puppeteer-core": "^24.2.0" + }, + "devDependencies": { + "electron": "^34.1.1", + "electron-builder": "^25.1.8" + }, + "type": "module", + "build": { + "appId": "com.migracion.uscis.id", + "productName": "Migracion Uscis", + "win": { + "target": "nsis", + "icon": "path/to/icon.ico" + }, + "extraResources": [ + "credenciales.json" + ] + } +} diff --git a/test.js b/test.js new file mode 100644 index 0000000..1a687b7 --- /dev/null +++ b/test.js @@ -0,0 +1,41 @@ +import puppeteer from 'puppeteer'; + +(async () => { + try { + + const browser = await puppeteer.launch({ + headless: false, + defaultViewport: null, + args: [ + // `--app=https://egov.uscis.gov/es`, + '--start-maximized', + '--no-sandbox', + '--disable-setuid-sandbox' + ], + executablePath: process.platform === 'win32' + ? 'C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe' + : undefined + }); + + // Create new page + const page = await browser.newPage(); + + // Go to website + await page.goto('https://egov.uscis.gov/es'); + + + + // // Get page title + // const title = await page.title(); + // console.log('Page title:', title); + + // // Type in search box (example) + // await page.type('textarea', 'puppeteer automation'); + + // // Press Enter key + // await page.keyboard.press('Enter'); + + } catch (error) { + console.error('An error occurred:', error); + } +})(); diff --git a/uscis.mjs b/uscis.mjs new file mode 100644 index 0000000..3f2e51f --- /dev/null +++ b/uscis.mjs @@ -0,0 +1,422 @@ +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(); +});