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'; import os from 'os'; console.log(app.getAppPath()); let url_cred = app.getAppPath().replace('app.asar', ''); url_cred = url_cred + '\\credenciales.json'; console.log(url_cred); 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' } } }; 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 }); async function listarArchivosEnCarpeta(folderId) { const res = await drive.files.list({ q: `'${folderId}' in parents and mimeType='application/vnd.google-apps.spreadsheet' and name='base_pruebas'`, fields: '*' }); return res.data.files; } async function obtenerDatosHoja(spreadsheetId) { const response = await sheets.spreadsheets.values.get({ spreadsheetId, range: 'A:H', }); 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; } } async function cerrarChromeDebugging() { return new Promise((resolve) => { if (process.platform === 'win32') { exec('wmic process where "commandline like \'%--remote-debugging-port=%\'" call terminate', () => resolve()); } else { exec("pkill -f 'chrome.*--remote-debugging-port='", () => resolve()); } }); } async function iniciarChromeConPuerto(url, port, userDir) { return new Promise((resolve) => { const chromeProcess = exec( `start chrome ` + `--remote-debugging-port=${port} ` + `--no-sandbox ` + `--disable-setuid-sandbox ` + `--user-data-dir="${userDir}" ` + `"${url}"`, () => resolve() ); }); } async function getWebSocketUrl(port) { const maxRetries = 15; for (let i = 0; i < maxRetries; i++) { try { const response = await fetch(`http://localhost:${port}/json/version`); const json = await response.json(); if (json.webSocketDebuggerUrl) return json.webSocketDebuggerUrl; } catch {} await new Promise(res => setTimeout(res, 2000)); } throw new Error(`No se pudo conectar a Chrome en el puerto ${port}`); } async function initBrowserEnPuerto(url, port, userDir) { await cerrarChromeDebugging(); await new Promise(resolve => setTimeout(resolve, 2000)); await iniciarChromeConPuerto(url, port, userDir); await new Promise(resolve => setTimeout(resolve, 8000)); const webSocketDebuggerUrl = await getWebSocketUrl(port); return await puppeteer.connect({ browserWSEndpoint: webSocketDebuggerUrl, defaultViewport: null }); } async function delay(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } async function procesarCasoUSCIS(page, element, spreadsheetId, intentos = 1) { const MAX_INTENTOS = 3; try { await page.goto(CONFIG.PLATFORMS.USCIS.URL, { waitUntil: 'domcontentloaded', timeout: 20000 }); await page.waitForSelector('#receipt_number', { timeout: 10000 }); await page.type('#receipt_number', element.valor); await delay(1000); await page.click('::-p-text(Verifique Estatus)'); await delay(2000); const errorMessage = await page.$('.errorMessage'); if (errorMessage) { await actualizarCelda(spreadsheetId, `G${element.fila}`, ['El número de recibo ingresado no es válido']); } else { const [titulo, descripcion] = await Promise.all([ page.$eval('.conditionalLanding h2', el => el.innerText), page.$eval('.conditionalLanding p', el => el.innerText) ]); await actualizarCelda(spreadsheetId, `G${element.fila}:H${element.fila}`, [titulo, descripcion]); } return true; } catch (error) { if (intentos < MAX_INTENTOS) { return await procesarCasoUSCIS(page, element, spreadsheetId, intentos + 1); } else { await actualizarCelda(spreadsheetId, `G${element.fila}`, ['Error al procesar el caso']); return false; } } } async function procesarArchivoEnGrupo(filas, archivo, port, userDir) { const browser = await initBrowserEnPuerto(CONFIG.PLATFORMS.USCIS.URL, port, userDir); const page = (await browser.pages())[0]; for (const element of filas) { const fechaHoraActual = new Date().toLocaleString('es-ES', { year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', second: '2-digit' }); await Promise.all([ actualizarCelda(archivo.id, `F${element.fila}`, [fechaHoraActual]), actualizarCelda(archivo.id, `G${element.fila}:H${element.fila}`, ['', '']) ]); await procesarCasoUSCIS(page, element, archivo.id); } await browser.close(); } async function procesarArchivo(archivo) { const datos = await obtenerDatosHoja(archivo.id); const filas = datos.slice(1).map((row, index) => ({ valor: row[4], fila: index + 2, ultimaConsulta: row[5], resultado: row[6] })).filter(row => !row.resultado || !row.ultimaConsulta || ((new Date() - new Date(row.ultimaConsulta)) / 3600000 > 1)); if (!filas.length) return; const numProcesos = 4; const chunkSize = Math.ceil(filas.length / numProcesos); const grupos = Array.from({ length: numProcesos }, (_, i) => filas.slice(i * chunkSize, (i + 1) * chunkSize)); await Promise.all(grupos.map((grupo, i) => { if (grupo.length === 0) return; const port = 9224 + i; const userDir = `C:/temp/chrome_debug_temp_migracion_${i}`; return procesarArchivoEnGrupo(grupo, archivo, port, userDir); })); } async function cerrarTodo() { await cerrarChromeDebugging(); process.exit(); app.quit(); } process.on('SIGINT', async () => { console.log('Ctrl + C detectado'); await cerrarTodo(); }); async function procesarArchivos() { const archivos = await listarArchivosEnCarpeta(CONFIG.DRIVE.FOLDER_ID); for (const archivo of archivos) { await procesarArchivo(archivo); } await cerrarTodo(); } app.on('ready', () => { procesarArchivos(); });