baileys-antiban 1.5.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +55 -0
- package/README.md +190 -1
- package/dist/antiban.d.ts +29 -0
- package/dist/antiban.js +73 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +5 -0
- package/dist/jidCanonicalizer.d.ts +78 -0
- package/dist/jidCanonicalizer.js +172 -0
- package/dist/lidResolver.d.ts +96 -0
- package/dist/lidResolver.js +292 -0
- package/dist/sessionStability.d.ts +93 -0
- package/dist/sessionStability.js +222 -0
- package/dist/wrapper.js +30 -10
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,61 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [2.0.0] - 2026-04-19
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- **Session Stability Module** — New middleware layer for Baileys socket stability (opt-in, backward compatible)
|
|
12
|
+
- `wrapWithSessionStability()` — Proxy wrapper for Baileys socket with stability features
|
|
13
|
+
- `SessionHealthMonitor` — Track decrypt success/fail ratio, emit degradation alerts when Bad MAC rate exceeds threshold
|
|
14
|
+
- `classifyDisconnect()` — Typed disconnect reason classification with recovery recommendations
|
|
15
|
+
- Canonical JID normalization before `sendMessage()` — Auto-resolves PN↔LID using `LidResolver` to reduce mutex race triggers
|
|
16
|
+
- Comprehensive disconnect code coverage: 401, 408, 428, 429, 440, 500, 503, 515, 1000, unknown
|
|
17
|
+
- Degradation detection: triggers `onDegraded` callback when Bad MAC count exceeds threshold in time window (default: 3 in 60s)
|
|
18
|
+
- Recovery detection: triggers `onRecovered` callback when Bad MAC rate drops below threshold
|
|
19
|
+
- 19 new tests with 100% coverage of disconnect classification and health monitoring
|
|
20
|
+
|
|
21
|
+
### Changed
|
|
22
|
+
- `AntiBan` class extended with optional `sessionStability` config (default: disabled)
|
|
23
|
+
- `AntiBanConfig` interface includes `sessionStability` options (enabled, canonicalJidNormalization, healthMonitoring, badMacThreshold, badMacWindowMs)
|
|
24
|
+
- `AntiBanStats` includes `sessionStability` stats when enabled
|
|
25
|
+
- `destroy()` now cleans up session stability monitor
|
|
26
|
+
- Exposed `sessionStability` getter for direct access to health monitor
|
|
27
|
+
|
|
28
|
+
### Technical Details
|
|
29
|
+
- Pure middleware layer — no Baileys internals modification required
|
|
30
|
+
- Works alongside existing v1.x LID resolver and canonicalizer modules
|
|
31
|
+
- Default configuration: disabled for backward compatibility, opt-in via `sessionStability: { enabled: true }`
|
|
32
|
+
- Health monitor uses sliding window for Bad MAC detection (default: 3 errors in 60 seconds)
|
|
33
|
+
- Socket wrapper uses ES6 Proxy for transparent method interception
|
|
34
|
+
- TypeScript strict mode compliant, no `any` types except socket wrapper generic
|
|
35
|
+
|
|
36
|
+
### Breaking Changes
|
|
37
|
+
None — all v2.0 features are opt-in and backward compatible with v1.x
|
|
38
|
+
|
|
39
|
+
## [1.6.0] - 2026-04-18
|
|
40
|
+
|
|
41
|
+
### Added
|
|
42
|
+
- **LID/PN Race Condition Mitigation** — New modules to address the #1 reported Baileys bug: "Bad MAC / No Session / Invalid PreKey" errors caused by WhatsApp's Linked Identity (LID) migration
|
|
43
|
+
- `LidResolver` — Standalone utility for maintaining bidirectional LID↔PN mappings learned from message events
|
|
44
|
+
- `JidCanonicalizer` — Opt-in middleware that auto-learns from incoming events and canonicalizes outbound send targets to a single form (phone number by default)
|
|
45
|
+
- Both modules default to **disabled** — backward compatible, zero behavior change for existing users
|
|
46
|
+
- Middleware-layer mitigation only — root fix still requires [PR #2372](https://github.com/WhiskeySockets/Baileys/pull/2372) merged upstream
|
|
47
|
+
- Comprehensive test coverage: 56 new tests (29 LidResolver + 18 JidCanonicalizer + 9 integration)
|
|
48
|
+
|
|
49
|
+
### Changed
|
|
50
|
+
- `AntiBan` class now exposes `lidResolver` and `jidCanonicalizer` getters for direct access
|
|
51
|
+
- `AntiBanConfig` extended with `lidResolver` and `jidCanonicalizer` config options
|
|
52
|
+
- `AntiBanStats` includes `lidResolver` and `jidCanonicalizer` stats when enabled
|
|
53
|
+
- Wrapper's `sendMessage` now canonicalizes JID before all rate-limit/timelock/graph checks
|
|
54
|
+
- `messages.upsert` and `messages.update` handlers now auto-learn LID mappings when canonicalizer enabled
|
|
55
|
+
|
|
56
|
+
### Technical Details
|
|
57
|
+
- LRU eviction at configurable `maxEntries` (default 10,000)
|
|
58
|
+
- Optional persistence hooks for cross-restart state survival
|
|
59
|
+
- Device suffix stripping (`:N` in JIDs) for robust matching
|
|
60
|
+
- Supports both `canonical: 'pn'` (phone number) and `canonical: 'lid'` modes
|
|
61
|
+
- Shared resolver mode allows multiple canonicalizers to reference same mapping state
|
|
62
|
+
|
|
8
63
|
## [1.5.0] - 2026-04-18
|
|
9
64
|
|
|
10
65
|
### Added
|
package/README.md
CHANGED
|
@@ -6,7 +6,144 @@
|
|
|
6
6
|
|
|
7
7
|
**Transport-agnostic** anti-ban middleware — protect your WhatsApp number with human-like messaging patterns. Works with both [Baileys](https://github.com/WhiskeySockets/Baileys) and [@oxidezap/baileyrs](https://github.com/oxidezap/baileyrs) (Rust/WASM).
|
|
8
8
|
|
|
9
|
-
##
|
|
9
|
+
## v2.0 New Features — Session Stability Module
|
|
10
|
+
|
|
11
|
+
### What's New in v2.0
|
|
12
|
+
|
|
13
|
+
Three powerful new features to improve session stability and reduce "Bad MAC" errors:
|
|
14
|
+
|
|
15
|
+
1. **Typed Disconnect Reason Classification** — Know exactly why you disconnected and how to recover
|
|
16
|
+
2. **Session Health Monitor** — Detect session degradation before it causes bans
|
|
17
|
+
3. **Socket Wrapper with JID Canonicalization** — Middleware-layer fix for LID/PN race conditions
|
|
18
|
+
|
|
19
|
+
All v2.0 features are **opt-in** and **100% backward compatible** with v1.x.
|
|
20
|
+
|
|
21
|
+
### 1. Typed Disconnect Reason Classification
|
|
22
|
+
|
|
23
|
+
```typescript
|
|
24
|
+
import { classifyDisconnect } from 'baileys-antiban';
|
|
25
|
+
|
|
26
|
+
sock.ev.on('connection.update', ({ connection, lastDisconnect }) => {
|
|
27
|
+
if (connection === 'close' && lastDisconnect?.error) {
|
|
28
|
+
const statusCode = lastDisconnect.error.output?.statusCode;
|
|
29
|
+
const classification = classifyDisconnect(statusCode);
|
|
30
|
+
|
|
31
|
+
console.log(`Disconnected: ${classification.message}`);
|
|
32
|
+
console.log(`Category: ${classification.category}`); // fatal | recoverable | rate-limited | unknown
|
|
33
|
+
console.log(`Should reconnect: ${classification.shouldReconnect}`);
|
|
34
|
+
|
|
35
|
+
if (classification.shouldReconnect && classification.backoffMs) {
|
|
36
|
+
console.log(`Recommended backoff: ${classification.backoffMs}ms`);
|
|
37
|
+
setTimeout(() => connectToWhatsApp(), classification.backoffMs);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
**Supported disconnect codes**: 401 (logged out), 408 (timeout), 428 (connection replaced), 429 (rate limited), 440 (logged out), 500 (internal error), 503 (unavailable), 515 (restart required), 1000 (graceful close), and unknown codes.
|
|
44
|
+
|
|
45
|
+
### 2. Session Health Monitor
|
|
46
|
+
|
|
47
|
+
Track decrypt success/failure ratio to detect session degradation **before** it causes a ban:
|
|
48
|
+
|
|
49
|
+
```typescript
|
|
50
|
+
import { SessionHealthMonitor } from 'baileys-antiban';
|
|
51
|
+
|
|
52
|
+
const healthMonitor = new SessionHealthMonitor({
|
|
53
|
+
badMacThreshold: 3, // Alert after 3 Bad MACs
|
|
54
|
+
badMacWindowMs: 60_000, // ...in 60 seconds
|
|
55
|
+
onDegraded: (stats) => {
|
|
56
|
+
console.error(`🔴 SESSION DEGRADED: ${stats.badMacCount} Bad MACs in last minute`);
|
|
57
|
+
console.error('Action required: Restart session or switch to LID-based canonical form');
|
|
58
|
+
},
|
|
59
|
+
onRecovered: (stats) => {
|
|
60
|
+
console.log('🟢 Session recovered — decrypt success rate improved');
|
|
61
|
+
},
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
// Wire to Baileys events
|
|
65
|
+
sock.ev.on('messages.update', (updates) => {
|
|
66
|
+
for (const { key, update } of updates) {
|
|
67
|
+
if (update.messageStubType === Types.WAMessageStubType.CIPHERTEXT) {
|
|
68
|
+
healthMonitor.recordDecryptFail(true); // Bad MAC detected
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
// Check status anytime
|
|
74
|
+
const stats = healthMonitor.getStats();
|
|
75
|
+
console.log(`Decrypt success: ${stats.decryptSuccess}`);
|
|
76
|
+
console.log(`Bad MAC count: ${stats.badMacCount}`);
|
|
77
|
+
console.log(`Is degraded: ${stats.isDegraded}`);
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### 3. Socket Wrapper with JID Canonicalization
|
|
81
|
+
|
|
82
|
+
The easiest way to use v2.0: wrap your socket for automatic JID canonicalization and health monitoring:
|
|
83
|
+
|
|
84
|
+
```typescript
|
|
85
|
+
import { wrapWithSessionStability, LidResolver } from 'baileys-antiban';
|
|
86
|
+
|
|
87
|
+
const resolver = new LidResolver({ canonical: 'pn' });
|
|
88
|
+
const sock = makeWASocket({ ... });
|
|
89
|
+
|
|
90
|
+
const safeSock = wrapWithSessionStability(sock, {
|
|
91
|
+
canonicalJidNormalization: true, // Auto-canonicalize JIDs before sendMessage
|
|
92
|
+
healthMonitoring: true, // Auto-track decrypt health
|
|
93
|
+
lidResolver: resolver,
|
|
94
|
+
health: {
|
|
95
|
+
badMacThreshold: 3,
|
|
96
|
+
badMacWindowMs: 60_000,
|
|
97
|
+
onDegraded: (stats) => console.error('Session degraded!'),
|
|
98
|
+
},
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
// Use safeSock exactly like normal sock
|
|
102
|
+
await safeSock.sendMessage('123456@lid', { text: 'hello' });
|
|
103
|
+
// ^ Automatically canonicalized to '27825651069@s.whatsapp.net' if mapping exists
|
|
104
|
+
|
|
105
|
+
// Access health stats
|
|
106
|
+
const healthStats = safeSock.sessionHealthStats;
|
|
107
|
+
console.log(`Bad MAC count: ${healthStats.badMacCount}`);
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### Integration with AntiBan Class
|
|
111
|
+
|
|
112
|
+
You can also enable session stability via the main `AntiBan` config:
|
|
113
|
+
|
|
114
|
+
```typescript
|
|
115
|
+
import { AntiBan } from 'baileys-antiban';
|
|
116
|
+
|
|
117
|
+
const antiban = new AntiBan({
|
|
118
|
+
sessionStability: {
|
|
119
|
+
enabled: true,
|
|
120
|
+
canonicalJidNormalization: true, // Auto-canonicalize JIDs
|
|
121
|
+
healthMonitoring: true, // Track Bad MAC rate
|
|
122
|
+
badMacThreshold: 3,
|
|
123
|
+
badMacWindowMs: 60_000,
|
|
124
|
+
},
|
|
125
|
+
jidCanonicalizer: {
|
|
126
|
+
enabled: true,
|
|
127
|
+
canonical: 'pn',
|
|
128
|
+
},
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
// Access health monitor directly
|
|
132
|
+
const healthMonitor = antiban.sessionStability;
|
|
133
|
+
if (healthMonitor) {
|
|
134
|
+
console.log(healthMonitor.getStats());
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Stats include session stability
|
|
138
|
+
const stats = antiban.getStats();
|
|
139
|
+
console.log(stats.sessionStability); // Health stats when enabled
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
**Why v2.0?** Bad MAC errors are the #1 reported Baileys issue. Session stability features give you early warning and automated mitigation, reducing bans caused by session degradation.
|
|
143
|
+
|
|
144
|
+
---
|
|
145
|
+
|
|
146
|
+
## v1.5 Features
|
|
10
147
|
|
|
11
148
|
### RetryReasonTracker
|
|
12
149
|
Tracks message retry reasons and detects retry spirals (when the same message keeps failing). Inspired by whatsapp-rust's protocol/retry.rs module.
|
|
@@ -61,6 +198,58 @@ console.log(stats.throttledSendCount); // Sends gated since reconnect
|
|
|
61
198
|
|
|
62
199
|
**Why?** When WhatsApp reconnects after a disconnection, sending messages at full rate immediately can trigger rate limit alarms. The reconnect throttle gradually ramps up sending rate over 60 seconds, mimicking how a human would resume messaging after their internet came back.
|
|
63
200
|
|
|
201
|
+
## LID / Phone Number Canonicalization
|
|
202
|
+
|
|
203
|
+
WhatsApp migrated to **Linked Identity (LID)** in 2024. A contact now has two JID forms:
|
|
204
|
+
- Phone number: `27825651069@s.whatsapp.net`
|
|
205
|
+
- LID: `123456789@lid`
|
|
206
|
+
|
|
207
|
+
Messages can arrive under either form. If an encryption session was established under one form and a message arrives under the other, decryption fails → **"Bad MAC / No Session / Invalid PreKey"** errors (the #1 reported Baileys bug).
|
|
208
|
+
|
|
209
|
+
baileys-antiban v1.6+ provides **middleware-layer mitigation** via two new modules:
|
|
210
|
+
|
|
211
|
+
```typescript
|
|
212
|
+
import { wrapSocket } from 'baileys-antiban';
|
|
213
|
+
|
|
214
|
+
const sock = makeWASocket({ ... });
|
|
215
|
+
const safeSock = wrapSocket(sock, {
|
|
216
|
+
jidCanonicalizer: {
|
|
217
|
+
enabled: true, // Enable LID/PN canonicalization
|
|
218
|
+
canonical: 'pn', // Normalize to phone-number form (default)
|
|
219
|
+
},
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
// That's it! Incoming events auto-learn LID↔PN mappings.
|
|
223
|
+
// Outbound sends are auto-canonicalized to phone-number form.
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
**Advanced: Standalone Resolver**
|
|
227
|
+
|
|
228
|
+
```typescript
|
|
229
|
+
import { LidResolver } from 'baileys-antiban';
|
|
230
|
+
|
|
231
|
+
const resolver = new LidResolver({
|
|
232
|
+
canonical: 'pn',
|
|
233
|
+
maxEntries: 10_000, // LRU cache size
|
|
234
|
+
persistence: {
|
|
235
|
+
load: async () => JSON.parse(await fs.readFile('lid-map.json', 'utf8')),
|
|
236
|
+
save: async (map) => fs.writeFile('lid-map.json', JSON.stringify(map)),
|
|
237
|
+
},
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
// Learn from message events
|
|
241
|
+
resolver.learn({
|
|
242
|
+
lid: '123456789@lid',
|
|
243
|
+
pn: '27825651069@s.whatsapp.net',
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
// Resolve canonical form
|
|
247
|
+
const canonical = resolver.resolveCanonical('123456789@lid');
|
|
248
|
+
// → '27825651069@s.whatsapp.net'
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
**Note:** This is a middleware-layer workaround. The root fix lives inside Baileys' crypto pipeline ([PR #2372](https://github.com/WhiskeySockets/Baileys/pull/2372)).
|
|
252
|
+
|
|
64
253
|
## v1.3 Features
|
|
65
254
|
|
|
66
255
|
### ReplyRatioGuard
|
package/dist/antiban.d.ts
CHANGED
|
@@ -22,6 +22,9 @@ import { ContactGraphWarmer, type ContactGraphConfig, type ContactGraphStats } f
|
|
|
22
22
|
import { PresenceChoreographer, type PresenceChoreographerConfig, type PresenceChoreographerStats } from './presenceChoreographer.js';
|
|
23
23
|
import { RetryReasonTracker, type RetryTrackerConfig, type RetryStats } from './retryTracker.js';
|
|
24
24
|
import { PostReconnectThrottle, type ReconnectThrottleConfig, type ReconnectThrottleStats } from './reconnectThrottle.js';
|
|
25
|
+
import { LidResolver, type LidResolverConfig, type LidResolverStats } from './lidResolver.js';
|
|
26
|
+
import { JidCanonicalizer, type JidCanonicalizerConfig, type JidCanonicalizerStats } from './jidCanonicalizer.js';
|
|
27
|
+
import { SessionHealthMonitor, type SessionHealthStats } from './sessionStability.js';
|
|
25
28
|
export interface AntiBanConfig {
|
|
26
29
|
rateLimiter?: Partial<RateLimiterConfig>;
|
|
27
30
|
warmUp?: Partial<WarmUpConfig>;
|
|
@@ -32,6 +35,20 @@ export interface AntiBanConfig {
|
|
|
32
35
|
presence?: Partial<PresenceChoreographerConfig>;
|
|
33
36
|
retryTracker?: Partial<RetryTrackerConfig>;
|
|
34
37
|
reconnectThrottle?: Partial<ReconnectThrottleConfig>;
|
|
38
|
+
lidResolver?: LidResolverConfig;
|
|
39
|
+
jidCanonicalizer?: JidCanonicalizerConfig;
|
|
40
|
+
/** Session stability features (v2.0) — default disabled for backward compatibility */
|
|
41
|
+
sessionStability?: {
|
|
42
|
+
enabled: boolean;
|
|
43
|
+
/** Enable canonical JID normalization before sendMessage (default: true if enabled) */
|
|
44
|
+
canonicalJidNormalization?: boolean;
|
|
45
|
+
/** Enable session health monitoring (default: true if enabled) */
|
|
46
|
+
healthMonitoring?: boolean;
|
|
47
|
+
/** Bad MAC threshold before declaring session degraded (default: 3) */
|
|
48
|
+
badMacThreshold?: number;
|
|
49
|
+
/** Time window for Bad MAC threshold in ms (default: 60000) */
|
|
50
|
+
badMacWindowMs?: number;
|
|
51
|
+
};
|
|
35
52
|
/** Log warnings and blocks to console (default: true) */
|
|
36
53
|
logging?: boolean;
|
|
37
54
|
}
|
|
@@ -54,6 +71,9 @@ export interface AntiBanStats {
|
|
|
54
71
|
presence?: PresenceChoreographerStats;
|
|
55
72
|
retryTracker?: RetryStats | null;
|
|
56
73
|
reconnectThrottle?: ReconnectThrottleStats | null;
|
|
74
|
+
lidResolver?: LidResolverStats | null;
|
|
75
|
+
jidCanonicalizer?: JidCanonicalizerStats | null;
|
|
76
|
+
sessionStability?: SessionHealthStats | null;
|
|
57
77
|
}
|
|
58
78
|
export declare class AntiBan {
|
|
59
79
|
private rateLimiter;
|
|
@@ -65,6 +85,9 @@ export declare class AntiBan {
|
|
|
65
85
|
private presenceChoreographer;
|
|
66
86
|
private retryTrackerModule;
|
|
67
87
|
private reconnectThrottleModule;
|
|
88
|
+
private lidResolverModule;
|
|
89
|
+
private jidCanonicalizerModule;
|
|
90
|
+
private sessionStabilityMonitor;
|
|
68
91
|
private logging;
|
|
69
92
|
private stats;
|
|
70
93
|
constructor(config?: AntiBanConfig, warmUpState?: WarmUpState);
|
|
@@ -114,6 +137,12 @@ export declare class AntiBan {
|
|
|
114
137
|
get retryTracker(): RetryReasonTracker;
|
|
115
138
|
/** Get the reconnect throttle for direct access */
|
|
116
139
|
get reconnectThrottle(): PostReconnectThrottle;
|
|
140
|
+
/** Get the LID resolver for direct access */
|
|
141
|
+
get lidResolver(): LidResolver | null;
|
|
142
|
+
/** Get the JID canonicalizer for direct access */
|
|
143
|
+
get jidCanonicalizer(): JidCanonicalizer | null;
|
|
144
|
+
/** Get the session stability monitor for direct access */
|
|
145
|
+
get sessionStability(): SessionHealthMonitor | null;
|
|
117
146
|
/**
|
|
118
147
|
* Export warm-up state for persistence between restarts
|
|
119
148
|
*/
|
package/dist/antiban.js
CHANGED
|
@@ -22,6 +22,9 @@ import { ContactGraphWarmer } from './contactGraph.js';
|
|
|
22
22
|
import { PresenceChoreographer } from './presenceChoreographer.js';
|
|
23
23
|
import { RetryReasonTracker } from './retryTracker.js';
|
|
24
24
|
import { PostReconnectThrottle } from './reconnectThrottle.js';
|
|
25
|
+
import { LidResolver } from './lidResolver.js';
|
|
26
|
+
import { JidCanonicalizer } from './jidCanonicalizer.js';
|
|
27
|
+
import { SessionHealthMonitor } from './sessionStability.js';
|
|
25
28
|
export class AntiBan {
|
|
26
29
|
rateLimiter;
|
|
27
30
|
warmUp;
|
|
@@ -32,6 +35,9 @@ export class AntiBan {
|
|
|
32
35
|
presenceChoreographer;
|
|
33
36
|
retryTrackerModule;
|
|
34
37
|
reconnectThrottleModule;
|
|
38
|
+
lidResolverModule = null;
|
|
39
|
+
jidCanonicalizerModule = null;
|
|
40
|
+
sessionStabilityMonitor = null;
|
|
35
41
|
logging;
|
|
36
42
|
stats = {
|
|
37
43
|
messagesAllowed: 0,
|
|
@@ -86,6 +92,49 @@ export class AntiBan {
|
|
|
86
92
|
...config.reconnectThrottle,
|
|
87
93
|
baselineRatePerMinute: () => this.rateLimiter.getStats().limits.perMinute,
|
|
88
94
|
});
|
|
95
|
+
// Initialize LID resolver and canonicalizer if configured
|
|
96
|
+
// If jidCanonicalizer is enabled but no resolver provided, create standalone resolver
|
|
97
|
+
if (config.jidCanonicalizer?.enabled) {
|
|
98
|
+
// Create or use provided resolver
|
|
99
|
+
if (config.jidCanonicalizer.resolver) {
|
|
100
|
+
// User provided their own resolver
|
|
101
|
+
this.jidCanonicalizerModule = new JidCanonicalizer(config.jidCanonicalizer);
|
|
102
|
+
this.lidResolverModule = config.jidCanonicalizer.resolver;
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
// Create new resolver using lidResolver config if provided
|
|
106
|
+
const resolverConfig = config.lidResolver || config.jidCanonicalizer.resolverConfig;
|
|
107
|
+
const resolver = new LidResolver(resolverConfig);
|
|
108
|
+
this.lidResolverModule = resolver;
|
|
109
|
+
this.jidCanonicalizerModule = new JidCanonicalizer({
|
|
110
|
+
...config.jidCanonicalizer,
|
|
111
|
+
resolver,
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
else if (config.lidResolver) {
|
|
116
|
+
// Standalone resolver without canonicalizer
|
|
117
|
+
this.lidResolverModule = new LidResolver(config.lidResolver);
|
|
118
|
+
}
|
|
119
|
+
// Initialize session stability monitor if enabled
|
|
120
|
+
if (config.sessionStability?.enabled) {
|
|
121
|
+
const healthConfig = {
|
|
122
|
+
badMacThreshold: config.sessionStability.badMacThreshold,
|
|
123
|
+
badMacWindowMs: config.sessionStability.badMacWindowMs,
|
|
124
|
+
onDegraded: (stats) => {
|
|
125
|
+
if (this.logging) {
|
|
126
|
+
console.log(`[baileys-antiban] 🔴 SESSION DEGRADED — Bad MAC rate: ${stats.badMacCount} in last ${config.sessionStability?.badMacWindowMs || 60000}ms`);
|
|
127
|
+
console.log(`[baileys-antiban] Consider restarting session or switching to LID-based canonical form`);
|
|
128
|
+
}
|
|
129
|
+
},
|
|
130
|
+
onRecovered: () => {
|
|
131
|
+
if (this.logging) {
|
|
132
|
+
console.log(`[baileys-antiban] 🟢 SESSION RECOVERED — decrypt success rate improved`);
|
|
133
|
+
}
|
|
134
|
+
},
|
|
135
|
+
};
|
|
136
|
+
this.sessionStabilityMonitor = new SessionHealthMonitor(healthConfig);
|
|
137
|
+
}
|
|
89
138
|
}
|
|
90
139
|
/**
|
|
91
140
|
* Check if a message can be sent and get required delay.
|
|
@@ -286,6 +335,15 @@ export class AntiBan {
|
|
|
286
335
|
if (this.reconnectThrottleModule['config']?.enabled) {
|
|
287
336
|
stats.reconnectThrottle = this.reconnectThrottleModule.getStats();
|
|
288
337
|
}
|
|
338
|
+
if (this.lidResolverModule) {
|
|
339
|
+
stats.lidResolver = this.lidResolverModule.getStats();
|
|
340
|
+
}
|
|
341
|
+
if (this.jidCanonicalizerModule) {
|
|
342
|
+
stats.jidCanonicalizer = this.jidCanonicalizerModule.getStats();
|
|
343
|
+
}
|
|
344
|
+
if (this.sessionStabilityMonitor) {
|
|
345
|
+
stats.sessionStability = this.sessionStabilityMonitor.getStats();
|
|
346
|
+
}
|
|
289
347
|
return stats;
|
|
290
348
|
}
|
|
291
349
|
/** Get the timelock guard for direct access */
|
|
@@ -312,6 +370,18 @@ export class AntiBan {
|
|
|
312
370
|
get reconnectThrottle() {
|
|
313
371
|
return this.reconnectThrottleModule;
|
|
314
372
|
}
|
|
373
|
+
/** Get the LID resolver for direct access */
|
|
374
|
+
get lidResolver() {
|
|
375
|
+
return this.lidResolverModule;
|
|
376
|
+
}
|
|
377
|
+
/** Get the JID canonicalizer for direct access */
|
|
378
|
+
get jidCanonicalizer() {
|
|
379
|
+
return this.jidCanonicalizerModule;
|
|
380
|
+
}
|
|
381
|
+
/** Get the session stability monitor for direct access */
|
|
382
|
+
get sessionStability() {
|
|
383
|
+
return this.sessionStabilityMonitor;
|
|
384
|
+
}
|
|
315
385
|
/**
|
|
316
386
|
* Export warm-up state for persistence between restarts
|
|
317
387
|
*/
|
|
@@ -364,6 +434,9 @@ export class AntiBan {
|
|
|
364
434
|
this.presenceChoreographer.reset();
|
|
365
435
|
this.retryTrackerModule.destroy();
|
|
366
436
|
this.reconnectThrottleModule.destroy();
|
|
437
|
+
this.jidCanonicalizerModule?.destroy();
|
|
438
|
+
this.lidResolverModule?.destroy();
|
|
439
|
+
this.sessionStabilityMonitor?.reset();
|
|
367
440
|
if (this.logging) {
|
|
368
441
|
console.log('[baileys-antiban] 🧹 Destroyed — all timers cleared');
|
|
369
442
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -17,6 +17,9 @@ export { ContactGraphWarmer, type ContactGraphConfig, type ContactGraphStats, ty
|
|
|
17
17
|
export { PresenceChoreographer, type PresenceChoreographerConfig, type PresenceChoreographerStats } from './presenceChoreographer.js';
|
|
18
18
|
export { RetryReasonTracker, type RetryTrackerConfig, type RetryStats, type RetryReason } from './retryTracker.js';
|
|
19
19
|
export { PostReconnectThrottle, type ReconnectThrottleConfig, type ReconnectThrottleStats } from './reconnectThrottle.js';
|
|
20
|
+
export { LidResolver, type LidResolverConfig, type LidResolverStats, type LidMapping } from './lidResolver.js';
|
|
21
|
+
export { JidCanonicalizer, type JidCanonicalizerConfig, type JidCanonicalizerStats } from './jidCanonicalizer.js';
|
|
22
|
+
export { SessionHealthMonitor, type SessionHealthStats, type SessionHealthConfig, wrapWithSessionStability, type SessionStabilityConfig, classifyDisconnect, type DisconnectClassification, type DisconnectCategory, } from './sessionStability.js';
|
|
20
23
|
export { wrapSocket, type WrappedSocket, type WrapSocketOptions } from './wrapper.js';
|
|
21
24
|
export { MessageQueue, type QueuedMessage, type MessageQueueConfig } from './messageQueue.js';
|
|
22
25
|
export { ContentVariator, type VariatorConfig } from './contentVariator.js';
|
package/dist/index.js
CHANGED
|
@@ -20,6 +20,11 @@ export { PresenceChoreographer } from './presenceChoreographer.js';
|
|
|
20
20
|
// v1.5 new modules
|
|
21
21
|
export { RetryReasonTracker } from './retryTracker.js';
|
|
22
22
|
export { PostReconnectThrottle } from './reconnectThrottle.js';
|
|
23
|
+
// v1.6 new modules
|
|
24
|
+
export { LidResolver } from './lidResolver.js';
|
|
25
|
+
export { JidCanonicalizer } from './jidCanonicalizer.js';
|
|
26
|
+
// v2.0 new modules
|
|
27
|
+
export { SessionHealthMonitor, wrapWithSessionStability, classifyDisconnect, } from './sessionStability.js';
|
|
23
28
|
// Socket wrapper
|
|
24
29
|
export { wrapSocket } from './wrapper.js';
|
|
25
30
|
// Optional features
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JID Canonicalizer — Opt-in middleware for LID/PN normalization
|
|
3
|
+
*
|
|
4
|
+
* Wraps LidResolver to provide automatic:
|
|
5
|
+
* 1. Learning from incoming message events
|
|
6
|
+
* 2. Canonicalization of outbound send targets
|
|
7
|
+
*
|
|
8
|
+
* This mitigates the LID/PN race condition that causes "Bad MAC / No Session /
|
|
9
|
+
* Invalid PreKey" errors (Baileys issue #1769, our PR #2372).
|
|
10
|
+
*
|
|
11
|
+
* Usage:
|
|
12
|
+
* const canonicalizer = new JidCanonicalizer({ enabled: true });
|
|
13
|
+
*
|
|
14
|
+
* // On incoming event
|
|
15
|
+
* canonicalizer.onIncomingEvent({ messages: [...] });
|
|
16
|
+
*
|
|
17
|
+
* // On outbound send
|
|
18
|
+
* const canonicalJid = canonicalizer.canonicalizeTarget(jid);
|
|
19
|
+
* await sock.sendMessage(canonicalJid, content);
|
|
20
|
+
*
|
|
21
|
+
* Note: This is a middleware-layer mitigation. The root fix requires merging
|
|
22
|
+
* PR #2372 into Baileys' crypto pipeline.
|
|
23
|
+
*/
|
|
24
|
+
import { LidResolver, type LidResolverConfig, type LidResolverStats } from './lidResolver.js';
|
|
25
|
+
export interface JidCanonicalizerConfig {
|
|
26
|
+
/** Enable canonicalization (default: false — opt-in) */
|
|
27
|
+
enabled?: boolean;
|
|
28
|
+
/** Provide your own resolver to share across modules. Otherwise one is created. */
|
|
29
|
+
resolver?: LidResolver;
|
|
30
|
+
/** Config for creating a new resolver (ignored if resolver provided) */
|
|
31
|
+
resolverConfig?: LidResolverConfig;
|
|
32
|
+
/** Canonicalize outbound sendMessage targets. Default true. */
|
|
33
|
+
canonicalizeOutbound?: boolean;
|
|
34
|
+
/** Learn from inbound events. Default true. */
|
|
35
|
+
learnFromEvents?: boolean;
|
|
36
|
+
}
|
|
37
|
+
export interface JidCanonicalizerStats {
|
|
38
|
+
resolver: LidResolverStats;
|
|
39
|
+
outboundCanonicalized: number;
|
|
40
|
+
outboundPassthrough: number;
|
|
41
|
+
inboundLearned: number;
|
|
42
|
+
}
|
|
43
|
+
export declare class JidCanonicalizer {
|
|
44
|
+
private config;
|
|
45
|
+
private lidResolver;
|
|
46
|
+
private ownsResolver;
|
|
47
|
+
private stats;
|
|
48
|
+
constructor(config?: JidCanonicalizerConfig);
|
|
49
|
+
/**
|
|
50
|
+
* Access the underlying resolver (for cross-module sharing)
|
|
51
|
+
*/
|
|
52
|
+
get resolver(): LidResolver;
|
|
53
|
+
/**
|
|
54
|
+
* Called by wrapper on every outbound send. Returns canonical JID.
|
|
55
|
+
*/
|
|
56
|
+
canonicalizeTarget(jid: string): string;
|
|
57
|
+
/**
|
|
58
|
+
* Called by wrapper on messages.upsert event. Learns mappings.
|
|
59
|
+
*/
|
|
60
|
+
onIncomingEvent(upsert: {
|
|
61
|
+
messages: Array<any>;
|
|
62
|
+
type?: string;
|
|
63
|
+
}): void;
|
|
64
|
+
/**
|
|
65
|
+
* Called by wrapper on messages.update event. Learns from sent-message refs.
|
|
66
|
+
*/
|
|
67
|
+
onMessageUpdate(updates: Array<any>): void;
|
|
68
|
+
getStats(): JidCanonicalizerStats;
|
|
69
|
+
destroy(): void;
|
|
70
|
+
/**
|
|
71
|
+
* Extract LID↔PN mappings from a message object
|
|
72
|
+
*/
|
|
73
|
+
private learnFromMessage;
|
|
74
|
+
/**
|
|
75
|
+
* Extract mappings from message.key
|
|
76
|
+
*/
|
|
77
|
+
private learnFromMessageKey;
|
|
78
|
+
}
|