@kelvdra/baileys 1.0.4 → 1.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/LICENSE +21 -0
  2. package/WAProto/index.js +65472 -137440
  3. package/lib/Defaults/index.d.ts +1 -1
  4. package/lib/Defaults/index.js +22 -3
  5. package/lib/Socket/chats.js +12 -13
  6. package/lib/Socket/groups.js +140 -7
  7. package/lib/Socket/hydra.js +44 -0
  8. package/lib/Socket/messages-recv.js +736 -324
  9. package/lib/Socket/messages-send.js +481 -110
  10. package/lib/Socket/mex.js +44 -6
  11. package/lib/Socket/newsletter.d.ts +16 -9
  12. package/lib/Socket/newsletter.js +259 -70
  13. package/lib/Types/Mex.d.ts +141 -0
  14. package/lib/Types/Mex.js +37 -0
  15. package/lib/Types/State.js +54 -1
  16. package/lib/Utils/auth-utils.js +12 -1
  17. package/lib/Utils/chat-utils.js +36 -2
  18. package/lib/Utils/companion-reg-client-utils.d.ts +17 -0
  19. package/lib/Utils/companion-reg-client-utils.js +35 -0
  20. package/lib/Utils/decode-wa-message.js +23 -4
  21. package/lib/Utils/generics.js +4 -1
  22. package/lib/Utils/identity-change-handler.d.ts +44 -0
  23. package/lib/Utils/identity-change-handler.js +50 -0
  24. package/lib/Utils/index.js +1 -1
  25. package/lib/Utils/message-retry-manager.js +25 -1
  26. package/lib/Utils/messages-media.js +162 -43
  27. package/lib/Utils/messages.d.ts +1 -1
  28. package/lib/Utils/messages.js +230 -9
  29. package/lib/Utils/offline-node-processor.d.ts +17 -0
  30. package/lib/Utils/offline-node-processor.js +40 -0
  31. package/lib/Utils/reporting-utils.d.ts +11 -0
  32. package/lib/Utils/reporting-utils.js +258 -0
  33. package/lib/Utils/signal.js +45 -1
  34. package/lib/Utils/stanza-ack.d.ts +11 -0
  35. package/lib/Utils/stanza-ack.js +38 -0
  36. package/lib/Utils/sync-action-utils.d.ts +19 -0
  37. package/lib/Utils/sync-action-utils.js +49 -0
  38. package/lib/Utils/tc-token-utils.d.ts +37 -0
  39. package/lib/Utils/tc-token-utils.js +163 -0
  40. package/lib/WAUSync/Protocols/USyncUsernameProtocol.d.ts +10 -0
  41. package/lib/WAUSync/Protocols/USyncUsernameProtocol.js +25 -0
  42. package/package.json +3 -1
@@ -2,17 +2,20 @@ import NodeCache from '@cacheable/node-cache';
2
2
  import { Boom } from '@hapi/boom';
3
3
  import { proto } from '../../WAProto/index.js';
4
4
  import { DEFAULT_CACHE_TTLS, WA_DEFAULT_EPHEMERAL } from '../Defaults/index.js';
5
- import { aggregateMessageKeysNotFromMe, assertMediaContent, bindWaitForEvent, decryptMediaRetryData, encodeNewsletterMessage, encodeSignedDeviceIdentity, encodeWAMessage, encryptMediaRetryRequest, extractDeviceJids, generateMessageIDV2, generateParticipantHashV2, generateWAMessage, getStatusCodeForMediaRetry, getUrlFromDirectPath, getWAUploadToServer, MessageRetryManager, normalizeMessageContent, parseAndInjectE2ESessions, unixTimestampSeconds } from '../Utils/index.js';
5
+ import * as Utils_1 from "../Utils/index.js"
6
+ import { aggregateMessageKeysNotFromMe, assertMediaContent, assertMeId, bindWaitForEvent, decryptMediaRetryData, encodeNewsletterMessage, encodeSignedDeviceIdentity, encodeWAMessage, encryptMediaRetryRequest, extractDeviceJids, generateMessageIDV2, generateParticipantHashV2, generateWAMessage, getStatusCodeForMediaRetry, getUrlFromDirectPath, getWAUploadToServer, MessageRetryManager, normalizeMessageContent, parseAndInjectE2ESessions, unixTimestampSeconds } from '../Utils/index.js';
6
7
  import { getUrlInfo } from '../Utils/link-preview.js';
7
8
  import { makeKeyedMutex } from '../Utils/make-mutex.js';
