@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,721 @@
1
+ 'use strict'
2
+
3
+ const { proto } = require('../../WAProto')
4
+ const { generateMessageID } = require('./generics')
5
+ const { getUrlFromDirectPath } = require('./messages-media')
6
+
7
+ const JS_KEYWORDS = new Set([
8
+ 'import',
9
+ 'export',
10
+ 'from',
11
+ 'default',
12
+ 'as',
13
+ 'const',
14
+ 'let',
15
+ 'var',
16
+ 'function',
17
+ 'class',
18
+ 'extends',
19
+ 'new',
20
+ 'return',
21
+ 'if',
22
+ 'else',
23
+ 'for',
24
+ 'while',
25
+ 'do',
26
+ 'switch',
27
+ 'case',
28
+ 'break',
29
+ 'continue',
30
+ 'try',
31
+ 'catch',
32
+ 'finally',
33
+ 'throw',
34
+ 'async',
35
+ 'await',
36
+ 'yield',
37
+ 'typeof',
38
+ 'instanceof',
39
+ 'in',
40
+ 'of',
41
+ 'delete',
42
+ 'void',
43
+ 'true',
44
+ 'false',
45
+ 'null',
46
+ 'undefined',
47
+ 'NaN',
48
+ 'Infinity',
49
+ 'this',
50
+ 'super',
51
+ 'static',
52
+ 'get',
53
+ 'set',
54
+ 'debugger',
55
+ 'with'
56
+ ])
57
+
58
+ const PYTHON_KEYWORDS = new Set([
59
+ 'import',
60
+ 'from',
61
+ 'as',
62
+ 'def',
63
+ 'class',
64
+ 'return',
65
+ 'if',
66
+ 'elif',
67
+ 'else',
68
+ 'for',
69
+ 'while',
70
+ 'break',
71
+ 'continue',
72
+ 'try',
73
+ 'except',
74
+ 'finally',
75
+ 'raise',
76
+ 'with',
77
+ 'yield',
78
+ 'lambda',
79
+ 'pass',
80
+ 'del',
81
+ 'global',
82
+ 'nonlocal',
83
+ 'assert',
84
+ 'True',
85
+ 'False',
86
+ 'None',
87
+ 'and',
88
+ 'or',
89
+ 'not',
90
+ 'in',
91
+ 'is',
92
+ 'async',
93
+ 'await',
94
+ 'self',
95
+ 'print'
96
+ ])
97
+
98
+ const LANGUAGE_KEYWORDS = {
99
+ javascript: JS_KEYWORDS,
100
+ typescript: JS_KEYWORDS,
101
+ js: JS_KEYWORDS,
102
+ ts: JS_KEYWORDS,
103
+ python: PYTHON_KEYWORDS,
104
+ py: PYTHON_KEYWORDS
105
+ }
106
+
107
+ const CodeHighlightType = {
108
+ DEFAULT: 0,
109
+ KEYWORD: 1,
110
+ METHOD: 2,
111
+ STRING: 3,
112
+ NUMBER: 4,
113
+ COMMENT: 5
114
+ }
115
+
116
+ // Reverse map for reference
117
+ CodeHighlightType[0] = 'DEFAULT'
118
+ CodeHighlightType[1] = 'KEYWORD'
119
+ CodeHighlightType[2] = 'METHOD'
120
+ CodeHighlightType[3] = 'STRING'
121
+ CodeHighlightType[4] = 'NUMBER'
122
+ CodeHighlightType[5] = 'COMMENT'
123
+
124
+ const RichSubMessageType = {
125
+ UNKNOWN: 0,
126
+ GRID_IMAGE: 1,
127
+ TEXT: 2,
128
+ INLINE_IMAGE: 3,
129
+ TABLE: 4,
130
+ CODE: 5,
131
+ DYNAMIC: 6,
132
+ MAP: 7,
133
+ LATEX: 8,
134
+ CONTENT_ITEMS: 9
135
+ }
136
+
137
+ const tokenizeCode = (codeStr, language = 'javascript') => {
138
+ const keywords = LANGUAGE_KEYWORDS[language] || JS_KEYWORDS
139
+ const blocks = []
140
+ const lines = codeStr.split('\n')
141
+
142
+ for (let li = 0; li < lines.length; li++) {
143
+ const line = lines[li]
144
+ const isLast = li === lines.length - 1
145
+ const nl = isLast ? '' : '\n'
146
+
147
+ if (!line.trim()) {
148
+ blocks.push({ highlightType: CodeHighlightType.DEFAULT, codeContent: line + nl })
149
+ continue
150
+ }
151
+
152
+ if (line.trim().startsWith('//') || line.trim().startsWith('#')) {
153
+ blocks.push({ highlightType: CodeHighlightType.COMMENT, codeContent: line + nl })
154
+ continue
155
+ }
156
+
157
+ const regex =
158
+ /(\/\/.*$|#.*$)|(\"(?:[^\"\\]|\\.)*\")|('(?:[^'\\]|\\.)*')|(`(?:[^`\\]|\\.)*`)|(\b\d+(?:\.\d+)?\b)|(\b[a-zA-Z_$][\w$]*\b)|([^\s\w$\"'`]+)|(\s+)/g
159
+
160
+ let match
161
+ const tokens = []
162
+
163
+ while ((match = regex.exec(line)) !== null) {
164
+ const val = match[0]
165
+
166
+ if (match[1]) {
167
+ tokens.push({ highlightType: CodeHighlightType.COMMENT, codeContent: val })
168
+ } else if (match[2] || match[3] || match[4]) {
169
+ tokens.push({ highlightType: CodeHighlightType.STRING, codeContent: val })
170
+ } else if (match[5]) {
171
+ tokens.push({ highlightType: CodeHighlightType.NUMBER, codeContent: val })
172
+ } else if (match[6]) {
173
+ if (keywords.has(val)) {
174
+ tokens.push({ highlightType: CodeHighlightType.KEYWORD, codeContent: val })
175
+ } else {
176
+ const after = line.slice(regex.lastIndex).trimStart()
177
+ if (after.startsWith('(')) {
178
+ tokens.push({ highlightType: CodeHighlightType.METHOD, codeContent: val })
179
+ } else {
180
+ tokens.push({ highlightType: CodeHighlightType.DEFAULT, codeContent: val })
181
+ }
182
+ }
183
+ } else {
184
+ tokens.push({ highlightType: CodeHighlightType.DEFAULT, codeContent: val })
185
+ }
186
+ }
187
+
188
+ if (tokens.length === 0) {
189
+ blocks.push({ highlightType: CodeHighlightType.DEFAULT, codeContent: line + nl })
190
+ continue
191
+ }
192
+
193
+ // Merge adjacent same-type tokens
194
+ const merged = []
195
+ for (const t of tokens) {
196
+ const prev = merged.length > 0 ? merged[merged.length - 1] : undefined
197
+ if (prev && prev.highlightType === t.highlightType) {
198
+ prev.codeContent += t.codeContent
199
+ } else {
200
+ merged.push({ ...t })
201
+ }
202
+ }
203
+
204
+ if (merged.length > 0) {
205
+ merged[merged.length - 1].codeContent += nl
206
+ }
207
+
208
+ blocks.push(...merged)
209
+ }
210
+
211
+ return blocks
212
+ }
213
+
214
+ /**
215
+ * Build a contextInfo object for botForwardedMessage payloads.
216
+ * @param {object|null} quoted - optional quoted WAMessage
217
+ */
218
+ const buildRichContextInfo = (quoted, options = {}) => {
219
+ const ctxInfo = {
220
+ forwardingScore: 1,
221
+ isForwarded: true,
222
+ forwardedAiBotMessageInfo: {
223
+ botJid: options.botJid ? options.botJid : '867051314767696@bot'
224
+ },
225
+ forwardOrigin: 4,
226
+ ...(options.mentions ? { mentionedJid: options.mentions } : {})
227
+ }
228
+
229
+ if (quoted?.key) {
230
+ ctxInfo.stanzaId = quoted.key.id
231
+ ctxInfo.participant = quoted.key.participant || quoted.sender || quoted.key.remoteJid
232
+ ctxInfo.quotedMessage = quoted.message
233
+ }
234
+
235
+ return ctxInfo
236
+ }
237
+
238
+ /**
239
+ * Wrap submessages into the botForwardedMessage → richResponseMessage proto structure.
240
+ * @param {Array} submessages
241
+ * @param {object} contextInfo
242
+ * @param {object|null} unifiedResponse - optional pre-captured unified response data
243
+ */
244
+ const buildBotForwardedMessage = (submessages, contextInfo, unifiedResponse) => {
245
+ const richResponse = {
246
+ messageType: 1,
247
+ submessages,
248
+ contextInfo
249
+ }
250
+
251
+ if (unifiedResponse) {
252
+ richResponse.unifiedResponse = unifiedResponse
253
+ }
254
+
255
+ return {
256
+ botForwardedMessage: {
257
+ message: {
258
+ richResponseMessage: richResponse
259
+ }
260
+ }
261
+ }
262
+ }
263
+
264
+ /**
265
+ * Generate a rich table message.
266
+ * @param {string} title
267
+ * @param {string[]} headers
268
+ * @param {string[][]} rows
269
+ * @param {object|null} quoted
270
+ * @param {object} options - { headerText?, footer? }
271
+ */
272
+ const generateTableContent = (title, headers, rows, quoted, options = {}) => {
273
+ const { footer, headerText } = options
274
+ const tableRows = [
275
+ { items: headers, isHeading: true },
276
+ ...rows.map((row) => ({ items: row.map(String) }))
277
+ ]
278
+
279
+ const submessages = []
280
+ if (headerText) {
281
+ submessages.push({ messageType: 2, messageText: headerText })
282
+ }
283
+ submessages.push({ messageType: 4, tableMetadata: { title, rows: tableRows } })
284
+ if (footer) {
285
+ submessages.push({ messageType: 2, messageText: footer })
286
+ }
287
+
288
+ const ctxInfo = buildRichContextInfo(quoted)
289
+ return {
290
+ message: buildBotForwardedMessage(submessages, ctxInfo),
291
+ messageId: generateMessageID()
292
+ }
293
+ }
294
+
295
+ /**
296
+ * Generate a rich list message.
297
+ * @param {string} title
298
+ * @param {string[]|string[][]} items
299
+ * @param {object|null} quoted
300
+ * @param {object} options - { headerText?, footer? }
301
+ */
302
+ const generateListContent = (title, items, quoted, options = {}) => {
303
+ const { footer, headerText } = options
304
+ const tableRows = items.map((item) => ({
305
+ items: Array.isArray(item) ? item.map(String) : [String(item)]
306
+ }))
307
+
308
+ const submessages = []
309
+ if (headerText) {
310
+ submessages.push({ messageType: 2, messageText: headerText })
311
+ }
312
+ submessages.push({ messageType: 4, tableMetadata: { title, rows: tableRows } })
313
+ if (footer) {
314
+ submessages.push({ messageType: 2, messageText: footer })
315
+ }
316
+
317
+ const ctxInfo = buildRichContextInfo(quoted)
318
+ return {
319
+ message: buildBotForwardedMessage(submessages, ctxInfo),
320
+ messageId: generateMessageID()
321
+ }
322
+ }
323
+
324
+ /**
325
+ * Generate a rich code-block message with syntax highlighting.
326
+ * @param {string} code
327
+ * @param {object|null} quoted
328
+ * @param {object} options - { title?, language?, footer? }
329
+ */
330
+ const generateCodeBlockContent = (code, quoted, options = {}) => {
331
+ const { title, footer, language = 'javascript' } = options
332
+ const submessages = []
333
+
334
+ if (title) {
335
+ submessages.push({ messageType: 2, messageText: title })
336
+ }
337
+ submessages.push({
338
+ messageType: 5,
339
+ codeMetadata: {
340
+ codeLanguage: language,
341
+ codeBlocks: tokenizeCode(code, language)
342
+ }
343
+ })
344
+ if (footer) {
345
+ submessages.push({ messageType: 2, messageText: footer })
346
+ }
347
+
348
+ const ctxInfo = buildRichContextInfo(quoted)
349
+ return {
350
+ message: buildBotForwardedMessage(submessages, ctxInfo),
351
+ messageId: generateMessageID()
352
+ }
353
+ }
354
+
355
+ /**
356
+ * Generate a LaTeX expression message (text only, no image rendering).
357
+ * @param {object|null} quoted
358
+ * @param {object} options - { text?, expressions, headerText?, footer? }
359
+ */
360
+ const generateLatexContent = (quoted, options) => {
361
+ const { text, expressions, headerText, footer } = options
362
+ const submessages = []
363
+
364
+ if (headerText) {
365
+ submessages.push({ messageType: 2, messageText: headerText })
366
+ }
367
+
368
+ const latexExpressions = expressions.map((expr) => {
369
+ const entry = {
370
+ latexExpression: expr.latexExpression,
371
+ url: expr.url,
372
+ width: expr.width,
373
+ height: expr.height
374
+ }
375
+ if (expr.fontHeight !== undefined) entry.fontHeight = expr.fontHeight
376
+ if (expr.imageTopPadding !== undefined) entry.imageTopPadding = expr.imageTopPadding
377
+ if (expr.imageLeadingPadding !== undefined)
378
+ entry.imageLeadingPadding = expr.imageLeadingPadding
379
+ if (expr.imageBottomPadding !== undefined)
380
+ entry.imageBottomPadding = expr.imageBottomPadding
381
+ if (expr.imageTrailingPadding !== undefined)
382
+ entry.imageTrailingPadding = expr.imageTrailingPadding
383
+ return entry
384
+ })
385
+
386
+ submessages.push({
387
+ messageType: 8,
388
+ latexMetadata: { text: text || '', expressions: latexExpressions }
389
+ })
390
+ if (footer) {
391
+ submessages.push({ messageType: 2, messageText: footer })
392
+ }
393
+
394
+ const ctxInfo = buildRichContextInfo(quoted)
395
+ return {
396
+ message: buildBotForwardedMessage(submessages, ctxInfo),
397
+ messageId: generateMessageID()
398
+ }
399
+ }
400
+
401
+ /**
402
+ * Render LaTeX expressions to images, upload them, and build a latex message.
403
+ * @param {object|null} quoted
404
+ * @param {object} optionsn - { text?, expressions, headerText?, footer? }
405
+ * @param {Function} uploadFn - async (buffer, type) => { url, directPath }
406
+ * @param {Function} renderLatexToPng - async (latexExpr) => { buffer, width, height }
407
+ */
408
+ const generateLatexImageContent = async (quoted, options, uploadFn, renderLatexToPng) => {
409
+ const { text, expressions, headerText, footer } = options
410
+ const submessages = []
411
+
412
+ if (headerText) {
413
+ submessages.push({ messageType: 2, messageText: headerText })
414
+ }
415
+
416
+ const latexExpressions = await Promise.all(
417
+ expressions.map(async (expr) => {
418
+ const { buffer, width, height } = await renderLatexToPng(expr.latexExpression)
419
+ const uploadResult = await uploadFn(buffer, 'image')
420
+ const imageUrl = uploadResult.url || uploadResult.directPath
421
+ return { latexExpression: expr.latexExpression, url: imageUrl, width, height }
422
+ })
423
+ )
424
+
425
+ submessages.push({
426
+ messageType: 8,
427
+ latexMetadata: { text: text || '', expressions: latexExpressions }
428
+ })
429
+ if (footer) {
430
+ submessages.push({ messageType: 2, messageText: footer })
431
+ }
432
+
433
+ const ctxInfo = buildRichContextInfo(quoted)
434
+ return {
435
+ message: buildBotForwardedMessage(submessages, ctxInfo),
436
+ messageId: generateMessageID()
437
+ }
438
+ }
439
+
440
+ /**
441
+ * Render each LaTeX expression as an inline image block.
442
+ * @param {object|null} quoted
443
+ * @param {object} options - { text?, expressions, headerText?, footer? }
444
+ * @param {Function} uploadFn - async (buffer, type) => { url, directPath }
445
+ * @param {Function} renderLatexToPng - async (latexExpr) => { buffer, width, height }
446
+ */
447
+ const generateLatexInlineImageContent = async (quoted, options, uploadFn, renderLatexToPng) => {
448
+ const { text, expressions, headerText, footer } = options
449
+ const submessages = []
450
+
451
+ if (headerText) {
452
+ submessages.push({ messageType: 2, messageText: headerText })
453
+ }
454
+ if (text) {
455
+ submessages.push({ messageType: 2, messageText: text })
456
+ }
457
+
458
+ for (const expr of expressions) {
459
+ const { buffer, width, height } = await renderLatexToPng(expr.latexExpression)
460
+ const uploadResult = await uploadFn(buffer, 'image')
461
+ const imageUrl = uploadResult.url || uploadResult.directPath
462
+ submessages.push({
463
+ messageType: 3,
464
+ imageMetadata: {
465
+ imageUrl: {
466
+ imagePreviewUrl: imageUrl,
467
+ imageHighResUrl: imageUrl
468
+ },
469
+ imageText: expr.latexExpression,
470
+ alignment: 2
471
+ }
472
+ })
473
+ }
474
+
475
+ if (footer) {
476
+ submessages.push({ messageType: 2, messageText: footer })
477
+ }
478
+
479
+ const ctxInfo = buildRichContextInfo(quoted)
480
+ return {
481
+ message: buildBotForwardedMessage(submessages, ctxInfo),
482
+ messageId: generateMessageID()
483
+ }
484
+ }
485
+
486
+ /**
487
+ * Generate a rich markdown message.
488
+ * @param {string} text
489
+ * @param {object|null} quoted
490
+ * @param {object} options - { botJid?, mentions? }
491
+ */
492
+ const generateMarkdownContent = (text, quoted, options = {}) => {
493
+ const crypto = require('crypto')
494
+ const submessages = [
495
+ {
496
+ messageType: 2,
497
+ messageText: text
498
+ }
499
+ ]
500
+
501
+ const sections = submessages
502
+ .map((sm) => {
503
+ if (sm.messageType === 2) {
504
+ return {
505
+ view_model: {
506
+ primitive: {
507
+ text: sm.messageText,
508
+ __typename: 'GenAIMarkdownTextUXPrimitive'
509
+ },
510
+ __typename: 'GenAISingleLayoutViewModel'
511
+ }
512
+ }
513
+ }
514
+ return null
515
+ })
516
+ .filter(Boolean)
517
+
518
+ const unifiedResponse = {
519
+ data: Buffer.from(
520
+ JSON.stringify({
521
+ response_id: crypto.randomUUID(),
522
+ sections
523
+ })
524
+ ).toString('base64')
525
+ }
526
+
527
+ const ctxInfo = buildRichContextInfo(quoted, options)
528
+ return {
529
+ message: buildBotForwardedMessage(submessages, ctxInfo, unifiedResponse),
530
+ messageId: generateMessageID()
531
+ }
532
+ }
533
+
534
+ /**
535
+ * Capture the unifiedResponse payload from an incoming Meta AI botForwardedMessage.
536
+ * Returns null if the message is not a rich response.
537
+ * @param {object} msg - the raw WAMessage.message object
538
+ */
539
+ const captureUnifiedResponse = (msg) => {
540
+ const botFwd = msg?.botForwardedMessage?.message
541
+ if (!botFwd) return null
542
+ const rich = botFwd.richResponseMessage
543
+ if (!rich?.unifiedResponse?.data) return null
544
+ return {
545
+ unifiedResponse: { data: rich.unifiedResponse.data },
546
+ submessages: rich.submessages || [],
547
+ contextInfo: rich.contextInfo || {}
548
+ }
549
+ }
550
+
551
+ /**
552
+ * Re-send a previously captured unified response to a new JID.
553
+ * @param {object|null} quoted
554
+ * @param {object} captured - result of captureUnifiedResponse()
555
+ */
556
+ const generateUnifiedResponseContent = (quoted, captured) => {
557
+ const ctxInfo = buildRichContextInfo(quoted)
558
+ return {
559
+ message: buildBotForwardedMessage(captured.submessages, ctxInfo, captured.unifiedResponse),
560
+ messageId: generateMessageID()
561
+ }
562
+ }
563
+
564
+ /**
565
+ * Build a fully custom rich message from an arbitrary submessages array.
566
+ * @param {Array} submessages
567
+ * @param {object|null} quoted
568
+ * @param {object} options - { botJid?, mentions?, useMarkdown?, unifiedResponse? }
569
+ */
570
+ const generateRichMessageContent = (submessages, quoted, options = {}) => {
571
+ const crypto = require('crypto')
572
+ const ctxInfo = buildRichContextInfo(quoted, options)
573
+
574
+ let unifiedResponse = options.unifiedResponse
575
+ if (options.useMarkdown && !unifiedResponse) {
576
+ const sections = submessages
577
+ .map((sm) => {
578
+ if (sm.messageType === 2) {
579
+ return {
580
+ view_model: {
581
+ primitive: {
582
+ text: sm.messageText,
583
+ __typename: 'GenAIMarkdownTextUXPrimitive'
584
+ },
585
+ __typename: 'GenAISingleLayoutViewModel'
586
+ }
587
+ }
588
+ }
589
+ if (sm.messageType === 4 && sm.tableMetadata) {
590
+ return {
591
+ view_model: {
592
+ primitive: {
593
+ rows: sm.tableMetadata.rows.map((r) => ({
594
+ is_header: !!r.isHeading,
595
+ cells: r.items
596
+ })),
597
+ __typename: 'GenATableUXPrimitive'
598
+ },
599
+ __typename: 'GenAISingleLayoutViewModel'
600
+ }
601
+ }
602
+ }
603
+ if (sm.messageType === 5 && sm.codeMetadata) {
604
+ return {
605
+ view_model: {
606
+ primitive: {
607
+ language: sm.codeMetadata.codeLanguage || 'javascript',
608
+ code_blocks: sm.codeMetadata.codeBlocks.map((cb) => ({
609
+ content: cb.codeContent,
610
+ type: 'DEFAULT'
611
+ })),
612
+ __typename: 'GenAICodeUXPrimitive'
613
+ },
614
+ __typename: 'GenAISingleLayoutViewModel'
615
+ }
616
+ }
617
+ }
618
+ if (sm.messageType === 3 && sm.imageMetadata) {
619
+ return {
620
+ view_model: {
621
+ primitive: {
622
+ media: {
623
+ url:
624
+ sm.imageMetadata.imageUrl?.imageHighResUrl ||
625
+ sm.imageMetadata.imageUrl?.imagePreviewUrl,
626
+ mime_type: 'image/png'
627
+ },
628
+ imagine_type: 'IMAGE',
629
+ status: { status: 'READY' },
630
+ __typename: 'GenAIImaginePrimitive'
631
+ },
632
+ __typename: 'GenAISingleLayoutViewModel'
633
+ }
634
+ }
635
+ }
636
+ return null
637
+ })
638
+ .filter(Boolean)
639
+
640
+ if (sections.length > 0) {
641
+ unifiedResponse = {
642
+ data: Buffer.from(
643
+ JSON.stringify({
644
+ response_id: crypto.randomUUID(),
645
+ sections
646
+ })
647
+ ).toString('base64')
648
+ }
649
+ }
650
+ }
651
+
652
+ return {
653
+ message: buildBotForwardedMessage(submessages, ctxInfo, unifiedResponse),
654
+ messageId: generateMessageID()
655
+ }
656
+ }
657
+
658
+ /**
659
+ * Render LaTeX to PNG image using online codecogs API.
660
+ * @param {string} latexExpr
661
+ */
662
+ async function renderLatexToPng(latexExpr) {
663
+ const encoded = encodeURIComponent(latexExpr)
664
+ const url = `https://latex.codecogs.com/png.image?%5Cdpi%7B1200%7D%5Cbg%7Bwhite%7D${encoded}`
665
+ const res = await fetch(url)
666
+ if (!res.ok) throw new Error(`[renderLatexToPng] HTTP ${res.status}`)
667
+ const buffer = Buffer.from(await res.arrayBuffer())
668
+ return { buffer, width: 1200, height: 600 }
669
+ }
670
+
671
+ /**
672
+ * Helper to upload an unencrypted buffer to WhatsApp CDN.
673
+ * @param {Buffer} buffer
674
+ * @param {Function} waUploadToServer
675
+ */
676
+ async function uploadUnencryptedToWA(buffer, waUploadToServer) {
677
+ const crypto = require('crypto')
678
+ const fs = require('fs')
679
+ const path = require('path')
680
+ const os = require('os')
681
+ const sha256B64 = crypto.createHash('sha256').update(buffer).digest('base64')
682
+ const tmpPath = path.join(os.tmpdir(), `wa_upload_${Date.now()}.png`)
683
+ await fs.promises.writeFile(tmpPath, buffer)
684
+ try {
685
+ const result = await waUploadToServer(tmpPath, {
686
+ mediaType: 'image',
687
+ fileEncSha256B64: sha256B64
688
+ })
689
+ return {
690
+ url: result.mediaUrl || getUrlFromDirectPath(result.directPath),
691
+ directPath: result.directPath
692
+ }
693
+ } finally {
694
+ try {
695
+ await fs.promises.unlink(tmpPath)
696
+ } catch (_) {}
697
+ }
698
+ }
699
+
700
+ module.exports = {
701
+ JS_KEYWORDS,
702
+ PYTHON_KEYWORDS,
703
+ LANGUAGE_KEYWORDS,
704
+ CodeHighlightType,
705
+ RichSubMessageType,
706
+ tokenizeCode,
707
+ buildRichContextInfo,
708
+ buildBotForwardedMessage,
709
+ generateTableContent,
710
+ generateListContent,
711
+ generateCodeBlockContent,
712
+ generateLatexContent,
713
+ generateLatexImageContent,
714
+ generateLatexInlineImageContent,
715
+ generateMarkdownContent,
716
+ captureUnifiedResponse,
717
+ generateUnifiedResponseContent,
718
+ generateRichMessageContent,
719
+ renderLatexToPng,
720
+ uploadUnencryptedToWA
721
+ }