@neykoor/baileys 7.0.5-rc15 → 7.0.8-rc15

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 (77) hide show
  1. package/lib/Defaults/index.js +6 -6
  2. package/lib/Defaults/index.js.map +1 -1
  3. package/lib/Signal/lid-mapping.d.ts.map +1 -1
  4. package/lib/Signal/lid-mapping.js.map +1 -1
  5. package/lib/Socket/business.d.ts +33 -0
  6. package/lib/Socket/business.d.ts.map +1 -1
  7. package/lib/Socket/chats.d.ts.map +1 -1
  8. package/lib/Socket/chats.js +9 -2
  9. package/lib/Socket/chats.js.map +1 -1
  10. package/lib/Socket/communities.d.ts +33 -0
  11. package/lib/Socket/communities.d.ts.map +1 -1
  12. package/lib/Socket/groups.d.ts.map +1 -1
  13. package/lib/Socket/groups.js +7 -2
  14. package/lib/Socket/groups.js.map +1 -1
  15. package/lib/Socket/index.d.ts +33 -0
  16. package/lib/Socket/index.d.ts.map +1 -1
  17. package/lib/Socket/messages-recv.d.ts +33 -0
  18. package/lib/Socket/messages-recv.d.ts.map +1 -1
  19. package/lib/Socket/messages-send.d.ts +34 -1
  20. package/lib/Socket/messages-send.d.ts.map +1 -1
  21. package/lib/Socket/messages-send.js +139 -65
  22. package/lib/Socket/messages-send.js.map +1 -1
  23. package/lib/Socket/newsletter.d.ts.map +1 -1
  24. package/lib/Socket/smgss.d.ts +203 -0
  25. package/lib/Socket/smgss.d.ts.map +1 -0
  26. package/lib/Socket/smgss.js +461 -0
  27. package/lib/Socket/smgss.js.map +1 -0
  28. package/lib/Store/make-in-memory-store.d.ts.map +1 -1
  29. package/lib/Store/make-in-memory-store.js +12 -10
  30. package/lib/Store/make-in-memory-store.js.map +1 -1
  31. package/lib/Types/Message.d.ts +94 -2
  32. package/lib/Types/Message.d.ts.map +1 -1
  33. package/lib/Types/Message.js +4 -0
  34. package/lib/Types/Message.js.map +1 -1
  35. package/lib/Utils/connection-watchdog.d.ts +30 -0
  36. package/lib/Utils/connection-watchdog.d.ts.map +1 -0
  37. package/lib/Utils/connection-watchdog.js +66 -0
  38. package/lib/Utils/connection-watchdog.js.map +1 -0
  39. package/lib/Utils/generics.js +1 -1
  40. package/lib/Utils/group-status-detection.d.ts +7 -0
  41. package/lib/Utils/group-status-detection.d.ts.map +1 -0
  42. package/lib/Utils/group-status-detection.js +72 -0
  43. package/lib/Utils/group-status-detection.js.map +1 -0
  44. package/lib/Utils/index.d.ts +3 -0
  45. package/lib/Utils/index.d.ts.map +1 -1
  46. package/lib/Utils/index.js +3 -0
  47. package/lib/Utils/index.js.map +1 -1
  48. package/lib/Utils/message-retry-manager.d.ts.map +1 -1
  49. package/lib/Utils/message-retry-manager.js.map +1 -1
  50. package/lib/Utils/messages-media.d.ts +1 -0
  51. package/lib/Utils/messages-media.d.ts.map +1 -1
  52. package/lib/Utils/messages-media.js +92 -0
  53. package/lib/Utils/messages-media.js.map +1 -1
  54. package/lib/Utils/messages-newsletter.d.ts +34 -0
  55. package/lib/Utils/messages-newsletter.d.ts.map +1 -1
  56. package/lib/Utils/messages-newsletter.js +67 -0
  57. package/lib/Utils/messages-newsletter.js.map +1 -1
  58. package/lib/Utils/messages.d.ts +3 -46
  59. package/lib/Utils/messages.d.ts.map +1 -1
  60. package/lib/Utils/messages.js +306 -84
  61. package/lib/Utils/messages.js.map +1 -1
  62. package/lib/Utils/scheduling.d.ts +46 -0
  63. package/lib/Utils/scheduling.d.ts.map +1 -0
  64. package/lib/Utils/scheduling.js +117 -0
  65. package/lib/Utils/scheduling.js.map +1 -0
  66. package/lib/Utils/signal.d.ts +0 -4
  67. package/lib/Utils/signal.d.ts.map +1 -1
  68. package/lib/Utils/signal.js +7 -14
  69. package/lib/Utils/signal.js.map +1 -1
  70. package/lib/Utils/use-sqlite-auth-state.d.ts +1 -0
  71. package/lib/Utils/use-sqlite-auth-state.d.ts.map +1 -1
  72. package/lib/Utils/use-sqlite-auth-state.js +18 -3
  73. package/lib/Utils/use-sqlite-auth-state.js.map +1 -1
  74. package/lib/Utils/validate-connection.d.ts.map +1 -1
  75. package/lib/Utils/validate-connection.js +1 -4
  76. package/lib/Utils/validate-connection.js.map +1 -1
  77. package/package.json +1 -1
