@wabot-dev/framework 0.5.7 → 0.5.9

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 (29) hide show
  1. package/dist/src/addon/chat-controller/cmd/CmdChannel.js +6 -2
  2. package/dist/src/addon/chat-controller/cmd/cmdChannelName.js +3 -0
  3. package/dist/src/addon/chat-controller/socket/SocketChannel.js +6 -2
  4. package/dist/src/addon/chat-controller/socket/socketChannelName.js +3 -0
  5. package/dist/src/addon/chat-controller/telegram/TelegramChannel.js +6 -2
  6. package/dist/src/addon/chat-controller/telegram/telegramChannelName.js +3 -0
  7. package/dist/src/addon/chat-controller/wasender/@whatsAppByWasender.js +20 -0
  8. package/dist/src/addon/chat-controller/wasender/WasenderWebhookController.js +82 -0
  9. package/dist/src/addon/chat-controller/wasender/WhatsAppByWasenderChannel.js +67 -0
  10. package/dist/src/addon/chat-controller/wasender/WhatsAppByWasenderChannelConfig.js +16 -0
  11. package/dist/src/addon/chat-controller/wasender/WhatsAppReceiverByWasender.js +39 -0
  12. package/dist/src/addon/chat-controller/wasender/WhatsAppSenderByWasender.js +33 -0
  13. package/dist/src/addon/chat-controller/wasender/extractNumberFromWasenderKey.js +5 -0
  14. package/dist/src/addon/chat-controller/wasender/whatsAppByWasenderChannelName.js +3 -0
  15. package/dist/src/addon/chat-controller/whatsapp/WhatsAppChannel.js +3 -0
  16. package/dist/src/addon/chat-controller/whatsapp/cloud-api/WhatsAppReceiverByCloudApi.js +2 -2
  17. package/dist/src/addon/chat-controller/whatsapp/proxy/WhatsAppReceiverByWabotProxy.js +2 -1
  18. package/dist/src/addon/chat-controller/whatsapp/whatsAppChannelName.js +3 -0
  19. package/dist/src/addon/chat-controller/whatsapp-by-wasender/@whatsAppByWasender.js +20 -0
  20. package/dist/src/addon/chat-controller/whatsapp-by-wasender/WhatsAppByWasenderChannel.js +52 -0
  21. package/dist/src/addon/chat-controller/whatsapp-by-wasender/WhatsAppByWasenderChannelConfig.js +16 -0
  22. package/dist/src/addon/chat-controller/whatsapp-by-wasender/WhatsAppReceiverByWasender.js +106 -0
  23. package/dist/src/addon/chat-controller/whatsapp-by-wasender/WhatsAppSenderByWasender.js +40 -0
  24. package/dist/src/addon/chat-controller/whatsapp-by-wasender/extractNumberFromWasenderKey.js +5 -0
  25. package/dist/src/feature/rest-controller/metadata/RestControllerMetadataStore.js +35 -8
  26. package/dist/src/index.d.ts +203 -7
  27. package/dist/src/index.js +12 -0
  28. package/dist/src/node_modules/wasenderapi/dist/index.js +553 -0
  29. package/package.json +3 -2
@@ -1,6 +1,7 @@
1
1
  import { __decorate, __metadata } from 'tslib';
2
2
  import { injectable } from '../../../core/injection/index.js';
3
3
  import { Logger } from '../../../core/logger/Logger.js';
4
+ import { cmdChannelName } from './cmdChannelName.js';
4
5
  import * as readline from 'readline';
5
6
  import * as fs from 'fs';
6
7
  import * as path from 'path';
@@ -10,13 +11,15 @@ import { Auth } from '../../../core/auth/Auth.js';
10
11
  var CmdChannel_1;
11
12
  const chatIdPath = '.cmd-channel/id.json';
12
13
  const authInfoPath = '.cmd-channel/auth-info.json';
