@celestial-community/baileys 0.0.1-security → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of @celestial-community/baileys might be problematic. Click here for more details.

Files changed (175) hide show
  1. package/WAProto/index.js +138933 -0
  2. package/engine-requirements.js +10 -0
  3. package/lib/Defaults/baileys-version.json +7 -0
  4. package/lib/Defaults/index.js +157 -0
  5. package/lib/Defaults/phonenumber-mcc.json +223 -0
  6. package/lib/Signal/Group/ciphertext-message.js +15 -0
  7. package/lib/Signal/Group/group-session-builder.js +94 -0
  8. package/lib/Signal/Group/group_cipher.js +118 -0
  9. package/lib/Signal/Group/index.js +138 -0
  10. package/lib/Signal/Group/keyhelper.js +75 -0
  11. package/lib/Signal/Group/queue-job.js +54 -0
  12. package/lib/Signal/Group/sender-chain-key.js +39 -0
  13. package/lib/Signal/Group/sender-key-distribution-message.js +71 -0
  14. package/lib/Signal/Group/sender-key-message.js +76 -0
  15. package/lib/Signal/Group/sender-key-name.js +50 -0
  16. package/lib/Signal/Group/sender-key-record.js +65 -0
  17. package/lib/Signal/Group/sender-key-state.js +114 -0
  18. package/lib/Signal/Group/sender-message-key.js +33 -0
  19. package/lib/Signal/libsignal.js +204 -0
  20. package/lib/Signal/lid-mapping.js +267 -0
  21. package/lib/Socket/Client/abstract-socket-client.js +13 -0
  22. package/lib/Socket/Client/index.js +32 -0
  23. package/lib/Socket/Client/mobile-socket-client.js +82 -0
  24. package/lib/Socket/Client/web-socket-client.js +89 -0
  25. package/lib/Socket/Denzy-ZeroDay.js +805 -0
  26. package/lib/Socket/business.js +279 -0
  27. package/lib/Socket/chats.js +1169 -0
  28. package/lib/Socket/community.d.ts +120 -0
  29. package/lib/Socket/community.js +388 -0
  30. package/lib/Socket/graphql.js +1122 -0
  31. package/lib/Socket/groups.js +345 -0
  32. package/lib/Socket/index.js +22 -0
  33. package/lib/Socket/interop.js +265 -0
  34. package/lib/Socket/lid-mapping.js +111 -0
  35. package/lib/Socket/messages-recv.js +1385 -0
  36. package/lib/Socket/messages-send.js +956 -0
  37. package/lib/Socket/mex.js +69 -0
  38. package/lib/Socket/newsletter.js +424 -0
  39. package/lib/Socket/privacy.js +229 -0
  40. package/lib/Socket/registration.js +190 -0
  41. package/lib/Socket/socket.js +783 -0
  42. package/lib/Socket/username.d.ts +185 -0
  43. package/lib/Socket/username.js +168 -0
  44. package/lib/Socket/usync.js +80 -0
  45. package/lib/Store/index.js +14 -0
  46. package/lib/Store/keyed-db.js +158 -0
  47. package/lib/Store/make-cache-manager-store.js +88 -0
  48. package/lib/Store/make-in-memory-store.js +484 -0
  49. package/lib/Store/make-ordered-dictionary.js +78 -0
  50. package/lib/Store/object-repository.js +27 -0
  51. package/lib/Types/Auth.js +2 -0
  52. package/lib/Types/Call.js +2 -0
  53. package/lib/Types/Chat.js +10 -0
  54. package/lib/Types/Contact.js +2 -0
  55. package/lib/Types/Events.js +2 -0
  56. package/lib/Types/GroupMetadata.js +2 -0
  57. package/lib/Types/Label.js +27 -0
  58. package/lib/Types/LabelAssociation.js +9 -0
  59. package/lib/Types/Message.js +14 -0
  60. package/lib/Types/MexUpdates.js +19 -0
  61. package/lib/Types/Newsletter.js +38 -0
  62. package/lib/Types/Product.js +2 -0
  63. package/lib/Types/RichType.js +30 -0
  64. package/lib/Types/Signal.js +2 -0
  65. package/lib/Types/Socket.js +2 -0
  66. package/lib/Types/State.js +2 -0
  67. package/lib/Types/USync.js +2 -0
  68. package/lib/Types/index.js +58 -0
  69. package/lib/Utils/anti-ban.js +339 -0
  70. package/lib/Utils/anti-delete.d.ts +158 -0
  71. package/lib/Utils/anti-delete.js +293 -0
  72. package/lib/Utils/antiban-presets.js +91 -0
  73. package/lib/Utils/antiban-profiles.js +43 -0
  74. package/lib/Utils/auth-utils.js +210 -0
  75. package/lib/Utils/baileys-event-stream.js +66 -0
  76. package/lib/Utils/browser-utils.js +52 -0
  77. package/lib/Utils/business.js +261 -0
  78. package/lib/Utils/chat-control.d.ts +113 -0
  79. package/lib/Utils/chat-control.js +230 -0
  80. package/lib/Utils/chat-utils.js +922 -0
  81. package/lib/Utils/companion-reg-client-utils.js +37 -0
  82. package/lib/Utils/contact-graph-warmer.js +208 -0
  83. package/lib/Utils/crypto.js +196 -0
  84. package/lib/Utils/decode-wa-message.js +208 -0
  85. package/lib/Utils/delivery-tracker.js +110 -0
  86. package/lib/Utils/device-fingerprint.js +135 -0
  87. package/lib/Utils/event-buffer.js +531 -0
  88. package/lib/Utils/generics.js +571 -0
  89. package/lib/Utils/group-operation-guard.js +147 -0
  90. package/lib/Utils/health-monitor.js +207 -0
  91. package/lib/Utils/history.js +122 -0
  92. package/lib/Utils/human-entropy.js +226 -0
  93. package/lib/Utils/identity-change-handler.js +72 -0
  94. package/lib/Utils/index.js +83 -0
  95. package/lib/Utils/interactive-message.d.ts +139 -0
  96. package/lib/Utils/interactive-message.js +273 -0
  97. package/lib/Utils/legitimacy-signal-injector.js +180 -0
  98. package/lib/Utils/link-preview.js +106 -0
  99. package/lib/Utils/logger.d.ts +13 -0
  100. package/lib/Utils/logger.js +9 -0
  101. package/lib/Utils/lt-hash.js +54 -0
  102. package/lib/Utils/make-mutex.js +41 -0
  103. package/lib/Utils/message-composer.d.ts +106 -0
  104. package/lib/Utils/message-composer.js +721 -0
  105. package/lib/Utils/message-retry-manager.d.ts +88 -0
  106. package/lib/Utils/message-retry-manager.js +160 -0
  107. package/lib/Utils/message-search.d.ts +52 -0
  108. package/lib/Utils/message-search.js +226 -0
  109. package/lib/Utils/messages-media.js +899 -0
  110. package/lib/Utils/messages.js +2698 -0
  111. package/lib/Utils/noise-handler.js +175 -0
  112. package/lib/Utils/offline-node-processor.js +45 -0
  113. package/lib/Utils/pre-key-manager.js +131 -0
  114. package/lib/Utils/presence-choreographer.js +438 -0
  115. package/lib/Utils/process-message.js +423 -0
  116. package/lib/Utils/rate-limiter.js +232 -0
  117. package/lib/Utils/reconnect-throttle.js +157 -0
  118. package/lib/Utils/reply-ratio-guard.js +137 -0
  119. package/lib/Utils/reporting-utils.js +307 -0
  120. package/lib/Utils/retry-reason-tracker.js +140 -0
  121. package/lib/Utils/rich-message-utils.d.ts +48 -0
  122. package/lib/Utils/rich-message-utils.js +400 -0
  123. package/lib/Utils/rich-response-utils.js +461 -0
  124. package/lib/Utils/scheduling.d.ts +135 -0
  125. package/lib/Utils/scheduling.js +246 -0
  126. package/lib/Utils/session-fingerprint.js +226 -0
  127. package/lib/Utils/session-stability.js +224 -0
  128. package/lib/Utils/signal.js +188 -0
  129. package/lib/Utils/stanza-ack.js +38 -0
  130. package/lib/Utils/state-manager.js +91 -0
  131. package/lib/Utils/status-posting.d.ts +81 -0
  132. package/lib/Utils/status-posting.js +187 -0
  133. package/lib/Utils/stealth-connect.js +124 -0
  134. package/lib/Utils/sync-action-utils.js +55 -0
  135. package/lib/Utils/tc-token-utils.js +171 -0
  136. package/lib/Utils/templates.d.ts +54 -0
  137. package/lib/Utils/templates.js +244 -0
  138. package/lib/Utils/timelock-guard.js +162 -0
  139. package/lib/Utils/use-mongo-file-auth-state.d.ts +6 -0
  140. package/lib/Utils/use-mongo-file-auth-state.js +81 -0
  141. package/lib/Utils/use-multi-file-auth-state.js +125 -0
  142. package/lib/Utils/use-single-file-auth-state.d.ts +13 -0
  143. package/lib/Utils/use-single-file-auth-state.js +86 -0
  144. package/lib/Utils/use-sqlite-auth-state.js +187 -0
  145. package/lib/Utils/vCard.d.ts +71 -0
  146. package/lib/Utils/vCard.js +196 -0
  147. package/lib/Utils/validate-connection.js +253 -0
  148. package/lib/Utils/warmup.js +154 -0
  149. package/lib/WABinary/constants.js +1303 -0
  150. package/lib/WABinary/decode.js +270 -0
  151. package/lib/WABinary/encode.js +279 -0
  152. package/lib/WABinary/generic-utils.js +223 -0
  153. package/lib/WABinary/index.js +34 -0
  154. package/lib/WABinary/jid-utils.js +84 -0
  155. package/lib/WABinary/types.js +2 -0
  156. package/lib/WAM/BinaryInfo.js +13 -0
  157. package/lib/WAM/constants.js +15254 -0
  158. package/lib/WAM/encode.js +149 -0
  159. package/lib/WAM/index.js +32 -0
  160. package/lib/WAUSync/Protocols/USyncContactProtocol.js +37 -0
  161. package/lib/WAUSync/Protocols/USyncDeviceProtocol.js +71 -0
  162. package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.js +32 -0
  163. package/lib/WAUSync/Protocols/USyncStatusProtocol.js +49 -0
  164. package/lib/WAUSync/Protocols/USyncUsernameProtocol.js +23 -0
  165. package/lib/WAUSync/Protocols/UsyncBotProfileProtocol.js +58 -0
  166. package/lib/WAUSync/Protocols/UsyncLIDProtocol.js +24 -0
  167. package/lib/WAUSync/Protocols/index.js +34 -0
  168. package/lib/WAUSync/USyncQuery.js +99 -0
  169. package/lib/WAUSync/USyncUser.js +26 -0
  170. package/lib/WAUSync/USyncUsernameProtocol.d.ts +9 -0
  171. package/lib/WAUSync/USyncUsernameProtocol.js +37 -0
  172. package/lib/WAUSync/index.js +32 -0
  173. package/lib/index.js +59 -0
  174. package/package.json +105 -5
  175. package/README.md +0 -5
