@kangwifi-pro/waliwa 1.0.0

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 (144) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +600 -0
  3. package/dist/advanced/features.d.ts +211 -0
  4. package/dist/advanced/features.d.ts.map +1 -0
  5. package/dist/advanced/features.js +671 -0
  6. package/dist/advanced/features.js.map +1 -0
  7. package/dist/auth/auth-state.d.ts +89 -0
  8. package/dist/auth/auth-state.d.ts.map +1 -0
  9. package/dist/auth/auth-state.js +330 -0
  10. package/dist/auth/auth-state.js.map +1 -0
  11. package/dist/auth/pairing-code.d.ts +78 -0
  12. package/dist/auth/pairing-code.d.ts.map +1 -0
  13. package/dist/auth/pairing-code.js +191 -0
  14. package/dist/auth/pairing-code.js.map +1 -0
  15. package/dist/auth/qr-code.d.ts +60 -0
  16. package/dist/auth/qr-code.d.ts.map +1 -0
  17. package/dist/auth/qr-code.js +151 -0
  18. package/dist/auth/qr-code.js.map +1 -0
  19. package/dist/calls/handler.d.ts +61 -0
  20. package/dist/calls/handler.d.ts.map +1 -0
  21. package/dist/calls/handler.js +117 -0
  22. package/dist/calls/handler.js.map +1 -0
  23. package/dist/core/binary.d.ts +74 -0
  24. package/dist/core/binary.d.ts.map +1 -0
  25. package/dist/core/binary.js +448 -0
  26. package/dist/core/binary.js.map +1 -0
  27. package/dist/core/crypto.d.ts +60 -0
  28. package/dist/core/crypto.d.ts.map +1 -0
  29. package/dist/core/crypto.js +170 -0
  30. package/dist/core/crypto.js.map +1 -0
  31. package/dist/core/noise.d.ts +106 -0
  32. package/dist/core/noise.d.ts.map +1 -0
  33. package/dist/core/noise.js +307 -0
  34. package/dist/core/noise.js.map +1 -0
  35. package/dist/events/emitter.d.ts +44 -0
  36. package/dist/events/emitter.d.ts.map +1 -0
  37. package/dist/events/emitter.js +79 -0
  38. package/dist/events/emitter.js.map +1 -0
  39. package/dist/features/index.d.ts +342 -0
  40. package/dist/features/index.d.ts.map +1 -0
  41. package/dist/features/index.js +755 -0
  42. package/dist/features/index.js.map +1 -0
  43. package/dist/fixes/index.d.ts +333 -0
  44. package/dist/fixes/index.d.ts.map +1 -0
  45. package/dist/fixes/index.js +762 -0
  46. package/dist/fixes/index.js.map +1 -0
  47. package/dist/groups/management.d.ts +86 -0
  48. package/dist/groups/management.d.ts.map +1 -0
  49. package/dist/groups/management.js +443 -0
  50. package/dist/groups/management.js.map +1 -0
  51. package/dist/index.d.ts +65 -0
  52. package/dist/index.d.ts.map +1 -0
  53. package/dist/index.js +236 -0
  54. package/dist/index.js.map +1 -0
  55. package/dist/messages/media.d.ts +93 -0
  56. package/dist/messages/media.d.ts.map +1 -0
  57. package/dist/messages/media.js +252 -0
  58. package/dist/messages/media.js.map +1 -0
  59. package/dist/messages/message-encoder.d.ts +70 -0
  60. package/dist/messages/message-encoder.d.ts.map +1 -0
  61. package/dist/messages/message-encoder.js +453 -0
  62. package/dist/messages/message-encoder.js.map +1 -0
  63. package/dist/messages/send.d.ts +101 -0
  64. package/dist/messages/send.d.ts.map +1 -0
  65. package/dist/messages/send.js +409 -0
  66. package/dist/messages/send.js.map +1 -0
  67. package/dist/recovery/index.d.ts +125 -0
  68. package/dist/recovery/index.d.ts.map +1 -0
  69. package/dist/recovery/index.js +584 -0
  70. package/dist/recovery/index.js.map +1 -0
  71. package/dist/skdm/index.d.ts +220 -0
  72. package/dist/skdm/index.d.ts.map +1 -0
  73. package/dist/skdm/index.js +600 -0
  74. package/dist/skdm/index.js.map +1 -0
  75. package/dist/socket/hybrid.d.ts +118 -0
  76. package/dist/socket/hybrid.d.ts.map +1 -0
  77. package/dist/socket/hybrid.js +352 -0
  78. package/dist/socket/hybrid.js.map +1 -0
  79. package/dist/socket/wa-socket.d.ts +300 -0
  80. package/dist/socket/wa-socket.d.ts.map +1 -0
  81. package/dist/socket/wa-socket.js +1094 -0
  82. package/dist/socket/wa-socket.js.map +1 -0
  83. package/dist/socket/ws-socket.d.ts +96 -0
  84. package/dist/socket/ws-socket.d.ts.map +1 -0
  85. package/dist/socket/ws-socket.js +302 -0
  86. package/dist/socket/ws-socket.js.map +1 -0
  87. package/dist/types/index.d.ts +444 -0
  88. package/dist/types/index.d.ts.map +1 -0
  89. package/dist/types/index.js +7 -0
  90. package/dist/types/index.js.map +1 -0
  91. package/dist/utils/jid.d.ts +46 -0
  92. package/dist/utils/jid.d.ts.map +1 -0
  93. package/dist/utils/jid.js +152 -0
  94. package/dist/utils/jid.js.map +1 -0
  95. package/dist/utils/logger.d.ts +45 -0
  96. package/dist/utils/logger.d.ts.map +1 -0
  97. package/dist/utils/logger.js +79 -0
  98. package/dist/utils/logger.js.map +1 -0
  99. package/dist/utils/retry.d.ts +43 -0
  100. package/dist/utils/retry.d.ts.map +1 -0
  101. package/dist/utils/retry.js +175 -0
  102. package/dist/utils/retry.js.map +1 -0
  103. package/docs/API.md +745 -0
  104. package/docs/ARCHITECTURE.md +307 -0
  105. package/docs/BAILEYS_FIXES.md +360 -0
  106. package/docs/FEATURES.md +532 -0
  107. package/docs/PROTOCOL.md +489 -0
  108. package/docs/RECOVERY.md +409 -0
  109. package/docs/SKDM.md +233 -0
  110. package/examples/ai-bot/index.ts +479 -0
  111. package/examples/ai-bot/package.json +17 -0
  112. package/examples/echo-bot/index.ts +171 -0
  113. package/examples/echo-bot/package.json +18 -0
  114. package/examples/group-bot/index.ts +557 -0
  115. package/examples/group-bot/package.json +17 -0
  116. package/examples/rest-gateway/index.ts +499 -0
  117. package/examples/rest-gateway/package.json +19 -0
  118. package/package.json +75 -0
  119. package/src/advanced/features.ts +817 -0
  120. package/src/auth/auth-state.ts +342 -0
  121. package/src/auth/pairing-code.ts +246 -0
  122. package/src/auth/qr-code.ts +191 -0
  123. package/src/calls/handler.ts +153 -0
  124. package/src/core/binary.ts +464 -0
  125. package/src/core/crypto.ts +189 -0
  126. package/src/core/noise.ts +406 -0
  127. package/src/events/emitter.ts +88 -0
  128. package/src/features/index.ts +921 -0
  129. package/src/fixes/index.ts +882 -0
  130. package/src/groups/management.ts +497 -0
  131. package/src/index.ts +274 -0
  132. package/src/messages/media.ts +372 -0
  133. package/src/messages/message-encoder.ts +520 -0
  134. package/src/messages/send.ts +521 -0
  135. package/src/recovery/index.ts +704 -0
  136. package/src/skdm/index.ts +693 -0
  137. package/src/socket/hybrid.ts +414 -0
  138. package/src/socket/wa-socket.ts +1347 -0
  139. package/src/socket/ws-socket.ts +355 -0
  140. package/src/types/index.ts +457 -0
  141. package/src/utils/jid.ts +156 -0
  142. package/src/utils/logger.ts +84 -0
  143. package/src/utils/retry.ts +205 -0
  144. package/tsconfig.json +30 -0
