@wabot-dev/framework 0.5.2 → 0.5.4
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 +9 -9
- package/dist/src/addon/auth/jwt/JwtGuardMiddleware.js +4 -1
- package/dist/src/addon/chat-bot/google/GoogleChatAdapter.js +5 -1
- package/dist/src/addon/chat-controller/whatsapp/WhatsAppChannel.js +1 -1
- package/dist/src/addon/chat-controller/whatsapp/cloud-api/WhatsAppReceiverByCloudApi.js +3 -3
- package/dist/src/addon/chat-controller/whatsapp/cloud-api/WhatsAppSenderByCloudApi.js +1 -1
- package/dist/src/addon/chat-controller/whatsapp/proxy/WhatsAppSenderByWabotProxy.js +1 -1
- package/dist/src/core/description/metadata/@description.js +1 -1
- package/dist/src/core/env/Env.js +1 -1
- package/dist/src/core/logger/Logger.js +30 -0
- package/dist/src/core/validation/metadata/@isArray.js +1 -1
- package/dist/src/core/validation/metadata/@isModel.js +1 -1
- package/dist/src/core/validation/validators/is-date/@isDate.js +1 -1
- package/dist/src/core/validation/validators/is-number/@isNumber.js +1 -1
- package/dist/src/core/validation/validators/is-string/@isString.js +1 -1
- package/dist/src/feature/async/CronScheduler.js +3 -3
- package/dist/src/feature/async/JobRunner.js +1 -1
- package/dist/src/feature/async/JobScheduler.js +9 -6
- package/dist/src/feature/chat-bot/ChatMemory.js +2 -2
- package/dist/src/feature/http/HttpServerProvider.js +1 -1
- package/dist/src/feature/pg/PgLockKey.js +4 -4
- package/dist/src/feature/rest-controller/runRestControllers.js +1 -1
- package/dist/src/feature/socket-controller/runSocketControllers.js +2 -2
- package/dist/src/index.d.ts +30 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -6,10 +6,9 @@
|
|
|
6
6
|
[](https://opensource.org/licenses/MIT)
|
|
7
7
|
[](https://docs.wabot.dev)
|
|
8
8
|
|
|
9
|
-
|
|
10
9
|
**Un framework moderno y flexible para crear bots con TypeScript e Inteligencia Artificial**
|
|
11
10
|
|
|
12
|
-
[Documentación](https://docs.wabot.dev) • [Inicio Rápido](https://docs.wabot.dev/guides/start-new-project/)
|
|
11
|
+
[Documentación](https://docs.wabot.dev) • [Inicio Rápido](https://docs.wabot.dev/guides/start-new-project/)
|
|
13
12
|
|
|
14
13
|
</div>
|
|
15
14
|
|
|
@@ -43,11 +42,11 @@ visita nuestra documentación 📘 **[Ver guía completa de inicio →](https://
|
|
|
43
42
|
|
|
44
43
|
Wabot se integra nativamente con las principales plataformas de mensajería:
|
|
45
44
|
|
|
46
|
-
| Plataforma
|
|
47
|
-
|
|
45
|
+
| Plataforma | Estado |
|
|
46
|
+
| --------------- | ------------ |
|
|
48
47
|
| 📱 **WhatsApp** | ✅ Soportado |
|
|
49
48
|
| ✈️ **Telegram** | ✅ Soportado |
|
|
50
|
-
| 🔌 **Socket**
|
|
49
|
+
| 🔌 **Socket** | ✅ Soportado |
|
|
51
50
|
|
|
52
51
|
---
|
|
53
52
|
|
|
@@ -55,11 +54,12 @@ Wabot se integra nativamente con las principales plataformas de mensajería:
|
|
|
55
54
|
|
|
56
55
|
Potencia tu bot con los principales proveedores de inteligencia artificial:
|
|
57
56
|
|
|
58
|
-
| Proveedor
|
|
59
|
-
|
|
60
|
-
| 🟢 **OpenAI**
|
|
61
|
-
| 🔵 **Google**
|
|
57
|
+
| Proveedor | Soporte en Wabot |
|
|
58
|
+
| ---------------- | ----------------------- |
|
|
59
|
+
| 🟢 **OpenAI** | ✅ Integración completa |
|
|
60
|
+
| 🔵 **Google** | ✅ Integración completa |
|
|
62
61
|
| 🟣 **Anthropic** | ✅ Integración completa |
|
|
62
|
+
|
|
63
63
|
---
|
|
64
64
|
|
|
65
65
|
## 📚 Documentación
|
|
@@ -20,7 +20,10 @@ let JwtGuardMiddleware = class JwtGuardMiddleware {
|
|
|
20
20
|
}
|
|
21
21
|
const parts = authorization.split(' ');
|
|
22
22
|
if (parts.length !== 2) {
|
|
23
|
-
throw new CustomError({
|
|
23
|
+
throw new CustomError({
|
|
24
|
+
httpCode: 401,
|
|
25
|
+
message: 'Authorization header must be: Bearer <token>',
|
|
26
|
+
});
|
|
24
27
|
}
|
|
25
28
|
const [bearer, token] = parts;
|
|
26
29
|
if (bearer.toLowerCase() !== 'bearer') {
|
|
@@ -124,7 +124,11 @@ let GoogleChatAdapter = class GoogleChatAdapter {
|
|
|
124
124
|
}
|
|
125
125
|
nextItems.push({
|
|
126
126
|
type: 'functionCall',
|
|
127
|
-
functionCall: {
|
|
127
|
+
functionCall: {
|
|
128
|
+
id: id ?? Random.alphaNumericLowerCase(10),
|
|
129
|
+
name,
|
|
130
|
+
arguments: args && JSON.stringify(args),
|
|
131
|
+
},
|
|
128
132
|
});
|
|
129
133
|
}
|
|
130
134
|
}
|
|
@@ -33,7 +33,7 @@ class WhatsAppReceiverByCloudApi extends WhatsAppReceiver {
|
|
|
33
33
|
res.status(200).send(challenge);
|
|
34
34
|
}
|
|
35
35
|
catch (e) {
|
|
36
|
-
this.logger.error(e);
|
|
36
|
+
this.logger.error('WhatsApp webhook verification failed', e);
|
|
37
37
|
res.sendStatus(500);
|
|
38
38
|
return;
|
|
39
39
|
}
|
|
@@ -66,7 +66,7 @@ class WhatsAppReceiverByCloudApi extends WhatsAppReceiver {
|
|
|
66
66
|
}
|
|
67
67
|
}
|
|
68
68
|
catch (err) {
|
|
69
|
-
this.logger.error(err);
|
|
69
|
+
this.logger.error('Failed to handle WhatsApp webhook payload', err);
|
|
70
70
|
}
|
|
71
71
|
}
|
|
72
72
|
async emmitMessage(metadata, message, contact) {
|
|
@@ -75,7 +75,7 @@ class WhatsAppReceiverByCloudApi extends WhatsAppReceiver {
|
|
|
75
75
|
return;
|
|
76
76
|
}
|
|
77
77
|
if (message.type !== 'text') {
|
|
78
|
-
this.logger.
|
|
78
|
+
this.logger.warn(`Message type '${message.type}' is not supported yet`);
|
|
79
79
|
return;
|
|
80
80
|
}
|
|
81
81
|
const channelName = 'WhatsAppChannel';
|
|
@@ -114,7 +114,7 @@ let WhatsAppSenderByCloudApi = class WhatsAppSenderByCloudApi extends WhatsAppSe
|
|
|
114
114
|
return template;
|
|
115
115
|
}
|
|
116
116
|
catch (error) {
|
|
117
|
-
this.logger.error(error);
|
|
117
|
+
this.logger.error('Failed to fetch WhatsApp template', error);
|
|
118
118
|
throw error;
|
|
119
119
|
}
|
|
120
120
|
}
|
|
@@ -43,7 +43,7 @@ let WhatsAppSenderByWabotProxy = class WhatsAppSenderByWabotProxy extends WhatsA
|
|
|
43
43
|
}
|
|
44
44
|
}
|
|
45
45
|
catch (err) {
|
|
46
|
-
this.logger.error(err);
|
|
46
|
+
this.logger.error(`Failed to send WhatsApp from '${request.from}' to '${request.to}'`, err);
|
|
47
47
|
throw new Error(undefined, { cause: err });
|
|
48
48
|
}
|
|
49
49
|
}
|
package/dist/src/core/env/Env.js
CHANGED
|
@@ -13,6 +13,30 @@ const levelToSeverity = {
|
|
|
13
13
|
error: 'error',
|
|
14
14
|
fatal: 'fatal',
|
|
15
15
|
};
|
|
16
|
+
/**
|
|
17
|
+
* Logger with 6 severity levels. Uses the `debug` library for output.
|
|
18
|
+
*
|
|
19
|
+
* ## Level verbosity contract
|
|
20
|
+
*
|
|
21
|
+
* - **fatal** — The process cannot continue. Something is critically broken
|
|
22
|
+
* (uncaught exceptions, unhandled rejections). Investigate immediately.
|
|
23
|
+
*
|
|
24
|
+
* - **error** — An operation failed unexpectedly. Always include: what failed,
|
|
25
|
+
* why (the Error), and enough context to locate the problem (IDs, names).
|
|
26
|
+
*
|
|
27
|
+
* - **warn** — Something unusual happened but the system handled it gracefully.
|
|
28
|
+
* Known limitations, safety guards triggered, recoverable issues.
|
|
29
|
+
*
|
|
30
|
+
* - **info** — Key lifecycle events the user cares about: systems starting or
|
|
31
|
+
* stopping, configuration applied, significant state changes. Should read
|
|
32
|
+
* like a high-level audit log.
|
|
33
|
+
*
|
|
34
|
+
* - **debug** — Internal operational details for developers troubleshooting.
|
|
35
|
+
* Step-by-step flow, lock acquisition, query execution, reconciliation steps.
|
|
36
|
+
*
|
|
37
|
+
* - **trace** — Very fine-grained: every HTTP request, every socket event,
|
|
38
|
+
* every message sent or received.
|
|
39
|
+
*/
|
|
16
40
|
class Logger {
|
|
17
41
|
static monitor = null;
|
|
18
42
|
debuggers;
|
|
@@ -32,21 +56,27 @@ class Logger {
|
|
|
32
56
|
static getMonitor() {
|
|
33
57
|
return Logger.monitor;
|
|
34
58
|
}
|
|
59
|
+
/** Very fine-grained: every HTTP request, socket event, message sent/received. */
|
|
35
60
|
trace(...args) {
|
|
36
61
|
this.log('trace', args);
|
|
37
62
|
}
|
|
63
|
+
/** Internal operational details for developers: step-by-step flow, lock acquisition, queries. */
|
|
38
64
|
debug(...args) {
|
|
39
65
|
this.log('debug', args);
|
|
40
66
|
}
|
|
67
|
+
/** Key lifecycle events: systems start/stop, configuration applied, significant state changes. */
|
|
41
68
|
info(...args) {
|
|
42
69
|
this.log('info', args);
|
|
43
70
|
}
|
|
71
|
+
/** Something unusual happened but the system recovered. Known limitations, safety guards. */
|
|
44
72
|
warn(...args) {
|
|
45
73
|
this.log('warn', args);
|
|
46
74
|
}
|
|
75
|
+
/** Operation failed unexpectedly. Always include: what failed + why (Error) + identifiers. */
|
|
47
76
|
error(...args) {
|
|
48
77
|
this.log('error', args);
|
|
49
78
|
}
|
|
79
|
+
/** Process cannot continue. Uncaught exceptions, unhandled rejections. Investigate immediately. */
|
|
50
80
|
fatal(...args) {
|
|
51
81
|
this.log('fatal', args);
|
|
52
82
|
}
|
|
@@ -60,12 +60,12 @@ let CronScheduler = class CronScheduler {
|
|
|
60
60
|
this.logger.debug(`start reconciliation of crons ${configToReconciliate.map((x) => x.name).join(', ')}`);
|
|
61
61
|
try {
|
|
62
62
|
const cronJobs = await Promise.all(configToReconciliate.map((x) => this.reconciliateConfig(x)));
|
|
63
|
-
this.logger.info(`
|
|
63
|
+
this.logger.info(`Cron reconciliation succeeded for ${configToReconciliate.map((x) => x.name).join(', ')}`);
|
|
64
64
|
this.jobScheduler.start(cronJobs.map((x) => x.commandName));
|
|
65
65
|
this.jobWatchdog.start(cronJobs.map((x) => x.commandName));
|
|
66
66
|
}
|
|
67
67
|
catch (e) {
|
|
68
|
-
this.logger.error(e);
|
|
68
|
+
this.logger.error('Cron reconciliation failed', e);
|
|
69
69
|
this.stop(configToReconciliate);
|
|
70
70
|
this.logger.error(`reconciliation fail - stopped crons ${configToReconciliate.map((x) => x.name).join(', ')}`);
|
|
71
71
|
throw e;
|
|
@@ -88,7 +88,7 @@ let CronScheduler = class CronScheduler {
|
|
|
88
88
|
});
|
|
89
89
|
}
|
|
90
90
|
catch (e) {
|
|
91
|
-
this.logger.error(e);
|
|
91
|
+
this.logger.error('Cron tick failed', e);
|
|
92
92
|
}
|
|
93
93
|
finally {
|
|
94
94
|
this.timeout = setTimeout(() => this.tick(), interval * 1000);
|
|
@@ -45,7 +45,7 @@ let JobRunner = class JobRunner {
|
|
|
45
45
|
job.setAsSuccess();
|
|
46
46
|
}
|
|
47
47
|
catch (e) {
|
|
48
|
-
this.logger.error(e);
|
|
48
|
+
this.logger.error(`Failed to run command '${job.commandName}'`, e);
|
|
49
49
|
job.setAsFailed(e instanceof Error ? e : new Error('Invalid Job error'));
|
|
50
50
|
}
|
|
51
51
|
finally {
|
|
@@ -22,16 +22,15 @@ let JobScheduler = class JobScheduler {
|
|
|
22
22
|
this.env = env;
|
|
23
23
|
}
|
|
24
24
|
start(commands) {
|
|
25
|
-
this.logger.info(`starting handlers for commands ${commands}`);
|
|
26
25
|
commands.forEach((x) => this.commands.add(x));
|
|
27
|
-
this.logger.info(`
|
|
26
|
+
this.logger.info(`Starting job handlers for commands ${commands.join(', ')}`);
|
|
28
27
|
if (this.commands.size > 0 && !this.running) {
|
|
29
28
|
this.running = true;
|
|
30
29
|
this.tick();
|
|
31
30
|
}
|
|
32
31
|
}
|
|
33
32
|
stop(commands) {
|
|
34
|
-
this.logger.info(`
|
|
33
|
+
this.logger.info(`Stopping job handlers for commands ${commands.join(', ')}`);
|
|
35
34
|
commands.forEach((x) => this.commands.delete(x));
|
|
36
35
|
if (this.commands.size === 0) {
|
|
37
36
|
this.running = false;
|
|
@@ -42,7 +41,9 @@ let JobScheduler = class JobScheduler {
|
|
|
42
41
|
}
|
|
43
42
|
tryExecuteNow(job) {
|
|
44
43
|
if (this.commands.has(job.commandName))
|
|
45
|
-
this.executor
|
|
44
|
+
this.executor
|
|
45
|
+
.execute(job)
|
|
46
|
+
.catch((e) => this.logger.error(`Failed to execute job ${job.id}`, e));
|
|
46
47
|
}
|
|
47
48
|
async tick() {
|
|
48
49
|
if (!this.running)
|
|
@@ -57,12 +58,14 @@ let JobScheduler = class JobScheduler {
|
|
|
57
58
|
await this.locker.withKey('wabot-job-scheduler-loop').run(async () => {
|
|
58
59
|
const jobs = await this.repo.findPendingForRunFrom(new Date(), this.executor.remainingSlots());
|
|
59
60
|
const readyToRunJobs = jobs.filter((job) => this.commands.has(job.commandName) && job.isScheduleReady());
|
|
60
|
-
readyToRunJobs.forEach((j) => this.executor
|
|
61
|
+
readyToRunJobs.forEach((j) => this.executor
|
|
62
|
+
.execute(j)
|
|
63
|
+
.catch((e) => this.logger.error(`Failed to execute job ${j.id}`, e)));
|
|
61
64
|
await new Promise((r) => setTimeout(r, 10));
|
|
62
65
|
});
|
|
63
66
|
}
|
|
64
67
|
catch (e) {
|
|
65
|
-
this.logger.error(e);
|
|
68
|
+
this.logger.error('Job scheduler tick failed', e);
|
|
66
69
|
}
|
|
67
70
|
finally {
|
|
68
71
|
this.timeout = setTimeout(() => this.tick(), interval * 1000);
|
|
@@ -20,7 +20,7 @@ let HttpServerProvider = class HttpServerProvider {
|
|
|
20
20
|
this.listening = true;
|
|
21
21
|
const PORT = process.env.PORT || 3000;
|
|
22
22
|
this.server.listen(PORT, () => {
|
|
23
|
-
this.logger.info(`
|
|
23
|
+
this.logger.info(`Server listening on port ${PORT}`);
|
|
24
24
|
});
|
|
25
25
|
}
|
|
26
26
|
};
|
|
@@ -16,9 +16,9 @@ class PgLockKey {
|
|
|
16
16
|
return withPgClient(this.pool, async (client) => {
|
|
17
17
|
let locked = false;
|
|
18
18
|
try {
|
|
19
|
-
this.logger.debug(`try to
|
|
19
|
+
this.logger.debug(`try to acquire ${this.key}`);
|
|
20
20
|
await client.query('SELECT pg_advisory_lock($1)', [this.value]);
|
|
21
|
-
this.logger.debug(`
|
|
21
|
+
this.logger.debug(`acquired ${this.key}`);
|
|
22
22
|
locked = true;
|
|
23
23
|
return await fn();
|
|
24
24
|
}
|
|
@@ -27,7 +27,7 @@ class PgLockKey {
|
|
|
27
27
|
this.logger.debug(`try to release ${this.key}`);
|
|
28
28
|
const res = await client.query('SELECT pg_advisory_unlock($1) AS unlocked', [this.value]);
|
|
29
29
|
if (!res.rows[0]?.unlocked) {
|
|
30
|
-
this.logger.error(
|
|
30
|
+
this.logger.error(`Failed to release lock '${this.key}'`);
|
|
31
31
|
}
|
|
32
32
|
else {
|
|
33
33
|
this.logger.debug(`released ${this.key}`);
|
|
@@ -50,7 +50,7 @@ class PgLockKey {
|
|
|
50
50
|
if (locked) {
|
|
51
51
|
const res = await client.query('SELECT pg_advisory_unlock($1) AS unlocked', [this.value]);
|
|
52
52
|
if (!res.rows[0]?.unlocked) {
|
|
53
|
-
this.logger.error(
|
|
53
|
+
this.logger.error(`Failed to release lock '${this.key}'`);
|
|
54
54
|
}
|
|
55
55
|
}
|
|
56
56
|
}
|
|
@@ -68,7 +68,7 @@ function runRestControllers(controllers) {
|
|
|
68
68
|
res.status(200).json(response ?? null);
|
|
69
69
|
}
|
|
70
70
|
catch (err) {
|
|
71
|
-
logger.error(err);
|
|
71
|
+
logger.error(`${method.toUpperCase()} ${route} failed`, err);
|
|
72
72
|
if (err instanceof Error) {
|
|
73
73
|
const keys = Object.keys(err).filter((key) => !['message', 'stack'].includes(key));
|
|
74
74
|
const { httpCode, ...info } = keys.reduce((acc, key) => {
|
|
@@ -70,7 +70,7 @@ function runSocketControllers(controllers) {
|
|
|
70
70
|
}
|
|
71
71
|
}
|
|
72
72
|
catch (err) {
|
|
73
|
-
logger.error(err);
|
|
73
|
+
logger.error(`Event '${event.config.event}' on '${namespace}' failed`, err);
|
|
74
74
|
if (err instanceof Error) {
|
|
75
75
|
const keys = Object.keys(err).filter((key) => !['message', 'stack'].includes(key));
|
|
76
76
|
const { httpCode, ...info } = keys.reduce((acc, key) => {
|
|
@@ -108,7 +108,7 @@ function runSocketControllers(controllers) {
|
|
|
108
108
|
}
|
|
109
109
|
}
|
|
110
110
|
catch (err) {
|
|
111
|
-
logger.error(err);
|
|
111
|
+
logger.error(`Connection setup on '${namespace}' failed`, err);
|
|
112
112
|
socket.disconnect();
|
|
113
113
|
connectionContainer.dispose();
|
|
114
114
|
}
|
package/dist/src/index.d.ts
CHANGED
|
@@ -129,6 +129,30 @@ interface IErrorMonitor {
|
|
|
129
129
|
captureMessage(message: string, context: IErrorMonitorContext): void;
|
|
130
130
|
}
|
|
131
131
|
|
|
132
|
+
/**
|
|
133
|
+
* Logger with 6 severity levels. Uses the `debug` library for output.
|
|
134
|
+
*
|
|
135
|
+
* ## Level verbosity contract
|
|
136
|
+
*
|
|
137
|
+
* - **fatal** — The process cannot continue. Something is critically broken
|
|
138
|
+
* (uncaught exceptions, unhandled rejections). Investigate immediately.
|
|
139
|
+
*
|
|
140
|
+
* - **error** — An operation failed unexpectedly. Always include: what failed,
|
|
141
|
+
* why (the Error), and enough context to locate the problem (IDs, names).
|
|
142
|
+
*
|
|
143
|
+
* - **warn** — Something unusual happened but the system handled it gracefully.
|
|
144
|
+
* Known limitations, safety guards triggered, recoverable issues.
|
|
145
|
+
*
|
|
146
|
+
* - **info** — Key lifecycle events the user cares about: systems starting or
|
|
147
|
+
* stopping, configuration applied, significant state changes. Should read
|
|
148
|
+
* like a high-level audit log.
|
|
149
|
+
*
|
|
150
|
+
* - **debug** — Internal operational details for developers troubleshooting.
|
|
151
|
+
* Step-by-step flow, lock acquisition, query execution, reconciliation steps.
|
|
152
|
+
*
|
|
153
|
+
* - **trace** — Very fine-grained: every HTTP request, every socket event,
|
|
154
|
+
* every message sent or received.
|
|
155
|
+
*/
|
|
132
156
|
declare class Logger {
|
|
133
157
|
private static monitor;
|
|
134
158
|
private debuggers;
|
|
@@ -136,11 +160,17 @@ declare class Logger {
|
|
|
136
160
|
constructor(name: string);
|
|
137
161
|
static setMonitor(monitor: IErrorMonitor): void;
|
|
138
162
|
static getMonitor(): IErrorMonitor | null;
|
|
163
|
+
/** Very fine-grained: every HTTP request, socket event, message sent/received. */
|
|
139
164
|
trace(...args: any[]): void;
|
|
165
|
+
/** Internal operational details for developers: step-by-step flow, lock acquisition, queries. */
|
|
140
166
|
debug(...args: any[]): void;
|
|
167
|
+
/** Key lifecycle events: systems start/stop, configuration applied, significant state changes. */
|
|
141
168
|
info(...args: any[]): void;
|
|
169
|
+
/** Something unusual happened but the system recovered. Known limitations, safety guards. */
|
|
142
170
|
warn(...args: any[]): void;
|
|
171
|
+
/** Operation failed unexpectedly. Always include: what failed + why (Error) + identifiers. */
|
|
143
172
|
error(...args: any[]): void;
|
|
173
|
+
/** Process cannot continue. Uncaught exceptions, unhandled rejections. Investigate immediately. */
|
|
144
174
|
fatal(...args: any[]): void;
|
|
145
175
|
private log;
|
|
146
176
|
private sendToMonitor;
|