@badzz88/baileys 8.5.7 → 8.5.8

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/README.md +337 -1055
  2. package/WAProto/WAProto.proto +284 -264
  3. package/WAProto/index.js +5 -5
  4. package/engine-requirements.js +7 -15
  5. package/lib/Defaults/index.js +130 -187
  6. package/lib/Signal/Group/ciphertext-message.js +11 -14
  7. package/lib/Signal/Group/group-session-builder.js +29 -91
  8. package/lib/Signal/Group/group_cipher.js +81 -88
  9. package/lib/Signal/Group/index.js +12 -136
  10. package/lib/Signal/Group/keyhelper.js +15 -70
  11. package/lib/Signal/Group/sender-chain-key.js +25 -31
  12. package/lib/Signal/Group/sender-key-distribution-message.js +62 -65
  13. package/lib/Signal/Group/sender-key-message.js +65 -68
  14. package/lib/Signal/Group/sender-key-name.js +41 -43
  15. package/lib/Signal/Group/sender-key-record.js +40 -43
  16. package/lib/Signal/Group/sender-key-state.js +83 -96
  17. package/lib/Signal/Group/sender-message-key.js +25 -29
  18. package/lib/Signal/libsignal.js +425 -453
  19. package/lib/Signal/lid-mapping.js +276 -259
  20. package/lib/Socket/Client/index.js +3 -30
  21. package/lib/Socket/Client/types.js +10 -12
  22. package/lib/Socket/Client/websocket.js +53 -61
  23. package/lib/Socket/business.js +379 -422
  24. package/lib/Socket/chats.js +1193 -1816
  25. package/lib/Socket/communities.js +431 -549
  26. package/lib/Socket/graphql.js +672 -612
  27. package/lib/Socket/groups.js +374 -764
  28. package/lib/Socket/index.js +17 -38
  29. package/lib/Socket/interop.js +417 -503
  30. package/lib/Socket/messages-recv.js +1915 -2441
  31. package/lib/Socket/messages-send.js +1213 -1630
  32. package/lib/Socket/mex.js +9 -15
  33. package/lib/Socket/newsletter.js +747 -808
  34. package/lib/Socket/privacy.js +310 -441
  35. package/lib/Socket/socket.js +1025 -1022
  36. package/lib/Socket/username.js +8 -15
  37. package/lib/Store/index.js +10 -36
  38. package/lib/Store/make-cache-manager-store.js +80 -85
  39. package/lib/Store/make-in-memory-store.js +231 -591
  40. package/lib/Store/make-ordered-dictionary.js +72 -78
  41. package/lib/Store/object-repository.js +28 -25
  42. package/lib/Types/Auth.js +2 -38
  43. package/lib/Types/Bussines.js +2 -2
  44. package/lib/Types/Call.js +2 -2
  45. package/lib/Types/Chat.js +8 -4
  46. package/lib/Types/Contact.js +2 -2
  47. package/lib/Types/Events.js +2 -2
  48. package/lib/Types/GroupMetadata.js +2 -2
  49. package/lib/Types/Label.js +24 -26
  50. package/lib/Types/LabelAssociation.js +6 -8
  51. package/lib/Types/Message.js +11 -95
  52. package/lib/Types/Mex.js +111 -112
  53. package/lib/Types/Product.js +2 -2
  54. package/lib/Types/Signal.js +2 -2
  55. package/lib/Types/Socket.js +3 -2
  56. package/lib/Types/State.js +56 -70
  57. package/lib/Types/USync.js +2 -2
  58. package/lib/Types/index.js +26 -55
  59. package/lib/Utils/auth-utils.js +288 -292
  60. package/lib/Utils/browser-utils.js +47 -113
  61. package/lib/Utils/business.js +224 -240
  62. package/lib/Utils/chat-utils.js +869 -1205
  63. package/lib/Utils/companion-reg-client-utils.js +34 -41
  64. package/lib/Utils/crypto.js +107 -144
  65. package/lib/Utils/decode-wa-message.js +308 -518
  66. package/lib/Utils/event-buffer.js +611 -596
  67. package/lib/Utils/generics.js +355 -515
  68. package/lib/Utils/history.js +134 -244
  69. package/lib/Utils/identity-change-handler.js +49 -51
  70. package/lib/Utils/index.js +23 -57
  71. package/lib/Utils/link-preview.js +77 -135
  72. package/lib/Utils/logger.js +3 -9
  73. package/lib/Utils/lt-hash.js +6 -37
  74. package/lib/Utils/make-mutex.js +33 -36
  75. package/lib/Utils/message-composer.js +233 -439
  76. package/lib/Utils/message-retry-manager.js +260 -265
  77. package/lib/Utils/messages-media.js +829 -855
  78. package/lib/Utils/messages.js +961 -2528
  79. package/lib/Utils/noise-handler.js +200 -195
  80. package/lib/Utils/offline-node-processor.js +33 -35
  81. package/lib/Utils/pre-key-manager.js +102 -103
  82. package/lib/Utils/process-message.js +560 -978
  83. package/lib/Utils/reporting-utils.js +253 -257
  84. package/lib/Utils/signal.js +195 -218
  85. package/lib/Utils/stanza-ack.js +29 -31
  86. package/lib/Utils/sticker.js +109 -144
  87. package/lib/Utils/sync-action-utils.js +45 -50
  88. package/lib/Utils/tc-token-utils.js +139 -137
  89. package/lib/Utils/use-multi-file-auth-state.js +109 -109
  90. package/lib/Utils/validate-connection.js +202 -218
  91. package/lib/WABinary/constants.js +1299 -1302
  92. package/lib/WABinary/decode.js +262 -343
  93. package/lib/WABinary/encode.js +4 -12
  94. package/lib/WABinary/generic-utils.js +187 -223
  95. package/lib/WABinary/index.js +6 -33
  96. package/lib/WABinary/jid-utils.js +88 -351
  97. package/lib/WABinary/types.js +2 -2
  98. package/lib/WAM/BinaryInfo.js +9 -12
  99. package/lib/WAM/constants.js +22853 -39486
  100. package/lib/WAM/encode.js +140 -132
  101. package/lib/WAM/index.js +4 -31
  102. package/lib/WAUSync/Protocols/USyncBotProfileProtocol.js +21 -23
  103. package/lib/WAUSync/Protocols/USyncBusinessProtocol.js +9 -9
  104. package/lib/WAUSync/Protocols/USyncContactProtocol.js +7 -7
  105. package/lib/WAUSync/Protocols/USyncDeviceProtocol.js +10 -10
  106. package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.js +7 -7
  107. package/lib/WAUSync/Protocols/USyncFeatureProtocol.js +11 -12
  108. package/lib/WAUSync/Protocols/USyncLIDProtocol.js +4 -5
  109. package/lib/WAUSync/Protocols/USyncPictureProtocol.js +7 -7
  110. package/lib/WAUSync/Protocols/USyncSidelistProtocol.js +7 -8
  111. package/lib/WAUSync/Protocols/USyncStatusProtocol.js +7 -7
  112. package/lib/WAUSync/Protocols/USyncTextStatusProtocol.js +8 -8
  113. package/lib/WAUSync/Protocols/USyncUsernameProtocol.js +7 -7
  114. package/lib/WAUSync/Protocols/index.js +12 -39
  115. package/lib/WAUSync/USyncQuery.js +67 -49
  116. package/lib/WAUSync/USyncUser.js +11 -5
  117. package/lib/WAUSync/index.js +3 -31
  118. package/lib/index.js +15 -48
  119. package/package.json +129 -99
  120. package/LICENSE +0 -21
  121. package/lib/Defaults/phonenumber-mcc.json +0 -223
  122. package/lib/Socket/aigroups.js +0 -240
  123. package/lib/Socket/interactive-handler.js +0 -522
  124. package/lib/Socket/managed-account.js +0 -183
  125. package/lib/Socket/registration.js +0 -427
  126. package/lib/Socket/text-router.js +0 -83
  127. package/lib/Types/Newsletter.js +0 -121
  128. package/lib/Utils/command-loader.d.ts +0 -31
  129. package/lib/Utils/command-loader.js +0 -100
  130. package/lib/Utils/consumer-application.js +0 -107
  131. package/lib/Utils/curve25519-js.js +0 -275
  132. package/lib/Utils/group-history.js +0 -60
  133. package/lib/Utils/jid-display-normalization.js +0 -218
  134. package/lib/Utils/meta-ai-msmsg.js +0 -262
  135. package/lib/Utils/native-bridge.js +0 -82
  136. package/lib/Utils/session-pool.d.ts +0 -16
  137. package/lib/Utils/session-pool.js +0 -94
  138. package/lib/Utils/sticker.d.ts +0 -17
  139. package/lib/Utils/view-once-cache.d.ts +0 -9
  140. package/lib/Utils/view-once-cache.js +0 -79
  141. package/lib/Utils/voip-rekey.js +0 -22
  142. package/lib/antiban.js +0 -4637
