@fhynella/baileys 2.1.7 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/lib/Defaults/baileys-version.json +2 -2
  2. package/lib/Defaults/connection.js +1 -1
  3. package/lib/Defaults/constants.js +13 -1
  4. package/lib/Defaults/history.js +3 -1
  5. package/lib/Signal/Group/sender-chain-key.js +1 -14
  6. package/lib/Signal/Group/sender-key-distribution-message.js +2 -2
  7. package/lib/Signal/Group/sender-key-record.js +2 -11
  8. package/lib/Signal/Group/sender-key-state.js +11 -57
  9. package/lib/Signal/libsignal.js +200 -116
  10. package/lib/Signal/lid-mapping.js +121 -68
  11. package/lib/Socket/Client/websocket.js +9 -2
  12. package/lib/Socket/business.js +5 -1
  13. package/lib/Socket/chats.js +180 -89
  14. package/lib/Socket/community.js +169 -41
  15. package/lib/Socket/groups.js +25 -21
  16. package/lib/Socket/messages-recv.js +458 -333
  17. package/lib/Socket/messages-send.js +517 -572
  18. package/lib/Socket/mex.js +61 -0
  19. package/lib/Socket/newsletter.js +159 -252
  20. package/lib/Socket/socket.js +283 -100
  21. package/lib/Types/Newsletter.js +32 -25
  22. package/lib/Utils/auth-utils.js +189 -354
  23. package/lib/Utils/browser-utils.js +43 -0
  24. package/lib/Utils/chat-utils.js +166 -41
  25. package/lib/Utils/decode-wa-message.js +77 -35
  26. package/lib/Utils/event-buffer.js +80 -24
  27. package/lib/Utils/generics.js +28 -128
  28. package/lib/Utils/history.js +10 -8
  29. package/lib/Utils/index.js +1 -1
  30. package/lib/Utils/link-preview.js +17 -32
  31. package/lib/Utils/lt-hash.js +28 -22
  32. package/lib/Utils/make-mutex.js +26 -28
  33. package/lib/Utils/message-retry-manager.js +51 -3
  34. package/lib/Utils/messages-media.js +364 -173
  35. package/lib/Utils/messages.js +735 -727
  36. package/lib/Utils/noise-handler.js +33 -2
  37. package/lib/Utils/pre-key-manager.js +126 -0
  38. package/lib/Utils/process-message.js +115 -55
  39. package/lib/Utils/signal.js +45 -18
  40. package/lib/Utils/validate-connection.js +52 -29
  41. package/lib/WABinary/constants.js +1268 -1268
  42. package/lib/WABinary/decode.js +58 -4
  43. package/lib/WABinary/encode.js +54 -7
  44. package/lib/WABinary/jid-utils.js +58 -11
  45. package/lib/WAM/constants.js +19064 -11563
  46. package/lib/WAM/encode.js +57 -8
  47. package/lib/WAUSync/USyncQuery.js +35 -19
  48. package/package.json +26 -7
  49. package/lib/Socket/usync.js +0 -83
@@ -3,7 +3,6 @@
3
3
  Object.defineProperty(exports, "__esModule", { value: true })
4
4
 
5
5
  const { Boom } = require("@hapi/boom")
6
- const axios_1 = require("axios")
7
6
  const { randomBytes } = require("crypto")
8
7
  const { promises } = require("fs")
9
8
  const { proto } = require("../../WAProto")