@@ -3,7 +3,7 @@ import { Boom } from '@neykoor/boom';
3
3
  import { randomBytes } from 'crypto';
4
4
  import { proto } from '../../WAProto/index.js';
5
5
  import { BIZ_BOT_SUPPORT_PAYLOAD, DEFAULT_CACHE_TTLS, WA_DEFAULT_EPHEMERAL } from '../Defaults/index.js';
6
- import { aggregateMessageKeysNotFromMe, assertMediaContent, assertMeId, bindWaitForEvent, captureUnifiedResponse, decryptMediaRetryData, DEF_MEDIA_HOST, delay, encodeNewsletterMessage, encodeSignedDeviceIdentity, encodeWAMessage, encryptMediaRetryRequest, extractDeviceJids, generateCodeBlockContent, generateLatexContent, generateListContent, generateMessageIDV2, generateParticipantHashV2, generateRichMessageContent, generateTableContent, generateUnifiedResponseContent, generateWAMessage, generateWAMessageFromContent, getStatusCodeForMediaRetry, getUrlFromDirectPath, getWAUploadToServer, MessageRetryManager, normalizeMessageContent, parseAndInjectE2ESessions, unixTimestampSeconds } from '../Utils/index.js';
6
+ import { aggregateMessageKeysNotFromMe, assertMediaContent, assertMeId, bindWaitForEvent, compressMedia, convertMedia, captureUnifiedResponse, decryptMediaRetryData, DEF_MEDIA_HOST, delay, encodeNewsletterMessage, encodeSignedDeviceIdentity, encodeWAMessage, encryptMediaRetryRequest, extractDeviceJids, generateCodeBlockContent, generateLatexContent, generateLatexImageContent, generateLatexInlineImageContent, generateListContent, generateMessageIDV2, generateParticipantHashV2, generateRichMessageContent, generateTableContent, generateUnifiedResponseContent, generateWAMessage, generateWAMessageFromContent, getMediaMetadata, getStatusCodeForMediaRetry, getUrlFromDirectPath, getWAUploadToServer, imageToWebpSticker, MessageRetryManager, MessageScheduler, normalizeMessageContent, parseAndInjectE2ESessions, resizeImage, unixTimestampSeconds } from '../Utils/index.js';
7
7
  import { getUrlInfo } from '../Utils/link-preview.js';
8
8
  import { makeKeyedMutex, makeMutex } from '../Utils/make-mutex.js';
9
9
  import { getMessageReportingToken, shouldIncludeReportingToken } from '../Utils/reporting-utils.js';
