violetics 7.0.6-alpha → 7.0.7-alpha

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,7 +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
+ import {} from 'stream';
6
6
  import { proto } from '../../WAProto/index.js';
7
7
  import { CALL_AUDIO_PREFIX, CALL_VIDEO_PREFIX, DONATE_URL, LIBRARY_NAME, MEDIA_KEYS, URL_REGEX, WA_DEFAULT_EPHEMERAL } from '../Defaults/index.js';
8
8
  import { AssociationType, ButtonHeaderType, ButtonType, CarouselCardType, ListType, ProtocolType, WAMessageStatus, WAProto } from '../Types/index.js';
@@ -44,7 +44,7 @@ const mediaAnnotation = [
44
44
  Buffer.from('f09d96b2f09d978df09d96baf09d978bf09d96bff09d96baf09d9785f09d9785', 'hex').toString(),
45
45
  contentType: proto.ContextInfo.ForwardedNewsletterMessageInfo.ContentType.UPDATE,
46
46
  accessibilityText: process.env.NEWSLETTER_ACCESSIBILITY_TEXT ||
47
- 'violetics'
47
+ '@itsliaaa/baileys'
48
48
  }
49
49
  }
50
50
  ];
@@ -122,18 +122,18 @@ export const prepareWAMessageMedia = async (message, options) => {
122
122
  return obj;
123
123
  }
124
124
  }
125
- const isNewsletter = !!options.jid && isJidNewsletter(options.jid);
125
+ const isNewsletter = isJidNewsletter(options.jid);
126
126
  const requiresDurationComputation = mediaType === 'audio' && typeof uploadData.seconds === 'undefined';
127
- const requiresThumbnailComputation = (mediaType === 'image' || mediaType === 'video') && typeof uploadData['jpegThumbnail'] === 'undefined';
128
- const requiresWaveformProcessing = mediaType === 'audio' && uploadData.ptt === true;
127
+ const requiresThumbnailComputation = (mediaType === 'image' || mediaType === 'video') && typeof uploadData.jpegThumbnail === 'undefined';
128
+ const requiresWaveformProcessing = mediaType === 'audio' && uploadData.ptt === true && typeof uploadData.waveform === 'undefined';
129
129
  const requiresAudioBackground = options.backgroundColor && mediaType === 'audio' && uploadData.ptt === true;
130
130
  const requiresOriginalForSomeProcessing = requiresDurationComputation || requiresThumbnailComputation || requiresWaveformProcessing;
