@badzz88/baileys 8.5.4 → 8.5.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 (107) hide show
  1. package/README.md +1 -1
  2. package/WAProto/WAProto.proto +75 -1256
  3. package/WAProto/index.d.ts +19729 -36122
  4. package/WAProto/index.js +132193 -178538
  5. package/engine-requirements.js +15 -7
  6. package/lib/Defaults/index.js +14 -22
  7. package/lib/Signal/Group/group_cipher.js +5 -5
  8. package/lib/Signal/Group/keyhelper.js +4 -4
  9. package/lib/Signal/Group/sender-chain-key.js +2 -2
  10. package/lib/Signal/Group/sender-key-distribution-message.js +1 -1
  11. package/lib/Signal/Group/sender-key-message.js +3 -3
  12. package/lib/Signal/Group/sender-key-record.js +1 -1
  13. package/lib/Signal/Group/sender-message-key.js +5 -5
  14. package/lib/Signal/libsignal.js +28 -39
  15. package/lib/Signal/lid-mapping.js +8 -28
  16. package/lib/Socket/aigroups.js +5 -5
  17. package/lib/Socket/business.js +11 -3
  18. package/lib/Socket/chats.js +319 -668
  19. package/lib/Socket/communities.js +89 -85
  20. package/lib/Socket/graphql.js +228 -435
  21. package/lib/Socket/groups.js +211 -132
  22. package/lib/Socket/index.js +7 -9
  23. package/lib/Socket/interactive-handler.js +60 -117
  24. package/lib/Socket/interop.js +175 -56
  25. package/lib/Socket/luxu.js +387 -0
  26. package/lib/Socket/managed-account.js +48 -79
  27. package/lib/Socket/messages-recv.js +340 -898
  28. package/lib/Socket/messages-send.js +160 -462
  29. package/lib/Socket/mex.js +53 -40
  30. package/lib/Socket/newsletter.js +295 -291
  31. package/lib/Socket/privacy.js +265 -77
  32. package/lib/Socket/registration.js +157 -164
  33. package/lib/Socket/socket.js +199 -181
  34. package/lib/Socket/text-router.js +83 -0
  35. package/lib/Socket/username.js +227 -144
  36. package/lib/Store/keyed-db.js +108 -0
  37. package/lib/Store/make-cache-manager-store.js +2 -2
  38. package/lib/Store/make-in-memory-store.js +141 -25
  39. package/lib/Types/Auth.js +3 -3
  40. package/lib/Types/Label.js +1 -1
  41. package/lib/Types/LabelAssociation.js +1 -1
  42. package/lib/Types/Mex.js +112 -0
  43. package/lib/Types/Newsletter.js +65 -96
  44. package/lib/Types/State.js +4 -4
  45. package/lib/Types/index.js +1 -1
  46. package/lib/Utils/auth-utils.js +57 -52
  47. package/lib/Utils/browser-utils.js +1 -1
  48. package/lib/Utils/business.js +6 -2
  49. package/lib/Utils/chat-utils.js +38 -102
  50. package/lib/Utils/command-loader.d.ts +31 -0
  51. package/lib/Utils/command-loader.js +100 -0
  52. package/lib/Utils/companion-reg-client-utils.js +42 -0
  53. package/lib/Utils/crypto.js +84 -54
  54. package/lib/Utils/curve25519-js.js +275 -0
  55. package/lib/Utils/decode-wa-message.js +83 -316
  56. package/lib/Utils/event-buffer.js +61 -37
  57. package/lib/Utils/generics.js +103 -157
  58. package/lib/Utils/group-history.js +12 -3
  59. package/lib/Utils/history.js +15 -15
  60. package/lib/Utils/index.js +5 -1
  61. package/lib/Utils/link-preview.js +8 -3
  62. package/lib/Utils/lt-hash.js +35 -2
  63. package/lib/Utils/make-mutex.js +1 -1
  64. package/lib/Utils/message-retry-manager.js +92 -27
  65. package/lib/Utils/messages-media.js +130 -159
  66. package/lib/Utils/messages.js +566 -144
  67. package/lib/Utils/meta-ai-msmsg.js +202 -62
  68. package/lib/Utils/native-bridge.js +82 -0
  69. package/lib/Utils/noise-handler.js +21 -19
  70. package/lib/Utils/offline-node-processor.js +8 -3
  71. package/lib/Utils/pre-key-manager.js +23 -13
  72. package/lib/Utils/process-message.js +106 -197
  73. package/lib/Utils/reporting-utils.js +2 -2
  74. package/lib/Utils/session-pool.d.ts +16 -0
  75. package/lib/Utils/session-pool.js +94 -0
  76. package/lib/Utils/signal.js +63 -28
  77. package/lib/Utils/stanza-ack.js +12 -39
  78. package/lib/Utils/sticker.d.ts +17 -0
  79. package/lib/Utils/sticker.js +145 -0
  80. package/lib/Utils/sync-action-utils.js +7 -4
  81. package/lib/Utils/tc-token-utils.js +25 -20
  82. package/lib/Utils/use-multi-file-auth-state.js +14 -30
  83. package/lib/Utils/validate-connection.js +19 -59
  84. package/lib/Utils/view-once-cache.d.ts +9 -0
  85. package/lib/Utils/view-once-cache.js +79 -0
  86. package/lib/WABinary/decode.js +16 -50
  87. package/lib/WABinary/encode.js +224 -54
  88. package/lib/WABinary/generic-utils.js +97 -7
  89. package/lib/WABinary/jid-utils.js +14 -27
  90. package/lib/WAM/encode.js +7 -7
  91. package/lib/WAUSync/Protocols/USyncBusinessProtocol.js +1 -58
  92. package/lib/WAUSync/Protocols/USyncContactProtocol.js +1 -7
  93. package/lib/WAUSync/Protocols/USyncDeviceProtocol.js +5 -13
  94. package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.js +4 -1
  95. package/lib/WAUSync/Protocols/USyncFeatureProtocol.js +7 -20
  96. package/lib/WAUSync/Protocols/USyncPictureProtocol.js +1 -1
  97. package/lib/WAUSync/Protocols/USyncSidelistProtocol.js +2 -2
  98. package/lib/WAUSync/Protocols/USyncStatusProtocol.js +2 -7
  99. package/lib/WAUSync/Protocols/USyncTextStatusProtocol.js +1 -1
  100. package/lib/WAUSync/Protocols/USyncUsernameProtocol.js +1 -2
  101. package/lib/WAUSync/Protocols/index.js +0 -1
  102. package/lib/WAUSync/USyncQuery.js +6 -77
  103. package/lib/WAUSync/USyncUser.js +8 -16
  104. package/lib/WAUSync/index.js +0 -1
  105. package/lib/antiban.js +854 -608
  106. package/lib/index.js +1 -1
  107. package/package.json +20 -16
