baileys-antiban 4.9.0 → 4.10.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/dist/antiban.d.ts CHANGED
@@ -32,6 +32,7 @@ import { type DeliveryTrackerStats } from './deliveryTracker.js';
32
32
  import { type InstanceCoordinatorStats } from './instanceCoordinator.js';
33
33
  import { MessageTypeRegistry } from './messageTypeRegistry.js';
34
34
  import { type AntibanSnapshot } from './stateExport.js';
35
+ import { ReputationVoucher } from './reputationVoucher.js';
35
36
  export interface AntiBanConfigLegacy {
36
37
  rateLimiter?: Partial<RateLimiterConfig>;
37
38
  warmUp?: Partial<WarmUpConfig>;
@@ -116,6 +117,8 @@ export declare class AntiBan {
116
117
  private stateManager;
117
118
  private resolvedConfig;
118
119
  private logging;
120
+ /** Optional reputation voucher (standalone — caller manages separate voucher sockets) */
121
+ reputationVoucher?: ReputationVoucher;
119
122
  private stats;
120
123
  constructor(input?: AntiBanInput | AntiBanConfigLegacy, warmUpStateArg?: WarmUpState);
121
124
  /**
package/dist/antiban.js CHANGED
@@ -117,6 +117,8 @@ export class AntiBan {
117
117
  stateManager = null;
118
118
  resolvedConfig;
119
119
  logging;
120
+ /** Optional reputation voucher (standalone — caller manages separate voucher sockets) */
121
+ reputationVoucher;
120
122
  stats = {
121
123
  messagesAllowed: 0,
122
124
  messagesBlocked: 0,
@@ -847,6 +849,7 @@ export class AntiBan {
847
849
  timelockGuard: this.timelockGuard,
848
850
  messageRegistry: this.messageTypeRegistry || undefined,
849
851
  topologyThrottler: this.topologyThrottlerModule || undefined,
852
+ reputationVoucher: this.reputationVoucher || undefined,
850
853
  instanceId: this.resolvedConfig.instanceId,
851
854
  });
852
855
  }
@@ -862,6 +865,7 @@ export class AntiBan {
862
865
  timelockGuard: this.timelockGuard,
863
866
  messageRegistry: this.messageTypeRegistry || undefined,
864
867
  topologyThrottler: this.topologyThrottlerModule || undefined,
868
+ reputationVoucher: this.reputationVoucher || undefined,
865
869
  });
866
870
  }
867
871
  /**
@@ -120,6 +120,8 @@ class AntiBan {
120
120
  stateManager = null;
121
121
  resolvedConfig;
122
122
  logging;
123
+ /** Optional reputation voucher (standalone — caller manages separate voucher sockets) */
124
+ reputationVoucher;
123
125
  stats = {
124
126
  messagesAllowed: 0,
125
127
  messagesBlocked: 0,
@@ -850,6 +852,7 @@ class AntiBan {
850
852
  timelockGuard: this.timelockGuard,
851
853
  messageRegistry: this.messageTypeRegistry || undefined,
852
854
  topologyThrottler: this.topologyThrottlerModule || undefined,
855
+ reputationVoucher: this.reputationVoucher || undefined,
853
856
  instanceId: this.resolvedConfig.instanceId,
854
857
  });
855
858
  }
@@ -865,6 +868,7 @@ class AntiBan {
865
868
  timelockGuard: this.timelockGuard,
866
869
  messageRegistry: this.messageTypeRegistry || undefined,
867
870
  topologyThrottler: this.topologyThrottlerModule || undefined,
871
+ reputationVoucher: this.reputationVoucher || undefined,
868
872
  });
869
873
  }
870
874
  /**
package/dist/cjs/index.js CHANGED
@@ -10,7 +10,7 @@
10
10
  */
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
12
  exports.getRetryJitter = exports.getTypingJitter = exports.getMessageSendJitter = exports.applySessionFingerprint = exports.generateSessionFingerprint = exports.proxyRotator = exports.readReceiptVariance = exports.credsSnapshot = exports.applyFingerprint = exports.generateFingerprint = exports.messageRecovery = exports.applyGroupMultiplier = exports.shouldUseGroupProfile = exports.isBroadcast = exports.isNewsletter = exports.isGroup = exports.StateManager = exports.PRESETS = exports.resolveConfig = exports.FileStateAdapter = exports.Scheduler = exports.WebhookAlerts = exports.ContentVariator = exports.MessageQueue = exports.wrapSocketWithFingerprint = exports.wrapSocket = exports.getRetryReasonDescription = exports.isMacError = exports.parseRetryReason = exports.MAC_ERROR_CODES = exports.MessageRetryReason = exports.createLidFirstResolver = exports.LidFirstResolver = exports.DeafSessionDetector = exports.classifyDisconnect = exports.wrapWithSessionStability = exports.SessionHealthMonitor = exports.JidCanonicalizer = exports.LidResolver = exports.PostReconnectThrottle = exports.RetryReasonTracker = exports.getCircadianMultiplier = exports.PresenceChoreographer = exports.ContactGraphWarmer = exports.ReplyRatioGuard = exports.TimelockGuard = exports.HealthMonitor = exports.WarmUp = exports.RateLimiter = exports.AntiBan = void 0;
13
- exports.createPeriodicExporter = exports.createMetricsHandler = exports.exportPrometheusMetrics = exports.createConsoleLogger = exports.TopologyThrottler = exports.importAntibanState = exports.exportAntibanState = exports.MessageTypeRegistry = exports.createHumanEntropyService = exports.HumanEntropyService = exports.createInMemoryEventStoreBackend = exports.createMySQLEventStoreBackend = exports.createFleetEventStore = exports.createJidCircuitBreaker = exports.JidCircuitBreaker = exports.InstanceCoordinator = exports.DeliveryTracker = exports.BanRecoveryOrchestrator = exports.LegitimacySignalInjector = exports.GROUP_OP_ERRORS = exports.extractPrivacyBlock = exports.classifyGroupOpError = exports.GroupOperationGuard = exports.AbortError = exports.STEALTH_BROWSER_POOL = exports.rampPresenceAfterConnect = exports.getStealthSocketConfig = exports.createStealthFingerprint = exports.getBatteryState = exports.getVoiceNoteMetadata = void 0;
13
+ exports.createPeriodicExporter = exports.createMetricsHandler = exports.exportPrometheusMetrics = exports.createConsoleLogger = exports.ReputationVoucher = exports.TopologyThrottler = exports.importAntibanState = exports.exportAntibanState = exports.MessageTypeRegistry = exports.createHumanEntropyService = exports.HumanEntropyService = exports.createInMemoryEventStoreBackend = exports.createMySQLEventStoreBackend = exports.createFleetEventStore = exports.createJidCircuitBreaker = exports.JidCircuitBreaker = exports.InstanceCoordinator = exports.DeliveryTracker = exports.BanRecoveryOrchestrator = exports.LegitimacySignalInjector = exports.GROUP_OP_ERRORS = exports.extractPrivacyBlock = exports.classifyGroupOpError = exports.GroupOperationGuard = exports.AbortError = exports.STEALTH_BROWSER_POOL = exports.rampPresenceAfterConnect = exports.getStealthSocketConfig = exports.createStealthFingerprint = exports.getBatteryState = exports.getVoiceNoteMetadata = void 0;
14
14
  // Core