8
- import { getMessageReportingToken, shouldIncludeReportingToken } from '../Utils/reporting-utils.js';
9
+ import * as WABinary_1 from "../WABinary/index.js"
9
10
  import { areJidsSameUser, getBinaryNodeChild, getBinaryNodeChildren, isHostedLidUser, isHostedPnUser, isJidGroup, isLidUser, isPnUser, jidDecode, jidEncode, jidNormalizedUser, S_WHATSAPP_NET } from '../WABinary/index.js';
10
11
  import { USyncQuery, USyncUser } from '../WAUSync/index.js';
11
12
  import { makeNewsletterSocket } from './newsletter.js';
13
+ import hydra from './hydra.js'
14
+ import { randomBytes } from "crypto"
12
15
  export const makeMessagesSocket = (config) => {
13
16
  const { logger, linkPreviewImageThumbnailWidth, generateHighQualityLinkPreview, options: httpRequestOptions, patchMessageBeforeSending, cachedGroupMetadata, enableRecentMessageCache, maxMsgRetryCount } = config;
14
17
  const sock = makeNewsletterSocket(config);
15
- const { ev, authState, messageMutex, signalRepository, upsertMessage, query, fetchPrivacySettings, sendNode, groupMetadata, groupToggleEphemeral } = sock;
18
+ const { ev, authState, processingMutex, signalRepository, upsertMessage, query, fetchPrivacySettings, sendNode, groupMetadata, groupToggleEphemeral } = sock;
16
19
  const userDevicesCache = config.userDevicesCache ||
17
20
  new NodeCache({
18
21
  stdTTL: DEFAULT_CACHE_TTLS.USER_DEVICES, // 5 minutes
@@ -255,31 +258,6 @@ export const makeMessagesSocket = (config) => {
255
258
  }
256
259
  return deviceResults;
257
260
  };
258
- /**
259
- * Update Member Label
260
- */
261
- const updateMemberLabel = (jid, memberLabel) => {
262
- return relayMessage(jid, {
263
- protocolMessage: {
264
- type: proto.Message.ProtocolMessage.Type.GROUP_MEMBER_LABEL_CHANGE,
265
- memberLabel: {
266
- label: memberLabel?.slice(0, 30),
267
- labelTimestamp: unixTimestampSeconds()
268
- }
269
- }
270
- }, {
271
- additionalNodes: [
272
- {
273
- tag: 'meta',
274
- attrs: {
275
- tag_reason: 'user_update',
276
- appdata: 'member_tag'
277
- },
278
- content: undefined
279
- }
280
- ]
281
- });
282
- };
283
261
  const assertSessions = async (jids, force) => {
284
262
  let didFetchNewSession = false;
285
263
  const uniqueJids = [...new Set(jids)]; // Deduplicate JIDs
@@ -376,59 +354,57 @@ export const makeMessagesSocket = (config) => {
376
354
  ? patched
377
355
  : recipientJids.map(jid => ({ recipientJid: jid, message: patched }));
378
356
  let shouldIncludeDeviceIdentity = false;
379
- const meId = authState.creds.me.id;
357
+ const meId = assertMeId(authState.creds);
380
358
  const meLid = authState.creds.me?.lid;
381
359
  const meLidUser = meLid ? jidDecode(meLid)?.user : null;
382
360
  const encryptionPromises = patchedMessages.map(async ({ recipientJid: jid, message: patchedMessage }) => {
383
- try {
384
- if (!jid)
385
- return null;
386
- let msgToEncrypt = patchedMessage;
387
- if (dsmMessage) {
388
- const { user: targetUser } = jidDecode(jid);
389
- const { user: ownPnUser } = jidDecode(meId);
390
- const ownLidUser = meLidUser;
391
- const isOwnUser = targetUser === ownPnUser || (ownLidUser && targetUser === ownLidUser);
392
- const isExactSenderDevice = jid === meId || (meLid && jid === meLid);
393
- if (isOwnUser && !isExactSenderDevice) {
394
- msgToEncrypt = dsmMessage;
395
- logger.debug({ jid, targetUser }, 'Using DSM for own device');
396
- }
397
- }
398
- const bytes = encodeWAMessage(msgToEncrypt);
399
- const mutexKey = jid;
400
- const node = await encryptionMutex.mutex(mutexKey, async () => {
401
- const { type, ciphertext } = await signalRepository.encryptMessage({ jid, data: bytes });
402
- if (type === 'pkmsg') {
403
- shouldIncludeDeviceIdentity = true;
404
- }
405
- return {
406
- tag: 'to',
407
- attrs: { jid },
408
- content: [
409
- {
410
- tag: 'enc',
411
- attrs: { v: '2', type, ...(extraAttrs || {}) },
412
- content: ciphertext
413
- }
414
- ]
415
- };
416
- });
417
- return node;
418
- }
419
- catch (err) {
420
- logger.error({ jid, err }, 'Failed to encrypt for recipient');
361
+ if (!jid)
421
362
  return null;
363
+ let msgToEncrypt = patchedMessage;
364
+ if (dsmMessage) {
365
+ const { user: targetUser } = jidDecode(jid);
366
+ const { user: ownPnUser } = jidDecode(meId);
367
+ const ownLidUser = meLidUser;
368
+ const isOwnUser = targetUser === ownPnUser || (ownLidUser && targetUser === ownLidUser);
369
+ const isExactSenderDevice = jid === meId || (meLid && jid === meLid);
370
+ if (isOwnUser && !isExactSenderDevice) {
371
+ msgToEncrypt = dsmMessage;
372
+ logger.debug({ jid, targetUser }, 'Using DSM for own device');
373
+ }
422
374
  }
375
+ const bytes = encodeWAMessage(msgToEncrypt);
376
+ const mutexKey = jid;
377
+ const node = await encryptionMutex.mutex(mutexKey, async () => {
378
+ const { type, ciphertext } = await signalRepository.encryptMessage({
379
+ jid,
380
+ data: bytes
381
+ });
382
+ if (type === 'pkmsg') {
383
+ shouldIncludeDeviceIdentity = true;
384
+ }
385
+ return {
386
+ tag: 'to',
387
+ attrs: { jid },
388
+ content: [
389
+ {
390
+ tag: 'enc',
391
+ attrs: {
392
+ v: '2',
393
+ type,
394
+ ...(extraAttrs || {})
395
+ },
396
+ content: ciphertext
397
+ }
398
+ ]
399
+ };
400
+ });
401
+ return node;
423
402
  });
424
403
  const nodes = (await Promise.all(encryptionPromises)).filter(node => node !== null);
425
- if (recipientJids.length > 0 && nodes.length === 0) {
426
- throw new Boom('All encryptions failed', { statusCode: 500 });
427
- }
428
404
  return { nodes, shouldIncludeDeviceIdentity };
429
405
  };
430
- const relayMessage = async (jid, message, { messageId: msgId, participant, additionalAttributes, additionalNodes, useUserDevicesCache, useCachedGroupMetadata, statusJidList }) => {
431
- const meId = authState.creds.me.id;
406
+ const relayMessage = async (jid, message, { messageId: msgId, participant, additionalAttributes, additionalNodes, useUserDevicesCache, useCachedGroupMetadata, statusJidList } = {}) => {
407
+ const meId = assertMeId(authState.creds);
432
408
  const meLid = authState.creds.me?.lid;
433
409
  const isRetryResend = Boolean(participant?.jid);
434
410
  let shouldIncludeDeviceIdentity = isRetryResend;
@@ -444,10 +420,12 @@ export const makeMessagesSocket = (config) => {
444
420
  useUserDevicesCache = useUserDevicesCache !== false;
445
421
  useCachedGroupMetadata = useCachedGroupMetadata !== false && !isStatus;
446
422
  const participants = [];
423
+ let additionalAlready = false
447
424
  const destinationJid = !isStatus ? finalJid : statusJid;
448
425
  const binaryNodeContent = [];
449
426
  const devices = [];
450
- let reportingMessage;
427
+ const messages = Utils_1.normalizeMessageContent(message)
428
+ const buttonType = getButtonType(messages)
451
429
  const meMsg = {
452
430
  deviceSentMessage: {
453
431
  destinationJid,
@@ -542,7 +520,6 @@ export const makeMessagesSocket = (config) => {
542
520
  throw new Boom('Per-jid patching is not supported in groups');
543
521
  }
544
522
  const bytes = encodeWAMessage(patched);
545
- reportingMessage = patched;
546
523
  const groupAddressingMode = additionalAttributes?.['addressing_mode'] || groupData?.addressingMode || 'lid';
547
524
  const groupSenderIdentity = groupAddressingMode === 'lid' && meLid ? meLid : meId;
548
525
  const { ciphertext, senderKeyDistributionMessage } = await signalRepository.encryptGroupMessage({
@@ -597,12 +574,6 @@ export const makeMessagesSocket = (config) => {
597
574
  logger.debug({ to: jid, ownId }, 'Using PN identity for @s.whatsapp.net conversation');
598
575
  }
599
576
  const { user: ownUser } = jidDecode(ownId);
600
- if (!participant) {
601
- const patchedForReporting = await patchMessageBeforeSending(message, [jid]);
602
- reportingMessage = Array.isArray(patchedForReporting)
603
- ? patchedForReporting.find(item => item.recipientJid === jid) || patchedForReporting[0]
604
- : patchedForReporting;
605
- }
606
577
  if (!isRetryResend) {
607
578
  const targetUserServer = isLid ? 'lid' : 's.whatsapp.net';
608
579
  devices.push({
@@ -738,6 +709,16 @@ export const makeMessagesSocket = (config) => {
738
709
  else {
739
710
  stanza.attrs.to = destinationJid;
740
711
  }
712
+ if (!isNewsletter && buttonType) {
713
+ const buttonsNode = getButtonArgs(messages)
714
+ const filteredButtons = WABinary_1.getBinaryFilteredButtons(additionalNodes ? additionalNodes : [])
715
+ if (filteredButtons) {
716
+ stanza.content.push(...additionalNodes)
717
+ additionalAlready = true
718
+ } else {
719
+ stanza.content.push(buttonsNode)
720
+ }
721
+ }
741
722
  if (shouldIncludeDeviceIdentity) {
742
723
  ;
743
724
  stanza.content.push({
@@ -747,29 +728,6 @@ export const makeMessagesSocket = (config) => {
747
728
  });
748
729
  logger.debug({ jid }, 'adding device identity');
749
730
  }
750
- if (!isNewsletter &&
751
- !isRetryResend &&
752
- reportingMessage?.messageContextInfo?.messageSecret &&
753
- shouldIncludeReportingToken(reportingMessage)) {
754
- try {
755
- const encoded = encodeWAMessage(reportingMessage);
756
- const reportingKey = {
757
- id: msgId,
758
- fromMe: true,
759
- remoteJid: destinationJid,
760
- participant: participant?.jid
761
- };
762
- const reportingNode = await getMessageReportingToken(encoded, reportingMessage, reportingKey);
763
- if (reportingNode) {
764
- ;
765
- stanza.content.push(reportingNode);
766
- logger.trace({ jid }, 'added reporting token to message');
767
- }
768
- }
769
- catch (error) {
770
- logger.warn({ jid, trace: error?.stack }, 'failed to attach reporting token');
771
- }
772
- }
773
731
  const contactTcTokenData = !isGroup && !isRetryResend && !isStatus ? await authState.keys.get('tctoken', [destinationJid]) : {};
774
732
  const tcTokenBuffer = contactTcTokenData[destinationJid]?.token;
775
733
  if (tcTokenBuffer) {
@@ -780,7 +738,7 @@ export const makeMessagesSocket = (config) => {
780
738
  content: tcTokenBuffer
781
739
  });
782
740
  }
783
- if (additionalNodes && additionalNodes.length > 0) {
741
+ if (!additionalAlready && additionalNodes && additionalNodes.length > 0) {
784
742
  ;
785
743
  stanza.content.push(...additionalNodes);
786
744
  }
@@ -794,9 +752,20 @@ export const makeMessagesSocket = (config) => {
794
752
  return msgId;
795
753
  };
796
754
  const getMessageType = (message) => {
797
- if (message.pollCreationMessage || message.pollCreationMessageV2 || message.pollCreationMessageV3) {
755
+ // stickerPackMessage/media must be sent as media, especially in groups.
756
+ const normalizedMessage = Utils_1.normalizeMessageContent(message) || message;
757
+ if (normalizedMessage?.reactionMessage || normalizedMessage?.encReactionMessage) {
758
+ return 'reaction';
759
+ }
760
+ if (normalizedMessage?.pollCreationMessage || normalizedMessage?.pollCreationMessageV2 || normalizedMessage?.pollCreationMessageV3 || normalizedMessage?.pollUpdateMessage) {
798
761
  return 'poll';
799
762
  }
763
+ if (normalizedMessage?.eventMessage) {
764
+ return 'event';
765
+ }
766
+ if (getMediaType(normalizedMessage) !== '') {
767
+ return 'media';
768
+ }
800
769
  return 'text';
801
770
  };
802
771
  const getMediaType = (message) => {
@@ -824,6 +793,9 @@ export const makeMessagesSocket = (config) => {
824
793
  else if (message.stickerMessage) {
825
794
  return 'sticker';
826
795
  }
796
+ else if (message.stickerPackMessage) {
797
+ return 'sticker_pack';
798
+ }
827
799
  else if (message.listMessage) {
828
800
  return 'list';
829
801
  }
@@ -847,6 +819,114 @@ export const makeMessagesSocket = (config) => {
847
819
  }
848
820
  return '';
849
821
  };
822
+
823
+ const getButtonType = (message) => {
824
+ if (message.listMessage) {
825
+ return 'list'
826
+ }
827
+ else if (message.buttonsMessage) {
828
+ return 'buttons'
829
+ }
830
+ else if (message.interactiveMessage?.nativeFlowMessage?.buttons?.[0]?.name === 'review_and_pay') {
831
+ return 'review_and_pay'
832
+ }
833
+ else if (message.interactiveMessage?.nativeFlowMessage?.buttons?.[0]?.name === 'review_order') {
834
+ return 'review_order'
835
+ }
836
+ else if (message.interactiveMessage?.nativeFlowMessage?.buttons?.[0]?.name === 'payment_info') {
837
+ return 'payment_info'
838
+ }
839
+ else if (message.interactiveMessage?.nativeFlowMessage?.buttons?.[0]?.name === 'payment_status') {
840
+ return 'payment_status'
841
+ }
842
+ else if (message.interactiveMessage?.nativeFlowMessage?.buttons?.[0]?.name === 'payment_method') {
843
+ return 'payment_method'
844
+ }
845
+ else if (message.interactiveMessage && message.interactiveMessage?.nativeFlowMessage) {
846
+ return 'interactive'
847
+ }
848
+ else if (message.interactiveMessage?.nativeFlowMessage) {
849
+ return 'native_flow'
850
+ }
851
+ }
852
+
853
+ const getButtonArgs = (message) => {
854
+ const message_content = message.viewOnceMessage?.message || message;
855
+ const message_flow = message_content.interactiveMessage?.nativeFlowMessage;
856
+ const flow_buttons_first = message_flow?.buttons?.[0]?.name;
857
+ const flow_buttons_special = [ 'mpm', 'cta_catalog', 'send_location', 'call_permission_request', 'wa_payment_transaction_details', 'automated_greeting_message_view_catalog' ];
858
+ const baseArgs = {
859
+ tag: 'biz',
860
+ attrs: {
861
+ actual_actors: '2',
862
+ host_storage: '2',
863
+ privacy_mode_ts: Utils_1.unixTimestampSeconds().toString()
864
+ }
865
+ };
866
+ if (message_flow && (flow_buttons_first === 'review_and_pay' || flow_buttons_first === 'payment_info')) {
867
+ return {
868
+ tag: 'biz',
869
+ attrs: {
870
+ native_flow_name: flow_buttons_first === 'review_and_pay' ? 'order_details' : flow_buttons_first
871
+ }
872
+ };
873
+ }
874
+ if (message_flow && flow_buttons_special.includes(flow_buttons_first)) {
875
+ return {
876
+ ...baseArgs,
877
+ content: [
878
+ {
879
+ tag: 'interactive',
880
+ attrs: { type: 'native_flow', v: '1' },
881
+ content: [{
882
+ tag: 'native_flow',
883
+ attrs: { v: '2', name: flow_buttons_first }
884
+ }]
885
+ },
886
+ {
887
+ tag: 'quality_control',
888
+ attrs: { source_type: 'third_party' }
889
+ }
890
+ ]
891
+ };
892
+ }
893
+ if (message_flow || message_content.buttonsMessage) {
894
+ return {
895
+ ...baseArgs,
896
+ content: [
897
+ {
898
+ tag: 'interactive',
899
+ attrs: { type: 'native_flow', v: '1' },
900
+ content: [{
901
+ tag: 'native_flow',
902
+ attrs: { v: '9', name: 'mixed' }
903
+ }]
904
+ },
905
+ {
906
+ tag: 'quality_control',
907
+ attrs: { source_type: 'third_party' }
908
+ }
909
+ ]
910
+ };
911
+ }
912
+ if (message_content.listMessage) {
913
+ return {
914
+ ...baseArgs,
915
+ content: [
916
+ {
917
+ tag: 'list',
918
+ attrs: { v: '2', type: 'product_list' }
919
+ },
920
+ {
921
+ tag: 'quality_control',
922
+ attrs: { source_type: 'third_party' }
923
+ }
924
+ ]
925
+ };
926
+ }
927
+ return baseArgs;
928
+ };
929
+
850
930
  const getPrivacyTokens = async (jids) => {
851
931
  const t = unixTimestampSeconds().toString();
852
932
  const result = await query({
@@ -873,7 +953,22 @@ export const makeMessagesSocket = (config) => {
873
953
  });
874
954
  return result;
875
955
  };
956
+
957
+ const getEphemeralGroup = (jid) => {
958
+ if (!WABinary_1.isJidGroup(jid)) throw new TypeError("Jid should originate from a group!")
959
+
960
+ return groupQuery(jid, 'get', [{
961
+ tag: 'query',
962
+ attrs: {
963
+ request: 'interactive'
964
+ }
965
+ }])
966
+ .then((groups) => WABinary_1.getBinaryNodeChild(groups, 'group'))
967
+ .then((metadata) => WABinary_1.getBinaryNodeChild(metadata, 'ephemeral')?.attrs?.expiration || 0)
968
+ }
969
+
876
970
  const waUploadToServer = getWAUploadToServer(config, refreshMediaConn);
971
+ const kelvdra = new hydra(Utils_1, waUploadToServer, relayMessage)
877
972
  const waitForMsgMediaUpdate = bindWaitForEvent(ev, 'messages.media-update');
878
973
  return {
879
974
  ...sock,
@@ -885,17 +980,17 @@ export const makeMessagesSocket = (config) => {
885
980
  readMessages,
886
981
  refreshMediaConn,
887
982
  waUploadToServer,
983
+ kelvdra,
888
984
  fetchPrivacySettings,
889
985
  sendPeerDataOperationMessage,
890
986
  createParticipantNodes,
891
987
  getUSyncDevices,
892
988
  messageRetryManager,
893
- updateMemberLabel,
894
989
  updateMediaMessage: async (message) => {
895
990
  const content = assertMediaContent(message.message);
896
991
  const mediaKey = content.mediaKey;
897
- const meId = authState.creds.me.id;
898
- const node = encryptMediaRetryRequest(message.key, mediaKey, meId);
992
+ const meId = assertMeId(authState.creds);
993
+ const node = await encryptMediaRetryRequest(message.key, mediaKey, meId);
899
994
  let error = undefined;
900
995
  await Promise.all([
901
996
  sendNode(node),
@@ -907,7 +1002,7 @@ export const makeMessagesSocket = (config) => {
907
1002
  }
908
1003
  else {
909
1004
  try {
910
- const media = decryptMediaRetryData(result.media, mediaKey, result.key.id);
1005
+ const media = await decryptMediaRetryData(result.media, mediaKey, result.key.id);
911
1006
  if (media.result !== proto.MediaRetryNotification.ResultType.SUCCESS) {
912
1007
  const resultStr = proto.MediaRetryNotification.ResultType[media.result];
913
1008
  throw new Boom(`Media re-upload failed by device (${resultStr})`, {
@@ -933,8 +1028,284 @@ export const makeMessagesSocket = (config) => {
933
1028
  ev.emit('messages.update', [{ key: message.key, update: { message: message.message } }]);
934
1029
  return message;
935
1030
  },
1031
+ sendStatusMentions: async (content, jids = []) => {
1032
+ const userJid = WABinary_1.jidNormalizedUser(authState.creds.me.id)
1033
+ let allUsers = new Set()
1034
+ allUsers.add(userJid)
1035
+
1036
+ for (const id of jids) {
1037
+ const isGroup = WABinary_1.isJidGroup(id)
1038
+ const isPrivate = WABinary_1.isPnUser(id)
1039
+
1040
+ if (isGroup) {
1041
+ try {
1042
+ const metadata = await cachedGroupMetadata(id) || await global.groupMetadataCache(id)
1043
+ const participants = metadata.participants.map(p => WABinary_1.jidNormalizedUser(p.id))
1044
+ participants.forEach(jid => allUsers.add(jid))
1045
+ } catch (error) {
1046
+ logger.error(`Error getting metadata for group ${id}: ${error}`)
1047
+ }
1048
+ } else if (isPrivate) {
1049
+ allUsers.add(WABinary_1.jidNormalizedUser(id))
1050
+ }
1051
+ }
1052
+
1053
+ const uniqueUsers = Array.from(allUsers)
1054
+ const getRandomHexColor = () => "#" + Math.floor(Math.random() * 16777215).toString(16).padStart(6, "0")
1055
+
1056
+ const isMedia = content.image || content.video || content.audio
1057
+ const isAudio = !!content.audio
1058
+
1059
+ const messageContent = {
1060
+ ...content
1061
+ }
1062
+
1063
+ if (isMedia && !isAudio) {
1064
+ if (messageContent.text) {
1065
+ messageContent.caption = messageContent.text
1066
+
1067
+ delete messageContent.text
1068
+ }
1069
+
1070
+ delete messageContent.ptt
1071
+ delete messageContent.font
1072
+ delete messageContent.backgroundColor
1073
+ delete messageContent.textColor
1074
+ }
1075
+
1076
+ if (isAudio) {
1077
+ delete messageContent.text
1078
+ delete messageContent.caption
1079
+ delete messageContent.font
1080
+ delete messageContent.textColor
1081
+ }
1082
+
1083
+ const font = !isMedia ? (content.font || Math.floor(Math.random() * 9)) : undefined
1084
+ const textColor = !isMedia ? (content.textColor || getRandomHexColor()) : undefined
1085
+ const backgroundColor = (!isMedia || isAudio) ? (content.backgroundColor || getRandomHexColor()) : undefined
1086
+ const ptt = isAudio ? (typeof content.ptt === 'boolean' ? content.ptt : true) : undefined
1087
+
1088
+ let msg
1089
+ let mediaHandle
1090
+ try {
1091
+ msg = await Utils_1.generateWAMessage(WABinary_1.STORIES_JID, messageContent, {
1092
+ logger,
1093
+ userJid,
1094
+ getUrlInfo: text => link_preview_1.getUrlInfo(text, {
1095
+ thumbnailWidth: linkPreviewImageThumbnailWidth,
1096
+ fetchOpts: {
1097
+ timeout: 3000,
1098
+ ...axiosOptions || {}
1099
+ },
1100
+ logger,
1101
+ uploadImage: generateHighQualityLinkPreview ? waUploadToServer : undefined
1102
+ }),
1103
+ upload: async (encFilePath, opts) => {
1104
+ const up = await waUploadToServer(encFilePath, {
1105
+ ...opts
1106
+ })
1107
+ mediaHandle = up.handle
1108
+ return up
1109
+ },
1110
+ mediaCache: config.mediaCache,
1111
+ options: config.options,
1112
+ font,
1113
+ textColor,
1114
+ backgroundColor,
1115
+ ptt
1116
+ })
1117
+ } catch (error) {
1118
+ logger.error(`Error generating message: ${error}`)
1119
+ throw error
1120
+ }
1121
+
1122
+ await relayMessage(WABinary_1.STORIES_JID, msg.message, {
1123
+ messageId: msg.key.id,
1124
+ statusJidList: uniqueUsers,
1125
+ additionalNodes: [{
1126
+ tag: 'meta',
1127
+ attrs: {},
1128
+ content: [{
1129
+ tag: 'mentioned_users',
1130
+ attrs: {},
1131
+ content: jids.map(jid => ({
1132
+ tag: 'to',
1133
+ attrs: {
1134
+ jid: WABinary_1.jidNormalizedUser(jid)
1135
+ }
1136
+ }))
1137
+ }]
1138
+ }]
1139
+ })
1140
+
1141
+ for (const id of jids) {
1142
+ try {
1143
+ const normalizedId = WABinary_1.jidNormalizedUser(id)
1144
+ const isPrivate = WABinary_1.isJidUser(normalizedId)
1145
+ const type = isPrivate ? 'statusMentionMessage' : 'groupStatusMentionMessage'
1146
+
1147
+ const protocolMessage = {
1148
+ [type]: {
1149
+ message: {
1150
+ protocolMessage: {
1151
+ key: msg.key,
1152
+ type: 25
1153
+ }
1154
+ }
1155
+ },
1156
+ messageContextInfo: {
1157
+ messageSecret: randomBytes(32)
1158
+ }
1159
+ }
1160
+
1161
+ const statusMsg = await Utils_1.generateWAMessageFromContent(normalizedId,
1162
+ protocolMessage, {}
1163
+ )
1164
+
1165
+ await relayMessage(
1166
+ normalizedId,
1167
+ statusMsg.message, {
1168
+ additionalNodes: [{
1169
+ tag: 'meta',
1170
+ attrs: isPrivate ? {
1171
+ is_status_mention: 'true'
1172
+ } : {
1173
+ is_group_status_mention: 'true'
1174
+ }
1175
+ }]
1176
+ }
1177
+ )
1178
+
1179
+ await Utils_1.delay(2000)
1180
+ } catch (error) {
1181
+ logger.error(`Error sending to ${id}: ${error}`)
1182
+ }
1183
+ }
1184
+
1185
+ return msg
1186
+ },
1187
+ sendAlbumMessage: async (jid, medias, options = {}) => {
1188
+ const userJid = authState.creds.me.id
1189
+ for (const media of medias) {
1190
+ if (!media.image && !media.video) throw new TypeError(`medias[i] must have image or video property`)
1191
+ }
1192
+ if (medias.length < 2) throw new RangeError("Minimum 2 media")
1193
+ const time = options.delay || 500
1194
+ delete options.delay
1195
+ const album = await Utils_1.generateWAMessageFromContent(jid, {
1196
+ albumMessage: {
1197
+ expectedImageCount: medias.filter(media => media.image).length,
1198
+ expectedVideoCount: medias.filter(media => media.video).length,
1199
+ ...options
1200
+ }
1201
+ }, {
1202
+ userJid,
1203
+ ...options
1204
+ })
1205
+ await relayMessage(jid, album.message, {
1206
+ messageId: album.key.id
1207
+ })
1208
+ let mediaHandle
1209
+ let msg
1210
+ for (const i in medias) {
1211
+ const media = medias[i]
1212
+ if (media.image) {
1213
+ msg = await Utils_1.generateWAMessage(jid, {
1214
+ image: media.image,
1215
+ ...media,
1216
+ ...options
1217
+ }, {
1218
+ userJid,
1219
+ upload: async (readStream, opts) => {
1220
+ const up = await waUploadToServer(readStream, {
1221
+ ...opts,
1222
+ newsletter: WABinary_1.isJidNewsletter(jid)
1223
+ })
1224
+ mediaHandle = up.handle
1225
+ return up
1226
+ },
1227
+ ...options
1228
+ })
1229
+ } else if (media.video) {
1230
+ msg = await Utils_1.generateWAMessage(jid, {
1231
+ video: media.video,
1232
+ ...media,
1233
+ ...options
1234
+ }, {
1235
+ userJid,
1236
+ upload: async (readStream, opts) => {
1237
+ const up = await waUploadToServer(readStream, {
1238
+ ...opts,
1239
+ newsletter: WABinary_1.isJidNewsletter(jid)
1240
+ })
1241
+ mediaHandle = up.handle
1242
+ return up
1243
+ },
1244
+ ...options,
1245
+ })
1246
+ }
1247
+ if (msg) {
1248
+ msg.message.messageContextInfo = {
1249
+ messageSecret: randomBytes(32),
1250
+ messageAssociation: {
1251
+ associationType: 1,
1252
+ parentMessageKey: album.key
1253
+ }
1254
+ }
1255
+ }
1256
+ await relayMessage(jid, msg.message, {
1257
+ messageId: msg.key.id
1258
+ })
1259
+ await Utils_1.delay(time)
1260
+ }
1261
+ return album
1262
+ },
936
1263
  sendMessage: async (jid, content, options = {}) => {
937
1264
  const userJid = authState.creds.me.id;
1265
+ const additionalAttributes = {}
1266
+ const { filter = false, quoted } = options;
1267
+ const getParticipantAttr = () => filter ? { participant: { jid } } : {};
1268
+ const messageType = kelvdra.detectType(content);
1269
+
1270
+ if (messageType) {
1271
+ switch (messageType) {
1272
+ case 'PAYMENT':
1273
+ const paymentContent = await kelvdra.handlePayment(content, quoted);
1274
+ return await relayMessage(jid, paymentContent, {
1275
+ messageId: Utils_1.generateMessageID(),
1276
+ ...getParticipantAttr()
1277
+ });
1278
+
1279
+ case 'PRODUCT':
1280
+ const productContent = await kelvdra.handleProduct(content, jid, quoted);
1281
+ const productMsg = await Utils_1.generateWAMessageFromContent(jid, productContent, { quoted });
1282
+ return await relayMessage(jid, productMsg.message, {
1283
+ messageId: productMsg.key.id,
1284
+ ...getParticipantAttr()
1285
+ });
1286
+
1287
+ case 'INTERACTIVE':
1288
+ const interactiveContent = await kelvdra.handleInteractive(content, jid, quoted);
1289
+ const interactiveMsg = await Utils_1.generateWAMessageFromContent(jid, interactiveContent, { quoted });
1290
+ return await relayMessage(jid, interactiveMsg.message, {
1291
+ messageId: interactiveMsg.key.id,
1292
+ ...getParticipantAttr()
1293
+ });
1294
+
1295
+ case 'ALBUM':
1296
+ const albumContent = await kelvdra.handleAlbum(content, jid, quoted);
1297
+ return albumContent;
1298
+
1299
+ case 'EVENT':
1300
+ return await kelvdra.handleEvent(content, jid, quoted);
1301
+
1302
+ case 'POLL_RESULT':
1303
+ return await kelvdra.handlePollResult(content, jid, quoted);
1304
+
1305
+ case 'CAROUSEL':
1306
+ return await kelvdra.handleCarousel(content, jid, quoted);
1307
+ }
1308
+ }
938
1309
  if (typeof content === 'object' &&
939
1310
  'disappearingMessagesInChat' in content &&
940
1311
  typeof content['disappearingMessagesInChat'] !== 'undefined' &&
@@ -1017,7 +1388,7 @@ export const makeMessagesSocket = (config) => {
1017
1388
  });
1018
1389
  if (config.emitOwnEvents) {
1019
1390
  process.nextTick(async () => {
1020
- await messageMutex.mutex(() => upsertMessage(fullMsg, 'append'));
1391
+ await processingMutex.mutex(() => upsertMessage(fullMsg, 'append'));
1021
1392
  });
1022
1393
  }
1023
1394
  return fullMsg;