@gamastudio/sendwave-provider 1.0.3 → 1.0.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 (103) hide show
  1. package/.github/workflows/build.yml +31 -31
  2. package/README.md +267 -25
  3. package/build/application/connection/connection-polling.service.d.ts +16 -0
  4. package/build/application/connection/connection-polling.service.js +49 -0
  5. package/build/application/message-buffer/message-buffer.service.d.ts +11 -0
  6. package/build/application/message-buffer/message-buffer.service.js +79 -0
  7. package/build/application/messaging/message-sender.service.d.ts +40 -0
  8. package/build/application/messaging/message-sender.service.js +101 -0
  9. package/build/application/messaging/strategies/interactive.strategy.d.ts +14 -0
  10. package/build/application/messaging/strategies/interactive.strategy.js +88 -0
  11. package/build/application/messaging/strategies/list.strategy.d.ts +8 -0
  12. package/build/application/messaging/strategies/list.strategy.js +61 -0
  13. package/build/application/messaging/strategies/media.strategy.d.ts +32 -0
  14. package/build/application/messaging/strategies/media.strategy.js +97 -0
  15. package/build/application/messaging/strategies/misc.strategy.d.ts +23 -0
  16. package/build/application/messaging/strategies/misc.strategy.js +95 -0
  17. package/build/application/messaging/strategies/presence.strategy.d.ts +8 -0
  18. package/build/application/messaging/strategies/presence.strategy.js +24 -0
  19. package/build/application/messaging/strategies/send-strategy.interface.d.ts +4 -0
  20. package/build/application/messaging/strategies/send-strategy.interface.js +2 -0
  21. package/build/application/messaging/strategies/text.strategy.d.ts +8 -0
  22. package/build/application/messaging/strategies/text.strategy.js +22 -0
  23. package/build/application/messaging/strategies/voice.strategy.d.ts +8 -0
  24. package/build/application/messaging/strategies/voice.strategy.js +37 -0
  25. package/build/application/queue-flow/queue-flow.service.d.ts +34 -0
  26. package/build/application/queue-flow/queue-flow.service.js +68 -0
  27. package/build/config/constants.d.ts +14 -0
  28. package/build/config/constants.js +17 -0
  29. package/build/config/defaults.d.ts +8 -0
  30. package/build/config/defaults.js +110 -0
  31. package/build/constants/html/error.js +34 -34
  32. package/build/constants/html/home.js +23 -23
  33. package/build/constants/svg/wave-error.js +88 -88
  34. package/build/constants/svg/wave.js +88 -88
  35. package/build/core/interface/provider.interface.d.ts +2 -1
  36. package/build/core/interface/types.d.ts +12 -0
  37. package/build/domain/interfaces/index.d.ts +2 -0
  38. package/build/domain/interfaces/index.js +18 -0
  39. package/build/domain/interfaces/parsed-message.interface.d.ts +16 -0
  40. package/build/domain/interfaces/parsed-message.interface.js +2 -0
  41. package/build/domain/interfaces/provider.interface.d.ts +21 -0
  42. package/build/domain/interfaces/provider.interface.js +2 -0
  43. package/build/domain/types/config.types.d.ts +43 -0
  44. package/build/domain/types/config.types.js +2 -0
  45. package/build/domain/types/index.d.ts +2 -0
  46. package/build/domain/types/index.js +18 -0
  47. package/build/domain/types/message.types.d.ts +134 -0
  48. package/build/domain/types/message.types.js +2 -0
  49. package/build/index.d.ts +5 -4
  50. package/build/index.js +7 -6
  51. package/build/infrastructure/http/axios/retry.interceptor.d.ts +2 -0
  52. package/build/infrastructure/http/axios/retry.interceptor.js +21 -0
  53. package/build/infrastructure/http/axios/sendwave-api.factory.d.ts +15 -0
  54. package/build/infrastructure/http/axios/sendwave-api.factory.js +50 -0
  55. package/build/infrastructure/http/middleware/body-parser.middleware.d.ts +2 -0
  56. package/build/infrastructure/http/middleware/body-parser.middleware.js +28 -0
  57. package/build/infrastructure/http/middleware/cors.middleware.d.ts +6 -0
  58. package/build/infrastructure/http/middleware/cors.middleware.js +8 -0
  59. package/build/infrastructure/http/middleware/global-args.middleware.d.ts +2 -0
  60. package/build/infrastructure/http/middleware/global-args.middleware.js +9 -0
  61. package/build/infrastructure/http/middleware/rate-limiter.middleware.d.ts +4 -0
  62. package/build/infrastructure/http/middleware/rate-limiter.middleware.js +31 -0
  63. package/build/infrastructure/http/middleware/static-assets.middleware.d.ts +2 -0
  64. package/build/infrastructure/http/middleware/static-assets.middleware.js +8 -0
  65. package/build/infrastructure/http/routes/routes.d.ts +4 -0
  66. package/build/infrastructure/http/routes/routes.js +56 -0
  67. package/build/infrastructure/media/media-detector.service.d.ts +27 -0
  68. package/build/infrastructure/media/media-detector.service.js +123 -0
  69. package/build/infrastructure/parser/incoming-message.parser.d.ts +2 -0
  70. package/build/infrastructure/parser/incoming-message.parser.js +41 -0
  71. package/build/infrastructure/persistence/instance.registry.d.ts +21 -0
  72. package/build/infrastructure/persistence/instance.registry.js +44 -0
  73. package/build/infrastructure/persistence/sendwave-api.repository.d.ts +21 -0
  74. package/build/infrastructure/persistence/sendwave-api.repository.js +64 -0
  75. package/build/package.json +48 -48
  76. package/build/presentation/pages/assets/logo.svg.d.ts +1 -0
  77. package/build/presentation/pages/assets/logo.svg.js +4 -0
  78. package/build/presentation/pages/assets/wave-error.svg.d.ts +1 -0
  79. package/build/presentation/pages/assets/wave-error.svg.js +92 -0
  80. package/build/presentation/pages/assets/wave.svg.d.ts +1 -0
  81. package/build/presentation/pages/assets/wave.svg.js +92 -0
  82. package/build/presentation/pages/connect.page.d.ts +1 -0
  83. package/build/presentation/pages/connect.page.js +182 -0
  84. package/build/presentation/pages/error.page.d.ts +1 -0
  85. package/build/presentation/pages/error.page.js +42 -0
  86. package/build/presentation/pages/home.page.d.ts +1 -0
  87. package/build/presentation/pages/home.page.js +29 -0
  88. package/build/presentation/provider/index.d.ts +2 -0
  89. package/build/presentation/provider/index.js +18 -0
  90. package/build/presentation/provider/sendwave-core.d.ts +25 -0
  91. package/build/presentation/provider/sendwave-core.js +125 -0
  92. package/build/presentation/provider/sendwave.provider.d.ts +76 -0
  93. package/build/presentation/provider/sendwave.provider.js +359 -0
  94. package/build/provider/sender.d.ts +3 -1
  95. package/build/provider/sender.js +52 -34
  96. package/build/shared/exceptions/payload-too-large.exception.d.ts +4 -0
  97. package/build/shared/exceptions/payload-too-large.exception.js +11 -0
  98. package/build/shared/utils/error-extractor.d.ts +1 -0
  99. package/build/shared/utils/error-extractor.js +7 -0
  100. package/build/shared/utils/size-parser.d.ts +1 -0
  101. package/build/shared/utils/size-parser.js +17 -0
  102. package/package.json +48 -48
  103. package/tsconfig.json +7 -5
