Files
RPAInmigracion/main.mjs
T
2025-05-22 09:16:52 -05:00

507 lines
18 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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();
});