@badzz88/baileys 8.4.9 → 8.5.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 (153) hide show
  1. package/WAProto/WAProto.proto +6 -347
  2. package/WAProto/fix-imports.js +85 -86
  3. package/WAProto/index.d.ts +25 -4913
  4. package/WAProto/index.js +93 -14069
  5. package/{src → lib}/Defaults/index.js +2 -13
  6. package/{src → lib}/Signal/Group/group_cipher.js +5 -5
  7. package/{src → lib}/Signal/Group/keyhelper.js +4 -4
  8. package/{src → lib}/Signal/Group/sender-chain-key.js +2 -2
  9. package/{src → lib}/Signal/Group/sender-key-distribution-message.js +1 -1
  10. package/{src → lib}/Signal/Group/sender-key-message.js +3 -3
  11. package/{src → lib}/Signal/Group/sender-key-record.js +1 -1
  12. package/{src → lib}/Signal/Group/sender-message-key.js +2 -2
  13. package/{src → lib}/Signal/libsignal.js +26 -6
  14. package/{src → lib}/Socket/chats.js +58 -616
  15. package/{src → lib}/Socket/communities.js +28 -59
  16. package/{src → lib}/Socket/graphql.js +103 -362
  17. package/{src → lib}/Socket/groups.js +29 -77
  18. package/{src → lib}/Socket/index.js +5 -3
  19. package/{src → lib}/Socket/interactive-handler.js +60 -117
  20. package/{src → lib}/Socket/interop.js +19 -36
  21. package/lib/Socket/luxu.js +387 -0
  22. package/{src → lib}/Socket/managed-account.js +27 -58
  23. package/{src → lib}/Socket/messages-recv.js +143 -715
  24. package/{src → lib}/Socket/messages-send.js +102 -412
  25. package/lib/Socket/mex.js +60 -0
  26. package/{src → lib}/Socket/newsletter.js +23 -266
  27. package/{src → lib}/Socket/privacy.js +38 -41
  28. package/{src → lib}/Socket/registration.js +106 -113
  29. package/{src → lib}/Socket/socket.js +101 -88
  30. package/{src → lib}/Socket/text-router.js +17 -1
  31. package/lib/Socket/username.js +243 -0
  32. package/lib/Store/keyed-db.js +108 -0
  33. package/{src → lib}/Store/make-in-memory-store.js +104 -6
  34. package/lib/Types/Mex.js +112 -0
  35. package/{src → lib}/Types/Newsletter.js +41 -72
  36. package/{src → lib}/Types/index.js +1 -0
  37. package/{src → lib}/Utils/chat-utils.js +9 -73
  38. package/lib/Utils/command-loader.d.ts +31 -0
  39. package/lib/Utils/command-loader.js +100 -0
  40. package/lib/Utils/companion-reg-client-utils.js +42 -0
  41. package/lib/Utils/crypto.js +155 -0
  42. package/lib/Utils/curve25519-js.js +275 -0
  43. package/{src → lib}/Utils/decode-wa-message.js +36 -284
  44. package/{src → lib}/Utils/event-buffer.js +27 -6
  45. package/{src → lib}/Utils/generics.js +37 -114
  46. package/{src → lib}/Utils/index.js +5 -1
  47. package/lib/Utils/lt-hash.js +39 -0
  48. package/{src → lib}/Utils/message-retry-manager.js +39 -0
  49. package/{src → lib}/Utils/messages-media.js +58 -105
  50. package/{src → lib}/Utils/messages.js +529 -115
  51. package/lib/Utils/meta-ai-msmsg.js +262 -0
  52. package/lib/Utils/native-bridge.js +82 -0
  53. package/{src → lib}/Utils/noise-handler.js +15 -13
  54. package/{src → lib}/Utils/process-message.js +55 -54
  55. package/{src → lib}/Utils/reporting-utils.js +2 -2
  56. package/lib/Utils/session-pool.d.ts +16 -0
  57. package/lib/Utils/session-pool.js +94 -0
  58. package/{src → lib}/Utils/signal.js +51 -19
  59. package/{src → lib}/Utils/stanza-ack.js +2 -36
  60. package/lib/Utils/sticker.d.ts +17 -0
  61. package/lib/Utils/sticker.js +145 -0
  62. package/{src → lib}/Utils/tc-token-utils.js +1 -1
  63. package/{src → lib}/Utils/validate-connection.js +11 -40
  64. package/lib/Utils/view-once-cache.d.ts +9 -0
  65. package/lib/Utils/view-once-cache.js +79 -0
  66. package/{src → lib}/WABinary/decode.js +4 -38
  67. package/lib/WABinary/encode.js +228 -0
  68. package/{src → lib}/WABinary/generic-utils.js +93 -1
  69. package/{src → lib}/WABinary/jid-utils.js +3 -16
  70. package/{src → lib}/WAM/encode.js +1 -1
  71. package/lib/WAUSync/Protocols/USyncBusinessProtocol.js +43 -0
  72. package/{src → lib}/WAUSync/Protocols/USyncContactProtocol.js +1 -7
  73. package/{src → lib}/WAUSync/Protocols/USyncDeviceProtocol.js +5 -13
  74. package/{src → lib}/WAUSync/Protocols/USyncDisappearingModeProtocol.js +4 -1
  75. package/{src → lib}/WAUSync/Protocols/USyncFeatureProtocol.js +2 -17
  76. package/{src → lib}/WAUSync/Protocols/USyncStatusProtocol.js +2 -7
  77. package/{src → lib}/WAUSync/Protocols/USyncUsernameProtocol.js +1 -2
  78. package/{src → lib}/WAUSync/Protocols/index.js +0 -1
  79. package/{src → lib}/WAUSync/USyncQuery.js +6 -77
  80. package/{src → lib}/WAUSync/USyncUser.js +8 -16
  81. package/{src → lib}/WAUSync/index.js +0 -1
  82. package/{src → lib}/antiban.js +266 -355
  83. package/{src → lib}/index.js +2 -2
  84. package/package.json +18 -15
  85. package/WAProto/fix-import.js +0 -38
  86. package/src/Socket/mex.js +0 -47
  87. package/src/Socket/username.js +0 -234
  88. package/src/Utils/crypto.js +0 -125
  89. package/src/Utils/lt-hash.js +0 -10
  90. package/src/Utils/message-inspect.js +0 -400
  91. package/src/Utils/meta-ai-msmsg.js +0 -133
  92. package/src/WABinary/encode.js +0 -58
  93. package/src/WAUSync/Protocols/USyncBusinessProtocol.js +0 -100
  94. package/src/WAUSync/USyncBackoff.js +0 -31
  95. /package/{src → lib}/Defaults/phonenumber-mcc.json +0 -0
  96. /package/{src → lib}/Signal/Group/ciphertext-message.js +0 -0
  97. /package/{src → lib}/Signal/Group/group-session-builder.js +0 -0
  98. /package/{src → lib}/Signal/Group/index.js +0 -0
  99. /package/{src → lib}/Signal/Group/sender-key-name.js +0 -0
  100. /package/{src → lib}/Signal/Group/sender-key-state.js +0 -0
  101. /package/{src → lib}/Signal/lid-mapping.js +0 -0
  102. /package/{src → lib}/Socket/Client/index.js +0 -0
  103. /package/{src → lib}/Socket/Client/types.js +0 -0
  104. /package/{src → lib}/Socket/Client/websocket.js +0 -0
  105. /package/{src → lib}/Socket/aigroups.js +0 -0
  106. /package/{src → lib}/Socket/business.js +0 -0
  107. /package/{src → lib}/Store/index.js +0 -0
  108. /package/{src → lib}/Store/make-cache-manager-store.js +0 -0
  109. /package/{src → lib}/Store/make-ordered-dictionary.js +0 -0
  110. /package/{src → lib}/Store/object-repository.js +0 -0
  111. /package/{src → lib}/Types/Auth.js +0 -0
  112. /package/{src → lib}/Types/Bussines.js +0 -0
  113. /package/{src → lib}/Types/Call.js +0 -0
  114. /package/{src → lib}/Types/Chat.js +0 -0
  115. /package/{src → lib}/Types/Contact.js +0 -0
  116. /package/{src → lib}/Types/Events.js +0 -0
  117. /package/{src → lib}/Types/GroupMetadata.js +0 -0
  118. /package/{src → lib}/Types/Label.js +0 -0
  119. /package/{src → lib}/Types/LabelAssociation.js +0 -0
  120. /package/{src → lib}/Types/Message.js +0 -0
  121. /package/{src → lib}/Types/Product.js +0 -0
  122. /package/{src → lib}/Types/Signal.js +0 -0
  123. /package/{src → lib}/Types/Socket.js +0 -0
  124. /package/{src → lib}/Types/State.js +0 -0
  125. /package/{src → lib}/Types/USync.js +0 -0
  126. /package/{src → lib}/Utils/auth-utils.js +0 -0
  127. /package/{src → lib}/Utils/browser-utils.js +0 -0
  128. /package/{src → lib}/Utils/business.js +0 -0
  129. /package/{src → lib}/Utils/consumer-application.js +0 -0
  130. /package/{src → lib}/Utils/group-history.js +0 -0
  131. /package/{src → lib}/Utils/history.js +0 -0
  132. /package/{src → lib}/Utils/identity-change-handler.js +0 -0
  133. /package/{src → lib}/Utils/jid-display-normalization.js +0 -0
  134. /package/{src → lib}/Utils/link-preview.js +0 -0
  135. /package/{src → lib}/Utils/logger.js +0 -0
  136. /package/{src → lib}/Utils/make-mutex.js +0 -0
  137. /package/{src → lib}/Utils/message-composer.js +0 -0
  138. /package/{src → lib}/Utils/offline-node-processor.js +0 -0
  139. /package/{src → lib}/Utils/pre-key-manager.js +0 -0
  140. /package/{src → lib}/Utils/sync-action-utils.js +0 -0
  141. /package/{src → lib}/Utils/use-multi-file-auth-state.js +0 -0
  142. /package/{src → lib}/Utils/voip-rekey.js +0 -0
  143. /package/{src → lib}/WABinary/constants.js +0 -0
  144. /package/{src → lib}/WABinary/index.js +0 -0
  145. /package/{src → lib}/WABinary/types.js +0 -0
  146. /package/{src → lib}/WAM/BinaryInfo.js +0 -0
  147. /package/{src → lib}/WAM/constants.js +0 -0
  148. /package/{src → lib}/WAM/index.js +0 -0
  149. /package/{src → lib}/WAUSync/Protocols/USyncBotProfileProtocol.js +0 -0
  150. /package/{src → lib}/WAUSync/Protocols/USyncLIDProtocol.js +0 -0
  151. /package/{src → lib}/WAUSync/Protocols/USyncPictureProtocol.js +0 -0
  152. /package/{src → lib}/WAUSync/Protocols/USyncSidelistProtocol.js +0 -0
  153. /package/{src → lib}/WAUSync/Protocols/USyncTextStatusProtocol.js +0 -0
