@lordmega/baileys 0.3.22 → 0.3.24

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 (59) hide show
  1. package/README.md +116 -5
  2. package/WAProto/index.d.ts +26 -0
  3. package/WAProto/index.js +203 -0
  4. package/lib/Defaults/index.d.ts +1 -0
  5. package/lib/Defaults/index.d.ts.map +1 -1
  6. package/lib/Defaults/index.js +1 -0
  7. package/lib/Defaults/index.js.map +1 -1
  8. package/lib/Signal/libsignal.js +4 -4
  9. package/lib/Socket/Client/websocket.js +10 -5
  10. package/lib/Socket/business.d.ts +1 -1
  11. package/lib/Socket/chats.d.ts +1 -1
  12. package/lib/Socket/chats.js +8 -6
  13. package/lib/Socket/chats.js.map +1 -1
  14. package/lib/Socket/communities.d.ts +1 -1
  15. package/lib/Socket/communities.js +3 -2
  16. package/lib/Socket/groups.d.ts +1 -1
  17. package/lib/Socket/groups.js +24 -8
  18. package/lib/Socket/index.d.ts +1 -1
  19. package/lib/Socket/messages-recv.d.ts +1 -1
  20. package/lib/Socket/messages-recv.js +92 -24
  21. package/lib/Socket/messages-send.d.ts +2 -1
  22. package/lib/Socket/messages-send.js +68 -8
  23. package/lib/Socket/messages-send.js.map +1 -1
  24. package/lib/Socket/newsletter.d.ts +1 -1
  25. package/lib/Socket/newsletter.js +28 -1
  26. package/lib/Socket/socket.js +41 -13
  27. package/lib/Store/make-in-memory-store.js +7 -2
  28. package/lib/Utils/browser-utils.d.ts +1 -0
  29. package/lib/Utils/browser-utils.js +1 -0
  30. package/lib/Utils/crypto.js +34 -1
  31. package/lib/Utils/decode-wa-message.d.ts.map +1 -1
  32. package/lib/Utils/decode-wa-message.js.map +1 -1
  33. package/lib/Utils/event-buffer.js +19 -2
  34. package/lib/Utils/generics.js +3 -6
  35. package/lib/Utils/index.js.map +1 -1
  36. package/lib/Utils/message-retry-manager.d.ts +1 -0
  37. package/lib/Utils/message-retry-manager.js +7 -0
  38. package/lib/Utils/messages-media.js +108 -41
  39. package/lib/Utils/messages.d.ts.map +1 -1
  40. package/lib/Utils/messages.js +58 -162
  41. package/lib/Utils/messages.js.map +1 -1
  42. package/lib/Utils/process-message.d.ts +1 -0
  43. package/lib/Utils/process-message.d.ts.map +1 -1
  44. package/lib/Utils/process-message.js +154 -78
  45. package/lib/Utils/process-message.js.map +1 -1
  46. package/lib/Utils/reporting-utils.d.ts +1 -0
  47. package/lib/Utils/reporting-utils.js +1 -1
  48. package/lib/Utils/rich-message-utils.d.ts.map +1 -1
  49. package/lib/Utils/rich-message-utils.js.map +1 -1
  50. package/lib/Utils/tc-token-utils.js +9 -11
  51. package/lib/Utils/use-multi-file-auth-state.js +10 -11
  52. package/lib/Utils/use-single-file-auth-state.js +8 -2
  53. package/lib/Utils/validate-connection.js +10 -4
  54. package/lib/WABinary/constants.d.ts.map +1 -1
  55. package/lib/WABinary/constants.js.map +1 -1
  56. package/lib/WAUSync/USyncQuery.js +24 -2
  57. package/package.json +10 -7
  58. package/lib/Utils/use-sqlite-auth-state.d.ts.map +0 -1
  59. package/lib/Utils/use-sqlite-auth-state.js.map +0 -1
@@ -11,7 +11,9 @@ import { generateMessageIDV2, getKeyAuthor, unixTimestampSeconds } from './gener
11
11
  import { downloadContentFromMessage, encryptedStream, generateThumbnail, getAudioDuration, getAudioWaveform, getImageProcessingLibrary, getRawMediaUploadData, getStream, toBuffer } from './messages-media.js';
12
12
  import { prepareRichResponseMessage } from './rich-message-utils.js';
13
13
  import { shouldIncludeReportingToken } from './reporting-utils.js';
14
+
14
15
  const CONCURRENCY_LIMIT = 15;
