@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,324 +0,0 @@
1
- /**
2
- * LID Resolver — Maintains bidirectional LID↔PN mapping for contacts
3
- *
4
- * WhatsApp migrated to LID (Linked Identity) in 2024. A contact now has two JIDs:
5
- * - Phone number form: "27825651069@s.whatsapp.net"
6
- * - LID form: "123456789@lid"
7
- *
8
- * Messages can arrive under either form. If an encryption session was established
9
- * under one form and a message arrives under the other, decryption fails → "Bad MAC".
10
- *
11
- * This utility:
12
- * - Learns LID↔PN mappings from message events
13
- * - Normalizes JIDs to a canonical form (phone number by default)
14
- * - Provides lookup for cross-form resolution
15
- * - Optionally persists state across restarts
16
- *
17
- * This is a standalone utility — can be used independently or via JidCanonicalizer.
18
- */
19
- const DEFAULT_CONFIG = {
20
- canonical: 'pn',
21
- maxEntries: 10_000,
22
- };
23
- export class LidResolver {
24
- config;
25
- persistence;
26
- // Bidirectional maps: lid→pn and pn→lid
27
- lidToPn = new Map();
28
- pnToLid = new Map(); // pn → lid (for quick reverse lookup)
29
- stats = {
30
- learnedFromEvents: 0,
31
- lookupsServed: 0,
32
- lookupMisses: 0,
33
- };
34
- constructor(config = {}) {
35
- this.config = { ...DEFAULT_CONFIG, ...config };
36
- this.persistence = config.persistence;
37
- // Auto-hydrate if persistence provided
38
- if (this.persistence?.load) {
39
- void this.hydrate();
40
- }
41
- }
42
- /**
43
- * Learn from a message event. Idempotent.
44
- * Accepts partial mappings — will use whatever fields are available.
45
- */
46
- learn(mapping) {
47
- let lid = mapping.lid ? this.normalizeJid(mapping.lid) : undefined;
48
- let pn = mapping.pn ? this.normalizeJid(mapping.pn) : undefined;
49
- const phone = mapping.phone;
50
- // Validate we have both lid and pn (or can derive pn from phone)
51
- if (!lid || (!pn && !phone)) {
52
- return; // Insufficient data
53
- }
54
- // Derive pn from phone if not provided
55
- if (!pn && phone) {
56
- pn = `${phone}@s.whatsapp.net`;
57
- }
58
- // Validate forms
59
- if (!lid || !pn)
60
- return;
61
- if (!lid.endsWith('@lid'))
62
- return;
63
- if (!pn.endsWith('@s.whatsapp.net'))
64
- return;
65
- // Check if we already know this mapping
66
- const existing = this.lidToPn.get(lid);
67
- if (existing) {
68
- // Already learned — just increment seen count
69
- existing.seenCount++;
70
- existing.learnedAt = Date.now(); // Update access time for LRU
71
- return;
72
- }
73
- // Learn new mapping
74
- const extractedPhone = phone || pn.split('@')[0];
75
- const newMapping = {
76
- lid,
77
- pn,
78
- phone: extractedPhone,
79
- learnedAt: Date.now(),
80
- seenCount: 1,
81
- };
82
- // Check if we need to evict (LRU)
83
- if (this.lidToPn.size >= this.config.maxEntries) {
84
- this.evictLRU();
85
- }
86
- this.lidToPn.set(lid, newMapping);
87
- this.pnToLid.set(pn, lid);
88
- this.stats.learnedFromEvents++;
89
- // Async flush to persistence (don't await)
90
- if (this.persistence?.save) {
91
- void this.flush();
92
- }
93
- }
94
- /**
95
- * Given any form (LID or PN), return the canonical form.
96
- * Falls back to input if unknown (no throw).
97
- */
98
- resolveCanonical(jid) {
99
- const normalized = this.normalizeJid(jid);
100
- if (this.config.canonical === 'pn') {
101
- // Want PN form
102
- if (normalized.endsWith('@lid')) {
103
- const mapping = this.lidToPn.get(normalized);
104
- if (mapping) {
105
- this.stats.lookupsServed++;
106
- mapping.learnedAt = Date.now(); // Update LRU access time
107
- return mapping.pn;
108
- }
109
- this.stats.lookupMisses++;
110
- return jid; // Fallback to original input
111
- }
112
- // Already PN form
113
- this.stats.lookupsServed++;
114
- return normalized;
115
- }
116
- else {
117
- // Want LID form
118
- if (normalized.endsWith('@s.whatsapp.net')) {
119
- const lid = this.pnToLid.get(normalized);
120
- if (lid) {
121
- this.stats.lookupsServed++;
122
- const mapping = this.lidToPn.get(lid);
123
- if (mapping) {
124
- mapping.learnedAt = Date.now(); // Update LRU access time
125
- }
126
- return lid;
127
- }
128
- this.stats.lookupMisses++;
129
- return jid; // Fallback to original input
130
- }
131
- // Already LID form
132
- this.stats.lookupsServed++;
133
- return normalized;
134
- }
135
- }
136
- /**
137
- * Lookup partner form. Returns null if unknown.
138
- */
139
- getLid(pn) {
140
- const normalized = this.normalizeJid(pn);
141
- const lid = this.pnToLid.get(normalized);
142
- if (lid) {
143
- const mapping = this.lidToPn.get(lid);
144
- if (mapping) {
145
- mapping.learnedAt = Date.now(); // Update LRU access time
146
- }
147
- }
148
- return lid || null;
149
- }
150
- getPn(lid) {
151
- const normalized = this.normalizeJid(lid);
152
- const mapping = this.lidToPn.get(normalized);
153
- if (mapping) {
154
- mapping.learnedAt = Date.now(); // Update LRU access time
155
- return mapping.pn;
156
- }
157
- return null;
158
- }
159
- /**
160
- * Full mapping for inspection
161
- */
162
- getMapping(jid) {
163
- const normalized = this.normalizeJid(jid);
164
- // Try as LID first
165
- const byLid = this.lidToPn.get(normalized);
166
- if (byLid) {
167
- byLid.learnedAt = Date.now(); // Update LRU access time
168
- return byLid;
169
- }
170
- // Try as PN
171
- const lid = this.pnToLid.get(normalized);
172
- if (lid) {
173
- const mapping = this.lidToPn.get(lid);
174
- if (mapping) {
175
- mapping.learnedAt = Date.now(); // Update LRU access time
176
- return mapping;
177
- }
178
- }
179
- return null;
180
- }
181
- /**
182
- * Learn LID↔PN mappings from group metadata participants.
183
- * Call this after fetchGroupMetadata() to pre-populate the map.
184
- * Supports both {id: '@lid', phoneNumber: '@s.whatsapp.net'} and
185
- * {id: '@s.whatsapp.net', lid: '@lid'} participant formats (v7 + v6 shapes).
186
- *
187
- * @param participants - Group metadata participants array from Baileys
188
- * @returns Number of new mappings learned
189
- */
190
- learnFromGroupMetadata(participants) {
191
- let learned = 0;
192
- for (const p of participants) {
193
- const domain = p.id.split('@')[1] || '';
194
- if (domain === 'lid' && (p.phoneNumber || p.phone || p.number)) {
195
- const pn = p.phoneNumber || (p.phone ? `${p.phone}@s.whatsapp.net` : null) || (p.number ? `${p.number}@s.whatsapp.net` : null);
196
- if (pn) {
197
- const prevSize = this.lidToPn.size;
198
- this.learn({ lid: p.id, pn });
199
- if (this.lidToPn.size > prevSize)
200
- learned++;
201
- }
202
- }
203
- else if (domain === 's.whatsapp.net' && p.lid) {
204
- const lid = p.lid.endsWith('@lid') ? p.lid : `${p.lid}@lid`;
205
- const prevSize = this.lidToPn.size;
206
- this.learn({ lid, pn: p.id });
207
- if (this.lidToPn.size > prevSize)
208
- learned++;
209
- }
210
- }
211
- return learned;
212
- }
213
- /**
214
- * Seed from persistence (called automatically in constructor if persistence provided)
215
- */
216
- async hydrate() {
217
- if (!this.persistence?.load)
218
- return;
219
- try {
220
- const stored = await this.persistence.load();
221
- if (!stored || typeof stored !== 'object')
222
- return;
223
- // Restore mappings
224
- for (const [lid, serialized] of Object.entries(stored)) {
225
- if (typeof serialized === 'string') {
226
- // Old format: lid → pn string
227
- const pn = serialized;
228
- const phone = pn.split('@')[0];
229
- const mapping = {
230
- lid,
231
- pn,
232
- phone,
233
- learnedAt: Date.now(),
234
- seenCount: 1,
235
- };
236
- this.lidToPn.set(lid, mapping);
237
- this.pnToLid.set(pn, lid);
238
- }
239
- else if (typeof serialized === 'object' && serialized !== null) {
240
- // New format: lid → LidMapping object
241
- const mapping = serialized;
242
- this.lidToPn.set(lid, mapping);
243
- this.pnToLid.set(mapping.pn, lid);
244
- }
245
- }
246
- }
247
- catch (error) {
248
- // Silently fail hydration — don't crash on corrupt persistence
249
- }
250
- }
251
- /**
252
- * Flush current map to persistence
253
- */
254
- async flush() {
255
- if (!this.persistence?.save)
256
- return;
257
- try {
258
- const toStore = {};
259
- for (const [lid, mapping] of this.lidToPn.entries()) {
260
- toStore[lid] = mapping;
261
- }
262
- await this.persistence.save(toStore);
263
- }
264
- catch (error) {
265
- // Silently fail flush — don't crash
266
- }
267
- }
268
- getStats() {
269
- return {
270
- totalMappings: this.lidToPn.size,
271
- learnedFromEvents: this.stats.learnedFromEvents,
272
- lookupsServed: this.stats.lookupsServed,
273
- lookupMisses: this.stats.lookupMisses,
274
- canonicalForm: this.config.canonical,
275
- };
276
- }
277
- /**
278
- * Clear everything
279
- */
280
- reset() {
281
- this.lidToPn.clear();
282
- this.pnToLid.clear();
283
- this.stats = {
284
- learnedFromEvents: 0,
285
- lookupsServed: 0,
286
- lookupMisses: 0,
287
- };
288
- }
289
- destroy() {
290
- this.reset();
291
- // Flush one final time
292
- if (this.persistence?.save) {
293
- void this.flush();
294
- }
295
- }
296
- // Private helpers
297
- /**
298
- * Normalize JID: strip device suffix `:N`
299
- */
300
- normalizeJid(jid) {
301
- // Strip device suffix e.g. "123:45@s.whatsapp.net" → "123@s.whatsapp.net"
302
- return jid.replace(/:\d+@/, '@');
303
- }
304
- /**
305
- * Evict least recently accessed mapping (LRU)
306
- */
307
- evictLRU() {
308
- let oldestLid = null;
309
- let oldestTime = Infinity;
310
- for (const [lid, mapping] of this.lidToPn.entries()) {
311
- if (mapping.learnedAt < oldestTime) {
312
- oldestTime = mapping.learnedAt;
313
- oldestLid = lid;
314
- }
315
- }
316
- if (oldestLid) {
317
- const mapping = this.lidToPn.get(oldestLid);
318
- if (mapping) {
319
- this.pnToLid.delete(mapping.pn);
320
- }
321
- this.lidToPn.delete(oldestLid);
322
- }
323
- }
324
- }
@@ -1,187 +0,0 @@
1
- /**
2
- * Persistent Message Queue — Messages survive crashes and restarts
3
- *
4
- * Instead of fire-and-forget, queue messages and let the anti-ban
5
- * system drain them at a safe pace. Failed messages auto-retry.
6
- */
7
- import { EventEmitter } from 'events';
8
- const DEFAULT_CONFIG = {
9
- maxAttempts: 3,
10
- retryBaseDelayMs: 30000,
11
- maxQueueSize: 1000,
12
- priorityOrder: true,
13
- };
14
- export class MessageQueue extends EventEmitter {
15
- config;
16
- queue = [];
17
- processing = false;
18
- sendFn = null;
19
- drainTimer = null;
20
- idCounter = 0;
21
- constructor(config = {}) {
22
- super();
23
- this.config = { ...DEFAULT_CONFIG, ...config };
24
- }
25
- /**
26
- * Set the send function (called for each message when drained)
27
- * This should be the anti-ban wrapped sendMessage
28
- */
29
- setSendFunction(fn) {
30
- this.sendFn = fn;
31
- }
32
- /**
33
- * Add a message to the queue
34
- */
35
- add(recipient, content, options) {
36
- if (this.queue.length >= this.config.maxQueueSize) {
37
- throw new Error(`Queue full (${this.config.maxQueueSize} messages)`);
38
- }
39
- const id = `msg_${Date.now()}_${++this.idCounter}`;
40
- const message = {
41
- id,
42
- recipient,
43
- content,
44
- priority: options?.priority || 'normal',
45
- addedAt: Date.now(),
46
- attempts: 0,
47
- maxAttempts: this.config.maxAttempts,
48
- scheduledFor: options?.scheduledFor?.getTime(),
49
- metadata: options?.metadata,
50
- };
51
- this.queue.push(message);
52
- this.sortQueue();
53
- this.emit('added', message);
54
- return id;
55
- }
56
- /**
57
- * Add multiple messages (e.g., broadcast to many recipients)
58
- */
59
- addBulk(recipients, content, options) {
60
- return recipients.map(r => this.add(r, content, options));
61
- }
62
- /**
63
- * Start processing the queue
64
- */
65
- start(intervalMs = 1000) {
66
- if (this.drainTimer)
67
- return;
68
- this.drainTimer = setInterval(() => this.processNext(), intervalMs);
69
- this.emit('started');
70
- }
71
- /**
72
- * Stop processing
73
- */
74
- stop() {
75
- if (this.drainTimer) {
76
- clearInterval(this.drainTimer);
77
- this.drainTimer = null;
78
- }
79
- this.emit('stopped');
80
- }
81
- /**
82
- * Clean up all timers and resources.
83
- * Call this when disposing of the queue.
84
- */
85
- destroy() {
86
- this.stop();
87
- }
88
- /**
89
- * Process the next message in the queue
90
- */
91
- async processNext() {
92
- if (this.processing || !this.sendFn)
93
- return;
94
- const now = Date.now();
95
- const message = this.queue.find(m => (!m.scheduledFor || m.scheduledFor <= now));
96
- if (!message)
97
- return;
98
- this.processing = true;
99
- try {
100
- message.attempts++;
101
- await this.sendFn(message.recipient, message.content);
102
- // Success — remove from queue
103
- this.queue = this.queue.filter(m => m.id !== message.id);
104
- this.emit('sent', message);
105
- }
106
- catch (err) {
107
- message.lastError = err.message;
108
- if (err.message?.includes('baileys-antiban')) {
109
- // Anti-ban blocked it — don't count as attempt, try later
110
- message.attempts--;
111
- this.emit('delayed', message, err.message);
112
- }
113
- else if (message.attempts >= message.maxAttempts) {
114
- // Max retries reached — move to dead letter
115
- this.queue = this.queue.filter(m => m.id !== message.id);
116
- this.emit('failed', message, err.message);
117
- }
118
- else {
119
- // Schedule retry with exponential backoff
120
- const backoff = this.config.retryBaseDelayMs * Math.pow(2, message.attempts - 1);
121
- message.scheduledFor = Date.now() + backoff;
122
- this.emit('retry', message, message.attempts, backoff);
123
- }
124
- }
125
- finally {
126
- this.processing = false;
127
- }
128
- }
129
- /**
130
- * Get queue stats
131
- */
132
- getStats() {
133
- const now = Date.now();
134
- return {
135
- total: this.queue.length,
136
- pending: this.queue.filter(m => !m.scheduledFor || m.scheduledFor <= now).length,
137
- scheduled: this.queue.filter(m => m.scheduledFor && m.scheduledFor > now).length,
138
- byPriority: {
139
- high: this.queue.filter(m => m.priority === 'high').length,
140
- normal: this.queue.filter(m => m.priority === 'normal').length,
141
- low: this.queue.filter(m => m.priority === 'low').length,
142
- },
143
- processing: this.processing,
144
- isRunning: this.drainTimer !== null,
145
- };
146
- }
147
- /**
148
- * Clear all messages
149
- */
150
- clear() {
151
- const count = this.queue.length;
152
- this.queue = [];
153
- this.emit('cleared', count);
154
- }
155
- /**
156
- * Remove a specific message
157
- */
158
- remove(id) {
159
- const before = this.queue.length;
160
- this.queue = this.queue.filter(m => m.id !== id);
161
- return this.queue.length < before;
162
- }
163
- /**
164
- * Export queue for persistence
165
- */
166
- export() {
167
- return [...this.queue];
168
- }
169
- /**
170
- * Import queue (e.g., after restart)
171
- */
172
- import(messages) {
173
- this.queue = [...messages];
174
- this.sortQueue();
175
- }
176
- sortQueue() {
177
- if (!this.config.priorityOrder)
178
- return;
179
- const priorityWeight = { high: 0, normal: 1, low: 2 };
180
- this.queue.sort((a, b) => {
181
- const pDiff = priorityWeight[a.priority] - priorityWeight[b.priority];
182
- if (pDiff !== 0)
183
- return pDiff;
184
- return a.addedAt - b.addedAt; // FIFO within same priority
185
- });
186
- }
187
- }