Initial
This commit is contained in:
+34
@@ -0,0 +1,34 @@
|
|||||||
|
# Directorios y archivos generados automáticamente
|
||||||
|
node_modules/
|
||||||
|
npm-debug.log
|
||||||
|
yarn-error.log
|
||||||
|
|
||||||
|
# Archivos de construcción
|
||||||
|
build/
|
||||||
|
dist/
|
||||||
|
out/
|
||||||
|
|
||||||
|
# Archivos de configuración locales (pueden variar según tus herramientas)
|
||||||
|
.env
|
||||||
|
.env.local
|
||||||
|
.env.*.local
|
||||||
|
|
||||||
|
# Dependencias locales (puedes ajustar esta lista según tus necesidades)
|
||||||
|
# /node_modules/
|
||||||
|
|
||||||
|
# Logs y archivos temporales
|
||||||
|
*.log
|
||||||
|
*.tmp
|
||||||
|
*.swp
|
||||||
|
|
||||||
|
# Archivos de usuario y configuración específica de IDE
|
||||||
|
.idea/
|
||||||
|
.vscode/
|
||||||
|
*.suo
|
||||||
|
*.ntvs*
|
||||||
|
*.njsproj
|
||||||
|
*.sln
|
||||||
|
*.sw?
|
||||||
|
|
||||||
|
# Dependencias globales de Yarn (opcional; úsalo si lo necesitas)
|
||||||
|
# /.yarn/
|
||||||
@@ -0,0 +1,201 @@
|
|||||||
|
import path from 'path';
|
||||||
|
import { exec } from 'child_process';
|
||||||
|
import puppeteer from 'puppeteer-core';
|
||||||
|
import fetch from 'node-fetch';
|
||||||
|
import { createWriteStream } from 'fs';
|
||||||
|
import fs from 'fs';
|
||||||
|
import { app, BrowserWindow } from 'electron';
|
||||||
|
import pie from 'puppeteer-in-electron';
|
||||||
|
|
||||||
|
// Configuración
|
||||||
|
const CONFIG = {
|
||||||
|
URL: 'https://rpa.mind.brm.co/app',
|
||||||
|
APP_CODE: 'SHP030',
|
||||||
|
URL_CONFIRM: 'https://cs.shopee.com.mx/portal/inhouse/'
|
||||||
|
};
|
||||||
|
|
||||||
|
// Initialize puppeteer with electron before app is ready
|
||||||
|
(async () => {
|
||||||
|
await pie.initialize(app);
|
||||||
|
})();
|
||||||
|
|
||||||
|
async function queryEndpoint(user) {
|
||||||
|
try {
|
||||||
|
const response = await fetch(CONFIG.URL, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
user: user,
|
||||||
|
code: CONFIG.APP_CODE
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`HTTP error! status: ${response.status}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await response.json();
|
||||||
|
return data;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error querying endpoint:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove the initialize function since we're initializing at startup
|
||||||
|
// and modify fillForm to remove the initialize call
|
||||||
|
async function fillForm(url_app, user, password, xpath_user, xpath_pass) {
|
||||||
|
try {
|
||||||
|
// Remove the initialize() call from here
|
||||||
|
|
||||||
|
// Create Electron window
|
||||||
|
const window = new BrowserWindow({
|
||||||
|
width: 1200,
|
||||||
|
height: 800,
|
||||||
|
webPreferences: {
|
||||||
|
nodeIntegration: true,
|
||||||
|
contextIsolation: false
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Get puppeteer browser and page
|
||||||
|
const browser = await pie.connect(app, puppeteer);
|
||||||
|
const page = await pie.getPage(browser, window);
|
||||||
|
|
||||||
|
// Navigate to the form page
|
||||||
|
console.log('Abriendo plataforma');
|
||||||
|
await page.goto(url_app);
|
||||||
|
console.log('Plataforma abierta');
|
||||||
|
|
||||||
|
console.log('Esperando boton');
|
||||||
|
await page.waitForSelector('.login-button');
|
||||||
|
await page.click('.login-button');
|
||||||
|
console.log('Boton encontrado');
|
||||||
|
|
||||||
|
try {
|
||||||
|
console.log('Verificando si ya hay una cuenta');
|
||||||
|
const account = await page.waitForSelector('::-p-xpath(//h1[contains(text(), "Choose an account")])');
|
||||||
|
console.log('Si hay una cuenta');
|
||||||
|
const account_active = await page.waitForSelector('::-p-xpath(//span[contains(text(), "'+user+'")])');
|
||||||
|
account_active.click();
|
||||||
|
console.log('Click en cuenta activa');
|
||||||
|
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 5000));
|
||||||
|
|
||||||
|
try {
|
||||||
|
console.log('Esperando boton continue');
|
||||||
|
const input_continue_top = await page.waitForSelector('::-p-xpath(//button[contains(text(), "Allow")])');
|
||||||
|
await input_continue_top.click();
|
||||||
|
console.log('Boton continue clickado');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error al encontrar el botón "Continue"');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log('No hay una cuenta');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
let startTime_page = Date.now();
|
||||||
|
let isLoggedIn_page = false;
|
||||||
|
|
||||||
|
while (Date.now() - startTime_page < 10000) { // 10 seconds timeout
|
||||||
|
const url_actual_page = page.url();
|
||||||
|
console.log(url_actual_page);
|
||||||
|
|
||||||
|
if (url_actual_page.includes(CONFIG.URL_CONFIRM)) {
|
||||||
|
console.log('Ya se encuentra en la pagina de inicio');
|
||||||
|
console.log('Logueado');
|
||||||
|
isLoggedIn_page = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 500)); // Check every 500ms
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isLoggedIn_page) {
|
||||||
|
console.log('No logueado');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isLoggedIn_page !== true) {
|
||||||
|
|
||||||
|
console.log('Esperando input user');
|
||||||
|
const input_user = await page.waitForSelector('::-p-xpath(' + xpath_user + ')');
|
||||||
|
await input_user.type(user);
|
||||||
|
await input_user.press('Enter');
|
||||||
|
console.log('Input user diligenciado');
|
||||||
|
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 5000));
|
||||||
|
|
||||||
|
console.log('Esperando input password');
|
||||||
|
const input_pass = await page.waitForSelector('::-p-xpath(' + xpath_pass + ')');
|
||||||
|
await input_pass.type(password);
|
||||||
|
await input_pass.press('Enter');
|
||||||
|
console.log('Input password diligenciado');
|
||||||
|
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 5000));
|
||||||
|
|
||||||
|
try {
|
||||||
|
console.log('Esperando boton continue');
|
||||||
|
const input_continue = await page.waitForSelector('::-p-xpath(//button[contains(text(), "Allow")])');
|
||||||
|
await input_continue.click();
|
||||||
|
console.log('Boton continue clickado');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error al encontrar el botón "Continue"');
|
||||||
|
}
|
||||||
|
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 5000));
|
||||||
|
|
||||||
|
let startTime = Date.now();
|
||||||
|
let isLoggedIn = false;
|
||||||
|
|
||||||
|
while (Date.now() - startTime < 10000) { // 10 seconds timeout
|
||||||
|
const url_actual = page.url();
|
||||||
|
console.log(url_actual);
|
||||||
|
|
||||||
|
if (url_actual.includes(CONFIG.URL_CONFIRM)) {
|
||||||
|
console.log('Ya se encuentra en la pagina de inicio');
|
||||||
|
console.log('Logueado');
|
||||||
|
isLoggedIn = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 500)); // Check every 500ms
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isLoggedIn) {
|
||||||
|
console.log('No logueado');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error filling form:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Modified main process
|
||||||
|
app.on('ready', async () => {
|
||||||
|
console.log('Aplicacion lista');
|
||||||
|
try {
|
||||||
|
const data_user = await queryEndpoint('45957768');
|
||||||
|
console.log(data_user);
|
||||||
|
if (data_user.App) {
|
||||||
|
console.log('Se obtuvo la info del usuario');
|
||||||
|
await fillForm(data_user.App, data_user.User, data_user.Password, data_user.Xpath_user, data_user.Xpath_pass);
|
||||||
|
} else {
|
||||||
|
console.log('No se pudo obtener la info del usuario');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error in main process:', error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Prevent app from closing when all windows are closed
|
||||||
|
app.on('window-all-closed', () => {
|
||||||
|
if (process.platform !== 'darwin') {
|
||||||
|
app.quit();
|
||||||
|
}
|
||||||
|
});
|
||||||
+201
@@ -0,0 +1,201 @@
|
|||||||
|
import puppeteer from 'puppeteer-core';
|
||||||
|
import fetch from 'node-fetch';
|
||||||
|
import { app, BrowserWindow, Notification } from 'electron';
|
||||||
|
import pie from 'puppeteer-in-electron';
|
||||||
|
|
||||||
|
// Configuración
|
||||||
|
const CONFIG = {
|
||||||
|
URL: 'https://rpa.mind.brm.co/app',
|
||||||
|
APP_CODE: 'SHP030',
|
||||||
|
URL_CONFIRM: 'https://cs.shopee.com.mx/portal/inhouse/',
|
||||||
|
TIMEOUT: 10000,
|
||||||
|
WAIT_TIME: 5000
|
||||||
|
};
|
||||||
|
|
||||||
|
// Inicialización de puppeteer
|
||||||
|
(async () => {
|
||||||
|
await pie.initialize(app);
|
||||||
|
})();
|
||||||
|
|
||||||
|
async function queryEndpoint(user) {
|
||||||
|
console.log('>> Iniciando consulta al endpoint para el usuario:', user);
|
||||||
|
try {
|
||||||
|
const response = await fetch(CONFIG.URL, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
user: user,
|
||||||
|
code: CONFIG.APP_CODE
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`HTTP error! status: ${response.status}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('>> Respuesta del endpoint recibida');
|
||||||
|
const data = await response.json();
|
||||||
|
return data;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('>> Error en consulta al endpoint:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fillForm(url_app, user, password, xpath_user, xpath_pass) {
|
||||||
|
// Agregar notificación al inicio
|
||||||
|
new Notification({
|
||||||
|
title: 'RPA Shopee',
|
||||||
|
body: 'Abriendo Shopee'
|
||||||
|
}).show();
|
||||||
|
|
||||||
|
console.log('>> Iniciando proceso de automatizacion');
|
||||||
|
try {
|
||||||
|
console.log('>> Creando ventana de Electron');
|
||||||
|
const window = new BrowserWindow({
|
||||||
|
width: 1200,
|
||||||
|
height: 800,
|
||||||
|
webPreferences: {
|
||||||
|
nodeIntegration: true,
|
||||||
|
contextIsolation: false
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('>> Conectando navegador puppeteer');
|
||||||
|
const browser = await pie.connect(app, puppeteer);
|
||||||
|
const page = await pie.getPage(browser, window);
|
||||||
|
|
||||||
|
console.log('>> Navegando a:', url_app);
|
||||||
|
await page.goto(url_app);
|
||||||
|
console.log('>> Pagina cargada correctamente');
|
||||||
|
|
||||||
|
console.log('>> Buscando boton de login');
|
||||||
|
await page.waitForSelector('.login-button');
|
||||||
|
await page.click('.login-button');
|
||||||
|
console.log('>> Boton de login clickeado');
|
||||||
|
|
||||||
|
const isExistingAccount = await checkExistingAccount(page, user);
|
||||||
|
|
||||||
|
if (!isExistingAccount && !await checkLoginStatus(page)) {
|
||||||
|
console.log('>> No se detectó sesión activa, iniciando login manual');
|
||||||
|
await performLogin(page, user, password, xpath_user, xpath_pass);
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('>> Error en el proceso de automatizacion:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function checkExistingAccount(page, user) {
|
||||||
|
console.log('>> Verificando cuenta existente');
|
||||||
|
try {
|
||||||
|
console.log('>> Buscando selector de cuentas');
|
||||||
|
await page.waitForSelector('::-p-xpath(//h1[contains(text(), "Choose an account")])', { timeout: CONFIG.TIMEOUT });
|
||||||
|
console.log('>> Buscando cuenta del usuario:', user);
|
||||||
|
const account_active = await page.waitForSelector('::-p-xpath(//span[contains(text(), "'+user+'")])', { timeout: CONFIG.TIMEOUT });
|
||||||
|
await account_active.click();
|
||||||
|
console.log('>> Cuenta seleccionada');
|
||||||
|
|
||||||
|
try {
|
||||||
|
console.log('>> Buscando boton Allow');
|
||||||
|
await new Promise(resolve => setTimeout(resolve, CONFIG.WAIT_TIME));
|
||||||
|
const input_continue = await page.waitForSelector('::-p-xpath(//button[contains(text(), "Allow")])', { timeout: CONFIG.TIMEOUT });
|
||||||
|
await input_continue.click();
|
||||||
|
console.log('>> Boton Allow clickeado');
|
||||||
|
|
||||||
|
// Agregar verificación de login
|
||||||
|
console.log('>> Verificando login después de Allow');
|
||||||
|
const loginStatus = await checkLoginStatus(page);
|
||||||
|
if (loginStatus) {
|
||||||
|
console.log('>> Login exitoso con cuenta existente');
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
console.log('>> Login no exitoso con cuenta existente');
|
||||||
|
} catch (error) {
|
||||||
|
console.log('>> No se encontro boton Allow');
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
} catch (error) {
|
||||||
|
console.log('>> No se encontraron cuentas existentes');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function checkLoginStatus(page) {
|
||||||
|
console.log('>> Verificando estado de login');
|
||||||
|
const startTime = Date.now();
|
||||||
|
|
||||||
|
while (Date.now() - startTime < CONFIG.TIMEOUT) {
|
||||||
|
const currentUrl = page.url();
|
||||||
|
console.log('>> URL actual:', currentUrl);
|
||||||
|
|
||||||
|
if (currentUrl.includes(CONFIG.URL_CONFIRM)) {
|
||||||
|
console.log('>> Usuario logueado exitosamente');
|
||||||
|
// Agregar notificación de login exitoso
|
||||||
|
new Notification({
|
||||||
|
title: 'RPA Shopee',
|
||||||
|
body: 'Usuario logueado exitosamente'
|
||||||
|
}).show();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 500));
|
||||||
|
}
|
||||||
|
console.log('>> Timeout en verificación de login');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function performLogin(page, user, password, xpath_user, xpath_pass) {
|
||||||
|
console.log('>> Iniciando proceso de login manual');
|
||||||
|
|
||||||
|
console.log('>> Ingresando usuario');
|
||||||
|
const input_user = await page.waitForSelector('::-p-xpath(' + xpath_user + ')');
|
||||||
|
await input_user.type(user);
|
||||||
|
await input_user.press('Enter');
|
||||||
|
console.log('>> Usuario ingresado');
|
||||||
|
|
||||||
|
await new Promise(resolve => setTimeout(resolve, CONFIG.WAIT_TIME));
|
||||||
|
|
||||||
|
console.log('>> Ingresando contraseña');
|
||||||
|
const input_pass = await page.waitForSelector('::-p-xpath(' + xpath_pass + ')');
|
||||||
|
await input_pass.type(password);
|
||||||
|
await input_pass.press('Enter');
|
||||||
|
console.log('>> Contraseña ingresada');
|
||||||
|
|
||||||
|
try {
|
||||||
|
console.log('>> Buscando botón Allow');
|
||||||
|
const input_continue = await page.waitForSelector('::-p-xpath(//button[contains(text(), "Allow")])');
|
||||||
|
await input_continue.click();
|
||||||
|
console.log('>> Botón Allow clickeado');
|
||||||
|
} catch (error) {
|
||||||
|
console.log('>> No se encontró botón Allow');
|
||||||
|
}
|
||||||
|
|
||||||
|
return await checkLoginStatus(page);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Modified main process
|
||||||
|
app.on('ready', async () => {
|
||||||
|
console.log('Aplicacion lista');
|
||||||
|
try {
|
||||||
|
const data_user = await queryEndpoint('45957768');
|
||||||
|
console.log(data_user);
|
||||||
|
if (data_user.App) {
|
||||||
|
console.log('Se obtuvo la info del usuario');
|
||||||
|
await fillForm(data_user.App, data_user.User, data_user.Password, data_user.Xpath_user, data_user.Xpath_pass);
|
||||||
|
} else {
|
||||||
|
console.log('No se pudo obtener la info del usuario');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error in main process:', error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Prevent app from closing when all windows are closed
|
||||||
|
app.on('window-all-closed', () => {
|
||||||
|
if (process.platform !== 'darwin') {
|
||||||
|
app.quit();
|
||||||
|
}
|
||||||
|
});
|
||||||
@@ -0,0 +1,405 @@
|
|||||||
|
import puppeteer from 'puppeteer-core';
|
||||||
|
import fetch from 'node-fetch';
|
||||||
|
import { app, BrowserWindow, Notification, dialog } from 'electron';
|
||||||
|
import pie from 'puppeteer-in-electron';
|
||||||
|
import os from 'os';
|
||||||
|
import path from 'path';
|
||||||
|
import fs from 'fs';
|
||||||
|
|
||||||
|
// Al inicio del archivo, después de los imports
|
||||||
|
if (process.defaultApp) {
|
||||||
|
if (process.argv.length >= 2) {
|
||||||
|
app.setAsDefaultProtocolClient('shopee', process.execPath, [path.resolve(process.argv[1])])
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
app.setAsDefaultProtocolClient('shopee')
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configuración
|
||||||
|
const CONFIG = {
|
||||||
|
URL: 'https://rpa.mind.brm.co/app',
|
||||||
|
URL_LOG: 'https://rpa.mind.brm.co/log',
|
||||||
|
APP_CODE: 'SHP030',
|
||||||
|
USER: os.userInfo().username,
|
||||||
|
// USER: '45957768',
|
||||||
|
// USER: '1019017231',
|
||||||
|
URL_CONFIRM: 'https://cs.shopee.com.mx/portal/inhouse/',
|
||||||
|
TIMEOUT: 10000,
|
||||||
|
WAIT_TIME: 5000,
|
||||||
|
ICON: app.getAppPath('icon.ico')
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log(">> Iniciando configuración de logs..");
|
||||||
|
const logsDir = 'C:/Shopee';
|
||||||
|
if (!fs.existsSync(logsDir)) {
|
||||||
|
fs.mkdirSync(logsDir, { recursive: true });
|
||||||
|
}
|
||||||
|
const logFilePath = path.join(logsDir, 'console-log.txt');
|
||||||
|
|
||||||
|
// Limpia el archivo de logs al inicio
|
||||||
|
await fs.promises.writeFile(logFilePath, '', 'utf-8');
|
||||||
|
// Helper para escribir logs en el archivo
|
||||||
|
const writeLogToFile = (message) => {
|
||||||
|
const timestamp = new Date().toISOString();
|
||||||
|
fs.appendFileSync(logFilePath, `[${timestamp}] ${message}\n`);
|
||||||
|
};
|
||||||
|
// Sobrescribe métodos de console
|
||||||
|
['log', 'info', 'warn', 'error'].forEach((method) => {
|
||||||
|
const originalMethod = console[method];
|
||||||
|
console[method] = (...args) => {
|
||||||
|
const message = args
|
||||||
|
.map((arg) => (typeof arg === 'object' ? JSON.stringify(arg, null, 2) : arg))
|
||||||
|
.join(' ');
|
||||||
|
writeLogToFile(message);
|
||||||
|
originalMethod.apply(console, args); // Mantén la salida en la consola
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
// Inicialización de puppeteer
|
||||||
|
(async () => {
|
||||||
|
await pie.initialize(app);
|
||||||
|
})();
|
||||||
|
|
||||||
|
async function queryEndpoint(user) {
|
||||||
|
try {
|
||||||
|
const response = await fetch(CONFIG.URL, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
user: user,
|
||||||
|
code: CONFIG.APP_CODE
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`HTTP error! status: ${response.status}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// First get the response as text
|
||||||
|
const textResponse = await response.text();
|
||||||
|
|
||||||
|
// Check if it's the invalid parameter message
|
||||||
|
if (textResponse === "Invalid parameter") {
|
||||||
|
console.log('>> El usuario no tiene acceso a Shopee');
|
||||||
|
// Modificar todas las notificaciones para incluir el ícono
|
||||||
|
// En queryEndpoint:
|
||||||
|
await dialog.showErrorBox(
|
||||||
|
'RPA Shopee',
|
||||||
|
'El usuario no tiene acceso a Shopee'
|
||||||
|
);
|
||||||
|
|
||||||
|
// Add delay before closing to ensure notification is shown
|
||||||
|
setTimeout(() => {
|
||||||
|
app.quit();
|
||||||
|
}, 3000);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If it's not "Invalid parameter", try to parse as JSON
|
||||||
|
try {
|
||||||
|
return JSON.parse(textResponse);
|
||||||
|
} catch (parseError) {
|
||||||
|
console.error('>> Error parsing response:', parseError);
|
||||||
|
throw parseError;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('>> Error querying endpoint:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fillForm(url_app, user, password, xpath_user, xpath_pass) {
|
||||||
|
// Agregar notificación al inicio
|
||||||
|
new Notification({
|
||||||
|
title: 'RPA Shopee',
|
||||||
|
body: 'Abriendo Shopee'
|
||||||
|
}).show();
|
||||||
|
|
||||||
|
console.log('>> Iniciando proceso de automatizacion');
|
||||||
|
try {
|
||||||
|
console.log('>> Creando ventana de Electron');
|
||||||
|
const window = new BrowserWindow({
|
||||||
|
width: 1200,
|
||||||
|
height: 800,
|
||||||
|
webPreferences: {
|
||||||
|
nodeIntegration: true,
|
||||||
|
contextIsolation: false
|
||||||
|
},
|
||||||
|
// show: process.argv.includes('--dev'), // Only show window if --dev parameter is present
|
||||||
|
show: true,
|
||||||
|
autoHideMenuBar: true // Ocultar menú por defecto
|
||||||
|
});
|
||||||
|
if (process.argv.includes('--dev')) {
|
||||||
|
window.openDevTools();
|
||||||
|
}
|
||||||
|
// Agregar botones de navegación
|
||||||
|
// window.webContents.on('did-finish-load', () => {
|
||||||
|
// window.webContents.executeJavaScript(`
|
||||||
|
// (function() {
|
||||||
|
// const nav = document.createElement('div');
|
||||||
|
// nav.style.cssText = 'position: fixed; top: 0; left: 0; z-index: 9999; background: #fff; padding: 5px; border-bottom: 1px solid #ddd;';
|
||||||
|
// nav.innerHTML = '<button onclick="history.back()" style="margin-right: 5px;">⬅️</button>' +
|
||||||
|
// '<button onclick="history.forward()" style="margin-right: 5px;">➡️</button>' +
|
||||||
|
// '<button onclick="location.reload()" style="margin-right: 5px;">🔄</button>';
|
||||||
|
// document.body.appendChild(nav);
|
||||||
|
// })();
|
||||||
|
// `, true); // Add the 'true' parameter to use the trusted types
|
||||||
|
// });
|
||||||
|
console.log('>> Conectando navegador puppeteer');
|
||||||
|
const browser = await pie.connect(app, puppeteer);
|
||||||
|
const page = await pie.getPage(browser, window);
|
||||||
|
console.log('>> Navegando a:', url_app);
|
||||||
|
await page.goto(url_app);
|
||||||
|
console.log('>> Pagina cargada correctamente');
|
||||||
|
|
||||||
|
console.log('>> Buscando boton de login');
|
||||||
|
await page.waitForSelector('.login-button');
|
||||||
|
await page.click('.login-button');
|
||||||
|
console.log('>> Boton de login clickeado');
|
||||||
|
|
||||||
|
const isExistingAccount = await checkExistingAccount(page, user);
|
||||||
|
|
||||||
|
if (!isExistingAccount && !await checkLoginStatus(page)) {
|
||||||
|
console.log('>> No se detectó sesión activa, iniciando login manual');
|
||||||
|
await performLogin(page, user, password, xpath_user, xpath_pass);
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('>> Error en el proceso de automatizacion:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function checkExistingAccount(page, user) {
|
||||||
|
console.log('>> Verificando cuenta existente');
|
||||||
|
try {
|
||||||
|
console.log('>> Buscando selector de cuentas');
|
||||||
|
await page.waitForSelector('::-p-xpath(//h1[contains(text(), "Choose an account")])', { timeout: CONFIG.TIMEOUT });
|
||||||
|
console.log('>> Buscando cuenta del usuario:', user);
|
||||||
|
// Convert username to lowercase before searching
|
||||||
|
user = user.toLowerCase();
|
||||||
|
const account_active = await page.waitForSelector('::-p-xpath(//span[contains(text(), "'+user+'")])', { timeout: CONFIG.TIMEOUT });
|
||||||
|
await account_active.click();
|
||||||
|
console.log('>> Cuenta seleccionada');
|
||||||
|
|
||||||
|
try {
|
||||||
|
console.log('>> Buscando boton Allow');
|
||||||
|
await new Promise(resolve => setTimeout(resolve, CONFIG.WAIT_TIME));
|
||||||
|
const input_continue = await page.waitForSelector('::-p-xpath(//button[contains(text(), "Allow")])', { timeout: CONFIG.TIMEOUT });
|
||||||
|
await input_continue.click();
|
||||||
|
console.log('>> Boton Allow clickeado');
|
||||||
|
|
||||||
|
// Agregar verificación de login
|
||||||
|
console.log('>> Verificando login después de Allow');
|
||||||
|
const loginStatus = await checkLoginStatus(page);
|
||||||
|
if (loginStatus) {
|
||||||
|
console.log('>> Login exitoso con cuenta existente');
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
console.log('>> Login no exitoso con cuenta existente');
|
||||||
|
} catch (error) {
|
||||||
|
console.log('>> No se encontro boton Allow');
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
} catch (error) {
|
||||||
|
console.log('>> No se encontraron cuentas existentes');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Add this new function for sending logs
|
||||||
|
// Agregar esta función para obtener IP y MAC
|
||||||
|
async function getNetworkInfo() {
|
||||||
|
const interfaces = os.networkInterfaces();
|
||||||
|
let ipAddress = '';
|
||||||
|
let macAddress = '';
|
||||||
|
|
||||||
|
// Buscar en todas las interfaces de red
|
||||||
|
for (const name of Object.keys(interfaces)) {
|
||||||
|
for (const net of interfaces[name]) {
|
||||||
|
// Buscar IPv4 y no localhost
|
||||||
|
if (net.family === 'IPv4' && !net.internal) {
|
||||||
|
ipAddress = net.address;
|
||||||
|
macAddress = net.mac;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ipAddress && macAddress) break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return { ip: ipAddress, mac: macAddress };
|
||||||
|
}
|
||||||
|
|
||||||
|
// Modificar la función sendLoginLog
|
||||||
|
async function sendLoginLog() {
|
||||||
|
try {
|
||||||
|
const networkInfo = await getNetworkInfo();
|
||||||
|
const response = await fetch(CONFIG.URL_LOG, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
user: CONFIG.USER,
|
||||||
|
ip: networkInfo.ip,
|
||||||
|
mac: networkInfo.mac,
|
||||||
|
code: CONFIG.APP_CODE,
|
||||||
|
host: os.hostname(),
|
||||||
|
type: 1,
|
||||||
|
status: 1
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`HTTP error! status: ${response.status}`);
|
||||||
|
}
|
||||||
|
console.log('>> Log enviado exitosamente');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('>> Error enviando log:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Modify checkLoginStatus function
|
||||||
|
async function checkLoginStatus(page) {
|
||||||
|
console.log('>> Verificando estado de login');
|
||||||
|
const startTime = Date.now();
|
||||||
|
|
||||||
|
while (Date.now() - startTime < CONFIG.TIMEOUT) {
|
||||||
|
const currentUrl = page.url();
|
||||||
|
console.log('>> URL actual:', currentUrl);
|
||||||
|
|
||||||
|
if (currentUrl.includes(CONFIG.URL_CONFIRM)) {
|
||||||
|
console.log('>> Usuario logueado exitosamente');
|
||||||
|
new Notification({
|
||||||
|
title: 'RPA Shopee',
|
||||||
|
body: 'Usuario logueado exitosamente'
|
||||||
|
}).show();
|
||||||
|
|
||||||
|
// Mostrar la ventana después del login exitoso
|
||||||
|
const windows = BrowserWindow.getAllWindows();
|
||||||
|
if (windows.length > 0) {
|
||||||
|
windows[0].maximize();
|
||||||
|
windows[0].show();
|
||||||
|
windows[0].focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add log sending after successful login
|
||||||
|
await sendLoginLog();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 500));
|
||||||
|
}
|
||||||
|
console.log('>> Timeout en verificación de login');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function performLogin(page, user, password, xpath_user, xpath_pass) {
|
||||||
|
console.log('>> Iniciando proceso de login manual');
|
||||||
|
|
||||||
|
console.log('>> Ingresando usuario');
|
||||||
|
const input_user = await page.waitForSelector('::-p-xpath(' + xpath_user + ')');
|
||||||
|
await input_user.type(user);
|
||||||
|
await input_user.press('Enter');
|
||||||
|
console.log('>> Usuario ingresado');
|
||||||
|
|
||||||
|
await new Promise(resolve => setTimeout(resolve, CONFIG.WAIT_TIME));
|
||||||
|
|
||||||
|
console.log('>> Ingresando contraseña');
|
||||||
|
const input_pass = await page.waitForSelector('::-p-xpath(' + xpath_pass + ')');
|
||||||
|
await input_pass.type(password);
|
||||||
|
await input_pass.press('Enter');
|
||||||
|
console.log('>> Contraseña ingresada');
|
||||||
|
|
||||||
|
try {
|
||||||
|
console.log('>> Buscando botón Allow');
|
||||||
|
const input_continue = await page.waitForSelector('::-p-xpath(//button[contains(text(), "Allow")])');
|
||||||
|
await input_continue.click();
|
||||||
|
console.log('>> Botón Allow clickeado');
|
||||||
|
} catch (error) {
|
||||||
|
console.log('>> No se encontró botón Allow');
|
||||||
|
}
|
||||||
|
|
||||||
|
return await checkLoginStatus(page);
|
||||||
|
}
|
||||||
|
// Modificar cerca del inicio del archivo, después de los imports
|
||||||
|
let isOpenedFromURL = false;
|
||||||
|
|
||||||
|
console.log('Url fuera logica: '+process.argv);
|
||||||
|
|
||||||
|
// Detectar si se abrió desde URL por argumentos de línea de comandos
|
||||||
|
if (process.argv.includes('shopee://open') || process.argv.some(arg => arg.includes('shopee://open')) || process.argv.includes('--dev')) {
|
||||||
|
isOpenedFromURL = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Modificar el manejo del protocolo
|
||||||
|
app.on('open-url', (event, url) => {
|
||||||
|
console.log('Url apertura: '+url);
|
||||||
|
event.preventDefault();
|
||||||
|
isOpenedFromURL = true;
|
||||||
|
if (url.includes('shopee://open')) {
|
||||||
|
if (BrowserWindow.getAllWindows().length > 0) {
|
||||||
|
BrowserWindow.getAllWindows()[0].focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Modificar el main process para manejar segundo inicio
|
||||||
|
const gotTheLock = app.requestSingleInstanceLock();
|
||||||
|
|
||||||
|
if (!gotTheLock) {
|
||||||
|
app.quit();
|
||||||
|
} else {
|
||||||
|
app.on('second-instance', (event, commandLine, workingDirectory) => {
|
||||||
|
// Si alguien trata de abrir otra instancia, enfocamos la ventana existente
|
||||||
|
if (BrowserWindow.getAllWindows().length > 0) {
|
||||||
|
const window_open = BrowserWindow.getAllWindows()[0];
|
||||||
|
if (window_open.isMinimized()) window_open.restore();
|
||||||
|
window_open.focus();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// El resto de tu código del main process se mantiene igual
|
||||||
|
app.on('ready', async () => {
|
||||||
|
console.log('Aplicacion lista');
|
||||||
|
console.log('>> Método de apertura:', isOpenedFromURL ? 'URL Protocol' : 'Manual');
|
||||||
|
|
||||||
|
if (!isOpenedFromURL) {
|
||||||
|
await dialog.showErrorBox(
|
||||||
|
'Error de acceso',
|
||||||
|
'Esta aplicación solo puede ser iniciada desde OKAN'
|
||||||
|
);
|
||||||
|
app.quit();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Mostrar alerta del método de apertura
|
||||||
|
// new Notification({
|
||||||
|
// title: 'RPA Shopee',
|
||||||
|
// body: `Aplicación iniciada ${isOpenedFromURL ? 'desde URL' : 'manualmente'}`,
|
||||||
|
// icon: CONFIG.ICON
|
||||||
|
// }).show();
|
||||||
|
|
||||||
|
try {
|
||||||
|
console.log('>> Obteniendo info del usuario: '+CONFIG.USER);
|
||||||
|
const data_user = await queryEndpoint(CONFIG.USER);
|
||||||
|
if (data_user.App) {
|
||||||
|
console.log('Se obtuvo la info del usuario');
|
||||||
|
await fillForm(data_user.App, data_user.User, data_user.Password, data_user.Xpath_user, data_user.Xpath_pass);
|
||||||
|
} else {
|
||||||
|
console.log('No se pudo obtener la info del usuario');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error in main process:', error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prevent app from closing when all windows are closed
|
||||||
|
app.on('window-all-closed', () => {
|
||||||
|
if (process.platform !== 'darwin') {
|
||||||
|
app.quit();
|
||||||
|
}
|
||||||
|
});
|
||||||
Generated
+5412
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,41 @@
|
|||||||
|
{
|
||||||
|
"name": "shopee",
|
||||||
|
"version": "0.0.4",
|
||||||
|
"main": "main.js",
|
||||||
|
"scripts": {
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1",
|
||||||
|
"start": "electron . --dev",
|
||||||
|
"build": "electron-builder"
|
||||||
|
},
|
||||||
|
"keywords": [],
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC",
|
||||||
|
"description": "Shopee",
|
||||||
|
"dependencies": {
|
||||||
|
"node-fetch": "^3.3.2",
|
||||||
|
"puppeteer": "^24.1.1",
|
||||||
|
"puppeteer-core": "^24.2.0",
|
||||||
|
"puppeteer-in-electron": "^3.0.5"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"electron": "^34.1.1",
|
||||||
|
"electron-builder": "^25.1.8"
|
||||||
|
},
|
||||||
|
"type": "module",
|
||||||
|
"build": {
|
||||||
|
"appId": "com.shopee.id",
|
||||||
|
"productName": "Shopee",
|
||||||
|
"win": {
|
||||||
|
"target": "nsis",
|
||||||
|
"icon": "shopee.ico"
|
||||||
|
},
|
||||||
|
"nsis": {
|
||||||
|
"perMachine": true,
|
||||||
|
"runAfterFinish": true,
|
||||||
|
"installerIcon": "shopee.ico",
|
||||||
|
"uninstallerIcon": "shopee.ico",
|
||||||
|
"createDesktopShortcut": false,
|
||||||
|
"createStartMenuShortcut": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
Binary file not shown.
|
After Width: | Height: | Size: 11 KiB |
Reference in New Issue
Block a user