@@ -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
- // Group-invite thumbnails are fetched (IQ + HTTP GET) per generateWAMessage call;
29
- // caching the downloaded bytes avoids redownloading the same group picture when
30
- // the same invite is sent to many recipients in a burst.
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',
@@ -224,9 +231,6 @@ const assertColor = async color => {
224
231
  }
225
232
  const prepareWAMessageMedia = async (message, options) => {
226
233
  const logger = options.logger
227
- // AB props for feature-flagged media paths (passed via options.mediaAbProps).
228
- const mediaAbProps = options.mediaAbProps || null
229
- const getMediaProp = propName => (0, messages_media_1.getMediaProp)(mediaAbProps, propName)
230
234
  let mediaType
231
235
  for (const key of Defaults_1.MEDIA_KEYS) {
232
236
  if (key in message) {
@@ -241,19 +245,6 @@ const prepareWAMessageMedia = async (message, options) => {
241
245
  media: message[mediaType]
242
246
  }
243
247
  delete uploadData[mediaType]
244
-
245
- // Feature B: HEVC video — when hevc_video_dual_upload is enabled, allow HEVC mimetype
246
- // through without forcing it to mp4. When the prop is absent we leave the existing
247
- // mimetype as-is (defaults are set further below).
248
- if (mediaType === 'video' && uploadData.mimetype) {
249
- const isHevc = uploadData.mimetype.includes('hevc') || uploadData.mimetype.includes('x-matroska')
250
- if (isHevc && !getMediaProp('hevc_video_dual_upload')) {
251
- // Prop not enabled: normalise to mp4 so receivers can decode it.
252
- uploadData.mimetype = MIMETYPE_MAP['video']
253
- }
254
- // If the prop is enabled we leave the caller-supplied HEVC mimetype intact.
255
- }
256
-
257
248
  // check if cacheable + generate cache key
258
249
  const cacheableKey =
259
250
  typeof uploadData.media === 'object' &&
@@ -271,7 +262,7 @@ const prepareWAMessageMedia = async (message, options) => {
271
262
  const mediaBuff = await options.mediaCache.get(cacheableKey)
272
263
  if (mediaBuff) {
273
264
  logger?.debug({ cacheableKey }, 'got media cache hit')
274
- const obj = WAProto_1.proto.Message.decode(mediaBuff)
265
+ const obj = index_js_1.proto.Message.decode(mediaBuff)
275
266
  const key = `${mediaType}Message`
276
267
  Object.assign(obj[key], { ...uploadData, media: undefined })
277
268
  return obj
@@ -384,27 +375,6 @@ const prepareWAMessageMedia = async (message, options) => {
384
375
  logger?.warn('failed to remove tmp file')
385
376
  }
386
377
  })
387
-
388
- // Feature A: HD image dual upload — set mediaHd flag when prop is enabled and caller
389
- // explicitly requests HD quality via uploadData.hd === true.
390
- const hdImageEnabled = mediaType === 'image' && getMediaProp('hd_image_dual_upload') && !!uploadData.hd
391
- const hdVideoEnabled = mediaType === 'video' && getMediaProp('hd_video_dual_upload') && !!uploadData.hd
392
- if (hdImageEnabled || hdVideoEnabled) {
393
- uploadData.mediaHd = true
394
- logger?.debug({ mediaType }, 'HD dual upload enabled, setting mediaHd flag')
395
- }
396
-
397
- // Feature C: Motion photo — propagate motionPhoto field for image messages when present.
398
- // The caller sets message.motionPhoto = true (or a Buffer for the motion data).
399
- if (mediaType === 'image' && uploadData.motionPhoto != null) {
400
- // Keep motionPhoto in uploadData; it will be spread into the proto below.
401
- // If it is a boolean we coerce to 1/0 for the proto field (uint32 in WA proto).
402
- if (typeof uploadData.motionPhoto === 'boolean') {
403
- uploadData.motionPhoto = uploadData.motionPhoto ? 1 : 0
404
- }
405
- logger?.debug('motion photo field set on image message')
406
- }
407
-
408
378
  const obj = WAProto_1.proto.Message.fromObject({
409
379
  [`${mediaType}Message`]: MessageTypeProto[mediaType].fromObject({
410
380
  url: mediaUrl,
@@ -456,7 +426,7 @@ const generateForwardMessageContent = (message, forceForward) => {
456
426
  }
457
427
  // hacky copy
458
428
  content = (0, exports.normalizeMessageContent)(content)
459
- 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())
460
430
  let key = Object.keys(content)[0]
461
431
  let score = content?.[key]?.contextInfo?.forwardingScore || 0
462
432
  score += message.key.fromMe && !forceForward ? 0 : 1
@@ -684,19 +654,15 @@ const generateWAMessageContent = async (message, options) => {
684
654
  m.groupInviteMessage.caption = message.groupInvite.text
685
655
  m.groupInviteMessage.groupJid = message.groupInvite.jid
686
656
  m.groupInviteMessage.groupName = message.groupInvite.subject
657
+ //TODO: use built-in interface and get disappearing mode info etc.
658
+ //TODO: cache / use store!?
687
659
  if (options.getProfilePicUrl) {
688
- const cached = groupInviteThumbnailCache.get(message.groupInvite.jid)
689
- if (cached) {
690
- m.groupInviteMessage.jpegThumbnail = cached
691
- } else {
692
- const pfpUrl = await options.getProfilePicUrl(message.groupInvite.jid, 'preview')
693
- if (pfpUrl) {
694
- const resp = await fetch(pfpUrl, { method: 'GET', dispatcher: options?.options?.dispatcher })
695
- if (resp.ok) {
696
- const buf = Buffer.from(await resp.arrayBuffer())
697
- m.groupInviteMessage.jpegThumbnail = buf
698
- groupInviteThumbnailCache.set(message.groupInvite.jid, buf)
699
- }
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
700
666
  }
701
667
  }
702
668
  }
@@ -720,7 +686,7 @@ const generateWAMessageContent = async (message, options) => {
720
686
  m.buttonsResponseMessage = {
721
687
  selectedButtonId: message.buttonReply.id,
722
688
  selectedDisplayText: message.buttonReply.displayText,
723
- type: WAProto_1.proto.Message.ButtonsResponseMessage.Type.DISPLAY_TEXT
689
+ type: index_js_1.proto.Message.ButtonsResponseMessage.Type.DISPLAY_TEXT
724
690
  }
725
691
  break
726
692
  case 'interactive':
@@ -814,12 +780,7 @@ const generateWAMessageContent = async (message, options) => {
814
780
  ...(message.poll.hideParticipantName !== undefined
815
781
  ? { hideParticipantName: !!message.poll.hideParticipantName }
816
782
  : {}),
817
- ...(message.poll.allowAddOption !== undefined ? { allowAddOption: !!message.poll.allowAddOption } : {}),
818
- // Feature D: media_poll — allow poll messages to carry a media attachment when the
819
- // AB prop is enabled and the caller supplies message.poll.mediaAttachment.
820
- ...((0, messages_media_1.getMediaProp)(options.mediaAbProps, 'media_poll') && message.poll.mediaPoll != null
821
- ? { mediaPoll: !!message.poll.mediaPoll }
822
- : {})
783
+ ...(message.poll.allowAddOption !== undefined ? { allowAddOption: !!message.poll.allowAddOption } : {})
823
784
  }
824
785
  if (message.poll.version === 6 || message.poll.v6) {
825
786
  m.pollCreationMessageV6 = pollCreationMessage
@@ -962,13 +923,13 @@ const generateWAMessageContent = async (message, options) => {
962
923
  }
963
924
  } else if ((0, exports.hasNonNullishProperty)(message, 'sharePhoneNumber')) {
964
925
  m.protocolMessage = {
965
- type: WAProto_1.proto.Message.ProtocolMessage.Type.SHARE_PHONE_NUMBER
926
+ type: index_js_1.proto.Message.ProtocolMessage.Type.SHARE_PHONE_NUMBER
966
927
  }
967
928
  } else if ((0, exports.hasNonNullishProperty)(message, 'requestPhoneNumber')) {
968
929
  m.requestPhoneNumberMessage = {}
969
930
  } else if ((0, exports.hasNonNullishProperty)(message, 'limitSharing')) {
970
931
  m.protocolMessage = {
971
- type: WAProto_1.proto.Message.ProtocolMessage.Type.LIMIT_SHARING,
932
+ type: index_js_1.proto.Message.ProtocolMessage.Type.LIMIT_SHARING,
972
933
  limitSharing: {
973
934
  sharingLimited: message.limitSharing === true,
974
935
  trigger: 1,
@@ -1071,7 +1032,7 @@ const generateWAMessageContent = async (message, options) => {
1071
1032
  m = { interactiveMessage }
1072
1033
  } else if ('templateButtons' in message && !!message.templateButtons) {
1073
1034
  const msg = {
1074
- hydratedButtons: message.templateButtons
1035
+ hydratedButtons: message.hasOwnProperty('templateButtons') ? message.templateButtons : message.templateButtons
1075
1036
  }
1076
1037
  if ('text' in message) {
1077
1038
  msg.hydratedContentText = message.text
@@ -1128,42 +1089,73 @@ const generateWAMessageContent = async (message, options) => {
1128
1089
  }
1129
1090
  }
1130
1091
  if ('interactiveButtons' in message && !!message.interactiveButtons) {
1131
- const interactiveMessage = {
1132
- nativeFlowMessage: WAProto_1.proto.Message.InteractiveMessage.NativeFlowMessage.fromObject({
1133
- buttons: message.interactiveButtons
1134
- })
1135
- }
1136
- if ('text' in message) {
1137
- interactiveMessage.body = {
1138
- text: message.text
1139
- }
1140
- } else if ('caption' in message) {
1141
- interactiveMessage.body = {
1142
- text: message.caption
1143
- }
1144
- interactiveMessage.header = {
1145
- title: message.title,
1146
- subtitle: message.subtitle,
1147
- hasMediaAttachment: Boolean(message.hasMediaAttachment)
1148
- }
1149
- Object.assign(interactiveMessage.header, m)
1150
- }
1151
- if ('footer' in message && !!message.footer) {
1152
- interactiveMessage.footer = {
1153
- text: message.footer
1154
- }
1155
- }
1156
- if ('title' in message && !!message.title) {
1157
- interactiveMessage.header = {
1158
- title: message.title,
1159
- subtitle: message.subtitle,
1160
- hasMediaAttachment: Boolean(message.hasMediaAttachment)
1161
- }
1162
- Object.assign(interactiveMessage.header, m)
1163
- }
1164
- applyContextInfoAndMentions(interactiveMessage, message)
1165
- m = { interactiveMessage }
1166
- }
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
+ }
1167
1159
  if ('shop' in message && !!message.shop) {
1168
1160
  const interactiveMessage = {
1169
1161
  shopStorefrontMessage: WAProto_1.proto.Message.InteractiveMessage.ShopMessage.fromObject({
@@ -1240,12 +1232,22 @@ const generateWAMessageContent = async (message, options) => {
1240
1232
  }
1241
1233
  })
1242
1234
  }
1243
- 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
+ }
1244
1244
  sections.push({
1245
1245
  view_model: {
1246
1246
  primitive: {
1247
- rows: table.rows.map(row => ({
1248
- 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
1249
1251
  })),
1250
1252
  __typename: 'GenAITableUXPrimitive'
1251
1253
  },
@@ -1876,10 +1878,9 @@ const generateWAMessageContent = async (message, options) => {
1876
1878
  ...existing,
1877
1879
  botThreadInfo: {
1878
1880
  clientInfo: {
1879
- type:
1880
- typeof bt.type === 'string'
1881
- ? (threadTypeMap[bt.type.toLowerCase()] ?? threadTypeEnum.UNKNOWN)
1882
- : (bt.type ?? threadTypeEnum.UNKNOWN),
1881
+ type: typeof bt.type === 'string'
1882
+ ? (threadTypeMap[bt.type.toLowerCase()] ?? threadTypeEnum.UNKNOWN)
1883
+ : (bt.type ?? threadTypeEnum.UNKNOWN),
1883
1884
  sourceChatJid: bt.sourceChatJid || ''
1884
1885
  }
1885
1886
  }
@@ -2280,7 +2281,7 @@ const generateWAMessageContent = async (message, options) => {
2280
2281
  m.messageContextInfo = {
2281
2282
  ...(m.messageContextInfo || {}),
2282
2283
  messageAddOnDurationInSecs: message.afterReadDuration,
2283
- messageAddOnExpiryType: WAProto_1.proto.MessageContextInfo.MessageAddonExpiryType.DEPENDENT_ON_PARENT
2284
+ messageAddOnExpiryType: index_js_1.proto.MessageContextInfo.MessageAddonExpiryType.DEPENDENT_ON_PARENT
2284
2285
  }
2285
2286
  }
2286
2287
  if ('raw' in message && !!message.raw) {
@@ -2425,7 +2426,7 @@ const generateWAMessageFromContent = (jid, message, options) => {
2425
2426
  let quotedMsg = (0, exports.normalizeMessageContent)(quoted.message)
2426
2427
  const msgType = (0, exports.getContentType)(quotedMsg)
2427
2428
  // strip any redundant properties
2428
- quotedMsg = WAProto_1.proto.Message.create({ [msgType]: quotedMsg[msgType] })
2429
+ quotedMsg = index_js_1.proto.Message.create({ [msgType]: quotedMsg[msgType] })
2429
2430
  const quotedContent = quotedMsg[msgType]
2430
2431
  if (typeof quotedContent === 'object' && quotedContent && 'contextInfo' in quotedContent) {
2431
2432
  delete quotedContent.contextInfo
@@ -2462,11 +2463,20 @@ const generateWAMessageFromContent = (jid, message, options) => {
2462
2463
  }
2463
2464
  }
2464
2465
  message = WAProto_1.proto.Message.create(message)
2466
+ const iosBros = options?.browser?.[0] === 'iOS' || options?.browser?.[1] === 'Safari'
2467
+ const androidBros = options?.browser?.[0] === 'Android'
2468
+ const pickedMessageId =
2469
+ options?.messageId ||
2470
+ (iosBros
2471
+ ? (0, generics_1.generateIOSMessageID)()
2472
+ : androidBros
2473
+ ? (0, generics_1.generateAndroMessageID)()
2474
+ : (0, generics_1.generateMessageIDV2)(userJid))
2465
2475
  const messageJSON = {
2466
2476
  key: {
2467
2477
  remoteJid: jid,
2468
2478
  fromMe: true,
2469
- id: options?.messageId || (0, generics_1.generateMessageIDV2)()
2479
+ id: pickedMessageId
2470
2480
  },
2471
2481
  message: message,
2472
2482
  messageTimestamp: timestamp,
@@ -2488,3 +2498,407 @@ const generateWAMessage = async (jid, content, options) => {
2488
2498
  )
2489
2499
  }
2490
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