@@ -1,31 +1,31 @@
1
- name: Build and Publish to NPM
2
-
3
- on:
4
- push:
5
- branches:
6
- - main
7
-
8
- jobs:
9
- build-and-publish:
10
- runs-on: ubuntu-latest
11
-
12
- steps:
13
- - name: 📥 Checkout code
14
- uses: actions/checkout@v3
15
-
16
- - name: 🧰 Set up Node.js
17
- uses: actions/setup-node@v3
18
- with:
19
- node-version: 18
20
- registry-url: https://registry.npmjs.org/
21
-
22
- - name: 📦 Install dependencies
23
- run: npm install
24
-
25
- - name: 🛠️ Build project
26
- run: npm run build
27
-
28
- - name: 🚀 Publish to npm
29
- run: npm publish --access public
30
- env:
31
- NODE_AUTH_TOKEN: ${{ secrets.NODE_AUTH_TOKEN }}
1
+ name: Build and Publish to NPM
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - main
7
+
8
+ jobs:
9
+ build-and-publish:
10
+ runs-on: ubuntu-latest
11
+
12
+ steps:
13
+ - name: 📥 Checkout code
14
+ uses: actions/checkout@v3
15
+
16
+ - name: 🧰 Set up Node.js
17
+ uses: actions/setup-node@v3
18
+ with:
19
+ node-version: 18
20
+ registry-url: https://registry.npmjs.org/
21
+
22
+ - name: 📦 Install dependencies
23
+ run: npm install
24
+
25
+ - name: 🛠️ Build project
26
+ run: npm run build
27
+
28
+ - name: 🚀 Publish to npm
29
+ run: npm publish --access public
30
+ env:
31
+ NODE_AUTH_TOKEN: ${{ secrets.NODE_AUTH_TOKEN }}
package/README.md CHANGED
@@ -25,6 +25,91 @@ const provider = createSendWaveProvider({
25
25
  await provider.initAll(provider.globalVendorArgs.port);
26
26
  ```
27
27
 
28
+ ### Multi-Tenant: Multiple Instances with One Webhook
29
+
30
+ You can manage multiple Sendwave instances with a single provider. The webhook will route messages automatically based on `instanceName` in the payload.
31
+
32
+ ```typescript
33
+ import { createSendWaveProvider } from "@gamastudio/sendwave-provider";
34
+
35
+ const provider = createSendWaveProvider({
36
+ port: 3000,
37
+ url: "https://msg.sendwaves.cloud",
38
+ instances: [
39
+ { name: "bot-sales", apiKey: "api-key-sales" },
40
+ { name: "bot-support", apiKey: "api-key-support" },
41
+ { name: "bot-marketing", apiKey: "api-key-marketing" },
42
+ ],
43
+ });
44
+
45
+ await provider.initAll(provider.globalVendorArgs.port);
46
+
47
+ // Send message to a specific instance
48
+ await provider.sendText(
49
+ { from: "573123456789", text: "Hello from sales!" },
50
+ { instance: "bot-sales" }
51
+ );
52
+
53
+ // Notify admin on another instance
54
+ await provider.sendText(
55
+ { from: "573199999999", text: "Nuevo pedido recibido" },
56
+ { instance: "bot-admin" }
57
+ );
58
+
59
+ // Or use the default (first instance)
60
+ await provider.sendText({
61
+ from: "573123456789",
62
+ text: "Hello!",
63
+ });
64
+
65
+ // List registered instances
66
+ console.log(provider.getInstances().map((i) => i.name));
67
+ ```
68
+
69
+ **Webhook routing:** the provider reads `instanceName` from `body.instanceName`, `body.inputs.instanceName`, or `body.instance.name`. Point all your Sendwave webhooks to the same `/webhook` URL.
70
+
71
+ **QR / State per instance:**
72
+ - `/qr` → default instance
73
+ - `/qr/bot-sales` → specific instance
74
+ - `/state` → default instance
75
+ - `/state/bot-sales` → specific instance
76
+
77
+ ## ⚙️ Configuration
78
+
79
+ ```typescript
80
+ const provider = createSendWaveProvider({
81
+ name: "MyBot",
82
+ apiKey: "your-api-key-here",
83
+ url: "https://msg.sendwaves.cloud", // Optional
84
+ port: 3000,
85
+ host: "0.0.0.0",
86
+ readMessages: true, // Auto-mark incoming messages as read
87
+ message: {
88
+ mergeMessage: true, // Merge rapid messages from same user
89
+ timeMergeMessage: 3, // Base wait time in seconds
90
+ maxWaitTime: 30, // Maximum wait time in seconds
91
+ shortMessageThreshold: 10, // Short messages merge faster
92
+ adaptiveTimer: true, // Smart timing based on message patterns
93
+ },
94
+ queueFlow: {
95
+ enabled: true,
96
+ warningTimeout: 30 * 60 * 1000, // 30 minutes
97
+ endTimeout: 2 * 60 * 1000, // 2 minutes after warning
98
+ },
99
+ payloadLimits: {
100
+ json: "50mb",
101
+ urlencoded: "50mb",
102
+ text: "10mb",
103
+ media: {
104
+ image: "10mb",
105
+ video: "100mb",
106
+ audio: "50mb",
107
+ document: "25mb",
108
+ },
109
+ },
110
+ });
111
+ ```
112
+
28
113
  ## 📤 Sending Messages
29
114
 
30
115
  ### Text Messages
@@ -33,31 +118,75 @@ await provider.initAll(provider.globalVendorArgs.port);
33
118
  await provider.sendText({
34
119
  from: "573123456789",
35
120
  text: "Hello! Welcome to our service.",
121
+ delay: 2000, // Optional typing delay
36
122
  });
123
+
124
+ // Send to a specific instance (multi-tenant)
125
+ await provider.sendText(
126
+ { from: "573123456789", text: "Hello!" },
127
+ { instance: "bot-sales" }
128
+ );
37
129
  ```
38
130
 
39
- ### Interactive Polls (New! 📊)
131
+ ### Presence Indicators
40
132
 
41
133
  ```typescript
42
- await provider.sendPoll({
134
+ await provider.sendPresence({
43
135
  from: "573123456789",
44
- poll: {
45
- name: "¿Qué te parece el nuevo proveedor?",
46
- values: ["Excelente", "Bueno", "Mejorable"],
47
- selectableCount: 1 // Optional, default: 1
48
- }
136
+ presence: "composing", // or "recording"
137
+ delay: 2000,
49
138
  });
50
139
  ```
51
140
 
52
- ### Intelligent Buttons (Easier! 🔘)
141
+ ### Media Messages
53
142
 
54
- The provider is now "intelligent" and accepts multiple formats for buttons.
143
+ ```typescript
144
+ // Image
145
+ await provider.sendImage({
146
+ from: "573123456789",
147
+ url: "https://example.com/image.jpg",
148
+ text: "Optional caption",
149
+ });
150
+
151
+ // Video
152
+ await provider.sendVideo({
153
+ from: "573123456789",
154
+ url: "https://example.com/video.mp4",
155
+ text: "Optional caption",
156
+ });
157
+
158
+ // Document/File
159
+ await provider.sendFile({
160
+ from: "573123456789",
161
+ url: "https://example.com/file.pdf",
162
+ fileName: "document.pdf",
163
+ text: "Optional caption",
164
+ });
165
+
166
+ // Audio
167
+ await provider.sendAudio({
168
+ from: "573123456789",
169
+ url: "https://example.com/audio.mp3",
170
+ });
171
+
172
+ // Voice message (PTT)
173
+ await provider.sendVoice({
174
+ from: "573123456789",
175
+ url: "https://example.com/voice.ogg",
176
+ });
177
+ ```
178
+
179
+ ### Interactive Buttons
180
+
181
+ The provider accepts multiple formats for buttons.
55
182
 
56
183
  ```typescript
57
184
  // 1. Super Simple (Quick Replies)
58
185
  await provider.sendButton({
59
186
  from: "573123456789",
60
187
  title: "Main Menu",
188
+ body: "Select an option",
189
+ footer: "Sendwaves Provider",
61
190
  buttons: ["🛒 Catalog", "🎁 Promotions", "📞 Support"]
62
191
  });
63
192
 
@@ -69,6 +198,7 @@ await provider.sendButton({
69
198
  buttons: [
70
199
  { type: "reply", displayText: "Cash" },
71
200
  { type: "url", displayText: "Visit Web", url: "https://example.com" },
201
+ { type: "call", displayText: "Call Us", phoneNumber: "+573123456789" },
72
202
  { type: "copy", displayText: "Copy Coupon", copyText: "SAVE20" },
73
203
  { type: "pix", displayText: "Pay with Pix", keyType: "email", key: "pix@bot.com" }
74
204
  ]
@@ -82,7 +212,40 @@ await provider.sendButton({
82
212
  - `copy`: Copies text or code to clipboard.
83
213
  - `pix`: Brazilian payment system integration.
84
214
 
85
- ### Intelligent Lists (Simple & Advanced 📜)
215
+ ### Carousel Messages
216
+
217
+ Send a scrollable carousel with multiple cards. Each card supports its own buttons.
218
+
219
+ ```typescript
220
+ await provider.sendCarousel({
221
+ from: "573123456789",
222
+ title: "Featured Products",
223
+ description: "Swipe to see our best offers",
224
+ footer: "Sendwaves Store",
225
+ cards: [
226
+ {
227
+ text: "Premium Plan",
228
+ thumbnailUrl: "https://example.com/plan1.jpg",
229
+ footerText: "Best value",
230
+ buttons: [
231
+ { type: "reply", displayText: "Buy Now" },
232
+ { type: "url", displayText: "More Info", url: "https://example.com/plan1" }
233
+ ]
234
+ },
235
+ {
236
+ text: "Basic Plan",
237
+ thumbnailUrl: "https://example.com/plan2.jpg",
238
+ footerText: "Affordable",
239
+ buttons: [
240
+ { type: "reply", displayText: "Buy Now" }
241
+ ]
242
+ }
243
+ ],
244
+ delay: 2000
245
+ });
246
+ ```
247
+
248
+ ### Interactive Lists
86
249
 
87
250
  ```typescript
88
251
  // Simple List (Array of strings)
@@ -100,7 +263,9 @@ await provider.sendList({
100
263
  from: "573123456789",
101
264
  list: {
102
265
  title: "Product Categories",
266
+ description: "Choose a category",
103
267
  button: "Select Category",
268
+ footerText: "Sendwaves Provider",
104
269
  content: [
105
270
  {
106
271
  title: "Electronics",
@@ -114,24 +279,79 @@ await provider.sendList({
114
279
  });
115
280
  ```
116
281
 
117
- ### Media Messages
282
+ ### Polls
283
+
284
+ ```typescript
285
+ await provider.sendPoll({
286
+ from: "573123456789",
287
+ poll: {
288
+ name: "¿Qué te parece el nuevo proveedor?",
289
+ values: ["Excelente", "Bueno", "Mejorable"],
290
+ selectableCount: 1 // Optional, default: 1
291
+ }
292
+ });
293
+ ```
294
+
295
+ ### Locations
296
+
297
+ ```typescript
298
+ await provider.sendLocation({
299
+ from: "573123456789",
300
+ name: "Our Office",
301
+ address: "123 Main St, Bogotá",
302
+ latitude: 4.711,
303
+ longitude: -74.0721,
304
+ });
305
+ ```
306
+
307
+ ### Reactions
308
+
309
+ ```typescript
310
+ await provider.sendReaction({
311
+ key: {
312
+ remoteJid: "573123456789@s.whatsapp.net",
313
+ fromMe: false,
314
+ id: "MESSAGE_ID",
315
+ },
316
+ reaction: "👍",
317
+ });
318
+ ```
319
+
320
+ ### Mark as Read
118
321
 
119
322
  ```typescript
120
- // Images, Videos, Documents, and Voice
121
- await provider.sendImage({ from: "573123456789", url: "https://...", text: "Caption" });
122
- await provider.sendVideo({ from: "573123456789", url: "https://...", text: "Caption" });
123
- await provider.sendFile({ from: "573123456789", url: "https://...", fileName: "file.pdf" });
124
- await provider.sendVoice({ from: "573123456789", url: "https://..." });
323
+ await provider.readMessages({
324
+ from: "573123456789@s.whatsapp.net",
325
+ messageId: "MESSAGE_ID",
326
+ });
125
327
  ```
126
328
 
127
- ## 🔄 Queue Flow System (100% Automatic)
329
+ ## 🧠 Intelligent Message Merging
128
330
 
129
- The Queue Flow system manages user sessions, handling timeouts and inactive users automatically.
331
+ When `mergeMessage` is enabled, rapid messages from the same user are merged into a single message.
332
+
333
+ ```typescript
334
+ const provider = createSendWaveProvider({
335
+ message: {
336
+ mergeMessage: true,
337
+ timeMergeMessage: 4,
338
+ maxWaitTime: 30,
339
+ shortMessageThreshold: 10,
340
+ adaptiveTimer: true,
341
+ }
342
+ });
343
+ ```
344
+
345
+ Example:
346
+ - User sends "Hola" then "como estas"
347
+ - Result: single message with body `"Hola como estas"`
348
+
349
+ ## 🔄 Queue Flow System
350
+
351
+ Automatic session management for inactive users.
130
352
 
131
353
  ```typescript
132
354
  const provider = createSendWaveProvider({
133
- name: "MyBot",
134
- apiKey: "your-key",
135
355
  queueFlow: {
136
356
  enabled: true,
137
357
  warningTimeout: 30 * 60 * 1000, // 30 mins
@@ -139,20 +359,42 @@ const provider = createSendWaveProvider({
139
359
  warningMessage: "⏳ You seem inactive. Still there?",
140
360
  },
141
361
  });
362
+
363
+ // Listen to inactivity events
364
+ provider.onQueueFlowEvent("userInactive-573123456789", (data) => {
365
+ console.log(data.isActive ? "User warned" : "Session ended");
366
+ });
142
367
  ```
143
368
 
144
369
  ## 🎯 Event System
145
370
 
146
371
  ```typescript
147
- provider.on('ready', (isReady) => console.log('Connected:', isReady));
148
- provider.on('message', (ctx) => console.log(`From ${ctx.from}: ${ctx.body}`));
372
+ provider.on("ready", (isReady) => console.log("Connected:", isReady));
373
+ provider.on("message", (ctx) => console.log(`From ${ctx.from}: ${ctx.body}`));
374
+ provider.on("user-message", (data) => console.log("User message:", data));
375
+ provider.on("flow-message", (data) => console.log("Flow message:", data));
376
+ provider.on("auth_failure", (payload) => console.error("Auth failed:", payload));
377
+ provider.on("notice", ({ title, instructions }) => console.log(title, instructions));
149
378
  ```
150
379
 
151
380
  ## 🏗️ Architecture
152
381
 
153
- - **Provider Layer**: Main SendWaveProvider class.
154
- - **Sender Layer**: Intelligent message transformation.
155
- - **Queue Flow**: Automatic session management.
382
+ The project is organized in modular layers:
383
+
384
+ - **`config/`**: Default values and constants.
385
+ - **`domain/`**: Pure types and interfaces.
386
+ - **`infrastructure/`**: HTTP client, API repository, media detection, message parser.
387
+ - **`application/`**: Business logic: message strategies, buffer merging, connection polling, queue flow.
388
+ - **`presentation/`**: Provider and core classes, HTML pages.
389
+ - **`shared/`**: Common utilities and exceptions.
390
+
391
+ ### Adding a New Message Type
392
+
393
+ 1. Create a strategy in `src/application/messaging/strategies/`.
394
+ 2. Implement the `SendStrategy<T>` interface.
395
+ 3. Register it in `MessageSenderService`.
396
+ 4. Add the public method to `SendWaveProvider`.
397
+ 5. Add the type to `ProviderInterface` and `message.types.ts`.
156
398
 
157
399
  ## 📜 License
158
400
 
@@ -0,0 +1,16 @@
1
+ import { SendWaveApiRepository } from "../../infrastructure/persistence/sendwave-api.repository";
2
+ export interface ConnectionStateListener {
3
+ onConnected: () => void;
4
+ onDisconnected: () => void;
5
+ onAuthFailure: (error: any) => void;
6
+ }
7
+ export declare class ConnectionPollingService {
8
+ private readonly api;
9
+ private readonly listener;
10
+ private timer?;
11
+ private lastReadyState?;
12
+ constructor(api: SendWaveApiRepository, listener: ConnectionStateListener);
13
+ check(): Promise<void>;
14
+ start(): void;
15
+ stop(): void;
16
+ }
@@ -0,0 +1,49 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ConnectionPollingService = void 0;
4
+ const constants_1 = require("../../config/constants");
5
+ class ConnectionPollingService {
6
+ constructor(api, listener) {
7
+ this.api = api;
8
+ this.listener = listener;
9
+ }
10
+ async check() {
11
+ var _a;
12
+ try {
13
+ const res = await this.api.getConnectionState();
14
+ const data = res.data;
15
+ if (((_a = data === null || data === void 0 ? void 0 : data.instance) === null || _a === void 0 ? void 0 : _a.state) === "open") {
16
+ if (this.lastReadyState !== true) {
17
+ this.listener.onConnected();
18
+ this.lastReadyState = true;
19
+ }
20
+ this.stop();
21
+ }
22
+ else {
23
+ if (this.lastReadyState !== false) {
24
+ this.listener.onDisconnected();
25
+ this.lastReadyState = false;
26
+ }
27
+ this.start();
28
+ }
29
+ }
30
+ catch (error) {
31
+ this.listener.onAuthFailure(error);
32
+ this.lastReadyState = false;
33
+ }
34
+ }
35
+ start() {
36
+ if (this.timer)
37
+ return;
38
+ this.timer = setInterval(() => {
39
+ this.check();
40
+ }, constants_1.CONNECTION_POLL_INTERVAL_MS);
41
+ }
42
+ stop() {
43
+ if (this.timer) {
44
+ clearInterval(this.timer);
45
+ this.timer = undefined;
46
+ }
47
+ }
48
+ }
49
+ exports.ConnectionPollingService = ConnectionPollingService;
@@ -0,0 +1,11 @@
1
+ import { EventEmitter } from "node:events";
2
+ import { ParsedMessage } from "../../domain/interfaces";
3
+ import { MessageMergeConfig } from "../../domain/types";
4
+ export declare class MessageBufferService extends EventEmitter {
5
+ private readonly config;
6
+ private buffers;
7
+ constructor(config: MessageMergeConfig);
8
+ buffer(data: ParsedMessage, enqueue: (task: () => Promise<void>) => void): void;
9
+ private calculateTimeout;
10
+ flush(user: string, enqueue: (task: () => Promise<void>) => void): void;
11
+ }
@@ -0,0 +1,79 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MessageBufferService = void 0;
4
+ const node_events_1 = require("node:events");
5
+ class MessageBufferService extends node_events_1.EventEmitter {
6
+ constructor(config) {
7
+ super();
8
+ this.config = config;
9
+ this.buffers = {};
10
+ }
11
+ buffer(data, enqueue) {
12
+ const user = data.from;
13
+ const now = Date.now();
14
+ if (!this.buffers[user]) {
15
+ this.buffers[user] = {
16
+ messages: [],
17
+ messageTimeout: null,
18
+ maxWaitTimeout: null,
19
+ lastData: null,
20
+ lastMessageTime: now,
21
+ messageCount: 0,
22
+ };
23
+ }
24
+ const buffer = this.buffers[user];
25
+ buffer.messages.push(data.body);
26
+ buffer.lastData = data;
27
+ buffer.messageCount++;
28
+ const timeBetweenMessages = now - buffer.lastMessageTime;
29
+ buffer.lastMessageTime = now;
30
+ if (buffer.messageTimeout) {
31
+ clearTimeout(buffer.messageTimeout);
32
+ buffer.messageTimeout = null;
33
+ }
34
+ if (buffer.messages.length === 1) {
35
+ const maxWaitTime = this.config.maxWaitTime || 30;
36
+ buffer.maxWaitTimeout = setTimeout(() => {
37
+ this.flush(user, enqueue);
38
+ }, maxWaitTime * 1000);
39
+ }
40
+ const timeout = this.calculateTimeout(data.body, timeBetweenMessages, buffer.messageCount);
41
+ buffer.messageTimeout = setTimeout(() => {
42
+ this.flush(user, enqueue);
43
+ }, timeout);
44
+ }
45
+ calculateTimeout(messageText, timeBetweenMessages, messageCount) {
46
+ var _a;
47
+ const baseTimeout = (this.config.timeMergeMessage || 3) * 1000;
48
+ const shortThreshold = this.config.shortMessageThreshold || 10;
49
+ const adaptiveTimer = (_a = this.config.adaptiveTimer) !== null && _a !== void 0 ? _a : true;
50
+ let timeout = baseTimeout;
51
+ if (messageText.length < shortThreshold) {
52
+ timeout = Math.max(1000, timeout * 0.5);
53
+ }
54
+ if (adaptiveTimer) {
55
+ if (timeBetweenMessages < 2000 && messageCount > 1) {
56
+ timeout = Math.max(timeout, 4000);
57
+ }
58
+ if (messageCount <= 2) {
59
+ timeout = Math.max(2000, timeout * 0.7);
60
+ }
61
+ }
62
+ return timeout;
63
+ }
64
+ flush(user, enqueue) {
65
+ const buffer = this.buffers[user];
66
+ if (!buffer || buffer.messages.length === 0)
67
+ return;
68
+ if (buffer.messageTimeout)
69
+ clearTimeout(buffer.messageTimeout);
70
+ if (buffer.maxWaitTimeout)
71
+ clearTimeout(buffer.maxWaitTimeout);
72
+ const fullMessage = buffer.messages.join(" ");
73
+ enqueue(async () => {
74
+ this.emit("message", { ...buffer.lastData, body: fullMessage });
75
+ });
76
+ delete this.buffers[user];
77
+ }
78
+ }
79
+ exports.MessageBufferService = MessageBufferService;
@@ -0,0 +1,40 @@
1
+ import { SendWaveApiRepository } from "../../infrastructure/persistence/sendwave-api.repository";
2
+ import { ReadMessage, SendButton, SendCarousel, SendList, SendLocation, SendMedia, SendMessage, SendPoll, SendPresence, SendReaction } from "../../domain/types";
3
+ import { ProviderInterface } from "../../domain/interfaces";
4
+ export declare class MessageSenderService implements ProviderInterface {
5
+ private readonly api;
6
+ private readonly text;
7
+ private readonly presence;
8
+ private readonly image;
9
+ private readonly video;
10
+ private readonly audio;
11
+ private readonly file;
12
+ private readonly voice;
13
+ private readonly list;
14
+ private readonly button;
15
+ private readonly carousel;
16
+ private readonly poll;
17
+ private readonly location;
18
+ private readonly reaction;
19
+ private readonly readMessagesStrategy;
20
+ constructor(api: SendWaveApiRepository);
21
+ dispatch(from: string, text: any, options?: any): Promise<any>;
22
+ sendMediaByType(data: SendMedia & {
23
+ text?: string;
24
+ }): Promise<any>;
25
+ sendText(data: SendMessage): Promise<any>;
26
+ sendPresence(data: SendPresence): Promise<any>;
27
+ sendImage(data: SendMedia): Promise<any>;
28
+ sendVideo(data: SendMedia): Promise<any>;
29
+ sendAudio(data: SendMedia): Promise<any>;
30
+ sendFile(data: SendMedia): Promise<any>;
31
+ sendVoice(data: SendMedia): Promise<any>;
32
+ sendList(data: SendList): Promise<any>;
33
+ sendButton(data: SendButton): Promise<any>;
34
+ sendCarousel(data: SendCarousel): Promise<any>;
35
+ sendPoll(data: SendPoll): Promise<any>;
36
+ sendLocation(data: SendLocation): Promise<any>;
37
+ sendReaction(data: SendReaction): Promise<any>;
38
+ sendMedia(data: SendMedia): Promise<any>;
39
+ readMessages(data: ReadMessage): Promise<any>;
40
+ }