@crysnovax/baileys 2.5.5 → 2.5.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,6 +2,7 @@ import { Boom } from '@hapi/boom';
2
2
  import { randomBytes } from 'crypto';
3
3
  import { zip } from 'fflate';
4
4
  import { promises as fs } from 'fs';
5
+ import {} from 'stream';
5
6
  import { proto } from '../../WAProto/index.js';
6
7
  import { CALL_AUDIO_PREFIX, CALL_VIDEO_PREFIX, DONATE_URL, LIBRARY_NAME, MEDIA_KEYS, URL_REGEX, WA_DEFAULT_EPHEMERAL } from '../Defaults/index.js';
7
8
  import { AssociationType, ButtonHeaderType, ButtonType, CarouselCardType, ListType, ProtocolType, WAMessageStatus, WAProto } from '../Types/index.js';
@@ -11,7 +12,6 @@ import { generateMessageIDV2, getKeyAuthor, unixTimestampSeconds } from './gener
11
12
  import { downloadContentFromMessage, encryptedStream, generateThumbnail, getAudioDuration, getAudioWaveform, getImageProcessingLibrary, getRawMediaUploadData, getStream, toBuffer } from './messages-media.js';
12
13
  import { prepareRichResponseMessage } from './rich-message-utils.js';
13
14
  import { shouldIncludeReportingToken } from './reporting-utils.js';
