@wabot-dev/framework 0.9.1 → 0.9.5

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.
Files changed (84) hide show
  1. package/dist/src/addon/async/in-memory/InMemoryCronJobRepository.js +45 -0
  2. package/dist/src/addon/async/in-memory/InMemoryJobRepository.js +75 -0
  3. package/dist/src/addon/async/in-memory/index.js +2 -0
  4. package/dist/src/addon/async/pg/PgCronJobRepository.js +7 -5
  5. package/dist/src/addon/async/pg/PgJobRepository.js +8 -5
  6. package/dist/src/addon/async/pg/PgTransactionAdapter.js +25 -0
  7. package/dist/src/addon/async/pg/index.js +3 -0
  8. package/dist/src/addon/auth/api-key/ApiKey.js +4 -4
  9. package/dist/src/addon/auth/api-key/PgApiKeyRepository.js +9 -8
  10. package/dist/src/addon/auth/jwt/JwtRefreshToken.js +4 -4
  11. package/dist/src/addon/auth/jwt/PgJwtRefreshTokenRepository.js +6 -5
  12. package/dist/src/addon/chat-bot/{ram/RamChatMemory.js → in-memory/InMemoryChatMemory.js} +2 -2
  13. package/dist/src/addon/chat-bot/{ram/RamChatRepository.js → in-memory/InMemoryChatRepository.js} +6 -6
  14. package/dist/src/addon/chat-bot/in-memory/index.js +2 -0
  15. package/dist/src/addon/chat-bot/pg/PgChatMemory.js +8 -7
  16. package/dist/src/addon/chat-bot/pg/PgChatRepository.js +7 -6
  17. package/dist/src/addon/chat-bot/pg/index.js +2 -0
  18. package/dist/src/addon/chat-controller/cmd/@cmd.js +7 -2
  19. package/dist/src/addon/chat-controller/cmd/CmdChannel.js +85 -61
  20. package/dist/src/addon/chat-controller/cmd/CmdChannelConfig.js +8 -0
  21. package/dist/src/addon/chat-controller/cmd/CmdChannelServer.js +169 -0
  22. package/dist/src/addon/chat-controller/cmd/cmdChannelSocketPath.js +16 -0
  23. package/dist/src/addon/chat-controller/cmd/runCmdClient.js +226 -0
  24. package/dist/src/addon/chat-controller/socket/@socket.js +3 -1
  25. package/dist/src/addon/chat-controller/socket/SocketChannelConfig.js +4 -4
  26. package/dist/src/addon/chat-controller/whatsapp/WhatsAppSender.js +2 -72
  27. package/dist/src/addon/chat-controller/whatsapp/{WhatsAppReceiver.js → cloud-api/WhatsAppApiReceiver.js} +2 -2
  28. package/dist/src/addon/chat-controller/whatsapp/cloud-api/WhatsAppApiSender.js +67 -0
  29. package/dist/src/addon/chat-controller/whatsapp/cloud-api/WhatsAppReceiverByCloudApi.js +5 -7
  30. package/dist/src/addon/chat-controller/whatsapp/wasender/@wasender.js +23 -0
  31. package/dist/src/addon/chat-controller/{wasender/WhatsAppByWasenderChannel.js → whatsapp/wasender/WasenderChannel.js} +20 -20
  32. package/dist/src/addon/chat-controller/{wasender/WhatsAppByWasenderChannelConfig.js → whatsapp/wasender/WasenderChannelConfig.js} +3 -3
  33. package/dist/src/addon/chat-controller/whatsapp/wasender/WasenderChannelName.js +3 -0
  34. package/dist/src/addon/chat-controller/{wasender/WhatsAppReceiverByWasender.js → whatsapp/wasender/WasenderReceiver.js} +7 -7
  35. package/dist/src/addon/chat-controller/{wasender/WhatsAppSenderByWasender.js → whatsapp/wasender/WasenderSender.js} +8 -5
  36. package/dist/src/addon/chat-controller/{wasender → whatsapp/wasender}/WasenderWebhookController.js +4 -4
  37. package/dist/src/addon/lock/InMemoryLockKey.js +45 -0
  38. package/dist/src/addon/lock/InMemoryLocker.js +15 -0
  39. package/dist/src/addon/lock/index.js +2 -0
  40. package/dist/src/core/repository/CrudRepository.js +25 -0
  41. package/dist/src/feature/async/{@cron.js → @cronHandler.js} +2 -2
  42. package/dist/src/feature/async/@transaction.js +22 -0
  43. package/dist/src/feature/async/AsyncMetadataStore.js +6 -0
  44. package/dist/src/feature/async/TransactionMetadataStore.js +28 -0
  45. package/dist/src/feature/chat-controller/metadata/ControllerMetadataStore.js +3 -0
  46. package/dist/src/feature/pg/@pgExtension.js +33 -0
  47. package/dist/src/feature/pg/PgJsonRepositoryAdapter.js +50 -0
  48. package/dist/src/feature/pg/index.js +10 -0
  49. package/dist/src/feature/project-runner/ProjectRunner.js +276 -0
  50. package/dist/src/feature/repository/@memoryExtension.js +29 -0
  51. package/dist/src/feature/repository/@query.js +22 -0
  52. package/dist/src/feature/repository/@queryExtension.js +21 -0
  53. package/dist/src/feature/repository/@repository.js +170 -0
  54. package/dist/src/feature/repository/MemoryRepositoryAdapter.js +110 -0
  55. package/dist/src/feature/repository/RepositoryAdapterRegistry.js +27 -0
  56. package/dist/src/feature/repository/RepositoryMetadataStore.js +102 -0
  57. package/dist/src/feature/repository/evaluateQueryAst.js +134 -0
  58. package/dist/src/feature/rest-controller/metadata/RestControllerMetadataStore.js +3 -0
  59. package/dist/src/feature/socket-controller/metadata/SocketControllerMetadataStore.js +3 -0
  60. package/dist/src/index.d.ts +377 -298
  61. package/dist/src/index.js +40 -34
  62. package/package.json +9 -7
  63. package/dist/src/addon/chat-controller/wasender/@whatsAppByWasender.js +0 -20
  64. package/dist/src/addon/chat-controller/wasender/whatsAppByWasenderChannelName.js +0 -3
  65. package/dist/src/addon/chat-controller/whatsapp/@whatsApp.js +0 -20
  66. package/dist/src/addon/chat-controller/whatsapp/EnvWhatsAppRepository.js +0 -49
  67. package/dist/src/addon/chat-controller/whatsapp/PgWhatsAppRepository.js +0 -48
  68. package/dist/src/addon/chat-controller/whatsapp/WhatsApp.js +0 -30
  69. package/dist/src/addon/chat-controller/whatsapp/WhatsAppChannel.js +0 -58
  70. package/dist/src/addon/chat-controller/whatsapp/WhatsAppChannelConfig.js +0 -8
  71. package/dist/src/addon/chat-controller/whatsapp/WhatsAppRepository.js +0 -10
  72. package/dist/src/addon/chat-controller/whatsapp/cloud-api/WhatsAppSenderByCloudApi.js +0 -133
  73. package/dist/src/addon/chat-controller/whatsapp/proxy/WhatsAppProxyContracts.js +0 -5
  74. package/dist/src/addon/chat-controller/whatsapp/proxy/WhatsAppReceiverByWabotProxy.js +0 -67
  75. package/dist/src/addon/chat-controller/whatsapp/proxy/WhatsAppSenderByWabotProxy.js +0 -63
  76. package/dist/src/addon/chat-controller/whatsapp/proxy/WhatsAppWabotProxyConnection.js +0 -45
  77. package/dist/src/feature/pg/query/@pgJsonRepository.js +0 -73
  78. package/dist/src/feature/pg/query/@query.js +0 -14
  79. package/dist/src/feature/pg/query/PgJsonRepository.js +0 -23
  80. package/dist/src/feature/pg/query/PgRepositoryMetadataStore.js +0 -44
  81. /package/dist/src/addon/chat-controller/whatsapp/{whatsAppChannelName.js → cloud-api/whatsAppChannelName.js} +0 -0
  82. /package/dist/src/addon/chat-controller/{wasender → whatsapp/wasender}/extractNumberFromWasenderKey.js +0 -0
  83. /package/dist/src/feature/pg/{query/buildQuerySql.js → buildQuerySql.js} +0 -0
  84. /package/dist/src/feature/{pg/query → repository}/parseQueryMethodName.js +0 -0