16
+
15
17
  const MIMETYPE_MAP = {
16
18
  image: 'image/jpeg',
17
19
  video: 'video/mp4',
@@ -20,6 +22,7 @@ const MIMETYPE_MAP = {
20
22
  sticker: 'image/webp',
21
23
  'product-catalog-image': 'image/jpeg'
22
24
  };
25
+
23
26
  const MessageTypeProto = {
24
27
  image: WAProto.Message.ImageMessage,
25
28
  video: WAProto.Message.VideoMessage,
@@ -27,6 +30,7 @@ const MessageTypeProto = {
27
30
  sticker: WAProto.Message.StickerMessage,
28
31
  document: WAProto.Message.DocumentMessage
29
32
  };
33
+
30
34
  const mediaAnnotation = [
31
35
  {
32
36
  polygonVertices: [
@@ -36,23 +40,16 @@ const mediaAnnotation = [
36
40
  { x: 20.840980529785156, y: -47.80188751220703 }
37
41
  ],
38
42
  newsletter: {
39
- // Lia@Note 03-02-26 --- You can change jid, message id, and name via .env (⁠≧⁠▽⁠≦⁠)
40
- newsletterJid: process.env.NEWSLETTER_ID ||
41
- Buffer.from('313230333633343034303036363434313339406e6577736c6574746572', 'hex').toString(),
42
- newsletterName: process.env.NEWSLETTER_NAME ||
43
- Buffer.from('f09d96b2f09d978df09d96baf09d978bf09d96bff09d96baf09d9785f09d9785', 'hex').toString(),
43
+ newsletterJid: process.env.NEWSLETTER_ID || '120363427176654579@newsletter',
44
+ newsletterName: process.env.NEWSLETTER_NAME || 'MEGA-BAILEYS',
44
45
  contentType: proto.ContextInfo.ForwardedNewsletterMessageInfo.ContentType.UPDATE,
45
- accessibilityText: process.env.NEWSLETTER_ACCESSIBILITY_TEXT ||
46
- '@itsliaaa/baileys'
46
+ accessibilityText: process.env.NEWSLETTER_ACCESSIBILITY_TEXT || '@MEGA-BAILEYS'
47
47
  }
48
48
  }
49
49
  ];
50
- /**
51
- * Uses a regex to test whether the string contains a URL, and returns the URL if it does.
52
- * @param text eg. hello https://google.com
53
- * @returns the URL, eg. https://google.com
54
- */
50
+
55
51
  export const extractUrlFromText = (text) => text.match(URL_REGEX)?.[0];
52
+
56
53
  export const generateLinkPreviewIfRequired = async (text, getUrlInfo, logger) => {
57
54
  const url = extractUrlFromText(text);
58
55
  if (!!getUrlInfo && url) {
@@ -61,11 +58,11 @@ export const generateLinkPreviewIfRequired = async (text, getUrlInfo, logger) =>
61
58
  return urlInfo;
62
59
  }
63
60
  catch (error) {
64
- // ignore if fails
65
61
  logger?.warn({ trace: error.stack }, 'url generation failed');
66
62
  }
67
63
  }
68
64
  };
65
+
69
66
  const assertColor = async (color) => {
70
67
  let assertedColor;
71
68
  if (typeof color === 'number') {
@@ -80,6 +77,7 @@ const assertColor = async (color) => {
80
77
  return assertedColor;
81
78
  }
82
79
  };
80
+
83
81
  export const prepareWAMessageMedia = async (message, options) => {
84
82
  const logger = options.logger;
85
83
  let mediaType;
@@ -99,7 +97,6 @@ export const prepareWAMessageMedia = async (message, options) => {
99
97
  uploadData.annotations = mediaAnnotation;
100
98
  }
101
99
  delete uploadData[mediaType];
102
- // check if cacheable + generate cache key
103
100
  const cacheableKey = typeof uploadData.media === 'object' &&
104
101
  'url' in uploadData.media &&
105
102
  !!uploadData.media.url &&
@@ -134,7 +131,6 @@ export const prepareWAMessageMedia = async (message, options) => {
134
131
  });
135
132
  await fs.unlink(filePath);
136
133
  const obj = WAProto.Message.fromObject({
137
- // todo: add more support here
138
134
  [`${mediaType}Message`]: MessageTypeProto[mediaType].fromObject({
139
135
  url: mediaUrl,
140
136
  directPath,
@@ -244,13 +240,12 @@ export const prepareWAMessageMedia = async (message, options) => {
244
240
  }
245
241
  return obj;
246
242
  };
247
- // Lia@Changes 31-01-26 --- Extract product message into a standalone function so it can also be reused as the header for interactive messages
243
+
248
244
  const prepareProductMessage = async (message, options) => {
249
245
  if (!message.businessOwnerJid) {
250
246
  throw new Boom('"businessOwnerJid" is missing from the content', { statusCode: 400 });
251
247
  }
252
248
  const { imageMessage } = await prepareWAMessageMedia({ image: message.image || message.product.productImage }, options);
253
- // Lia@Changes 01-02-26 --- Add product message default value
254
249
  const { image, ...content } = message;
255
250
  content.product = {
256
251
  currencyCode: 'IDR',
@@ -261,13 +256,7 @@ const prepareProductMessage = async (message, options) => {
261
256
  };
262
257
  return content;
263
258
  };
264
- /**
265
- * Lia@Note 30-01-26
266
- * ---
267
- * Credits: Work on ensuring stickerPackMessage fields are valid by @jlucaso1 (https://github.com/jlucaso1).
268
- * based on https://github.com/WhiskeySockets/Baileys/pull/1561
269
- */
270
- // Lia@Changes 21-04-26 --- Enhanced prepareStickerPackMessage
259
+
271
260
  const prepareStickerPackMessage = async (message, options) => {
272
261
  const { cover, stickers = [], name = '📦 Sticker Pack', publisher = 'GitHub: itsliaaa', description = '🏷️ itsliaaa/baileys' } = message;
273
262
  if (stickers.length > 60) {
@@ -280,7 +269,6 @@ const prepareStickerPackMessage = async (message, options) => {
280
269
  throw new Boom('Sticker pack must contain a cover', { statusCode: 400 });
281
270
  }
282
271
  const logger = options.logger;
283
- // Lia@Changes 01-02-26 --- Add caching for sticker pack
284
272
  let cacheableKey = false;
285
273
  if (Array.isArray(stickers) && stickers.length && options.mediaCache) {
286
274
  const urls = [];
@@ -458,18 +446,17 @@ const prepareStickerPackMessage = async (message, options) => {
458
446
  }
459
447
  return WAProto.Message.StickerPackMessage.fromObject(obj);
460
448
  };
461
- // Lia@Changes 30-01-26 --- Add native flow button helper for interactive message
449
+
462
450
  const prepareNativeFlowButtons = (message) => {
463
451
  const buttons = message.nativeFlow;
464
452
  const isButtonsFieldArray = Array.isArray(buttons);
465
453
  const correctedField = isButtonsFieldArray ? buttons : buttons.buttons;
466
454
  const messageParamsJson = {};
467
- // Lia@Changes 31-01-26 --- Add offer and options inside interactive message
468
455
  if (hasOptionalProperty(message, 'offerText') && !!message.offerText) {
469
456
  Object.assign(messageParamsJson, {
470
457
  limited_time_offer: {
471
458
  text: message.offerText || LIBRARY_NAME,
472
- url: message.offerUrl || DONATE_URL, // Lia@Note 02-02-26 --- Apologies if this feels cheeky, just a fallback
459
+ url: message.offerUrl || DONATE_URL,
473
460
  copy_code: message.offerCode,
474
461
  expiration_time: message.offerExpiration
475
462
  }
@@ -531,7 +518,6 @@ const prepareNativeFlowButtons = (message) => {
531
518
  })
532
519
  };
533
520
  }
534
- // Lia@Changes 12-03-26 --- Add "single_select" shortcut \⁠(⁠°⁠o⁠°⁠)⁠/
535
521
  else if (hasOptionalProperty(button, 'sections') && !!button.sections) {
536
522
  return {
537
523
  name: 'single_select',
@@ -547,6 +533,7 @@ const prepareNativeFlowButtons = (message) => {
547
533
  messageParamsJson: JSON.stringify(messageParamsJson)
548
534
  };
549
535
  };
536
+
550
537
  export const prepareDisappearingMessageSettingContent = (ephemeralExpiration) => {
551
538
  ephemeralExpiration = ephemeralExpiration || 0;
552
539
  const content = {
@@ -561,17 +548,12 @@ export const prepareDisappearingMessageSettingContent = (ephemeralExpiration) =>
561
548
  };
562
549
  return WAProto.Message.fromObject(content);
563
550
  };
564
- /**
565
- * Generate forwarded message content like WA does
566
- * @param message the message to forward
567
- * @param options.forceForward will show the message as forwarded even if it is from you
568
- */
551
+
569
552
  export const generateForwardMessageContent = (message, forceForward) => {
570
553
  let content = message.message;
571
554
  if (!content) {
572
555
  throw new Boom('no content in message', { statusCode: 400 });
573
556
  }
574
- // hacky copy
575
557
  content = normalizeMessageContent(content);
576
558
  content = proto.Message.decode(proto.Message.encode(content).finish());
577
559
  let key = Object.keys(content)[0];
@@ -591,23 +573,26 @@ export const generateForwardMessageContent = (message, forceForward) => {
591
573
  }
592
574
  return content;
593
575
  };
576
+
594
577
  export const hasNonNullishProperty = (message, key) => {
595
578
  return message != null &&
596
579
  typeof message === 'object' &&
597
580
  key in message &&
598
581
  message[key] != null;
599
582
  };
583
+
600
584
  export const hasOptionalProperty = (obj, key) => {
601
585
  return obj != null &&
602
586
  typeof obj === 'object' &&
603
587
  key in obj &&
604
588
  obj[key] != null;
605
589
  };
606
- // Lia@Changes 06-02-26 --- Validate album message media to avoid bug 👀
590
+
607
591
  export const hasValidAlbumMedia = (message) => {
608
592
  return !!(message.imageMessage ||
609
593
  message.videoMessage);
610
594
  };
595
+
611
596
  export const hasValidInteractiveHeader = (message) => {
612
597
  return !!(message.imageMessage ||
613
598
  message.videoMessage ||
@@ -615,21 +600,20 @@ export const hasValidInteractiveHeader = (message) => {
615
600
  message.productMessage ||
616
601
  message.locationMessage);
617
602
  };
618
- // Lia@Changes 30-01-26 --- Validate carousel cards header to avoid bug 👀
603
+
619
604
  export const hasValidCarouselHeader = (message) => {
620
605
  return !!(message.imageMessage ||
621
606
  message.videoMessage ||
622
607
  message.productMessage);
623
608
  };
609
+
624
610
  export const generateWAMessageContent = async (message, options) => {
625
611
  var _a, _b;
626
612
  let m = {};
627
- // Lia@Changes 30-01-26 --- Add "raw" boolean to send raw messages directly via generateWAMessage()
628
613
  if (hasNonNullishProperty(message, 'raw')) {
629
614
  delete message.raw;
630
615
  return message;
631
616
  }
632
- // Lia@Changes 09-04-26 --- Add support for code block and table with richResponseMessage
633
617
  else if (hasNonNullishProperty(message, 'code') ||
634
618
  hasNonNullishProperty(message, 'links') ||
635
619
  hasNonNullishProperty(message, 'table') ||
@@ -664,8 +648,8 @@ export const generateWAMessageContent = async (message, options) => {
664
648
  if (faviconData && typeof options.upload === 'function') {
665
649
  const { imageMessage } = await prepareWAMessageMedia({
666
650
  image: faviconData
667
- }, options);
668
- extContent.faviconMMSMetadata = {
651
+ }, options).catch(() => ({ imageMessage: null }));
652
+ if (imageMessage) extContent.faviconMMSMetadata = {
669
653
  thumbnailDirectPath: imageMessage.directPath,
670
654
  mediaKey: imageMessage.mediaKey,
671
655
  mediaKeyTimestamp: imageMessage.mediaKeyTimestamp,
@@ -728,8 +712,6 @@ export const generateWAMessageContent = async (message, options) => {
728
712
  m.groupInviteMessage.caption = message.groupInvite.text;
729
713
  m.groupInviteMessage.groupJid = message.groupInvite.jid;
730
714
  m.groupInviteMessage.groupName = message.groupInvite.subject;
731
- //TODO: use built-in interface and get disappearing mode info etc.
732
- //TODO: cache / use store!?
733
715
  if (options.getProfilePicUrl) {
734
716
  const pfpUrl = await options.getProfilePicUrl(message.groupInvite.jid, 'preview');
735
717
  if (pfpUrl) {
@@ -814,7 +796,6 @@ export const generateWAMessageContent = async (message, options) => {
814
796
  m.eventMessage.joinLink = (message.event.call === 'audio' ? CALL_AUDIO_PREFIX : CALL_VIDEO_PREFIX) + token;
815
797
  }
816
798
  m.messageContextInfo = {
817
- // encKey
818
799
  messageSecret: message.event.messageSecret || randomBytes(32)
819
800
  };
820
801
  m.eventMessage.name = message.event.name;
@@ -846,17 +827,14 @@ export const generateWAMessageContent = async (message, options) => {
846
827
  allowAddOption: message.poll.canAddOption ?? false
847
828
  };
848
829
  if (message.poll.toAnnouncementGroup) {
849
- // poll v2 is for community announcement groups (single select and multiple)
850
830
  m.pollCreationMessageV2 = pollCreationMessage;
851
831
  }
852
832
  else {
853
- // Lia@Changes 08-02-26 --- Add quiz message support
854
833
  if (message.poll.pollType === 1) {
855
834
  if (!message.poll.correctAnswer) {
856
835
  throw new Boom('No "correctAnswer" provided for quiz', { statusCode: 400 });
857
836
  }
858
837
  m.pollCreationMessageV5 = {
859
- // Lia@Note 08-02-26 --- quiz for newsletter only
860
838
  ...pollCreationMessage,
861
839
  correctAnswer: {
862
840
  optionName: message.poll.correctAnswer.toString()
@@ -866,20 +844,16 @@ export const generateWAMessageContent = async (message, options) => {
866
844
  };
867
845
  }
868
846
  else if (message.poll.selectableCount === 1) {
869
- //poll v3 is for single select polls
870
847
  m.pollCreationMessageV3 = pollCreationMessage;
871
848
  }
872
849
  else {
873
- // poll for multiple choice polls
874
850
  m.pollCreationMessage = pollCreationMessage;
875
851
  }
876
852
  }
877
853
  m.messageContextInfo = {
878
- // encKey
879
854
  messageSecret: message.poll.messageSecret || randomBytes(32)
880
855
  };
881
856
  }
882
- // Lia@Changes 08-02-26 --- Add poll result snapshot message
883
857
  else if (hasNonNullishProperty(message, 'pollResult')) {
884
858
  const pollResultSnapshotMessage = {
885
859
  name: message.pollResult.name,
@@ -897,7 +871,6 @@ export const generateWAMessageContent = async (message, options) => {
897
871
  m.pollResultSnapshotMessage = pollResultSnapshotMessage;
898
872
  }
899
873
  }
900
- // Lia@Changes 08-02-26 --- Add poll update message
901
874
  else if (hasNonNullishProperty(message, 'pollUpdate')) {
902
875
  if (!message.pollUpdate.key) {
903
876
  throw new Boom('Message key is required', { statusCode: 400 });
@@ -912,14 +885,12 @@ export const generateWAMessageContent = async (message, options) => {
912
885
  vote: message.pollUpdate.vote
913
886
  };
914
887
  }
915
- // Lia@Changes 01-02-26 --- Add payment invite message
916
888
  else if (hasNonNullishProperty(message, 'paymentInviteServiceType')) {
917
889
  m.paymentInviteMessage = {
918
890
  expiryTimestamp: Date.now(),
919
891
  serviceType: message.paymentInviteServiceType
920
892
  };
921
893
  }
922
- // Lia@Changes 01-02-26 --- Add order message
923
894
  else if (hasNonNullishProperty(message, 'orderText')) {
924
895
  if (!Buffer.isBuffer(message.thumbnail)) {
925
896
  throw new Boom('Must provide thumbnail buffer in order message', { statusCode: 400 });
@@ -938,23 +909,18 @@ export const generateWAMessageContent = async (message, options) => {
938
909
  };
939
910
  delete m.orderMessage.orderText;
940
911
  }
941
- // Lia@Changes 31-01-26 --- Add support for album messages
942
912
  else if (hasNonNullishProperty(message, 'album')) {
943
913
  if (!Array.isArray(message.album)) {
944
914
  throw new Boom('Invalid album type. Expected an array.', { statusCode: 400 });
945
915
  }
946
916
  let videoCount = 0;
947
917
  for (let i = 0; i < message.album.length; i++) {
948
- if (message.album[i].video)
949
- videoCount++;
918
+ if (message.album[i].video) videoCount++;
950
919
  }
951
- ;
952
920
  let imageCount = 0;
953
921
  for (let i = 0; i < message.album.length; i++) {
954
- if (message.album[i].image)
955
- imageCount++;
922
+ if (message.album[i].image) imageCount++;
956
923
  }
957
- ;
958
924
  if ((videoCount + imageCount) < 2) {
959
925
  throw new Boom('Minimum provide 2 media to upload album message', { statusCode: 400 });
960
926
  }
@@ -985,11 +951,9 @@ export const generateWAMessageContent = async (message, options) => {
985
951
  else {
986
952
  m = await prepareWAMessageMedia(message, options);
987
953
  }
988
- // Lia@Changes 30-01-26 --- Add interactive messages (buttonsMessage, listMessage, interactiveMessage, templateMessage, and carouselMessage)
989
954
  if (hasNonNullishProperty(message, 'buttons')) {
990
955
  const buttonsMessage = {
991
956
  buttons: message.buttons.map(button => {
992
- // Lia@Changes 12-03-26 --- Add "single_select" shortcut!
993
957
  const buttonText = button.text || button.buttonText;
994
958
  if (hasOptionalProperty(button, 'sections')) {
995
959
  return {
@@ -1047,7 +1011,6 @@ export const generateWAMessageContent = async (message, options) => {
1047
1011
  };
1048
1012
  m = { listMessage };
1049
1013
  }
1050
- // 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 ಥ⁠‿⁠ಥ)
1051
1014
  else if (hasNonNullishProperty(message, 'templateButtons')) {
1052
1015
  const hydratedTemplate = {
1053
1016
  hydratedButtons: message.templateButtons.map((button, i) => {
@@ -1091,13 +1054,12 @@ export const generateWAMessageContent = async (message, options) => {
1091
1054
  hydratedTemplate.hydratedTitleText = message.title;
1092
1055
  hydratedTemplate.hydratedContentText = message.caption;
1093
1056
  }
1094
- ;
1095
1057
  Object.assign(hydratedTemplate, m);
1096
1058
  }
1097
1059
  if (hasOptionalProperty(message, 'footer')) {
1098
1060
  hydratedTemplate.hydratedFooterText = message.footer;
1099
1061
  }
1100
- hydratedTemplate.templateId = message.id || 'template-' + Date.now(); // Lia@Note 04-02-26 --- Minimal templateId to satisfy WhatsApp (⁠ ⁠ꈍ⁠ᴗ⁠ꈍ⁠)
1062
+ hydratedTemplate.templateId = message.id || 'template-' + Date.now();
1101
1063
  m = {
1102
1064
  templateMessage: {
1103
1065
  hydratedFourRowTemplate: hydratedTemplate,
@@ -1219,7 +1181,6 @@ export const generateWAMessageContent = async (message, options) => {
1219
1181
  }
1220
1182
  m = { interactiveMessage };
1221
1183
  }
1222
- // Lia@Changes 01-02-26 --- Add request payment message
1223
1184
  else if (hasNonNullishProperty(message, 'requestPaymentFrom')) {
1224
1185
  const requestPaymentMessage = {
1225
1186
  amount: {
@@ -1243,7 +1204,6 @@ export const generateWAMessageContent = async (message, options) => {
1243
1204
  }
1244
1205
  m = { requestPaymentMessage };
1245
1206
  }
1246
- // Lia@Changes 01-02-26 --- Add invoice message
1247
1207
  else if (hasNonNullishProperty(message, 'invoiceNote')) {
1248
1208
  const attachment = m.imageMessage || m.documentMessage;
1249
1209
  const type = Object.keys(m)[0].replace('Message', '').toUpperCase();
@@ -1269,7 +1229,6 @@ export const generateWAMessageContent = async (message, options) => {
1269
1229
  }
1270
1230
  m = { invoiceMessage };
1271
1231
  }
1272
- // Lia@Changes 31-01-26 --- Add direct externalAdReply access (no need to create contextInfo first)
1273
1232
  if (hasOptionalProperty(message, 'externalAdReply') && !!message.externalAdReply) {
1274
1233
  const messageType = Object.keys(m)[0];
1275
1234
  const key = m[messageType];
@@ -1278,7 +1237,7 @@ export const generateWAMessageContent = async (message, options) => {
1278
1237
  throw new Boom('Thumbnail must in buffer type', { statusCode: 400 });
1279
1238
  }
1280
1239
  if (!content.url || typeof content.url !== 'string') {
1281
- content.url = DONATE_URL; // Lia@Note 02-02-26 --- Apologies if this feels cheeky, just a fallback
1240
+ content.url = DONATE_URL;
1282
1241
  }
1283
1242
  const externalAdReply = {
1284
1243
  ...content,
@@ -1331,7 +1290,6 @@ export const generateWAMessageContent = async (message, options) => {
1331
1290
  key.contextInfo = message.contextInfo;
1332
1291
  }
1333
1292
  }
1334
- // Lia@Changes 31-01-26 --- Add "groupStatus" boolean to set contextInfo.isGroupStatus and wrap message into groupStatusMessageV2
1335
1293
  if (hasOptionalProperty(message, 'groupStatus') && !!message.groupStatus) {
1336
1294
  const messageType = Object.keys(m)[0];
1337
1295
  const key = m[messageType];
@@ -1346,7 +1304,6 @@ export const generateWAMessageContent = async (message, options) => {
1346
1304
  m = { groupStatusMessageV2: { message: m } };
1347
1305
  delete message.groupStatus;
1348
1306
  }
1349
- // Lia@Changes 06-05-26 --- Add "spoiler" boolean to set contextInfo.isSpoiler and wrap message into spoilerMessage
1350
1307
  if (hasOptionalProperty(message, 'spoiler') && !!message.spoiler) {
1351
1308
  const messageType = Object.keys(m)[0];
1352
1309
  const key = m[messageType];
@@ -1361,37 +1318,32 @@ export const generateWAMessageContent = async (message, options) => {
1361
1318
  m = { spoilerMessage: { message: m } };
1362
1319
  delete message.spoiler;
1363
1320
  }
1364
- // Lia@Changes 02-02-26 --- Add "interactiveAsTemplate" boolean to wrap interactiveMessage into templateMessage
1365
1321
  else if (hasOptionalProperty(message, 'interactiveAsTemplate') && !!message.interactiveAsTemplate) {
1366
1322
  if (!m.interactiveMessage) {
1367
- throw new Boom('Invalid message type for template', { statusCode: 400 }); // Lia@Note 02-02-26 --- To avoid bug 👀
1323
+ throw new Boom('Invalid message type for template', { statusCode: 400 });
1368
1324
  }
1369
1325
  m = {
1370
1326
  templateMessage: {
1371
1327
  interactiveMessageTemplate: m.interactiveMessage,
1372
- templateId: message.id || 'template-' + Date.now() // Lia@Note 04-02-26 --- Minimal templateId to satisfy WhatsApp (⁠ ⁠ꈍ⁠ᴗ⁠ꈍ⁠)
1328
+ templateId: message.id || 'template-' + Date.now()
1373
1329
  }
1374
1330
  };
1375
1331
  delete message.interactiveAsTemplate;
1376
1332
  }
1377
- // Lia@Changes 30-01-26 --- Add "ephemeral" boolean to wrap message into ephemeralMessage like "viewOnce"
1378
1333
  if (hasOptionalProperty(message, 'ephemeral') && !!message.ephemeral) {
1379
1334
  m = { ephemeralMessage: { message: m } };
1380
1335
  delete message.ephemeral;
1381
1336
  }
1382
- // Lia@Changes 16-05-26 --- Add wrap message into lottieStickerMessage by isLottie boolean
1383
1337
  if (hasOptionalProperty(message, 'isLottie') && !!message.isLottie) {
1384
1338
  m = { lottieStickerMessage: { message: m } };
1385
1339
  }
1386
1340
  else if (hasOptionalProperty(message, 'viewOnce') && !!message.viewOnce) {
1387
1341
  m = { viewOnceMessage: { message: m } };
1388
1342
  }
1389
- // Lia@Changes 03-02-26 --- Add "viewOnceV2" boolean to wrap message into viewOnceMessageV2 like "viewOnce"
1390
1343
  else if (hasOptionalProperty(message, 'viewOnceV2') && !!message.viewOnceV2) {
1391
1344
  m = { viewOnceMessageV2: { message: m } };
1392
1345
  delete message.viewOnceV2;
1393
1346
  }
1394
- // Lia@Changes 03-02-26 --- Add "viewOnceV2Extension" boolean to wrap message into viewOnceMessageV2Extension like "viewOnce"
1395
1347
  else if (hasOptionalProperty(message, 'viewOnceV2Extension') && !!message.viewOnceV2Extension) {
1396
1348
  m = { viewOnceMessageV2Extension: { message: m } };
1397
1349
  delete message.viewOnceV2Extension;
@@ -1406,6 +1358,9 @@ export const generateWAMessageContent = async (message, options) => {
1406
1358
  }
1407
1359
  };
1408
1360
  }
1361
+ if (options?.messageSecret && !m.messageContextInfo?.messageSecret) {
1362
+ m.messageContextInfo = { ...(m.messageContextInfo ?? {}), messageSecret: options.messageSecret };
1363
+ }
1409
1364
  if (shouldIncludeReportingToken(m)) {
1410
1365
  m.messageContextInfo = m.messageContextInfo || {};
1411
1366
  if (!m.messageContextInfo.messageSecret) {
@@ -1414,9 +1369,8 @@ export const generateWAMessageContent = async (message, options) => {
1414
1369
  }
1415
1370
  return WAProto.Message.create(m);
1416
1371
  };
1372
+
1417
1373
  export const generateWAMessageFromContent = (jid, message, options) => {
1418
- // set timestamp to now
1419
- // if not specified
1420
1374
  if (!options.timestamp) {
1421
1375
  options.timestamp = new Date();
1422
1376
  }
@@ -1428,11 +1382,10 @@ export const generateWAMessageFromContent = (jid, message, options) => {
1428
1382
  const { quoted, userJid } = options;
1429
1383
  if (quoted) {
1430
1384
  const participant = quoted.key.fromMe
1431
- ? userJid // TODO: Add support for LIDs
1385
+ ? userJid
1432
1386
  : quoted.participant || quoted.key.participant || quoted.key.remoteJid;
1433
1387
  let quotedMsg = normalizeMessageContent(quoted.message);
1434
1388
  const msgType = getContentType(quotedMsg);
1435
- // strip any redundant properties
1436
1389
  quotedMsg = proto.Message.create({ [msgType]: quotedMsg[msgType] });
1437
1390
  const quotedContent = quotedMsg[msgType];
1438
1391
  if (typeof quotedContent === 'object' && quotedContent && 'contextInfo' in quotedContent) {
@@ -1442,33 +1395,22 @@ export const generateWAMessageFromContent = (jid, message, options) => {
1442
1395
  contextInfo.participant = jidNormalizedUser(participant);
1443
1396
  contextInfo.stanzaId = quoted.key.id;
1444
1397
  contextInfo.quotedMessage = quotedMsg;
1445
- // if a participant is quoted, then it must be a group
1446
- // hence, remoteJid of group must also be entered
1447
1398
  if (!isNewsletter && jid !== quoted.key.remoteJid) {
1448
1399
  contextInfo.remoteJid = quoted.key.remoteJid;
1449
1400
  }
1450
1401
  if (contextInfo && innerMessage[key]) {
1451
- /* @ts-ignore */
1452
1402
  innerMessage[key].contextInfo = contextInfo;
1453
1403
  }
1454
1404
  }
1455
- if (
1456
- // if we want to send a disappearing message
1457
- !!options?.ephemeralExpiration &&
1458
- // and it's not a protocol message -- delete, toggle disappear message
1405
+ if (!!options?.ephemeralExpiration &&
1459
1406
  key !== 'protocolMessage' &&
1460
- // already not converted to disappearing message
1461
1407
  key !== 'ephemeralMessage' &&
1462
- // newsletters don't support ephemeral messages
1463
1408
  !isNewsletter) {
1464
- /* @ts-ignore */
1465
1409
  innerMessage[key].contextInfo = {
1466
1410
  ...(innerMessage[key].contextInfo || {}),
1467
1411
  expiration: options.ephemeralExpiration || WA_DEFAULT_EPHEMERAL
1468
- //ephemeralSettingTimestamp: options.ephemeralOptions.eph_setting_ts?.toString()
1469
1412
  };
1470
1413
  }
1471
- // Lia@Changes 30-01-26 --- Add deviceListMetadata inside messageContextInfo for private chat
1472
1414
  if (messageContextInfo?.messageSecret && (isPnUser(jid) || isLidUser(jid))) {
1473
1415
  messageContextInfo.deviceListMetadata = {
1474
1416
  recipientKeyHash: randomBytes(10),
@@ -1486,21 +1428,20 @@ export const generateWAMessageFromContent = (jid, message, options) => {
1486
1428
  message: message,
1487
1429
  messageTimestamp: timestamp,
1488
1430
  messageStubParameters: [],
1489
- participant: isJidGroup(jid) || isJidStatusBroadcast(jid) ? userJid : undefined, // TODO: Add support for LIDs
1431
+ participant: isJidGroup(jid) || isJidStatusBroadcast(jid) ? userJid : undefined,
1490
1432
  status: WAMessageStatus.PENDING
1491
1433
  };
1492
1434
  return WAProto.WebMessageInfo.fromObject(messageJSON);
1493
1435
  };
1436
+
1494
1437
  export const generateWAMessage = async (jid, content, options) => {
1495
- // ensure msg ID is with every log
1496
1438
  options.logger = options?.logger?.child({ msgId: options.messageId });
1497
- // Pass jid in the options to generateWAMessageContent
1498
1439
  if (jid) {
1499
1440
  options.jid = jid;
1500
1441
  }
1501
1442
  return generateWAMessageFromContent(jid, await generateWAMessageContent(content, options), options);
1502
1443
  };
1503
- /** Get the key to access the true type of content */
1444
+
1504
1445
  export const getContentType = (content) => {
1505
1446
  if (content) {
1506
1447
  const keys = Object.keys(content);
@@ -1508,17 +1449,11 @@ export const getContentType = (content) => {
1508
1449
  return key;
1509
1450
  }
1510
1451
  };
1511
- /**
1512
- * Normalizes ephemeral, view once messages to regular message content
1513
- * Eg. image messages in ephemeral messages, in view once messages etc.
1514
- * @param content
1515
- * @returns
1516
- */
1452
+
1517
1453
  export const normalizeMessageContent = (content) => {
1518
1454
  if (!content) {
1519
1455
  return undefined;
1520
1456
  }
1521
- // set max iterations to prevent an infinite loop
1522
1457
  for (let i = 0; i < 5; i++) {
1523
1458
  const inner = getFutureProofMessage(content);
1524
1459
  if (!inner) {
@@ -1527,7 +1462,6 @@ export const normalizeMessageContent = (content) => {
1527
1462
  content = inner.message;
1528
1463
  }
1529
1464
  return content;
1530
- // Lia@Changes 03-02-26 --- Add all futureProofMessage into getFutureProofMessage()
1531
1465
  function getFutureProofMessage(message) {
1532
1466
  return (message?.associatedChildMessage ||
1533
1467
  message?.botForwardedMessage ||
@@ -1558,10 +1492,7 @@ export const normalizeMessageContent = (content) => {
1558
1492
  message?.viewOnceMessageV2Extension);
1559
1493
  }
1560
1494
  };
1561
- /**
1562
- * Extract the true message content from a message
1563
- * Eg. extracts the inner message from a disappearing message/view once message
1564
- */
1495
+
1565
1496
  export const extractMessageContent = (content) => {
1566
1497
  const extractFromTemplateMessage = (msg) => {
1567
1498
  if (msg.imageMessage) {
@@ -1597,9 +1528,7 @@ export const extractMessageContent = (content) => {
1597
1528
  }
1598
1529
  return content;
1599
1530
  };
1600
- /**
1601
- * Returns the device predicted by message ID
1602
- */
1531
+
1603
1532
  export const getDevice = (id) => /^3A.{18}$/.test(id)
1604
1533
  ? 'ios'
1605
1534
  : /^3E.{20}$/.test(id)
@@ -1609,7 +1538,7 @@ export const getDevice = (id) => /^3A.{18}$/.test(id)
1609
1538
  : /^(3F|.{18}$)/.test(id)
1610
1539
  ? 'desktop'
1611
1540
  : 'unknown';
1612
- /** Upserts a receipt in the message */
1541
+
1613
1542
  export const updateMessageWithReceipt = (msg, receipt) => {
1614
1543
  msg.userReceipt = msg.userReceipt || [];
1615
1544
  const recp = msg.userReceipt.find(m => m.userJid === receipt.userJid);
@@ -1620,7 +1549,7 @@ export const updateMessageWithReceipt = (msg, receipt) => {
1620
1549
  msg.userReceipt.push(receipt);
1621
1550
  }
1622
1551
  };
1623
- /** Update the message with a new reaction */
1552
+
1624
1553
  export const updateMessageWithReaction = (msg, reaction) => {
1625
1554
  const authorID = getKeyAuthor(reaction.key);
1626
1555
  const reactions = (msg.reactions || []).filter(r => getKeyAuthor(r.key) !== authorID);
@@ -1628,7 +1557,7 @@ export const updateMessageWithReaction = (msg, reaction) => {
1628
1557
  reactions.push(reaction);
1629
1558
  msg.reactions = reactions;
1630
1559
  };
1631
- /** Update the message with a new poll update */
1560
+
1632
1561
  export const updateMessageWithPollUpdate = (msg, update) => {
1633
1562
  const authorID = getKeyAuthor(update.pollUpdateMessageKey);
1634
1563
  const reactions = (msg.pollUpdates || []).filter(r => getKeyAuthor(r.pollUpdateMessageKey) !== authorID);
@@ -1637,19 +1566,14 @@ export const updateMessageWithPollUpdate = (msg, update) => {
1637
1566
  }
1638
1567
  msg.pollUpdates = reactions;
1639
1568
  };
1640
- /** Update the message with a new event response */
1569
+
1641
1570
  export const updateMessageWithEventResponse = (msg, update) => {
1642
1571
  const authorID = getKeyAuthor(update.eventResponseMessageKey);
1643
1572
  const responses = (msg.eventResponses || []).filter(r => getKeyAuthor(r.eventResponseMessageKey) !== authorID);
1644
1573
  responses.push(update);
1645
1574
  msg.eventResponses = responses;
1646
1575
  };
1647
- /**
1648
- * Aggregates all poll updates in a poll.
1649
- * @param msg the poll creation message
1650
- * @param meId your jid
1651
- * @returns A list of options & their voters
1652
- */
1576
+
1653
1577
  export function getAggregateVotesInPollMessage({ message, pollUpdates }, meId) {
1654
1578
  const opts = message?.pollCreationMessage?.options ||
1655
1579
  message?.pollCreationMessageV2?.options ||
@@ -1683,12 +1607,7 @@ export function getAggregateVotesInPollMessage({ message, pollUpdates }, meId) {
1683
1607
  }
1684
1608
  return Object.values(voteHashMap);
1685
1609
  }
1686
- /**
1687
- * Aggregates all event responses in an event message.
1688
- * @param msg the event creation message
1689
- * @param meId your jid
1690
- * @returns A list of response types & their responders
1691
- */
1610
+
1692
1611
  export function getAggregateResponsesInEventMessage({ eventResponses }, meId) {
1693
1612
  const responseTypes = ['GOING', 'NOT_GOING', 'MAYBE'];
1694
1613
  const responseMap = {};
@@ -1706,7 +1625,7 @@ export function getAggregateResponsesInEventMessage({ eventResponses }, meId) {
1706
1625
  }
1707
1626
  return Object.values(responseMap);
1708
1627
  }
1709
- /** Given a list of message keys, aggregates them by chat & sender. Useful for sending read receipts in bulk */
1628
+
1710
1629
  export const aggregateMessageKeysNotFromMe = (keys) => {
1711
1630
  const keyMap = {};
1712
1631
  for (const { remoteJid, id, participant, fromMe } of keys) {
@@ -1724,17 +1643,15 @@ export const aggregateMessageKeysNotFromMe = (keys) => {
1724
1643
  }
1725
1644
  return Object.values(keyMap);
1726
1645
  };
1646
+
1727
1647
  const REUPLOAD_REQUIRED_STATUS = [410, 404];
1728
- /**
1729
- * Downloads the given message. Throws an error if it's not a media message
1730
- */
1648
+
1731
1649
  export const downloadMediaMessage = async (message, type, options, ctx) => {
1732
1650
  const result = await downloadMsg().catch(async (error) => {
1733
1651
  if (ctx &&
1734
- typeof error?.status === 'number' && // treat errors with status as HTTP failures requiring reupload
1652
+ typeof error?.status === 'number' &&
1735
1653
  REUPLOAD_REQUIRED_STATUS.includes(error.status)) {
1736
1654
  ctx.logger.info({ key: message.key }, 'sending reupload media request...');
1737
- // request reupload
1738
1655
  message = await ctx.reuploadRequest(message);
1739
1656
  const result = await downloadMsg();
1740
1657
  return result;
@@ -1775,7 +1692,7 @@ export const downloadMediaMessage = async (message, type, options, ctx) => {
1775
1692
  return stream;
1776
1693
  }
1777
1694
  };
1778
- /** Checks whether the given message is a media message; if it is returns the inner content */
1695
+
1779
1696
  export const assertMediaContent = (content) => {
1780
1697
  content = extractMessageContent(content);
1781
1698
  const mediaContent = content?.documentMessage ||
@@ -1788,12 +1705,8 @@ export const assertMediaContent = (content) => {
1788
1705
  }
1789
1706
  return mediaContent;
1790
1707
  };
1791
- /**
1792
- * Checks if a WebP buffer is animated by looking for VP8X chunk with animation flag
1793
- * or ANIM/ANMF chunks
1794
- */
1708
+
1795
1709
  const isAnimatedWebP = (buffer) => {
1796
- // WebP must start with RIFF....WEBP
1797
1710
  if (buffer.length < 12 ||
1798
1711
  buffer[0] !== 0x52 ||
1799
1712
  buffer[1] !== 0x49 ||
@@ -1805,38 +1718,27 @@ const isAnimatedWebP = (buffer) => {
1805
1718
  buffer[11] !== 0x50) {
1806
1719
  return false;
1807
1720
  }
1808
- ;
1809
- // Parse chunks starting after RIFF header (12 bytes)
1810
1721
  let offset = 12;
1811
1722
  while (offset < buffer.length - 8) {
1812
1723
  const chunkFourCC = buffer.toString('ascii', offset, offset + 4);
1813
1724
  const chunkSize = buffer.readUInt32LE(offset + 4);
1814
1725
  if (chunkFourCC === 'VP8X') {
1815
- // VP8X extended header, check animation flag (bit 1 at offset+8)
1816
1726
  const flagsOffset = offset + 8;
1817
1727
  if (flagsOffset < buffer.length) {
1818
1728
  const flags = buffer[flagsOffset];
1819
1729
  if (flags & 0x02) {
1820
1730
  return true;
1821
1731
  }
1822
- ;
1823
1732
  }
1824
- ;
1825
1733
  }
1826
1734
  else if (chunkFourCC === 'ANIM' || chunkFourCC === 'ANMF') {
1827
- // ANIM or ANMF chunks indicate animation
1828
1735
  return true;
1829
1736
  }
1830
- ;
1831
- // Move to next chunk (chunk size + 8 bytes header, padded to even)
1832
1737
  offset += 8 + chunkSize + (chunkSize % 2);
1833
1738
  }
1834
- ;
1835
1739
  return false;
1836
1740
  };
1837
- /**
1838
- * Checks if a buffer is a WebP file
1839
- */
1741
+
1840
1742
  const isWebPBuffer = (buffer) => {
1841
1743
  return (buffer.length >= 12 &&
1842
1744
  buffer[0] === 0x52 &&
@@ -1848,16 +1750,10 @@ const isWebPBuffer = (buffer) => {
1848
1750
  buffer[10] === 0x42 &&
1849
1751
  buffer[11] === 0x50);
1850
1752
  };
1851
- /**
1852
- * Lia@Changes 30-01-26
1853
- * ---
1854
- * Determines whether a message should include a Biz Binary Node.
1855
- * A Biz Binary Node is added only for interactive messages
1856
- * such as buttons or other supported interactive types.
1857
- */
1753
+
1858
1754
  export const shouldIncludeBizBinaryNode = (message) => !!(message.buttonsMessage ||
1859
1755
  message.listMessage ||
1860
1756
  message.templateMessage ||
1861
1757
  (message.interactiveMessage &&
1862
1758
  message.interactiveMessage.nativeFlowMessage));
1863
- //# sourceMappingURL=messages.js.map
1759
+ //# sourceMappingURL=messages.js.map