@badzz88/baileys 8.5.4 → 8.5.5

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 (107) hide show
  1. package/README.md +1 -1
  2. package/WAProto/WAProto.proto +75 -1256
  3. package/WAProto/index.d.ts +19729 -36122
  4. package/WAProto/index.js +132193 -178538
  5. package/engine-requirements.js +15 -7
  6. package/lib/Defaults/index.js +14 -22
  7. package/lib/Signal/Group/group_cipher.js +5 -5
  8. package/lib/Signal/Group/keyhelper.js +4 -4
  9. package/lib/Signal/Group/sender-chain-key.js +2 -2
  10. package/lib/Signal/Group/sender-key-distribution-message.js +1 -1
  11. package/lib/Signal/Group/sender-key-message.js +3 -3
  12. package/lib/Signal/Group/sender-key-record.js +1 -1
  13. package/lib/Signal/Group/sender-message-key.js +5 -5
  14. package/lib/Signal/libsignal.js +28 -39
  15. package/lib/Signal/lid-mapping.js +8 -28
  16. package/lib/Socket/aigroups.js +5 -5
  17. package/lib/Socket/business.js +11 -3
  18. package/lib/Socket/chats.js +319 -668
  19. package/lib/Socket/communities.js +89 -85
  20. package/lib/Socket/graphql.js +228 -435
  21. package/lib/Socket/groups.js +211 -132
  22. package/lib/Socket/index.js +7 -9
  23. package/lib/Socket/interactive-handler.js +60 -117
  24. package/lib/Socket/interop.js +175 -56
  25. package/lib/Socket/luxu.js +387 -0
  26. package/lib/Socket/managed-account.js +48 -79
  27. package/lib/Socket/messages-recv.js +340 -898
  28. package/lib/Socket/messages-send.js +160 -462
  29. package/lib/Socket/mex.js +53 -40
  30. package/lib/Socket/newsletter.js +295 -291
  31. package/lib/Socket/privacy.js +265 -77
  32. package/lib/Socket/registration.js +157 -164
  33. package/lib/Socket/socket.js +199 -181
  34. package/lib/Socket/text-router.js +83 -0
  35. package/lib/Socket/username.js +227 -144
  36. package/lib/Store/keyed-db.js +108 -0
  37. package/lib/Store/make-cache-manager-store.js +2 -2
  38. package/lib/Store/make-in-memory-store.js +141 -25
  39. package/lib/Types/Auth.js +3 -3
  40. package/lib/Types/Label.js +1 -1
  41. package/lib/Types/LabelAssociation.js +1 -1
  42. package/lib/Types/Mex.js +112 -0
  43. package/lib/Types/Newsletter.js +65 -96
  44. package/lib/Types/State.js +4 -4
  45. package/lib/Types/index.js +1 -1
  46. package/lib/Utils/auth-utils.js +57 -52
  47. package/lib/Utils/browser-utils.js +1 -1
  48. package/lib/Utils/business.js +6 -2
  49. package/lib/Utils/chat-utils.js +38 -102
  50. package/lib/Utils/command-loader.d.ts +31 -0
  51. package/lib/Utils/command-loader.js +100 -0
  52. package/lib/Utils/companion-reg-client-utils.js +42 -0
  53. package/lib/Utils/crypto.js +84 -54
  54. package/lib/Utils/curve25519-js.js +275 -0
  55. package/lib/Utils/decode-wa-message.js +83 -316
  56. package/lib/Utils/event-buffer.js +61 -37
  57. package/lib/Utils/generics.js +103 -157
  58. package/lib/Utils/group-history.js +12 -3
  59. package/lib/Utils/history.js +15 -15
  60. package/lib/Utils/index.js +5 -1
  61. package/lib/Utils/link-preview.js +8 -3
  62. package/lib/Utils/lt-hash.js +35 -2
  63. package/lib/Utils/make-mutex.js +1 -1
  64. package/lib/Utils/message-retry-manager.js +92 -27
  65. package/lib/Utils/messages-media.js +130 -159
  66. package/lib/Utils/messages.js +566 -144
  67. package/lib/Utils/meta-ai-msmsg.js +202 -62
  68. package/lib/Utils/native-bridge.js +82 -0
  69. package/lib/Utils/noise-handler.js +21 -19
  70. package/lib/Utils/offline-node-processor.js +8 -3
  71. package/lib/Utils/pre-key-manager.js +23 -13
  72. package/lib/Utils/process-message.js +106 -197
  73. package/lib/Utils/reporting-utils.js +2 -2
  74. package/lib/Utils/session-pool.d.ts +16 -0
  75. package/lib/Utils/session-pool.js +94 -0
  76. package/lib/Utils/signal.js +63 -28
  77. package/lib/Utils/stanza-ack.js +12 -39
  78. package/lib/Utils/sticker.d.ts +17 -0
  79. package/lib/Utils/sticker.js +145 -0
  80. package/lib/Utils/sync-action-utils.js +7 -4
  81. package/lib/Utils/tc-token-utils.js +25 -20
  82. package/lib/Utils/use-multi-file-auth-state.js +14 -30
  83. package/lib/Utils/validate-connection.js +19 -59
  84. package/lib/Utils/view-once-cache.d.ts +9 -0
  85. package/lib/Utils/view-once-cache.js +79 -0
  86. package/lib/WABinary/decode.js +16 -50
  87. package/lib/WABinary/encode.js +224 -54
  88. package/lib/WABinary/generic-utils.js +97 -7
  89. package/lib/WABinary/jid-utils.js +14 -27
  90. package/lib/WAM/encode.js +7 -7
  91. package/lib/WAUSync/Protocols/USyncBusinessProtocol.js +1 -58
  92. package/lib/WAUSync/Protocols/USyncContactProtocol.js +1 -7
  93. package/lib/WAUSync/Protocols/USyncDeviceProtocol.js +5 -13
  94. package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.js +4 -1
  95. package/lib/WAUSync/Protocols/USyncFeatureProtocol.js +7 -20
  96. package/lib/WAUSync/Protocols/USyncPictureProtocol.js +1 -1
  97. package/lib/WAUSync/Protocols/USyncSidelistProtocol.js +2 -2
  98. package/lib/WAUSync/Protocols/USyncStatusProtocol.js +2 -7
  99. package/lib/WAUSync/Protocols/USyncTextStatusProtocol.js +1 -1
  100. package/lib/WAUSync/Protocols/USyncUsernameProtocol.js +1 -2
  101. package/lib/WAUSync/Protocols/index.js +0 -1
  102. package/lib/WAUSync/USyncQuery.js +6 -77
  103. package/lib/WAUSync/USyncUser.js +8 -16
  104. package/lib/WAUSync/index.js +0 -1
  105. package/lib/antiban.js +854 -608
  106. package/lib/index.js +1 -1
  107. package/package.json +20 -16
