Files
RPAInmigracion/corte.mjs
T
2025-06-11 19:41:29 -05:00

439 lines
15 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 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();
});