@gamastudio/sendwave-provider 1.0.2 → 1.0.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.
Files changed (103) hide show
  1. package/.github/workflows/build.yml +31 -31
  2. package/README.md +261 -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 +73 -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,85 @@ 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
+ "bot-sales"
51
+ );
52
+
53
+ // Or use the default (first instance)
54
+ await provider.sendText({
55
+ from: "573123456789",
56
+ text: "Hello!",
57
+ });
58
+
59
+ // List registered instances
60
+ console.log(provider.getInstances().map((i) => i.name));
61
+ ```
62
+
63
+ **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.
64
+
65
+ **QR / State per instance:**
66
+ - `/qr` → default instance
67
+ - `/qr/bot-sales` → specific instance
68
+ - `/state` → default instance
69
+ - `/state/bot-sales` → specific instance
70
+
71
+ ## ⚙️ Configuration
72
+
73
+ ```typescript
74
+ const provider = createSendWaveProvider({
75
+ name: "MyBot",
76
+ apiKey: "your-api-key-here",
77
+ url: "https://msg.sendwaves.cloud", // Optional
78
+ port: 3000,
79
+ host: "0.0.0.0",
80
+ readMessages: true, // Auto-mark incoming messages as read
81
+ message: {
82
+ mergeMessage: true, // Merge rapid messages from same user
83
+ timeMergeMessage: 3, // Base wait time in seconds
84
+ maxWaitTime: 30, // Maximum wait time in seconds
85
+ shortMessageThreshold: 10, // Short messages merge faster
86
+ adaptiveTimer: true, // Smart timing based on message patterns
87
+ },
88
+ queueFlow: {
89
+ enabled: true,
90
+ warningTimeout: 30 * 60 * 1000, // 30 minutes
91
+ endTimeout: 2 * 60 * 1000, // 2 minutes after warning
92
+ },
93
+ payloadLimits: {
94
+ json: "50mb",
95
+ urlencoded: "50mb",
96
+ text: "10mb",
97
+ media: {
98
+ image: "10mb",
99
+ video: "100mb",
100
+ audio: "50mb",
101
+ document: "25mb",
102
+ },
103
+ },
104
+ });
105
+ ```
106
+
28
107
  ## 📤 Sending Messages
29
108
 
30
109
  ### Text Messages
@@ -33,31 +112,75 @@ await provider.initAll(provider.globalVendorArgs.port);
33
112
  await provider.sendText({
34
113
  from: "573123456789",
35
114
  text: "Hello! Welcome to our service.",
115
+ delay: 2000, // Optional typing delay
36
116
  });
117
+
118
+ // Send to a specific instance (multi-tenant)
119
+ await provider.sendText(
120
+ { from: "573123456789", text: "Hello!" },
121
+ "bot-sales"
122
+ );
37
123
  ```
38
124
 
39
- ### Interactive Polls (New! 📊)
125
+ ### Presence Indicators
40
126
 
41
127
  ```typescript
42
- await provider.sendPoll({
128
+ await provider.sendPresence({
43
129
  from: "573123456789",
44
- poll: {
45
- name: "¿Qué te parece el nuevo proveedor?",
46
- values: ["Excelente", "Bueno", "Mejorable"],
47
- selectableCount: 1 // Optional, default: 1
48
- }
130
+ presence: "composing", // or "recording"
131
+ delay: 2000,
132
+ });
133
+ ```
134
+
135
+ ### Media Messages
136
+
137
+ ```typescript
138
+ // Image
139
+ await provider.sendImage({
140
+ from: "573123456789",
141
+ url: "https://example.com/image.jpg",
142
+ text: "Optional caption",
143
+ });
144
+
145
+ // Video
146
+ await provider.sendVideo({
147
+ from: "573123456789",
148
+ url: "https://example.com/video.mp4",
149
+ text: "Optional caption",
150
+ });
151
+
152
+ // Document/File
153
+ await provider.sendFile({
154
+ from: "573123456789",
155
+ url: "https://example.com/file.pdf",
156
+ fileName: "document.pdf",
157
+ text: "Optional caption",
158
+ });
159
+
160
+ // Audio
161
+ await provider.sendAudio({
162
+ from: "573123456789",
163
+ url: "https://example.com/audio.mp3",
164
+ });
165
+
166
+ // Voice message (PTT)
167
+ await provider.sendVoice({
168
+ from: "573123456789",
169
+ url: "https://example.com/voice.ogg",
49
170
  });
50
171
  ```
