@kelvdra/baileys 1.0.4 → 1.0.5-rc.2

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 (46) 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 +321 -18
  7. package/lib/Socket/hydra.js +521 -0
  8. package/lib/Socket/messages-recv.js +736 -324
  9. package/lib/Socket/messages-send.js +526 -113
  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/GroupMetadata.d.ts +16 -0
  14. package/lib/Types/Mex.d.ts +141 -0
  15. package/lib/Types/Mex.js +37 -0
  16. package/lib/Types/State.js +54 -1
  17. package/lib/Utils/auth-utils.js +12 -1
  18. package/lib/Utils/chat-utils.js +36 -2
  19. package/lib/Utils/companion-reg-client-utils.d.ts +17 -0
  20. package/lib/Utils/companion-reg-client-utils.js +35 -0
  21. package/lib/Utils/decode-wa-message.js +23 -4
  22. package/lib/Utils/generics.js +4 -1
  23. package/lib/Utils/identity-change-handler.d.ts +44 -0
  24. package/lib/Utils/identity-change-handler.js +50 -0
  25. package/lib/Utils/index.js +1 -1
  26. package/lib/Utils/message-retry-manager.js +25 -1
  27. package/lib/Utils/messages-media.js +162 -43
  28. package/lib/Utils/messages.d.ts +1 -1
  29. package/lib/Utils/messages.js +230 -9
  30. package/lib/Utils/offline-node-processor.d.ts +17 -0
  31. package/lib/Utils/offline-node-processor.js +40 -0
  32. package/lib/Utils/reporting-utils.d.ts +11 -0
  33. package/lib/Utils/reporting-utils.js +258 -0
  34. package/lib/Utils/signal.js +45 -1
  35. package/lib/Utils/stanza-ack.d.ts +11 -0
  36. package/lib/Utils/stanza-ack.js +38 -0
  37. package/lib/Utils/sync-action-utils.d.ts +19 -0
  38. package/lib/Utils/sync-action-utils.js +49 -0
  39. package/lib/Utils/tc-token-utils.d.ts +37 -0
  40. package/lib/Utils/tc-token-utils.js +163 -0
  41. package/lib/WAUSync/Protocols/USyncUsernameProtocol.d.ts +10 -0
  42. package/lib/WAUSync/Protocols/USyncUsernameProtocol.js +25 -0
  43. package/lib/WAUSync/Protocols/index.d.ts +1 -0
  44. package/lib/WAUSync/Protocols/index.js +1 -0
  45. package/lib/WAUSync/USyncQuery.js +5 -1
  46. 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;
