This commit is contained in:
2025-05-22 09:16:52 -05:00
commit a6eb70136a
6 changed files with 6365 additions and 0 deletions
+506
View File
@@ -0,0 +1,506 @@
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();
});