14
- const CONCURRENCY_LIMIT = 15;
15
15
  const MIMETYPE_MAP = {
16
16
  image: 'image/jpeg',
17
17
  video: 'video/mp4',
@@ -36,7 +36,7 @@ const mediaAnnotation = [
36
36
  { x: 20.840980529785156, y: -47.80188751220703 }
37
37
  ],
38
38
  newsletter: {
39
- // crysnovax@Note 03-02-26 --- You can change jid, message id, and name via .env (⁠≧⁠▽⁠≦⁠)
39
+ // Lia@Note 03-02-26 --- You can change jid, message id, and name via .env (⁠≧⁠▽⁠≦⁠)
40
40
  newsletterJid: process.env.NEWSLETTER_ID ||
41
41
  '120363402922206865@newsletter',
42
42
  serverMessageId: process.env.NEWSLETTER_MESSAGE_ID ||
@@ -106,9 +106,9 @@ export const prepareWAMessageMedia = async (message, options) => {
106
106
  'url' in uploadData.media &&
107
107
  !!uploadData.media.url &&
108
108
  !!options.mediaCache &&
109
- mediaType + ':' + uploadData.media.url.toString();
109
+ mediaType + ':' + uploadData.media.url;
110
110
  if (mediaType === 'document' && !uploadData.fileName) {
111
- uploadData.fileName = 'file';
111
+ uploadData.fileName = LIBRARY_NAME;
112
112
  }
113
113
  if (!uploadData.mimetype) {
114
114
  uploadData.mimetype = MIMETYPE_MAP[mediaType];
@@ -123,13 +123,13 @@ export const prepareWAMessageMedia = async (message, options) => {
123
123
  return obj;
124
124
  }
125
125
  }
126
- const isNewsletter = !!options.jid && isJidNewsletter(options.jid);
126
+ const isNewsletter = isJidNewsletter(options.jid);
127
127
  const requiresDurationComputation = mediaType === 'audio' && typeof uploadData.seconds === 'undefined';
128
128
  const requiresThumbnailComputation = (mediaType === 'image' || mediaType === 'video') && typeof uploadData.jpegThumbnail === 'undefined';
129
129
  const requiresWaveformProcessing = mediaType === 'audio' && uploadData.ptt === true && typeof uploadData.waveform === 'undefined';
130
130
  const requiresAudioBackground = options.backgroundColor && mediaType === 'audio' && uploadData.ptt === true;
131
131
  const requiresOriginalForSomeProcessing = requiresDurationComputation || requiresThumbnailComputation || requiresWaveformProcessing;
132
- // crysnovax@Changes 06-02-26 --- Add few support for sending media to newsletter (⁠≧⁠▽⁠≦⁠)
132
+ // Lia@Changes 06-02-26 --- Add few support for sending media to newsletter (⁠≧⁠▽⁠≦⁠)
133
133
  if (isNewsletter) {
134
134
  logger?.info({ key: cacheableKey }, 'Preparing raw media for newsletter');
135
135
  const { filePath, fileSha256, fileLength } = await getRawMediaUploadData(uploadData.media, options.mediaTypeOverride || mediaType, logger);
@@ -285,33 +285,36 @@ export const prepareDisappearingMessageSettingContent = (ephemeralExpiration) =>
285
285
  ephemeralMessage: {
286
286
  message: {
287
287
  protocolMessage: {
288
- type: WAProto.Message.ProtocolMessage.Type.EPHEMERAL_SETTING,
288
+ type: ProtocolType.EPHEMERAL_SETTING,
289
289
  ephemeralExpiration
290
290
  }
291
291
  }
292
292
  }
293
293
  };
294
- return WAProto.Message.fromObject(content);
294
+ return content;
295
295
  };
296
- // crysnovax@Changes 31-01-26 --- Extract product message into a standalone function so it can also be reused as the header for interactive messages
296
+ // Lia@Changes 31-01-26 --- Extract product message into a standalone function so it can also be reused as the header for interactive messages
297
297
  const prepareProductMessage = async (message, options) => {
298
298
  if (!message.businessOwnerJid) {
299
299
  throw new Boom('"businessOwnerJid" is missing from the content', { statusCode: 400 });
300
300
  }
301
301
  const { imageMessage } = await prepareWAMessageMedia({ image: message.image || message.product.productImage }, options);
302
- // crysnovax@Changes 01-02-26 --- Add product message default value
303
- const { image, ...content } = message;
304
- content.product = {
305
- currencyCode: 'IDR',
306
- priceAmount1000: 1000,
307
- title: LIBRARY_NAME,
308
- ...message.product,
309
- productImage: imageMessage
302
+ // Lia@Changes 01-02-26 --- Add product message default value
303
+ const content = {
304
+ ...message,
305
+ product: {
306
+ currencyCode: 'IDR',
307
+ priceAmount1000: 1000,
308
+ title: LIBRARY_NAME,
309
+ ...message.product,
310
+ productImage: imageMessage
311
+ }
310
312
  };
313
+ delete content.image;
311
314
  return content;
312
315
  };
313
316
  /**
314
- * crysnovax@Note 30-01-26
317
+ * Lia@Note 30-01-26
315
318
  * ---
316
319
  * Credits: Work on ensuring stickerPackMessage fields are valid by @jlucaso1 (https://github.com/jlucaso1).
317
320
  * based on https://github.com/WhiskeySockets/Baileys/pull/1561
@@ -327,7 +330,7 @@ const prepareStickerPackMessage = async (message, options) => {
327
330
  if (!cover) {
328
331
  throw new Boom('Sticker pack must contain a cover', { statusCode: 400 });
329
332
  }
330
- // crysnovax@Changes 01-02-26 --- Add caching for sticker pack (similiar to prepareWAMessageMedia)
333
+ // Lia@Changes 01-02-26 --- Add caching for sticker pack (similiar to prepareWAMessageMedia)
331
334
  const cacheableKey = Array.isArray(stickers) &&
332
335
  stickers.length &&
333
336
  !!options.mediaCache &&
@@ -526,18 +529,18 @@ const prepareStickerPackMessage = async (message, options) => {
526
529
  }
527
530
  return WAProto.Message.StickerPackMessage.fromObject(content);
528
531
  };
529
- // crysnovax@Changes 30-01-26 --- Add native flow button helper for interactive message
532
+ // Lia@Changes 30-01-26 --- Add native flow button helper for interactive message
530
533
  const prepareNativeFlowButtons = (message) => {
531
- const buttons = message.nativeFlow;
534
+ const buttons = message.nativeFlow
532
535
  const isButtonsFieldArray = Array.isArray(buttons);
533
536
  const correctedField = isButtonsFieldArray ? buttons : buttons.buttons;
534
537
  const messageParamsJson = {};
535
- // crysnovax@Changes 31-01-26 --- Add offer and options inside interactive message
538
+ // Lia@Changes 31-01-26 --- Add offer and options inside interactive message
536
539
  if (hasOptionalProperty(message, 'offerText') && !!message.offerText) {
537
540
  Object.assign(messageParamsJson, {
538
541
  limited_time_offer: {
539
542
  text: message.offerText || LIBRARY_NAME,
540
- url: message.offerUrl || DONATE_URL, // crysnovax@Note 02-02-26 --- Apologies if this feels cheeky, just a fallback
543
+ url: message.offerUrl || DONATE_URL, // Lia@Note 02-02-26 --- Apologies if this feels cheeky, just a fallback
541
544
  copy_code: message.offerCode,
542
545
  expiration_time: message.offerExpiration
543
546
  }
@@ -587,7 +590,6 @@ const prepareNativeFlowButtons = (message) => {
587
590
  display_text: buttonText || '🌐 Visit',
588
591
  url: button.url,
589
592
  merchant_url: button.url,
590
- webview_interaction: button.useWebview,
591
593
  icon: buttonIcon
592
594
  })
593
595
  };
@@ -602,7 +604,7 @@ const prepareNativeFlowButtons = (message) => {
602
604
  })
603
605
  };
604
606
  }
605
- // crysnovax@Changes 12-03-26 --- Add "single_select" shortcut \⁠(⁠°⁠o⁠°⁠)⁠/
607
+ // Lia@Changes 12-03-26 --- Add "single_select" shortcut \⁠(⁠°⁠o⁠°⁠)⁠/
606
608
  else if (hasOptionalProperty(button, 'sections') && !!button.sections) {
607
609
  return {
608
610
  name: 'single_select',
@@ -625,7 +627,7 @@ const prepareNativeFlowButtons = (message) => {
625
627
  * @param options.forceForward will show the message as forwarded even if it is from you
626
628
  */
627
629
  export const generateForwardMessageContent = (message, forceForward) => {
628
- let content = message.message;
630
+ let content = message.message || message;
629
631
  if (!content) {
630
632
  throw new Boom('no content in message', { statusCode: 400 });
631
633
  }
@@ -673,35 +675,36 @@ export const hasOptionalProperty = (obj, key) => {
673
675
  key in obj &&
674
676
  obj[key] != null;
675
677
  };
676
- // crysnovax@Changes 06-02-26 --- Validate album message media to avoid bug 👀
678
+ // Lia@Changes 06-02-26 --- Validate album message media to avoid bug 👀
677
679
  export const hasValidAlbumMedia = (message) => {
678
- return !!(message.imageMessage ||
679
- message.videoMessage);
680
+ return message.imageMessage ||
681
+ message.videoMessage;
680
682
  };
681
683
  export const hasValidInteractiveHeader = (message) => {
682
- return !!(message.imageMessage ||
684
+ return message.imageMessage ||
683
685
  message.videoMessage ||
684
686
  message.documentMessage ||
685
687
  message.productMessage ||
686
- message.locationMessage);
688
+ message.locationMessage;
687
689
  };
688
- // crysnovax@Changes 30-01-26 --- Validate carousel cards header to avoid bug 👀
690
+ // Lia@Changes 30-01-26 --- Validate carousel cards header to avoid bug 👀
689
691
  export const hasValidCarouselHeader = (message) => {
690
- return !!(message.imageMessage ||
692
+ return message.imageMessage ||
691
693
  message.videoMessage ||
692
- message.productMessage);
694
+ message.productMessage;
693
695
  };
694
696
  export const generateWAMessageContent = async (message, options) => {
695
697
  var _a, _b;
696
698
  let m = {};
697
- // crysnovax@Changes 30-01-26 --- Add "raw" boolean to send raw messages directly via generateWAMessage()
699
+ // Lia@Changes 30-01-26 --- Add "raw" boolean to send raw messages directly via generateWAMessage()
698
700
  if (hasNonNullishProperty(message, 'raw')) {
699
701
  delete message.raw;
700
702
  return message;
701
703
  }
702
- // crysnovax@Changes 09-04-26 --- Add support for code block and table with richResponseMessage
704
+ // Lia@Changes 09-04-26 --- Add support for code block, latex, reels carousel, table with richResponseMessage
703
705
  else if (hasNonNullishProperty(message, 'code') ||
704
- hasNonNullishProperty(message, 'links') ||
706
+ hasNonNullishProperty(message, 'expressions') ||
707
+ hasNonNullishProperty(message, 'items') ||
705
708
  hasNonNullishProperty(message, 'table') ||
706
709
  hasNonNullishProperty(message, 'richResponse')) {
707
710
  m = prepareRichResponseMessage(message);
@@ -717,8 +720,7 @@ export const generateWAMessageContent = async (message, options) => {
717
720
  extContent.jpegThumbnail = urlInfo.jpegThumbnail;
718
721
  extContent.description = urlInfo.description;
719
722
  extContent.title = urlInfo.title;
720
- extContent.previewType = urlInfo.previewType ?? 0;
721
- extContent.linkPreviewMetadata = urlInfo.linkPreviewMetadata;
723
+ extContent.previewType = 0;
722
724
  const img = urlInfo.highQualityThumbnail;
723
725
  if (img) {
724
726
  extContent.thumbnailDirectPath = img.directPath;
@@ -730,21 +732,6 @@ export const generateWAMessageContent = async (message, options) => {
730
732
  extContent.thumbnailEncSha256 = img.fileEncSha256;
731
733
  }
732
734
  }
733
- const faviconData = message.favicon;
734
- if (faviconData && typeof options.upload === 'function') {
735
- const { imageMessage } = await prepareWAMessageMedia({
736
- image: faviconData
737
- }, options);
738
- extContent.faviconMMSMetadata = {
739
- thumbnailDirectPath: imageMessage.directPath,
740
- mediaKey: imageMessage.mediaKey,
741
- mediaKeyTimestamp: imageMessage.mediaKeyTimestamp,
742
- thumbnailWidth: 32,
743
- thumbnailHeight: 32,
744
- thumbnailSha256: imageMessage.fileSha256,
745
- thumbnailEncSha256: imageMessage.fileEncSha256
746
- };
747
- }
748
735
  if (options.backgroundColor) {
749
736
  extContent.backgroundArgb = await assertColor(options.backgroundColor);
750
737
  }
@@ -759,25 +746,25 @@ export const generateWAMessageContent = async (message, options) => {
759
746
  throw new Boom('require atleast 1 contact', { statusCode: 400 });
760
747
  }
761
748
  if (contactLen === 1) {
762
- m.contactMessage = WAProto.Message.ContactMessage.create(message.contacts.contacts[0]);
749
+ m.contactMessage = message.contacts.contacts[0];
763
750
  }
764
751
  else {
765
- m.contactsArrayMessage = WAProto.Message.ContactsArrayMessage.create(message.contacts);
752
+ m.contactsArrayMessage = message.contacts;
766
753
  }
767
754
  }
768
755
  else if (hasNonNullishProperty(message, 'location')) {
769
- m.locationMessage = WAProto.Message.LocationMessage.create(message.location);
756
+ m.locationMessage = message.location;
770
757
  }
771
758
  else if (hasNonNullishProperty(message, 'react')) {
772
759
  if (!message.react.senderTimestampMs) {
773
760
  message.react.senderTimestampMs = Date.now();
774
761
  }
775
- m.reactionMessage = WAProto.Message.ReactionMessage.create(message.react);
762
+ m.reactionMessage = message.react;
776
763
  }
777
764
  else if (hasNonNullishProperty(message, 'delete')) {
778
765
  m.protocolMessage = {
779
766
  key: message.delete,
780
- type: WAProto.Message.ProtocolMessage.Type.REVOKE
767
+ type: ProtocolType.REVOKE
781
768
  };
782
769
  }
783
770
  else if (hasNonNullishProperty(message, 'forward')) {
@@ -907,26 +894,27 @@ export const generateWAMessageContent = async (message, options) => {
907
894
  statusCode: 400
908
895
  });
909
896
  }
897
+ m.messageContextInfo = {
898
+ // encKey
899
+ messageSecret: message.poll.messageSecret || randomBytes(32)
900
+ };
910
901
  const pollCreationMessage = {
911
902
  name: message.poll.name,
912
903
  selectableOptionsCount: message.poll.selectableCount,
913
- options: message.poll.values.map(optionName => ({ optionName })),
914
- endTime: message.poll.endDate ? message.poll.endDate.getTime() : undefined,
915
- hideParticipantName: message.poll.hideVoter ?? false,
916
- allowAddOption: message.poll.canAddOption ?? false
904
+ options: message.poll.values.map(optionName => ({ optionName }))
917
905
  };
918
906
  if (message.poll.toAnnouncementGroup) {
919
907
  // poll v2 is for community announcement groups (single select and multiple)
920
908
  m.pollCreationMessageV2 = pollCreationMessage;
921
909
  }
922
910
  else {
923
- // crysnovax@Changes 08-02-26 --- Add quiz message support
924
- if (message.poll.pollType === 1) {
911
+ // Lia@Changes 08-02-26 --- Add quiz message support
912
+ if (message.poll.pollType == 1) {
925
913
  if (!message.poll.correctAnswer) {
926
914
  throw new Boom('No "correctAnswer" provided for quiz', { statusCode: 400 });
927
915
  }
928
916
  m.pollCreationMessageV5 = {
929
- // crysnovax@Note 08-02-26 --- quiz for newsletter only
917
+ // Lia@Note 08-02-26 --- quiz for newsletter only
930
918
  ...pollCreationMessage,
931
919
  correctAnswer: {
932
920
  optionName: message.poll.correctAnswer.toString()
@@ -944,12 +932,8 @@ export const generateWAMessageContent = async (message, options) => {
944
932
  m.pollCreationMessage = pollCreationMessage;
945
933
  }
946
934
  }
947
- m.messageContextInfo = {
948
- // encKey
949
- messageSecret: message.poll.messageSecret || randomBytes(32)
950
- };
951
935
  }
952
- // crysnovax@Changes 08-02-26 --- Add poll result snapshot message
936
+ // Lia@Changes 08-02-26 --- Add poll result snapshot message
953
937
  else if (hasNonNullishProperty(message, 'pollResult')) {
954
938
  const pollResultSnapshotMessage = {
955
939
  name: message.pollResult.name,
@@ -958,7 +942,7 @@ export const generateWAMessageContent = async (message, options) => {
958
942
  optionVoteCount: parseInt(vote.voteCount)
959
943
  }))
960
944
  };
961
- if (message.pollResult.pollType === 1) {
945
+ if (message.pollResult.pollType == 1) {
962
946
  pollResultSnapshotMessage.pollType = proto.Message.PollType.QUIZ;
963
947
  m.pollResultSnapshotMessageV3 = pollResultSnapshotMessage;
964
948
  }
@@ -967,7 +951,7 @@ export const generateWAMessageContent = async (message, options) => {
967
951
  m.pollResultSnapshotMessage = pollResultSnapshotMessage;
968
952
  }
969
953
  }
970
- // crysnovax@Changes 08-02-26 --- Add poll update message
954
+ // Lia@Changes 08-02-26 --- Add poll update message
971
955
  else if (hasNonNullishProperty(message, 'pollUpdate')) {
972
956
  if (!message.pollUpdate.key) {
973
957
  throw new Boom('Message key is required', { statusCode: 400 });
@@ -984,7 +968,7 @@ export const generateWAMessageContent = async (message, options) => {
984
968
  }
985
969
  else if (hasNonNullishProperty(message, 'sharePhoneNumber')) {
986
970
  m.protocolMessage = {
987
- type: proto.Message.ProtocolMessage.Type.SHARE_PHONE_NUMBER
971
+ type: ProtocolType.SHARE_PHONE_NUMBER
988
972
  };
989
973
  }
990
974
  else if (hasNonNullishProperty(message, 'requestPhoneNumber')) {
@@ -992,7 +976,7 @@ export const generateWAMessageContent = async (message, options) => {
992
976
  }
993
977
  else if (hasNonNullishProperty(message, 'limitSharing')) {
994
978
  m.protocolMessage = {
995
- type: proto.Message.ProtocolMessage.Type.LIMIT_SHARING,
979
+ type: ProtocolType.LIMIT_SHARING,
996
980
  limitSharing: {
997
981
  sharingLimited: message.limitSharing === true,
998
982
  trigger: 1,
@@ -1001,14 +985,14 @@ export const generateWAMessageContent = async (message, options) => {
1001
985
  }
1002
986
  };
1003
987
  }
1004
- // crysnovax@Changes 01-02-26 --- Add payment invite message
988
+ // Lia@Changes 01-02-26 --- Add payment invite message
1005
989
  else if (hasNonNullishProperty(message, 'paymentInviteServiceType')) {
1006
990
  m.paymentInviteMessage = {
1007
991
  expiryTimestamp: Date.now(),
1008
992
  serviceType: message.paymentInviteServiceType
1009
993
  };
1010
994
  }
1011
- // crysnovax@Changes 01-02-26 --- Add order message
995
+ // Lia@Changes 01-02-26 --- Add order message
1012
996
  else if (hasNonNullishProperty(message, 'orderText')) {
1013
997
  if (!Buffer.isBuffer(message.thumbnail)) {
1014
998
  throw new Boom('Must provide thumbnail buffer in order message', { statusCode: 400 });
@@ -1027,21 +1011,19 @@ export const generateWAMessageContent = async (message, options) => {
1027
1011
  };
1028
1012
  delete m.orderMessage.orderText;
1029
1013
  }
1030
- // crysnovax@Changes 31-01-26 --- Add support for album messages
1014
+ // Lia@Changes 31-01-26 --- Add support for album messages
1031
1015
  else if (hasNonNullishProperty(message, 'album')) {
1032
1016
  if (!Array.isArray(message.album)) {
1033
1017
  throw new Boom('Invalid album type. Expected an array.', { statusCode: 400 });
1034
1018
  }
1035
1019
  let videoCount = 0;
1036
1020
  for (let i = 0; i < message.album.length; i++) {
1037
- if (message.album[i].video)
1038
- videoCount++;
1039
- }
1021
+ if (message.album[i].video) videoCount++;
1022
+ };
1040
1023
  let imageCount = 0;
1041
1024
  for (let i = 0; i < message.album.length; i++) {
1042
- if (message.album[i].image)
1043
- imageCount++;
1044
- }
1025
+ if (message.album[i].image) imageCount++;
1026
+ };
1045
1027
  if ((videoCount + imageCount) < 2) {
1046
1028
  throw new Boom('Minimum provide 2 media to upload album message', { statusCode: 400 });
1047
1029
  }
@@ -1053,11 +1035,11 @@ export const generateWAMessageContent = async (message, options) => {
1053
1035
  else {
1054
1036
  m = await prepareWAMessageMedia(message, options);
1055
1037
  }
1056
- // crysnovax@Changes 30-01-26 --- Add interactive messages (buttonsMessage, listMessage, interactiveMessage, templateMessage, and carouselMessage)
1038
+ // Lia@Changes 30-01-26 --- Add interactive messages (buttonsMessage, listMessage, interactiveMessage, templateMessage, and carouselMessage)
1057
1039
  if (hasNonNullishProperty(message, 'buttons')) {
1058
1040
  const buttonsMessage = {
1059
1041
  buttons: message.buttons.map(button => {
1060
- // crysnovax@Changes 12-03-26 --- Add "single_select" shortcut!
1042
+ // Lia@Changes 12-03-26 --- Add "single_select" shortcut!
1061
1043
  const buttonText = button.text || button.buttonText;
1062
1044
  if (hasOptionalProperty(button, 'sections')) {
1063
1045
  return {
@@ -1115,7 +1097,7 @@ export const generateWAMessageContent = async (message, options) => {
1115
1097
  };
1116
1098
  m = { listMessage };
1117
1099
  }
1118
- // crysnovax@Note 03-02-26 --- This message type is shown on WhatsApp Web/Desktop and iOS (I guess 。⁠◕⁠‿⁠◕⁠。). On Android, it only appears in newsletter (so far ಥ⁠‿⁠ಥ)
1100
+ // Lia@Note 03-02-26 --- This message type is shown on WhatsApp Web/Desktop and iOS (I guess 。⁠◕⁠‿⁠◕⁠。). On Android, it only appears in newsletter (so far ಥ⁠‿⁠ಥ)
1119
1101
  else if (hasNonNullishProperty(message, 'templateButtons')) {
1120
1102
  const hydratedTemplate = {
1121
1103
  hydratedButtons: message.templateButtons.map((button, i) => {
@@ -1164,7 +1146,7 @@ export const generateWAMessageContent = async (message, options) => {
1164
1146
  if (hasOptionalProperty(message, 'footer')) {
1165
1147
  hydratedTemplate.hydratedFooterText = message.footer;
1166
1148
  }
1167
- hydratedTemplate.templateId = message.id || 'template-' + Date.now(); // crysnovax@Note 04-02-26 --- Minimal templateId to satisfy WhatsApp (⁠ ⁠ꈍ⁠ᴗ⁠ꈍ⁠)
1149
+ hydratedTemplate.templateId = message.id || 'template-' + Date.now(); // Lia@Note 04-02-26 --- Minimal templateId to satisfy WhatsApp (⁠ ⁠ꈍ⁠ᴗ⁠ꈍ⁠)
1168
1150
  m = {
1169
1151
  templateMessage: {
1170
1152
  hydratedFourRowTemplate: hydratedTemplate,
@@ -1236,9 +1218,9 @@ export const generateWAMessageContent = async (message, options) => {
1236
1218
  else {
1237
1219
  carouselHeader = await prepareWAMessageMedia(card, options).catch(() => ({ }));
1238
1220
  }
1239
- const isValidHeader = hasValidCarouselHeader(carouselHeader);
1221
+ const isValidHeader = hasValidCarouselHeader(carouselHeader)
1240
1222
  if (!isValidHeader) {
1241
- throw new Boom('Invalid media type for carousel card', { statusCode: 400 });
1223
+ throw new Boom('Invalid media type for carousel card', { statusCode: 400 });
1242
1224
  }
1243
1225
  const carouselCard = {
1244
1226
  nativeFlowMessage: prepareNativeFlowButtons(card.nativeFlow ? card : [])
@@ -1261,11 +1243,11 @@ export const generateWAMessageContent = async (message, options) => {
1261
1243
  Object.assign(carouselCard.header, carouselHeader);
1262
1244
  }
1263
1245
  if (hasOptionalProperty(card, 'audioFooter')) {
1264
- const { audioMessage } = await prepareWAMessageMedia({
1246
+ const parseFooter = await prepareWAMessageMedia({
1265
1247
  audio: card.audioFooter
1266
1248
  }, options);
1267
1249
  carouselCard.footer = {
1268
- audioMessage,
1250
+ audioMessage: parseFooter.audioMessage,
1269
1251
  hasMediaAttachment: true
1270
1252
  };
1271
1253
  }
@@ -1286,7 +1268,7 @@ export const generateWAMessageContent = async (message, options) => {
1286
1268
  }
1287
1269
  m = { interactiveMessage };
1288
1270
  }
1289
- // crysnovax@Changes 01-02-26 --- Add request payment message
1271
+ // Lia@Changes 01-02-26 --- Add request payment message
1290
1272
  else if (hasNonNullishProperty(message, 'requestPaymentFrom')) {
1291
1273
  const requestPaymentMessage = {
1292
1274
  amount: {
@@ -1310,7 +1292,7 @@ export const generateWAMessageContent = async (message, options) => {
1310
1292
  }
1311
1293
  m = { requestPaymentMessage };
1312
1294
  }
1313
- // crysnovax@Changes 01-02-26 --- Add invoice message
1295
+ // Lia@Changes 01-02-26 --- Add invoice message
1314
1296
  else if (hasNonNullishProperty(message, 'invoiceNote')) {
1315
1297
  const attachment = m.imageMessage || m.documentMessage;
1316
1298
  const type = Object.keys(m)[0].replace('Message', '').toUpperCase();
@@ -1336,7 +1318,7 @@ export const generateWAMessageContent = async (message, options) => {
1336
1318
  }
1337
1319
  m = { invoiceMessage };
1338
1320
  }
1339
- // crysnovax@Changes 31-01-26 --- Add direct externalAdReply access (no need to create contextInfo first)
1321
+ // Lia@Changes 31-01-26 --- Add direct externalAdReply access (no need to create contextInfo first)
1340
1322
  if (hasOptionalProperty(message, 'externalAdReply') && !!message.externalAdReply) {
1341
1323
  const messageType = Object.keys(m)[0];
1342
1324
  const key = m[messageType];
@@ -1345,7 +1327,7 @@ export const generateWAMessageContent = async (message, options) => {
1345
1327
  throw new Boom('Thumbnail must in buffer type', { statusCode: 400 });
1346
1328
  }
1347
1329
  if (!content.url || typeof content.url !== 'string') {
1348
- content.url = DONATE_URL; // crysnovax@Note 02-02-26 --- Apologies if this feels cheeky, just a fallback
1330
+ content.url = DONATE_URL; // Lia@Note 02-02-26 --- Apologies if this feels cheeky, just a fallback
1349
1331
  }
1350
1332
  const externalAdReply = {
1351
1333
  ...content,
@@ -1353,9 +1335,9 @@ export const generateWAMessageContent = async (message, options) => {
1353
1335
  mediaType: content.mediaType || 1,
1354
1336
  mediaUrl: content.url,
1355
1337
  renderLargerThumbnail: content.largeThumbnail,
1356
- sourceUrl: content.url,
1338
+ sourceUrl: content.url + '?update=' + Date.now(),
1357
1339
  thumbnail: content.thumbnail,
1358
- thumbnailUrl: content.url + '?update=' + Date.now(),
1340
+ thumbnailUrl: content.url,
1359
1341
  title: content.title || LIBRARY_NAME
1360
1342
  };
1361
1343
  delete externalAdReply.subTitle;
@@ -1372,21 +1354,18 @@ export const generateWAMessageContent = async (message, options) => {
1372
1354
  (hasOptionalProperty(message, 'mentionAll') && message.mentionAll)) {
1373
1355
  const messageType = Object.keys(m)[0];
1374
1356
  const key = m[messageType];
1375
- if (key && 'contextInfo' in key) {
1376
- key.contextInfo = key.contextInfo || {};
1377
- if (message.mentions?.length) {
1378
- key.contextInfo.mentionedJid = message.mentions;
1379
- }
1380
- if (message.mentionAll) {
1381
- key.contextInfo.nonJidMentions = 1;
1382
- }
1357
+ if ('contextInfo' in key && !!key.contextInfo) {
1358
+ key.contextInfo.mentionedJid = message.mentions || [];
1383
1359
  }
1384
1360
  else if (key) {
1385
1361
  key.contextInfo = {
1386
- mentionedJid: message.mentions,
1387
- nonJidMentions: message.mentionAll ? 1 : 0
1362
+ mentionedJid: message.mentions || []
1388
1363
  };
1389
1364
  }
1365
+ if (message.mentionAll) {
1366
+ key.contextInfo.mentionedJid = [];
1367
+ key.contextInfo.nonJidMentions = 1;
1368
+ }
1390
1369
  }
1391
1370
  if (hasOptionalProperty(message, 'contextInfo') && !!message.contextInfo) {
1392
1371
  const messageType = Object.keys(m)[0];
@@ -1398,7 +1377,7 @@ export const generateWAMessageContent = async (message, options) => {
1398
1377
  key.contextInfo = message.contextInfo;
1399
1378
  }
1400
1379
  }
1401
- // crysnovax@Changes 31-01-26 --- Add "groupStatus" boolean to set contextInfo.isGroupStatus and wrap message into groupStatusMessageV2
1380
+ // Lia@Changes 31-01-26 --- Add "groupStatus" boolean to set contextInfo.isGroupStatus and wrap message into groupStatusMessageV2
1402
1381
  if (hasOptionalProperty(message, 'groupStatus') && !!message.groupStatus) {
1403
1382
  const messageType = Object.keys(m)[0];
1404
1383
  const key = m[messageType];
@@ -1413,35 +1392,20 @@ export const generateWAMessageContent = async (message, options) => {
1413
1392
  m = { groupStatusMessageV2: { message: m } };
1414
1393
  delete message.groupStatus;
1415
1394
  }
1416
- // crysnovax@Changes 06-05-26 --- Add "spoiler" boolean to set contextInfo.isSpoiler and wrap message into spoilerMessage
1417
- if (hasOptionalProperty(message, 'spoiler') && !!message.spoiler) {
1418
- const messageType = Object.keys(m)[0];
1419
- const key = m[messageType];
1420
- if ('contextInfo' in key && !!key.contextInfo) {
1421
- key.contextInfo.isSpoiler = message.spoiler;
1422
- }
1423
- else if (key) {
1424
- key.contextInfo = {
1425
- isSpoiler: message.spoiler
1426
- };
1427
- }
1428
- m = { spoilerMessage: { message: m } };
1429
- delete message.spoiler;
1430
- }
1431
- // crysnovax@Changes 02-02-26 --- Add "interactiveAsTemplate" boolean to wrap interactiveMessage into templateMessage
1395
+ // Lia@Changes 02-02-26 --- Add "interactiveAsTemplate" boolean to wrap interactiveMessage into templateMessage
1432
1396
  else if (hasOptionalProperty(message, 'interactiveAsTemplate') && !!message.interactiveAsTemplate) {
1433
1397
  if (!m.interactiveMessage) {
1434
- throw new Boom('Invalid message type for template', { statusCode: 400 }); // crysnovax@Note 02-02-26 --- To avoid bug 👀
1398
+ throw new Boom('Invalid message type for template', { statusCode: 400 }); // Lia@Note 02-02-26 --- To avoid bug 👀
1435
1399
  }
1436
1400
  m = {
1437
1401
  templateMessage: {
1438
1402
  interactiveMessageTemplate: m.interactiveMessage,
1439
- templateId: message.id || 'template-' + Date.now() // crysnovax@Note 04-02-26 --- Minimal templateId to satisfy WhatsApp (⁠ ⁠ꈍ⁠ᴗ⁠ꈍ⁠)
1403
+ templateId: message.id || 'template-' + Date.now() // Lia@Note 04-02-26 --- Minimal templateId to satisfy WhatsApp (⁠ ⁠ꈍ⁠ᴗ⁠ꈍ⁠)
1440
1404
  }
1441
1405
  };
1442
1406
  delete message.interactiveAsTemplate;
1443
1407
  }
1444
- // crysnovax@Changes 30-01-26 --- Add "ephemeral" boolean to wrap message into ephemeralMessage like "viewOnce"
1408
+ // Lia@Changes 30-01-26 --- Add "ephemeral" boolean to wrap message into ephemeralMessage like "viewOnce"
1445
1409
  if (hasOptionalProperty(message, 'ephemeral') && !!message.ephemeral) {
1446
1410
  m = { ephemeralMessage: { message: m } };
1447
1411
  delete message.ephemeral;
@@ -1449,12 +1413,12 @@ export const generateWAMessageContent = async (message, options) => {
1449
1413
  else if (hasOptionalProperty(message, 'viewOnce') && !!message.viewOnce) {
1450
1414
  m = { viewOnceMessage: { message: m } };
1451
1415
  }
1452
- // crysnovax@Changes 03-02-26 --- Add "viewOnceV2" boolean to wrap message into viewOnceMessageV2 like "viewOnce"
1416
+ // Lia@Changes 03-02-26 --- Add "viewOnceV2" boolean to wrap message into viewOnceMessageV2 like "viewOnce"
1453
1417
  else if (hasOptionalProperty(message, 'viewOnceV2') && !!message.viewOnceV2) {
1454
1418
  m = { viewOnceMessageV2: { message: m } };
1455
1419
  delete message.viewOnceV2;
1456
1420
  }
1457
- // crysnovax@Changes 03-02-26 --- Add "viewOnceV2Extension" boolean to wrap message into viewOnceMessageV2Extension like "viewOnce"
1421
+ // Lia@Changes 03-02-26 --- Add "viewOnceV2Extension" boolean to wrap message into viewOnceMessageV2Extension like "viewOnce"
1458
1422
  else if (hasOptionalProperty(message, 'viewOnceV2Extension') && !!message.viewOnceV2Extension) {
1459
1423
  m = { viewOnceMessageV2Extension: { message: m } };
1460
1424
  delete message.viewOnceV2Extension;
@@ -1465,7 +1429,7 @@ export const generateWAMessageContent = async (message, options) => {
1465
1429
  key: message.edit,
1466
1430
  editedMessage: m,
1467
1431
  timestampMs: Date.now(),
1468
- type: WAProto.Message.ProtocolMessage.Type.MESSAGE_EDIT
1432
+ type: ProtocolType.MESSAGE_EDIT
1469
1433
  }
1470
1434
  }
1471
1435
  }
@@ -1475,13 +1439,13 @@ export const generateWAMessageContent = async (message, options) => {
1475
1439
  m.messageContextInfo.messageSecret = randomBytes(32);
1476
1440
  }
1477
1441
  }
1478
- return WAProto.Message.create(m);
1442
+ return proto.Message.create(m);
1479
1443
  };
1480
1444
  export const generateWAMessageFromContent = (jid, message, options) => {
1481
1445
  // set timestamp to now
1482
1446
  // if not specified
1483
1447
  if (!options.timestamp) {
1484
- options.timestamp = new Date();
1448
+ options.timestamp = Date.now();
1485
1449
  }
1486
1450
  const messageContextInfo = message.messageContextInfo
1487
1451
  const innerMessage = normalizeMessageContent(message);
@@ -1531,7 +1495,7 @@ export const generateWAMessageFromContent = (jid, message, options) => {
1531
1495
  //ephemeralSettingTimestamp: options.ephemeralOptions.eph_setting_ts?.toString()
1532
1496
  };
1533
1497
  }
1534
- // crysnovax@Changes 30-01-26 --- Add deviceListMetadata inside messageContextInfo for private chat
1498
+ // Lia@Changes 30-01-26 --- Add deviceListMetadata inside messageContextInfo for private chat
1535
1499
  if (messageContextInfo?.messageSecret && (isPnUser(jid) || isLidUser(jid))) {
1536
1500
  messageContextInfo.deviceListMetadata = {
1537
1501
  recipientKeyHash: randomBytes(10),
@@ -1539,7 +1503,7 @@ export const generateWAMessageFromContent = (jid, message, options) => {
1539
1503
  };
1540
1504
  messageContextInfo.deviceListMetadataVersion = 2
1541
1505
  }
1542
- message = WAProto.Message.create(message);
1506
+ message = proto.Message.create(message);
1543
1507
  const messageJSON = {
1544
1508
  key: {
1545
1509
  remoteJid: jid,
@@ -1554,11 +1518,11 @@ export const generateWAMessageFromContent = (jid, message, options) => {
1554
1518
  };
1555
1519
  return WAProto.WebMessageInfo.fromObject(messageJSON);
1556
1520
  };
1557
- export const generateWAMessage = async (jid, content, options) => {
1521
+ export const generateWAMessage = async (jid, content, options = {}) => {
1558
1522
  // ensure msg ID is with every log
1559
1523
  options.logger = options?.logger?.child({ msgId: options.messageId });
1560
1524
  // Pass jid in the options to generateWAMessageContent
1561
- if (jid) {
1525
+ if (jid && typeof options === 'object') {
1562
1526
  options.jid = jid;
1563
1527
  }
1564
1528
  return generateWAMessageFromContent(jid, await generateWAMessageContent(content, options), options);
@@ -1590,7 +1554,7 @@ export const normalizeMessageContent = (content) => {
1590
1554
  content = inner.message;
1591
1555
  }
1592
1556
  return content;
1593
- // crysnovax@Changes 03-02-26 --- Add all futureProofMessage into getFutureProofMessage()
1557
+ // Lia@Changes 03-02-26 --- Add all futureProofMessage into getFutureProofMessage()
1594
1558
  function getFutureProofMessage(message) {
1595
1559
  return (
1596
1560
  message?.associatedChildMessage ||
@@ -1607,14 +1571,10 @@ export const normalizeMessageContent = (content) => {
1607
1571
  message?.groupStatusMessageV2 ||
1608
1572
  message?.limitSharingMessage ||
1609
1573
  message?.lottieStickerMessage ||
1610
- message?.newsletterAdminProfileMessage ||
1611
- message?.newsletterAdminProfileMessageV2 ||
1612
- message?.newsletterAdminProfileStatusMessage ||
1613
1574
  message?.pollCreationMessageV4 ||
1614
1575
  message?.pollCreationOptionImageMessage ||
1615
1576
  message?.questionMessage ||
1616
1577
  message?.questionReplyMessage ||
1617
- message?.spoilerMessage ||
1618
1578
  message?.statusAddYours ||
1619
1579
  message?.statusMentionMessage ||
1620
1580
  message?.viewOnceMessage ||
@@ -1912,14 +1872,20 @@ const isWebPBuffer = (buffer) => {
1912
1872
  );
1913
1873
  };
1914
1874
  /**
1915
- * crysnovax@Changes 30-01-26
1875
+ * Lia@Changes 30-01-26
1916
1876
  * ---
1917
1877
  * Determines whether a message should include a Biz Binary Node.
1918
1878
  * A Biz Binary Node is added only for interactive messages
1919
1879
  * such as buttons or other supported interactive types.
1920
1880
  */
1921
- export const shouldIncludeBizBinaryNode = (message) => !!(message.buttonsMessage ||
1922
- message.listMessage ||
1923
- message.templateMessage ||
1924
- (message.interactiveMessage &&
1925
- message.interactiveMessage.nativeFlowMessage));
1881
+ export const shouldIncludeBizBinaryNode = (message) => {
1882
+ const hasValidInteractive =
1883
+ message.interactiveMessage &&
1884
+ !message.interactiveMessage.carouselMessage &&
1885
+ !message.interactiveMessage.collectionMessage &&
1886
+ !message.interactiveMessage.shopStorefrontMessage;
1887
+ return (message.buttonsMessage ||
1888
+ message.interactiveMessage ||
1889
+ message.listMessage ||
1890
+ hasValidInteractive);
1891
+ };