@@ -11,30 +11,22 @@ import { buildMergedTcTokenIndexWrite, isTcTokenExpired, resolveIssuanceJid, res
11
11
  import { areJidsSameUser, getBinaryNodeChild, getBinaryNodeChildren, isHostedLidUser, isHostedPnUser, isJidBot, isJidGroup, isJidMetaAI, isLidUser, isPnUser, jidDecode, jidEncode, jidNormalizedUser, PSA_WID, S_WHATSAPP_NET, STORIES_JID } from '../WABinary/index.js';
12
12
  import { USyncQuery, USyncUser } from '../WAUSync/index.js';
13
13
  import { makeNewsletterSocket } from './newsletter.js';
14
+ import { Smgss } from './smgss.js';
14
15
  export const makeMessagesSocket = (config) => {
15
16
  const { logger, linkPreviewImageThumbnailWidth, generateHighQualityLinkPreview, options: httpRequestOptions, patchMessageBeforeSending, cachedGroupMetadata, enableRecentMessageCache, maxMsgRetryCount } = config;
16
17
  const sock = makeNewsletterSocket(config);
17
18
  const { ev, authState, messageMutex, signalRepository, upsertMessage, query, fetchPrivacySettings, sendNode, groupMetadata, groupToggleEphemeral, registerSocketEndHandler } = sock;
18
19
  const getLIDForPN = signalRepository.lidMapping.getLIDForPN.bind(signalRepository.lidMapping);
19
- /**
20
- * Set of tctoken storage JIDs with a fire-and-forget `issuePrivacyTokens` IQ in flight.
21
- * Prevents duplicate IQs from rapid back-to-back sends before `senderTimestamp` persists.
22
- * Entries are always removed in `.finally()`, so the set is bounded by concurrency.
23
- */
24
20
  const inFlightTcTokenIssuance = new Set();
25
21
  const userDevicesCache = config.userDevicesCache ||
26
22
  new NodeCache({
27
- stdTTL: DEFAULT_CACHE_TTLS.USER_DEVICES, // 5 minutes
23
+ stdTTL: DEFAULT_CACHE_TTLS.USER_DEVICES,
28
24
  useClones: false
29
25
  });
30
- /** Serializes writes to userDevicesCache across USync refresh and device-notification handling. */
31
26
  const devicesMutex = makeMutex();
32
- // Initialize message retry manager if enabled
33
27
  const messageRetryManager = enableRecentMessageCache ? new MessageRetryManager(logger, maxMsgRetryCount) : null;
34
- // Prevent race conditions in Signal session encryption by user
35
28
  const encryptionMutex = makeKeyedMutex();
36
29
  let mediaConn;
37
- /** Per-socket media host; updated whenever media_conn is fetched. Defaults to the public WhatsApp host. */
38
30
  let mediaHost = DEF_MEDIA_HOST;
39
31
  const refreshMediaConn = async (forceGet = false) => {
40
32
  const media = await mediaConn;
@@ -50,7 +42,6 @@ export const makeMessagesSocket = (config) => {
50
42
  content: [{ tag: 'media_conn', attrs: {} }]
51
43
  });
52
44
  const mediaConnNode = getBinaryNodeChild(result, 'media_conn');
53
- // TODO: explore full length of data that whatsapp provides
54
45
  const node = {
55
46
  hosts: getBinaryNodeChildren(mediaConnNode, 'host').map(({ attrs }) => ({
56
47
  hostname: attrs.hostname,
@@ -69,10 +60,6 @@ export const makeMessagesSocket = (config) => {
69
60
  }
70
61
  return mediaConn;
71
62
  };
72
- /**
73
- * generic send receipt function
74
- * used for receipts of phone call, read, delivery etc.
75
- * */
76
63
  const sendReceipt = async (jid, participant, messageIds, type) => {
77
64
  if (!messageIds || messageIds.length === 0) {
78
65
  throw new Boom('missing ids in receipt');
@@ -116,21 +103,17 @@ export const makeMessagesSocket = (config) => {
116
103
  logger.debug({ attrs: node.attrs, messageIds }, 'sending receipt for messages');
117
104
  await sendNode(node);
118
105
  };
119
- /** Correctly bulk send receipts to multiple chats, participants */
120
106
  const sendReceipts = async (keys, type) => {
121
107
  const recps = aggregateMessageKeysNotFromMe(keys);
122
108
  for (const { jid, participant, messageIds } of recps) {
123
109
  await sendReceipt(jid, participant, messageIds, type);
124
110
  }
125
111
  };
126
- /** Bulk read messages. Keys can be from different chats & participants */
127
112
  const readMessages = async (keys) => {
128
113
  const privacySettings = await fetchPrivacySettings();
129
- // based on privacy settings, we have to change the read type
130
114
  const readType = privacySettings.readreceipts === 'all' ? 'read' : 'read-self';
131
115
  await sendReceipts(keys, readType);
132
116
  };
133
- /** Fetch all the devices we've to send a message to */
134
117
  const getUSyncDevices = async (jids, useCache, ignoreZeroDevices) => {
135
118
  const deviceResults = [];
136
119
  if (!useCache) {
@@ -193,16 +176,14 @@ export const makeMessagesSocket = (config) => {
193
176
  }
194
177
  const query = new USyncQuery().withContext('message').withDeviceProtocol().withLIDProtocol();
195
178
  for (const jid of toFetch) {
196
- query.withUser(new USyncUser().withId(jid)); // todo: investigate - the idea here is that <user> should have an inline lid field with the lid being the pn equivalent
179
+ query.withUser(new USyncUser().withId(jid));
197
180
  }
198
181
  const result = await sock.executeUSyncQuery(query);
199
182
  if (result) {
200
- // TODO: LID MAP this stuff (lid protocol will now return lid with devices)
201
183
  const lidResults = result.list.filter(a => !!a.lid);
202
184
  if (lidResults.length > 0) {
203
185
  logger.trace('Storing LID maps from device call');
204
186
  await signalRepository.lidMapping.storeLIDPNMappings(lidResults.map(a => ({ lid: a.lid, pn: a.id })));
205
- // Force-refresh sessions for newly mapped LIDs to align identity addressing
206
187
  try {
207
188
  const lids = lidResults.map(a => a.lid);
208
189
  if (lids.length) {
@@ -219,10 +200,8 @@ export const makeMessagesSocket = (config) => {
219
200
  deviceMap[item.user] = deviceMap[item.user] || [];
220
201
  deviceMap[item.user]?.push(item);
221
202
  }
222
- // Process each user's devices as a group for bulk LID migration
223
203
  for (const [user, userDevices] of Object.entries(deviceMap)) {
224
204
  const isLidUser = requestedLidUsers.has(user);
225
- // Process all devices for this user
226
205
  for (const item of userDevices) {
227
206
  const finalJid = isLidUser
228
207
  ? jidEncode(user, item.server, item.device)
@@ -241,7 +220,6 @@ export const makeMessagesSocket = (config) => {
241
220
  }
242
221
  await devicesMutex.mutex(async () => {
243
222
  if (userDevicesCache.mset) {
244
- // if the cache supports mset, we can set all devices in one go
245
223
  await userDevicesCache.mset(Object.entries(deviceMap).map(([key, value]) => ({ key, value })));
246
224
  }
247
225
  else {
@@ -269,9 +247,6 @@ export const makeMessagesSocket = (config) => {
269
247
  }
270
248
  return deviceResults;
271
249
  };
272
- /**
273
- * Update Member Label
274
- */
275
250
  const updateMemberLabel = (jid, memberLabel) => {
276
251
  return relayMessage(jid, {
277
252
  protocolMessage: {
@@ -309,7 +284,6 @@ export const makeMessagesSocket = (config) => {
309
284
  jidsRequiringFetch.push(jid);
310
285
  }
311
286
  if (jidsRequiringFetch.length) {
312
- // LID if mapped, otherwise original
313
287
  const wireJids = [
314
288
  ...jidsRequiringFetch.filter(jid => !!isLidUser(jid) || !!isHostedLidUser(jid)),
315
289
  ...((await signalRepository.lidMapping.getLIDsForPNs(jidsRequiringFetch.filter(jid => !!isPnUser(jid) || !!isHostedPnUser(jid)))) || []).map(a => a.lid)
@@ -341,7 +315,6 @@ export const makeMessagesSocket = (config) => {
341
315
  return didFetchNewSession;
342
316
  };
343
317
  const sendPeerDataOperationMessage = async (pdoMessage) => {
344
- //TODO: for later, abstract the logic to send a Peer Message instead of just PDO - useful for App State Key Resync with phone
345
318
  if (!authState.creds.me?.id) {
346
319
  throw new Boom('Not authenticated');
347
320
  }
@@ -494,25 +467,23 @@ export const makeMessagesSocket = (config) => {
494
467
  return;
495
468
  }
496
469
  if (normalizeMessageContent(message)?.pinInChatMessage || normalizeMessageContent(message)?.reactionMessage) {
497
- extraAttrs['decrypt-fail'] = 'hide'; // todo: expand for reactions and other types
470
+ extraAttrs['decrypt-fail'] = 'hide';
498
471
  }
499
472
  if (isGroupOrStatus && !isRetryResend) {
500
473
  const [groupData, senderKeyMap] = await Promise.all([
501
474
  (async () => {
502
- let groupData = useCachedGroupMetadata && cachedGroupMetadata ? await cachedGroupMetadata(jid) : undefined; // todo: should we rely on the cache specially if the cache is outdated and the metadata has new fields?
475
+ let groupData = useCachedGroupMetadata && cachedGroupMetadata ? await cachedGroupMetadata(jid) : undefined;
503
476
  if (groupData && Array.isArray(groupData?.participants)) {
504
477
  logger.trace({ jid, participants: groupData.participants.length }, 'using cached group metadata');
505
478
  }
506
479
  else if (!isStatus) {
507
- groupData = await groupMetadata(jid); // TODO: start storing group participant list + addr mode in Signal & stop relying on this
480
+ groupData = await groupMetadata(jid);
508
481
  }
509
482
  return groupData;
510
483
  })(),
511
484
  (async () => {
512
485
  if (!participant && !isStatus) {
513
- // what if sender memory is less accurate than the cached metadata
514
- // on participant change in group, we should do sender memory manipulation
515
- const result = await authState.keys.get('sender-key-memory', [jid]); // TODO: check out what if the sender key memory doesn't include the LID stuff now?
486
+ const result = await authState.keys.get('sender-key-memory', [jid]);
516
487
  return result[jid] || {};
517
488
  }
518
489
  return {};
@@ -557,8 +528,6 @@ export const makeMessagesSocket = (config) => {
557
528
  !isHostedLidUser(deviceJid) &&
558
529
  !isHostedPnUser(deviceJid) &&
559
530
  device.device !== 99) {
560
- //todo: revamp all this logic
561
- // the goal is to follow with what I said above for each group, and instead of a true false map of ids, we can set an array full of those the app has already sent pkmsgs
562
531
  senderKeyRecipients.push(deviceJid);
563
532
  senderKeyMap[deviceJid] = true;
564
533
  }
@@ -585,8 +554,6 @@ export const makeMessagesSocket = (config) => {
585
554
  await authState.keys.set({ 'sender-key-memory': { [jid]: senderKeyMap } });
586
555
  }
587
556
  else {
588
- // ADDRESSING CONSISTENCY: Match own identity to conversation context
589
- // TODO: investigate if this is true
590
557
  let ownId = meId;
591
558
  if (isLid && meLid) {
592
559
  ownId = meLid;
@@ -607,7 +574,7 @@ export const makeMessagesSocket = (config) => {
607
574
  devices.push({
608
575
  user,
609
576
  device: 0,
610
- jid: jidEncode(user, targetUserServer, 0) // rajeh, todo: this entire logic is convoluted and weird.
577
+ jid: jidEncode(user, targetUserServer, 0)
611
578
  });
612
579
  if (user !== ownUser) {
613
580
  const ownUserServer = isLid ? 'lid' : 's.whatsapp.net';
@@ -619,13 +586,10 @@ export const makeMessagesSocket = (config) => {
619
586
  });
620
587
  }
621
588
  if (additionalAttributes?.['category'] !== 'peer') {
622
- // Clear placeholders and enumerate actual devices
623
589
  devices.length = 0;
624
- // Use conversation-appropriate sender identity
625
590
  const senderIdentity = isLid && meLid
626
591
  ? jidEncode(jidDecode(meLid)?.user, 'lid', undefined)
627
592
  : jidEncode(jidDecode(meId)?.user, 's.whatsapp.net', undefined);
628
- // Enumerate devices for sender and target with consistent addressing
629
593
  const sessionDevices = await getUSyncDevices([senderIdentity, jid], true, false);
630
594
  devices.push(...sessionDevices);
631
595
  logger.debug({
@@ -645,7 +609,6 @@ export const makeMessagesSocket = (config) => {
645
609
  logger.debug({ jid, meId, meLid }, 'Skipping exact sender device (whatsmeow pattern)');
646
610
  continue;
647
611
  }
648
- // Check if this is our device (could match either PN or LID user)
649
612
  const isMe = user === mePnUser || user === meLidUser;
650
613
  if (isMe) {
651
614
  meRecipients.push(jid);
@@ -657,7 +620,6 @@ export const makeMessagesSocket = (config) => {
657
620
  }
658
621
  await assertSessions(allRecipients);
659
622
  const [{ nodes: meNodes, shouldIncludeDeviceIdentity: s1 }, { nodes: otherNodes, shouldIncludeDeviceIdentity: s2 }] = await Promise.all([
660
- // For own devices: use DSM if available (1:1 chats only)
661
623
  createParticipantNodes(meRecipients, meMsg || message, extraAttrs),
662
624
  createParticipantNodes(otherRecipients, message, extraAttrs, meMsg)
663
625
  ]);
@@ -725,7 +687,7 @@ export const makeMessagesSocket = (config) => {
725
687
  if (additionalAttributes?.['category'] === 'peer') {
726
688
  const peerNode = participants[0]?.content?.[0];
727
689
  if (peerNode) {
728
- binaryNodeContent.push(peerNode); // push only enc
690
+ binaryNodeContent.push(peerNode);
729
691
  }
730
692
  }
731
693
  else {
@@ -746,9 +708,6 @@ export const makeMessagesSocket = (config) => {
746
708
  },
747
709
  content: binaryNodeContent
748
710
  };
749
- // if the participant to send to is explicitly specified (generally retry recp)
750
- // ensure the message is only sent to that person
751
- // if a retry receipt is sent to everyone -- it'll fail decryption for everyone else who received the msg
752
711
  if (participant) {
753
712
  if (isJidGroup(destinationJid)) {
754
713
  stanza.attrs.to = destinationJid;
@@ -797,19 +756,15 @@ export const makeMessagesSocket = (config) => {
797
756
  logger.warn({ jid, trace: error?.stack }, 'failed to attach reporting token');
798
757
  }
799
758
  }
800
- // WA Web never attaches tctoken to peer (AppStateSync) messages — server rejects with 479
801
759
  const isPeerMessage = additionalAttributes?.['category'] === 'peer';
802
760
  const is1on1Send = !isGroup && !isRetryResend && !isStatus && !isNewsletter && !isPeerMessage;
803
- // Resolve destination to LID for tctoken storage — matches Signal session key pattern
804
761
  const tcTokenJid = is1on1Send ? await resolveTcTokenJid(destinationJid, getLIDForPN) : destinationJid;
805
762
  const contactTcTokenData = is1on1Send ? await authState.keys.get('tctoken', [tcTokenJid]) : {};
806
763
  const existingTokenEntry = contactTcTokenData[tcTokenJid];
807
764
  let tcTokenBuffer = existingTokenEntry?.token;
808
- // Treat expired tokens the same as missing — clear from cache
809
765
  if (tcTokenBuffer?.length && isTcTokenExpired(existingTokenEntry?.timestamp)) {
810
766
  logger.debug({ jid: destinationJid, timestamp: existingTokenEntry?.timestamp }, 'tctoken expired, clearing');
811
767
  tcTokenBuffer = undefined;
812
- // Preserve senderTimestamp so the fire-and-forget issuance dedupe survives cleanup.
813
768
  const cleared = existingTokenEntry?.senderTimestamp !== undefined
814
769
  ? { token: Buffer.alloc(0), senderTimestamp: existingTokenEntry.senderTimestamp }
815
770
  : null;
@@ -834,8 +789,6 @@ export const makeMessagesSocket = (config) => {
834
789
  }
835
790
  logger.debug({ msgId }, `sending message to ${participants.length} devices`);
836
791
  await sendNode(stanza);
837
- // Fire-and-forget: issue our token to the contact AFTER message send.
838
- // WA Web skips protocol messages and PSA/bot contacts (TcTokenChatAction: isRegularUser)
839
792
  const isProtocolMsg = !!normalizeMessageContent(message)?.protocolMessage;
840
793
  const isBotOrPSA = destinationJid === PSA_WID || isJidBot(destinationJid) || isJidMetaAI(destinationJid);
841
794
  if (is1on1Send &&
@@ -876,7 +829,6 @@ export const makeMessagesSocket = (config) => {
876
829
  inFlightTcTokenIssuance.delete(tcTokenJid);
877
830
  });
878
831
  }
879
- // Add message to retry cache if enabled
880
832
  if (messageRetryManager && !participant) {
881
833
  messageRetryManager.addRecentMessage(destinationJid, msgId, message);
882
834
  }
@@ -938,12 +890,18 @@ export const makeMessagesSocket = (config) => {
938
890
  else if (message.listResponseMessage) {
939
891
  return 'list_response';
940
892
  }
893
+ else if (message.buttonsResponseMessage) {
894
+ return 'buttons_response';
895
+ }
941
896
  else if (message.orderMessage) {
942
897
  return 'order';
943
898
  }
944
899
  else if (message.productMessage) {
945
900
  return 'product';
946
901
  }
902
+ else if (message.interactiveResponseMessage) {
903
+ return 'native_flow_response';
904
+ }
947
905
  else if (message.groupInviteMessage) {
948
906
  return 'url';
949
907
  }
@@ -976,6 +934,83 @@ export const makeMessagesSocket = (config) => {
976
934
  return result;
977
935
  };
978
936
  const waUploadToServer = getWAUploadToServer(config, refreshMediaConn);
937
+ const smgss = new Smgss(waUploadToServer, relayMessage, config, sock);
938
+ const getButtonType = (message) => {
939
+ if (message.listMessage)
940
+ return 'list';
941
+ if (message.buttonsMessage)
942
+ return 'buttons';
943
+ if (message.templateMessage)
944
+ return 'template';
945
+ if (message.interactiveMessage?.nativeFlowMessage)
946
+ return 'native_flow';
947
+ if (message.interactiveMessage?.shopStorefrontMessage)
948
+ return 'shop';
949
+ if (message.interactiveMessage?.collectionMessage)
950
+ return 'collection';
951
+ if (message.interactiveMessage?.carouselMessage)
952
+ return 'carousel';
953
+ if (message.interactiveMessage)
954
+ return 'interactive';
955
+ return null;
956
+ };
957
+ const getButtonArgs = (message) => {
958
+ const nativeFlow = message.interactiveMessage?.nativeFlowMessage;
959
+ const firstButtonName = nativeFlow?.buttons?.[0]?.name;
960
+ const nativeFlowSpecials = [
961
+ 'mpm',
962
+ 'cta_catalog',
963
+ 'send_location',
964
+ 'call_permission_request',
965
+ 'wa_payment_transaction_details',
966
+ 'automated_greeting_message_view_catalog'
967
+ ];
968
+ const ts = unixTimestampSeconds().toString();
969
+ const bizBase = { actual_actors: '2', host_storage: '2', privacy_mode_ts: ts };
970
+ const qualityControl = { tag: 'quality_control', attrs: { source_type: 'third_party' } };
971
+ if (nativeFlow && (firstButtonName === 'review_and_pay' || firstButtonName === 'payment_info')) {
972
+ return {
973
+ tag: 'biz',
974
+ attrs: { native_flow_name: firstButtonName === 'review_and_pay' ? 'order_details' : firstButtonName }
975
+ };
976
+ }
977
+ else if (nativeFlow && nativeFlowSpecials.includes(firstButtonName)) {
978
+ return {
979
+ tag: 'biz',
980
+ attrs: bizBase,
981
+ content: [
982
+ {
983
+ tag: 'interactive',
984
+ attrs: { type: 'native_flow', v: '1' },
985
+ content: [{ tag: 'native_flow', attrs: { v: '2', name: firstButtonName } }]
986
+ },
987
+ qualityControl
988
+ ]
989
+ };
990
+ }
991
+ else if (nativeFlow || message.buttonsMessage || message.templateMessage || message.interactiveMessage) {
992
+ return {
993
+ tag: 'biz',
994
+ attrs: bizBase,
995
+ content: [
996
+ {
997
+ tag: 'interactive',
998
+ attrs: { type: 'native_flow', v: '1' },
999
+ content: [{ tag: 'native_flow', attrs: { v: '9', name: 'mixed' } }]
1000
+ },
1001
+ qualityControl
1002
+ ]
1003
+ };
1004
+ }
1005
+ else if (message.listMessage) {
1006
+ return {
1007
+ tag: 'biz',
1008
+ attrs: bizBase,
1009
+ content: [{ tag: 'list', attrs: { v: '2', type: 'product_list' } }, qualityControl]
1010
+ };
1011
+ }
1012
+ return { tag: 'biz', attrs: bizBase };
1013
+ };
979
1014
  const waitForMsgMediaUpdate = bindWaitForEvent(ev, 'messages.media-update');
980
1015
  registerSocketEndHandler(() => {
981
1016
  if (!config.userDevicesCache && userDevicesCache.close) {
@@ -1101,7 +1136,7 @@ export const makeMessagesSocket = (config) => {
1101
1136
  }
1102
1137
  return msg;
1103
1138
  };
1104
- return {
1139
+ const messagesSock = {
1105
1140
  ...sock,
1106
1141
  userDevicesCache,
1107
1142
  devicesMutex,
@@ -1113,7 +1148,11 @@ export const makeMessagesSocket = (config) => {
1113
1148
  sendReceipts,
1114
1149
  readMessages,
1115
1150
  refreshMediaConn,
1116
- // Function (not getter) so the spread in chats.ts preserves the live closure binding.
1151
+ resize: resizeImage,
1152
+ convert: convertMedia,
1153
+ toSticker: imageToWebpSticker,
1154
+ compress: compressMedia,
1155
+ metadata: getMediaMetadata,
1117
1156
  getMediaHost: () => mediaHost,
1118
1157
  waUploadToServer,
1119
1158
  fetchPrivacySettings,
@@ -1185,6 +1224,16 @@ export const makeMessagesSocket = (config) => {
1185
1224
  await relayMessage(jid, message, { messageId });
1186
1225
  return { message, messageId };
1187
1226
  },
1227
+ sendLatexImage: async (jid, quoted, options, renderLatexToPng, uploadFn) => {
1228
+ const { message, messageId } = await generateLatexImageContent(quoted, options, uploadFn, renderLatexToPng);
1229
+ await relayMessage(jid, message, { messageId });
1230
+ return { message, messageId };
1231
+ },
1232
+ sendLatexInlineImage: async (jid, quoted, options, renderLatexToPng, uploadFn) => {
1233
+ const { message, messageId } = await generateLatexInlineImageContent(quoted, options, uploadFn, renderLatexToPng);
1234
+ await relayMessage(jid, message, { messageId });
1235
+ return { message, messageId };
1236
+ },
1188
1237
  sendUnifiedResponse: async (jid, quoted, captured) => {
1189
1238
  const { message, messageId } = generateUnifiedResponseContent(quoted, captured);
1190
1239
  await relayMessage(jid, message, { messageId });
@@ -1197,6 +1246,15 @@ export const makeMessagesSocket = (config) => {
1197
1246
  },
1198
1247
  sendMessage: async (jid, content, options = {}) => {
1199
1248
  const userJid = authState.creds.me.id;
1249
+ const smgssType = smgss.detectType(content);
1250
+ if (smgssType) {
1251
+ const quoted = options?.quoted;
1252
+ const built = await smgss.handle(smgssType, content, jid, quoted);
1253
+ if (smgssType === 'ALBUM' || smgssType === 'EVENT' || smgssType === 'POLL_RESULT' || smgssType === 'GROUP_STORY') {
1254
+ return built;
1255
+ }
1256
+ content = built;
1257
+ }
1200
1258
  if (typeof content === 'object' &&
1201
1259
  'disappearingMessagesInChat' in content &&
1202
1260
  typeof content['disappearingMessagesInChat'] !== 'undefined' &&
@@ -1222,7 +1280,6 @@ export const makeMessagesSocket = (config) => {
1222
1280
  logger,
1223
1281
  uploadImage: generateHighQualityLinkPreview ? waUploadToServer : undefined
1224
1282
  }),
1225
- //TODO: CACHE
1226
1283
  getProfilePicUrl: sock.profilePictureUrl,
1227
1284
  getCallLink: sock.createCallLink,
1228
1285
  upload: waUploadToServer,
@@ -1232,17 +1289,16 @@ export const makeMessagesSocket = (config) => {
1232
1289
  ...options
1233
1290
  });
1234
1291
  const isEventMsg = 'event' in content && !!content.event;
1235
- const isDeleteMsg = 'delete' in content && !!content.delete;
1292
+ const deleteKey = 'delete' in content ? content.delete : undefined;
1293
+ const isDeleteMsg = !!deleteKey;
1236
1294
  const isEditMsg = 'edit' in content && !!content.edit;
1237
1295
  const isPinMsg = 'pin' in content && !!content.pin;
1238
1296
  const isPollMessage = 'poll' in content && !!content.poll;
1239
1297
  const isAiMsg = 'ai' in content && !!content.ai;
1240
1298
  const additionalAttributes = {};
1241
1299
  const additionalNodes = [];
1242
- // required for delete
1243
1300
  if (isDeleteMsg) {
1244
- // if the chat is a group, and I am not the author, then delete the message as an admin
1245
- if (isJidGroup(content.delete?.remoteJid) && !content.delete?.fromMe) {
1301
+ if (isJidGroup(deleteKey?.remoteJid) && !deleteKey?.fromMe) {
1246
1302
  additionalAttributes.edit = '8';
1247
1303
  }
1248
1304
  else {
@@ -1289,6 +1345,12 @@ export const makeMessagesSocket = (config) => {
1289
1345
  });
1290
1346
  delete content.ai;
1291
1347
  }
1348
+ const buttonType = getButtonType(fullMsg.message);
1349
+ if (buttonType) {
1350
+ const btnNode = getButtonArgs(fullMsg.message);
1351
+ if (btnNode)
1352
+ additionalNodes.push(btnNode);
1353
+ }
1292
1354
  await relayMessage(jid, fullMsg.message, {
1293
1355
  messageId: fullMsg.key.id,
1294
1356
  useCachedGroupMetadata: options.useCachedGroupMetadata,
@@ -1305,5 +1367,17 @@ export const makeMessagesSocket = (config) => {
1305
1367
  }
1306
1368
  }
1307
1369
  };
1370
+ const messageScheduler = new MessageScheduler(messagesSock.sendMessage, {
1371
+ logger: config.logger
1372
+ });
1373
+ return {
1374
+ ...messagesSock,
1375
+ messageScheduler,
1376
+ scheduleMessage: messageScheduler.schedule.bind(messageScheduler),
1377
+ scheduleMessageDelay: messageScheduler.scheduleDelay.bind(messageScheduler),
1378
+ cancelScheduledMessage: messageScheduler.cancel.bind(messageScheduler),
1379
+ cancelScheduledMessagesForJid: messageScheduler.cancelForJid.bind(messageScheduler),
1380
+ getPendingScheduledMessages: messageScheduler.getPending.bind(messageScheduler)
1381
+ };
1308
1382
  };
1309
1383
  //# sourceMappingURL=messages-send.js.map