13
- let CmdChannel = CmdChannel_1 = class CmdChannel {
14
+ let CmdChannel = class CmdChannel {
15
+ static { CmdChannel_1 = this; }
14
16
  auth;
15
17
  chatId = undefined;
16
18
  rl = readline.createInterface({
17
19
  input: process.stdin,
18
20
  output: process.stdout,
19
21
  });
22
+ static channelName = cmdChannelName;
20
23
  callBack = null;
21
24
  constructor(auth) {
22
25
  this.auth = auth;
@@ -50,7 +53,7 @@ let CmdChannel = CmdChannel_1 = class CmdChannel {
50
53
  const chatConnection = {
51
54
  id: this.chatId,
52
55
  chatType: 'PRIVATE',
53
- channelName: CmdChannel_1.name,
56
+ channelName: CmdChannel_1.channelName,
54
57
  };
55
58
  if (!this.callBack)
56
59
  return;
@@ -61,6 +64,7 @@ let CmdChannel = CmdChannel_1 = class CmdChannel {
61
64
  }
62
65
  }
63
66
  await this.callBack({
67
+ channel: cmdChannelName,
64
68
  chatConnection,
65
69
  message: {
66
70
  text: trimmedInput,
@@ -0,0 +1,3 @@
1
+ const cmdChannelName = 'CmdChannel';
2
+
3
+ export { cmdChannelName };
@@ -1,5 +1,6 @@
1
1
  import { __decorate, __metadata } from 'tslib';
2
2
  import { injectable } from '../../../core/injection/index.js';
3
+ import { socketChannelName } from './socketChannelName.js';
3
4
  import { handshakeMiddlewares } from '../../../feature/socket-controller/metadata/@handshakeMiddlewares.js';
4
5
  import { socketController } from '../../../feature/socket-controller/metadata/@socketController.js';
5
6
  import { onSocketEvent } from '../../../feature/socket-controller/metadata/@onSocketEvent.js';
@@ -33,8 +34,10 @@ __decorate([
33
34
  isNotEmpty(),
34
35
  __metadata("design:type", String)
35
36
  ], SocketChannelReceivedMessage.prototype, "text", void 0);
36
- let SocketChannel = SocketChannel_1 = class SocketChannel {
37
+ let SocketChannel = class SocketChannel {
38
+ static { SocketChannel_1 = this; }
37
39
  config;
40
+ static channelName = socketChannelName;
38
41
  callBack = null;
39
42
  controller = null;
40
43
  constructor(config) {
@@ -58,9 +61,10 @@ let SocketChannel = SocketChannel_1 = class SocketChannel {
58
61
  const chatConnection = {
59
62
  id: message.chatId,
60
63
  chatType: 'PRIVATE',
61
- channelName: SocketChannel_1.name,
64
+ channelName: SocketChannel_1.channelName,
62
65
  };
63
66
  await channel.callBack({
67
+ channel: socketChannelName,
64
68
  chatConnection,
65
69
  message: {
66
70
  text: message.text,
@@ -0,0 +1,3 @@
1
+ const socketChannelName = 'SocketChannel';
2
+
3
+ export { socketChannelName };
@@ -2,10 +2,13 @@ import { __decorate, __metadata } from 'tslib';
2
2
  import { Bot } from 'grammy';
3
3
  import { TelegramChannelConfig } from './TelegramChannelConfig.js';
4
4
  import { injectable } from '../../../core/injection/index.js';
5
+ import { telegramChannelName } from './telegramChannelName.js';
5
6
 
6
7
  var TelegramChannel_1;
7
- let TelegramChannel = TelegramChannel_1 = class TelegramChannel {
8
+ let TelegramChannel = class TelegramChannel {
9
+ static { TelegramChannel_1 = this; }
8
10
  config;
11
+ static channelName = telegramChannelName;
9
12
  bot;
10
13
  constructor(config) {
11
14
  this.config = config;
@@ -19,9 +22,10 @@ let TelegramChannel = TelegramChannel_1 = class TelegramChannel {
19
22
  const chatConnection = {
20
23
  id: ctx.message.chat.id.toString(),
21
24
  chatType: ctx.message.chat.type === 'private' ? 'PRIVATE' : 'GROUP',
22
- channelName: TelegramChannel_1.name,
25
+ channelName: TelegramChannel_1.channelName,
23
26
  };
24
27
  await callback({
28
+ channel: telegramChannelName,
25
29
  chatConnection,
26
30
  message: {
27
31
  senderName: ctx.from.first_name,
@@ -0,0 +1,3 @@
1
+ const telegramChannelName = 'TelegramChannel';
2
+
3
+ export { telegramChannelName };
@@ -0,0 +1,20 @@
1
+ import { container } from '../../../core/injection/index.js';
2
+ import { WhatsAppByWasenderChannelConfig } from './WhatsAppByWasenderChannelConfig.js';
3
+ import { ControllerMetadataStore } from '../../../feature/chat-controller/metadata/ControllerMetadataStore.js';
4
+ import '../../../feature/chat-controller/ChatResolver.js';
5
+ import '../../../feature/chat-controller/runChatControllers.js';
6
+ import { WhatsAppByWasenderChannel } from './WhatsAppByWasenderChannel.js';
7
+
8
+ function whatsAppByWasender(config) {
9
+ return function (target, propertyKey) {
10
+ const store = container.resolve(ControllerMetadataStore);
11
+ store.saveChannelMetadata({
12
+ channelConstructor: WhatsAppByWasenderChannel,
13
+ functionName: propertyKey.toString(),
14
+ controllerConstructor: target.constructor,
15
+ channelConfig: new WhatsAppByWasenderChannelConfig(config ?? {}),
16
+ });
17
+ };
18
+ }
19
+
20
+ export { whatsAppByWasender };
@@ -0,0 +1,82 @@
1
+ import { __decorate, __metadata } from 'tslib';
2
+ import { Logger } from '../../../core/logger/Logger.js';
3
+ import '../../../core/injection/index.js';
4
+ import '../../../feature/rest-controller/metadata/RestControllerMetadataStore.js';
5
+ import { onPost } from '../../../feature/rest-controller/metadata/@onPost.js';
6
+ import { extractNumberFromWasenderMessageKey } from './extractNumberFromWasenderKey.js';
7
+ import { IncomingMessage } from 'http';
8
+
9
+ class WasenderWebhookController {
10
+ wasender;
11
+ listener;
12
+ logger = new Logger('wabot:wasender-webhook');
13
+ constructor(wasender, listener) {
14
+ this.wasender = wasender;
15
+ this.listener = listener;
16
+ }
17
+ async handleWebhook(req) {
18
+ const rawBody = await this.getRawBody(req);
19
+ const event = await this.parseEvent(req, rawBody);
20
+ this.logger.trace(`received event ${event.event}`);
21
+ switch (event.event) {
22
+ case 'messages.received':
23
+ const messages = Array.isArray(event.data.messages)
24
+ ? event.data.messages
25
+ : [event.data.messages];
26
+ await this.handleMessages(messages);
27
+ break;
28
+ default:
29
+ this.logger.warn(`unhandled event type ${event.event}`);
30
+ }
31
+ }
32
+ async handleMessages(messages) {
33
+ for (const message of messages) {
34
+ const rawNumber = extractNumberFromWasenderMessageKey(message.key);
35
+ const from = rawNumber.startsWith('+') ? rawNumber : `+${rawNumber}`;
36
+ this.logger.trace(`new message from '${from}'`);
37
+ if (message.message.conversation) {
38
+ await this.listener({
39
+ text: message.message.conversation,
40
+ senderName: message.pushName,
41
+ senderId: from,
42
+ metadata: {
43
+ whatsAppNumber: from,
44
+ },
45
+ }, from);
46
+ }
47
+ }
48
+ }
49
+ async parseEvent(req, rawBody) {
50
+ const adapter = {
51
+ getHeader: (name) => {
52
+ const value = req.headers[name.toLowerCase()];
53
+ return Array.isArray(value) ? value[0] : value;
54
+ },
55
+ getRawBody: () => rawBody,
56
+ };
57
+ const event = await this.wasender.handleWebhookEvent(adapter);
58
+ return event;
59
+ }
60
+ getRawBody(req) {
61
+ return new Promise((resolve, reject) => {
62
+ let data = '';
63
+ req.on('data', (chunk) => {
64
+ data += chunk;
65
+ });
66
+ req.on('end', () => {
67
+ resolve(data);
68
+ });
69
+ req.on('error', (err) => {
70
+ reject(err);
71
+ });
72
+ });
73
+ }
74
+ }
75
+ __decorate([
76
+ onPost({ disableJsonParser: true, disableUrlEncodedParser: true }),
77
+ __metadata("design:type", Function),
78
+ __metadata("design:paramtypes", [IncomingMessage]),
79
+ __metadata("design:returntype", Promise)
80
+ ], WasenderWebhookController.prototype, "handleWebhook", null);
81
+
82
+ export { WasenderWebhookController };
@@ -0,0 +1,67 @@
1
+ import { __decorate, __metadata } from 'tslib';
2
+ import { injectable } from '../../../core/injection/index.js';
3
+ import { Env } from '../../../core/env/Env.js';
4
+ import { Logger } from '../../../core/logger/Logger.js';
5
+ import { WhatsAppByWasenderChannelConfig } from './WhatsAppByWasenderChannelConfig.js';
6
+ import { WhatsAppReceiverByWasender } from './WhatsAppReceiverByWasender.js';
7
+ import { WhatsAppSenderByWasender } from './WhatsAppSenderByWasender.js';
8
+ import { whatsAppByWasenderChannelName } from './whatsAppByWasenderChannelName.js';
9
+
10
+ var WhatsAppByWasenderChannel_1;
11
+ let WhatsAppByWasenderChannel = class WhatsAppByWasenderChannel {
12
+ static { WhatsAppByWasenderChannel_1 = this; }
13
+ logger = new Logger('wabot:whatsapp-by-wasender-channel');
14
+ sender;
15
+ receiver;
16
+ phoneNumber;
17
+ static channelName = whatsAppByWasenderChannelName;
18
+ constructor(config, env) {
19
+ const apiKey = config.apiKey ?? env.requireString('WASENDER_API_KEY');
20
+ const webhookSecret = config.webhookSecret ?? env.requireString('WASENDER_WEBHOOK_SECRET');
21
+ this.phoneNumber = config.phoneNumber ?? env.requireString('WASENDER_PHONE_NUMBER');
22
+ this.sender = new WhatsAppSenderByWasender(apiKey, config.retryOptions);
23
+ this.receiver = new WhatsAppReceiverByWasender({
24
+ apiKey,
25
+ webhookSecret,
26
+ webhookPath: config.webhookPath,
27
+ retryOptions: config.retryOptions,
28
+ });
29
+ }
30
+ listen(callback) {
31
+ this.receiver.listenMessage(async (message, from) => {
32
+ try {
33
+ await callback({
34
+ channel: whatsAppByWasenderChannelName,
35
+ chatConnection: {
36
+ chatType: 'PRIVATE',
37
+ channelName: WhatsAppByWasenderChannel_1.channelName,
38
+ id: from,
39
+ },
40
+ message,
41
+ reply: async (replyMessage) => {
42
+ await this.sender.send({
43
+ from: this.phoneNumber,
44
+ to: from,
45
+ message: replyMessage,
46
+ });
47
+ },
48
+ });
49
+ }
50
+ catch (err) {
51
+ this.logger.error('Failed to handle WhatsApp message', err);
52
+ }
53
+ });
54
+ }
55
+ connect() {
56
+ this.receiver.connect();
57
+ }
58
+ disconnect() {
59
+ this.receiver.disconnect();
60
+ }
61
+ };
62
+ WhatsAppByWasenderChannel = WhatsAppByWasenderChannel_1 = __decorate([
63
+ injectable(),
64
+ __metadata("design:paramtypes", [WhatsAppByWasenderChannelConfig, Env])
65
+ ], WhatsAppByWasenderChannel);
66
+
67
+ export { WhatsAppByWasenderChannel };
@@ -0,0 +1,16 @@
1
+ class WhatsAppByWasenderChannelConfig {
2
+ apiKey;
3
+ webhookSecret;
4
+ phoneNumber;
5
+ webhookPath;
6
+ retryOptions;
7
+ constructor(config) {
8
+ this.apiKey = config.apiKey;
9
+ this.webhookSecret = config.webhookSecret;
10
+ this.phoneNumber = config.phoneNumber;
11
+ this.webhookPath = config.webhookPath ?? '/wasender/hook';
12
+ this.retryOptions = config.retryOptions ?? { enabled: true, maxRetries: 3 };
13
+ }
14
+ }
15
+
16
+ export { WhatsAppByWasenderChannelConfig };
@@ -0,0 +1,39 @@
1
+ import { __decorate, __metadata } from 'tslib';
2
+ import '../../../core/injection/index.js';
3
+ import '../../../feature/rest-controller/metadata/RestControllerMetadataStore.js';
4
+ import { restController } from '../../../feature/rest-controller/metadata/@restController.js';
5
+ import { runRestControllers } from '../../../feature/rest-controller/runRestControllers.js';
6
+ import { createWasender } from '../../../node_modules/wasenderapi/dist/index.js';
7
+ import { WasenderWebhookController } from './WasenderWebhookController.js';
8
+
9
+ class WhatsAppReceiverByWasender {
10
+ config;
11
+ wasender;
12
+ listener = null;
13
+ constructor(config) {
14
+ this.config = config;
15
+ this.wasender = createWasender(config.apiKey, undefined, undefined, undefined, config.retryOptions, config.webhookSecret);
16
+ }
17
+ listenMessage(listener) {
18
+ this.listener = listener;
19
+ }
20
+ connect() {
21
+ const wasender = this.wasender;
22
+ const listener = this.listener;
23
+ let UniqueController = class UniqueController extends WasenderWebhookController {
24
+ constructor() {
25
+ super(wasender, listener);
26
+ }
27
+ };
28
+ UniqueController = __decorate([
29
+ restController(this.config.webhookPath),
30
+ __metadata("design:paramtypes", [])
31
+ ], UniqueController);
32
+ runRestControllers([UniqueController]);
33
+ }
34
+ disconnect() {
35
+ // Nothing to disconnect
36
+ }
37
+ }
38
+
39
+ export { WhatsAppReceiverByWasender };
@@ -0,0 +1,33 @@
1
+ import { Logger } from '../../../core/logger/Logger.js';
2
+ import { createWasender } from '../../../node_modules/wasenderapi/dist/index.js';
3
+
4
+ class WhatsAppSenderByWasender {
5
+ wasender;
6
+ logger = new Logger('wabot:whatsapp-sender-by-wasender');
7
+ constructor(apiKey, retryOptions) {
8
+ this.wasender = createWasender(apiKey, undefined, undefined, undefined, retryOptions, undefined);
9
+ }
10
+ async send(request) {
11
+ try {
12
+ const textPayload = {
13
+ messageType: 'text',
14
+ to: `+${request.to.replace(/\D+/g, '')}`,
15
+ text: request.message.text ?? 'No Text',
16
+ };
17
+ const result = await this.wasender.send(textPayload);
18
+ this.logger.trace(`message sent from '${request.from}' to '${request.to}'`);
19
+ this.logger.trace(`rate limit remaining: ${result.rateLimit?.remaining}`);
20
+ }
21
+ catch (error) {
22
+ this.logger.error(`Failed to send message from '${request.from}' to '${request.to}'`, error);
23
+ if (error instanceof Error) {
24
+ throw new Error(error.message, { cause: error });
25
+ }
26
+ else {
27
+ throw new Error('error sending message');
28
+ }
29
+ }
30
+ }
31
+ }
32
+
33
+ export { WhatsAppSenderByWasender };
@@ -0,0 +1,5 @@
1
+ function extractNumberFromWasenderMessageKey(key) {
2
+ return key.cleanedSenderPn ?? key.senderPn?.split('@')?.at(0) ?? key.remoteJid.split('@').at(0);
3
+ }
4
+
5
+ export { extractNumberFromWasenderMessageKey };
@@ -0,0 +1,3 @@
1
+ const whatsAppByWasenderChannelName = 'WhatsAppByWasenderChannel';
2
+
3
+ export { whatsAppByWasenderChannelName };
@@ -4,11 +4,13 @@ import { Logger } from '../../../core/logger/Logger.js';
4
4
  import { WhatsappChannelConfig } from './WhatsAppChannelConfig.js';
5
5
  import { WhatsAppReceiver } from './WhatsAppReceiver.js';
6
6
  import { WhatsAppSender } from './WhatsAppSender.js';
7
+ import { whatsAppChannelName } from './whatsAppChannelName.js';
7
8
 
8
9
  let WhatsAppChannel = class WhatsAppChannel {
9
10
  config;
10
11
  sender;
11
12
  receiver;
13
+ static channelName = whatsAppChannelName;
12
14
  logger = new Logger('wabot:whatsapp-channel');
13
15
  constructor(config, sender, receiver) {
14
16
  this.config = config;
@@ -21,6 +23,7 @@ let WhatsAppChannel = class WhatsAppChannel {
21
23
  listener: async (message) => {
22
24
  try {
23
25
  await callback({
26
+ channel: whatsAppChannelName,
24
27
  chatConnection: message.chatConnection,
25
28
  message: message.message,
26
29
  reply: async (replyMessage) => {
@@ -1,4 +1,5 @@
1
1
  import { Logger } from '../../../../core/logger/Logger.js';
2
+ import { whatsAppChannelName } from '../whatsAppChannelName.js';
2
3
  import { json } from 'express';
3
4
  import { WhatsAppReceiver } from '../WhatsAppReceiver.js';
4
5
 
@@ -78,11 +79,10 @@ class WhatsAppReceiverByCloudApi extends WhatsAppReceiver {
78
79
  this.logger.warn(`Message type '${message.type}' is not supported yet`);
79
80
  return;
80
81
  }
81
- const channelName = 'WhatsAppChannel';
82
82
  const chatConnection = {
83
83
  id: contact.wa_id,
84
84
  chatType: 'PRIVATE',
85
- channelName,
85
+ channelName: whatsAppChannelName,
86
86
  };
87
87
  await listener({
88
88
  chatConnection,
@@ -1,5 +1,6 @@
1
1
  import { __decorate, __metadata } from 'tslib';
2
2
  import { WhatsAppReceiver } from '../WhatsAppReceiver.js';
3
+ import { whatsAppChannelName } from '../whatsAppChannelName.js';
3
4
  import { injectable } from '../../../../core/injection/index.js';
4
5
  import { Logger } from '../../../../core/logger/Logger.js';
5
6
  import { WHATSAPP_MESSAGE_EVENT } from './WhatsAppProxyContracts.js';
@@ -45,7 +46,7 @@ let WhatsAppReceiverByWabotProxy = class WhatsAppReceiverByWabotProxy extends Wh
45
46
  request.listener({
46
47
  chatConnection: {
47
48
  chatType: 'PRIVATE',
48
- channelName: 'WhatsAppChannel',
49
+ channelName: whatsAppChannelName,
49
50
  id: data.from,
50
51
  },
51
52
  message: {
@@ -0,0 +1,3 @@
1
+ const whatsAppChannelName = 'WhatsAppChannel';
2
+
3
+ export { whatsAppChannelName };
@@ -0,0 +1,20 @@
1
+ import { container } from '../../../core/injection/index.js';
2
+ import { WhatsAppByWasenderChannelConfig } from './WhatsAppByWasenderChannelConfig.js';
3
+ import { ControllerMetadataStore } from '../../../feature/chat-controller/metadata/ControllerMetadataStore.js';
4
+ import '../../../feature/chat-controller/ChatResolver.js';
5
+ import '../../../feature/chat-controller/runChatControllers.js';
6
+ import { WhatsAppByWasenderChannel } from './WhatsAppByWasenderChannel.js';
7
+
8
+ function whatsAppByWasender(config) {
9
+ return function (target, propertyKey) {
10
+ const store = container.resolve(ControllerMetadataStore);
11
+ store.saveChannelMetadata({
12
+ channelConstructor: WhatsAppByWasenderChannel,
13
+ functionName: propertyKey.toString(),
14
+ controllerConstructor: target.constructor,
15
+ channelConfig: new WhatsAppByWasenderChannelConfig(config),
16
+ });
17
+ };
18
+ }
19
+
20
+ export { whatsAppByWasender };
@@ -0,0 +1,52 @@
1
+ import { __decorate, __metadata } from 'tslib';
2
+ import { injectable } from '../../../core/injection/index.js';
3
+ import { Logger } from '../../../core/logger/Logger.js';
4
+ import { WhatsAppByWasenderChannelConfig } from './WhatsAppByWasenderChannelConfig.js';
5
+ import { WhatsAppReceiverByWasender } from './WhatsAppReceiverByWasender.js';
6
+ import { WhatsAppSenderByWasender } from './WhatsAppSenderByWasender.js';
7
+
8
+ let WhatsAppByWasenderChannel = class WhatsAppByWasenderChannel {
9
+ config;
10
+ sender;
11
+ receiver;
12
+ logger = new Logger('wabot:whatsapp-by-wasender-channel');
13
+ constructor(config, sender, receiver) {
14
+ this.config = config;
15
+ this.sender = sender;
16
+ this.receiver = receiver;
17
+ }
18
+ listen(callback) {
19
+ this.receiver.listenMessage(async (message) => {
20
+ try {
21
+ await callback({
22
+ chatConnection: message.chatConnection,
23
+ message: message.message,
24
+ reply: async (replyMessage) => {
25
+ await this.sender.send({
26
+ from: this.config.phoneNumber,
27
+ to: message.chatConnection.id,
28
+ message: replyMessage,
29
+ });
30
+ },
31
+ });
32
+ }
33
+ catch (err) {
34
+ this.logger.error('Failed to handle WhatsApp message', err);
35
+ }
36
+ });
37
+ }
38
+ connect() {
39
+ this.receiver.connect();
40
+ }
41
+ disconnect() {
42
+ this.receiver.disconnect();
43
+ }
44
+ };
45
+ WhatsAppByWasenderChannel = __decorate([
46
+ injectable(),
47
+ __metadata("design:paramtypes", [WhatsAppByWasenderChannelConfig,
48
+ WhatsAppSenderByWasender,
49
+ WhatsAppReceiverByWasender])
50
+ ], WhatsAppByWasenderChannel);
51
+
52
+ export { WhatsAppByWasenderChannel };
@@ -0,0 +1,16 @@
1
+ class WhatsAppByWasenderChannelConfig {
2
+ apiKey;
3
+ webhookSecret;
4
+ phoneNumber;
5
+ webhookPath;
6
+ retryOptions;
7
+ constructor(config) {
8
+ this.apiKey = config.apiKey;
9
+ this.webhookSecret = config.webhookSecret;
10
+ this.phoneNumber = config.phoneNumber;
11
+ this.webhookPath = config.webhookPath ?? '/wasender/hook';
12
+ this.retryOptions = config.retryOptions ?? { enabled: true, maxRetries: 3 };
13
+ }
14
+ }
15
+
16
+ export { WhatsAppByWasenderChannelConfig };
@@ -0,0 +1,106 @@
1
+ import { __decorate, __metadata } from 'tslib';
2
+ import { Logger } from '../../../core/logger/Logger.js';
3
+ import { injectable } from '../../../core/injection/index.js';
4
+ import { ExpressProvider } from '../../../feature/express/ExpressProvider.js';
5
+ import { createWasender } from '../../../node_modules/wasenderapi/dist/index.js';
6
+ import { WhatsAppByWasenderChannelConfig } from './WhatsAppByWasenderChannelConfig.js';
7
+ import { extractNumberFromWasenderMessageKey } from './extractNumberFromWasenderKey.js';
8
+
9
+ let WhatsAppReceiverByWasender = class WhatsAppReceiverByWasender {
10
+ expressProvider;
11
+ config;
12
+ wasender;
13
+ listener = null;
14
+ logger = new Logger('wabot:whatsapp-receiver-by-wasender');
15
+ constructor(expressProvider, config) {
16
+ this.expressProvider = expressProvider;
17
+ this.config = config;
18
+ this.wasender = createWasender(config.apiKey, undefined, undefined, undefined, config.retryOptions, config.webhookSecret);
19
+ }
20
+ listenMessage(listener) {
21
+ this.listener = listener;
22
+ }
23
+ connect() {
24
+ const expressApp = this.expressProvider.getExpress();
25
+ expressApp.post(this.config.webhookPath, async (req, res) => {
26
+ try {
27
+ const rawBody = await this.getRawBody(req);
28
+ const event = await this.parseEvent(req, rawBody);
29
+ this.logger.trace(`received event ${event.event}`);
30
+ switch (event.event) {
31
+ case 'messages.received':
32
+ const messages = Array.isArray(event.data.messages)
33
+ ? event.data.messages
34
+ : [event.data.messages];
35
+ await this.handleMessages(messages);
36
+ break;
37
+ default:
38
+ this.logger.warn(`unhandled event type ${event.event}`);
39
+ }
40
+ res.sendStatus(200);
41
+ }
42
+ catch (err) {
43
+ this.logger.error('Failed to handle Wasender webhook', err);
44
+ res.sendStatus(500);
45
+ }
46
+ });
47
+ this.expressProvider.listen();
48
+ }
49
+ disconnect() {
50
+ // Nothing to disconnect
51
+ }
52
+ async handleMessages(messages) {
53
+ if (!this.listener) {
54
+ this.logger.warn('No listener registered, ignoring messages');
55
+ return;
56
+ }
57
+ for (const message of messages) {
58
+ const from = extractNumberFromWasenderMessageKey(message.key);
59
+ this.logger.trace(`new message from '${from}' to '${this.config.phoneNumber}'`);
60
+ if (message.message.conversation) {
61
+ const chatConnection = {
62
+ chatType: 'PRIVATE',
63
+ channelName: 'WhatsAppByWasenderChannel',
64
+ id: from,
65
+ };
66
+ await this.listener({
67
+ chatConnection,
68
+ message: {
69
+ text: message.message.conversation,
70
+ senderName: message.pushName,
71
+ senderId: from,
72
+ },
73
+ });
74
+ }
75
+ }
76
+ }
77
+ async parseEvent(req, rawBody) {
78
+ const adapter = {
79
+ getHeader: (name) => req.header(name),
80
+ getRawBody: () => rawBody,
81
+ };
82
+ const event = await this.wasender.handleWebhookEvent(adapter);
83
+ return event;
84
+ }
85
+ getRawBody(req) {
86
+ return new Promise((resolve, reject) => {
87
+ let data = '';
88
+ req.on('data', (chunk) => {
89
+ data += chunk;
90
+ });
91
+ req.on('end', () => {
92
+ resolve(data);
93
+ });
94
+ req.on('error', (err) => {
95
+ reject(err);
96
+ });
97
+ });
98
+ }
99
+ };
100
+ WhatsAppReceiverByWasender = __decorate([
101
+ injectable(),
102
+ __metadata("design:paramtypes", [ExpressProvider,
103
+ WhatsAppByWasenderChannelConfig])
104
+ ], WhatsAppReceiverByWasender);
105
+
106
+ export { WhatsAppReceiverByWasender };