@@ -0,0 +1,532 @@
1
+ # New Features
2
+
3
+ Waliwa menambahkan fitur-fitur baru yang tidak ada di Baileys atau ZapoJS. Semua fitur terintegrasi dengan WASocket dan dapat diakses dengan mudah.
4
+
5
+ ## Daftar Fitur
6
+
7
+ 1. [Health Monitor](#1-health-monitor)
8
+ 2. [Multi-account Manager](#2-multi-account-manager)
9
+ 3. [Webhook System](#3-webhook-system)
10
+ 4. [Circuit Breaker](#4-circuit-breaker)
11
+ 5. [Rate Limit Tracker](#5-rate-limit-tracker)
12
+ 6. [Message Persistence](#6-message-persistence)
13
+
14
+ ---
15
+
16
+ ## 1. Health Monitor
17
+
18
+ Real-time health tracking dengan auto-recovery. Memberikan score 0-100 berdasarkan multiple metrics.
19
+
20
+ ```typescript
21
+ const sock = makeWASocket({ authState: state });
22
+
23
+ // Get current health
24
+ const health = sock.getHealth();
25
+ console.log(health);
26
+ // {
27
+ // status: 'healthy', // 'healthy' | 'degraded' | 'unhealthy'
28
+ // score: 95, // 0-100
29
+ // connection: {
30
+ // connected: true,
31
+ // uptime: 3600000, // ms
32
+ // lastPingTime: 1234567890,
33
+ // reconnectCount: 2
34
+ // },
35
+ // metrics: {
36
+ // messagesSent: 145,
37
+ // messagesReceived: 89,
38
+ // sendFailures: 3,
39
+ // avgLatencyMs: 245
40
+ // },
41
+ // resources: {
42
+ // memoryUsageMb: 75.5,
43
+ // pendingSends: 0,
44
+ // queueSize: 0
45
+ // },
46
+ // issues: [], // List of issues jika ada
47
+ // timestamp: 1234567890
48
+ // }
49
+ ```
50
+
51
+ ### Health Events
52
+
53
+ ```typescript
54
+ import { HealthMonitor } from 'waliwa';
55
+
56
+ sock.healthMonitor.on('health:check', (status) => {
57
+ console.log(`Health: ${status.status} (${status.score}/100)`);
58
+ if (status.issues.length > 0) {
59
+ console.warn('Issues:', status.issues);
60
+ }
61
+ });
62
+
63
+ sock.healthMonitor.on('health:recovery', (status) => {
64
+ console.warn('Health degraded, triggering recovery');
65
+ // Custom recovery: reconnect, restart services, dll
66
+ });
67
+ ```
68
+
69
+ ### Custom Recovery Actions
70
+
71
+ ```typescript
72
+ sock.healthMonitor.on('health:recovery', async (status) => {
73
+ console.warn(`Health score: ${status.score}, issues: ${status.issues}`);
74
+
75
+ // Different recovery strategies based on issues
76
+ if (status.issues.includes('Connection appears down')) {
77
+ await sock.end();
78
+ await sock.connect();
79
+ }
80
+
81
+ if (status.issues.includes('High memory usage')) {
82
+ // Force garbage collection
83
+ if (global.gc) global.gc();
84
+ }
85
+
86
+ if (status.issues.includes('High send failure rate')) {
87
+ // Investigate failed messages
88
+ const failed = sock.messagePersistence.getFailed();
89
+ console.log(`${failed.length} failed messages`);
90
+ }
91
+ });
92
+ ```
93
+
94
+ ---
95
+
96
+ ## 2. Multi-account Manager
97
+
98
+ Manage multiple WhatsApp accounts dari satu process. Berguna untuk customer service bots, multiple business accounts, dll.
99
+
100
+ ```typescript
101
+ import { MultiAccountManager } from 'waliwa';
102
+
103
+ const manager = new MultiAccountManager({
104
+ maxAccounts: 10,
105
+ logger: defaultLogger
106
+ });
107
+
108
+ // Register accounts
109
+ manager.registerAccount({
110
+ id: 'sales',
111
+ name: 'Sales Bot',
112
+ authFolder: './auth-sales',
113
+ enabled: true,
114
+ config: {
115
+ printQRInTerminal: false,
116
+ ramOptimizationLevel: 3 // Ultra untuk multi-account
117
+ }
118
+ });
119
+
120
+ manager.registerAccount({
121
+ id: 'support',
122
+ name: 'Support Bot',
123
+ authFolder: './auth-support',
124
+ enabled: true,
125
+ config: {
126
+ printQRInTerminal: false
127
+ }
128
+ });
129
+
130
+ // Connect accounts
131
+ await manager.connectAccount('sales');
132
+ await manager.connectAccount('support');
133
+
134
+ // Send message from specific account
135
+ await manager.sendMessageFrom('sales', '6281@s.whatsapp.net', {
136
+ text: 'Hello from Sales!'
137
+ });
138
+
139
+ await manager.sendMessageFrom('support', '6282@s.whatsapp.net', {
140
+ text: 'Hello from Support!'
141
+ });
142
+
143
+ // Broadcast to all accounts
144
+ await manager.broadcast('6281@s.whatsapp.net', {
145
+ text: 'Broadcast from all accounts'
146
+ });
147
+
148
+ // List all accounts status
149
+ console.log(manager.listAccounts());
150
+ // [
151
+ // { id: 'sales', name: 'Sales Bot', connected: true, messagesSent: 5, ... },
152
+ // { id: 'support', name: 'Support Bot', connected: true, messagesSent: 3, ... }
153
+ // ]
154
+ ```
155
+
156
+ ### Per-account Events
157
+
158
+ ```typescript
159
+ manager.on('account:connected', (accountId) => {
160
+ console.log(`Account ${accountId} connected`);
161
+ });
162
+
163
+ manager.on('account:disconnected', (accountId) => {
164
+ console.log(`Account ${accountId} disconnected`);
165
+ });
166
+
167
+ manager.on('account:message', (accountId, messages) => {
168
+ console.log(`Account ${accountId} received ${messages.length} messages`);
169
+ // Route to different handlers based on accountId
170
+ if (accountId === 'sales') {
171
+ handleSalesMessage(messages);
172
+ } else if (accountId === 'support') {
173
+ handleSupportMessage(messages);
174
+ }
175
+ });
176
+ ```
177
+
178
+ ---
179
+
180
+ ## 3. Webhook System
181
+
182
+ Reliable webhook delivery dengan HMAC-SHA256 signature + retry + dead letter queue.
183
+
184
+ ### Register Webhook
185
+
186
+ ```typescript
187
+ sock.registerWebhook({
188
+ url: 'https://yourapp.com/webhook',
189
+ events: ['message.sent', 'message.failed', 'auth.success'],
190
+ secret: 'your-webhook-secret', // HMAC signing
191
+ enabled: true,
192
+ retryCount: 5,
193
+ retryDelayMs: 1000,
194
+ timeoutMs: 10000
195
+ });
196
+
197
+ // Register untuk all events
198
+ sock.registerWebhook({
199
+ url: 'https://yourapp.com/all-events',
200
+ events: ['*'],
201
+ enabled: true
202
+ });
203
+ ```
204
+
205
+ ### Verify Webhook Signature (di receiver)
206
+
207
+ ```javascript
208
+ // Express middleware untuk verify HMAC signature
209
+ const crypto = require('crypto');
210
+
211
+ app.post('/webhook', (req, res) => {
212
+ const signature = req.headers['x-waliwa-signature'];
213
+ const event = req.headers['x-waliwa-event'];
214
+
215
+ // Verify signature
216
+ const expected = 'sha256=' + crypto
217
+ .createHmac('sha256', 'your-webhook-secret')
218
+ .update(JSON.stringify(req.body))
219
+ .digest('hex');
220
+
221
+ if (signature !== expected) {
222
+ return res.status(401).send('Invalid signature');
223
+ }
224
+
225
+ // Process event
226
+ console.log(`Received event: ${event}`);
227
+ console.log('Payload:', req.body);
228
+
229
+ res.json({ received: true });
230
+ });
231
+ ```
232
+
233
+ ### Webhook Stats & Dead Letter Queue
234
+
235
+ ```typescript
236
+ const stats = sock.webhookManager.getStats();
237
+ console.log(stats);
238
+ // {
239
+ // sent: 145,
240
+ // failed: 3,
241
+ // retried: 8,
242
+ // pending: 0,
243
+ // deadLettered: 2
244
+ // }
245
+
246
+ // Get dead letter queue
247
+ const dlq = sock.webhookManager.getDeadLetterQueue();
248
+ console.log(`${dlq.length} permanently failed webhooks`);
249
+
250
+ // Retry dari DLQ
251
+ sock.webhookManager.retryDeadLetter(); // Retry all
252
+ // atau specific:
253
+ sock.webhookManager.retryDeadLetter('wh-1234567890-abc');
254
+ ```
255
+
256
+ ---
257
+
258
+ ## 4. Circuit Breaker
259
+
260
+ Prevent retry storms untuk failed recipients. Saat recipient terus gagal (e.g., user block bot, invalid number), circuit breaker akan "open" dan stop sending sementara.
261
+
262
+ ```typescript
263
+ // Otomatis aktif di WASocket.sendMessage()
264
+ // Check sebelum send:
265
+ if (!sock.circuitBreaker.canSend(jid)) {
266
+ console.log(`Circuit open for ${jid}, skipping send`);
267
+ return;
268
+ }
269
+
270
+ // Stats
271
+ console.log(sock.circuitBreaker.getOpenCircuits());
272
+ // [
273
+ // { jid: '6281@s.whatsapp.net', failureCount: 5, lastFailureTime: 1234567890 },
274
+ // { jid: '6282@s.whatsapp.net', failureCount: 3, lastFailureTime: 1234567891 }
275
+ // ]
276
+
277
+ // Manually reset circuit
278
+ sock.resetCircuitBreaker('6281@s.whatsapp.net');
279
+
280
+ // Get state untuk specific recipient
281
+ const state = sock.circuitBreaker.getState('6281@s.whatsapp.net');
282
+ // 'closed' (normal) | 'open' (blocked) | 'half-open' (testing)
283
+ ```
284
+
285
+ ### States
286
+
287
+ | State | Behavior |
288
+ |-------|----------|
289
+ | `closed` | Normal operation, sends allowed |
290
+ | `open` | Blocked, no sends (after 5 failures) |
291
+ | `half-open` | Allow 1 test send (after cooldown 60s) |
292
+
293
+ ### Configuration
294
+
295
+ ```typescript
296
+ import { CircuitBreaker } from 'waliwa';
297
+
298
+ // Custom instance
299
+ const breaker = new CircuitBreaker({
300
+ failureThreshold: 5, // Open after 5 failures
301
+ cooldownMs: 60000 // 1 minute cooldown
302
+ });
303
+ ```
304
+
305
+ ---
306
+
307
+ ## 5. Rate Limit Tracker
308
+
309
+ Per-recipient rate limiting untuk comply dengan WhatsApp server limits.
310
+
311
+ ```typescript
312
+ // Default limits (WhatsApp server):
313
+ // - 5 messages/sec per recipient
314
+ // - 60 messages/min per recipient
315
+ // - 1000 messages/hour per recipient
316
+
317
+ // Otomatis di WASocket.sendMessage()
318
+ // Throw error jika exceeded:
319
+ try {
320
+ await sock.sendMessage(jid, { text: 'Hi' });
321
+ } catch (err) {
322
+ if (err.message.includes('Rate limit exceeded')) {
323
+ console.log('Rate limited, waiting...');
324
+ await sleep(60000); // Wait 1 min
325
+ await sock.sendMessage(jid, { text: 'Hi' });
326
+ }
327
+ }
328
+
329
+ // Check remaining quota
330
+ const quota = sock.rateLimitTracker.getRemainingQuota('6281@s.whatsapp.net');
331
+ console.log(quota);
332
+ // { second: 4, minute: 45, hour: 950 }
333
+
334
+ // Manual reset (e.g., after user interaction)
335
+ sock.rateLimitTracker.reset('6281@s.whatsapp.net');
336
+ ```
337
+
338
+ ### Custom Limits
339
+
340
+ ```typescript
341
+ import { RateLimitTracker } from 'waliwa';
342
+
343
+ const tracker = new RateLimitTracker({
344
+ perSecond: 3, // Custom: 3/sec
345
+ perMinute: 30, // Custom: 30/min
346
+ perHour: 500 // Custom: 500/hour
347
+ });
348
+ ```
349
+
350
+ ---
351
+
352
+ ## 6. Message Persistence
353
+
354
+ Persist outgoing messages untuk survive restarts. Pending messages akan di-retry setelah bot restart.
355
+
356
+ ```typescript
357
+ // Otomatis aktif di WASocket.sendMessage()
358
+ // Setiap message disimpan dengan status 'pending'
359
+ // Setelah send berhasil: status='sent'
360
+ // Setelah send gagal: status='failed'
361
+
362
+ // Get pending messages (untuk manual retry)
363
+ const pending = sock.messagePersistence.getPending();
364
+ console.log(`${pending.length} pending messages`);
365
+
366
+ // Get failed messages
367
+ const failed = sock.messagePersistence.getFailed();
368
+ console.log(`${failed.length} failed messages`);
369
+
370
+ // Retry all failed messages
371
+ await sock.retryFailedMessages();
372
+
373
+ // Manual update
374
+ sock.messagePersistence.update('msg-123', {
375
+ status: 'sent',
376
+ attempts: 2
377
+ });
378
+
379
+ // Delete old message
380
+ sock.messagePersistence.delete('msg-123');
381
+
382
+ // Cleanup old messages (older than 24 hours)
383
+ const cleaned = sock.messagePersistence.cleanup(24 * 60 * 60 * 1000);
384
+ console.log(`Cleaned ${cleaned} old messages`);
385
+ ```
386
+
387
+ ### Auto-recovery on Restart
388
+
389
+ Saat bot restart, pending messages otomatis di-retry:
390
+
391
+ ```typescript
392
+ const sock = makeWASocket({ authState: state });
393
+
394
+ sock.ev.on('connection.update', async ({ connection }) => {
395
+ if (connection === 'open') {
396
+ // Otomatis: pending messages dari session sebelumnya di-retry
397
+ console.log('Bot reconnected, retrying pending messages...');
398
+ }
399
+ });
400
+ ```
401
+
402
+ ---
403
+
404
+ ## Integration Examples
405
+
406
+ ### Complete Production Setup
407
+
408
+ ```typescript
409
+ import { makeWASocket, useFileAuthState } from 'waliwa';
410
+
411
+ async function main() {
412
+ const state = await useFileAuthState('./auth');
413
+
414
+ const sock = makeWASocket({
415
+ authState: state,
416
+ printQRInTerminal: true,
417
+ ramOptimizationLevel: 2,
418
+
419
+ // SKDM config
420
+ minPreKeys: 5,
421
+ targetPreKeyCount: 100,
422
+ maxSessionCacheSize: 500
423
+ });
424
+
425
+ // Register webhook untuk external system
426
+ sock.registerWebhook({
427
+ url: 'https://api.yourapp.com/whatsapp/events',
428
+ events: ['message.sent', 'message.failed', 'auth.success', 'auth.failure'],
429
+ secret: process.env.WEBHOOK_SECRET,
430
+ enabled: true,
431
+ retryCount: 5,
432
+ retryDelayMs: 1000,
433
+ timeoutMs: 10000
434
+ });
435
+
436
+ // Health monitoring
437
+ sock.healthMonitor.on('health:check', (status) => {
438
+ if (status.status === 'degraded' || status.status === 'unhealthy') {
439
+ console.warn(`Health degraded: ${status.score}/100`, status.issues);
440
+ // Send alert to admin
441
+ }
442
+ });
443
+
444
+ sock.healthMonitor.on('health:recovery', async (status) => {
445
+ console.warn('Triggering recovery actions');
446
+ // Custom recovery: restart, notify admin, dll
447
+ });
448
+
449
+ // Message handling
450
+ sock.ev.on('message.upsert', async ({ messages, type }) => {
451
+ if (type !== 'notify') return;
452
+
453
+ for (const msg of messages) {
454
+ if (msg.key.fromMe) continue;
455
+
456
+ try {
457
+ // sendMessage sudah otomatis:
458
+ // - Check circuit breaker
459
+ // - Check rate limit
460
+ // - Persist message
461
+ // - Queue per recipient
462
+ // - Trigger webhook
463
+ await sock.sendMessage(msg.key.remoteJid, {
464
+ text: 'Hello!',
465
+ quoted: msg
466
+ });
467
+ } catch (err) {
468
+ if (err.message.includes('Circuit breaker')) {
469
+ console.log(`Skipping send to ${msg.key.remoteJid} (circuit open)`);
470
+ } else if (err.message.includes('Rate limit')) {
471
+ console.log(`Rate limited for ${msg.key.remoteJid}`);
472
+ } else {
473
+ console.error('Send failed:', err.message);
474
+ }
475
+ }
476
+ }
477
+ });
478
+
479
+ // Periodic stats logging
480
+ setInterval(() => {
481
+ const stats = sock.getSystemStats();
482
+ console.log('System stats:', JSON.stringify(stats, null, 2));
483
+ }, 60000);
484
+
485
+ // Graceful shutdown
486
+ process.on('SIGINT', async () => {
487
+ console.log('Shutting down...');
488
+ await sock.end();
489
+ process.exit(0);
490
+ });
491
+ }
492
+
493
+ main();
494
+ ```
495
+
496
+ ### Monitoring Dashboard
497
+
498
+ ```typescript
499
+ // Endpoint untuk monitoring (e.g., di REST gateway)
500
+ app.get('/health', (req, res) => {
501
+ res.json(sock.getHealth());
502
+ });
503
+
504
+ app.get('/stats', (req, res) => {
505
+ res.json({
506
+ system: sock.getSystemStats(),
507
+ skdm: sock.getSKDMStats(),
508
+ health: sock.getHealth()
509
+ });
510
+ });
511
+
512
+ app.get('/stats/circuits', (req, res) => {
513
+ res.json({
514
+ open: sock.circuitBreaker.getOpenCircuits(),
515
+ webhooks: sock.webhookManager.getStats(),
516
+ messages: {
517
+ pending: sock.messagePersistence.getPending().length,
518
+ failed: sock.messagePersistence.getFailed().length
519
+ }
520
+ });
521
+ });
522
+
523
+ app.post('/circuits/:jid/reset', (req, res) => {
524
+ sock.resetCircuitBreaker(req.params.jid);
525
+ res.json({ success: true });
526
+ });
527
+
528
+ app.post('/messages/retry-failed', async (req, res) => {
529
+ await sock.retryFailedMessages();
530
+ res.json({ success: true });
531
+ });
532
+ ```