baileys-antiban 3.9.0 → 4.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/README.md +163 -2
  2. package/dist/antiban.d.ts +18 -1
  3. package/dist/antiban.js +139 -1
  4. package/dist/banRecoveryOrchestrator.d.ts +87 -0
  5. package/dist/banRecoveryOrchestrator.js +284 -0
  6. package/dist/cjs/antiban.js +139 -1
  7. package/dist/cjs/banRecoveryOrchestrator.js +288 -0
  8. package/dist/cjs/deliveryTracker.js +111 -0
  9. package/dist/cjs/fleetEventStore.js +162 -0
  10. package/dist/cjs/groupOperationGuard.js +122 -0
  11. package/dist/cjs/humanEntropy.js +342 -0
  12. package/dist/cjs/index.js +32 -2
  13. package/dist/cjs/instanceCoordinator.js +189 -0
  14. package/dist/cjs/jidCircuitBreaker.js +157 -0
  15. package/dist/cjs/legitimacySignalInjector.js +235 -0
  16. package/dist/cjs/presets.js +14 -6
  17. package/dist/cjs/rateLimiter.js +24 -0
  18. package/dist/cjs/warmup.js +5 -1
  19. package/dist/cjs/wrapper.js +176 -6
  20. package/dist/deliveryTracker.d.ts +55 -0
  21. package/dist/deliveryTracker.js +107 -0
  22. package/dist/fleetEventStore.d.ts +65 -0
  23. package/dist/fleetEventStore.js +157 -0
  24. package/dist/groupOperationGuard.d.ts +79 -0
  25. package/dist/groupOperationGuard.js +116 -0
  26. package/dist/humanEntropy.d.ts +133 -0
  27. package/dist/humanEntropy.js +337 -0
  28. package/dist/index.d.ts +9 -1
  29. package/dist/index.js +15 -1
  30. package/dist/instanceCoordinator.d.ts +60 -0
  31. package/dist/instanceCoordinator.js +152 -0
  32. package/dist/jidCircuitBreaker.d.ts +48 -0
  33. package/dist/jidCircuitBreaker.js +152 -0
  34. package/dist/legitimacySignalInjector.d.ts +90 -0
  35. package/dist/legitimacySignalInjector.js +231 -0
  36. package/dist/presets.d.ts +3 -0
  37. package/dist/presets.js +14 -6
  38. package/dist/rateLimiter.d.ts +12 -0
  39. package/dist/rateLimiter.js +24 -0
  40. package/dist/warmup.d.ts +7 -2
  41. package/dist/warmup.js +5 -1
  42. package/dist/wrapper.d.ts +54 -0
  43. package/dist/wrapper.js +175 -6
  44. package/package.json +1 -1
package/README.md CHANGED
@@ -8,9 +8,9 @@
8
8
 
9
9
  **Drop-in anti-ban middleware for Baileys WhatsApp bots. Free, self-hosted, TypeScript-first. Whapi.Cloud alternative — zero monthly fees.**
10
10
 
