@badzz88/baileys 8.5.5 → 8.5.6

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 (282) hide show
  1. package/README.md +2 -2
  2. package/WAProto/WAProto.proto +1256 -75
  3. package/WAProto/fix-imports.js +69 -79
  4. package/WAProto/index.d.ts +15254 -71491
  5. package/WAProto/index.js +19816 -126438
  6. package/engine-requirements.js +3 -4
  7. package/package.json +4 -4
  8. package/src/Defaults/index.js +186 -0
  9. package/src/Signal/Group/ciphertext-message.js +15 -0
  10. package/src/Signal/Group/group-session-builder.js +86 -0
  11. package/src/Signal/Group/group_cipher.js +82 -0
  12. package/src/Signal/Group/index.js +140 -0
  13. package/src/Signal/Group/keyhelper.js +77 -0
  14. package/src/Signal/Group/sender-chain-key.js +29 -0
  15. package/src/Signal/Group/sender-key-distribution-message.js +66 -0
  16. package/src/Signal/Group/sender-key-message.js +69 -0
  17. package/src/Signal/Group/sender-key-name.js +51 -0
  18. package/src/Signal/Group/sender-key-record.js +44 -0
  19. package/src/Signal/Group/sender-key-state.js +87 -0
  20. package/src/Signal/Group/sender-message-key.js +29 -0
  21. package/src/Signal/libsignal.js +455 -0
  22. package/src/Signal/lid-mapping.js +273 -0
  23. package/src/Socket/Client/index.js +31 -0
  24. package/src/Socket/Client/types.js +13 -0
  25. package/src/Socket/Client/websocket.js +61 -0
  26. package/src/Socket/aigroups.js +221 -0
  27. package/src/Socket/business.js +411 -0
  28. package/src/Socket/chats.js +1449 -0
  29. package/src/Socket/communities.js +463 -0
  30. package/src/Socket/graphql.js +523 -0
  31. package/src/Socket/groups.js +516 -0
  32. package/src/Socket/index.js +31 -0
  33. package/src/Socket/interactive-handler.js +527 -0
  34. package/src/Socket/interop.js +339 -0
  35. package/{lib → src}/Socket/luxu.js +82 -120
  36. package/src/Socket/managed-account.js +98 -0
  37. package/src/Socket/messages-recv.js +2685 -0
  38. package/src/Socket/messages-send.js +2047 -0
  39. package/src/Socket/mex.js +57 -0
  40. package/src/Socket/newsletter.js +455 -0
  41. package/src/Socket/privacy.js +125 -0
  42. package/src/Socket/registration.js +236 -0
  43. package/src/Socket/socket.js +983 -0
  44. package/src/Socket/text-router.js +65 -0
  45. package/src/Socket/username.js +130 -0
  46. package/src/Store/index.js +33 -0
  47. package/{lib → src}/Store/keyed-db.js +21 -11
  48. package/src/Store/make-cache-manager-store.js +84 -0
  49. package/src/Store/make-in-memory-store.js +551 -0
  50. package/src/Store/make-ordered-dictionary.js +81 -0
  51. package/src/Store/object-repository.js +26 -0
  52. package/src/Types/Auth.js +31 -0
  53. package/src/Types/Bussines.js +2 -0
  54. package/src/Types/Call.js +2 -0
  55. package/src/Types/Chat.js +4 -0
  56. package/src/Types/Contact.js +2 -0
  57. package/src/Types/Events.js +2 -0
  58. package/src/Types/GroupMetadata.js +2 -0
  59. package/src/Types/Label.js +26 -0
  60. package/src/Types/LabelAssociation.js +8 -0
  61. package/src/Types/Message.js +93 -0
  62. package/src/Types/Mex.js +112 -0
  63. package/src/Types/Newsletter.js +109 -0
  64. package/src/Types/Product.js +2 -0
  65. package/src/Types/Signal.js +2 -0
  66. package/src/Types/Socket.js +2 -0
  67. package/src/Types/State.js +62 -0
  68. package/src/Types/USync.js +2 -0
  69. package/src/Types/index.js +56 -0
  70. package/src/Utils/auth-utils.js +254 -0
  71. package/src/Utils/browser-utils.js +112 -0
  72. package/src/Utils/business.js +240 -0
  73. package/src/Utils/chat-utils.js +1230 -0
  74. package/src/Utils/command-loader.d.ts +27 -0
  75. package/src/Utils/command-loader.js +89 -0
  76. package/src/Utils/companion-reg-client-utils.js +40 -0
  77. package/src/Utils/consumer-application.js +105 -0
  78. package/src/Utils/crypto.js +118 -0
  79. package/src/Utils/curve25519-js.js +242 -0
  80. package/src/Utils/decode-wa-message.js +529 -0
  81. package/src/Utils/event-buffer.js +580 -0
  82. package/src/Utils/generics.js +483 -0
  83. package/src/Utils/group-history.js +44 -0
  84. package/src/Utils/history.js +232 -0
  85. package/src/Utils/identity-change-handler.js +52 -0
  86. package/src/Utils/index.js +58 -0
  87. package/src/Utils/jid-display-normalization.js +195 -0
  88. package/src/Utils/link-preview.js +135 -0
  89. package/src/Utils/logger.js +8 -0
  90. package/src/Utils/lt-hash.js +25 -0
  91. package/src/Utils/make-mutex.js +36 -0
  92. package/src/Utils/message-composer.js +471 -0
  93. package/src/Utils/message-retry-manager.js +217 -0
  94. package/src/Utils/messages-media.js +843 -0
  95. package/src/Utils/messages.js +2817 -0
  96. package/src/Utils/meta-ai-msmsg.js +222 -0
  97. package/src/Utils/native-bridge.js +68 -0
  98. package/src/Utils/noise-handler.js +169 -0
  99. package/src/Utils/offline-node-processor.js +34 -0
  100. package/src/Utils/pre-key-manager.js +90 -0
  101. package/src/Utils/process-message.js +950 -0
  102. package/src/Utils/reporting-utils.js +261 -0
  103. package/src/Utils/session-pool.d.ts +14 -0
  104. package/src/Utils/session-pool.js +70 -0
  105. package/src/Utils/signal.js +217 -0
  106. package/src/Utils/stanza-ack.js +30 -0
  107. package/src/Utils/sticker.d.ts +13 -0
  108. package/src/Utils/sticker.js +123 -0
  109. package/src/Utils/sync-action-utils.js +45 -0
  110. package/src/Utils/tc-token-utils.js +158 -0
  111. package/src/Utils/use-multi-file-auth-state.js +111 -0
  112. package/src/Utils/validate-connection.js +209 -0
  113. package/src/Utils/view-once-cache.d.ts +12 -0
  114. package/src/Utils/view-once-cache.js +63 -0
  115. package/src/Utils/voip-rekey.js +22 -0
  116. package/src/WABinary/constants.js +1304 -0
  117. package/src/WABinary/decode.js +342 -0
  118. package/{lib → src}/WABinary/encode.js +9 -12
  119. package/src/WABinary/generic-utils.js +238 -0
  120. package/src/WABinary/index.js +34 -0
  121. package/src/WABinary/jid-utils.js +351 -0
  122. package/src/WABinary/types.js +2 -0
  123. package/src/WAM/BinaryInfo.js +13 -0
  124. package/src/WAM/constants.js +39484 -0
  125. package/src/WAM/encode.js +149 -0
  126. package/src/WAM/index.js +32 -0
  127. package/src/WAUSync/Protocols/USyncBotProfileProtocol.js +53 -0
  128. package/src/WAUSync/Protocols/USyncBusinessProtocol.js +44 -0
  129. package/src/WAUSync/Protocols/USyncContactProtocol.js +55 -0
  130. package/src/WAUSync/Protocols/USyncDeviceProtocol.js +55 -0
  131. package/src/WAUSync/Protocols/USyncDisappearingModeProtocol.js +31 -0
  132. package/src/WAUSync/Protocols/USyncFeatureProtocol.js +54 -0
  133. package/src/WAUSync/Protocols/USyncLIDProtocol.js +32 -0
  134. package/src/WAUSync/Protocols/USyncNewsletterProtocol.js +473 -0
  135. package/src/WAUSync/Protocols/USyncPictureProtocol.js +34 -0
  136. package/src/WAUSync/Protocols/USyncSidelistProtocol.js +29 -0
  137. package/src/WAUSync/Protocols/USyncStatusProtocol.js +42 -0
  138. package/src/WAUSync/Protocols/USyncTextStatusProtocol.js +38 -0
  139. package/src/WAUSync/Protocols/USyncUsernameProtocol.js +28 -0
  140. package/src/WAUSync/Protocols/index.js +40 -0
  141. package/src/WAUSync/USyncQuery.js +126 -0
  142. package/src/WAUSync/USyncUser.js +50 -0
  143. package/src/WAUSync/index.js +32 -0
  144. package/src/antiban.js +4152 -0
  145. package/{lib → src}/index.js +51 -49
  146. package/lib/Defaults/index.js +0 -187
  147. package/lib/Signal/Group/ciphertext-message.js +0 -15
  148. package/lib/Signal/Group/group-session-builder.js +0 -92
  149. package/lib/Signal/Group/group_cipher.js +0 -89
  150. package/lib/Signal/Group/index.js +0 -136
  151. package/lib/Signal/Group/keyhelper.js +0 -73
  152. package/lib/Signal/Group/sender-chain-key.js +0 -32
  153. package/lib/Signal/Group/sender-key-distribution-message.js +0 -66
  154. package/lib/Signal/Group/sender-key-message.js +0 -69
  155. package/lib/Signal/Group/sender-key-name.js +0 -50
  156. package/lib/Signal/Group/sender-key-record.js +0 -44
  157. package/lib/Signal/Group/sender-key-state.js +0 -97
  158. package/lib/Signal/Group/sender-message-key.js +0 -30
  159. package/lib/Signal/libsignal.js +0 -459
  160. package/lib/Signal/lid-mapping.js +0 -260
  161. package/lib/Socket/Client/index.js +0 -30
  162. package/lib/Socket/Client/types.js +0 -13
  163. package/lib/Socket/Client/websocket.js +0 -62
  164. package/lib/Socket/aigroups.js +0 -240
  165. package/lib/Socket/business.js +0 -422
  166. package/lib/Socket/chats.js +0 -1816
  167. package/lib/Socket/communities.js +0 -549
  168. package/lib/Socket/graphql.js +0 -656
  169. package/lib/Socket/groups.js +0 -764
  170. package/lib/Socket/index.js +0 -39
  171. package/lib/Socket/interactive-handler.js +0 -522
  172. package/lib/Socket/interop.js +0 -549
  173. package/lib/Socket/managed-account.js +0 -183
  174. package/lib/Socket/messages-recv.js +0 -2442
  175. package/lib/Socket/messages-send.js +0 -1853
  176. package/lib/Socket/mex.js +0 -60
  177. package/lib/Socket/newsletter.js +0 -818
  178. package/lib/Socket/privacy.js +0 -449
  179. package/lib/Socket/registration.js +0 -427
  180. package/lib/Socket/socket.js +0 -1092
  181. package/lib/Socket/text-router.js +0 -83
  182. package/lib/Socket/username.js +0 -243
  183. package/lib/Store/index.js +0 -36
  184. package/lib/Store/make-cache-manager-store.js +0 -90
  185. package/lib/Store/make-in-memory-store.js +0 -604
  186. package/lib/Store/make-ordered-dictionary.js +0 -81
  187. package/lib/Store/object-repository.js +0 -29
  188. package/lib/Types/Auth.js +0 -38
  189. package/lib/Types/Bussines.js +0 -2
  190. package/lib/Types/Call.js +0 -2
  191. package/lib/Types/Chat.js +0 -4
  192. package/lib/Types/Contact.js +0 -2
  193. package/lib/Types/Events.js +0 -2
  194. package/lib/Types/GroupMetadata.js +0 -2
  195. package/lib/Types/Label.js +0 -27
  196. package/lib/Types/LabelAssociation.js +0 -9
  197. package/lib/Types/Message.js +0 -95
  198. package/lib/Types/Mex.js +0 -112
  199. package/lib/Types/Newsletter.js +0 -121
  200. package/lib/Types/Product.js +0 -2
  201. package/lib/Types/Signal.js +0 -2
  202. package/lib/Types/Socket.js +0 -2
  203. package/lib/Types/State.js +0 -70
  204. package/lib/Types/USync.js +0 -2
  205. package/lib/Types/index.js +0 -55
  206. package/lib/Utils/auth-utils.js +0 -306
  207. package/lib/Utils/browser-utils.js +0 -114
  208. package/lib/Utils/business.js +0 -247
  209. package/lib/Utils/chat-utils.js +0 -1208
  210. package/lib/Utils/command-loader.d.ts +0 -31
  211. package/lib/Utils/command-loader.js +0 -100
  212. package/lib/Utils/companion-reg-client-utils.js +0 -42
  213. package/lib/Utils/consumer-application.js +0 -107
  214. package/lib/Utils/crypto.js +0 -155
  215. package/lib/Utils/curve25519-js.js +0 -275
  216. package/lib/Utils/decode-wa-message.js +0 -560
  217. package/lib/Utils/event-buffer.js +0 -607
  218. package/lib/Utils/generics.js +0 -563
  219. package/lib/Utils/group-history.js +0 -60
  220. package/lib/Utils/history.js +0 -244
  221. package/lib/Utils/identity-change-handler.js +0 -52
  222. package/lib/Utils/index.js +0 -57
  223. package/lib/Utils/jid-display-normalization.js +0 -218
  224. package/lib/Utils/link-preview.js +0 -143
  225. package/lib/Utils/logger.js +0 -9
  226. package/lib/Utils/lt-hash.js +0 -39
  227. package/lib/Utils/make-mutex.js +0 -36
  228. package/lib/Utils/message-composer.js +0 -479
  229. package/lib/Utils/message-inspect.js +0 -393
  230. package/lib/Utils/message-retry-manager.js +0 -270
  231. package/lib/Utils/messages-media.js +0 -896
  232. package/lib/Utils/messages.js +0 -2904
  233. package/lib/Utils/meta-ai-msmsg.js +0 -262
  234. package/lib/Utils/native-bridge.js +0 -82
  235. package/lib/Utils/noise-handler.js +0 -196
  236. package/lib/Utils/offline-node-processor.js +0 -42
  237. package/lib/Utils/pre-key-manager.js +0 -107
  238. package/lib/Utils/process-message.js +0 -1048
  239. package/lib/Utils/reporting-utils.js +0 -262
  240. package/lib/Utils/session-pool.d.ts +0 -16
  241. package/lib/Utils/session-pool.js +0 -94
  242. package/lib/Utils/signal.js +0 -224
  243. package/lib/Utils/stanza-ack.js +0 -40
  244. package/lib/Utils/sticker.d.ts +0 -17
  245. package/lib/Utils/sticker.js +0 -145
  246. package/lib/Utils/sync-action-utils.js +0 -54
  247. package/lib/Utils/tc-token-utils.js +0 -161
  248. package/lib/Utils/use-multi-file-auth-state.js +0 -121
  249. package/lib/Utils/validate-connection.js +0 -219
  250. package/lib/Utils/view-once-cache.d.ts +0 -9
  251. package/lib/Utils/view-once-cache.js +0 -79
  252. package/lib/Utils/voip-rekey.js +0 -22
  253. package/lib/WABinary/constants.js +0 -1304
  254. package/lib/WABinary/decode.js +0 -343
  255. package/lib/WABinary/generic-utils.js +0 -240
  256. package/lib/WABinary/index.js +0 -33
  257. package/lib/WABinary/jid-utils.js +0 -361
  258. package/lib/WABinary/types.js +0 -2
  259. package/lib/WAM/BinaryInfo.js +0 -13
  260. package/lib/WAM/constants.js +0 -39486
  261. package/lib/WAM/encode.js +0 -142
  262. package/lib/WAM/index.js +0 -31
  263. package/lib/WAUSync/Protocols/USyncBotProfileProtocol.js +0 -55
  264. package/lib/WAUSync/Protocols/USyncBusinessProtocol.js +0 -43
  265. package/lib/WAUSync/Protocols/USyncContactProtocol.js +0 -54
  266. package/lib/WAUSync/Protocols/USyncDeviceProtocol.js +0 -57
  267. package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.js +0 -30
  268. package/lib/WAUSync/Protocols/USyncFeatureProtocol.js +0 -59
  269. package/lib/WAUSync/Protocols/USyncLIDProtocol.js +0 -31
  270. package/lib/WAUSync/Protocols/USyncNewsletterProtocol.js +0 -865
  271. package/lib/WAUSync/Protocols/USyncPictureProtocol.js +0 -32
  272. package/lib/WAUSync/Protocols/USyncSidelistProtocol.js +0 -29
  273. package/lib/WAUSync/Protocols/USyncStatusProtocol.js +0 -39
  274. package/lib/WAUSync/Protocols/USyncTextStatusProtocol.js +0 -38
  275. package/lib/WAUSync/Protocols/USyncUsernameProtocol.js +0 -27
  276. package/lib/WAUSync/Protocols/index.js +0 -39
  277. package/lib/WAUSync/USyncBackoff.js +0 -26
  278. package/lib/WAUSync/USyncQuery.js +0 -133
  279. package/lib/WAUSync/USyncUser.js +0 -50
  280. package/lib/WAUSync/index.js +0 -31
  281. package/lib/antiban.js +0 -4637
  282. /package/{lib → src}/Defaults/phonenumber-mcc.json +0 -0