@@ -1,73 +1,60 @@
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')
1
+ import { Boom } from '@hapi/boom';
2
+ import { randomUUID, randomBytes } from 'crypto';
3
+ import { promises as fs } from 'fs';
4
+ import {} from 'stream';
5
+ import { proto } from '../../WAProto/index.js';
6
+ import { CALL_AUDIO_PREFIX, CALL_VIDEO_PREFIX, MEDIA_KEYS, URL_REGEX, WA_DEFAULT_EPHEMERAL } from '../Defaults/index.js';
7
+ import { WAMessageStatus, WAProto } from '../Types/index.js';
8
+ import { isJidGroup, isJidNewsletter, isJidStatusBroadcast, jidNormalizedUser } from '../WABinary/index.js';
9
+ import { sha256 } from './crypto.js';
10
+ import { generateMessageIDV2, getKeyAuthor, unixTimestampSeconds } from './generics.js';
11
+ import { buildZipArchive, downloadContentFromMessage, encryptedStream, generateThumbnail, getAudioDuration, getAudioWaveform, getRawMediaUploadData, resolveStickerBuffer } from './messages-media.js';
12
+ import { makeSticker } from './sticker.js';
13
+ import { shouldIncludeReportingToken } from './reporting-utils.js';
39
14
  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
- }
15
+ image: 'image/jpeg',
16
+ video: 'video/mp4',
17
+ document: 'application/pdf',
18
+ audio: 'audio/ogg; codecs=opus',
19
+ sticker: 'image/webp',
20
+ 'product-catalog-image': 'image/jpeg'
21
+ };
22
+ const RAW_PROTO_MESSAGE_KEYS = [
23
+ 'conversation', 'senderKeyDistributionMessage', 'imageMessage', 'contactMessage', 'locationMessage',
24
+ 'extendedTextMessage', 'documentMessage', 'audioMessage', 'videoMessage', 'call', 'chat', 'protocolMessage',
25
+ 'contactsArrayMessage', 'highlyStructuredMessage', 'fastRatchetKeySenderKeyDistributionMessage',
26
+ 'sendPaymentMessage', 'liveLocationMessage', 'requestPaymentMessage', 'declinePaymentRequestMessage',
27
+ 'cancelPaymentRequestMessage', 'templateMessage', 'stickerMessage', 'groupInviteMessage',
28
+ 'templateButtonReplyMessage', 'productMessage', 'deviceSentMessage', 'messageContextInfo', 'listMessage',
29
+ 'viewOnceMessage', 'orderMessage', 'listResponseMessage', 'ephemeralMessage', 'invoiceMessage',
30
+ 'buttonsMessage', 'buttonsResponseMessage', 'paymentInviteMessage', 'interactiveMessage', 'reactionMessage',
31
+ 'stickerSyncRmrMessage', 'interactiveResponseMessage', 'pollCreationMessage', 'pollUpdateMessage',
32
+ 'keepInChatMessage', 'documentWithCaptionMessage', 'requestPhoneNumberMessage', 'viewOnceMessageV2',
33
+ 'encReactionMessage', 'editedMessage', 'viewOnceMessageV2Extension', 'pollCreationMessageV2',
34
+ 'scheduledCallCreationMessage', 'groupMentionedMessage', 'pinInChatMessage', 'pollCreationMessageV3',
35
+ 'scheduledCallEditMessage', 'ptvMessage', 'botInvokeMessage', 'callLogMesssage', 'messageHistoryBundle',
36
+ 'encCommentMessage', 'bcallMessage', 'lottieStickerMessage', 'eventMessage', 'encEventResponseMessage',
37
+ 'commentMessage', 'newsletterAdminInviteMessage', 'placeholderMessage', 'secretEncryptedMessage',
38
+ 'albumMessage', 'eventCoverImage', 'stickerPackMessage', 'statusMentionMessage', 'pollResultSnapshotMessage',
39
+ 'pollCreationOptionImageMessage', 'associatedChildMessage', 'groupStatusMentionMessage',
40
+ 'pollCreationMessageV4', 'statusAddYours', 'groupStatusMessage', 'richResponseMessage',
41
+ 'statusNotificationMessage', 'limitSharingMessage', 'botTaskMessage', 'questionMessage',
42
+ 'messageHistoryNotice', 'groupStatusMessageV2', 'botForwardedMessage', 'statusQuestionAnswerMessage',
43
+ 'questionReplyMessage', 'questionResponseMessage', 'statusQuotedMessage', 'statusStickerInteractionMessage',
44
+ 'pollCreationMessageV5', 'newsletterFollowerInviteMessageV2', 'pollResultSnapshotMessageV3',
45
+ 'newsletterAdminProfileMessage', 'newsletterAdminProfileMessageV2', 'spoilerMessage',
46
+ 'pollCreationMessageV6', 'conditionalRevealMessage', 'pollAddOptionMessage', 'eventInviteMessage',
47
+ 'groupRootKeyShare', 'paymentReminderMessage', 'splitPaymentMessage', 'newsletterAdminProfileStatusMessage',
48
+ 'rootSecretDistributeMessage'
49
+ ];
47
50
  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