@@ -1,6 +1,17 @@
1
1
  'use strict'
2
2
  Object.defineProperty(exports, '__esModule', { value: true })
3
- exports.generateWAMessage =
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 =
4
15
  exports.generateWAMessageFromContent =
5
16
  exports.generateWAMessageContent =
6
17
  exports.hasNonNullishProperty =
@@ -10,10 +21,12 @@ exports.generateWAMessage =
10
21
  exports.generateLinkPreviewIfRequired =
11
22
  exports.extractUrlFromText =
12
23
  void 0
24
+ exports.getAggregateVotesInPollMessage = getAggregateVotesInPollMessage
25
+ exports.getAggregateResponsesInEventMessage = getAggregateResponsesInEventMessage
13
26
  const boom_1 = require('@hapi/boom')
14
27
  const crypto_1 = require('crypto')
15
28
  const fs_1 = require('fs')
16
- const lru_cache_1 = require('lru-cache')
29
+ const index_js_1 = require('../../WAProto/index.js')
17
30
  const WAProto_1 = require('../../WAProto/index.js')
18
31
  const Defaults_1 = require('../Defaults')
19
32
  const Types_1 = require('../Types')
@@ -23,12 +36,6 @@ const generics_1 = require('./generics')
23
36
  const messages_media_1 = require('./messages-media')
24
37
  const reporting_utils_1 = require('./reporting-utils')
25
38
  const jid_display_normalization_1 = require('./jid-display-normalization')
