@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,438 @@
1
+ 'use strict'
2
+ Object.defineProperty(exports, '__esModule', { value: true })
3
+ exports.presence_choreographer = exports.get_circadian_multiplier = exports.ACTIVITY_CURVES = void 0
4
+
5
+ /**
6
+ * @file presence-choreographer.js
7
+ * @description Models a realistic human daily rhythm (circadian curve),
8
+ * Gaussian-variance typing speed with think-pauses, delayed/skipped read
9
+ * receipts, distraction pauses, and offline gaps. Produces a "typing plan"
10
+ * the caller executes step by step against sock.sendPresenceUpdate.
11
+ * Off by default.
12
+ * @author Denzy ZeroDay
13
+ */
14
+
15
+ const default_config = {
16
+ enabled: false,
17
+ enable_circadian_rhythm: true,
18
+ timezone: 'UTC',
19
+ activity_curve: 'office',
20
+ circadian: { enabled: true, profile: 'default', timezone: 'UTC' },
21
+ distraction_pause_probability: 0.05,
22
+ distraction_pause_min_ms: 300000,
23
+ distraction_pause_max_ms: 1200000,
24
+ read_receipt_delay_min_ms: 3000,
25
+ read_receipt_delay_max_ms: 45000,
26
+ read_receipt_skip_probability: 0.15,
27
+ offline_gap_probability: 0.03,
28
+ offline_gap_min_ms: 300000,
29
+ offline_gap_max_ms: 900000,
30
+ enable_typing_model: true,
31
+ typing_wpm: 45,
32
+ typing_wpm_std_dev: 15,
33
+ think_pause_probability: 0.08,
34
+ think_pause_min_ms: 800,
35
+ think_pause_max_ms: 3500,
36
+ intermittent_paused_probability: 0.4,
37
+ typing_max_ms: 90000,
38
+ typing_min_ms: 600
39
+ }
40
+
41
+ const ACTIVITY_CURVES = {
42
+ office: [
43
+ 0.1,
44
+ 0.1,
45
+ 0.1,
46
+ 0.1,
47
+ 0.1,
48
+ 0.1,
49
+ 0.1,
50
+ 0.1, // 0-7: night quiet
51
+ 0.5,
52
+ 0.5, // 8-9: morning ramp
53
+ 0.95,
54
+ 0.95, // 10-11: morning peak
55
+ 0.6, // 12: lunch dip
56
+ 0.9,
57
+ 0.9,
58
+ 0.9,
59
+ 0.9, // 13-16: afternoon
60
+ 0.6,
61
+ 0.6, // 17-18: wind-down
62
+ 0.4,
63
+ 0.4, // 19-20: evening
64
+ 0.2,
65
+ 0.2,
66
+ 0.2,
67
+ 0.2 // 21-24: taper
68
+ ],
69
+ social: [
70
+ 0.1,
71
+ 0.1,
72
+ 0.1,
73
+ 0.1,
74
+ 0.1,
75
+ 0.1,
76
+ 0.1,
77
+ 0.1, // 0-7: night quiet
78
+ 0.3,
79
+ 0.4, // 8-9: slow start
80
+ 0.7,
81
+ 0.8, // 10-11: ramp up
82
+ 0.5, // 12: lunch
83
+ 0.7,
84
+ 0.7, // 13-14: afternoon
85
+ 0.4, // 15: tea time dip
86
+ 0.8,
87
+ 0.9,
88
+ 0.9, // 16-18: active
89
+ 0.6, // 19: dinner dip
90
+ 0.8,
91
+ 0.85,
92
+ 0.9,
93
+ 0.95,
94
+ 1 // 20-24: evening peak
95
+ ],
96
+ global: [
97
+ 0.5,
98
+ 0.5,
99
+ 0.5,
100
+ 0.5,
101
+ 0.5,
102
+ 0.5, // 0-5: night
103
+ 0.4,
104
+ 0.4, // 6-7: dawn dip
105
+ 0.6,
106
+ 0.7,
107
+ 0.8,
108
+ 0.8, // 8-11: morning
109
+ 0.6, // 12: lunch
110
+ 0.8,
111
+ 0.8,
112
+ 0.8,
113
+ 0.8, // 13-16: afternoon
114
+ 0.7,
115
+ 0.7, // 17-18: evening
116
+ 0.6,
117
+ 0.5,
118
+ 0.5,
119
+ 0.5,
120
+ 0.5,
121
+ 0.5 // 19-24: night taper
122
+ ]
123
+ }
124
+ exports.ACTIVITY_CURVES = ACTIVITY_CURVES
125
+
126
+ /**
127
+ * Compute the circadian activity multiplier for a given moment.
128
+ * >1 = slower/less active (night), ~1 = normal, <1 would mean faster (not used here).
129
+ */
130
+ function get_circadian_multiplier(date = new Date(), profile = 'default', timezone) {
131
+ if (profile === 'always_on') return 1
132
+
133
+ let hour
134
+ if (timezone) {
135
+ try {
136
+ const formatter = new Intl.DateTimeFormat('en-US', {
137
+ timeZone: timezone,
138
+ hour: 'numeric',
139
+ hour12: false
140
+ })
141
+ const parts = formatter.formatToParts(date)
142
+ const hour_part = parts.find((p) => p.type === 'hour')
143
+ hour = hour_part ? parseInt(hour_part.value, 10) : date.getHours()
144
+ } catch {
145
+ hour = date.getHours()
146
+ }
147
+ } else {
148
+ hour = date.getHours()
149
+ }
150
+
151
+ let shifted_hour = hour
152
+ if (profile === 'nightOwl') shifted_hour = (hour - 3 + 24) % 24
153
+ else if (profile === 'earlyBird') shifted_hour = (hour + 2) % 24
154
+
155
+ if (shifted_hour >= 9 && shifted_hour < 22) {
156
+ const t = (shifted_hour - 9) / 13
157
+ return 1 + 0.2 * Math.cos(2 * Math.PI * t)
158
+ } else if (shifted_hour >= 22 && shifted_hour < 24) {
159
+ const t = (shifted_hour - 22) / 2
160
+ return 1.2 + 1.3 * t
161
+ } else if (shifted_hour >= 0 && shifted_hour < 2) {
162
+ const t = shifted_hour / 2
163
+ return 2.5 + 1.5 * t
164
+ } else if (shifted_hour >= 2 && shifted_hour < 6) {
165
+ const t = (shifted_hour - 2) / 4
166
+ return 5 + 1 * Math.cos(Math.PI * t)
167
+ } else {
168
+ const t = (shifted_hour - 6) / 3
169
+ return 4 - 3 * t
170
+ }
171
+ }
172
+ exports.get_circadian_multiplier = get_circadian_multiplier
173
+
174
+ class presence_choreographer {
175
+ constructor(config = {}) {
176
+ this.config = {
177
+ ...default_config,
178
+ ...config,
179
+ circadian: { ...default_config.circadian, ...(config.circadian || {}) }
180
+ }
181
+ this.stats = {
182
+ distractionPausesInjected: 0,
183
+ offlineGapsInjected: 0,
184
+ readReceiptsDelayed: 0,
185
+ readReceiptsSkipped: 0,
186
+ typingPlansComputed: 0,
187
+ typingPlansExecuted: 0,
188
+ totalTypingTimeMs: 0
189
+ }
190
+ }
191
+
192
+ /**
193
+ * Get current activity factor (0.1 to 1.0).
194
+ * Higher = more active = shorter delays. If circadian disabled, returns 1.0.
195
+ */
196
+ get_current_activity_factor() {
197
+ if (!this.config.enabled || !this.config.enable_circadian_rhythm) return 1
198
+ const hour = this.get_local_hour()
199
+ const curve = ACTIVITY_CURVES[this.config.activity_curve]
200
+ return curve[hour] || 0.5
201
+ }
202
+
203
+ /** Check if should pause for distraction. Returns { pause, duration_ms } */
204
+ should_pause_for_distraction() {
205
+ if (!this.config.enabled) return { pause: false, duration_ms: 0 }
206
+ if (Math.random() < this.config.distraction_pause_probability) {
207
+ const duration_ms = this.random_between(
208
+ this.config.distraction_pause_min_ms,
209
+ this.config.distraction_pause_max_ms
210
+ )
211
+ this.stats.distractionPausesInjected++
212
+ return { pause: true, duration_ms }
213
+ }
214
+ return { pause: false, duration_ms: 0 }
215
+ }
216
+
217
+ /** Check if should take offline gap. Returns { offline, duration_ms } */
218
+ should_take_offline_gap() {
219
+ if (!this.config.enabled) return { offline: false, duration_ms: 0 }
220
+ if (Math.random() < this.config.offline_gap_probability) {
221
+ const duration_ms = this.random_between(
222
+ this.config.offline_gap_min_ms,
223
+ this.config.offline_gap_max_ms
224
+ )
225
+ this.stats.offlineGapsInjected++
226
+ return { offline: true, duration_ms }
227
+ }
228
+ return { offline: false, duration_ms: 0 }
229
+ }
230
+
231
+ /**
232
+ * Check if should mark message as read.
233
+ * Returns { mark: false } if skip probability hit, else { mark: true, delay_ms }.
234
+ * Applies circadian multiplier to delay.
235
+ */
236
+ should_mark_read() {
237
+ if (!this.config.enabled) return { mark: true, delay_ms: 0 }
238
+ if (Math.random() < this.config.read_receipt_skip_probability) {
239
+ this.stats.readReceiptsSkipped++
240
+ return { mark: false, delay_ms: 0 }
241
+ }
242
+ const base_delay_ms = this.random_between(
243
+ this.config.read_receipt_delay_min_ms,
244
+ this.config.read_receipt_delay_max_ms
245
+ )
246
+ let delay_ms = base_delay_ms
247
+ if (this.config.circadian.enabled) {
248
+ const multiplier = get_circadian_multiplier(
249
+ new Date(),
250
+ this.config.circadian.profile,
251
+ this.config.circadian.timezone
252
+ )
253
+ delay_ms = Math.floor(base_delay_ms * multiplier)
254
+ }
255
+ this.stats.readReceiptsDelayed++
256
+ return { mark: true, delay_ms }
257
+ }
258
+
259
+ /**
260
+ * Compute realistic typing duration for a message of given length.
261
+ * Includes Gaussian WPM variance + think-pause injection + circadian timing multiplier.
262
+ * Returns a "typing plan": array of { state, duration_ms } steps the caller should execute sequentially.
263
+ *
264
+ * plan = [
265
+ * { state: 'composing', duration_ms: 4200 },
266
+ * { state: 'paused', duration_ms: 950 }, // think pause
267
+ * { state: 'composing', duration_ms: 6800 },
268
+ * { state: 'paused', duration_ms: 600 }, // brief stop before send
269
+ * ]
270
+ */
271
+ compute_typing_plan(message_length) {
272
+ if (!this.config.enabled || !this.config.enable_typing_model) {
273
+ return [{ state: 'composing', duration_ms: this.config.typing_min_ms }]
274
+ }
275
+ this.stats.typingPlansComputed++
276
+ if (message_length === 0) {
277
+ return [{ state: 'composing', duration_ms: this.config.typing_min_ms }]
278
+ }
279
+
280
+ const wpm_sample = this.clamp(
281
+ this.gaussian_sample(this.config.typing_wpm, this.config.typing_wpm_std_dev),
282
+ 10,
283
+ 120
284
+ )
285
+ const cps = (wpm_sample * 5) / 60
286
+ const base_ms = (message_length / cps) * 1000
287
+
288
+ let circadian_multiplier = 1
289
+ if (this.config.circadian.enabled) {
290
+ circadian_multiplier = get_circadian_multiplier(
291
+ new Date(),
292
+ this.config.circadian.profile,
293
+ this.config.circadian.timezone
294
+ )
295
+ }
296
+
297
+ const target_ms = this.clamp(
298
+ base_ms * circadian_multiplier,
299
+ this.config.typing_min_ms,
300
+ this.config.typing_max_ms
301
+ )
302
+
303
+ const plan = []
304
+ let remaining_budget = target_ms
305
+ let position = 0
306
+ const chunk_size = 10
307
+ const num_chunks = Math.max(1, Math.ceil(message_length / chunk_size))
308
+
309
+ for (let i = 0; i < num_chunks && remaining_budget > 0; i++) {
310
+ const chars_in_chunk = Math.min(chunk_size, message_length - position)
311
+ const remaining_chunks = num_chunks - i
312
+ const chunk_budget = remaining_budget / remaining_chunks
313
+ const chunk_typing_ms = Math.floor(Math.min(chunk_budget, remaining_budget))
314
+ if (chunk_typing_ms <= 0) break
315
+
316
+ if (
317
+ i > 0 &&
318
+ i < num_chunks - 1 &&
319
+ Math.random() < this.config.think_pause_probability
320
+ ) {
321
+ plan.push({ state: 'composing', duration_ms: chunk_typing_ms })
322
+ remaining_budget -= chunk_typing_ms
323
+ const base_pause_ms = this.random_between(
324
+ this.config.think_pause_min_ms,
325
+ this.config.think_pause_max_ms
326
+ )
327
+ const pause_ms = Math.floor(base_pause_ms * circadian_multiplier)
328
+ plan.push({ state: 'paused', duration_ms: pause_ms })
329
+ } else {
330
+ if (plan.length === 0 || plan[plan.length - 1].state === 'paused') {
331
+ plan.push({ state: 'composing', duration_ms: chunk_typing_ms })
332
+ } else {
333
+ plan[plan.length - 1].duration_ms += chunk_typing_ms
334
+ }
335
+ remaining_budget -= chunk_typing_ms
336
+ }
337
+ position += chars_in_chunk
338
+ }
339
+
340
+ if (Math.random() < this.config.intermittent_paused_probability) {
341
+ const base_final_pause_ms = this.random_between(200, 800)
342
+ const final_pause_ms = Math.floor(base_final_pause_ms * circadian_multiplier)
343
+ plan.push({ state: 'paused', duration_ms: final_pause_ms })
344
+ }
345
+
346
+ if (plan.length === 0 || !plan.some((step) => step.state === 'composing')) {
347
+ return [{ state: 'composing', duration_ms: this.config.typing_min_ms }]
348
+ }
349
+
350
+ return plan
351
+ }
352
+
353
+ /**
354
+ * Execute a typing plan against a Baileys-shaped sock with sendPresenceUpdate(state, jid).
355
+ * Awaits each step's duration. Updates stats.
356
+ *
357
+ * await choreographer.execute_typing_plan(sock, jid, plan)
358
+ * await sock.sendMessage(jid, content)
359
+ */
360
+ async execute_typing_plan(sock, jid, plan, options) {
361
+ this.stats.typingPlansExecuted++
362
+ for (const step of plan) {
363
+ if (options?.signal?.aborted) {
364
+ await Promise.resolve(sock.sendPresenceUpdate('paused', jid))
365
+ throw new Error('Typing plan aborted')
366
+ }
367
+ await Promise.resolve(sock.sendPresenceUpdate(step.state, jid))
368
+ await this.sleep(step.duration_ms)
369
+ this.stats.totalTypingTimeMs += step.duration_ms
370
+ }
371
+ }
372
+
373
+ /** Get statistics */
374
+ get_stats() {
375
+ return {
376
+ currentActivityFactor: this.get_current_activity_factor(),
377
+ distractionPausesInjected: this.stats.distractionPausesInjected,
378
+ offlineGapsInjected: this.stats.offlineGapsInjected,
379
+ readReceiptsDelayed: this.stats.readReceiptsDelayed,
380
+ readReceiptsSkipped: this.stats.readReceiptsSkipped,
381
+ currentHourLocal: this.get_local_hour(),
382
+ typingPlansComputed: this.stats.typingPlansComputed,
383
+ typingPlansExecuted: this.stats.typingPlansExecuted,
384
+ totalTypingTimeMs: this.stats.totalTypingTimeMs
385
+ }
386
+ }
387
+
388
+ /** Reset statistics */
389
+ reset() {
390
+ this.stats = {
391
+ distractionPausesInjected: 0,
392
+ offlineGapsInjected: 0,
393
+ readReceiptsDelayed: 0,
394
+ readReceiptsSkipped: 0,
395
+ typingPlansComputed: 0,
396
+ typingPlansExecuted: 0,
397
+ totalTypingTimeMs: 0
398
+ }
399
+ }
400
+
401
+ // Private helpers
402
+ get_local_hour() {
403
+ try {
404
+ const formatter = new Intl.DateTimeFormat('en-US', {
405
+ timeZone: this.config.timezone,
406
+ hour: 'numeric',
407
+ hour12: false
408
+ })
409
+ const parts = formatter.formatToParts(new Date())
410
+ const hour_part = parts.find((p) => p.type === 'hour')
411
+ if (hour_part) return parseInt(hour_part.value, 10)
412
+ } catch {}
413
+ return new Date().getUTCHours()
414
+ }
415
+
416
+ random_between(min, max) {
417
+ return Math.floor(Math.random() * (max - min + 1)) + min
418
+ }
419
+
420
+ clamp(value, min, max) {
421
+ return Math.max(min, Math.min(max, value))
422
+ }
423
+
424
+ /** Generate Gaussian sample using Box-Muller transform. Returns a sample from N(mean, std_dev). */
425
+ gaussian_sample(mean, std_dev) {
426
+ const u1 = Math.random()
427
+ const u2 = Math.random()
428
+ const z0 = Math.sqrt(-2 * Math.log(u1)) * Math.cos(2 * Math.PI * u2)
429
+ return mean + z0 * std_dev
430
+ }
431
+
432
+ sleep(ms) {
433
+ return new Promise((resolve) => setTimeout(resolve, ms))
434
+ }
435
+ }
436
+
437
+ exports.presence_choreographer = presence_choreographer
438
+ module.exports = { presence_choreographer, get_circadian_multiplier, ACTIVITY_CURVES }