-
51
+ image: WAProto.Message.ImageMessage,
52
+ video: WAProto.Message.VideoMessage,
53
+ audio: WAProto.Message.AudioMessage,
54
+ sticker: WAProto.Message.StickerMessage,
55
+ document: WAProto.Message.DocumentMessage
56
+ };
57
+ const ButtonType = proto.Message.ButtonsMessage.HeaderType;
71
58
  const RICH_RESPONSE_CODE_KEYWORDS = new Set([
72
59
  'break',
73
60
  'case',
@@ -195,333 +182,251 @@ const tokenizeCode = code => {
195
182
  }
196
183
  return merged
197
184
  }
198
-
199
185
  /**
200
186
  * Uses a regex to test whether the string contains a URL, and returns the URL if it does.
201
187
  * @param text eg. hello https://google.com
202
188
  * @returns the URL, eg. https://google.com
203
189
  */
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
190
+ export const extractUrlFromText = (text) => text.match(URL_REGEX)?.[0];
191
+ export const generateLinkPreviewIfRequired = async (text, getUrlInfo, logger) => {
192
+ const url = extractUrlFromText(text);
193
+ if (!!getUrlInfo && url) {
194
+ try {
195
+ const urlInfo = await getUrlInfo(url);
196
+ return urlInfo;
197
+ }
198
+ catch (error) {
199
+ // ignore if fails
200
+ logger?.warn({ trace: error.stack }, 'url generation failed');
201
+ }
202
+ }
203
+ };
204
+ const assertColor = async (color) => {
205
+ let assertedColor;
206
+ if (typeof color === 'number') {
207
+ assertedColor = color > 0 ? color : 0xffffffff + Number(color) + 1;
208
+ }
209
+ else {
210
+ let hex = color.trim().replace('#', '');
211
+ if (hex.length <= 6) {
212
+ hex = 'FF' + hex.padStart(6, '0');
213
+ }
214
+ assertedColor = parseInt(hex, 16);
215
+ return assertedColor;
216
+ }
217
+ };
218
+ export const prepareWAMessageMedia = async (message, options) => {
219
+ const logger = options.logger;
220
+ let mediaType;
221
+ for (const key of MEDIA_KEYS) {
222
+ if (key in message) {
223
+ mediaType = key;
224
+ }
225
+ }
226
+ if (!mediaType) {
227
+ throw new Boom('Invalid media type', { statusCode: 400 });
228
+ }
229
+ const uploadData = {
230
+ ...message,
231
+ media: message[mediaType]
232
+ };
233
+ delete uploadData[mediaType];
234
+ // check if cacheable + generate cache key
235
+ const cacheableKey = typeof uploadData.media === 'object' &&
236
+ 'url' in uploadData.media &&
237
+ !!uploadData.media.url &&
238
+ !!options.mediaCache &&
239
+ mediaType + ':' + uploadData.media.url.toString();
240
+ if (mediaType === 'document' && !uploadData.fileName) {
241
+ uploadData.fileName = 'file';
242
+ }
243
+ if (!uploadData.mimetype) {
244
+ uploadData.mimetype = MIMETYPE_MAP[mediaType];
245
+ }
246
+ if (cacheableKey) {
247
+ const mediaBuff = await options.mediaCache.get(cacheableKey);
248
+ if (mediaBuff) {
249
+ logger?.debug({ cacheableKey }, 'got media cache hit');
250
+ const obj = proto.Message.decode(mediaBuff);
251
+ const key = `${mediaType}Message`;
252
+ Object.assign(obj[key], { ...uploadData, media: undefined });
253
+ return obj;
254
+ }
255
+ }
256
+ const isNewsletter = !!options.jid && isJidNewsletter(options.jid);
257
+ if (isNewsletter) {
258
+ logger?.info({ key: cacheableKey }, 'Preparing raw media for newsletter');
259
+ const { filePath, fileSha256, fileLength } = await getRawMediaUploadData(uploadData.media, options.mediaTypeOverride || mediaType, logger);
260
+ const fileSha256B64 = fileSha256.toString('base64');
261
+ const { mediaUrl, directPath } = await options.upload(filePath, {
262
+ fileEncSha256B64: fileSha256B64,
263
+ mediaType: mediaType,
264
+ timeoutMs: options.mediaUploadTimeoutMs
265
+ });
266
+ await fs.unlink(filePath);
267
+ const obj = WAProto.Message.fromObject({
268
+ // todo: add more support here
269
+ [`${mediaType}Message`]: MessageTypeProto[mediaType].fromObject({
270
+ url: mediaUrl,
271
+ directPath,
272
+ fileSha256,
273
+ fileLength,
274
+ ...uploadData,
275
+ media: undefined
276
+ })
277
+ });
278
+ if (uploadData.ptv) {
279
+ obj.ptvMessage = obj.videoMessage;
280
+ delete obj.videoMessage;
281
+ }
282
+ if (obj.stickerMessage) {
283
+ obj.stickerMessage.stickerSentTs = Date.now();
284
+ }
285
+ if (cacheableKey) {
286
+ logger?.debug({ cacheableKey }, 'set cache');
287
+ await options.mediaCache.set(cacheableKey, WAProto.Message.encode(obj).finish());
288
+ }
289
+ return obj;
290
+ }
291
+ const requiresDurationComputation = mediaType === 'audio' && typeof uploadData.seconds === 'undefined';
292
+ const requiresThumbnailComputation = (mediaType === 'image' || mediaType === 'video') && typeof uploadData['jpegThumbnail'] === 'undefined';
293
+ const requiresWaveformProcessing = mediaType === 'audio' && uploadData.ptt === true && typeof uploadData.waveform === 'undefined';
294
+ const requiresAudioBackground = options.backgroundColor && mediaType === 'audio' && uploadData.ptt === true;
295
+ const requiresOriginalForSomeProcessing = requiresDurationComputation || requiresThumbnailComputation;
296
+ const { mediaKey, encFilePath, originalFilePath, fileEncSha256, fileSha256, fileLength } = await encryptedStream(uploadData.media, options.mediaTypeOverride || mediaType, {
297
+ logger,
298
+ saveOriginalFileIfRequired: requiresOriginalForSomeProcessing,
299
+ opts: options.options
300
+ });
301
+ const fileEncSha256B64 = fileEncSha256.toString('base64');
302
+ const [{ mediaUrl, directPath }] = await Promise.all([
303
+ (async () => {
304
+ const result = await options.upload(encFilePath, {
305
+ fileEncSha256B64,
306
+ mediaType,
307
+ timeoutMs: options.mediaUploadTimeoutMs
308
+ });
309
+ logger?.debug({ mediaType, cacheableKey }, 'uploaded media');
310
+ return result;
311
+ })(),
312
+ (async () => {
313
+ try {
314
+ if (requiresThumbnailComputation) {
315
+ const { thumbnail, originalImageDimensions } = await generateThumbnail(originalFilePath, mediaType, options);
316
+ uploadData.jpegThumbnail = thumbnail;
317
+ if (!uploadData.width && originalImageDimensions) {
318
+ uploadData.width = originalImageDimensions.width;
319
+ uploadData.height = originalImageDimensions.height;
320
+ logger?.debug('set dimensions');
321
+ }
322
+ logger?.debug('generated thumbnail');
323
+ }
324
+ if (requiresDurationComputation) {
325
+ uploadData.seconds = await getAudioDuration(originalFilePath);
326
+ logger?.debug('computed audio duration');
327
+ }
328
+ if (requiresWaveformProcessing) {
329
+ uploadData.waveform = await getAudioWaveform(originalFilePath, logger);
330
+ logger?.debug('processed waveform');
331
+ }
332
+ if (requiresAudioBackground) {
333
+ uploadData.backgroundArgb = await assertColor(options.backgroundColor);
334
+ logger?.debug('computed backgroundColor audio status');
335
+ }
336
+ }
337
+ catch (error) {
338
+ logger?.warn({ trace: error.stack }, 'failed to obtain extra info');
339
+ }
340
+ })()
341
+ ]).finally(async () => {
342
+ try {
343
+ await fs.unlink(encFilePath);
344
+ if (originalFilePath) {
345
+ await fs.unlink(originalFilePath);
346
+ }
347
+ logger?.debug('removed tmp files');
348
+ }
349
+ catch (error) {
350
+ logger?.warn('failed to remove tmp file');
351
+ }
352
+ });
353
+ const obj = WAProto.Message.fromObject({
354
+ [`${mediaType}Message`]: MessageTypeProto[mediaType].fromObject({
355
+ url: mediaUrl,
356
+ directPath,
357
+ mediaKey,
358
+ fileEncSha256,
359
+ fileSha256,
360
+ fileLength,
361
+ mediaKeyTimestamp: unixTimestampSeconds(),
362
+ ...uploadData,
363
+ media: undefined
364
+ })
365
+ });
366
+ if (uploadData.ptv) {
367
+ obj.ptvMessage = obj.videoMessage;
368
+ delete obj.videoMessage;
369
+ }
370
+ if (cacheableKey) {
371
+ logger?.debug({ cacheableKey }, 'set cache');
372
+ await options.mediaCache.set(cacheableKey, WAProto.Message.encode(obj).finish());
373
+ }
374
+ return obj;
375
+ };
376
+ export const prepareDisappearingMessageSettingContent = (ephemeralExpiration) => {
377
+ ephemeralExpiration = ephemeralExpiration || 0;
378
+ const content = {
379
+ ephemeralMessage: {
380
+ message: {
381
+ protocolMessage: {
382
+ type: WAProto.Message.ProtocolMessage.Type.EPHEMERAL_SETTING,
383
+ ephemeralExpiration
384
+ }
385
+ }
386
+ }
387
+ };
388
+ return WAProto.Message.fromObject(content);
389
+ };
417
390
  /**
418
391
  * Generate forwarded message content like WA does
419
392
  * @param message the message to forward
420
393
  * @param options.forceForward will show the message as forwarded even if it is from you
421
394
  */
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
395
+ export const generateForwardMessageContent = (message, forceForward) => {
396
+ let content = message.message;
397
+ if (!content) {
398
+ throw new Boom('no content in message', { statusCode: 400 });
399
+ }
400
+ // hacky copy
401
+ content = normalizeMessageContent(content);
402
+ content = proto.Message.decode(proto.Message.encode(content).finish());
403
+ let key = Object.keys(content)[0];
404
+ let score = content?.[key]?.contextInfo?.forwardingScore || 0;
405
+ score += message.key.fromMe && !forceForward ? 0 : 1;
406
+ if (key === 'conversation') {
407
+ content.extendedTextMessage = { text: content[key] };
408
+ delete content.conversation;
409
+ key = 'extendedTextMessage';
410
+ }
411
+ const key_ = content?.[key];
412
+ if (score > 0) {
413
+ key_.contextInfo = { forwardingScore: score, isForwarded: true };
414
+ }
415
+ else {
416
+ key_.contextInfo = {};
417
+ }
418
+ return content;
419
+ };
420
+ export const hasNonNullishProperty = (message, key) => {
421
+ return (typeof message === 'object' &&
422
+ message !== null &&
423
+ key in message &&
424
+ message[key] !== null &&
425
+ message[key] !== undefined);
426
+ };
457
427
  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
- }
428
+ return typeof obj === 'object' && obj !== null && key in obj && obj[key] !== null;
522
429
  }
