@prototypevip/baileys 0.0.9 → 0.1.1

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 (50) hide show
  1. package/lib/Socket/index.js +12 -135
  2. package/lib/Socket/messages-send.js +27 -44
  3. package/package.json +2 -2
  4. package/lib/AntiBan/antiban.js +0 -922
  5. package/lib/AntiBan/banRecoveryOrchestrator.js +0 -284
  6. package/lib/AntiBan/cli.js +0 -177
  7. package/lib/AntiBan/contactGraph.js +0 -236
  8. package/lib/AntiBan/contentVariator.js +0 -150
  9. package/lib/AntiBan/credsSnapshot.js +0 -121
  10. package/lib/AntiBan/deliveryTracker.js +0 -107
  11. package/lib/AntiBan/deviceFingerprint.js +0 -106
  12. package/lib/AntiBan/fleetEventStore.js +0 -157
  13. package/lib/AntiBan/groupOperationGuard.js +0 -116
  14. package/lib/AntiBan/health.js +0 -207
  15. package/lib/AntiBan/humanEntropy.js +0 -337
  16. package/lib/AntiBan/index.js +0 -77
  17. package/lib/AntiBan/instanceCoordinator.js +0 -175
  18. package/lib/AntiBan/jidCanonicalizer.js +0 -256
  19. package/lib/AntiBan/jidCircuitBreaker.js +0 -186
  20. package/lib/AntiBan/legitimacySignalInjector.js +0 -231
  21. package/lib/AntiBan/lidFirstResolver.js +0 -174
  22. package/lib/AntiBan/lidResolver.js +0 -324
  23. package/lib/AntiBan/messageQueue.js +0 -187
  24. package/lib/AntiBan/messageRecovery.js +0 -283
  25. package/lib/AntiBan/messageTypeRegistry.js +0 -369
  26. package/lib/AntiBan/observability.js +0 -145
  27. package/lib/AntiBan/patch.js +0 -302
  28. package/lib/AntiBan/persist.js +0 -87
  29. package/lib/AntiBan/presenceChoreographer.js +0 -430
  30. package/lib/AntiBan/presets.js +0 -105
  31. package/lib/AntiBan/profiles.js +0 -31
  32. package/lib/AntiBan/proxyRotator.js +0 -306
  33. package/lib/AntiBan/rateLimiter.js +0 -261
  34. package/lib/AntiBan/readReceiptVariance.js +0 -88
  35. package/lib/AntiBan/reconnectThrottle.js +0 -180
  36. package/lib/AntiBan/replyRatio.js +0 -161
  37. package/lib/AntiBan/reputationVoucher.js +0 -373
  38. package/lib/AntiBan/retryReason.js +0 -91
  39. package/lib/AntiBan/retryTracker.js +0 -172
  40. package/lib/AntiBan/scheduler.js +0 -111
  41. package/lib/AntiBan/sessionFingerprint.js +0 -248
  42. package/lib/AntiBan/sessionStability.js +0 -330
  43. package/lib/AntiBan/stateAdapter.js +0 -73
  44. package/lib/AntiBan/stateExport.js +0 -152
  45. package/lib/AntiBan/stealthConnect.js +0 -130
  46. package/lib/AntiBan/timelockGuard.js +0 -181
  47. package/lib/AntiBan/topologyThrottler.js +0 -339
  48. package/lib/AntiBan/warmup.js +0 -140
  49. package/lib/AntiBan/webhooks.js +0 -80
  50. package/lib/AntiBan/wrapper.js +0 -456