@@ -0,0 +1,8 @@
1
+ class CmdChannelConfig {
2
+ route;
3
+ constructor(route) {
4
+ this.route = route;
5
+ }
6
+ }
7
+
8
+ export { CmdChannelConfig };
@@ -0,0 +1,169 @@
1
+ import { __decorate } from 'tslib';
2
+ import * as fs from 'node:fs';
3
+ import * as net from 'node:net';
4
+ import * as path from 'node:path';
5
+ import { singleton } from '../../../core/injection/index.js';
6
+ import { Logger } from '../../../core/logger/Logger.js';
7
+ import { cmdChannelSocketPath } from './cmdChannelSocketPath.js';
8
+
9
+ const logger = new Logger('wabot:cmd-channel-server');
10
+ let CmdChannelServer = class CmdChannelServer {
11
+ server = null;
12
+ channels = new Map();
13
+ client = null;
14
+ buffer = '';
15
+ activeRoute = null;
16
+ register(route, handlers) {
17
+ this.channels.set(route, handlers);
18
+ this.ensureStarted();
19
+ }
20
+ unregister(route) {
21
+ this.channels.delete(route);
22
+ if (this.activeRoute === route) {
23
+ this.activeRoute = null;
24
+ }
25
+ }
26
+ ensureStarted() {
27
+ if (this.server)
28
+ return;
29
+ const socketPath = cmdChannelSocketPath();
30
+ if (process.platform !== 'win32') {
31
+ try {
32
+ fs.unlinkSync(socketPath);
33
+ }
34
+ catch {
35
+ // ignore: socket file did not exist
36
+ }
37
+ fs.mkdirSync(path.dirname(socketPath), { recursive: true });
38
+ }
39
+ this.server = net.createServer((socket) => this.handleConnection(socket));
40
+ this.server.on('error', (err) => logger.error('cmd channel server error', err));
41
+ this.server.listen(socketPath, () => {
42
+ logger.info(`cmd channel server listening at ${socketPath}`);
43
+ });
44
+ process.once('exit', () => this.shutdown());
45
+ process.once('SIGINT', () => {
46
+ this.shutdown();
47
+ process.exit(0);
48
+ });
49
+ process.once('SIGTERM', () => {
50
+ this.shutdown();
51
+ process.exit(0);
52
+ });
53
+ }
54
+ shutdown() {
55
+ if (this.client) {
56
+ this.client.destroy();
57
+ this.client = null;
58
+ }
59
+ if (this.server) {
60
+ this.server.close();
61
+ this.server = null;
62
+ }
63
+ if (process.platform !== 'win32') {
64
+ try {
65
+ fs.unlinkSync(cmdChannelSocketPath());
66
+ }
67
+ catch {
68
+ // ignore
69
+ }
70
+ }
71
+ }
72
+ handleConnection(socket) {
73
+ if (this.client) {
74
+ this.send(socket, {
75
+ type: 'error',
76
+ message: 'Another cmd client is already connected',
77
+ });
78
+ socket.end();
79
+ return;
80
+ }
81
+ this.client = socket;
82
+ this.buffer = '';
83
+ this.activeRoute = null;
84
+ socket.on('data', (chunk) => this.handleData(chunk.toString()));
85
+ socket.on('close', () => {
86
+ if (this.client === socket) {
87
+ this.client = null;
88
+ this.activeRoute = null;
89
+ this.buffer = '';
90
+ }
91
+ });
92
+ socket.on('error', (err) => {
93
+ logger.warn('cmd client connection error', err);
94
+ if (this.client === socket) {
95
+ this.client = null;
96
+ this.activeRoute = null;
97
+ }
98
+ });
99
+ }
100
+ handleData(data) {
101
+ this.buffer += data;
102
+ let idx;
103
+ while ((idx = this.buffer.indexOf('\n')) !== -1) {
104
+ const line = this.buffer.slice(0, idx);
105
+ this.buffer = this.buffer.slice(idx + 1);
106
+ if (!line)
107
+ continue;
108
+ let msg;
109
+ try {
110
+ msg = JSON.parse(line);
111
+ }
112
+ catch {
113
+ this.sendToClient({ type: 'error', message: 'invalid json' });
114
+ continue;
115
+ }
116
+ this.handleMessage(msg).catch((err) => {
117
+ logger.error('cmd channel server error handling message', err);
118
+ this.sendToClient({ type: 'error', message: err.message });
119
+ });
120
+ }
121
+ }
122
+ async handleMessage(msg) {
123
+ switch (msg.type) {
124
+ case 'hello':
125
+ this.sendToClient({
126
+ type: 'channels',
127
+ list: Array.from(this.channels.keys()).map((route) => ({ route })),
128
+ });
129
+ return;
130
+ case 'select':
131
+ if (!this.channels.has(msg.route)) {
132
+ this.sendToClient({ type: 'error', message: `unknown route: ${msg.route}` });
133
+ return;
134
+ }
135
+ this.activeRoute = msg.route;
136
+ this.sendToClient({ type: 'selected', route: msg.route });
137
+ return;
138
+ case 'message': {
139
+ if (!this.activeRoute) {
140
+ this.sendToClient({ type: 'error', message: 'no channel selected' });
141
+ return;
142
+ }
143
+ const handlers = this.channels.get(this.activeRoute);
144
+ if (!handlers) {
145
+ this.sendToClient({ type: 'error', message: 'channel no longer available' });
146
+ this.activeRoute = null;
147
+ return;
148
+ }
149
+ await handlers.onMessage(msg.text, (reply) => {
150
+ this.sendToClient({ type: 'reply', ...reply });
151
+ });
152
+ return;
153
+ }
154
+ }
155
+ }
156
+ sendToClient(msg) {
157
+ if (!this.client)
158
+ return;
159
+ this.send(this.client, msg);
160
+ }
161
+ send(socket, msg) {
162
+ socket.write(JSON.stringify(msg) + '\n');
163
+ }
164
+ };
165
+ CmdChannelServer = __decorate([
166
+ singleton()
167
+ ], CmdChannelServer);
168
+
169
+ export { CmdChannelServer };
@@ -0,0 +1,16 @@
1
+ import * as crypto from 'node:crypto';
2
+ import * as path from 'node:path';
3
+
4
+ function cmdChannelSocketPath() {
5
+ if (process.platform === 'win32') {
6
+ const cwdHash = crypto
7
+ .createHash('sha1')
8
+ .update(process.cwd())
9
+ .digest('hex')
10
+ .slice(0, 12);
11
+ return `\\\\.\\pipe\\wabot-cmd-channel-${cwdHash}`;
12
+ }
13
+ return path.resolve(process.cwd(), '.cmd-channel/socket');
14
+ }
15
+
16
+ export { cmdChannelSocketPath };
@@ -0,0 +1,226 @@
1
+ import * as net from 'node:net';
2
+ import * as readline from 'node:readline';
3
+ import { cmdChannelSocketPath } from './cmdChannelSocketPath.js';
4
+
5
+ const useColor = process.stdout.isTTY && !process.env.NO_COLOR && process.env.TERM !== 'dumb';
6
+ const ansi = (code) => (text) => useColor ? `\x1b[${code}m${text}\x1b[0m` : text;
7
+ const bold = ansi('1');
8
+ const dim = ansi('2');
9
+ const cyan = ansi('1;36');
10
+ const green = ansi('1;32');
11
+ const greenText = ansi('32');
12
+ const red = ansi('1;31');
13
+ const yellow = ansi('33');
14
+ const COMMANDS = ['/channels', '/help', '/exit'];
15
+ const HELP_LINES = [
16
+ 'Commands:',
17
+ ' /channels list channels and switch',
18
+ ' /help show this help',
19
+ ' /exit quit',
20
+ ];
21
+ const RECONNECT_DELAY_MS = 1000;
22
+ function runCmdClient() {
23
+ const socketPath = cmdChannelSocketPath();
24
+ let socket = null;
25
+ let buffer = '';
26
+ let routes = [];
27
+ let state = 'disconnected';
28
+ let selected = null;
29
+ let reconnectTimer = null;
30
+ let waitingNoticeShown = false;
31
+ let exiting = false;
32
+ let restoring = false;
33
+ const completer = (line) => {
34
+ if (line.startsWith('/')) {
35
+ const hits = COMMANDS.filter((c) => c.startsWith(line));
36
+ return [hits.length > 0 ? hits : [...COMMANDS], line];
37
+ }
38
+ if (state === 'choosing') {
39
+ const hits = routes
40
+ .map((r) => r.route)
41
+ .filter((r) => r.toLowerCase().startsWith(line.toLowerCase()));
42
+ return [hits, line];
43
+ }
44
+ return [[], line];
45
+ };
46
+ const rl = readline.createInterface({
47
+ input: process.stdin,
48
+ output: process.stdout,
49
+ completer,
50
+ });
51
+ const send = (msg) => {
52
+ if (!socket) {
53
+ process.stderr.write(red('not connected to framework — waiting for server...') + '\n');
54
+ rl.prompt();
55
+ return false;
56
+ }
57
+ socket.write(JSON.stringify(msg) + '\n');
58
+ return true;
59
+ };
60
+ const printChannels = (list) => {
61
+ routes = list;
62
+ if (list.length === 0) {
63
+ process.stdout.write(yellow('No cmd channels are registered on the server yet.') + '\n');
64
+ rl.prompt();
65
+ return;
66
+ }
67
+ if (list.length === 1) {
68
+ send({ type: 'select', route: list[0].route });
69
+ return;
70
+ }
71
+ process.stdout.write(bold('Available cmd channels:') + '\n');
72
+ list.forEach((entry, i) => {
73
+ const marker = entry.route === selected ? ' ' + yellow('(active)') : '';
74
+ process.stdout.write(` ${dim(`${i + 1}.`)} ${cyan(entry.route)}${marker}\n`);
75
+ });
76
+ state = 'choosing';
77
+ rl.setPrompt(bold('select # ') + dim('> '));
78
+ rl.prompt();
79
+ };
80
+ const handleServerMessage = (msg) => {
81
+ switch (msg.type) {
82
+ case 'channels':
83
+ printChannels(msg.list);
84
+ return;
85
+ case 'selected':
86
+ selected = msg.route;
87
+ state = 'chatting';
88
+ if (!restoring) {
89
+ process.stdout.write(green(`[connected to ${msg.route}]`) + '\n');
90
+ }
91
+ restoring = false;
92
+ rl.setPrompt(cyan(msg.route) + dim(' > '));
93
+ rl.prompt();
94
+ return;
95
+ case 'reply':
96
+ process.stdout.write(`\n${green(`[${msg.senderName ?? 'bot'}]:`)} ${greenText(msg.text)}\n\n`);
97
+ rl.prompt();
98
+ return;
99
+ case 'error':
100
+ process.stderr.write(red('error: ') + red(msg.message) + '\n');
101
+ rl.prompt();
102
+ return;
103
+ }
104
+ };
105
+ const scheduleReconnect = () => {
106
+ if (exiting || reconnectTimer)
107
+ return;
108
+ reconnectTimer = setTimeout(() => {
109
+ reconnectTimer = null;
110
+ connect();
111
+ }, RECONNECT_DELAY_MS);
112
+ };
113
+ const connect = () => {
114
+ if (exiting)
115
+ return;
116
+ buffer = '';
117
+ socket = net.connect(socketPath);
118
+ socket.on('connect', () => {
119
+ waitingNoticeShown = false;
120
+ // Silent reconnect when we're restoring a prior selection;
121
+ // otherwise announce the connection and ask for channels.
122
+ if (selected) {
123
+ restoring = true;
124
+ socket.write(JSON.stringify({ type: 'select', route: selected }) + '\n');
125
+ state = 'chatting';
126
+ rl.setPrompt(cyan(selected) + dim(' > '));
127
+ rl.prompt();
128
+ }
129
+ else {
130
+ process.stdout.write(green('[connected to framework]') + '\n');
131
+ socket.write(JSON.stringify({ type: 'hello' }) + '\n');
132
+ }
133
+ });
134
+ socket.on('data', (chunk) => {
135
+ buffer += chunk.toString();
136
+ let idx;
137
+ while ((idx = buffer.indexOf('\n')) !== -1) {
138
+ const line = buffer.slice(0, idx);
139
+ buffer = buffer.slice(idx + 1);
140
+ if (!line)
141
+ continue;
142
+ try {
143
+ handleServerMessage(JSON.parse(line));
144
+ }
145
+ catch (err) {
146
+ process.stderr.write(red(`invalid server message: ${err.message}`) + '\n');
147
+ }
148
+ }
149
+ });
150
+ socket.on('error', (err) => {
151
+ const transient = err.code === 'ENOENT' ||
152
+ err.code === 'ECONNREFUSED' ||
153
+ err.code === 'ECONNRESET' ||
154
+ err.code === 'EPIPE';
155
+ if (!transient) {
156
+ process.stderr.write(red(`socket error: ${err.message}`) + '\n');
157
+ }
158
+ });
159
+ socket.on('close', () => {
160
+ const wasConnected = state !== 'disconnected';
161
+ state = 'disconnected';
162
+ socket = null;
163
+ if (exiting)
164
+ return;
165
+ if (wasConnected) {
166
+ process.stdout.write(yellow('\n[disconnected — waiting for framework to come back...]') + '\n');
167
+ }
168
+ else if (!waitingNoticeShown && !selected) {
169
+ waitingNoticeShown = true;
170
+ process.stdout.write(dim('Waiting for framework...') + '\n');
171
+ }
172
+ scheduleReconnect();
173
+ });
174
+ };
175
+ const cleanup = (code) => {
176
+ exiting = true;
177
+ if (reconnectTimer) {
178
+ clearTimeout(reconnectTimer);
179
+ reconnectTimer = null;
180
+ }
181
+ rl.close();
182
+ if (socket)
183
+ socket.end();
184
+ process.exit(code);
185
+ };
186
+ rl.on('line', (input) => {
187
+ const trimmed = input.trim();
188
+ if (!trimmed) {
189
+ rl.prompt();
190
+ return;
191
+ }
192
+ if (trimmed === '/exit' || trimmed.toLowerCase() === 'exit') {
193
+ cleanup(0);
194
+ return;
195
+ }
196
+ if (trimmed === '/help') {
197
+ process.stdout.write(HELP_LINES.join('\n') + '\n');
198
+ rl.prompt();
199
+ return;
200
+ }
201
+ if (trimmed === '/channels') {
202
+ send({ type: 'hello' });
203
+ return;
204
+ }
205
+ if (state === 'disconnected') {
206
+ process.stderr.write(red('not connected to framework — waiting for server...') + '\n');
207
+ rl.prompt();
208
+ return;
209
+ }
210
+ if (state === 'choosing') {
211
+ const num = Number.parseInt(trimmed, 10);
212
+ if (!Number.isInteger(num) || num < 1 || num > routes.length) {
213
+ process.stderr.write(red(`Invalid selection. Enter a number between 1 and ${routes.length}.`) + '\n');
214
+ rl.prompt();
215
+ return;
216
+ }
217
+ send({ type: 'select', route: routes[num - 1].route });
218
+ return;
219
+ }
220
+ send({ type: 'message', text: trimmed });
221
+ });
222
+ rl.on('close', () => cleanup(0));
223
+ connect();
224
+ }
225
+
226
+ export { runCmdClient };
@@ -2,17 +2,19 @@ import { container } from '../../../core/injection/index.js';
2
2
  import { ControllerMetadataStore } from '../../../feature/chat-controller/metadata/ControllerMetadataStore.js';