523
- const asciiDecode = arr => arr.map(e => String.fromCharCode(e)).join('')
524
-
525
430
  const applyContextInfoAndMentions = (interactiveMessage, message) => {
526
431
  if ('contextInfo' in message && !!message.contextInfo) {
527
432
  interactiveMessage.contextInfo = message.contextInfo
@@ -533,563 +438,11 @@ const applyContextInfoAndMentions = (interactiveMessage, message) => {
533
438
  }
534
439
  }
535
440
  }
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({
441
+ export const generateWAMessageContent = async (message, options) => {
442
+ var _a, _b;
443
+ let m = {};
444
+ if ('interactiveButtons' in message && !!message.interactiveButtons) {
445
+ const nativeFlow = proto.Message.InteractiveMessage.NativeFlowMessage.fromObject({
1093
446
  buttons: message.interactiveButtons,
1094
447
  messageParamsJson: message.messageParams ?? "{}"
1095
448
  });
@@ -1156,53 +509,34 @@ const generateWAMessageContent = async (message, options) => {
1156
509
  applyContextInfoAndMentions(interactiveMessage, message);
1157
510
  m = { interactiveMessage };
1158
511
  }
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) {
512
+ else if ('buttons' in message && !!message.buttons) {
513
+ const buttonsMessage = {
514
+ buttons: message.buttons.map(b => ({ ...b, type: proto.Message.ButtonsMessage.Button.Type.RESPONSE }))
515
+ };
516
+ if ('text' in message) {
517
+ buttonsMessage.contentText = message.text;
518
+ buttonsMessage.headerType = ButtonType.EMPTY;
519
+ }
520
+ else {
521
+ if ('caption' in message) {
522
+ buttonsMessage.contentText = message.caption;
523
+ }
524
+ const contentType = getContentType(m);
525
+ const type = contentType.replace('Message', '').toUpperCase();
526
+ buttonsMessage.headerType = ButtonType[type];
527
+ Object.assign(buttonsMessage, m);
528
+ }
529
+ if ('footer' in message && !!message.footer) {
530
+ buttonsMessage.footerText = message.footer;
531
+ }
532
+ m = { buttonsMessage };
533
+ }
534
+ else if ('richResponse' in message) {
1201
535
  const {
1202
536
  text,
1203
537
  code,
1204
538
  language = 'javascript',
1205
- botJid = '259786046210223@bot',
539
+ botJid = '867051314767696@bot',
1206
540
  table,
1207
541
  latex,
1208
542
  map,
@@ -1232,22 +566,12 @@ const generateWAMessageContent = async (message, options) => {
1232
566
  }
1233
567
  })
1234
568
  }
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
- }
569
+ if (table && Array.isArray(table.rows)) {
1244
570
  sections.push({
1245
571
  view_model: {
1246
572
  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
573
+ rows: table.rows.map(row => ({
574
+ cells: Array.isArray(row) ? row.map(c => ({ text: String(c) })) : row.cells
1251
575
  })),
1252
576
  __typename: 'GenAITableUXPrimitive'
1253
577
  },
@@ -1309,14 +633,14 @@ const generateWAMessageContent = async (message, options) => {
1309
633
  })
1310
634
  }
1311
635
  const unifiedData = {
1312
- response_id: responseId || (0, crypto_1.randomUUID)(),
636
+ response_id: responseId || randomUUID(),
1313
637
  sections
1314
638
  }
1315
- return WAProto_1.proto.Message.fromObject({
639
+ return proto.Message.fromObject({
1316
640
  messageContextInfo: {
1317
641
  deviceListMetadata: {},
1318
642
  deviceListMetadataVersion: 2,
1319
- messageSecret: richSecret || (0, crypto_1.randomBytes)(32)
643
+ messageSecret: richSecret || randomBytes(32)
1320
644
  },
1321
645
  botForwardedMessage: {
1322
646
  message: {
@@ -1338,1567 +662,676 @@ const generateWAMessageContent = async (message, options) => {
1338
662
  }
1339
663
  })
1340
664
  }
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
665
+ else if ('botInvoke' in message && !!message.botInvoke) {
666
+ m = {
667
+ botInvokeMessage: {
668
+ message: message.botInvoke
669
+ }
670
+ }
671
+ }
672
+ else if (hasNonNullishProperty(message, 'text')) {
673
+ const extContent = { text: message.text };
674
+ let urlInfo = message.linkPreview;
675
+ if (typeof urlInfo === 'undefined') {
676
+ urlInfo = await generateLinkPreviewIfRequired(message.text, options.getUrlInfo, options.logger);
677
+ }
678
+ if (urlInfo) {
679
+ extContent.matchedText = urlInfo['matched-text'];
680
+ extContent.jpegThumbnail = urlInfo.jpegThumbnail;
681
+ extContent.description = urlInfo.description;
682
+ extContent.title = urlInfo.title;
683
+ extContent.previewType = 0;
684
+ const img = urlInfo.highQualityThumbnail;
685
+ if (img) {
686
+ extContent.thumbnailDirectPath = img.directPath;
687
+ extContent.mediaKey = img.mediaKey;
688
+ extContent.mediaKeyTimestamp = img.mediaKeyTimestamp;
689
+ extContent.thumbnailWidth = img.width;
690
+ extContent.thumbnailHeight = img.height;
691
+ extContent.thumbnailSha256 = img.fileSha256;
692
+ extContent.thumbnailEncSha256 = img.fileEncSha256;
693
+ }
694
+ }
695
+ if (options.backgroundColor) {
696
+ extContent.backgroundArgb = await assertColor(options.backgroundColor);
697
+ }
698
+ if (options.font) {
699
+ extContent.font = options.font;
700
+ }
701
+ m.extendedTextMessage = extContent;
702
+ }
703
+ else if (hasNonNullishProperty(message, 'contacts')) {
704
+ const contactLen = message.contacts.contacts.length;
705
+ if (!contactLen) {
706
+ throw new Boom('require atleast 1 contact', { statusCode: 400 });
707
+ }
708
+ if (contactLen === 1) {
709
+ m.contactMessage = WAProto.Message.ContactMessage.create(message.contacts.contacts[0]);
710
+ }
711
+ else {
712
+ m.contactsArrayMessage = WAProto.Message.ContactsArrayMessage.create(message.contacts);
713
+ }
714
+ }
715
+ else if (hasNonNullishProperty(message, 'location')) {
716
+ m.locationMessage = WAProto.Message.LocationMessage.create(message.location);
717
+ }
718
+ else if (hasNonNullishProperty(message, 'react')) {
719
+ if (!message.react.senderTimestampMs) {
720
+ message.react.senderTimestampMs = Date.now();
721
+ }
722
+ m.reactionMessage = WAProto.Message.ReactionMessage.create(message.react);
723
+ }
724
+ else if (hasNonNullishProperty(message, 'delete')) {
725
+ m.protocolMessage = {
726
+ key: message.delete,
727
+ type: WAProto.Message.ProtocolMessage.Type.REVOKE
728
+ };
729
+ }
730
+ else if (hasNonNullishProperty(message, 'forward')) {
731
+ m = generateForwardMessageContent(message.forward, message.force);
732
+ }
733
+ else if (hasNonNullishProperty(message, 'disappearingMessagesInChat')) {
734
+ const exp = typeof message.disappearingMessagesInChat === 'boolean'
735
+ ? message.disappearingMessagesInChat
736
+ ? WA_DEFAULT_EPHEMERAL
737
+ : 0
738
+ : message.disappearingMessagesInChat;
739
+ m = prepareDisappearingMessageSettingContent(exp);
740
+ }
741
+ else if (hasNonNullishProperty(message, 'groupInvite')) {
742
+ m.groupInviteMessage = {};
743
+ m.groupInviteMessage.inviteCode = message.groupInvite.inviteCode;
744
+ m.groupInviteMessage.inviteExpiration = message.groupInvite.inviteExpiration;
745
+ m.groupInviteMessage.caption = message.groupInvite.text;
746
+ m.groupInviteMessage.groupJid = message.groupInvite.jid;
747
+ m.groupInviteMessage.groupName = message.groupInvite.subject;
748
+ //TODO: use built-in interface and get disappearing mode info etc.
749
+ //TODO: cache / use store!?
750
+ if (options.getProfilePicUrl) {
751
+ const pfpUrl = await options.getProfilePicUrl(message.groupInvite.jid, 'preview');
752
+ if (pfpUrl) {
753
+ const resp = await fetch(pfpUrl, { method: 'GET', dispatcher: options?.options?.dispatcher });
754
+ if (resp.ok) {
755
+ const buf = Buffer.from(await resp.arrayBuffer());
756
+ m.groupInviteMessage.jpegThumbnail = buf;
757
+ }
758
+ }
759
+ }
760
+ }
761
+ else if (hasNonNullishProperty(message, 'pin')) {
762
+ m.pinInChatMessage = {};
763
+ m.messageContextInfo = {};
764
+ m.pinInChatMessage.key = message.pin;
765
+ m.pinInChatMessage.type = message.type;
766
+ m.pinInChatMessage.senderTimestampMs = Date.now();
767
+ m.messageContextInfo.messageAddOnDurationInSecs = message.type === 1 ? message.time || 86400 : 0;
768
+ }
769
+ else if (hasNonNullishProperty(message, 'buttonReply')) {
770
+ switch (message.type) {
771
+ case 'template':
772
+ m.templateButtonReplyMessage = {
773
+ selectedDisplayText: message.buttonReply.displayText,
774
+ selectedId: message.buttonReply.id,
775
+ selectedIndex: message.buttonReply.index
776
+ };
777
+ break;
778
+ case 'plain':
779
+ m.buttonsResponseMessage = {
780
+ selectedButtonId: message.buttonReply.id,
781
+ selectedDisplayText: message.buttonReply.displayText,
782
+ type: proto.Message.ButtonsResponseMessage.Type.DISPLAY_TEXT
783
+ };
784
+ break;
785
+ }
786
+ }
787
+ else if (hasOptionalProperty(message, 'ptv') && message.ptv) {
788
+ const { videoMessage } = await prepareWAMessageMedia({ video: message.ptv }, options);
789
+ m.ptvMessage = videoMessage;
790
+ }
791
+ else if (hasNonNullishProperty(message, 'product')) {
792
+ const { imageMessage } = await prepareWAMessageMedia({ image: message.product.productImage }, options);
793
+ m.productMessage = WAProto.Message.ProductMessage.create({
794
+ ...message,
795
+ product: {
796
+ ...message.product,
797
+ productImage: imageMessage
798
+ }
799
+ });
800
+ }
801
+ else if (hasNonNullishProperty(message, 'listReply')) {
802
+ m.listResponseMessage = { ...message.listReply };
803
+ }
804
+ else if (hasNonNullishProperty(message, 'event')) {
805
+ m.eventMessage = {};
806
+ const startTime = Math.floor(message.event.startDate.getTime() / 1000);
807
+ if (message.event.call && options.getCallLink) {
808
+ const token = await options.getCallLink(message.event.call, { startTime });
809
+ m.eventMessage.joinLink = (message.event.call === 'audio' ? CALL_AUDIO_PREFIX : CALL_VIDEO_PREFIX) + token;
810
+ }
811
+ m.messageContextInfo = {
812
+ // encKey
813
+ messageSecret: message.event.messageSecret || randomBytes(32)
814
+ };
815
+ m.eventMessage.name = message.event.name;
816
+ m.eventMessage.description = message.event.description;
817
+ m.eventMessage.startTime = startTime;
818
+ m.eventMessage.endTime = message.event.endDate ? message.event.endDate.getTime() / 1000 : undefined;
819
+ m.eventMessage.isCanceled = message.event.isCancelled ?? false;
820
+ m.eventMessage.extraGuestsAllowed = message.event.extraGuestsAllowed;
821
+ m.eventMessage.isScheduleCall = message.event.isScheduleCall ?? false;
822
+ m.eventMessage.location = message.event.location;
823
+ }
824
+ else if (hasNonNullishProperty(message, 'poll')) {
825
+ (_a = message.poll).selectableCount || (_a.selectableCount = 0);
826
+ (_b = message.poll).toAnnouncementGroup || (_b.toAnnouncementGroup = false);
827
+ if (!Array.isArray(message.poll.values)) {
828
+ throw new Boom('Invalid poll values', { statusCode: 400 });
829
+ }
830
+ if (message.poll.selectableCount < 0 || message.poll.selectableCount > message.poll.values.length) {
831
+ throw new Boom(`poll.selectableCount in poll should be >= 0 and <= ${message.poll.values.length}`, {
832
+ statusCode: 400
833
+ });
834
+ }
835
+ m.messageContextInfo = {
836
+ // encKey
837
+ messageSecret: message.poll.messageSecret || randomBytes(32)
838
+ };
839
+ const pollCreationMessage = {
840
+ name: message.poll.name,
841
+ selectableOptionsCount: message.poll.selectableCount,
842
+ options: message.poll.values.map(optionName => ({ optionName }))
843
+ };
844
+ if (message.poll.toAnnouncementGroup) {
845
+ // poll v2 is for community announcement groups (single select and multiple)
846
+ m.pollCreationMessageV2 = pollCreationMessage;
847
+ }
848
+ else {
849
+ if (message.poll.selectableCount === 1) {
850
+ //poll v3 is for single select polls
851
+ m.pollCreationMessageV3 = pollCreationMessage;
852
+ }
853
+ else {
854
+ // poll for multiple choice polls
855
+ m.pollCreationMessage = pollCreationMessage;
856
+ }
857
+ }
858
+ }
859
+ else if (hasNonNullishProperty(message, 'album')) {
860
+ m.albumMessage = {
861
+ expectedImageCount: message.album.expectedImageCount,
862
+ expectedVideoCount: message.album.expectedVideoCount
863
+ };
864
+ }
865
+ else if (hasNonNullishProperty(message, 'sharePhoneNumber')) {
866
+ m.protocolMessage = {
867
+ type: proto.Message.ProtocolMessage.Type.SHARE_PHONE_NUMBER
868
+ };
869
+ }
870
+ else if (hasNonNullishProperty(message, 'requestPhoneNumber')) {
871
+ m.requestPhoneNumberMessage = {};
872
+ }
873
+ else if (hasNonNullishProperty(message, 'limitSharing')) {
874
+ m.protocolMessage = {
875
+ type: proto.Message.ProtocolMessage.Type.LIMIT_SHARING,
876
+ limitSharing: {
877
+ sharingLimited: message.limitSharing === true,
878
+ trigger: 1,
879
+ limitSharingSettingTimestamp: Date.now(),
880
+ initiatedByMe: true
881
+ }
882
+ };
883
+ }
884
+ else if (hasNonNullishProperty(message, 'stickerPacks')) {
885
+ const pack = message.stickerPacks;
886
+ const rawStickers = Array.isArray(pack.stickers) ? pack.stickers : [];
887
+ if (rawStickers.length === 0) {
888
+ throw new Boom('stickerPacks.stickers must have at least one sticker', { statusCode: 400 });
889
+ }
890
+ const stickerBuffers = await Promise.all(rawStickers.map(s => resolveStickerBuffer(s, options.options)));
891
+ const zipEntries = stickerBuffers.map((data, i) => ({ name: `sticker_${i}.webp`, data }));
892
+ const archive = buildZipArchive(zipEntries);
893
+ const packName = pack.name || 'Sticker Pack';
894
+ const captionParts = [];
895
+ if (pack.publisher) captionParts.push(`Publisher: ${pack.publisher}`);
896
+ if (pack.author) captionParts.push(`Author: ${pack.author}`);
897
+ // sent as a plain .zip document through the normal, well-tested upload path
898
+ m = await prepareWAMessageMedia({
899
+ document: archive,
900
+ mimetype: 'application/zip',
901
+ fileName: `${packName}.zip`,
902
+ caption: captionParts.length ? captionParts.join('\n') : undefined
903
+ }, options);
904
+ }
905
+ else if (hasNonNullishProperty(message, 'sticker') && (message.pack || message.author || message.isPrivate || message.premium || message.animated)) {
906
+ const rawBuffer = await resolveStickerBuffer(message.sticker, options.options);
907
+ const finalWebp = await makeSticker(rawBuffer, {
908
+ pack: message.pack || '',
909
+ author: message.author || '',
910
+ emojis: message.emojis || [],
911
+ isPrivate: !!message.isPrivate,
912
+ animated: !!message.animated,
913
+ quality: message.quality || 60
914
+ });
915
+ m = await prepareWAMessageMedia({ sticker: finalWebp }, options);
916
+ if (m.stickerMessage) {
917
+ if (message.premium) {
918
+ m.stickerMessage.premium = typeof message.premium === 'number' ? message.premium : 1;
919
+ }
920
+ if (message.isPrivate) {
921
+ m.stickerMessage.isAvatar = true;
922
+ }
923
+ }
924
+ }
925
+ else {
926
+ const rawProtoKey = RAW_PROTO_MESSAGE_KEYS.find(k => hasNonNullishProperty(message, k));
927
+ if (rawProtoKey) {
928
+ const built = proto.Message.fromObject({ [rawProtoKey]: message[rawProtoKey] });
929
+ m = { [rawProtoKey]: built[rawProtoKey] };
930
+ }
931
+ else {
932
+ m = await prepareWAMessageMedia(message, options);
933
+ }
934
+ }
935
+ if (hasOptionalProperty(message, 'viewOnce') && !!message.viewOnce) {
936
+ m = { viewOnceMessage: { message: m } };
937
+ }
938
+ if ((hasOptionalProperty(message, 'mentions') && message.mentions?.length) ||
939
+ (hasOptionalProperty(message, 'mentionAll') && message.mentionAll)) {
940
+ const messageType = Object.keys(m)[0];
941
+ const key = m[messageType];
942
+ if (key && 'contextInfo' in key) {
943
+ key.contextInfo = key.contextInfo || {};
944
+ if (message.mentions?.length) {
945
+ key.contextInfo.mentionedJid = message.mentions;
946
+ }
947
+ if (message.mentionAll) {
948
+ key.contextInfo.nonJidMentions = 1;
949
+ }
950
+ }
951
+ else if (key) {
952
+ key.contextInfo = {
953
+ mentionedJid: message.mentions,
954
+ nonJidMentions: message.mentionAll ? 1 : 0
955
+ };
956
+ }
957
+ }
958
+ if (hasOptionalProperty(message, 'edit')) {
959
+ m = {
960
+ protocolMessage: {
961
+ key: message.edit,
962
+ editedMessage: m,
963
+ timestampMs: Date.now(),
964
+ type: WAProto.Message.ProtocolMessage.Type.MESSAGE_EDIT
965
+ }
966
+ };
967
+ }
968
+ if (hasOptionalProperty(message, 'contextInfo') && !!message.contextInfo) {
969
+ const messageType = Object.keys(m)[0];
970
+ const key = m[messageType];
971
+ if ('contextInfo' in key && !!key.contextInfo) {
972
+ key.contextInfo = { ...key.contextInfo, ...message.contextInfo };
973
+ }
974
+ else if (key) {
975
+ key.contextInfo = message.contextInfo;
976
+ }
977
+ }
978
+ if (hasOptionalProperty(message, 'albumParentKey') && !!message.albumParentKey) {
979
+ m.messageContextInfo = {
980
+ ...m.messageContextInfo,
981
+ messageAssociation: {
982
+ associationType: WAProto.MessageAssociation.AssociationType.MEDIA_ALBUM,
983
+ parentMessageKey: message.albumParentKey
984
+ }
985
+ };
986
+ }
987
+ if (shouldIncludeReportingToken(m)) {
988
+ m.messageContextInfo = m.messageContextInfo || {};
989
+ if (!m.messageContextInfo.messageSecret) {
990
+ m.messageContextInfo.messageSecret = randomBytes(32);
991
+ }
992
+ }
993
+ return WAProto.Message.create(m);
994
+ };
995
+ export const generateWAMessageFromContent = (jid, message, options) => {
996
+ // set timestamp to now
997
+ // if not specified
998
+ if (!options.timestamp) {
999
+ options.timestamp = new Date();
1000
+ }
1001
+ const innerMessage = normalizeMessageContent(message);
1002
+ const key = getContentType(innerMessage);
1003
+ const timestamp = unixTimestampSeconds(options.timestamp);
1004
+ const { quoted, userJid } = options;
1005
+ if (quoted && !isJidNewsletter(jid)) {
1006
+ const participant = quoted.key.fromMe
1007
+ ? userJid // TODO: Add support for LIDs
1008
+ : quoted.participant || quoted.key.participant || quoted.key.remoteJid;
1009
+ let quotedMsg = normalizeMessageContent(quoted.message);
1010
+ const msgType = getContentType(quotedMsg);
1011
+ // strip any redundant properties
1012
+ quotedMsg = proto.Message.create({ [msgType]: quotedMsg[msgType] });
1013
+ const quotedContent = quotedMsg[msgType];
1014
+ if (typeof quotedContent === 'object' && quotedContent && 'contextInfo' in quotedContent) {
1015
+ delete quotedContent.contextInfo;
1016
+ }
1017
+ const contextInfo = ('contextInfo' in innerMessage[key] && innerMessage[key]?.contextInfo) || {};
1018
+ contextInfo.participant = jidNormalizedUser(participant);
1019
+ contextInfo.stanzaId = quoted.key.id;
1020
+ contextInfo.quotedMessage = quotedMsg;
1021
+ // if a participant is quoted, then it must be a group
1022
+ // hence, remoteJid of group must also be entered
1023
+ if (jid !== quoted.key.remoteJid) {
1024
+ contextInfo.remoteJid = quoted.key.remoteJid;
1025
+ }
1026
+ if (contextInfo && innerMessage[key]) {
1027
+ /* @ts-ignore */
1028
+ innerMessage[key].contextInfo = contextInfo;
1029
+ }
1030
+ }
1031
+ if (
1032
+ // if we want to send a disappearing message
1033
+ !!options?.ephemeralExpiration &&
1034
+ // and it's not a protocol message -- delete, toggle disappear message
1035
+ key !== 'protocolMessage' &&
1036
+ // already not converted to disappearing message
1037
+ key !== 'ephemeralMessage' &&
1038
+ // newsletters don't support ephemeral messages
1039
+ !isJidNewsletter(jid)) {
1040
+ /* @ts-ignore */
1041
+ innerMessage[key].contextInfo = {
1042
+ ...(innerMessage[key].contextInfo || {}),
1043
+ expiration: options.ephemeralExpiration || WA_DEFAULT_EPHEMERAL
1044
+ //ephemeralSettingTimestamp: options.ephemeralOptions.eph_setting_ts?.toString()
1045
+ };
1046
+ }
1047
+ message = WAProto.Message.create(message);
1048
+ const messageJSON = {
1049
+ key: {
1050
+ remoteJid: jid,
1051
+ fromMe: true,
1052
+ id: options?.messageId || generateMessageIDV2()
1053
+ },
1054
+ message: message,
1055
+ messageTimestamp: timestamp,
1056
+ messageStubParameters: [],
1057
+ participant: isJidGroup(jid) || isJidStatusBroadcast(jid) ? userJid : undefined, // TODO: Add support for LIDs
1058
+ status: WAMessageStatus.PENDING
1059
+ };
1060
+ return WAProto.WebMessageInfo.fromObject(messageJSON);
1061
+ };
1062
+ export const generateWAMessage = async (jid, content, options) => {
1063
+ // ensure msg ID is with every log
1064
+ options.logger = options?.logger?.child({ msgId: options.messageId });
1065
+ // Pass jid in the options to generateWAMessageContent
1066
+ return generateWAMessageFromContent(jid, await generateWAMessageContent(content, { ...options, jid }), options);
1067
+ };
2501
1068
  /** 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
1069
+ export const getContentType = (content) => {
1070
+ if (content) {
1071
+ const keys = Object.keys(content);
1072
+ const key = keys.find(k => (k === 'conversation' || k.includes('Message')) && k !== 'senderKeyDistributionMessage');
1073
+ return key;
1074
+ }
1075
+ };
2510
1076
  /**
2511
1077
  * Normalizes ephemeral, view once messages to regular message content
2512
1078
  * Eg. image messages in ephemeral messages, in view once messages etc.
2513
1079
  * @param content
2514
1080
  * @returns
2515
1081
  */
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
1082
+ export const normalizeMessageContent = (content) => {
1083
+ if (!content) {
1084
+ return undefined;
1085
+ }
1086
+ // set max iterations to prevent an infinite loop
1087
+ for (let i = 0; i < 5; i++) {
1088
+ const inner = getFutureProofMessage(content);
1089
+ if (!inner) {
1090
+ break;
1091
+ }
1092
+ content = inner.message;
1093
+ }
1094
+ return content;
1095
+ function getFutureProofMessage(message) {
1096
+ return (message?.ephemeralMessage ||
1097
+ message?.viewOnceMessage ||
1098
+ message?.documentWithCaptionMessage ||
1099
+ message?.viewOnceMessageV2 ||
1100
+ message?.viewOnceMessageV2Extension ||
1101
+ message?.editedMessage ||
1102
+ message?.associatedChildMessage ||
1103
+ message?.groupStatusMessage ||
1104
+ message?.groupStatusMessageV2);
1105
+ }
1106
+ };
2565
1107
  /**
2566
1108
  * Extract the true message content from a message
2567
1109
  * Eg. extracts the inner message from a disappearing message/view once message
2568
1110
  */
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
1111
+ export const extractMessageContent = (content) => {
1112
+ const extractFromTemplateMessage = (msg) => {
1113
+ if (msg.imageMessage) {
1114
+ return { imageMessage: msg.imageMessage };
1115
+ }
1116
+ else if (msg.documentMessage) {
1117
+ return { documentMessage: msg.documentMessage };
1118
+ }
1119
+ else if (msg.videoMessage) {
1120
+ return { videoMessage: msg.videoMessage };
1121
+ }
1122
+ else if (msg.locationMessage) {
1123
+ return { locationMessage: msg.locationMessage };
1124
+ }
1125
+ else {
1126
+ return {
1127
+ conversation: 'contentText' in msg ? msg.contentText : 'hydratedContentText' in msg ? msg.hydratedContentText : ''
1128
+ };
1129
+ }
1130
+ };
1131
+ content = normalizeMessageContent(content);
1132
+ if (content?.buttonsMessage) {
1133
+ return extractFromTemplateMessage(content.buttonsMessage);
1134
+ }
1135
+ if (content?.templateMessage?.hydratedFourRowTemplate) {
1136
+ return extractFromTemplateMessage(content?.templateMessage?.hydratedFourRowTemplate);
1137
+ }
1138
+ if (content?.templateMessage?.hydratedTemplate) {
1139
+ return extractFromTemplateMessage(content?.templateMessage?.hydratedTemplate);
1140
+ }
1141
+ if (content?.templateMessage?.fourRowTemplate) {
1142
+ return extractFromTemplateMessage(content?.templateMessage?.fourRowTemplate);
1143
+ }
1144
+ return content;
1145
+ };
2602
1146
  /**
2603
1147
  * Returns the device predicted by message ID
2604
1148
  */
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
1149
+ export const getDevice = (id) => /^3A.{18}$/.test(id)
1150
+ ? 'ios'
1151
+ : /^3E.{20}$/.test(id)
1152
+ ? 'web'
1153
+ : /^(.{21}|.{32})$/.test(id)
1154
+ ? 'android'
1155
+ : /^(3F|.{18}$)/.test(id)
1156
+ ? 'desktop'
1157
+ : 'wa bot';
2616
1158
  /** 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
1159
+ export const updateMessageWithReceipt = (msg, receipt) => {
1160
+ msg.userReceipt = msg.userReceipt || [];
1161
+ const recp = msg.userReceipt.find(m => m.userJid === receipt.userJid);
1162
+ if (recp) {
1163
+ Object.assign(recp, receipt);
1164
+ }
1165
+ else {
1166
+ msg.userReceipt.push(receipt);
1167
+ }
1168
+ };
2627
1169
  /** 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
1170
+ export const updateMessageWithReaction = (msg, reaction) => {
1171
+ const authorID = getKeyAuthor(reaction.key);
1172
+ const reactions = (msg.reactions || []).filter(r => getKeyAuthor(r.key) !== authorID);
1173
+ reaction.text = reaction.text || '';
1174
+ reactions.push(reaction);
1175
+ msg.reactions = reactions;
1176
+ };
2636
1177
  /** 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
1178
+ export const updateMessageWithPollUpdate = (msg, update) => {
1179
+ const authorID = getKeyAuthor(update.pollUpdateMessageKey);
1180
+ const reactions = (msg.pollUpdates || []).filter(r => getKeyAuthor(r.pollUpdateMessageKey) !== authorID);
1181
+ if (update.vote?.selectedOptions?.length) {
1182
+ reactions.push(update);
1183
+ }
1184
+ msg.pollUpdates = reactions;
1185
+ };
2648
1186
  /** 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
1187
+ export const updateMessageWithEventResponse = (msg, update) => {
1188
+ const authorID = getKeyAuthor(update.eventResponseMessageKey);
1189
+ const responses = (msg.eventResponses || []).filter(r => getKeyAuthor(r.eventResponseMessageKey) !== authorID);
1190
+ responses.push(update);
1191
+ msg.eventResponses = responses;
1192
+ };
2658
1193
  /**
2659
1194
  * Aggregates all poll updates in a poll.
2660
1195
  * @param msg the poll creation message
2661
1196
  * @param meId your jid
2662
1197
  * @returns A list of options & their voters
2663
1198
  */
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
1199
+ export function getAggregateVotesInPollMessage({ message, pollUpdates }, meId) {
1200
+ const opts = message?.pollCreationMessage?.options ||
1201
+ message?.pollCreationMessageV2?.options ||
1202
+ message?.pollCreationMessageV3?.options ||
1203
+ [];
1204
+ const voteHashMap = opts.reduce((acc, opt) => {
1205
+ const hash = sha256(Buffer.from(opt.optionName || '')).toString();
1206
+ acc[hash] = {
1207
+ name: opt.optionName || '',
1208
+ voters: []
1209
+ };
1210
+ return acc;
1211
+ }, {});
1212
+ for (const update of pollUpdates || []) {
1213
+ const { vote } = update;
1214
+ if (!vote) {
1215
+ continue;
1216
+ }
1217
+ for (const option of vote.selectedOptions || []) {
1218
+ const hash = option.toString();
1219
+ let data = voteHashMap[hash];
1220
+ if (!data) {
1221
+ voteHashMap[hash] = {
1222
+ name: 'Unknown',
1223
+ voters: []
1224
+ };
1225
+ data = voteHashMap[hash];
1226
+ }
1227
+ voteHashMap[hash].voters.push(getKeyAuthor(update.pollUpdateMessageKey, meId));
1228
+ }
1229
+ }
1230
+ return Object.values(voteHashMap);
2774
1231
  }
2775
- exports.getPollCorrectAnswer = getPollCorrectAnswer
2776
-
2777
1232
  /**
2778
1233
  * Aggregates all event responses in an event message.
2779
1234
  * @param msg the event creation message
2780
- * @param meLid your lid
1235
+ * @param meId your jid
2781
1236
  * @returns A list of response types & their responders
2782
1237
  */
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)
1238
+ export function getAggregateResponsesInEventMessage({ eventResponses }, meId) {
1239
+ const responseTypes = ['GOING', 'NOT_GOING', 'MAYBE'];
1240
+ const responseMap = {};
1241
+ for (const type of responseTypes) {
1242
+ responseMap[type] = {
1243
+ response: type,
1244
+ responders: []
1245
+ };
1246
+ }
1247
+ for (const update of eventResponses || []) {
1248
+ const responseType = update.eventResponse || 'UNKNOWN';
1249
+ if (responseType !== 'UNKNOWN' && responseMap[responseType]) {
1250
+ responseMap[responseType].responders.push(getKeyAuthor(update.eventResponseMessageKey, meId));
1251
+ }
1252
+ }
1253
+ return Object.values(responseMap);
2802
1254
  }
2803
-
2804
- exports.getAggregateResponsesInEventMessage = getAggregateResponsesInEventMessage
2805
-
2806
- const REUPLOAD_REQUIRED_STATUS = [410, 404]
1255
+ /** Given a list of message keys, aggregates them by chat & sender. Useful for sending read receipts in bulk */
1256
+ export const aggregateMessageKeysNotFromMe = (keys) => {
1257
+ const keyMap = {};
1258
+ for (const { remoteJid, id, participant, fromMe } of keys) {
1259
+ if (!fromMe) {
1260
+ const uqKey = `${remoteJid}:${participant || ''}`;
1261
+ if (!keyMap[uqKey]) {
1262
+ keyMap[uqKey] = {
1263
+ jid: remoteJid,
1264
+ participant: participant,
1265
+ messageIds: []
1266
+ };
1267
+ }
1268
+ keyMap[uqKey].messageIds.push(id);
1269
+ }
1270
+ }
1271
+ return Object.values(keyMap);
1272
+ };
1273
+ const REUPLOAD_REQUIRED_STATUS = [410, 404];
2807
1274
  /**
2808
1275
  * Downloads the given message. Throws an error if it's not a media message
2809
1276
  */
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
1277
+ export const downloadMediaMessage = async (message, type, options, ctx) => {
1278
+ const result = await downloadMsg().catch(async (error) => {
1279
+ if (ctx &&
1280
+ typeof error?.status === 'number' && // treat errors with status as HTTP failures requiring reupload
1281
+ REUPLOAD_REQUIRED_STATUS.includes(error.status)) {
1282
+ ctx.logger.info({ key: message.key }, 'sending reupload media request...');
1283
+ // request reupload
1284
+ message = await ctx.reuploadRequest(message);
1285
+ const result = await downloadMsg();
1286
+ return result;
1287
+ }
1288
+ throw error;
1289
+ });
1290
+ return result;
1291
+ async function downloadMsg() {
1292
+ const mContent = extractMessageContent(message.message);
1293
+ if (!mContent) {
1294
+ throw new Boom('No message present', { statusCode: 400, data: message });
1295
+ }
1296
+ const contentType = getContentType(mContent);
1297
+ let mediaType = contentType?.replace('Message', '');
1298
+ const media = mContent[contentType];
1299
+ if (!media || typeof media !== 'object' || (!('url' in media) && !('thumbnailDirectPath' in media))) {
1300
+ throw new Boom(`"${contentType}" message is not a media message`);
1301
+ }
1302
+ let download;
1303
+ if ('thumbnailDirectPath' in media && !('url' in media)) {
1304
+ download = {
1305
+ directPath: media.thumbnailDirectPath,
1306
+ mediaKey: media.mediaKey
1307
+ };
1308
+ mediaType = 'thumbnail-link';
1309
+ }
1310
+ else {
1311
+ download = media;
1312
+ }
1313
+ const stream = await downloadContentFromMessage(download, mediaType, options);
1314
+ if (type === 'buffer') {
1315
+ const bufferArray = [];
1316
+ for await (const chunk of stream) {
1317
+ bufferArray.push(chunk);
1318
+ }
1319
+ return Buffer.concat(bufferArray);
1320
+ }
1321
+ return stream;
1322
+ }
1323
+ };
2859
1324
  /** 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
1325
+ export const assertMediaContent = (content) => {
1326
+ content = extractMessageContent(content);
1327
+ const mediaContent = content?.documentMessage ||
1328
+ content?.imageMessage ||
1329
+ content?.videoMessage ||
1330
+ content?.audioMessage ||
1331
+ content?.stickerMessage;
1332
+ if (!mediaContent) {
1333
+ throw new Boom('given message is not a media message', { statusCode: 400, data: content });
1334
+ }
1335
+ return mediaContent;
1336
+ };
1337
+ //# sourceMappingURL=messages.js.map