15
15
  var antiban_js_1 = require("./antiban.js");
16
16
  Object.defineProperty(exports, "AntiBan", { enumerable: true, get: function () { return antiban_js_1.AntiBan; } });
@@ -151,6 +151,9 @@ Object.defineProperty(exports, "exportAntibanState", { enumerable: true, get: fu
151
151
  Object.defineProperty(exports, "importAntibanState", { enumerable: true, get: function () { return stateExport_js_1.importAntibanState; } });
152
152
  var topologyThrottler_js_1 = require("./topologyThrottler.js");
153
153
  Object.defineProperty(exports, "TopologyThrottler", { enumerable: true, get: function () { return topologyThrottler_js_1.TopologyThrottler; } });
154
+ // v4.9 new modules
155
+ var reputationVoucher_js_1 = require("./reputationVoucher.js");
156
+ Object.defineProperty(exports, "ReputationVoucher", { enumerable: true, get: function () { return reputationVoucher_js_1.ReputationVoucher; } });
154
157
  // Observability
155
158
  var observability_js_1 = require("./observability.js");
156
159
  Object.defineProperty(exports, "createConsoleLogger", { enumerable: true, get: function () { return observability_js_1.createConsoleLogger; } });
@@ -0,0 +1,377 @@
1
+ "use strict";
2
+ /**
3
+ * ReputationVoucher — High-trust accounts vouch for new numbers
4
+ *
5
+ * Establishes genuine conversation history between trusted accounts and new numbers
6
+ * before those new numbers contact customers. Reduces warmup time and makes new
7
+ * accounts appear established with real bidirectional message history.
8
+ *
9
+ * Key design principles:
10
+ * - Dedicated sacrificial vouch accounts (separate from business accounts)
11
+ * - Max 5 vouches per week per vouching account (not per day)
12
+ * - Targets must complete 3 qualifying events before vouching
13
+ * - Strike system: 3 failed vouches = 90-day suspension for voucher
14
+ * - Blast radius containment: vouching accounts isolated from main business
15
+ *
16
+ * Usage:
17
+ * const rv = new ReputationVoucher();
18
+ *
19
+ * // Register a trusted account
20
+ * rv.registerVoucher({
21
+ * jid: '27123456789@s.whatsapp.net',
22
+ * trustScore: 85,
23
+ * accountAgeDays: 240
24
+ * });
25
+ *
26
+ * // Queue a new number for vouching
27
+ * rv.queueTarget({ jid: '27987654321@s.whatsapp.net' });
28
+ *
29
+ * // Record qualifying events (auction completion, payment, etc)
30
+ * rv.recordQualifyingEvent('27987654321@s.whatsapp.net');
31
+ * rv.recordQualifyingEvent('27987654321@s.whatsapp.net');
32
+ * rv.recordQualifyingEvent('27987654321@s.whatsapp.net');
33
+ *
34
+ * // Check if target qualifies
35
+ * const check = rv.targetQualifies('27987654321@s.whatsapp.net');
36
+ * if (check.qualified) {
37
+ * const voucher = rv.getAvailableVoucher();
38
+ * if (voucher) {
39
+ * // Plan the conversation
40
+ * const conversation = rv.planVouchConversation(voucher.jid, '27987654321@s.whatsapp.net');
41
+ *
42
+ * // Execute sends (caller must have separate socket for voucher account)
43
+ * // ... send conversation.messages ...
44
+ *
45
+ * // After target replies
46
+ * rv.recordVouchOutcome('27987654321@s.whatsapp.net', true);
47
+ *
48
+ * // Calculate warmup credit
49
+ * const daysCredit = rv.calculateWarmupCredit('27987654321@s.whatsapp.net');
50
+ * // Skip daysCredit days of warmup for this target
51
+ * }
52
+ * }
53
+ */
54
+ Object.defineProperty(exports, "__esModule", { value: true });
55
+ exports.ReputationVoucher = void 0;
56
+ const DEFAULT_CONFIG = {
57
+ maxVouchesPerWeek: 5,
58
+ qualifyingEventsRequired: 3,
59
+ strikesForSuspension: 3,
60
+ suspensionDurationMs: 90 * 24 * 60 * 60 * 1000, // 90 days
61
+ minVoucherTrustScore: 60,
62
+ minVoucherAgeDays: 180, // 6 months
63
+ warmupMessages: [
64
+ "Hey, just checking this is the right number?",
65
+ "Hi! Got your details from the group",
66
+ "Morning! Are you available this week?",
67
+ "Hey there, hope this is a good time to connect",
68
+ "Hi, just wanted to reach out",
69
+ ],
70
+ };
71
+ class ReputationVoucher {
72
+ config;
73
+ vouchers = new Map();
74
+ targets = new Map();
75
+ conversations = [];
76
+ constructor(config) {
77
+ this.config = { ...DEFAULT_CONFIG, ...config };
78
+ }
79
+ /**
80
+ * Register a vouching account (high-trust, established number)
81
+ */
82
+ registerVoucher(account) {
83
+ // Validate account meets minimum requirements
84
+ if (account.trustScore < this.config.minVoucherTrustScore) {
85
+ throw new Error(`Voucher trust score ${account.trustScore} below minimum ${this.config.minVoucherTrustScore}`);
86
+ }
87
+ if (account.accountAgeDays < this.config.minVoucherAgeDays) {
88
+ throw new Error(`Voucher account age ${account.accountAgeDays} days below minimum ${this.config.minVoucherAgeDays} days`);
89
+ }
90
+ const existing = this.vouchers.get(account.jid);
91
+ if (existing) {
92
+ // Update existing voucher (preserve counters)
93
+ this.vouchers.set(account.jid, {
94
+ ...account,
95
+ vouchesThisWeek: existing.vouchesThisWeek,
96
+ totalVouches: existing.totalVouches,
97
+ failedVouches: existing.failedVouches,
98
+ strikes: existing.strikes,
99
+ suspendedUntil: existing.suspendedUntil,
100
+ lastVouchAt: existing.lastVouchAt,
101
+ });
102
+ }
103
+ else {
104
+ // New voucher
105
+ this.vouchers.set(account.jid, {
106
+ ...account,
107
+ vouchesThisWeek: 0,
108
+ totalVouches: 0,
109
+ failedVouches: 0,
110
+ strikes: 0,
111
+ });
112
+ }
113
+ }
114
+ /**
115
+ * Queue a target for vouching
116
+ */
117
+ queueTarget(target) {
118
+ const existing = this.targets.get(target.jid);
119
+ if (existing && existing.status !== 'failed') {
120
+ // Already queued or in progress
121
+ return;
122
+ }
123
+ this.targets.set(target.jid, {
124
+ ...target,
125
+ qualifyingEvents: target.qualifyingEvents || 0,
126
+ requestedAt: Date.now(),
127
+ status: 'pending',
128
+ });
129
+ }
130
+ /**
131
+ * Check if a target qualifies for vouching
132
+ */
133
+ targetQualifies(jid) {
134
+ const target = this.targets.get(jid);
135
+ if (!target) {
136
+ return { qualified: false, reason: 'Target not queued', eventsNeeded: this.config.qualifyingEventsRequired };
137
+ }
138
+ const events = target.qualifyingEvents || 0;
139
+ if (events < this.config.qualifyingEventsRequired) {
140
+ return {
141
+ qualified: false,
142
+ reason: `Not enough qualifying events (${events}/${this.config.qualifyingEventsRequired})`,
143
+ eventsNeeded: this.config.qualifyingEventsRequired - events,
144
+ };
145
+ }
146
+ if (target.status === 'completed') {
147
+ return { qualified: false, reason: 'Already vouched' };
148
+ }
149
+ if (target.status === 'failed') {
150
+ return { qualified: false, reason: 'Previous vouch failed' };
151
+ }
152
+ return { qualified: true };
153
+ }
154
+ /**
155
+ * Record a qualifying event for a target (e.g., auction completion, payment cleared)
156
+ */
157
+ recordQualifyingEvent(jid) {
158
+ const target = this.targets.get(jid);
159
+ if (!target) {
160
+ // Auto-queue with first event
161
+ this.queueTarget({ jid, qualifyingEvents: 1 });
162
+ return;
163
+ }
164
+ target.qualifyingEvents = (target.qualifyingEvents || 0) + 1;
165
+ }
166
+ /**
167
+ * Get next available voucher for a target (respects limits + suspension)
168
+ */
169
+ getAvailableVoucher() {
170
+ const now = Date.now();
171
+ const weekMs = 7 * 24 * 60 * 60 * 1000;
172
+ // Find eligible vouchers
173
+ const eligible = [];
174
+ for (const voucher of this.vouchers.values()) {
175
+ // Check suspension
176
+ if (voucher.suspendedUntil && voucher.suspendedUntil > now) {
177
+ continue;
178
+ }
179
+ // Reset weekly counter if 7 days have passed since last vouch
180
+ if (voucher.lastVouchAt && now - voucher.lastVouchAt > weekMs) {
181
+ voucher.vouchesThisWeek = 0;
182
+ }
183
+ // Check weekly limit
184
+ if (voucher.vouchesThisWeek >= this.config.maxVouchesPerWeek) {
185
+ continue;
186
+ }
187
+ eligible.push(voucher);
188
+ }
189
+ if (eligible.length === 0) {
190
+ return null;
191
+ }
192
+ // Sort by:
193
+ // 1. Lowest vouchesThisWeek (spread load)
194
+ // 2. Highest trustScore (best vouchers first)
195
+ // 3. Lowest totalVouches (rotate usage)
196
+ eligible.sort((a, b) => {
197
+ if (a.vouchesThisWeek !== b.vouchesThisWeek) {
198
+ return a.vouchesThisWeek - b.vouchesThisWeek;
199
+ }
200
+ if (a.trustScore !== b.trustScore) {
201
+ return b.trustScore - a.trustScore;
202
+ }
203
+ return a.totalVouches - b.totalVouches;
204
+ });
205
+ return eligible[0];
206
+ }
207
+ /**
208
+ * Plan a vouch conversation.
209
+ * Returns a conversation plan — caller executes the sends.
210
+ */
211
+ planVouchConversation(voucherJid, targetJid) {
212
+ const voucher = this.vouchers.get(voucherJid);
213
+ if (!voucher) {
214
+ throw new Error(`Voucher ${voucherJid} not registered`);
215
+ }
216
+ const target = this.targets.get(targetJid);
217
+ if (!target) {
218
+ throw new Error(`Target ${targetJid} not queued`);
219
+ }
220
+ const check = this.targetQualifies(targetJid);
221
+ if (!check.qualified) {
222
+ throw new Error(`Target ${targetJid} not qualified: ${check.reason}`);
223
+ }
224
+ // Update voucher counters
225
+ voucher.vouchesThisWeek++;
226
+ voucher.totalVouches++;
227
+ voucher.lastVouchAt = Date.now();
228
+ // Update target status
229
+ target.status = 'active';
230
+ target.vouchedAt = Date.now();
231
+ target.vouchedBy = voucherJid;
232
+ // Pick 2-3 warmup messages randomly
233
+ const messageCount = 2 + Math.floor(Math.random() * 2); // 2-3 messages
234
+ const selectedMessages = [];
235
+ const pool = [...this.config.warmupMessages];
236
+ for (let i = 0; i < messageCount && pool.length > 0; i++) {
237
+ const idx = Math.floor(Math.random() * pool.length);
238
+ selectedMessages.push(pool[idx]);
239
+ pool.splice(idx, 1); // Remove to avoid duplicates
240
+ }
241
+ // Create conversation plan
242
+ const conversation = {
243
+ targetJid,
244
+ voucherJid,
245
+ messages: selectedMessages.map((text, idx) => ({
246
+ direction: 'outbound',
247
+ timestamp: Date.now() + idx * 5000, // Stagger by 5s
248
+ text,
249
+ })),
250
+ startedAt: Date.now(),
251
+ success: false, // Will be updated when recordVouchOutcome is called
252
+ };
253
+ this.conversations.push(conversation);
254
+ return conversation;
255
+ }
256
+ /**
257
+ * Record outcome of a vouch.
258
+ * success = true if target replied within reasonable time (e.g., 7 days)
259
+ * success = false if target got banned within 7 days of vouch
260
+ */
261
+ recordVouchOutcome(targetJid, success) {
262
+ const target = this.targets.get(targetJid);
263
+ if (!target || !target.vouchedBy) {
264
+ throw new Error(`Target ${targetJid} not vouched`);
265
+ }
266
+ const voucher = this.vouchers.get(target.vouchedBy);
267
+ if (!voucher) {
268
+ throw new Error(`Voucher ${target.vouchedBy} not found`);
269
+ }
270
+ // Find the conversation
271
+ const conversation = this.conversations.find((c) => c.targetJid === targetJid && c.voucherJid === target.vouchedBy);
272
+ if (conversation) {
273
+ conversation.success = success;
274
+ conversation.completedAt = Date.now();
275
+ }
276
+ if (success) {
277
+ target.status = 'completed';
278
+ }
279
+ else {
280
+ // Failed vouch — apply strike to voucher
281
+ target.status = 'failed';
282
+ voucher.failedVouches++;
283
+ voucher.strikes++;
284
+ // Check if suspension threshold reached
285
+ if (voucher.strikes >= this.config.strikesForSuspension) {
286
+ voucher.suspendedUntil = Date.now() + this.config.suspensionDurationMs;
287
+ }
288
+ }
289
+ }
290
+ /**
291
+ * Calculate warmup credit for a successfully vouched target.
292
+ * Returns number of warmup days that can be skipped (0-3).
293
+ *
294
+ * Credit logic:
295
+ * - 1 reply = 1 day credit
296
+ * - 2+ replies = 2 days credit
297
+ * - Reply + 3+ days elapsed = 3 days credit (max)
298
+ */
299
+ calculateWarmupCredit(targetJid) {
300
+ const target = this.targets.get(targetJid);
301
+ if (!target || target.status !== 'completed') {
302
+ return 0;
303
+ }
304
+ const conversation = this.conversations.find((c) => c.targetJid === targetJid && c.success);
305
+ if (!conversation) {
306
+ return 0;
307
+ }
308
+ // Count inbound messages (replies from target)
309
+ const inboundCount = conversation.messages.filter((m) => m.direction === 'inbound').length;
310
+ if (inboundCount === 0) {
311
+ return 0;
312
+ }
313
+ if (inboundCount === 1) {
314
+ return 1;
315
+ }
316
+ if (inboundCount >= 2) {
317
+ // Check if 3+ days elapsed
318
+ const daysElapsed = (Date.now() - conversation.startedAt) / (24 * 60 * 60 * 1000);
319
+ if (daysElapsed >= 3) {
320
+ return 3; // Max credit
321
+ }
322
+ return 2;
323
+ }
324
+ return 0;
325
+ }
326
+ /**
327
+ * Get stats for a specific voucher
328
+ */
329
+ getVoucherStats(jid) {
330
+ const voucher = this.vouchers.get(jid);
331
+ return voucher ? { ...voucher } : null;
332
+ }
333
+ /**
334
+ * Get all pending targets (awaiting vouching)
335
+ */
336
+ getPendingTargets() {
337
+ return Array.from(this.targets.values()).filter((t) => t.status === 'pending');
338
+ }
339
+ /**
340
+ * Get vouch history
341
+ */
342
+ getVouchHistory() {
343
+ return [...this.conversations];
344
+ }
345
+ /**
346
+ * Export state for persistence
347
+ */
348
+ exportState() {
349
+ return {
350
+ version: 1,
351
+ exportedAt: Date.now(),
352
+ vouchers: Array.from(this.vouchers.values()),
353
+ targets: Array.from(this.targets.values()),
354
+ conversations: [...this.conversations],
355
+ };
356
+ }
357
+ /**
358
+ * Import state from persistence
359
+ */
360
+ importState(state) {
361
+ // Clear existing state
362
+ this.vouchers.clear();
363
+ this.targets.clear();
364
+ this.conversations = [];
365
+ // Import vouchers
366
+ for (const voucher of state.vouchers) {
367
+ this.vouchers.set(voucher.jid, { ...voucher });
368
+ }
369
+ // Import targets
370
+ for (const target of state.targets) {
371
+ this.targets.set(target.jid, { ...target });
372
+ }
373
+ // Import conversations
374
+ this.conversations = [...state.conversations];
375
+ }
376
+ }
377
+ exports.ReputationVoucher = ReputationVoucher;
@@ -75,6 +75,10 @@ function exportAntibanState(modules) {
75
75
  if (modules.topologyThrottler) {
76
76
  snapshot.topologyThrottler = modules.topologyThrottler.exportState();
77
77
  }
78
+ // Export reputation voucher state
79
+ if (modules.reputationVoucher) {
80
+ snapshot.reputationVoucher = modules.reputationVoucher.exportState();
81
+ }
78
82
  // Export engagement scores
79
83
  if (modules.engagementScores) {
80
84
  snapshot.engagementScores = Object.fromEntries(modules.engagementScores.entries());
@@ -152,6 +156,10 @@ function importAntibanState(snapshot, modules) {
152
156
  if (snapshot.topologyThrottler && modules.topologyThrottler) {
153
157
  modules.topologyThrottler.importState(snapshot.topologyThrottler);
154
158
  }
159
+ // Import reputation voucher state
160
+ if (snapshot.reputationVoucher && modules.reputationVoucher) {
161
+ modules.reputationVoucher.importState(snapshot.reputationVoucher);
162
+ }
155
163
  // Import engagement scores
156
164
  if (snapshot.engagementScores && modules.engagementScores) {
157
165
  for (const [jid, score] of Object.entries(snapshot.engagementScores)) {
package/dist/index.d.ts CHANGED
@@ -49,4 +49,5 @@ export { HumanEntropyService, createHumanEntropyService, type HumanEntropyConfig
49
49
  export { MessageTypeRegistry, type MessageTypeDefinition, type MessageProvenance, type MessageTypeStats, type MessageTypeWarning, type MessageTypeRegistryState } from './messageTypeRegistry.js';
50
50
  export { exportAntibanState, importAntibanState, type AntibanSnapshot, type RateLimiterState, type TimelockGuardState, type CircuitState as CircuitStateExport, type DisconnectEvent } from './stateExport.js';
51
51
  export { TopologyThrottler, type TopologyThrottlerConfig, type ContactRisk, type ContactRiskAssessment, type ContactRiskConfig, type TopologyThrottlerState } from './topologyThrottler.js';
52
+ export { ReputationVoucher, type ReputationVoucherConfig, type VouchTarget, type VouchingAccount, type VouchConversation, type ReputationVoucherState } from './reputationVoucher.js';
52
53
  export { createConsoleLogger, exportPrometheusMetrics, createMetricsHandler, createPeriodicExporter, type AntiBanLogger, type PeriodicExporterConfig, type PeriodicExporterHandle, } from './observability.js';
package/dist/index.js CHANGED
@@ -71,5 +71,7 @@ export { HumanEntropyService, createHumanEntropyService } from './humanEntropy.j
71
71
  export { MessageTypeRegistry } from './messageTypeRegistry.js';
72
72
  export { exportAntibanState, importAntibanState } from './stateExport.js';
73
73
  export { TopologyThrottler } from './topologyThrottler.js';
74
+ // v4.9 new modules
75
+ export { ReputationVoucher } from './reputationVoucher.js';
74
76
  // Observability
75
77
  export { createConsoleLogger, exportPrometheusMetrics, createMetricsHandler, createPeriodicExporter, } from './observability.js';
@@ -0,0 +1,171 @@
1
+ /**
2
+ * ReputationVoucher — High-trust accounts vouch for new numbers
3
+ *
4
+ * Establishes genuine conversation history between trusted accounts and new numbers
5
+ * before those new numbers contact customers. Reduces warmup time and makes new
6
+ * accounts appear established with real bidirectional message history.
7
+ *
8
+ * Key design principles:
9
+ * - Dedicated sacrificial vouch accounts (separate from business accounts)
10
+ * - Max 5 vouches per week per vouching account (not per day)
11
+ * - Targets must complete 3 qualifying events before vouching
12
+ * - Strike system: 3 failed vouches = 90-day suspension for voucher
13
+ * - Blast radius containment: vouching accounts isolated from main business
14
+ *
15
+ * Usage:
16
+ * const rv = new ReputationVoucher();
17
+ *
18
+ * // Register a trusted account
19
+ * rv.registerVoucher({
20
+ * jid: '27123456789@s.whatsapp.net',
21
+ * trustScore: 85,
22
+ * accountAgeDays: 240
23
+ * });
24
+ *
25
+ * // Queue a new number for vouching
26
+ * rv.queueTarget({ jid: '27987654321@s.whatsapp.net' });
27
+ *
28
+ * // Record qualifying events (auction completion, payment, etc)
29
+ * rv.recordQualifyingEvent('27987654321@s.whatsapp.net');
30
+ * rv.recordQualifyingEvent('27987654321@s.whatsapp.net');
31
+ * rv.recordQualifyingEvent('27987654321@s.whatsapp.net');
32
+ *
33
+ * // Check if target qualifies
34
+ * const check = rv.targetQualifies('27987654321@s.whatsapp.net');
35
+ * if (check.qualified) {
36
+ * const voucher = rv.getAvailableVoucher();
37
+ * if (voucher) {
38
+ * // Plan the conversation
39
+ * const conversation = rv.planVouchConversation(voucher.jid, '27987654321@s.whatsapp.net');
40
+ *
41
+ * // Execute sends (caller must have separate socket for voucher account)
42
+ * // ... send conversation.messages ...
43
+ *
44
+ * // After target replies
45
+ * rv.recordVouchOutcome('27987654321@s.whatsapp.net', true);
46
+ *
47
+ * // Calculate warmup credit
48
+ * const daysCredit = rv.calculateWarmupCredit('27987654321@s.whatsapp.net');
49
+ * // Skip daysCredit days of warmup for this target
50
+ * }
51
+ * }
52
+ */
53
+ export interface VouchTarget {
54
+ jid: string;
55
+ qualifyingEvents?: number;
56
+ requestedAt: number;
57
+ vouchedAt?: number;
58
+ vouchedBy?: string;
59
+ status: 'pending' | 'active' | 'completed' | 'failed';
60
+ }
61
+ export interface VouchingAccount {
62
+ jid: string;
63
+ trustScore: number;
64
+ accountAgeDays: number;
65
+ vouchesThisWeek: number;
66
+ totalVouches: number;
67
+ failedVouches: number;
68
+ strikes: number;
69
+ suspendedUntil?: number;
70
+ lastVouchAt?: number;
71
+ }
72
+ export interface VouchConversation {
73
+ targetJid: string;
74
+ voucherJid: string;
75
+ messages: Array<{
76
+ direction: 'outbound' | 'inbound';
77
+ timestamp: number;
78
+ text: string;
79
+ }>;
80
+ startedAt: number;
81
+ completedAt?: number;
82
+ success: boolean;
83
+ }
84
+ export interface ReputationVoucherConfig {
85
+ maxVouchesPerWeek?: number;
86
+ qualifyingEventsRequired?: number;
87
+ strikesForSuspension?: number;
88
+ suspensionDurationMs?: number;
89
+ minVoucherTrustScore?: number;
90
+ minVoucherAgeDays?: number;
91
+ warmupMessages?: string[];
92
+ }
93
+ export interface ReputationVoucherState {
94
+ version: number;
95
+ exportedAt: number;
96
+ vouchers: VouchingAccount[];
97
+ targets: VouchTarget[];
98
+ conversations: VouchConversation[];
99
+ }
100
+ export declare class ReputationVoucher {
101
+ private config;
102
+ private vouchers;
103
+ private targets;
104
+ private conversations;
105
+ constructor(config?: ReputationVoucherConfig);
106
+ /**
107
+ * Register a vouching account (high-trust, established number)
108
+ */
109
+ registerVoucher(account: Omit<VouchingAccount, 'vouchesThisWeek' | 'totalVouches' | 'failedVouches' | 'strikes'>): void;
110
+ /**
111
+ * Queue a target for vouching
112
+ */
113
+ queueTarget(target: Omit<VouchTarget, 'status' | 'requestedAt'>): void;
114
+ /**
115
+ * Check if a target qualifies for vouching
116
+ */
117
+ targetQualifies(jid: string): {
118
+ qualified: boolean;
119
+ reason?: string;
120
+ eventsNeeded?: number;
121
+ };
122
+ /**
123
+ * Record a qualifying event for a target (e.g., auction completion, payment cleared)
124
+ */
125
+ recordQualifyingEvent(jid: string): void;
126
+ /**
127
+ * Get next available voucher for a target (respects limits + suspension)
128
+ */
129
+ getAvailableVoucher(): VouchingAccount | null;
130
+ /**
131
+ * Plan a vouch conversation.
132
+ * Returns a conversation plan — caller executes the sends.
133
+ */
134
+ planVouchConversation(voucherJid: string, targetJid: string): VouchConversation;
135
+ /**
136
+ * Record outcome of a vouch.
137
+ * success = true if target replied within reasonable time (e.g., 7 days)
138
+ * success = false if target got banned within 7 days of vouch
139
+ */
140
+ recordVouchOutcome(targetJid: string, success: boolean): void;
141
+ /**
142
+ * Calculate warmup credit for a successfully vouched target.
143
+ * Returns number of warmup days that can be skipped (0-3).
144
+ *
145
+ * Credit logic:
146
+ * - 1 reply = 1 day credit
147
+ * - 2+ replies = 2 days credit
148
+ * - Reply + 3+ days elapsed = 3 days credit (max)
149
+ */
150
+ calculateWarmupCredit(targetJid: string): number;
151
+ /**
152
+ * Get stats for a specific voucher
153
+ */
154
+ getVoucherStats(jid: string): VouchingAccount | null;
155
+ /**
156
+ * Get all pending targets (awaiting vouching)
157
+ */
158
+ getPendingTargets(): VouchTarget[];
159
+ /**
160
+ * Get vouch history
161
+ */
162
+ getVouchHistory(): VouchConversation[];
163
+ /**
164
+ * Export state for persistence
165
+ */
166
+ exportState(): ReputationVoucherState;
167
+ /**
168
+ * Import state from persistence
169
+ */
170
+ importState(state: ReputationVoucherState): void;
171
+ }
@@ -0,0 +1,373 @@
1
+ /**
2
+ * ReputationVoucher — High-trust accounts vouch for new numbers
3
+ *
4
+ * Establishes genuine conversation history between trusted accounts and new numbers
5
+ * before those new numbers contact customers. Reduces warmup time and makes new
6
+ * accounts appear established with real bidirectional message history.
7
+ *
8
+ * Key design principles:
9
+ * - Dedicated sacrificial vouch accounts (separate from business accounts)
10
+ * - Max 5 vouches per week per vouching account (not per day)
11
+ * - Targets must complete 3 qualifying events before vouching
12
+ * - Strike system: 3 failed vouches = 90-day suspension for voucher
13
+ * - Blast radius containment: vouching accounts isolated from main business
14
+ *
15
+ * Usage:
16
+ * const rv = new ReputationVoucher();
17
+ *
18
+ * // Register a trusted account
19
+ * rv.registerVoucher({
20
+ * jid: '27123456789@s.whatsapp.net',
21
+ * trustScore: 85,
22
+ * accountAgeDays: 240
23
+ * });
24
+ *
25
+ * // Queue a new number for vouching
26
+ * rv.queueTarget({ jid: '27987654321@s.whatsapp.net' });
27
+ *
28
+ * // Record qualifying events (auction completion, payment, etc)
29
+ * rv.recordQualifyingEvent('27987654321@s.whatsapp.net');
30
+ * rv.recordQualifyingEvent('27987654321@s.whatsapp.net');
31
+ * rv.recordQualifyingEvent('27987654321@s.whatsapp.net');
32
+ *
33
+ * // Check if target qualifies
34
+ * const check = rv.targetQualifies('27987654321@s.whatsapp.net');
35
+ * if (check.qualified) {
36
+ * const voucher = rv.getAvailableVoucher();
37
+ * if (voucher) {
38
+ * // Plan the conversation
39
+ * const conversation = rv.planVouchConversation(voucher.jid, '27987654321@s.whatsapp.net');
40
+ *
41
+ * // Execute sends (caller must have separate socket for voucher account)
42
+ * // ... send conversation.messages ...
43
+ *
44
+ * // After target replies
45
+ * rv.recordVouchOutcome('27987654321@s.whatsapp.net', true);
46
+ *
47
+ * // Calculate warmup credit
48
+ * const daysCredit = rv.calculateWarmupCredit('27987654321@s.whatsapp.net');
49
+ * // Skip daysCredit days of warmup for this target
50
+ * }
51
+ * }
52
+ */
53
+ const DEFAULT_CONFIG = {
54
+ maxVouchesPerWeek: 5,
55
+ qualifyingEventsRequired: 3,
56
+ strikesForSuspension: 3,
57
+ suspensionDurationMs: 90 * 24 * 60 * 60 * 1000, // 90 days
58
+ minVoucherTrustScore: 60,
59
+ minVoucherAgeDays: 180, // 6 months
60
+ warmupMessages: [
61
+ "Hey, just checking this is the right number?",
62
+ "Hi! Got your details from the group",
63
+ "Morning! Are you available this week?",
64
+ "Hey there, hope this is a good time to connect",
65
+ "Hi, just wanted to reach out",
66
+ ],
67
+ };
68
+ export class ReputationVoucher {
69
+ config;
70
+ vouchers = new Map();
71
+ targets = new Map();
72
+ conversations = [];
73
+ constructor(config) {
74
+ this.config = { ...DEFAULT_CONFIG, ...config };
75
+ }
76
+ /**
77
+ * Register a vouching account (high-trust, established number)
78
+ */
79
+ registerVoucher(account) {
80
+ // Validate account meets minimum requirements
81
+ if (account.trustScore < this.config.minVoucherTrustScore) {
82
+ throw new Error(`Voucher trust score ${account.trustScore} below minimum ${this.config.minVoucherTrustScore}`);
83
+ }
84
+ if (account.accountAgeDays < this.config.minVoucherAgeDays) {
85
+ throw new Error(`Voucher account age ${account.accountAgeDays} days below minimum ${this.config.minVoucherAgeDays} days`);
86
+ }
87
+ const existing = this.vouchers.get(account.jid);
88
+ if (existing) {
89
+ // Update existing voucher (preserve counters)
90
+ this.vouchers.set(account.jid, {
91
+ ...account,
92
+ vouchesThisWeek: existing.vouchesThisWeek,
93
+ totalVouches: existing.totalVouches,
94
+ failedVouches: existing.failedVouches,
95
+ strikes: existing.strikes,
96
+ suspendedUntil: existing.suspendedUntil,
97
+ lastVouchAt: existing.lastVouchAt,
98
+ });
99
+ }
100
+ else {
101
+ // New voucher
102
+ this.vouchers.set(account.jid, {
103
+ ...account,
104
+ vouchesThisWeek: 0,
105
+ totalVouches: 0,
106
+ failedVouches: 0,
107
+ strikes: 0,
108
+ });
109
+ }
110
+ }
111
+ /**
112
+ * Queue a target for vouching
113
+ */
114
+ queueTarget(target) {
115
+ const existing = this.targets.get(target.jid);
116
+ if (existing && existing.status !== 'failed') {
117
+ // Already queued or in progress
118
+ return;
119
+ }
120
+ this.targets.set(target.jid, {
121
+ ...target,
122
+ qualifyingEvents: target.qualifyingEvents || 0,
123
+ requestedAt: Date.now(),
124
+ status: 'pending',
125
+ });
126
+ }
127
+ /**
128
+ * Check if a target qualifies for vouching
129
+ */
130
+ targetQualifies(jid) {
131
+ const target = this.targets.get(jid);
132
+ if (!target) {
133
+ return { qualified: false, reason: 'Target not queued', eventsNeeded: this.config.qualifyingEventsRequired };
134
+ }
135
+ const events = target.qualifyingEvents || 0;
136
+ if (events < this.config.qualifyingEventsRequired) {
137
+ return {
138
+ qualified: false,
139
+ reason: `Not enough qualifying events (${events}/${this.config.qualifyingEventsRequired})`,
140
+ eventsNeeded: this.config.qualifyingEventsRequired - events,
141
+ };
142
+ }
143
+ if (target.status === 'completed') {
144
+ return { qualified: false, reason: 'Already vouched' };
145
+ }
146
+ if (target.status === 'failed') {
147
+ return { qualified: false, reason: 'Previous vouch failed' };
148
+ }
149
+ return { qualified: true };
150
+ }
151
+ /**
152
+ * Record a qualifying event for a target (e.g., auction completion, payment cleared)
153
+ */
154
+ recordQualifyingEvent(jid) {
155
+ const target = this.targets.get(jid);
156
+ if (!target) {
157
+ // Auto-queue with first event
158
+ this.queueTarget({ jid, qualifyingEvents: 1 });
159
+ return;
160
+ }
161
+ target.qualifyingEvents = (target.qualifyingEvents || 0) + 1;
162
+ }
163
+ /**
164
+ * Get next available voucher for a target (respects limits + suspension)
165
+ */
166
+ getAvailableVoucher() {
167
+ const now = Date.now();
168
+ const weekMs = 7 * 24 * 60 * 60 * 1000;
169
+ // Find eligible vouchers
170
+ const eligible = [];
171
+ for (const voucher of this.vouchers.values()) {
172
+ // Check suspension
173
+ if (voucher.suspendedUntil && voucher.suspendedUntil > now) {
174
+ continue;
175
+ }
176
+ // Reset weekly counter if 7 days have passed since last vouch
177
+ if (voucher.lastVouchAt && now - voucher.lastVouchAt > weekMs) {
178
+ voucher.vouchesThisWeek = 0;
179
+ }
180
+ // Check weekly limit
181
+ if (voucher.vouchesThisWeek >= this.config.maxVouchesPerWeek) {
182
+ continue;
183
+ }
184
+ eligible.push(voucher);
185
+ }
186
+ if (eligible.length === 0) {
187
+ return null;
188
+ }
189
+ // Sort by:
190
+ // 1. Lowest vouchesThisWeek (spread load)
191
+ // 2. Highest trustScore (best vouchers first)
192
+ // 3. Lowest totalVouches (rotate usage)
193
+ eligible.sort((a, b) => {
194
+ if (a.vouchesThisWeek !== b.vouchesThisWeek) {
195
+ return a.vouchesThisWeek - b.vouchesThisWeek;
196
+ }
197
+ if (a.trustScore !== b.trustScore) {
198
+ return b.trustScore - a.trustScore;
199
+ }
200
+ return a.totalVouches - b.totalVouches;
201
+ });
202
+ return eligible[0];
203
+ }
204
+ /**
205
+ * Plan a vouch conversation.
206
+ * Returns a conversation plan — caller executes the sends.
207
+ */
208
+ planVouchConversation(voucherJid, targetJid) {
209
+ const voucher = this.vouchers.get(voucherJid);
210
+ if (!voucher) {
211
+ throw new Error(`Voucher ${voucherJid} not registered`);
212
+ }
213
+ const target = this.targets.get(targetJid);
214
+ if (!target) {
215
+ throw new Error(`Target ${targetJid} not queued`);
216
+ }
217
+ const check = this.targetQualifies(targetJid);
218
+ if (!check.qualified) {
219
+ throw new Error(`Target ${targetJid} not qualified: ${check.reason}`);
220
+ }
221
+ // Update voucher counters
222
+ voucher.vouchesThisWeek++;
223
+ voucher.totalVouches++;
224
+ voucher.lastVouchAt = Date.now();
225
+ // Update target status
226
+ target.status = 'active';
227
+ target.vouchedAt = Date.now();
228
+ target.vouchedBy = voucherJid;
229
+ // Pick 2-3 warmup messages randomly
230
+ const messageCount = 2 + Math.floor(Math.random() * 2); // 2-3 messages
231
+ const selectedMessages = [];
232
+ const pool = [...this.config.warmupMessages];
233
+ for (let i = 0; i < messageCount && pool.length > 0; i++) {
234
+ const idx = Math.floor(Math.random() * pool.length);
235
+ selectedMessages.push(pool[idx]);
236
+ pool.splice(idx, 1); // Remove to avoid duplicates
237
+ }
238
+ // Create conversation plan
239
+ const conversation = {
240
+ targetJid,
241
+ voucherJid,
242
+ messages: selectedMessages.map((text, idx) => ({
243
+ direction: 'outbound',
244
+ timestamp: Date.now() + idx * 5000, // Stagger by 5s
245
+ text,
246
+ })),
247
+ startedAt: Date.now(),
248
+ success: false, // Will be updated when recordVouchOutcome is called
249
+ };
250
+ this.conversations.push(conversation);
251
+ return conversation;
252
+ }
253
+ /**
254
+ * Record outcome of a vouch.
255
+ * success = true if target replied within reasonable time (e.g., 7 days)
256
+ * success = false if target got banned within 7 days of vouch
257
+ */
258
+ recordVouchOutcome(targetJid, success) {
259
+ const target = this.targets.get(targetJid);
260
+ if (!target || !target.vouchedBy) {
261
+ throw new Error(`Target ${targetJid} not vouched`);
262
+ }
263
+ const voucher = this.vouchers.get(target.vouchedBy);
264
+ if (!voucher) {
265
+ throw new Error(`Voucher ${target.vouchedBy} not found`);
266
+ }
267
+ // Find the conversation
268
+ const conversation = this.conversations.find((c) => c.targetJid === targetJid && c.voucherJid === target.vouchedBy);
269
+ if (conversation) {
270
+ conversation.success = success;
271
+ conversation.completedAt = Date.now();
272
+ }
273
+ if (success) {
274
+ target.status = 'completed';
275
+ }
276
+ else {
277
+ // Failed vouch — apply strike to voucher
278
+ target.status = 'failed';
279
+ voucher.failedVouches++;
280
+ voucher.strikes++;
281
+ // Check if suspension threshold reached
282
+ if (voucher.strikes >= this.config.strikesForSuspension) {
283
+ voucher.suspendedUntil = Date.now() + this.config.suspensionDurationMs;
284
+ }
285
+ }
286
+ }
287
+ /**
288
+ * Calculate warmup credit for a successfully vouched target.
289
+ * Returns number of warmup days that can be skipped (0-3).
290
+ *
291
+ * Credit logic:
292
+ * - 1 reply = 1 day credit
293
+ * - 2+ replies = 2 days credit
294
+ * - Reply + 3+ days elapsed = 3 days credit (max)
295
+ */
296
+ calculateWarmupCredit(targetJid) {
297
+ const target = this.targets.get(targetJid);
298
+ if (!target || target.status !== 'completed') {
299
+ return 0;
300
+ }
301
+ const conversation = this.conversations.find((c) => c.targetJid === targetJid && c.success);
302
+ if (!conversation) {
303
+ return 0;
304
+ }
305
+ // Count inbound messages (replies from target)
306
+ const inboundCount = conversation.messages.filter((m) => m.direction === 'inbound').length;
307
+ if (inboundCount === 0) {
308
+ return 0;
309
+ }
310
+ if (inboundCount === 1) {
311
+ return 1;
312
+ }
313
+ if (inboundCount >= 2) {
314
+ // Check if 3+ days elapsed
315
+ const daysElapsed = (Date.now() - conversation.startedAt) / (24 * 60 * 60 * 1000);
316
+ if (daysElapsed >= 3) {
317
+ return 3; // Max credit
318
+ }
319
+ return 2;
320
+ }
321
+ return 0;
322
+ }
323
+ /**
324
+ * Get stats for a specific voucher
325
+ */
326
+ getVoucherStats(jid) {
327
+ const voucher = this.vouchers.get(jid);
328
+ return voucher ? { ...voucher } : null;
329
+ }
330
+ /**
331
+ * Get all pending targets (awaiting vouching)
332
+ */
333
+ getPendingTargets() {
334
+ return Array.from(this.targets.values()).filter((t) => t.status === 'pending');
335
+ }
336
+ /**
337
+ * Get vouch history
338
+ */
339
+ getVouchHistory() {
340
+ return [...this.conversations];
341
+ }
342
+ /**
343
+ * Export state for persistence
344
+ */
345
+ exportState() {
346
+ return {
347
+ version: 1,
348
+ exportedAt: Date.now(),
349
+ vouchers: Array.from(this.vouchers.values()),
350
+ targets: Array.from(this.targets.values()),
351
+ conversations: [...this.conversations],
352
+ };
353
+ }
354
+ /**
355
+ * Import state from persistence
356
+ */
357
+ importState(state) {
358
+ // Clear existing state
359
+ this.vouchers.clear();
360
+ this.targets.clear();
361
+ this.conversations = [];
362
+ // Import vouchers
363
+ for (const voucher of state.vouchers) {
364
+ this.vouchers.set(voucher.jid, { ...voucher });
365
+ }
366
+ // Import targets
367
+ for (const target of state.targets) {
368
+ this.targets.set(target.jid, { ...target });
369
+ }
370
+ // Import conversations
371
+ this.conversations = [...state.conversations];
372
+ }
373
+ }
@@ -13,6 +13,7 @@
13
13
  import type { WarmUpState } from './warmup.js';
14
14
  import type { MessageTypeRegistryState } from './messageTypeRegistry.js';
15
15
  import type { TopologyThrottlerState } from './topologyThrottler.js';
16
+ import type { ReputationVoucherState } from './reputationVoucher.js';
16
17
  export interface DisconnectEvent {
17
18
  type: 'disconnect' | 'forbidden' | 'loggedOut' | 'messageFailed' | 'reconnect' | 'reachoutTimelocked';
18
19
  timestamp: number;
@@ -78,6 +79,8 @@ export interface AntibanSnapshot {
78
79
  messageRegistry?: MessageTypeRegistryState;
79
80
  /** Topology throttler state */
80
81
  topologyThrottler?: TopologyThrottlerState;
82
+ /** Reputation voucher state */
83
+ reputationVoucher?: ReputationVoucherState;
81
84
  /** Per-JID engagement scores */
82
85
  engagementScores?: Record<string, number>;
83
86
  }
@@ -119,6 +122,9 @@ export declare function exportAntibanState(modules: {
119
122
  topologyThrottler?: {
120
123
  exportState: () => TopologyThrottlerState;
121
124
  };
125
+ reputationVoucher?: {
126
+ exportState: () => ReputationVoucherState;
127
+ };
122
128
  engagementScores?: Map<string, number>;
123
129
  instanceId?: string;
124
130
  }): AntibanSnapshot;
@@ -150,5 +156,8 @@ export declare function importAntibanState(snapshot: AntibanSnapshot, modules: {
150
156
  topologyThrottler?: {
151
157
  importState: (state: TopologyThrottlerState) => void;
152
158
  };
159
+ reputationVoucher?: {
160
+ importState: (state: ReputationVoucherState) => void;
161
+ };
153
162
  engagementScores?: Map<string, number>;
154
163
  }): void;
@@ -71,6 +71,10 @@ export function exportAntibanState(modules) {
71
71
  if (modules.topologyThrottler) {
72
72
  snapshot.topologyThrottler = modules.topologyThrottler.exportState();
73
73
  }
74
+ // Export reputation voucher state
75
+ if (modules.reputationVoucher) {
76
+ snapshot.reputationVoucher = modules.reputationVoucher.exportState();
77
+ }
74
78
  // Export engagement scores
75
79
  if (modules.engagementScores) {
76
80
  snapshot.engagementScores = Object.fromEntries(modules.engagementScores.entries());
@@ -148,6 +152,10 @@ export function importAntibanState(snapshot, modules) {
148
152
  if (snapshot.topologyThrottler && modules.topologyThrottler) {
149
153
  modules.topologyThrottler.importState(snapshot.topologyThrottler);
150
154
  }
155
+ // Import reputation voucher state
156
+ if (snapshot.reputationVoucher && modules.reputationVoucher) {
157
+ modules.reputationVoucher.importState(snapshot.reputationVoucher);
158
+ }
151
159
  // Import engagement scores
152
160
  if (snapshot.engagementScores && modules.engagementScores) {
153
161
  for (const [jid, score] of Object.entries(snapshot.engagementScores)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "baileys-antiban",
3
- "version": "4.9.0",
3
+ "version": "4.10.0",
4
4
  "description": "Anti-ban middleware for Baileys WhatsApp bots. Rate limiting, warmup, health monitor, LID resolver, disconnect classifier. Free Whapi.Cloud alternative.",
5
5
  "main": "dist/cjs/index.js",
6
6
  "types": "dist/index.d.ts",