@@ -1,456 +0,0 @@
1
- /**
2
- * Socket Wrapper — Drop-in replacement that wraps sendMessage with anti-ban protection
3
- *
4
- * Works with both baileys and @oxidezap/baileyrs transports.
5
- *
6
- * Usage with baileys:
7
- * import makeWASocket from 'baileys';
8
- * import { wrapSocket } from 'baileys-antiban';
9
- *
10
- * const sock = makeWASocket({ ... });
11
- * const safeSock = wrapSocket(sock);
12
- *
13
- * Usage with baileyrs:
14
- * import { makeWASocket } from '@oxidezap/baileyrs';
15
- * import { wrapSocket } from 'baileys-antiban';
16
- *
17
- * const sock = makeWASocket({ ... });
18
- * const safeSock = wrapSocket(sock);
19
- *
20
- * // Use safeSock.sendMessage() — automatically rate-limited and monitored
21
- * await safeSock.sendMessage(jid, { text: 'Hello!' });
22
- *
23
- * // Check health anytime
24
- * console.log(safeSock.antiban.getStats());
25
- *
26
- * Note: reachoutTimeLock timelock module silently noops on baileyrs until upstream
27
- * emits reachoutTimeLock events — confirmed NOT present in baileyrs v0.0.8.
28
- * Timelock guard will operate in detection-only mode (relies on 463 errors only).
29
- */
30
- import { AntiBan } from './antiban.js';
31
- import { DeafSessionDetector } from './sessionStability.js';
32
- import { GroupOperationGuard } from './groupOperationGuard.js';
33
- import { LegitimacySignalInjector } from './legitimacySignalInjector.js';
34
- /**
35
- * Wrap a Baileys socket with anti-ban protection.
36
- * The returned socket has the same API but sendMessage() is protected.
37
- */
38
- export function wrapSocket(sock, config, warmUpState, wrapOptions) {
39
- const antiban = new AntiBan(config, warmUpState);
40
- const options = {
41
- autoRespondToIncoming: false,
42
- ...wrapOptions,
43
- };
44
- // Deaf session detector — optional, enabled via wrapOptions.deafSession
45
- const deafDetector = options.deafSession
46
- ? new DeafSessionDetector(options.deafSession)
47
- : null;
48
- if (deafDetector)
49
- deafDetector.attach(sock);
50
- // Group operation guard — intercept group methods with rate limiting
51
- if (options.groupOpGuard !== false) {
52
- const guard = new GroupOperationGuard(options.groupOpGuard || {});
53
- const origUpdate = sock.groupParticipantsUpdate?.bind(sock);
54
- if (origUpdate) {
55
- sock.groupParticipantsUpdate = async (jid, participants, action) => {
56
- const op = action === 'add' ? 'add' : action === 'remove' ? 'remove' : 'add';
57
- const check = guard.check(op, jid);
58
- if (!check.allowed)
59
- throw new Error(check.reason || 'Group operation rate limited');
60
- return origUpdate(jid, participants, action);
61
- };
62
- }
63
- const origCreate = sock.groupCreate?.bind(sock);
64
- if (origCreate) {
65
- sock.groupCreate = async (subject, participants) => {
66
- const check = guard.check('create', subject);
67
- if (!check.allowed)
68
- throw new Error(check.reason || 'Group creation rate limited');
69
- return origCreate(subject, participants);
70
- };
71
- }
72
- }
73
- // Legitimacy signal injector — wrap sendMessage for typo injection
74
- let legitimacyInjector = null;
75
- if (options.legitimacySignals !== false) {
76
- legitimacyInjector = new LegitimacySignalInjector(typeof options.legitimacySignals === 'object' ? options.legitimacySignals : {});
77
- }
78
- // Hook into Baileys events for health monitoring
79
- // Prefer ev.process() (Baileys ≥ late 2022) for batched event handling
80
- // Fall back to ev.on() for older versions
81
- if (typeof sock.ev.process === 'function') {
82
- sock.ev.process(async (events) => {
83
- // Handle connection updates
84
- if (events['connection.update']) {
85
- const update = events['connection.update'];
86
- if (update.connection === 'close') {
87
- const reason = update.lastDisconnect?.error?.output?.statusCode || 'unknown';
88
- antiban.onDisconnect(reason);
89
- antiban.destroy(); // Clean up all timers
90
- deafDetector?.onDisconnect();
91
- deafDetector?.destroy();
92
- // Fleet event: detect ban-like disconnect codes
93
- if (options.fleetEventStore) {
94
- const banCodes = [401, 403, 428, 515]; // 401=logged out, 403=forbidden, 428=banned, 515=restart required
95
- if (typeof reason === 'number' && banCodes.includes(reason)) {
96
- void options.fleetEventStore.emit('ban', { statusCode: reason });
97
- }
98
- }
99
- }
100
- if (update.connection === 'open') {
101
- antiban.onReconnect();
102
- deafDetector?.onConnect();
103
- // Fleet event: recovery from previous ban/warn
104
- if (options.fleetEventStore) {
105
- const health = antiban.getStats().health;
106
- if (health.risk === 'low') {
107
- void options.fleetEventStore.emit('recovery', { risk: health.risk });
108
- }
109
- }
110
- }
111
- // Reachout timelock detection
112
- if (update.reachoutTimeLock) {
113
- antiban.timelock.onTimelockUpdate({
114
- isActive: update.reachoutTimeLock.isActive,
115
- timeEnforcementEnds: update.reachoutTimeLock.timeEnforcementEnds,
116
- enforcementType: update.reachoutTimeLock.enforcementType,
117
- });
118
- // Fleet event: timelock
119
- if (options.fleetEventStore && update.reachoutTimeLock.isActive) {
120
- void options.fleetEventStore.emit('timelock', {
121
- enforcementType: update.reachoutTimeLock.enforcementType,
122
- endsAt: update.reachoutTimeLock.timeEnforcementEnds,
123
- });
124
- }
125
- }
126
- }
127
- // Catch 463 errors from message updates + track retries + learn LID mappings
128
- if (events['messages.update']) {
129
- const updates = events['messages.update'];
130
- deafDetector?.onMessageActivity();
131
- for (const update of updates) {
132
- // 463 error detection
133
- if (update?.update?.messageStubParameters) {
134
- const params = Array.isArray(update.update.messageStubParameters)
135
- ? update.update.messageStubParameters
136
- : [];
137
- if (params.includes(463) || params.includes('463')) {
138
- antiban.timelock.record463Error();
139
- // Fleet event: rate limit detected
140
- if (options.fleetEventStore) {
141
- void options.fleetEventStore.emit('rate_limit', { error: '463' });
142
- }
143
- }
144
- }
145
- // Retry tracking
146
- antiban.retryTracker.onMessageUpdate(update);
147
- // Delivery receipt tracking
148
- const status = update?.update?.status;
149
- const msgId = update?.key?.id;
150
- // status 3 = DELIVERY_ACK (double tick), status 4 = READ
151
- if (msgId && (status === 3 || status === 4)) {
152
- antiban.onDeliveryReceipt(msgId);
153
- }
154
- }
155
- // LID canonicalizer learning
156
- antiban.jidCanonicalizer?.onMessageUpdate(updates);
157
- }
158
- // Register known chats from incoming messages + handle reply suggestions + learn LID mappings
159
- if (events['messages.upsert']) {
160
- const { messages } = events['messages.upsert'];
161
- deafDetector?.onMessageActivity();
162
- // Learn LID mappings FIRST (before any other processing)
163
- antiban.jidCanonicalizer?.onIncomingEvent(events['messages.upsert']);
164
- for (const msg of messages || []) {
165
- const jid = msg.key?.remoteJid;
166
- if (!jid)
167
- continue;
168
- // Register known chat
169
- antiban.timelock.registerKnownChat(jid);
170
- // Skip self messages
171
- const isSelf = msg.key?.fromMe || false;
172
- if (isSelf)
173
- continue;
174
- // Extract message text
175
- const msgText = msg.message?.conversation ||
176
- msg.message?.extendedTextMessage?.text ||
177
- msg.message?.imageMessage?.caption ||
178
- msg.message?.videoMessage?.caption ||
179
- '';
180
- // Handle incoming message (updates reply ratio + contact graph)
181
- const replySuggestion = antiban.onIncomingMessage(jid, msgText);
182
- // Auto-respond if enabled and suggested
183
- if (options.autoRespondToIncoming && replySuggestion.shouldReply && replySuggestion.suggestedText) {
184
- // Random delay 3-15s
185
- const replyDelay = Math.floor(Math.random() * 12000) + 3000;
186
- setTimeout(async () => {
187
- try {
188
- await sock.sendMessage(jid, { text: replySuggestion.suggestedText });
189
- }
190
- catch (error) {
191
- // Silently fail — auto-reply is best-effort
192
- }
193
- }, replyDelay);
194
- }
195
- }
196
- }
197
- });
198
- }
199
- else {
200
- // Fallback to ev.on() for older Baileys versions
201
- sock.ev.on('connection.update', (update) => {
202
- if (update.connection === 'close') {
203
- const reason = update.lastDisconnect?.error?.output?.statusCode || 'unknown';
204
- antiban.onDisconnect(reason);
205
- antiban.destroy(); // Clean up all timers
206
- deafDetector?.onDisconnect();
207
- deafDetector?.destroy();
208
- // Fleet event: detect ban-like disconnect codes
209
- if (options.fleetEventStore) {
210
- const banCodes = [401, 403, 428, 515]; // 401=logged out, 403=forbidden, 428=banned, 515=restart required
211
- if (typeof reason === 'number' && banCodes.includes(reason)) {
212
- void options.fleetEventStore.emit('ban', { statusCode: reason });
213
- }
214
- }
215
- }
216
- if (update.connection === 'open') {
217
- antiban.onReconnect();
218
- deafDetector?.onConnect();
219
- // Fleet event: recovery from previous ban/warn
220
- if (options.fleetEventStore) {
221
- const health = antiban.getStats().health;
222
- if (health.risk === 'low') {
223
- void options.fleetEventStore.emit('recovery', { risk: health.risk });
224
- }
225
- }
226
- }
227
- // Reachout timelock detection
228
- if (update.reachoutTimeLock) {
229
- antiban.timelock.onTimelockUpdate({
230
- isActive: update.reachoutTimeLock.isActive,
231
- timeEnforcementEnds: update.reachoutTimeLock.timeEnforcementEnds,
232
- enforcementType: update.reachoutTimeLock.enforcementType,
233
- });
234
- // Fleet event: timelock
235
- if (options.fleetEventStore && update.reachoutTimeLock.isActive) {
236
- void options.fleetEventStore.emit('timelock', {
237
- enforcementType: update.reachoutTimeLock.enforcementType,
238
- endsAt: update.reachoutTimeLock.timeEnforcementEnds,
239
- });
240
- }
241
- }
242
- });
243
- // Catch 463 errors from message updates + track retries + learn LID mappings
244
- sock.ev.on('messages.update', (updates) => {
245
- deafDetector?.onMessageActivity();
246
- for (const update of updates) {
247
- // 463 error detection
248
- if (update?.update?.messageStubParameters) {
249
- const params = update.update.messageStubParameters;
250
- if (params.includes(463) || params.includes('463')) {
251
- antiban.timelock.record463Error();
252
- // Fleet event: rate limit detected
253
- if (options.fleetEventStore) {
254
- void options.fleetEventStore.emit('rate_limit', { error: '463' });
255
- }
256
- }
257
- }
258
- // Retry tracking
259
- antiban.retryTracker.onMessageUpdate(update);
260
- // Delivery receipt tracking
261
- const status = update?.update?.status;
262
- const msgId = update?.key?.id;
263
- // status 3 = DELIVERY_ACK (double tick), status 4 = READ
264
- if (msgId && (status === 3 || status === 4)) {
265
- antiban.onDeliveryReceipt(msgId);
266
- }
267
- }
268
- // LID canonicalizer learning
269
- antiban.jidCanonicalizer?.onMessageUpdate(updates);
270
- });
271
- // Register known chats from incoming messages + handle reply suggestions + learn LID mappings
272
- sock.ev.on('messages.upsert', (upsert) => {
273
- const { messages } = upsert;
274
- deafDetector?.onMessageActivity();
275
- // Learn LID mappings FIRST (before any other processing)
276
- antiban.jidCanonicalizer?.onIncomingEvent(upsert);
277
- for (const msg of messages || []) {
278
- const jid = msg.key?.remoteJid;
279
- if (!jid)
280
- continue;
281
- // Register known chat
282
- antiban.timelock.registerKnownChat(jid);
283
- // Skip self messages
284
- const isSelf = msg.key?.fromMe || false;
285
- if (isSelf)
286
- continue;
287
- // Extract message text
288
- const msgText = msg.message?.conversation ||
289
- msg.message?.extendedTextMessage?.text ||
290
- msg.message?.imageMessage?.caption ||
291
- msg.message?.videoMessage?.caption ||
292
- '';
293
- // Handle incoming message (updates reply ratio + contact graph)
294
- const replySuggestion = antiban.onIncomingMessage(jid, msgText);
295
- // Auto-respond if enabled and suggested
296
- if (options.autoRespondToIncoming && replySuggestion.shouldReply && replySuggestion.suggestedText) {
297
- // Random delay 3-15s
298
- const replyDelay = Math.floor(Math.random() * 12000) + 3000;
299
- setTimeout(async () => {
300
- try {
301
- await sock.sendMessage(jid, { text: replySuggestion.suggestedText });
302
- }
303
- catch (error) {
304
- // Silently fail — auto-reply is best-effort
305
- }
306
- }, replyDelay);
307
- }
308
- }
309
- });
310
- }
311
- // Create proxy that intercepts sendMessage
312
- const originalSendMessage = sock.sendMessage.bind(sock);
313
- // Serializes beforeSend→afterSend so rate limiter accounting is accurate
314
- // under concurrent sends. Without this, all concurrent callers read the same
315
- // committed state before any afterSend records, bypassing per-minute limits.
316
- let sendLock = Promise.resolve();
317
- const wrappedSendMessage = async (jid, content, options) => {
318
- /**
319
- * LID/PN Canonicalization — Normalize JID to canonical form
320
- *
321
- * This mitigates the LID/PN race that causes "Bad MAC / No Session / Invalid PreKey"
322
- * errors (Baileys #1769, our PR #2372). When a message event arrives under one form
323
- * (e.g. LID) but the crypto session was established under another (e.g. PN), decryption
324
- * fails. By normalizing all outbound targets to a single form, we reduce this race.
325
- *
326
- * This is middleware-layer mitigation only. Root fix requires PR #2372 merged upstream.
327
- */
328
- const canonicalJid = antiban.jidCanonicalizer?.canonicalizeTarget(jid) || jid;
329
- // Extract text content for rate limiter analysis
330
- let text = content?.text || content?.caption || content?.image?.caption || '';
331
- // Check for typo injection (only for text messages)
332
- let typoResult = null;
333
- if (legitimacyInjector && content?.text && typeof content.text === 'string' && content.text.length > 10) {
334
- typoResult = legitimacyInjector.shouldInjectTypo(content.text);
335
- }
336
- // Chain this send onto the previous — each waits for the prior send's
337
- // afterSend to commit before running its own beforeSend check.
338
- const sendResult = sendLock.then(async () => {
339
- // Circuit breaker check (before antiban.beforeSend)
340
- if (options.circuitBreaker) {
341
- if (!options.circuitBreaker.canSend(canonicalJid)) {
342
- throw new Error('[baileys-antiban] circuit-breaker: send blocked for ' + canonicalJid);
343
- }
344
- }
345
- const decision = await antiban.beforeSend(canonicalJid, text);
346
- if (!decision.allowed) {
347
- throw new Error(`[baileys-antiban] Message blocked: ${decision.reason}`);
348
- }
349
- // Apply delay from rate limiter
350
- if (decision.delayMs > 0) {
351
- await new Promise(resolve => setTimeout(resolve, decision.delayMs));
352
- }
353
- // Apply circuit breaker jitter for broadcast messages
354
- if (options.circuitBreaker) {
355
- const isBroadcast = canonicalJid.endsWith('@broadcast') || canonicalJid.endsWith('@newsletter');
356
- const jitter = options.circuitBreaker.getJitter(isBroadcast);
357
- if (jitter > 0) {
358
- await new Promise(resolve => setTimeout(resolve, jitter));
359
- }
360
- }
361
- // Send message (using canonical JID)
362
- // If typo injection is active, send typo first, then correction
363
- try {
364
- let result;
365
- if (typoResult) {
366
- // Send typo version first
367
- const typoContent = { ...content, text: typoResult.typoText };
368
- await originalSendMessage(canonicalJid, typoContent, options);
369
- // Wait for correction delay
370
- await new Promise(r => setTimeout(r, typoResult.correctionDelay));
371
- // Send corrected version
372
- const correctionContent = { ...content, text: typoResult.correctionText };
373
- result = await originalSendMessage(canonicalJid, correctionContent, options);
374
- }
375
- else {
376
- // Normal send
377
- result = await originalSendMessage(canonicalJid, content, options);
378
- }
379
- // Pass msgId to afterSend for delivery tracking
380
- const msgId = result?.key?.id;
381
- antiban.afterSend(canonicalJid, text, msgId);
382
- antiban.timelock.registerKnownChat(canonicalJid);
383
- // Clear retry tracking on successful send
384
- if (msgId) {
385
- antiban.retryTracker.clear(msgId);
386
- }
387
- // Circuit breaker success
388
- if (options.circuitBreaker) {
389
- options.circuitBreaker.recordSuccess(canonicalJid);
390
- }
391
- return result;
392
- }
393
- catch (error) {
394
- // Circuit breaker failure
395
- if (options.circuitBreaker) {
396
- options.circuitBreaker.recordFailure(canonicalJid);
397
- }
398
- // Baileys PR #2587: partial-encrypt Boom now carries structured data:
399
- // error.data.failed[] — per-recipient { jid, error } failures
400
- // error.data.firstCause — most likely root cause string
401
- // Extract for richer health-monitor diagnostics vs plain error.message.
402
- const boomData = error?.data;
403
- if (boomData?.failed?.length) {
404
- const cause = boomData.firstCause ?? 'unknown';
405
- const failedJids = boomData.failed.map((f) => f.jid).join(', ');
406
- antiban.afterSendFailed(`encrypt-all-failed firstCause=${cause} jids=[${failedJids}]`);
407
- }
408
- else {
409
- antiban.afterSendFailed(error instanceof Error ? error.message : String(error));
410
- }
411
- throw error;
412
- }
413
- });
414
- // Advance the lock regardless of success/failure so the chain never stalls
415
- sendLock = sendResult.then(() => { }, () => { });
416
- return sendResult;
417
- };
418
- // Return enhanced socket
419
- const wrapped = Object.create(sock);
420
- wrapped.sendMessage = wrappedSendMessage;
421
- wrapped.antiban = antiban;
422
- // Expose destroy method directly so consumers can call it manually if needed
423
- wrapped.antiban.destroy = antiban.destroy.bind(antiban);
424
- return wrapped;
425
- }
426
- /**
427
- * Helper function to create a wrapped socket with device fingerprint applied.
428
- *
429
- * This combines device fingerprint generation, socket creation, and wrapping
430
- * into a single call.
431
- *
432
- * Usage:
433
- * import makeWASocket from 'baileys';
434
- * import { wrapSocketWithFingerprint } from 'baileys-antiban';
435
- *
436
- * const wrapped = wrapSocketWithFingerprint(makeWASocket, socketConfig, {
437
- * fingerprintSeed: 'stable-seed-123',
438
- * groupOpGuard: {},
439
- * legitimacySignals: {}
440
- * });
441
- *
442
- * @param makeWASocket - Baileys makeWASocket factory function
443
- * @param socketConfig - Base socket configuration
444
- * @param wrapOptions - Combined wrapper options + fingerprintSeed
445
- */
446
- export function wrapSocketWithFingerprint(makeWASocket, socketConfig, wrapOptions) {
447
- // Import fingerprint functions dynamically to avoid circular deps
448
- const { generateFingerprint, applyFingerprint } = require('./deviceFingerprint.js');
449
- const seed = wrapOptions?.fingerprintSeed ||
450
- socketConfig.auth?.creds?.me?.id ||
451
- String(Date.now());
452
- const fp = generateFingerprint({ seed });
453
- const config = applyFingerprint(socketConfig, fp);
454
- const sock = makeWASocket(config);
455
- return wrapSocket(sock, undefined, undefined, wrapOptions);
456
- }