@@ -441,13 +417,19 @@ export const makeMessagesSocket = (config) => {
441
417
  const isGroupOrStatus = isGroup || isStatus;
442
418
  const finalJid = jid;
443
419
  msgId = msgId || generateMessageIDV2(meId);
444
- useUserDevicesCache = useUserDevicesCache !== false;
445
- useCachedGroupMetadata = useCachedGroupMetadata !== false && !isStatus;
420
+ // Anti waiting-message fix: default to fresh device/group lookup.
421
+ // Stale device cache/sender-key metadata can make recipient clients show
422
+ // "menunggu pesan" until a retry happens. Existing callers can still
423
+ // opt in to cache with useUserDevicesCache: true / useCachedGroupMetadata: true.
424
+ useUserDevicesCache = useUserDevicesCache === true;
425
+ useCachedGroupMetadata = useCachedGroupMetadata === true && !isStatus;
446
426
  const participants = [];
427
+ let additionalAlready = false
447
428
  const destinationJid = !isStatus ? finalJid : statusJid;
448
429
  const binaryNodeContent = [];
449
430
  const devices = [];
450
- let reportingMessage;
431
+ const messages = Utils_1.normalizeMessageContent(message)
432
+ const buttonType = getButtonType(messages)
451
433
  const meMsg = {
452
434
  deviceSentMessage: {
453
435
  destinationJid,
@@ -542,7 +524,6 @@ export const makeMessagesSocket = (config) => {
542
524
  throw new Boom('Per-jid patching is not supported in groups');
543
525
  }
544
526
  const bytes = encodeWAMessage(patched);
545
- reportingMessage = patched;
546
527
  const groupAddressingMode = additionalAttributes?.['addressing_mode'] || groupData?.addressingMode || 'lid';
547
528
  const groupSenderIdentity = groupAddressingMode === 'lid' && meLid ? meLid : meId;
548
529
  const { ciphertext, senderKeyDistributionMessage } = await signalRepository.encryptGroupMessage({
@@ -597,12 +578,6 @@ export const makeMessagesSocket = (config) => {
597
578
  logger.debug({ to: jid, ownId }, 'Using PN identity for @s.whatsapp.net conversation');
598
579
  }
599
580
  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
581
  if (!isRetryResend) {
607
582
  const targetUserServer = isLid ? 'lid' : 's.whatsapp.net';
608
583
  devices.push({
@@ -627,7 +602,7 @@ export const makeMessagesSocket = (config) => {
627
602
  ? jidEncode(jidDecode(meLid)?.user, 'lid', undefined)
628
603
  : jidEncode(jidDecode(meId)?.user, 's.whatsapp.net', undefined);
629
604
  // Enumerate devices for sender and target with consistent addressing
630
- const sessionDevices = await getUSyncDevices([senderIdentity, jid], true, false);
605
+ const sessionDevices = await getUSyncDevices([senderIdentity, jid], !!useUserDevicesCache, false);
631
606
  devices.push(...sessionDevices);
632
607
  logger.debug({
633
608
  deviceCount: devices.length,
@@ -738,6 +713,16 @@ export const makeMessagesSocket = (config) => {
738
713
  else {
739
714
  stanza.attrs.to = destinationJid;
740
715
  }
716
+ if (!isNewsletter && buttonType) {
717
+ const buttonsNode = getButtonArgs(messages)
718
+ const filteredButtons = WABinary_1.getBinaryFilteredButtons(additionalNodes ? additionalNodes : [])
719
+ if (filteredButtons) {
720
+ stanza.content.push(...additionalNodes)
721
+ additionalAlready = true
722
+ } else {
723
+ stanza.content.push(buttonsNode)
724
+ }
725
+ }
741
726
  if (shouldIncludeDeviceIdentity) {
742
727
  ;
743
728
  stanza.content.push({
@@ -747,29 +732,6 @@ export const makeMessagesSocket = (config) => {
747
732
  });
748
733
  logger.debug({ jid }, 'adding device identity');
749
734
  }
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
735
  const contactTcTokenData = !isGroup && !isRetryResend && !isStatus ? await authState.keys.get('tctoken', [destinationJid]) : {};
774
736
  const tcTokenBuffer = contactTcTokenData[destinationJid]?.token;
775
737
  if (tcTokenBuffer) {
@@ -780,7 +742,7 @@ export const makeMessagesSocket = (config) => {
780
742
  content: tcTokenBuffer
781
743
  });
782
744
  }
783
- if (additionalNodes && additionalNodes.length > 0) {
745
+ if (!additionalAlready && additionalNodes && additionalNodes.length > 0) {
784
746
  ;
785
747
  stanza.content.push(...additionalNodes);
786
748
  }
@@ -794,9 +756,26 @@ export const makeMessagesSocket = (config) => {
794
756
  return msgId;
795
757
  };
796
758
  const getMessageType = (message) => {
797
- if (message.pollCreationMessage || message.pollCreationMessageV2 || message.pollCreationMessageV3) {
759
+ // Keep group status wrappers as text stanza like 1.0.4.
760
+ // If this wrapper is normalized first, inner image/video content can be
761
+ // detected as media and status-group relay may fail on WA clients.
762
+ if (message?.groupStatusMessageV2 || message?.groupStatusMessage) {
763
+ return 'text';
764
+ }
765
+ // stickerPackMessage/media must be sent as media, especially in groups.
766
+ const normalizedMessage = Utils_1.normalizeMessageContent(message) || message;
767
+ if (normalizedMessage?.reactionMessage || normalizedMessage?.encReactionMessage) {
768
+ return 'reaction';
769
+ }
770
+ if (normalizedMessage?.pollCreationMessage || normalizedMessage?.pollCreationMessageV2 || normalizedMessage?.pollCreationMessageV3 || normalizedMessage?.pollUpdateMessage) {
798
771
  return 'poll';
799
772
  }
773
+ if (normalizedMessage?.eventMessage) {
774
+ return 'event';
775
+ }
776
+ if (getMediaType(normalizedMessage) !== '') {
777
+ return 'media';
778
+ }
800
779
  return 'text';
801
780
  };
802
781
  const getMediaType = (message) => {
@@ -824,6 +803,9 @@ export const makeMessagesSocket = (config) => {
824
803
  else if (message.stickerMessage) {
825
804
  return 'sticker';
826
805
  }
806
+ else if (message.stickerPackMessage) {
807
+ return 'sticker_pack';
808
+ }
827
809
  else if (message.listMessage) {
828
810
  return 'list';
829
811
  }
@@ -847,6 +829,114 @@ export const makeMessagesSocket = (config) => {
847
829
  }
848
830
  return '';
849
831
  };
832
+
833
+ const getButtonType = (message) => {
834
+ if (message.listMessage) {
835
+ return 'list'
836
+ }
837
+ else if (message.buttonsMessage) {
838
+ return 'buttons'
839
+ }
840
+ else if (message.interactiveMessage?.nativeFlowMessage?.buttons?.[0]?.name === 'review_and_pay') {
841
+ return 'review_and_pay'
842
+ }
843
+ else if (message.interactiveMessage?.nativeFlowMessage?.buttons?.[0]?.name === 'review_order') {
844
+ return 'review_order'
845
+ }
846
+ else if (message.interactiveMessage?.nativeFlowMessage?.buttons?.[0]?.name === 'payment_info') {
847
+ return 'payment_info'
848
+ }
849
+ else if (message.interactiveMessage?.nativeFlowMessage?.buttons?.[0]?.name === 'payment_status') {
850
+ return 'payment_status'
851
+ }
852
+ else if (message.interactiveMessage?.nativeFlowMessage?.buttons?.[0]?.name === 'payment_method') {
853
+ return 'payment_method'
854
+ }
855
+ else if (message.interactiveMessage && message.interactiveMessage?.nativeFlowMessage) {
856
+ return 'interactive'
857
+ }
858
+ else if (message.interactiveMessage?.nativeFlowMessage) {
859
+ return 'native_flow'
860
+ }
861
+ }
862
+
863
+ const getButtonArgs = (message) => {
864
+ const message_content = message.viewOnceMessage?.message || message;
865
+ const message_flow = message_content.interactiveMessage?.nativeFlowMessage;
866
+ const flow_buttons_first = message_flow?.buttons?.[0]?.name;
867
+ const flow_buttons_special = [ 'mpm', 'cta_catalog', 'send_location', 'call_permission_request', 'wa_payment_transaction_details', 'automated_greeting_message_view_catalog' ];
868
+ const baseArgs = {
869
+ tag: 'biz',
870
+ attrs: {
871
+ actual_actors: '2',
872
+ host_storage: '2',
873
+ privacy_mode_ts: Utils_1.unixTimestampSeconds().toString()
874
+ }
875
+ };
876
+ if (message_flow && (flow_buttons_first === 'review_and_pay' || flow_buttons_first === 'payment_info')) {
877
+ return {
878
+ tag: 'biz',
879
+ attrs: {
880
+ native_flow_name: flow_buttons_first === 'review_and_pay' ? 'order_details' : flow_buttons_first
881
+ }
882
+ };
883
+ }
884
+ if (message_flow && flow_buttons_special.includes(flow_buttons_first)) {
885
+ return {
886
+ ...baseArgs,
887
+ content: [
888
+ {
889
+ tag: 'interactive',
890
+ attrs: { type: 'native_flow', v: '1' },
891
+ content: [{
892
+ tag: 'native_flow',
893
+ attrs: { v: '2', name: flow_buttons_first }
894
+ }]
895
+ },
896
+ {
897
+ tag: 'quality_control',
898
+ attrs: { source_type: 'third_party' }
899
+ }
900
+ ]
901
+ };
902
+ }
903
+ if (message_flow || message_content.buttonsMessage) {
904
+ return {
905
+ ...baseArgs,
906
+ content: [
907
+ {
908
+ tag: 'interactive',
909
+ attrs: { type: 'native_flow', v: '1' },
910
+ content: [{
911
+ tag: 'native_flow',
912
+ attrs: { v: '9', name: 'mixed' }
913
+ }]
914
+ },
915
+ {
916
+ tag: 'quality_control',
917
+ attrs: { source_type: 'third_party' }
918
+ }
919
+ ]
920
+ };
921
+ }
922
+ if (message_content.listMessage) {
923
+ return {
924
+ ...baseArgs,
925
+ content: [
926
+ {
927
+ tag: 'list',
928
+ attrs: { v: '2', type: 'product_list' }
929
+ },
930
+ {
931
+ tag: 'quality_control',
932
+ attrs: { source_type: 'third_party' }
933
+ }
934
+ ]
935
+ };
936
+ }
937
+ return baseArgs;
938
+ };
939
+
850
940
  const getPrivacyTokens = async (jids) => {
851
941
  const t = unixTimestampSeconds().toString();
852
942
  const result = await query({
@@ -873,7 +963,22 @@ export const makeMessagesSocket = (config) => {
873
963
  });
874
964
  return result;
875
965
  };
966
+
967
+ const getEphemeralGroup = (jid) => {
968
+ if (!WABinary_1.isJidGroup(jid)) throw new TypeError("Jid should originate from a group!")
969
+
970
+ return groupQuery(jid, 'get', [{
971
+ tag: 'query',
972
+ attrs: {
973
+ request: 'interactive'
974
+ }
975
+ }])
976
+ .then((groups) => WABinary_1.getBinaryNodeChild(groups, 'group'))
977
+ .then((metadata) => WABinary_1.getBinaryNodeChild(metadata, 'ephemeral')?.attrs?.expiration || 0)
978
+ }
979
+
876
980
  const waUploadToServer = getWAUploadToServer(config, refreshMediaConn);
981
+ const kelvdra = new hydra(Utils_1, waUploadToServer, relayMessage)
877
982
  const waitForMsgMediaUpdate = bindWaitForEvent(ev, 'messages.media-update');
878
983
  return {
879
984
  ...sock,
@@ -885,17 +990,17 @@ export const makeMessagesSocket = (config) => {
885
990
  readMessages,
886
991
  refreshMediaConn,
887
992
  waUploadToServer,
993
+ kelvdra,
888
994
  fetchPrivacySettings,
889
995
  sendPeerDataOperationMessage,
890
996
  createParticipantNodes,
891
997
  getUSyncDevices,
892
998
  messageRetryManager,
893
- updateMemberLabel,
894
999
  updateMediaMessage: async (message) => {
895
1000
  const content = assertMediaContent(message.message);
896
1001
  const mediaKey = content.mediaKey;
897
- const meId = authState.creds.me.id;
898
- const node = encryptMediaRetryRequest(message.key, mediaKey, meId);
1002
+ const meId = assertMeId(authState.creds);
1003
+ const node = await encryptMediaRetryRequest(message.key, mediaKey, meId);
899
1004
  let error = undefined;
900
1005
  await Promise.all([
901
1006
  sendNode(node),
@@ -907,7 +1012,7 @@ export const makeMessagesSocket = (config) => {
907
1012
  }
908
1013
  else {
909
1014
  try {
910
- const media = decryptMediaRetryData(result.media, mediaKey, result.key.id);
1015
+ const media = await decryptMediaRetryData(result.media, mediaKey, result.key.id);
911
1016
  if (media.result !== proto.MediaRetryNotification.ResultType.SUCCESS) {
912
1017
  const resultStr = proto.MediaRetryNotification.ResultType[media.result];
913
1018
  throw new Boom(`Media re-upload failed by device (${resultStr})`, {
@@ -933,8 +1038,316 @@ export const makeMessagesSocket = (config) => {
933
1038
  ev.emit('messages.update', [{ key: message.key, update: { message: message.message } }]);
934
1039
  return message;
935
1040
  },
1041
+ sendStatusMentions: async (content, jids = []) => {
1042
+ const userJid = WABinary_1.jidNormalizedUser(authState.creds.me.id)
1043
+ let allUsers = new Set()
1044
+ allUsers.add(userJid)
1045
+
1046
+ for (const id of jids) {
1047
+ const isGroup = WABinary_1.isJidGroup(id)
1048
+ const isPrivate = WABinary_1.isPnUser(id)
1049
+
1050
+ if (isGroup) {
1051
+ try {
1052
+ const metadata = await cachedGroupMetadata(id) || await global.groupMetadataCache(id)
1053
+ const participants = metadata.participants.map(p => WABinary_1.jidNormalizedUser(p.id))
1054
+ participants.forEach(jid => allUsers.add(jid))
1055
+ } catch (error) {
1056
+ logger.error(`Error getting metadata for group ${id}: ${error}`)
1057
+ }
1058
+ } else if (isPrivate) {
1059
+ allUsers.add(WABinary_1.jidNormalizedUser(id))
1060
+ }
1061
+ }
1062
+
1063
+ const uniqueUsers = Array.from(allUsers)
1064
+ const getRandomHexColor = () => "#" + Math.floor(Math.random() * 16777215).toString(16).padStart(6, "0")
1065
+
1066
+ const isMedia = content.image || content.video || content.audio
1067
+ const isAudio = !!content.audio
1068
+
1069
+ const messageContent = {
1070
+ ...content
1071
+ }
1072
+
1073
+ if (isMedia && !isAudio) {
1074
+ if (messageContent.text) {
1075
+ messageContent.caption = messageContent.text
1076
+
1077
+ delete messageContent.text
1078
+ }
1079
+
1080
+ delete messageContent.ptt
1081
+ delete messageContent.font
1082
+ delete messageContent.backgroundColor
1083
+ delete messageContent.textColor
1084
+ }
1085
+
1086
+ if (isAudio) {
1087
+ delete messageContent.text
1088
+ delete messageContent.caption
1089
+ delete messageContent.font
1090
+ delete messageContent.textColor
1091
+ }
1092
+
1093
+ const font = !isMedia ? (content.font || Math.floor(Math.random() * 9)) : undefined
1094
+ const textColor = !isMedia ? (content.textColor || getRandomHexColor()) : undefined
1095
+ const backgroundColor = (!isMedia || isAudio) ? (content.backgroundColor || getRandomHexColor()) : undefined
1096
+ const ptt = isAudio ? (typeof content.ptt === 'boolean' ? content.ptt : true) : undefined
1097
+
1098
+ let msg
1099
+ let mediaHandle
1100
+ try {
1101
+ msg = await Utils_1.generateWAMessage(WABinary_1.STORIES_JID, messageContent, {
1102
+ logger,
1103
+ userJid,
1104
+ getUrlInfo: text => link_preview_1.getUrlInfo(text, {
1105
+ thumbnailWidth: linkPreviewImageThumbnailWidth,
1106
+ fetchOpts: {
1107
+ timeout: 3000,
1108
+ ...axiosOptions || {}
1109
+ },
1110
+ logger,
1111
+ uploadImage: generateHighQualityLinkPreview ? waUploadToServer : undefined
1112
+ }),
1113
+ upload: async (encFilePath, opts) => {
1114
+ const up = await waUploadToServer(encFilePath, {
1115
+ ...opts
1116
+ })
1117
+ mediaHandle = up.handle
1118
+ return up
1119
+ },
1120
+ mediaCache: config.mediaCache,
1121
+ options: config.options,
1122
+ font,
1123
+ textColor,
1124
+ backgroundColor,
1125
+ ptt
1126
+ })
1127
+ } catch (error) {
1128
+ logger.error(`Error generating message: ${error}`)
1129
+ throw error
1130
+ }
1131
+
1132
+ await relayMessage(WABinary_1.STORIES_JID, msg.message, {
1133
+ messageId: msg.key.id,
1134
+ statusJidList: uniqueUsers,
1135
+ additionalNodes: [{
1136
+ tag: 'meta',
1137
+ attrs: {},
1138
+ content: [{
1139
+ tag: 'mentioned_users',
1140
+ attrs: {},
1141
+ content: jids.map(jid => ({
1142
+ tag: 'to',
1143
+ attrs: {
1144
+ jid: WABinary_1.jidNormalizedUser(jid)
1145
+ }
1146
+ }))
1147
+ }]
1148
+ }]
1149
+ })
1150
+
1151
+ for (const id of jids) {
1152
+ try {
1153
+ const normalizedId = WABinary_1.jidNormalizedUser(id)
1154
+ const isPrivate = WABinary_1.isJidUser(normalizedId)
1155
+ const type = isPrivate ? 'statusMentionMessage' : 'groupStatusMentionMessage'
1156
+
1157
+ const protocolMessage = {
1158
+ [type]: {
1159
+ message: {
1160
+ protocolMessage: {
1161
+ key: msg.key,
1162
+ type: 25
1163
+ }
1164
+ }
1165
+ },
1166
+ messageContextInfo: {
1167
+ messageSecret: randomBytes(32)
1168
+ }
1169
+ }
1170
+
1171
+ const statusMsg = await Utils_1.generateWAMessageFromContent(normalizedId,
1172
+ protocolMessage, {}
1173
+ )
1174
+
1175
+ await relayMessage(
1176
+ normalizedId,
1177
+ statusMsg.message, {
1178
+ additionalNodes: [{
1179
+ tag: 'meta',
1180
+ attrs: isPrivate ? {
1181
+ is_status_mention: 'true'
1182
+ } : {
1183
+ is_group_status_mention: 'true'
1184
+ }
1185
+ }]
1186
+ }
1187
+ )
1188
+
1189
+ await Utils_1.delay(2000)
1190
+ } catch (error) {
1191
+ logger.error(`Error sending to ${id}: ${error}`)
1192
+ }
1193
+ }
1194
+
1195
+ return msg
1196
+ },
1197
+ sendAlbumMessage: async (jid, medias, options = {}) => {
1198
+ const userJid = authState.creds.me.id
1199
+ for (const media of medias) {
1200
+ if (!media.image && !media.video) throw new TypeError(`medias[i] must have image or video property`)
1201
+ }
1202
+ if (medias.length < 2) throw new RangeError("Minimum 2 media")
1203
+ const time = options.delay || 500
1204
+ delete options.delay
1205
+ const album = await Utils_1.generateWAMessageFromContent(jid, {
1206
+ albumMessage: {
1207
+ expectedImageCount: medias.filter(media => media.image).length,
1208
+ expectedVideoCount: medias.filter(media => media.video).length,
1209
+ ...options
1210
+ }
1211
+ }, {
1212
+ userJid,
1213
+ ...options
1214
+ })
1215
+ await relayMessage(jid, album.message, {
1216
+ messageId: album.key.id
1217
+ })
1218
+ let mediaHandle
1219
+ let msg
1220
+ for (const i in medias) {
1221
+ const media = medias[i]
1222
+ if (media.image) {
1223
+ msg = await Utils_1.generateWAMessage(jid, {
1224
+ image: media.image,
1225
+ ...media,
1226
+ ...options
1227
+ }, {
1228
+ userJid,
1229
+ upload: async (readStream, opts) => {
1230
+ const up = await waUploadToServer(readStream, {
1231
+ ...opts,
1232
+ newsletter: WABinary_1.isJidNewsletter(jid)
1233
+ })
1234
+ mediaHandle = up.handle
1235
+ return up
1236
+ },
1237
+ ...options
1238
+ })
1239
+ } else if (media.video) {
1240
+ msg = await Utils_1.generateWAMessage(jid, {
1241
+ video: media.video,
1242
+ ...media,
1243
+ ...options
1244
+ }, {
1245
+ userJid,
1246
+ upload: async (readStream, opts) => {
1247
+ const up = await waUploadToServer(readStream, {
1248
+ ...opts,
1249
+ newsletter: WABinary_1.isJidNewsletter(jid)
1250
+ })
1251
+ mediaHandle = up.handle
1252
+ return up
1253
+ },
1254
+ ...options,
1255
+ })
1256
+ }
1257
+ if (msg) {
1258
+ msg.message.messageContextInfo = {
1259
+ messageSecret: randomBytes(32),
1260
+ messageAssociation: {
1261
+ associationType: 1,
1262
+ parentMessageKey: album.key
1263
+ }
1264
+ }
1265
+ }
1266
+ await relayMessage(jid, msg.message, {
1267
+ messageId: msg.key.id
1268
+ })
1269
+ await Utils_1.delay(time)
1270
+ }
1271
+ return album
1272
+ },
1273
+ sendAIRich: async (jid, content = {}, options = {}) => {
1274
+ const { quoted, filter = false } = options;
1275
+ const getParticipantAttr = () => filter ? { participant: { jid } } : {};
1276
+ return await kelvdra.handleAIRich({ aiRich: content }, jid, quoted, {
1277
+ ...getParticipantAttr()
1278
+ });
1279
+ },
936
1280
  sendMessage: async (jid, content, options = {}) => {
937
1281
  const userJid = authState.creds.me.id;
1282
+ const normalizeDeleteKey = async (key = {}) => {
1283
+ const normalized = { ...key };
1284
+ const resolveLid = async (value) => {
1285
+ if (!value || !isLidUser(value)) return value;
1286
+ try {
1287
+ return (await signalRepository?.lidMapping?.getPNForLID(value)) || value;
1288
+ } catch {
1289
+ return value;
1290
+ }
1291
+ };
1292
+ normalized.remoteJid = await resolveLid(normalized.remoteJid || jid);
1293
+ normalized.participant = await resolveLid(normalized.participant);
1294
+ if (isJidGroup(normalized.remoteJid || jid) && !normalized.participant) {
1295
+ normalized.participant = normalized.fromMe ? userJid : undefined;
1296
+ }
1297
+ return normalized;
1298
+ };
1299
+ if (content?.delete) {
1300
+ content = { ...content, delete: await normalizeDeleteKey(content.delete) };
1301
+ }
1302
+ const additionalAttributes = {}
1303
+ const { filter = false, quoted } = options;
1304
+ const getParticipantAttr = () => filter ? { participant: { jid } } : {};
1305
+ const messageType = kelvdra.detectType(content);
1306
+
1307
+ if (messageType) {
1308
+ switch (messageType) {
1309
+ case 'PAYMENT':
1310
+ const paymentContent = await kelvdra.handlePayment(content, quoted);
1311
+ return await relayMessage(jid, paymentContent, {
1312
+ messageId: Utils_1.generateMessageID(),
1313
+ ...getParticipantAttr()
1314
+ });
1315
+
1316
+ case 'PRODUCT':
1317
+ const productContent = await kelvdra.handleProduct(content, jid, quoted);
1318
+ const productMsg = await Utils_1.generateWAMessageFromContent(jid, productContent, { quoted });
1319
+ return await relayMessage(jid, productMsg.message, {
1320
+ messageId: productMsg.key.id,
1321
+ ...getParticipantAttr()
1322
+ });
1323
+
1324
+ case 'INTERACTIVE':
1325
+ const interactiveContent = await kelvdra.handleInteractive(content, jid, quoted);
1326
+ const interactiveMsg = await Utils_1.generateWAMessageFromContent(jid, interactiveContent, { quoted });
1327
+ return await relayMessage(jid, interactiveMsg.message, {
1328
+ messageId: interactiveMsg.key.id,
1329
+ ...getParticipantAttr()
1330
+ });
1331
+
1332
+ case 'ALBUM':
1333
+ const albumContent = await kelvdra.handleAlbum(content, jid, quoted);
1334
+ return albumContent;
1335
+
1336
+ case 'EVENT':
1337
+ return await kelvdra.handleEvent(content, jid, quoted);
1338
+
1339
+ case 'POLL_RESULT':
1340
+ return await kelvdra.handlePollResult(content, jid, quoted);
1341
+
1342
+ case 'CAROUSEL':
1343
+ return await kelvdra.handleCarousel(content, jid, quoted);
1344
+
1345
+ case 'AI_RICH':
1346
+ return await kelvdra.handleAIRich(content, jid, quoted, {
1347
+ ...getParticipantAttr()
1348
+ });
1349
+ }
1350
+ }
938
1351
  if (typeof content === 'object' &&
939
1352
  'disappearingMessagesInChat' in content &&
940
1353
  typeof content['disappearingMessagesInChat'] !== 'undefined' &&
@@ -1017,7 +1430,7 @@ export const makeMessagesSocket = (config) => {
1017
1430
  });
1018
1431
  if (config.emitOwnEvents) {
1019
1432
  process.nextTick(async () => {
1020
- await messageMutex.mutex(() => upsertMessage(fullMsg, 'append'));
1433
+ await processingMutex.mutex(() => upsertMessage(fullMsg, 'append'));
1021
1434
  });
1022
1435
  }
1023
1436
  return fullMsg;