@bellaxchuu/flowred 0.8.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 (142) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +214 -0
  3. package/WAProto/GenerateStatics.sh +3 -0
  4. package/WAProto/WAProto.proto +6902 -0
  5. package/WAProto/fix-imports.js +85 -0
  6. package/WAProto/index.js +242946 -0
  7. package/engine-requirements.js +10 -0
  8. package/lib/Defaults/index.js +190 -0
  9. package/lib/Defaults/phonenumber-mcc.json +223 -0
  10. package/lib/Signal/Group/ciphertext-message.js +15 -0
  11. package/lib/Signal/Group/group-session-builder.js +92 -0
  12. package/lib/Signal/Group/group_cipher.js +89 -0
  13. package/lib/Signal/Group/index.js +136 -0
  14. package/lib/Signal/Group/keyhelper.js +73 -0
  15. package/lib/Signal/Group/sender-chain-key.js +32 -0
  16. package/lib/Signal/Group/sender-key-distribution-message.js +66 -0
  17. package/lib/Signal/Group/sender-key-message.js +69 -0
  18. package/lib/Signal/Group/sender-key-name.js +50 -0
  19. package/lib/Signal/Group/sender-key-record.js +44 -0
  20. package/lib/Signal/Group/sender-key-state.js +97 -0
  21. package/lib/Signal/Group/sender-message-key.js +30 -0
  22. package/lib/Signal/libsignal.js +490 -0
  23. package/lib/Signal/lid-mapping.js +262 -0
  24. package/lib/Socket/Client/index.js +30 -0
  25. package/lib/Socket/Client/types.js +13 -0
  26. package/lib/Socket/Client/websocket.js +62 -0
  27. package/lib/Socket/aigroups.js +240 -0
  28. package/lib/Socket/business.js +422 -0
  29. package/lib/Socket/chats.js +1816 -0
  30. package/lib/Socket/communities.js +549 -0
  31. package/lib/Socket/graphql.js +656 -0
  32. package/lib/Socket/groups.js +764 -0
  33. package/lib/Socket/index.js +39 -0
  34. package/lib/Socket/interactive-handler.js +522 -0
  35. package/lib/Socket/interop.js +549 -0
  36. package/lib/Socket/luxu.js +387 -0
  37. package/lib/Socket/managed-account.js +183 -0
  38. package/lib/Socket/messages-recv.js +2440 -0
  39. package/lib/Socket/messages-send.js +1853 -0
  40. package/lib/Socket/mex.js +60 -0
  41. package/lib/Socket/newsletter.js +820 -0
  42. package/lib/Socket/privacy.js +449 -0
  43. package/lib/Socket/registration.js +427 -0
  44. package/lib/Socket/socket.js +1092 -0
  45. package/lib/Socket/text-router.js +83 -0
  46. package/lib/Socket/username.js +243 -0
  47. package/lib/Store/index.js +36 -0
  48. package/lib/Store/keyed-db.js +108 -0
  49. package/lib/Store/make-cache-manager-store.js +90 -0
  50. package/lib/Store/make-in-memory-store.js +604 -0
  51. package/lib/Store/make-ordered-dictionary.js +81 -0
  52. package/lib/Store/object-repository.js +29 -0
  53. package/lib/Types/Auth.js +38 -0
  54. package/lib/Types/Bussines.js +2 -0
  55. package/lib/Types/Call.js +2 -0
  56. package/lib/Types/Chat.js +4 -0
  57. package/lib/Types/Contact.js +2 -0
  58. package/lib/Types/Events.js +2 -0
  59. package/lib/Types/GroupMetadata.js +2 -0
  60. package/lib/Types/Label.js +27 -0
  61. package/lib/Types/LabelAssociation.js +9 -0
  62. package/lib/Types/Message.js +95 -0
  63. package/lib/Types/Mex.js +112 -0
  64. package/lib/Types/Newsletter.js +121 -0
  65. package/lib/Types/Product.js +2 -0
  66. package/lib/Types/Signal.js +2 -0
  67. package/lib/Types/Socket.js +2 -0
  68. package/lib/Types/State.js +70 -0
  69. package/lib/Types/USync.js +2 -0
  70. package/lib/Types/index.js +55 -0
  71. package/lib/Utils/auth-utils.js +306 -0
  72. package/lib/Utils/browser-utils.js +114 -0
  73. package/lib/Utils/business.js +247 -0
  74. package/lib/Utils/chat-utils.js +1208 -0
  75. package/lib/Utils/command-loader.js +100 -0
  76. package/lib/Utils/companion-reg-client-utils.js +42 -0
  77. package/lib/Utils/consumer-application.js +107 -0
  78. package/lib/Utils/crypto.js +155 -0
  79. package/lib/Utils/curve25519-js.js +275 -0
  80. package/lib/Utils/decode-wa-message.js +560 -0
  81. package/lib/Utils/event-buffer.js +607 -0
  82. package/lib/Utils/generics.js +563 -0
  83. package/lib/Utils/group-history.js +60 -0
  84. package/lib/Utils/history.js +244 -0
  85. package/lib/Utils/identity-change-handler.js +52 -0
  86. package/lib/Utils/index.js +57 -0
  87. package/lib/Utils/jid-display-normalization.js +218 -0
  88. package/lib/Utils/link-preview.js +143 -0
  89. package/lib/Utils/logger.js +9 -0
  90. package/lib/Utils/lt-hash.js +39 -0
  91. package/lib/Utils/make-mutex.js +36 -0
  92. package/lib/Utils/message-composer.js +479 -0
  93. package/lib/Utils/message-retry-manager.js +270 -0
  94. package/lib/Utils/messages-media.js +896 -0
  95. package/lib/Utils/messages.js +2904 -0
  96. package/lib/Utils/meta-ai-msmsg.js +262 -0
  97. package/lib/Utils/native-bridge.js +82 -0
  98. package/lib/Utils/noise-handler.js +196 -0
  99. package/lib/Utils/offline-node-processor.js +42 -0
  100. package/lib/Utils/pre-key-manager.js +107 -0
  101. package/lib/Utils/process-message.js +1048 -0
  102. package/lib/Utils/reporting-utils.js +262 -0
  103. package/lib/Utils/session-pool.js +94 -0
  104. package/lib/Utils/signal.js +224 -0
  105. package/lib/Utils/stanza-ack.js +40 -0
  106. package/lib/Utils/sticker.js +145 -0
  107. package/lib/Utils/sync-action-utils.js +54 -0
  108. package/lib/Utils/tc-token-utils.js +161 -0
  109. package/lib/Utils/use-multi-file-auth-state.js +121 -0
  110. package/lib/Utils/validate-connection.js +219 -0
  111. package/lib/Utils/view-once-cache.js +79 -0
  112. package/lib/Utils/voip-rekey.js +22 -0
  113. package/lib/WABinary/constants.js +1304 -0
  114. package/lib/WABinary/decode.js +343 -0
  115. package/lib/WABinary/encode.js +228 -0
  116. package/lib/WABinary/generic-utils.js +240 -0
  117. package/lib/WABinary/index.js +33 -0
  118. package/lib/WABinary/jid-utils.js +361 -0
  119. package/lib/WABinary/types.js +2 -0
  120. package/lib/WAM/BinaryInfo.js +13 -0
  121. package/lib/WAM/constants.js +39486 -0
  122. package/lib/WAM/encode.js +142 -0
  123. package/lib/WAM/index.js +31 -0
  124. package/lib/WAUSync/Protocols/USyncBotProfileProtocol.js +55 -0
  125. package/lib/WAUSync/Protocols/USyncBusinessProtocol.js +43 -0
  126. package/lib/WAUSync/Protocols/USyncContactProtocol.js +54 -0
  127. package/lib/WAUSync/Protocols/USyncDeviceProtocol.js +57 -0
  128. package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.js +30 -0
  129. package/lib/WAUSync/Protocols/USyncFeatureProtocol.js +59 -0
  130. package/lib/WAUSync/Protocols/USyncLIDProtocol.js +31 -0
  131. package/lib/WAUSync/Protocols/USyncPictureProtocol.js +32 -0
  132. package/lib/WAUSync/Protocols/USyncSidelistProtocol.js +29 -0
  133. package/lib/WAUSync/Protocols/USyncStatusProtocol.js +39 -0
  134. package/lib/WAUSync/Protocols/USyncTextStatusProtocol.js +38 -0
  135. package/lib/WAUSync/Protocols/USyncUsernameProtocol.js +27 -0
  136. package/lib/WAUSync/Protocols/index.js +39 -0
  137. package/lib/WAUSync/USyncQuery.js +133 -0
  138. package/lib/WAUSync/USyncUser.js +50 -0
  139. package/lib/WAUSync/index.js +31 -0
  140. package/lib/antiban.js +4637 -0
  141. package/lib/index.js +45 -0
  142. package/package.json +107 -0