51
172
 
52
- ### Intelligent Buttons (Easier! 🔘)
173
+ ### Interactive Buttons
53
174
 
54
- The provider is now "intelligent" and accepts multiple formats for buttons.
175
+ The provider accepts multiple formats for buttons.
55
176
 
56
177
  ```typescript
57
178
  // 1. Super Simple (Quick Replies)
58
179
  await provider.sendButton({
59
180
  from: "573123456789",
60
181
  title: "Main Menu",
182
+ body: "Select an option",
183
+ footer: "Sendwaves Provider",
61
184
  buttons: ["🛒 Catalog", "🎁 Promotions", "📞 Support"]
62
185
  });
63
186
 
@@ -69,6 +192,7 @@ await provider.sendButton({
69
192
  buttons: [
70
193
  { type: "reply", displayText: "Cash" },
71
194
  { type: "url", displayText: "Visit Web", url: "https://example.com" },
195
+ { type: "call", displayText: "Call Us", phoneNumber: "+573123456789" },
72
196
  { type: "copy", displayText: "Copy Coupon", copyText: "SAVE20" },
73
197
  { type: "pix", displayText: "Pay with Pix", keyType: "email", key: "pix@bot.com" }
74
198
  ]
@@ -82,7 +206,40 @@ await provider.sendButton({
82
206
  - `copy`: Copies text or code to clipboard.
83
207
  - `pix`: Brazilian payment system integration.
84
208
 
85
- ### Intelligent Lists (Simple & Advanced 📜)
209
+ ### Carousel Messages
210
+
211
+ Send a scrollable carousel with multiple cards. Each card supports its own buttons.
212
+
213
+ ```typescript
214
+ await provider.sendCarousel({
215
+ from: "573123456789",
216
+ title: "Featured Products",
217
+ description: "Swipe to see our best offers",
218
+ footer: "Sendwaves Store",
219
+ cards: [
220
+ {
221
+ text: "Premium Plan",
222
+ thumbnailUrl: "https://example.com/plan1.jpg",
223
+ footerText: "Best value",
224
+ buttons: [
225
+ { type: "reply", displayText: "Buy Now" },
226
+ { type: "url", displayText: "More Info", url: "https://example.com/plan1" }
227
+ ]
228
+ },
229
+ {
230
+ text: "Basic Plan",
231
+ thumbnailUrl: "https://example.com/plan2.jpg",
232
+ footerText: "Affordable",
233
+ buttons: [
234
+ { type: "reply", displayText: "Buy Now" }
235
+ ]
236
+ }
237
+ ],
238
+ delay: 2000
239
+ });
240
+ ```
241
+
242
+ ### Interactive Lists
86
243
 
87
244
  ```typescript
88
245
  // Simple List (Array of strings)