package/src/antiban.js ADDED
@@ -0,0 +1,4152 @@
1
+ const __importMetaUrl = require('url').pathToFileURL(__filename).href;
2
+ ('use strict');
3
+ var __create = Object.create;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getProtoOf = Object.getPrototypeOf;
8
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __export = (target, all) => {
10
+ for (var name in all)
11
+ __defProp(target, name, { get: all[name], enumerable: true });
12
+ };
13
+ var __copyProps = (to, from, except, desc) => {
14
+ if ((from && typeof from === 'object') || typeof from === 'function') {
15
+ for (let key of __getOwnPropNames(from))
16
+ if (!__hasOwnProp.call(to, key) && key !== except)
17
+ __defProp(to, key, {
18
+ get: () => from[key],
19
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
20
+ });
21
+ }
22
+ return to;
23
+ };
24
+ var __toESM = (mod, isNodeMode, target) => ((target = mod != null ? __create(__getProtoOf(mod)) : {}),
25
+ __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, 'default', { value: mod, enumerable: true }) : target, mod));
26
+ var __toCommonJS = mod => __copyProps(__defProp({}, '__esModule', { value: true }), mod);
27
+ var index_exports = {};
28
+ __export(index_exports, {
29
+ AntiBan: () => AntiBan,
30
+ ContactGraphWarmer: () => ContactGraphWarmer,
31
+ ContentVariator: () => ContentVariator,
32
+ FileStateAdapter: () => FileStateAdapter,
33
+ HealthMonitor: () => HealthMonitor,
34
+ JidCanonicalizer: () => JidCanonicalizer,
35
+ LidFirstResolver: () => LidFirstResolver,
36
+ LidResolver: () => LidResolver,
37
+ MAC_ERROR_CODES: () => MAC_ERROR_CODES,
38
+ MessageQueue: () => MessageQueue,
39
+ MessageRetryReason: () => MessageRetryReason,
40
+ PRESETS: () => PRESETS,
41
+ PostReconnectThrottle: () => PostReconnectThrottle,
42
+ PresenceChoreographer: () => PresenceChoreographer,
43
+ RateLimiter: () => RateLimiter,
44
+ ReplyRatioGuard: () => ReplyRatioGuard,
45
+ RetryReasonTracker: () => RetryReasonTracker,
46
+ Scheduler: () => Scheduler,
47
+ SessionHealthMonitor: () => SessionHealthMonitor,
48
+ StateManager: () => StateManager,
49
+ TimelockGuard: () => TimelockGuard,
50
+ WarmUp: () => WarmUp,
51
+ WebhookAlerts: () => WebhookAlerts,
52
+ applyFingerprint: () => applyFingerprint,
53
+ applyGroupMultiplier: () => applyGroupMultiplier,
54
+ classifyDisconnect: () => classifyDisconnect,
55
+ createLidFirstResolver: () => createLidFirstResolver,
56
+ credsSnapshot: () => credsSnapshot,
57
+ generateFingerprint: () => generateFingerprint,
58
+ getCircadianMultiplier: () => getCircadianMultiplier,
59
+ getRetryReasonDescription: () => getRetryReasonDescription,
60
+ isBroadcast: () => isBroadcast,
61
+ isGroup: () => isGroup,
62
+ isMacError: () => isMacError,
63
+ isNewsletter: () => isNewsletter,
64
+ messageRecovery: () => messageRecovery,
65
+ parseRetryReason: () => parseRetryReason,
66
+ proxyRotator: () => proxyRotator,
67
+ readReceiptVariance: () => readReceiptVariance,
68
+ resolveConfig: () => resolveConfig,
69
+ shouldUseGroupProfile: () => shouldUseGroupProfile,
70
+ wrapSocket: () => wrapSocket,
71
+ wrapWithSessionStability: () => wrapWithSessionStability
72
+ });
73
+ module.exports = __toCommonJS(index_exports);
74
+ var TIME_CONSTANTS = {
75
+ MS_PER_SECOND: 1e3,
76
+ MS_PER_MINUTE: 6e4,
77
+ MS_PER_HOUR: 36e5,
78
+ MS_PER_DAY: 864e5,
79
+ BURST_RESET_MS: 3e4,
80
+ IDENTICAL_WINDOW_MS: 36e5
81
+ };
82
+ var DEFAULT_CONFIG = {
83
+ maxPerMinute: 8,
84
+ maxPerHour: 200,
85
+ maxPerDay: 1500,
86
+ minDelayMs: 1500,
87
+ maxDelayMs: 5e3,
88
+ newChatDelayMs: 3e3,
89
+ maxIdenticalMessages: 3,
90
+ burstAllowance: 3,
91
+ identicalMessageWindowMs: TIME_CONSTANTS.IDENTICAL_WINDOW_MS
92
+ };
93
+ var RateLimiter = class {
94
+ config;
95
+ messages = [];
96
+ identicalCount = new Map();
97
+ knownChats = new Set();
98
+ burstCount = 0;
99
+ lastMessageTime = 0;
100
+ constructor(config = {}) {
101
+ this.config = { ...DEFAULT_CONFIG, ...config };
102
+ }
103
+ async getDelay(recipient, content) {
104
+ const now = Date.now();
105
+ this.cleanup(now);
106
+ const contentHash = this.hashContent(content);
107
+ const dayMessages = this.messages.filter(m => now - m.timestamp < TIME_CONSTANTS.MS_PER_DAY);
108
+ if (dayMessages.length >= this.config.maxPerDay) {
109
+ return -1;
110
+ }
111
+ const hourMessages = this.messages.filter(m => now - m.timestamp < TIME_CONSTANTS.MS_PER_HOUR);
112
+ if (hourMessages.length >= this.config.maxPerHour) {
113
+ hourMessages.sort((a, b) => a.timestamp - b.timestamp);
114
+ const oldestInHour = hourMessages[0];
115
+ const delay2 = oldestInHour
116
+ ? oldestInHour.timestamp + TIME_CONSTANTS.MS_PER_HOUR - now
117
+ : TIME_CONSTANTS.MS_PER_HOUR;
118
+ return Math.max(delay2, TIME_CONSTANTS.MS_PER_MINUTE);
119
+ }
120
+ const minuteMessages = this.messages.filter(m => now - m.timestamp < TIME_CONSTANTS.MS_PER_MINUTE);
121
+ if (minuteMessages.length >= this.config.maxPerMinute) {
122
+ minuteMessages.sort((a, b) => a.timestamp - b.timestamp);
123
+ const oldestInMinute = minuteMessages[0];
124
+ const delay2 = oldestInMinute
125
+ ? oldestInMinute.timestamp + TIME_CONSTANTS.MS_PER_MINUTE - now
126
+ : TIME_CONSTANTS.MS_PER_MINUTE;
127
+ return Math.max(delay2, TIME_CONSTANTS.MS_PER_SECOND);
128
+ }
129
+ const tracker = this.identicalCount.get(contentHash);
130
+ if (tracker) {
131
+ if (now - tracker.firstSeen < this.config.identicalMessageWindowMs) {
132
+ if (tracker.count >= this.config.maxIdenticalMessages) {
133
+ return -1;
134
+ }
135
+ }
136
+ }
137
+ let delay = 0;
138
+ if (this.burstCount < this.config.burstAllowance) {
139
+ this.burstCount++;
140
+ delay = this.jitter(this.config.minDelayMs * 0.5, this.config.minDelayMs);
141
+ }
142
+ else {
143
+ delay = this.jitter(this.config.minDelayMs, this.config.maxDelayMs);
144
+ }
145
+ const isInterop = recipient.endsWith('@interop');
146
+ if (!this.knownChats.has(recipient) || isInterop) {
147
+ delay += this.jitter(this.config.newChatDelayMs * 0.5, this.config.newChatDelayMs);
148
+ }
149
+ const timeSinceLast = now - this.lastMessageTime;
150
+ if (timeSinceLast < this.config.minDelayMs) {
151
+ delay = Math.max(delay, this.config.minDelayMs - timeSinceLast);
152
+ }
153
+ const typingDelay = Math.min(content.length * 30, 3e3);
154
+ delay += this.jitter(typingDelay * 0.5, typingDelay);
155
+ return Math.round(delay);
156
+ }
157
+ record(recipient, content) {
158
+ const now = Date.now();
159
+ const contentHash = this.hashContent(content);
160
+ const timeSinceLast = now - this.lastMessageTime;
161
+ if (timeSinceLast > TIME_CONSTANTS.BURST_RESET_MS) {
162
+ this.burstCount = 0;
163
+ }
164
+ this.messages.push({ timestamp: now, recipient, contentHash });
165
+ this.knownChats.add(recipient);
166
+ this.lastMessageTime = now;
167
+ const tracker = this.identicalCount.get(contentHash);
168
+ if (tracker) {
169
+ if (now - tracker.firstSeen < this.config.identicalMessageWindowMs) {
170
+ tracker.count++;
171
+ tracker.lastSeen = now;
172
+ }
173
+ else {
174
+ this.identicalCount.set(contentHash, { count: 1, firstSeen: now, lastSeen: now });
175
+ }
176
+ }
177
+ else {
178
+ this.identicalCount.set(contentHash, { count: 1, firstSeen: now, lastSeen: now });
179
+ }
180
+ }
181
+ getStats() {
182
+ const now = Date.now();
183
+ this.cleanup(now);
184
+ return {
185
+ lastMinute: this.messages.filter(m => now - m.timestamp < TIME_CONSTANTS.MS_PER_MINUTE).length,
186
+ lastHour: this.messages.filter(m => now - m.timestamp < TIME_CONSTANTS.MS_PER_HOUR).length,
187
+ lastDay: this.messages.filter(m => now - m.timestamp < TIME_CONSTANTS.MS_PER_DAY).length,
188
+ limits: {
189
+ perMinute: this.config.maxPerMinute,
190
+ perHour: this.config.maxPerHour,
191
+ perDay: this.config.maxPerDay
192
+ },
193
+ knownChats: this.knownChats.size
194
+ };
195
+ }
196
+ getKnownChats() {
197
+ return this.knownChats;
198
+ }
199
+ restoreKnownChats(chats) {
200
+ for (const jid of chats) {
201
+ this.knownChats.add(jid);
202
+ }
203
+ }
204
+ cleanup(now) {
205
+ this.messages = this.messages.filter(m => now - m.timestamp < TIME_CONSTANTS.MS_PER_DAY);
206
+ for (const [hash, tracker] of this.identicalCount.entries()) {
207
+ if (now - tracker.lastSeen > this.config.identicalMessageWindowMs) {
208
+ this.identicalCount.delete(hash);
209
+ }
210
+ }
211
+ }
212
+ jitter(min, max) {
213
+ const u1 = Math.random();
214
+ const u2 = Math.random();
215
+ const normal = Math.sqrt(-2 * Math.log(u1)) * Math.cos(2 * Math.PI * u2);
216
+ const normalized = (normal + 3) / 6;
217
+ const clamped = Math.max(0, Math.min(1, normalized));
218
+ return Math.round(min + clamped * (max - min));
219
+ }
220
+ hashContent(content) {
221
+ let hash = 0;
222
+ for (let i = 0; i < content.length; i++) {
223
+ const char = content.charCodeAt(i);
224
+ hash = (hash << 5) - hash + char;
225
+ hash |= 0;
226
+ }
227
+ return hash.toString(36);
228
+ }
229
+ };
230
+ var DEFAULT_CONFIG2 = {
231
+ warmUpDays: 7,
232
+ day1Limit: 20,
233
+ growthFactor: 1.8,
234
+ inactivityThresholdHours: 72
235
+ };
236
+ var WarmUp = class {
237
+ config;
238
+ state;
239
+ constructor(config = {}, existingState) {
240
+ this.config = { ...DEFAULT_CONFIG2, ...config };
241
+ this.state = existingState || this.freshState();
242
+ }
243
+ getDailyLimit() {
244
+ if (this.state.graduated)
245
+ return Infinity;
246
+ const day = this.getCurrentDay();
247
+ if (day >= this.config.warmUpDays) {
248
+ this.state.graduated = true;
249
+ return Infinity;
250
+ }
251
+ return Math.round(this.config.day1Limit * Math.pow(this.config.growthFactor, day));
252
+ }
253
+ canSend() {
254
+ this.checkInactivity();
255
+ if (this.state.graduated)
256
+ return true;
257
+ const day = this.getCurrentDay();
258
+ const todayCount = this.state.dailyCounts[day] || 0;
259
+ return todayCount < this.getDailyLimit();
260
+ }
261
+ record() {
262
+ const now = Date.now();
263
+ const day = this.getCurrentDay();
264
+ while (this.state.dailyCounts.length <= day) {
265
+ this.state.dailyCounts.push(0);
266
+ }
267
+ this.state.dailyCounts[day]++;
268
+ this.state.lastActiveAt = now;
269
+ }
270
+ getStatus() {
271
+ const day = this.getCurrentDay();
272
+ const todaySent = this.state.dailyCounts[day] || 0;
273
+ const limit = this.getDailyLimit();
274
+ return {
275
+ phase: this.state.graduated ? 'graduated' : 'warming',
276
+ day: Math.min(day + 1, this.config.warmUpDays),
277
+ totalDays: this.config.warmUpDays,
278
+ todayLimit: limit === Infinity ? -1 : limit,
279
+ todaySent,
280
+ progress: this.state.graduated ? 100 : Math.round((day / this.config.warmUpDays) * 100)
281
+ };
282
+ }
283
+ exportState() {
284
+ return { ...this.state };
285
+ }
286
+ reset() {
287
+ this.state = this.freshState();
288
+ }
289
+ getCurrentDay() {
290
+ return Math.floor((Date.now() - this.state.startedAt) / 864e5);
291
+ }
292
+ checkInactivity() {
293
+ const hoursSinceActive = (Date.now() - this.state.lastActiveAt) / 36e5;
294
+ if (hoursSinceActive > this.config.inactivityThresholdHours && this.state.graduated) {
295
+ this.state = this.freshState();
296
+ this.state.graduated = false;
297
+ }
298
+ }
299
+ freshState() {
300
+ const now = Date.now();
301
+ return {
302
+ startedAt: now,
303
+ lastActiveAt: now,
304
+ dailyCounts: [],
305
+ graduated: false
306
+ };
307
+ }
308
+ };
309
+ var DEFAULT_CONFIG3 = {
310
+ disconnectWarningThreshold: 3,
311
+ disconnectCriticalThreshold: 5,
312
+ failedMessageThreshold: 5,
313
+ autoPauseAt: 'high'
314
+ };
315
+ var HealthMonitor = class {
316
+ config;
317
+ events = [];
318
+ startTime = Date.now();
319
+ paused = false;
320
+ lastRisk = 'low';
321
+ lastBadEventTime = Date.now();
322
+ lastEventWasSevere = false;
323
+ constructor(config = {}) {
324
+ this.config = { ...DEFAULT_CONFIG3, ...config };
325
+ }
326
+ recordDisconnect(reason) {
327
+ const reasonStr = String(reason);
328
+ if (reasonStr === '403' || reasonStr === 'forbidden') {
329
+ this.events.push({ type: 'forbidden', timestamp: Date.now(), detail: reasonStr });
330
+ this.lastBadEventTime = Date.now();
331
+ this.lastEventWasSevere = true;
332
+ }
333
+ else if (reasonStr === '401' || reasonStr === 'loggedOut') {
334
+ this.events.push({ type: 'loggedOut', timestamp: Date.now(), detail: reasonStr });
335
+ this.lastBadEventTime = Date.now();
336
+ this.lastEventWasSevere = true;
337
+ }
338
+ else if (reasonStr === '463') {
339
+ this.events.push({ type: 'reachoutTimelocked', timestamp: Date.now(), detail: reasonStr });
340
+ this.lastBadEventTime = Date.now();
341
+ this.lastEventWasSevere = false;
342
+ }
343
+ else {
344
+ this.events.push({ type: 'disconnect', timestamp: Date.now(), detail: reasonStr });
345
+ this.lastBadEventTime = Date.now();
346
+ this.lastEventWasSevere = false;
347
+ }
348
+ this.checkAndNotify();
349
+ }
350
+ recordReconnect() {
351
+ this.events.push({ type: 'reconnect', timestamp: Date.now() });
352
+ }
353
+ recordMessageFailed(error) {
354
+ this.events.push({ type: 'messageFailed', timestamp: Date.now(), detail: error });
355
+ this.lastBadEventTime = Date.now();
356
+ this.lastEventWasSevere = false;
357
+ this.checkAndNotify();
358
+ }
359
+ recordReachoutTimelock(detail) {
360
+ this.events.push({ type: 'reachoutTimelocked', timestamp: Date.now(), detail });
361
+ this.lastBadEventTime = Date.now();
362
+ this.lastEventWasSevere = false;
363
+ this.checkAndNotify();
364
+ }
365
+ getStatus() {
366
+ const now = Date.now();
367
+ this.cleanup(now);
368
+ const hourEvents = this.events.filter(e => now - e.timestamp < 36e5);
369
+ const disconnects = hourEvents.filter(e => e.type === 'disconnect').length;
370
+ const forbidden = hourEvents.filter(e => e.type === 'forbidden').length;
371
+ const loggedOut = hourEvents.filter(e => e.type === 'loggedOut').length;
372
+ const failedMessages = hourEvents.filter(e => e.type === 'messageFailed').length;
373
+ let score = 0;
374
+ const reasons = [];
375
+ if (forbidden > 0) {
376
+ score += 40 * forbidden;
377
+ reasons.push(`${forbidden} forbidden (403) error${forbidden > 1 ? 's' : ''} in last hour`);
378
+ }
379
+ if (loggedOut > 0) {
380
+ score += 60;
381
+ reasons.push('Logged out by WhatsApp \u2014 possible temporary ban');
382
+ }
383
+ const timelocked = hourEvents.filter(e => e.type === 'reachoutTimelocked').length;
384
+ if (timelocked > 0) {
385
+ score += 25;
386
+ reasons.push(`${timelocked} reachout timelock (463) error${timelocked > 1 ? 's' : ''} in last hour`);
387
+ }
388
+ if (disconnects >= this.config.disconnectCriticalThreshold) {
389
+ score += 30;
390
+ reasons.push(`${disconnects} disconnects in last hour (critical threshold)`);
391
+ }
392
+ else if (disconnects >= this.config.disconnectWarningThreshold) {
393
+ score += 30;
394
+ reasons.push(`${disconnects} disconnects in last hour`);
395
+ }
396
+ if (failedMessages >= this.config.failedMessageThreshold) {
397
+ score += 20;
398
+ reasons.push(`${failedMessages} failed messages in last hour`);
399
+ }
400
+ score = Math.min(100, score);
401
+ const minutesSinceLastBad = (now - this.lastBadEventTime) / 6e4;
402
+ const decayRate = this.lastEventWasSevere ? 2 : 5;
403
+ score = Math.max(0, score - Math.floor(minutesSinceLastBad * decayRate));
404
+ let risk;
405
+ if (score >= 80)
406
+ risk = 'critical';
407
+ else if (score >= 40)
408
+ risk = 'high';
409
+ else if (score >= 15)
410
+ risk = 'medium';
411
+ else
412
+ risk = 'low';
413
+ let recommendation;
414
+ switch (risk) {
415
+ case 'critical':
416
+ recommendation = 'STOP ALL MESSAGING IMMEDIATELY. Disconnect and wait 24-48 hours before reconnecting.';
417
+ break;
418
+ case 'high':
419
+ recommendation = 'Reduce messaging rate by 80%. Consider pausing for 1-2 hours.';
420
+ break;
421
+ case 'medium':
422
+ recommendation = 'Reduce messaging rate by 50%. Increase delays between messages.';
423
+ break;
424
+ default:
425
+ recommendation = 'Operating normally. Continue monitoring.';
426
+ }
427
+ const lastDisconnect = [...this.events]
428
+ .reverse()
429
+ .find(e => e.type === 'disconnect' || e.type === 'forbidden' || e.type === 'loggedOut');
430
+ return {
431
+ risk,
432
+ score,
433
+ reasons: reasons.length ? reasons : ['No issues detected'],
434
+ recommendation,
435
+ stats: {
436
+ disconnectsLastHour: disconnects,
437
+ failedMessagesLastHour: failedMessages,
438
+ forbiddenErrors: forbidden,
439
+ timelockErrors: timelocked,
440
+ uptimeMs: now - this.startTime,
441
+ lastDisconnectReason: lastDisconnect?.detail
442
+ }
443
+ };
444
+ }
445
+ isPaused() {
446
+ if (this.paused)
447
+ return true;
448
+ const status = this.getStatus();
449
+ const riskOrder = ['low', 'medium', 'high', 'critical'];
450
+ return riskOrder.indexOf(status.risk) >= riskOrder.indexOf(this.config.autoPauseAt);
451
+ }
452
+ setPaused(paused) {
453
+ this.paused = paused;
454
+ }
455
+ reset() {
456
+ this.events = [];
457
+ this.startTime = Date.now();
458
+ this.paused = false;
459
+ this.lastRisk = 'low';
460
+ this.lastBadEventTime = Date.now();
461
+ this.lastEventWasSevere = false;
462
+ }
463
+ cleanup(now) {
464
+ this.events = this.events.filter(e => now - e.timestamp < 216e5);
465
+ }
466
+ checkAndNotify() {
467
+ const status = this.getStatus();
468
+ if (status.risk !== this.lastRisk) {
469
+ this.lastRisk = status.risk;
470
+ this.config.onRiskChange?.(status);
471
+ }
472
+ }
473
+ };
474
+ var DEFAULT_CONFIG4 = {
475
+ resumeBufferMs: 1e4
476
+ };
477
+ var TimelockGuard = class {
478
+ config;
479
+ state = {
480
+ isActive: false,
481
+ errorCount: 0
482
+ };
483
+ knownChats = new Set();
484
+ resumeTimer = null;
485
+ timerGeneration = 0;
486
+ constructor(config = {}) {
487
+ this.config = { ...DEFAULT_CONFIG4, ...config };
488
+ }
489
+ onTimelockUpdate(data) {
490
+ const wasActive = this.state.isActive;
491
+ this.state.isActive = !!data.isActive;
492
+ this.state.enforcementType = data.enforcementType;
493
+ this.state.expiresAt = data.timeEnforcementEnds;
494
+ if (this.state.isActive && !wasActive) {
495
+ this.state.detectedAt = new Date();
496
+ this.state.errorCount = 0;
497
+ this.config.onTimelockDetected?.(this.getState());
498
+ this.scheduleResume();
499
+ }
500
+ else if (this.state.isActive && wasActive) {
501
+ this.scheduleResume();
502
+ }
503
+ if (!this.state.isActive && wasActive) {
504
+ this.clearResumeTimer();
505
+ this.config.onTimelockLifted?.(this.getState());
506
+ }
507
+ }
508
+ record463Error() {
509
+ this.state.errorCount++;
510
+ if (!this.state.isActive) {
511
+ this.state.isActive = true;
512
+ this.state.detectedAt = new Date();
513
+ this.state.expiresAt = new Date(Date.now() + 6e4);
514
+ this.config.onTimelockDetected?.(this.getState());
515
+ this.scheduleResume();
516
+ }
517
+ }
518
+ registerKnownChat(jid) {
519
+ this.knownChats.add(jid);
520
+ }
521
+ registerKnownChats(jids) {
522
+ for (const jid of jids) {
523
+ this.knownChats.add(jid);
524
+ }
525
+ }
526
+ canSend(jid) {
527
+ if (!this.state.isActive) {
528
+ return { allowed: true };
529
+ }
530
+ if (this.state.expiresAt) {
531
+ const expiryWithBuffer = this.state.expiresAt.getTime() + this.config.resumeBufferMs;
532
+ if (Date.now() >= expiryWithBuffer) {
533
+ this.lift();
534
+ return { allowed: true };
535
+ }
536
+ }
537
+ if (jid.endsWith('@g.us') || jid.endsWith('@newsletter')) {
538
+ return { allowed: true };
539
+ }
540
+ if (this.knownChats.has(jid)) {
541
+ return { allowed: true };
542
+ }
543
+ const expiresIn = this.state.expiresAt ? Math.max(0, this.state.expiresAt.getTime() - Date.now()) : 6e4;
544
+ return {
545
+ allowed: false,
546
+ reason: `Reachout timelocked (${this.state.enforcementType || 'unknown'}). New contacts blocked. Expires in ${Math.ceil(expiresIn / 1e3)}s.`
547
+ };
548
+ }
549
+ getState() {
550
+ return { ...this.state };
551
+ }
552
+ isTimelocked() {
553
+ if (!this.state.isActive)
554
+ return false;
555
+ if (this.state.expiresAt) {
556
+ const expiryWithBuffer = this.state.expiresAt.getTime() + this.config.resumeBufferMs;
557
+ if (Date.now() >= expiryWithBuffer) {
558
+ this.lift();
559
+ return false;
560
+ }
561
+ }
562
+ return true;
563
+ }
564
+ getKnownChats() {
565
+ return new Set(this.knownChats);
566
+ }
567
+ lift() {
568
+ if (this.state.isActive) {
569
+ this.state.isActive = false;
570
+ this.clearResumeTimer();
571
+ this.config.onTimelockLifted?.(this.getState());
572
+ }
573
+ }
574
+ reset() {
575
+ this.state = { isActive: false, errorCount: 0 };
576
+ this.knownChats.clear();
577
+ this.clearResumeTimer();
578
+ }
579
+ scheduleResume() {
580
+ this.clearResumeTimer();
581
+ if (this.state.expiresAt) {
582
+ const delay = this.state.expiresAt.getTime() - Date.now() + this.config.resumeBufferMs;
583
+ if (delay > 0) {
584
+ this.timerGeneration++;
585
+ const currentGeneration = this.timerGeneration;
586
+ this.resumeTimer = setTimeout(() => {
587
+ if (currentGeneration === this.timerGeneration) {
588
+ this.lift();
589
+ }
590
+ }, delay);
591
+ }
592
+ }
593
+ }
594
+ clearResumeTimer() {
595
+ if (this.resumeTimer) {
596
+ clearTimeout(this.resumeTimer);
597
+ this.resumeTimer = null;
598
+ this.timerGeneration++;
599
+ }
600
+ }
601
+ };
602
+ var DEFAULT_CONFIG5 = {
603
+ enabled: false,
604
+ minRatio: 0.1,
605
+ minMessagesBeforeEnforce: 5,
606
+ inboundAutoReplyProbability: 0.25,
607
+ autoReplyTemplates: ['\u{1F44D}', '\u{1F44C}', 'ok', 'noted', 'thanks', '\u{1F64F}', 'got it'],
608
+ cooldownHoursOnViolation: 24,
609
+ scope: 'individual'
610
+ };
611
+ var ReplyRatioGuard = class {
612
+ config;
613
+ contacts = new Map();
614
+ constructor(config = {}) {
615
+ this.config = { ...DEFAULT_CONFIG5, ...config };
616
+ }
617
+ beforeSend(jid) {
618
+ if (!this.config.enabled) {
619
+ return { allowed: true };
620
+ }
621
+ if (this.isGroup(jid) && this.config.scope === 'individual') {
622
+ return { allowed: true };
623
+ }
624
+ const record = this.contacts.get(jid);
625
+ if (!record) {
626
+ return { allowed: true };
627
+ }
628
+ if (record.cooledUntil && Date.now() < record.cooledUntil) {
629
+ const hoursLeft = Math.ceil((record.cooledUntil - Date.now()) / 36e5);
630
+ return {
631
+ allowed: false,
632
+ reason: `Reply ratio cooldown \u2014 ${record.sent} sent, ${record.received} received. Retry in ${hoursLeft}h`
633
+ };
634
+ }
635
+ if (record.sent >= this.config.minMessagesBeforeEnforce) {
636
+ const ratio = record.sent === 0 ? 1 : record.received / record.sent;
637
+ if (ratio < this.config.minRatio) {
638
+ record.cooledUntil = Date.now() + this.config.cooldownHoursOnViolation * 36e5;
639
+ return {
640
+ allowed: false,
641
+ reason: `Reply ratio too low (${(ratio * 100).toFixed(1)}% < ${(this.config.minRatio * 100).toFixed(1)}%). Cooldown ${this.config.cooldownHoursOnViolation}h`
642
+ };
643
+ }
644
+ }
645
+ return { allowed: true };
646
+ }
647
+ recordSent(jid) {
648
+ if (!this.config.enabled)
649
+ return;
650
+ const record = this.contacts.get(jid) || { sent: 0, received: 0 };
651
+ record.sent++;
652
+ this.contacts.set(jid, record);
653
+ }
654
+ recordReceived(jid) {
655
+ if (!this.config.enabled)
656
+ return;
657
+ const record = this.contacts.get(jid) || { sent: 0, received: 0 };
658
+ record.received++;
659
+ delete record.cooledUntil;
660
+ this.contacts.set(jid, record);
661
+ }
662
+ suggestReply(jid, _msgText) {
663
+ if (!this.config.enabled) {
664
+ return { shouldReply: false };
665
+ }
666
+ if (this.isGroup(jid) && this.config.scope === 'individual') {
667
+ return { shouldReply: false };
668
+ }
669
+ if (Math.random() < this.config.inboundAutoReplyProbability) {
670
+ const templates = this.config.autoReplyTemplates;
671
+ const suggestedText = templates[Math.floor(Math.random() * templates.length)];
672
+ return { shouldReply: true, suggestedText };
673
+ }
674
+ return { shouldReply: false };
675
+ }
676
+ getStats() {
677
+ const perContact = Array.from(this.contacts.entries()).map(([jid, record]) => ({
678
+ jid,
679
+ sent: record.sent,
680
+ received: record.received,
681
+ ratio: record.sent === 0 ? 0 : record.received / record.sent,
682
+ cooledUntil: record.cooledUntil
683
+ }));
684
+ const globalSent = perContact.reduce((sum, c) => sum + c.sent, 0);
685
+ const globalReceived = perContact.reduce((sum, c) => sum + c.received, 0);
686
+ const globalRatio = globalSent === 0 ? 0 : globalReceived / globalSent;
687
+ const contactsOnCooldown = perContact.filter(c => c.cooledUntil && Date.now() < c.cooledUntil).length;
688
+ return {
689
+ perContact,
690
+ globalSent,
691
+ globalReceived,
692
+ globalRatio,
693
+ contactsOnCooldown
694
+ };
695
+ }
696
+ reset() {
697
+ this.contacts.clear();
698
+ }
699
+ exportState() {
700
+ return {
701
+ contacts: Array.from(this.contacts.entries())
702
+ };
703
+ }
704
+ restoreState(state) {
705
+ if (state?.contacts && Array.isArray(state.contacts)) {
706
+ this.contacts = new Map(state.contacts);
707
+ }
708
+ }
709
+ isGroup(jid) {
710
+ return jid.endsWith('@g.us');
711
+ }
712
+ };
713
+ var DEFAULT_CONFIG6 = {
714
+ enabled: false,
715
+ requireHandshakeBeforeGroupSend: true,
716
+ handshakeMinDelayMs: 36e5,
717
+ groupLurkPeriodMs: 432e5,
718
+ maxStrangerMessagesPerDay: 5,
719
+ autoRegisterOnIncoming: true
720
+ };
721
+ var ContactGraphWarmer = class {
722
+ config;
723
+ contacts = new Map();
724
+ groups = new Map();
725
+ strangerMessagesToday = 0;
726
+ lastStrangerResetDay = this.getCurrentDay();
727
+ constructor(config = {}) {
728
+ this.config = { ...DEFAULT_CONFIG6, ...config };
729
+ }
730
+ canMessage(jid) {
731
+ if (!this.config.enabled) {
732
+ return { allowed: true };
733
+ }
734
+ const currentDay = this.getCurrentDay();
735
+ if (currentDay !== this.lastStrangerResetDay) {
736
+ this.strangerMessagesToday = 0;
737
+ this.lastStrangerResetDay = currentDay;
738
+ }
739
+ if (this.isGroup(jid)) {
740
+ return this.checkGroupMessage(jid);
741
+ }
742
+ return this.checkIndividualMessage(jid);
743
+ }
744
+ markHandshakeSent(jid) {
745
+ if (!this.config.enabled)
746
+ return;
747
+ if (this.isGroup(jid))
748
+ return;
749
+ const record = this.contacts.get(jid) || { state: 'stranger' };
750
+ record.state = 'handshake_sent';
751
+ record.handshakeSentAt = Date.now();
752
+ this.contacts.set(jid, record);
753
+ }
754
+ markHandshakeComplete(jid) {
755
+ if (!this.config.enabled)
756
+ return;
757
+ if (this.isGroup(jid))
758
+ return;
759
+ const record = this.contacts.get(jid) || { state: 'stranger' };
760
+ record.state = 'handshake_complete';
761
+ this.contacts.set(jid, record);
762
+ }
763
+ registerKnownContact(jid) {
764
+ if (!this.config.enabled)
765
+ return;
766
+ if (this.isGroup(jid))
767
+ return;
768
+ const record = this.contacts.get(jid) || { state: 'stranger' };
769
+ record.state = 'known';
770
+ this.contacts.set(jid, record);
771
+ }
772
+ registerGroupJoin(groupJid) {
773
+ if (!this.config.enabled)
774
+ return;
775
+ if (!this.isGroup(groupJid))
776
+ return;
777
+ this.groups.set(groupJid, { joinedAt: Date.now() });
778
+ }
779
+ getContactState(jid) {
780
+ if (this.isGroup(jid))
781
+ return 'known';
782
+ return this.contacts.get(jid)?.state || 'stranger';
783
+ }
784
+ onIncomingMessage(jid) {
785
+ if (!this.config.enabled)
786
+ return;
787
+ if (this.isGroup(jid))
788
+ return;
789
+ if (this.config.autoRegisterOnIncoming) {
790
+ this.registerKnownContact(jid);
791
+ }
792
+ }
793
+ getStats() {
794
+ const knownContacts = Array.from(this.contacts.values()).filter(c => c.state === 'known').length;
795
+ const pendingHandshakes = Array.from(this.contacts.values()).filter(c => c.state === 'handshake_sent').length;
796
+ const groupsJoined = Array.from(this.groups.entries()).map(([groupJid, record]) => ({
797
+ groupJid,
798
+ joinedAt: record.joinedAt,
799
+ firstSendUnlocksAt: record.joinedAt + this.config.groupLurkPeriodMs
800
+ }));
801
+ return {
802
+ knownContacts,
803
+ pendingHandshakes,
804
+ strangersToday: this.strangerMessagesToday,
805
+ groupsJoined
806
+ };
807
+ }
808
+ reset() {
809
+ this.contacts.clear();
810
+ this.groups.clear();
811
+ this.strangerMessagesToday = 0;
812
+ this.lastStrangerResetDay = this.getCurrentDay();
813
+ }
814
+ exportState() {
815
+ return {
816
+ contacts: Array.from(this.contacts.entries()),
817
+ groups: Array.from(this.groups.entries()),
818
+ strangerMessagesToday: this.strangerMessagesToday,
819
+ lastStrangerResetDay: this.lastStrangerResetDay
820
+ };
821
+ }
822
+ restoreState(state) {
823
+ if (state?.contacts && Array.isArray(state.contacts)) {
824
+ this.contacts = new Map(state.contacts);
825
+ }
826
+ if (state?.groups && Array.isArray(state.groups)) {
827
+ this.groups = new Map(state.groups);
828
+ }
829
+ if (typeof state?.strangerMessagesToday === 'number') {
830
+ this.strangerMessagesToday = state.strangerMessagesToday;
831
+ }
832
+ if (typeof state?.lastStrangerResetDay === 'number') {
833
+ this.lastStrangerResetDay = state.lastStrangerResetDay;
834
+ }
835
+ }
836
+ isGroup(jid) {
837
+ return jid.endsWith('@g.us');
838
+ }
839
+ getCurrentDay() {
840
+ return Math.floor(Date.now() / 864e5);
841
+ }
842
+ checkGroupMessage(groupJid) {
843
+ const record = this.groups.get(groupJid);
844
+ if (!record) {
845
+ return { allowed: true };
846
+ }
847
+ const lurkEndsAt = record.joinedAt + this.config.groupLurkPeriodMs;
848
+ if (Date.now() < lurkEndsAt) {
849
+ const minutesLeft = Math.ceil((lurkEndsAt - Date.now()) / 6e4);
850
+ return {
851
+ allowed: false,
852
+ reason: `Group lurk period not elapsed \u2014 wait ${minutesLeft} minutes`
853
+ };
854
+ }
855
+ return { allowed: true };
856
+ }
857
+ checkIndividualMessage(jid) {
858
+ const record = this.contacts.get(jid);
859
+ if (!record || record.state === 'stranger') {
860
+ if (this.config.requireHandshakeBeforeGroupSend) {
861
+ if (this.strangerMessagesToday >= this.config.maxStrangerMessagesPerDay) {
862
+ return {
863
+ allowed: false,
864
+ reason: `Daily new-contact limit reached (${this.config.maxStrangerMessagesPerDay})`,
865
+ needsHandshake: true
866
+ };
867
+ }
868
+ this.strangerMessagesToday++;
869
+ }
870
+ return { allowed: true, needsHandshake: true };
871
+ }
872
+ if (record.state === 'handshake_sent') {
873
+ if (!record.handshakeSentAt) {
874
+ return { allowed: true };
875
+ }
876
+ const elapsed = Date.now() - record.handshakeSentAt;
877
+ if (elapsed < this.config.handshakeMinDelayMs) {
878
+ const minutesLeft = Math.ceil((this.config.handshakeMinDelayMs - elapsed) / 6e4);
879
+ return {
880
+ allowed: false,
881
+ reason: `Handshake too recent \u2014 wait ${minutesLeft} minutes`
882
+ };
883
+ }
884
+ }
885
+ return { allowed: true };
886
+ }
887
+ };
888
+ var DEFAULT_CONFIG7 = {
889
+ enabled: false,
890
+ enableCircadianRhythm: true,
891
+ timezone: 'UTC',
892
+ activityCurve: 'office',
893
+ circadian: {
894
+ enabled: true,
895
+ profile: 'default',
896
+ timezone: 'UTC'
897
+ },
898
+ distractionPauseProbability: 0.05,
899
+ distractionPauseMinMs: 3e5,
900
+ distractionPauseMaxMs: 12e5,
901
+ readReceiptDelayMinMs: 3e3,
902
+ readReceiptDelayMaxMs: 45e3,
903
+ readReceiptSkipProbability: 0.15,
904
+ offlineGapProbability: 0.03,
905
+ offlineGapMinMs: 3e5,
906
+ offlineGapMaxMs: 9e5,
907
+ enableTypingModel: true,
908
+ typingWPM: 45,
909
+ typingWPMStdDev: 15,
910
+ thinkPauseProbability: 0.08,
911
+ thinkPauseMinMs: 800,
912
+ thinkPauseMaxMs: 3500,
913
+ intermittentPausedProbability: 0.4,
914
+ typingMaxMs: 9e4,
915
+ typingMinMs: 600
916
+ };
917
+ var ACTIVITY_CURVES = {
918
+ office: [
919
+ 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1,
920
+ 0.5, 0.5,
921
+ 0.95, 0.95,
922
+ 0.6,
923
+ 0.9, 0.9, 0.9, 0.9,
924
+ 0.6, 0.6,
925
+ 0.4, 0.4,
926
+ 0.2, 0.2, 0.2, 0.2
927
+ ],
928
+ social: [
929
+ 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1,
930
+ 0.3, 0.4,
931
+ 0.7, 0.8,
932
+ 0.5,
933
+ 0.7, 0.7,
934
+ 0.4,
935
+ 0.8, 0.9, 0.9,
936
+ 0.6,
937
+ 0.8, 0.85, 0.9, 0.95, 1
938
+ ],
939
+ global: [
940
+ 0.5, 0.5, 0.5, 0.5, 0.5, 0.5,
941
+ 0.4, 0.4,
942
+ 0.6, 0.7, 0.8, 0.8,
943
+ 0.6,
944
+ 0.8, 0.8, 0.8, 0.8,
945
+ 0.7, 0.7,
946
+ 0.6, 0.5, 0.5, 0.5, 0.5, 0.5
947
+ ]
948
+ };
949
+ function getCircadianMultiplier(date = new Date(), profile = 'default', timezone) {
950
+ if (profile === 'always_on') {
951
+ return 1;
952
+ }
953
+ let hour;
954
+ if (timezone) {
955
+ try {
956
+ const formatter = new Intl.DateTimeFormat('en-US', {
957
+ timeZone: timezone,
958
+ hour: 'numeric',
959
+ hour12: false
960
+ });
961
+ const parts = formatter.formatToParts(date);
962
+ const hourPart = parts.find(p => p.type === 'hour');
963
+ hour = hourPart ? parseInt(hourPart.value, 10) : date.getHours();
964
+ }
965
+ catch {
966
+ hour = date.getHours();
967
+ }
968
+ }
969
+ else {
970
+ hour = date.getHours();
971
+ }
972
+ let shiftedHour = hour;
973
+ if (profile === 'nightOwl') {
974
+ shiftedHour = (hour - 3 + 24) % 24;
975
+ }
976
+ else if (profile === 'earlyBird') {
977
+ shiftedHour = (hour + 2) % 24;
978
+ }
979
+ if (shiftedHour >= 9 && shiftedHour < 22) {
980
+ const t = (shiftedHour - 9) / 13;
981
+ return 1 + 0.2 * Math.cos(2 * Math.PI * t);
982
+ }
983
+ else if (shiftedHour >= 22 && shiftedHour < 24) {
984
+ const t = (shiftedHour - 22) / 2;
985
+ return 1.2 + 1.3 * t;
986
+ }
987
+ else if (shiftedHour >= 0 && shiftedHour < 2) {
988
+ const t = shiftedHour / 2;
989
+ return 2.5 + 1.5 * t;
990
+ }
991
+ else if (shiftedHour >= 2 && shiftedHour < 6) {
992
+ const t = (shiftedHour - 2) / 4;
993
+ return 5 + 1 * Math.cos(Math.PI * t);
994
+ }
995
+ else {
996
+ const t = (shiftedHour - 6) / 3;
997
+ return 4 - 3 * t;
998
+ }
999
+ }
1000
+ var PresenceChoreographer = class {
1001
+ config;
1002
+ stats = {
1003
+ distractionPausesInjected: 0,
1004
+ offlineGapsInjected: 0,
1005
+ readReceiptsDelayed: 0,
1006
+ readReceiptsSkipped: 0,
1007
+ typingPlansComputed: 0,
1008
+ typingPlansExecuted: 0,
1009
+ totalTypingTimeMs: 0
1010
+ };
1011
+ constructor(config = {}) {
1012
+ this.config = {
1013
+ ...DEFAULT_CONFIG7,
1014
+ ...config,
1015
+ circadian: {
1016
+ ...DEFAULT_CONFIG7.circadian,
1017
+ ...(config.circadian || {})
1018
+ }
1019
+ };
1020
+ }
1021
+ getCurrentActivityFactor() {
1022
+ if (!this.config.enabled || !this.config.enableCircadianRhythm) {
1023
+ return 1;
1024
+ }
1025
+ const hour = this.getLocalHour();
1026
+ const curve = ACTIVITY_CURVES[this.config.activityCurve];
1027
+ return curve[hour] || 0.5;
1028
+ }
1029
+ shouldPauseForDistraction() {
1030
+ if (!this.config.enabled) {
1031
+ return { pause: false, durationMs: 0 };
1032
+ }
1033
+ if (Math.random() < this.config.distractionPauseProbability) {
1034
+ const durationMs = this.randomBetween(this.config.distractionPauseMinMs, this.config.distractionPauseMaxMs);
1035
+ this.stats.distractionPausesInjected++;
1036
+ return { pause: true, durationMs };
1037
+ }
1038
+ return { pause: false, durationMs: 0 };
1039
+ }
1040
+ shouldTakeOfflineGap() {
1041
+ if (!this.config.enabled) {
1042
+ return { offline: false, durationMs: 0 };
1043
+ }
1044
+ if (Math.random() < this.config.offlineGapProbability) {
1045
+ const durationMs = this.randomBetween(this.config.offlineGapMinMs, this.config.offlineGapMaxMs);
1046
+ this.stats.offlineGapsInjected++;
1047
+ return { offline: true, durationMs };
1048
+ }
1049
+ return { offline: false, durationMs: 0 };
1050
+ }
1051
+ shouldMarkRead() {
1052
+ if (!this.config.enabled) {
1053
+ return { mark: true, delayMs: 0 };
1054
+ }
1055
+ if (Math.random() < this.config.readReceiptSkipProbability) {
1056
+ this.stats.readReceiptsSkipped++;
1057
+ return { mark: false, delayMs: 0 };
1058
+ }
1059
+ const baseDelayMs = this.randomBetween(this.config.readReceiptDelayMinMs, this.config.readReceiptDelayMaxMs);
1060
+ let delayMs = baseDelayMs;
1061
+ if (this.config.circadian.enabled) {
1062
+ const circadianMultiplier = getCircadianMultiplier(new Date(), this.config.circadian.profile, this.config.circadian.timezone);
1063
+ delayMs = Math.floor(baseDelayMs * circadianMultiplier);
1064
+ }
1065
+ this.stats.readReceiptsDelayed++;
1066
+ return { mark: true, delayMs };
1067
+ }
1068
+ computeTypingPlan(messageLength) {
1069
+ if (!this.config.enabled || !this.config.enableTypingModel) {
1070
+ return [{ state: 'composing', durationMs: this.config.typingMinMs }];
1071
+ }
1072
+ this.stats.typingPlansComputed++;
1073
+ if (messageLength === 0) {
1074
+ return [{ state: 'composing', durationMs: this.config.typingMinMs }];
1075
+ }
1076
+ const wpmSample = this.clamp(this.gaussianSample(this.config.typingWPM, this.config.typingWPMStdDev), 10, 120);
1077
+ const cps = (wpmSample * 5) / 60;
1078
+ const baseMs = (messageLength / cps) * 1e3;
1079
+ let circadianMultiplier = 1;
1080
+ if (this.config.circadian.enabled) {
1081
+ circadianMultiplier = getCircadianMultiplier(new Date(), this.config.circadian.profile, this.config.circadian.timezone);
1082
+ }
1083
+ const targetMs = this.clamp(baseMs * circadianMultiplier, this.config.typingMinMs, this.config.typingMaxMs);
1084
+ const plan = [];
1085
+ let remainingBudget = targetMs;
1086
+ let position = 0;
1087
+ const chunkSize = 10;
1088
+ const numChunks = Math.max(1, Math.ceil(messageLength / chunkSize));
1089
+ for (let i = 0; i < numChunks && remainingBudget > 0; i++) {
1090
+ const charsInChunk = Math.min(chunkSize, messageLength - position);
1091
+ const remainingChunks = numChunks - i;
1092
+ const chunkBudget = remainingBudget / remainingChunks;
1093
+ const chunkTypingMs = Math.floor(Math.min(chunkBudget, remainingBudget));
1094
+ if (chunkTypingMs <= 0)
1095
+ break;
1096
+ if (i > 0 && i < numChunks - 1 && Math.random() < this.config.thinkPauseProbability) {
1097
+ plan.push({ state: 'composing', durationMs: chunkTypingMs });
1098
+ remainingBudget -= chunkTypingMs;
1099
+ const basePauseMs = this.randomBetween(this.config.thinkPauseMinMs, this.config.thinkPauseMaxMs);
1100
+ const pauseMs = Math.floor(basePauseMs * circadianMultiplier);
1101
+ plan.push({ state: 'paused', durationMs: pauseMs });
1102
+ }
1103
+ else {
1104
+ if (plan.length === 0 || plan[plan.length - 1].state === 'paused') {
1105
+ plan.push({ state: 'composing', durationMs: chunkTypingMs });
1106
+ }
1107
+ else {
1108
+ plan[plan.length - 1].durationMs += chunkTypingMs;
1109
+ }
1110
+ remainingBudget -= chunkTypingMs;
1111
+ }
1112
+ position += charsInChunk;
1113
+ }
1114
+ if (Math.random() < this.config.intermittentPausedProbability) {
1115
+ const baseFinalPauseMs = this.randomBetween(200, 800);
1116
+ const finalPauseMs = Math.floor(baseFinalPauseMs * circadianMultiplier);
1117
+ plan.push({ state: 'paused', durationMs: finalPauseMs });
1118
+ }
1119
+ if (plan.length === 0 || !plan.some(step => step.state === 'composing')) {
1120
+ return [{ state: 'composing', durationMs: this.config.typingMinMs }];
1121
+ }
1122
+ return plan;
1123
+ }
1124
+ async executeTypingPlan(sock, jid, plan, options) {
1125
+ this.stats.typingPlansExecuted++;
1126
+ for (const step of plan) {
1127
+ if (options?.signal?.aborted) {
1128
+ await Promise.resolve(sock.sendPresenceUpdate('paused', jid));
1129
+ throw new Error('Typing plan aborted');
1130
+ }
1131
+ await Promise.resolve(sock.sendPresenceUpdate(step.state, jid));
1132
+ await this.sleep(step.durationMs);
1133
+ this.stats.totalTypingTimeMs += step.durationMs;
1134
+ }
1135
+ }
1136
+ getStats() {
1137
+ return {
1138
+ currentActivityFactor: this.getCurrentActivityFactor(),
1139
+ distractionPausesInjected: this.stats.distractionPausesInjected,
1140
+ offlineGapsInjected: this.stats.offlineGapsInjected,
1141
+ readReceiptsDelayed: this.stats.readReceiptsDelayed,
1142
+ readReceiptsSkipped: this.stats.readReceiptsSkipped,
1143
+ currentHourLocal: this.getLocalHour(),
1144
+ typingPlansComputed: this.stats.typingPlansComputed,
1145
+ typingPlansExecuted: this.stats.typingPlansExecuted,
1146
+ totalTypingTimeMs: this.stats.totalTypingTimeMs
1147
+ };
1148
+ }
1149
+ reset() {
1150
+ this.stats = {
1151
+ distractionPausesInjected: 0,
1152
+ offlineGapsInjected: 0,
1153
+ readReceiptsDelayed: 0,
1154
+ readReceiptsSkipped: 0,
1155
+ typingPlansComputed: 0,
1156
+ typingPlansExecuted: 0,
1157
+ totalTypingTimeMs: 0
1158
+ };
1159
+ }
1160
+ getLocalHour() {
1161
+ try {
1162
+ const formatter = new Intl.DateTimeFormat('en-US', {
1163
+ timeZone: this.config.timezone,
1164
+ hour: 'numeric',
1165
+ hour12: false
1166
+ });
1167
+ const parts = formatter.formatToParts(new Date());
1168
+ const hourPart = parts.find(p => p.type === 'hour');
1169
+ if (hourPart) {
1170
+ return parseInt(hourPart.value, 10);
1171
+ }
1172
+ }
1173
+ catch (error) { }
1174
+ return new Date().getUTCHours();
1175
+ }
1176
+ randomBetween(min, max) {
1177
+ return Math.floor(Math.random() * (max - min + 1)) + min;
1178
+ }
1179
+ clamp(value, min, max) {
1180
+ return Math.max(min, Math.min(max, value));
1181
+ }
1182
+ gaussianSample(mean, stdDev) {
1183
+ const u1 = Math.random();
1184
+ const u2 = Math.random();
1185
+ const z0 = Math.sqrt(-2 * Math.log(u1)) * Math.cos(2 * Math.PI * u2);
1186
+ return mean + z0 * stdDev;
1187
+ }
1188
+ sleep(ms) {
1189
+ return new Promise(resolve => setTimeout(resolve, ms));
1190
+ }
1191
+ };
1192
+ var DEFAULT_CONFIG8 = {
1193
+ enabled: false,
1194
+ maxRetries: 5,
1195
+ spiralThreshold: 3,
1196
+ onSpiral: () => { }
1197
+ };
1198
+ var RetryReasonTracker = class {
1199
+ config;
1200
+ retries = new Map();
1201
+ totalRetries = 0;
1202
+ reasonCounts = {
1203
+ no_session: 0,
1204
+ invalid_key: 0,
1205
+ bad_mac: 0,
1206
+ decryption_failure: 0,
1207
+ server_error_463: 0,
1208
+ server_error_429: 0,
1209
+ timeout: 0,
1210
+ no_route: 0,
1211
+ node_malformed: 0,
1212
+ unknown: 0
1213
+ };
1214
+ spiralsDetected = 0;
1215
+ constructor(config) {
1216
+ this.config = { ...DEFAULT_CONFIG8, ...config };
1217
+ }
1218
+ onMessageUpdate(update) {
1219
+ if (!this.config.enabled)
1220
+ return;
1221
+ const msgId = update.key?.id;
1222
+ if (!msgId)
1223
+ return;
1224
+ if (update.status !== 0 && !update.error)
1225
+ return;
1226
+ const reason = this.classify(update.error || update);
1227
+ this.recordRetry(msgId, reason);
1228
+ }
1229
+ classify(err) {
1230
+ if (!err)
1231
+ return 'unknown';
1232
+ const statusCode = err.output?.statusCode || err.statusCode || err.status;
1233
+ if (statusCode === 463)
1234
+ return 'server_error_463';
1235
+ if (statusCode === 429)
1236
+ return 'server_error_429';
1237
+ const errorMsg = (err.message || err.text || String(err)).toLowerCase();
1238
+ if (errorMsg.includes('bad mac'))
1239
+ return 'bad_mac';
1240
+ if (errorMsg.includes('no session') || errorMsg.includes('session not found'))
1241
+ return 'no_session';
1242
+ if (errorMsg.includes('invalid key') || errorMsg.includes('key error'))
1243
+ return 'invalid_key';
1244
+ if (errorMsg.includes('decryption') || errorMsg.includes('decrypt'))
1245
+ return 'decryption_failure';
1246
+ if (errorMsg.includes('timeout') || errorMsg.includes('timed out'))
1247
+ return 'timeout';
1248
+ if (errorMsg.includes('no route') || errorMsg.includes('unreachable') || errorMsg.includes('offline'))
1249
+ return 'no_route';
1250
+ if (errorMsg.includes('malformed') || errorMsg.includes('invalid node'))
1251
+ return 'node_malformed';
1252
+ return 'unknown';
1253
+ }
1254
+ recordRetry(msgId, reason) {
1255
+ const now = Date.now();
1256
+ let record = this.retries.get(msgId);
1257
+ if (!record) {
1258
+ record = {
1259
+ msgId,
1260
+ count: 0,
1261
+ reasons: [],
1262
+ firstRetry: now,
1263
+ lastRetry: now
1264
+ };
1265
+ this.retries.set(msgId, record);
1266
+ }
1267
+ record.count++;
1268
+ record.reasons.push(reason);
1269
+ record.lastRetry = now;
1270
+ this.totalRetries++;
1271
+ this.reasonCounts[reason]++;
1272
+ if (record.count >= this.config.spiralThreshold) {
1273
+ this.spiralsDetected++;
1274
+ this.config.onSpiral(msgId, reason);
1275
+ }
1276
+ }
1277
+ isSpiraling(msgId) {
1278
+ const record = this.retries.get(msgId);
1279
+ return record ? record.count >= this.config.spiralThreshold : false;
1280
+ }
1281
+ clear(msgId) {
1282
+ this.retries.delete(msgId);
1283
+ }
1284
+ getStats() {
1285
+ return {
1286
+ totalRetries: this.totalRetries,
1287
+ byReason: { ...this.reasonCounts },
1288
+ spiralsDetected: this.spiralsDetected,
1289
+ activeRetries: this.retries.size
1290
+ };
1291
+ }
1292
+ cleanup() {
1293
+ const now = Date.now();
1294
+ const maxAge = 5 * 60 * 1e3;
1295
+ for (const [msgId, record] of this.retries.entries()) {
1296
+ if (now - record.lastRetry > maxAge) {
1297
+ this.retries.delete(msgId);
1298
+ }
1299
+ }
1300
+ }
1301
+ destroy() {
1302
+ this.retries.clear();
1303
+ this.cleanup();
1304
+ }
1305
+ };
1306
+ var DEFAULT_CONFIG9 = {
1307
+ enabled: false,
1308
+ rampDurationMs: 6e4,
1309
+ initialRateMultiplier: 0.1,
1310
+ rampSteps: 6,
1311
+ baselineRatePerMinute: null
1312
+ };
1313
+ var PostReconnectThrottle = class {
1314
+ config;
1315
+ throttledSince = null;
1316
+ throttledSendCount = 0;
1317
+ lifetimeReconnects = 0;
1318
+ rampTimer = null;
1319
+ currentStep = 0;
1320
+ sendsInCurrentWindow = 0;
1321
+ currentWindowStart = 0;
1322
+ WINDOW_DURATION_MS = 6e4;
1323
+ constructor(config) {
1324
+ this.config = {
1325
+ ...DEFAULT_CONFIG9,
1326
+ ...config,
1327
+ baselineRatePerMinute: config?.baselineRatePerMinute || null
1328
+ };
1329
+ }
1330
+ onReconnect() {
1331
+ if (!this.config.enabled)
1332
+ return;
1333
+ this.throttledSince = Date.now();
1334
+ this.currentStep = 0;
1335
+ this.throttledSendCount = 0;
1336
+ this.lifetimeReconnects++;
1337
+ this.sendsInCurrentWindow = 0;
1338
+ this.currentWindowStart = Date.now();
1339
+ if (this.rampTimer) {
1340
+ clearTimeout(this.rampTimer);
1341
+ }
1342
+ this.scheduleNextRampStep();
1343
+ }
1344
+ onDisconnect() { }
1345
+ scheduleNextRampStep() {
1346
+ if (this.currentStep >= this.config.rampSteps) {
1347
+ this.throttledSince = null;
1348
+ this.rampTimer = null;
1349
+ return;
1350
+ }
1351
+ const stepDuration = this.config.rampDurationMs / this.config.rampSteps;
1352
+ this.rampTimer = setTimeout(() => {
1353
+ this.currentStep++;
1354
+ this.scheduleNextRampStep();
1355
+ }, stepDuration);
1356
+ }
1357
+ getCurrentMultiplier() {
1358
+ if (!this.config.enabled || !this.throttledSince) {
1359
+ return 1;
1360
+ }
1361
+ const elapsed = Date.now() - this.throttledSince;
1362
+ if (elapsed >= this.config.rampDurationMs) {
1363
+ return 1;
1364
+ }
1365
+ const progress = this.currentStep / this.config.rampSteps;
1366
+ const multiplier = this.config.initialRateMultiplier + (1 - this.config.initialRateMultiplier) * progress;
1367
+ return Math.min(1, multiplier);
1368
+ }
1369
+ beforeSend() {
1370
+ if (!this.config.enabled || !this.throttledSince) {
1371
+ return { allowed: true };
1372
+ }
1373
+ const now = Date.now();
1374
+ const multiplier = this.getCurrentMultiplier();
1375
+ if (multiplier >= 1) {
1376
+ this.throttledSince = null;
1377
+ return { allowed: true };
1378
+ }
1379
+ if (now - this.currentWindowStart >= this.WINDOW_DURATION_MS) {
1380
+ this.sendsInCurrentWindow = 0;
1381
+ this.currentWindowStart = now;
1382
+ }
1383
+ const baselineRate = this.config.baselineRatePerMinute ? this.config.baselineRatePerMinute() : 8;
1384
+ const allowedInWindow = Math.max(1, Math.floor(baselineRate * multiplier));
1385
+ if (this.sendsInCurrentWindow >= allowedInWindow) {
1386
+ const windowRemaining = this.WINDOW_DURATION_MS - (now - this.currentWindowStart);
1387
+ return {
1388
+ allowed: false,
1389
+ reason: `Post-reconnect throttle: ${Math.floor(multiplier * 100)}% rate (${this.sendsInCurrentWindow}/${allowedInWindow} sends in window)`,
1390
+ retryAfterMs: windowRemaining
1391
+ };
1392
+ }
1393
+ this.sendsInCurrentWindow++;
1394
+ this.throttledSendCount++;
1395
+ return { allowed: true };
1396
+ }
1397
+ getStats() {
1398
+ const multiplier = this.getCurrentMultiplier();
1399
+ const isThrottled = this.throttledSince !== null && multiplier < 1;
1400
+ const remainingMs = isThrottled && this.throttledSince
1401
+ ? Math.max(0, this.config.rampDurationMs - (Date.now() - this.throttledSince))
1402
+ : 0;
1403
+ return {
1404
+ isThrottled,
1405
+ currentMultiplier: multiplier,
1406
+ throttledSinceMs: this.throttledSince,
1407
+ remainingMs,
1408
+ throttledSendCount: this.throttledSendCount,
1409
+ lifetimeReconnects: this.lifetimeReconnects
1410
+ };
1411
+ }
1412
+ destroy() {
1413
+ if (this.rampTimer) {
1414
+ clearTimeout(this.rampTimer);
1415
+ this.rampTimer = null;
1416
+ }
1417
+ this.throttledSince = null;
1418
+ }
1419
+ };
1420
+ var DEFAULT_CONFIG10 = {
1421
+ canonical: 'pn',
1422
+ maxEntries: 1e4
1423
+ };
1424
+ var LidResolver = class {
1425
+ config;
1426
+ persistence;
1427
+ lidToPn = new Map();
1428
+ pnToLid = new Map();
1429
+ stats = {
1430
+ learnedFromEvents: 0,
1431
+ lookupsServed: 0,
1432
+ lookupMisses: 0
1433
+ };
1434
+ constructor(config = {}) {
1435
+ this.config = { ...DEFAULT_CONFIG10, ...config };
1436
+ this.persistence = config.persistence;
1437
+ if (this.persistence?.load) {
1438
+ void this.hydrate();
1439
+ }
1440
+ }
1441
+ learn(mapping) {
1442
+ let lid = mapping.lid ? this.normalizeJid(mapping.lid) : void 0;
1443
+ let pn = mapping.pn ? this.normalizeJid(mapping.pn) : void 0;
1444
+ const phone = mapping.phone;
1445
+ if (!lid || (!pn && !phone)) {
1446
+ return;
1447
+ }
1448
+ if (!pn && phone) {
1449
+ pn = `${phone}@s.whatsapp.net`;
1450
+ }
1451
+ if (!lid || !pn)
1452
+ return;
1453
+ if (!lid.endsWith('@lid'))
1454
+ return;
1455
+ if (!pn.endsWith('@s.whatsapp.net'))
1456
+ return;
1457
+ const existing = this.lidToPn.get(lid);
1458
+ if (existing) {
1459
+ existing.seenCount++;
1460
+ existing.learnedAt = Date.now();
1461
+ return;
1462
+ }
1463
+ const extractedPhone = phone || pn.split('@')[0];
1464
+ const newMapping = {
1465
+ lid,
1466
+ pn,
1467
+ phone: extractedPhone,
1468
+ learnedAt: Date.now(),
1469
+ seenCount: 1
1470
+ };
1471
+ if (this.lidToPn.size >= this.config.maxEntries) {
1472
+ this.evictLRU();
1473
+ }
1474
+ this.lidToPn.set(lid, newMapping);
1475
+ this.pnToLid.set(pn, lid);
1476
+ this.stats.learnedFromEvents++;
1477
+ if (this.persistence?.save) {
1478
+ void this.flush();
1479
+ }
1480
+ }
1481
+ resolveCanonical(jid) {
1482
+ const normalized = this.normalizeJid(jid);
1483
+ if (this.config.canonical === 'pn') {
1484
+ if (normalized.endsWith('@lid')) {
1485
+ const mapping = this.lidToPn.get(normalized);
1486
+ if (mapping) {
1487
+ this.stats.lookupsServed++;
1488
+ mapping.learnedAt = Date.now();
1489
+ return mapping.pn;
1490
+ }
1491
+ this.stats.lookupMisses++;
1492
+ return jid;
1493
+ }
1494
+ this.stats.lookupsServed++;
1495
+ return normalized;
1496
+ }
1497
+ else {
1498
+ if (normalized.endsWith('@s.whatsapp.net')) {
1499
+ const lid = this.pnToLid.get(normalized);
1500
+ if (lid) {
1501
+ this.stats.lookupsServed++;
1502
+ const mapping = this.lidToPn.get(lid);
1503
+ if (mapping) {
1504
+ mapping.learnedAt = Date.now();
1505
+ }
1506
+ return lid;
1507
+ }
1508
+ this.stats.lookupMisses++;
1509
+ return jid;
1510
+ }
1511
+ this.stats.lookupsServed++;
1512
+ return normalized;
1513
+ }
1514
+ }
1515
+ getLid(pn) {
1516
+ const normalized = this.normalizeJid(pn);
1517
+ const lid = this.pnToLid.get(normalized);
1518
+ if (lid) {
1519
+ const mapping = this.lidToPn.get(lid);
1520
+ if (mapping) {
1521
+ mapping.learnedAt = Date.now();
1522
+ }
1523
+ }
1524
+ return lid || null;
1525
+ }
1526
+ getPn(lid) {
1527
+ const normalized = this.normalizeJid(lid);
1528
+ const mapping = this.lidToPn.get(normalized);
1529
+ if (mapping) {
1530
+ mapping.learnedAt = Date.now();
1531
+ return mapping.pn;
1532
+ }
1533
+ return null;
1534
+ }
1535
+ getMapping(jid) {
1536
+ const normalized = this.normalizeJid(jid);
1537
+ const byLid = this.lidToPn.get(normalized);
1538
+ if (byLid) {
1539
+ byLid.learnedAt = Date.now();
1540
+ return byLid;
1541
+ }
1542
+ const lid = this.pnToLid.get(normalized);
1543
+ if (lid) {
1544
+ const mapping = this.lidToPn.get(lid);
1545
+ if (mapping) {
1546
+ mapping.learnedAt = Date.now();
1547
+ return mapping;
1548
+ }
1549
+ }
1550
+ return null;
1551
+ }
1552
+ async hydrate() {
1553
+ if (!this.persistence?.load)
1554
+ return;
1555
+ try {
1556
+ const stored = await this.persistence.load();
1557
+ if (!stored || typeof stored !== 'object')
1558
+ return;
1559
+ for (const [lid, serialized] of Object.entries(stored)) {
1560
+ if (typeof serialized === 'string') {
1561
+ const pn = serialized;
1562
+ const phone = pn.split('@')[0];
1563
+ const mapping = {
1564
+ lid,
1565
+ pn,
1566
+ phone,
1567
+ learnedAt: Date.now(),
1568
+ seenCount: 1
1569
+ };
1570
+ this.lidToPn.set(lid, mapping);
1571
+ this.pnToLid.set(pn, lid);
1572
+ }
1573
+ else if (typeof serialized === 'object' && serialized !== null) {
1574
+ const mapping = serialized;
1575
+ this.lidToPn.set(lid, mapping);
1576
+ this.pnToLid.set(mapping.pn, lid);
1577
+ }
1578
+ }
1579
+ }
1580
+ catch (error) { }
1581
+ }
1582
+ async flush() {
1583
+ if (!this.persistence?.save)
1584
+ return;
1585
+ try {
1586
+ const toStore = {};
1587
+ for (const [lid, mapping] of this.lidToPn.entries()) {
1588
+ toStore[lid] = mapping;
1589
+ }
1590
+ await this.persistence.save(toStore);
1591
+ }
1592
+ catch (error) { }
1593
+ }
1594
+ getStats() {
1595
+ return {
1596
+ totalMappings: this.lidToPn.size,
1597
+ learnedFromEvents: this.stats.learnedFromEvents,
1598
+ lookupsServed: this.stats.lookupsServed,
1599
+ lookupMisses: this.stats.lookupMisses,
1600
+ canonicalForm: this.config.canonical
1601
+ };
1602
+ }
1603
+ reset() {
1604
+ this.lidToPn.clear();
1605
+ this.pnToLid.clear();
1606
+ this.stats = {
1607
+ learnedFromEvents: 0,
1608
+ lookupsServed: 0,
1609
+ lookupMisses: 0
1610
+ };
1611
+ }
1612
+ destroy() {
1613
+ this.reset();
1614
+ if (this.persistence?.save) {
1615
+ void this.flush();
1616
+ }
1617
+ }
1618
+ normalizeJid(jid) {
1619
+ return jid.replace(/:\d+@/, '@');
1620
+ }
1621
+ evictLRU() {
1622
+ let oldestLid = null;
1623
+ let oldestTime = Infinity;
1624
+ for (const [lid, mapping] of this.lidToPn.entries()) {
1625
+ if (mapping.learnedAt < oldestTime) {
1626
+ oldestTime = mapping.learnedAt;
1627
+ oldestLid = lid;
1628
+ }
1629
+ }
1630
+ if (oldestLid) {
1631
+ const mapping = this.lidToPn.get(oldestLid);
1632
+ if (mapping) {
1633
+ this.pnToLid.delete(mapping.pn);
1634
+ }
1635
+ this.lidToPn.delete(oldestLid);
1636
+ }
1637
+ }
1638
+ };
1639
+ var DEFAULT_CONFIG11 = {
1640
+ enabled: false,
1641
+ canonicalizeOutbound: true,
1642
+ learnFromEvents: true
1643
+ };
1644
+ var JidCanonicalizer = class {
1645
+ config;
1646
+ lidResolver;
1647
+ ownsResolver;
1648
+ stats = {
1649
+ outboundCanonicalized: 0,
1650
+ outboundPassthrough: 0,
1651
+ inboundLearned: 0,
1652
+ canonicalKeyHits: 0,
1653
+ canonicalKeyMisses: 0
1654
+ };
1655
+ constructor(config = {}) {
1656
+ this.config = { ...DEFAULT_CONFIG11, ...config };
1657
+ if (config.resolver) {
1658
+ this.lidResolver = config.resolver;
1659
+ this.ownsResolver = false;
1660
+ }
1661
+ else {
1662
+ this.lidResolver = new LidResolver(config.resolverConfig);
1663
+ this.ownsResolver = true;
1664
+ }
1665
+ }
1666
+ get resolver() {
1667
+ return this.lidResolver;
1668
+ }
1669
+ canonicalizeTarget(jid) {
1670
+ if (!this.config.enabled || !this.config.canonicalizeOutbound) {
1671
+ return jid;
1672
+ }
1673
+ const canonical = this.lidResolver.resolveCanonical(jid);
1674
+ if (canonical !== jid) {
1675
+ this.stats.outboundCanonicalized++;
1676
+ }
1677
+ else {
1678
+ this.stats.outboundPassthrough++;
1679
+ }
1680
+ return canonical;
1681
+ }
1682
+ canonicalKey(jid) {
1683
+ if (!jid || typeof jid !== 'string' || jid.trim() === '') {
1684
+ return 'thread:invalid';
1685
+ }
1686
+ const normalized = jid.trim().toLowerCase();
1687
+ const atIndex = normalized.indexOf('@');
1688
+ if (atIndex === -1) {
1689
+ return 'thread:invalid';
1690
+ }
1691
+ const user = normalized.substring(0, atIndex);
1692
+ const domain = normalized.substring(atIndex + 1);
1693
+ if (domain === 'g.us') {
1694
+ return `thread:group:${user}`;
1695
+ }
1696
+ if (domain === 'broadcast') {
1697
+ return `thread:broadcast:${user}`;
1698
+ }
1699
+ if (domain === 'newsletter') {
1700
+ return `thread:newsletter:${user}`;
1701
+ }
1702
+ if (domain === 's.whatsapp.net') {
1703
+ this.stats.canonicalKeyHits++;
1704
+ return `thread:${user}`;
1705
+ }
1706
+ if (domain === 'lid') {
1707
+ const mapping = this.lidResolver.getMapping(normalized);
1708
+ if (mapping?.pn) {
1709
+ const pnUser = mapping.pn.split('@')[0];
1710
+ this.stats.canonicalKeyHits++;
1711
+ return `thread:${pnUser}`;
1712
+ }
1713
+ else {
1714
+ this.stats.canonicalKeyMisses++;
1715
+ return `thread:lid:${user}`;
1716
+ }
1717
+ }
1718
+ return `thread:${domain}:${user}`;
1719
+ }
1720
+ onIncomingEvent(upsert) {
1721
+ if (!this.config.enabled || !this.config.learnFromEvents) {
1722
+ return;
1723
+ }
1724
+ for (const msg of upsert.messages || []) {
1725
+ this.learnFromMessage(msg);
1726
+ }
1727
+ }
1728
+ onMessageUpdate(updates) {
1729
+ if (!this.config.enabled || !this.config.learnFromEvents) {
1730
+ return;
1731
+ }
1732
+ for (const update of updates) {
1733
+ if (update.key) {
1734
+ this.learnFromMessageKey(update.key);
1735
+ }
1736
+ }
1737
+ }
1738
+ getStats() {
1739
+ return {
1740
+ resolver: this.lidResolver.getStats(),
1741
+ outboundCanonicalized: this.stats.outboundCanonicalized,
1742
+ outboundPassthrough: this.stats.outboundPassthrough,
1743
+ inboundLearned: this.stats.inboundLearned,
1744
+ canonicalKeyHits: this.stats.canonicalKeyHits,
1745
+ canonicalKeyMisses: this.stats.canonicalKeyMisses
1746
+ };
1747
+ }
1748
+ destroy() {
1749
+ if (this.ownsResolver) {
1750
+ this.lidResolver.destroy();
1751
+ }
1752
+ }
1753
+ learnFromMessage(msg) {
1754
+ if (!msg.key)
1755
+ return;
1756
+ this.learnFromMessageKey(msg.key);
1757
+ if (msg.participantPn && msg.key.participant) {
1758
+ this.lidResolver.learn({
1759
+ lid: msg.key.participant.endsWith('@lid') ? msg.key.participant : void 0,
1760
+ pn: msg.participantPn
1761
+ });
1762
+ this.stats.inboundLearned++;
1763
+ }
1764
+ }
1765
+ learnFromMessageKey(key) {
1766
+ if (!key)
1767
+ return;
1768
+ if (key.participant && key.participantPn) {
1769
+ if (key.participant.endsWith('@lid')) {
1770
+ this.lidResolver.learn({
1771
+ lid: key.participant,
1772
+ pn: key.participantPn
1773
+ });
1774
+ this.stats.inboundLearned++;
1775
+ }
1776
+ }
1777
+ if (key.remoteJid && key.senderPn) {
1778
+ if (key.remoteJid.endsWith('@lid')) {
1779
+ this.lidResolver.learn({
1780
+ lid: key.remoteJid,
1781
+ pn: key.senderPn
1782
+ });
1783
+ this.stats.inboundLearned++;
1784
+ }
1785
+ }
1786
+ if (key.participant && key.remoteJid) {
1787
+ if (key.participant.endsWith('@s.whatsapp.net') && key.remoteJid.endsWith('@lid')) {
1788
+ this.lidResolver.learn({
1789
+ lid: key.remoteJid,
1790
+ pn: key.participant
1791
+ });
1792
+ this.stats.inboundLearned++;
1793
+ }
1794
+ }
1795
+ }
1796
+ };
1797
+ function classifyDisconnect(statusCode) {
1798
+ if (statusCode === 401 || statusCode === 440) {
1799
+ return {
1800
+ category: 'fatal',
1801
+ shouldReconnect: false,
1802
+ message: 'Logged out \u2014 restart with QR code required',
1803
+ code: statusCode
1804
+ };
1805
+ }
1806
+ if (statusCode === 515) {
1807
+ return {
1808
+ category: 'fatal',
1809
+ shouldReconnect: false,
1810
+ message: 'Restart required by WhatsApp \u2014 client too old or protocol mismatch',
1811
+ code: statusCode
1812
+ };
1813
+ }
1814
+ if (statusCode === 405) {
1815
+ return {
1816
+ category: 'fatal',
1817
+ shouldReconnect: false,
1818
+ message: 'Method not allowed \u2014 server rejected connection method',
1819
+ code: statusCode
1820
+ };
1821
+ }
1822
+ if (statusCode === 409 || statusCode === 428) {
1823
+ return {
1824
+ category: 'fatal',
1825
+ shouldReconnect: false,
1826
+ message: 'Connection replaced \u2014 another device took over',
1827
+ code: statusCode
1828
+ };
1829
+ }
1830
+ if (statusCode === 412) {
1831
+ return {
1832
+ category: 'recoverable',
1833
+ shouldReconnect: true,
1834
+ backoffMs: 3e4,
1835
+ message: 'Precondition failed \u2014 auth state mismatch, retry after delay',
1836
+ code: statusCode
1837
+ };
1838
+ }
1839
+ if (statusCode === 429) {
1840
+ return {
1841
+ category: 'rate-limited',
1842
+ shouldReconnect: true,
1843
+ backoffMs: 3e5,
1844
+ message: 'Rate limited by WhatsApp \u2014 cool-off period required',
1845
+ code: statusCode
1846
+ };
1847
+ }
1848
+ if (statusCode === 503) {
1849
+ return {
1850
+ category: 'rate-limited',
1851
+ shouldReconnect: true,
1852
+ backoffMs: 6e4,
1853
+ message: 'WhatsApp service unavailable \u2014 temporary outage',
1854
+ code: statusCode
1855
+ };
1856
+ }
1857
+ if (statusCode === 408) {
1858
+ return {
1859
+ category: 'recoverable',
1860
+ shouldReconnect: true,
1861
+ backoffMs: 5e3,
1862
+ message: 'Connection timeout \u2014 network issue, safe to retry',
1863
+ code: statusCode
1864
+ };
1865
+ }
1866
+ if (statusCode === 500) {
1867
+ return {
1868
+ category: 'recoverable',
1869
+ shouldReconnect: true,
1870
+ backoffMs: 1e4,
1871
+ message: 'WhatsApp internal error \u2014 temporary server issue',
1872
+ code: statusCode
1873
+ };
1874
+ }
1875
+ if (statusCode === 1e3) {
1876
+ return {
1877
+ category: 'recoverable',
1878
+ shouldReconnect: true,
1879
+ backoffMs: 2e3,
1880
+ message: 'Connection closed gracefully \u2014 safe to reconnect',
1881
+ code: statusCode
1882
+ };
1883
+ }
1884
+ return {
1885
+ category: 'unknown',
1886
+ shouldReconnect: true,
1887
+ backoffMs: 15e3,
1888
+ message: `Unknown disconnect reason (code ${statusCode}) \u2014 reconnect with caution`,
1889
+ code: statusCode
1890
+ };
1891
+ }
1892
+ var DEFAULT_HEALTH_CONFIG = {
1893
+ badMacThreshold: 3,
1894
+ badMacWindowMs: 6e4
1895
+ };
1896
+ var SessionHealthMonitor = class {
1897
+ config;
1898
+ onDegraded;
1899
+ onRecovered;
1900
+ stats = {
1901
+ decryptSuccess: 0,
1902
+ decryptFail: 0,
1903
+ badMacCount: 0,
1904
+ isDegraded: false
1905
+ };
1906
+ badMacTimestamps = [];
1907
+ constructor(config = {}) {
1908
+ this.config = { ...DEFAULT_HEALTH_CONFIG, ...config };
1909
+ this.onDegraded = config.onDegraded;
1910
+ this.onRecovered = config.onRecovered;
1911
+ }
1912
+ recordDecryptSuccess() {
1913
+ this.stats.decryptSuccess++;
1914
+ this.checkRecovery();
1915
+ }
1916
+ recordDecryptFail(isBadMac = false) {
1917
+ this.stats.decryptFail++;
1918
+ if (isBadMac) {
1919
+ const now = Date.now();
1920
+ this.stats.badMacCount++;
1921
+ this.stats.lastBadMac = new Date(now);
1922
+ this.badMacTimestamps.push(now);
1923
+ const cutoff = now - this.config.badMacWindowMs;
1924
+ this.badMacTimestamps = this.badMacTimestamps.filter(ts => ts > cutoff);
1925
+ if (!this.stats.isDegraded && this.badMacTimestamps.length >= this.config.badMacThreshold) {
1926
+ this.stats.isDegraded = true;
1927
+ this.stats.degradedSince = new Date(now);
1928
+ this.onDegraded?.(this.getStats());
1929
+ }
1930
+ }
1931
+ }
1932
+ checkRecovery() {
1933
+ if (!this.stats.isDegraded)
1934
+ return;
1935
+ const now = Date.now();
1936
+ const cutoff = now - this.config.badMacWindowMs;
1937
+ this.badMacTimestamps = this.badMacTimestamps.filter(ts => ts > cutoff);
1938
+ if (this.badMacTimestamps.length < this.config.badMacThreshold) {
1939
+ this.stats.isDegraded = false;
1940
+ this.stats.degradedSince = void 0;
1941
+ this.onRecovered?.(this.getStats());
1942
+ }
1943
+ }
1944
+ getStats() {
1945
+ return { ...this.stats };
1946
+ }
1947
+ reset() {
1948
+ this.stats = {
1949
+ decryptSuccess: 0,
1950
+ decryptFail: 0,
1951
+ badMacCount: 0,
1952
+ isDegraded: false
1953
+ };
1954
+ this.badMacTimestamps = [];
1955
+ }
1956
+ };
1957
+ function wrapWithSessionStability(sock, config = {}) {
1958
+ const { canonicalJidNormalization = true, healthMonitoring = true, health: healthConfig, lidResolver } = config;
1959
+ const healthMonitor = healthMonitoring ? new SessionHealthMonitor(healthConfig) : null;
1960
+ return new Proxy(sock, {
1961
+ get(target, prop) {
1962
+ if (prop === 'sendMessage' && canonicalJidNormalization && lidResolver) {
1963
+ return async (jid, content, options) => {
1964
+ const canonical = lidResolver.resolveCanonical(jid);
1965
+ return target.sendMessage(canonical, content, options);
1966
+ };
1967
+ }
1968
+ if (prop === 'sessionHealthStats' && healthMonitor) {
1969
+ return healthMonitor.getStats();
1970
+ }
1971
+ if (prop === 'sessionHealthMonitor' && healthMonitor) {
1972
+ return healthMonitor;
1973
+ }
1974
+ return target[prop];
1975
+ }
1976
+ });
1977
+ }
1978
+ var PRESETS = {
1979
+ conservative: {
1980
+ maxPerMinute: 5,
1981
+ maxPerHour: 100,
1982
+ maxPerDay: 800,
1983
+ minDelayMs: 2500,
1984
+ maxDelayMs: 7e3,
1985
+ newChatDelayMs: 4e3,
1986
+ warmupDays: 10,
1987
+ day1Limit: 15,
1988
+ growthFactor: 1.8,
1989
+ inactivityThresholdHours: 72,
1990
+ autoPauseAt: 'medium',
1991
+ groupMultiplier: 0.5,
1992
+ groupProfiles: false,
1993
+ logging: true
1994
+ },
1995
+ moderate: {
1996
+ maxPerMinute: 10,
1997
+ maxPerHour: 300,
1998
+ maxPerDay: 1500,
1999
+ minDelayMs: 1500,
2000
+ maxDelayMs: 5e3,
2001
+ newChatDelayMs: 3e3,
2002
+ warmupDays: 7,
2003
+ day1Limit: 20,
2004
+ growthFactor: 1.8,
2005
+ inactivityThresholdHours: 72,
2006
+ autoPauseAt: 'high',
2007
+ groupMultiplier: 0.7,
2008
+ groupProfiles: false,
2009
+ logging: true
2010
+ },
2011
+ aggressive: {
2012
+ maxPerMinute: 20,
2013
+ maxPerHour: 800,
2014
+ maxPerDay: 4e3,
2015
+ minDelayMs: 800,
2016
+ maxDelayMs: 3e3,
2017
+ newChatDelayMs: 2e3,
2018
+ warmupDays: 4,
2019
+ day1Limit: 35,
2020
+ growthFactor: 2,
2021
+ inactivityThresholdHours: 48,
2022
+ autoPauseAt: 'critical',
2023
+ groupMultiplier: 0.9,
2024
+ groupProfiles: false,
2025
+ logging: true
2026
+ }
2027
+ };
2028
+ function resolveConfig(input) {
2029
+ if (input === void 0) {
2030
+ return { ...PRESETS.conservative };
2031
+ }
2032
+ if (typeof input === 'string') {
2033
+ if (!(input in PRESETS)) {
2034
+ throw new Error(`Unknown preset "${input}". Valid: ${Object.keys(PRESETS).join(', ')}`);
2035
+ }
2036
+ return { ...PRESETS[input] };
2037
+ }
2038
+ const { preset = 'conservative', ...overrides } = input;
2039
+ if (!(preset in PRESETS)) {
2040
+ throw new Error(`Unknown preset "${preset}". Valid: ${Object.keys(PRESETS).join(', ')}`);
2041
+ }
2042
+ return { ...PRESETS[preset], ...overrides };
2043
+ }
2044
+ var fs = __toESM(require('fs'), 1);
2045
+ var KNOWN_CHATS_MAX = 1e3;
2046
+ var DEBOUNCE_MS = 5e3;
2047
+ var StateManager = class {
2048
+ path;
2049
+ debounceTimer = null;
2050
+ constructor(filePath) {
2051
+ this.path = filePath;
2052
+ }
2053
+ load() {
2054
+ try {
2055
+ const raw = fs.readFileSync(this.path, 'utf-8');
2056
+ const parsed = JSON.parse(raw);
2057
+ if (parsed.version !== 3) {
2058
+ process.stderr.write('[baileys-antiban] WARN: corrupt state file or version mismatch, starting fresh\n');
2059
+ return null;
2060
+ }
2061
+ return parsed;
2062
+ }
2063
+ catch {
2064
+ if (fs.existsSync(this.path)) {
2065
+ process.stderr.write('[baileys-antiban] WARN: corrupt state file, starting fresh\n');
2066
+ }
2067
+ return null;
2068
+ }
2069
+ }
2070
+ saveDebounced(state) {
2071
+ if (this.debounceTimer) {
2072
+ clearTimeout(this.debounceTimer);
2073
+ }
2074
+ this.debounceTimer = setTimeout(() => {
2075
+ this.writeFile(state);
2076
+ this.debounceTimer = null;
2077
+ }, DEBOUNCE_MS);
2078
+ }
2079
+ saveImmediate(state) {
2080
+ if (this.debounceTimer) {
2081
+ clearTimeout(this.debounceTimer);
2082
+ this.debounceTimer = null;
2083
+ }
2084
+ this.writeFile(state);
2085
+ }
2086
+ flush() {
2087
+ if (this.debounceTimer) {
2088
+ clearTimeout(this.debounceTimer);
2089
+ this.debounceTimer = null;
2090
+ }
2091
+ }
2092
+ destroy() {
2093
+ this.flush();
2094
+ }
2095
+ writeFile(state) {
2096
+ const toSave = {
2097
+ ...state,
2098
+ savedAt: Date.now(),
2099
+ knownChats: state.knownChats.length > KNOWN_CHATS_MAX ? state.knownChats.slice(-KNOWN_CHATS_MAX) : state.knownChats
2100
+ };
2101
+ try {
2102
+ fs.writeFileSync(this.path, JSON.stringify(toSave, null, 2), 'utf-8');
2103
+ }
2104
+ catch (err) {
2105
+ process.stderr.write(`[baileys-antiban] WARN: failed to write state to ${this.path}: ${err}\n`);
2106
+ }
2107
+ }
2108
+ };
2109
+ function isGroup(jid) {
2110
+ return jid.endsWith('@g.us');
2111
+ }
2112
+ function isNewsletter(jid) {
2113
+ return jid.endsWith('@newsletter');
2114
+ }
2115
+ function isBroadcast(jid) {
2116
+ return jid === 'status@broadcast' || jid.endsWith('@broadcast');
2117
+ }
2118
+ function shouldUseGroupProfile(jid) {
2119
+ return isGroup(jid) || isNewsletter(jid);
2120
+ }
2121
+ function applyGroupMultiplier(limits, multiplier) {
2122
+ return {
2123
+ maxPerMinute: Math.max(1, Math.floor(limits.maxPerMinute * multiplier)),
2124
+ maxPerHour: Math.max(1, Math.floor(limits.maxPerHour * multiplier)),
2125
+ maxPerDay: Math.max(1, Math.floor(limits.maxPerDay * multiplier))
2126
+ };
2127
+ }
2128
+ function isLegacyConfig(cfg) {
2129
+ if (typeof cfg !== 'object' || cfg === null)
2130
+ return false;
2131
+ return ('rateLimiter' in cfg ||
2132
+ 'warmUp' in cfg ||
2133
+ 'health' in cfg ||
2134
+ 'timelock' in cfg ||
2135
+ 'replyRatio' in cfg ||
2136
+ 'contactGraph' in cfg ||
2137
+ 'presence' in cfg ||
2138
+ 'retryTracker' in cfg ||
2139
+ 'reconnectThrottle' in cfg ||
2140
+ 'lidResolver' in cfg ||
2141
+ 'jidCanonicalizer' in cfg ||
2142
+ 'sessionStability' in cfg);
2143
+ }
2144
+ function mapLegacyToFlat(legacy) {
2145
+ process.stderr.write('[baileys-antiban] DEPRECATED: Nested config (v2 style) detected. Migrate to flat config: new AntiBan({ maxPerMinute: 8 }).\n');
2146
+ const flat = {};
2147
+ if (legacy.rateLimiter?.maxPerMinute !== void 0)
2148
+ flat.maxPerMinute = legacy.rateLimiter.maxPerMinute;
2149
+ if (legacy.rateLimiter?.maxPerHour !== void 0)
2150
+ flat.maxPerHour = legacy.rateLimiter.maxPerHour;
2151
+ if (legacy.rateLimiter?.maxPerDay !== void 0)
2152
+ flat.maxPerDay = legacy.rateLimiter.maxPerDay;
2153
+ if (legacy.rateLimiter?.minDelayMs !== void 0)
2154
+ flat.minDelayMs = legacy.rateLimiter.minDelayMs;
2155
+ if (legacy.rateLimiter?.maxDelayMs !== void 0)
2156
+ flat.maxDelayMs = legacy.rateLimiter.maxDelayMs;
2157
+ if (legacy.rateLimiter?.newChatDelayMs !== void 0)
2158
+ flat.newChatDelayMs = legacy.rateLimiter.newChatDelayMs;
2159
+ if (legacy.warmUp?.warmUpDays !== void 0)
2160
+ flat.warmupDays = legacy.warmUp.warmUpDays;
2161
+ if (legacy.warmUp?.day1Limit !== void 0)
2162
+ flat.day1Limit = legacy.warmUp.day1Limit;
2163
+ if (legacy.warmUp?.growthFactor !== void 0)
2164
+ flat.growthFactor = legacy.warmUp.growthFactor;
2165
+ if (legacy.logging !== void 0)
2166
+ flat.logging = legacy.logging;
2167
+ return flat;
2168
+ }
2169
+ var AntiBan = class {
2170
+ rateLimiter;
2171
+ warmUp;
2172
+ health;
2173
+ timelockGuard;
2174
+ replyRatioGuard;
2175
+ contactGraphWarmer;
2176
+ presenceChoreographer;
2177
+ retryTrackerModule;
2178
+ reconnectThrottleModule;
2179
+ lidResolverModule = null;
2180
+ jidCanonicalizerModule = null;
2181
+ sessionStabilityMonitor = null;
2182
+ stateManager = null;
2183
+ resolvedConfig;
2184
+ logging;
2185
+ stats = {
2186
+ messagesAllowed: 0,
2187
+ messagesBlocked: 0,
2188
+ totalDelayMs: 0
2189
+ };
2190
+ constructor(input, warmUpStateArg) {
2191
+ let flatConfig;
2192
+ let legacyPassthrough = null;
2193
+ let warmUpState = warmUpStateArg;
2194
+ if (isLegacyConfig(input)) {
2195
+ legacyPassthrough = input;
2196
+ flatConfig = mapLegacyToFlat(legacyPassthrough);
2197
+ }
2198
+ else {
2199
+ flatConfig = {};
2200
+ legacyPassthrough = null;
2201
+ }
2202
+ const cfg = isLegacyConfig(input) ? resolveConfig(flatConfig) : resolveConfig(input);
2203
+ this.resolvedConfig = cfg;
2204
+ let savedState = null;
2205
+ if (cfg.persist) {
2206
+ this.stateManager = new StateManager(cfg.persist);
2207
+ savedState = this.stateManager.load();
2208
+ if (savedState) {
2209
+ warmUpState = savedState.warmup;
2210
+ }
2211
+ }
2212
+ this.logging = cfg.logging ?? true;
2213
+ this._log = (msg) => { if (this.logging)
2214
+ process.stdout.write(`[antiban] ${msg}\n`); };
2215
+ this.rateLimiter = new RateLimiter({
2216
+ maxPerMinute: cfg.maxPerMinute,
2217
+ maxPerHour: cfg.maxPerHour,
2218
+ maxPerDay: cfg.maxPerDay,
2219
+ minDelayMs: cfg.minDelayMs,
2220
+ maxDelayMs: cfg.maxDelayMs,
2221
+ newChatDelayMs: cfg.newChatDelayMs,
2222
+ ...(legacyPassthrough?.rateLimiter || {})
2223
+ });
2224
+ if (savedState?.knownChats) {
2225
+ this.rateLimiter.restoreKnownChats(savedState.knownChats);
2226
+ }
2227
+ this.warmUp = new WarmUp({
2228
+ warmUpDays: cfg.warmupDays,
2229
+ day1Limit: cfg.day1Limit,
2230
+ growthFactor: cfg.growthFactor,
2231
+ inactivityThresholdHours: cfg.inactivityThresholdHours,
2232
+ ...(legacyPassthrough?.warmUp || {})
2233
+ }, warmUpState);
2234
+ this.health = new HealthMonitor({
2235
+ autoPauseAt: cfg.autoPauseAt,
2236
+ ...(legacyPassthrough?.health || {}),
2237
+ onRiskChange: status => {
2238
+ const emoji = { low: '\u{1F7E2}', medium: '\u{1F7E1}', high: '\u{1F7E0}', critical: '\u{1F534}' };
2239
+ this._log(`${emoji[status.risk]} Risk level: ${status.risk.toUpperCase()} (score: ${status.score})`);
2240
+ this._log(status.recommendation);
2241
+ status.reasons.forEach(r => this._log(` \u2192 ${r}`));
2242
+ legacyPassthrough?.health?.onRiskChange?.(status);
2243
+ }
2244
+ });
2245
+ this.timelockGuard = new TimelockGuard({
2246
+ ...(legacyPassthrough?.timelock || {}),
2247
+ onTimelockDetected: state => {
2248
+ this.health.recordReachoutTimelock(state.enforcementType);
2249
+ this._log(`REACHOUT TIMELOCKED \u2014 ${state.enforcementType || 'unknown'}, expires ${state.expiresAt?.toISOString() || 'unknown'}`);
2250
+ legacyPassthrough?.timelock?.onTimelockDetected?.(state);
2251
+ },
2252
+ onTimelockLifted: state => {
2253
+ this._log('Timelock lifted \u2014 resuming new contact messages');
2254
+ legacyPassthrough?.timelock?.onTimelockLifted?.(state);
2255
+ }
2256
+ });
2257
+ this.replyRatioGuard = new ReplyRatioGuard(legacyPassthrough?.replyRatio);
2258
+ this.contactGraphWarmer = new ContactGraphWarmer(legacyPassthrough?.contactGraph);
2259
+ this.presenceChoreographer = new PresenceChoreographer(legacyPassthrough?.presence);
2260
+ this.retryTrackerModule = new RetryReasonTracker({
2261
+ ...(legacyPassthrough?.retryTracker || {}),
2262
+ onSpiral: (msgId, reason) => {
2263
+ this._log(`\u26A0\uFE0F Message ${msgId} stuck in retry spiral (${reason})`);
2264
+ legacyPassthrough?.retryTracker?.onSpiral?.(msgId, reason);
2265
+ }
2266
+ });
2267
+ this.reconnectThrottleModule = new PostReconnectThrottle({
2268
+ ...(legacyPassthrough?.reconnectThrottle || {}),
2269
+ baselineRatePerMinute: () => this.rateLimiter.getStats().limits.perMinute
2270
+ });
2271
+ if (legacyPassthrough?.jidCanonicalizer?.enabled) {
2272
+ if (legacyPassthrough.jidCanonicalizer.resolver) {
2273
+ this.jidCanonicalizerModule = new JidCanonicalizer(legacyPassthrough.jidCanonicalizer);
2274
+ this.lidResolverModule = legacyPassthrough.jidCanonicalizer.resolver;
2275
+ }
2276
+ else {
2277
+ const resolverConfig = legacyPassthrough.lidResolver || legacyPassthrough.jidCanonicalizer.resolverConfig;
2278
+ const resolver = new LidResolver(resolverConfig);
2279
+ this.lidResolverModule = resolver;
2280
+ this.jidCanonicalizerModule = new JidCanonicalizer({
2281
+ ...legacyPassthrough.jidCanonicalizer,
2282
+ resolver
2283
+ });
2284
+ }
2285
+ }
2286
+ else if (legacyPassthrough?.lidResolver) {
2287
+ this.lidResolverModule = new LidResolver(legacyPassthrough.lidResolver);
2288
+ }
2289
+ if (legacyPassthrough?.sessionStability?.enabled) {
2290
+ const healthConfig = {
2291
+ badMacThreshold: legacyPassthrough.sessionStability.badMacThreshold,
2292
+ badMacWindowMs: legacyPassthrough.sessionStability.badMacWindowMs,
2293
+ onDegraded: stats => {
2294
+ this._log(`\u{1F534} SESSION DEGRADED \u2014 Bad MAC rate: ${stats.badMacCount} in last ${legacyPassthrough?.sessionStability?.badMacWindowMs || 6e4}ms`);
2295
+ this._log('Consider restarting session or switching to LID-based canonical form');
2296
+ },
2297
+ onRecovered: () => {
2298
+ this._log('\u{1F7E2} SESSION RECOVERED \u2014 decrypt success rate improved');
2299
+ }
2300
+ };
2301
+ this.sessionStabilityMonitor = new SessionHealthMonitor(healthConfig);
2302
+ }
2303
+ }
2304
+ async beforeSend(recipient, content) {
2305
+ const healthStatus = this.health.getStatus();
2306
+ if (this.health.isPaused()) {
2307
+ this.stats.messagesBlocked++;
2308
+ this._log(`\u26D4 BLOCKED \u2014 health risk too high (${healthStatus.risk})`);
2309
+ return {
2310
+ allowed: false,
2311
+ delayMs: 0,
2312
+ reason: `Health risk ${healthStatus.risk}: ${healthStatus.recommendation}`,
2313
+ health: healthStatus
2314
+ };
2315
+ }
2316
+ const timelockDecision = this.timelockGuard.canSend(recipient);
2317
+ if (!timelockDecision.allowed) {
2318
+ this.stats.messagesBlocked++;
2319
+ this._log(`TIMELOCKED \u2014 ${timelockDecision.reason}`);
2320
+ return {
2321
+ allowed: false,
2322
+ delayMs: 0,
2323
+ reason: timelockDecision.reason,
2324
+ health: healthStatus
2325
+ };
2326
+ }
2327
+ if (!this.warmUp.canSend()) {
2328
+ this.stats.messagesBlocked++;
2329
+ const warmUpStatus = this.warmUp.getStatus();
2330
+ this._log(`\u23F3 BLOCKED \u2014 warm-up day ${warmUpStatus.day}/${warmUpStatus.totalDays}, limit reached (${warmUpStatus.todaySent}/${warmUpStatus.todayLimit})`);
2331
+ return {
2332
+ allowed: false,
2333
+ delayMs: 0,
2334
+ reason: `Warm-up limit: ${warmUpStatus.todaySent}/${warmUpStatus.todayLimit} messages today (day ${warmUpStatus.day})`,
2335
+ health: healthStatus,
2336
+ warmUpDay: warmUpStatus.day
2337
+ };
2338
+ }
2339
+ const contactGraphDecision = this.contactGraphWarmer.canMessage(recipient);
2340
+ if (!contactGraphDecision.allowed) {
2341
+ this.stats.messagesBlocked++;
2342
+ this._log(`\u{1F4CA} BLOCKED \u2014 contact graph: ${contactGraphDecision.reason}`);
2343
+ return {
2344
+ allowed: false,
2345
+ delayMs: 0,
2346
+ reason: `Contact graph: ${contactGraphDecision.reason}`,
2347
+ health: healthStatus
2348
+ };
2349
+ }
2350
+ const replyRatioDecision = this.replyRatioGuard.beforeSend(recipient);
2351
+ if (!replyRatioDecision.allowed) {
2352
+ this.stats.messagesBlocked++;
2353
+ this._log(`\u{1F4AC} BLOCKED \u2014 reply ratio: ${replyRatioDecision.reason}`);
2354
+ return {
2355
+ allowed: false,
2356
+ delayMs: 0,
2357
+ reason: `Reply ratio: ${replyRatioDecision.reason}`,
2358
+ health: healthStatus
2359
+ };
2360
+ }
2361
+ const reconnectThrottleDecision = this.reconnectThrottleModule.beforeSend();
2362
+ if (!reconnectThrottleDecision.allowed) {
2363
+ this.stats.messagesBlocked++;
2364
+ this._log(`\u{1F504} BLOCKED \u2014 reconnect throttle: ${reconnectThrottleDecision.reason}`);
2365
+ return {
2366
+ allowed: false,
2367
+ delayMs: reconnectThrottleDecision.retryAfterMs || 0,
2368
+ reason: reconnectThrottleDecision.reason || 'Post-reconnect throttle',
2369
+ health: healthStatus
2370
+ };
2371
+ }
2372
+ if (this.resolvedConfig.groupProfiles && shouldUseGroupProfile(recipient)) {
2373
+ const groupLimits = applyGroupMultiplier({
2374
+ maxPerMinute: this.resolvedConfig.maxPerMinute,
2375
+ maxPerHour: this.resolvedConfig.maxPerHour,
2376
+ maxPerDay: this.resolvedConfig.maxPerDay
2377
+ }, this.resolvedConfig.groupMultiplier);
2378
+ const stats = this.rateLimiter.getStats();
2379
+ if (stats.lastMinute >= groupLimits.maxPerMinute ||
2380
+ stats.lastHour >= groupLimits.maxPerHour ||
2381
+ stats.lastDay >= groupLimits.maxPerDay) {
2382
+ this.stats.messagesBlocked++;
2383
+ this._log(`\u{1F6AB} BLOCKED \u2014 group rate limit exceeded for ${recipient}`);
2384
+ return { allowed: false, delayMs: 0, reason: 'Group rate limit exceeded', health: healthStatus };
2385
+ }
2386
+ }
2387
+ let delay = await this.rateLimiter.getDelay(recipient, content);
2388
+ if (delay === -1) {
2389
+ this.stats.messagesBlocked++;
2390
+ this._log(`\u{1F6AB} BLOCKED \u2014 rate limit or identical message spam`);
2391
+ return {
2392
+ allowed: false,
2393
+ delayMs: 0,
2394
+ reason: 'Rate limit exceeded or identical message spam detected',
2395
+ health: healthStatus
2396
+ };
2397
+ }
2398
+ const activityFactor = this.presenceChoreographer.getCurrentActivityFactor();
2399
+ if (activityFactor < 1) {
2400
+ const multiplier = Math.min(5, 1 / activityFactor);
2401
+ delay = Math.floor(delay * multiplier);
2402
+ }
2403
+ const distractionCheck = this.presenceChoreographer.shouldPauseForDistraction();
2404
+ if (distractionCheck.pause) {
2405
+ delay += distractionCheck.durationMs;
2406
+ this._log(`\u23F8\uFE0F Distraction pause: +${Math.floor(distractionCheck.durationMs / 6e4)}min`);
2407
+ }
2408
+ const offlineCheck = this.presenceChoreographer.shouldTakeOfflineGap();
2409
+ if (offlineCheck.offline) {
2410
+ delay += offlineCheck.durationMs;
2411
+ this._log(`\u{1F4F4} Offline gap: +${Math.floor(offlineCheck.durationMs / 6e4)}min`);
2412
+ }
2413
+ this.stats.totalDelayMs += delay;
2414
+ return {
2415
+ allowed: true,
2416
+ delayMs: delay,
2417
+ health: healthStatus
2418
+ };
2419
+ }
2420
+ afterSend(recipient, content) {
2421
+ this.rateLimiter.record(recipient, content);
2422
+ this.warmUp.record();
2423
+ this.replyRatioGuard.recordSent(recipient);
2424
+ this.stats.messagesAllowed++;
2425
+ this.persistStateDebounced();
2426
+ }
2427
+ afterSendFailed(error) {
2428
+ this.health.recordMessageFailed(error);
2429
+ }
2430
+ onDisconnect(reason) {
2431
+ this.health.recordDisconnect(reason);
2432
+ this.reconnectThrottleModule.onDisconnect();
2433
+ const reasonStr = String(reason);
2434
+ if (reasonStr === '403' || reasonStr === '401' || reasonStr === 'forbidden' || reasonStr === 'loggedOut') {
2435
+ this.persistStateImmediate();
2436
+ }
2437
+ }
2438
+ onReconnect() {
2439
+ this.health.recordReconnect();
2440
+ this.reconnectThrottleModule.onReconnect();
2441
+ }
2442
+ onIncomingMessage(jid, msgText) {
2443
+ this.replyRatioGuard.recordReceived(jid);
2444
+ this.contactGraphWarmer.onIncomingMessage(jid);
2445
+ return this.replyRatioGuard.suggestReply(jid, msgText);
2446
+ }
2447
+ getStats() {
2448
+ const stats = {
2449
+ ...this.stats,
2450
+ health: this.health.getStatus(),
2451
+ warmUp: this.warmUp.getStatus(),
2452
+ rateLimiter: this.rateLimiter.getStats()
2453
+ };
2454
+ if (this.replyRatioGuard['config']?.enabled) {
2455
+ stats.replyRatio = this.replyRatioGuard.getStats();
2456
+ }
2457
+ if (this.contactGraphWarmer['config']?.enabled) {
2458
+ stats.contactGraph = this.contactGraphWarmer.getStats();
2459
+ }
2460
+ if (this.presenceChoreographer['config']?.enabled) {
2461
+ stats.presence = this.presenceChoreographer.getStats();
2462
+ }
2463
+ if (this.retryTrackerModule['config']?.enabled) {
2464
+ stats.retryTracker = this.retryTrackerModule.getStats();
2465
+ }
2466
+ if (this.reconnectThrottleModule['config']?.enabled) {
2467
+ stats.reconnectThrottle = this.reconnectThrottleModule.getStats();
2468
+ }
2469
+ if (this.lidResolverModule) {
2470
+ stats.lidResolver = this.lidResolverModule.getStats();
2471
+ }
2472
+ if (this.jidCanonicalizerModule) {
2473
+ stats.jidCanonicalizer = this.jidCanonicalizerModule.getStats();
2474
+ }
2475
+ if (this.sessionStabilityMonitor) {
2476
+ stats.sessionStability = this.sessionStabilityMonitor.getStats();
2477
+ }
2478
+ return stats;
2479
+ }
2480
+ get timelock() {
2481
+ return this.timelockGuard;
2482
+ }
2483
+ get replyRatio() {
2484
+ return this.replyRatioGuard;
2485
+ }
2486
+ get contactGraph() {
2487
+ return this.contactGraphWarmer;
2488
+ }
2489
+ get presence() {
2490
+ return this.presenceChoreographer;
2491
+ }
2492
+ get retryTracker() {
2493
+ return this.retryTrackerModule;
2494
+ }
2495
+ get reconnectThrottle() {
2496
+ return this.reconnectThrottleModule;
2497
+ }
2498
+ get lidResolver() {
2499
+ return this.lidResolverModule;
2500
+ }
2501
+ get jidCanonicalizer() {
2502
+ return this.jidCanonicalizerModule;
2503
+ }
2504
+ get sessionStability() {
2505
+ return this.sessionStabilityMonitor;
2506
+ }
2507
+ exportWarmUpState() {
2508
+ return this.warmUp.exportState();
2509
+ }
2510
+ pause() {
2511
+ this.health.setPaused(true);
2512
+ this._log('\u23F8\uFE0F Sending paused manually');
2513
+ }
2514
+ resume() {
2515
+ this.health.setPaused(false);
2516
+ this._log('\u25B6\uFE0F Sending resumed');
2517
+ }
2518
+ reset() {
2519
+ this.timelockGuard.reset();
2520
+ this.health.reset();
2521
+ this.warmUp.reset();
2522
+ this.replyRatioGuard.reset();
2523
+ this.contactGraphWarmer.reset();
2524
+ this.presenceChoreographer.reset();
2525
+ this.retryTrackerModule.destroy();
2526
+ this.reconnectThrottleModule.destroy();
2527
+ this.stats = { messagesAllowed: 0, messagesBlocked: 0, totalDelayMs: 0 };
2528
+ this._log('\u{1F504} Reset \u2014 starting fresh warm-up');
2529
+ }
2530
+ persistStateDebounced() {
2531
+ if (!this.stateManager)
2532
+ return;
2533
+ const state = {
2534
+ warmup: this.warmUp.exportState(),
2535
+ knownChats: Array.from(this.rateLimiter.getKnownChats()),
2536
+ savedAt: Date.now(),
2537
+ version: 3
2538
+ };
2539
+ this.stateManager.saveDebounced(state);
2540
+ }
2541
+ persistStateImmediate() {
2542
+ if (!this.stateManager)
2543
+ return;
2544
+ const state = {
2545
+ warmup: this.warmUp.exportState(),
2546
+ knownChats: Array.from(this.rateLimiter.getKnownChats()),
2547
+ savedAt: Date.now(),
2548
+ version: 3
2549
+ };
2550
+ this.stateManager.saveImmediate(state);
2551
+ }
2552
+ destroy() {
2553
+ this.stateManager?.destroy();
2554
+ this.timelockGuard.reset();
2555
+ this.replyRatioGuard.reset();
2556
+ this.contactGraphWarmer.reset();
2557
+ this.presenceChoreographer.reset();
2558
+ this.retryTrackerModule.destroy();
2559
+ this.reconnectThrottleModule.destroy();
2560
+ this.jidCanonicalizerModule?.destroy();
2561
+ this.lidResolverModule?.destroy();
2562
+ this.sessionStabilityMonitor?.reset();
2563
+ this._log('\u{1F9F9} Destroyed \u2014 all timers cleared');
2564
+ }
2565
+ };
2566
+ var fs2 = __toESM(require('fs'), 1);
2567
+ var path = __toESM(require('path'), 1);
2568
+ var LidFirstResolver = class {
2569
+ lidToPhone = new Map();
2570
+ phoneToLid = new Map();
2571
+ loadFromAuthDir(authDir) {
2572
+ try {
2573
+ if (!fs2.existsSync(authDir)) {
2574
+ return;
2575
+ }
2576
+ const files = fs2.readdirSync(authDir);
2577
+ const reverseMappingFiles = files.filter(f => f.startsWith('lid-mapping-') && f.endsWith('_reverse.json'));
2578
+ for (const file of reverseMappingFiles) {
2579
+ const filePath = path.join(authDir, file);
2580
+ const content = fs2.readFileSync(filePath, 'utf-8');
2581
+ const data = JSON.parse(content);
2582
+ for (const [lid, pnJid] of Object.entries(data)) {
2583
+ if (typeof pnJid === 'string') {
2584
+ const phone = this.extractPhone(pnJid);
2585
+ if (phone && lid.endsWith('@lid')) {
2586
+ const mapping = {
2587
+ lid: this.normalizeLid(lid),
2588
+ phone,
2589
+ learnedAt: Date.now(),
2590
+ source: 'auth-dir'
2591
+ };
2592
+ this.lidToPhone.set(mapping.lid, mapping);
2593
+ this.phoneToLid.set(phone, mapping.lid);
2594
+ }
2595
+ }
2596
+ }
2597
+ }
2598
+ }
2599
+ catch (error) { }
2600
+ }
2601
+ learnFromEvent(event) {
2602
+ try {
2603
+ if (event.key?.remoteJid) {
2604
+ const jid = event.key.remoteJid;
2605
+ this.learnJid(jid, 'event');
2606
+ }
2607
+ if (event.key?.participant) {
2608
+ const jid = event.key.participant;
2609
+ this.learnJid(jid, 'event');
2610
+ }
2611
+ if (event.id) {
2612
+ this.learnJid(event.id, 'event');
2613
+ }
2614
+ if (event.pushName && event.key?.remoteJid) {
2615
+ this.learnJid(event.key.remoteJid, 'event');
2616
+ }
2617
+ }
2618
+ catch (error) { }
2619
+ }
2620
+ resolveToLID(phoneOrJid) {
2621
+ const phone = this.extractPhone(phoneOrJid);
2622
+ if (!phone)
2623
+ return null;
2624
+ return this.phoneToLid.get(phone) || null;
2625
+ }
2626
+ resolveToPhone(lid) {
2627
+ const normalized = this.normalizeLid(lid);
2628
+ const mapping = this.lidToPhone.get(normalized);
2629
+ return mapping ? mapping.phone : null;
2630
+ }
2631
+ getMapping(jid) {
2632
+ const normalized = this.normalizeLid(jid);
2633
+ const byLid = this.lidToPhone.get(normalized);
2634
+ if (byLid)
2635
+ return byLid;
2636
+ const phone = this.extractPhone(jid);
2637
+ if (phone) {
2638
+ const lid = this.phoneToLid.get(phone);
2639
+ if (lid)
2640
+ return this.lidToPhone.get(lid) || null;
2641
+ }
2642
+ return null;
2643
+ }
2644
+ size() {
2645
+ return this.lidToPhone.size;
2646
+ }
2647
+ clear() {
2648
+ this.lidToPhone.clear();
2649
+ this.phoneToLid.clear();
2650
+ }
2651
+ learnJid(_jid, _source) { }
2652
+ extractPhone(jid) {
2653
+ if (!jid)
2654
+ return null;
2655
+ let cleaned = jid.replace('@s.whatsapp.net', '');
2656
+ cleaned = cleaned.replace(/:\d+$/, '');
2657
+ if (/^\d+$/.test(cleaned)) {
2658
+ return cleaned;
2659
+ }
2660
+ return null;
2661
+ }
2662
+ normalizeLid(lid) {
2663
+ return lid.replace(/:\d+@/, '@');
2664
+ }
2665
+ };
2666
+ function createLidFirstResolver() {
2667
+ return new LidFirstResolver();
2668
+ }
2669
+ var MessageRetryReason;
2670
+ (function (MessageRetryReason2) {
2671
+ MessageRetryReason2[(MessageRetryReason2['UnknownError'] = 0)] = 'UnknownError';
2672
+ MessageRetryReason2[(MessageRetryReason2['GenericError'] = 1)] = 'GenericError';
2673
+ MessageRetryReason2[(MessageRetryReason2['SignalErrorInvalidKeyId'] = 3)] = 'SignalErrorInvalidKeyId';
2674
+ MessageRetryReason2[(MessageRetryReason2['SignalErrorInvalidMessage'] = 4)] = 'SignalErrorInvalidMessage';
2675
+ MessageRetryReason2[(MessageRetryReason2['SignalErrorNoSession'] = 5)] = 'SignalErrorNoSession';
2676
+ MessageRetryReason2[(MessageRetryReason2['SignalErrorBadMac'] = 7)] = 'SignalErrorBadMac';
2677
+ MessageRetryReason2[(MessageRetryReason2['MessageExpired'] = 8)] = 'MessageExpired';
2678
+ MessageRetryReason2[(MessageRetryReason2['DecryptionError'] = 9)] = 'DecryptionError';
2679
+ })(MessageRetryReason || (MessageRetryReason = {}));
2680
+ var MAC_ERROR_CODES = new Set([
2681
+ MessageRetryReason.SignalErrorBadMac,
2682
+ MessageRetryReason.SignalErrorInvalidMessage,
2683
+ MessageRetryReason.SignalErrorNoSession,
2684
+ MessageRetryReason.SignalErrorInvalidKeyId
2685
+ ]);
2686
+ function parseRetryReason(code) {
2687
+ if (code === void 0 || code === null) {
2688
+ return MessageRetryReason.UnknownError;
2689
+ }
2690
+ const n = typeof code === 'string' ? parseInt(code, 10) : code;
2691
+ if (isNaN(n)) {
2692
+ return MessageRetryReason.UnknownError;
2693
+ }
2694
+ if (Object.values(MessageRetryReason).includes(n)) {
2695
+ return n;
2696
+ }
2697
+ return MessageRetryReason.UnknownError;
2698
+ }
2699
+ function isMacError(reason) {
2700
+ return MAC_ERROR_CODES.has(reason);
2701
+ }
2702
+ function getRetryReasonDescription(reason) {
2703
+ switch (reason) {
2704
+ case MessageRetryReason.UnknownError:
2705
+ return 'Unknown error';
2706
+ case MessageRetryReason.GenericError:
2707
+ return 'Generic error';
2708
+ case MessageRetryReason.SignalErrorInvalidKeyId:
2709
+ return 'Invalid key ID \u2014 peer prekey rotated';
2710
+ case MessageRetryReason.SignalErrorInvalidMessage:
2711
+ return 'Invalid message format';
2712
+ case MessageRetryReason.SignalErrorNoSession:
2713
+ return 'No session \u2014 peer not initialized';
2714
+ case MessageRetryReason.SignalErrorBadMac:
2715
+ return 'Bad MAC \u2014 encryption session mismatch';
2716
+ case MessageRetryReason.MessageExpired:
2717
+ return 'Message expired \u2014 too old to decrypt';
2718
+ case MessageRetryReason.DecryptionError:
2719
+ return 'Decryption failed';
2720
+ default:
2721
+ return `Unknown reason code ${reason}`;
2722
+ }
2723
+ }
2724
+ function wrapSocket(sock, config, warmUpState, wrapOptions) {
2725
+ const antiban = new AntiBan(config, warmUpState);
2726
+ const options = {
2727
+ autoRespondToIncoming: false,
2728
+ ...wrapOptions
2729
+ };
2730
+ if (typeof sock.ev.process === 'function') {
2731
+ sock.ev.process(async (events) => {
2732
+ if (events['connection.update']) {
2733
+ const update = events['connection.update'];
2734
+ if (update.connection === 'close') {
2735
+ const reason = update.lastDisconnect?.error?.output?.statusCode || 'unknown';
2736
+ antiban.onDisconnect(reason);
2737
+ }
2738
+ if (update.connection === 'open') {
2739
+ antiban.onReconnect();
2740
+ }
2741
+ if (update.reachoutTimeLock) {
2742
+ antiban.timelock.onTimelockUpdate({
2743
+ isActive: update.reachoutTimeLock.isActive,
2744
+ timeEnforcementEnds: update.reachoutTimeLock.timeEnforcementEnds,
2745
+ enforcementType: update.reachoutTimeLock.enforcementType
2746
+ });
2747
+ }
2748
+ }
2749
+ if (events['messages.update']) {
2750
+ const updates = events['messages.update'];
2751
+ for (const update of updates) {
2752
+ if (update?.update?.messageStubParameters) {
2753
+ const params = update.update.messageStubParameters;
2754
+ if (params.includes(463) || params.includes('463')) {
2755
+ antiban.timelock.record463Error();
2756
+ }
2757
+ }
2758
+ antiban.retryTracker.onMessageUpdate(update);
2759
+ }
2760
+ antiban.jidCanonicalizer?.onMessageUpdate(updates);
2761
+ }
2762
+ if (events['messages.upsert']) {
2763
+ const { messages } = events['messages.upsert'];
2764
+ antiban.jidCanonicalizer?.onIncomingEvent(events['messages.upsert']);
2765
+ for (const msg of messages || []) {
2766
+ const jid = msg.key?.remoteJid;
2767
+ if (!jid)
2768
+ continue;
2769
+ antiban.timelock.registerKnownChat(jid);
2770
+ const isSelf = msg.key?.fromMe || false;
2771
+ if (isSelf)
2772
+ continue;
2773
+ const msgText = msg.message?.conversation ||
2774
+ msg.message?.extendedTextMessage?.text ||
2775
+ msg.message?.imageMessage?.caption ||
2776
+ msg.message?.videoMessage?.caption ||
2777
+ '';
2778
+ const replySuggestion = antiban.onIncomingMessage(jid, msgText);
2779
+ if (options.autoRespondToIncoming && replySuggestion.shouldReply && replySuggestion.suggestedText) {
2780
+ const replyDelay = Math.floor(Math.random() * 12e3) + 3e3;
2781
+ setTimeout(async () => {
2782
+ try {
2783
+ await sock.sendMessage(jid, { text: replySuggestion.suggestedText });
2784
+ }
2785
+ catch (error) { }
2786
+ }, replyDelay);
2787
+ }
2788
+ }
2789
+ }
2790
+ });
2791
+ }
2792
+ else {
2793
+ sock.ev.on('connection.update', update => {
2794
+ if (update.connection === 'close') {
2795
+ const reason = update.lastDisconnect?.error?.output?.statusCode || 'unknown';
2796
+ antiban.onDisconnect(reason);
2797
+ }
2798
+ if (update.connection === 'open') {
2799
+ antiban.onReconnect();
2800
+ }
2801
+ if (update.reachoutTimeLock) {
2802
+ antiban.timelock.onTimelockUpdate({
2803
+ isActive: update.reachoutTimeLock.isActive,
2804
+ timeEnforcementEnds: update.reachoutTimeLock.timeEnforcementEnds,
2805
+ enforcementType: update.reachoutTimeLock.enforcementType
2806
+ });
2807
+ }
2808
+ });
2809
+ sock.ev.on('messages.update', updates => {
2810
+ for (const update of updates) {
2811
+ if (update?.update?.messageStubParameters) {
2812
+ const params = update.update.messageStubParameters;
2813
+ if (params.includes(463) || params.includes('463')) {
2814
+ antiban.timelock.record463Error();
2815
+ }
2816
+ }
2817
+ antiban.retryTracker.onMessageUpdate(update);
2818
+ }
2819
+ antiban.jidCanonicalizer?.onMessageUpdate(updates);
2820
+ });
2821
+ sock.ev.on('messages.upsert', upsert => {
2822
+ const { messages } = upsert;
2823
+ antiban.jidCanonicalizer?.onIncomingEvent(upsert);
2824
+ for (const msg of messages || []) {
2825
+ const jid = msg.key?.remoteJid;
2826
+ if (!jid)
2827
+ continue;
2828
+ antiban.timelock.registerKnownChat(jid);
2829
+ const isSelf = msg.key?.fromMe || false;
2830
+ if (isSelf)
2831
+ continue;
2832
+ const msgText = msg.message?.conversation ||
2833
+ msg.message?.extendedTextMessage?.text ||
2834
+ msg.message?.imageMessage?.caption ||
2835
+ msg.message?.videoMessage?.caption ||
2836
+ '';
2837
+ const replySuggestion = antiban.onIncomingMessage(jid, msgText);
2838
+ if (options.autoRespondToIncoming && replySuggestion.shouldReply && replySuggestion.suggestedText) {
2839
+ const replyDelay = Math.floor(Math.random() * 12e3) + 3e3;
2840
+ setTimeout(async () => {
2841
+ try {
2842
+ await sock.sendMessage(jid, { text: replySuggestion.suggestedText });
2843
+ }
2844
+ catch (error) { }
2845
+ }, replyDelay);
2846
+ }
2847
+ }
2848
+ });
2849
+ }
2850
+ const originalSendMessage = sock.sendMessage.bind(sock);
2851
+ const wrappedSendMessage = async (jid, content, options2) => {
2852
+ const canonicalJid = antiban.jidCanonicalizer?.canonicalizeTarget(jid) || jid;
2853
+ const text = content?.text || content?.caption || content?.image?.caption || '';
2854
+ const decision = await antiban.beforeSend(canonicalJid, text);
2855
+ if (!decision.allowed) {
2856
+ throw new Error(`[baileys-antiban] Message blocked: ${decision.reason}`);
2857
+ }
2858
+ if (decision.delayMs > 0) {
2859
+ await new Promise(resolve => setTimeout(resolve, decision.delayMs));
2860
+ }
2861
+ try {
2862
+ const result = await originalSendMessage(canonicalJid, content, options2);
2863
+ antiban.afterSend(canonicalJid, text);
2864
+ antiban.timelock.registerKnownChat(canonicalJid);
2865
+ if (result?.key?.id) {
2866
+ antiban.retryTracker.clear(result.key.id);
2867
+ }
2868
+ return result;
2869
+ }
2870
+ catch (error) {
2871
+ antiban.afterSendFailed(error instanceof Error ? error.message : String(error));
2872
+ throw error;
2873
+ }
2874
+ };
2875
+ const wrapped = Object.create(sock);
2876
+ wrapped.sendMessage = wrappedSendMessage;
2877
+ wrapped.antiban = antiban;
2878
+ wrapped.antiban.destroy = antiban.destroy.bind(antiban);
2879
+ return wrapped;
2880
+ }
2881
+ var import_events = require('events');
2882
+ var DEFAULT_CONFIG12 = {
2883
+ maxAttempts: 3,
2884
+ retryBaseDelayMs: 3e4,
2885
+ maxQueueSize: 1e3,
2886
+ priorityOrder: true
2887
+ };
2888
+ var MessageQueue = class extends import_events.EventEmitter {
2889
+ config;
2890
+ queue = [];
2891
+ processing = false;
2892
+ sendFn = null;
2893
+ drainTimer = null;
2894
+ idCounter = 0;
2895
+ constructor(config = {}) {
2896
+ super();
2897
+ this.config = { ...DEFAULT_CONFIG12, ...config };
2898
+ }
2899
+ setSendFunction(fn) {
2900
+ this.sendFn = fn;
2901
+ }
2902
+ add(recipient, content, options) {
2903
+ if (this.queue.length >= this.config.maxQueueSize) {
2904
+ throw new Error(`Queue full (${this.config.maxQueueSize} messages)`);
2905
+ }
2906
+ const id = `msg_${Date.now()}_${++this.idCounter}`;
2907
+ const message = {
2908
+ id,
2909
+ recipient,
2910
+ content,
2911
+ priority: options?.priority || 'normal',
2912
+ addedAt: Date.now(),
2913
+ attempts: 0,
2914
+ maxAttempts: this.config.maxAttempts,
2915
+ scheduledFor: options?.scheduledFor?.getTime(),
2916
+ metadata: options?.metadata
2917
+ };
2918
+ this.queue.push(message);
2919
+ this.sortQueue();
2920
+ this.emit('added', message);
2921
+ return id;
2922
+ }
2923
+ addBulk(recipients, content, options) {
2924
+ return recipients.map(r => this.add(r, content, options));
2925
+ }
2926
+ start(intervalMs = 1e3) {
2927
+ if (this.drainTimer)
2928
+ return;
2929
+ this.drainTimer = setInterval(() => this.processNext(), intervalMs);
2930
+ this.emit('started');
2931
+ }
2932
+ stop() {
2933
+ if (this.drainTimer) {
2934
+ clearInterval(this.drainTimer);
2935
+ this.drainTimer = null;
2936
+ }
2937
+ this.emit('stopped');
2938
+ }
2939
+ destroy() {
2940
+ this.stop();
2941
+ }
2942
+ async processNext() {
2943
+ if (this.processing || !this.sendFn)
2944
+ return;
2945
+ const now = Date.now();
2946
+ const message = this.queue.find(m => !m.scheduledFor || m.scheduledFor <= now);
2947
+ if (!message)
2948
+ return;
2949
+ this.processing = true;
2950
+ try {
2951
+ message.attempts++;
2952
+ await this.sendFn(message.recipient, message.content);
2953
+ this.queue = this.queue.filter(m => m.id !== message.id);
2954
+ this.emit('sent', message);
2955
+ }
2956
+ catch (err) {
2957
+ message.lastError = err.message;
2958
+ if (err.message?.includes('baileys-antiban')) {
2959
+ message.attempts--;
2960
+ this.emit('delayed', message, err.message);
2961
+ }
2962
+ else if (message.attempts >= message.maxAttempts) {
2963
+ this.queue = this.queue.filter(m => m.id !== message.id);
2964
+ this.emit('failed', message, err.message);
2965
+ }
2966
+ else {
2967
+ const backoff = this.config.retryBaseDelayMs * Math.pow(2, message.attempts - 1);
2968
+ message.scheduledFor = Date.now() + backoff;
2969
+ this.emit('retry', message, message.attempts, backoff);
2970
+ }
2971
+ }
2972
+ finally {
2973
+ this.processing = false;
2974
+ }
2975
+ }
2976
+ getStats() {
2977
+ const now = Date.now();
2978
+ return {
2979
+ total: this.queue.length,
2980
+ pending: this.queue.filter(m => !m.scheduledFor || m.scheduledFor <= now).length,
2981
+ scheduled: this.queue.filter(m => m.scheduledFor && m.scheduledFor > now).length,
2982
+ byPriority: {
2983
+ high: this.queue.filter(m => m.priority === 'high').length,
2984
+ normal: this.queue.filter(m => m.priority === 'normal').length,
2985
+ low: this.queue.filter(m => m.priority === 'low').length
2986
+ },
2987
+ processing: this.processing,
2988
+ isRunning: this.drainTimer !== null
2989
+ };
2990
+ }
2991
+ clear() {
2992
+ const count = this.queue.length;
2993
+ this.queue = [];
2994
+ this.emit('cleared', count);
2995
+ }
2996
+ remove(id) {
2997
+ const before = this.queue.length;
2998
+ this.queue = this.queue.filter(m => m.id !== id);
2999
+ return this.queue.length < before;
3000
+ }
3001
+ export() {
3002
+ return [...this.queue];
3003
+ }
3004
+ import(messages) {
3005
+ this.queue = [...messages];
3006
+ this.sortQueue();
3007
+ }
3008
+ sortQueue() {
3009
+ if (!this.config.priorityOrder)
3010
+ return;
3011
+ const priorityWeight = { high: 0, normal: 1, low: 2 };
3012
+ this.queue.sort((a, b) => {
3013
+ const pDiff = priorityWeight[a.priority] - priorityWeight[b.priority];
3014
+ if (pDiff !== 0)
3015
+ return pDiff;
3016
+ return a.addedAt - b.addedAt;
3017
+ });
3018
+ }
3019
+ };
3020
+ var DEFAULT_CONFIG13 = {
3021
+ zeroWidthChars: true,
3022
+ punctuationVariation: true,
3023
+ emojiPadding: false,
3024
+ synonyms: false
3025
+ };
3026
+ var ZERO_WIDTH = [
3027
+ '\u200B',
3028
+ '\u200C',
3029
+ '\u200D',
3030
+ '\uFEFF'
3031
+ ];
3032
+ var SYNONYMS = {
3033
+ hello: ['hi', 'hey', 'howdy'],
3034
+ hi: ['hello', 'hey', 'howdy'],
3035
+ thanks: ['thank you', 'thx', 'cheers'],
3036
+ please: ['kindly', 'pls'],
3037
+ great: ['awesome', 'excellent', 'wonderful'],
3038
+ good: ['great', 'nice', 'fine'],
3039
+ buy: ['purchase', 'get', 'grab'],
3040
+ sell: ['offer', 'list'],
3041
+ price: ['cost', 'amount', 'value'],
3042
+ available: ['in stock', 'on offer'],
3043
+ check: ['look at', 'see', 'view'],
3044
+ join: ['participate', 'enter', 'come to'],
3045
+ start: ['begin', 'kick off', 'commence'],
3046
+ end: ['finish', 'close', 'conclude'],
3047
+ bid: ['offer', 'place a bid'],
3048
+ win: ['secure', 'take home'],
3049
+ item: ['lot', 'piece', 'product']
3050
+ };
3051
+ var ContentVariator = class {
3052
+ config;
3053
+ counter = 0;
3054
+ constructor(config = {}) {
3055
+ this.config = { ...DEFAULT_CONFIG13, ...config };
3056
+ }
3057
+ vary(text) {
3058
+ let result = text;
3059
+ this.counter++;
3060
+ if (this.config.customVariator) {
3061
+ return this.config.customVariator(result, this.counter);
3062
+ }
3063
+ if (this.config.synonyms) {
3064
+ result = this.applySynonyms(result);
3065
+ }
3066
+ if (this.config.zeroWidthChars) {
3067
+ result = this.addZeroWidth(result);
3068
+ }
3069
+ if (this.config.punctuationVariation) {
3070
+ result = this.varyPunctuation(result);
3071
+ }
3072
+ if (this.config.emojiPadding) {
3073
+ result = this.addEmojiPadding(result);
3074
+ }
3075
+ return result;
3076
+ }
3077
+ varyBulk(text, count) {
3078
+ const results = [];
3079
+ const seen = new Set();
3080
+ for (let i = 0; i < count; i++) {
3081
+ let variation = this.vary(text);
3082
+ let attempts = 0;
3083
+ while (seen.has(variation) && attempts < 10) {
3084
+ variation = this.vary(text);
3085
+ attempts++;
3086
+ }
3087
+ seen.add(variation);
3088
+ results.push(variation);
3089
+ }
3090
+ return results;
3091
+ }
3092
+ addZeroWidth(text) {
3093
+ const words = text.split(' ');
3094
+ if (words.length < 2)
3095
+ return text;
3096
+ const positions = this.randomPositions(words.length - 1, Math.min(2, words.length - 1));
3097
+ return words
3098
+ .map((word, i) => {
3099
+ if (positions.includes(i)) {
3100
+ const zwc = ZERO_WIDTH[Math.floor(Math.random() * ZERO_WIDTH.length)];
3101
+ return word + zwc;
3102
+ }
3103
+ return word;
3104
+ })
3105
+ .join(' ');
3106
+ }
3107
+ varyPunctuation(text) {
3108
+ const variations = [
3109
+ () => text + ' ',
3110
+ () => text + ' ',
3111
+ () => (text.endsWith('.') ? text.slice(0, -1) : text + '.'),
3112
+ () => text,
3113
+ () => (text.charAt(0) === text.charAt(0).toUpperCase() ? text.charAt(0).toLowerCase() + text.slice(1) : text)
3114
+ ];
3115
+ return variations[this.counter % variations.length]();
3116
+ }
3117
+ addEmojiPadding(text) {
3118
+ const emojis = ['', ' \u{1F44D}', ' \u2705', ' \u{1F4CC}', ' \u{1F4AC}', ' \u{1F4E2}'];
3119
+ return text + emojis[this.counter % emojis.length];
3120
+ }
3121
+ applySynonyms(text) {
3122
+ const words = text.split(/\b/);
3123
+ let replaced = false;
3124
+ return words
3125
+ .map(word => {
3126
+ if (replaced)
3127
+ return word;
3128
+ const lower = word.toLowerCase();
3129
+ const synonymList = SYNONYMS[lower];
3130
+ if (synonymList && Math.random() > 0.5) {
3131
+ replaced = true;
3132
+ const synonym = synonymList[Math.floor(Math.random() * synonymList.length)];
3133
+ return word[0] === word[0].toUpperCase() ? synonym.charAt(0).toUpperCase() + synonym.slice(1) : synonym;
3134
+ }
3135
+ return word;
3136
+ })
3137
+ .join('');
3138
+ }
3139
+ randomPositions(max, count) {
3140
+ const positions = [];
3141
+ while (positions.length < count) {
3142
+ const pos = Math.floor(Math.random() * max);
3143
+ if (!positions.includes(pos))
3144
+ positions.push(pos);
3145
+ }
3146
+ return positions;
3147
+ }
3148
+ };
3149
+ var DEFAULT_CONFIG14 = {
3150
+ urls: [],
3151
+ minRiskLevel: 'medium',
3152
+ cooldownMs: 3e5,
3153
+ includeStats: true
3154
+ };
3155
+ var WebhookAlerts = class {
3156
+ config;
3157
+ lastAlertTime = 0;
3158
+ constructor(config = {}) {
3159
+ this.config = { ...DEFAULT_CONFIG14, ...config };
3160
+ }
3161
+ async alert(data) {
3162
+ const riskOrder = ['low', 'medium', 'high', 'critical'];
3163
+ if (riskOrder.indexOf(data.risk) < riskOrder.indexOf(this.config.minRiskLevel)) {
3164
+ return;
3165
+ }
3166
+ const now = Date.now();
3167
+ if (now - this.lastAlertTime < this.config.cooldownMs) {
3168
+ return;
3169
+ }
3170
+ this.lastAlertTime = now;
3171
+ const payload = {
3172
+ source: 'baileys-antiban',
3173
+ timestamp: new Date().toISOString(),
3174
+ ...data
3175
+ };
3176
+ for (const url of this.config.urls) {
3177
+ this.postWebhook(url, payload).catch(() => { });
3178
+ }
3179
+ if (this.config.telegram) {
3180
+ const emoji = { low: '\u{1F7E2}', medium: '\u{1F7E1}', high: '\u{1F7E0}', critical: '\u{1F534}' }[data.risk] || '\u26AA';
3181
+ const text = `${emoji} *baileys-antiban Alert*
3182
+
3183
+ Risk: *${data.risk.toUpperCase()}* (score: ${data.score})
3184
+ ${data.recommendation}
3185
+
3186
+ Reasons:
3187
+ ${data.reasons.map(r => `\u2022 ${r}`).join('\n')}`;
3188
+ this.postWebhook(`https://api.telegram.org/bot${this.config.telegram.botToken}/sendMessage`, {
3189
+ chat_id: this.config.telegram.chatId,
3190
+ text,
3191
+ parse_mode: 'Markdown'
3192
+ }).catch(() => { });
3193
+ }
3194
+ if (this.config.discord) {
3195
+ const color = { low: 65280, medium: 16776960, high: 16746496, critical: 16711680 }[data.risk] || 0;
3196
+ this.postWebhook(this.config.discord.webhookUrl, {
3197
+ embeds: [
3198
+ {
3199
+ title: '\u{1F6E1}\uFE0F baileys-antiban Alert',
3200
+ color,
3201
+ fields: [
3202
+ { name: 'Risk', value: data.risk.toUpperCase(), inline: true },
3203
+ { name: 'Score', value: String(data.score), inline: true },
3204
+ { name: 'Recommendation', value: data.recommendation },
3205
+ { name: 'Reasons', value: data.reasons.join('\n') }
3206
+ ],
3207
+ timestamp: new Date().toISOString()
3208
+ }
3209
+ ]
3210
+ }).catch(() => { });
3211
+ }
3212
+ }
3213
+ async postWebhook(url, payload) {
3214
+ try {
3215
+ const response = await fetch(url, {
3216
+ method: 'POST',
3217
+ headers: {
3218
+ 'Content-Type': 'application/json',
3219
+ ...this.config.headers
3220
+ },
3221
+ body: JSON.stringify(payload)
3222
+ });
3223
+ if (!response.ok) {
3224
+ process.stderr.write(`[baileys-antiban] Webhook failed: ${response.status}\n`);
3225
+ }
3226
+ }
3227
+ catch (err) {
3228
+ process.stderr.write(`[baileys-antiban] Webhook error: ${err}\n`);
3229
+ }
3230
+ }
3231
+ };
3232
+ var DEFAULT_CONFIG15 = {
3233
+ timezone: 'UTC',
3234
+ activeHours: [8, 21],
3235
+ weekendFactor: 0.5,
3236
+ peakHours: [10, 14],
3237
+ peakFactor: 1.3,
3238
+ lunchBreak: [12, 13],
3239
+ lunchFactor: 0.5
3240
+ };
3241
+ var Scheduler = class {
3242
+ config;
3243
+ constructor(config = {}) {
3244
+ this.config = { ...DEFAULT_CONFIG15, ...config };
3245
+ }
3246
+ isActiveTime() {
3247
+ const hour = this.getCurrentHour();
3248
+ const [start, end] = this.config.activeHours;
3249
+ return hour >= start && hour < end;
3250
+ }
3251
+ getSpeedFactor() {
3252
+ if (!this.isActiveTime())
3253
+ return 0;
3254
+ const hour = this.getCurrentHour();
3255
+ const day = this.getCurrentDay();
3256
+ let factor = 1;
3257
+ if (day === 0 || day === 6) {
3258
+ factor *= this.config.weekendFactor;
3259
+ }
3260
+ const [peakStart, peakEnd] = this.config.peakHours;
3261
+ if (hour >= peakStart && hour < peakEnd) {
3262
+ factor *= this.config.peakFactor;
3263
+ }
3264
+ const [lunchStart, lunchEnd] = this.config.lunchBreak;
3265
+ if (hour >= lunchStart && hour < lunchEnd) {
3266
+ factor *= this.config.lunchFactor;
3267
+ }
3268
+ return factor;
3269
+ }
3270
+ msUntilActive() {
3271
+ if (this.isActiveTime())
3272
+ return 0;
3273
+ const now = new Date();
3274
+ const hour = now.getHours();
3275
+ const [start] = this.config.activeHours;
3276
+ let nextActive;
3277
+ if (hour >= this.config.activeHours[1]) {
3278
+ nextActive = new Date(now);
3279
+ nextActive.setDate(nextActive.getDate() + 1);
3280
+ nextActive.setHours(start, 0, 0, 0);
3281
+ }
3282
+ else {
3283
+ nextActive = new Date(now);
3284
+ nextActive.setHours(start, 0, 0, 0);
3285
+ }
3286
+ return nextActive.getTime() - now.getTime();
3287
+ }
3288
+ adjustDelay(baseDelayMs) {
3289
+ const factor = this.getSpeedFactor();
3290
+ if (factor === 0)
3291
+ return -1;
3292
+ return Math.round(baseDelayMs / factor);
3293
+ }
3294
+ getStatus() {
3295
+ const hour = this.getCurrentHour();
3296
+ const day = this.getCurrentDay();
3297
+ const dayNames = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
3298
+ return {
3299
+ active: this.isActiveTime(),
3300
+ currentHour: hour,
3301
+ day: dayNames[day],
3302
+ isWeekend: day === 0 || day === 6,
3303
+ speedFactor: this.getSpeedFactor(),
3304
+ msUntilActive: this.msUntilActive(),
3305
+ activeWindow: `${this.config.activeHours[0]}:00 - ${this.config.activeHours[1]}:00`
3306
+ };
3307
+ }
3308
+ getCurrentHour() {
3309
+ return new Date().getHours();
3310
+ }
3311
+ getCurrentDay() {
3312
+ return new Date().getDay();
3313
+ }
3314
+ };
3315
+ var _fsPromises = require('fs/promises');
3316
+ var _fsSync = require('fs');
3317
+ var _path3 = require('path');
3318
+ var FileStateAdapter = class {
3319
+ basePath;
3320
+ constructor(basePath) {
3321
+ this.basePath = basePath;
3322
+ }
3323
+ async save(key, state) {
3324
+ const filePath = _path3.join(this.basePath, `${key}.json`);
3325
+ await _fsPromises.mkdir(this.basePath, { recursive: true });
3326
+ await _fsPromises.writeFile(filePath, JSON.stringify(state, null, 2), 'utf-8');
3327
+ }
3328
+ async load(key) {
3329
+ const filePath = _path3.join(this.basePath, `${key}.json`);
3330
+ try {
3331
+ const data = await _fsPromises.readFile(filePath, 'utf-8');
3332
+ return JSON.parse(data);
3333
+ }
3334
+ catch (err) {
3335
+ if (err.code === 'ENOENT')
3336
+ return null;
3337
+ throw err;
3338
+ }
3339
+ }
3340
+ async delete(key) {
3341
+ const filePath = _path3.join(this.basePath, `${key}.json`);
3342
+ try {
3343
+ await _fsPromises.unlink(filePath);
3344
+ }
3345
+ catch (err) {
3346
+ if (err.code !== 'ENOENT')
3347
+ throw err;
3348
+ }
3349
+ }
3350
+ async list() {
3351
+ try {
3352
+ const files = await _fsPromises.readdir(this.basePath);
3353
+ return files.filter(f => f.endsWith('.json')).map(f => f.replace(/\.json$/, ''));
3354
+ }
3355
+ catch (err) {
3356
+ if (err.code === 'ENOENT')
3357
+ return [];
3358
+ throw err;
3359
+ }
3360
+ }
3361
+ };
3362
+ var DEFAULT_CONFIG16 = {
3363
+ maxTrackedChats: 1e3,
3364
+ maxGapMs: 30 * 6e4,
3365
+ persistDebounceMs: 2e3,
3366
+ onGapFilled: () => { },
3367
+ logger: {
3368
+ info: () => { },
3369
+ warn: () => { },
3370
+ error: () => { }
3371
+ }
3372
+ };
3373
+ function messageRecovery(sock, config) {
3374
+ const cfg = { ...DEFAULT_CONFIG16, ...config };
3375
+ const logger = cfg.logger;
3376
+ const lastSeen = new Map();
3377
+ let disconnectedAt = null;
3378
+ let totalRecovered = 0;
3379
+ let lastReconnectAt = null;
3380
+ let lastGapMs = null;
3381
+ let persistTimer = null;
3382
+ let loggedFetchWarning = false;
3383
+ if (cfg.persistPath) {
3384
+ loadPersistence();
3385
+ }
3386
+ const messagesListener = sock.ev.process ? setupProcessListener() : setupLegacyListener();
3387
+ const connectionListener = update => {
3388
+ if (update.connection === 'close') {
3389
+ disconnectedAt = Date.now();
3390
+ logger.info?.(`[messageRecovery] Disconnected at ${new Date(disconnectedAt).toISOString()}`);
3391
+ }
3392
+ if (update.connection === 'open' && disconnectedAt !== null) {
3393
+ void recoverMessages();
3394
+ }
3395
+ };
3396
+ sock.ev.on('connection.update', connectionListener);
3397
+ function setupProcessListener() {
3398
+ const listener = async (events) => {
3399
+ if (events['messages.upsert']) {
3400
+ const { messages, type } = events['messages.upsert'];
3401
+ if (type === 'notify') {
3402
+ for (const msg of messages || []) {
3403
+ trackMessage(msg);
3404
+ }
3405
+ }
3406
+ }
3407
+ };
3408
+ sock.ev.process(listener);
3409
+ return listener;
3410
+ }
3411
+ function setupLegacyListener() {
3412
+ const listener = upsert => {
3413
+ const { messages, type } = upsert;
3414
+ if (type === 'notify') {
3415
+ for (const msg of messages || []) {
3416
+ trackMessage(msg);
3417
+ }
3418
+ }
3419
+ };
3420
+ sock.ev.on('messages.upsert', listener);
3421
+ return listener;
3422
+ }
3423
+ function trackMessage(msg) {
3424
+ const jid = msg.key?.remoteJid;
3425
+ const messageId = msg.key?.id;
3426
+ const timestamp = msg.messageTimestamp;
3427
+ if (!jid || !messageId || !timestamp)
3428
+ return;
3429
+ if (msg.key?.fromMe)
3430
+ return;
3431
+ const now = Date.now();
3432
+ lastSeen.set(jid, {
3433
+ messageId,
3434
+ timestamp: typeof timestamp === 'number' ? timestamp : parseInt(timestamp, 10),
3435
+ lastTouchedAt: now
3436
+ });
3437
+ if (lastSeen.size > cfg.maxTrackedChats) {
3438
+ evictOldest();
3439
+ }
3440
+ schedulePersist();
3441
+ }
3442
+ function evictOldest() {
3443
+ let oldestJid = null;
3444
+ let oldestTime = Infinity;
3445
+ for (const [jid, entry] of lastSeen) {
3446
+ if (entry.lastTouchedAt < oldestTime) {
3447
+ oldestTime = entry.lastTouchedAt;
3448
+ oldestJid = jid;
3449
+ }
3450
+ }
3451
+ if (oldestJid) {
3452
+ lastSeen.delete(oldestJid);
3453
+ }
3454
+ }
3455
+ async function recoverMessages() {
3456
+ const recoveryStartMs = Date.now();
3457
+ const gapMs = recoveryStartMs - disconnectedAt;
3458
+ logger.info?.(`[messageRecovery] Reconnected after ${(gapMs / 1e3).toFixed(1)}s`);
3459
+ if (gapMs > cfg.maxGapMs) {
3460
+ logger.warn?.(`[messageRecovery] Gap too large (${(gapMs / 1e3).toFixed(0)}s > ${(cfg.maxGapMs / 1e3).toFixed(0)}s) \u2014 skipping recovery`);
3461
+ disconnectedAt = null;
3462
+ lastGapMs = gapMs;
3463
+ await cfg.onGapTooLarge?.(gapMs);
3464
+ return;
3465
+ }
3466
+ let recovered = 0;
3467
+ const chatsToRecover = Array.from(lastSeen.entries());
3468
+ if (typeof sock.fetchMessageHistory !== 'function') {
3469
+ if (!loggedFetchWarning) {
3470
+ logger.warn?.(`[messageRecovery] sock.fetchMessageHistory not available \u2014 recovery disabled. Baileys version may not support history fetch. User must implement manual reconciliation.`);
3471
+ loggedFetchWarning = true;
3472
+ }
3473
+ disconnectedAt = null;
3474
+ lastReconnectAt = new Date();
3475
+ lastGapMs = gapMs;
3476
+ await cfg.onRecoveryComplete?.({
3477
+ chats: 0,
3478
+ recovered: 0,
3479
+ durationMs: Date.now() - recoveryStartMs
3480
+ });
3481
+ return;
3482
+ }
3483
+ for (const [jid, lastSeenEntry] of chatsToRecover) {
3484
+ try {
3485
+ const messages = await sock.fetchMessageHistory(jid, 50, {
3486
+ before: void 0
3487
+ });
3488
+ if (!messages || !Array.isArray(messages))
3489
+ continue;
3490
+ const gapMessages = messages.filter(msg => {
3491
+ const ts = msg.messageTimestamp;
3492
+ if (!ts)
3493
+ return false;
3494
+ const msgTs = typeof ts === 'number' ? ts : parseInt(ts, 10);
3495
+ return msgTs > lastSeenEntry.timestamp;
3496
+ });
3497
+ gapMessages.sort((a, b) => {
3498
+ const aTs = typeof a.messageTimestamp === 'number' ? a.messageTimestamp : parseInt(a.messageTimestamp, 10);
3499
+ const bTs = typeof b.messageTimestamp === 'number' ? b.messageTimestamp : parseInt(b.messageTimestamp, 10);
3500
+ return aTs - bTs;
3501
+ });
3502
+ for (const msg of gapMessages) {
3503
+ await cfg.onGapFilled(msg, jid);
3504
+ recovered++;
3505
+ const msgTs = typeof msg.messageTimestamp === 'number' ? msg.messageTimestamp : parseInt(msg.messageTimestamp, 10);
3506
+ if (msgTs > lastSeenEntry.timestamp) {
3507
+ lastSeenEntry.timestamp = msgTs;
3508
+ lastSeenEntry.messageId = msg.key?.id || lastSeenEntry.messageId;
3509
+ lastSeenEntry.lastTouchedAt = Date.now();
3510
+ }
3511
+ }
3512
+ if (gapMessages.length > 0) {
3513
+ logger.info?.(`[messageRecovery] Recovered ${gapMessages.length} messages from ${jid}`);
3514
+ }
3515
+ }
3516
+ catch (err) {
3517
+ logger.error?.(`[messageRecovery] Failed to recover from ${jid}: ${err.message}`);
3518
+ }
3519
+ }
3520
+ totalRecovered += recovered;
3521
+ lastReconnectAt = new Date();
3522
+ lastGapMs = gapMs;
3523
+ disconnectedAt = null;
3524
+ logger.info?.(`[messageRecovery] Recovery complete: ${recovered} messages across ${chatsToRecover.length} chats in ${Date.now() - recoveryStartMs}ms`);
3525
+ await cfg.onRecoveryComplete?.({
3526
+ chats: chatsToRecover.length,
3527
+ recovered,
3528
+ durationMs: Date.now() - recoveryStartMs
3529
+ });
3530
+ }
3531
+ function schedulePersist() {
3532
+ if (!cfg.persistPath)
3533
+ return;
3534
+ if (persistTimer) {
3535
+ clearTimeout(persistTimer);
3536
+ }
3537
+ persistTimer = setTimeout(() => {
3538
+ void flushPersistence();
3539
+ }, cfg.persistDebounceMs);
3540
+ }
3541
+ async function flushPersistence() {
3542
+ if (!cfg.persistPath)
3543
+ return;
3544
+ try {
3545
+ const data = {};
3546
+ for (const [jid, entry] of lastSeen) {
3547
+ data[jid] = {
3548
+ id: entry.messageId,
3549
+ timestamp: entry.timestamp
3550
+ };
3551
+ }
3552
+ await _fsPromises.writeFile(cfg.persistPath, JSON.stringify(data, null, 2), 'utf-8');
3553
+ }
3554
+ catch (err) {
3555
+ logger.error?.(`[messageRecovery] Failed to persist state: ${err.message}`);
3556
+ }
3557
+ }
3558
+ function loadPersistence() {
3559
+ if (!cfg.persistPath)
3560
+ return;
3561
+ try {
3562
+ const fs4 = require('fs');
3563
+ if (!fs4.existsSync(cfg.persistPath))
3564
+ return;
3565
+ const raw = fs4.readFileSync(cfg.persistPath, 'utf-8');
3566
+ const data = JSON.parse(raw);
3567
+ for (const [jid, entry] of Object.entries(data)) {
3568
+ lastSeen.set(jid, {
3569
+ messageId: entry.id,
3570
+ timestamp: entry.timestamp,
3571
+ lastTouchedAt: Date.now()
3572
+ });
3573
+ }
3574
+ logger.info?.(`[messageRecovery] Loaded ${lastSeen.size} entries from ${cfg.persistPath}`);
3575
+ }
3576
+ catch (err) {
3577
+ logger.warn?.(`[messageRecovery] Failed to load persisted state: ${err.message}`);
3578
+ }
3579
+ }
3580
+ return {
3581
+ async stop() {
3582
+ sock.ev.off('connection.update', connectionListener);
3583
+ if (!sock.ev.process) {
3584
+ sock.ev.off('messages.upsert', messagesListener);
3585
+ }
3586
+ if (persistTimer) {
3587
+ clearTimeout(persistTimer);
3588
+ persistTimer = null;
3589
+ }
3590
+ await flushPersistence();
3591
+ logger.info?.(`[messageRecovery] Stopped \u2014 total recovered: ${totalRecovered}`);
3592
+ },
3593
+ markSeen(chatJid, messageId, timestamp) {
3594
+ lastSeen.set(chatJid, {
3595
+ messageId,
3596
+ timestamp,
3597
+ lastTouchedAt: Date.now()
3598
+ });
3599
+ schedulePersist();
3600
+ },
3601
+ getStats() {
3602
+ return {
3603
+ trackedChats: lastSeen.size,
3604
+ totalRecovered,
3605
+ lastReconnectAt,
3606
+ lastGapMs
3607
+ };
3608
+ }
3609
+ };
3610
+ }
3611
+ var DEFAULT_APP_VERSION_POOL = [
3612
+ [2, 24, 5, 18],
3613
+ [2, 24, 5, 17],
3614
+ [2, 24, 4, 77],
3615
+ [2, 24, 5, 15],
3616
+ [2, 24, 3, 91],
3617
+ [2, 24, 5, 20]
3618
+ ];
3619
+ var DEFAULT_OS_VERSION_POOL = ['10', '11', '12', '13', '14'];
3620
+ var DEFAULT_DEVICE_MODEL_POOL = [
3621
+ 'Pixel 6',
3622
+ 'Pixel 7',
3623
+ 'Galaxy S22',
3624
+ 'Galaxy S23',
3625
+ 'Xiaomi 13',
3626
+ 'Xiaomi 12',
3627
+ 'OnePlus 11',
3628
+ 'Moto G84',
3629
+ 'Moto G54',
3630
+ 'Realme 11',
3631
+ 'Vivo V29',
3632
+ 'Oppo Find X6'
3633
+ ];
3634
+ var SeededRandom = class {
3635
+ state;
3636
+ constructor(seed) {
3637
+ let hash = 0;
3638
+ for (let i = 0; i < seed.length; i++) {
3639
+ hash = (hash << 5) - hash + seed.charCodeAt(i);
3640
+ hash = hash & hash;
3641
+ }
3642
+ this.state = Math.abs(hash) || 1;
3643
+ }
3644
+ next() {
3645
+ let t = (this.state += 1831565813);
3646
+ t = Math.imul(t ^ (t >>> 15), t | 1);
3647
+ t ^= t + Math.imul(t ^ (t >>> 7), t | 61);
3648
+ return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
3649
+ }
3650
+ pick(array) {
3651
+ return array[Math.floor(this.next() * array.length)];
3652
+ }
3653
+ };
3654
+ function generateFingerprint(config = {}, sessionId) {
3655
+ const { enabled = true, randomizeAppVersion = true, randomizeOsVersion = true, randomizeDeviceModel = true, seed, appVersionPool = DEFAULT_APP_VERSION_POOL, osVersionPool = DEFAULT_OS_VERSION_POOL, deviceModelPool = DEFAULT_DEVICE_MODEL_POOL } = config;
3656
+ const finalSessionId = sessionId || `session-${Date.now()}-${Math.random()}`;
3657
+ const rng = new SeededRandom(seed || finalSessionId);
3658
+ const appVersion = enabled && randomizeAppVersion ? rng.pick(appVersionPool) : appVersionPool[0];
3659
+ const osVersion = enabled && randomizeOsVersion ? rng.pick(osVersionPool) : osVersionPool[0];
3660
+ const deviceModel = enabled && randomizeDeviceModel ? rng.pick(deviceModelPool) : deviceModelPool[0];
3661
+ return {
3662
+ appVersion: [...appVersion],
3663
+ osVersion,
3664
+ deviceModel,
3665
+ sessionId: finalSessionId
3666
+ };
3667
+ }
3668
+ function applyFingerprint(socketConfig, fp) {
3669
+ const config = { ...socketConfig };
3670
+ if (config.version !== void 0 || 'version' in config || true) {
3671
+ config.version = fp.appVersion;
3672
+ }
3673
+ if (config.browser !== void 0 || 'browser' in config || true) {
3674
+ config.browser = [fp.deviceModel, fp.osVersion, `WhatsApp/${fp.appVersion.join('.')}`];
3675
+ }
3676
+ return config;
3677
+ }
3678
+ var import_fs = require('fs');
3679
+ var path2 = __toESM(require('path'), 1);
3680
+ var noop = () => { };
3681
+ function credsSnapshot(config) {
3682
+ const { credsPath, snapshotDir = path2.join(path2.dirname(credsPath), '.snapshots'), keep = 3, logger = {} } = config;
3683
+ const log = {
3684
+ info: logger.info || noop,
3685
+ warn: logger.warn || noop,
3686
+ error: logger.error || noop
3687
+ };
3688
+ async function take() {
3689
+ try {
3690
+ try {
3691
+ await import_fs.promises.access(credsPath);
3692
+ }
3693
+ catch {
3694
+ log.warn(`[credsSnapshot] Creds file not found: ${credsPath}`);
3695
+ return null;
3696
+ }
3697
+ await import_fs.promises.mkdir(snapshotDir, { recursive: true });
3698
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
3699
+ const snapshotPath = path2.join(snapshotDir, `creds-${timestamp}.json`);
3700
+ const tmpPath = `${snapshotPath}.tmp`;
3701
+ await import_fs.promises.copyFile(credsPath, tmpPath);
3702
+ await import_fs.promises.rename(tmpPath, snapshotPath);
3703
+ log.info(`[credsSnapshot] Snapshot taken: ${snapshotPath}`);
3704
+ await rotate();
3705
+ return snapshotPath;
3706
+ }
3707
+ catch (err) {
3708
+ log.error(`[credsSnapshot] Failed to take snapshot: ${err}`);
3709
+ return null;
3710
+ }
3711
+ }
3712
+ async function rotate() {
3713
+ try {
3714
+ const snapshots = await list();
3715
+ const toDelete = snapshots.slice(keep);
3716
+ for (const snap of toDelete) {
3717
+ await import_fs.promises.unlink(snap.path);
3718
+ log.info(`[credsSnapshot] Rotated out: ${snap.path}`);
3719
+ }
3720
+ }
3721
+ catch (err) {
3722
+ log.error(`[credsSnapshot] Rotation failed: ${err}`);
3723
+ }
3724
+ }
3725
+ async function list() {
3726
+ try {
3727
+ await import_fs.promises.access(snapshotDir);
3728
+ }
3729
+ catch {
3730
+ return [];
3731
+ }
3732
+ try {
3733
+ const files = await import_fs.promises.readdir(snapshotDir);
3734
+ const snapshots = await Promise.all(files
3735
+ .filter(f => f.startsWith('creds-') && f.endsWith('.json'))
3736
+ .map(async (f) => {
3737
+ const fullPath = path2.join(snapshotDir, f);
3738
+ const stat = await import_fs.promises.stat(fullPath);
3739
+ return {
3740
+ path: fullPath,
3741
+ takenAt: stat.mtime,
3742
+ size: stat.size
3743
+ };
3744
+ }));
3745
+ return snapshots.sort((a, b) => b.takenAt.getTime() - a.takenAt.getTime());
3746
+ }
3747
+ catch (err) {
3748
+ log.error(`[credsSnapshot] Failed to list snapshots: ${err}`);
3749
+ return [];
3750
+ }
3751
+ }
3752
+ async function restoreLatest() {
3753
+ const snapshots = await list();
3754
+ if (snapshots.length === 0) {
3755
+ log.warn('[credsSnapshot] No snapshots available to restore');
3756
+ return false;
3757
+ }
3758
+ return restore(snapshots[0].path);
3759
+ }
3760
+ async function restore(snapshotPath) {
3761
+ try {
3762
+ await import_fs.promises.access(snapshotPath);
3763
+ const tmpPath = `${credsPath}.tmp`;
3764
+ await import_fs.promises.copyFile(snapshotPath, tmpPath);
3765
+ await import_fs.promises.rename(tmpPath, credsPath);
3766
+ log.info(`[credsSnapshot] Restored from: ${snapshotPath}`);
3767
+ return true;
3768
+ }
3769
+ catch (err) {
3770
+ log.error(`[credsSnapshot] Failed to restore from ${snapshotPath}: ${err}`);
3771
+ return false;
3772
+ }
3773
+ }
3774
+ return {
3775
+ take,
3776
+ restoreLatest,
3777
+ restore,
3778
+ list
3779
+ };
3780
+ }
3781
+ function gaussianRandom() {
3782
+ let u = 0;
3783
+ let v = 0;
3784
+ while (u === 0)
3785
+ u = Math.random();
3786
+ while (v === 0)
3787
+ v = Math.random();
3788
+ return Math.sqrt(-2 * Math.log(u)) * Math.cos(2 * Math.PI * v);
3789
+ }
3790
+ function readReceiptVariance(config = {}) {
3791
+ const { meanMs = 1500, stdDevMs = 800, minMs = 200, maxMs = 8e3, skipIfOlderThanMs = 6e4 } = config;
3792
+ const pendingTimers = new Set();
3793
+ function delayMs() {
3794
+ const gaussian = gaussianRandom();
3795
+ const value = meanMs + gaussian * stdDevMs;
3796
+ return Math.max(minMs, Math.min(maxMs, value));
3797
+ }
3798
+ function wrap(sock) {
3799
+ const originalReadMessages = sock.readMessages.bind(sock);
3800
+ const wrappedReadMessages = async (keys) => {
3801
+ const now = Date.now();
3802
+ const oldMessages = keys.every(key => {
3803
+ if (!key.messageTimestamp)
3804
+ return false;
3805
+ const msgTime = typeof key.messageTimestamp === 'number'
3806
+ ? key.messageTimestamp * 1e3
3807
+ : parseInt(key.messageTimestamp, 10) * 1e3;
3808
+ return now - msgTime > skipIfOlderThanMs;
3809
+ });
3810
+ if (oldMessages) {
3811
+ return originalReadMessages(keys);
3812
+ }
3813
+ const delay = delayMs();
3814
+ return new Promise((resolve, reject) => {
3815
+ const timer = setTimeout(async () => {
3816
+ pendingTimers.delete(timer);
3817
+ try {
3818
+ const result = await originalReadMessages(keys);
3819
+ resolve(result);
3820
+ }
3821
+ catch (err) {
3822
+ reject(err);
3823
+ }
3824
+ }, delay);
3825
+ pendingTimers.add(timer);
3826
+ });
3827
+ };
3828
+ return new Proxy(sock, {
3829
+ get(target, prop) {
3830
+ if (prop === 'readMessages') {
3831
+ return wrappedReadMessages;
3832
+ }
3833
+ return target[prop];
3834
+ }
3835
+ });
3836
+ }
3837
+ function stop() {
3838
+ for (const timer of pendingTimers) {
3839
+ clearTimeout(timer);
3840
+ }
3841
+ pendingTimers.clear();
3842
+ }
3843
+ return {
3844
+ wrap,
3845
+ delayMs,
3846
+ stop
3847
+ };
3848
+ }
3849
+ var import_node_module = require('node:module');
3850
+ var require2 = (0, import_node_module.createRequire)(__importMetaUrl);
3851
+ var NoopLogger = {
3852
+ info: () => { },
3853
+ warn: () => { },
3854
+ error: () => { }
3855
+ };
3856
+ function proxyRotator(config) {
3857
+ const { pool, strategy = 'round-robin', rotateOn = ['disconnect', 'ban-warning'], scheduledIntervalMs = 0, maxFailures = 3, deadCooldownMs = 6e5, logger = NoopLogger } = config;
3858
+ if (!pool || pool.length === 0) {
3859
+ throw new Error('proxyRotator: pool cannot be empty');
3860
+ }
3861
+ if (pool.length === 1) {
3862
+ logger.warn?.('proxyRotator: pool size is 1. Rotation is a no-op.');
3863
+ }
3864
+ if (scheduledIntervalMs > 0 && scheduledIntervalMs < 6e4) {
3865
+ logger.warn?.(`proxyRotator: scheduledIntervalMs (${scheduledIntervalMs}ms) is < 60s. May hammer proxy provider.`);
3866
+ }
3867
+ const states = pool.map(endpoint => ({
3868
+ endpoint,
3869
+ failures: 0,
3870
+ lastUsedAt: null,
3871
+ isDead: false
3872
+ }));
3873
+ let currentIndex = 0;
3874
+ let totalRotations = 0;
3875
+ const rotationsByTrigger = {};
3876
+ let scheduledTimer = null;
3877
+ const agentCache = new Map();
3878
+ const moduleCache = {};
3879
+ function buildProxyUrl(endpoint) {
3880
+ const { type, host, port, username, password } = endpoint;
3881
+ const auth = username && password ? `${username}:${password}@` : '';
3882
+ return `${type}://${auth}${host}:${port}`;
3883
+ }
3884
+ function createAgentForEndpointSync(endpoint) {
3885
+ if (agentCache.has(endpoint)) {
3886
+ return agentCache.get(endpoint);
3887
+ }
3888
+ const url = buildProxyUrl(endpoint);
3889
+ let agent = null;
3890
+ try {
3891
+ if (endpoint.type === 'socks5' || endpoint.type === 'socks5h') {
3892
+ if (!moduleCache['socks-proxy-agent']) {
3893
+ try {
3894
+ moduleCache['socks-proxy-agent'] = require2('socks-proxy-agent');
3895
+ }
3896
+ catch {
3897
+ logger.error?.('socks-proxy-agent not installed. Run: npm install socks-proxy-agent');
3898
+ return null;
3899
+ }
3900
+ }
3901
+ agent = new moduleCache['socks-proxy-agent'].SocksProxyAgent(url);
3902
+ }
3903
+ else if (endpoint.type === 'http') {
3904
+ if (!moduleCache['http-proxy-agent']) {
3905
+ try {
3906
+ moduleCache['http-proxy-agent'] = require2('http-proxy-agent');
3907
+ }
3908
+ catch {
3909
+ logger.error?.('http-proxy-agent not installed. Run: npm install http-proxy-agent');
3910
+ return null;
3911
+ }
3912
+ }
3913
+ agent = new moduleCache['http-proxy-agent'].HttpProxyAgent(url);
3914
+ }
3915
+ else if (endpoint.type === 'https') {
3916
+ if (!moduleCache['https-proxy-agent']) {
3917
+ try {
3918
+ moduleCache['https-proxy-agent'] = require2('https-proxy-agent');
3919
+ }
3920
+ catch {
3921
+ logger.error?.('https-proxy-agent not installed. Run: npm install https-proxy-agent');
3922
+ return null;
3923
+ }
3924
+ }
3925
+ agent = new moduleCache['https-proxy-agent'].HttpsProxyAgent(url);
3926
+ }
3927
+ else {
3928
+ logger.error?.(`Unknown proxy type: ${endpoint.type}`);
3929
+ return null;
3930
+ }
3931
+ if (agent) {
3932
+ agentCache.set(endpoint, agent);
3933
+ }
3934
+ return agent;
3935
+ }
3936
+ catch (err) {
3937
+ logger.error?.(`Failed to create agent for ${endpoint.label || endpoint.host}: ${err}`);
3938
+ return null;
3939
+ }
3940
+ }
3941
+ function getAliveEndpoints() {
3942
+ const now = Date.now();
3943
+ return states
3944
+ .map((s, idx) => {
3945
+ if (s.isDead && s.lastUsedAt) {
3946
+ if (now - s.lastUsedAt.getTime() >= deadCooldownMs) {
3947
+ s.isDead = false;
3948
+ s.failures = 0;
3949
+ logger.info?.(`Resurrected endpoint ${s.endpoint.label || s.endpoint.host} after cooldown`);
3950
+ }
3951
+ }
3952
+ const cooldown = s.endpoint.cooldownMs || 0;
3953
+ if (cooldown > 0 && s.lastUsedAt) {
3954
+ if (now - s.lastUsedAt.getTime() < cooldown) {
3955
+ return -1;
3956
+ }
3957
+ }
3958
+ return !s.isDead ? idx : -1;
3959
+ })
3960
+ .filter(idx => idx !== -1);
3961
+ }
3962
+ function selectNextIndex(alive) {
3963
+ if (alive.length === 0)
3964
+ return currentIndex;
3965
+ if (strategy === 'round-robin') {
3966
+ const afterCurrent = alive.filter(idx => idx > currentIndex);
3967
+ if (afterCurrent.length > 0)
3968
+ return afterCurrent[0];
3969
+ return alive[0];
3970
+ }
3971
+ if (strategy === 'random') {
3972
+ return alive[Math.floor(Math.random() * alive.length)];
3973
+ }
3974
+ if (strategy === 'least-recently-used') {
3975
+ const neverUsed = alive.filter(idx => states[idx].lastUsedAt === null);
3976
+ if (neverUsed.length > 0) {
3977
+ return neverUsed[0];
3978
+ }
3979
+ let oldestIdx = alive[0];
3980
+ let oldestTime = states[oldestIdx].lastUsedAt.getTime();
3981
+ for (const idx of alive) {
3982
+ const time = states[idx].lastUsedAt.getTime();
3983
+ if (time < oldestTime) {
3984
+ oldestTime = time;
3985
+ oldestIdx = idx;
3986
+ }
3987
+ }
3988
+ return oldestIdx;
3989
+ }
3990
+ if (strategy === 'weighted') {
3991
+ const weights = alive.map(idx => {
3992
+ const failures = states[idx].failures;
3993
+ return 1 / (failures + 1);
3994
+ });
3995
+ const totalWeight = weights.reduce((a, b) => a + b, 0);
3996
+ let rand = Math.random() * totalWeight;
3997
+ for (let i = 0; i < alive.length; i++) {
3998
+ rand -= weights[i];
3999
+ if (rand <= 0)
4000
+ return alive[i];
4001
+ }
4002
+ return alive[alive.length - 1];
4003
+ }
4004
+ return alive[0];
4005
+ }
4006
+ function rotateImpl(reason = 'manual') {
4007
+ if (pool.length === 1) {
4008
+ return states[0].endpoint;
4009
+ }
4010
+ const alive = getAliveEndpoints();
4011
+ if (alive.length === 0) {
4012
+ logger.warn?.('All endpoints are dead. Cannot rotate.');
4013
+ return states[currentIndex].endpoint;
4014
+ }
4015
+ const nextIdx = selectNextIndex(alive);
4016
+ if (nextIdx === currentIndex && alive.length > 1) {
4017
+ const others = alive.filter(idx => idx !== currentIndex);
4018
+ if (others.length > 0) {
4019
+ currentIndex = others[0];
4020
+ }
4021
+ else {
4022
+ currentIndex = nextIdx;
4023
+ }
4024
+ }
4025
+ else {
4026
+ currentIndex = nextIdx;
4027
+ }
4028
+ states[currentIndex].lastUsedAt = new Date();
4029
+ totalRotations++;
4030
+ rotationsByTrigger[reason] = (rotationsByTrigger[reason] || 0) + 1;
4031
+ const label = states[currentIndex].endpoint.label || states[currentIndex].endpoint.host;
4032
+ logger.info?.(`Rotated to endpoint ${label} (reason: ${reason})`);
4033
+ return states[currentIndex].endpoint;
4034
+ }
4035
+ function markFailureImpl() {
4036
+ const state = states[currentIndex];
4037
+ state.failures++;
4038
+ const label = state.endpoint.label || state.endpoint.host;
4039
+ logger.warn?.(`Endpoint ${label} failed (${state.failures}/${maxFailures})`);
4040
+ if (state.failures >= maxFailures) {
4041
+ state.isDead = true;
4042
+ logger.error?.(`Endpoint ${label} marked DEAD after ${maxFailures} failures`);
4043
+ const alive = getAliveEndpoints();
4044
+ if (alive.length > 0) {
4045
+ rotateImpl('manual');
4046
+ }
4047
+ }
4048
+ }
4049
+ function resurrectAllImpl() {
4050
+ let count = 0;
4051
+ for (const state of states) {
4052
+ if (state.isDead) {
4053
+ state.isDead = false;
4054
+ state.failures = 0;
4055
+ count++;
4056
+ }
4057
+ }
4058
+ if (count > 0) {
4059
+ logger.info?.(`Resurrected ${count} dead endpoint(s)`);
4060
+ }
4061
+ }
4062
+ function stopImpl() {
4063
+ if (scheduledTimer) {
4064
+ clearInterval(scheduledTimer);
4065
+ scheduledTimer = null;
4066
+ logger.info?.('Stopped scheduled rotation timer');
4067
+ }
4068
+ }
4069
+ function getStatsImpl() {
4070
+ return {
4071
+ totalRotations,
4072
+ rotationsByTrigger: { ...rotationsByTrigger },
4073
+ endpointHealth: states.map(s => ({
4074
+ label: s.endpoint.label || s.endpoint.host,
4075
+ inUse: states[currentIndex] === s,
4076
+ failures: s.failures,
4077
+ lastUsedAt: s.lastUsedAt,
4078
+ isDead: s.isDead
4079
+ })),
4080
+ currentEndpoint: states[currentIndex].endpoint.label || states[currentIndex].endpoint.host
4081
+ };
4082
+ }
4083
+ function currentAgentImpl() {
4084
+ const endpoint = states[currentIndex].endpoint;
4085
+ return createAgentForEndpointSync(endpoint);
4086
+ }
4087
+ function currentImpl() {
4088
+ return states[currentIndex].endpoint;
4089
+ }
4090
+ if (rotateOn.includes('scheduled') && scheduledIntervalMs > 0) {
4091
+ scheduledTimer = setInterval(() => {
4092
+ rotateImpl('scheduled');
4093
+ }, scheduledIntervalMs);
4094
+ logger.info?.(`Scheduled rotation enabled (every ${scheduledIntervalMs}ms)`);
4095
+ }
4096
+ states[0].lastUsedAt = new Date();
4097
+ return {
4098
+ currentAgent: currentAgentImpl,
4099
+ current: currentImpl,
4100
+ rotate: rotateImpl,
4101
+ markFailure: markFailureImpl,
4102
+ resurrectAll: resurrectAllImpl,
4103
+ stop: stopImpl,
4104
+ getStats: getStatsImpl
4105
+ };
4106
+ }
4107
+ 0 &&
4108
+ (module.exports = {
4109
+ AntiBan,
4110
+ ContactGraphWarmer,
4111
+ ContentVariator,
4112
+ FileStateAdapter,
4113
+ HealthMonitor,
4114
+ JidCanonicalizer,
4115
+ LidFirstResolver,
4116
+ LidResolver,
4117
+ MAC_ERROR_CODES,
4118
+ MessageQueue,
4119
+ MessageRetryReason,
4120
+ PRESETS,
4121
+ PostReconnectThrottle,
4122
+ PresenceChoreographer,
4123
+ RateLimiter,
4124
+ ReplyRatioGuard,
4125
+ RetryReasonTracker,
4126
+ Scheduler,
4127
+ SessionHealthMonitor,
4128
+ StateManager,
4129
+ TimelockGuard,
4130
+ WarmUp,
4131
+ WebhookAlerts,
4132
+ applyFingerprint,
4133
+ applyGroupMultiplier,
4134
+ classifyDisconnect,
4135
+ createLidFirstResolver,
4136
+ credsSnapshot,
4137
+ generateFingerprint,
4138
+ getCircadianMultiplier,
4139
+ getRetryReasonDescription,
4140
+ isBroadcast,
4141
+ isGroup,
4142
+ isMacError,
4143
+ isNewsletter,
4144
+ messageRecovery,
4145
+ parseRetryReason,
4146
+ proxyRotator,
4147
+ readReceiptVariance,
4148
+ resolveConfig,
4149
+ shouldUseGroupProfile,
4150
+ wrapSocket,
4151
+ wrapWithSessionStability
4152
+ });