@@ -0,0 +1,2904 @@
1
+ 'use strict'
2
+ Object.defineProperty(exports, '__esModule', { value: true })
3
+ exports.assertMediaContent =
4
+ exports.downloadMediaMessage =
5
+ exports.aggregateMessageKeysNotFromMe =
6
+ exports.updateMessageWithEventResponse =
7
+ exports.updateMessageWithPollUpdate =
8
+ exports.updateMessageWithReaction =
9
+ exports.updateMessageWithReceipt =
10
+ exports.getDevice =
11
+ exports.extractMessageContent =
12
+ exports.normalizeMessageContent =
13
+ exports.getContentType =
14
+ exports.generateWAMessage =
15
+ exports.generateWAMessageFromContent =
16
+ exports.generateWAMessageContent =
17
+ exports.hasNonNullishProperty =
18
+ exports.generateForwardMessageContent =
19
+ exports.prepareDisappearingMessageSettingContent =
20
+ exports.prepareWAMessageMedia =
21
+ exports.generateLinkPreviewIfRequired =
22
+ exports.extractUrlFromText =
23
+ void 0
24
+ exports.getAggregateVotesInPollMessage = getAggregateVotesInPollMessage
25
+ exports.getAggregateResponsesInEventMessage = getAggregateResponsesInEventMessage
26
+ const boom_1 = require('@hapi/boom')
27
+ const crypto_1 = require('crypto')
28
+ const fs_1 = require('fs')
29
+ const index_js_1 = require('../../WAProto/index.js')
30
+ const WAProto_1 = require('../../WAProto/index.js')
31
+ const Defaults_1 = require('../Defaults')
32
+ const Types_1 = require('../Types')
33
+ const WABinary_1 = require('../WABinary')
34
+ const crypto_2 = require('./crypto')
35
+ const generics_1 = require('./generics')
36
+ const messages_media_1 = require('./messages-media')
37
+ const reporting_utils_1 = require('./reporting-utils')
38
+ const jid_display_normalization_1 = require('./jid-display-normalization')
39
+ const MIMETYPE_MAP = {
40
+ image: 'image/jpeg',
41
+ video: 'video/mp4',
42
+ document: 'application/pdf',
43
+ audio: 'audio/ogg; codecs=opus',
44
+ sticker: 'image/webp',
45
+ 'product-catalog-image': 'image/jpeg'
46
+ }
47
+ const MessageTypeProto = {
48
+ image: WAProto_1.proto.Message.ImageMessage,
49
+ video: WAProto_1.proto.Message.VideoMessage,
50
+ audio: WAProto_1.proto.Message.AudioMessage,
51
+ sticker: WAProto_1.proto.Message.StickerMessage,
52
+ document: WAProto_1.proto.Message.DocumentMessage
53
+ }
54
+
55
+ // Input payloads can carry protobuf media message keys (e.g. imageMessage).
56
+ const MEDIA_MESSAGE_TYPE_ALIASES = {
57
+ imageMessage: 'image',
58
+ videoMessage: 'video',
59
+ audioMessage: 'audio',
60
+ documentMessage: 'document',
61
+ stickerMessage: 'sticker',
62
+ // PTV = push-to-video (video note) payload.
63
+ ptvMessage: 'video'
64
+ }
65
+ const MEDIA_MESSAGE_TYPE_ALIAS_KEYS = Object.keys(MEDIA_MESSAGE_TYPE_ALIASES)
66
+ const hasMediaPayload = message =>
67
+ Defaults_1.MEDIA_KEYS.some(key => key in message) || MEDIA_MESSAGE_TYPE_ALIAS_KEYS.some(key => key in message)
68
+
69
+ const ButtonType = WAProto_1.proto.Message.ButtonsMessage.HeaderType
70
+
71
+ const RICH_RESPONSE_CODE_KEYWORDS = new Set([
72
+ 'break',
73
+ 'case',
74
+ 'catch',
75
+ 'continue',
76
+ 'debugger',
77
+ 'default',
78
+ 'delete',
79
+ 'do',
80
+ 'else',
81
+ 'finally',
82
+ 'for',
83
+ 'function',
84
+ 'if',
85
+ 'in',
86
+ 'instanceof',
87
+ 'new',
88
+ 'return',
89
+ 'switch',
90
+ 'this',
91
+ 'throw',
92
+ 'try',
93
+ 'typeof',
94
+ 'var',
95
+ 'void',
96
+ 'while',
97
+ 'with',
98
+ 'true',
99
+ 'false',
100
+ 'null',
101
+ 'undefined',
102
+ 'NaN',
103
+ 'Infinity',
104
+ 'class',
105
+ 'const',
106
+ 'let',
107
+ 'super',
108
+ 'extends',
109
+ 'export',
110
+ 'import',
111
+ 'yield',
112
+ 'static',
113
+ 'constructor',
114
+ 'of',
115
+ 'async',
116
+ 'await',
117
+ 'get',
118
+ 'set',
119
+ 'implements',
120
+ 'interface',
121
+ 'package',
122
+ 'private',
123
+ 'protected',
124
+ 'public',
125
+ 'enum',
126
+ 'throws',
127
+ 'transient'
128
+ ])
129
+ const tokenizeCode = code => {
130
+ const tokens = []
131
+ let i = 0
132
+ const len = code.length
133
+ while (i < len) {
134
+ if (/\s/.test(code[i])) {
135
+ const start = i
136
+ while (i < len && /\s/.test(code[i])) i++
137
+ tokens.push({ content: code.slice(start, i), type: 'DEFAULT' })
138
+ continue
139
+ }
140
+ if (code[i] === '"' || code[i] === "'" || code[i] === '`') {
141
+ const start = i
142
+ const quote = code[i]
143
+ i++
144
+ while (i < len && code[i] !== quote) {
145
+ if (code[i] === '\\') i++
146
+ i++
147
+ }
148
+ i++
149
+ tokens.push({ content: code.slice(start, i), type: 'STR' })
150
+ continue
151
+ }
152
+ if (code[i] === '/' && i + 1 < len && code[i + 1] === '/') {
153
+ const start = i
154
+ while (i < len && code[i] !== '\n') i++
155
+ tokens.push({ content: code.slice(start, i), type: 'COMMENT' })
156
+ continue
157
+ }
158
+ if (code[i] === '/' && i + 1 < len && code[i + 1] === '*') {
159
+ const start = i
160
+ i += 2
161
+ while (i + 1 < len && !(code[i] === '*' && code[i + 1] === '/')) i++
162
+ i += 2
163
+ tokens.push({ content: code.slice(start, i), type: 'COMMENT' })
164
+ continue
165
+ }
166
+ if (/[0-9]/.test(code[i])) {
167
+ const start = i
168
+ while (i < len && /[0-9.]/.test(code[i])) i++
169
+ tokens.push({ content: code.slice(start, i), type: 'NUMBER' })
170
+ continue
171
+ }
172
+ if (/[a-zA-Z_$]/.test(code[i])) {
173
+ const start = i
174
+ while (i < len && /[a-zA-Z0-9_$]/.test(code[i])) i++
175
+ const word = code.slice(start, i)
176
+ if (RICH_RESPONSE_CODE_KEYWORDS.has(word)) {
177
+ tokens.push({ content: word, type: 'KEYWORD' })
178
+ } else {
179
+ let j = i
180
+ while (j < len && /\s/.test(code[j])) j++
181
+ tokens.push({ content: word, type: j < len && code[j] === '(' ? 'METHOD' : 'DEFAULT' })
182
+ }
183
+ continue
184
+ }
185
+ tokens.push({ content: code[i], type: 'DEFAULT' })
186
+ i++
187
+ }
188
+ const merged = []
189
+ for (const t of tokens) {
190
+ if (merged.length && merged[merged.length - 1].type === 'DEFAULT' && t.type === 'DEFAULT') {
191
+ merged[merged.length - 1].content += t.content
192
+ } else {
193
+ merged.push(t)
194
+ }
195
+ }
196
+ return merged
197
+ }
198
+
199
+ /**
200
+ * Uses a regex to test whether the string contains a URL, and returns the URL if it does.
201
+ * @param text eg. hello https://google.com
202
+ * @returns the URL, eg. https://google.com
203
+ */
204
+ const extractUrlFromText = text => text.match(Defaults_1.URL_REGEX)?.[0]
205
+ exports.extractUrlFromText = extractUrlFromText
206
+ const generateLinkPreviewIfRequired = async (text, getUrlInfo, logger) => {
207
+ const url = (0, exports.extractUrlFromText)(text)
208
+ if (!!getUrlInfo && url) {
209
+ try {
210
+ const urlInfo = await getUrlInfo(url)
211
+ return urlInfo
212
+ } catch (error) {
213
+ // ignore if fails
214
+ logger?.warn({ trace: error.stack }, 'url generation failed')
215
+ }
216
+ }
217
+ }
218
+ exports.generateLinkPreviewIfRequired = generateLinkPreviewIfRequired
219
+ const assertColor = async color => {
220
+ let assertedColor
221
+ if (typeof color === 'number') {
222
+ assertedColor = color > 0 ? color : 0xffffffff + Number(color) + 1
223
+ } else {
224
+ let hex = color.trim().replace('#', '')
225
+ if (hex.length <= 6) {
226
+ hex = 'FF' + hex.padStart(6, '0')
227
+ }
228
+ assertedColor = parseInt(hex, 16)
229
+ return assertedColor
230
+ }
231
+ }
232
+ const prepareWAMessageMedia = async (message, options) => {
233
+ const logger = options.logger
234
+ let mediaType
235
+ for (const key of Defaults_1.MEDIA_KEYS) {
236
+ if (key in message) {
237
+ mediaType = key
238
+ }
239
+ }
240
+ if (!mediaType) {
241
+ throw new boom_1.Boom('Invalid media type', { statusCode: 400 })
242
+ }
243
+ const uploadData = {
244
+ ...message,
245
+ media: message[mediaType]
246
+ }
247
+ delete uploadData[mediaType]
248
+ // check if cacheable + generate cache key
249
+ const cacheableKey =
250
+ typeof uploadData.media === 'object' &&
251
+ 'url' in uploadData.media &&
252
+ !!uploadData.media.url &&
253
+ !!options.mediaCache &&
254
+ mediaType + ':' + uploadData.media.url.toString()
255
+ if (mediaType === 'document' && !uploadData.fileName) {
256
+ uploadData.fileName = 'file'
257
+ }
258
+ if (!uploadData.mimetype) {
259
+ uploadData.mimetype = MIMETYPE_MAP[mediaType]
260
+ }
261
+ if (cacheableKey) {
262
+ const mediaBuff = await options.mediaCache.get(cacheableKey)
263
+ if (mediaBuff) {
264
+ logger?.debug({ cacheableKey }, 'got media cache hit')
265
+ const obj = index_js_1.proto.Message.decode(mediaBuff)
266
+ const key = `${mediaType}Message`
267
+ Object.assign(obj[key], { ...uploadData, media: undefined })
268
+ return obj
269
+ }
270
+ }
271
+ const isNewsletter = !!options.jid && (0, WABinary_1.isJidNewsletter)(options.jid)
272
+ if (isNewsletter) {
273
+ logger?.info({ key: cacheableKey }, 'Preparing raw media for newsletter')
274
+ const { filePath, fileSha256, fileLength } = await (0, messages_media_1.getRawMediaUploadData)(
275
+ uploadData.media,
276
+ options.mediaTypeOverride || mediaType,
277
+ logger
278
+ )
279
+ const fileSha256B64 = fileSha256.toString('base64')
280
+ const { mediaUrl, directPath } = await options.upload(filePath, {
281
+ fileEncSha256B64: fileSha256B64,
282
+ mediaType: mediaType,
283
+ timeoutMs: options.mediaUploadTimeoutMs
284
+ })
285
+ await fs_1.promises.unlink(filePath)
286
+ const obj = WAProto_1.proto.Message.fromObject({
287
+ // todo: add more support here
288
+ [`${mediaType}Message`]: MessageTypeProto[mediaType].fromObject({
289
+ url: mediaUrl,
290
+ directPath,
291
+ fileSha256,
292
+ fileLength,
293
+ ...uploadData,
294
+ media: undefined
295
+ })
296
+ })
297
+ if (uploadData.ptv) {
298
+ obj.ptvMessage = obj.videoMessage
299
+ delete obj.videoMessage
300
+ }
301
+ if (obj.stickerMessage) {
302
+ obj.stickerMessage.stickerSentTs = Date.now()
303
+ }
304
+ if (cacheableKey) {
305
+ logger?.debug({ cacheableKey }, 'set cache')
306
+ await options.mediaCache.set(cacheableKey, WAProto_1.proto.Message.encode(obj).finish())
307
+ }
308
+ return obj
309
+ }
310
+ const requiresDurationComputation = mediaType === 'audio' && typeof uploadData.seconds === 'undefined'
311
+ const requiresThumbnailComputation =
312
+ (mediaType === 'image' || mediaType === 'video') && typeof uploadData['jpegThumbnail'] === 'undefined'
313
+
314
+ const requiresWaveformProcessing =
315
+ mediaType === 'audio' && uploadData.ptt === true && typeof uploadData.waveform === 'undefined'
316
+ const requiresAudioBackground = options.backgroundColor && mediaType === 'audio' && uploadData.ptt === true
317
+ const requiresOriginalForSomeProcessing = requiresDurationComputation || requiresThumbnailComputation
318
+ const { mediaKey, encFilePath, originalFilePath, fileEncSha256, fileSha256, fileLength } = await (0,
319
+ messages_media_1.encryptedStream)(uploadData.media, options.mediaTypeOverride || mediaType, {
320
+ logger,
321
+ saveOriginalFileIfRequired: requiresOriginalForSomeProcessing,
322
+ opts: options.options
323
+ })
324
+ const fileEncSha256B64 = fileEncSha256.toString('base64')
325
+ const [{ mediaUrl, directPath }] = await Promise.all([
326
+ (async () => {
327
+ const result = await options.upload(encFilePath, {
328
+ fileEncSha256B64,
329
+ mediaType,
330
+ timeoutMs: options.mediaUploadTimeoutMs
331
+ })
332
+ logger?.debug({ mediaType, cacheableKey }, 'uploaded media')
333
+ return result
334
+ })(),
335
+ (async () => {
336
+ try {
337
+ if (requiresThumbnailComputation) {
338
+ const { thumbnail, originalImageDimensions } = await (0, messages_media_1.generateThumbnail)(
339
+ originalFilePath,
340
+ mediaType,
341
+ options
342
+ )
343
+ uploadData.jpegThumbnail = thumbnail
344
+ if (!uploadData.width && originalImageDimensions) {
345
+ uploadData.width = originalImageDimensions.width
346
+ uploadData.height = originalImageDimensions.height
347
+ logger?.debug('set dimensions')
348
+ }
349
+ logger?.debug('generated thumbnail')
350
+ }
351
+ if (requiresDurationComputation) {
352
+ uploadData.seconds = await (0, messages_media_1.getAudioDuration)(originalFilePath)
353
+ logger?.debug('computed audio duration')
354
+ }
355
+ if (requiresWaveformProcessing) {
356
+ uploadData.waveform = await (0, messages_media_1.getAudioWaveform)(originalFilePath, logger)
357
+ logger?.debug('processed waveform')
358
+ }
359
+ if (requiresAudioBackground) {
360
+ uploadData.backgroundArgb = await assertColor(options.backgroundColor)
361
+ logger?.debug('computed backgroundColor audio status')
362
+ }
363
+ } catch (error) {
364
+ logger?.warn({ trace: error.stack }, 'failed to obtain extra info')
365
+ }
366
+ })()
367
+ ]).finally(async () => {
368
+ try {
369
+ await fs_1.promises.unlink(encFilePath)
370
+ if (originalFilePath) {
371
+ await fs_1.promises.unlink(originalFilePath)
372
+ }
373
+ logger?.debug('removed tmp files')
374
+ } catch (error) {
375
+ logger?.warn('failed to remove tmp file')
376
+ }
377
+ })
378
+ const obj = WAProto_1.proto.Message.fromObject({
379
+ [`${mediaType}Message`]: MessageTypeProto[mediaType].fromObject({
380
+ url: mediaUrl,
381
+ directPath,
382
+ mediaKey,
383
+ fileEncSha256,
384
+ fileSha256,
385
+ fileLength,
386
+ mediaKeyTimestamp: (0, generics_1.unixTimestampSeconds)(),
387
+ ...uploadData,
388
+ media: undefined
389
+ })
390
+ })
391
+ if (uploadData.ptv) {
392
+ obj.ptvMessage = obj.videoMessage
393
+ delete obj.videoMessage
394
+ }
395
+ if (cacheableKey) {
396
+ logger?.debug({ cacheableKey }, 'set cache')
397
+ await options.mediaCache.set(cacheableKey, WAProto_1.proto.Message.encode(obj).finish())
398
+ }
399
+ return obj
400
+ }
401
+ exports.prepareWAMessageMedia = prepareWAMessageMedia
402
+ const prepareDisappearingMessageSettingContent = ephemeralExpiration => {
403
+ ephemeralExpiration = ephemeralExpiration || 0
404
+ const content = {
405
+ ephemeralMessage: {
406
+ message: {
407
+ protocolMessage: {
408
+ type: WAProto_1.proto.Message.ProtocolMessage.Type.EPHEMERAL_SETTING,
409
+ ephemeralExpiration
410
+ }
411
+ }
412
+ }
413
+ }
414
+ return WAProto_1.proto.Message.fromObject(content)
415
+ }
416
+ exports.prepareDisappearingMessageSettingContent = prepareDisappearingMessageSettingContent
417
+ /**
418
+ * Generate forwarded message content like WA does
419
+ * @param message the message to forward
420
+ * @param options.forceForward will show the message as forwarded even if it is from you
421
+ */
422
+ const generateForwardMessageContent = (message, forceForward) => {
423
+ let content = message.message
424
+ if (!content) {
425
+ throw new boom_1.Boom('no content in message', { statusCode: 400 })
426
+ }
427
+ // hacky copy
428
+ content = (0, exports.normalizeMessageContent)(content)
429
+ content = index_js_1.proto.Message.decode(index_js_1.proto.Message.encode(content).finish())
430
+ let key = Object.keys(content)[0]
431
+ let score = content?.[key]?.contextInfo?.forwardingScore || 0
432
+ score += message.key.fromMe && !forceForward ? 0 : 1
433
+ if (key === 'conversation') {
434
+ content.extendedTextMessage = { text: content[key] }
435
+ delete content.conversation
436
+ key = 'extendedTextMessage'
437
+ }
438
+ const key_ = content?.[key]
439
+ if (score > 0) {
440
+ key_.contextInfo = { forwardingScore: score, isForwarded: true }
441
+ } else {
442
+ key_.contextInfo = {}
443
+ }
444
+ return content
445
+ }
446
+ exports.generateForwardMessageContent = generateForwardMessageContent
447
+ const hasNonNullishProperty = (message, key) => {
448
+ return (
449
+ typeof message === 'object' &&
450
+ message !== null &&
451
+ key in message &&
452
+ message[key] !== null &&
453
+ message[key] !== undefined
454
+ )
455
+ }
456
+ exports.hasNonNullishProperty = hasNonNullishProperty
457
+ function hasOptionalProperty(obj, key) {
458
+ return typeof obj === 'object' && obj !== null && key in obj && obj[key] !== null
459
+ }
460
+ const normalizeEarFields = ear => {
461
+ const result = { ...ear }
462
+ const applyAlias = (fromKey, toKey) => {
463
+ if (result[fromKey] !== undefined && result[toKey] === undefined) result[toKey] = result[fromKey]
464
+ }
465
+ applyAlias('thumbnail_url', 'thumbnailUrl')
466
+ applyAlias('thumbnailUrl', 'thumbnail')
467
+ applyAlias('source_url', 'sourceUrl')
468
+ applyAlias('media_type', 'mediaType')
469
+ applyAlias('show_ad_attribution', 'showAdAttribution')
470
+ applyAlias('render_larger_thumbnail', 'renderLargerThumbnail')
471
+ if (result.thumbnail && !result.jpegThumbnail) result.jpegThumbnail = result.thumbnail
472
+ if (result.largeThumbnail !== undefined && result.renderLargerThumbnail === undefined)
473
+ result.renderLargerThumbnail = result.largeThumbnail
474
+ if (result.url && !result.sourceUrl) result.sourceUrl = result.url
475
+ delete result.thumbnail
476
+ delete result.largeThumbnail
477
+ delete result.url
478
+ delete result.thumbnail_url
479
+ delete result.source_url
480
+ delete result.media_type
481
+ delete result.show_ad_attribution
482
+ delete result.render_larger_thumbnail
483
+ return result
484
+ }
485
+ const normalizeQuickReplyButton = button => {
486
+ var _a
487
+ if (button.name && typeof button.name === 'string') {
488
+ return {
489
+ name: button.name,
490
+ buttonParamsJson:
491
+ typeof button.buttonParamsJson === 'string'
492
+ ? button.buttonParamsJson
493
+ : JSON.stringify(button.buttonParamsJson || {})
494
+ }
495
+ }
496
+ if (button.type === 4 && button.nativeFlowInfo) {
497
+ const { name, paramsJson } = button.nativeFlowInfo
498
+ return {
499
+ name: name || 'quick_reply',
500
+ buttonParamsJson: typeof paramsJson === 'string' ? paramsJson : JSON.stringify(paramsJson || {})
501
+ }
502
+ }
503
+ const buttonTextObject = button.buttonText && typeof button.buttonText === 'object' ? button.buttonText : undefined
504
+ const displayTextCandidates = [
505
+ button.text,
506
+ button.displayText,
507
+ button.display_text,
508
+ typeof button.buttonText === 'string' ? button.buttonText : undefined,
509
+ buttonTextObject === null || buttonTextObject === void 0 ? void 0 : buttonTextObject.displayText,
510
+ buttonTextObject === null || buttonTextObject === void 0 ? void 0 : buttonTextObject.display_text
511
+ ]
512
+ const displayText = displayTextCandidates.find(value => typeof value === 'string' && value.length > 0) || ''
513
+ const id =
514
+ button.buttonId || button.id || ((_a = button.buttonParamsJson) === null || _a === void 0 ? void 0 : _a.id) || ''
515
+ return {
516
+ name: 'quick_reply',
517
+ buttonParamsJson: JSON.stringify({
518
+ display_text: displayText,
519
+ id
520
+ })
521
+ }
522
+ }
523
+ const asciiDecode = arr => arr.map(e => String.fromCharCode(e)).join('')
524
+
525
+ const applyContextInfoAndMentions = (interactiveMessage, message) => {
526
+ if ('contextInfo' in message && !!message.contextInfo) {
527
+ interactiveMessage.contextInfo = message.contextInfo
528
+ }
529
+ if ('mentions' in message && !!message.mentions) {
530
+ interactiveMessage.contextInfo = {
531
+ ...(interactiveMessage.contextInfo || {}),
532
+ mentionedJid: message.mentions
533
+ }
534
+ }
535
+ }
536
+ const buildPaymentNoteMessage = async (paymentPayload, options, fallbackText = '') => {
537
+ let notes
538
+ if (paymentPayload === null || paymentPayload === void 0 ? void 0 : paymentPayload.sticker) {
539
+ const stickerPrep = await (0, exports.prepareWAMessageMedia)({ sticker: paymentPayload.sticker }, options)
540
+ notes = {
541
+ stickerMessage: {
542
+ ...(stickerPrep === null || stickerPrep === void 0 ? void 0 : stickerPrep.stickerMessage),
543
+ contextInfo: paymentPayload === null || paymentPayload === void 0 ? void 0 : paymentPayload.contextInfo
544
+ }
545
+ }
546
+ } else if (
547
+ typeof (paymentPayload === null || paymentPayload === void 0 ? void 0 : paymentPayload.note) === 'string'
548
+ ) {
549
+ notes = {
550
+ extendedTextMessage: {
551
+ text: paymentPayload.note,
552
+ contextInfo: paymentPayload === null || paymentPayload === void 0 ? void 0 : paymentPayload.contextInfo
553
+ }
554
+ }
555
+ } else if (
556
+ (paymentPayload === null || paymentPayload === void 0 ? void 0 : paymentPayload.noteMessage) &&
557
+ typeof paymentPayload.noteMessage === 'object'
558
+ ) {
559
+ const noteKeys = Object.keys(paymentPayload.noteMessage)
560
+ const allowedNoteMessageKeys = ['extendedTextMessage', 'stickerMessage']
561
+ const hasOnlyAllowedKeys = noteKeys.length > 0 && noteKeys.every(key => allowedNoteMessageKeys.includes(key))
562
+ if (!noteKeys.length || !hasOnlyAllowedKeys) {
563
+ throw new boom_1.Boom('Invalid payment noteMessage', { statusCode: 400 })
564
+ }
565
+ notes = paymentPayload.noteMessage
566
+ } else {
567
+ notes = { extendedTextMessage: { text: fallbackText } }
568
+ }
569
+ return notes
570
+ }
571
+
572
+ const generateWAMessageContent = async (message, options) => {
573
+ var _a, _b
574
+ let m = {}
575
+ const hasCaptionWithoutMedia = 'caption' in message && !hasMediaPayload(message)
576
+ const hasCaptionContainer =
577
+ ('groupStatus' in message && !!message.groupStatus) || ('viewOnce' in message && !!message.viewOnce)
578
+ if ((0, exports.hasNonNullishProperty)(message, 'text')) {
579
+ const extContent = { text: message.text }
580
+ let urlInfo = message.linkPreview
581
+ if (typeof urlInfo === 'undefined') {
582
+ urlInfo = await (0, exports.generateLinkPreviewIfRequired)(message.text, options.getUrlInfo, options.logger)
583
+ }
584
+ if (urlInfo) {
585
+ extContent.matchedText = urlInfo['matched-text']
586
+ extContent.jpegThumbnail = urlInfo.jpegThumbnail
587
+ extContent.description = urlInfo.description
588
+ extContent.title = urlInfo.title
589
+ extContent.previewType = 0
590
+ const img = urlInfo.highQualityThumbnail
591
+ if (img) {
592
+ extContent.thumbnailDirectPath = img.directPath
593
+ extContent.mediaKey = img.mediaKey
594
+ extContent.mediaKeyTimestamp = img.mediaKeyTimestamp
595
+ extContent.thumbnailWidth = img.width
596
+ extContent.thumbnailHeight = img.height
597
+ extContent.thumbnailSha256 = img.fileSha256
598
+ extContent.thumbnailEncSha256 = img.fileEncSha256
599
+ }
600
+ }
601
+ if (options.backgroundColor) {
602
+ extContent.backgroundArgb = await assertColor(options.backgroundColor)
603
+ }
604
+ if (options.font) {
605
+ extContent.font = options.font
606
+ }
607
+ if (
608
+ options.jid &&
609
+ (0, WABinary_1.isInteropUser)(options.jid) &&
610
+ !options.backgroundColor &&
611
+ !options.font &&
612
+ !urlInfo
613
+ ) {
614
+ m.conversation = message.text
615
+ } else {
616
+ m.extendedTextMessage = extContent
617
+ }
618
+ } else if ((0, exports.hasNonNullishProperty)(message, 'contacts')) {
619
+ const contactLen = message.contacts.contacts.length
620
+ if (!contactLen) {
621
+ throw new boom_1.Boom('require atleast 1 contact', { statusCode: 400 })
622
+ }
623
+ if (contactLen === 1) {
624
+ m.contactMessage = WAProto_1.proto.Message.ContactMessage.create(message.contacts.contacts[0])
625
+ } else {
626
+ m.contactsArrayMessage = WAProto_1.proto.Message.ContactsArrayMessage.create(message.contacts)
627
+ }
628
+ } else if ((0, exports.hasNonNullishProperty)(message, 'location')) {
629
+ m.locationMessage = WAProto_1.proto.Message.LocationMessage.create(message.location)
630
+ } else if ((0, exports.hasNonNullishProperty)(message, 'react')) {
631
+ if (!message.react.senderTimestampMs) {
632
+ message.react.senderTimestampMs = Date.now()
633
+ }
634
+ m.reactionMessage = WAProto_1.proto.Message.ReactionMessage.create(message.react)
635
+ } else if ((0, exports.hasNonNullishProperty)(message, 'delete')) {
636
+ m.protocolMessage = {
637
+ key: message.delete,
638
+ type: WAProto_1.proto.Message.ProtocolMessage.Type.REVOKE
639
+ }
640
+ } else if ((0, exports.hasNonNullishProperty)(message, 'forward')) {
641
+ m = (0, exports.generateForwardMessageContent)(message.forward, message.force)
642
+ } else if ((0, exports.hasNonNullishProperty)(message, 'disappearingMessagesInChat')) {
643
+ const exp =
644
+ typeof message.disappearingMessagesInChat === 'boolean'
645
+ ? message.disappearingMessagesInChat
646
+ ? Defaults_1.WA_DEFAULT_EPHEMERAL
647
+ : 0
648
+ : message.disappearingMessagesInChat
649
+ m = (0, exports.prepareDisappearingMessageSettingContent)(exp)
650
+ } else if ((0, exports.hasNonNullishProperty)(message, 'groupInvite')) {
651
+ m.groupInviteMessage = {}
652
+ m.groupInviteMessage.inviteCode = message.groupInvite.inviteCode
653
+ m.groupInviteMessage.inviteExpiration = message.groupInvite.inviteExpiration
654
+ m.groupInviteMessage.caption = message.groupInvite.text
655
+ m.groupInviteMessage.groupJid = message.groupInvite.jid
656
+ m.groupInviteMessage.groupName = message.groupInvite.subject
657
+ //TODO: use built-in interface and get disappearing mode info etc.
658
+ //TODO: cache / use store!?
659
+ if (options.getProfilePicUrl) {
660
+ const pfpUrl = await options.getProfilePicUrl(message.groupInvite.jid, 'preview')
661
+ if (pfpUrl) {
662
+ const resp = await fetch(pfpUrl, { method: 'GET', dispatcher: options?.options?.dispatcher })
663
+ if (resp.ok) {
664
+ const buf = Buffer.from(await resp.arrayBuffer())
665
+ m.groupInviteMessage.jpegThumbnail = buf
666
+ }
667
+ }
668
+ }
669
+ } else if ((0, exports.hasNonNullishProperty)(message, 'pin')) {
670
+ m.pinInChatMessage = {}
671
+ m.messageContextInfo = {}
672
+ m.pinInChatMessage.key = message.pin
673
+ m.pinInChatMessage.type = message.type
674
+ m.pinInChatMessage.senderTimestampMs = Date.now()
675
+ m.messageContextInfo.messageAddOnDurationInSecs = message.type === 1 ? message.time || 86400 : 0
676
+ } else if ((0, exports.hasNonNullishProperty)(message, 'buttonReply')) {
677
+ switch (message.type) {
678
+ case 'template':
679
+ m.templateButtonReplyMessage = {
680
+ selectedDisplayText: message.buttonReply.displayText,
681
+ selectedId: message.buttonReply.id,
682
+ selectedIndex: message.buttonReply.index
683
+ }
684
+ break
685
+ case 'plain':
686
+ m.buttonsResponseMessage = {
687
+ selectedButtonId: message.buttonReply.id,
688
+ selectedDisplayText: message.buttonReply.displayText,
689
+ type: index_js_1.proto.Message.ButtonsResponseMessage.Type.DISPLAY_TEXT
690
+ }
691
+ break
692
+ case 'interactive':
693
+ m.interactiveResponseMessage = {
694
+ body: {
695
+ text: message.buttonReply.displayText,
696
+ format: WAProto_1.proto.Message.InteractiveResponseMessage.Body.Format.EXTENSIONS_1
697
+ },
698
+ nativeFlowResponseMessage: {
699
+ name: message.buttonReply.nativeFlows.name,
700
+ paramsJson: message.buttonReply.nativeFlows.paramsJson,
701
+ version: message.buttonReply.nativeFlows.version
702
+ }
703
+ }
704
+ break
705
+ case 'list':
706
+ m.listResponseMessage = {
707
+ title: message.buttonReply.title,
708
+ description: message.buttonReply.description,
709
+ singleSelectReply: {
710
+ selectedRowId: message.buttonReply.rowId
711
+ },
712
+ listType: WAProto_1.proto.Message.ListResponseMessage.ListType.SINGLE_SELECT
713
+ }
714
+ break
715
+ }
716
+ } else if (hasOptionalProperty(message, 'ptv') && message.ptv) {
717
+ const { videoMessage } = await (0, exports.prepareWAMessageMedia)({ video: message.video }, options)
718
+ m.ptvMessage = videoMessage
719
+ } else if ((0, exports.hasNonNullishProperty)(message, 'product')) {
720
+ const { imageMessage } = await (0, exports.prepareWAMessageMedia)({ image: message.product.productImage }, options)
721
+ m.productMessage = WAProto_1.proto.Message.ProductMessage.create({
722
+ ...message,
723
+ product: {
724
+ ...message.product,
725
+ productImage: imageMessage
726
+ }
727
+ })
728
+ } else if ((0, exports.hasNonNullishProperty)(message, 'listReply')) {
729
+ m.listResponseMessage = { ...message.listReply }
730
+ } else if ((0, exports.hasNonNullishProperty)(message, 'event')) {
731
+ m.eventMessage = {}
732
+ const startTime = Math.floor(message.event.startDate.getTime() / 1000)
733
+ if (message.event.call && options.getCallLink) {
734
+ const token = await options.getCallLink(message.event.call, { startTime })
735
+ m.eventMessage.joinLink =
736
+ (message.event.call === 'audio' ? Defaults_1.CALL_AUDIO_PREFIX : Defaults_1.CALL_VIDEO_PREFIX) + token
737
+ }
738
+ m.messageContextInfo = {
739
+ // encKey
740
+ messageSecret: message.event.messageSecret || (0, crypto_1.randomBytes)(32)
741
+ }
742
+ m.eventMessage.name = message.event.name
743
+ m.eventMessage.description = message.event.description
744
+ m.eventMessage.startTime = startTime
745
+ m.eventMessage.endTime = message.event.endDate ? message.event.endDate.getTime() / 1000 : undefined
746
+ m.eventMessage.isCanceled = message.event.isCancelled ?? false
747
+ m.eventMessage.extraGuestsAllowed = message.event.extraGuestsAllowed
748
+ m.eventMessage.isScheduleCall = message.event.isScheduleCall ?? false
749
+ m.eventMessage.location = message.event.location
750
+ } else if ((0, exports.hasNonNullishProperty)(message, 'poll')) {
751
+ ;(_a = message.poll).selectableCount || (_a.selectableCount = 0)
752
+ ;(_b = message.poll).toAnnouncementGroup || (_b.toAnnouncementGroup = false)
753
+ if (!Array.isArray(message.poll.values)) {
754
+ throw new boom_1.Boom('Invalid poll values', { statusCode: 400 })
755
+ }
756
+ if (message.poll.selectableCount < 0 || message.poll.selectableCount > message.poll.values.length) {
757
+ throw new boom_1.Boom(`poll.selectableCount in poll should be >= 0 and <= ${message.poll.values.length}`, {
758
+ statusCode: 400
759
+ })
760
+ }
761
+ m.messageContextInfo = {
762
+ messageSecret: message.poll.messageSecret || (0, crypto_1.randomBytes)(32)
763
+ }
764
+ const isQuiz = message.poll.type === 'quiz' || message.poll.pollType === WAProto_1.proto.Message.PollType.QUIZ
765
+ const pollTypeEnum = WAProto_1.proto.Message.PollType
766
+ const pollCreationMessage = {
767
+ name: message.poll.name,
768
+ selectableOptionsCount: message.poll.selectableCount,
769
+ options: message.poll.values.map(optionName => ({ optionName })),
770
+ pollType: isQuiz ? pollTypeEnum.QUIZ : pollTypeEnum.POLL,
771
+ ...(isQuiz && message.poll.correctAnswer ? { correctAnswer: { optionName: message.poll.correctAnswer } } : {}),
772
+ ...(message.poll.endTime
773
+ ? {
774
+ endTime:
775
+ message.poll.endTime instanceof Date
776
+ ? Math.floor(message.poll.endTime.getTime() / 1000)
777
+ : Number(message.poll.endTime)
778
+ }
779
+ : {}),
780
+ ...(message.poll.hideParticipantName !== undefined
781
+ ? { hideParticipantName: !!message.poll.hideParticipantName }
782
+ : {}),
783
+ ...(message.poll.allowAddOption !== undefined ? { allowAddOption: !!message.poll.allowAddOption } : {})
784
+ }
785
+ if (message.poll.version === 6 || message.poll.v6) {
786
+ m.pollCreationMessageV6 = pollCreationMessage
787
+ } else if (message.poll.version === 5 || message.poll.v5) {
788
+ m.pollCreationMessageV5 = pollCreationMessage
789
+ } else if (message.poll.toAnnouncementGroup) {
790
+ // poll v2 is for community announcement groups (single select and multiple)
791
+ m.pollCreationMessageV2 = pollCreationMessage
792
+ } else {
793
+ if (message.poll.selectableCount === 1) {
794
+ //poll v3 is for single select polls
795
+ m.pollCreationMessageV3 = pollCreationMessage
796
+ } else {
797
+ // poll for multiple choice polls
798
+ m.pollCreationMessage = pollCreationMessage
799
+ }
800
+ }
801
+ } else if ('inviteAdmin' in message) {
802
+ m.newsletterAdminInviteMessage = {}
803
+ m.newsletterAdminInviteMessage.inviteExpiration = message.inviteAdmin.inviteExpiration
804
+ m.newsletterAdminInviteMessage.caption = message.inviteAdmin.text
805
+ m.newsletterAdminInviteMessage.newsletterJid = message.inviteAdmin.jid
806
+ m.newsletterAdminInviteMessage.newsletterName = message.inviteAdmin.subject
807
+ m.newsletterAdminInviteMessage.jpegThumbnail = message.inviteAdmin.thumbnail
808
+ } else if ('requestPayment' in message || 'requestPaymentMessage' in message) {
809
+ if ('requestPayment' in message && 'requestPaymentMessage' in message) {
810
+ throw new boom_1.Boom('Use either requestPayment or requestPaymentMessage, not both', { statusCode: 400 })
811
+ }
812
+ const requestPayment = message.requestPayment || message.requestPaymentMessage
813
+ const notes = await buildPaymentNoteMessage(requestPayment, options)
814
+ const amountValue = requestPayment.amount ?? requestPayment.amount1000
815
+ const amount1000Raw =
816
+ typeof (amountValue === null || amountValue === void 0 ? void 0 : amountValue.toNumber) === 'function'
817
+ ? amountValue.toNumber()
818
+ : Number(amountValue)
819
+ const amount1000 = Number.isFinite(amount1000Raw) ? Math.round(amount1000Raw) : amount1000Raw
820
+ const currencyCodeIso4217 = requestPayment.currency ?? requestPayment.currencyCodeIso4217
821
+ const requestFrom = requestPayment.from ?? requestPayment.requestFrom ?? options.recipientJid
822
+ const missingFields = []
823
+ if (amountValue === undefined) missingFields.push('amount/amount1000')
824
+ if (currencyCodeIso4217 === undefined) missingFields.push('currency/currencyCodeIso4217')
825
+ if (requestFrom === undefined) missingFields.push('from/requestFrom')
826
+ if (missingFields.length) {
827
+ throw new boom_1.Boom(`Invalid requestPayment fields: missing ${missingFields.join(', ')}`, { statusCode: 400 })
828
+ }
829
+ if (
830
+ typeof amount1000 !== 'number' ||
831
+ !Number.isFinite(amount1000) ||
832
+ !Number.isInteger(amount1000) ||
833
+ amount1000 <= 0
834
+ ) {
835
+ throw new boom_1.Boom('Invalid requestPayment fields: amount/amount1000 must be a positive integer', {
836
+ statusCode: 400
837
+ })
838
+ }
839
+ const bg = requestPayment.background
840
+ m.requestPaymentMessage = WAProto_1.proto.Message.RequestPaymentMessage.fromObject({
841
+ expiryTimestamp: requestPayment.expiry ?? requestPayment.expiryTimestamp,
842
+ amount1000,
843
+ currencyCodeIso4217,
844
+ requestFrom,
845
+ noteMessage: notes,
846
+ ...(bg != null ? { background: bg } : {})
847
+ })
848
+ } else if ('sendPayment' in message || 'sendPaymentMessage' in message) {
849
+ if ('sendPayment' in message && 'sendPaymentMessage' in message) {
850
+ throw new boom_1.Boom('Use either sendPayment or sendPaymentMessage, not both', { statusCode: 400 })
851
+ }
852
+ const sendPayment = message.sendPayment || message.sendPaymentMessage
853
+ const notes = await buildPaymentNoteMessage(sendPayment, options, message.text || '')
854
+ const requestMessageKey = sendPayment.requestMessageKey ?? sendPayment.requestKey ?? sendPayment.request
855
+ if (!requestMessageKey) {
856
+ throw new boom_1.Boom('Invalid sendPayment fields: missing requestMessageKey/requestKey/request', {
857
+ statusCode: 400
858
+ })
859
+ }
860
+ m.sendPaymentMessage = WAProto_1.proto.Message.SendPaymentMessage.fromObject({
861
+ noteMessage: notes,
862
+ requestMessageKey,
863
+ ...(sendPayment.background != null ? { background: sendPayment.background } : {}),
864
+ ...(sendPayment.transactionData != null ? { transactionData: sendPayment.transactionData } : {})
865
+ })
866
+ } else if ('declinePaymentRequest' in message || 'declinePaymentRequestMessage' in message) {
867
+ if ('declinePaymentRequest' in message && 'declinePaymentRequestMessage' in message) {
868
+ throw new boom_1.Boom('Use either declinePaymentRequest or declinePaymentRequestMessage, not both', {
869
+ statusCode: 400
870
+ })
871
+ }
872
+ const declinePayment = message.declinePaymentRequest || message.declinePaymentRequestMessage
873
+ const key = (declinePayment === null || declinePayment === void 0 ? void 0 : declinePayment.key) || declinePayment
874
+ if (!key) {
875
+ throw new boom_1.Boom('Invalid declinePaymentRequest fields: missing key', { statusCode: 400 })
876
+ }
877
+ m.declinePaymentRequestMessage = WAProto_1.proto.Message.DeclinePaymentRequestMessage.fromObject({ key })
878
+ } else if ('cancelPaymentRequest' in message || 'cancelPaymentRequestMessage' in message) {
879
+ if ('cancelPaymentRequest' in message && 'cancelPaymentRequestMessage' in message) {
880
+ throw new boom_1.Boom('Use either cancelPaymentRequest or cancelPaymentRequestMessage, not both', {
881
+ statusCode: 400
882
+ })
883
+ }
884
+ const cancelPayment = message.cancelPaymentRequest || message.cancelPaymentRequestMessage
885
+ const key = (cancelPayment === null || cancelPayment === void 0 ? void 0 : cancelPayment.key) || cancelPayment
886
+ if (!key) {
887
+ throw new boom_1.Boom('Invalid cancelPaymentRequest fields: missing key', { statusCode: 400 })
888
+ }
889
+ m.cancelPaymentRequestMessage = WAProto_1.proto.Message.CancelPaymentRequestMessage.fromObject({ key })
890
+ } else if ('requestPaymentFrom' in message && !!message.requestPaymentFrom) {
891
+ const noteText = message.text || ''
892
+ m.requestPaymentMessage = WAProto_1.proto.Message.RequestPaymentMessage.fromObject({
893
+ requestFrom: message.requestPaymentFrom,
894
+ noteMessage: { extendedTextMessage: { text: noteText } }
895
+ })
896
+ } else if ('invoiceNote' in message) {
897
+ const preparedInvoice = await (0, exports.prepareWAMessageMedia)(message, options)
898
+ const mediaType = Object.keys(preparedInvoice)[0]
899
+ const mediaMsg = preparedInvoice[mediaType] || {}
900
+ m.invoiceMessage = WAProto_1.proto.Message.InvoiceMessage.fromObject({
901
+ note: message.invoiceNote,
902
+ token: message.invoiceToken || '',
903
+ attachmentType: mediaType === 'imageMessage' ? 1 : 0,
904
+ attachmentMimetype: mediaMsg.mimetype,
905
+ attachmentMediaKey: mediaMsg.mediaKey,
906
+ attachmentMediaKeyTimestamp: mediaMsg.mediaKeyTimestamp,
907
+ attachmentFileSha256: mediaMsg.fileSha256,
908
+ attachmentFileEncSha256: mediaMsg.fileEncSha256,
909
+ attachmentDirectPath: mediaMsg.directPath,
910
+ attachmentJpegThumbnail: mediaMsg.jpegThumbnail
911
+ })
912
+ } else if ('orderText' in message) {
913
+ m.orderMessage = WAProto_1.proto.Message.OrderMessage.fromObject({
914
+ message: message.orderText,
915
+ thumbnail: message.thumbnail,
916
+ status: message.orderStatus || 1,
917
+ surface: message.orderSurface || 1
918
+ })
919
+ } else if ('paymentInviteServiceType' in message) {
920
+ m.paymentInviteMessage = {
921
+ serviceType: message.paymentInviteServiceType,
922
+ expiryTimestamp: message.paymentInviteExpiry
923
+ }
924
+ } else if ((0, exports.hasNonNullishProperty)(message, 'sharePhoneNumber')) {
925
+ m.protocolMessage = {
926
+ type: index_js_1.proto.Message.ProtocolMessage.Type.SHARE_PHONE_NUMBER
927
+ }
928
+ } else if ((0, exports.hasNonNullishProperty)(message, 'requestPhoneNumber')) {
929
+ m.requestPhoneNumberMessage = {}
930
+ } else if ((0, exports.hasNonNullishProperty)(message, 'limitSharing')) {
931
+ m.protocolMessage = {
932
+ type: index_js_1.proto.Message.ProtocolMessage.Type.LIMIT_SHARING,
933
+ limitSharing: {
934
+ sharingLimited: message.limitSharing === true,
935
+ trigger: 1,
936
+ limitSharingSettingTimestamp: Date.now(),
937
+ initiatedByMe: true
938
+ }
939
+ }
940
+ } else if ('album' in message) {
941
+ const imageMessages = message.album.filter(item => 'image' in item)
942
+ const videoMessages = message.album.filter(item => 'video' in item)
943
+ m.albumMessage = WAProto_1.proto.Message.AlbumMessage.fromObject({
944
+ expectedImageCount: imageMessages.length,
945
+ expectedVideoCount: videoMessages.length
946
+ })
947
+ } else if ('pollResult' in message) {
948
+ if (!Array.isArray(message.pollResult.values)) {
949
+ throw new boom_1.Boom('Invalid pollResult values', { statusCode: 400 })
950
+ }
951
+ const isQuizResult =
952
+ message.pollResult.type === 'quiz' || message.pollResult.pollType === WAProto_1.proto.Message.PollType.QUIZ
953
+ const pollVotes = message.pollResult.values.map(([optionName, optionVoteCount]) => ({
954
+ optionName,
955
+ optionVoteCount
956
+ }))
957
+ const snapshotPayload = {
958
+ name: message.pollResult.name,
959
+ pollVotes,
960
+ pollType: isQuizResult ? WAProto_1.proto.Message.PollType.QUIZ : WAProto_1.proto.Message.PollType.POLL
961
+ }
962
+ if (message.pollResult.version === 3 || message.pollResult.v3) {
963
+ m.pollResultSnapshotMessageV3 = WAProto_1.proto.Message.PollResultSnapshotMessage.fromObject(snapshotPayload)
964
+ } else {
965
+ m.pollResultSnapshotMessage = snapshotPayload
966
+ }
967
+ } else if ('stickerPack' in message || 'stickerPackMessage' in message) {
968
+ if ('stickerPack' in message && 'stickerPackMessage' in message) {
969
+ throw new boom_1.Boom('Cannot specify both stickerPack and stickerPackMessage; use only one property.', {
970
+ statusCode: 400
971
+ })
972
+ }
973
+ const stickerPackMessage = 'stickerPack' in message ? message.stickerPack : message.stickerPackMessage
974
+ m.stickerPackMessage = WAProto_1.proto.Message.StickerPackMessage.fromObject(stickerPackMessage)
975
+ } else if ('listMessage' in message) {
976
+ const lm = { ...message.listMessage }
977
+ if (lm.text !== undefined && lm.description === undefined) {
978
+ lm.description = lm.text
979
+ delete lm.text
980
+ }
981
+ m = { listMessage: lm }
982
+ } else if ('buttonsMessage' in message) {
983
+ m = {
984
+ buttonsMessage: WAProto_1.proto.Message.ButtonsMessage.fromObject(message.buttonsMessage)
985
+ }
986
+ } else if ('interactiveMessage' in message) {
987
+ m = { interactiveMessage: message.interactiveMessage }
988
+ } else if ('richResponse' in message) {
989
+ // handled in richResponse block below
990
+ } else if ('groupStatusMessage' in message) {
991
+ m = { groupStatusMessage: WAProto_1.proto.Message.GroupStatusMessage.fromObject(message.groupStatusMessage) }
992
+ } else if (hasCaptionWithoutMedia && !hasCaptionContainer) {
993
+ m.extendedTextMessage = { text: message.caption }
994
+ } else if (hasCaptionWithoutMedia && hasCaptionContainer) {
995
+ m = {}
996
+ } else if (!hasCaptionWithoutMedia && hasMediaPayload(message)) {
997
+ m = await (0, exports.prepareWAMessageMedia)(message, options)
998
+ }
999
+ if ('buttons' in message && !!message.buttons) {
1000
+ const interactiveMessage = {
1001
+ nativeFlowMessage: WAProto_1.proto.Message.InteractiveMessage.NativeFlowMessage.fromObject({
1002
+ buttons: message.buttons.map(normalizeQuickReplyButton)
1003
+ })
1004
+ }
1005
+ if ('text' in message) {
1006
+ interactiveMessage.body = { text: message.text }
1007
+ } else if ('caption' in message) {
1008
+ interactiveMessage.body = { text: message.caption }
1009
+ interactiveMessage.header = {
1010
+ title: message.title || '',
1011
+ subtitle: message.subtitle,
1012
+ hasMediaAttachment: Boolean(message.hasMediaAttachment)
1013
+ }
1014
+ Object.assign(interactiveMessage.header, m)
1015
+ }
1016
+ if ('title' in message && !!message.title && !interactiveMessage.header) {
1017
+ interactiveMessage.header = {
1018
+ title: message.title,
1019
+ subtitle: message.subtitle,
1020
+ hasMediaAttachment: Boolean(message.hasMediaAttachment)
1021
+ }
1022
+ } else if ('title' in message && !!message.title && interactiveMessage.header) {
1023
+ interactiveMessage.header.title = message.title
1024
+ if (message.subtitle !== undefined) {
1025
+ interactiveMessage.header.subtitle = message.subtitle
1026
+ }
1027
+ }
1028
+ if ('footer' in message && !!message.footer) {
1029
+ interactiveMessage.footer = { text: message.footer }
1030
+ }
1031
+ applyContextInfoAndMentions(interactiveMessage, message)
1032
+ m = { interactiveMessage }
1033
+ } else if ('templateButtons' in message && !!message.templateButtons) {
1034
+ const msg = {
1035
+ hydratedButtons: message.hasOwnProperty('templateButtons') ? message.templateButtons : message.templateButtons
1036
+ }
1037
+ if ('text' in message) {
1038
+ msg.hydratedContentText = message.text
1039
+ } else {
1040
+ if ('caption' in message) {
1041
+ msg.hydratedContentText = message.caption
1042
+ }
1043
+ Object.assign(msg, m)
1044
+ }
1045
+ if ('footer' in message && !!message.footer) {
1046
+ msg.hydratedFooterText = message.footer
1047
+ }
1048
+ m = {
1049
+ templateMessage: {
1050
+ fourRowTemplate: msg,
1051
+ hydratedTemplate: msg
1052
+ }
1053
+ }
1054
+ }
1055
+ if ('sections' in message && !!message.sections) {
1056
+ const listMessage = {
1057
+ sections: message.sections,
1058
+ buttonText: message.buttonText,
1059
+ title: message.title,
1060
+ footerText: message.footer,
1061
+ description: message.text,
1062
+ listType: WAProto_1.proto.Message.ListMessage.ListType.SINGLE_SELECT
1063
+ }
1064
+ m = { listMessage }
1065
+ } else if ('productList' in message && !!message.productList) {
1066
+ if (
1067
+ !Array.isArray(message.productList) ||
1068
+ message.productList.length === 0 ||
1069
+ !Array.isArray(message.productList[0].products) ||
1070
+ message.productList[0].products.length === 0
1071
+ ) {
1072
+ throw new boom_1.Boom('Invalid productList: must contain at least one section with one product', {
1073
+ statusCode: 400
1074
+ })
1075
+ }
1076
+ m.listMessage = {
1077
+ title: message.title,
1078
+ buttonText: message.buttonText,
1079
+ footerText: message.footer,
1080
+ description: message.text,
1081
+ productListInfo: {
1082
+ productSections: message.productList,
1083
+ headerImage: {
1084
+ productId: message.productList[0].products[0].productId
1085
+ },
1086
+ businessOwnerJid: message.businessOwnerJid
1087
+ },
1088
+ listType: WAProto_1.proto.Message.ListMessage.ListType.PRODUCT_LIST
1089
+ }
1090
+ }
1091
+ if ('interactiveButtons' in message && !!message.interactiveButtons) {
1092
+ const nativeFlow = WAProto_1.proto.Message.InteractiveMessage.NativeFlowMessage.fromObject({
1093
+ buttons: message.interactiveButtons,
1094
+ messageParamsJson: message.messageParams ?? "{}"
1095
+ });
1096
+ let interactiveMessage = { nativeFlowMessage: nativeFlow };
1097
+ if ('text' in message) {
1098
+ interactiveMessage.body = { text: message.text };
1099
+ } else if ('caption' in message) {
1100
+ interactiveMessage.body = { text: message.caption };
1101
+ }
1102
+ if ('title' in message && !!message.title) {
1103
+ interactiveMessage.header = {
1104
+ title: message.title,
1105
+ subtitle: message.subtitle || "",
1106
+ hasMediaAttachment: false
1107
+ };
1108
+ let media = null;
1109
+ if ('image' in message && !!message.image) {
1110
+ media = await prepareWAMessageMedia({ image: message.image }, options);
1111
+ interactiveMessage.header.imageMessage = media.imageMessage;
1112
+ if (message.image.caption) {
1113
+ interactiveMessage.header.imageMessage.caption = message.image.caption;
1114
+ }
1115
+ } else if ('video' in message && !!message.video) {
1116
+ media = await prepareWAMessageMedia({ video: message.video }, options);
1117
+ interactiveMessage.header.videoMessage = media.videoMessage;
1118
+ if (message.video.caption) {
1119
+ interactiveMessage.header.videoMessage.caption = message.video.caption;
1120
+ }
1121
+ } else if ('gif' in message && !!message.gif) {
1122
+ media = await prepareWAMessageMedia({ video: message.gif }, options);
1123
+ interactiveMessage.header.videoMessage = media.videoMessage;
1124
+ interactiveMessage.header.videoMessage.gifPlayback = true;
1125
+ if (message.gif.caption) {
1126
+ interactiveMessage.header.videoMessage.caption = message.gif.caption;
1127
+ }
1128
+ } else if ('document' in message && !!message.document) {
1129
+ media = await prepareWAMessageMedia({ document: message.document }, options);
1130
+ interactiveMessage.header.documentMessage = media.documentMessage;
1131
+ let docuR = interactiveMessage.header.documentMessage;
1132
+ docuR.fileName = message.document.fileName || "Document";
1133
+ docuR.mimetype = message.document.mimetype || docuR.mimetype;
1134
+ if (message.document.caption) docuR.caption = message.document.caption;
1135
+ if (message.document.jpegThumbnail) docuR.jpegThumbnail = message.document.jpegThumbnail;
1136
+ } else if ('location' in message && !!message.location) {
1137
+ let mLoc = message.location;
1138
+ interactiveMessage.header.locationMessage = {
1139
+ degreesLongitude: mLoc.longitude || 0,
1140
+ degreesLatitude: mLoc.latitude || 0,
1141
+ name: mLoc.name || null,
1142
+ address: mLoc.address || null,
1143
+ url: mLoc.url || null
1144
+ };
1145
+ media = true;
1146
+ } else if ('thumbnail' in message && !!message.thumbnail) {
1147
+ interactiveMessage.header.jpegThumbnail = message.thumbnail;
1148
+ }
1149
+ if (media || ('thumbnail' in message && !!message.thumbnail)) {
1150
+ interactiveMessage.header.hasMediaAttachment = true;
1151
+ }
1152
+ }
1153
+ if ('footer' in message && !!message.footer) {
1154
+ interactiveMessage.footer = { text: message.footer };
1155
+ }
1156
+ applyContextInfoAndMentions(interactiveMessage, message);
1157
+ m = { interactiveMessage };
1158
+ }
1159
+ if ('shop' in message && !!message.shop) {
1160
+ const interactiveMessage = {
1161
+ shopStorefrontMessage: WAProto_1.proto.Message.InteractiveMessage.ShopMessage.fromObject({
1162
+ surface: (_l = message.shop) === null || _l === void 0 ? void 0 : _l.surface,
1163
+ id: (_m = message.shop) === null || _m === void 0 ? void 0 : _m.id
1164
+ })
1165
+ }
1166
+ if ('text' in message) {
1167
+ interactiveMessage.body = {
1168
+ text: message.text
1169
+ }
1170
+ } else if ('caption' in message) {
1171
+ interactiveMessage.body = {
1172
+ text: message.caption
1173
+ }
1174
+ interactiveMessage.header = {
1175
+ title: message.title,
1176
+ subtitle: message.subtitle,
1177
+ hasMediaAttachment: Boolean(message.hasMediaAttachment)
1178
+ }
1179
+ Object.assign(interactiveMessage.header, m)
1180
+ }
1181
+ if ('footer' in message && !!message.footer) {
1182
+ interactiveMessage.footer = {
1183
+ text: message.footer
1184
+ }
1185
+ }
1186
+ if ('title' in message && !!message.title) {
1187
+ interactiveMessage.header = {
1188
+ title: message.title,
1189
+ subtitle: message.subtitle,
1190
+ hasMediaAttachment: Boolean(message.hasMediaAttachment)
1191
+ }
1192
+ Object.assign(interactiveMessage.header, m)
1193
+ }
1194
+ applyContextInfoAndMentions(interactiveMessage, message)
1195
+ m = { interactiveMessage }
1196
+ if ('interactiveAsTemplate' in message && message.interactiveAsTemplate !== false) {
1197
+ m = { templateMessage: { interactiveMessageTemplate: interactiveMessage } }
1198
+ }
1199
+ }
1200
+ if ('richResponse' in message) {
1201
+ const {
1202
+ text,
1203
+ code,
1204
+ language = 'javascript',
1205
+ botJid = '259786046210223@bot',
1206
+ table,
1207
+ latex,
1208
+ map,
1209
+ imageUrl,
1210
+ imageUrls,
1211
+ responseId,
1212
+ messageSecret: richSecret
1213
+ } = message.richResponse
1214
+ const sections = []
1215
+ if (text) {
1216
+ sections.push({
1217
+ view_model: {
1218
+ primitive: { text, __typename: 'GenAIMarkdownTextUXPrimitive' },
1219
+ __typename: 'GenAISingleLayoutViewModel'
1220
+ }
1221
+ })
1222
+ }
1223
+ if (code) {
1224
+ sections.push({
1225
+ view_model: {
1226
+ primitive: {
1227
+ language,
1228
+ code_blocks: tokenizeCode(String(code)),
1229
+ __typename: 'GenAICodeUXPrimitive'
1230
+ },
1231
+ __typename: 'GenAISingleLayoutViewModel'
1232
+ }
1233
+ })
1234
+ }
1235
+ if (table) {
1236
+ const isPlainArrayTable = Array.isArray(table)
1237
+ const rows = isPlainArrayTable ? table : table.rows
1238
+ if (!Array.isArray(rows)) {
1239
+ throw new boom_1.Boom(
1240
+ 'richResponse.table must be an array of rows (e.g. [["h1","h2"],["a","b"]]) or { rows: [...] }',
1241
+ { statusCode: 400 }
1242
+ )
1243
+ }
1244
+ sections.push({
1245
+ view_model: {
1246
+ primitive: {
1247
+ rows: rows.map((row, idx) => ({
1248
+ cells: Array.isArray(row) ? row.map(c => ({ text: String(c) })) : row.cells,
1249
+ // when a plain 2D array is passed, the first row is treated as the table header
1250
+ isHeading: Array.isArray(row) ? isPlainArrayTable && idx === 0 : row.isHeading
1251
+ })),
1252
+ __typename: 'GenAITableUXPrimitive'
1253
+ },
1254
+ __typename: 'GenAISingleLayoutViewModel'
1255
+ }
1256
+ })
1257
+ }
1258
+ if (latex) {
1259
+ const expressions = Array.isArray(latex)
1260
+ ? latex.map(e => (typeof e === 'string' ? { expression: e } : e))
1261
+ : [{ expression: String(latex) }]
1262
+ sections.push({
1263
+ view_model: {
1264
+ primitive: { expressions, __typename: 'GenAILatexUXPrimitive' },
1265
+ __typename: 'GenAISingleLayoutViewModel'
1266
+ }
1267
+ })
1268
+ }
1269
+ if (map) {
1270
+ sections.push({
1271
+ view_model: {
1272
+ primitive: {
1273
+ latitude: map.latitude,
1274
+ longitude: map.longitude,
1275
+ zoom: map.zoom,
1276
+ title: map.title,
1277
+ annotations: map.annotations || [],
1278
+ __typename: 'GenAIMapUXPrimitive'
1279
+ },
1280
+ __typename: 'GenAISingleLayoutViewModel'
1281
+ }
1282
+ })
1283
+ }
1284
+ if (imageUrl) {
1285
+ sections.push({
1286
+ view_model: {
1287
+ primitive: { url: imageUrl, __typename: 'GenAIInlineImageUXPrimitive' },
1288
+ __typename: 'GenAISingleLayoutViewModel'
1289
+ }
1290
+ })
1291
+ }
1292
+ if (imageUrls && Array.isArray(imageUrls) && imageUrls.length > 0) {
1293
+ sections.push({
1294
+ view_model: {
1295
+ primitive: {
1296
+ urls: imageUrls.map(u => (typeof u === 'string' ? { url: u } : u)),
1297
+ __typename: 'GenAIGridImageUXPrimitive'
1298
+ },
1299
+ __typename: 'GenAISingleLayoutViewModel'
1300
+ }
1301
+ })
1302
+ }
1303
+ if (!sections.length && !text) {
1304
+ sections.push({
1305
+ view_model: {
1306
+ primitive: { text: '', __typename: 'GenAIMarkdownTextUXPrimitive' },
1307
+ __typename: 'GenAISingleLayoutViewModel'
1308
+ }
1309
+ })
1310
+ }
1311
+ const unifiedData = {
1312
+ response_id: responseId || (0, crypto_1.randomUUID)(),
1313
+ sections
1314
+ }
1315
+ return WAProto_1.proto.Message.fromObject({
1316
+ messageContextInfo: {
1317
+ deviceListMetadata: {},
1318
+ deviceListMetadataVersion: 2,
1319
+ messageSecret: richSecret || (0, crypto_1.randomBytes)(32)
1320
+ },
1321
+ botForwardedMessage: {
1322
+ message: {
1323
+ richResponseMessage: {
1324
+ submessages: [],
1325
+ messageType: 1,
1326
+ unifiedResponse: { data: Buffer.from(JSON.stringify(unifiedData)) },
1327
+ contextInfo: {
1328
+ forwardingScore: 2,
1329
+ isForwarded: true,
1330
+ forwardedAiBotMessageInfo: { botJid },
1331
+ botMessageSharingInfo: {
1332
+ botEntryPointOrigin: 1,
1333
+ forwardScore: 2
1334
+ }
1335
+ }
1336
+ }
1337
+ }
1338
+ }
1339
+ })
1340
+ }
1341
+ if ('statusNotification' in message || 'statusNotificationMessage' in message) {
1342
+ const notifData = 'statusNotification' in message ? message.statusNotification : message.statusNotificationMessage
1343
+ m = { statusNotificationMessage: WAProto_1.proto.Message.StatusNotificationMessage.fromObject(notifData) }
1344
+ } else if ('statusQuestionAnswer' in message || 'statusQuestionAnswerMessage' in message) {
1345
+ const qaData =
1346
+ 'statusQuestionAnswer' in message ? message.statusQuestionAnswer : message.statusQuestionAnswerMessage
1347
+ m = { statusQuestionAnswerMessage: WAProto_1.proto.Message.StatusQuestionAnswerMessage.fromObject(qaData) }
1348
+ } else if ('questionResponse' in message || 'questionResponseMessage' in message) {
1349
+ const qrData = 'questionResponse' in message ? message.questionResponse : message.questionResponseMessage
1350
+ m = { questionResponseMessage: WAProto_1.proto.Message.QuestionResponseMessage.fromObject(qrData) }
1351
+ } else if ('statusQuoted' in message || 'statusQuotedMessage' in message) {
1352
+ const sqData = 'statusQuoted' in message ? message.statusQuoted : message.statusQuotedMessage
1353
+ m = { statusQuotedMessage: WAProto_1.proto.Message.StatusQuotedMessage.fromObject(sqData) }
1354
+ } else if ('statusStickerInteraction' in message || 'statusStickerInteractionMessage' in message) {
1355
+ const ssiData =
1356
+ 'statusStickerInteraction' in message ? message.statusStickerInteraction : message.statusStickerInteractionMessage
1357
+ m = { statusStickerInteractionMessage: WAProto_1.proto.Message.StatusStickerInteractionMessage.fromObject(ssiData) }
1358
+ } else if ('newsletterFollowerInvite' in message || 'newsletterFollowerInviteMessageV2' in message) {
1359
+ const nfiData =
1360
+ 'newsletterFollowerInvite' in message
1361
+ ? message.newsletterFollowerInvite
1362
+ : message.newsletterFollowerInviteMessageV2
1363
+ m = {
1364
+ newsletterFollowerInviteMessageV2: WAProto_1.proto.Message.NewsletterFollowerInviteMessage.fromObject(nfiData)
1365
+ }
1366
+ } else if ('messageHistoryNotice' in message) {
1367
+ m = { messageHistoryNotice: WAProto_1.proto.Message.MessageHistoryNotice.fromObject(message.messageHistoryNotice) }
1368
+ } else if ('scheduledCall' in message || 'scheduledCallCreationMessage' in message) {
1369
+ if ('scheduledCall' in message && 'scheduledCallCreationMessage' in message) {
1370
+ throw new boom_1.Boom('Use either scheduledCall or scheduledCallCreationMessage, not both', { statusCode: 400 })
1371
+ }
1372
+ const sc = message.scheduledCall || message.scheduledCallCreationMessage
1373
+ const scheduledTs =
1374
+ sc.scheduledAt instanceof Date
1375
+ ? sc.scheduledAt.getTime()
1376
+ : sc.scheduledAt
1377
+ ? Number(sc.scheduledAt)
1378
+ : sc.scheduledTimestampMs
1379
+ ? Number(sc.scheduledTimestampMs)
1380
+ : Date.now() + 3600000
1381
+ const callTypeEnum = WAProto_1.proto.Message.ScheduledCallCreationMessage.CallType
1382
+ let callType = callTypeEnum.UNKNOWN
1383
+ if (sc.isVideo || sc.callType === 'video' || sc.callType === callTypeEnum.VIDEO) {
1384
+ callType = callTypeEnum.VIDEO
1385
+ } else if (sc.callType === 'voice' || sc.callType === callTypeEnum.VOICE || sc.callType === undefined) {
1386
+ callType = callTypeEnum.VOICE
1387
+ } else {
1388
+ callType = sc.callType ?? callTypeEnum.VOICE
1389
+ }
1390
+ m.scheduledCallCreationMessage = WAProto_1.proto.Message.ScheduledCallCreationMessage.fromObject({
1391
+ scheduledTimestampMs: scheduledTs,
1392
+ callType,
1393
+ title: sc.title || ''
1394
+ })
1395
+ } else if ('editScheduledCall' in message || 'scheduledCallEditMessage' in message) {
1396
+ if ('editScheduledCall' in message && 'scheduledCallEditMessage' in message) {
1397
+ throw new boom_1.Boom('Use either editScheduledCall or scheduledCallEditMessage, not both', { statusCode: 400 })
1398
+ }
1399
+ const esc = message.editScheduledCall || message.scheduledCallEditMessage
1400
+ const key = esc.key || esc
1401
+ if (!key || typeof key !== 'object' || !key.id) {
1402
+ throw new boom_1.Boom('editScheduledCall requires a valid message key with id', { statusCode: 400 })
1403
+ }
1404
+ m.scheduledCallEditMessage = WAProto_1.proto.Message.ScheduledCallEditMessage.fromObject({
1405
+ key,
1406
+ editType: WAProto_1.proto.Message.ScheduledCallEditMessage.EditType.CANCEL
1407
+ })
1408
+ } else if ('eventInvite' in message || 'eventInviteMessage' in message) {
1409
+ if ('eventInvite' in message && 'eventInviteMessage' in message) {
1410
+ throw new boom_1.Boom('Use either eventInvite or eventInviteMessage, not both', { statusCode: 400 })
1411
+ }
1412
+ const ei = message.eventInvite || message.eventInviteMessage
1413
+ const startTime =
1414
+ ei.startDate instanceof Date
1415
+ ? Math.floor(ei.startDate.getTime() / 1000)
1416
+ : ei.startTime
1417
+ ? Number(ei.startTime)
1418
+ : undefined
1419
+ const endTime =
1420
+ ei.endDate instanceof Date ? Math.floor(ei.endDate.getTime() / 1000) : ei.endTime ? Number(ei.endTime) : undefined
1421
+ if (!ei.eventId && !ei.id) {
1422
+ throw new boom_1.Boom('eventInvite requires an eventId', { statusCode: 400 })
1423
+ }
1424
+ m.eventInviteMessage = WAProto_1.proto.Message.EventInviteMessage.fromObject({
1425
+ eventId: ei.eventId || ei.id,
1426
+ eventTitle: ei.title || ei.eventTitle || '',
1427
+ caption: ei.text || ei.caption || '',
1428
+ startTime,
1429
+ endTime,
1430
+ isCanceled: ei.isCancelled ?? ei.isCanceled ?? false,
1431
+ jpegThumbnail: ei.thumbnail || ei.jpegThumbnail,
1432
+ contextInfo: ei.contextInfo
1433
+ })
1434
+ } else if ('comment' in message || 'commentMessage' in message) {
1435
+ if ('comment' in message && 'commentMessage' in message) {
1436
+ throw new boom_1.Boom('Use either comment or commentMessage, not both', { statusCode: 400 })
1437
+ }
1438
+ const cm = message.comment || message.commentMessage
1439
+ if (!cm.targetMessageKey && !cm.key) {
1440
+ throw new boom_1.Boom('comment requires a targetMessageKey', { statusCode: 400 })
1441
+ }
1442
+ m.commentMessage = WAProto_1.proto.Message.CommentMessage.fromObject({
1443
+ message: cm.message || cm.replyMessage,
1444
+ targetMessageKey: cm.targetMessageKey || cm.key
1445
+ })
1446
+ } else if ('splitPayment' in message || 'splitPaymentMessage' in message) {
1447
+ if ('splitPayment' in message && 'splitPaymentMessage' in message) {
1448
+ throw new boom_1.Boom('Use either splitPayment or splitPaymentMessage, not both', { statusCode: 400 })
1449
+ }
1450
+ const sp = message.splitPayment || message.splitPaymentMessage
1451
+ if (!sp.totalAmount || !sp.participants?.length) {
1452
+ throw new boom_1.Boom('splitPayment requires totalAmount and at least one participant', { statusCode: 400 })
1453
+ }
1454
+ m.splitPaymentMessage = WAProto_1.proto.Message.SplitPaymentMessage.fromObject({
1455
+ splitId: sp.splitId || (0, crypto_1.randomUUID)(),
1456
+ totalAmount: sp.totalAmount,
1457
+ description: sp.description || '',
1458
+ requesterJid: sp.requesterJid || options.userJid,
1459
+ participants: sp.participants.map(p => ({
1460
+ jid: p.jid,
1461
+ amount: p.amount,
1462
+ status: p.status ?? WAProto_1.proto.Message.SplitPaymentParticipant.SplitPaymentStatus.PENDING
1463
+ })),
1464
+ createdAtMs: sp.createdAtMs || Date.now(),
1465
+ contextInfo: sp.contextInfo
1466
+ })
1467
+ } else if ('p2pPaymentReminder' in message || 'p2PPaymentReminderNotification' in message) {
1468
+ if ('p2pPaymentReminder' in message && 'p2PPaymentReminderNotification' in message) {
1469
+ throw new boom_1.Boom('Use either p2pPaymentReminder or p2PPaymentReminderNotification, not both', {
1470
+ statusCode: 400
1471
+ })
1472
+ }
1473
+ const pr = message.p2pPaymentReminder || message.p2PPaymentReminderNotification
1474
+ const freqEnum = WAProto_1.proto.Message.P2PPaymentReminderNotification.ReminderFrequency
1475
+ const stateEnum = WAProto_1.proto.Message.P2PPaymentReminderNotification.ReminderState
1476
+ const freqMap = {
1477
+ weekly: freqEnum.WEEKLY,
1478
+ biweekly: freqEnum.BIWEEKLY,
1479
+ monthly: freqEnum.MONTHLY,
1480
+ custom: freqEnum.CUSTOM
1481
+ }
1482
+ const stateMap = {
1483
+ active: stateEnum.ACTIVE,
1484
+ paused: stateEnum.PAUSED,
1485
+ stopped: stateEnum.STOPPED,
1486
+ expired: stateEnum.EXPIRED,
1487
+ cancelled: stateEnum.CANCELLED
1488
+ }
1489
+ m.p2PPaymentReminderNotification = WAProto_1.proto.Message.P2PPaymentReminderNotification.fromObject({
1490
+ reminderId: pr.reminderId || (0, crypto_1.randomUUID)(),
1491
+ amount: pr.amount,
1492
+ frequency:
1493
+ typeof pr.frequency === 'string'
1494
+ ? (freqMap[pr.frequency.toLowerCase()] ?? freqEnum.UNKNOWN_FREQUENCY)
1495
+ : (pr.frequency ?? freqEnum.UNKNOWN_FREQUENCY),
1496
+ nextReminderTimestamp: pr.nextReminderTimestamp,
1497
+ expiryTimestamp: pr.expiryTimestamp,
1498
+ state:
1499
+ typeof pr.state === 'string'
1500
+ ? (stateMap[pr.state.toLowerCase()] ?? stateEnum.ACTIVE)
1501
+ : (pr.state ?? stateEnum.ACTIVE),
1502
+ description: pr.description || '',
1503
+ creatorJid: pr.creatorJid || options.userJid,
1504
+ receiverJid: pr.receiverJid,
1505
+ upiId: pr.upiId,
1506
+ createdTimestamp: pr.createdTimestamp || Date.now()
1507
+ })
1508
+ } else if ('conditionalReveal' in message || 'conditionalRevealMessage' in message) {
1509
+ if ('conditionalReveal' in message && 'conditionalRevealMessage' in message) {
1510
+ throw new boom_1.Boom('Use either conditionalReveal or conditionalRevealMessage, not both', { statusCode: 400 })
1511
+ }
1512
+ const cr = message.conditionalReveal || message.conditionalRevealMessage
1513
+ m.conditionalRevealMessage = WAProto_1.proto.Message.ConditionalRevealMessage.fromObject({
1514
+ conditionalRevealMessageType:
1515
+ WAProto_1.proto.Message.ConditionalRevealMessage.ConditionalRevealMessageType.SCHEDULED_MESSAGE,
1516
+ revealKeyId: cr.revealKeyId || (0, crypto_1.randomUUID)()
1517
+ })
1518
+ m.messageContextInfo = {
1519
+ messageSecret: cr.messageSecret || (0, crypto_1.randomBytes)(32)
1520
+ }
1521
+ } else if ('callLog' in message || 'callLogMessage' in message) {
1522
+ if ('callLog' in message && 'callLogMessage' in message) {
1523
+ throw new boom_1.Boom('Use either callLog or callLogMessage, not both', { statusCode: 400 })
1524
+ }
1525
+ const cl = message.callLog || message.callLogMessage
1526
+ const outcomeEnum = WAProto_1.proto.Message.CallLogMessage.CallOutcome
1527
+ const callTypeEnum = WAProto_1.proto.Message.CallLogMessage.CallType
1528
+ const outcomeMap = {
1529
+ connected: outcomeEnum.CONNECTED,
1530
+ missed: outcomeEnum.MISSED,
1531
+ failed: outcomeEnum.FAILED,
1532
+ rejected: outcomeEnum.REJECTED,
1533
+ accepted_elsewhere: outcomeEnum.ACCEPTED_ELSEWHERE,
1534
+ ongoing: outcomeEnum.ONGOING,
1535
+ silenced_by_dnd: outcomeEnum.SILENCED_BY_DND,
1536
+ silenced_unknown_caller: outcomeEnum.SILENCED_UNKNOWN_CALLER
1537
+ }
1538
+ const callTypeMap = {
1539
+ regular: callTypeEnum.REGULAR,
1540
+ scheduled_call: callTypeEnum.SCHEDULED_CALL,
1541
+ voice_chat: callTypeEnum.VOICE_CHAT
1542
+ }
1543
+ m.callLogMesssage = WAProto_1.proto.Message.CallLogMessage.fromObject({
1544
+ isVideo: cl.isVideo ?? false,
1545
+ callOutcome:
1546
+ typeof cl.outcome === 'string'
1547
+ ? (outcomeMap[cl.outcome.toLowerCase()] ?? outcomeEnum.CONNECTED)
1548
+ : (cl.outcome ?? outcomeEnum.CONNECTED),
1549
+ durationSecs: cl.durationSecs || 0,
1550
+ callType:
1551
+ typeof cl.callType === 'string'
1552
+ ? (callTypeMap[cl.callType.toLowerCase()] ?? callTypeEnum.REGULAR)
1553
+ : (cl.callType ?? callTypeEnum.REGULAR),
1554
+ participants: (cl.participants || []).map(p => ({
1555
+ jid: p.jid,
1556
+ callOutcome:
1557
+ typeof p.outcome === 'string'
1558
+ ? (outcomeMap[p.outcome.toLowerCase()] ?? outcomeEnum.CONNECTED)
1559
+ : (p.outcome ?? p.callOutcome ?? outcomeEnum.CONNECTED)
1560
+ }))
1561
+ })
1562
+ } else if ('statusMention' in message || 'statusMentionMsg' in message) {
1563
+ const sm = message.statusMention || message.statusMentionMsg
1564
+ if (!sm || typeof sm !== 'object') {
1565
+ throw new boom_1.Boom('statusMention must be an object with a message property', { statusCode: 400 })
1566
+ }
1567
+ m.statusMentionMessage = WAProto_1.proto.Message.FutureProofMessage.fromObject({
1568
+ message: sm.message || sm
1569
+ })
1570
+ } else if ('question' in message || 'questionMessage' in message) {
1571
+ const q = message.question || message.questionMessage
1572
+ m.questionMessage = WAProto_1.proto.Message.FutureProofMessage.fromObject({
1573
+ message: q.message || (typeof q === 'object' && 'conversation' in q ? q : { conversation: String(q) })
1574
+ })
1575
+ } else if ('questionReply' in message || 'questionReplyMessage' in message) {
1576
+ const qr = message.questionReply || message.questionReplyMessage
1577
+ m.questionReplyMessage = WAProto_1.proto.Message.FutureProofMessage.fromObject({
1578
+ message: qr.message || (typeof qr === 'object' && 'conversation' in qr ? qr : { conversation: String(qr) })
1579
+ })
1580
+ } else if ('statusAddYours' in message) {
1581
+ const say = message.statusAddYours
1582
+ m.statusAddYours = WAProto_1.proto.Message.FutureProofMessage.fromObject({
1583
+ message: say.message || say
1584
+ })
1585
+ } else if ('eventCoverImage' in message) {
1586
+ const eci = message.eventCoverImage
1587
+ m.eventCoverImage = WAProto_1.proto.Message.FutureProofMessage.fromObject({
1588
+ message: eci.message || eci
1589
+ })
1590
+ } else if ('spoilerMessage' in message || 'spoiler' in message) {
1591
+ const sp = message.spoilerMessage || message.spoiler
1592
+ m.spoilerMessage = WAProto_1.proto.Message.FutureProofMessage.fromObject({
1593
+ message: sp.message || sp
1594
+ })
1595
+ } else if ('lottieStickerMessage' in message || 'lottieSticker' in message) {
1596
+ const ls = message.lottieStickerMessage || message.lottieSticker
1597
+ m.lottieStickerMessage = WAProto_1.proto.Message.FutureProofMessage.fromObject({
1598
+ message: ls.message || ls
1599
+ })
1600
+ } else if ('groupStatusV2' in message || 'groupStatusMessageV2' in message) {
1601
+ const gsv2 = message.groupStatusV2 || message.groupStatusMessageV2
1602
+ m.groupStatusMessageV2 = WAProto_1.proto.Message.FutureProofMessage.fromObject({
1603
+ message: gsv2.message || gsv2
1604
+ })
1605
+ } else if ('newsletterAdminProfile' in message || 'newsletterAdminProfileMessage' in message) {
1606
+ const nap = message.newsletterAdminProfile || message.newsletterAdminProfileMessage
1607
+ m.newsletterAdminProfileMessage = WAProto_1.proto.Message.FutureProofMessage.fromObject({
1608
+ message: nap.message || nap
1609
+ })
1610
+ } else if ('newsletterAdminProfileV2' in message || 'newsletterAdminProfileMessageV2' in message) {
1611
+ const nap2 = message.newsletterAdminProfileV2 || message.newsletterAdminProfileMessageV2
1612
+ m.newsletterAdminProfileMessageV2 = WAProto_1.proto.Message.FutureProofMessage.fromObject({
1613
+ message: nap2.message || nap2
1614
+ })
1615
+ } else if ('botTask' in message || 'botTaskMessage' in message) {
1616
+ const bt = message.botTask || message.botTaskMessage
1617
+ m.botTaskMessage = WAProto_1.proto.Message.FutureProofMessage.fromObject({
1618
+ message: bt.message || bt
1619
+ })
1620
+ } else if ('botInvoke' in message || 'botInvokeMessage' in message) {
1621
+ const bi = message.botInvoke || message.botInvokeMessage
1622
+ m.botInvokeMessage = WAProto_1.proto.Message.FutureProofMessage.fromObject({
1623
+ message: bi.message || bi
1624
+ })
1625
+ } else if ('associatedChild' in message || 'associatedChildMessage' in message) {
1626
+ const ac = message.associatedChild || message.associatedChildMessage
1627
+ m.associatedChildMessage = WAProto_1.proto.Message.FutureProofMessage.fromObject({
1628
+ message: ac.message || ac
1629
+ })
1630
+ } else if ('groupStatusMention' in message || 'groupStatusMentionMessage' in message) {
1631
+ const gsm = message.groupStatusMention || message.groupStatusMentionMessage
1632
+ m.groupStatusMentionMessage = WAProto_1.proto.Message.FutureProofMessage.fromObject({
1633
+ message: gsm.message || gsm
1634
+ })
1635
+ } else if ('pollCreationOptionImage' in message || 'pollCreationOptionImageMessage' in message) {
1636
+ const pcoi = message.pollCreationOptionImage || message.pollCreationOptionImageMessage
1637
+ m.pollCreationOptionImageMessage = WAProto_1.proto.Message.FutureProofMessage.fromObject({
1638
+ message: pcoi.message || pcoi
1639
+ })
1640
+ } else if ('newsletterAdminProfileStatus' in message || 'newsletterAdminProfileStatusMessage' in message) {
1641
+ const naps = message.newsletterAdminProfileStatus || message.newsletterAdminProfileStatusMessage
1642
+ m.newsletterAdminProfileStatusMessage = WAProto_1.proto.Message.FutureProofMessage.fromObject({
1643
+ message: naps.message || naps
1644
+ })
1645
+ } else if ('placeholder' in message || 'placeholderMessage' in message) {
1646
+ const ph = message.placeholder || message.placeholderMessage
1647
+ const phTypeEnum = WAProto_1.proto.Message.PlaceholderMessage.PlaceholderType
1648
+ const phTypeMap = {
1649
+ e2e_reenable: phTypeEnum.PLACEHOLDER_MESSAGE_TYPE_E2E_REENABLE_MSG,
1650
+ linked_devices: phTypeEnum.PLACEHOLDER_MESSAGE_TYPE_LINKED_DEVICES_MESSAGE
1651
+ }
1652
+ m.placeholderMessage = WAProto_1.proto.Message.PlaceholderMessage.fromObject({
1653
+ type:
1654
+ typeof ph.type === 'string'
1655
+ ? (phTypeMap[ph.type.toLowerCase()] ?? phTypeEnum.PLACEHOLDER_MESSAGE_TYPE_E2E_REENABLE_MSG)
1656
+ : (ph.type ?? phTypeEnum.PLACEHOLDER_MESSAGE_TYPE_E2E_REENABLE_MSG)
1657
+ })
1658
+ } else if ('tapLink' in message || 'tapLinkMessage' in message) {
1659
+ const tl = message.tapLink || message.tapLinkMessage
1660
+ const inner = {
1661
+ extendedTextMessage: {
1662
+ text: tl.text || tl.title || '',
1663
+ matchedText: tl.url || tl.tapUrl,
1664
+ title: tl.title || tl.text || '',
1665
+ contextInfo: {
1666
+ actionLink: {
1667
+ url: tl.url || tl.tapUrl || '',
1668
+ buttonTitle: tl.buttonTitle || 'Open'
1669
+ }
1670
+ }
1671
+ }
1672
+ }
1673
+ Object.assign(m, inner)
1674
+ } else if ('citation' in message) {
1675
+ const cit = message.citation
1676
+ m.extendedTextMessage = {
1677
+ text: cit.text || cit.title || '',
1678
+ title: cit.title || '',
1679
+ description: cit.subtitle || cit.description || '',
1680
+ ...(cit.imageUrl ? { jpegThumbnail: undefined, previewType: 0 } : {})
1681
+ }
1682
+ } else if ('embeddedMusic' in message) {
1683
+ const em = message.embeddedMusic
1684
+ m.extendedTextMessage = {
1685
+ text: em.text || `🎵 ${em.title || ''} — ${em.author || ''}`,
1686
+ title: em.title || '',
1687
+ description: em.author || em.artistAttribution || '',
1688
+ previewType: 0
1689
+ }
1690
+ }
1691
+ if ('keepInChat' in message || 'keepInChatMessage' in message) {
1692
+ const kic = message.keepInChat || message.keepInChatMessage
1693
+ const keepTypeEnum = WAProto_1.proto.KeepType
1694
+ const keepTypeMap = { keep: keepTypeEnum.KEEP_FOR_ALL, undo: keepTypeEnum.UNDO_KEEP_FOR_ALL }
1695
+ m.keepInChatMessage = WAProto_1.proto.Message.KeepInChatMessage.fromObject({
1696
+ key: kic.key || kic,
1697
+ keepType:
1698
+ typeof kic.keepType === 'string'
1699
+ ? (keepTypeMap[kic.keepType.toLowerCase()] ?? keepTypeEnum.KEEP_FOR_ALL)
1700
+ : (kic.keepType ?? keepTypeEnum.KEEP_FOR_ALL),
1701
+ timestampMs: kic.timestampMs || Date.now()
1702
+ })
1703
+ }
1704
+ if ('botFeedback' in message || 'botFeedbackMessage' in message) {
1705
+ const bf = message.botFeedback || message.botFeedbackMessage
1706
+ const kindEnum = WAProto_1.proto.BotFeedbackMessage.BotFeedbackKind
1707
+ const kindMap = {
1708
+ positive: kindEnum.BOT_FEEDBACK_POSITIVE,
1709
+ negative: kindEnum.BOT_FEEDBACK_NEGATIVE,
1710
+ negative_generic: kindEnum.BOT_FEEDBACK_NEGATIVE_GENERIC,
1711
+ negative_helpful: kindEnum.BOT_FEEDBACK_NEGATIVE_HELPFUL,
1712
+ negative_interesting: kindEnum.BOT_FEEDBACK_NEGATIVE_INTERESTING,
1713
+ negative_accurate: kindEnum.BOT_FEEDBACK_NEGATIVE_ACCURATE,
1714
+ negative_safe: kindEnum.BOT_FEEDBACK_NEGATIVE_SAFE,
1715
+ negative_other: kindEnum.BOT_FEEDBACK_NEGATIVE_OTHER
1716
+ }
1717
+ m.protocolMessage = {
1718
+ type: WAProto_1.proto.Message.ProtocolMessage.Type.BOT_FEEDBACK_MESSAGE,
1719
+ key: bf.key,
1720
+ botFeedbackMessage: WAProto_1.proto.BotFeedbackMessage.fromObject({
1721
+ feedbackId: bf.feedbackId || bf.key?.id || '',
1722
+ kind:
1723
+ typeof bf.kind === 'string'
1724
+ ? (kindMap[bf.kind.toLowerCase()] ?? kindEnum.BOT_FEEDBACK_POSITIVE)
1725
+ : (bf.kind ?? kindEnum.BOT_FEEDBACK_POSITIVE)
1726
+ })
1727
+ }
1728
+ }
1729
+ if ('pollAddOption' in message || 'pollAddOptionMessage' in message) {
1730
+ const pao = message.pollAddOption || message.pollAddOptionMessage
1731
+ if (!pao.pollCreationMessageKey && !pao.key) {
1732
+ throw new boom_1.Boom('pollAddOption requires a pollCreationMessageKey', { statusCode: 400 })
1733
+ }
1734
+ m.pollAddOptionMessage = WAProto_1.proto.Message.PollAddOptionMessage.fromObject({
1735
+ pollCreationMessageKey: pao.pollCreationMessageKey || pao.key,
1736
+ addOption: { optionName: pao.optionName || pao.option }
1737
+ })
1738
+ }
1739
+ if ('chatTheme' in message || 'chatThemeMessage' in message) {
1740
+ const ct = message.chatTheme || message.chatThemeMessage
1741
+ const themeSetting = {
1742
+ settingTimestampMs: Date.now(),
1743
+ clearTheme: ct.clear ?? false,
1744
+ colorSchemeId: ct.colorSchemeId
1745
+ }
1746
+ if (ct.solidColor) {
1747
+ const sc = typeof ct.solidColor === 'object' ? ct.solidColor : {}
1748
+ themeSetting.solidColor = WAProto_1.proto.Message.ChatSolidColorWallpaper.fromObject({
1749
+ colorLight: sc.colorLight || sc.color || '#FFFFFF',
1750
+ colorDark: sc.colorDark || sc.color || '#000000',
1751
+ isDoodleEnabled: sc.isDoodleEnabled ?? false
1752
+ })
1753
+ } else if (ct.stockImage) {
1754
+ const si = typeof ct.stockImage === 'object' ? ct.stockImage : {}
1755
+ themeSetting.stockImage = WAProto_1.proto.Message.ChatStockImageWallpaper.fromObject({
1756
+ stockImageId: si.id || si.stockImageId || String(ct.stockImage),
1757
+ dimLevel: (si.dimLevel ?? si.opacity != null) ? (si.opacity ?? 100) / 100 : 0
1758
+ })
1759
+ } else if (ct.defaultWallpaper) {
1760
+ themeSetting.defaultWallpaper = WAProto_1.proto.Message.ChatDefaultWallpaper.fromObject({
1761
+ isDoodleEnabled: ct.defaultWallpaper.isDoodleEnabled ?? false
1762
+ })
1763
+ } else if (ct.customImage) {
1764
+ themeSetting.customImage = WAProto_1.proto.Message.ChatCustomImageWallpaper.fromObject({
1765
+ directPath: ct.customImage.directPath,
1766
+ mediaKey: ct.customImage.mediaKey,
1767
+ fileEncSha256: ct.customImage.fileEncSha256,
1768
+ fileSha256: ct.customImage.fileSha256,
1769
+ dimLevel: ct.customImage.dimLevel ?? 0
1770
+ })
1771
+ }
1772
+ m.protocolMessage = {
1773
+ type: WAProto_1.proto.Message.ProtocolMessage.Type.CHAT_THEME_SETTING,
1774
+ chatThemeSetting: WAProto_1.proto.Message.ChatThemeSetting.fromObject(themeSetting)
1775
+ }
1776
+ }
1777
+ if ('stopGeneration' in message) {
1778
+ const sg = message.stopGeneration
1779
+ m.protocolMessage = {
1780
+ type: WAProto_1.proto.Message.ProtocolMessage.Type.STOP_GENERATION_MESSAGE,
1781
+ key: sg.key || sg
1782
+ }
1783
+ }
1784
+ if ('unscheduleMessage' in message) {
1785
+ const us = message.unscheduleMessage
1786
+ m.protocolMessage = {
1787
+ type: WAProto_1.proto.Message.ProtocolMessage.Type.MESSAGE_UNSCHEDULE,
1788
+ key: us.key || us
1789
+ }
1790
+ }
1791
+ if ('bcall' in message || 'bcallMessage' in message) {
1792
+ const bc = message.bcall || message.bcallMessage
1793
+ const mediaTypeEnum = WAProto_1.proto.Message.BCallMessage.MediaType
1794
+ const mediaTypeMap = { audio: mediaTypeEnum.AUDIO, video: mediaTypeEnum.VIDEO }
1795
+ m.bcallMessage = WAProto_1.proto.Message.BCallMessage.fromObject({
1796
+ sessionId: bc.sessionId || (0, crypto_1.randomUUID)(),
1797
+ mediaType:
1798
+ typeof bc.mediaType === 'string'
1799
+ ? (mediaTypeMap[bc.mediaType.toLowerCase()] ?? mediaTypeEnum.AUDIO)
1800
+ : (bc.mediaType ?? mediaTypeEnum.AUDIO),
1801
+ masterKey: bc.masterKey || (0, crypto_1.randomBytes)(32),
1802
+ caption: bc.caption || ''
1803
+ })
1804
+ }
1805
+ if ('liveLocationUpdate' in message) {
1806
+ const llu = message.liveLocationUpdate
1807
+ m.liveLocationMessage = WAProto_1.proto.Message.LiveLocationMessage.fromObject({
1808
+ degreesLatitude: llu.latitude,
1809
+ degreesLongitude: llu.longitude,
1810
+ accuracyInMeters: llu.accuracy,
1811
+ speedInMps: llu.speed,
1812
+ degreesClockwiseFromMagneticNorth: llu.heading,
1813
+ sequenceNumber: llu.sequence || 1,
1814
+ timeOffset: llu.timeOffset || 0,
1815
+ jpegThumbnail: llu.thumbnail
1816
+ })
1817
+ }
1818
+ if ('stopLiveLocation' in message) {
1819
+ const sll = message.stopLiveLocation
1820
+ m.protocolMessage = {
1821
+ type: WAProto_1.proto.Message.ProtocolMessage.Type.REVOKE,
1822
+ key: sll.key || sll
1823
+ }
1824
+ }
1825
+ if ('carousel' in message || 'carouselMessage' in message) {
1826
+ const c = message.carousel || message.carouselMessage
1827
+ if (!Array.isArray(c.cards) || !c.cards.length) {
1828
+ throw new boom_1.Boom('carousel requires at least one card', { statusCode: 400 })
1829
+ }
1830
+ const cardTypeEnum = WAProto_1.proto.Message.InteractiveMessage.CarouselMessage.CarouselCardType
1831
+ const cardTypeMap = {
1832
+ horizontal: cardTypeEnum.HSCROLL_CARDS,
1833
+ album: cardTypeEnum.ALBUM_IMAGE,
1834
+ hscroll: cardTypeEnum.HSCROLL_CARDS
1835
+ }
1836
+ m.interactiveMessage = {
1837
+ carouselMessage: WAProto_1.proto.Message.InteractiveMessage.CarouselMessage.fromObject({
1838
+ cards: c.cards.map(card => card.interactiveMessage || card),
1839
+ messageVersion: c.messageVersion || 1,
1840
+ carouselCardType:
1841
+ typeof c.cardType === 'string'
1842
+ ? (cardTypeMap[c.cardType.toLowerCase()] ?? cardTypeEnum.HSCROLL_CARDS)
1843
+ : (c.carouselCardType ?? cardTypeEnum.HSCROLL_CARDS)
1844
+ })
1845
+ }
1846
+ }
1847
+ if ('aiMediaCollection' in message || 'aiMediaCollectionMessage' in message) {
1848
+ const amc = message.aiMediaCollection || message.aiMediaCollectionMessage
1849
+ m.protocolMessage = {
1850
+ type: WAProto_1.proto.Message.ProtocolMessage.Type.AI_MEDIA_COLLECTION_MESSAGE,
1851
+ aiMediaCollectionMessage: WAProto_1.proto.AIMediaCollectionMessage.fromObject({
1852
+ collectionId: amc.collectionId || (0, crypto_1.randomUUID)(),
1853
+ expectedMediaCount: amc.expectedMediaCount || amc.count || 1,
1854
+ hasGlobalCaption: amc.hasGlobalCaption ?? false
1855
+ })
1856
+ }
1857
+ }
1858
+ if ('botCapabilities' in message && Array.isArray(message.botCapabilities) && message.botCapabilities.length) {
1859
+ const capEnum = WAProto_1.proto.BotCapabilityMetadata.BotCapabilityType
1860
+ const capStrMap = Object.fromEntries(Object.entries(capEnum).map(([k, v]) => [k.toLowerCase(), v]))
1861
+ const caps = message.botCapabilities
1862
+ .map(c => (typeof c === 'string' ? (capStrMap[c.toLowerCase()] ?? capEnum.UNKNOWN) : c))
1863
+ .filter(c => typeof c === 'number' && c !== capEnum.UNKNOWN)
1864
+ m.messageContextInfo = m.messageContextInfo || {}
1865
+ m.messageContextInfo.botMetadata = WAProto_1.proto.BotMetadata.fromObject({
1866
+ capabilityMetadata: { capabilities: caps }
1867
+ })
1868
+ }
1869
+ if ('botThreadInfo' in message && message.botThreadInfo) {
1870
+ const bt = message.botThreadInfo
1871
+ const threadTypeEnum = WAProto_1.proto.AIThreadInfo.AIThreadClientInfo.AIThreadType
1872
+ const threadTypeMap = Object.fromEntries(Object.entries(threadTypeEnum).map(([k, v]) => [k.toLowerCase(), v]))
1873
+ const existing = m.messageContextInfo?.botMetadata
1874
+ ? WAProto_1.proto.BotMetadata.toObject(m.messageContextInfo.botMetadata)
1875
+ : {}
1876
+ m.messageContextInfo = m.messageContextInfo || {}
1877
+ m.messageContextInfo.botMetadata = WAProto_1.proto.BotMetadata.fromObject({
1878
+ ...existing,
1879
+ botThreadInfo: {
1880
+ clientInfo: {
1881
+ type: typeof bt.type === 'string'
1882
+ ? (threadTypeMap[bt.type.toLowerCase()] ?? threadTypeEnum.UNKNOWN)
1883
+ : (bt.type ?? threadTypeEnum.UNKNOWN),
1884
+ sourceChatJid: bt.sourceChatJid || ''
1885
+ }
1886
+ }
1887
+ })
1888
+ }
1889
+ if ('messageAssociation' in message && message.messageAssociation) {
1890
+ const ma = message.messageAssociation
1891
+ const assocTypeEnum = WAProto_1.proto.MessageAssociation.AssociationType
1892
+ const assocTypeMap = Object.fromEntries(Object.entries(assocTypeEnum).map(([k, v]) => [k.toLowerCase(), v]))
1893
+ m.messageContextInfo = m.messageContextInfo || {}
1894
+ m.messageContextInfo.messageAssociation = WAProto_1.proto.MessageAssociation.fromObject({
1895
+ associationType:
1896
+ typeof ma.type === 'string'
1897
+ ? (assocTypeMap[ma.type.toLowerCase()] ?? assocTypeEnum.UNKNOWN)
1898
+ : (ma.associationType ?? ma.type ?? assocTypeEnum.UNKNOWN),
1899
+ parentMessageKey: ma.parentMessageKey || ma.parentKey,
1900
+ messageIndex: ma.messageIndex || 0
1901
+ })
1902
+ }
1903
+ if ('threadId' in message && message.threadId) {
1904
+ const tid = message.threadId
1905
+ const threadTypeEnum = WAProto_1.proto.ThreadID.ThreadType
1906
+ m.messageContextInfo = m.messageContextInfo || {}
1907
+ m.messageContextInfo.threadId = [
1908
+ WAProto_1.proto.ThreadID.fromObject({
1909
+ threadType:
1910
+ tid.type === 'ai'
1911
+ ? threadTypeEnum.AI_THREAD
1912
+ : tid.type === 'replies'
1913
+ ? threadTypeEnum.VIEW_REPLIES
1914
+ : (tid.threadType ?? threadTypeEnum.UNKNOWN),
1915
+ threadKey: tid.key || tid.threadKey
1916
+ })
1917
+ ]
1918
+ }
1919
+ if ('featureEligibilities' in message && message.featureEligibilities) {
1920
+ const fe = message.featureEligibilities
1921
+ const [msgType] = Object.keys(m)
1922
+ if (msgType && m[msgType] && typeof m[msgType] === 'object') {
1923
+ m[msgType].contextInfo = {
1924
+ ...(m[msgType].contextInfo || {}),
1925
+ featureEligibilities: {
1926
+ cannotBeReactedTo: fe.cannotBeReactedTo ?? false,
1927
+ cannotBeRanked: fe.cannotBeRanked ?? false,
1928
+ canRequestFeedback: fe.canRequestFeedback ?? false,
1929
+ canBeReshared: fe.canBeReshared ?? true,
1930
+ canReceiveMultiReact: fe.canReceiveMultiReact ?? true
1931
+ }
1932
+ }
1933
+ }
1934
+ }
1935
+ if ('forwardOrigin' in message) {
1936
+ const [msgType] = Object.keys(m)
1937
+ if (msgType && m[msgType] && typeof m[msgType] === 'object') {
1938
+ const foEnum = WAProto_1.proto.ContextInfo.ForwardOrigin
1939
+ const foMap = {
1940
+ chat: foEnum.CHAT,
1941
+ status: foEnum.STATUS,
1942
+ channels: foEnum.CHANNELS,
1943
+ meta_ai: foEnum.META_AI,
1944
+ ugc: foEnum.UGC
1945
+ }
1946
+ m[msgType].contextInfo = {
1947
+ ...(m[msgType].contextInfo || {}),
1948
+ forwardOrigin:
1949
+ typeof message.forwardOrigin === 'string'
1950
+ ? (foMap[message.forwardOrigin.toLowerCase()] ?? foEnum.CHAT)
1951
+ : (message.forwardOrigin ?? foEnum.CHAT)
1952
+ }
1953
+ }
1954
+ }
1955
+ if ('statusAudienceMetadata' in message && message.statusAudienceMetadata) {
1956
+ const [msgType] = Object.keys(m)
1957
+ if (msgType && m[msgType] && typeof m[msgType] === 'object') {
1958
+ const audType = WAProto_1.proto.ContextInfo.StatusAudienceMetadata.AudienceType
1959
+ const sam = message.statusAudienceMetadata
1960
+ m[msgType].contextInfo = {
1961
+ ...(m[msgType].contextInfo || {}),
1962
+ statusAudienceMetadata: {
1963
+ audienceType: sam.closeFriends ? audType.CLOSE_FRIENDS : audType.UNKNOWN,
1964
+ listName: sam.listName,
1965
+ listEmoji: sam.listEmoji
1966
+ }
1967
+ }
1968
+ }
1969
+ }
1970
+ if ('aiGenerated' in message && message.aiGenerated === true) {
1971
+ if (m.videoMessage) {
1972
+ m.videoMessage.videoSourceType = WAProto_1.proto.Message.VideoMessage.VideoSourceType.AI_GENERATED
1973
+ }
1974
+ if (m.imageMessage) {
1975
+ m.imageMessage.imageSourceType = WAProto_1.proto.Message.ImageMessage.ImageSourceType.AI_GENERATED
1976
+ }
1977
+ }
1978
+ if ('aiModified' in message && message.aiModified === true) {
1979
+ if (m.imageMessage) {
1980
+ m.imageMessage.imageSourceType = WAProto_1.proto.Message.ImageMessage.ImageSourceType.AI_MODIFIED
1981
+ }
1982
+ }
1983
+ if ('imageSourceType' in message) {
1984
+ if (m.imageMessage) {
1985
+ const istEnum = WAProto_1.proto.Message.ImageMessage.ImageSourceType
1986
+ const istMap = {
1987
+ user: istEnum.USER_IMAGE,
1988
+ ai_generated: istEnum.AI_GENERATED,
1989
+ ai_modified: istEnum.AI_MODIFIED,
1990
+ rasterized: istEnum.RASTERIZED_TEXT_STATUS
1991
+ }
1992
+ m.imageMessage.imageSourceType =
1993
+ typeof message.imageSourceType === 'string'
1994
+ ? (istMap[message.imageSourceType.toLowerCase()] ?? istEnum.USER_IMAGE)
1995
+ : (message.imageSourceType ?? istEnum.USER_IMAGE)
1996
+ }
1997
+ }
1998
+ if ('qrUrl' in message && message.qrUrl && m.imageMessage) {
1999
+ m.imageMessage.qrUrl = message.qrUrl
2000
+ }
2001
+ if ('videoContentUrl' in message && message.videoContentUrl && m.extendedTextMessage) {
2002
+ m.extendedTextMessage.videoContentUrl = message.videoContentUrl
2003
+ }
2004
+ if ('endCardTiles' in message && Array.isArray(message.endCardTiles) && m.extendedTextMessage) {
2005
+ m.extendedTextMessage.endCardTiles = message.endCardTiles.map(tile =>
2006
+ WAProto_1.proto.Message.VideoEndCard.fromObject({
2007
+ username: tile.username || tile.user,
2008
+ caption: tile.caption || tile.text,
2009
+ thumbnailImageUrl: tile.thumbnailUrl || tile.thumbnail,
2010
+ profilePictureUrl: tile.profilePictureUrl || tile.avatar
2011
+ })
2012
+ )
2013
+ }
2014
+ if ('musicMetadata' in message && message.musicMetadata && m.extendedTextMessage) {
2015
+ const mm = message.musicMetadata
2016
+ m.extendedTextMessage.musicMetadata = WAProto_1.proto.EmbeddedMusic.fromObject({
2017
+ songId: mm.songId,
2018
+ author: mm.author || mm.artist,
2019
+ title: mm.title,
2020
+ artistAttribution: mm.artistAttribution,
2021
+ isExplicit: mm.isExplicit ?? false,
2022
+ musicSongStartTimeInMs: mm.startTimeMs || 0,
2023
+ derivedContentStartTimeInMs: mm.derivedStartTimeMs || 0,
2024
+ overlapDurationInMs: mm.overlapDurationMs || 0
2025
+ })
2026
+ }
2027
+ if ('businessInteractionPills' in message && message.businessInteractionPills) {
2028
+ const bip = message.businessInteractionPills
2029
+ const pillTypeEnum = WAProto_1.proto.ContextInfo.BusinessInteractionPills.PillType
2030
+ const entryEnum = WAProto_1.proto.ContextInfo.BusinessInteractionPills.EntryPoint
2031
+ const pillMap = Object.fromEntries(Object.entries(pillTypeEnum).map(([k, v]) => [k.toLowerCase(), v]))
2032
+ const entryMap = Object.fromEntries(Object.entries(entryEnum).map(([k, v]) => [k.toLowerCase(), v]))
2033
+ const [msgType] = Object.keys(m)
2034
+ if (msgType && m[msgType] && typeof m[msgType] === 'object') {
2035
+ m[msgType].contextInfo = {
2036
+ ...(m[msgType].contextInfo || {}),
2037
+ businessInteractionPills: WAProto_1.proto.ContextInfo.BusinessInteractionPills.fromObject({
2038
+ businessJid: bip.businessJid,
2039
+ entryPoint:
2040
+ typeof bip.entryPoint === 'string'
2041
+ ? (entryMap[bip.entryPoint.toLowerCase()] ?? entryEnum.ENTRY_POINT_UNKNOWN)
2042
+ : bip.entryPoint,
2043
+ pills: (bip.pills || []).map(p => ({
2044
+ pillType:
2045
+ typeof p.type === 'string'
2046
+ ? (pillMap[p.type.toLowerCase()] ?? pillTypeEnum.UNKNOWN)
2047
+ : (p.pillType ?? pillTypeEnum.UNKNOWN),
2048
+ actionUrl: p.url || p.actionUrl
2049
+ }))
2050
+ })
2051
+ }
2052
+ }
2053
+ }
2054
+ if ('dataSharingContext' in message && message.dataSharingContext) {
2055
+ const dsc = message.dataSharingContext
2056
+ const [msgType] = Object.keys(m)
2057
+ if (msgType && m[msgType] && typeof m[msgType] === 'object') {
2058
+ m[msgType].contextInfo = {
2059
+ ...(m[msgType].contextInfo || {}),
2060
+ dataSharingContext: WAProto_1.proto.ContextInfo.DataSharingContext.fromObject({
2061
+ showMmDisclosure: dsc.showMmDisclosure ?? false,
2062
+ encryptedSignalTokenConsented: dsc.encryptedToken,
2063
+ dataSharingFlags: dsc.flags || 0
2064
+ })
2065
+ }
2066
+ }
2067
+ }
2068
+ if ('botSession' in message && message.botSession) {
2069
+ const bs = message.botSession
2070
+ const srcEnum = WAProto_1.proto.BotSessionSource
2071
+ const srcMap = Object.fromEntries(Object.entries(srcEnum).map(([k, v]) => [k.toLowerCase(), v]))
2072
+ m.messageContextInfo = m.messageContextInfo || {}
2073
+ const existing = m.messageContextInfo.botMetadata
2074
+ ? WAProto_1.proto.BotMetadata.toObject(m.messageContextInfo.botMetadata)
2075
+ : {}
2076
+ m.messageContextInfo.botMetadata = WAProto_1.proto.BotMetadata.fromObject({
2077
+ ...existing,
2078
+ sessionMetadata: {
2079
+ sessionId: bs.sessionId,
2080
+ sessionSource:
2081
+ typeof bs.source === 'string'
2082
+ ? (srcMap[bs.source.toLowerCase()] ?? srcEnum.NONE)
2083
+ : (bs.sessionSource ?? srcEnum.NONE)
2084
+ }
2085
+ })
2086
+ }
2087
+ if ('botReminder' in message && message.botReminder) {
2088
+ const br = message.botReminder
2089
+ const actionEnum = WAProto_1.proto.BotReminderMetadata.ReminderAction
2090
+ const freqEnum = WAProto_1.proto.BotReminderMetadata.ReminderFrequency
2091
+ const actionMap = {
2092
+ notify: actionEnum.NOTIFY,
2093
+ create: actionEnum.CREATE,
2094
+ delete: actionEnum.DELETE,
2095
+ update: actionEnum.UPDATE
2096
+ }
2097
+ const freqMap = {
2098
+ once: freqEnum.ONCE,
2099
+ daily: freqEnum.DAILY,
2100
+ weekly: freqEnum.WEEKLY,
2101
+ biweekly: freqEnum.BIWEEKLY,
2102
+ monthly: freqEnum.MONTHLY
2103
+ }
2104
+ m.messageContextInfo = m.messageContextInfo || {}
2105
+ const existing = m.messageContextInfo.botMetadata
2106
+ ? WAProto_1.proto.BotMetadata.toObject(m.messageContextInfo.botMetadata)
2107
+ : {}
2108
+ m.messageContextInfo.botMetadata = WAProto_1.proto.BotMetadata.fromObject({
2109
+ ...existing,
2110
+ reminderMetadata: {
2111
+ requestMessageKey: br.requestMessageKey || br.key,
2112
+ action:
2113
+ typeof br.action === 'string'
2114
+ ? (actionMap[br.action.toLowerCase()] ?? actionEnum.NOTIFY)
2115
+ : (br.action ?? actionEnum.NOTIFY),
2116
+ name: br.name,
2117
+ nextTriggerTimestamp: br.nextTriggerTimestamp || br.timestamp,
2118
+ frequency:
2119
+ typeof br.frequency === 'string'
2120
+ ? (freqMap[br.frequency.toLowerCase()] ?? freqEnum.ONCE)
2121
+ : (br.frequency ?? freqEnum.ONCE)
2122
+ }
2123
+ })
2124
+ }
2125
+ if ('botPlugin' in message && message.botPlugin) {
2126
+ const bp = message.botPlugin
2127
+ const ptEnum = WAProto_1.proto.BotPluginMetadata.PluginType
2128
+ const spEnum = WAProto_1.proto.BotPluginMetadata.SearchProvider
2129
+ const ptMap = { reels: ptEnum.REELS, search: ptEnum.SEARCH }
2130
+ const spMap = { bing: spEnum.BING, google: spEnum.GOOGLE, support: spEnum.SUPPORT }
2131
+ m.messageContextInfo = m.messageContextInfo || {}
2132
+ const existing = m.messageContextInfo.botMetadata
2133
+ ? WAProto_1.proto.BotMetadata.toObject(m.messageContextInfo.botMetadata)
2134
+ : {}
2135
+ m.messageContextInfo.botMetadata = WAProto_1.proto.BotMetadata.fromObject({
2136
+ ...existing,
2137
+ pluginMetadata: WAProto_1.proto.BotPluginMetadata.fromObject({
2138
+ provider:
2139
+ typeof bp.provider === 'string'
2140
+ ? (spMap[bp.provider.toLowerCase()] ?? spEnum.UNKNOWN)
2141
+ : (bp.provider ?? spEnum.UNKNOWN),
2142
+ pluginType:
2143
+ typeof bp.pluginType === 'string'
2144
+ ? (ptMap[bp.pluginType.toLowerCase()] ?? ptEnum.UNKNOWN_PLUGIN)
2145
+ : (bp.pluginType ?? ptEnum.UNKNOWN_PLUGIN),
2146
+ searchProviderUrl: bp.searchProviderUrl,
2147
+ searchQuery: bp.searchQuery,
2148
+ thumbnailCdnUrl: bp.thumbnailCdnUrl,
2149
+ profilePhotoCdnUrl: bp.profilePhotoCdnUrl,
2150
+ expectedLinksCount: bp.expectedLinksCount || 0,
2151
+ referenceIndex: bp.referenceIndex || 0
2152
+ })
2153
+ })
2154
+ }
2155
+ if ('isSpoiler' in message && message.isSpoiler === true) {
2156
+ const [msgType] = Object.keys(m)
2157
+ if (msgType && m[msgType] && typeof m[msgType] === 'object') {
2158
+ m[msgType].contextInfo = {
2159
+ ...(m[msgType].contextInfo || {}),
2160
+ isSpoiler: true
2161
+ }
2162
+ }
2163
+ }
2164
+ if ('expiration' in message && typeof message.expiration === 'number') {
2165
+ const [msgType] = Object.keys(m)
2166
+ if (msgType && m[msgType] && typeof m[msgType] === 'object') {
2167
+ m[msgType].contextInfo = {
2168
+ ...(m[msgType].contextInfo || {}),
2169
+ expiration: message.expiration
2170
+ }
2171
+ }
2172
+ }
2173
+ if ('ephemeralSettingTimestamp' in message) {
2174
+ const [msgType] = Object.keys(m)
2175
+ if (msgType && m[msgType] && typeof m[msgType] === 'object') {
2176
+ m[msgType].contextInfo = {
2177
+ ...(m[msgType].contextInfo || {}),
2178
+ ephemeralSettingTimestamp: message.ephemeralSettingTimestamp
2179
+ }
2180
+ }
2181
+ }
2182
+ if ('groupSubject' in message && message.groupSubject) {
2183
+ const [msgType] = Object.keys(m)
2184
+ if (msgType && m[msgType] && typeof m[msgType] === 'object') {
2185
+ m[msgType].contextInfo = {
2186
+ ...(m[msgType].contextInfo || {}),
2187
+ groupSubject: message.groupSubject
2188
+ }
2189
+ }
2190
+ }
2191
+ if ('parentGroupJid' in message && message.parentGroupJid) {
2192
+ const [msgType] = Object.keys(m)
2193
+ if (msgType && m[msgType] && typeof m[msgType] === 'object') {
2194
+ m[msgType].contextInfo = {
2195
+ ...(m[msgType].contextInfo || {}),
2196
+ parentGroupJid: message.parentGroupJid
2197
+ }
2198
+ }
2199
+ }
2200
+ if ('memberLabel' in message && message.memberLabel) {
2201
+ const [msgType] = Object.keys(m)
2202
+ if (msgType && m[msgType] && typeof m[msgType] === 'object') {
2203
+ m[msgType].contextInfo = {
2204
+ ...(m[msgType].contextInfo || {}),
2205
+ memberLabel: message.memberLabel
2206
+ }
2207
+ }
2208
+ }
2209
+ if ('trustBanner' in message && message.trustBanner) {
2210
+ const tb = message.trustBanner
2211
+ const [msgType] = Object.keys(m)
2212
+ if (msgType && m[msgType] && typeof m[msgType] === 'object') {
2213
+ m[msgType].contextInfo = {
2214
+ ...(m[msgType].contextInfo || {}),
2215
+ ...(tb.type != null ? { trustBannerType: tb.type } : {}),
2216
+ ...(tb.action != null ? { trustBannerAction: tb.action } : {})
2217
+ }
2218
+ }
2219
+ }
2220
+ if ('entryPoint' in message && message.entryPoint) {
2221
+ const ep = message.entryPoint
2222
+ const [msgType] = Object.keys(m)
2223
+ if (msgType && m[msgType] && typeof m[msgType] === 'object') {
2224
+ m[msgType].contextInfo = {
2225
+ ...(m[msgType].contextInfo || {}),
2226
+ ...(ep.source ? { entryPointConversionSource: ep.source } : {}),
2227
+ ...(ep.app ? { entryPointConversionApp: ep.app } : {}),
2228
+ ...(ep.delaySecs != null ? { entryPointConversionDelaySeconds: ep.delaySecs } : {}),
2229
+ ...(ep.externalSource ? { entryPointConversionExternalSource: ep.externalSource } : {}),
2230
+ ...(ep.externalMedium ? { entryPointConversionExternalMedium: ep.externalMedium } : {})
2231
+ }
2232
+ }
2233
+ }
2234
+ if ('utm' in message && message.utm) {
2235
+ const [msgType] = Object.keys(m)
2236
+ if (msgType && m[msgType] && typeof m[msgType] === 'object') {
2237
+ m[msgType].contextInfo = {
2238
+ ...(m[msgType].contextInfo || {}),
2239
+ utm: WAProto_1.proto.ContextInfo.UTMInfo.fromObject({
2240
+ utmSource: message.utm.source,
2241
+ utmCampaign: message.utm.campaign
2242
+ })
2243
+ }
2244
+ }
2245
+ }
2246
+ if ('partiallySelectedContent' in message && message.partiallySelectedContent) {
2247
+ const [msgType] = Object.keys(m)
2248
+ if (msgType && m[msgType] && typeof m[msgType] === 'object') {
2249
+ m[msgType].contextInfo = {
2250
+ ...(m[msgType].contextInfo || {}),
2251
+ partiallySelectedContent: message.partiallySelectedContent
2252
+ }
2253
+ }
2254
+ }
2255
+ if ('crossAppSource' in message && message.crossAppSource) {
2256
+ const [msgType] = Object.keys(m)
2257
+ if (msgType && m[msgType] && typeof m[msgType] === 'object') {
2258
+ m[msgType].contextInfo = {
2259
+ ...(m[msgType].contextInfo || {}),
2260
+ crossAppSource: message.crossAppSource
2261
+ }
2262
+ }
2263
+ }
2264
+ if ('isQuestion' in message && message.isQuestion === true) {
2265
+ const [msgType] = Object.keys(m)
2266
+ if (msgType && m[msgType] && typeof m[msgType] === 'object') {
2267
+ m[msgType].contextInfo = {
2268
+ ...(m[msgType].contextInfo || {}),
2269
+ isQuestion: true
2270
+ }
2271
+ }
2272
+ }
2273
+ if ('afterReadDuration' in message && typeof message.afterReadDuration === 'number') {
2274
+ const [msgType] = Object.keys(m)
2275
+ if (msgType && m[msgType] && typeof m[msgType] === 'object') {
2276
+ m[msgType].contextInfo = {
2277
+ ...(m[msgType].contextInfo || {}),
2278
+ afterReadDuration: message.afterReadDuration
2279
+ }
2280
+ }
2281
+ m.messageContextInfo = {
2282
+ ...(m.messageContextInfo || {}),
2283
+ messageAddOnDurationInSecs: message.afterReadDuration,
2284
+ messageAddOnExpiryType: index_js_1.proto.MessageContextInfo.MessageAddonExpiryType.DEPENDENT_ON_PARENT
2285
+ }
2286
+ }
2287
+ if ('raw' in message && !!message.raw) {
2288
+ const { raw: _, externalAdReply: _ear, ...rawMsg } = message
2289
+ if ('externalAdReply' in message && !!message.externalAdReply) {
2290
+ const ear = normalizeEarFields(message.externalAdReply)
2291
+ const [rawType] = Object.keys(rawMsg)
2292
+ if (rawType && rawMsg[rawType]) {
2293
+ rawMsg[rawType].contextInfo = {
2294
+ ...(rawMsg[rawType].contextInfo || {}),
2295
+ externalAdReply: ear
2296
+ }
2297
+ }
2298
+ }
2299
+ return WAProto_1.proto.Message.fromObject(rawMsg)
2300
+ } else if (Object.keys(m).length === 0) {
2301
+ m = await (0, exports.prepareWAMessageMedia)(message, options)
2302
+ }
2303
+
2304
+ if (hasOptionalProperty(message, 'viewOnce') && !!message.viewOnce) {
2305
+ const viewOnceVersion = message.viewOnceVersion || message.viewOnce
2306
+ if (viewOnceVersion === 'v2' || viewOnceVersion === 2) {
2307
+ m = { viewOnceMessageV2: { message: m } }
2308
+ } else if (viewOnceVersion === 'v2ext' || viewOnceVersion === 'v2extension') {
2309
+ m = { viewOnceMessageV2Extension: { message: m } }
2310
+ } else {
2311
+ m = { viewOnceMessage: { message: m } }
2312
+ }
2313
+ }
2314
+ if (hasOptionalProperty(message, 'documentWithCaption') && !!message.documentWithCaption) {
2315
+ m = { documentWithCaptionMessage: { message: m } }
2316
+ }
2317
+ if (hasOptionalProperty(message, 'ephemeral') && !!message.ephemeral) {
2318
+ m = { ephemeralMessage: { message: m } }
2319
+ }
2320
+ if (hasOptionalProperty(message, 'groupMentioned') && !!message.groupMentioned) {
2321
+ m = { groupMentionedMessage: { message: m } }
2322
+ }
2323
+ if ('groupStatus' in message && !!message.groupStatus) {
2324
+ m = { groupStatusMessage: { message: m } }
2325
+ }
2326
+ if (
2327
+ (hasOptionalProperty(message, 'mentions') && message.mentions?.length) ||
2328
+ (hasOptionalProperty(message, 'mentionAll') && message.mentionAll)
2329
+ ) {
2330
+ const normalizedMentions = await (0, jid_display_normalization_1.normalizeMentionedJidsForSend)(
2331
+ message.mentions,
2332
+ options.groupData,
2333
+ options.signalRepository,
2334
+ options.logger
2335
+ )
2336
+ const messageType = Object.keys(m)[0]
2337
+ const key = m[messageType]
2338
+ if (key && 'contextInfo' in key) {
2339
+ key.contextInfo = key.contextInfo || {}
2340
+ if (normalizedMentions?.length) {
2341
+ key.contextInfo.mentionedJid = normalizedMentions
2342
+ }
2343
+ if (message.mentionAll) {
2344
+ key.contextInfo.nonJidMentions = 1
2345
+ } else if (!key) {
2346
+ key.contextInfo = {
2347
+ mentionedJid: normalizedMentions,
2348
+ nonJidMentions: message.mentionAll ? 1 : 0
2349
+ }
2350
+ }
2351
+ }
2352
+ }
2353
+ if (hasOptionalProperty(message, 'edit')) {
2354
+ m = {
2355
+ protocolMessage: {
2356
+ key: message.edit,
2357
+ editedMessage: m,
2358
+ timestampMs: Date.now(),
2359
+ type: WAProto_1.proto.Message.ProtocolMessage.Type.MESSAGE_EDIT
2360
+ }
2361
+ }
2362
+ }
2363
+ if (hasOptionalProperty(message, 'contextInfo') && !!message.contextInfo) {
2364
+ const messageType = Object.keys(m)[0]
2365
+ const key = m[messageType]
2366
+ if ('contextInfo' in key && !!key.contextInfo) {
2367
+ key.contextInfo = { ...key.contextInfo, ...message.contextInfo }
2368
+ } else if (key) {
2369
+ key.contextInfo = message.contextInfo
2370
+ }
2371
+ }
2372
+ if ((0, reporting_utils_1.shouldIncludeReportingToken)(m) && !(0, WABinary_1.isInteropUser)(options.jid)) {
2373
+ m.messageContextInfo = m.messageContextInfo || {}
2374
+ if (!m.messageContextInfo.messageSecret) {
2375
+ m.messageContextInfo.messageSecret = (0, crypto_1.randomBytes)(32)
2376
+ }
2377
+ }
2378
+
2379
+ if ('externalAdReply' in message && !!message.externalAdReply) {
2380
+ const wrappers = [
2381
+ 'viewOnceMessage',
2382
+ 'viewOnceMessageV2',
2383
+ 'viewOnceMessageV2Extension',
2384
+ 'ephemeralMessage',
2385
+ 'groupStatusMessage',
2386
+ 'templateMessage'
2387
+ ]
2388
+ const [outerType] = Object.keys(m)
2389
+ const inner = wrappers.includes(outerType) ? m[outerType].message : m
2390
+ const [innerType] = Object.keys(inner)
2391
+ const innerPayload = innerType ? inner[innerType] : undefined
2392
+ if (innerType && innerType !== 'carouselMessage' && innerPayload && typeof innerPayload === 'object') {
2393
+ const ear = normalizeEarFields(message.externalAdReply)
2394
+ innerPayload.contextInfo = {
2395
+ ...(innerPayload.contextInfo || {}),
2396
+ externalAdReply: ear
2397
+ }
2398
+ }
2399
+ }
2400
+ if ('secureMetaServiceLabel' in message && !!message.secureMetaServiceLabel) {
2401
+ const [messageType] = Object.keys(m)
2402
+ m[messageType] = m[messageType] || {}
2403
+ m[messageType].contextInfo = {
2404
+ ...(m[messageType].contextInfo || {}),
2405
+ secureMetaServiceLabel: 1
2406
+ }
2407
+ }
2408
+
2409
+ return WAProto_1.proto.Message.create(m)
2410
+ }
2411
+ exports.generateWAMessageContent = generateWAMessageContent
2412
+ const generateWAMessageFromContent = (jid, message, options) => {
2413
+ // set timestamp to now
2414
+ // if not specified
2415
+ if (!options.timestamp) {
2416
+ options.timestamp = new Date()
2417
+ }
2418
+ const innerMessage = (0, exports.normalizeMessageContent)(message)
2419
+ const key = (0, exports.getContentType)(innerMessage)
2420
+ const timestamp = (0, generics_1.unixTimestampSeconds)(options.timestamp)
2421
+ const { quoted, userJid } = options
2422
+ if (quoted && !(0, WABinary_1.isJidNewsletter)(jid)) {
2423
+ const participant = quoted.key.fromMe
2424
+ ? userJid // TODO: Add support for LIDs
2425
+ : quoted.participant || quoted.key.participant || quoted.key.remoteJid
2426
+ let quotedMsg = (0, exports.normalizeMessageContent)(quoted.message)
2427
+ const msgType = (0, exports.getContentType)(quotedMsg)
2428
+ // strip any redundant properties
2429
+ quotedMsg = index_js_1.proto.Message.create({ [msgType]: quotedMsg[msgType] })
2430
+ const quotedContent = quotedMsg[msgType]
2431
+ if (typeof quotedContent === 'object' && quotedContent && 'contextInfo' in quotedContent) {
2432
+ delete quotedContent.contextInfo
2433
+ }
2434
+ const contextInfo = ('contextInfo' in innerMessage[key] && innerMessage[key]?.contextInfo) || {}
2435
+ contextInfo.participant = (0, WABinary_1.jidNormalizedUser)(participant)
2436
+ contextInfo.stanzaId = quoted.key.id
2437
+ contextInfo.quotedMessage = quotedMsg
2438
+ // if a participant is quoted, then it must be a group
2439
+ // hence, remoteJid of group must also be entered
2440
+ if (jid !== quoted.key.remoteJid) {
2441
+ contextInfo.remoteJid = quoted.key.remoteJid
2442
+ }
2443
+ if (contextInfo && innerMessage[key]) {
2444
+ /* @ts-ignore */
2445
+ innerMessage[key].contextInfo = contextInfo
2446
+ }
2447
+ }
2448
+ if (
2449
+ // if we want to send a disappearing message
2450
+ !!options.ephemeralExpiration &&
2451
+ // and it's not a protocol message -- delete, toggle disappear message
2452
+ key !== 'protocolMessage' &&
2453
+ // already not converted to disappearing message
2454
+ key !== 'ephemeralMessage' &&
2455
+ // newsletters don't support ephemeral messages
2456
+ !(0, WABinary_1.isJidNewsletter)(jid)
2457
+ ) {
2458
+ /* @ts-ignore */
2459
+ innerMessage[key].contextInfo = {
2460
+ ...(innerMessage[key].contextInfo || {}),
2461
+ expiration: options.ephemeralExpiration || Defaults_1.WA_DEFAULT_EPHEMERAL
2462
+ //ephemeralSettingTimestamp: options.ephemeralOptions.eph_setting_ts?.toString()
2463
+ }
2464
+ }
2465
+ message = WAProto_1.proto.Message.create(message)
2466
+ const iosBros = options?.browser?.[0] === 'iOS' || options?.browser?.[1] === 'Safari'
2467
+ const androidBros = options?.browser?.[0] === 'Android'
2468
+ const pickedMessageId =
2469
+ options?.messageId ||
2470
+ (iosBros
2471
+ ? (0, generics_1.generateIOSMessageID)()
2472
+ : androidBros
2473
+ ? (0, generics_1.generateAndroMessageID)()
2474
+ : (0, generics_1.generateMessageIDV2)(userJid))
2475
+ const messageJSON = {
2476
+ key: {
2477
+ remoteJid: jid,
2478
+ fromMe: true,
2479
+ id: pickedMessageId
2480
+ },
2481
+ message: message,
2482
+ messageTimestamp: timestamp,
2483
+ messageStubParameters: [],
2484
+ participant: (0, WABinary_1.isJidGroup)(jid) || (0, WABinary_1.isJidStatusBroadcast)(jid) ? userJid : undefined, // TODO: Add support for LIDs
2485
+ status: Types_1.WAMessageStatus.PENDING
2486
+ }
2487
+ return WAProto_1.proto.WebMessageInfo.fromObject(messageJSON)
2488
+ }
2489
+ exports.generateWAMessageFromContent = generateWAMessageFromContent
2490
+ const generateWAMessage = async (jid, content, options) => {
2491
+ // ensure msg ID is with every log
2492
+ options.logger = options?.logger?.child({ msgId: options.messageId })
2493
+ // Pass jid in the options to generateWAMessageContent
2494
+ return (0, exports.generateWAMessageFromContent)(
2495
+ jid,
2496
+ await (0, exports.generateWAMessageContent)(content, { ...options, jid }),
2497
+ options
2498
+ )
2499
+ }
2500
+ exports.generateWAMessage = generateWAMessage
2501
+ /** Get the key to access the true type of content */
2502
+ const getContentType = content => {
2503
+ if (content) {
2504
+ const keys = Object.keys(content)
2505
+ const key = keys.find(k => (k === 'conversation' || k.includes('Message')) && k !== 'senderKeyDistributionMessage')
2506
+ return key
2507
+ }
2508
+ }
2509
+ exports.getContentType = getContentType
2510
+ /**
2511
+ * Normalizes ephemeral, view once messages to regular message content
2512
+ * Eg. image messages in ephemeral messages, in view once messages etc.
2513
+ * @param content
2514
+ * @returns
2515
+ */
2516
+ const normalizeMessageContent = content => {
2517
+ if (!content) {
2518
+ return undefined
2519
+ }
2520
+ // set max iterations to prevent an infinite loop
2521
+ for (let i = 0; i < 5; i++) {
2522
+ const inner = getFutureProofMessage(content)
2523
+ if (!inner) {
2524
+ break
2525
+ }
2526
+ content = inner.message
2527
+ }
2528
+ return content
2529
+ function getFutureProofMessage(message) {
2530
+ return (
2531
+ message.ephemeralMessage ||
2532
+ message.viewOnceMessage ||
2533
+ message.documentWithCaptionMessage ||
2534
+ message.viewOnceMessageV2 ||
2535
+ message.viewOnceMessageV2Extension ||
2536
+ message.editedMessage ||
2537
+ message.groupMentionedMessage ||
2538
+ message.botInvokeMessage ||
2539
+ message.lottieStickerMessage ||
2540
+ message.eventCoverImage ||
2541
+ message.statusMentionMessage ||
2542
+ message.pollCreationOptionImageMessage ||
2543
+ message.associatedChildMessage ||
2544
+ message.groupStatusMentionMessage ||
2545
+ message.pollCreationMessageV4 ||
2546
+ message.pollCreationMessageV5 ||
2547
+ message.statusAddYours ||
2548
+ message.groupStatusMessage ||
2549
+ message.limitSharingMessage ||
2550
+ message.botTaskMessage ||
2551
+ message.questionMessage ||
2552
+ message.groupStatusMessageV2 ||
2553
+ message.botForwardedMessage ||
2554
+ message.questionReplyMessage ||
2555
+ message.newsletterAdminProfileMessage ||
2556
+ message.newsletterAdminProfileMessageV2 ||
2557
+ message.newsletterAdminProfileStatusMessage ||
2558
+ message.spoilerMessage ||
2559
+ message.groupStatusV3Message ||
2560
+ message.pollCreationMessageV6
2561
+ )
2562
+ }
2563
+ }
2564
+ exports.normalizeMessageContent = normalizeMessageContent
2565
+ /**
2566
+ * Extract the true message content from a message
2567
+ * Eg. extracts the inner message from a disappearing message/view once message
2568
+ */
2569
+ const extractMessageContent = content => {
2570
+ const extractFromTemplateMessage = msg => {
2571
+ if (msg.imageMessage) {
2572
+ return { imageMessage: msg.imageMessage }
2573
+ } else if (msg.documentMessage) {
2574
+ return { documentMessage: msg.documentMessage }
2575
+ } else if (msg.videoMessage) {
2576
+ return { videoMessage: msg.videoMessage }
2577
+ } else if (msg.locationMessage) {
2578
+ return { locationMessage: msg.locationMessage }
2579
+ } else {
2580
+ return {
2581
+ conversation:
2582
+ 'contentText' in msg ? msg.contentText : 'hydratedContentText' in msg ? msg.hydratedContentText : ''
2583
+ }
2584
+ }
2585
+ }
2586
+ content = (0, exports.normalizeMessageContent)(content)
2587
+ if (content?.buttonsMessage) {
2588
+ return extractFromTemplateMessage(content.buttonsMessage)
2589
+ }
2590
+ if (content?.templateMessage?.hydratedFourRowTemplate) {
2591
+ return extractFromTemplateMessage(content?.templateMessage?.hydratedFourRowTemplate)
2592
+ }
2593
+ if (content?.templateMessage?.hydratedTemplate) {
2594
+ return extractFromTemplateMessage(content?.templateMessage?.hydratedTemplate)
2595
+ }
2596
+ if (content?.templateMessage?.fourRowTemplate) {
2597
+ return extractFromTemplateMessage(content?.templateMessage?.fourRowTemplate)
2598
+ }
2599
+ return content
2600
+ }
2601
+ exports.extractMessageContent = extractMessageContent
2602
+ /**
2603
+ * Returns the device predicted by message ID
2604
+ */
2605
+ const getDevice = id =>
2606
+ /^3A.{18}$/.test(id)
2607
+ ? 'ios'
2608
+ : /^3E.{20}$/.test(id)
2609
+ ? 'web'
2610
+ : /^(.{21}|.{32})$/.test(id)
2611
+ ? 'android'
2612
+ : /^(3F|.{18}$)/.test(id)
2613
+ ? 'desktop'
2614
+ : 'api/baileys'
2615
+ exports.getDevice = getDevice
2616
+ /** Upserts a receipt in the message */
2617
+ const updateMessageWithReceipt = (msg, receipt) => {
2618
+ msg.userReceipt = msg.userReceipt || []
2619
+ const recp = msg.userReceipt.find(m => m.userJid === receipt.userJid)
2620
+ if (recp) {
2621
+ Object.assign(recp, receipt)
2622
+ } else {
2623
+ msg.userReceipt.push(receipt)
2624
+ }
2625
+ }
2626
+ exports.updateMessageWithReceipt = updateMessageWithReceipt
2627
+ /** Update the message with a new reaction */
2628
+ const updateMessageWithReaction = (msg, reaction) => {
2629
+ const authorID = (0, generics_1.getKeyAuthor)(reaction.key)
2630
+ const reactions = (msg.reactions || []).filter(r => (0, generics_1.getKeyAuthor)(r.key) !== authorID)
2631
+ reaction.text = reaction.text || ''
2632
+ reactions.push(reaction)
2633
+ msg.reactions = reactions
2634
+ }
2635
+ exports.updateMessageWithReaction = updateMessageWithReaction
2636
+ /** Update the message with a new poll update */
2637
+ const updateMessageWithPollUpdate = (msg, update) => {
2638
+ const authorID = (0, generics_1.getKeyAuthor)(update.pollUpdateMessageKey)
2639
+ const reactions = (msg.pollUpdates || []).filter(
2640
+ r => (0, generics_1.getKeyAuthor)(r.pollUpdateMessageKey) !== authorID
2641
+ )
2642
+ if (update.vote?.selectedOptions?.length) {
2643
+ reactions.push(update)
2644
+ }
2645
+ msg.pollUpdates = reactions
2646
+ }
2647
+ exports.updateMessageWithPollUpdate = updateMessageWithPollUpdate
2648
+ /** Update the message with a new event response */
2649
+ const updateMessageWithEventResponse = (msg, update) => {
2650
+ const authorID = (0, generics_1.getKeyAuthor)(update.eventResponseMessageKey)
2651
+ const responses = (msg.eventResponses || []).filter(
2652
+ r => (0, generics_1.getKeyAuthor)(r.eventResponseMessageKey) !== authorID
2653
+ )
2654
+ responses.push(update)
2655
+ msg.eventResponses = responses
2656
+ }
2657
+ exports.updateMessageWithEventResponse = updateMessageWithEventResponse
2658
+ /**
2659
+ * Aggregates all poll updates in a poll.
2660
+ * @param msg the poll creation message
2661
+ * @param meId your jid
2662
+ * @returns A list of options & their voters
2663
+ */
2664
+ function getAggregateVotesInPollMessage({ message, pollUpdates }, meId) {
2665
+ const opts =
2666
+ message?.pollCreationMessage?.options ||
2667
+ message?.pollCreationMessageV2?.options ||
2668
+ message?.pollCreationMessageV3?.options ||
2669
+ message?.pollCreationMessageV5?.options ||
2670
+ message?.pollCreationMessageV6?.options ||
2671
+ message?.pollCreationMessageV4?.message?.pollCreationMessage?.options ||
2672
+ message?.pollCreationMessageV4?.message?.pollCreationMessageV3?.options ||
2673
+ []
2674
+ const voteHashMap = opts.reduce((acc, opt) => {
2675
+ const hash = (0, crypto_2.sha256)(Buffer.from(opt.optionName || '')).toString()
2676
+ acc[hash] = {
2677
+ name: opt.optionName || '',
2678
+ voters: []
2679
+ }
2680
+ return acc
2681
+ }, {})
2682
+ for (const update of pollUpdates || []) {
2683
+ const { vote } = update
2684
+ if (!vote) {
2685
+ continue
2686
+ }
2687
+ for (const option of vote.selectedOptions || []) {
2688
+ const hash = option.toString()
2689
+ let data = voteHashMap[hash]
2690
+ if (!data) {
2691
+ voteHashMap[hash] = {
2692
+ name: 'Unknown',
2693
+ voters: []
2694
+ }
2695
+ data = voteHashMap[hash]
2696
+ }
2697
+ voteHashMap[hash].voters.push((0, generics_1.getKeyAuthor)(update.pollUpdateMessageKey, meId))
2698
+ }
2699
+ }
2700
+ return Object.values(voteHashMap)
2701
+ }
2702
+ /** Given a list of message keys, aggregates them by chat & sender. Useful for sending read receipts in bulk */
2703
+ const aggregateMessageKeysNotFromMe = keys => {
2704
+ const keyMap = {}
2705
+ for (const { remoteJid, id, participant, fromMe, sts } of keys) {
2706
+ if (!fromMe) {
2707
+ const uqKey = `${remoteJid}:${participant || ''}`
2708
+ if (!keyMap[uqKey]) {
2709
+ keyMap[uqKey] = {
2710
+ jid: remoteJid,
2711
+ participant: participant,
2712
+ messageIds: [],
2713
+ // sts is per-message; kept only for single-message interop receipts
2714
+ sts: sts
2715
+ }
2716
+ } else if (keyMap[uqKey].sts !== sts) {
2717
+ // Multiple messages with different sts — can't aggregate a single sts
2718
+ keyMap[uqKey].sts = undefined
2719
+ }
2720
+ keyMap[uqKey].messageIds.push(id)
2721
+ }
2722
+ }
2723
+ return Object.values(keyMap)
2724
+ }
2725
+ exports.aggregateMessageKeysNotFromMe = aggregateMessageKeysNotFromMe
2726
+
2727
+ /**
2728
+ * Check if a WebMessageInfo has a scheduled reveal time (ConditionalRevealMessage)
2729
+ */
2730
+ const isScheduledMessage = msg => !!msg?.scheduledMessageMetadata?.scheduledTime
2731
+ exports.isScheduledMessage = isScheduledMessage
2732
+
2733
+ /**
2734
+ * Get scheduled reveal time of a message as a Date, or null
2735
+ */
2736
+ const getScheduledMessageTime = msg => {
2737
+ const t = msg?.scheduledMessageMetadata?.scheduledTime
2738
+ if (!t) return null
2739
+ return new Date(Number(t) * 1000)
2740
+ }
2741
+ exports.getScheduledMessageTime = getScheduledMessageTime
2742
+
2743
+ /**
2744
+ * Extract PaymentInfo from a WebMessageInfo (the payment status field, not the message content)
2745
+ */
2746
+ const getMessagePaymentInfo = msg => msg?.paymentInfo || msg?.quotedPaymentInfo || null
2747
+ exports.getMessagePaymentInfo = getMessagePaymentInfo
2748
+
2749
+ /**
2750
+ * Get all comment metadata from a WebMessageInfo
2751
+ */
2752
+ const getMessageCommentMetadata = msg => msg?.commentMetadata || null
2753
+ exports.getMessageCommentMetadata = getMessageCommentMetadata
2754
+
2755
+ /**
2756
+ * Get all message add-ons (reactions, poll updates, pins) from a WebMessageInfo
2757
+ */
2758
+ const getMessageAddOns = msg => msg?.messageAddOns || []
2759
+ exports.getMessageAddOns = getMessageAddOns
2760
+
2761
+ /**
2762
+ * Get the quiz correct answer from a poll creation message, if it's a quiz
2763
+ */
2764
+ const getPollCorrectAnswer = pollMsg => {
2765
+ const poll =
2766
+ pollMsg?.pollCreationMessage ||
2767
+ pollMsg?.pollCreationMessageV2 ||
2768
+ pollMsg?.pollCreationMessageV3 ||
2769
+ pollMsg?.pollCreationMessageV5 ||
2770
+ pollMsg?.pollCreationMessageV6
2771
+ if (!poll) return null
2772
+ const isQuiz = poll.pollType === WAProto_1.proto.Message.PollType?.QUIZ || poll.pollType === 1
2773
+ return isQuiz ? poll.correctAnswer?.optionName || null : null
2774
+ }
2775
+ exports.getPollCorrectAnswer = getPollCorrectAnswer
2776
+
2777
+ /**
2778
+ * Aggregates all event responses in an event message.
2779
+ * @param msg the event creation message
2780
+ * @param meLid your lid
2781
+ * @returns A list of response types & their responders
2782
+ */
2783
+ function getAggregateResponsesInEventMessage({ eventResponses }, meLid) {
2784
+ const responseTypes = ['GOING', 'NOT_GOING', 'MAYBE']
2785
+ const responseMap = {}
2786
+
2787
+ for (const type of responseTypes) {
2788
+ responseMap[type] = {
2789
+ response: type,
2790
+ responders: []
2791
+ }
2792
+ }
2793
+ for (const update of eventResponses) {
2794
+ const { response } = update.response || {}
2795
+ const responseType = index_js_1.proto.Message.EventResponseMessage.EventResponseType[response]
2796
+ if (responseType !== 'UNKNOWN' && responseMap[responseType]) {
2797
+ responseMap[responseType].responders.push(generics_1.getKeyAuthor(update.eventResponseMessageKey, meLid))
2798
+ }
2799
+ }
2800
+
2801
+ return Object.values(responseMap)
2802
+ }
2803
+
2804
+ exports.getAggregateResponsesInEventMessage = getAggregateResponsesInEventMessage
2805
+
2806
+ const REUPLOAD_REQUIRED_STATUS = [410, 404]
2807
+ /**
2808
+ * Downloads the given message. Throws an error if it's not a media message
2809
+ */
2810
+ const downloadMediaMessage = async (message, type, options, ctx) => {
2811
+ const result = await downloadMsg().catch(async error => {
2812
+ if (
2813
+ ctx &&
2814
+ typeof error?.status === 'number' && // treat errors with status as HTTP failures requiring reupload
2815
+ REUPLOAD_REQUIRED_STATUS.includes(error.status)
2816
+ ) {
2817
+ ctx.logger.info({ key: message.key }, 'sending reupload media request...')
2818
+ // request reupload
2819
+ message = await ctx.reuploadRequest(message)
2820
+ const result = await downloadMsg()
2821
+ return result
2822
+ }
2823
+ throw error
2824
+ })
2825
+ return result
2826
+ async function downloadMsg() {
2827
+ const mContent = (0, exports.extractMessageContent)(message.message)
2828
+ if (!mContent) {
2829
+ throw new boom_1.Boom('No message present', { statusCode: 400, data: message })
2830
+ }
2831
+ const contentType = (0, exports.getContentType)(mContent)
2832
+ let mediaType = contentType?.replace('Message', '')
2833
+ const media = mContent[contentType]
2834
+ if (!media || typeof media !== 'object' || (!('url' in media) && !('thumbnailDirectPath' in media))) {
2835
+ throw new boom_1.Boom(`"${contentType}" message is not a media message`)
2836
+ }
2837
+ let download
2838
+ if ('thumbnailDirectPath' in media && !('url' in media)) {
2839
+ download = {
2840
+ directPath: media.thumbnailDirectPath,
2841
+ mediaKey: media.mediaKey
2842
+ }
2843
+ mediaType = 'thumbnail-link'
2844
+ } else {
2845
+ download = media
2846
+ }
2847
+ const stream = await (0, messages_media_1.downloadContentFromMessage)(download, mediaType, options)
2848
+ if (type === 'buffer') {
2849
+ const bufferArray = []
2850
+ for await (const chunk of stream) {
2851
+ bufferArray.push(chunk)
2852
+ }
2853
+ return Buffer.concat(bufferArray)
2854
+ }
2855
+ return stream
2856
+ }
2857
+ }
2858
+ exports.downloadMediaMessage = downloadMediaMessage
2859
+ /** Checks whether the given message is a media message; if it is returns the inner content */
2860
+ const assertMediaContent = content => {
2861
+ content = (0, exports.extractMessageContent)(content)
2862
+ const mediaContent =
2863
+ content?.documentMessage ||
2864
+ content?.imageMessage ||
2865
+ content?.videoMessage ||
2866
+ content?.audioMessage ||
2867
+ content?.stickerMessage
2868
+ if (!mediaContent) {
2869
+ throw new boom_1.Boom('given message is not a media message', { statusCode: 400, data: content })
2870
+ }
2871
+ return mediaContent
2872
+ }
2873
+ exports.assertMediaContent = assertMediaContent
2874
+ /**
2875
+ * Normalizes a bare user id to @s.whatsapp.net. Does not convert LID↔PN; use lidMapping / PN in key.remoteJidAlt when needed.
2876
+ */
2877
+ const toJid = id => {
2878
+ if (!id) return ''
2879
+ if (id.includes('@')) return id
2880
+ return `${id}@s.whatsapp.net`
2881
+ }
2882
+ exports.toJid = toJid
2883
+ /**
2884
+ * Returns the peer LID JID when the key is LID-primary (decode sets remoteJid/participant to @lid when WA sends LID).
2885
+ */
2886
+ const getSenderLid = message => {
2887
+ const k = message.key
2888
+ if (!k) {
2889
+ return { jid: '', lid: '' }
2890
+ }
2891
+ const jid = k.participant || k.remoteJid || ''
2892
+ if (jid.endsWith('@lid') || jid.endsWith('@hosted.lid')) {
2893
+ return { jid, lid: jid }
2894
+ }
2895
+ if (k.lid && typeof k.lid === 'string') {
2896
+ const lid = k.lid.includes('@') ? k.lid : (0, WABinary_1.jidEncode)(k.lid, 'lid')
2897
+ return { jid, lid }
2898
+ }
2899
+ if (k.participantLid && (0, WABinary_1.isLidUser)(k.participantLid)) {
2900
+ return { jid, lid: k.participantLid }
2901
+ }
2902
+ return { jid, lid: '' }
2903
+ }
2904
+ exports.getSenderLid = getSenderLid