@@ -100,7 +257,9 @@ await provider.sendList({
100
257
  from: "573123456789",
101
258
  list: {
102
259
  title: "Product Categories",
260
+ description: "Choose a category",
103
261
  button: "Select Category",
262
+ footerText: "Sendwaves Provider",
104
263
  content: [
105
264
  {
106
265
  title: "Electronics",
@@ -114,24 +273,79 @@ await provider.sendList({
114
273
  });
115
274
  ```
116
275
 
117
- ### Media Messages
276
+ ### Polls
277
+
278
+ ```typescript
279
+ await provider.sendPoll({
280
+ from: "573123456789",
281
+ poll: {
282
+ name: "¿Qué te parece el nuevo proveedor?",
283
+ values: ["Excelente", "Bueno", "Mejorable"],
284
+ selectableCount: 1 // Optional, default: 1
285
+ }
286
+ });
287
+ ```
288
+
289
+ ### Locations
290
+
291
+ ```typescript
292
+ await provider.sendLocation({
293
+ from: "573123456789",
294
+ name: "Our Office",
295
+ address: "123 Main St, Bogotá",
296
+ latitude: 4.711,
297
+ longitude: -74.0721,
298
+ });
299
+ ```
300
+
301
+ ### Reactions
302
+
303
+ ```typescript
304
+ await provider.sendReaction({
305
+ key: {
306
+ remoteJid: "573123456789@s.whatsapp.net",
307
+ fromMe: false,
308
+ id: "MESSAGE_ID",
309
+ },
310
+ reaction: "👍",
311
+ });
312
+ ```
313
+
314
+ ### Mark as Read
118
315
 
119
316
  ```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://..." });
317
+ await provider.readMessages({
318
+ from: "573123456789@s.whatsapp.net",
319
+ messageId: "MESSAGE_ID",
320
+ });
125
321
  ```
126
322
 
127
- ## 🔄 Queue Flow System (100% Automatic)
323
+ ## 🧠 Intelligent Message Merging
128
324
 
129
- The Queue Flow system manages user sessions, handling timeouts and inactive users automatically.
325
+ When `mergeMessage` is enabled, rapid messages from the same user are merged into a single message.
326
+
327
+ ```typescript
328
+ const provider = createSendWaveProvider({
329
+ message: {
330
+ mergeMessage: true,
331
+ timeMergeMessage: 4,
332
+ maxWaitTime: 30,
333
+ shortMessageThreshold: 10,
334
+ adaptiveTimer: true,
335
+ }
336
+ });
337
+ ```
338
+
339
+ Example:
340
+ - User sends "Hola" then "como estas"
341
+ - Result: single message with body `"Hola como estas"`
342
+
343
+ ## 🔄 Queue Flow System
344
+
345
+ Automatic session management for inactive users.
130
346
 
131
347
  ```typescript
132
348
  const provider = createSendWaveProvider({
133
- name: "MyBot",
134
- apiKey: "your-key",
135
349
  queueFlow: {
136
350
  enabled: true,
137
351
  warningTimeout: 30 * 60 * 1000, // 30 mins
@@ -139,20 +353,42 @@ const provider = createSendWaveProvider({
139
353
  warningMessage: "⏳ You seem inactive. Still there?",
140
354
  },
141
355
  });
356
+
357
+ // Listen to inactivity events
358
+ provider.onQueueFlowEvent("userInactive-573123456789", (data) => {
359
+ console.log(data.isActive ? "User warned" : "Session ended");
360
+ });
142
361
  ```
143
362
 
144
363
  ## 🎯 Event System
145
364
 
146
365
  ```typescript
147
- provider.on('ready', (isReady) => console.log('Connected:', isReady));
148
- provider.on('message', (ctx) => console.log(`From ${ctx.from}: ${ctx.body}`));
366
+ provider.on("ready", (isReady) => console.log("Connected:", isReady));
367
+ provider.on("message", (ctx) => console.log(`From ${ctx.from}: ${ctx.body}`));
368
+ provider.on("user-message", (data) => console.log("User message:", data));
369
+ provider.on("flow-message", (data) => console.log("Flow message:", data));
370
+ provider.on("auth_failure", (payload) => console.error("Auth failed:", payload));
371
+ provider.on("notice", ({ title, instructions }) => console.log(title, instructions));
149
372
  ```
150
373
 
151
374
  ## 🏗️ Architecture
152
375
 
153
- - **Provider Layer**: Main SendWaveProvider class.
154
- - **Sender Layer**: Intelligent message transformation.
155
- - **Queue Flow**: Automatic session management.
376
+ The project is organized in modular layers:
377
+
378
+ - **`config/`**: Default values and constants.
379
+ - **`domain/`**: Pure types and interfaces.
380
+ - **`infrastructure/`**: HTTP client, API repository, media detection, message parser.
381
+ - **`application/`**: Business logic: message strategies, buffer merging, connection polling, queue flow.
382
+ - **`presentation/`**: Provider and core classes, HTML pages.
383
+ - **`shared/`**: Common utilities and exceptions.
384
+
385
+ ### Adding a New Message Type
386
+
387
+ 1. Create a strategy in `src/application/messaging/strategies/`.
388
+ 2. Implement the `SendStrategy<T>` interface.
389
+ 3. Register it in `MessageSenderService`.
390
+ 4. Add the public method to `SendWaveProvider`.
391
+ 5. Add the type to `ProviderInterface` and `message.types.ts`.
156
392
 
157
393
  ## 📜 License
158
394
 
@@ -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
+ }