26
- const message_inspect_1 = require('./message-inspect')
27
- Object.assign(exports, message_inspect_1)
28
-
29
-
30
-
31
- const groupInviteThumbnailCache = new lru_cache_1.LRUCache({ max: 100, ttl: 5 * 60 * 1000 })
32
39
  const MIMETYPE_MAP = {
33
40
  image: 'image/jpeg',
34
41
  video: 'video/mp4',
@@ -45,14 +52,14 @@ const MessageTypeProto = {
45
52
  document: WAProto_1.proto.Message.DocumentMessage
46
53
  }
47
54
 
48
-
55
+ // Input payloads can carry protobuf media message keys (e.g. imageMessage).
49
56
  const MEDIA_MESSAGE_TYPE_ALIASES = {
50
57
  imageMessage: 'image',
51
58
  videoMessage: 'video',
52
59
  audioMessage: 'audio',
53
60
  documentMessage: 'document',
54
61
  stickerMessage: 'sticker',
55
-
62
+ // PTV = push-to-video (video note) payload.
56
63
  ptvMessage: 'video'
57
64
  }
58
65
  const MEDIA_MESSAGE_TYPE_ALIAS_KEYS = Object.keys(MEDIA_MESSAGE_TYPE_ALIASES)
@@ -189,7 +196,11 @@ const tokenizeCode = code => {
189
196
  return merged
190
197
  }
191
198
 
192
-
199
+ /**
200
+ * Uses a regex to test whether the string contains a URL, and returns the URL if it does.
201
+ * @param text eg. hello https://google.com
202
+ * @returns the URL, eg. https://google.com
203
+ */
193
204
  const extractUrlFromText = text => text.match(Defaults_1.URL_REGEX)?.[0]
194
205
  exports.extractUrlFromText = extractUrlFromText
195
206
  const generateLinkPreviewIfRequired = async (text, getUrlInfo, logger) => {
@@ -199,7 +210,7 @@ const generateLinkPreviewIfRequired = async (text, getUrlInfo, logger) => {
199
210
  const urlInfo = await getUrlInfo(url)
200
211
  return urlInfo
201
212
  } catch (error) {
202
-
213
+ // ignore if fails
203
214
  logger?.warn({ trace: error.stack }, 'url generation failed')
204
215
  }
205
216
  }
@@ -220,9 +231,6 @@ const assertColor = async color => {
220
231
  }
221
232
  const prepareWAMessageMedia = async (message, options) => {
222
233
  const logger = options.logger
223
-
224
- const mediaAbProps = options.mediaAbProps || null
225
- const getMediaProp = propName => (0, messages_media_1.getMediaProp)(mediaAbProps, propName)
226
234
  let mediaType
227
235
  for (const key of Defaults_1.MEDIA_KEYS) {
228
236
  if (key in message) {
@@ -237,20 +245,7 @@ const prepareWAMessageMedia = async (message, options) => {
237
245
  media: message[mediaType]
238
246
  }
239
247
  delete uploadData[mediaType]
240
-
241
-
242
-
243
-
244
- if (mediaType === 'video' && uploadData.mimetype) {
245
- const isHevc = uploadData.mimetype.includes('hevc') || uploadData.mimetype.includes('x-matroska')
246
- if (isHevc && !getMediaProp('hevc_video_dual_upload')) {
247
-
248
- uploadData.mimetype = MIMETYPE_MAP['video']
249
- }
250
-
251
- }
252
-
253
-
248
+ // check if cacheable + generate cache key
254
249
  const cacheableKey =
255
250
  typeof uploadData.media === 'object' &&
256
251
  'url' in uploadData.media &&
@@ -267,7 +262,7 @@ const prepareWAMessageMedia = async (message, options) => {
267
262
  const mediaBuff = await options.mediaCache.get(cacheableKey)
268
263
  if (mediaBuff) {
269
264
  logger?.debug({ cacheableKey }, 'got media cache hit')
270
- const obj = WAProto_1.proto.Message.decode(mediaBuff)
265
+ const obj = index_js_1.proto.Message.decode(mediaBuff)
271
266
  const key = `${mediaType}Message`
272
267
  Object.assign(obj[key], { ...uploadData, media: undefined })
273
268
  return obj
@@ -289,7 +284,7 @@ const prepareWAMessageMedia = async (message, options) => {
289
284
  })
290
285
  await fs_1.promises.unlink(filePath)
291
286
  const obj = WAProto_1.proto.Message.fromObject({
292
-
287
+ // todo: add more support here
293
288
  [`${mediaType}Message`]: MessageTypeProto[mediaType].fromObject({
294
289
  url: mediaUrl,
295
290
  directPath,
@@ -380,27 +375,6 @@ const prepareWAMessageMedia = async (message, options) => {
380
375
  logger?.warn('failed to remove tmp file')
381
376
  }
382
377
  })
383
-
384
-
385
-
386
- const hdImageEnabled = mediaType === 'image' && getMediaProp('hd_image_dual_upload') && !!uploadData.hd
387
- const hdVideoEnabled = mediaType === 'video' && getMediaProp('hd_video_dual_upload') && !!uploadData.hd
388
- if (hdImageEnabled || hdVideoEnabled) {
389
- uploadData.mediaHd = true
390
- logger?.debug({ mediaType }, 'HD dual upload enabled, setting mediaHd flag')
391
- }
392
-
393
-
394
-
395
- if (mediaType === 'image' && uploadData.motionPhoto != null) {
396
-
397
-
398
- if (typeof uploadData.motionPhoto === 'boolean') {
399
- uploadData.motionPhoto = uploadData.motionPhoto ? 1 : 0
400
- }
401
- logger?.debug('motion photo field set on image message')
402
- }
403
-
404
378
  const obj = WAProto_1.proto.Message.fromObject({
405
379
  [`${mediaType}Message`]: MessageTypeProto[mediaType].fromObject({
406
380
  url: mediaUrl,
@@ -440,15 +414,19 @@ const prepareDisappearingMessageSettingContent = ephemeralExpiration => {
440
414
  return WAProto_1.proto.Message.fromObject(content)
441
415
  }
442
416
  exports.prepareDisappearingMessageSettingContent = prepareDisappearingMessageSettingContent
443
-
417
+ /**
418
+ * Generate forwarded message content like WA does
419
+ * @param message the message to forward
420
+ * @param options.forceForward will show the message as forwarded even if it is from you
421
+ */
444
422
  const generateForwardMessageContent = (message, forceForward) => {
445
423
  let content = message.message
446
424
  if (!content) {
447
425
  throw new boom_1.Boom('no content in message', { statusCode: 400 })
448
426
  }
449
-
427
+ // hacky copy
450
428
  content = (0, exports.normalizeMessageContent)(content)
451
- content = WAProto_1.proto.Message.decode(WAProto_1.proto.Message.encode(content).finish())
429
+ content = index_js_1.proto.Message.decode(index_js_1.proto.Message.encode(content).finish())
452
430
  let key = Object.keys(content)[0]
453
431
  let score = content?.[key]?.contextInfo?.forwardingScore || 0
454
432
  score += message.key.fromMe && !forceForward ? 0 : 1
@@ -676,19 +654,15 @@ const generateWAMessageContent = async (message, options) => {
676
654
  m.groupInviteMessage.caption = message.groupInvite.text
677
655
  m.groupInviteMessage.groupJid = message.groupInvite.jid
678
656
  m.groupInviteMessage.groupName = message.groupInvite.subject
657
+ //TODO: use built-in interface and get disappearing mode info etc.
658
+ //TODO: cache / use store!?
679
659
  if (options.getProfilePicUrl) {
680
- const cached = groupInviteThumbnailCache.get(message.groupInvite.jid)
681
- if (cached) {
682
- m.groupInviteMessage.jpegThumbnail = cached
683
- } else {
684
- const pfpUrl = await options.getProfilePicUrl(message.groupInvite.jid, 'preview')
685
- if (pfpUrl) {
686
- const resp = await fetch(pfpUrl, { method: 'GET', dispatcher: options?.options?.dispatcher })
687
- if (resp.ok) {
688
- const buf = Buffer.from(await resp.arrayBuffer())
689
- m.groupInviteMessage.jpegThumbnail = buf
690
- groupInviteThumbnailCache.set(message.groupInvite.jid, buf)
691
- }
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
692
666
  }
693
667
  }
694
668
  }
@@ -712,7 +686,7 @@ const generateWAMessageContent = async (message, options) => {
712
686
  m.buttonsResponseMessage = {
713
687
  selectedButtonId: message.buttonReply.id,
714
688
  selectedDisplayText: message.buttonReply.displayText,
715
- type: WAProto_1.proto.Message.ButtonsResponseMessage.Type.DISPLAY_TEXT
689
+ type: index_js_1.proto.Message.ButtonsResponseMessage.Type.DISPLAY_TEXT
716
690
  }
717
691
  break
718
692
  case 'interactive':
@@ -762,7 +736,7 @@ const generateWAMessageContent = async (message, options) => {
762
736
  (message.event.call === 'audio' ? Defaults_1.CALL_AUDIO_PREFIX : Defaults_1.CALL_VIDEO_PREFIX) + token
763
737
  }
764
738
  m.messageContextInfo = {
765
-
739
+ // encKey
766
740
  messageSecret: message.event.messageSecret || (0, crypto_1.randomBytes)(32)
767
741
  }
768
742
  m.eventMessage.name = message.event.name
@@ -806,26 +780,21 @@ const generateWAMessageContent = async (message, options) => {
806
780
  ...(message.poll.hideParticipantName !== undefined
807
781
  ? { hideParticipantName: !!message.poll.hideParticipantName }
808
782
  : {}),
809
- ...(message.poll.allowAddOption !== undefined ? { allowAddOption: !!message.poll.allowAddOption } : {}),
810
-
811
-
812
- ...((0, messages_media_1.getMediaProp)(options.mediaAbProps, 'media_poll') && message.poll.mediaPoll != null
813
- ? { mediaPoll: !!message.poll.mediaPoll }
814
- : {})
783
+ ...(message.poll.allowAddOption !== undefined ? { allowAddOption: !!message.poll.allowAddOption } : {})
815
784
  }
816
785
  if (message.poll.version === 6 || message.poll.v6) {
817
786
  m.pollCreationMessageV6 = pollCreationMessage
818
787
  } else if (message.poll.version === 5 || message.poll.v5) {
819
788
  m.pollCreationMessageV5 = pollCreationMessage
820
789
  } else if (message.poll.toAnnouncementGroup) {
821
-
790
+ // poll v2 is for community announcement groups (single select and multiple)
822
791
  m.pollCreationMessageV2 = pollCreationMessage
823
792
  } else {
824
793
  if (message.poll.selectableCount === 1) {
825
-
794
+ //poll v3 is for single select polls
826
795
  m.pollCreationMessageV3 = pollCreationMessage
827
796
  } else {
828
-
797
+ // poll for multiple choice polls
829
798
  m.pollCreationMessage = pollCreationMessage
830
799
  }
831
800
  }
@@ -954,13 +923,13 @@ const generateWAMessageContent = async (message, options) => {
954
923
  }
955
924
  } else if ((0, exports.hasNonNullishProperty)(message, 'sharePhoneNumber')) {
956
925
  m.protocolMessage = {
957
- type: WAProto_1.proto.Message.ProtocolMessage.Type.SHARE_PHONE_NUMBER
926
+ type: index_js_1.proto.Message.ProtocolMessage.Type.SHARE_PHONE_NUMBER
958
927
  }
959
928
  } else if ((0, exports.hasNonNullishProperty)(message, 'requestPhoneNumber')) {
960
929
  m.requestPhoneNumberMessage = {}
961
930
  } else if ((0, exports.hasNonNullishProperty)(message, 'limitSharing')) {
962
931
  m.protocolMessage = {
963
- type: WAProto_1.proto.Message.ProtocolMessage.Type.LIMIT_SHARING,
932
+ type: index_js_1.proto.Message.ProtocolMessage.Type.LIMIT_SHARING,
964
933
  limitSharing: {
965
934
  sharingLimited: message.limitSharing === true,
966
935
  trigger: 1,
@@ -1017,7 +986,7 @@ const generateWAMessageContent = async (message, options) => {
1017
986
  } else if ('interactiveMessage' in message) {
1018
987
  m = { interactiveMessage: message.interactiveMessage }
1019
988
  } else if ('richResponse' in message) {
1020
-
989
+ // handled in richResponse block below
1021
990
  } else if ('groupStatusMessage' in message) {
1022
991
  m = { groupStatusMessage: WAProto_1.proto.Message.GroupStatusMessage.fromObject(message.groupStatusMessage) }
1023
992
  } else if (hasCaptionWithoutMedia && !hasCaptionContainer) {
@@ -1063,7 +1032,7 @@ const generateWAMessageContent = async (message, options) => {
1063
1032
  m = { interactiveMessage }
1064
1033
  } else if ('templateButtons' in message && !!message.templateButtons) {
1065
1034
  const msg = {
1066
- hydratedButtons: message.templateButtons
1035
+ hydratedButtons: message.hasOwnProperty('templateButtons') ? message.templateButtons : message.templateButtons
1067
1036
  }
1068
1037
  if ('text' in message) {
1069
1038
  msg.hydratedContentText = message.text
@@ -1120,42 +1089,73 @@ const generateWAMessageContent = async (message, options) => {
1120
1089
  }
1121
1090
  }
1122
1091
  if ('interactiveButtons' in message && !!message.interactiveButtons) {
1123
- const interactiveMessage = {
1124
- nativeFlowMessage: WAProto_1.proto.Message.InteractiveMessage.NativeFlowMessage.fromObject({
1125
- buttons: message.interactiveButtons
1126
- })
1127
- }
1128
- if ('text' in message) {
1129
- interactiveMessage.body = {
1130
- text: message.text
1131
- }
1132
- } else if ('caption' in message) {
1133
- interactiveMessage.body = {
1134
- text: message.caption
1135
- }
1136
- interactiveMessage.header = {
1137
- title: message.title,
1138
- subtitle: message.subtitle,
1139
- hasMediaAttachment: Boolean(message.hasMediaAttachment)
1140
- }
1141
- Object.assign(interactiveMessage.header, m)
1142
- }
1143
- if ('footer' in message && !!message.footer) {
1144
- interactiveMessage.footer = {
1145
- text: message.footer
1146
- }
1147
- }
1148
- if ('title' in message && !!message.title) {
1149
- interactiveMessage.header = {
1150
- title: message.title,
1151
- subtitle: message.subtitle,
1152
- hasMediaAttachment: Boolean(message.hasMediaAttachment)
1153
- }
1154
- Object.assign(interactiveMessage.header, m)
1155
- }
1156
- applyContextInfoAndMentions(interactiveMessage, message)
1157
- m = { interactiveMessage }
1158
- }
1092
+ const nativeFlow = WAProto_1.proto.Message.InteractiveMessage.NativeFlowMessage.fromObject({
1093
+ buttons: message.interactiveButtons,
1094
+ messageParamsJson: message.messageParams ?? "{}"
1095
+ });
1096
+ let interactiveMessage = { nativeFlowMessage: nativeFlow };
1097
+ if ('text' in message) {
1098
+ interactiveMessage.body = { text: message.text };
1099
+ } else if ('caption' in message) {
1100
+ interactiveMessage.body = { text: message.caption };
1101
+ }
1102
+ if ('title' in message && !!message.title) {
1103
+ interactiveMessage.header = {
1104
+ title: message.title,
1105
+ subtitle: message.subtitle || "",
1106
+ hasMediaAttachment: false
1107
+ };
1108
+ let media = null;
1109
+ if ('image' in message && !!message.image) {
1110
+ media = await prepareWAMessageMedia({ image: message.image }, options);
1111
+ interactiveMessage.header.imageMessage = media.imageMessage;
1112
+ if (message.image.caption) {
1113
+ interactiveMessage.header.imageMessage.caption = message.image.caption;
1114
+ }
1115
+ } else if ('video' in message && !!message.video) {
1116
+ media = await prepareWAMessageMedia({ video: message.video }, options);
1117
+ interactiveMessage.header.videoMessage = media.videoMessage;
1118
+ if (message.video.caption) {
1119
+ interactiveMessage.header.videoMessage.caption = message.video.caption;
1120
+ }
1121
+ } else if ('gif' in message && !!message.gif) {
1122
+ media = await prepareWAMessageMedia({ video: message.gif }, options);
1123
+ interactiveMessage.header.videoMessage = media.videoMessage;
1124
+ interactiveMessage.header.videoMessage.gifPlayback = true;
1125
+ if (message.gif.caption) {
1126
+ interactiveMessage.header.videoMessage.caption = message.gif.caption;
1127
+ }
1128
+ } else if ('document' in message && !!message.document) {
1129
+ media = await prepareWAMessageMedia({ document: message.document }, options);
1130
+ interactiveMessage.header.documentMessage = media.documentMessage;
1131
+ let docuR = interactiveMessage.header.documentMessage;
1132
+ docuR.fileName = message.document.fileName || "Document";
1133
+ docuR.mimetype = message.document.mimetype || docuR.mimetype;
1134
+ if (message.document.caption) docuR.caption = message.document.caption;
1135
+ if (message.document.jpegThumbnail) docuR.jpegThumbnail = message.document.jpegThumbnail;
1136
+ } else if ('location' in message && !!message.location) {
1137
+ let mLoc = message.location;
1138
+ interactiveMessage.header.locationMessage = {
1139
+ degreesLongitude: mLoc.longitude || 0,
1140
+ degreesLatitude: mLoc.latitude || 0,
1141
+ name: mLoc.name || null,
1142
+ address: mLoc.address || null,
1143
+ url: mLoc.url || null
1144
+ };
1145
+ media = true;
1146
+ } else if ('thumbnail' in message && !!message.thumbnail) {
1147
+ interactiveMessage.header.jpegThumbnail = message.thumbnail;
1148
+ }
1149
+ if (media || ('thumbnail' in message && !!message.thumbnail)) {
1150
+ interactiveMessage.header.hasMediaAttachment = true;
1151
+ }
1152
+ }
1153
+ if ('footer' in message && !!message.footer) {
1154
+ interactiveMessage.footer = { text: message.footer };
1155
+ }
1156
+ applyContextInfoAndMentions(interactiveMessage, message);
1157
+ m = { interactiveMessage };
1158
+ }
1159
1159
  if ('shop' in message && !!message.shop) {
1160
1160
  const interactiveMessage = {
1161
1161
  shopStorefrontMessage: WAProto_1.proto.Message.InteractiveMessage.ShopMessage.fromObject({
@@ -1232,12 +1232,22 @@ const generateWAMessageContent = async (message, options) => {
1232
1232
  }
1233
1233
  })
1234
1234
  }
1235
- if (table && Array.isArray(table.rows)) {
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
+ }
1236
1244
  sections.push({
1237
1245
  view_model: {
1238
1246
  primitive: {
1239
- rows: table.rows.map(row => ({
1240
- cells: Array.isArray(row) ? row.map(c => ({ text: String(c) })) : row.cells
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
1241
1251
  })),
1242
1252
  __typename: 'GenAITableUXPrimitive'
1243
1253
  },
@@ -1868,10 +1878,9 @@ const generateWAMessageContent = async (message, options) => {
1868
1878
  ...existing,
1869
1879
  botThreadInfo: {
1870
1880
  clientInfo: {
1871
- type:
1872
- typeof bt.type === 'string'
1873
- ? (threadTypeMap[bt.type.toLowerCase()] ?? threadTypeEnum.UNKNOWN)
1874
- : (bt.type ?? threadTypeEnum.UNKNOWN),
1881
+ type: typeof bt.type === 'string'
1882
+ ? (threadTypeMap[bt.type.toLowerCase()] ?? threadTypeEnum.UNKNOWN)
1883
+ : (bt.type ?? threadTypeEnum.UNKNOWN),
1875
1884
  sourceChatJid: bt.sourceChatJid || ''
1876
1885
  }
1877
1886
  }
@@ -2272,7 +2281,7 @@ const generateWAMessageContent = async (message, options) => {
2272
2281
  m.messageContextInfo = {
2273
2282
  ...(m.messageContextInfo || {}),
2274
2283
  messageAddOnDurationInSecs: message.afterReadDuration,
2275
- messageAddOnExpiryType: WAProto_1.proto.MessageContextInfo.MessageAddonExpiryType.DEPENDENT_ON_PARENT
2284
+ messageAddOnExpiryType: index_js_1.proto.MessageContextInfo.MessageAddonExpiryType.DEPENDENT_ON_PARENT
2276
2285
  }
2277
2286
  }
2278
2287
  if ('raw' in message && !!message.raw) {
@@ -2401,8 +2410,8 @@ const generateWAMessageContent = async (message, options) => {
2401
2410
  }
2402
2411
  exports.generateWAMessageContent = generateWAMessageContent
2403
2412
  const generateWAMessageFromContent = (jid, message, options) => {
2404
-
2405
-
2413
+ // set timestamp to now
2414
+ // if not specified
2406
2415
  if (!options.timestamp) {
2407
2416
  options.timestamp = new Date()
2408
2417
  }
@@ -2412,12 +2421,12 @@ const generateWAMessageFromContent = (jid, message, options) => {
2412
2421
  const { quoted, userJid } = options
2413
2422
  if (quoted && !(0, WABinary_1.isJidNewsletter)(jid)) {
2414
2423
  const participant = quoted.key.fromMe
2415
- ? userJid
2424
+ ? userJid // TODO: Add support for LIDs
2416
2425
  : quoted.participant || quoted.key.participant || quoted.key.remoteJid
2417
2426
  let quotedMsg = (0, exports.normalizeMessageContent)(quoted.message)
2418
2427
  const msgType = (0, exports.getContentType)(quotedMsg)
2419
-
2420
- quotedMsg = WAProto_1.proto.Message.create({ [msgType]: quotedMsg[msgType] })
2428
+ // strip any redundant properties
2429
+ quotedMsg = index_js_1.proto.Message.create({ [msgType]: quotedMsg[msgType] })
2421
2430
  const quotedContent = quotedMsg[msgType]
2422
2431
  if (typeof quotedContent === 'object' && quotedContent && 'contextInfo' in quotedContent) {
2423
2432
  delete quotedContent.contextInfo
@@ -2426,53 +2435,62 @@ const generateWAMessageFromContent = (jid, message, options) => {
2426
2435
  contextInfo.participant = (0, WABinary_1.jidNormalizedUser)(participant)
2427
2436
  contextInfo.stanzaId = quoted.key.id
2428
2437
  contextInfo.quotedMessage = quotedMsg
2429
-
2430
-
2438
+ // if a participant is quoted, then it must be a group
2439
+ // hence, remoteJid of group must also be entered
2431
2440
  if (jid !== quoted.key.remoteJid) {
2432
2441
  contextInfo.remoteJid = quoted.key.remoteJid
2433
2442
  }
2434
2443
  if (contextInfo && innerMessage[key]) {
2435
-
2444
+ /* @ts-ignore */
2436
2445
  innerMessage[key].contextInfo = contextInfo
2437
2446
  }
2438
2447
  }
2439
2448
  if (
2440
-
2449
+ // if we want to send a disappearing message
2441
2450
  !!options.ephemeralExpiration &&
2442
-
2451
+ // and it's not a protocol message -- delete, toggle disappear message
2443
2452
  key !== 'protocolMessage' &&
2444
-
2453
+ // already not converted to disappearing message
2445
2454
  key !== 'ephemeralMessage' &&
2446
-
2455
+ // newsletters don't support ephemeral messages
2447
2456
  !(0, WABinary_1.isJidNewsletter)(jid)
2448
2457
  ) {
2449
-
2458
+ /* @ts-ignore */
2450
2459
  innerMessage[key].contextInfo = {
2451
2460
  ...(innerMessage[key].contextInfo || {}),
2452
2461
  expiration: options.ephemeralExpiration || Defaults_1.WA_DEFAULT_EPHEMERAL
2453
-
2462
+ //ephemeralSettingTimestamp: options.ephemeralOptions.eph_setting_ts?.toString()
2454
2463
  }
2455
2464
  }
2456
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))
2457
2475
  const messageJSON = {
2458
2476
  key: {
2459
2477
  remoteJid: jid,
2460
2478
  fromMe: true,
2461
- id: options?.messageId || (0, generics_1.generateMessageIDV2)()
2479
+ id: pickedMessageId
2462
2480
  },
2463
2481
  message: message,
2464
2482
  messageTimestamp: timestamp,
2465
2483
  messageStubParameters: [],
2466
- participant: (0, WABinary_1.isJidGroup)(jid) || (0, WABinary_1.isJidStatusBroadcast)(jid) ? userJid : undefined,
2484
+ participant: (0, WABinary_1.isJidGroup)(jid) || (0, WABinary_1.isJidStatusBroadcast)(jid) ? userJid : undefined, // TODO: Add support for LIDs
2467
2485
  status: Types_1.WAMessageStatus.PENDING
2468
2486
  }
2469
2487
  return WAProto_1.proto.WebMessageInfo.fromObject(messageJSON)
2470
2488
  }
2471
2489
  exports.generateWAMessageFromContent = generateWAMessageFromContent
2472
2490
  const generateWAMessage = async (jid, content, options) => {
2473
-
2491
+ // ensure msg ID is with every log
2474
2492
  options.logger = options?.logger?.child({ msgId: options.messageId })
2475
-
2493
+ // Pass jid in the options to generateWAMessageContent
2476
2494
  return (0, exports.generateWAMessageFromContent)(
2477
2495
  jid,
2478
2496
  await (0, exports.generateWAMessageContent)(content, { ...options, jid }),
@@ -2480,3 +2498,407 @@ const generateWAMessage = async (jid, content, options) => {
2480
2498
  )
2481
2499
  }
2482
2500
  exports.generateWAMessage = generateWAMessage
2501
+ /** Get the key to access the true type of content */
2502
+ const getContentType = content => {
2503
+ if (content) {
2504
+ const keys = Object.keys(content)
2505
+ const key = keys.find(k => (k === 'conversation' || k.includes('Message')) && k !== 'senderKeyDistributionMessage')
2506
+ return key
2507
+ }
2508
+ }
2509
+ exports.getContentType = getContentType
2510
+ /**
2511
+ * Normalizes ephemeral, view once messages to regular message content
2512
+ * Eg. image messages in ephemeral messages, in view once messages etc.
2513
+ * @param content
2514
+ * @returns
2515
+ */
2516
+ const normalizeMessageContent = content => {
2517
+ if (!content) {
2518
+ return undefined
2519
+ }
2520
+ // set max iterations to prevent an infinite loop
2521
+ for (let i = 0; i < 5; i++) {
2522
+ const inner = getFutureProofMessage(content)
2523
+ if (!inner) {
2524
+ break
2525
+ }
2526
+ content = inner.message
2527
+ }
2528
+ return content
2529
+ function getFutureProofMessage(message) {
2530
+ return (
2531
+ message.ephemeralMessage ||
2532
+ message.viewOnceMessage ||
2533
+ message.documentWithCaptionMessage ||
2534
+ message.viewOnceMessageV2 ||
2535
+ message.viewOnceMessageV2Extension ||
2536
+ message.editedMessage ||
2537
+ message.groupMentionedMessage ||
2538
+ message.botInvokeMessage ||
2539
+ message.lottieStickerMessage ||
2540
+ message.eventCoverImage ||
2541
+ message.statusMentionMessage ||
2542
+ message.pollCreationOptionImageMessage ||
2543
+ message.associatedChildMessage ||
2544
+ message.groupStatusMentionMessage ||
2545
+ message.pollCreationMessageV4 ||
2546
+ message.pollCreationMessageV5 ||
2547
+ message.statusAddYours ||
2548
+ message.groupStatusMessage ||
2549
+ message.limitSharingMessage ||
2550
+ message.botTaskMessage ||
2551
+ message.questionMessage ||
2552
+ message.groupStatusMessageV2 ||
2553
+ message.botForwardedMessage ||
2554
+ message.questionReplyMessage ||
2555
+ message.newsletterAdminProfileMessage ||
2556
+ message.newsletterAdminProfileMessageV2 ||
2557
+ message.newsletterAdminProfileStatusMessage ||
2558
+ message.spoilerMessage ||
2559
+ message.groupStatusV3Message ||
2560
+ message.pollCreationMessageV6
2561
+ )
2562
+ }
2563
+ }
2564
+ exports.normalizeMessageContent = normalizeMessageContent
2565
+ /**
2566
+ * Extract the true message content from a message
2567
+ * Eg. extracts the inner message from a disappearing message/view once message
2568
+ */
2569
+ const extractMessageContent = content => {
2570
+ const extractFromTemplateMessage = msg => {
2571
+ if (msg.imageMessage) {
2572
+ return { imageMessage: msg.imageMessage }
2573
+ } else if (msg.documentMessage) {
2574
+ return { documentMessage: msg.documentMessage }
2575
+ } else if (msg.videoMessage) {
2576
+ return { videoMessage: msg.videoMessage }
2577
+ } else if (msg.locationMessage) {
2578
+ return { locationMessage: msg.locationMessage }
2579
+ } else {
2580
+ return {
2581
+ conversation:
2582
+ 'contentText' in msg ? msg.contentText : 'hydratedContentText' in msg ? msg.hydratedContentText : ''
2583
+ }
2584
+ }
2585
+ }
2586
+ content = (0, exports.normalizeMessageContent)(content)
2587
+ if (content?.buttonsMessage) {
2588
+ return extractFromTemplateMessage(content.buttonsMessage)
2589
+ }
2590
+ if (content?.templateMessage?.hydratedFourRowTemplate) {
2591
+ return extractFromTemplateMessage(content?.templateMessage?.hydratedFourRowTemplate)
2592
+ }
2593
+ if (content?.templateMessage?.hydratedTemplate) {
2594
+ return extractFromTemplateMessage(content?.templateMessage?.hydratedTemplate)
2595
+ }
2596
+ if (content?.templateMessage?.fourRowTemplate) {
2597
+ return extractFromTemplateMessage(content?.templateMessage?.fourRowTemplate)
2598
+ }
2599
+ return content
2600
+ }
2601
+ exports.extractMessageContent = extractMessageContent
2602
+ /**
2603
+ * Returns the device predicted by message ID
2604
+ */
2605
+ const getDevice = id =>
2606
+ /^3A.{18}$/.test(id)
2607
+ ? 'ios'
2608
+ : /^3E.{20}$/.test(id)
2609
+ ? 'web'
2610
+ : /^(.{21}|.{32})$/.test(id)
2611
+ ? 'android'
2612
+ : /^(3F|.{18}$)/.test(id)
2613
+ ? 'desktop'
2614
+ : 'api/baileys'
2615
+ exports.getDevice = getDevice
2616
+ /** Upserts a receipt in the message */
2617
+ const updateMessageWithReceipt = (msg, receipt) => {
2618
+ msg.userReceipt = msg.userReceipt || []
2619
+ const recp = msg.userReceipt.find(m => m.userJid === receipt.userJid)
2620
+ if (recp) {
2621
+ Object.assign(recp, receipt)
2622
+ } else {
2623
+ msg.userReceipt.push(receipt)
2624
+ }
2625
+ }
2626
+ exports.updateMessageWithReceipt = updateMessageWithReceipt
2627
+ /** Update the message with a new reaction */
2628
+ const updateMessageWithReaction = (msg, reaction) => {
2629
+ const authorID = (0, generics_1.getKeyAuthor)(reaction.key)
2630
+ const reactions = (msg.reactions || []).filter(r => (0, generics_1.getKeyAuthor)(r.key) !== authorID)
2631
+ reaction.text = reaction.text || ''
2632
+ reactions.push(reaction)
2633
+ msg.reactions = reactions
2634
+ }
2635
+ exports.updateMessageWithReaction = updateMessageWithReaction
2636
+ /** Update the message with a new poll update */
2637
+ const updateMessageWithPollUpdate = (msg, update) => {
2638
+ const authorID = (0, generics_1.getKeyAuthor)(update.pollUpdateMessageKey)
2639
+ const reactions = (msg.pollUpdates || []).filter(
2640
+ r => (0, generics_1.getKeyAuthor)(r.pollUpdateMessageKey) !== authorID
2641
+ )
2642
+ if (update.vote?.selectedOptions?.length) {
2643
+ reactions.push(update)
2644
+ }
2645
+ msg.pollUpdates = reactions
2646
+ }
2647
+ exports.updateMessageWithPollUpdate = updateMessageWithPollUpdate
2648
+ /** Update the message with a new event response */
2649
+ const updateMessageWithEventResponse = (msg, update) => {
2650
+ const authorID = (0, generics_1.getKeyAuthor)(update.eventResponseMessageKey)
2651
+ const responses = (msg.eventResponses || []).filter(
2652
+ r => (0, generics_1.getKeyAuthor)(r.eventResponseMessageKey) !== authorID
2653
+ )
2654
+ responses.push(update)
2655
+ msg.eventResponses = responses
2656
+ }
2657
+ exports.updateMessageWithEventResponse = updateMessageWithEventResponse
2658
+ /**
2659
+ * Aggregates all poll updates in a poll.
2660
+ * @param msg the poll creation message
2661
+ * @param meId your jid
2662
+ * @returns A list of options & their voters
2663
+ */
2664
+ function getAggregateVotesInPollMessage({ message, pollUpdates }, meId) {
2665
+ const opts =
2666
+ message?.pollCreationMessage?.options ||
2667
+ message?.pollCreationMessageV2?.options ||
2668
+ message?.pollCreationMessageV3?.options ||
2669
+ message?.pollCreationMessageV5?.options ||
2670
+ message?.pollCreationMessageV6?.options ||
2671
+ message?.pollCreationMessageV4?.message?.pollCreationMessage?.options ||
2672
+ message?.pollCreationMessageV4?.message?.pollCreationMessageV3?.options ||
2673
+ []
2674
+ const voteHashMap = opts.reduce((acc, opt) => {
2675
+ const hash = (0, crypto_2.sha256)(Buffer.from(opt.optionName || '')).toString()
2676
+ acc[hash] = {
2677
+ name: opt.optionName || '',
2678
+ voters: []
2679
+ }
2680
+ return acc
2681
+ }, {})
2682
+ for (const update of pollUpdates || []) {
2683
+ const { vote } = update
2684
+ if (!vote) {
2685
+ continue
2686
+ }
2687
+ for (const option of vote.selectedOptions || []) {
2688
+ const hash = option.toString()
2689
+ let data = voteHashMap[hash]
2690
+ if (!data) {
2691
+ voteHashMap[hash] = {
2692
+ name: 'Unknown',
2693
+ voters: []
2694
+ }
2695
+ data = voteHashMap[hash]
2696
+ }
2697
+ voteHashMap[hash].voters.push((0, generics_1.getKeyAuthor)(update.pollUpdateMessageKey, meId))
2698
+ }
2699
+ }
2700
+ return Object.values(voteHashMap)
2701
+ }
2702
+ /** Given a list of message keys, aggregates them by chat & sender. Useful for sending read receipts in bulk */
2703
+ const aggregateMessageKeysNotFromMe = keys => {
2704
+ const keyMap = {}
2705
+ for (const { remoteJid, id, participant, fromMe, sts } of keys) {
2706
+ if (!fromMe) {
2707
+ const uqKey = `${remoteJid}:${participant || ''}`
2708
+ if (!keyMap[uqKey]) {
2709
+ keyMap[uqKey] = {
2710
+ jid: remoteJid,
2711
+ participant: participant,
2712
+ messageIds: [],
2713
+ // sts is per-message; kept only for single-message interop receipts
2714
+ sts: sts
2715
+ }
2716
+ } else if (keyMap[uqKey].sts !== sts) {
2717
+ // Multiple messages with different sts — can't aggregate a single sts
2718
+ keyMap[uqKey].sts = undefined
2719
+ }
2720
+ keyMap[uqKey].messageIds.push(id)
2721
+ }
2722
+ }
2723
+ return Object.values(keyMap)
2724
+ }
2725
+ exports.aggregateMessageKeysNotFromMe = aggregateMessageKeysNotFromMe
2726
+
2727
+ /**
2728
+ * Check if a WebMessageInfo has a scheduled reveal time (ConditionalRevealMessage)
2729
+ */
2730
+ const isScheduledMessage = msg => !!msg?.scheduledMessageMetadata?.scheduledTime
2731
+ exports.isScheduledMessage = isScheduledMessage
2732
+
2733
+ /**
2734
+ * Get scheduled reveal time of a message as a Date, or null
2735
+ */
2736
+ const getScheduledMessageTime = msg => {
2737
+ const t = msg?.scheduledMessageMetadata?.scheduledTime
2738
+ if (!t) return null
2739
+ return new Date(Number(t) * 1000)
2740
+ }
2741
+ exports.getScheduledMessageTime = getScheduledMessageTime
2742
+
2743
+ /**
2744
+ * Extract PaymentInfo from a WebMessageInfo (the payment status field, not the message content)
2745
+ */
2746
+ const getMessagePaymentInfo = msg => msg?.paymentInfo || msg?.quotedPaymentInfo || null
2747
+ exports.getMessagePaymentInfo = getMessagePaymentInfo
2748
+
2749
+ /**
2750
+ * Get all comment metadata from a WebMessageInfo
2751
+ */
2752
+ const getMessageCommentMetadata = msg => msg?.commentMetadata || null
2753
+ exports.getMessageCommentMetadata = getMessageCommentMetadata
2754
+
2755
+ /**
2756
+ * Get all message add-ons (reactions, poll updates, pins) from a WebMessageInfo
2757
+ */
2758
+ const getMessageAddOns = msg => msg?.messageAddOns || []
2759
+ exports.getMessageAddOns = getMessageAddOns
2760
+
2761
+ /**
2762
+ * Get the quiz correct answer from a poll creation message, if it's a quiz
2763
+ */
2764
+ const getPollCorrectAnswer = pollMsg => {
2765
+ const poll =
2766
+ pollMsg?.pollCreationMessage ||
2767
+ pollMsg?.pollCreationMessageV2 ||
2768
+ pollMsg?.pollCreationMessageV3 ||
2769
+ pollMsg?.pollCreationMessageV5 ||
2770
+ pollMsg?.pollCreationMessageV6
2771
+ if (!poll) return null
2772
+ const isQuiz = poll.pollType === WAProto_1.proto.Message.PollType?.QUIZ || poll.pollType === 1
2773
+ return isQuiz ? poll.correctAnswer?.optionName || null : null
2774
+ }
2775
+ exports.getPollCorrectAnswer = getPollCorrectAnswer
2776
+
2777
+ /**
2778
+ * Aggregates all event responses in an event message.
2779
+ * @param msg the event creation message
2780
+ * @param meLid your lid
2781
+ * @returns A list of response types & their responders
2782
+ */
2783
+ function getAggregateResponsesInEventMessage({ eventResponses }, meLid) {
2784
+ const responseTypes = ['GOING', 'NOT_GOING', 'MAYBE']
2785
+ const responseMap = {}
2786
+
2787
+ for (const type of responseTypes) {
2788
+ responseMap[type] = {
2789
+ response: type,
2790
+ responders: []
2791
+ }
2792
+ }
2793
+ for (const update of eventResponses) {
2794
+ const { response } = update.response || {}
2795
+ const responseType = index_js_1.proto.Message.EventResponseMessage.EventResponseType[response]
2796
+ if (responseType !== 'UNKNOWN' && responseMap[responseType]) {
2797
+ responseMap[responseType].responders.push(generics_1.getKeyAuthor(update.eventResponseMessageKey, meLid))
2798
+ }
2799
+ }
2800
+
2801
+ return Object.values(responseMap)
2802
+ }
2803
+
2804
+ exports.getAggregateResponsesInEventMessage = getAggregateResponsesInEventMessage
2805
+
2806
+ const REUPLOAD_REQUIRED_STATUS = [410, 404]
2807
+ /**
2808
+ * Downloads the given message. Throws an error if it's not a media message
2809
+ */
2810
+ const downloadMediaMessage = async (message, type, options, ctx) => {
2811
+ const result = await downloadMsg().catch(async error => {
2812
+ if (
2813
+ ctx &&
2814
+ typeof error?.status === 'number' && // treat errors with status as HTTP failures requiring reupload
2815
+ REUPLOAD_REQUIRED_STATUS.includes(error.status)
2816
+ ) {
2817
+ ctx.logger.info({ key: message.key }, 'sending reupload media request...')
2818
+ // request reupload
2819
+ message = await ctx.reuploadRequest(message)
2820
+ const result = await downloadMsg()
2821
+ return result
2822
+ }
2823
+ throw error
2824
+ })
2825
+ return result
2826
+ async function downloadMsg() {
2827
+ const mContent = (0, exports.extractMessageContent)(message.message)
2828
+ if (!mContent) {
2829
+ throw new boom_1.Boom('No message present', { statusCode: 400, data: message })
2830
+ }
2831
+ const contentType = (0, exports.getContentType)(mContent)
2832
+ let mediaType = contentType?.replace('Message', '')
2833
+ const media = mContent[contentType]
2834
+ if (!media || typeof media !== 'object' || (!('url' in media) && !('thumbnailDirectPath' in media))) {
2835
+ throw new boom_1.Boom(`"${contentType}" message is not a media message`)
2836
+ }
2837
+ let download
2838
+ if ('thumbnailDirectPath' in media && !('url' in media)) {
2839
+ download = {
2840
+ directPath: media.thumbnailDirectPath,
2841
+ mediaKey: media.mediaKey
2842
+ }
2843
+ mediaType = 'thumbnail-link'
2844
+ } else {
2845
+ download = media
2846
+ }
2847
+ const stream = await (0, messages_media_1.downloadContentFromMessage)(download, mediaType, options)
2848
+ if (type === 'buffer') {
2849
+ const bufferArray = []
2850
+ for await (const chunk of stream) {
2851
+ bufferArray.push(chunk)
2852
+ }
2853
+ return Buffer.concat(bufferArray)
2854
+ }
2855
+ return stream
2856
+ }
2857
+ }
2858
+ exports.downloadMediaMessage = downloadMediaMessage
2859
+ /** Checks whether the given message is a media message; if it is returns the inner content */
2860
+ const assertMediaContent = content => {
2861
+ content = (0, exports.extractMessageContent)(content)
2862
+ const mediaContent =
2863
+ content?.documentMessage ||
2864
+ content?.imageMessage ||
2865
+ content?.videoMessage ||
2866
+ content?.audioMessage ||
2867
+ content?.stickerMessage
2868
+ if (!mediaContent) {
2869
+ throw new boom_1.Boom('given message is not a media message', { statusCode: 400, data: content })
2870
+ }
2871
+ return mediaContent
2872
+ }
2873
+ exports.assertMediaContent = assertMediaContent
2874
+ /**
2875
+ * Normalizes a bare user id to @s.whatsapp.net. Does not convert LID↔PN; use lidMapping / PN in key.remoteJidAlt when needed.
2876
+ */
2877
+ const toJid = id => {
2878
+ if (!id) return ''
2879
+ if (id.includes('@')) return id
2880
+ return `${id}@s.whatsapp.net`
2881
+ }
2882
+ exports.toJid = toJid
2883
+ /**
2884
+ * Returns the peer LID JID when the key is LID-primary (decode sets remoteJid/participant to @lid when WA sends LID).
2885
+ */
2886
+ const getSenderLid = message => {
2887
+ const k = message.key
2888
+ if (!k) {
2889
+ return { jid: '', lid: '' }
2890
+ }
2891
+ const jid = k.participant || k.remoteJid || ''
2892
+ if (jid.endsWith('@lid') || jid.endsWith('@hosted.lid')) {
2893
+ return { jid, lid: jid }
2894
+ }
2895
+ if (k.lid && typeof k.lid === 'string') {
2896
+ const lid = k.lid.includes('@') ? k.lid : (0, WABinary_1.jidEncode)(k.lid, 'lid')
2897
+ return { jid, lid }
2898
+ }
2899
+ if (k.participantLid && (0, WABinary_1.isLidUser)(k.participantLid)) {
2900
+ return { jid, lid: k.participantLid }
2901
+ }
2902
+ return { jid, lid: '' }
2903
+ }
2904
+ exports.getSenderLid = getSenderLid