@@ -97,6 +96,7 @@ const assertColor = async (color) => {
97
96
 
98
97
  const prepareWAMessageMedia = async (message, options) => {
99
98
  const logger = options.logger
99
+
100
100
  let mediaType
101
101
 
102
102
  for (const key of MEDIA_KEYS) {
@@ -106,7 +106,7 @@ const prepareWAMessageMedia = async (message, options) => {
106
106
  }
107
107
 
108
108
  if (!mediaType) {
109
- throw new Boom('Invalid media type', { statusCode: 400 });
109
+ throw new Boom('Invalid media type', { statusCode: 400 })
110
110
  }
111
111
 
112
112
  const uploadData = {
@@ -133,11 +133,15 @@ const prepareWAMessageMedia = async (message, options) => {
133
133
 
134
134
  if (cacheableKey) {
135
135
  const mediaBuff = await options.mediaCache.get(cacheableKey)
136
+
136
137
  if (mediaBuff) {
137
138
  logger?.debug({ cacheableKey }, 'got media cache hit')
138
- const obj = WAProto.Message.decode(mediaBuff)
139
+
140
+ const obj = proto.Message.decode(mediaBuff)
139
141
  const key = `${mediaType}Message`
142
+
140
143
  Object.assign(obj[key], { ...uploadData, media: undefined })
144
+
141
145
  return obj
142
146
  }
143
147
  }
@@ -147,7 +151,7 @@ const prepareWAMessageMedia = async (message, options) => {
147
151
  if (isNewsletter) {
148
152
  logger?.info({ key: cacheableKey }, 'Preparing raw media for newsletter')
149
153
  const { filePath, fileSha256, fileLength } = await getRawMediaUploadData(uploadData.media, options.mediaTypeOverride || mediaType, logger)
150
- const fileSha256B64 = fileSha256.toString('base64')
154
+ const fileSha256B64 = fileSha256.toString('base64');
151
155
  const { mediaUrl, directPath } = await options.upload(filePath, {
152
156
  fileEncSha256B64: fileSha256B64,
153
157
  mediaType: mediaType,
@@ -173,6 +177,10 @@ const prepareWAMessageMedia = async (message, options) => {
173
177
  delete obj.videoMessage
174
178
  }
175
179
 
180
+ if (obj.stickerMessage) {
181
+ obj.stickerMessage.stickerSentTs = Date.now()
182
+ }
183
+
176
184
  if (cacheableKey) {
177
185
  logger?.debug({ cacheableKey }, 'set cache');
178
186
  await options.mediaCache.set(cacheableKey, WAProto.Message.encode(obj).finish())
@@ -186,15 +194,12 @@ const prepareWAMessageMedia = async (message, options) => {
186
194
  const requiresWaveformProcessing = mediaType === 'audio' && uploadData.ptt === true
187
195
  const requiresAudioBackground = options.backgroundColor && mediaType === 'audio' && uploadData.ptt === true
188
196
  const requiresOriginalForSomeProcessing = requiresDurationComputation || requiresThumbnailComputation
189
-
190
197
  const { mediaKey, encFilePath, originalFilePath, fileEncSha256, fileSha256, fileLength } = await encryptedStream(uploadData.media, options.mediaTypeOverride || mediaType, {
191
198
  logger,
192
199
  saveOriginalFileIfRequired: requiresOriginalForSomeProcessing,
193
200
  opts: options.options
194
201
  })
195
-
196
202
  const fileEncSha256B64 = fileEncSha256.toString('base64')
197
-
198
203
  const [{ mediaUrl, directPath }] = await Promise.all([
199
204
  (async () => {
200
205
  const result = await options.upload(encFilePath, {
@@ -205,20 +210,19 @@ const prepareWAMessageMedia = async (message, options) => {
205
210
  logger?.debug({ mediaType, cacheableKey }, 'uploaded media')
206
211
  return result
207
212
  })(),
208
-
209
213
  (async () => {
210
214
  try {
211
215
  if (requiresThumbnailComputation) {
212
216
  const { thumbnail, originalImageDimensions } = await generateThumbnail(originalFilePath, mediaType, options)
217
+
213
218
  uploadData.jpegThumbnail = thumbnail
219
+
214
220
  if (!uploadData.width && originalImageDimensions) {
215
221
  uploadData.width = originalImageDimensions.width
216
222
  uploadData.height = originalImageDimensions.height
217
-
218
223
  logger?.debug('set dimensions')
219
224
  }
220
-
221
- logger?.debug('generated thumbnail');
225
+ logger?.debug('generated thumbnail')
222
226
  }
223
227
 
224
228
  if (requiresDurationComputation) {
@@ -275,7 +279,7 @@ const prepareWAMessageMedia = async (message, options) => {
275
279
  }
276
280
 
277
281
  if (cacheableKey) {
278
- logger?.debug({ cacheableKey }, 'set cache');
282
+ logger?.debug({ cacheableKey }, 'set cache')
279
283
  await options.mediaCache.set(cacheableKey, WAProto.Message.encode(obj).finish())
280
284
  }
281
285
 
@@ -292,7 +296,7 @@ const prepareAlbumMessageContent = async (jid, albums, options) => {
292
296
  }
293
297
 
294
298
  const validCount = albums.filter(m => ('image' in m) || ('video' in m)).length
295
-
299
+
296
300
  if (validCount === 0) {
297
301
  throw new Error("albums contains no valid media. Use 'image' or 'video' keys.")
298
302
  }
@@ -411,23 +415,49 @@ const generateForwardMessageContent = (message, forceForward) => {
411
415
  return content
412
416
  }
413
417
 
418
+ const hasNonNullishProperty = (message, key) => {
419
+ return (
420
+ typeof message === 'object' &&
421
+ message !== null &&
422
+ key in message &&
423
+ message[key] !== null &&
424
+ message[key] !== undefined
425
+ )
426
+ }
427
+
428
+ function hasOptionalProperty(obj, key) {
429
+ return (
430
+ typeof obj === 'object' &&
431
+ obj !== null &&
432
+ key in obj &&
433
+ obj[key] !== null
434
+ )
435
+ }
436
+
414
437
  const generateWAMessageContent = async (message, options) => {
415
438
  let m = {}
416
- if ('text' in message) {
439
+
440
+ if (hasNonNullishProperty(message, "text")) {
417
441
  const extContent = { text: message.text }
442
+
418
443
  let urlInfo = message.linkPreview
419
444
 
420
- if (typeof urlInfo === 'undefined') {
421
- urlInfo = await generateLinkPreviewIfRequired(message.text, options.getUrlInfo, options.logger)
445
+ if (typeof urlInfo === "undefined") {
446
+ urlInfo = await generateLinkPreviewIfRequired(
447
+ message.text,
448
+ options.getUrlInfo,
449
+ options.logger
450
+ )
422
451
  }
423
452
 
424
453
  if (urlInfo) {
425
- extContent.canonicalUrl = urlInfo['canonical-url']
426
- extContent.matchedText = urlInfo['matched-text']
454
+ extContent.canonicalUrl = urlInfo["canonical-url"]
455
+ extContent.matchedText = urlInfo["matched-text"]
427
456
  extContent.jpegThumbnail = urlInfo.jpegThumbnail
428
457
  extContent.description = urlInfo.description
429
458
  extContent.title = urlInfo.title
430
459
  extContent.previewType = 0
460
+
431
461
  const img = urlInfo.highQualityThumbnail
432
462
 
433
463
  if (img) {
@@ -442,848 +472,853 @@ const generateWAMessageContent = async (message, options) => {
442
472
  }
443
473
 
444
474
  if (options.backgroundColor) {
445
- extContent.backgroundArgb = await assertColor(options.backgroundColor)
475
+ extContent.backgroundArgb = await assertColor(
476
+ options.backgroundColor
477
+ )
446
478
  }
447
479
 
448
480
  if (options.textColor) {
449
- extContent.textArgb = await assertColor(options.textColor)
481
+ extContent.textArgb = await assertColor(options.textColor)
450
482
  }
451
483
 
452
484
  if (options.font) {
453
485
  extContent.font = options.font
454
486
  }
455
487
 
456
- extContent.contextInfo = {
457
- ...(message.contextInfo || {}),
458
- ...(message.mentions ? { mentionedJid: message.mentions } : {})
459
- }
460
-
461
488
  m.extendedTextMessage = extContent
462
- }
463
-
464
- else if ('contacts' in message) {
489
+ } else if (hasNonNullishProperty(message, "contacts")) {
465
490
  const contactLen = message.contacts.contacts.length
466
491
 
467
492
  let contactMessage
468
493
 
469
494
  if (!contactLen) {
470
- throw new Boom('require atleast 1 contact', { statusCode: 400 })
495
+ throw new Boom("require atleast 1 contact", { statusCode: 400 })
471
496
  }
472
497
 
473
498
  if (contactLen === 1) {
474
499
  contactMessage = {
475
- contactMessage: WAProto.Message.ContactMessage.fromObject(message.contacts.contacts[0])
500
+ contactMessage: WAProto.Message.ContactMessage.fromObject(
501
+ message.contacts.contacts[0]
502
+ ),
476
503
  }
477
- }
478
-
479
- else {
504
+ } else {
480
505
  contactMessage = {
481
- contactsArrayMessage: WAProto.Message.ContactsArrayMessage.fromObject(message.contacts)
506
+ contactsArrayMessage: WAProto.Message.ContactsArrayMessage.fromObject(
507
+ message.contacts
508
+ ),
482
509
  }
483
510
  }
484
511
 
485
- const [type] = Object.keys(contactMessage)
486
-
487
- contactMessage[type].contextInfo = {
488
- ...(message.contextInfo || {}),
489
- ...(message.mentions ? { mentionedJid: message.mentions } : {})
490
- }
491
-
492
512
  m = contactMessage
493
- }
513
+ } else if (hasNonNullishProperty(message, "contacts")) {
514
+ const contactLen = message.contacts.contacts.length
494
515
 
495
- else if ('location' in message) {
496
- let locationMessage
516
+ let contactMessage
497
517
 
498
- if (message.live) {
499
- locationMessage = {
500
- liveLocationMessage: WAProto.Message.LiveLocationMessage.fromObject(message.location)
501
- }
518
+ if (!contactLen) {
519
+ throw new Boom("require atleast 1 contact", { statusCode: 400 })
502
520
  }
503
521
 
504
- else {
505
- locationMessage = {
506
- locationMessage: WAProto.Message.LocationMessage.fromObject(message.location)
522
+ if (contactLen === 1) {
523
+ contactMessage = {
524
+ contactMessage: WAProto.Message.ContactMessage.fromObject(
525
+ message.contacts.contacts[0]
526
+ ),
527
+ }
528
+ } else {
529
+ contactMessage = {
530
+ contactsArrayMessage: WAProto.Message.ContactsArrayMessage.fromObject(
531
+ message.contacts
532
+ ),
507
533
  }
508
534
  }
509
535
 
510
- const [type] = Object.keys(locationMessage)
511
-
512
- locationMessage[type].contextInfo = {
513
- ...(message.contextInfo || {}),
514
- ...(message.mentions ? { mentionedJid: message.mentions } : {})
536
+ m = contactMessage
537
+ } else if (hasNonNullishProperty(message, "location")) {
538
+ let locationMessage
539
+
540
+ if (message.live) {
541
+ locationMessage = {
542
+ liveLocationMessage: WAProto.Message.LiveLocationMessage.fromObject(
543
+ message.location
544
+ ),
545
+ }
546
+ } else {
547
+ locationMessage = {
548
+ locationMessage: WAProto.Message.LocationMessage.fromObject(
549
+ message.location
550
+ ),
551
+ }
515
552
  }
516
553
 
517
554
  m = locationMessage
518
- }
519
-
520
- else if ('react' in message) {
555
+ } else if (hasNonNullishProperty(message, "react")) {
521
556
  if (!message.react.senderTimestampMs) {
522
557
  message.react.senderTimestampMs = Date.now()
523
558
  }
524
559
 
525
- m.reactionMessage = WAProto.Message.ReactionMessage.fromObject(message.react)
526
- }
527
-
528
- else if ('delete' in message) {
560
+ m.reactionMessage = WAProto.Message.ReactionMessage.fromObject(
561
+ message.react
562
+ )
563
+ } else if (hasNonNullishProperty(message, "delete")) {
529
564
  m.protocolMessage = {
530
565
  key: message.delete,
531
- type: WAProto.Message.ProtocolMessage.Type.REVOKE
566
+ type: WAProto.Message.ProtocolMessage.Type.REVOKE,
532
567
  }
533
- }
534
-
535
- else if ('forward' in message) {
536
- const mess = generateForwardMessageContent(message.forward, message.force)
537
- const [type] = Object.keys(mess)
538
-
539
- mess[type].contextInfo = {
540
- ...(message.contextInfo || {}),
541
- ...(message.mentions ? { mentionedJid: message.mentions } : {})
568
+ } else if (hasNonNullishProperty(message, "sharePhoneNumber")) {
569
+ m.protocolMessage = {
570
+ type: WAProto.Message.ProtocolMessage.Type.SHARE_PHONE_NUMBER,
542
571
  }
543
-
544
- m = mess
545
- }
546
-
547
- else if ('disappearingMessagesInChat' in message) {
548
- const exp = typeof message.disappearingMessagesInChat === 'boolean' ?
549
- (message.disappearingMessagesInChat ? WA_DEFAULT_EPHEMERAL : 0) :
550
- message.disappearingMessagesInChat
572
+ } else if (hasNonNullishProperty(message, "requestPhoneNumber")) {
573
+ m.requestPhoneNumberMessage = {}
574
+ } else if (hasNonNullishProperty(message, "forward")) {
575
+ m = generateForwardMessageContent(message.forward, message.force)
576
+ } else if (hasNonNullishProperty(message, "disappearingMessagesInChat")) {
577
+ const exp =
578
+ typeof message.disappearingMessagesInChat === "boolean"
579
+ ? message.disappearingMessagesInChat
580
+ ? WA_DEFAULT_EPHEMERAL
581
+ : 0
582
+ : message.disappearingMessagesInChat
551
583
  m = prepareDisappearingMessageSettingContent(exp)
552
- }
553
-
554
- else if ('groupInvite' in message) {
555
- m.groupInviteMessage = {}
584
+ } else if (hasNonNullishProperty(message, "groupInvite")) {
585
+ m.groupInviteMessage = {}
556
586
 
557
587
  m.groupInviteMessage.inviteCode = message.groupInvite.code
558
588
  m.groupInviteMessage.inviteExpiration = message.groupInvite.expiration
559
589
  m.groupInviteMessage.caption = message.groupInvite.caption
560
590
  m.groupInviteMessage.groupJid = message.groupInvite.jid
561
- m.groupInviteMessage.groupName = message.groupInvite.name
562
- m.groupInviteMessage.contextInfo = message.contextInfo
591
+ m.groupInviteMessage.groupName = message.groupInvite.name
592
+ m.groupInviteMessage.contextInfo = message.contextInfo
563
593
 
564
594
  if (options.getProfilePicUrl) {
565
- const pfpUrl = await options.getProfilePicUrl(message.groupInvite.jid)
566
- const { thumbnail } = await generateThumbnail(pfpUrl, 'image')
595
+ const pfpUrl = await options.getProfilePicUrl(
596
+ message.groupInvite.jid
597
+ )
598
+ const { thumbnail } = await generateThumbnail(pfpUrl, "image")
567
599
  m.groupInviteMessage.jpegThumbnail = thumbnail
568
600
  }
569
-
570
- m.groupInviteMessage.contextInfo = {
571
- ...(message.contextInfo || {}),
572
- ...(message.mentions ? { mentionedJid: message.mentions } : {})
573
- }
574
- }
575
-
576
- else if ('adminInvite' in message) {
577
- m.newsletterAdminInviteMessage = {}
601
+ } else if (hasNonNullishProperty(message, "adminInvite")) {
602
+ m.newsletterAdminInviteMessage = {}
578
603
 
579
604
  m.newsletterAdminInviteMessage.newsletterJid = message.adminInvite.jid
580
- m.newsletterAdminInviteMessage.newsletterName= message.adminInvite.name
605
+ m.newsletterAdminInviteMessage.newsletterName =
606
+ message.adminInvite.name
581
607
  m.newsletterAdminInviteMessage.caption = message.adminInvite.caption
582
- m.newsletterAdminInviteMessage.inviteExpiration = message.adminInvite.expiration
583
- m.newsletterAdminInviteMessage.contextInfo = message.contextInfo
608
+ m.newsletterAdminInviteMessage.inviteExpiration =
609
+ message.adminInvite.expiration
610
+ m.newsletterAdminInviteMessage.contextInfo = message.contextInfo
584
611
 
585
612
  if (options.getProfilePicUrl) {
586
- const pfpUrl = await options.getProfilePicUrl(message.adminInvite.jid)
587
- const { thumbnail } = await generateThumbnail(pfpUrl, 'image')
613
+ const pfpUrl = await options.getProfilePicUrl(
614
+ message.adminInvite.jid
615
+ )
616
+ const { thumbnail } = await generateThumbnail(pfpUrl, "image")
588
617
  m.newsletterAdminInviteMessage.jpegThumbnail = thumbnail
589
618
  }
590
-
591
- m.newsletterAdminInviteMessage.contextInfo = {
592
- ...(message.contextInfo || {}),
593
- ...(message.mentions ? { mentionedJid: message.mentions } : {})
594
- }
595
- }
596
-
597
- else if ('pin' in message) {
598
- m.pinInChatMessage = {}
599
- m.messageContextInfo = {}
600
-
601
- m.pinInChatMessage.key = message.pin.key
602
- m.pinInChatMessage.type = message.pin?.type || 1
603
- m.pinInChatMessage.senderTimestampMs = message.pin?.time || Date.now()
604
- m.messageContextInfo.messageAddOnDurationInSecs = message.pin.type === 1 ? message.pin.time || 86400 : 0
605
- m.messageContextInfo.messageAddOnExpiryType = proto.MessageContextInfo.MessageAddonExpiryType.STATIC
606
- }
607
-
608
- else if ('keep' in message) {
619
+ } else if (hasNonNullishProperty(message, "keep")) {
609
620
  m.keepInChatMessage = {}
610
621
 
611
622
  m.keepInChatMessage.key = message.keep.key
612
623
  m.keepInChatMessage.keepType = message.keep?.type || 1
613
624
  m.keepInChatMessage.timestampMs = message.keep?.time || Date.now()
614
- }
615
-
616
- else if ('call' in message) {
625
+ } else if (hasNonNullishProperty(message, "call")) {
617
626
  m.scheduledCallCreationMessage = {}
618
627
 
619
- m.scheduledCallCreationMessage.scheduledTimestampMs = message.call?.time || Date.now()
628
+ m.scheduledCallCreationMessage.scheduledTimestampMs =
629
+ message.call?.time || Date.now()
620
630
  m.scheduledCallCreationMessage.callType = message.call?.type || 1
621
- m.scheduledCallCreationMessage.title = message.call?.name || 'Call Creation'
631
+ m.scheduledCallCreationMessage.title =
632
+ message.call?.name || "Call Creation"
633
+ } else if (hasNonNullishProperty(message, "paymentInvite")) {
634
+ m.messageContextInfo = {}
635
+ m.paymentInviteMessage = {}
636
+
637
+ m.paymentInviteMessage.expiryTimestamp =
638
+ message.paymentInvite?.expiry || 0
639
+ m.paymentInviteMessage.serviceType = message.paymentInvite?.type || 2
640
+ } else if (hasNonNullishProperty(message, "ptv")) {
641
+ const { videoMessage } = await prepareWAMessageMedia(
642
+ { video: message.video },
643
+ options
644
+ )
645
+
646
+ m.ptvMessage = videoMessage
647
+ } else if (hasNonNullishProperty(message, "order")) {
648
+ m.orderMessage = WAProto.Message.OrderMessage.fromObject(message.order)
649
+ } else if (hasNonNullishProperty(message, "product")) {
650
+ const { imageMessage } = await prepareWAMessageMedia(
651
+ { image: message.product.productImage },
652
+ options
653
+ )
654
+
655
+ m.productMessage = WAProto.Message.ProductMessage.fromObject({
656
+ ...message,
657
+ product: {
658
+ ...message.product,
659
+ productImage: imageMessage,
660
+ },
661
+ })
662
+ } else if (hasNonNullishProperty(message, "album")) {
663
+ const imageMessages = message.album.filter((item) => "image" in item)
664
+ const videoMessages = message.album.filter((item) => "video" in item)
665
+
666
+ m.albumMessage = WAProto.Message.AlbumMessage.fromObject({
667
+ expectedImageCount: imageMessages.length,
668
+ expectedVideoCount: videoMessages.length,
669
+ })
670
+ } else if (hasNonNullishProperty(message, "event")) {
671
+ m.eventMessage = WAProto.Message.EventMessage.fromObject(message.event)
622
672
 
623
- m.scheduledCallCreationMessage.contextInfo = {
624
- ...(message.contextInfo || {}),
625
- ...(message.mentions ? { mentionedJid: message.mentions } : {})
673
+ if (!message.event.startTime) {
674
+ m.eventMessage.startTime = unixTimestampSeconds() + 86400
626
675
  }
627
- }
628
676
 
629
- else if ('paymentInvite' in message) {
630
- m.messageContextInfo = {}
631
- m.paymentInviteMessage = {}
677
+ if (options.getCallLink && message.event.call) {
678
+ const link = await options.getCallLink(
679
+ message.event.call,
680
+ m.eventMessage.startTime
681
+ )
682
+ m.eventMessage.joinLink = link
683
+ }
684
+ } else if (hasNonNullishProperty(message, "pollResult")) {
685
+ if (!Array.isArray(message.pollResult.values)) {
686
+ throw new Boom("Invalid pollResult values", { statusCode: 400 })
687
+ }
632
688
 
633
- m.paymentInviteMessage.expiryTimestamp = message.paymentInvite?.expiry || 0
634
- m.paymentInviteMessage.serviceType = message.paymentInvite?.type || 2
689
+ const pollResultSnapshotMessage = {
690
+ name: message.pollResult.name,
691
+ pollVotes: message.pollResult.values.map(
692
+ ([optionName, optionVoteCount]) => ({
693
+ optionName,
694
+ optionVoteCount,
695
+ })
696
+ ),
697
+ }
635
698
 
636
- m.paymentInviteMessage.contextInfo = {
637
- ...(message.contextInfo || {}),
638
- ...(message.mentions ? { mentionedJid: message.mentions } : {})
699
+ m.pollResultSnapshotMessage = pollResultSnapshotMessage
700
+ } else if (hasNonNullishProperty(message, "poll")) {
701
+ if (!Array.isArray(message.poll.values)) {
702
+ throw new Boom("Invalid poll values", { statusCode: 400 })
639
703
  }
640
- }
641
704
 
642
- else if ('buttonReply' in message) {
705
+ if (
706
+ message.poll.selectableCount < 0 ||
707
+ message.poll.selectableCount > message.poll.values.length
708
+ ) {
709
+ throw new Boom(
710
+ `poll.selectableCount in poll should be >= 0 and <= ${message.poll.values.length}`,
711
+ { statusCode: 400 }
712
+ )
713
+ }
714
+
715
+ const pollCreationMessage = {
716
+ name: message.poll.name,
717
+ selectableOptionsCount: message.poll?.selectableCount || 0,
718
+ options: message.poll.values.map((optionName) => ({ optionName })),
719
+ }
720
+
721
+ if (message.poll?.toAnnouncementGroup) {
722
+ m.pollCreationMessageV2 = pollCreationMessage
723
+ } else {
724
+ if (message.poll.selectableCount > 0) {
725
+ m.pollCreationMessageV3 = pollCreationMessage
726
+ } else {
727
+ m.pollCreationMessage = pollCreationMessage
728
+ }
729
+ }
730
+ } else if (hasNonNullishProperty(message, "payment")) {
731
+ const requestPaymentMessage = {
732
+ amount: {
733
+ currencyCode: message.payment?.currency || "IDR",
734
+ offset: message.payment?.offset || 0,
735
+ value: message.payment?.amount || 999999999,
736
+ },
737
+ expiryTimestamp: message.payment?.expiry || 0,
738
+ amount1000: message.payment?.amount || 999999999 * 1000,
739
+ currencyCodeIso4217: message.payment?.currency || "IDR",
740
+ requestFrom: message.payment?.from || "0@s.whatsapp.net",
741
+ noteMessage: {
742
+ extendedTextMessage: {
743
+ text: message.payment?.note || "Notes",
744
+ },
745
+ },
746
+ background: {
747
+ placeholderArgb:
748
+ message.payment?.image?.placeholderArgb || 4278190080,
749
+ textArgb: message.payment?.image?.textArgb || 4294967295,
750
+ subtextArgb: message.payment?.image?.subtextArgb || 4294967295,
751
+ type: 1,
752
+ },
753
+ }
754
+
755
+ m.requestPaymentMessage = requestPaymentMessage
756
+ } else if (hasNonNullishProperty(message, "stickerPack")) {
757
+ const {
758
+ stickers,
759
+ cover,
760
+ name,
761
+ publisher,
762
+ packId,
763
+ description,
764
+ } = message.stickerPack
765
+
766
+ const { zip } = require("fflate")
767
+
768
+ const stickerData = {}
769
+ const stickerPromises = stickers.map(async (s, i) => {
770
+ const { stream } = await getStream(s.sticker)
771
+ const buffer = await toBuffer(stream)
772
+ const hash = sha256(buffer).toString("base64url")
773
+ const fileName = `${i.toString().padStart(2, "0")}_${hash}.webp`
774
+
775
+ stickerData[fileName] = [new Uint8Array(buffer), { level: 0 }]
776
+
777
+ return {
778
+ fileName,
779
+ mimetype: "image/webp",
780
+ isAnimated: s.isAnimated || false,
781
+ isLottie: s.isLottie || false,
782
+ emojis: s.emojis || [],
783
+ accessibilityLabel: s.accessibilityLabel || "",
784
+ }
785
+ })
786
+
787
+ const stickerMetadata = await Promise.all(stickerPromises)
788
+
789
+ const zipBuffer = await new Promise((resolve, reject) => {
790
+ zip(stickerData, (err, data) => {
791
+ if (err) {
792
+ reject(err)
793
+ } else {
794
+ resolve(Buffer.from(data))
795
+ }
796
+ })
797
+ })
798
+
799
+ const coverBuffer = await toBuffer((await getStream(cover)).stream)
800
+
801
+ const [stickerPackUpload, coverUpload] = await Promise.all([
802
+ encryptedStream(zipBuffer, "sticker-pack", {
803
+ logger: options.logger,
804
+ opts: options.options,
805
+ }),
806
+ prepareWAMessageMedia(
807
+ { image: coverBuffer },
808
+ { ...options, mediaTypeOverride: "image" }
809
+ ),
810
+ ])
811
+
812
+ const stickerPackUploadResult = await options.upload(
813
+ stickerPackUpload.encFilePath,
814
+ {
815
+ fileEncSha256B64: stickerPackUpload.fileEncSha256.toString(
816
+ "base64"
817
+ ),
818
+ mediaType: "sticker-pack",
819
+ timeoutMs: options.mediaUploadTimeoutMs,
820
+ }
821
+ )
822
+
823
+ const coverImage = coverUpload.imageMessage
824
+ const imageDataHash = sha256(coverBuffer).toString("base64")
825
+ const stickerPackId = packId || generateMessageID()
826
+
827
+ m.stickerPackMessage = {
828
+ name,
829
+ publisher,
830
+ stickerPackId,
831
+ packDescription: description,
832
+ stickerPackOrigin:
833
+ proto.Message.StickerPackMessage.StickerPackOrigin.THIRD_PARTY,
834
+ stickerPackSize: stickerPackUpload.fileLength,
835
+ stickers: stickerMetadata,
836
+ fileSha256: stickerPackUpload.fileSha256,
837
+ fileEncSha256: stickerPackUpload.fileEncSha256,
838
+ mediaKey: stickerPackUpload.mediaKey,
839
+ directPath: stickerPackUploadResult.directPath,
840
+ fileLength: stickerPackUpload.fileLength,
841
+ mediaKeyTimestamp: unixTimestampSeconds(),
842
+ trayIconFileName: `${stickerPackId}.png`,
843
+ imageDataHash,
844
+ thumbnailDirectPath: coverImage.directPath,
845
+ thumbnailFileSha256: coverImage.fileSha256,
846
+ thumbnailFileEncSha256: coverImage.fileEncSha256,
847
+ thumbnailHeight: coverImage.height,
848
+ thumbnailWidth: coverImage.width,
849
+ }
850
+ } else {
851
+ m = await prepareWAMessageMedia(message, options)
852
+ }
853
+
854
+ if (hasNonNullishProperty(message, "buttonReply")) {
643
855
  switch (message.type) {
644
- case 'list':
856
+ case "list":
645
857
  m.listResponseMessage = {
646
858
  title: message.buttonReply.title,
647
859
  description: message.buttonReply.description,
648
860
  singleSelectReply: {
649
- selectedRowId: message.buttonReply.rowId
650
- },
651
- lisType: proto.Message.ListResponseMessage.ListType.SINGLE_SELECT
861
+ selectedRowId: message.buttonReply.rowId,
862
+ },
863
+ lisType:
864
+ proto.Message.ListResponseMessage.ListType
865
+ .SINGLE_SELECT,
652
866
  }
653
867
  break
654
- case 'template':
868
+ case "template":
655
869
  m.templateButtonReplyMessage = {
656
870
  selectedDisplayText: message.buttonReply.displayText,
657
871
  selectedId: message.buttonReply.id,
658
- selectedIndex: message.buttonReply.index
872
+ selectedIndex: message.buttonReply.index,
659
873
  }
660
874
  break
661
- case 'plain':
875
+ case "plain":
662
876
  m.buttonsResponseMessage = {
663
877
  selectedButtonId: message.buttonReply.id,
664
878
  selectedDisplayText: message.buttonReply.displayText,
665
- type: proto.Message.ButtonsResponseMessage.Type.DISPLAY_TEXT
879
+ type:
880
+ proto.Message.ButtonsResponseMessage.Type.DISPLAY_TEXT,
666
881
  }
667
882
  break
668
- case 'interactive':
883
+ case "interactive":
669
884
  m.interactiveResponseMessage = {
670
885
  body: {
671
- text: message.buttonReply.displayText,
672
- format: proto.Message.InteractiveResponseMessage.Body.Format.EXTENSIONS_1
673
- },
886
+ text: message.buttonReply.displayText,
887
+ format:
888
+ proto.Message.InteractiveResponseMessage.Body.Format
889
+ .EXTENSIONS_1,
890
+ },
674
891
  nativeFlowResponseMessage: {
675
- name: message.buttonReply.nativeFlows.name,
676
- paramsJson: message.buttonReply.nativeFlows.paramsJson,
677
- version: message.buttonReply.nativeFlows.version
678
- }
892
+ name: message.buttonReply.nativeFlows.name,
893
+ paramsJson: message.buttonReply.nativeFlows.paramsJson,
894
+ version: message.buttonReply.nativeFlows.version,
895
+ },
679
896
  }
680
- break
897
+ break
681
898
  }
682
- }
683
-
684
- else if ('ptv' in message && message.ptv) {
685
- const { videoMessage } = await prepareWAMessageMedia({ video: message.video }, options)
686
-
687
- m.ptvMessage = videoMessage
688
- }
689
-
690
- else if ('album' in message) {
691
- const imageMessages = message.album.filter(item => 'image' in item)
692
- const videoMessages = message.album.filter(item => 'video' in item)
693
-
694
- m.albumMessage = WAProto.Message.AlbumMessage.fromObject({
695
- expectedImageCount: imageMessages.length,
696
- expectedVideoCount: videoMessages.length
697
- })
698
- }
699
-
700
- else if ('order' in message) {
701
- m.orderMessage = WAProto.Message.OrderMessage.fromObject(message.order)
702
-
703
- m.orderMessage.contextInfo = {
704
- ...(message.contextInfo || {}),
705
- ...(message.mentions ? { mentionedJid: message.mentions } : {})
899
+ } else if (hasNonNullishProperty(message, "sections")) {
900
+ m.listMessage = {
901
+ title: message.title,
902
+ buttonText: message.buttonText,
903
+ footerText: message.footer,
904
+ description: message.text,
905
+ sections: message.sections,
906
+ listType: proto.Message.ListMessage.ListType.SINGLE_SELECT,
907
+ }
908
+ } else if (hasNonNullishProperty(message, "productList")) {
909
+ const thumbnail = message.thumbnail
910
+ ? await generateThumbnail(message.thumbnail, "image")
911
+ : null
912
+
913
+ m.listMessage = {
914
+ title: message.title,
915
+ buttonText: message.buttonText,
916
+ footerText: message.footer,
917
+ description: message.text,
918
+ productListInfo: {
919
+ productSections: message.productList,
920
+ headerImage: {
921
+ productId: message.productList[0].products[0].productId,
922
+ jpegThumbnail: thumbnail?.thumbnail || null,
923
+ },
924
+ businessOwnerJid: message.businessOwnerJid,
925
+ },
926
+ listType: proto.Message.ListMessage.ListType.PRODUCT_LIST,
706
927
  }
707
- }
708
-
709
- else if ('event' in message) {
710
- m.eventMessage = WAProto.Message.EventMessage.fromObject(message.event)
711
-
712
- if (!message.event.startTime) {
713
- m.eventMessage.startTime = unixTimestampSeconds() + 86400
714
- }
715
-
716
- if (options.getCallLink && message.event.call) {
717
- const link = await options.getCallLink(message.event.call, m.eventMessage.startTime)
718
- m.eventMessage.joinLink = link
719
- }
720
-
721
-
722
- m.eventMessage.contextInfo = {
723
- ...(message.contextInfo || {}),
724
- ...(message.mentions ? { mentionedJid: message.mentions } : {})
928
+ } else if (hasNonNullishProperty(message, "buttons")) {
929
+ const buttonsMessage = {
930
+ buttons: message.buttons.map((b) => ({
931
+ ...b,
932
+ type: proto.Message.ButtonsMessage.Button.Type.RESPONSE,
933
+ })),
725
934
  }
726
- }
727
935
 
728
- else if ('product' in message) {
729
- const { imageMessage } = await prepareWAMessageMedia({ image: message.product.productImage }, options)
730
-
731
- m.productMessage = WAProto.Message.ProductMessage.fromObject({
732
- ...message,
733
- product: {
734
- ...message.product,
735
- productImage: imageMessage,
936
+ if (hasNonNullishProperty(message, "text")) {
937
+ buttonsMessage.contentText = message.text
938
+ buttonsMessage.headerType =
939
+ proto.Message.ButtonsMessage.HeaderType.EMPTY
940
+ } else {
941
+ if (hasNonNullishProperty(message, "caption")) {
942
+ buttonsMessage.contentText = message.caption
736
943
  }
737
- })
738
-
739
- m.productMessage.contextInfo = {
740
- ...(message.contextInfo || {}),
741
- ...(message.mentions ? { mentionedJid: message.mentions } : {})
742
- }
743
- }
744
944
 
745
- else if ('pollResult' in message) {
746
- if (!Array.isArray(message.pollResult.values)) {
747
- throw new Boom('Invalid pollResult values', { statusCode: 400 })
748
- }
945
+ const type = Object.keys(m)[0].replace("Message", "").toUpperCase()
749
946
 
750
- const pollResultSnapshotMessage = {
751
- name: message.pollResult.name,
752
- pollVotes: message.pollResult.values.map(([optionName, optionVoteCount]) => ({
753
- optionName,
754
- optionVoteCount
755
- }))
756
- }
947
+ buttonsMessage.headerType =
948
+ proto.Message.ButtonsMessage.HeaderType[type]
757
949
 
758
- pollResultSnapshotMessage.contextInfo = {
759
- ...(message.contextInfo || {}),
760
- ...(message.mentions ? { mentionedJid: message.mentions } : {})
950
+ Object.assign(buttonsMessage, m)
761
951
  }
762
952
 
763
- m.pollResultSnapshotMessage = pollResultSnapshotMessage
764
- }
765
-
766
- else if ('poll' in message) {
767
- if (!Array.isArray(message.poll.values)) {
768
- throw new Boom('Invalid poll values', { statusCode: 400 })
953
+ if (hasNonNullishProperty(message, "title")) {
954
+ buttonsMessage.text = message.title
955
+ buttonsMessage.headerType =
956
+ proto.Message.ButtonsMessage.HeaderType.TEXT
769
957
  }
770
958
 
771
- if (message.poll.selectableCount < 0
772
- || message.poll.selectableCount > message.poll.values.length) {
773
- throw new Boom(`poll.selectableCount in poll should be >= 0 and <= ${message.poll.values.length}`, { statusCode: 400 })
959
+ if (hasNonNullishProperty(message, "footer")) {
960
+ buttonsMessage.footerText = message.footer
774
961
  }
775
962
 
776
- const pollCreationMessage = {
777
- name: message.poll.name,
778
- selectableOptionsCount: message.poll?.selectableCount || 0,
779
- options: message.poll.values.map(optionName => ({ optionName })),
780
- }
781
-
782
- pollCreationMessage.contextInfo = {
783
- ...(message.contextInfo || {}),
784
- ...(message.mentions ? { mentionedJid: message.mentions } : {})
785
- }
963
+ m = { buttonsMessage }
964
+ } else if (hasNonNullishProperty(message, "templateButtons")) {
965
+ const hydratedTemplate = {
966
+ hydratedButtons: message.templateButtons,
967
+ }
786
968
 
787
- if(message.poll?.toAnnouncementGroup) {
788
- m.pollCreationMessageV2 = pollCreationMessage
789
- } else {
790
- if(message.poll.selectableCount > 0) {
791
- m.pollCreationMessageV3 = pollCreationMessage
792
- } else {
793
- m.pollCreationMessage = pollCreationMessage
794
- }
795
- }
796
- }
797
-
798
- else if ('payment' in message) {
799
- const requestPaymentMessage = {
800
- amount: {
801
- currencyCode: message.payment?.currency || 'IDR',
802
- offset: message.payment?.offset || 0,
803
- value: message.payment?.amount || 999999999
804
- },
805
- expiryTimestamp: message.payment?.expiry || 0,
806
- amount1000: message.payment?.amount || 999999999 * 1000,
807
- currencyCodeIso4217: message.payment?.currency || 'IDR',
808
- requestFrom: message.payment?.from || '0@s.whatsapp.net',
809
- noteMessage: {
810
- extendedTextMessage: {
811
- text: message.payment?.note || 'Notes'
812
- }
813
- },
814
- background: {
815
- placeholderArgb: message.payment?.image?.placeholderArgb || 4278190080,
816
- textArgb: message.payment?.image?.textArgb || 4294967295,
817
- subtextArgb: message.payment?.image?.subtextArgb || 4294967295,
818
- type: 1
819
- }
820
- }
969
+ if (hasNonNullishProperty(message, "text")) {
970
+ hydratedTemplate.hydratedContentText = message.text
971
+ } else {
972
+ if (hasNonNullishProperty(message, "caption")) {
973
+ hydratedTemplate.hydratedContentText = message.caption
974
+ }
821
975
 
822
- requestPaymentMessage.noteMessage.extendedTextMessage.contextInfo = {
823
- ...(message.contextInfo || {}),
824
- ...(message.mentions ? { mentionedJid: message.mentions } : {})
976
+ Object.assign(msg, m)
825
977
  }
826
978
 
827
- m.requestPaymentMessage = requestPaymentMessage
828
- }
829
-
830
- else if ('stickerPack' in message) {
831
- const { stickers, cover, name, publisher, packId, description } = message.stickerPack
832
-
833
- const { zip } = require('fflate')
834
-
835
- const stickerData = {}
836
- const stickerPromises = stickers.map(async (s, i) => {
837
- const { stream } = await getStream(s.sticker)
838
- const buffer = await toBuffer(stream)
839
- const hash = sha256(buffer).toString('base64url')
840
- const fileName = `${i.toString().padStart(2, '0')}_${hash}.webp`
841
- stickerData[fileName] = [new Uint8Array(buffer), { level: 0 }]
842
-
843
- return {
844
- fileName,
845
- mimetype: 'image/webp',
846
- isAnimated: s.isAnimated || false,
847
- isLottie: s.isLottie || false,
848
- emojis: s.emojis || [],
849
- accessibilityLabel: s.accessibilityLabel || ''
850
- }
851
- })
852
-
853
- const stickerMetadata = await Promise.all(stickerPromises)
854
-
855
- const zipBuffer = await new Promise((resolve, reject) => {
856
- zip(stickerData, (err, data) => {
857
- if (err) {
858
- reject(err)
859
- } else {
860
- resolve(Buffer.from(data))
861
- }
862
- })
863
- })
864
-
865
- const coverBuffer = await toBuffer((await getStream(cover)).stream)
866
-
867
- const [stickerPackUpload, coverUpload] = await Promise.all([
868
- encryptedStream(zipBuffer, 'sticker-pack', { logger: options.logger, opts: options.options }),
869
- prepareWAMessageMedia({ image: coverBuffer }, { ...options, mediaTypeOverride: 'image' })
870
- ])
871
-
872
- const stickerPackUploadResult = await options.upload(stickerPackUpload.encFilePath, {
873
- fileEncSha256B64: stickerPackUpload.fileEncSha256.toString('base64'),
874
- mediaType: 'sticker-pack',
875
- timeoutMs: options.mediaUploadTimeoutMs
876
- })
877
-
878
-
879
- const coverImage = coverUpload.imageMessage
880
- const imageDataHash = sha256(coverBuffer).toString('base64')
881
- const stickerPackId = packId || generateMessageID()
882
-
883
- m.stickerPackMessage = {
884
- name,
885
- publisher,
886
- stickerPackId,
887
- packDescription: description,
888
- stickerPackOrigin: proto.Message.StickerPackMessage.StickerPackOrigin.THIRD_PARTY,
889
- stickerPackSize: stickerPackUpload.fileLength,
890
- stickers: stickerMetadata,
891
- fileSha256: stickerPackUpload.fileSha256,
892
- fileEncSha256: stickerPackUpload.fileEncSha256,
893
- mediaKey: stickerPackUpload.mediaKey,
894
- directPath: stickerPackUploadResult.directPath,
895
- fileLength: stickerPackUpload.fileLength,
896
- mediaKeyTimestamp: unixTimestampSeconds(),
897
- trayIconFileName: `${stickerPackId}.png`,
898
- imageDataHash,
899
- thumbnailDirectPath: coverImage.directPath,
900
- thumbnailFileSha256: coverImage.fileSha256,
901
- thumbnailFileEncSha256: coverImage.fileEncSha256,
902
- thumbnailHeight: coverImage.height,
903
- thumbnailWidth: coverImage.width
904
- }
905
-
906
- m.stickerPackMessage.contextInfo = {
907
- ...(message.contextInfo || {}),
908
- ...(message.mentions ? { mentionedJid: message.mentions } : {})
909
- }
910
- }
911
-
912
- else if ('sharePhoneNumber' in message) {
913
- m.protocolMessage = {
914
- type: WAProto.Message.ProtocolMessage.Type.SHARE_PHONE_NUMBER
979
+ if (hasNonNullishProperty(message, "footer")) {
980
+ hydratedTemplate.hydratedFooterText = message.footer
915
981
  }
916
- }
917
982
 
918
- else if ('requestPhoneNumber' in message) {
919
- m.requestPhoneNumberMessage = {}
920
- }
921
-
922
- else {
923
- const mess = await prepareWAMessageMedia(message, options)
924
- const [type] = Object.keys(mess)
983
+ m = { templateMessage: { hydratedTemplate } }
984
+ } else if (hasNonNullishProperty(message, "interactiveButtons")) {
985
+ const interactiveMessage = {
986
+ nativeFlowMessage: {
987
+ buttons: message.interactiveButtons,
988
+ },
989
+ }
925
990
 
926
- mess[type].contextInfo = {
927
- ...(message.contextInfo || {}),
928
- ...(message.mentions ? { mentionedJid: message.mentions } : {})
991
+ if (hasNonNullishProperty(message, "text")) {
992
+ interactiveMessage.body = {
993
+ text: message.text,
994
+ }
929
995
  }
930
996
 
931
- m = mess
932
- }
997
+ if (hasNonNullishProperty(message, "title")) {
998
+ interactiveMessage.header = {
999
+ title: message.title,
1000
+ subtitle: null,
1001
+ hasMediaAttachment: false,
1002
+ }
933
1003
 
934
- if ('sections' in message && !!message.sections) {
935
- const listMessage = {
936
- title: message.title,
937
- buttonText: message.buttonText,
938
- footerText: message.footer,
939
- description: message.text,
940
- sections: message.sections,
941
- listType: proto.Message.ListMessage.ListType.SINGLE_SELECT
942
- }
1004
+ if (hasNonNullishProperty(message, "subtitle")) {
1005
+ interactiveMessage.header.subtitle = message.subtitle
1006
+ }
1007
+ } else {
1008
+ if (hasNonNullishProperty(message, "caption")) {
1009
+ interactiveMessage.body = {
1010
+ text: message.caption,
1011
+ }
943
1012
 
944
- listMessage.contextInfo = {
945
- ...(message.contextInfo || {}),
946
- ...(message.mentions ? { mentionedJid: message.mentions } : {})
947
- }
1013
+ interactiveMessage.header = {
1014
+ title: null,
1015
+ subtitle: null,
1016
+ hasMediaAttachment: false,
1017
+ ...Object.assign(interactiveMessage, m),
1018
+ }
948
1019
 
949
- m = { listMessage }
950
- }
1020
+ if (hasNonNullishProperty(message, "title")) {
1021
+ interactiveMessage.header.title = message.title
1022
+ }
951
1023
 
952
- else if ('productList' in message && !!message.productList) {
953
- const thumbnail = message.thumbnail ? await generateThumbnail(message.thumbnail, 'image') : null
1024
+ if (hasNonNullishProperty(message, "subtitle")) {
1025
+ interactiveMessage.header.subtitle = message.subtitle
1026
+ }
954
1027
 
955
- const listMessage = {
956
- title: message.title,
957
- buttonText: message.buttonText,
958
- footerText: message.footer,
959
- description: message.text,
960
- productListInfo: {
961
- productSections: message.productList,
962
- headerImage: {
963
- productId: message.productList[0].products[0].productId,
964
- jpegThumbnail: thumbnail?.thumbnail || null
965
- },
966
- businessOwnerJid: message.businessOwnerJid
967
- },
968
- listType: proto.Message.ListMessage.ListType.PRODUCT_LIST
1028
+ if (hasNonNullishProperty(message, "hasMediaAttachment")) {
1029
+ interactiveMessage.header.hasMediaAttachment = Boolean(
1030
+ message.hasMediaAttachment
1031
+ )
1032
+ }
1033
+ }
969
1034
  }
970
1035
 
971
- listMessage.contextInfo = {
972
- ...(message.contextInfo || {}),
973
- ...(message.mentions ? { mentionedJid: message.mentions } : {})
1036
+ if (hasNonNullishProperty(message, "footer")) {
1037
+ interactiveMessage.footer = {
1038
+ text: message.footer,
1039
+ }
974
1040
  }
975
1041
 
976
- m = { listMessage }
977
- }
978
-
979
- else if ('buttons' in message && !!message.buttons) {
980
- const buttonsMessage = {
981
- buttons: message.buttons.map(b => ({ ...b, type: proto.Message.ButtonsMessage.Button.Type.RESPONSE }))
1042
+ m = { interactiveMessage }
1043
+ } else if (hasNonNullishProperty(message, "shop")) {
1044
+ const interactiveMessage = {
1045
+ shopStorefrontMessage: {
1046
+ surface: message.shop.surface,
1047
+ id: message.shop.id,
1048
+ },
982
1049
  }
983
1050
 
984
- if ('text' in message) {
985
- buttonsMessage.contentText = message.text
986
- buttonsMessage.headerType = proto.Message.ButtonsMessage.HeaderType.EMPTY
1051
+ if (hasNonNullishProperty(message, "text")) {
1052
+ interactiveMessage.body = {
1053
+ text: message.text,
1054
+ }
987
1055
  }
988
1056
 
989
- else {
990
- if ('caption' in message) {
991
- buttonsMessage.contentText = message.caption
1057
+ if (hasNonNullishProperty(message, "title")) {
1058
+ interactiveMessage.header = {
1059
+ title: message.title,
1060
+ subtitle: null,
1061
+ hasMediaAttachment: false,
992
1062
  }
993
1063
 
994
- const type = Object.keys(m)[0].replace('Message', '').toUpperCase()
1064
+ if (hasNonNullishProperty(message, "subtitle")) {
1065
+ interactiveMessage.header.subtitle = message.subtitle
1066
+ }
1067
+ } else {
1068
+ if (hasNonNullishProperty(message, "caption")) {
1069
+ interactiveMessage.body = {
1070
+ text: message.caption,
1071
+ }
995
1072
 
996
- buttonsMessage.headerType = proto.Message.ButtonsMessage.HeaderType[type]
1073
+ interactiveMessage.header = {
1074
+ title: null,
1075
+ subtitle: null,
1076
+ hasMediaAttachment: false,
1077
+ ...Object.assign(interactiveMessage, m),
1078
+ }
997
1079
 
998
- Object.assign(buttonsMessage, m)
999
- }
1080
+ if (hasNonNullishProperty(message, "title")) {
1081
+ interactiveMessage.header.title = message.title
1082
+ }
1000
1083
 
1001
- if ('footer' in message && !!message.footer) {
1002
- buttonsMessage.footerText = message.footer
1003
- }
1084
+ if (hasNonNullishProperty(message, "subtitle")) {
1085
+ interactiveMessage.header.subtitle = message.subtitle
1086
+ }
1004
1087
 
1005
- if ('title' in message && !!message.title) {
1006
- buttonsMessage.text = message.title
1007
- buttonsMessage.headerType = proto.Message.ButtonsMessage.HeaderType.TEXT
1088
+ if (hasNonNullishProperty(message, "hasMediaAttachment")) {
1089
+ interactiveMessage.header.hasMediaAttachment = Boolean(
1090
+ message.hasMediaAttachment
1091
+ )
1092
+ }
1093
+ }
1008
1094
  }
1009
1095
 
1010
- buttonsMessage.contextInfo = {
1011
- ...(message.contextInfo || {}),
1012
- ...(message.mentions ? { mentionedJid: message.mentions } : {})
1096
+ if (hasNonNullishProperty(message, "footer")) {
1097
+ interactiveMessage.footer = {
1098
+ text: message.footer,
1099
+ }
1013
1100
  }
1014
1101
 
1015
- m = { buttonsMessage }
1016
- }
1017
-
1018
- else if ('templateButtons' in message && !!message.templateButtons) {
1019
- const hydratedTemplate = {
1020
- hydratedButtons: message.templateButtons
1102
+ m = { interactiveMessage }
1103
+ } else if (hasNonNullishProperty(message, "collection")) {
1104
+ const interactiveMessage = {
1105
+ collectionMessage: {
1106
+ bizJid: message.collection.bizJid,
1107
+ id: message.collection.id,
1108
+ messageVersion: message?.collection?.version,
1109
+ },
1021
1110
  }
1022
1111
 
1023
- if ('text' in message) {
1024
- hydratedTemplate.hydratedContentText = message.text
1112
+ if (hasNonNullishProperty(message, "text")) {
1113
+ interactiveMessage.body = {
1114
+ text: message.text,
1115
+ }
1025
1116
  }
1026
1117
 
1027
- else {
1028
- if ('caption' in message) {
1029
- hydratedTemplate.hydratedContentText = message.caption
1118
+ if (hasNonNullishProperty(message, "title")) {
1119
+ interactiveMessage.header = {
1120
+ title: message.title,
1121
+ subtitle: null,
1122
+ hasMediaAttachment: false,
1030
1123
  }
1031
1124
 
1032
- Object.assign(msg, m)
1033
- }
1125
+ if (hasNonNullishProperty(message, "subtitle")) {
1126
+ interactiveMessage.header.subtitle = message.subtitle
1127
+ }
1128
+ } else {
1129
+ if (hasNonNullishProperty(message, "caption")) {
1130
+ interactiveMessage.body = {
1131
+ text: message.caption,
1132
+ }
1034
1133
 
1035
- if ('footer' in message && !!message.footer) {
1036
- hydratedTemplate.hydratedFooterText = message.footer
1037
- }
1134
+ interactiveMessage.header = {
1135
+ title: null,
1136
+ subtitle: null,
1137
+ hasMediaAttachment: false,
1138
+ ...Object.assign(interactiveMessage, m),
1139
+ }
1038
1140
 
1039
- hydratedTemplate.contextInfo = {
1040
- ...(message.contextInfo || {}),
1041
- ...(message.mentions ? { mentionedJid: message.mentions } : {})
1042
- }
1141
+ if (hasNonNullishProperty(message, "title")) {
1142
+ interactiveMessage.header.title = message.title
1143
+ }
1043
1144
 
1044
- m = { templateMessage: { hydratedTemplate }}
1045
- }
1145
+ if (hasNonNullishProperty(message, "subtitle")) {
1146
+ interactiveMessage.header.subtitle = message.subtitle
1147
+ }
1046
1148
 
1047
- else if ('interactiveButtons' in message && !!message.interactiveButtons) {
1048
- const interactiveMessage = {
1049
- nativeFlowMessage: {
1050
- buttons: message.interactiveButtons
1051
- }
1052
- }
1053
-
1054
- if ('text' in message) {
1055
- interactiveMessage.body = {
1056
- text: message.text
1057
- },
1058
- interactiveMessage.header = {
1059
- title: message.title,
1060
- subtitle: message.subtitle,
1061
- hasMediaAttachment: false
1062
- }
1063
- }
1064
-
1065
- else {
1066
- if ('caption' in message) {
1067
- interactiveMessage.body = {
1068
- text: message.caption
1069
- }
1070
-
1071
- interactiveMessage.header = {
1072
- title: message.title,
1073
- subtitle: message.subtitle,
1074
- hasMediaAttachment: message.hasMediaAttachment ? message.hasMediaAttachment : false,
1075
- ...Object.assign(interactiveMessage, m)
1076
- }
1077
- }
1078
- }
1079
-
1080
- if ('footer' in message && !!message.footer) {
1081
- interactiveMessage.footer = {
1082
- text: message.footer
1083
- }
1084
- }
1085
-
1086
- interactiveMessage.contextInfo = {
1087
- ...(message.contextInfo || {}),
1088
- ...(message.mentions ? { mentionedJid: message.mentions } : {})
1089
- }
1090
-
1091
- m = { interactiveMessage }
1092
- }
1093
-
1094
- else if ('shop' in message && !!message.shop) {
1095
- const interactiveMessage = {
1096
- shopStorefrontMessage: {
1097
- surface: message.shop.surface,
1098
- id: message.shop.id
1099
- }
1100
- }
1101
-
1102
- if ('text' in message) {
1103
- interactiveMessage.body = {
1104
- text: message.text
1105
- },
1106
- interactiveMessage.header = {
1107
- title: message.title,
1108
- subtitle: message.subtitle,
1109
- hasMediaAttachment: false
1110
- }
1111
- }
1112
-
1113
- else {
1114
- if ('caption' in message) {
1115
- interactiveMessage.body = {
1116
- text: message.caption
1117
- }
1118
-
1119
- interactiveMessage.header = {
1120
- title: message.title,
1121
- subtitle: message.subtitle,
1122
- hasMediaAttachment: message.hasMediaAttachment ? message.hasMediaAttachment : false,
1123
- ...Object.assign(interactiveMessage, m)
1124
- }
1125
- }
1126
- }
1127
-
1128
- if ('footer' in message && !!message.footer) {
1129
- interactiveMessage.footer = {
1130
- text: message.footer
1131
- }
1132
- }
1133
-
1134
- interactiveMessage.contextInfo = {
1135
- ...(message.contextInfo || {}),
1136
- ...(message.mentions ? { mentionedJid: message.mentions } : {})
1137
- }
1138
-
1139
- m = { interactiveMessage }
1140
- }
1141
-
1142
- else if ('collection' in message && !!message.collection) {
1143
- const interactiveMessage = {
1144
- collectionMessage: {
1145
- bizJid: message.collection.bizJid,
1146
- id: message.collection.id,
1147
- messageVersion: message?.collection?.version
1148
- }
1149
- }
1150
-
1151
- if ('text' in message) {
1152
- interactiveMessage.body = {
1153
- text: message.text
1154
- },
1155
- interactiveMessage.header = {
1156
- title: message.title,
1157
- subtitle: message.subtitle,
1158
- hasMediaAttachment: false
1159
- }
1160
- }
1161
-
1162
- else {
1163
- if ('caption' in message) {
1164
- interactiveMessage.body = {
1165
- text: message.caption
1166
- }
1167
- interactiveMessage.header = {
1168
- title: message.title,
1169
- subtitle: message.subtitle,
1170
- hasMediaAttachment: message.hasMediaAttachment ? message.hasMediaAttachment : false,
1171
- ...Object.assign(interactiveMessage, m)
1149
+ if (hasNonNullishProperty(message, "hasMediaAttachment")) {
1150
+ interactiveMessage.header.hasMediaAttachment = Boolean(
1151
+ message.hasMediaAttachment
1152
+ )
1153
+ }
1172
1154
  }
1173
- }
1174
- }
1155
+ }
1175
1156
 
1176
- if ('footer' in message && !message.footer) {
1177
- interactiveMessage.footer = {
1178
- text: message.footer
1179
- }
1180
- }
1157
+ if (hasNonNullishProperty(message, "footer")) {
1158
+ interactiveMessage.footer = {
1159
+ text: message.footer,
1160
+ }
1161
+ }
1181
1162
 
1182
- interactiveMessage.contextInfo = {
1183
- ...(message.contextInfo || {}),
1184
- ...(message.mentions ? { mentionedJid: message.mentions } : {})
1185
- }
1163
+ m = { interactiveMessage }
1164
+ } else if (hasNonNullishProperty(message, "cards")) {
1165
+ const slides = await Promise.all(
1166
+ message.cards.map(async (slide) => {
1167
+ const {
1168
+ image,
1169
+ video,
1170
+ product,
1171
+ title,
1172
+ body,
1173
+ footer,
1174
+ buttons,
1175
+ } = slide
1186
1176
 
1187
- m = { interactiveMessage }
1188
- }
1177
+ let header
1189
1178
 
1190
- else if ('cards' in message && !!message.cards) {
1191
- const slides = await Promise.all(message.cards.map(async (slide) => {
1192
- const { image, video, product, title, body, footer, buttons } = slide
1193
- let header
1194
-
1195
- if (product) {
1196
- const { imageMessage } = await prepareWAMessageMedia({ image: product.productImage, ...options }, options)
1197
- header = {
1198
- productMessage: {
1199
- product: {
1200
- ...product,
1201
- productImage: imageMessage,
1179
+ if (product) {
1180
+ const { imageMessage } = await prepareWAMessageMedia(
1181
+ { image: product.productImage, ...options },
1182
+ options
1183
+ )
1184
+ header = {
1185
+ productMessage: {
1186
+ product: {
1187
+ ...product,
1188
+ productImage: imageMessage,
1189
+ },
1190
+ ...slide,
1202
1191
  },
1203
- ...slide
1204
1192
  }
1193
+ } else if (image) {
1194
+ header = await prepareWAMessageMedia(
1195
+ { image: image, ...options },
1196
+ options
1197
+ )
1198
+ } else if (video) {
1199
+ header = await prepareWAMessageMedia(
1200
+ { video: video, ...options },
1201
+ options
1202
+ )
1205
1203
  }
1206
- }
1207
-
1208
- else if (image) {
1209
- header = await prepareWAMessageMedia({ image: image, ...options }, options)
1210
- }
1211
-
1212
- else if (video) {
1213
- header = await prepareWAMessageMedia({ video: video, ...options }, options)
1214
- }
1215
1204
 
1216
- const msg = {
1217
- header: {
1218
- title,
1219
- hasMediaAttachment: true,
1220
- ...header
1221
- },
1222
- body: {
1223
- text: body
1224
- },
1225
- footer: {
1226
- text: footer
1227
- },
1228
- nativeFlowMessage: {
1229
- buttons,
1205
+ const msg = {
1206
+ header: {
1207
+ title,
1208
+ hasMediaAttachment: true,
1209
+ ...header,
1210
+ },
1211
+ body: {
1212
+ text: body,
1213
+ },
1214
+ footer: {
1215
+ text: footer,
1216
+ },
1217
+ nativeFlowMessage: {
1218
+ buttons,
1219
+ },
1230
1220
  }
1231
- }
1232
1221
 
1233
- return msg
1234
- }))
1222
+ return msg
1223
+ })
1224
+ )
1235
1225
 
1236
1226
  const interactiveMessage = {
1237
1227
  carouselMessage: {
1238
- cards: slides
1228
+ cards: slides,
1229
+ },
1230
+ }
1231
+
1232
+ if (hasNonNullishProperty(message, "text")) {
1233
+ interactiveMessage.body = {
1234
+ text: message.text,
1239
1235
  }
1240
1236
  }
1241
1237
 
1242
- if ('text' in message) {
1243
- interactiveMessage.body = {
1244
- text: message.text
1245
- },
1238
+ if (hasNonNullishProperty(message, "title")) {
1246
1239
  interactiveMessage.header = {
1247
- title: message.title,
1248
- subtitle: message.subtitle,
1249
- hasMediaAttachment: false
1240
+ title: message.title,
1241
+ subtitle: null,
1242
+ hasMediaAttachment: false,
1250
1243
  }
1251
- }
1252
1244
 
1253
- if ('footer' in message && !!message.footer) {
1254
- interactiveMessage.footer = {
1255
- text: message.footer
1245
+ if (hasNonNullishProperty(message, "subtitle")) {
1246
+ interactiveMessage.header.subtitle = message.subtitle
1247
+ }
1248
+ } else {
1249
+ if (hasNonNullishProperty(message, "caption")) {
1250
+ interactiveMessage.body = {
1251
+ text: message.caption,
1252
+ }
1253
+
1254
+ interactiveMessage.header = {
1255
+ title: null,
1256
+ subtitle: null,
1257
+ hasMediaAttachment: false,
1258
+ ...Object.assign(interactiveMessage, m),
1259
+ }
1260
+
1261
+ if (hasNonNullishProperty(message, "title")) {
1262
+ interactiveMessage.header.title = message.title
1263
+ }
1264
+
1265
+ if (hasNonNullishProperty(message, "subtitle")) {
1266
+ interactiveMessage.header.subtitle = message.subtitle
1267
+ }
1268
+
1269
+ if (hasNonNullishProperty(message, "hasMediaAttachment")) {
1270
+ interactiveMessage.header.hasMediaAttachment = Boolean(
1271
+ message.hasMediaAttachment
1272
+ )
1273
+ }
1256
1274
  }
1257
1275
  }
1258
1276
 
1259
- interactiveMessage.contextInfo = {
1260
- ...(message.contextInfo || {}),
1261
- ...(message.mentions ? { mentionedJid: message.mentions } : {})
1277
+ if (hasNonNullishProperty(message, "footer")) {
1278
+ interactiveMessage.footer = {
1279
+ text: message.footer,
1280
+ }
1262
1281
  }
1263
1282
 
1264
1283
  m = { interactiveMessage }
1265
1284
  }
1266
1285
 
1267
- if ('ephemeral' in message && !!message.ephemeral) {
1268
- m = { ephemeralMessage: { message: m } }
1269
- }
1286
+ if (hasOptionalProperty(message, "ephemeral")) {
1287
+ m = { ephemeralMessage: { message: m } }
1288
+ }
1289
+
1290
+ if (hasOptionalProperty(message, "mentions") && message.mentions?.length) {
1291
+ const messageType = Object.keys(m)[0]
1292
+ const key = m[messageType]
1270
1293
 
1271
- if ('viewOnce' in message && !!message.viewOnce) {
1272
- m = { viewOnceMessageV2: { message: m } }
1294
+ if ("contextInfo" in key && !!key.contextInfo) {
1295
+ key.contextInfo.mentionedJid = message.mentions
1296
+ } else if (key) {
1297
+ key.contextInfo = {
1298
+ mentionedJid: message.mentions,
1299
+ }
1300
+ }
1273
1301
  }
1274
1302
 
1275
- if ('viewOnceExt' in message && !!message.viewOnceExt) {
1276
- m = { viewOnceMessageV2Extension: { message: m } }
1303
+ if (hasOptionalProperty(message, "contextInfo") && !!message.contextInfo) {
1304
+ const messageType = Object.keys(m)[0]
1305
+ const key = m[messageType]
1306
+
1307
+ if ("contextInfo" in key && !!key.contextInfo) {
1308
+ key.contextInfo = { ...key.contextInfo, ...message.contextInfo }
1309
+ } else if (key) {
1310
+ key.contextInfo = message.contextInfo
1311
+ }
1277
1312
  }
1278
1313
 
1279
- if ('edit' in message) {
1314
+ if (hasOptionalProperty(message, "edit")) {
1280
1315
  m = {
1281
1316
  protocolMessage: {
1282
1317
  key: message.edit,
1283
1318
  editedMessage: m,
1284
1319
  timestampMs: Date.now(),
1285
- type: WAProto.Message.ProtocolMessage.Type.MESSAGE_EDIT
1286
- }
1320
+ type: WAProto.Message.ProtocolMessage.Type.MESSAGE_EDIT,
1321
+ },
1287
1322
  }
1288
1323
  }
1289
1324
 
@@ -1401,7 +1436,7 @@ const generateWAMessage = async (jid, content, options) => {
1401
1436
  const getContentType = (content) => {
1402
1437
  if (content) {
1403
1438
  const keys = Object.keys(content)
1404
- const key = keys.find(k => (k === 'conversation' || k.endsWith('Message') || k.endsWith('V2') || k.endsWith('V3') || k.endsWith('V4') || k.endsWith('V5')) && k !== 'senderKeyDistributionMessage' && k !== 'messageContextInfo')
1439
+ const key = keys.find(k => (k === 'conversation' || k.endsWith('Message') || k.endsWith('V2') || k.endsWith('V3') || k.endsWith('V4')) && k !== 'senderKeyDistributionMessage' && k !== 'messageContextInfo')
1405
1440
 
1406
1441
  return key
1407
1442
  }
@@ -1448,7 +1483,6 @@ const normalizeMessageContent = (content) => {
1448
1483
  || (message?.statusMentionMessage)
1449
1484
  || (message?.groupStatusMessageV2)
1450
1485
  || (message?.pollCreationMessageV4)
1451
- || (message?.pollCreationMessageV5)
1452
1486
  || (message?.associatedChildMessage)
1453
1487
  || (message?.groupMentionedMessage)
1454
1488
  || (message?.groupStatusMentionMessage)
@@ -1575,8 +1609,8 @@ const updateMessageWithPollUpdate = (msg, update) => {
1575
1609
  const updateMessageWithEventResponse = (msg, update) => {
1576
1610
  const authorID = getKeyAuthor(update.eventResponseMessageKey)
1577
1611
  const responses = (msg.eventResponses || [])
1578
- .filter(r => getKeyAuthor(r.eventResponseMessageKey) !== authorID)
1579
-
1612
+ .filter(r => getKeyAuthor(r.eventResponseMessageKey) !== authorID)
1613
+
1580
1614
  responses.push(update)
1581
1615
  msg.eventResponses = responses
1582
1616
  }
@@ -1640,18 +1674,18 @@ function getAggregateResponsesInEventMessage({ eventResponses }, meLid) {
1640
1674
  const responseMap = {}
1641
1675
 
1642
1676
  for (const type of responseTypes) {
1643
- responseMap[type] = {
1644
- response: type,
1645
- responders: []
1646
- }
1677
+ responseMap[type] = {
1678
+ response: type,
1679
+ responders: []
1680
+ }
1647
1681
  }
1648
-
1682
+
1649
1683
  for (const update of eventResponses) {
1650
- const { response } = update.response || 0
1651
- const responseType = proto.Message.EventResponseMessage.EventResponseType[response]
1652
- if (responseType !== 'UNKNOWN' && responseMap[responseType]) {
1653
- responseMap[responseType].responders.push(getKeyAuthor(update.eventResponseMessageKey, meLid))
1654
- }
1684
+ const { response } = update.response || 0
1685
+ const responseType = proto.Message.EventResponseMessage.EventResponseType[response]
1686
+ if (responseType !== 'UNKNOWN' && responseMap[responseType]) {
1687
+ responseMap[responseType].responders.push(getKeyAuthor(update.eventResponseMessageKey, meLid))
1688
+ }
1655
1689
  }
1656
1690
 
1657
1691
  return Object.values(responseMap)
@@ -1687,15 +1721,15 @@ const REUPLOAD_REQUIRED_STATUS = [410, 404]
1687
1721
  */
1688
1722
  const downloadMediaMessage = async (message, type, options, ctx) => {
1689
1723
  const result = await downloadMsg().catch(async (error) => {
1690
- if (ctx && axios_1.default.isAxiosError(error) && // check if the message requires a reupload
1691
- REUPLOAD_REQUIRED_STATUS.includes(error.response?.status)) {
1724
+ if (ctx &&
1725
+ typeof error?.status === 'number' && // treat errors with status as HTTP failures requiring reupload
1726
+ REUPLOAD_REQUIRED_STATUS.includes(error.status)) {
1692
1727
  ctx.logger.info({ key: message.key }, 'sending reupload media request...')
1693
1728
 
1694
1729
  // request reupload
1695
1730
  message = await ctx.reuploadRequest(message)
1696
1731
 
1697
1732
  const result = await downloadMsg()
1698
-
1699
1733
  return result
1700
1734
  }
1701
1735
 
@@ -1712,7 +1746,9 @@ const downloadMediaMessage = async (message, type, options, ctx) => {
1712
1746
  }
1713
1747
 
1714
1748
  const contentType = getContentType(mContent)
1749
+
1715
1750
  let mediaType = contentType?.replace('Message', '')
1751
+
1716
1752
  const media = contentType === 'productMessage' ? mContent[contentType]?.product?.productImage : mContent[contentType]
1717
1753
 
1718
1754
  if (!media || typeof media !== 'object' || (!('url' in media) && !('thumbnailDirectPath' in media))) {
@@ -1728,7 +1764,6 @@ const downloadMediaMessage = async (message, type, options, ctx) => {
1728
1764
  }
1729
1765
  mediaType = 'thumbnail-link'
1730
1766
  }
1731
-
1732
1767
  else {
1733
1768
  download = media
1734
1769
  }
@@ -1737,57 +1772,30 @@ const downloadMediaMessage = async (message, type, options, ctx) => {
1737
1772
 
1738
1773
  if (type === 'buffer') {
1739
1774
  const bufferArray = []
1740
-
1741
1775
  for await (const chunk of stream) {
1742
- bufferArray.push(chunk)
1776
+ bufferArray.push(chunk);
1743
1777
  }
1744
-
1745
- return Buffer.concat(bufferArray)
1778
+ return Buffer.concat(bufferArray);
1746
1779
  }
1747
-
1748
1780
  return stream
1749
1781
  }
1750
1782
  }
1751
1783
 
1752
- /** Checks whether the given message is a media message if it is returns the inner content */
1784
+ /** Checks whether the given message is a media message; if it is returns the inner content */
1753
1785
  const assertMediaContent = (content) => {
1754
1786
  content = extractMessageContent(content)
1755
1787
 
1756
- const mediaContent = content?.documentMessage
1757
- || content?.imageMessage
1758
- || content?.videoMessage
1759
- || content?.audioMessage
1760
- || content?.stickerMessage
1788
+ const mediaContent = content?.documentMessage ||
1789
+ content?.imageMessage ||
1790
+ content?.videoMessage ||
1791
+ content?.audioMessage ||
1792
+ content?.stickerMessage
1761
1793
 
1762
1794
  if (!mediaContent) {
1763
- throw new Boom('given message is not a media message', { statusCode: 400, data: content })
1795
+ throw new Boom('given message is not a media message', { statusCode: 400, data: content });
1764
1796
  }
1765
- return mediaContent
1766
- }
1767
1797
 
1768
- /**
1769
- * this is an experimental patch to make buttons work
1770
- * Don't know how it works, but it does for now
1771
- */
1772
- const patchMessageForMdIfRequired = (message) => {
1773
- if (message?.buttonsMessage ||
1774
- message?.templateMessage ||
1775
- message?.listMessage ||
1776
- message?.interactiveMessage?.nativeFlowMesaage
1777
- ) {
1778
- message = {
1779
- viewOnceMessageV2Extension: {
1780
- message: {
1781
- messageContextInfo: {
1782
- deviceListMetadataVersion: 2,
1783
- deviceListMetadata: {}
1784
- },
1785
- ...message
1786
- }
1787
- }
1788
- }
1789
- }
1790
- return message
1798
+ return mediaContent
1791
1799
  }
1792
1800
 
1793
1801
  module.exports = {
@@ -1801,6 +1809,7 @@ module.exports = {
1801
1809
  generateWAMessageFromContent,
1802
1810
  generateWAMessage,
1803
1811
  getContentType,
1812
+ hasNonNullishProperty,
1804
1813
  normalizeMessageContent,
1805
1814
  extractMessageContent,
1806
1815
  getDevice,
@@ -1812,6 +1821,5 @@ module.exports = {
1812
1821
  getAggregateResponsesInEventMessage,
1813
1822
  aggregateMessageKeysNotFromMe,
1814
1823
  downloadMediaMessage,
1815
- assertMediaContent,
1816
- patchMessageForMdIfRequired
1824
+ assertMediaContent
1817
1825
  }