11
- > Rate limiting with Gaussian jitter, 7-day warmup, session health monitoring, LID resolver, disconnect classification, contact graph enforcement — all in one `npm install`. Works with [Baileys](https://github.com/WhiskeySockets/Baileys) and [@oxidezap/baileyrs](https://github.com/oxidezap/baileyrs) (Rust/WASM).
11
+ > Rate limiting with Gaussian jitter, 7-day warmup, session health monitoring, LID resolver, disconnect classification, contact graph enforcement, device fingerprinting, group operation guards, recovery orchestration, cross-instance coordination — all in one `npm install`. Works with [Baileys](https://github.com/WhiskeySockets/Baileys) and [@oxidezap/baileyrs](https://github.com/oxidezap/baileyrs) (Rust/WASM).
12
12
 
13
- > **New in v3.3:** [LID Migration Guide](./docs/lid-migration.md) survive Baileys v7's @lid default with stable thread keys.
13
+ > **New in v4.7:** HumanEntropyService background human-like activity (typing indicators, delayed read receipts, presence cycles) to prevent "too perfect bot" detection. Works with WaSP and any session manager, no socket access needed.
14
14
 
15
15
  ## Why Trust This Package
16
16
 
@@ -27,6 +27,167 @@ The npm WhatsApp ecosystem has a malware problem. In April 2026, [`lotusbail`](h
27
27
 
28
28
  If you can't read the code yourself, lean on these signals: signed releases, public audit trail, no telemetry, and a real product behind it. That's the floor. Everything below is the feature set.
29
29
 
30
+ ## v4.x New Features — Production-Grade Ban Prevention
31
+
32
+ v4.0–v4.7 ship seven major anti-ban modules. All are **auto-wired** by default via `wrapSocket()` or `wrapSocketWithFingerprint()`.
33
+
34
+ ### v4.0 — GroupOperationGuard
35
+
36
+ Rate-limits group operations to prevent `account_reachout_restricted` errors. WA limits: ~3 adds/10min, 2 creates/10min.
37
+
38
+ ```typescript
39
+ import { wrapSocket } from 'baileys-antiban';
40
+
41
+ const sock = wrapSocket(makeWASocket({ ... }), {
42
+ groupOpGuard: { limits: { add: { max: 3, windowMs: 600_000 } } },
43
+ });
44
+ ```
45
+
46
+ Classify errors: `classifyGroupOpError(err)` returns `GROUP_OP_ERRORS.REACHOUT_RESTRICTED` | `RATE_OVERLIMIT` | `PRIVACY_BLOCK` | etc.
47
+ Disable: `groupOpGuard: false`
48
+
49
+ ### v4.1 — LegitimacySignalInjector
50
+
51
+ Injects realistic imperfections: typos + corrections (2.5% of messages), read gaps, mid-typing pauses. WhatsApp's ML flags accounts that are "too perfect".
52
+
53
+ ```typescript
54
+ const sock = wrapSocket(makeWASocket({ ... }), {
55
+ legitimacySignals: { typoProbability: 0.03 },
56
+ });
57
+ ```
58
+
59
+ Disable: `legitimacySignals: false`
60
+
61
+ ### v4.2 — BanRecoveryOrchestrator
62
+
63
+ Structured recovery after ban events. Auto-triggers via HealthMonitor at critical risk.
64
+
65
+ ```typescript
66
+ import { BanRecoveryOrchestrator } from 'baileys-antiban';
67
+
68
+ const recovery = new BanRecoveryOrchestrator({
69
+ onPhaseChange: (phase, plan) => console.log(`Phase: ${phase}`),
70
+ onHardBan: () => { /* replace SIM */ },
71
+ });
72
+
73
+ recovery.triggerRecovery('timelock');
74
+ const status = recovery.getStatus();
75
+ console.log(status.rateMultiplier); // 0.1 = 10% speed
76
+ ```
77
+
78
+ **Recovery plans:** timelock: 24h pause, 10% resume, 15%/week ramp | rate_overlimit: 4h, 25%, 25%/week | soft_ban: 48h, 5%, 10%/week | hard_ban: dead
79
+ Access via: `antiban.recoveryOrchestrator`
80
+
81
+ ### v4.3 — wrapSocketWithFingerprint
82
+
83
+ One-call setup with device fingerprint randomization (appVersion, osVersion, deviceModel).
84
+
85
+ ```typescript
86
+ import { wrapSocketWithFingerprint } from 'baileys-antiban';
87
+
88
+ const sock = wrapSocketWithFingerprint(makeWASocket, { auth }, { preset: 'moderate' });
89
+ ```
90
+
91
+ **Preset changes:** All presets default `groupProfiles: true`; `aggressive`/`high-volume` now `autoPauseAt: 'high'`
92
+
93
+ ### v4.4 — Per-Contact Risk Delays + DeliveryTracker
94
+
95
+ Strangers get 2.5× delay, known contacts 1.0×. Active when `contactGraph.enabled: true`.
96
+
97
+ ```typescript
98
+ const sock = wrapSocket(makeWASocket({ ... }), { contactGraph: { enabled: true } });
99
+ // stranger: 2.5×, handshake_sent: 1.8×, handshake_complete: 1.3×, known: 1.0×
100
+ ```
101
+
102
+ **DeliveryTracker** tracks double-tick receipts. <60% delivery = soft-ban signal.
103
+
104
+ ```typescript
105
+ import { DeliveryTracker } from 'baileys-antiban';
106
+
107
+ const tracker = new DeliveryTracker({
108
+ lowRateThreshold: 0.6,
109
+ onLowDeliveryRate: (rate) => console.error(`Delivery: ${rate * 100}%`),
110
+ });
111
+
112
+ sock.ev.on('messages.upsert', ({ messages }) => {
113
+ messages.forEach(m => m.key.fromMe && tracker.onMessageSent(m.key.id));
114
+ });
115
+ sock.ev.on('messages.update', (updates) => {
116
+ updates.forEach(({ key, update }) => {
117
+ if (update.status >= 3) tracker.onDeliveryReceipt(key.id);
118
+ });
119
+ });
120
+ ```
121
+
122
+ ### v4.5 — Adaptive Rate Limiting
123
+
124
+ Auto-adjusts rate based on delivery success: ≥85% → 100% speed, <55% → 25% speed. Auto-wired.
125
+
126
+ ```typescript
127
+ import { RateLimiter } from 'baileys-antiban';
128
+
129
+ const limiter = new RateLimiter({ maxPerMinute: 10 });
130
+ limiter.adaptLimits(0.5); // manual throttle to 50%
131
+ const factor = limiter.getCurrentFactor();
132
+ ```
133
+
134
+ ### v4.6 — Cross-Instance Coordination
135
+
136
+ Shared token bucket across processes. Solves: 5 bots × 8/min = 40/min → flag.
137
+
138
+ ```typescript
139
+ const sock = wrapSocket(makeWASocket({ ... }), {
140
+ instanceCoordinator: '/tmp/wa-pool.json',
141
+ instancePoolMaxPerMinute: 20,
142
+ });
143
+ ```
144
+
145
+ All instances share 20/min IP-level budget. Atomic writes via rename-swap.
146
+
147
+ ### v4.7 — HumanEntropyService
148
+
149
+ Background noise generator that makes a WA session indistinguishable from a real human user during idle periods. Runs independently of your message flow — no socket access required, works with WaSP or any session manager.
150
+
151
+ **The problem it solves:** WhatsApp's ML flags accounts with "too perfect" patterns — instant read receipts, zero typing activity, always-on presence. A listen-only bot that never idles looks like a bot.
152
+
153
+ **What it does every 2-6 hours (randomized):**
154
+ - Sends typing indicator to a recent contact for 3-8 seconds, then stops (mimics "started typing, changed mind")
155
+ - Marks a received message as read with 10-60 min delay (mimics "opened notification, read later")
156
+ - Toggles own presence available → unavailable over 30-120s (mimics "checked phone, put it down")
157
+
158
+ **Safety:** Only contacts people who already messaged you first. Never cold-contacts strangers. All errors caught silently.
159
+
160
+ ```typescript
161
+ import { createHumanEntropyService } from 'baileys-antiban';
162
+
163
+ // Works with WaSP (no direct socket access needed)
164
+ const entropy = createHumanEntropyService(wasp, sessionId, {
165
+ enabled: true,
166
+ minIntervalMs: 2 * 60 * 60 * 1000, // 2 hours
167
+ maxIntervalMs: 6 * 60 * 60 * 1000, // 6 hours
168
+ });
169
+
170
+ entropy.start(); // runs in background
171
+ entropy.stop(); // call on shutdown
172
+
173
+ // Or with direct Baileys socket
174
+ import { HumanEntropyService } from 'baileys-antiban';
175
+ const svc = new HumanEntropyService(socket, { enabled: true });
176
+ svc.start();
177
+ ```
178
+
179
+ **Tracking recent contacts:** Feed incoming messages so the service knows who to interact with:
180
+
181
+ ```typescript
182
+ sock.ev.on('messages.upsert', ({ messages }) => {
183
+ messages.forEach(m => entropy.addRecentContact(m.key.remoteJid, m.key));
184
+ });
185
+ ```
186
+
187
+ Stats: `entropy.getStats()` returns `{ typingActions, readActions, presenceActions, cycles }`.
188
+
189
+ ---
190
+
30
191
  ## v2.0 New Features — Session Stability Module
31
192
 
32
193
  ### What's New in v2.0
package/dist/antiban.d.ts CHANGED
@@ -25,7 +25,10 @@ import { PostReconnectThrottle, type ReconnectThrottleConfig, type ReconnectThro
25
25
  import { LidResolver, type LidResolverConfig, type LidResolverStats } from './lidResolver.js';
26
26
  import { JidCanonicalizer, type JidCanonicalizerConfig, type JidCanonicalizerStats } from './jidCanonicalizer.js';
27
27
  import { SessionHealthMonitor, type SessionHealthStats } from './sessionStability.js';
28
+ import { BanRecoveryOrchestrator, type RecoveryStatus } from './banRecoveryOrchestrator.js';
28
29
  import { type AntiBanInput, type ResolvedConfig } from './presets.js';
30
+ import { type DeliveryTrackerStats } from './deliveryTracker.js';
31
+ import { type InstanceCoordinatorStats } from './instanceCoordinator.js';
29
32
  export interface AntiBanConfigLegacy {
30
33
  rateLimiter?: Partial<RateLimiterConfig>;
31
34
  warmUp?: Partial<WarmUpConfig>;
@@ -70,6 +73,9 @@ export interface AntiBanStats {
70
73
  lidResolver?: LidResolverStats | null;
71
74
  jidCanonicalizer?: JidCanonicalizerStats | null;
72
75
  sessionStability?: SessionHealthStats | null;
76
+ banRecovery?: RecoveryStatus | null;
77
+ deliveryTracker: DeliveryTrackerStats;
78
+ instanceCoordinator?: InstanceCoordinatorStats | null;
73
79
  }
74
80
  export declare class AntiBan {
75
81
  private rateLimiter;
@@ -84,6 +90,9 @@ export declare class AntiBan {
84
90
  private lidResolverModule;
85
91
  private jidCanonicalizerModule;
86
92
  private sessionStabilityMonitor;
93
+ private banRecovery;
94
+ private deliveryTracker;
95
+ private instanceCoordinator;
87
96
  private stateManager;
88
97
  private resolvedConfig;
89
98
  private logging;
@@ -98,7 +107,7 @@ export declare class AntiBan {
98
107
  * Record a successfully sent message.
99
108
  * Call this AFTER every successful sendMessage().
100
109
  */
101
- afterSend(recipient: string, content: string): void;
110
+ afterSend(recipient: string, content: string, msgId?: string): void;
102
111
  /**
103
112
  * Record a failed message send
104
113
  */
@@ -119,6 +128,11 @@ export declare class AntiBan {
119
128
  shouldReply: boolean;
120
129
  suggestedText?: string;
121
130
  };
131
+ /**
132
+ * Record a delivery receipt (status 3 = DELIVERY_ACK, status 4 = READ).
133
+ * Call from messages.update handler when delivery status is received.
134
+ */
135
+ onDeliveryReceipt(msgId: string): void;
122
136
  /**
123
137
  * Get the resolved configuration
124
138
  */
@@ -145,6 +159,8 @@ export declare class AntiBan {
145
159
  get jidCanonicalizer(): JidCanonicalizer | null;
146
160
  /** Get the session stability monitor for direct access */
147
161
  get sessionStability(): SessionHealthMonitor | null;
162
+ /** Get the ban recovery orchestrator for direct access */
163
+ get recoveryOrchestrator(): BanRecoveryOrchestrator;
148
164
  /**
149
165
  * Export warm-up state for persistence between restarts
150
166
  */
@@ -161,6 +177,7 @@ export declare class AntiBan {
161
177
  * Reset everything (use after a ban period)
162
178
  */
163
179
  reset(): void;
180
+ private runAdaptiveCheck;
164
181
  private persistStateDebounced;
165
182
  private persistStateImmediate;
166
183
  /**
package/dist/antiban.js CHANGED
@@ -25,9 +25,12 @@ import { PostReconnectThrottle } from './reconnectThrottle.js';
25
25
  import { LidResolver } from './lidResolver.js';
26
26
  import { JidCanonicalizer } from './jidCanonicalizer.js';
27
27
  import { SessionHealthMonitor } from './sessionStability.js';
28
+ import { BanRecoveryOrchestrator } from './banRecoveryOrchestrator.js';
28
29
  import { resolveConfig } from './presets.js';
29
30
  import { StateManager } from './persist.js';
30
31
  import { shouldUseGroupProfile, applyGroupMultiplier } from './profiles.js';
32
+ import { DeliveryTracker } from './deliveryTracker.js';
33
+ import { InstanceCoordinator } from './instanceCoordinator.js';
31
34
  function isLegacyConfig(cfg) {
32
35
  if (typeof cfg !== 'object' || cfg === null)
33
36
  return false;
@@ -103,6 +106,9 @@ export class AntiBan {
103
106
  lidResolverModule = null;
104
107
  jidCanonicalizerModule = null;
105
108
  sessionStabilityMonitor = null;
109
+ banRecovery;
110
+ deliveryTracker;
111
+ instanceCoordinator = null;
106
112
  stateManager = null;
107
113
  resolvedConfig;
108
114
  logging;
@@ -173,14 +179,40 @@ export class AntiBan {
173
179
  if ((status.risk === 'high' || status.risk === 'critical') && cfg.onAtRisk) {
174
180
  cfg.onAtRisk(status);
175
181
  }
182
+ // Trigger recovery orchestrator on critical risk
183
+ if (status.risk === 'critical') {
184
+ this.banRecovery.recordBanEvent('soft_ban');
185
+ }
176
186
  cfg.onRiskChange?.(status);
177
187
  legacyPassthrough?.health?.onRiskChange?.(status);
178
188
  },
179
189
  });
190
+ // Initialize ban recovery orchestrator
191
+ this.banRecovery = new BanRecoveryOrchestrator({
192
+ onPhaseChange: (phase, plan) => {
193
+ if (this.logging) {
194
+ console.log(`[baileys-antiban] 🔄 Recovery phase: ${phase} — ${plan.description}`);
195
+ }
196
+ },
197
+ onHardBan: () => {
198
+ if (this.logging) {
199
+ console.log(`[baileys-antiban] 💀 HARD BAN detected — account likely dead, replace SIM`);
200
+ }
201
+ },
202
+ });
203
+ // Initialize delivery tracker
204
+ this.deliveryTracker = new DeliveryTracker({
205
+ onLowDeliveryRate: (rate) => {
206
+ if (this.logging) {
207
+ console.log(`[baileys-antiban] ⚠️ Low delivery rate detected: ${Math.round(rate * 100)}% — possible soft ban`);
208
+ }
209
+ },
210
+ });
180
211
  this.timelockGuard = new TimelockGuard({
181
212
  ...(legacyPassthrough?.timelock || {}),
182
213
  onTimelockDetected: (state) => {
183
214
  this.health.recordReachoutTimelock(state.enforcementType);
215
+ this.banRecovery.recordBanEvent('timelock');
184
216
  if (this.logging) {
185
217
  console.log(`[baileys-antiban] REACHOUT TIMELOCKED — ${state.enforcementType || 'unknown'}, expires ${state.expiresAt?.toISOString() || 'unknown'}`);
186
218
  }
@@ -254,6 +286,17 @@ export class AntiBan {
254
286
  };
255
287
  this.sessionStabilityMonitor = new SessionHealthMonitor(healthConfig);
256
288
  }
289
+ // Initialize instance coordinator if configured
290
+ if (cfg.instanceCoordinator) {
291
+ this.instanceCoordinator = new InstanceCoordinator({
292
+ sharedFilePath: cfg.instanceCoordinator,
293
+ poolMaxPerMinute: cfg.instancePoolMaxPerMinute,
294
+ poolMaxPerHour: cfg.instancePoolMaxPerHour,
295
+ });
296
+ if (this.logging) {
297
+ console.log(`[baileys-antiban] 🌐 Instance coordination enabled: ${cfg.instanceCoordinator}`);
298
+ }
299
+ }
257
300
  }
258
301
  /**
259
302
  * Check if a message can be sent and get required delay.
@@ -274,6 +317,17 @@ export class AntiBan {
274
317
  health: healthStatus,
275
318
  };
276
319
  }
320
+ // Recovery orchestrator rate multiplier
321
+ const recoveryStatus = this.banRecovery.getStatus();
322
+ if (recoveryStatus.phase === 'paused') {
323
+ this.stats.messagesBlocked++;
324
+ return {
325
+ allowed: false,
326
+ delayMs: recoveryStatus.pauseRemainingMs || 0,
327
+ reason: `Ban recovery: ${recoveryStatus.recommendation}`,
328
+ health: healthStatus,
329
+ };
330
+ }
277
331
  // Timelock guard (allows existing chats, blocks new contacts)
278
332
  const timelockDecision = this.timelockGuard.canSend(recipient);
279
333
  if (!timelockDecision.allowed) {
@@ -345,6 +399,22 @@ export class AntiBan {
345
399
  health: healthStatus,
346
400
  };
347
401
  }
402
+ // Cross-instance coordination — check shared IP-level pool
403
+ if (this.instanceCoordinator) {
404
+ const slot = this.instanceCoordinator.tryAcquireSlot();
405
+ if (!slot.allowed) {
406
+ this.stats.messagesBlocked++;
407
+ if (this.logging) {
408
+ console.log(`[baileys-antiban] 🌐 BLOCKED — instance pool exhausted (shared IP limit), retry in ${slot.retryAfterMs}ms`);
409
+ }
410
+ return {
411
+ allowed: false,
412
+ delayMs: slot.retryAfterMs || 5000,
413
+ reason: 'Cross-instance rate pool exhausted',
414
+ health: healthStatus,
415
+ };
416
+ }
417
+ }
348
418
  // Group profile rate check (runs before rateLimiter.getDelay for timing)
349
419
  if (this.resolvedConfig.groupProfiles && shouldUseGroupProfile(recipient)) {
350
420
  const groupLimits = applyGroupMultiplier({
@@ -384,6 +454,24 @@ export class AntiBan {
384
454
  const multiplier = Math.min(5, 1 / activityFactor);
385
455
  delay = Math.floor(delay * multiplier);
386
456
  }
457
+ // Per-contact risk multiplier — cold contacts need longer delays
458
+ // Only apply when contact graph is enabled, otherwise all contacts appear as 'stranger'
459
+ if (this.contactGraphWarmer['config']?.enabled) {
460
+ const contactState = this.contactGraphWarmer.getContactState(recipient);
461
+ const coldMultiplier = {
462
+ stranger: 2.5,
463
+ handshake_sent: 1.8,
464
+ handshake_complete: 1.3,
465
+ known: 1.0,
466
+ };
467
+ const contactRiskMult = coldMultiplier[contactState] ?? 1.0;
468
+ if (contactRiskMult > 1.0) {
469
+ delay = Math.floor(delay * contactRiskMult);
470
+ if (this.logging && contactRiskMult >= 2.0) {
471
+ console.log(`[baileys-antiban] ⚠️ Cold contact ${recipient} — ${contactState}, delay ×${contactRiskMult}`);
472
+ }
473
+ }
474
+ }
387
475
  // Roll for distraction pause
388
476
  const distractionCheck = this.presenceChoreographer.shouldPauseForDistraction();
389
477
  if (distractionCheck.pause) {
@@ -411,11 +499,15 @@ export class AntiBan {
411
499
  * Record a successfully sent message.
412
500
  * Call this AFTER every successful sendMessage().
413
501
  */
414
- afterSend(recipient, content) {
502
+ afterSend(recipient, content, msgId) {
415
503
  this.rateLimiter.record(recipient, content);
416
504
  this.warmUp.record();
417
505
  this.replyRatioGuard.recordSent(recipient);
418
506
  this.stats.messagesAllowed++;
507
+ if (msgId) {
508
+ this.deliveryTracker.onMessageSent(msgId);
509
+ }
510
+ this.runAdaptiveCheck();
419
511
  this.persistStateDebounced();
420
512
  }
421
513
  /**
@@ -441,6 +533,7 @@ export class AntiBan {
441
533
  onReconnect() {
442
534
  this.health.recordReconnect();
443
535
  this.reconnectThrottleModule.onReconnect();
536
+ this.rateLimiter.adaptLimits(1.0);
444
537
  }
445
538
  /**
446
539
  * Handle incoming message — record in reply ratio + contact graph.
@@ -451,6 +544,13 @@ export class AntiBan {
451
544
  this.contactGraphWarmer.onIncomingMessage(jid);
452
545
  return this.replyRatioGuard.suggestReply(jid, msgText);
453
546
  }
547
+ /**
548
+ * Record a delivery receipt (status 3 = DELIVERY_ACK, status 4 = READ).
549
+ * Call from messages.update handler when delivery status is received.
550
+ */
551
+ onDeliveryReceipt(msgId) {
552
+ this.deliveryTracker.onDeliveryReceipt(msgId);
553
+ }
454
554
  /**
455
555
  * Get the resolved configuration
456
556
  */
@@ -466,6 +566,8 @@ export class AntiBan {
466
566
  health: this.health.getStatus(),
467
567
  warmUp: this.warmUp.getStatus(),
468
568
  rateLimiter: this.rateLimiter.getStats(),
569
+ banRecovery: this.banRecovery.getStatus(),
570
+ deliveryTracker: this.deliveryTracker.getStats(),
469
571
  };
470
572
  // Only include new stats if enabled
471
573
  if (this.replyRatioGuard['config']?.enabled) {
@@ -492,6 +594,9 @@ export class AntiBan {
492
594
  if (this.sessionStabilityMonitor) {
493
595
  stats.sessionStability = this.sessionStabilityMonitor.getStats();
494
596
  }
597
+ if (this.instanceCoordinator) {
598
+ stats.instanceCoordinator = this.instanceCoordinator.getStats();
599
+ }
495
600
  return stats;
496
601
  }
497
602
  /** Get the timelock guard for direct access */
@@ -530,6 +635,10 @@ export class AntiBan {
530
635
  get sessionStability() {
531
636
  return this.sessionStabilityMonitor;
532
637
  }
638
+ /** Get the ban recovery orchestrator for direct access */
639
+ get recoveryOrchestrator() {
640
+ return this.banRecovery;
641
+ }
533
642
  /**
534
643
  * Export warm-up state for persistence between restarts
535
644
  */
@@ -571,6 +680,35 @@ export class AntiBan {
571
680
  console.log('[baileys-antiban] 🔄 Reset — starting fresh warm-up');
572
681
  }
573
682
  }
683
+ runAdaptiveCheck() {
684
+ const delivery = this.deliveryTracker.getStats();
685
+ // Need min sample to be meaningful
686
+ if (delivery.deliveryRate === null)
687
+ return;
688
+ const rate = delivery.deliveryRate;
689
+ let targetFactor;
690
+ if (rate >= 0.85) {
691
+ targetFactor = 1.0; // Excellent — full speed
692
+ }
693
+ else if (rate >= 0.70) {
694
+ targetFactor = 0.75; // Good — slight reduction
695
+ }
696
+ else if (rate >= 0.55) {
697
+ targetFactor = 0.50; // Poor — halve throughput
698
+ }
699
+ else {
700
+ targetFactor = 0.25; // Bad — severe throttle (soft ban likely)
701
+ }
702
+ const current = this.rateLimiter.getCurrentFactor();
703
+ // Only log + adjust when factor changes meaningfully (>5% delta)
704
+ if (Math.abs(targetFactor - current) > 0.05) {
705
+ if (this.logging) {
706
+ const dir = targetFactor > current ? '📈' : '📉';
707
+ console.log(`[baileys-antiban] ${dir} Adaptive rate: delivery=${(rate * 100).toFixed(0)}% → factor ${current.toFixed(2)}→${targetFactor.toFixed(2)}`);
708
+ }
709
+ this.rateLimiter.adaptLimits(targetFactor);
710
+ }
711
+ }
574
712
  persistStateDebounced() {
575
713
  if (!this.stateManager)
576
714
  return;
@@ -0,0 +1,87 @@
1
+ /**
2
+ * BanRecoveryOrchestrator — Structured recovery after ban/restriction events
3
+ *
4
+ * When WhatsApp restricts your account, the worst thing to do is immediately
5
+ * resume normal activity. This module provides a evidence-based recovery protocol:
6
+ *
7
+ * - Timelocked (reachout_restricted): 24h pause, resume at 10% rate, ramp 15%/week
8
+ * - Rate-overlimit (429): 4h pause, resume at 25% rate, ramp 25%/week
9
+ * - Soft-ban (repeated disconnects): 48h pause, resume at 5% rate, ramp 10%/week
10
+ * - Hard-ban (loggedOut): account is dead, signal for replacement
11
+ *
12
+ * Based on observed recovery times from community reports. Not guaranteed —
13
+ * WA's enforcement is non-deterministic. Treat as best-effort guidance.
14
+ */
15
+ export type BanEventType = 'timelock' | 'rate_overlimit' | 'soft_ban' | 'hard_ban';
16
+ export type RecoveryPhase = 'paused' | 'recovering' | 'ramping' | 'graduated' | 'dead';
17
+ export interface RecoveryPlan {
18
+ eventType: BanEventType;
19
+ pauseDurationMs: number;
20
+ resumeRateMultiplier: number;
21
+ weeklyRampPercent: number;
22
+ estimatedRecoveryDays: number;
23
+ description: string;
24
+ }
25
+ export interface BanRecoveryConfig {
26
+ /** Custom recovery plans per event type (overrides defaults) */
27
+ plans?: Partial<Record<BanEventType, Partial<RecoveryPlan>>>;
28
+ /** Called when recovery phase changes */
29
+ onPhaseChange?: (phase: RecoveryPhase, plan: RecoveryPlan) => void;
30
+ /** Called when account appears dead (hard ban) — signal to replace SIM */
31
+ onHardBan?: () => void;
32
+ /** Max weeks before giving up on recovery and declaring dead (default: 8) */
33
+ maxRecoveryWeeks?: number;
34
+ }
35
+ export interface RecoveryState {
36
+ active: boolean;
37
+ eventType?: BanEventType;
38
+ phase: RecoveryPhase;
39
+ banDetectedAt?: number;
40
+ pauseUntil?: number;
41
+ currentRateMultiplier: number;
42
+ weeksSinceResume: number;
43
+ banCount30d: number;
44
+ lastBanAt?: number;
45
+ }
46
+ export interface RecoveryStatus {
47
+ phase: RecoveryPhase;
48
+ rateMultiplier: number;
49
+ pauseRemainingMs?: number;
50
+ estimatedFullRecoveryDate?: number;
51
+ recommendation: string;
52
+ shouldReplaceNumber: boolean;
53
+ }
54
+ export declare class BanRecoveryOrchestrator {
55
+ private config;
56
+ private state;
57
+ private plans;
58
+ constructor(config?: BanRecoveryConfig);
59
+ /**
60
+ * Record a ban event and start recovery protocol
61
+ */
62
+ recordBanEvent(eventType: BanEventType): RecoveryStatus;
63
+ /**
64
+ * Get current recovery status (call before sending to check rate)
65
+ */
66
+ getStatus(): RecoveryStatus;
67
+ /**
68
+ * Current rate multiplier — multiply your normal limits by this
69
+ */
70
+ getRateMultiplier(): number;
71
+ /**
72
+ * Should be called daily/weekly to advance the ramp
73
+ */
74
+ tick(): void;
75
+ /**
76
+ * Classify a raw error into a BanEventType
77
+ */
78
+ static classifyError(err: unknown): BanEventType | null;
79
+ /**
80
+ * Serializable state for persistence
81
+ */
82
+ getState(): RecoveryState;
83
+ /**
84
+ * Restore from persisted state
85
+ */
86
+ static fromState(state: RecoveryState, config?: BanRecoveryConfig): BanRecoveryOrchestrator;
87
+ }