Files
2025-12-03 09:32:47 -05:00

541 lines
19 KiB
JavaScript

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: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, 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;
// Validate that case number has 9 digits
if (element.valor.length !== 9) {
console.log('>> Invalid case number - must be 9 digits');
await actualizarCelda(
spreadsheetId,
`D${element.fila}`,
['Invalid case number - must contain exactly 9 digits']
);
return true;
}
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);
await delay(1000);
await page.keyboard.press('Tab');
await delay(1000);
await page.keyboard.press('Tab');
console.log('>> Se diligencio ID de caso');
} catch (error) {
console.log('>> No se pudo encontrar el campo de entrada de caso Corte.');
}
await delay(1000);
try {
// await page.waitForSelector('input[type="text"]', { timeout: 10000 });
await page.type('input[type="text"]', element.nacionalidad);
console.log('>> Se diligencio nacionalidad');
} catch (error) {
console.log('>> No se pudo encontrar el campo nacionalidad');
}
await delay(1000);
try {
const elements = await page.$$('div[id^="react-select-2-option-"]');
for (const element of elements) {
await element.click();
}
console.log('>> Se dio click a la nacionalidad');
} catch (error) {
console.log('>> No se pudo encontrar el campo nacionalidad');
}
await delay(1000);
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(3000);
try {
await delay(3000);
// Wait for the 'No case found' message
// Check for "No case found" message
const result = await page.evaluate(() => {
const elements = document.querySelectorAll('div');
for (const el of elements) {
if (el.textContent.includes('No case found for this A-Number.') || el.textContent.includes('Case information is unavailable.')) {
return true;
}
}
return false;
}).catch(() => false);
if (result) {
console.log(">> No case found for this A-Number");
await actualizarCelda(
spreadsheetId,
`D${element.fila}`,
['No case found for this A-Number']
);
return true;
} else {
console.log('>> No error found');
try {
const isCaptchaPresent = await page.evaluate(() => {
const captchaFrames = document.querySelectorAll('iframe[src*="hcaptcha"]');
return Array.from(captchaFrames).some(frame => {
return frame !== null && getComputedStyle(frame).display !== 'none';
});
});
if (isCaptchaPresent) {
await delay(5000);
console.log('>> hCaptcha detected');
await actualizarCelda(
spreadsheetId,
`D${element.fila}`,
['Captcha detected - Please try again later']
);
await delay(5000);
return false;
} else {
console.log('>> No captcha detected');
}
} catch (error) {
console.error('>> Error checking for captcha:', error);
}
}
} catch (error) {
console.error('Error during case processing:', error);
// Check for hCaptcha presence
}
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,
`D${element.fila}:G${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 todasLasFilas = datos.slice(1)
.map((row, index) => ({
valor: row[0], // Columna A
fila: index + 2, // Número de fila real
nacionalidad: row[1], // Nacionalidad
ultimaConsulta: row[2], // Columna C
columnaD: row[3] // Columna D
}));
// Separate rows into three groups based on priority
const filasVaciasD = todasLasFilas.filter(row => !row.columnaD);
const filasConCaptcha = todasLasFilas.filter(row => row.columnaD && row.columnaD.includes('Captcha detected - Please try again later'));
const filasConFechaB = todasLasFilas.filter(row => {
if (!row.columnaD && !row.ultimaConsulta) return false; // Already handled by filasVaciasD or filasVaciasB
if (!row.ultimaConsulta) return false; // Handled by filasVaciasB
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();
const diferenciaHoras = (ahora - fechaUltimaConsulta) / (1000 * 60 * 60);
if (fechaUltimaConsulta > ahora) return false; // For future dates, return false to skip processing
return diferenciaHoras > 1; // Process only if more than 1 hour has passed
});
const filasVaciasB = todasLasFilas.filter(row => {
return !row.ultimaConsulta && row.columnaD; // Empty F, but G is not empty
});
// Sort rows with date in B by oldest date first
filasConFechaB.sort((a, b) => {
const [dateA, timeA] = a.ultimaConsulta.split(', ');
const [dayA, monthA, yearA] = dateA.split('/');
const [hoursA, minutesA, secondsA] = timeA.split(':');
const fechaA = new Date(yearA, monthA - 1, dayA, hoursA, minutesA, secondsA);
const [dateB, timeB] = b.ultimaConsulta.split(', ');
const [dayB, monthB, yearB] = dateB.split('/');
const [hoursB, minutesB, secondsB] = timeB.split(':');
const fechaB = new Date(yearB, monthB - 1, dayB, hoursB, minutesB, secondsB);
return fechaA - fechaB; // Ascending order (oldest first)
});
// Combine the arrays in the specified priority order
const filasOrdenadas = [...filasVaciasD, ...filasConCaptcha, ...filasConFechaB, ...filasVaciasB];
console.log(filasOrdenadas);
console.log(`>> Total de filas a procesar: ${filasOrdenadas.length}`);
if (filasOrdenadas.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 filasOrdenadas) {
procesadas++;
console.log(`\n>> Procesando fila ${element.fila} (${procesadas}/${filasOrdenadas.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,
`C${element.fila}`,
[fechaHoraActual]
),
actualizarCelda(
archivo.id,
`D${element.fila}:G${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();
});