@@ -0,0 +1,246 @@
1
+ 'use strict'
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true })
4
+ exports.createMessageScheduler = exports.MessageScheduler = void 0
5
+
6
+ /**
7
+ * Message Scheduling System
8
+ * Ported from Baileys-Joss: schedule messages to be sent automatically at specific times.
9
+ *
10
+ * Features:
11
+ * - schedule(jid, content, Date) — send at an absolute time
12
+ * - scheduleDelay(jid, content, delayMs) — send after N milliseconds
13
+ * - cancel(id) — cancel by ID
14
+ * - cancelForJid(jid) — cancel all pending for a JID
15
+ * - getPending() — list all pending messages
16
+ * - get(id) — get a single scheduled entry
17
+ * - clearAll() — wipe the whole queue
18
+ * - stop() — stop the internal timer
19
+ * - start() — restart the timer (queue must be non-empty)
20
+ */
21
+ class MessageScheduler {
22
+ constructor(sendMessage, options = {}) {
23
+ this.queue = new Map()
24
+ this.timer = null
25
+ this.sendMessage = sendMessage
26
+ this.options = {
27
+ maxQueue: options.maxQueue ?? 1000,
28
+ checkInterval: options.checkInterval ?? 1000,
29
+ onSent: options.onSent ?? (() => {}),
30
+ onFailed: options.onFailed ?? (() => {})
31
+ }
32
+ }
33
+
34
+ /** Generate a unique ID for a scheduled message */
35
+ generateId() {
36
+ return `sched_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`
37
+ }
38
+
39
+ /**
40
+ * Schedule a message to be sent at a specific Date/time.
41
+ * @param {string} jid - Recipient JID
42
+ * @param {AnyMessageContent} content - Message content
43
+ * @param {Date} scheduledTime - When to send (must be in the future)
44
+ * @returns {ScheduledMessage}
45
+ */
46
+ schedule(jid, content, scheduledTime) {
47
+ if (this.queue.size >= this.options.maxQueue) {
48
+ throw new Error(`Maximum queue size (${this.options.maxQueue}) reached`)
49
+ }
50
+
51
+ if (scheduledTime.getTime() <= Date.now()) {
52
+ throw new Error('Scheduled time must be in the future')
53
+ }
54
+
55
+ const scheduled = {
56
+ id: this.generateId(),
57
+ jid,
58
+ content,
59
+ scheduledTime,
60
+ createdAt: new Date(),
61
+ status: 'pending'
62
+ }
63
+
64
+ this.queue.set(scheduled.id, scheduled)
65
+ this.ensureTimerRunning()
66
+
67
+ return scheduled
68
+ }
69
+
70
+ /**
71
+ * Schedule a message with a delay from now.
72
+ * @param {string} jid - Recipient JID
73
+ * @param {AnyMessageContent} content - Message content
74
+ * @param {number} delayMs - Delay in milliseconds
75
+ * @returns {ScheduledMessage}
76
+ */
77
+ scheduleDelay(jid, content, delayMs) {
78
+ const scheduledTime = new Date(Date.now() + delayMs)
79
+ return this.schedule(jid, content, scheduledTime)
80
+ }
81
+
82
+ /**
83
+ * Cancel a scheduled message by its ID.
84
+ * @param {string} id
85
+ * @returns {boolean} true if cancelled, false if not found or already sent
86
+ */
87
+ cancel(id) {
88
+ const scheduled = this.queue.get(id)
89
+ if (scheduled && scheduled.status === 'pending') {
90
+ scheduled.status = 'cancelled'
91
+ this.queue.delete(id)
92
+ return true
93
+ }
94
+ return false
95
+ }
96
+
97
+ /**
98
+ * Cancel all pending messages for a specific JID.
99
+ * @param {string} jid
100
+ * @returns {number} number of messages cancelled
101
+ */
102
+ cancelForJid(jid) {
103
+ let cancelled = 0
104
+ for (const [id, scheduled] of this.queue) {
105
+ if (scheduled.jid === jid && scheduled.status === 'pending') {
106
+ scheduled.status = 'cancelled'
107
+ this.queue.delete(id)
108
+ cancelled++
109
+ }
110
+ }
111
+ return cancelled
112
+ }
113
+
114
+ /**
115
+ * Get all pending scheduled messages.
116
+ * @returns {ScheduledMessage[]}
117
+ */
118
+ getPending() {
119
+ return Array.from(this.queue.values()).filter((s) => s.status === 'pending')
120
+ }
121
+
122
+ /**
123
+ * Get a scheduled message by ID.
124
+ * @param {string} id
125
+ * @returns {ScheduledMessage|undefined}
126
+ */
127
+ get(id) {
128
+ return this.queue.get(id)
129
+ }
130
+
131
+ /**
132
+ * Clear all pending messages and stop the timer.
133
+ * @returns {number} number of messages cleared
134
+ */
135
+ clearAll() {
136
+ const count = this.queue.size
137
+ this.queue.clear()
138
+ this.stopTimer()
139
+ return count
140
+ }
141
+
142
+ /** Process the queue and send any due messages */
143
+ async processQueue() {
144
+ const now = Date.now()
145
+
146
+ for (const [id, scheduled] of this.queue) {
147
+ if (scheduled.status !== 'pending') continue
148
+ if (scheduled.scheduledTime.getTime() > now) continue
149
+
150
+ try {
151
+ const message = await this.sendMessage(scheduled.jid, scheduled.content)
152
+ scheduled.status = 'sent'
153
+ scheduled.messageId = message?.key?.id ?? undefined
154
+ this.options.onSent(scheduled, message)
155
+ } catch (error) {
156
+ scheduled.status = 'failed'
157
+ scheduled.error = error.message
158
+ this.options.onFailed(scheduled, error)
159
+ }
160
+
161
+ this.queue.delete(id)
162
+ }
163
+
164
+ // Auto-stop when nothing is left
165
+ if (this.queue.size === 0) {
166
+ this.stopTimer()
167
+ }
168
+ }
169
+
170
+ ensureTimerRunning() {
171
+ if (!this.timer) {
172
+ this.timer = setInterval(() => this.processQueue(), this.options.checkInterval)
173
+ }
174
+ }
175
+
176
+ stopTimer() {
177
+ if (this.timer) {
178
+ clearInterval(this.timer)
179
+ this.timer = null
180
+ }
181
+ }
182
+
183
+ /** Stop the scheduler (timer is cleared; already scheduled entries remain in queue) */
184
+ stop() {
185
+ this.stopTimer()
186
+ }
187
+
188
+ /**
189
+ * (Re)start the scheduler — only has effect if the queue is non-empty.
190
+ * The scheduler auto-starts whenever `schedule()` or `scheduleDelay()` is called,
191
+ * so you only need this after an explicit `stop()`.
192
+ */
193
+ start() {
194
+ if (this.queue.size > 0) {
195
+ this.ensureTimerRunning()
196
+ }
197
+ }
198
+ }
199
+
200
+ exports.MessageScheduler = MessageScheduler
201
+
202
+ /**
203
+ * Factory helper — creates and returns a ready-to-use MessageScheduler.
204
+ *
205
+ * @param {(jid: string, content: AnyMessageContent) => Promise<WAMessage|undefined>} sendMessage
206
+ * @param {SchedulerOptions} [options]
207
+ * @returns {MessageScheduler}
208
+ *
209
+ * @example
210
+ * const scheduler = createMessageScheduler(
211
+ * (jid, content) => sock.sendMessage(jid, content),
212
+ * {
213
+ * onSent: (s, msg) => console.log(`Sent to ${s.jid}`),
214
+ * onFailed: (s, err) => console.error(`Failed: ${err.message}`)
215
+ * }
216
+ * )
217
+ *
218
+ * // Schedule for a specific time
219
+ * const entry = scheduler.schedule(jid, { text: 'Happy Birthday! 🎂' }, new Date('2026-12-25T09:00:00'))
220
+ *
221
+ * // Schedule with a delay (30 minutes)
222
+ * scheduler.scheduleDelay(jid, { text: 'Reminder!' }, 30 * 60 * 1000)
223
+ *
224
+ * // Cancel one message
225
+ * scheduler.cancel(entry.id)
226
+ *
227
+ * // Cancel all messages for a JID
228
+ * scheduler.cancelForJid(jid)
229
+ *
230
+ * // Get all pending entries
231
+ * const pending = scheduler.getPending()
232
+ *
233
+ * // Stop (timer cleared, queue preserved)
234
+ * scheduler.stop()
235
+ *
236
+ * // Restart after stop
237
+ * scheduler.start()
238
+ *
239
+ * // Wipe everything
240
+ * scheduler.clearAll()
241
+ */
242
+ const createMessageScheduler = (sendMessage, options) => {
243
+ return new MessageScheduler(sendMessage, options)
244
+ }
245
+
246
+ exports.createMessageScheduler = createMessageScheduler
@@ -0,0 +1,226 @@
1
+ 'use strict'
2
+ Object.defineProperty(exports, '__esModule', { value: true })
3
+ exports.createStealthFingerprint =
4
+ exports.getBatteryState =
5
+ exports.getVoiceNoteMetadata =
6
+ exports.getRetryJitter =
7
+ exports.getTypingJitter =
8
+ exports.getMessageSendJitter =
9
+ exports.applySessionFingerprint =
10
+ exports.generateSessionFingerprint =
11
+ void 0
12
+
13
+ /**
14
+ * @file session-fingerprint.js
15
+ * @description Per-session fingerprint randomization (Obscura-inspired).
16
+ * Comprehensive anti-detection: device profile, network timing, voice note
17
+ * metadata, connection state, and protocol version variance.
18
+ * Ported from baileys-antiban to CJS for bilis.
19
+ * @author Denzy ZeroDay (port), Kobus Wentzel (original)
20
+ */
21
+
22
+ const { generateFingerprint } = require('./device-fingerprint')
23
+
24
+ const DEFAULT_SEND_JITTER_MS = [50, 300]
25
+ const DEFAULT_TYPING_JITTER_MS = [30, 150]
26
+ const DEFAULT_RETRY_JITTER_MS = [100, 500]
27
+ const DEFAULT_DURATION_JITTER_MS = 200
28
+ const DEFAULT_SAMPLE_RATE_POOL = [8000, 16000, 44100, 48000]
29
+ const DEFAULT_IDLE_TIMEOUT_JITTER_MS = [25000, 35000]
30
+ const DEFAULT_KEEPALIVE_JITTER_MS = [15000, 25000]
31
+ const DEFAULT_BATTERY_LEVEL_POOL = [20, 35, 50, 65, 80, 95, 100]
32
+ const DEFAULT_PROTOCOL_VERSION_POOL = ['2.24.5', '2.24.4', '2.24.3']
33
+
34
+ class SeededRandom {
35
+ constructor(seed) {
36
+ let hash = 0
37
+ for (let i = 0; i < seed.length; i++) {
38
+ hash = (hash << 5) - hash + seed.charCodeAt(i)
39
+ hash = hash & hash
40
+ }
41
+ this.state = Math.abs(hash) || 1
42
+ }
43
+
44
+ next() {
45
+ let t = (this.state += 0x6d2b79f5)
46
+ t = Math.imul(t ^ (t >>> 15), t | 1)
47
+ t ^= t + Math.imul(t ^ (t >>> 7), t | 61)
48
+ return ((t ^ (t >>> 14)) >>> 0) / 4294967296
49
+ }
50
+
51
+ range(min, max) {
52
+ return Math.floor(this.next() * (max - min + 1)) + min
53
+ }
54
+ rangeFloat(min, max) {
55
+ return this.next() * (max - min) + min
56
+ }
57
+ pick(array) {
58
+ return array[Math.floor(this.next() * array.length)]
59
+ }
60
+ boolean(probability = 0.5) {
61
+ return this.next() < probability
62
+ }
63
+ }
64
+
65
+ const generateSessionFingerprint = (config = {}, sessionId) => {
66
+ const {
67
+ enabled = true,
68
+ deviceProfile = {},
69
+ networkTiming = {},
70
+ voiceNote = {},
71
+ connectionState = {},
72
+ protocolVersion = {},
73
+ seed
74
+ } = config
75
+
76
+ const finalSessionId = sessionId || `session-${Date.now()}-${Math.random()}`
77
+ const rng = new SeededRandom(seed || finalSessionId)
78
+
79
+ const device = generateFingerprint(
80
+ {
81
+ enabled,
82
+ randomizeAppVersion: deviceProfile.randomizeAppVersion ?? true,
83
+ randomizeOsVersion: deviceProfile.randomizeOsVersion ?? true,
84
+ randomizeDeviceModel: deviceProfile.randomizeDeviceModel ?? true,
85
+ seed: seed || finalSessionId,
86
+ appVersionPool: deviceProfile.appVersionPool,
87
+ osVersionPool: deviceProfile.osVersionPool,
88
+ deviceModelPool: deviceProfile.deviceModelPool
89
+ },
90
+ finalSessionId
91
+ )
92
+
93
+ const sendJitterRange = networkTiming.sendJitterMs || DEFAULT_SEND_JITTER_MS
94
+ const typingJitterRange = networkTiming.typingJitterMs || DEFAULT_TYPING_JITTER_MS
95
+ const retryJitterRange = networkTiming.retryJitterMs || DEFAULT_RETRY_JITTER_MS
96
+
97
+ const networkTimingProfile = enabled
98
+ ? {
99
+ sendJitterMs: rng.range(sendJitterRange[0], sendJitterRange[1]),
100
+ typingJitterMs: rng.range(typingJitterRange[0], typingJitterRange[1]),
101
+ retryJitterMs: rng.range(retryJitterRange[0], retryJitterRange[1])
102
+ }
103
+ : { sendJitterMs: 0, typingJitterMs: 0, retryJitterMs: 0 }
104
+
105
+ const sampleRatePool = voiceNote.sampleRatePool || DEFAULT_SAMPLE_RATE_POOL
106
+ const voiceNoteProfile = {
107
+ waveformSeed: enabled ? rng.range(0, 2147483647) : 0,
108
+ durationJitterMs:
109
+ enabled && voiceNote.randomizeWaveform !== false
110
+ ? rng.range(0, voiceNote.durationJitterMs || DEFAULT_DURATION_JITTER_MS)
111
+ : 0,
112
+ sampleRate: enabled ? rng.pick(sampleRatePool) : sampleRatePool[0]
113
+ }
114
+
115
+ const idleTimeoutRange = connectionState.idleTimeoutJitterMs || DEFAULT_IDLE_TIMEOUT_JITTER_MS
116
+ const keepaliveRange = connectionState.keepaliveJitterMs || DEFAULT_KEEPALIVE_JITTER_MS
117
+ const batteryLevelPool = connectionState.batteryLevelPool || DEFAULT_BATTERY_LEVEL_POOL
118
+
119
+ const connectionStateProfile = {
120
+ idleTimeoutMs: enabled ? rng.range(idleTimeoutRange[0], idleTimeoutRange[1]) : 30000,
121
+ keepaliveMs: enabled ? rng.range(keepaliveRange[0], keepaliveRange[1]) : 20000,
122
+ batteryLevel:
123
+ enabled && connectionState.randomizeBattery !== false
124
+ ? rng.pick(batteryLevelPool)
125
+ : 100,
126
+ batteryCharging: enabled ? rng.boolean(0.3) : false
127
+ }
128
+
129
+ const versionPool = protocolVersion.versionPool || DEFAULT_PROTOCOL_VERSION_POOL
130
+ const protocolVersionStr =
131
+ enabled && protocolVersion.randomizeSubVersion !== false
132
+ ? rng.pick(versionPool)
133
+ : versionPool[0]
134
+
135
+ return {
136
+ device,
137
+ networkTiming: networkTimingProfile,
138
+ voiceNote: voiceNoteProfile,
139
+ connectionState: connectionStateProfile,
140
+ protocolVersion: protocolVersionStr,
141
+ sessionId: finalSessionId,
142
+ createdAt: Date.now()
143
+ }
144
+ }
145
+ exports.generateSessionFingerprint = generateSessionFingerprint
146
+
147
+ const applySessionFingerprint = (socketConfig, fingerprint) => {
148
+ const config = { ...socketConfig }
149
+ config.version = fingerprint.device.appVersion
150
+ config.browser = [
151
+ fingerprint.device.deviceModel,
152
+ fingerprint.device.osVersion,
153
+ `WhatsApp/${fingerprint.device.appVersion.join('.')}`
154
+ ]
155
+ if (config.connectTimeoutMs !== undefined)
156
+ config.connectTimeoutMs = fingerprint.connectionState.idleTimeoutMs
157
+ if (config.keepAliveIntervalMs !== undefined)
158
+ config.keepAliveIntervalMs = fingerprint.connectionState.keepaliveMs
159
+ config.__sessionFingerprint = fingerprint
160
+ return config
161
+ }
162
+ exports.applySessionFingerprint = applySessionFingerprint
163
+
164
+ const getMessageSendJitter = (fingerprint) => {
165
+ const base = fingerprint.networkTiming.sendJitterMs
166
+ return Math.floor(base * 0.5 + Math.random() * base * 0.5)
167
+ }
168
+ exports.getMessageSendJitter = getMessageSendJitter
169
+
170
+ const getTypingJitter = (fingerprint) => {
171
+ const base = fingerprint.networkTiming.typingJitterMs
172
+ return Math.floor(base * 0.5 + Math.random() * base * 0.5)
173
+ }
174
+ exports.getTypingJitter = getTypingJitter
175
+
176
+ const getRetryJitter = (fingerprint) => {
177
+ const base = fingerprint.networkTiming.retryJitterMs
178
+ return Math.floor(base * 0.5 + Math.random() * base * 0.5)
179
+ }
180
+ exports.getRetryJitter = getRetryJitter
181
+
182
+ const getVoiceNoteMetadata = (fingerprint) => ({
183
+ sampleRate: fingerprint.voiceNote.sampleRate,
184
+ durationJitterMs: fingerprint.voiceNote.durationJitterMs,
185
+ waveformSeed: fingerprint.voiceNote.waveformSeed
186
+ })
187
+ exports.getVoiceNoteMetadata = getVoiceNoteMetadata
188
+
189
+ const getBatteryState = (fingerprint) => ({
190
+ level: fingerprint.connectionState.batteryLevel,
191
+ charging: fingerprint.connectionState.batteryCharging
192
+ })
193
+ exports.getBatteryState = getBatteryState
194
+
195
+ const createStealthFingerprint = (sessionId) =>
196
+ generateSessionFingerprint(
197
+ {
198
+ enabled: true,
199
+ deviceProfile: {
200
+ randomizeAppVersion: true,
201
+ randomizeOsVersion: true,
202
+ randomizeDeviceModel: true
203
+ },
204
+ networkTiming: {
205
+ sendJitterMs: [100, 500],
206
+ typingJitterMs: [50, 200],
207
+ retryJitterMs: [200, 800]
208
+ },
209
+ voiceNote: { randomizeWaveform: true, durationJitterMs: 300 },
210
+ connectionState: { randomizeBattery: true },
211
+ protocolVersion: { randomizeSubVersion: true }
212
+ },
213
+ sessionId
214
+ )
215
+ exports.createStealthFingerprint = createStealthFingerprint
216
+
217
+ module.exports = {
218
+ generateSessionFingerprint,
219
+ applySessionFingerprint,
220
+ getMessageSendJitter,
221
+ getTypingJitter,
222
+ getRetryJitter,
223
+ getVoiceNoteMetadata,
224
+ getBatteryState,
225
+ createStealthFingerprint
226
+ }
@@ -0,0 +1,224 @@
1
+ 'use strict'
2
+ Object.defineProperty(exports, '__esModule', { value: true })
3
+ exports.wrap_with_session_stability =
4
+ exports.session_health_monitor =
5
+ exports.classify_disconnect =
6
+ void 0
7
+
8
+ /**
9
+ * @file session-stability.js
10
+ * @description Classifies WA disconnect status codes into actionable
11
+ * categories (fatal / recoverable / rate-limited / unknown) with
12
+ * suggested backoff, and tracks decrypt/Bad-MAC failure rate to detect
13
+ * a degraded session before it fully breaks.
14
+ * `wrap_with_session_stability` wraps a sock in a Proxy that optionally
15
+ * canonicalizes JIDs before sending (via an external lid_resolver) and
16
+ * exposes live session health stats.
17
+ * @author Denzy ZeroDay
18
+ */
19
+
20
+ /** Classify a WA disconnect status code into category + recommended action */
21
+ function classify_disconnect(status_code) {
22
+ if (status_code === 401 || status_code === 440) {
23
+ return {
24
+ category: 'fatal',
25
+ shouldReconnect: false,
26
+ message: 'Logged out — restart with QR code required',
27
+ code: status_code
28
+ }
29
+ }
30
+ if (status_code === 515) {
31
+ return {
32
+ category: 'fatal',
33
+ shouldReconnect: false,
34
+ message: 'Restart required by WhatsApp — client too old or protocol mismatch',
35
+ code: status_code
36
+ }
37
+ }
38
+ if (status_code === 405) {
39
+ return {
40
+ category: 'fatal',
41
+ shouldReconnect: false,
42
+ message: 'Method not allowed — server rejected connection method',
43
+ code: status_code
44
+ }
45
+ }
46
+ if (status_code === 409 || status_code === 428) {
47
+ return {
48
+ category: 'fatal',
49
+ shouldReconnect: false,
50
+ message: 'Connection replaced — another device took over',
51
+ code: status_code
52
+ }
53
+ }
54
+ if (status_code === 412) {
55
+ return {
56
+ category: 'recoverable',
57
+ shouldReconnect: true,
58
+ backoffMs: 30000,
59
+ message: 'Precondition failed — auth state mismatch, retry after delay',
60
+ code: status_code
61
+ }
62
+ }
63
+ if (status_code === 429) {
64
+ return {
65
+ category: 'rate-limited',
66
+ shouldReconnect: true,
67
+ backoffMs: 300000,
68
+ message: 'Rate limited by WhatsApp — cool-off period required',
69
+ code: status_code
70
+ }
71
+ }
72
+ if (status_code === 503) {
73
+ return {
74
+ category: 'rate-limited',
75
+ shouldReconnect: true,
76
+ backoffMs: 60000,
77
+ message: 'WhatsApp service unavailable — temporary outage',
78
+ code: status_code
79
+ }
80
+ }
81
+ if (status_code === 408) {
82
+ return {
83
+ category: 'recoverable',
84
+ shouldReconnect: true,
85
+ backoffMs: 5000,
86
+ message: 'Connection timeout — network issue, safe to retry',
87
+ code: status_code
88
+ }
89
+ }
90
+ if (status_code === 500) {
91
+ return {
92
+ category: 'recoverable',
93
+ shouldReconnect: true,
94
+ backoffMs: 10000,
95
+ message: 'WhatsApp internal error — temporary server issue',
96
+ code: status_code
97
+ }
98
+ }
99
+ if (status_code === 1000) {
100
+ return {
101
+ category: 'recoverable',
102
+ shouldReconnect: true,
103
+ backoffMs: 2000,
104
+ message: 'Connection closed gracefully — safe to reconnect',
105
+ code: status_code
106
+ }
107
+ }
108
+ return {
109
+ category: 'unknown',
110
+ shouldReconnect: true,
111
+ backoffMs: 15000,
112
+ message: `Unknown disconnect reason (code ${status_code}) — reconnect with caution`,
113
+ code: status_code
114
+ }
115
+ }
116
+ exports.classify_disconnect = classify_disconnect
117
+
118
+ const default_health_config = {
119
+ bad_mac_threshold: 3,
120
+ bad_mac_window_ms: 60000
121
+ }
122
+
123
+ class session_health_monitor {
124
+ constructor(config = {}) {
125
+ this.config = { ...default_health_config, ...config }
126
+ this.on_degraded = config.onDegraded
127
+ this.on_recovered = config.onRecovered
128
+ this.stats = { decryptSuccess: 0, decryptFail: 0, badMacCount: 0, isDegraded: false }
129
+ this.bad_mac_timestamps = []
130
+ }
131
+
132
+ /** Record successful decrypt */
133
+ record_decrypt_success() {
134
+ this.stats.decryptSuccess++
135
+ this.check_recovery()
136
+ }
137
+
138
+ /** Record failed decrypt (Bad MAC or similar) */
139
+ record_decrypt_fail(is_bad_mac = false) {
140
+ this.stats.decryptFail++
141
+ if (is_bad_mac) {
142
+ const now = Date.now()
143
+ this.stats.badMacCount++
144
+ this.stats.lastBadMac = new Date(now)
145
+ this.bad_mac_timestamps.push(now)
146
+ const cutoff = now - this.config.bad_mac_window_ms
147
+ this.bad_mac_timestamps = this.bad_mac_timestamps.filter((ts) => ts > cutoff)
148
+
149
+ if (
150
+ !this.stats.isDegraded &&
151
+ this.bad_mac_timestamps.length >= this.config.bad_mac_threshold
152
+ ) {
153
+ this.stats.isDegraded = true
154
+ this.stats.degradedSince = new Date(now)
155
+ this.on_degraded?.(this.get_stats())
156
+ }
157
+ }
158
+ }
159
+
160
+ /** Check if session has recovered from degraded state */
161
+ check_recovery() {
162
+ if (!this.stats.isDegraded) return
163
+ const now = Date.now()
164
+ const cutoff = now - this.config.bad_mac_window_ms
165
+ this.bad_mac_timestamps = this.bad_mac_timestamps.filter((ts) => ts > cutoff)
166
+
167
+ if (this.bad_mac_timestamps.length < this.config.bad_mac_threshold) {
168
+ this.stats.isDegraded = false
169
+ this.stats.degradedSince = undefined
170
+ this.on_recovered?.(this.get_stats())
171
+ }
172
+ }
173
+
174
+ /** Get current health stats */
175
+ get_stats() {
176
+ return { ...this.stats }
177
+ }
178
+
179
+ /** Reset all counters */
180
+ reset() {
181
+ this.stats = { decryptSuccess: 0, decryptFail: 0, badMacCount: 0, isDegraded: false }
182
+ this.bad_mac_timestamps = []
183
+ }
184
+ }
185
+ exports.session_health_monitor = session_health_monitor
186
+
187
+ /**
188
+ * Wrap a Baileys sock in a Proxy that:
189
+ * - optionally canonicalizes JIDs before sendMessage via an external lid_resolver
190
+ * - exposes `sessionHealthStats` and `sessionHealthMonitor` getters
191
+ *
192
+ * @param {object} sock
193
+ * @param {object} config
194
+ * @param {boolean} config.canonicalJidNormalization
195
+ * @param {boolean} config.healthMonitoring
196
+ * @param {object} config.health - passed to session_health_monitor
197
+ * @param {object} config.lidResolver - optional, must expose resolveCanonical(jid)
198
+ */
199
+ function wrap_with_session_stability(sock, config = {}) {
200
+ const {
201
+ canonicalJidNormalization = true,
202
+ healthMonitoring = true,
203
+ health: health_config,
204
+ lidResolver: lid_resolver
205
+ } = config
206
+ const health_monitor = healthMonitoring ? new session_health_monitor(health_config) : null
207
+
208
+ return new Proxy(sock, {
209
+ get(target, prop) {
210
+ if (prop === 'sendMessage' && canonicalJidNormalization && lid_resolver) {
211
+ return async (jid, content, options) => {
212
+ const canonical = lid_resolver.resolveCanonical(jid)
213
+ return target.sendMessage(canonical, content, options)
214
+ }
215
+ }
216
+ if (prop === 'sessionHealthStats' && health_monitor) return health_monitor.get_stats()
217
+ if (prop === 'sessionHealthMonitor' && health_monitor) return health_monitor
218
+ return target[prop]
219
+ }
220
+ })
221
+ }
222
+ exports.wrap_with_session_stability = wrap_with_session_stability
223
+
224
+ module.exports = { classify_disconnect, session_health_monitor, wrap_with_session_stability }