3
3
  import '../../../feature/chat-controller/ChatResolver.js';
4
4
  import '../../../feature/chat-controller/runChatControllers.js';
5
+ import { resolveConfigReferences } from '../../../core/config/resolver.js';
5
6
  import { SocketChannel } from './SocketChannel.js';
6
7
  import { SocketChannelConfig } from './SocketChannelConfig.js';
7
8
 
8
9
  function socket(config) {
9
10
  return function (target, propertyKey) {
11
+ const resolved = resolveConfigReferences(config);
10
12
  const store = container.resolve(ControllerMetadataStore);
11
13
  store.saveChannelMetadata({
12
14
  channelConstructor: SocketChannel,
13
15
  functionName: propertyKey.toString(),
14
16
  controllerConstructor: target.constructor,
15
- channelConfig: new SocketChannelConfig(config.namespace, config.handshakeMidlewares),
17
+ channelConfig: new SocketChannelConfig(resolved),
16
18
  });
17
19
  };
18
20
  }
@@ -4,14 +4,14 @@ import { injectable } from '../../../core/injection/index.js';
4
4
  let SocketChannelConfig = class SocketChannelConfig {
5
5
  namespace;
6
6
  handshakeMidlewares;
7
- constructor(namespace, handshakeMidlewares) {
8
- this.namespace = namespace;
9
- this.handshakeMidlewares = handshakeMidlewares;
7
+ constructor(config) {
8
+ this.namespace = config.namespace;
9
+ this.handshakeMidlewares = config.handshakeMidlewares;
10
10
  }
11
11
  };
12
12
  SocketChannelConfig = __decorate([
13
13
  injectable(),
14
- __metadata("design:paramtypes", [String, Array])
14
+ __metadata("design:paramtypes", [Object])
15
15
  ], SocketChannelConfig);
16
16
 
17
17
  export { SocketChannelConfig };
@@ -1,80 +1,10 @@
1
- import '../../../feature/chat-bot/ChatAdapterRegistry.js';
2
- import '../../../feature/chat-bot/ChatBot.js';
3
- import { ChatItem } from '../../../feature/chat-bot/ChatItem.js';
4
- import '../../../feature/chat-bot/ChatOperator.js';
5
- import '../../../feature/chat-bot/UnionChatAdapter.js';
6
- import '../../../core/injection/index.js';
7
- import '../../../feature/chat-bot/metadata/ChatAdapterMetadataStore.js';
8
- import 'uuid';
9
- import '../../../feature/chat-bot/metadata/ChatBotMetadataStore.js';
10
- import '../../../core/error/setupErrorHandlers.js';
11
-
12
1
  class WhatsAppSender {
13
- chatRepository;
14
- chatResolver;
15
- whatsAppRepository;
16
- constructor(chatRepository, chatResolver, whatsAppRepository) {
17
- this.chatRepository = chatRepository;
18
- this.chatResolver = chatResolver;
19
- this.whatsAppRepository = whatsAppRepository;
20
- }
21
- async sendWhatsApp(request, options) {
22
- throw new Error('Not implemented');
23
- }
24
- async sendWhatsAppTemplate(request, options) {
2
+ async sendMessage(request) {
25
3
  throw new Error('Not implemented');
26
4
  }
27
- getWhatsAppTemplate(request) {
5
+ async sendTemplate(request) {
28
6
  throw new Error('Not implemented');
29
7
  }
30
- async mapTemplateToChatMessage(request) {
31
- const template = await this.getWhatsAppTemplate({
32
- from: request.from,
33
- templateName: request.templateMessage.templateName,
34
- languageCode: request.templateMessage.languageCode,
35
- });
36
- if (!template) {
37
- throw new Error(`WhatsAppTemplate with name ${request.templateMessage.templateName} and language ${request.templateMessage.languageCode} not found`);
38
- }
39
- const components = template.components
40
- .filter((x) => x.text != null)
41
- .map((x) => x.text)
42
- .join('\n');
43
- return {
44
- text: this.replaceTemplateParameters(components, request.templateMessage.parameters),
45
- senderName: request.senderName,
46
- };
47
- }
48
- async writePrivateChatMemory(message, to) {
49
- const chatConnection = {
50
- id: to,
51
- chatType: 'PRIVATE',
52
- channelName: 'WhatsAppChannel',
53
- };
54
- const chat = await this.chatResolver.resolve(chatConnection);
55
- const chatMemory = await this.chatRepository.findMemory(chat.id);
56
- const chatItem = new ChatItem({
57
- type: 'botMessage',
58
- botMessage: message,
59
- });
60
- await chatMemory.create(chatItem);
61
- }
62
- replaceTemplateParameters(template, data) {
63
- let result = template;
64
- data.forEach((param) => {
65
- if (param.type === 'text' && param.parameter_name) {
66
- const tag = `{{${param.parameter_name}}}`;
67
- result = result.split(tag).join(param.text);
68
- }
69
- });
70
- data.forEach((param, index) => {
71
- if (param.type === 'text') {
72
- const tag = `{{${index + 1}}}`;
73
- result = result.split(tag).join(param.text);
74
- }
75
- });
76
- return result;
77
- }
78
8
  }
79
9
 
80
10
  export { WhatsAppSender };
@@ -1,4 +1,4 @@
1
- class WhatsAppReceiver {
1
+ class WhatsAppApiReceiver {
2
2
  connect() {
3
3
  throw new Error('Not Implemented');
4
4
  }
@@ -10,4 +10,4 @@ class WhatsAppReceiver {
10
10
  }
11
11
  }
12
12
 
13
- export { WhatsAppReceiver };
13
+ export { WhatsAppApiReceiver };
@@ -0,0 +1,67 @@
1
+ class WhatsAppApiSender {
2
+ accessToken;
3
+ businessNumberId;
4
+ constructor(accessToken, businessNumberId) {
5
+ this.accessToken = accessToken;
6
+ this.businessNumberId = businessNumberId;
7
+ }
8
+ async sendMessage(request) {
9
+ const url = `https://graph.facebook.com/v23.0/${this.businessNumberId}/messages`;
10
+ const payload = {
11
+ messaging_product: 'whatsapp',
12
+ recipient_type: 'individual',
13
+ to: request.to,
14
+ type: 'text',
15
+ text: {
16
+ preview_url: false,
17
+ body: request.message.text ?? '',
18
+ },
19
+ };
20
+ const response = await fetch(url, {
21
+ method: 'POST',
22
+ headers: {
23
+ Authorization: `Bearer ${this.accessToken}`,
24
+ 'Content-Type': 'application/json',
25
+ },
26
+ body: JSON.stringify(payload),
27
+ });
28
+ const data = await response.json();
29
+ if (!response.ok) {
30
+ throw new Error(JSON.stringify(data));
31
+ }
32
+ }
33
+ async sendTemplate(request) {
34
+ const payload = {
35
+ messaging_product: 'whatsapp',
36
+ recipient_type: 'individual',
37
+ to: request.to,
38
+ type: 'template',
39
+ template: {
40
+ name: request.templateData.templateName,
41
+ language: {
42
+ code: request.templateData.languageCode,
43
+ },
44
+ components: [
45
+ {
46
+ type: 'body',
47
+ parameters: request.templateData.parameters,
48
+ },
49
+ ],
50
+ },
51
+ };
52
+ const response = await fetch(`https://graph.facebook.com/v23.0/${this.businessNumberId}/messages`, {
53
+ method: 'POST',
54
+ headers: {
55
+ Authorization: `Bearer ${this.accessToken}`,
56
+ 'Content-Type': 'application/json',
57
+ },
58
+ body: JSON.stringify(payload),
59
+ });
60
+ const data = await response.json();
61
+ if (!response.ok) {
62
+ throw new Error(JSON.stringify(data));
63
+ }
64
+ }
65
+ }
66
+
67
+ export { WhatsAppApiSender };
@@ -1,19 +1,17 @@
1
1
  import { Logger } from '../../../../core/logger/Logger.js';
2
- import { whatsAppChannelName } from '../whatsAppChannelName.js';
2
+ import { whatsAppChannelName } from './whatsAppChannelName.js';
3
3
  import { json } from 'express';
4
- import { WhatsAppReceiver } from '../WhatsAppReceiver.js';
4
+ import { WhatsAppApiReceiver } from './WhatsAppApiReceiver.js';
5
5
 
6
- class WhatsAppReceiverByCloudApi extends WhatsAppReceiver {
6
+ class WhatsAppReceiverByCloudApi extends WhatsAppApiReceiver {
7
7
  expressProvider;
8
- whatsAppRepository;
9
8
  listeners = new Map();
10
9
  expressApp;
11
10
  logger = new Logger('wabot:whatsapp-receiver-by-webhook');
12
11
  webhookPath = '/whatsapp/web-hook/:slug';
13
- constructor(expressProvider, whatsAppRepository) {
12
+ constructor(expressProvider) {
14
13
  super();
15
14
  this.expressProvider = expressProvider;
16
- this.whatsAppRepository = whatsAppRepository;
17
15
  this.expressApp = this.expressProvider.getExpress();
18
16
  }
19
17
  async connect() {
@@ -26,7 +24,7 @@ class WhatsAppReceiverByCloudApi extends WhatsAppReceiver {
26
24
  res.sendStatus(400);
27
25
  return;
28
26
  }
29
- const whatsApp = await this.whatsAppRepository.findBySlug(req.params.slug);
27
+ const whatsApp = 5; // TODO: await this.whatsAppRepository.findBySlug(req.params.slug)
30
28
  if (!whatsApp || mode !== 'subscribe' || token !== whatsApp.getVerifyToken()) {
31
29
  res.sendStatus(403);
32
30
  return;
@@ -0,0 +1,23 @@
1
+ import { container } from '../../../../core/injection/index.js';
2
+ import { resolveConfigReferences } from '../../../../core/config/resolver.js';
3
+ import { WasenderChannelConfig } from './WasenderChannelConfig.js';
4
+ import { ControllerMetadataStore } from '../../../../feature/chat-controller/metadata/ControllerMetadataStore.js';
5
+ import '../../../../feature/chat-controller/ChatResolver.js';
6
+ import '../../../../feature/chat-controller/runChatControllers.js';
7
+ import { WasenderChannel } from './WasenderChannel.js';
8
+
9
+ function wasender(config) {
10
+ return function (target, propertyKey) {
11
+ const cfg = config ?? {};
12
+ const resolvedConfig = resolveConfigReferences(cfg);
13
+ const store = container.resolve(ControllerMetadataStore);
14
+ store.saveChannelMetadata({
15
+ channelConstructor: WasenderChannel,
16
+ functionName: propertyKey.toString(),
17
+ controllerConstructor: target.constructor,
18
+ channelConfig: new WasenderChannelConfig(resolvedConfig),
19
+ });
20
+ };
21
+ }
22
+
23
+ export { wasender };