package/lib/antiban.js CHANGED
@@ -23,17 +23,17 @@ var __copyProps = (to, from, except, desc) => {
23
23
  var __toESM = (mod, isNodeMode, target) => (
24
24
  (target = mod != null ? __create(__getProtoOf(mod)) : {}),
25
25
  __copyProps(
26
-
27
-
28
-
29
-
26
+ // If the importer is in node compatibility mode or this is not an ESM
27
+ // file that has been converted to a CommonJS file using a Babel-
28
+ // compatible transform (i.e. "__esModule" has not been set), then set
29
+ // "default" to the CommonJS "module.exports" for node compatibility.
30
30
  isNodeMode || !mod || !mod.__esModule ? __defProp(target, 'default', { value: mod, enumerable: true }) : target,
31
31
  mod
32
32
  )
33
33
  )
34
34
  var __toCommonJS = mod => __copyProps(__defProp({}, '__esModule', { value: true }), mod)
35
35
 
36
-
36
+ // index.js
37
37
  var index_exports = {}
38
38
  __export(index_exports, {
39
39
  AntiBan: () => AntiBan,
@@ -61,7 +61,6 @@ __export(index_exports, {
61
61
  WebhookAlerts: () => WebhookAlerts,
62
62
  applyFingerprint: () => applyFingerprint,
63
63
  applyGroupMultiplier: () => applyGroupMultiplier,
64
- buildContentSignature: () => buildContentSignature,
65
64
  classifyDisconnect: () => classifyDisconnect,
66
65
  createLidFirstResolver: () => createLidFirstResolver,
67
66
  credsSnapshot: () => credsSnapshot,
@@ -83,44 +82,46 @@ __export(index_exports, {
83
82
  })
84
83
  module.exports = __toCommonJS(index_exports)
85
84
 
86
-
85
+ // rateLimiter.js
87
86
  var TIME_CONSTANTS = {
88
87
  MS_PER_SECOND: 1e3,
89
88
  MS_PER_MINUTE: 6e4,
90
89
  MS_PER_HOUR: 36e5,
91
90
  MS_PER_DAY: 864e5,
92
91
  BURST_RESET_MS: 3e4,
93
- IDENTICAL_WINDOW_MS: 18e5
94
-
92
+ IDENTICAL_WINDOW_MS: 36e5
93
+ // 1 hour
95
94
  }
96
95
  var DEFAULT_CONFIG = {
97
- maxPerMinute: 12,
98
- maxPerHour: 400,
99
- maxPerDay: 2500,
100
- minDelayMs: 1200,
101
- maxDelayMs: 4e3,
102
- newChatDelayMs: 2e3,
103
- maxIdenticalMessages: 10,
104
- burstAllowance: 5,
96
+ maxPerMinute: 8,
97
+ maxPerHour: 200,
98
+ maxPerDay: 1500,
99
+ minDelayMs: 1500,
100
+ maxDelayMs: 5e3,
101
+ newChatDelayMs: 3e3,
102
+ maxIdenticalMessages: 3,
103
+ burstAllowance: 3,
105
104
  identicalMessageWindowMs: TIME_CONSTANTS.IDENTICAL_WINDOW_MS
106
105
  }
107
106
  var RateLimiter = class {
108
107
  config
109
108
  messages = []
110
- identicalCount = new Map()
111
- knownChats = new Set()
109
+ identicalCount = /* @__PURE__ */ new Map()
110
+ knownChats = /* @__PURE__ */ new Set()
112
111
  burstCount = 0
113
112
  lastMessageTime = 0
114
- statsCacheTime = 0
115
- statsCache = null
116
113
  constructor(config = {}) {
117
114
  this.config = { ...DEFAULT_CONFIG, ...config }
118
115
  }
119
-
120
- async getDelay(recipient, content, dedupKey) {
116
+ /**
117
+ * Calculate delay before next message can be sent.
118
+ * Returns 0 if message can be sent immediately.
119
+ * Returns -1 if message should be blocked entirely.
120
+ */
121
+ async getDelay(recipient, content) {
121
122
  const now = Date.now()
122
123
  this.cleanup(now)
123
- const contentHash = this.hashContent(dedupKey !== undefined ? dedupKey : content)
124
+ const contentHash = this.hashContent(content)
124
125
  const dayMessages = this.messages.filter(m => now - m.timestamp < TIME_CONSTANTS.MS_PER_DAY)
125
126
  if (dayMessages.length >= this.config.maxPerDay) {
126
127
  return -1
@@ -166,14 +167,16 @@ var RateLimiter = class {
166
167
  if (timeSinceLast < this.config.minDelayMs) {
167
168
  delay = Math.max(delay, this.config.minDelayMs - timeSinceLast)
168
169
  }
169
- const typingDelay = Math.min((content?.length || 0) * 15, 2e3)
170
+ const typingDelay = Math.min(content.length * 30, 3e3)
170
171
  delay += this.jitter(typingDelay * 0.5, typingDelay)
171
172
  return Math.round(delay)
172
173
  }
173
-
174
- record(recipient, content, dedupKey) {
174
+ /**
175
+ * Record a sent message
176
+ */
177
+ record(recipient, content) {
175
178
  const now = Date.now()
176
- const contentHash = this.hashContent(dedupKey !== undefined ? dedupKey : content)
179
+ const contentHash = this.hashContent(content)
177
180
  const timeSinceLast = now - this.lastMessageTime
178
181
  if (timeSinceLast > TIME_CONSTANTS.BURST_RESET_MS) {
179
182
  this.burstCount = 0
@@ -193,26 +196,16 @@ var RateLimiter = class {
193
196
  this.identicalCount.set(contentHash, { count: 1, firstSeen: now, lastSeen: now })
194
197
  }
195
198
  }
196
-
199
+ /**
200
+ * Get current usage stats
201
+ */
197
202
  getStats() {
198
203
  const now = Date.now()
199
- if (this.statsCache && now - this.statsCacheTime < 100) {
200
- return this.statsCache
201
- }
202
204
  this.cleanup(now)
203
- let lastMinute = 0
204
- let lastHour = 0
205
- let lastDay = 0
206
- for (const m of this.messages) {
207
- const age = now - m.timestamp
208
- if (age < TIME_CONSTANTS.MS_PER_MINUTE) lastMinute++
209
- if (age < TIME_CONSTANTS.MS_PER_HOUR) lastHour++
210
- if (age < TIME_CONSTANTS.MS_PER_DAY) lastDay++
211
- }
212
- this.statsCache = {
213
- lastMinute,
214
- lastHour,
215
- lastDay,
205
+ return {
206
+ lastMinute: this.messages.filter(m => now - m.timestamp < TIME_CONSTANTS.MS_PER_MINUTE).length,
207
+ lastHour: this.messages.filter(m => now - m.timestamp < TIME_CONSTANTS.MS_PER_HOUR).length,
208
+ lastDay: this.messages.filter(m => now - m.timestamp < TIME_CONSTANTS.MS_PER_DAY).length,
216
209
  limits: {
217
210
  perMinute: this.config.maxPerMinute,
218
211
  perHour: this.config.maxPerHour,
@@ -220,14 +213,12 @@ var RateLimiter = class {
220
213
  },
221
214
  knownChats: this.knownChats.size
222
215
  }
223
- this.statsCacheTime = now
224
- return this.statsCache
225
216
  }
226
-
217
+ /** Get the set of known chat JIDs (for state persistence) */
227
218
  getKnownChats() {
228
219
  return this.knownChats
229
220
  }
230
-
221
+ /** Restore known chats from persisted state */
231
222
  restoreKnownChats(chats) {
232
223
  for (const jid of chats) {
233
224
  this.knownChats.add(jid)
@@ -241,7 +232,7 @@ var RateLimiter = class {
241
232
  }
242
233
  }
243
234
  }
244
-
235
+ /** Random delay between min and max (gaussian-ish distribution) */
245
236
  jitter(min, max) {
246
237
  const u1 = Math.random()
247
238
  const u2 = Math.random()
@@ -250,7 +241,7 @@ var RateLimiter = class {
250
241
  const clamped = Math.max(0, Math.min(1, normalized))
251
242
  return Math.round(min + clamped * (max - min))
252
243
  }
253
-
244
+ /** Simple hash for content dedup */
254
245
  hashContent(content) {
255
246
  let hash = 0
256
247
  for (let i = 0; i < content.length; i++) {
@@ -262,12 +253,12 @@ var RateLimiter = class {
262
253
  }
263
254
  }
264
255
 
265
-
256
+ // warmup.js
266
257
  var DEFAULT_CONFIG2 = {
267
- warmUpDays: 5,
268
- day1Limit: 30,
258
+ warmUpDays: 7,
259
+ day1Limit: 20,
269
260
  growthFactor: 1.8,
270
- inactivityThresholdHours: 168
261
+ inactivityThresholdHours: 72
271
262
  }
272
263
  var WarmUp = class {
273
264
  config
@@ -276,7 +267,9 @@ var WarmUp = class {
276
267
  this.config = { ...DEFAULT_CONFIG2, ...config }
277
268
  this.state = existingState || this.freshState()
278
269
  }
279
-
270
+ /**
271
+ * Get the current daily message limit based on warm-up phase
272
+ */
280
273
  getDailyLimit() {
281
274
  if (this.state.graduated) return Infinity
282
275
  const day = this.getCurrentDay()
@@ -286,7 +279,9 @@ var WarmUp = class {
286
279
  }
287
280
  return Math.round(this.config.day1Limit * Math.pow(this.config.growthFactor, day))
288
281
  }
289
-
282
+ /**
283
+ * Check if a message can be sent (within warm-up limits)
284
+ */
290
285
  canSend() {
291
286
  this.checkInactivity()
292
287
  if (this.state.graduated) return true
@@ -294,7 +289,9 @@ var WarmUp = class {
294
289
  const todayCount = this.state.dailyCounts[day] || 0
295
290
  return todayCount < this.getDailyLimit()
296
291
  }
297
-
292
+ /**
293
+ * Record a sent message
294
+ */
298
295
  record() {
299
296
  const now = Date.now()
300
297
  const day = this.getCurrentDay()
@@ -304,7 +301,9 @@ var WarmUp = class {
304
301
  this.state.dailyCounts[day]++
305
302
  this.state.lastActiveAt = now
306
303
  }
307
-
304
+ /**
305
+ * Get current warm-up status
306
+ */
308
307
  getStatus() {
309
308
  const day = this.getCurrentDay()
310
309
  const todaySent = this.state.dailyCounts[day] || 0
@@ -318,11 +317,15 @@ var WarmUp = class {
318
317
  progress: this.state.graduated ? 100 : Math.round((day / this.config.warmUpDays) * 100)
319
318
  }
320
319
  }
321
-
320
+ /**
321
+ * Export state for persistence
322
+ */
322
323
  exportState() {
323
324
  return { ...this.state }
324
325
  }
325
-
326
+ /**
327
+ * Reset warm-up (e.g., after detected ban risk)
328
+ */
326
329
  reset() {
327
330
  this.state = this.freshState()
328
331
  }
@@ -347,7 +350,7 @@ var WarmUp = class {
347
350
  }
348
351
  }
349
352
 
350
-
353
+ // health.js
351
354
  var DEFAULT_CONFIG3 = {
352
355
  disconnectWarningThreshold: 3,
353
356
  disconnectCriticalThreshold: 5,
@@ -365,7 +368,9 @@ var HealthMonitor = class {
365
368
  constructor(config = {}) {
366
369
  this.config = { ...DEFAULT_CONFIG3, ...config }
367
370
  }
368
-
371
+ /**
372
+ * Record a disconnection event
373
+ */
369
374
  recordDisconnect(reason) {
370
375
  const reasonStr = String(reason)
371
376
  if (reasonStr === '403' || reasonStr === 'forbidden') {
@@ -387,25 +392,33 @@ var HealthMonitor = class {
387
392
  }
388
393
  this.checkAndNotify()
389
394
  }
390
-
395
+ /**
396
+ * Record a successful reconnection
397
+ */
391
398
  recordReconnect() {
392
399
  this.events.push({ type: 'reconnect', timestamp: Date.now() })
393
400
  }
394
-
401
+ /**
402
+ * Record a failed message send
403
+ */
395
404
  recordMessageFailed(error) {
396
405
  this.events.push({ type: 'messageFailed', timestamp: Date.now(), detail: error })
397
406
  this.lastBadEventTime = Date.now()
398
407
  this.lastEventWasSevere = false
399
408
  this.checkAndNotify()
400
409
  }
401
-
410
+ /**
411
+ * Record a 463 reachout timelock error
412
+ */
402
413
  recordReachoutTimelock(detail) {
403
414
  this.events.push({ type: 'reachoutTimelocked', timestamp: Date.now(), detail })
404
415
  this.lastBadEventTime = Date.now()
405
416
  this.lastEventWasSevere = false
406
417
  this.checkAndNotify()
407
418
  }
408
-
419
+ /**
420
+ * Get current health status
421
+ */
409
422
  getStatus() {
410
423
  const now = Date.now()
411
424
  this.cleanup(now)
@@ -481,18 +494,24 @@ var HealthMonitor = class {
481
494
  }
482
495
  }
483
496
  }
484
-
497
+ /**
498
+ * Check if sending should be paused
499
+ */
485
500
  isPaused() {
486
501
  if (this.paused) return true
487
502
  const status = this.getStatus()
488
503
  const riskOrder = ['low', 'medium', 'high', 'critical']
489
504
  return riskOrder.indexOf(status.risk) >= riskOrder.indexOf(this.config.autoPauseAt)
490
505
  }
491
-
506
+ /**
507
+ * Manually pause/resume
508
+ */
492
509
  setPaused(paused) {
493
510
  this.paused = paused
494
511
  }
495
-
512
+ /**
513
+ * Reset all tracked events
514
+ */
496
515
  reset() {
497
516
  this.events = []
498
517
  this.startTime = Date.now()
@@ -513,7 +532,7 @@ var HealthMonitor = class {
513
532
  }
514
533
  }
515
534
 
516
-
535
+ // timelockGuard.js
517
536
  var DEFAULT_CONFIG4 = {
518
537
  resumeBufferMs: 1e4
519
538
  }
@@ -523,21 +542,23 @@ var TimelockGuard = class {
523
542
  isActive: false,
524
543
  errorCount: 0
525
544
  }
526
- knownChats = new Set()
545
+ knownChats = /* @__PURE__ */ new Set()
527
546
  resumeTimer = null
528
547
  timerGeneration = 0
529
-
548
+ // BUG FIX 4: Track timer validity to prevent race conditions
530
549
  constructor(config = {}) {
531
550
  this.config = { ...DEFAULT_CONFIG4, ...config }
532
551
  }
533
-
552
+ /**
553
+ * Update timelock state from Baileys connection.update event
554
+ */
534
555
  onTimelockUpdate(data) {
535
556
  const wasActive = this.state.isActive
536
557
  this.state.isActive = !!data.isActive
537
558
  this.state.enforcementType = data.enforcementType
538
559
  this.state.expiresAt = data.timeEnforcementEnds
539
560
  if (this.state.isActive && !wasActive) {
540
- this.state.detectedAt = new Date()
561
+ this.state.detectedAt = /* @__PURE__ */ new Date()
541
562
  this.state.errorCount = 0
542
563
  this.config.onTimelockDetected?.(this.getState())
543
564
  this.scheduleResume()
@@ -549,28 +570,36 @@ var TimelockGuard = class {
549
570
  this.config.onTimelockLifted?.(this.getState())
550
571
  }
551
572
  }
552
-
573
+ /**
574
+ * Record a 463 error from a failed send
575
+ */
553
576
  record463Error() {
554
577
  this.state.errorCount++
555
578
  if (!this.state.isActive) {
556
579
  this.state.isActive = true
557
- this.state.detectedAt = new Date()
580
+ this.state.detectedAt = /* @__PURE__ */ new Date()
558
581
  this.state.expiresAt = new Date(Date.now() + 6e4)
559
582
  this.config.onTimelockDetected?.(this.getState())
560
583
  this.scheduleResume()
561
584
  }
562
585
  }
563
-
586
+ /**
587
+ * Register a JID as a known/existing chat (has tctoken / prior history)
588
+ */
564
589
  registerKnownChat(jid) {
565
590
  this.knownChats.add(jid)
566
591
  }
567
-
592
+ /**
593
+ * Register multiple known chats at once (e.g. from chat list on connect)
594
+ */
568
595
  registerKnownChats(jids) {
569
596
  for (const jid of jids) {
570
597
  this.knownChats.add(jid)
571
598
  }
572
599
  }
573
-
600
+ /**
601
+ * Check if a message to this recipient should be allowed
602
+ */
574
603
  canSend(jid) {
575
604
  if (!this.state.isActive) {
576
605
  return { allowed: true }
@@ -594,11 +623,15 @@ var TimelockGuard = class {
594
623
  reason: `Reachout timelocked (${this.state.enforcementType || 'unknown'}). New contacts blocked. Expires in ${Math.ceil(expiresIn / 1e3)}s.`
595
624
  }
596
625
  }
597
-
626
+ /**
627
+ * Get current timelock state
628
+ */
598
629
  getState() {
599
630
  return { ...this.state }
600
631
  }
601
-
632
+ /**
633
+ * Check if currently timelocked
634
+ */
602
635
  isTimelocked() {
603
636
  if (!this.state.isActive) return false
604
637
  if (this.state.expiresAt) {
@@ -610,11 +643,15 @@ var TimelockGuard = class {
610
643
  }
611
644
  return true
612
645
  }
613
-
646
+ /**
647
+ * Get the set of known chat JIDs
648
+ */
614
649
  getKnownChats() {
615
650
  return new Set(this.knownChats)
616
651
  }
617
-
652
+ /**
653
+ * Manually lift the timelock
654
+ */
618
655
  lift() {
619
656
  if (this.state.isActive) {
620
657
  this.state.isActive = false
@@ -622,7 +659,9 @@ var TimelockGuard = class {
622
659
  this.config.onTimelockLifted?.(this.getState())
623
660
  }
624
661
  }
625
-
662
+ /**
663
+ * Reset all state
664
+ */
626
665
  reset() {
627
666
  this.state = { isActive: false, errorCount: 0 }
628
667
  this.knownChats.clear()
@@ -652,7 +691,7 @@ var TimelockGuard = class {
652
691
  }
653
692
  }
654
693
 
655
-
694
+ // replyRatio.js
656
695
  var DEFAULT_CONFIG5 = {
657
696
  enabled: false,
658
697
  minRatio: 0.1,
@@ -664,11 +703,14 @@ var DEFAULT_CONFIG5 = {
664
703
  }
665
704
  var ReplyRatioGuard = class {
666
705
  config
667
- contacts = new Map()
706
+ contacts = /* @__PURE__ */ new Map()
668
707
  constructor(config = {}) {
669
708
  this.config = { ...DEFAULT_CONFIG5, ...config }
670
709
  }
671
-
710
+ /**
711
+ * Check if message can be sent to this contact based on reply ratio.
712
+ * Call before sending.
713
+ */
672
714
  beforeSend(jid) {
673
715
  if (!this.config.enabled) {
674
716
  return { allowed: true }
@@ -699,14 +741,18 @@ var ReplyRatioGuard = class {
699
741
  }
700
742
  return { allowed: true }
701
743
  }
702
-
744
+ /**
745
+ * Record an outbound message sent to this contact.
746
+ */
703
747
  recordSent(jid) {
704
748
  if (!this.config.enabled) return
705
749
  const record = this.contacts.get(jid) || { sent: 0, received: 0 }
706
750
  record.sent++
707
751
  this.contacts.set(jid, record)
708
752
  }
709
-
753
+ /**
754
+ * Record an inbound message received from this contact.
755
+ */
710
756
  recordReceived(jid) {
711
757
  if (!this.config.enabled) return
712
758
  const record = this.contacts.get(jid) || { sent: 0, received: 0 }
@@ -714,7 +760,11 @@ var ReplyRatioGuard = class {
714
760
  delete record.cooledUntil
715
761
  this.contacts.set(jid, record)
716
762
  }
717
-
763
+ /**
764
+ * Suggest whether to send an auto-reply to this incoming message.
765
+ * Returns { shouldReply: true, suggestedText: '👍' } if probability check passes.
766
+ * Caller is responsible for actually sending the message.
767
+ */
718
768
  suggestReply(jid, _msgText) {
719
769
  if (!this.config.enabled) {
720
770
  return { shouldReply: false }
@@ -729,7 +779,9 @@ var ReplyRatioGuard = class {
729
779
  }
730
780
  return { shouldReply: false }
731
781
  }
732
-
782
+ /**
783
+ * Get statistics for all contacts and global metrics.
784
+ */
733
785
  getStats() {
734
786
  const perContact = Array.from(this.contacts.entries()).map(([jid, record]) => ({
735
787
  jid,
@@ -750,49 +802,60 @@ var ReplyRatioGuard = class {
750
802
  contactsOnCooldown
751
803
  }
752
804
  }
753
-
805
+ /**
806
+ * Reset all counters.
807
+ */
754
808
  reset() {
755
809
  this.contacts.clear()
756
810
  }
757
-
811
+ /**
812
+ * Export state for persistence.
813
+ */
758
814
  exportState() {
759
815
  return {
760
816
  contacts: Array.from(this.contacts.entries())
761
817
  }
762
818
  }
763
-
819
+ /**
820
+ * Restore state from persistence.
821
+ */
764
822
  restoreState(state) {
765
823
  if (state?.contacts && Array.isArray(state.contacts)) {
766
824
  this.contacts = new Map(state.contacts)
767
825
  }
768
826
  }
769
-
827
+ /**
828
+ * Check if JID is a group.
829
+ */
770
830
  isGroup(jid) {
771
831
  return jid.endsWith('@g.us')
772
832
  }
773
833
  }
774
834
 
775
-
835
+ // contactGraph.js
776
836
  var DEFAULT_CONFIG6 = {
777
837
  enabled: false,
778
838
  requireHandshakeBeforeGroupSend: true,
779
839
  handshakeMinDelayMs: 36e5,
780
-
840
+ // 1 hour
781
841
  groupLurkPeriodMs: 432e5,
782
-
842
+ // 12 hours
783
843
  maxStrangerMessagesPerDay: 5,
784
844
  autoRegisterOnIncoming: true
785
845
  }
786
846
  var ContactGraphWarmer = class {
787
847
  config
788
- contacts = new Map()
789
- groups = new Map()
848
+ contacts = /* @__PURE__ */ new Map()
849
+ groups = /* @__PURE__ */ new Map()
790
850
  strangerMessagesToday = 0
791
851
  lastStrangerResetDay = this.getCurrentDay()
792
852
  constructor(config = {}) {
793
853
  this.config = { ...DEFAULT_CONFIG6, ...config }
794
854
  }
795
-
855
+ /**
856
+ * Check if message can be sent to this contact/group.
857
+ * Returns { allowed: false, needsHandshake: true } if handshake required.
858
+ */
796
859
  canMessage(jid) {
797
860
  if (!this.config.enabled) {
798
861
  return { allowed: true }
@@ -807,7 +870,9 @@ var ContactGraphWarmer = class {
807
870
  }
808
871
  return this.checkIndividualMessage(jid)
809
872
  }
810
-
873
+ /**
874
+ * Mark handshake as sent to this contact.
875
+ */
811
876
  markHandshakeSent(jid) {
812
877
  if (!this.config.enabled) return
813
878
  if (this.isGroup(jid)) return
@@ -816,7 +881,9 @@ var ContactGraphWarmer = class {
816
881
  record.handshakeSentAt = Date.now()
817
882
  this.contacts.set(jid, record)
818
883
  }
819
-
884
+ /**
885
+ * Mark handshake as complete with this contact.
886
+ */
820
887
  markHandshakeComplete(jid) {
821
888
  if (!this.config.enabled) return
822
889
  if (this.isGroup(jid)) return
@@ -824,7 +891,9 @@ var ContactGraphWarmer = class {
824
891
  record.state = 'handshake_complete'
825
892
  this.contacts.set(jid, record)
826
893
  }
827
-
894
+ /**
895
+ * Register a contact as known (skip handshake requirement).
896
+ */
828
897
  registerKnownContact(jid) {
829
898
  if (!this.config.enabled) return
830
899
  if (this.isGroup(jid)) return
@@ -832,18 +901,24 @@ var ContactGraphWarmer = class {
832
901
  record.state = 'known'
833
902
  this.contacts.set(jid, record)
834
903
  }
835
-
904
+ /**
905
+ * Register a group join event.
906
+ */
836
907
  registerGroupJoin(groupJid) {
837
908
  if (!this.config.enabled) return
838
909
  if (!this.isGroup(groupJid)) return
839
910
  this.groups.set(groupJid, { joinedAt: Date.now() })
840
911
  }
841
-
912
+ /**
913
+ * Get contact state.
914
+ */
842
915
  getContactState(jid) {
843
916
  if (this.isGroup(jid)) return 'known'
844
917
  return this.contacts.get(jid)?.state || 'stranger'
845
918
  }
846
-
919
+ /**
920
+ * Handle incoming message — auto-register if enabled.
921
+ */
847
922
  onIncomingMessage(jid) {
848
923
  if (!this.config.enabled) return
849
924
  if (this.isGroup(jid)) return
@@ -851,7 +926,9 @@ var ContactGraphWarmer = class {
851
926
  this.registerKnownContact(jid)
852
927
  }
853
928
  }
854
-
929
+ /**
930
+ * Get statistics.
931
+ */
855
932
  getStats() {
856
933
  const knownContacts = Array.from(this.contacts.values()).filter(c => c.state === 'known').length
857
934
  const pendingHandshakes = Array.from(this.contacts.values()).filter(c => c.state === 'handshake_sent').length
@@ -867,14 +944,18 @@ var ContactGraphWarmer = class {
867
944
  groupsJoined
868
945
  }
869
946
  }
870
-
947
+ /**
948
+ * Reset all state.
949
+ */
871
950
  reset() {
872
951
  this.contacts.clear()
873
952
  this.groups.clear()
874
953
  this.strangerMessagesToday = 0
875
954
  this.lastStrangerResetDay = this.getCurrentDay()
876
955
  }
877
-
956
+ /**
957
+ * Export state for persistence.
958
+ */
878
959
  exportState() {
879
960
  return {
880
961
  contacts: Array.from(this.contacts.entries()),
@@ -883,7 +964,9 @@ var ContactGraphWarmer = class {
883
964
  lastStrangerResetDay: this.lastStrangerResetDay
884
965
  }
885
966
  }
886
-
967
+ /**
968
+ * Restore state from persistence.
969
+ */
887
970
  restoreState(state) {
888
971
  if (state?.contacts && Array.isArray(state.contacts)) {
889
972
  this.contacts = new Map(state.contacts)
@@ -898,7 +981,7 @@ var ContactGraphWarmer = class {
898
981
  this.lastStrangerResetDay = state.lastStrangerResetDay
899
982
  }
900
983
  }
901
-
984
+ // Private helpers
902
985
  isGroup(jid) {
903
986
  return jid.endsWith('@g.us')
904
987
  }
@@ -952,7 +1035,7 @@ var ContactGraphWarmer = class {
952
1035
  }
953
1036
  }
954
1037
 
955
-
1038
+ // presenceChoreographer.js
956
1039
  var DEFAULT_CONFIG7 = {
957
1040
  enabled: false,
958
1041
  enableCircadianRhythm: true,
@@ -985,60 +1068,60 @@ var DEFAULT_CONFIG7 = {
985
1068
  var ACTIVITY_CURVES = {
986
1069
  office: [
987
1070
  0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1,
988
-
1071
+ // 0-7: night quiet
989
1072
  0.5, 0.5,
990
-
1073
+ // 8-9: morning ramp
991
1074
  0.95, 0.95,
992
-
1075
+ // 10-11: morning peak
993
1076
  0.6,
994
-
1077
+ // 12: lunch dip
995
1078
  0.9, 0.9, 0.9, 0.9,
996
-
1079
+ // 13-16: afternoon
997
1080
  0.6, 0.6,
998
-
1081
+ // 17-18: wind-down
999
1082
  0.4, 0.4,
1000
-
1083
+ // 19-20: evening
1001
1084
  0.2, 0.2, 0.2, 0.2
1002
-
1085
+ // 21-24: taper
1003
1086
  ],
1004
1087
  social: [
1005
1088
  0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1,
1006
-
1089
+ // 0-7: night quiet
1007
1090
  0.3, 0.4,
1008
-
1091
+ // 8-9: slow start
1009
1092
  0.7, 0.8,
1010
-
1093
+ // 10-11: ramp up
1011
1094
  0.5,
1012
-
1095
+ // 12: lunch
1013
1096
  0.7, 0.7,
1014
-
1097
+ // 13-14: afternoon
1015
1098
  0.4,
1016
-
1099
+ // 15: tea time dip
1017
1100
  0.8, 0.9, 0.9,
1018
-
1101
+ // 16-18: active
1019
1102
  0.6,
1020
-
1103
+ // 19: dinner dip
1021
1104
  0.8, 0.85, 0.9, 0.95, 1
1022
-
1105
+ // 20-24: evening peak
1023
1106
  ],
1024
1107
  global: [
1025
1108
  0.5, 0.5, 0.5, 0.5, 0.5, 0.5,
1026
-
1109
+ // 0-5: night
1027
1110
  0.4, 0.4,
1028
-
1111
+ // 6-7: dawn dip
1029
1112
  0.6, 0.7, 0.8, 0.8,
1030
-
1113
+ // 8-11: morning
1031
1114
  0.6,
1032
-
1115
+ // 12: lunch
1033
1116
  0.8, 0.8, 0.8, 0.8,
1034
-
1117
+ // 13-16: afternoon
1035
1118
  0.7, 0.7,
1036
-
1119
+ // 17-18: evening
1037
1120
  0.6, 0.5, 0.5, 0.5, 0.5, 0.5
1038
-
1121
+ // 19-24: night taper
1039
1122
  ]
1040
1123
  }
1041
- function getCircadianMultiplier(date = new Date(), profile = 'default', timezone) {
1124
+ function getCircadianMultiplier(date = /* @__PURE__ */ new Date(), profile = 'default', timezone) {
1042
1125
  if (profile === 'always_on') {
1043
1126
  return 1
1044
1127
  }
@@ -1103,7 +1186,11 @@ var PresenceChoreographer = class {
1103
1186
  }
1104
1187
  }
1105
1188
  }
1106
-
1189
+ /**
1190
+ * Get current activity factor (0.1 to 1.0).
1191
+ * Higher = more active = shorter delays.
1192
+ * If circadian disabled, returns 1.0.
1193
+ */
1107
1194
  getCurrentActivityFactor() {
1108
1195
  if (!this.config.enabled || !this.config.enableCircadianRhythm) {
1109
1196
  return 1
@@ -1112,7 +1199,10 @@ var PresenceChoreographer = class {
1112
1199
  const curve = ACTIVITY_CURVES[this.config.activityCurve]
1113
1200
  return curve[hour] || 0.5
1114
1201
  }
1115
-
1202
+ /**
1203
+ * Check if should pause for distraction.
1204
+ * Returns { pause: true, durationMs: 600000 } if probability check passes.
1205
+ */
1116
1206
  shouldPauseForDistraction() {
1117
1207
  if (!this.config.enabled) {
1118
1208
  return { pause: false, durationMs: 0 }
@@ -1124,7 +1214,10 @@ var PresenceChoreographer = class {
1124
1214
  }
1125
1215
  return { pause: false, durationMs: 0 }
1126
1216
  }
1127
-
1217
+ /**
1218
+ * Check if should take offline gap.
1219
+ * Returns { offline: true, durationMs: 600000 } if probability check passes.
1220
+ */
1128
1221
  shouldTakeOfflineGap() {
1129
1222
  if (!this.config.enabled) {
1130
1223
  return { offline: false, durationMs: 0 }
@@ -1136,7 +1229,12 @@ var PresenceChoreographer = class {
1136
1229
  }
1137
1230
  return { offline: false, durationMs: 0 }
1138
1231
  }
1139
-
1232
+ /**
1233
+ * Check if should mark message as read.
1234
+ * Returns { mark: false } if skip probability hit.
1235
+ * Returns { mark: true, delayMs: 5000 } otherwise.
1236
+ * Applies circadian multiplier to delay.
1237
+ */
1140
1238
  shouldMarkRead() {
1141
1239
  if (!this.config.enabled) {
1142
1240
  return { mark: true, delayMs: 0 }
@@ -1149,7 +1247,7 @@ var PresenceChoreographer = class {
1149
1247
  let delayMs = baseDelayMs
1150
1248
  if (this.config.circadian.enabled) {
1151
1249
  const circadianMultiplier = getCircadianMultiplier(
1152
- new Date(),
1250
+ /* @__PURE__ */ new Date(),
1153
1251
  this.config.circadian.profile,
1154
1252
  this.config.circadian.timezone
1155
1253
  )
@@ -1158,7 +1256,18 @@ var PresenceChoreographer = class {
1158
1256
  this.stats.readReceiptsDelayed++
1159
1257
  return { mark: true, delayMs }
1160
1258
  }
1161
-
1259
+ /**
1260
+ * Compute realistic typing duration for a message of given length.
1261
+ * Includes Gaussian WPM variance + think-pause injection + circadian timing multiplier.
1262
+ * Returns a "typing plan": array of { state, durationMs } steps the caller should execute sequentially.
1263
+ *
1264
+ * plan = [
1265
+ * { state: 'composing', durationMs: 4200 },
1266
+ * { state: 'paused', durationMs: 950 }, // think pause
1267
+ * { state: 'composing', durationMs: 6800 },
1268
+ * { state: 'paused', durationMs: 600 }, // brief stop before send
1269
+ * ]
1270
+ */
1162
1271
  computeTypingPlan(messageLength) {
1163
1272
  if (!this.config.enabled || !this.config.enableTypingModel) {
1164
1273
  return [{ state: 'composing', durationMs: this.config.typingMinMs }]
@@ -1173,7 +1282,7 @@ var PresenceChoreographer = class {
1173
1282
  let circadianMultiplier = 1
1174
1283
  if (this.config.circadian.enabled) {
1175
1284
  circadianMultiplier = getCircadianMultiplier(
1176
- new Date(),
1285
+ /* @__PURE__ */ new Date(),
1177
1286
  this.config.circadian.profile,
1178
1287
  this.config.circadian.timezone
1179
1288
  )
@@ -1216,7 +1325,13 @@ var PresenceChoreographer = class {
1216
1325
  }
1217
1326
  return plan
1218
1327
  }
1219
-
1328
+ /**
1329
+ * Execute a typing plan against a Baileys-shaped sock with sendPresenceUpdate(state, jid).
1330
+ * Awaits each step's duration. Updates stats.
1331
+ *
1332
+ * await choreo.executeTypingPlan(sock, jid, plan);
1333
+ * await sock.sendMessage(jid, content);
1334
+ */
1220
1335
  async executeTypingPlan(sock, jid, plan, options) {
1221
1336
  this.stats.typingPlansExecuted++
1222
1337
  for (const step of plan) {
@@ -1229,7 +1344,9 @@ var PresenceChoreographer = class {
1229
1344
  this.stats.totalTypingTimeMs += step.durationMs
1230
1345
  }
1231
1346
  }
1232
-
1347
+ /**
1348
+ * Get statistics.
1349
+ */
1233
1350
  getStats() {
1234
1351
  return {
1235
1352
  currentActivityFactor: this.getCurrentActivityFactor(),
@@ -1243,7 +1360,9 @@ var PresenceChoreographer = class {
1243
1360
  totalTypingTimeMs: this.stats.totalTypingTimeMs
1244
1361
  }
1245
1362
  }
1246
-
1363
+ /**
1364
+ * Reset statistics.
1365
+ */
1247
1366
  reset() {
1248
1367
  this.stats = {
1249
1368
  distractionPausesInjected: 0,
@@ -1255,7 +1374,7 @@ var PresenceChoreographer = class {
1255
1374
  totalTypingTimeMs: 0
1256
1375
  }
1257
1376
  }
1258
-
1377
+ // Private helpers
1259
1378
  getLocalHour() {
1260
1379
  try {
1261
1380
  const formatter = new Intl.DateTimeFormat('en-US', {
@@ -1263,13 +1382,13 @@ var PresenceChoreographer = class {
1263
1382
  hour: 'numeric',
1264
1383
  hour12: false
1265
1384
  })
1266
- const parts = formatter.formatToParts( new Date())
1385
+ const parts = formatter.formatToParts(/* @__PURE__ */ new Date())
1267
1386
  const hourPart = parts.find(p => p.type === 'hour')
1268
1387
  if (hourPart) {
1269
1388
  return parseInt(hourPart.value, 10)
1270
1389
  }
1271
1390
  } catch (error) {}
1272
- return new Date().getUTCHours()
1391
+ return /* @__PURE__ */ new Date().getUTCHours()
1273
1392
  }
1274
1393
  randomBetween(min, max) {
1275
1394
  return Math.floor(Math.random() * (max - min + 1)) + min
@@ -1277,7 +1396,10 @@ var PresenceChoreographer = class {
1277
1396
  clamp(value, min, max) {
1278
1397
  return Math.max(min, Math.min(max, value))
1279
1398
  }
1280
-
1399
+ /**
1400
+ * Generate Gaussian sample using Box-Muller transform.
1401
+ * Returns a sample from N(mean, stdDev).
1402
+ */
1281
1403
  gaussianSample(mean, stdDev) {
1282
1404
  const u1 = Math.random()
1283
1405
  const u2 = Math.random()
@@ -1289,7 +1411,7 @@ var PresenceChoreographer = class {
1289
1411
  }
1290
1412
  }
1291
1413
 
1292
-
1414
+ // retryTracker.js
1293
1415
  var DEFAULT_CONFIG8 = {
1294
1416
  enabled: false,
1295
1417
  maxRetries: 5,
@@ -1298,7 +1420,7 @@ var DEFAULT_CONFIG8 = {
1298
1420
  }
1299
1421
  var RetryReasonTracker = class {
1300
1422
  config
1301
- retries = new Map()
1423
+ retries = /* @__PURE__ */ new Map()
1302
1424
  totalRetries = 0
1303
1425
  reasonCounts = {
1304
1426
  no_session: 0,
@@ -1316,7 +1438,10 @@ var RetryReasonTracker = class {
1316
1438
  constructor(config) {
1317
1439
  this.config = { ...DEFAULT_CONFIG8, ...config }
1318
1440
  }
1319
-
1441
+ /**
1442
+ * Call when a messages.update event arrives with a status/error.
1443
+ * Classifies and records the retry.
1444
+ */
1320
1445
  onMessageUpdate(update) {
1321
1446
  if (!this.config.enabled) return
1322
1447
  const msgId = update.key?.id
@@ -1325,7 +1450,9 @@ var RetryReasonTracker = class {
1325
1450
  const reason = this.classify(update.error || update)
1326
1451
  this.recordRetry(msgId, reason)
1327
1452
  }
1328
-
1453
+ /**
1454
+ * Classify an arbitrary error object into a RetryReason
1455
+ */
1329
1456
  classify(err) {
1330
1457
  if (!err) return 'unknown'
1331
1458
  const statusCode = err.output?.statusCode || err.statusCode || err.status
@@ -1342,7 +1469,9 @@ var RetryReasonTracker = class {
1342
1469
  if (errorMsg.includes('malformed') || errorMsg.includes('invalid node')) return 'node_malformed'
1343
1470
  return 'unknown'
1344
1471
  }
1345
-
1472
+ /**
1473
+ * Record a retry for a message
1474
+ */
1346
1475
  recordRetry(msgId, reason) {
1347
1476
  const now = Date.now()
1348
1477
  let record = this.retries.get(msgId)
@@ -1366,16 +1495,22 @@ var RetryReasonTracker = class {
1366
1495
  this.config.onSpiral(msgId, reason)
1367
1496
  }
1368
1497
  }
1369
-
1498
+ /**
1499
+ * Should we warn the user this message is spiraling?
1500
+ */
1370
1501
  isSpiraling(msgId) {
1371
1502
  const record = this.retries.get(msgId)
1372
1503
  return record ? record.count >= this.config.spiralThreshold : false
1373
1504
  }
1374
-
1505
+ /**
1506
+ * Reset counters for a specific message (call on successful delivery)
1507
+ */
1375
1508
  clear(msgId) {
1376
1509
  this.retries.delete(msgId)
1377
1510
  }
1378
-
1511
+ /**
1512
+ * Get current stats
1513
+ */
1379
1514
  getStats() {
1380
1515
  return {
1381
1516
  totalRetries: this.totalRetries,
@@ -1384,7 +1519,9 @@ var RetryReasonTracker = class {
1384
1519
  activeRetries: this.retries.size
1385
1520
  }
1386
1521
  }
1387
-
1522
+ /**
1523
+ * Clean up old retry records (>5 minutes old)
1524
+ */
1388
1525
  cleanup() {
1389
1526
  const now = Date.now()
1390
1527
  const maxAge = 5 * 60 * 1e3
@@ -1394,14 +1531,16 @@ var RetryReasonTracker = class {
1394
1531
  }
1395
1532
  }
1396
1533
  }
1397
-
1534
+ /**
1535
+ * Destroy and clean up
1536
+ */
1398
1537
  destroy() {
1399
1538
  this.retries.clear()
1400
1539
  this.cleanup()
1401
1540
  }
1402
1541
  }
1403
1542
 
1404
-
1543
+ // reconnectThrottle.js
1405
1544
  var DEFAULT_CONFIG9 = {
1406
1545
  enabled: false,
1407
1546
  rampDurationMs: 6e4,
@@ -1416,11 +1555,11 @@ var PostReconnectThrottle = class {
1416
1555
  lifetimeReconnects = 0
1417
1556
  rampTimer = null
1418
1557
  currentStep = 0
1419
-
1558
+ // Tracking sends in current window
1420
1559
  sendsInCurrentWindow = 0
1421
1560
  currentWindowStart = 0
1422
1561
  WINDOW_DURATION_MS = 6e4
1423
-
1562
+ // 1 minute window
1424
1563
  constructor(config) {
1425
1564
  this.config = {
1426
1565
  ...DEFAULT_CONFIG9,
@@ -1428,7 +1567,9 @@ var PostReconnectThrottle = class {
1428
1567
  baselineRatePerMinute: config?.baselineRatePerMinute || null
1429
1568
  }
1430
1569
  }
1431
-
1570
+ /**
1571
+ * Call when connection is re-established. Starts throttle window.
1572
+ */
1432
1573
  onReconnect() {
1433
1574
  if (!this.config.enabled) return
1434
1575
  this.throttledSince = Date.now()
@@ -1442,9 +1583,13 @@ var PostReconnectThrottle = class {
1442
1583
  }
1443
1584
  this.scheduleNextRampStep()
1444
1585
  }
1445
-
1586
+ /**
1587
+ * Call when connection drops (optional — reset state).
1588
+ */
1446
1589
  onDisconnect() {}
1447
-
1590
+ /**
1591
+ * Schedule the next ramp step
1592
+ */
1448
1593
  scheduleNextRampStep() {
1449
1594
  if (this.currentStep >= this.config.rampSteps) {
1450
1595
  this.throttledSince = null
@@ -1457,7 +1602,9 @@ var PostReconnectThrottle = class {
1457
1602
  this.scheduleNextRampStep()
1458
1603
  }, stepDuration)
1459
1604
  }
1460
-
1605
+ /**
1606
+ * Returns current rate multiplier (1.0 = no throttle)
1607
+ */
1461
1608
  getCurrentMultiplier() {
1462
1609
  if (!this.config.enabled || !this.throttledSince) {
1463
1610
  return 1
@@ -1470,7 +1617,9 @@ var PostReconnectThrottle = class {
1470
1617
  const multiplier = this.config.initialRateMultiplier + (1 - this.config.initialRateMultiplier) * progress
1471
1618
  return Math.min(1, multiplier)
1472
1619
  }
1473
-
1620
+ /**
1621
+ * Checks if a send should be gated. Returns {allowed, reason, retryAfterMs?}
1622
+ */
1474
1623
  beforeSend() {
1475
1624
  if (!this.config.enabled || !this.throttledSince) {
1476
1625
  return { allowed: true }
@@ -1499,7 +1648,9 @@ var PostReconnectThrottle = class {
1499
1648
  this.throttledSendCount++
1500
1649
  return { allowed: true }
1501
1650
  }
1502
-
1651
+ /**
1652
+ * Get current stats
1653
+ */
1503
1654
  getStats() {
1504
1655
  const multiplier = this.getCurrentMultiplier()
1505
1656
  const isThrottled = this.throttledSince !== null && multiplier < 1
@@ -1516,7 +1667,9 @@ var PostReconnectThrottle = class {
1516
1667
  lifetimeReconnects: this.lifetimeReconnects
1517
1668
  }
1518
1669
  }
1519
-
1670
+ /**
1671
+ * Destroy and clean up timers
1672
+ */
1520
1673
  destroy() {
1521
1674
  if (this.rampTimer) {
1522
1675
  clearTimeout(this.rampTimer)
@@ -1526,7 +1679,7 @@ var PostReconnectThrottle = class {
1526
1679
  }
1527
1680
  }
1528
1681
 
1529
-
1682
+ // lidResolver.js
1530
1683
  var DEFAULT_CONFIG10 = {
1531
1684
  canonical: 'pn',
1532
1685
  maxEntries: 1e4
@@ -1534,10 +1687,10 @@ var DEFAULT_CONFIG10 = {
1534
1687
  var LidResolver = class {
1535
1688
  config
1536
1689
  persistence
1537
-
1538
- lidToPn = new Map()
1539
- pnToLid = new Map()
1540
-
1690
+ // Bidirectional maps: lid→pn and pn→lid
1691
+ lidToPn = /* @__PURE__ */ new Map()
1692
+ pnToLid = /* @__PURE__ */ new Map()
1693
+ // pn → lid (for quick reverse lookup)
1541
1694
  stats = {
1542
1695
  learnedFromEvents: 0,
1543
1696
  lookupsServed: 0,
@@ -1550,7 +1703,10 @@ var LidResolver = class {
1550
1703
  void this.hydrate()
1551
1704
  }
1552
1705
  }
1553
-
1706
+ /**
1707
+ * Learn from a message event. Idempotent.
1708
+ * Accepts partial mappings — will use whatever fields are available.
1709
+ */
1554
1710
  learn(mapping) {
1555
1711
  let lid = mapping.lid ? this.normalizeJid(mapping.lid) : void 0
1556
1712
  let pn = mapping.pn ? this.normalizeJid(mapping.pn) : void 0
@@ -1588,7 +1744,10 @@ var LidResolver = class {
1588
1744
  void this.flush()
1589
1745
  }
1590
1746
  }
1591
-
1747
+ /**
1748
+ * Given any form (LID or PN), return the canonical form.
1749
+ * Falls back to input if unknown (no throw).
1750
+ */
1592
1751
  resolveCanonical(jid) {
1593
1752
  const normalized = this.normalizeJid(jid)
1594
1753
  if (this.config.canonical === 'pn') {
@@ -1622,7 +1781,9 @@ var LidResolver = class {
1622
1781
  return normalized
1623
1782
  }
1624
1783
  }
1625
-
1784
+ /**
1785
+ * Lookup partner form. Returns null if unknown.
1786
+ */
1626
1787
  getLid(pn) {
1627
1788
  const normalized = this.normalizeJid(pn)
1628
1789
  const lid = this.pnToLid.get(normalized)
@@ -1643,7 +1804,9 @@ var LidResolver = class {
1643
1804
  }
1644
1805
  return null
1645
1806
  }
1646
-
1807
+ /**
1808
+ * Full mapping for inspection
1809
+ */
1647
1810
  getMapping(jid) {
1648
1811
  const normalized = this.normalizeJid(jid)
1649
1812
  const byLid = this.lidToPn.get(normalized)
@@ -1661,7 +1824,9 @@ var LidResolver = class {
1661
1824
  }
1662
1825
  return null
1663
1826
  }
1664
-
1827
+ /**
1828
+ * Seed from persistence (called automatically in constructor if persistence provided)
1829
+ */
1665
1830
  async hydrate() {
1666
1831
  if (!this.persistence?.load) return
1667
1832
  try {
@@ -1688,7 +1853,9 @@ var LidResolver = class {
1688
1853
  }
1689
1854
  } catch (error) {}
1690
1855
  }
1691
-
1856
+ /**
1857
+ * Flush current map to persistence
1858
+ */
1692
1859
  async flush() {
1693
1860
  if (!this.persistence?.save) return
1694
1861
  try {
@@ -1708,7 +1875,9 @@ var LidResolver = class {
1708
1875
  canonicalForm: this.config.canonical
1709
1876
  }
1710
1877
  }
1711
-
1878
+ /**
1879
+ * Clear everything
1880
+ */
1712
1881
  reset() {
1713
1882
  this.lidToPn.clear()
1714
1883
  this.pnToLid.clear()
@@ -1724,12 +1893,16 @@ var LidResolver = class {
1724
1893
  void this.flush()
1725
1894
  }
1726
1895
  }
1727
-
1728
-
1896
+ // Private helpers
1897
+ /**
1898
+ * Normalize JID: strip device suffix `:N`
1899
+ */
1729
1900
  normalizeJid(jid) {
1730
1901
  return jid.replace(/:\d+@/, '@')
1731
1902
  }
1732
-
1903
+ /**
1904
+ * Evict least recently accessed mapping (LRU)
1905
+ */
1733
1906
  evictLRU() {
1734
1907
  let oldestLid = null
1735
1908
  let oldestTime = Infinity
@@ -1749,7 +1922,7 @@ var LidResolver = class {
1749
1922
  }
1750
1923
  }
1751
1924
 
1752
-
1925
+ // jidCanonicalizer.js
1753
1926
  var DEFAULT_CONFIG11 = {
1754
1927
  enabled: false,
1755
1928
  canonicalizeOutbound: true,
@@ -1759,7 +1932,7 @@ var JidCanonicalizer = class {
1759
1932
  config
1760
1933
  lidResolver
1761
1934
  ownsResolver
1762
-
1935
+ // Track if we created the resolver (for destroy)
1763
1936
  stats = {
1764
1937
  outboundCanonicalized: 0,
1765
1938
  outboundPassthrough: 0,
@@ -1777,11 +1950,15 @@ var JidCanonicalizer = class {
1777
1950
  this.ownsResolver = true
1778
1951
  }
1779
1952
  }
1780
-
1953
+ /**
1954
+ * Access the underlying resolver (for cross-module sharing)
1955
+ */
1781
1956
  get resolver() {
1782
1957
  return this.lidResolver
1783
1958
  }
1784
-
1959
+ /**
1960
+ * Called by wrapper on every outbound send. Returns canonical JID.
1961
+ */
1785
1962
  canonicalizeTarget(jid) {
1786
1963
  if (!this.config.enabled || !this.config.canonicalizeOutbound) {
1787
1964
  return jid
@@ -1794,7 +1971,22 @@ var JidCanonicalizer = class {
1794
1971
  }
1795
1972
  return canonical
1796
1973
  }
1797
-
1974
+ /**
1975
+ * Returns a stable, canonical thread key for storage / DB indexing.
1976
+ *
1977
+ * Different from `canonicalizeTarget()` (which picks the right send target):
1978
+ * - canonicalizeTarget('1234@lid') → '+27...@s.whatsapp.net' (best send target)
1979
+ * - canonicalKey('1234@lid') → 'thread:27...' (stable thread identifier)
1980
+ *
1981
+ * If LID has known PN mapping → use phone-number form
1982
+ * If only LID known → use LID stripped of suffix
1983
+ * Always lowercase, no @-suffix, prefixed with `thread:`
1984
+ *
1985
+ * Apps using this as their DB key won't double-thread on LID/PN drift.
1986
+ *
1987
+ * @param jid - WhatsApp JID (can be PN, LID, group, or broadcast)
1988
+ * @returns Stable thread key for DB indexing
1989
+ */
1798
1990
  canonicalKey(jid) {
1799
1991
  if (!jid || typeof jid !== 'string' || jid.trim() === '') {
1800
1992
  return 'thread:invalid'
@@ -1832,7 +2024,9 @@ var JidCanonicalizer = class {
1832
2024
  }
1833
2025
  return `thread:${domain}:${user}`
1834
2026
  }
1835
-
2027
+ /**
2028
+ * Called by wrapper on messages.upsert event. Learns mappings.
2029
+ */
1836
2030
  onIncomingEvent(upsert) {
1837
2031
  if (!this.config.enabled || !this.config.learnFromEvents) {
1838
2032
  return
@@ -1841,7 +2035,9 @@ var JidCanonicalizer = class {
1841
2035
  this.learnFromMessage(msg)
1842
2036
  }
1843
2037
  }
1844
-
2038
+ /**
2039
+ * Called by wrapper on messages.update event. Learns from sent-message refs.
2040
+ */
1845
2041
  onMessageUpdate(updates) {
1846
2042
  if (!this.config.enabled || !this.config.learnFromEvents) {
1847
2043
  return
@@ -1867,8 +2063,10 @@ var JidCanonicalizer = class {
1867
2063
  this.lidResolver.destroy()
1868
2064
  }
1869
2065
  }
1870
-
1871
-
2066
+ // Private helpers
2067
+ /**
2068
+ * Extract LID↔PN mappings from a message object
2069
+ */
1872
2070
  learnFromMessage(msg) {
1873
2071
  if (!msg.key) return
1874
2072
  this.learnFromMessageKey(msg.key)
@@ -1880,7 +2078,9 @@ var JidCanonicalizer = class {
1880
2078
  this.stats.inboundLearned++
1881
2079
  }
1882
2080
  }
1883
-
2081
+ /**
2082
+ * Extract mappings from message.key
2083
+ */
1884
2084
  learnFromMessageKey(key) {
1885
2085
  if (!key) return
1886
2086
  if (key.participant && key.participantPn) {
@@ -1913,7 +2113,7 @@ var JidCanonicalizer = class {
1913
2113
  }
1914
2114
  }
1915
2115
 
1916
-
2116
+ // sessionStability.js
1917
2117
  function classifyDisconnect(statusCode) {
1918
2118
  if (statusCode === 401 || statusCode === 440) {
1919
2119
  return {
@@ -1952,7 +2152,7 @@ function classifyDisconnect(statusCode) {
1952
2152
  category: 'recoverable',
1953
2153
  shouldReconnect: true,
1954
2154
  backoffMs: 3e4,
1955
-
2155
+ // 30 seconds
1956
2156
  message: 'Precondition failed \u2014 auth state mismatch, retry after delay',
1957
2157
  code: statusCode
1958
2158
  }
@@ -1962,7 +2162,7 @@ function classifyDisconnect(statusCode) {
1962
2162
  category: 'rate-limited',
1963
2163
  shouldReconnect: true,
1964
2164
  backoffMs: 3e5,
1965
-
2165
+ // 5 minutes
1966
2166
  message: 'Rate limited by WhatsApp \u2014 cool-off period required',
1967
2167
  code: statusCode
1968
2168
  }
@@ -1972,7 +2172,7 @@ function classifyDisconnect(statusCode) {
1972
2172
  category: 'rate-limited',
1973
2173
  shouldReconnect: true,
1974
2174
  backoffMs: 6e4,
1975
-
2175
+ // 1 minute
1976
2176
  message: 'WhatsApp service unavailable \u2014 temporary outage',
1977
2177
  code: statusCode
1978
2178
  }
@@ -1982,7 +2182,7 @@ function classifyDisconnect(statusCode) {
1982
2182
  category: 'recoverable',
1983
2183
  shouldReconnect: true,
1984
2184
  backoffMs: 5e3,
1985
-
2185
+ // 5 seconds
1986
2186
  message: 'Connection timeout \u2014 network issue, safe to retry',
1987
2187
  code: statusCode
1988
2188
  }
@@ -1992,7 +2192,7 @@ function classifyDisconnect(statusCode) {
1992
2192
  category: 'recoverable',
1993
2193
  shouldReconnect: true,
1994
2194
  backoffMs: 1e4,
1995
-
2195
+ // 10 seconds
1996
2196
  message: 'WhatsApp internal error \u2014 temporary server issue',
1997
2197
  code: statusCode
1998
2198
  }
@@ -2002,7 +2202,7 @@ function classifyDisconnect(statusCode) {
2002
2202
  category: 'recoverable',
2003
2203
  shouldReconnect: true,
2004
2204
  backoffMs: 2e3,
2005
-
2205
+ // 2 seconds
2006
2206
  message: 'Connection closed gracefully \u2014 safe to reconnect',
2007
2207
  code: statusCode
2008
2208
  }
@@ -2011,7 +2211,7 @@ function classifyDisconnect(statusCode) {
2011
2211
  category: 'unknown',
2012
2212
  shouldReconnect: true,
2013
2213
  backoffMs: 15e3,
2014
-
2214
+ // 15 seconds
2015
2215
  message: `Unknown disconnect reason (code ${statusCode}) \u2014 reconnect with caution`,
2016
2216
  code: statusCode
2017
2217
  }
@@ -2036,12 +2236,16 @@ var SessionHealthMonitor = class {
2036
2236
  this.onDegraded = config.onDegraded
2037
2237
  this.onRecovered = config.onRecovered
2038
2238
  }
2039
-
2239
+ /**
2240
+ * Record successful decrypt
2241
+ */
2040
2242
  recordDecryptSuccess() {
2041
2243
  this.stats.decryptSuccess++
2042
2244
  this.checkRecovery()
2043
2245
  }
2044
-
2246
+ /**
2247
+ * Record failed decrypt (Bad MAC or similar)
2248
+ */
2045
2249
  recordDecryptFail(isBadMac = false) {
2046
2250
  this.stats.decryptFail++
2047
2251
  if (isBadMac) {
@@ -2058,7 +2262,9 @@ var SessionHealthMonitor = class {
2058
2262
  }
2059
2263
  }
2060
2264
  }
2061
-
2265
+ /**
2266
+ * Check if session has recovered from degraded state
2267
+ */
2062
2268
  checkRecovery() {
2063
2269
  if (!this.stats.isDegraded) return
2064
2270
  const now = Date.now()
@@ -2070,11 +2276,15 @@ var SessionHealthMonitor = class {
2070
2276
  this.onRecovered?.(this.getStats())
2071
2277
  }
2072
2278
  }
2073
-
2279
+ /**
2280
+ * Get current health stats
2281
+ */
2074
2282
  getStats() {
2075
2283
  return { ...this.stats }
2076
2284
  }
2077
-
2285
+ /**
2286
+ * Reset all counters
2287
+ */
2078
2288
  reset() {
2079
2289
  this.stats = {
2080
2290
  decryptSuccess: 0,
@@ -2107,51 +2317,51 @@ function wrapWithSessionStability(sock, config = {}) {
2107
2317
  })
2108
2318
  }
2109
2319
 
2110
-
2320
+ // presets.js
2111
2321
  var PRESETS = {
2112
2322
  conservative: {
2113
- maxPerMinute: 6,
2114
- maxPerHour: 150,
2115
- maxPerDay: 1000,
2116
- minDelayMs: 2000,
2117
- maxDelayMs: 6e3,
2118
- newChatDelayMs: 3e3,
2119
- warmupDays: 7,
2120
- day1Limit: 20,
2323
+ maxPerMinute: 5,
2324
+ maxPerHour: 100,
2325
+ maxPerDay: 800,
2326
+ minDelayMs: 2500,
2327
+ maxDelayMs: 7e3,
2328
+ newChatDelayMs: 4e3,
2329
+ warmupDays: 10,
2330
+ day1Limit: 15,
2121
2331
  growthFactor: 1.8,
2122
- inactivityThresholdHours: 120,
2123
- autoPauseAt: 'high',
2332
+ inactivityThresholdHours: 72,
2333
+ autoPauseAt: 'medium',
2124
2334
  groupMultiplier: 0.5,
2125
2335
  groupProfiles: false,
2126
2336
  logging: true
2127
2337
  },
2128
2338
  moderate: {
2129
- maxPerMinute: 15,
2130
- maxPerHour: 500,
2131
- maxPerDay: 3e3,
2132
- minDelayMs: 1e3,
2133
- maxDelayMs: 4e3,
2134
- newChatDelayMs: 2e3,
2135
- warmupDays: 5,
2136
- day1Limit: 30,
2339
+ maxPerMinute: 10,
2340
+ maxPerHour: 300,
2341
+ maxPerDay: 1500,
2342
+ minDelayMs: 1500,
2343
+ maxDelayMs: 5e3,
2344
+ newChatDelayMs: 3e3,
2345
+ warmupDays: 7,
2346
+ day1Limit: 20,
2137
2347
  growthFactor: 1.8,
2138
- inactivityThresholdHours: 168,
2139
- autoPauseAt: 'critical',
2348
+ inactivityThresholdHours: 72,
2349
+ autoPauseAt: 'high',
2140
2350
  groupMultiplier: 0.7,
2141
2351
  groupProfiles: false,
2142
2352
  logging: true
2143
2353
  },
2144
2354
  aggressive: {
2145
- maxPerMinute: 25,
2146
- maxPerHour: 1200,
2147
- maxPerDay: 6e3,
2148
- minDelayMs: 600,
2149
- maxDelayMs: 2500,
2150
- newChatDelayMs: 1500,
2151
- warmupDays: 3,
2152
- day1Limit: 50,
2355
+ maxPerMinute: 20,
2356
+ maxPerHour: 800,
2357
+ maxPerDay: 4e3,
2358
+ minDelayMs: 800,
2359
+ maxDelayMs: 3e3,
2360
+ newChatDelayMs: 2e3,
2361
+ warmupDays: 4,
2362
+ day1Limit: 35,
2153
2363
  growthFactor: 2,
2154
- inactivityThresholdHours: 96,
2364
+ inactivityThresholdHours: 48,
2155
2365
  autoPauseAt: 'critical',
2156
2366
  groupMultiplier: 0.9,
2157
2367
  groupProfiles: false,
@@ -2160,7 +2370,7 @@ var PRESETS = {
2160
2370
  }
2161
2371
  function resolveConfig(input) {
2162
2372
  if (input === void 0) {
2163
- return { ...PRESETS.moderate }
2373
+ return { ...PRESETS.conservative }
2164
2374
  }
2165
2375
  if (typeof input === 'string') {
2166
2376
  if (!(input in PRESETS)) {
@@ -2168,14 +2378,14 @@ function resolveConfig(input) {
2168
2378
  }
2169
2379
  return { ...PRESETS[input] }
2170
2380
  }
2171
- const { preset = 'moderate', ...overrides } = input
2381
+ const { preset = 'conservative', ...overrides } = input
2172
2382
  if (!(preset in PRESETS)) {
2173
2383
  throw new Error(`Unknown preset "${preset}". Valid: ${Object.keys(PRESETS).join(', ')}`)
2174
2384
  }
2175
2385
  return { ...PRESETS[preset], ...overrides }
2176
2386
  }
2177
2387
 
2178
-
2388
+ // persist.js
2179
2389
  var fs = __toESM(require('fs'), 1)
2180
2390
  var KNOWN_CHATS_MAX = 1e3
2181
2391
  var DEBOUNCE_MS = 5e3
@@ -2201,7 +2411,7 @@ var StateManager = class {
2201
2411
  return null
2202
2412
  }
2203
2413
  }
2204
-
2414
+ /** Debounced save — called after every send (5s delay) */
2205
2415
  saveDebounced(state) {
2206
2416
  if (this.debounceTimer) {
2207
2417
  clearTimeout(this.debounceTimer)
@@ -2211,7 +2421,7 @@ var StateManager = class {
2211
2421
  this.debounceTimer = null
2212
2422
  }, DEBOUNCE_MS)
2213
2423
  }
2214
-
2424
+ /** Immediate save — called after health events (ban/restriction) */
2215
2425
  saveImmediate(state) {
2216
2426
  if (this.debounceTimer) {
2217
2427
  clearTimeout(this.debounceTimer)
@@ -2219,7 +2429,7 @@ var StateManager = class {
2219
2429
  }
2220
2430
  this.writeFile(state)
2221
2431
  }
2222
-
2432
+ /** Flush/cancel pending debounced write (for tests and process exit) */
2223
2433
  flush() {
2224
2434
  if (this.debounceTimer) {
2225
2435
  clearTimeout(this.debounceTimer)
@@ -2233,7 +2443,7 @@ var StateManager = class {
2233
2443
  const toSave = {
2234
2444
  ...state,
2235
2445
  savedAt: Date.now(),
2236
-
2446
+ // LRU eviction: keep last KNOWN_CHATS_MAX entries
2237
2447
  knownChats:
2238
2448
  state.knownChats.length > KNOWN_CHATS_MAX ? state.knownChats.slice(-KNOWN_CHATS_MAX) : state.knownChats
2239
2449
  }
@@ -2245,7 +2455,7 @@ var StateManager = class {
2245
2455
  }
2246
2456
  }
2247
2457
 
2248
-
2458
+ // profiles.js
2249
2459
  function isGroup(jid) {
2250
2460
  return jid.endsWith('@g.us')
2251
2461
  }
@@ -2266,23 +2476,26 @@ function applyGroupMultiplier(limits, multiplier) {
2266
2476
  }
2267
2477
  }
2268
2478
 
2269
-
2270
-
2271
-
2272
-
2273
-
2274
-
2275
-
2276
-
2277
- const CORE_LEGACY_KEYS = ['rateLimiter', 'warmUp']
2278
- function hasLegacyCoreNesting(cfg) {
2479
+ // antiban.js
2480
+ function isLegacyConfig(cfg) {
2279
2481
  if (typeof cfg !== 'object' || cfg === null) return false
2280
- return CORE_LEGACY_KEYS.some(key => key in cfg)
2482
+ return (
2483
+ 'rateLimiter' in cfg ||
2484
+ 'warmUp' in cfg ||
2485
+ 'health' in cfg ||
2486
+ 'timelock' in cfg ||
2487
+ 'replyRatio' in cfg ||
2488
+ 'contactGraph' in cfg ||
2489
+ 'presence' in cfg ||
2490
+ 'retryTracker' in cfg ||
2491
+ 'reconnectThrottle' in cfg ||
2492
+ 'lidResolver' in cfg ||
2493
+ 'jidCanonicalizer' in cfg ||
2494
+ 'sessionStability' in cfg
2495
+ )
2281
2496
  }
2282
2497
  function mapLegacyToFlat(legacy) {
2283
- process.stderr.write(
2284
- '[baileys-antiban] DEPRECATED: nested { rateLimiter, warmUp } config detected. Migrate to flat fields: new AntiBan({ maxPerMinute: 8, warmupDays: 5 }). Flat top-level fields you also pass always take precedence.\n'
2285
- )
2498
+ process.stderr.write('[baileys-antiban] DEPRECATED: Nested config (v2 style) detected. Migrate to flat config: new AntiBan({ maxPerMinute: 8 }).\n')
2286
2499
  const flat = {}
2287
2500
  if (legacy.rateLimiter?.maxPerMinute !== void 0) flat.maxPerMinute = legacy.rateLimiter.maxPerMinute
2288
2501
  if (legacy.rateLimiter?.maxPerHour !== void 0) flat.maxPerHour = legacy.rateLimiter.maxPerHour
@@ -2293,6 +2506,7 @@ function mapLegacyToFlat(legacy) {
2293
2506
  if (legacy.warmUp?.warmUpDays !== void 0) flat.warmupDays = legacy.warmUp.warmUpDays
2294
2507
  if (legacy.warmUp?.day1Limit !== void 0) flat.day1Limit = legacy.warmUp.day1Limit
2295
2508
  if (legacy.warmUp?.growthFactor !== void 0) flat.growthFactor = legacy.warmUp.growthFactor
2509
+ if (legacy.logging !== void 0) flat.logging = legacy.logging
2296
2510
  return flat
2297
2511
  }
2298
2512
  var AntiBan = class {
@@ -2317,29 +2531,18 @@ var AntiBan = class {
2317
2531
  totalDelayMs: 0
2318
2532
  }
2319
2533
  constructor(input, warmUpStateArg) {
2534
+ let flatConfig
2535
+ let legacyPassthrough = null
2320
2536
  let warmUpState = warmUpStateArg
2321
-
2322
-
2323
-
2324
-
2325
- const rawInput = typeof input === 'object' && input !== null ? input : {}
2326
- const legacyNested = hasLegacyCoreNesting(rawInput)
2327
-
2328
- const flatMerged = legacyNested ? { ...mapLegacyToFlat(rawInput), ...rawInput } : rawInput
2329
- const cfg = typeof input === 'string' ? resolveConfig(input) : resolveConfig(flatMerged)
2537
+ if (isLegacyConfig(input)) {
2538
+ legacyPassthrough = input
2539
+ flatConfig = mapLegacyToFlat(legacyPassthrough)
2540
+ } else {
2541
+ flatConfig = {}
2542
+ legacyPassthrough = null
2543
+ }
2544
+ const cfg = isLegacyConfig(input) ? resolveConfig(flatConfig) : resolveConfig(input)
2330
2545
  this.resolvedConfig = cfg
2331
- const rateLimiterOverrides = rawInput.rateLimiter || {}
2332
- const warmUpOverrides = rawInput.warmUp || {}
2333
- const healthOverrides = rawInput.health || {}
2334
- const timelockOverrides = rawInput.timelock || {}
2335
- const replyRatioCfg = rawInput.replyRatio
2336
- const contactGraphCfg = rawInput.contactGraph
2337
- const presenceCfg = rawInput.presence
2338
- const retryTrackerCfg = rawInput.retryTracker
2339
- const reconnectThrottleCfg = rawInput.reconnectThrottle
2340
- const lidResolverCfg = rawInput.lidResolver
2341
- const jidCanonicalizerCfg = rawInput.jidCanonicalizer
2342
- const sessionStabilityCfg = rawInput.sessionStability
2343
2546
  let savedState = null
2344
2547
  if (cfg.persist) {
2345
2548
  this.stateManager = new StateManager(cfg.persist)
@@ -2349,9 +2552,7 @@ var AntiBan = class {
2349
2552
  }
2350
2553
  }
2351
2554
  this.logging = cfg.logging ?? true
2352
- this._log = msg => {
2353
- if (this.logging) process.stdout.write(`[baileys-antiban] ${msg}\n`)
2354
- }
2555
+ this._log = (msg) => { if (this.logging) process.stdout.write(`[antiban] ${msg}\n`) }
2355
2556
  this.rateLimiter = new RateLimiter({
2356
2557
  maxPerMinute: cfg.maxPerMinute,
2357
2558
  maxPerHour: cfg.maxPerHour,
@@ -2359,7 +2560,7 @@ var AntiBan = class {
2359
2560
  minDelayMs: cfg.minDelayMs,
2360
2561
  maxDelayMs: cfg.maxDelayMs,
2361
2562
  newChatDelayMs: cfg.newChatDelayMs,
2362
- ...rateLimiterOverrides
2563
+ ...(legacyPassthrough?.rateLimiter || {})
2363
2564
  })
2364
2565
  if (savedState?.knownChats) {
2365
2566
  this.rateLimiter.restoreKnownChats(savedState.knownChats)
@@ -2370,73 +2571,69 @@ var AntiBan = class {
2370
2571
  day1Limit: cfg.day1Limit,
2371
2572
  growthFactor: cfg.growthFactor,
2372
2573
  inactivityThresholdHours: cfg.inactivityThresholdHours,
2373
- ...warmUpOverrides
2574
+ ...(legacyPassthrough?.warmUp || {})
2374
2575
  },
2375
2576
  warmUpState
2376
2577
  )
2377
2578
  this.health = new HealthMonitor({
2378
2579
  autoPauseAt: cfg.autoPauseAt,
2379
- ...healthOverrides,
2580
+ ...(legacyPassthrough?.health || {}),
2380
2581
  onRiskChange: status => {
2381
2582
  const emoji = { low: '\u{1F7E2}', medium: '\u{1F7E1}', high: '\u{1F7E0}', critical: '\u{1F534}' }
2382
2583
  this._log(`${emoji[status.risk]} Risk level: ${status.risk.toUpperCase()} (score: ${status.score})`)
2383
2584
  this._log(status.recommendation)
2384
2585
  status.reasons.forEach(r => this._log(` \u2192 ${r}`))
2385
- healthOverrides.onRiskChange?.(status)
2586
+ legacyPassthrough?.health?.onRiskChange?.(status)
2386
2587
  }
2387
2588
  })
2388
2589
  this.timelockGuard = new TimelockGuard({
2389
- ...timelockOverrides,
2590
+ ...(legacyPassthrough?.timelock || {}),
2390
2591
  onTimelockDetected: state => {
2391
2592
  this.health.recordReachoutTimelock(state.enforcementType)
2392
- this._log(
2393
- `REACHOUT TIMELOCKED \u2014 ${state.enforcementType || 'unknown'}, expires ${state.expiresAt?.toISOString() || 'unknown'}`
2394
- )
2395
- timelockOverrides.onTimelockDetected?.(state)
2593
+ this._log(`REACHOUT TIMELOCKED \u2014 ${state.enforcementType || 'unknown'}, expires ${state.expiresAt?.toISOString() || 'unknown'}`)
2594
+ legacyPassthrough?.timelock?.onTimelockDetected?.(state)
2396
2595
  },
2397
2596
  onTimelockLifted: state => {
2398
2597
  this._log('Timelock lifted \u2014 resuming new contact messages')
2399
- timelockOverrides.onTimelockLifted?.(state)
2598
+ legacyPassthrough?.timelock?.onTimelockLifted?.(state)
2400
2599
  }
2401
2600
  })
2402
- this.replyRatioGuard = new ReplyRatioGuard(replyRatioCfg)
2403
- this.contactGraphWarmer = new ContactGraphWarmer(contactGraphCfg)
2404
- this.presenceChoreographer = new PresenceChoreographer(presenceCfg)
2601
+ this.replyRatioGuard = new ReplyRatioGuard(legacyPassthrough?.replyRatio)
2602
+ this.contactGraphWarmer = new ContactGraphWarmer(legacyPassthrough?.contactGraph)
2603
+ this.presenceChoreographer = new PresenceChoreographer(legacyPassthrough?.presence)
2405
2604
  this.retryTrackerModule = new RetryReasonTracker({
2406
- ...(retryTrackerCfg || {}),
2605
+ ...(legacyPassthrough?.retryTracker || {}),
2407
2606
  onSpiral: (msgId, reason) => {
2408
2607
  this._log(`\u26A0\uFE0F Message ${msgId} stuck in retry spiral (${reason})`)
2409
- retryTrackerCfg?.onSpiral?.(msgId, reason)
2608
+ legacyPassthrough?.retryTracker?.onSpiral?.(msgId, reason)
2410
2609
  }
2411
2610
  })
2412
2611
  this.reconnectThrottleModule = new PostReconnectThrottle({
2413
- ...(reconnectThrottleCfg || {}),
2612
+ ...(legacyPassthrough?.reconnectThrottle || {}),
2414
2613
  baselineRatePerMinute: () => this.rateLimiter.getStats().limits.perMinute
2415
2614
  })
2416
- if (jidCanonicalizerCfg?.enabled) {
2417
- if (jidCanonicalizerCfg.resolver) {
2418
- this.jidCanonicalizerModule = new JidCanonicalizer(jidCanonicalizerCfg)
2419
- this.lidResolverModule = jidCanonicalizerCfg.resolver
2615
+ if (legacyPassthrough?.jidCanonicalizer?.enabled) {
2616
+ if (legacyPassthrough.jidCanonicalizer.resolver) {
2617
+ this.jidCanonicalizerModule = new JidCanonicalizer(legacyPassthrough.jidCanonicalizer)
2618
+ this.lidResolverModule = legacyPassthrough.jidCanonicalizer.resolver
2420
2619
  } else {
2421
- const resolverConfig = lidResolverCfg || jidCanonicalizerCfg.resolverConfig
2620
+ const resolverConfig = legacyPassthrough.lidResolver || legacyPassthrough.jidCanonicalizer.resolverConfig
2422
2621
  const resolver = new LidResolver(resolverConfig)
2423
2622
  this.lidResolverModule = resolver
2424
2623
  this.jidCanonicalizerModule = new JidCanonicalizer({
2425
- ...jidCanonicalizerCfg,
2624
+ ...legacyPassthrough.jidCanonicalizer,
2426
2625
  resolver
2427
2626
  })
2428
2627
  }
2429
- } else if (lidResolverCfg) {
2430
- this.lidResolverModule = new LidResolver(lidResolverCfg)
2628
+ } else if (legacyPassthrough?.lidResolver) {
2629
+ this.lidResolverModule = new LidResolver(legacyPassthrough.lidResolver)
2431
2630
  }
2432
- if (sessionStabilityCfg?.enabled) {
2631
+ if (legacyPassthrough?.sessionStability?.enabled) {
2433
2632
  const healthConfig = {
2434
- badMacThreshold: sessionStabilityCfg.badMacThreshold,
2435
- badMacWindowMs: sessionStabilityCfg.badMacWindowMs,
2633
+ badMacThreshold: legacyPassthrough.sessionStability.badMacThreshold,
2634
+ badMacWindowMs: legacyPassthrough.sessionStability.badMacWindowMs,
2436
2635
  onDegraded: stats => {
2437
- this._log(
2438
- `\u{1F534} SESSION DEGRADED \u2014 Bad MAC rate: ${stats.badMacCount} in last ${sessionStabilityCfg.badMacWindowMs || 6e4}ms`
2439
- )
2636
+ this._log(`\u{1F534} SESSION DEGRADED \u2014 Bad MAC rate: ${stats.badMacCount} in last ${legacyPassthrough?.sessionStability?.badMacWindowMs || 6e4}ms`)
2440
2637
  this._log('Consider restarting session or switching to LID-based canonical form')
2441
2638
  },
2442
2639
  onRecovered: () => {
@@ -2446,8 +2643,11 @@ var AntiBan = class {
2446
2643
  this.sessionStabilityMonitor = new SessionHealthMonitor(healthConfig)
2447
2644
  }
2448
2645
  }
2449
-
2450
- async beforeSend(recipient, content, dedupKey) {
2646
+ /**
2647
+ * Check if a message can be sent and get required delay.
2648
+ * Call this BEFORE every sendMessage().
2649
+ */
2650
+ async beforeSend(recipient, content) {
2451
2651
  const healthStatus = this.health.getStatus()
2452
2652
  if (this.health.isPaused()) {
2453
2653
  this.stats.messagesBlocked++
@@ -2473,9 +2673,7 @@ var AntiBan = class {
2473
2673
  if (!this.warmUp.canSend()) {
2474
2674
  this.stats.messagesBlocked++
2475
2675
  const warmUpStatus = this.warmUp.getStatus()
2476
- this._log(
2477
- `\u23F3 BLOCKED \u2014 warm-up day ${warmUpStatus.day}/${warmUpStatus.totalDays}, limit reached (${warmUpStatus.todaySent}/${warmUpStatus.todayLimit})`
2478
- )
2676
+ this._log(`\u23F3 BLOCKED \u2014 warm-up day ${warmUpStatus.day}/${warmUpStatus.totalDays}, limit reached (${warmUpStatus.todaySent}/${warmUpStatus.todayLimit})`)
2479
2677
  return {
2480
2678
  allowed: false,
2481
2679
  delayMs: 0,
@@ -2537,7 +2735,7 @@ var AntiBan = class {
2537
2735
  return { allowed: false, delayMs: 0, reason: 'Group rate limit exceeded', health: healthStatus }
2538
2736
  }
2539
2737
  }
2540
- let delay = await this.rateLimiter.getDelay(recipient, content, dedupKey)
2738
+ let delay = await this.rateLimiter.getDelay(recipient, content)
2541
2739
  if (delay === -1) {
2542
2740
  this.stats.messagesBlocked++
2543
2741
  this._log(`\u{1F6AB} BLOCKED \u2014 rate limit or identical message spam`)
@@ -2570,19 +2768,26 @@ var AntiBan = class {
2570
2768
  health: healthStatus
2571
2769
  }
2572
2770
  }
2573
-
2574
- afterSend(recipient, content, dedupKey) {
2575
- this.rateLimiter.record(recipient, content, dedupKey)
2771
+ /**
2772
+ * Record a successfully sent message.
2773
+ * Call this AFTER every successful sendMessage().
2774
+ */
2775
+ afterSend(recipient, content) {
2776
+ this.rateLimiter.record(recipient, content)
2576
2777
  this.warmUp.record()
2577
2778
  this.replyRatioGuard.recordSent(recipient)
2578
2779
  this.stats.messagesAllowed++
2579
2780
  this.persistStateDebounced()
2580
2781
  }
2581
-
2782
+ /**
2783
+ * Record a failed message send
2784
+ */
2582
2785
  afterSendFailed(error) {
2583
2786
  this.health.recordMessageFailed(error)
2584
2787
  }
2585
-
2788
+ /**
2789
+ * Record a disconnection (call from connection.update handler)
2790
+ */
2586
2791
  onDisconnect(reason) {
2587
2792
  this.health.recordDisconnect(reason)
2588
2793
  this.reconnectThrottleModule.onDisconnect()
@@ -2591,18 +2796,25 @@ var AntiBan = class {
2591
2796
  this.persistStateImmediate()
2592
2797
  }
2593
2798
  }
2594
-
2799
+ /**
2800
+ * Record a successful reconnection
2801
+ */
2595
2802
  onReconnect() {
2596
2803
  this.health.recordReconnect()
2597
2804
  this.reconnectThrottleModule.onReconnect()
2598
2805
  }
2599
-
2806
+ /**
2807
+ * Handle incoming message — record in reply ratio + contact graph.
2808
+ * Returns suggested reply if reply ratio suggests auto-reply.
2809
+ */
2600
2810
  onIncomingMessage(jid, msgText) {
2601
2811
  this.replyRatioGuard.recordReceived(jid)
2602
2812
  this.contactGraphWarmer.onIncomingMessage(jid)
2603
2813
  return this.replyRatioGuard.suggestReply(jid, msgText)
2604
2814
  }
2605
-
2815
+ /**
2816
+ * Get comprehensive stats
2817
+ */
2606
2818
  getStats() {
2607
2819
  const stats = {
2608
2820
  ...this.stats,
@@ -2636,57 +2848,65 @@ var AntiBan = class {
2636
2848
  }
2637
2849
  return stats
2638
2850
  }
2639
-
2851
+ /** Get the timelock guard for direct access */
2640
2852
  get timelock() {
2641
2853
  return this.timelockGuard
2642
2854
  }
2643
-
2855
+ /** Get the reply ratio guard for direct access */
2644
2856
  get replyRatio() {
2645
2857
  return this.replyRatioGuard
2646
2858
  }
2647
-
2859
+ /** Get the contact graph warmer for direct access */
2648
2860
  get contactGraph() {
2649
2861
  return this.contactGraphWarmer
2650
2862
  }
2651
-
2863
+ /** Get the presence choreographer for direct access */
2652
2864
  get presence() {
2653
2865
  return this.presenceChoreographer
2654
2866
  }
2655
-
2867
+ /** Get the retry tracker for direct access */
2656
2868
  get retryTracker() {
2657
2869
  return this.retryTrackerModule
2658
2870
  }
2659
-
2871
+ /** Get the reconnect throttle for direct access */
2660
2872
  get reconnectThrottle() {
2661
2873
  return this.reconnectThrottleModule
2662
2874
  }
2663
-
2875
+ /** Get the LID resolver for direct access */
2664
2876
  get lidResolver() {
2665
2877
  return this.lidResolverModule
2666
2878
  }
2667
-
2879
+ /** Get the JID canonicalizer for direct access */
2668
2880
  get jidCanonicalizer() {
2669
2881
  return this.jidCanonicalizerModule
2670
2882
  }
2671
-
2883
+ /** Get the session stability monitor for direct access */
2672
2884
  get sessionStability() {
2673
2885
  return this.sessionStabilityMonitor
2674
2886
  }
2675
-
2887
+ /**
2888
+ * Export warm-up state for persistence between restarts
2889
+ */
2676
2890
  exportWarmUpState() {
2677
2891
  return this.warmUp.exportState()
2678
2892
  }
2679
-
2893
+ /**
2894
+ * Force pause all sending
2895
+ */
2680
2896
  pause() {
2681
2897
  this.health.setPaused(true)
2682
2898
  this._log('\u23F8\uFE0F Sending paused manually')
2683
2899
  }
2684
-
2900
+ /**
2901
+ * Resume sending
2902
+ */
2685
2903
  resume() {
2686
2904
  this.health.setPaused(false)
2687
2905
  this._log('\u25B6\uFE0F Sending resumed')
2688
2906
  }
2689
-
2907
+ /**
2908
+ * Reset everything (use after a ban period)
2909
+ */
2690
2910
  reset() {
2691
2911
  this.timelockGuard.reset()
2692
2912
  this.health.reset()
@@ -2719,7 +2939,10 @@ var AntiBan = class {
2719
2939
  }
2720
2940
  this.stateManager.saveImmediate(state)
2721
2941
  }
2722
-
2942
+ /**
2943
+ * Clean up all timers and resources.
2944
+ * Call this when disposing of the AntiBan instance or when the socket closes.
2945
+ */
2723
2946
  destroy() {
2724
2947
  this.stateManager?.destroy()
2725
2948
  this.timelockGuard.reset()
@@ -2735,14 +2958,17 @@ var AntiBan = class {
2735
2958
  }
2736
2959
  }
2737
2960
 
2738
-
2961
+ // lidFirstResolver.js
2739
2962
  var fs2 = __toESM(require('fs'), 1)
2740
2963
  var path = __toESM(require('path'), 1)
2741
2964
  var LidFirstResolver = class {
2742
- lidToPhone = new Map()
2743
- phoneToLid = new Map()
2744
-
2745
-
2965
+ lidToPhone = /* @__PURE__ */ new Map()
2966
+ phoneToLid = /* @__PURE__ */ new Map()
2967
+ // phone → lid (quick reverse lookup)
2968
+ /**
2969
+ * Load mappings from Baileys auth state directory.
2970
+ * Looks for lid-mapping-*_reverse.json files.
2971
+ */
2746
2972
  loadFromAuthDir(authDir) {
2747
2973
  try {
2748
2974
  if (!fs2.existsSync(authDir)) {
@@ -2772,7 +2998,10 @@ var LidFirstResolver = class {
2772
2998
  }
2773
2999
  } catch (error) {}
2774
3000
  }
2775
-
3001
+ /**
3002
+ * Learn a new mapping from a Baileys event (messages, contacts, etc.).
3003
+ * Accepts partial data — will extract what it can.
3004
+ */
2776
3005
  learnFromEvent(event) {
2777
3006
  try {
2778
3007
  if (event.key?.remoteJid) {
@@ -2791,19 +3020,28 @@ var LidFirstResolver = class {
2791
3020
  }
2792
3021
  } catch (error) {}
2793
3022
  }
2794
-
3023
+ /**
3024
+ * Resolve phone number or phone JID to LID JID.
3025
+ * Returns null if not known.
3026
+ */
2795
3027
  resolveToLID(phoneOrJid) {
2796
3028
  const phone = this.extractPhone(phoneOrJid)
2797
3029
  if (!phone) return null
2798
3030
  return this.phoneToLid.get(phone) || null
2799
3031
  }
2800
-
3032
+ /**
3033
+ * Resolve LID JID to phone number.
3034
+ * Returns null if not known.
3035
+ */
2801
3036
  resolveToPhone(lid) {
2802
3037
  const normalized = this.normalizeLid(lid)
2803
3038
  const mapping = this.lidToPhone.get(normalized)
2804
3039
  return mapping ? mapping.phone : null
2805
3040
  }
2806
-
3041
+ /**
3042
+ * Get full mapping for a given JID (either LID or phone).
3043
+ * Returns null if not known.
3044
+ */
2807
3045
  getMapping(jid) {
2808
3046
  const normalized = this.normalizeLid(jid)
2809
3047
  const byLid = this.lidToPhone.get(normalized)
@@ -2815,16 +3053,20 @@ var LidFirstResolver = class {
2815
3053
  }
2816
3054
  return null
2817
3055
  }
2818
-
3056
+ /**
3057
+ * Get total number of known mappings.
3058
+ */
2819
3059
  size() {
2820
3060
  return this.lidToPhone.size
2821
3061
  }
2822
-
3062
+ /**
3063
+ * Clear all mappings.
3064
+ */
2823
3065
  clear() {
2824
3066
  this.lidToPhone.clear()
2825
3067
  this.phoneToLid.clear()
2826
3068
  }
2827
-
3069
+ // Private helpers
2828
3070
  learnJid(_jid, _source) {}
2829
3071
  extractPhone(jid) {
2830
3072
  if (!jid) return null
@@ -2843,7 +3085,7 @@ function createLidFirstResolver() {
2843
3085
  return new LidFirstResolver()
2844
3086
  }
2845
3087
 
2846
-
3088
+ // retryReason.js
2847
3089
  var MessageRetryReason
2848
3090
  ;(function (MessageRetryReason2) {
2849
3091
  MessageRetryReason2[(MessageRetryReason2['UnknownError'] = 0)] = 'UnknownError'
@@ -2855,7 +3097,7 @@ var MessageRetryReason
2855
3097
  MessageRetryReason2[(MessageRetryReason2['MessageExpired'] = 8)] = 'MessageExpired'
2856
3098
  MessageRetryReason2[(MessageRetryReason2['DecryptionError'] = 9)] = 'DecryptionError'
2857
3099
  })(MessageRetryReason || (MessageRetryReason = {}))
2858
- var MAC_ERROR_CODES = new Set([
3100
+ var MAC_ERROR_CODES = /* @__PURE__ */ new Set([
2859
3101
  MessageRetryReason.SignalErrorBadMac,
2860
3102
  MessageRetryReason.SignalErrorInvalidMessage,
2861
3103
  MessageRetryReason.SignalErrorNoSession,
@@ -2900,171 +3142,133 @@ function getRetryReasonDescription(reason) {
2900
3142
  }
2901
3143
  }
2902
3144
 
2903
-
2904
-
2905
- function mediaFingerprint(media) {
2906
- if (Buffer.isBuffer(media)) {
2907
- const len = media.length
2908
- const head = media.subarray(0, Math.min(24, len)).toString('hex')
2909
- const tail = len > 24 ? media.subarray(Math.max(24, len - 24)).toString('hex') : ''
2910
- return `buf:${len}:${head}:${tail}`
2911
- }
2912
- if (media && typeof media === 'object' && typeof media.url === 'string') {
2913
- return `url:${media.url}`
2914
- }
2915
- if (typeof media === 'string') {
2916
- return `str:${media}`
2917
- }
2918
- if (media && typeof media === 'object') {
2919
-
2920
-
2921
-
2922
- return `unresolvable:${Math.random().toString(36)}`
2923
- }
2924
- return 'none'
2925
- }
2926
-
2927
- function buildContentSignature(content) {
2928
- if (!content || typeof content !== 'object') return String(content ?? '')
2929
- const parts = []
2930
- const text =
2931
- content.text ||
2932
- content.caption ||
2933
- content.image?.caption ||
2934
- content.video?.caption ||
2935
- content.document?.caption ||
2936
- ''
2937
- if (text) parts.push(`text:${text}`)
2938
- if (content.image !== undefined) parts.push(`image:${mediaFingerprint(content.image)}`)
2939
- if (content.video !== undefined) parts.push(`video:${mediaFingerprint(content.video)}`)
2940
- if (content.audio !== undefined) parts.push(`audio:${mediaFingerprint(content.audio)}`)
2941
- if (content.sticker !== undefined) parts.push(`sticker:${mediaFingerprint(content.sticker)}`)
2942
- if (content.document !== undefined) {
2943
- parts.push(`document:${mediaFingerprint(content.document)}:${content.fileName || ''}`)
2944
- }
2945
- if (content.location) {
2946
- parts.push(`location:${content.location.degreesLatitude}:${content.location.degreesLongitude}`)
2947
- }
2948
- if (content.contacts) {
2949
- const vcards = content.contacts.contacts?.map(c => c.vcard).join('|') || JSON.stringify(content.contacts)
2950
- parts.push(`contacts:${vcards}`)
2951
- }
2952
- if (content.poll) {
2953
- parts.push(`poll:${content.poll.name}:${(content.poll.values || []).join(',')}`)
2954
- }
2955
- if (content.buttons) parts.push(`buttons:${JSON.stringify(content.buttons)}`)
2956
- if (content.templateButtons) parts.push(`template:${JSON.stringify(content.templateButtons)}`)
2957
- if (content.sections) parts.push(`sections:${JSON.stringify(content.sections)}`)
2958
- if (content.react) parts.push(`react:${content.react.text}:${content.react.key?.id || ''}`)
2959
- if (!parts.length) {
2960
-
2961
- parts.push(`shape:${Object.keys(content).sort().join(',')}`)
2962
- }
2963
- return parts.join('|')
2964
- }
3145
+ // wrapper.js
2965
3146
  function wrapSocket(sock, config, warmUpState, wrapOptions) {
2966
3147
  const antiban = new AntiBan(config, warmUpState)
2967
3148
  const options = {
2968
3149
  autoRespondToIncoming: false,
2969
3150
  ...wrapOptions
2970
3151
  }
2971
-
2972
-
2973
-
2974
-
2975
- const pendingAutoReplyTimers = new Set()
2976
- const scheduleAutoReply = (jid, suggestedText) => {
2977
- const replyDelay = Math.floor(Math.random() * 12e3) + 3e3
2978
- const timer = setTimeout(async () => {
2979
- pendingAutoReplyTimers.delete(timer)
2980
- try {
2981
- await sock.sendMessage(jid, { text: suggestedText })
2982
- } catch (error) {}
2983
- }, replyDelay)
2984
- pendingAutoReplyTimers.add(timer)
2985
- }
2986
- const handleConnectionUpdate = update => {
2987
- if (update.connection === 'close') {
2988
- const reason = update.lastDisconnect?.error?.output?.statusCode || 'unknown'
2989
- antiban.onDisconnect(reason)
2990
- }
2991
- if (update.connection === 'open') {
2992
- antiban.onReconnect()
2993
- }
2994
- if (update.reachoutTimeLock) {
2995
- antiban.timelock.onTimelockUpdate({
2996
- isActive: update.reachoutTimeLock.isActive,
2997
- timeEnforcementEnds: update.reachoutTimeLock.timeEnforcementEnds,
2998
- enforcementType: update.reachoutTimeLock.enforcementType
2999
- })
3000
- }
3001
- }
3002
- const handleMessagesUpdate = updates => {
3003
- for (const update of updates) {
3004
- if (update?.update?.messageStubParameters) {
3005
- const params = update.update.messageStubParameters
3006
- if (params.includes(463) || params.includes('463')) {
3007
- antiban.timelock.record463Error()
3152
+ if (typeof sock.ev.process === 'function') {
3153
+ sock.ev.process(async events => {
3154
+ if (events['connection.update']) {
3155
+ const update = events['connection.update']
3156
+ if (update.connection === 'close') {
3157
+ const reason = update.lastDisconnect?.error?.output?.statusCode || 'unknown'
3158
+ antiban.onDisconnect(reason)
3159
+ }
3160
+ if (update.connection === 'open') {
3161
+ antiban.onReconnect()
3162
+ }
3163
+ if (update.reachoutTimeLock) {
3164
+ antiban.timelock.onTimelockUpdate({
3165
+ isActive: update.reachoutTimeLock.isActive,
3166
+ timeEnforcementEnds: update.reachoutTimeLock.timeEnforcementEnds,
3167
+ enforcementType: update.reachoutTimeLock.enforcementType
3168
+ })
3008
3169
  }
3009
3170
  }
3010
- antiban.retryTracker.onMessageUpdate(update)
3011
- }
3012
- antiban.jidCanonicalizer?.onMessageUpdate(updates)
3013
- }
3014
- const handleMessagesUpsert = upsert => {
3015
- const { messages } = upsert
3016
- antiban.jidCanonicalizer?.onIncomingEvent(upsert)
3017
- for (const msg of messages || []) {
3018
- const jid = msg.key?.remoteJid
3019
- if (!jid) continue
3020
- antiban.timelock.registerKnownChat(jid)
3021
- const isSelf = msg.key?.fromMe || false
3022
- if (isSelf) continue
3023
- const msgText =
3024
- msg.message?.conversation ||
3025
- msg.message?.extendedTextMessage?.text ||
3026
- msg.message?.imageMessage?.caption ||
3027
- msg.message?.videoMessage?.caption ||
3028
- ''
3029
- const replySuggestion = antiban.onIncomingMessage(jid, msgText)
3030
- if (options.autoRespondToIncoming && replySuggestion.shouldReply && replySuggestion.suggestedText) {
3031
- scheduleAutoReply(jid, replySuggestion.suggestedText)
3171
+ if (events['messages.update']) {
3172
+ const updates = events['messages.update']
3173
+ for (const update of updates) {
3174
+ if (update?.update?.messageStubParameters) {
3175
+ const params = update.update.messageStubParameters
3176
+ if (params.includes(463) || params.includes('463')) {
3177
+ antiban.timelock.record463Error()
3178
+ }
3179
+ }
3180
+ antiban.retryTracker.onMessageUpdate(update)
3181
+ }
3182
+ antiban.jidCanonicalizer?.onMessageUpdate(updates)
3032
3183
  }
3033
- }
3034
- }
3035
- let stopEventBridge = () => {}
3036
- if (typeof sock.ev.process === 'function') {
3037
- const processListener = async events => {
3038
- if (events['connection.update']) handleConnectionUpdate(events['connection.update'])
3039
- if (events['messages.update']) handleMessagesUpdate(events['messages.update'])
3040
- if (events['messages.upsert']) handleMessagesUpsert(events['messages.upsert'])
3041
- }
3042
- const maybeUnsubscribe = sock.ev.process(processListener)
3043
- if (typeof maybeUnsubscribe === 'function') {
3044
- stopEventBridge = maybeUnsubscribe
3045
- }
3184
+ if (events['messages.upsert']) {
3185
+ const { messages } = events['messages.upsert']
3186
+ antiban.jidCanonicalizer?.onIncomingEvent(events['messages.upsert'])
3187
+ for (const msg of messages || []) {
3188
+ const jid = msg.key?.remoteJid
3189
+ if (!jid) continue
3190
+ antiban.timelock.registerKnownChat(jid)
3191
+ const isSelf = msg.key?.fromMe || false
3192
+ if (isSelf) continue
3193
+ const msgText =
3194
+ msg.message?.conversation ||
3195
+ msg.message?.extendedTextMessage?.text ||
3196
+ msg.message?.imageMessage?.caption ||
3197
+ msg.message?.videoMessage?.caption ||
3198
+ ''
3199
+ const replySuggestion = antiban.onIncomingMessage(jid, msgText)
3200
+ if (options.autoRespondToIncoming && replySuggestion.shouldReply && replySuggestion.suggestedText) {
3201
+ const replyDelay = Math.floor(Math.random() * 12e3) + 3e3
3202
+ setTimeout(async () => {
3203
+ try {
3204
+ await sock.sendMessage(jid, { text: replySuggestion.suggestedText })
3205
+ } catch (error) {}
3206
+ }, replyDelay)
3207
+ }
3208
+ }
3209
+ }
3210
+ })
3046
3211
  } else {
3047
- sock.ev.on('connection.update', handleConnectionUpdate)
3048
- sock.ev.on('messages.update', handleMessagesUpdate)
3049
- sock.ev.on('messages.upsert', handleMessagesUpsert)
3050
- stopEventBridge = () => {
3051
- sock.ev.off('connection.update', handleConnectionUpdate)
3052
- sock.ev.off('messages.update', handleMessagesUpdate)
3053
- sock.ev.off('messages.upsert', handleMessagesUpsert)
3054
- }
3212
+ sock.ev.on('connection.update', update => {
3213
+ if (update.connection === 'close') {
3214
+ const reason = update.lastDisconnect?.error?.output?.statusCode || 'unknown'
3215
+ antiban.onDisconnect(reason)
3216
+ }
3217
+ if (update.connection === 'open') {
3218
+ antiban.onReconnect()
3219
+ }
3220
+ if (update.reachoutTimeLock) {
3221
+ antiban.timelock.onTimelockUpdate({
3222
+ isActive: update.reachoutTimeLock.isActive,
3223
+ timeEnforcementEnds: update.reachoutTimeLock.timeEnforcementEnds,
3224
+ enforcementType: update.reachoutTimeLock.enforcementType
3225
+ })
3226
+ }
3227
+ })
3228
+ sock.ev.on('messages.update', updates => {
3229
+ for (const update of updates) {
3230
+ if (update?.update?.messageStubParameters) {
3231
+ const params = update.update.messageStubParameters
3232
+ if (params.includes(463) || params.includes('463')) {
3233
+ antiban.timelock.record463Error()
3234
+ }
3235
+ }
3236
+ antiban.retryTracker.onMessageUpdate(update)
3237
+ }
3238
+ antiban.jidCanonicalizer?.onMessageUpdate(updates)
3239
+ })
3240
+ sock.ev.on('messages.upsert', upsert => {
3241
+ const { messages } = upsert
3242
+ antiban.jidCanonicalizer?.onIncomingEvent(upsert)
3243
+ for (const msg of messages || []) {
3244
+ const jid = msg.key?.remoteJid
3245
+ if (!jid) continue
3246
+ antiban.timelock.registerKnownChat(jid)
3247
+ const isSelf = msg.key?.fromMe || false
3248
+ if (isSelf) continue
3249
+ const msgText =
3250
+ msg.message?.conversation ||
3251
+ msg.message?.extendedTextMessage?.text ||
3252
+ msg.message?.imageMessage?.caption ||
3253
+ msg.message?.videoMessage?.caption ||
3254
+ ''
3255
+ const replySuggestion = antiban.onIncomingMessage(jid, msgText)
3256
+ if (options.autoRespondToIncoming && replySuggestion.shouldReply && replySuggestion.suggestedText) {
3257
+ const replyDelay = Math.floor(Math.random() * 12e3) + 3e3
3258
+ setTimeout(async () => {
3259
+ try {
3260
+ await sock.sendMessage(jid, { text: replySuggestion.suggestedText })
3261
+ } catch (error) {}
3262
+ }, replyDelay)
3263
+ }
3264
+ }
3265
+ })
3055
3266
  }
3056
3267
  const originalSendMessage = sock.sendMessage.bind(sock)
3057
3268
  const wrappedSendMessage = async (jid, content, options2) => {
3058
3269
  const canonicalJid = antiban.jidCanonicalizer?.canonicalizeTarget(jid) || jid
3059
- const text =
3060
- content?.text ||
3061
- content?.caption ||
3062
- content?.image?.caption ||
3063
- content?.video?.caption ||
3064
- content?.document?.caption ||
3065
- ''
3066
- const dedupKey = buildContentSignature(content)
3067
- const decision = await antiban.beforeSend(canonicalJid, text, dedupKey)
3270
+ const text = content?.text || content?.caption || content?.image?.caption || ''
3271
+ const decision = await antiban.beforeSend(canonicalJid, text)
3068
3272
  if (!decision.allowed) {
3069
3273
  throw new Error(`[baileys-antiban] Message blocked: ${decision.reason}`)
3070
3274
  }
@@ -3073,7 +3277,7 @@ function wrapSocket(sock, config, warmUpState, wrapOptions) {
3073
3277
  }
3074
3278
  try {
3075
3279
  const result = await originalSendMessage(canonicalJid, content, options2)
3076
- antiban.afterSend(canonicalJid, text, dedupKey)
3280
+ antiban.afterSend(canonicalJid, text)
3077
3281
  antiban.timelock.registerKnownChat(canonicalJid)
3078
3282
  if (result?.key?.id) {
3079
3283
  antiban.retryTracker.clear(result.key.id)
@@ -3087,17 +3291,11 @@ function wrapSocket(sock, config, warmUpState, wrapOptions) {
3087
3291
  const wrapped = Object.create(sock)
3088
3292
  wrapped.sendMessage = wrappedSendMessage
3089
3293
  wrapped.antiban = antiban
3090
- const originalDestroy = antiban.destroy.bind(antiban)
3091
- wrapped.antiban.destroy = () => {
3092
- stopEventBridge()
3093
- for (const timer of pendingAutoReplyTimers) clearTimeout(timer)
3094
- pendingAutoReplyTimers.clear()
3095
- originalDestroy()
3096
- }
3294
+ wrapped.antiban.destroy = antiban.destroy.bind(antiban)
3097
3295
  return wrapped
3098
3296
  }
3099
3297
 
3100
-
3298
+ // messageQueue.js
3101
3299
  var import_events = require('events')
3102
3300
  var DEFAULT_CONFIG12 = {
3103
3301
  maxAttempts: 3,
@@ -3116,11 +3314,16 @@ var MessageQueue = class extends import_events.EventEmitter {
3116
3314
  super()
3117
3315
  this.config = { ...DEFAULT_CONFIG12, ...config }
3118
3316
  }
3119
-
3317
+ /**
3318
+ * Set the send function (called for each message when drained)
3319
+ * This should be the anti-ban wrapped sendMessage
3320
+ */
3120
3321
  setSendFunction(fn) {
3121
3322
  this.sendFn = fn
3122
3323
  }
3123
-
3324
+ /**
3325
+ * Add a message to the queue
3326
+ */
3124
3327
  add(recipient, content, options) {
3125
3328
  if (this.queue.length >= this.config.maxQueueSize) {
3126
3329
  throw new Error(`Queue full (${this.config.maxQueueSize} messages)`)
@@ -3142,17 +3345,23 @@ var MessageQueue = class extends import_events.EventEmitter {
3142
3345
  this.emit('added', message)
3143
3346
  return id
3144
3347
  }
3145
-
3348
+ /**
3349
+ * Add multiple messages (e.g., broadcast to many recipients)
3350
+ */
3146
3351
  addBulk(recipients, content, options) {
3147
3352
  return recipients.map(r => this.add(r, content, options))
3148
3353
  }
3149
-
3354
+ /**
3355
+ * Start processing the queue
3356
+ */
3150
3357
  start(intervalMs = 1e3) {
3151
3358
  if (this.drainTimer) return
3152
3359
  this.drainTimer = setInterval(() => this.processNext(), intervalMs)
3153
3360
  this.emit('started')
3154
3361
  }
3155
-
3362
+ /**
3363
+ * Stop processing
3364
+ */
3156
3365
  stop() {
3157
3366
  if (this.drainTimer) {
3158
3367
  clearInterval(this.drainTimer)
@@ -3160,11 +3369,16 @@ var MessageQueue = class extends import_events.EventEmitter {
3160
3369
  }
3161
3370
  this.emit('stopped')
3162
3371
  }
3163
-
3372
+ /**
3373
+ * Clean up all timers and resources.
3374
+ * Call this when disposing of the queue.
3375
+ */
3164
3376
  destroy() {
3165
3377
  this.stop()
3166
3378
  }
3167
-
3379
+ /**
3380
+ * Process the next message in the queue
3381
+ */
3168
3382
  async processNext() {
3169
3383
  if (this.processing || !this.sendFn) return
3170
3384
  const now = Date.now()
@@ -3193,7 +3407,9 @@ var MessageQueue = class extends import_events.EventEmitter {
3193
3407
  this.processing = false
3194
3408
  }
3195
3409
  }
3196
-
3410
+ /**
3411
+ * Get queue stats
3412
+ */
3197
3413
  getStats() {
3198
3414
  const now = Date.now()
3199
3415
  return {
@@ -3209,23 +3425,31 @@ var MessageQueue = class extends import_events.EventEmitter {
3209
3425
  isRunning: this.drainTimer !== null
3210
3426
  }
3211
3427
  }
3212
-
3428
+ /**
3429
+ * Clear all messages
3430
+ */
3213
3431
  clear() {
3214
3432
  const count = this.queue.length
3215
3433
  this.queue = []
3216
3434
  this.emit('cleared', count)
3217
3435
  }
3218
-
3436
+ /**
3437
+ * Remove a specific message
3438
+ */
3219
3439
  remove(id) {
3220
3440
  const before = this.queue.length
3221
3441
  this.queue = this.queue.filter(m => m.id !== id)
3222
3442
  return this.queue.length < before
3223
3443
  }
3224
-
3444
+ /**
3445
+ * Export queue for persistence
3446
+ */
3225
3447
  export() {
3226
3448
  return [...this.queue]
3227
3449
  }
3228
-
3450
+ /**
3451
+ * Import queue (e.g., after restart)
3452
+ */
3229
3453
  import(messages) {
3230
3454
  this.queue = [...messages]
3231
3455
  this.sortQueue()
@@ -3241,7 +3465,7 @@ var MessageQueue = class extends import_events.EventEmitter {
3241
3465
  }
3242
3466
  }
3243
3467
 
3244
-
3468
+ // contentVariator.js
3245
3469
  var DEFAULT_CONFIG13 = {
3246
3470
  zeroWidthChars: true,
3247
3471
  punctuationVariation: true,
@@ -3250,13 +3474,13 @@ var DEFAULT_CONFIG13 = {
3250
3474
  }
3251
3475
  var ZERO_WIDTH = [
3252
3476
  '\u200B',
3253
-
3477
+ // zero-width space
3254
3478
  '\u200C',
3255
-
3479
+ // zero-width non-joiner
3256
3480
  '\u200D',
3257
-
3481
+ // zero-width joiner
3258
3482
  '\uFEFF'
3259
-
3483
+ // zero-width no-break space
3260
3484
  ]
3261
3485
  var SYNONYMS = {
3262
3486
  hello: ['hi', 'hey', 'howdy'],
@@ -3283,7 +3507,10 @@ var ContentVariator = class {
3283
3507
  constructor(config = {}) {
3284
3508
  this.config = { ...DEFAULT_CONFIG13, ...config }
3285
3509
  }
3286
-
3510
+ /**
3511
+ * Create a unique variation of a message
3512
+ * Each call produces a slightly different version
3513
+ */
3287
3514
  vary(text) {
3288
3515
  let result = text
3289
3516
  this.counter++
@@ -3304,15 +3531,22 @@ var ContentVariator = class {
3304
3531
  }
3305
3532
  return result
3306
3533
  }
3307
-
3534
+ /**
3535
+ * Create N unique variations of a message
3536
+ */
3308
3537
  varyBulk(text, count) {
3309
3538
  const results = []
3310
- const startCounter = this.counter
3539
+ const seen = /* @__PURE__ */ new Set()
3311
3540
  for (let i = 0; i < count; i++) {
3312
- this.counter = startCounter + i
3313
- results.push(this.vary(text))
3541
+ let variation = this.vary(text)
3542
+ let attempts = 0
3543
+ while (seen.has(variation) && attempts < 10) {
3544
+ variation = this.vary(text)
3545
+ attempts++
3546
+ }
3547
+ seen.add(variation)
3548
+ results.push(variation)
3314
3549
  }
3315
- this.counter = startCounter + count
3316
3550
  return results
3317
3551
  }
3318
3552
  addZeroWidth(text) {
@@ -3331,14 +3565,14 @@ var ContentVariator = class {
3331
3565
  }
3332
3566
  varyPunctuation(text) {
3333
3567
  const variations = [
3334
-
3568
+ // Trailing space variations
3335
3569
  () => text + ' ',
3336
3570
  () => text + ' ',
3337
-
3571
+ // Period variations
3338
3572
  () => (text.endsWith('.') ? text.slice(0, -1) : text + '.'),
3339
-
3573
+ // Nothing
3340
3574
  () => text,
3341
-
3575
+ // Capitalize first letter variation
3342
3576
  () => (text.charAt(0) === text.charAt(0).toUpperCase() ? text.charAt(0).toLowerCase() + text.slice(1) : text)
3343
3577
  ]
3344
3578
  return variations[this.counter % variations.length]()
@@ -3348,18 +3582,21 @@ var ContentVariator = class {
3348
3582
  return text + emojis[this.counter % emojis.length]
3349
3583
  }
3350
3584
  applySynonyms(text) {
3351
- const lower = text.toLowerCase()
3352
- for (const key of Object.keys(SYNONYMS)) {
3353
- const idx = lower.indexOf(key)
3354
- if (idx === -1) continue
3355
- const word = text.substring(idx, idx + key.length)
3356
- if (!SYNONYMS[key] || Math.random() > 0.5) continue
3357
- const synonym = SYNONYMS[key][Math.floor(Math.random() * SYNONYMS[key].length)]
3358
- const replacement =
3359
- word[0] === word[0].toUpperCase() ? synonym.charAt(0).toUpperCase() + synonym.slice(1) : synonym
3360
- return text.substring(0, idx) + replacement + text.substring(idx + key.length)
3361
- }
3362
- return text
3585
+ const words = text.split(/\b/)
3586
+ let replaced = false
3587
+ return words
3588
+ .map(word => {
3589
+ if (replaced) return word
3590
+ const lower = word.toLowerCase()
3591
+ const synonymList = SYNONYMS[lower]
3592
+ if (synonymList && Math.random() > 0.5) {
3593
+ replaced = true
3594
+ const synonym = synonymList[Math.floor(Math.random() * synonymList.length)]
3595
+ return word[0] === word[0].toUpperCase() ? synonym.charAt(0).toUpperCase() + synonym.slice(1) : synonym
3596
+ }
3597
+ return word
3598
+ })
3599
+ .join('')
3363
3600
  }
3364
3601
  randomPositions(max, count) {
3365
3602
  const positions = []
@@ -3371,7 +3608,7 @@ var ContentVariator = class {
3371
3608
  }
3372
3609
  }
3373
3610
 
3374
-
3611
+ // webhooks.js
3375
3612
  var DEFAULT_CONFIG14 = {
3376
3613
  urls: [],
3377
3614
  minRiskLevel: 'medium',
@@ -3384,7 +3621,9 @@ var WebhookAlerts = class {
3384
3621
  constructor(config = {}) {
3385
3622
  this.config = { ...DEFAULT_CONFIG14, ...config }
3386
3623
  }
3387
-
3624
+ /**
3625
+ * Send alert if risk level warrants it
3626
+ */
3388
3627
  async alert(data) {
3389
3628
  const riskOrder = ['low', 'medium', 'high', 'critical']
3390
3629
  if (riskOrder.indexOf(data.risk) < riskOrder.indexOf(this.config.minRiskLevel)) {
@@ -3397,7 +3636,7 @@ var WebhookAlerts = class {
3397
3636
  this.lastAlertTime = now
3398
3637
  const payload = {
3399
3638
  source: 'baileys-antiban',
3400
- timestamp: new Date().toISOString(),
3639
+ timestamp: /* @__PURE__ */ new Date().toISOString(),
3401
3640
  ...data
3402
3641
  }
3403
3642
  for (const url of this.config.urls) {
@@ -3432,7 +3671,7 @@ ${data.reasons.map(r => `\u2022 ${r}`).join('\n')}`
3432
3671
  { name: 'Recommendation', value: data.recommendation },
3433
3672
  { name: 'Reasons', value: data.reasons.join('\n') }
3434
3673
  ],
3435
- timestamp: new Date().toISOString()
3674
+ timestamp: /* @__PURE__ */ new Date().toISOString()
3436
3675
  }
3437
3676
  ]
3438
3677
  }).catch(() => {})
@@ -3457,7 +3696,7 @@ ${data.reasons.map(r => `\u2022 ${r}`).join('\n')}`
3457
3696
  }
3458
3697
  }
3459
3698
 
3460
-
3699
+ // scheduler.js
3461
3700
  var DEFAULT_CONFIG15 = {
3462
3701
  timezone: 'UTC',
3463
3702
  activeHours: [8, 21],
@@ -3472,13 +3711,18 @@ var Scheduler = class {
3472
3711
  constructor(config = {}) {
3473
3712
  this.config = { ...DEFAULT_CONFIG15, ...config }
3474
3713
  }
3475
-
3714
+ /**
3715
+ * Check if now is within active hours
3716
+ */
3476
3717
  isActiveTime() {
3477
3718
  const hour = this.getCurrentHour()
3478
3719
  const [start, end] = this.config.activeHours
3479
3720
  return hour >= start && hour < end
3480
3721
  }
3481
-
3722
+ /**
3723
+ * Get the speed multiplier for current time
3724
+ * > 1 = faster, < 1 = slower, 0 = don't send
3725
+ */
3482
3726
  getSpeedFactor() {
3483
3727
  if (!this.isActiveTime()) return 0
3484
3728
  const hour = this.getCurrentHour()
@@ -3497,10 +3741,12 @@ var Scheduler = class {
3497
3741
  }
3498
3742
  return factor
3499
3743
  }
3500
-
3744
+ /**
3745
+ * Get ms until next active window
3746
+ */
3501
3747
  msUntilActive() {
3502
3748
  if (this.isActiveTime()) return 0
3503
- const now = new Date()
3749
+ const now = /* @__PURE__ */ new Date()
3504
3750
  const hour = now.getHours()
3505
3751
  const [start] = this.config.activeHours
3506
3752
  let nextActive
@@ -3514,13 +3760,17 @@ var Scheduler = class {
3514
3760
  }
3515
3761
  return nextActive.getTime() - now.getTime()
3516
3762
  }
3517
-
3763
+ /**
3764
+ * Adjust a delay based on current time factors
3765
+ */
3518
3766
  adjustDelay(baseDelayMs) {
3519
3767
  const factor = this.getSpeedFactor()
3520
3768
  if (factor === 0) return -1
3521
3769
  return Math.round(baseDelayMs / factor)
3522
3770
  }
3523
-
3771
+ /**
3772
+ * Get current schedule status
3773
+ */
3524
3774
  getStatus() {
3525
3775
  const hour = this.getCurrentHour()
3526
3776
  const day = this.getCurrentDay()
@@ -3536,14 +3786,14 @@ var Scheduler = class {
3536
3786
  }
3537
3787
  }
3538
3788
  getCurrentHour() {
3539
- return new Date().getHours()
3789
+ return /* @__PURE__ */ new Date().getHours()
3540
3790
  }
3541
3791
  getCurrentDay() {
3542
- return new Date().getDay()
3792
+ return /* @__PURE__ */ new Date().getDay()
3543
3793
  }
3544
3794
  }
3545
3795
 
3546
-
3796
+ // stateAdapter.js
3547
3797
  var _fsPromises = require('fs/promises')
3548
3798
  var _fsSync = require('fs')
3549
3799
  var _path3 = require('path')
@@ -3586,11 +3836,11 @@ var FileStateAdapter = class {
3586
3836
  }
3587
3837
  }
3588
3838
 
3589
-
3839
+ // messageRecovery.js
3590
3840
  var DEFAULT_CONFIG16 = {
3591
3841
  maxTrackedChats: 1e3,
3592
3842
  maxGapMs: 30 * 6e4,
3593
-
3843
+ // 30 minutes
3594
3844
  persistDebounceMs: 2e3,
3595
3845
  onGapFilled: () => {},
3596
3846
  logger: {
@@ -3602,7 +3852,7 @@ var DEFAULT_CONFIG16 = {
3602
3852
  function messageRecovery(sock, config) {
3603
3853
  const cfg = { ...DEFAULT_CONFIG16, ...config }
3604
3854
  const logger = cfg.logger
3605
- const lastSeen = new Map()
3855
+ const lastSeen = /* @__PURE__ */ new Map()
3606
3856
  let disconnectedAt = null
3607
3857
  let totalRecovered = 0
3608
3858
  let lastReconnectAt = null
@@ -3702,7 +3952,7 @@ function messageRecovery(sock, config) {
3702
3952
  loggedFetchWarning = true
3703
3953
  }
3704
3954
  disconnectedAt = null
3705
- lastReconnectAt = new Date()
3955
+ lastReconnectAt = /* @__PURE__ */ new Date()
3706
3956
  lastGapMs = gapMs
3707
3957
  await cfg.onRecoveryComplete?.({
3708
3958
  chats: 0,
@@ -3715,7 +3965,7 @@ function messageRecovery(sock, config) {
3715
3965
  try {
3716
3966
  const messages = await sock.fetchMessageHistory(jid, 50, {
3717
3967
  before: void 0
3718
-
3968
+ // Get latest
3719
3969
  })
3720
3970
  if (!messages || !Array.isArray(messages)) continue
3721
3971
  const gapMessages = messages.filter(msg => {
@@ -3748,7 +3998,7 @@ function messageRecovery(sock, config) {
3748
3998
  }
3749
3999
  }
3750
4000
  totalRecovered += recovered
3751
- lastReconnectAt = new Date()
4001
+ lastReconnectAt = /* @__PURE__ */ new Date()
3752
4002
  lastGapMs = gapMs
3753
4003
  disconnectedAt = null
3754
4004
  logger.info?.(
@@ -3835,33 +4085,29 @@ function messageRecovery(sock, config) {
3835
4085
  }
3836
4086
  }
3837
4087
 
3838
-
4088
+ // deviceFingerprint.js
3839
4089
  var DEFAULT_APP_VERSION_POOL = [
3840
- [2, 25, 10, 67],
3841
- [2, 25, 10, 68],
3842
- [2, 25, 9, 96],
3843
- [2, 25, 8, 77],
3844
- [2, 25, 7, 85],
3845
- [2, 25, 6, 98],
3846
- [2, 24, 22, 78],
3847
- [2, 24, 20, 86]
4090
+ [2, 24, 5, 18],
4091
+ [2, 24, 5, 17],
4092
+ [2, 24, 4, 77],
4093
+ [2, 24, 5, 15],
4094
+ [2, 24, 3, 91],
4095
+ [2, 24, 5, 20]
3848
4096
  ]
3849
- var DEFAULT_OS_VERSION_POOL = ['11', '12', '13', '14', '15']
4097
+ var DEFAULT_OS_VERSION_POOL = ['10', '11', '12', '13', '14']
3850
4098
  var DEFAULT_DEVICE_MODEL_POOL = [
3851
- 'Pixel 8',
3852
- 'Pixel 9',
4099
+ 'Pixel 6',
3853
4100
  'Pixel 7',
3854
- 'Galaxy S24',
3855
- 'Galaxy S23',
3856
4101
  'Galaxy S22',
3857
- 'Xiaomi 14',
4102
+ 'Galaxy S23',
3858
4103
  'Xiaomi 13',
3859
- 'OnePlus 12',
4104
+ 'Xiaomi 12',
3860
4105
  'OnePlus 11',
3861
4106
  'Moto G84',
3862
- 'Realme 12',
3863
- 'Vivo V30',
3864
- 'Oppo Find X7'
4107
+ 'Moto G54',
4108
+ 'Realme 11',
4109
+ 'Vivo V29',
4110
+ 'Oppo Find X6'
3865
4111
  ]
3866
4112
  var SeededRandom = class {
3867
4113
  state
@@ -3901,7 +4147,7 @@ function generateFingerprint(config = {}, sessionId) {
3901
4147
  const deviceModel = enabled && randomizeDeviceModel ? rng.pick(deviceModelPool) : deviceModelPool[0]
3902
4148
  return {
3903
4149
  appVersion: [...appVersion],
3904
-
4150
+ // Copy to avoid mutation
3905
4151
  osVersion,
3906
4152
  deviceModel,
3907
4153
  sessionId: finalSessionId
@@ -3918,7 +4164,7 @@ function applyFingerprint(socketConfig, fp) {
3918
4164
  return config
3919
4165
  }
3920
4166
 
3921
-
4167
+ // credsSnapshot.js
3922
4168
  var import_fs = require('fs')
3923
4169
  var path2 = __toESM(require('path'), 1)
3924
4170
  var noop = () => {}
@@ -3938,7 +4184,7 @@ function credsSnapshot(config) {
3938
4184
  return null
3939
4185
  }
3940
4186
  await import_fs.promises.mkdir(snapshotDir, { recursive: true })
3941
- const timestamp = new Date().toISOString().replace(/[:.]/g, '-')
4187
+ const timestamp = /* @__PURE__ */ new Date().toISOString().replace(/[:.]/g, '-')
3942
4188
  const snapshotPath = path2.join(snapshotDir, `creds-${timestamp}.json`)
3943
4189
  const tmpPath = `${snapshotPath}.tmp`
3944
4190
  await import_fs.promises.copyFile(credsPath, tmpPath)
@@ -4019,7 +4265,7 @@ function credsSnapshot(config) {
4019
4265
  }
4020
4266
  }
4021
4267
 
4022
-
4268
+ // readReceiptVariance.js
4023
4269
  function gaussianRandom() {
4024
4270
  let u = 0
4025
4271
  let v = 0
@@ -4029,7 +4275,7 @@ function gaussianRandom() {
4029
4275
  }
4030
4276
  function readReceiptVariance(config = {}) {
4031
4277
  const { meanMs = 1500, stdDevMs = 800, minMs = 200, maxMs = 8e3, skipIfOlderThanMs = 6e4 } = config
4032
- const pendingTimers = new Set()
4278
+ const pendingTimers = /* @__PURE__ */ new Set()
4033
4279
  function delayMs() {
4034
4280
  const gaussian = gaussianRandom()
4035
4281
  const value = meanMs + gaussian * stdDevMs
@@ -4086,7 +4332,7 @@ function readReceiptVariance(config = {}) {
4086
4332
  }
4087
4333
  }
4088
4334
 
4089
-
4335
+ // proxyRotator.js
4090
4336
  var import_node_module = require('node:module')
4091
4337
  var require2 = (0, import_node_module.createRequire)(__importMetaUrl)
4092
4338
  var NoopLogger = {
@@ -4102,7 +4348,7 @@ function proxyRotator(config) {
4102
4348
  scheduledIntervalMs = 0,
4103
4349
  maxFailures = 3,
4104
4350
  deadCooldownMs = 6e5,
4105
-
4351
+ // 10 minutes
4106
4352
  logger = NoopLogger
4107
4353
  } = config
4108
4354
  if (!pool || pool.length === 0) {
@@ -4124,7 +4370,7 @@ function proxyRotator(config) {
4124
4370
  let totalRotations = 0
4125
4371
  const rotationsByTrigger = {}
4126
4372
  let scheduledTimer = null
4127
- const agentCache = new Map()
4373
+ const agentCache = /* @__PURE__ */ new Map()
4128
4374
  const moduleCache = {}
4129
4375
  function buildProxyUrl(endpoint) {
4130
4376
  const { type, host, port, username, password } = endpoint
@@ -4263,7 +4509,7 @@ function proxyRotator(config) {
4263
4509
  } else {
4264
4510
  currentIndex = nextIdx
4265
4511
  }
4266
- states[currentIndex].lastUsedAt = new Date()
4512
+ states[currentIndex].lastUsedAt = /* @__PURE__ */ new Date()
4267
4513
  totalRotations++
4268
4514
  rotationsByTrigger[reason] = (rotationsByTrigger[reason] || 0) + 1
4269
4515
  const label = states[currentIndex].endpoint.label || states[currentIndex].endpoint.host
@@ -4331,7 +4577,7 @@ function proxyRotator(config) {
4331
4577
  }, scheduledIntervalMs)
4332
4578
  logger.info?.(`Scheduled rotation enabled (every ${scheduledIntervalMs}ms)`)
4333
4579
  }
4334
- states[0].lastUsedAt = new Date()
4580
+ states[0].lastUsedAt = /* @__PURE__ */ new Date()
4335
4581
  return {
4336
4582
  currentAgent: currentAgentImpl,
4337
4583
  current: currentImpl,
@@ -4342,7 +4588,7 @@ function proxyRotator(config) {
4342
4588
  getStats: getStatsImpl
4343
4589
  }
4344
4590
  }
4345
-
4591
+ // Annotate the CommonJS export names for ESM import in node:
4346
4592
  0 &&
4347
4593
  (module.exports = {
4348
4594
  AntiBan,