@gnpdev/rpa-tools 1.0.17 → 1.0.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +29 -8
- package/package.json +1 -1
- package/src/errorCapture.js +4 -4
- package/src/index.d.ts +6 -0
- package/src/index.js +26 -1
- package/src/watcher.js +138 -136
package/README.md
CHANGED
|
@@ -92,9 +92,25 @@ if (credentials) {
|
|
|
92
92
|
}
|
|
93
93
|
```
|
|
94
94
|
|
|
95
|
-
### 4.
|
|
96
|
-
|
|
95
|
+
### 4. Verificar Estado del Bot (Kill Switch)
|
|
96
|
+
Permite consultar si el bot debe seguir en ejecución según la base de datos (columna `estado` en `bots.tb_bots`). Útil para implementar una parada de emergencia desde el panel de control.
|
|
97
97
|
|
|
98
|
+
```javascript
|
|
99
|
+
import { rpa, logger } from './lib/rpa.js';
|
|
100
|
+
|
|
101
|
+
const active = await rpa.isActive();
|
|
102
|
+
|
|
103
|
+
if (!active) {
|
|
104
|
+
logger.warn('El bot ha sido desactivado desde la base de datos. Finalizando...');
|
|
105
|
+
await rpa.destroy();
|
|
106
|
+
process.exit(0);
|
|
107
|
+
}
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### 5. Capturas de Pantalla bajo demanda (Playwright / Puppeteer)
|
|
111
|
+
Activa el watcher pasando la instancia de `page`. El bot detectará cambios en la tabla de la DB para tomar screenshots automáticamente. Compatible con ambos motores.
|
|
112
|
+
|
|
113
|
+
#### Ejemplo con Playwright
|
|
98
114
|
```javascript
|
|
99
115
|
import { chromium } from 'playwright';
|
|
100
116
|
import { rpa } from './lib/rpa.js';
|
|
@@ -102,20 +118,25 @@ import { rpa } from './lib/rpa.js';
|
|
|
102
118
|
const browser = await chromium.launch();
|
|
103
119
|
const page = await browser.newPage();
|
|
104
120
|
|
|
105
|
-
// Inicia el monitoreo (polling cada 3s por defecto)
|
|
106
121
|
rpa.watchDebugFlag(page);
|
|
122
|
+
```
|
|
107
123
|
|
|
124
|
+
#### Ejemplo con Puppeteer
|
|
125
|
+
```javascript
|
|
126
|
+
import puppeteer from 'puppeteer';
|
|
127
|
+
import { rpa } from './lib/rpa.js';
|
|
108
128
|
|
|
109
|
-
|
|
110
|
-
|
|
129
|
+
const browser = await puppeteer.launch();
|
|
130
|
+
const page = await browser.newPage();
|
|
111
131
|
|
|
112
|
-
|
|
113
|
-
browser.on('disconnected', () => rpa.destroy());
|
|
132
|
+
rpa.watchDebugFlag(page);
|
|
114
133
|
```
|
|
115
134
|
|
|
116
|
-
###
|
|
135
|
+
### 6. Captura Automática de Errores (Screenshot + Trace)
|
|
117
136
|
Captura el estado completo del bot cuando ocurre una excepción. **`captureError` detecta automáticamente el paso actual** definido con `step()`.
|
|
118
137
|
|
|
138
|
+
> **Nota:** El soporte de `tracing` (archivos .zip de depuración) es exclusivo de **Playwright**. En **Puppeteer** se capturará el error y el screenshot, pero el `traceKey` será nulo.
|
|
139
|
+
|
|
119
140
|
```javascript
|
|
120
141
|
import { chromium } from 'playwright';
|
|
121
142
|
import { rpa, logger, step } from './lib/rpa.js';
|
package/package.json
CHANGED
package/src/errorCapture.js
CHANGED
|
@@ -35,13 +35,13 @@ function errorPrefix(botId) {
|
|
|
35
35
|
|
|
36
36
|
/**
|
|
37
37
|
* Captura el estado completo cuando ocurre un error:
|
|
38
|
-
* - Screenshot WebP de la página actual
|
|
39
|
-
* - Trace de Playwright (.zip con video, red, consola, DOM snapshots)
|
|
38
|
+
* - Screenshot WebP de la página actual (Playwright/Puppeteer)
|
|
39
|
+
* - Trace de Playwright (.zip con video, red, consola, DOM snapshots) [Opcional]
|
|
40
40
|
* - Registro en bots.tb_error_bots
|
|
41
41
|
*
|
|
42
42
|
* @param {object} p
|
|
43
|
-
* @param {
|
|
44
|
-
* @param {
|
|
43
|
+
* @param {any} p.page - Instancia de Page (Playwright o Puppeteer)
|
|
44
|
+
* @param {any} [p.context] - BrowserContext (solo Playwright para tracing)
|
|
45
45
|
* @param {Error} p.err
|
|
46
46
|
* @param {string} p.botId - UUID del bot
|
|
47
47
|
* @param {import('pg').Pool} p.pool
|
package/src/index.d.ts
CHANGED
|
@@ -74,6 +74,12 @@ export interface RpaTools {
|
|
|
74
74
|
*/
|
|
75
75
|
getCredentials: (nombre: string) => Promise<{ usuario: string; password: string; idUsuario: string } | null>;
|
|
76
76
|
|
|
77
|
+
/**
|
|
78
|
+
* Consulta si el bot está activo en la base de datos (columna 'estado').
|
|
79
|
+
* @returns {Promise<boolean>}
|
|
80
|
+
*/
|
|
81
|
+
isActive: () => Promise<boolean>;
|
|
82
|
+
|
|
77
83
|
/**
|
|
78
84
|
* Captura screenshot, trace y registra error en base de datos.
|
|
79
85
|
*/
|
package/src/index.js
CHANGED
|
@@ -14,7 +14,7 @@ const { captureError } = require('./errorCapture');
|
|
|
14
14
|
* @param {object} opts.minio - config de MinIO
|
|
15
15
|
* @param {object} [opts.log] - opciones de Pino
|
|
16
16
|
*
|
|
17
|
-
* @returns {{ logger, watchDebugFlag, getCredentials, destroy }}
|
|
17
|
+
* @returns {{ logger, state, step, watchDebugFlag, getCredentials, isActive, captureError, destroy }}
|
|
18
18
|
*/
|
|
19
19
|
async function createRpaTools(opts = {}) {
|
|
20
20
|
const {
|
|
@@ -55,8 +55,33 @@ async function createRpaTools(opts = {}) {
|
|
|
55
55
|
state.currentStep = name;
|
|
56
56
|
logger.info(name + ' - Nuevo paso');
|
|
57
57
|
},
|
|
58
|
+
/**
|
|
59
|
+
* Inicia el polling que activa/desactiva screenshots según bot_debug_config.
|
|
60
|
+
* @param {any} page - Objeto Page de Playwright o Puppeteer
|
|
61
|
+
* @param {number} [pollMs=3000] - Intervalo de polling en ms
|
|
62
|
+
*/
|
|
58
63
|
watchDebugFlag: (page, pollMs) => watcher.watch(page, pollMs),
|
|
59
64
|
getCredentials: (nombre) => credentials.getAppCredentials(nombre),
|
|
65
|
+
/**
|
|
66
|
+
* Consulta si el bot está activo en la base de datos (columna 'estado').
|
|
67
|
+
* @returns {Promise<boolean>}
|
|
68
|
+
*/
|
|
69
|
+
isActive: async () => {
|
|
70
|
+
try {
|
|
71
|
+
const { rows } = await db.query(
|
|
72
|
+
'SELECT estado FROM bots.tb_bots WHERE id = $1',
|
|
73
|
+
[botId]
|
|
74
|
+
);
|
|
75
|
+
return rows.length > 0 ? rows[0].estado : false;
|
|
76
|
+
} catch (err) {
|
|
77
|
+
logger.error({ err }, 'Error consultando estado en isActive');
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
/**
|
|
82
|
+
* Captura screenshot y registra error en base de datos.
|
|
83
|
+
* Soporta Playwright (con tracing opcional) y Puppeteer.
|
|
84
|
+
*/
|
|
60
85
|
captureError: ({ page, context, err, step }) => captureError({
|
|
61
86
|
page,
|
|
62
87
|
context,
|
package/src/watcher.js
CHANGED
|
@@ -1,137 +1,139 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
const { uploadScreenshot } = require('./storage');
|
|
3
|
-
|
|
4
|
-
class ScreenshotWatcher {
|
|
5
|
-
/**
|
|
6
|
-
* @param {object} p
|
|
7
|
-
* @param {string} p.botId
|
|
8
|
-
* @param {import('pg').Pool} p.pool
|
|
9
|
-
* @param {import('minio').Client} p.minioClient
|
|
10
|
-
* @param {string} p.bucket
|
|
11
|
-
* @param {import('pino').Logger} p.logger
|
|
12
|
-
* @param {object} [p.screenshot]
|
|
13
|
-
* @param {'webp'|'jpeg'} [p.screenshot.format='webp']
|
|
14
|
-
* @param {number} [p.screenshot.quality=70] - calidad conversión 1-100
|
|
15
|
-
* @param {number} [p.screenshot.captureQuality=90] - calidad captura Playwright
|
|
16
|
-
*/
|
|
17
|
-
constructor({ botId, pool, minioClient, bucket, logger, screenshot = {} }) {
|
|
18
|
-
this.botId = botId;
|
|
19
|
-
this.pool = pool;
|
|
20
|
-
this.minioClient = minioClient;
|
|
21
|
-
this.bucket = bucket;
|
|
22
|
-
this.logger = logger;
|
|
23
|
-
|
|
24
|
-
this.screenshotOpts = {
|
|
25
|
-
format: screenshot.format ?? 'webp',
|
|
26
|
-
quality: screenshot.quality ?? 70,
|
|
27
|
-
captureQuality: screenshot.captureQuality ?? 90,
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
this._watchTimer = null;
|
|
31
|
-
this._shotTimer = null;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Inicia el polling que activa/desactiva screenshots según bot_debug_config.
|
|
36
|
-
* Llama una sola vez al arrancar el bot.
|
|
37
|
-
*
|
|
38
|
-
* @param {
|
|
39
|
-
* @param {number} [pollMs=3000] - cada cuántos ms revisar la BD
|
|
40
|
-
*/
|
|
41
|
-
watch(page, pollMs = 3000) {
|
|
42
|
-
if (this._watchTimer) {
|
|
43
|
-
this.logger.warn('ScreenshotWatcher ya está corriendo, ignorando llamada duplicada');
|
|
44
|
-
return;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
this.logger.info({ pollMs }, 'Iniciando polling de debug config');
|
|
48
|
-
|
|
49
|
-
this._watchTimer = setInterval(async () => {
|
|
50
|
-
try {
|
|
51
|
-
const { rows } = await this.pool.query(
|
|
52
|
-
`SELECT screenshots_activo, intervalo_sec
|
|
53
|
-
FROM bots.tb_bots
|
|
54
|
-
WHERE id = $1`,
|
|
55
|
-
[this.botId]
|
|
56
|
-
);
|
|
57
|
-
|
|
58
|
-
const cfg = rows[0];
|
|
59
|
-
// console.log('DEBUG CONFIG', cfg); // <-- loguear config para debug
|
|
60
|
-
|
|
61
|
-
if (!cfg) {
|
|
62
|
-
this.logger.debug('Sin config en bot_debug_config para este bot');
|
|
63
|
-
return;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
if (cfg.screenshots_activo && !this._shotTimer) {
|
|
67
|
-
this.logger.info({ intervalSec: cfg.intervalo_sec }, 'Screenshots activados');
|
|
68
|
-
this._startScreenshots(page, cfg.intervalo_sec * 1000);
|
|
69
|
-
return;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
if (!cfg.screenshots_activo && this._shotTimer) {
|
|
73
|
-
this.logger.info('Screenshots desactivados');
|
|
74
|
-
this._stopScreenshots();
|
|
75
|
-
}
|
|
76
|
-
} catch (err) {
|
|
77
|
-
this.logger.error({ err }, 'Error leyendo bot_debug_config');
|
|
78
|
-
}
|
|
79
|
-
}, pollMs);
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
/**
|
|
83
|
-
* @private
|
|
84
|
-
*/
|
|
85
|
-
_startScreenshots(page, intervalMs) {
|
|
86
|
-
this._shotTimer = setInterval(async () => {
|
|
87
|
-
const t0 = Date.now();
|
|
88
|
-
|
|
89
|
-
try {
|
|
90
|
-
// Playwright captura en jpeg (más rápido), sharp convierte a WebP después
|
|
91
|
-
const rawBuffer = await page.screenshot({
|
|
92
|
-
type: 'jpeg',
|
|
93
|
-
quality: this.screenshotOpts.captureQuality,
|
|
94
|
-
fullPage: false,
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
const key = await uploadScreenshot(
|
|
98
|
-
this.minioClient,
|
|
99
|
-
this.bucket,
|
|
100
|
-
this.botId,
|
|
101
|
-
rawBuffer,
|
|
102
|
-
{
|
|
103
|
-
format: this.screenshotOpts.format,
|
|
104
|
-
quality: this.screenshotOpts.quality,
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
this.
|
|
133
|
-
this.
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
1
|
+
'use strict';
|
|
2
|
+
const { uploadScreenshot } = require('./storage');
|
|
3
|
+
|
|
4
|
+
class ScreenshotWatcher {
|
|
5
|
+
/**
|
|
6
|
+
* @param {object} p
|
|
7
|
+
* @param {string} p.botId
|
|
8
|
+
* @param {import('pg').Pool} p.pool
|
|
9
|
+
* @param {import('minio').Client} p.minioClient
|
|
10
|
+
* @param {string} p.bucket
|
|
11
|
+
* @param {import('pino').Logger} p.logger
|
|
12
|
+
* @param {object} [p.screenshot]
|
|
13
|
+
* @param {'webp'|'jpeg'} [p.screenshot.format='webp']
|
|
14
|
+
* @param {number} [p.screenshot.quality=70] - calidad conversión 1-100
|
|
15
|
+
* @param {number} [p.screenshot.captureQuality=90] - calidad captura Playwright
|
|
16
|
+
*/
|
|
17
|
+
constructor({ botId, pool, minioClient, bucket, logger, screenshot = {} }) {
|
|
18
|
+
this.botId = botId;
|
|
19
|
+
this.pool = pool;
|
|
20
|
+
this.minioClient = minioClient;
|
|
21
|
+
this.bucket = bucket;
|
|
22
|
+
this.logger = logger;
|
|
23
|
+
|
|
24
|
+
this.screenshotOpts = {
|
|
25
|
+
format: screenshot.format ?? 'webp',
|
|
26
|
+
quality: screenshot.quality ?? 70,
|
|
27
|
+
captureQuality: screenshot.captureQuality ?? 90,
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
this._watchTimer = null;
|
|
31
|
+
this._shotTimer = null;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Inicia el polling que activa/desactiva screenshots según bot_debug_config.
|
|
36
|
+
* Llama una sola vez al arrancar el bot.
|
|
37
|
+
*
|
|
38
|
+
* @param {any} page - Objeto Page de Playwright o Puppeteer
|
|
39
|
+
* @param {number} [pollMs=3000] - cada cuántos ms revisar la BD
|
|
40
|
+
*/
|
|
41
|
+
watch(page, pollMs = 3000) {
|
|
42
|
+
if (this._watchTimer) {
|
|
43
|
+
this.logger.warn('ScreenshotWatcher ya está corriendo, ignorando llamada duplicada');
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
this.logger.info({ pollMs }, 'Iniciando polling de debug config');
|
|
48
|
+
|
|
49
|
+
this._watchTimer = setInterval(async () => {
|
|
50
|
+
try {
|
|
51
|
+
const { rows } = await this.pool.query(
|
|
52
|
+
`SELECT screenshots_activo, intervalo_sec
|
|
53
|
+
FROM bots.tb_bots
|
|
54
|
+
WHERE id = $1`,
|
|
55
|
+
[this.botId]
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
const cfg = rows[0];
|
|
59
|
+
// console.log('DEBUG CONFIG', cfg); // <-- loguear config para debug
|
|
60
|
+
|
|
61
|
+
if (!cfg) {
|
|
62
|
+
this.logger.debug('Sin config en bot_debug_config para este bot');
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (cfg.screenshots_activo && !this._shotTimer) {
|
|
67
|
+
this.logger.info({ intervalSec: cfg.intervalo_sec }, 'Screenshots activados');
|
|
68
|
+
this._startScreenshots(page, cfg.intervalo_sec * 1000);
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (!cfg.screenshots_activo && this._shotTimer) {
|
|
73
|
+
this.logger.info('Screenshots desactivados');
|
|
74
|
+
this._stopScreenshots();
|
|
75
|
+
}
|
|
76
|
+
} catch (err) {
|
|
77
|
+
this.logger.error({ err }, 'Error leyendo bot_debug_config');
|
|
78
|
+
}
|
|
79
|
+
}, pollMs);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* @private
|
|
84
|
+
*/
|
|
85
|
+
_startScreenshots(page, intervalMs) {
|
|
86
|
+
this._shotTimer = setInterval(async () => {
|
|
87
|
+
const t0 = Date.now();
|
|
88
|
+
|
|
89
|
+
try {
|
|
90
|
+
// Playwright captura en jpeg (más rápido), sharp convierte a WebP después
|
|
91
|
+
const rawBuffer = await page.screenshot({
|
|
92
|
+
type: 'jpeg',
|
|
93
|
+
quality: this.screenshotOpts.captureQuality,
|
|
94
|
+
fullPage: false,
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
const key = await uploadScreenshot(
|
|
98
|
+
this.minioClient,
|
|
99
|
+
this.bucket,
|
|
100
|
+
this.botId,
|
|
101
|
+
rawBuffer,
|
|
102
|
+
{
|
|
103
|
+
format: this.screenshotOpts.format,
|
|
104
|
+
quality: this.screenshotOpts.quality,
|
|
105
|
+
pool: this.pool,
|
|
106
|
+
logger: this.logger,
|
|
107
|
+
}
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
this.logger.debug(
|
|
111
|
+
{ key, ms: Date.now() - t0 },
|
|
112
|
+
'Screenshot subido'
|
|
113
|
+
);
|
|
114
|
+
} catch (err) {
|
|
115
|
+
this.logger.error({ err }, 'Error capturando o subiendo screenshot');
|
|
116
|
+
}
|
|
117
|
+
}, intervalMs);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* @private
|
|
122
|
+
*/
|
|
123
|
+
_stopScreenshots() {
|
|
124
|
+
clearInterval(this._shotTimer);
|
|
125
|
+
this._shotTimer = null;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Detiene todos los intervalos. Llamar siempre al cerrar el bot.
|
|
130
|
+
*/
|
|
131
|
+
destroy() {
|
|
132
|
+
this._stopScreenshots();
|
|
133
|
+
clearInterval(this._watchTimer);
|
|
134
|
+
this._watchTimer = null;
|
|
135
|
+
this.logger.info('ScreenshotWatcher destruido');
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
137
139
|
module.exports = { ScreenshotWatcher };
|