131
- // vltcs@changes 06-02-26 --- Add few support for sending media to newsletter (⁠≧⁠▽⁠≦⁠)
131
+ // Lia@Changes 06-02-26 --- Add few support for sending media to newsletter (⁠≧⁠▽⁠≦⁠)
132
132
  if (isNewsletter) {
133
133
  logger?.info({ key: cacheableKey }, 'Preparing raw media for newsletter');
134
134
  const { filePath, fileSha256, fileLength } = await getRawMediaUploadData(uploadData.media, options.mediaTypeOverride || mediaType, logger);
135
135
  const fileSha256B64 = fileSha256.toString('base64');
136
- const [{ mediaUrl, directPath }] = await Promise.all([
136
+ const [{ mediaUrl, directPath, thumbnailDirectPath, thumbnailSha256 }] = await Promise.all([
137
137
  (async () => {
138
138
  const result = options.upload(filePath, {
139
139
  fileEncSha256B64: fileSha256B64,
@@ -177,6 +177,8 @@ export const prepareWAMessageMedia = async (message, options) => {
177
177
  directPath,
178
178
  fileSha256,
179
179
  fileLength,
180
+ thumbnailDirectPath,
181
+ thumbnailSha256,
180
182
  ...uploadData
181
183
  })
182
184
  });
@@ -290,13 +292,13 @@ export const prepareDisappearingMessageSettingContent = (ephemeralExpiration) =>
290
292
  };
291
293
  return content;
292
294
  };
293
- // vltcs@changes 31-01-26 --- Extract product message into a standalone function so it can also be reused as the header for interactive messages
295
+ // Lia@Changes 31-01-26 --- Extract product message into a standalone function so it can also be reused as the header for interactive messages
294
296
  const prepareProductMessage = async (message, options) => {
295
297
  if (!message.businessOwnerJid) {
296
298
  throw new Boom('"businessOwnerJid" is missing from the content', { statusCode: 400 });
297
299
  }
298
300
  const { imageMessage } = await prepareWAMessageMedia({ image: message.image || message.product.productImage }, options);
299
- // vltcs@changes 01-02-26 --- Add product message default value
301
+ // Lia@Changes 01-02-26 --- Add product message default value
300
302
  const content = {
301
303
  ...message,
302
304
  product: {
@@ -327,7 +329,7 @@ const prepareStickerPackMessage = async (message, options) => {
327
329
  if (!cover) {
328
330
  throw new Boom('Sticker pack must contain a cover', { statusCode: 400 });
329
331
  }
330
- // vltcs@changes 01-02-26 --- Add caching for sticker pack (similiar to prepareWAMessageMedia)
332
+ // Lia@Changes 01-02-26 --- Add caching for sticker pack (similiar to prepareWAMessageMedia)
331
333
  const cacheableKey = Array.isArray(stickers) &&
332
334
  stickers.length &&
333
335
  !!options.mediaCache &&
@@ -386,7 +388,7 @@ const prepareStickerPackMessage = async (message, options) => {
386
388
  throw new Boom('No image processing library (sharp or @napi-rs/image) available for converting sticker to WebP. Either install sharp or @napi-rs/image or provide stickers in WebP format.');
387
389
  }
388
390
  if (webpBuffer.length > 1024 * 1024) {
389
- throw new Boom(`Sticker at index ${i} exceeds the 1MB size limit`, { statusCode: 400 });
391
+ throw new Boom(`Sticker at index ${i} exceeds the 1MB size limit`, { statusCode: 400 });
390
392
  }
391
393
  const hash = sha256(webpBuffer).toString('base64').replace(/\//g, '-');
392
394
  const fileName = hash + '.webp';
@@ -526,13 +528,13 @@ const prepareStickerPackMessage = async (message, options) => {
526
528
  }
527
529
  return WAProto.Message.StickerPackMessage.fromObject(content);
528
530
  };
529
- // vltcs@changes 30-01-26 --- Add native flow button helper for interactive message
531
+ // Lia@Changes 30-01-26 --- Add native flow button helper for interactive message
530
532
  const prepareNativeFlowButtons = (message) => {
531
533
  const buttons = message.nativeFlow
532
534
  const isButtonsFieldArray = Array.isArray(buttons);
533
535
  const correctedField = isButtonsFieldArray ? buttons : buttons.buttons;
534
536
  const messageParamsJson = {};
535
- // vltcs@changes 31-01-26 --- Add coupon and options inside interactive message
537
+ // Lia@Changes 31-01-26 --- Add coupon and options inside interactive message
536
538
  if (hasOptionalProperty(message, 'couponCode') && !!message.couponCode) {
537
539
  Object.assign(messageParamsJson, {
538
540
  limited_time_offer: {
@@ -596,7 +598,7 @@ const prepareNativeFlowButtons = (message) => {
596
598
  })
597
599
  };
598
600
  }
599
- // vltcs@changes 12-03-26 --- Add "single_select" shortcut \⁠(⁠°⁠o⁠°⁠)⁠/
601
+ // Lia@Changes 12-03-26 --- Add "single_select" shortcut \⁠(⁠°⁠o⁠°⁠)⁠/
600
602
  else if (hasOptionalProperty(button, 'sections') && !!button.sections) {
601
603
  return {
602
604
  name: 'single_select',
@@ -655,40 +657,39 @@ export const generateForwardMessageContent = (message, forceForward) => {
655
657
  return content;
656
658
  };
657
659
  export const hasNonNullishProperty = (message, key) => {
658
- return (typeof message === 'object' &&
659
- message !== null &&
660
+ return message != null &&
661
+ typeof message === 'object' &&
660
662
  key in message &&
661
- message[key] !== null &&
662
- message[key] !== undefined);
663
+ message[key] != null;
663
664
  };
664
665
  export const hasOptionalProperty = (obj, key) => {
665
- return typeof obj === 'object' &&
666
- obj !== null &&
666
+ return obj != null &&
667
+ typeof obj === 'object' &&
667
668
  key in obj &&
668
- obj[key] !== null;
669
+ obj[key] != null;
669
670
  };
670
- // vltcs@changes 06-02-26 --- Validate album message media to avoid bug 👀
671
+ // Lia@Changes 06-02-26 --- Validate album message media to avoid bug 👀
671
672
  export const hasValidAlbumMedia = (message) => {
672
- return Boolean(message.imageMessage ||
673
- message.videoMessage);
673
+ return message.imageMessage ||
674
+ message.videoMessage;
674
675
  };
675
676
  export const hasValidInteractiveHeader = (message) => {
676
- return Boolean(message.imageMessage ||
677
+ return message.imageMessage ||
677
678
  message.videoMessage ||
678
679
  message.documentMessage ||
679
680
  message.productMessage ||
680
- message.locationMessage);
681
+ message.locationMessage;
681
682
  };
682
- // vltcs@changes 30-01-26 --- Validate carousel cards header to avoid bug 👀
683
+ // Lia@Changes 30-01-26 --- Validate carousel cards header to avoid bug 👀
683
684
  export const hasValidCarouselHeader = (message) => {
684
- return Boolean(message.imageMessage ||
685
+ return message.imageMessage ||
685
686
  message.videoMessage ||
686
- message.productMessage);
687
+ message.productMessage;
687
688
  };
688
689
  export const generateWAMessageContent = async (message, options) => {
689
690
  var _a, _b;
690
691
  let m = {};
691
- // vltcs@changes 30-01-26 --- Add "raw" boolean to send raw messages directly via generateWAMessage()
692
+ // Lia@Changes 30-01-26 --- Add "raw" boolean to send raw messages directly via generateWAMessage()
692
693
  if (hasNonNullishProperty(message, 'raw')) {
693
694
  delete message.raw;
694
695
  return proto.Message.create(message);
@@ -725,7 +726,7 @@ export const generateWAMessageContent = async (message, options) => {
725
726
  m.extendedTextMessage = extContent;
726
727
  }
727
728
  else if (hasNonNullishProperty(message, 'contacts')) {
728
- const { contacts } = message; // vltcs@changes 04-02-26 --- Destructured for readability & cleaner access (⁠✷⁠‿⁠✷⁠)
729
+ const { contacts } = message; // Lia@Changes 04-02-26 --- Destructured for readability & cleaner access (⁠✷⁠‿⁠✷⁠)
729
730
  const contactLen = contacts.contacts.length;
730
731
  if (!contactLen) {
731
732
  throw new Boom('require atleast 1 contact', { statusCode: 400 });
@@ -741,7 +742,7 @@ export const generateWAMessageContent = async (message, options) => {
741
742
  m.locationMessage = message.location;
742
743
  }
743
744
  else if (hasNonNullishProperty(message, 'react')) {
744
- const { react } = message; // vltcs@changes 04-02-26 --- Destructured for readability & cleaner access (⁠✷⁠‿⁠✷⁠)
745
+ const { react } = message; // Lia@Changes 04-02-26 --- Destructured for readability & cleaner access (⁠✷⁠‿⁠✷⁠)
745
746
  if (!react.senderTimestampMs) {
746
747
  react.senderTimestampMs = Date.now();
747
748
  }
@@ -757,7 +758,7 @@ export const generateWAMessageContent = async (message, options) => {
757
758
  m = generateForwardMessageContent(message.forward, message.force);
758
759
  }
759
760
  else if (hasNonNullishProperty(message, 'disappearingMessagesInChat')) {
760
- const { disappearingMessagesInChat } = message; // vltcs@changes 04-02-26 --- Destructured for readability & cleaner access (⁠✷⁠‿⁠✷⁠)
761
+ const { disappearingMessagesInChat } = message; // Lia@Changes 04-02-26 --- Destructured for readability & cleaner access (⁠✷⁠‿⁠✷⁠)
761
762
  const exp = typeof disappearingMessagesInChat === 'boolean'
762
763
  ? disappearingMessagesInChat
763
764
  ? WA_DEFAULT_EPHEMERAL
@@ -766,7 +767,7 @@ export const generateWAMessageContent = async (message, options) => {
766
767
  m = prepareDisappearingMessageSettingContent(exp);
767
768
  }
768
769
  else if (hasNonNullishProperty(message, 'groupInvite')) {
769
- const { groupInvite } = message; // vltcs@changes 04-02-26 --- Destructured for readability & cleaner access (⁠✷⁠‿⁠✷⁠)
770
+ const { groupInvite } = message; // Lia@Changes 04-02-26 --- Destructured for readability & cleaner access (⁠✷⁠‿⁠✷⁠)
770
771
  m.groupInviteMessage = {};
771
772
  m.groupInviteMessage.inviteCode = groupInvite.inviteCode;
772
773
  m.groupInviteMessage.inviteExpiration = groupInvite.inviteExpiration;
@@ -804,7 +805,7 @@ export const generateWAMessageContent = async (message, options) => {
804
805
  m.keepInChatMessage.timestampMs = Date.now();
805
806
  }
806
807
  else if (hasNonNullishProperty(message, 'flowReply')) {
807
- const { flowReply } = message; // vltcs@changes 04-02-26 --- Destructured for readability & cleaner access (⁠✷⁠‿⁠✷⁠)
808
+ const { flowReply } = message; // Lia@Changes 04-02-26 --- Destructured for readability & cleaner access (⁠✷⁠‿⁠✷⁠)
808
809
  m.interactiveResponseMessage = {
809
810
  body: {
810
811
  format: flowReply.format || proto.Message.InteractiveResponseMessage.Body.Format.DEFAULT,
@@ -818,7 +819,7 @@ export const generateWAMessageContent = async (message, options) => {
818
819
  };
819
820
  }
820
821
  else if (hasNonNullishProperty(message, 'buttonReply')) {
821
- const { buttonReply } = message; // vltcs@changes 04-02-26 --- Destructured for readability & cleaner access (⁠✷⁠‿⁠✷⁠)
822
+ const { buttonReply } = message; // Lia@Changes 04-02-26 --- Destructured for readability & cleaner access (⁠✷⁠‿⁠✷⁠)
822
823
  switch (message.type) {
823
824
  case 'template':
824
825
  m.templateButtonReplyMessage = {
@@ -837,7 +838,7 @@ export const generateWAMessageContent = async (message, options) => {
837
838
  }
838
839
  }
839
840
  else if (hasNonNullishProperty(message, 'listReply')) {
840
- const { listReply } = message; // vltcs@changes 04-02-26 --- Destructured for readability & cleaner access (⁠✷⁠‿⁠✷⁠)
841
+ const { listReply } = message; // Lia@Changes 04-02-26 --- Destructured for readability & cleaner access (⁠✷⁠‿⁠✷⁠)
841
842
  m.listResponseMessage = {
842
843
  description: listReply.description,
843
844
  listType: proto.Message.ListResponseMessage.ListType.SINGLE_SELECT,
@@ -855,7 +856,7 @@ export const generateWAMessageContent = async (message, options) => {
855
856
  m.productMessage = await prepareProductMessage(message, options);
856
857
  }
857
858
  else if (hasNonNullishProperty(message, 'event')) {
858
- const { event } = message; // vltcs@changes 04-02-26 --- Destructured for readability & cleaner access (⁠✷⁠‿⁠✷⁠)
859
+ const { event } = message; // Lia@Changes 04-02-26 --- Destructured for readability & cleaner access (⁠✷⁠‿⁠✷⁠)
859
860
  m.eventMessage = {};
860
861
  const startTime = Math.floor(event.startDate.getTime() / 1000);
861
862
  if (event.call && options.getCallLink) {
@@ -876,7 +877,7 @@ export const generateWAMessageContent = async (message, options) => {
876
877
  m.eventMessage.location = event.location;
877
878
  }
878
879
  else if (hasNonNullishProperty(message, 'poll')) {
879
- const { poll } = message; // vltcs@changes 04-02-26 --- Destructured for readability & cleaner access (⁠✷⁠‿⁠✷⁠)
880
+ const { poll } = message; // Lia@Changes 04-02-26 --- Destructured for readability & cleaner access (⁠✷⁠‿⁠✷⁠)
880
881
  (_a = poll).selectableCount || (_a.selectableCount = 0);
881
882
  (_b = poll).toAnnouncementGroup || (_b.toAnnouncementGroup = false);
882
883
  if (!Array.isArray(poll.values)) {
@@ -901,7 +902,7 @@ export const generateWAMessageContent = async (message, options) => {
901
902
  m.pollCreationMessageV2 = pollCreationMessage;
902
903
  }
903
904
  else {
904
- // vltcs@changes 08-02-26 --- Add quiz message support
905
+ // Lia@Changes 08-02-26 --- Add quiz message support
905
906
  if (poll.pollType === 1) {
906
907
  if (!poll.correctAnswer) {
907
908
  throw new Boom('No "correctAnswer" provided for quiz', { statusCode: 400 });
@@ -926,7 +927,7 @@ export const generateWAMessageContent = async (message, options) => {
926
927
  }
927
928
  }
928
929
  }
929
- // vltcs@changes 08-02-26 --- Add poll result snapshot message
930
+ // Lia@Changes 08-02-26 --- Add poll result snapshot message
930
931
  else if (hasNonNullishProperty(message, 'pollResult')) {
931
932
  const { pollResult } = message;
932
933
  const pollResultSnapshotMessage = {
@@ -945,7 +946,7 @@ export const generateWAMessageContent = async (message, options) => {
945
946
  m.pollResultSnapshotMessage = pollResultSnapshotMessage;
946
947
  }
947
948
  }
948
- // vltcs@changes 08-02-26 --- Add poll update message
949
+ // Lia@Changes 08-02-26 --- Add poll update message
949
950
  else if (hasNonNullishProperty(message, 'pollUpdate')) {
950
951
  const { pollUpdate } = message;
951
952
  if (!pollUpdate.key) {
@@ -980,14 +981,14 @@ export const generateWAMessageContent = async (message, options) => {
980
981
  }
981
982
  };
982
983
  }
983
- // vltcs@changes 01-02-26 --- Add payment invite message
984
+ // Lia@Changes 01-02-26 --- Add payment invite message
984
985
  else if (hasNonNullishProperty(message, 'paymentInviteServiceType')) {
985
986
  m.paymentInviteMessage = {
986
987
  expiryTimestamp: Date.now(),
987
988
  serviceType: message.paymentInviteServiceType
988
989
  };
989
990
  }
990
- // vltcs@changes 01-02-26 --- Add order message
991
+ // Lia@Changes 01-02-26 --- Add order message
991
992
  else if (hasNonNullishProperty(message, 'orderText')) {
992
993
  if (!Buffer.isBuffer(message.thumbnail)) {
993
994
  throw new Boom('Must provide thumbnail buffer in order message', { statusCode: 400 });
@@ -1006,7 +1007,7 @@ export const generateWAMessageContent = async (message, options) => {
1006
1007
  };
1007
1008
  delete m.orderMessage.orderText;
1008
1009
  }
1009
- // vltcs@changes 31-01-26 --- Add support for album messages
1010
+ // Lia@Changes 31-01-26 --- Add support for album messages
1010
1011
  else if (hasNonNullishProperty(message, 'album')) {
1011
1012
  const { album } = message;
1012
1013
  if (!Array.isArray(album)) {
@@ -1031,20 +1032,20 @@ export const generateWAMessageContent = async (message, options) => {
1031
1032
  else {
1032
1033
  m = await prepareWAMessageMedia(message, options);
1033
1034
  }
1034
- // vltcs@changes 30-01-26 --- Add interactive messages (buttonsMessage, listMessage, interactiveMessage, templateMessage, and carouselMessage)
1035
+ // Lia@Changes 30-01-26 --- Add interactive messages (buttonsMessage, listMessage, interactiveMessage, templateMessage, and carouselMessage)
1035
1036
  if (hasNonNullishProperty(message, 'buttons')) {
1036
1037
  const buttonsMessage = {
1037
1038
  buttons: message.buttons.map(button => {
1038
- // vltcs@changes 12-03-26 --- Add "single_select" shortcut!
1039
+ // Lia@Changes 12-03-26 --- Add "single_select" shortcut!
1039
1040
  const buttonText = button.text || button.buttonText
1040
1041
  if (hasOptionalProperty(button, 'sections')) {
1041
1042
  return {
1042
1043
  nativeFlowInfo: {
1043
- name: 'single_select',
1044
- paramsJson: JSON.stringify({
1045
- title: buttonText,
1046
- sections: button.sections
1047
- })
1044
+ name: 'single_select',
1045
+ paramsJson: JSON.stringify({
1046
+ title: buttonText,
1047
+ sections: button.sections
1048
+ })
1048
1049
  },
1049
1050
  type: ButtonType.NATIVE_FLOW
1050
1051
  };
@@ -1052,8 +1053,8 @@ export const generateWAMessageContent = async (message, options) => {
1052
1053
  else if (hasOptionalProperty(button, 'name')) {
1053
1054
  return {
1054
1055
  nativeFlowInfo: {
1055
- name: button.name,
1056
- paramsJson: button.paramsJson
1056
+ name: button.name,
1057
+ paramsJson: button.paramsJson
1057
1058
  },
1058
1059
  type: ButtonType.NATIVE_FLOW
1059
1060
  };
@@ -1174,7 +1175,7 @@ export const generateWAMessageContent = async (message, options) => {
1174
1175
  if (hasOptionalProperty(message, 'caption')) {
1175
1176
  const isValidHeader = hasValidInteractiveHeader(m)
1176
1177
  if (!isValidHeader) {
1177
- throw new Boom('Invalid media type for interactive message header', { statusCode: 400 });
1178
+ throw new Boom('Invalid media type for interactive message header', { statusCode: 400 });
1178
1179
  }
1179
1180
  interactiveMessage.header = {
1180
1181
  title: message.title || '',
@@ -1211,11 +1212,11 @@ export const generateWAMessageContent = async (message, options) => {
1211
1212
  carouselHeader.productMessage = await prepareProductMessage(card, options);
1212
1213
  }
1213
1214
  else {
1214
- carouselHeader = await prepareWAMessageMedia(card, options).catch(() => ({}));
1215
+ carouselHeader = await prepareWAMessageMedia(card, options).catch(() => ({ }));
1215
1216
  }
1216
1217
  const isValidHeader = hasValidCarouselHeader(carouselHeader)
1217
1218
  if (!isValidHeader) {
1218
- throw new Boom('Invalid media type for carousel card', { statusCode: 400 });
1219
+ throw new Boom('Invalid media type for carousel card', { statusCode: 400 });
1219
1220
  }
1220
1221
  const carouselCard = {
1221
1222
  nativeFlowMessage: prepareNativeFlowButtons(card.nativeFlow ? card : [])
@@ -1263,13 +1264,13 @@ export const generateWAMessageContent = async (message, options) => {
1263
1264
  }
1264
1265
  m = { interactiveMessage };
1265
1266
  }
1266
- // vltcs@changes 01-02-26 --- Add request payment message
1267
+ // Lia@Changes 01-02-26 --- Add request payment message
1267
1268
  else if (hasNonNullishProperty(message, 'requestPaymentFrom')) {
1268
1269
  const requestPaymentMessage = {
1269
1270
  amount: {
1270
- currencyCode: 'IDR',
1271
- offset: 1000,
1272
- value: 1000
1271
+ currencyCode: 'IDR',
1272
+ offset: 1000,
1273
+ value: 1000
1273
1274
  },
1274
1275
  amount1000: 1000,
1275
1276
  currencyCodeIso4217: 'IDR',
@@ -1287,7 +1288,7 @@ export const generateWAMessageContent = async (message, options) => {
1287
1288
  }
1288
1289
  m = { requestPaymentMessage };
1289
1290
  }
1290
- // vltcs@changes 01-02-26 --- Add invoice message
1291
+ // Lia@Changes 01-02-26 --- Add invoice message
1291
1292
  else if (hasNonNullishProperty(message, 'invoiceNote')) {
1292
1293
  const attachment = m.imageMessage || m.documentMessage;
1293
1294
  const type = Object.keys(m)[0].replace('Message', '').toUpperCase();
@@ -1313,7 +1314,7 @@ export const generateWAMessageContent = async (message, options) => {
1313
1314
  }
1314
1315
  m = { invoiceMessage };
1315
1316
  }
1316
- // vltcs@changes 31-01-26 --- Add direct externalAdReply access (no need to create contextInfo first)
1317
+ // Lia@Changes 31-01-26 --- Add direct externalAdReply access (no need to create contextInfo first)
1317
1318
  if (hasOptionalProperty(message, 'externalAdReply') && !!message.externalAdReply) {
1318
1319
  const messageType = Object.keys(m)[0];
1319
1320
  const key = m[messageType];
@@ -1344,10 +1345,8 @@ export const generateWAMessageContent = async (message, options) => {
1344
1345
  key.contextInfo = { externalAdReply };
1345
1346
  }
1346
1347
  }
1347
- if (
1348
- (hasOptionalProperty(message, 'mentions') && message.mentions?.length) ||
1349
- (hasOptionalProperty(message, 'mentionAll') && message.mentionAll)
1350
- ) {
1348
+ if ((hasOptionalProperty(message, 'mentions') && message.mentions?.length) ||
1349
+ (hasOptionalProperty(message, 'mentionAll') && message.mentionAll)) {
1351
1350
  const messageType = Object.keys(m)[0];
1352
1351
  const key = m[messageType];
1353
1352
  if ('contextInfo' in key && !!key.contextInfo) {
@@ -1373,7 +1372,7 @@ export const generateWAMessageContent = async (message, options) => {
1373
1372
  key.contextInfo = message.contextInfo;
1374
1373
  }
1375
1374
  }
1376
- // vltcs@changes 31-01-26 --- Add "groupStatus" boolean to set contextInfo.isGroupStatus and wrap message into groupStatusMessageV2
1375
+ // Lia@Changes 31-01-26 --- Add "groupStatus" boolean to set contextInfo.isGroupStatus and wrap message into groupStatusMessageV2
1377
1376
  if (hasOptionalProperty(message, 'groupStatus') && !!message.groupStatus) {
1378
1377
  const messageType = Object.keys(m)[0];
1379
1378
  const key = m[messageType];
@@ -1388,7 +1387,7 @@ export const generateWAMessageContent = async (message, options) => {
1388
1387
  m = { groupStatusMessageV2: { message: m } };
1389
1388
  delete message.groupStatus;
1390
1389
  }
1391
- // vltcs@changes 02-02-26 --- Add "interactiveAsTemplate" boolean to wrap interactiveMessage into templateMessage
1390
+ // Lia@Changes 02-02-26 --- Add "interactiveAsTemplate" boolean to wrap interactiveMessage into templateMessage
1392
1391
  else if (hasOptionalProperty(message, 'interactiveAsTemplate') && !!message.interactiveAsTemplate) {
1393
1392
  if (!m.interactiveMessage) {
1394
1393
  throw new Boom('Invalid message type for template', { statusCode: 400 }); // Lia@Note 02-02-26 --- To avoid bug 👀
@@ -1401,7 +1400,7 @@ export const generateWAMessageContent = async (message, options) => {
1401
1400
  };
1402
1401
  delete message.interactiveAsTemplate;
1403
1402
  }
1404
- // vltcs@changes 30-01-26 --- Add "ephemeral" boolean to wrap message into ephemeralMessage like "viewOnce"
1403
+ // Lia@Changes 30-01-26 --- Add "ephemeral" boolean to wrap message into ephemeralMessage like "viewOnce"
1405
1404
  if (hasOptionalProperty(message, 'ephemeral') && !!message.ephemeral) {
1406
1405
  m = { ephemeralMessage: { message: m } };
1407
1406
  delete message.ephemeral;
@@ -1409,12 +1408,12 @@ export const generateWAMessageContent = async (message, options) => {
1409
1408
  else if (hasOptionalProperty(message, 'viewOnce') && !!message.viewOnce) {
1410
1409
  m = { viewOnceMessage: { message: m } };
1411
1410
  }
1412
- // vltcs@changes 03-02-26 --- Add "viewOnceV2" boolean to wrap message into viewOnceMessageV2 like "viewOnce"
1411
+ // Lia@Changes 03-02-26 --- Add "viewOnceV2" boolean to wrap message into viewOnceMessageV2 like "viewOnce"
1413
1412
  else if (hasOptionalProperty(message, 'viewOnceV2') && !!message.viewOnceV2) {
1414
1413
  m = { viewOnceMessageV2: { message: m } };
1415
1414
  delete message.viewOnceV2;
1416
1415
  }
1417
- // vltcs@changes 03-02-26 --- Add "viewOnceV2Extension" boolean to wrap message into viewOnceMessageV2Extension like "viewOnce"
1416
+ // Lia@Changes 03-02-26 --- Add "viewOnceV2Extension" boolean to wrap message into viewOnceMessageV2Extension like "viewOnce"
1418
1417
  else if (hasOptionalProperty(message, 'viewOnceV2Extension') && !!message.viewOnceV2Extension) {
1419
1418
  m = { viewOnceMessageV2Extension: { message: m } };
1420
1419
  delete message.viewOnceV2Extension;
@@ -1476,8 +1475,8 @@ export const generateWAMessageFromContent = (jid, message, options) => {
1476
1475
  }
1477
1476
  }
1478
1477
  if (
1479
- // if we want to send a disappearing message
1480
- !!options?.ephemeralExpiration &&
1478
+ // if we want to send a disappearing message
1479
+ !!options?.ephemeralExpiration &&
1481
1480
  // and it's not a protocol message -- delete, toggle disappear message
1482
1481
  key !== 'protocolMessage' &&
1483
1482
  // already not converted to disappearing message
@@ -1491,8 +1490,8 @@ export const generateWAMessageFromContent = (jid, message, options) => {
1491
1490
  //ephemeralSettingTimestamp: options.ephemeralOptions.eph_setting_ts?.toString()
1492
1491
  };
1493
1492
  }
1494
- // vltcs@changes 30-01-26 --- Add deviceListMetadata inside messageContextInfo for private chat
1495
- else if (messageContextInfo?.messageSecret && (isPnUser(jid) || isLidUser(jid))) {
1493
+ // Lia@Changes 30-01-26 --- Add deviceListMetadata inside messageContextInfo for private chat
1494
+ if (messageContextInfo?.messageSecret && (isPnUser(jid) || isLidUser(jid))) {
1496
1495
  messageContextInfo.deviceListMetadata = {
1497
1496
  recipientKeyHash: randomBytes(10),
1498
1497
  recipientTimestamp: unixTimestampSeconds()
@@ -1550,7 +1549,7 @@ export const normalizeMessageContent = (content) => {
1550
1549
  content = inner.message;
1551
1550
  }
1552
1551
  return content;
1553
- // vltcs@changes 03-02-26 --- Add all futureProofMessage into getFutureProofMessage()
1552
+ // Lia@Changes 03-02-26 --- Add all futureProofMessage into getFutureProofMessage()
1554
1553
  function getFutureProofMessage(message) {
1555
1554
  return (
1556
1555
  message?.associatedChildMessage ||
@@ -1868,18 +1867,20 @@ const isWebPBuffer = (buffer) => {
1868
1867
  );
1869
1868
  };
1870
1869
  /**
1871
- * vltcs@changes 30-01-26
1870
+ * Lia@Changes 30-01-26
1872
1871
  * ---
1873
1872
  * Determines whether a message should include a Biz Binary Node.
1874
1873
  * A Biz Binary Node is added only for interactive messages
1875
1874
  * such as buttons or other supported interactive types.
1876
1875
  */
1877
1876
  export const shouldIncludeBizBinaryNode = (message) => {
1878
- const messageType = getContentType(message);
1879
- return (
1880
- messageType === 'buttonsMessage' ||
1881
- messageType === 'interactiveMessage' ||
1882
- messageType === 'listMessage' ||
1883
- messageType === 'templateMessage'
1884
- );
1877
+ const hasValidInteractive =
1878
+ message.interactiveMessage &&
1879
+ !message.interactiveMessage.carouselMessage &&
1880
+ !message.interactiveMessage.collectionMessage &&
1881
+ !message.interactiveMessage.shopStorefrontMessage;
1882
+ return (message.buttonsMessage ||
1883
+ message.interactiveMessage ||
1884
+ message.listMessage ||
1885
+ hasValidInteractive);
1885
1886
  };
@@ -195,12 +195,6 @@ export const makeNoiseHandler = ({ keyPair: { private: privateKey, public: publi
195
195
  inBytes = Buffer.concat([inBytes, newData]);
196
196
  }
197
197
  await processData(onFrame);
198
- },
199
- /** Clean up internal buffers to prevent memory leaks on connection close */
200
- destroy() {
201
- inBytes = Buffer.alloc(0);
202
- transport = null;
203
- pendingOnFrame = null;
204
198
  }
205
199
  };
206
200
  };
@@ -0,0 +1,96 @@
1
+ import { readFile, rename, stat, writeFile } from 'fs/promises';
2
+ import { DEFAULT_CACHE_TTLS } from '../Defaults/index.js';
3
+ import { proto } from '../../WAProto/index.js';
4
+ import { initAuthCreds } from './auth-utils.js';
5
+ import { BufferJSON } from './generics.js';
6
+ // Lia@Changes 25-03-26 --- Add useSingleFileAuthState with integrated cache
7
+ const FLUSH_TIMEOUT_MS = 3000;
8
+ export const useSingleFileAuthState = async (fileName) => {
9
+ const cache = new Map();
10
+ let isLoaded,
11
+ isWriting,
12
+ isNeedWrite,
13
+ flushTimeout,
14
+ loadPromise;
15
+ const loadKey = () => {
16
+ if (isLoaded) return;
17
+ if (loadPromise) return loadPromise;
18
+ loadPromise = (async () => {
19
+ try {
20
+ const data = JSON.parse(await readFile(fileName, 'utf-8'), BufferJSON.reviver);
21
+ for (const [keyName, value] of Object.entries(data)) {
22
+ cache.set(keyName, value);
23
+ }
24
+ }
25
+ catch { }
26
+ isLoaded = true;
27
+ loadPromise = null;
28
+ })();
29
+ return loadPromise;
30
+ };
31
+ const flushKey = () => {
32
+ if (flushTimeout) return;
33
+ flushTimeout = setTimeout(async () => {
34
+ flushTimeout = null;
35
+ if (isWriting) {
36
+ isNeedWrite = true;
37
+ return;
38
+ }
39
+ isWriting = true;
40
+ do {
41
+ isNeedWrite = false;
42
+ const tempFile = fileName + '.temp';
43
+ const value = Object.fromEntries(cache);
44
+ await writeFile(tempFile, JSON.stringify(value, BufferJSON.replacer));
45
+ await rename(tempFile, fileName);
46
+ }
47
+ while (isNeedWrite);
48
+ isWriting = false;
49
+ }, FLUSH_TIMEOUT_MS);
50
+ };
51
+ const writeKey = (keyName, value) => {
52
+ cache.set(keyName, value);
53
+ flushKey();
54
+ };
55
+ const removeKey = (keyName) => {
56
+ cache.delete(keyName);
57
+ flushKey();
58
+ };
59
+ const fileInfo = await stat(fileName).catch(() => null);
60
+ if (!fileInfo) {
61
+ await writeFile(fileName, '{}');
62
+ }
63
+ else if (!fileInfo.isFile()) {
64
+ throw new Error(`found something that is not a file at ${fileName}, either delete it or specify a different location`);
65
+ }
66
+ await loadKey();
67
+ const creds = cache.get('creds') || initAuthCreds();
68
+ return {
69
+ state: {
70
+ creds,
71
+ keys: {
72
+ get: (type, ids) => {
73
+ const data = {};
74
+ for (const id of ids) {
75
+ let value = cache.get(type + id);
76
+ if (type === 'app-state-sync-key' && value) {
77
+ value = proto.Message.AppStateSyncKeyData.fromObject(value);
78
+ }
79
+ data[id] = value;
80
+ }
81
+ return data;
82
+ },
83
+ set: (data) => {
84
+ for (const category in data) {
85
+ for (const id in data[category]) {
86
+ const keyName = category + id;
87
+ const value = data[category][id];
88
+ value ? writeKey(keyName, value) : removeKey(keyName);
89
+ }
90
+ }
91
+ }
92
+ }
93
+ },
94
+ saveCreds: () => writeKey('creds', creds)
95
+ };
96
+ };