amiudmodz 6.2.2 → 6.2.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 (115) hide show
  1. package/lib/Defaults/index.d.ts +8 -0
  2. package/lib/Defaults/index.d.ts.map +1 -1
  3. package/lib/Defaults/index.js +26 -10
  4. package/lib/Defaults/index.js.map +1 -1
  5. package/lib/Signal/lid-mapping.d.ts +3 -3
  6. package/lib/Signal/lid-mapping.d.ts.map +1 -1
  7. package/lib/Signal/lid-mapping.js +21 -14
  8. package/lib/Signal/lid-mapping.js.map +1 -1
  9. package/lib/Socket/business.d.ts +19 -12
  10. package/lib/Socket/business.d.ts.map +1 -1
  11. package/lib/Socket/chats.d.ts +2 -5
  12. package/lib/Socket/chats.d.ts.map +1 -1
  13. package/lib/Socket/chats.js +29 -11
  14. package/lib/Socket/chats.js.map +1 -1
  15. package/lib/Socket/communities.d.ts +19 -12
  16. package/lib/Socket/communities.d.ts.map +1 -1
  17. package/lib/Socket/groups.d.ts +1 -4
  18. package/lib/Socket/groups.d.ts.map +1 -1
  19. package/lib/Socket/index.d.ts +2 -277
  20. package/lib/Socket/index.d.ts.map +1 -1
  21. package/lib/Socket/index.js +30 -1
  22. package/lib/Socket/index.js.map +1 -1
  23. package/lib/Socket/messages-recv.d.ts +19 -12
  24. package/lib/Socket/messages-recv.d.ts.map +1 -1
  25. package/lib/Socket/messages-recv.js +117 -28
  26. package/lib/Socket/messages-recv.js.map +1 -1
  27. package/lib/Socket/messages-send.d.ts +18 -11
  28. package/lib/Socket/messages-send.d.ts.map +1 -1
  29. package/lib/Socket/messages-send.js +362 -250
  30. package/lib/Socket/messages-send.js.map +1 -1
  31. package/lib/Socket/mex.d.ts +1 -1
  32. package/lib/Socket/mex.d.ts.map +1 -1
  33. package/lib/Socket/mex.js +4 -4
  34. package/lib/Socket/mex.js.map +1 -1
  35. package/lib/Socket/newsletter.d.ts +6 -7
  36. package/lib/Socket/newsletter.d.ts.map +1 -1
  37. package/lib/Socket/newsletter.js +74 -26
  38. package/lib/Socket/newsletter.js.map +1 -1
  39. package/lib/Socket/socket.d.ts.map +1 -1
  40. package/lib/Socket/socket.js +7 -26
  41. package/lib/Socket/socket.js.map +1 -1
  42. package/lib/Types/Auth.d.ts +10 -0
  43. package/lib/Types/Auth.d.ts.map +1 -1
  44. package/lib/Types/Events.d.ts +2 -1
  45. package/lib/Types/Events.d.ts.map +1 -1
  46. package/lib/Types/Message.d.ts +59 -10
  47. package/lib/Types/Message.d.ts.map +1 -1
  48. package/lib/Types/Message.js +6 -0
  49. package/lib/Types/Message.js.map +1 -1
  50. package/lib/Types/Mex.d.ts +2 -0
  51. package/lib/Types/Mex.d.ts.map +1 -1
  52. package/lib/Types/Mex.js +2 -0
  53. package/lib/Types/Mex.js.map +1 -1
  54. package/lib/Types/Socket.d.ts +18 -0
  55. package/lib/Types/Socket.d.ts.map +1 -1
  56. package/lib/Types/State.d.ts +10 -0
  57. package/lib/Types/State.d.ts.map +1 -1
  58. package/lib/Types/State.js.map +1 -1
  59. package/lib/Types/index.d.ts +1 -1
  60. package/lib/Types/index.d.ts.map +1 -1
  61. package/lib/Types/index.js +1 -0
  62. package/lib/Types/index.js.map +1 -1
  63. package/lib/Utils/MessageBuilder.d.ts +11 -10
  64. package/lib/Utils/MessageBuilder.d.ts.map +1 -1
  65. package/lib/Utils/MessageBuilder.js +14 -3
  66. package/lib/Utils/MessageBuilder.js.map +1 -1
  67. package/lib/Utils/auth-utils.d.ts.map +1 -1
  68. package/lib/Utils/auth-utils.js +23 -8
  69. package/lib/Utils/auth-utils.js.map +1 -1
  70. package/lib/Utils/browser-utils.js +4 -4
  71. package/lib/Utils/browser-utils.js.map +1 -1
  72. package/lib/Utils/decode-wa-message.d.ts.map +1 -1
  73. package/lib/Utils/decode-wa-message.js +9 -5
  74. package/lib/Utils/decode-wa-message.js.map +1 -1
  75. package/lib/Utils/generics.d.ts.map +1 -1
  76. package/lib/Utils/generics.js +8 -5
  77. package/lib/Utils/generics.js.map +1 -1
  78. package/lib/Utils/index.d.ts +6 -1
  79. package/lib/Utils/index.d.ts.map +1 -1
  80. package/lib/Utils/index.js +6 -1
  81. package/lib/Utils/index.js.map +1 -1
  82. package/lib/Utils/messages-media.d.ts +12 -18
  83. package/lib/Utils/messages-media.d.ts.map +1 -1
  84. package/lib/Utils/messages-media.js +163 -65
  85. package/lib/Utils/messages-media.js.map +1 -1
  86. package/lib/Utils/messages.d.ts +26 -71
  87. package/lib/Utils/messages.d.ts.map +1 -1
  88. package/lib/Utils/messages.js +1198 -328
  89. package/lib/Utils/messages.js.map +1 -1
  90. package/lib/Utils/tc-token-utils.d.ts.map +1 -1
  91. package/lib/Utils/tc-token-utils.js +2 -3
  92. package/lib/Utils/tc-token-utils.js.map +1 -1
  93. package/lib/Utils/use-multi-file-auth-state.d.ts.map +1 -1
  94. package/lib/Utils/use-multi-file-auth-state.js +30 -1
  95. package/lib/Utils/use-multi-file-auth-state.js.map +1 -1
  96. package/lib/Utils/use-sqlite-auth-state.js +12 -12
  97. package/lib/Utils/validate-connection.d.ts.map +1 -1
  98. package/lib/Utils/validate-connection.js +5 -11
  99. package/lib/Utils/validate-connection.js.map +1 -1
  100. package/lib/WABinary/constants.d.ts +8 -0
  101. package/lib/WABinary/constants.d.ts.map +1 -1
  102. package/lib/WABinary/constants.js +72 -0
  103. package/lib/WABinary/constants.js.map +1 -1
  104. package/lib/WABinary/generic-utils.d.ts +1 -0
  105. package/lib/WABinary/generic-utils.d.ts.map +1 -1
  106. package/lib/WABinary/generic-utils.js +100 -1
  107. package/lib/WABinary/generic-utils.js.map +1 -1
  108. package/lib/WABinary/jid-utils.d.ts.map +1 -1
  109. package/lib/WABinary/jid-utils.js +45 -6
  110. package/lib/WABinary/jid-utils.js.map +1 -1
  111. package/lib/index.d.ts +1 -0
  112. package/lib/index.d.ts.map +1 -1
  113. package/lib/index.js +1 -0
  114. package/lib/index.js.map +1 -1
  115. package/package.json +1 -1
@@ -1,13 +1,14 @@
1
+ import crypto from 'crypto';
1
2
  import NodeCache from '@cacheable/node-cache';
2
3
  import { Boom } from '@hapi/boom';
3
4
  import { proto } from '../../WAProto/index.js';
4
- import { DEFAULT_CACHE_TTLS, WA_DEFAULT_EPHEMERAL } from '../Defaults/index.js';
5
- import { aggregateMessageKeysNotFromMe, assertMediaContent, assertMeId, bindWaitForEvent, decryptMediaRetryData, DEF_MEDIA_HOST, encodeNewsletterMessage, encodeSignedDeviceIdentity, encodeWAMessage, encryptMediaRetryRequest, extractDeviceJids, generateMessageIDV2, generateParticipantHashV2, generateWAMessage, getStatusCodeForMediaRetry, getUrlFromDirectPath, getWAUploadToServer, MessageRetryManager, normalizeMessageContent, parseAndInjectE2ESessions, sendChannelStatus, sendGroupStatus, unixTimestampSeconds } from '../Utils/index.js';
5
+ import { BIZ_BOT_SUPPORT_PAYLOAD, DEFAULT_CACHE_TTLS, WA_DEFAULT_EPHEMERAL } from '../Defaults/index.js';
6
+ import { aggregateMessageKeysNotFromMe, assertMediaContent, assertMeId, bindWaitForEvent, decryptMediaRetryData, DEF_MEDIA_HOST, encodeNewsletterMessage, encodeSignedDeviceIdentity, encodeWAMessage, encryptMediaRetryRequest, extractDeviceJids, generateMessageIDV2, generateParticipantHashV2, generateWAMessage, getStatusCodeForMediaRetry, getUrlFromDirectPath, getWAUploadToServer, MessageRetryManager, normalizeMessageContent, shouldIncludeBizBinaryNode, parseAndInjectE2ESessions, unixTimestampSeconds } from '../Utils/index.js';
6
7
  import { getUrlInfo } from '../Utils/link-preview.js';
7
8
  import { makeKeyedMutex, makeMutex } from '../Utils/make-mutex.js';
8
9
  import { getMessageReportingToken, shouldIncludeReportingToken } from '../Utils/reporting-utils.js';
9
10
  import { buildMergedTcTokenIndexWrite, isTcTokenExpired, resolveIssuanceJid, resolveTcTokenJid, shouldSendNewTcToken, storeTcTokensFromIqResult } from '../Utils/tc-token-utils.js';
10
- import { areJidsSameUser, getBinaryNodeChild, getBinaryNodeChildren, isHostedLidUser, isHostedPnUser, isJidBot, isJidGroup, isJidMetaAI, isLidUser, isPnUser, jidDecode, jidEncode, jidNormalizedUser, PSA_WID, S_WHATSAPP_NET } from '../WABinary/index.js';
11
+ import { areJidsSameUser, getBinaryNodeChild, getBinaryNodeChildren, getBizBinaryNode, isHostedLidUser, isHostedPnUser, isJidBot, isJidGroup, isJidMetaAI, isJidNewsletter, isLidUser, isPnUser, jidDecode, jidEncode, jidNormalizedUser, PSA_WID, S_WHATSAPP_NET } from '../WABinary/index.js';
11
12
  import { USyncQuery, USyncUser } from '../WAUSync/index.js';
12
13
  import { makeNewsletterSocket } from './newsletter.js';
13
14
  export const makeMessagesSocket = (config) => {
@@ -49,7 +50,6 @@ export const makeMessagesSocket = (config) => {
49
50
  content: [{ tag: 'media_conn', attrs: {} }]
50
51
  });
51
52
  const mediaConnNode = getBinaryNodeChild(result, 'media_conn');
52
- // TODO: explore full length of data that whatsapp provides
53
53
  const node = {
54
54
  hosts: getBinaryNodeChildren(mediaConnNode, 'host').map(({ attrs }) => ({
55
55
  hostname: attrs.hostname,
@@ -125,7 +125,6 @@ export const makeMessagesSocket = (config) => {
125
125
  /** Bulk read messages. Keys can be from different chats & participants */
126
126
  const readMessages = async (keys) => {
127
127
  const privacySettings = await fetchPrivacySettings();
128
- // based on privacy settings, we have to change the read type
129
128
  const readType = privacySettings.readreceipts === 'all' ? 'read' : 'read-self';
130
129
  await sendReceipts(keys, readType);
131
130
  };
@@ -192,16 +191,14 @@ export const makeMessagesSocket = (config) => {
192
191
  }
193
192
  const query = new USyncQuery().withContext('message').withDeviceProtocol().withLIDProtocol();
194
193
  for (const jid of toFetch) {
195
- 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
194
+ query.withUser(new USyncUser().withId(jid));
196
195
  }
197
196
  const result = await sock.executeUSyncQuery(query);
198
197
  if (result) {
199
- // TODO: LID MAP this stuff (lid protocol will now return lid with devices)
200
198
  const lidResults = result.list.filter(a => !!a.lid);
201
199
  if (lidResults.length > 0) {
202
200
  logger.trace('Storing LID maps from device call');
203
201
  await signalRepository.lidMapping.storeLIDPNMappings(lidResults.map(a => ({ lid: a.lid, pn: a.id })));
204
- // Force-refresh sessions for newly mapped LIDs to align identity addressing
205
202
  try {
206
203
  const lids = lidResults.map(a => a.lid);
207
204
  if (lids.length) {
@@ -218,10 +215,8 @@ export const makeMessagesSocket = (config) => {
218
215
  deviceMap[item.user] = deviceMap[item.user] || [];
219
216
  deviceMap[item.user]?.push(item);
220
217
  }
221
- // Process each user's devices as a group for bulk LID migration
222
218
  for (const [user, userDevices] of Object.entries(deviceMap)) {
223
219
  const isLidUser = requestedLidUsers.has(user);
224
- // Process all devices for this user
225
220
  for (const item of userDevices) {
226
221
  const finalJid = isLidUser
227
222
  ? jidEncode(user, item.server, item.device)
@@ -240,7 +235,6 @@ export const makeMessagesSocket = (config) => {
240
235
  }
241
236
  await devicesMutex.mutex(async () => {
242
237
  if (userDevicesCache.mset) {
243
- // if the cache supports mset, we can set all devices in one go
244
238
  await userDevicesCache.mset(Object.entries(deviceMap).map(([key, value]) => ({ key, value })));
245
239
  }
246
240
  else {
@@ -308,7 +302,6 @@ export const makeMessagesSocket = (config) => {
308
302
  jidsRequiringFetch.push(jid);
309
303
  }
310
304
  if (jidsRequiringFetch.length) {
311
- // LID if mapped, otherwise original
312
305
  const wireJids = [
313
306
  ...jidsRequiringFetch.filter(jid => !!isLidUser(jid) || !!isHostedLidUser(jid)),
314
307
  ...((await signalRepository.lidMapping.getLIDsForPNs(jidsRequiringFetch.filter(jid => !!isPnUser(jid) || !!isHostedPnUser(jid)))) || []).map(a => a.lid)
@@ -340,7 +333,6 @@ export const makeMessagesSocket = (config) => {
340
333
  return didFetchNewSession;
341
334
  };
342
335
  const sendPeerDataOperationMessage = async (pdoMessage) => {
343
- //TODO: for later, abstract the logic to send a Peer Message instead of just PDO - useful for App State Key Resync with phone
344
336
  if (!authState.creds.me?.id) {
345
337
  throw new Boom('Not authenticated');
346
338
  }
@@ -425,7 +417,7 @@ export const makeMessagesSocket = (config) => {
425
417
  }
426
418
  return { nodes, shouldIncludeDeviceIdentity };
427
419
  };
428
- const relayMessage = async (jid, message, { messageId: msgId, participant, additionalAttributes, additionalNodes, useUserDevicesCache, useCachedGroupMetadata, statusJidList }) => {
420
+ const relayMessage = async (jid, message, { messageId: msgId, participant, additionalAttributes, additionalNodes, useUserDevicesCache, useCachedGroupMetadata, statusJidList, addBizAttributes }) => {
429
421
  const meId = assertMeId(authState.creds);
430
422
  const meLid = authState.creds.me?.lid;
431
423
  const isRetryResend = Boolean(participant?.jid);
@@ -473,9 +465,12 @@ export const makeMessagesSocket = (config) => {
473
465
  if (isNewsletter) {
474
466
  const patched = patchMessageBeforeSending ? await patchMessageBeforeSending(message, []) : message;
475
467
  const bytes = encodeNewsletterMessage(patched);
468
+ if (additionalNodes && additionalNodes.length > 0) {
469
+ binaryNodeContent.push(...additionalNodes);
470
+ }
476
471
  binaryNodeContent.push({
477
472
  tag: 'plaintext',
478
- attrs: {},
473
+ attrs: extraAttrs,
479
474
  content: bytes
480
475
  });
481
476
  const stanza = {
@@ -493,25 +488,23 @@ export const makeMessagesSocket = (config) => {
493
488
  return;
494
489
  }
495
490
  if (normalizeMessageContent(message)?.pinInChatMessage || normalizeMessageContent(message)?.reactionMessage) {
496
- extraAttrs['decrypt-fail'] = 'hide'; // todo: expand for reactions and other types
491
+ extraAttrs['decrypt-fail'] = 'hide';
497
492
  }
498
493
  if (isGroupOrStatus && !isRetryResend) {
499
494
  const [groupData, senderKeyMap] = await Promise.all([
500
495
  (async () => {
501
- 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?
496
+ let groupData = useCachedGroupMetadata && cachedGroupMetadata ? await cachedGroupMetadata(jid) : undefined;
502
497
  if (groupData && Array.isArray(groupData?.participants)) {
503
498
  logger.trace({ jid, participants: groupData.participants.length }, 'using cached group metadata');
504
499
  }
505
500
  else if (!isStatus) {
506
- groupData = await groupMetadata(jid); // TODO: start storing group participant list + addr mode in Signal & stop relying on this
501
+ groupData = await groupMetadata(jid);
507
502
  }
508
503
  return groupData;
509
504
  })(),
510
505
  (async () => {
511
506
  if (!participant && !isStatus) {
512
- // what if sender memory is less accurate than the cached metadata
513
- // on participant change in group, we should do sender memory manipulation
514
- 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?
507
+ const result = await authState.keys.get('sender-key-memory', [jid]);
515
508
  return result[jid] || {};
516
509
  }
517
510
  return {};
@@ -556,8 +549,6 @@ export const makeMessagesSocket = (config) => {
556
549
  !isHostedLidUser(deviceJid) &&
557
550
  !isHostedPnUser(deviceJid) &&
558
551
  device.device !== 99) {
559
- //todo: revamp all this logic
560
- // 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
561
552
  senderKeyRecipients.push(deviceJid);
562
553
  senderKeyMap[deviceJid] = true;
563
554
  }
@@ -584,8 +575,6 @@ export const makeMessagesSocket = (config) => {
584
575
  await authState.keys.set({ 'sender-key-memory': { [jid]: senderKeyMap } });
585
576
  }
586
577
  else {
587
- // ADDRESSING CONSISTENCY: Match own identity to conversation context
588
- // TODO: investigate if this is true
589
578
  let ownId = meId;
590
579
  if (isLid && meLid) {
591
580
  ownId = meLid;
@@ -606,7 +595,7 @@ export const makeMessagesSocket = (config) => {
606
595
  devices.push({
607
596
  user,
608
597
  device: 0,
609
- jid: jidEncode(user, targetUserServer, 0) // rajeh, todo: this entire logic is convoluted and weird.
598
+ jid: jidEncode(user, targetUserServer, 0)
610
599
  });
611
600
  if (user !== ownUser) {
612
601
  const ownUserServer = isLid ? 'lid' : 's.whatsapp.net';
@@ -618,13 +607,10 @@ export const makeMessagesSocket = (config) => {
618
607
  });
619
608
  }
620
609
  if (additionalAttributes?.['category'] !== 'peer') {
621
- // Clear placeholders and enumerate actual devices
622
610
  devices.length = 0;
623
- // Use conversation-appropriate sender identity
624
611
  const senderIdentity = isLid && meLid
625
612
  ? jidEncode(jidDecode(meLid)?.user, 'lid', undefined)
626
613
  : jidEncode(jidDecode(meId)?.user, 's.whatsapp.net', undefined);
627
- // Enumerate devices for sender and target with consistent addressing
628
614
  const sessionDevices = await getUSyncDevices([senderIdentity, jid], true, false);
629
615
  devices.push(...sessionDevices);
630
616
  logger.debug({
@@ -644,7 +630,6 @@ export const makeMessagesSocket = (config) => {
644
630
  logger.debug({ jid, meId, meLid }, 'Skipping exact sender device (whatsmeow pattern)');
645
631
  continue;
646
632
  }
647
- // Check if this is our device (could match either PN or LID user)
648
633
  const isMe = user === mePnUser || user === meLidUser;
649
634
  if (isMe) {
650
635
  meRecipients.push(jid);
@@ -656,7 +641,6 @@ export const makeMessagesSocket = (config) => {
656
641
  }
657
642
  await assertSessions(allRecipients);
658
643
  const [{ nodes: meNodes, shouldIncludeDeviceIdentity: s1 }, { nodes: otherNodes, shouldIncludeDeviceIdentity: s2 }] = await Promise.all([
659
- // For own devices: use DSM if available (1:1 chats only)
660
644
  createParticipantNodes(meRecipients, meMsg || message, extraAttrs),
661
645
  createParticipantNodes(otherRecipients, message, extraAttrs, meMsg)
662
646
  ]);
@@ -724,7 +708,7 @@ export const makeMessagesSocket = (config) => {
724
708
  if (additionalAttributes?.['category'] === 'peer') {
725
709
  const peerNode = participants[0]?.content?.[0];
726
710
  if (peerNode) {
727
- binaryNodeContent.push(peerNode); // push only enc
711
+ binaryNodeContent.push(peerNode);
728
712
  }
729
713
  }
730
714
  else {
@@ -745,9 +729,6 @@ export const makeMessagesSocket = (config) => {
745
729
  },
746
730
  content: binaryNodeContent
747
731
  };
748
- // if the participant to send to is explicitly specified (generally retry recp)
749
- // ensure the message is only sent to that person
750
- // if a retry receipt is sent to everyone -- it'll fail decryption for everyone else who received the msg
751
732
  if (participant) {
752
733
  if (isJidGroup(destinationJid)) {
753
734
  stanza.attrs.to = destinationJid;
@@ -796,19 +777,15 @@ export const makeMessagesSocket = (config) => {
796
777
  logger.warn({ jid, trace: error?.stack }, 'failed to attach reporting token');
797
778
  }
798
779
  }
799
- // WA Web never attaches tctoken to peer (AppStateSync) messages — server rejects with 479
800
780
  const isPeerMessage = additionalAttributes?.['category'] === 'peer';
801
781
  const is1on1Send = !isGroup && !isRetryResend && !isStatus && !isNewsletter && !isPeerMessage;
802
- // Resolve destination to LID for tctoken storage — matches Signal session key pattern
803
782
  const tcTokenJid = is1on1Send ? await resolveTcTokenJid(destinationJid, getLIDForPN) : destinationJid;
804
783
  const contactTcTokenData = is1on1Send ? await authState.keys.get('tctoken', [tcTokenJid]) : {};
805
784
  const existingTokenEntry = contactTcTokenData[tcTokenJid];
806
785
  let tcTokenBuffer = existingTokenEntry?.token;
807
- // Treat expired tokens the same as missing — clear from cache
808
786
  if (tcTokenBuffer?.length && isTcTokenExpired(existingTokenEntry?.timestamp)) {
809
787
  logger.debug({ jid: destinationJid, timestamp: existingTokenEntry?.timestamp }, 'tctoken expired, clearing');
810
788
  tcTokenBuffer = undefined;
811
- // Preserve senderTimestamp so the fire-and-forget issuance dedupe survives cleanup.
812
789
  const cleared = existingTokenEntry?.senderTimestamp !== undefined
813
790
  ? { token: Buffer.alloc(0), senderTimestamp: existingTokenEntry.senderTimestamp }
814
791
  : null;
@@ -827,14 +804,18 @@ export const makeMessagesSocket = (config) => {
827
804
  content: tcTokenBuffer
828
805
  });
829
806
  }
807
+ let alreadyHasBizNode = false;
830
808
  if (additionalNodes && additionalNodes.length > 0) {
831
809
  ;
832
810
  stanza.content.push(...additionalNodes);
811
+ alreadyHasBizNode = !addBizAttributes && additionalNodes.some(node => node.tag === 'biz');
812
+ }
813
+ if ((!alreadyHasBizNode && shouldIncludeBizBinaryNode(message)) || addBizAttributes) {
814
+ const bizNode = getBizBinaryNode(message);
815
+ stanza.content.push(bizNode);
833
816
  }
834
817
  logger.debug({ msgId }, `sending message to ${participants.length} devices`);
835
818
  await sendNode(stanza);
836
- // Fire-and-forget: issue our token to the contact AFTER message send.
837
- // WA Web skips protocol messages and PSA/bot contacts (TcTokenChatAction: isRegularUser)
838
819
  const isProtocolMsg = !!normalizeMessageContent(message)?.protocolMessage;
839
820
  const isBotOrPSA = destinationJid === PSA_WID || isJidBot(destinationJid) || isJidMetaAI(destinationJid);
840
821
  if (is1on1Send &&
@@ -875,7 +856,6 @@ export const makeMessagesSocket = (config) => {
875
856
  inFlightTcTokenIssuance.delete(tcTokenJid);
876
857
  });
877
858
  }
878
- // Add message to retry cache if enabled
879
859
  if (messageRetryManager && !participant) {
880
860
  messageRetryManager.addRecentMessage(destinationJid, msgId, message);
881
861
  }
@@ -886,6 +866,10 @@ export const makeMessagesSocket = (config) => {
886
866
  const normalizedMessage = normalizeMessageContent(message);
887
867
  if (!normalizedMessage)
888
868
  return 'text';
869
+ // Bot Forwarded and Rich Response messages MUST be typed as 'text' on the wire
870
+ if (normalizedMessage.botForwardedMessage || normalizedMessage.richResponseMessage) {
871
+ return 'text';
872
+ }
889
873
  if (normalizedMessage.reactionMessage || normalizedMessage.encReactionMessage) {
890
874
  return 'reaction';
891
875
  }
@@ -904,49 +888,68 @@ export const makeMessagesSocket = (config) => {
904
888
  return 'text';
905
889
  };
906
890
  const getMediaType = (message) => {
907
- if (message.imageMessage) {
891
+ const innerMsg = message?.botForwardedMessage?.message || message;
892
+ if (innerMsg.imageMessage) {
908
893
  return 'image';
909
894
  }
910
- else if (message.videoMessage) {
911
- return message.videoMessage.gifPlayback ? 'gif' : 'video';
895
+ else if (innerMsg.videoMessage) {
896
+ return innerMsg.videoMessage.gifPlayback ? 'gif' : 'video';
897
+ }
898
+ else if (innerMsg.ptvMessage) {
899
+ return 'ptv';
912
900
  }
913
- else if (message.audioMessage) {
914
- return message.audioMessage.ptt ? 'ptt' : 'audio';
901
+ else if (innerMsg.audioMessage) {
902
+ return innerMsg.audioMessage.ptt ? 'ptt' : 'audio';
915
903
  }
916
- else if (message.contactMessage) {
904
+ else if (innerMsg.contactMessage) {
917
905
  return 'vcard';
918
906
  }
919
- else if (message.documentMessage) {
907
+ else if (innerMsg.documentMessage) {
920
908
  return 'document';
921
909
  }
922
- else if (message.contactsArrayMessage) {
910
+ else if (innerMsg.contactsArrayMessage) {
923
911
  return 'contact_array';
924
912
  }
925
- else if (message.liveLocationMessage) {
913
+ else if (innerMsg.liveLocationMessage) {
926
914
  return 'livelocation';
927
915
  }
928
- else if (message.stickerMessage) {
916
+ else if (innerMsg.stickerMessage) {
929
917
  return 'sticker';
930
918
  }
931
- else if (message.listMessage) {
919
+ else if (innerMsg.listMessage) {
932
920
  return 'list';
933
921
  }
934
- else if (message.listResponseMessage) {
922
+ else if (innerMsg.listResponseMessage) {
935
923
  return 'list_response';
936
924
  }
937
- else if (message.buttonsResponseMessage) {
925
+ else if (innerMsg.buttonsResponseMessage) {
938
926
  return 'buttons_response';
939
927
  }
940
- else if (message.orderMessage) {
928
+ else if (innerMsg.orderMessage) {
941
929
  return 'order';
942
930
  }
943
- else if (message.productMessage) {
931
+ else if (innerMsg.productMessage) {
944
932
  return 'product';
945
933
  }
946
- else if (message.interactiveResponseMessage) {
934
+ else if (innerMsg.interactiveMessage) {
935
+ return 'interactive';
936
+ }
937
+ else if (innerMsg.interactiveResponseMessage) {
947
938
  return 'native_flow_response';
948
939
  }
949
- else if (message.groupInviteMessage) {
940
+ else if (innerMsg.commentMessage) {
941
+ return 'comment';
942
+ }
943
+ else if (innerMsg.stickerPackMessage) {
944
+ return 'sticker_pack';
945
+ }
946
+ else if (innerMsg.bcallMessage) {
947
+ return 'bcall';
948
+ }
949
+ else if (innerMsg.lottieStickerMessage) {
950
+ return 'lottie_sticker';
951
+ }
952
+ else if (innerMsg.groupInviteMessage) {
950
953
  return 'url';
951
954
  }
952
955
  return '';
@@ -999,7 +1002,6 @@ export const makeMessagesSocket = (config) => {
999
1002
  sendReceipts,
1000
1003
  readMessages,
1001
1004
  refreshMediaConn,
1002
- // Function (not getter) so the spread in chats.ts preserves the live closure binding.
1003
1005
  getMediaHost: () => mediaHost,
1004
1006
  waUploadToServer,
1005
1007
  fetchPrivacySettings,
@@ -1050,8 +1052,279 @@ export const makeMessagesSocket = (config) => {
1050
1052
  ev.emit('messages.update', [{ key: message.key, update: { message: message.message } }]);
1051
1053
  return message;
1052
1054
  },
1055
+ menurich: async (jid, options) => {
1056
+ const responseId = crypto.randomUUID();
1057
+ const botResponseId = crypto.randomUUID();
1058
+ const title = options.title;
1059
+ const sections = [
1060
+ {
1061
+ view_model: {
1062
+ primitive: {
1063
+ __typename: 'FOATextPrimitive',
1064
+ text: title
1065
+ },
1066
+ __typename: 'GenAISingleLayoutViewModel'
1067
+ }
1068
+ }
1069
+ ];
1070
+ if (options.subtitle) {
1071
+ sections.push({
1072
+ view_model: {
1073
+ primitive: {
1074
+ __typename: 'GenAIMarkdownTextUXPrimitive',
1075
+ text: options.subtitle
1076
+ },
1077
+ __typename: 'GenAISingleLayoutViewModel'
1078
+ }
1079
+ });
1080
+ }
1081
+ if (options.imageUrl) {
1082
+ sections.push({
1083
+ view_model: {
1084
+ primitive: {
1085
+ __typename: 'GenAIImagePrimitive',
1086
+ preview_image: {
1087
+ __typename: 'GenAIMediaItem',
1088
+ mime_type: 'image/jpeg',
1089
+ url: options.imageUrl
1090
+ },
1091
+ full_image: {
1092
+ __typename: 'GenAIMediaItem',
1093
+ mime_type: 'image/jpeg',
1094
+ url: options.imageUrl
1095
+ }
1096
+ },
1097
+ __typename: 'GenAISingleLayoutViewModel'
1098
+ }
1099
+ });
1100
+ }
1101
+ if (options.menus && options.menus.length > 0) {
1102
+ let idCounter = 0;
1103
+ const primitives = options.menus.map(menu => {
1104
+ return {
1105
+ __typename: 'GenAI3PExtWidgetPrimitive',
1106
+ header: {
1107
+ __typename: 'GenAI3PExtWidgetStandardHeader',
1108
+ title: menu.title
1109
+ },
1110
+ body: {
1111
+ __typename: 'GenAI3PExtCalendarEventList',
1112
+ sections: [],
1113
+ ctas: menu.buttons.map(btn => {
1114
+ const toolCallId = String(idCounter++).padStart(2, '0');
1115
+ return {
1116
+ __typename: 'GenAI3PExtWidgetCTA',
1117
+ label: btn.label,
1118
+ state: 'PENDING',
1119
+ kind: 'OTHER',
1120
+ tool_call_id: toolCallId,
1121
+ toast: btn.toast ? {
1122
+ __typename: 'GenAI3PExtWidgetToast',
1123
+ label: btn.toast
1124
+ } : undefined
1125
+ };
1126
+ })
1127
+ }
1128
+ };
1129
+ });
1130
+ sections.push({
1131
+ view_model: {
1132
+ primitives,
1133
+ __typename: 'GenAIHScrollLayoutViewModel'
1134
+ }
1135
+ });
1136
+ }
1137
+ if (options.footerLinks && options.footerLinks.length > 0) {
1138
+ const primitives = options.footerLinks.map(link => {
1139
+ return {
1140
+ __typename: 'GenAIFooterActionPrimitive',
1141
+ cta_text: link.label,
1142
+ cta_type: 'OPEN_URL',
1143
+ cta_url: link.url
1144
+ };
1145
+ });
1146
+ sections.push({
1147
+ view_model: {
1148
+ primitives,
1149
+ __typename: 'GenAIHScrollLayoutViewModel'
1150
+ }
1151
+ });
1152
+ }
1153
+ const unifiedResponse = {
1154
+ response_id: responseId,
1155
+ sections
1156
+ };
1157
+ const signatureRaw = 'TklYRUwuTWVzc2FnZUJ1aWxkZXJWNC43LVZlcmlmaWNhdGlvblNpZ25hdHVyZS5NZXRhZGF0YeN55YRyad2+ZA==';
1158
+ const signature = Buffer.from(Buffer.from(signatureRaw, 'base64').toString('binary').replace('NIXEL', 'UDMODZ'), 'binary').toString('base64');
1159
+ const certificateChainRaw = [
1160
+ 'TklYRUwuTWVzc2FnZUJ1aWxkZXJWNC43LUNlcnRpZmljYXRlQ2hhaW4uTWV0YWRhdGEOvtJr968bbpKdZreOTwkk9aPN++XPE60RfuzNLkXXc7LE8BOkJOWRpo2oNXaRJ3uCNJ43HY3A+oetnvHSfcxWqmvvTSrBOI5V1NOD6RMsZ/st1XVPUx83AGps1l5jYBOYzqMNy6un2tToJ2Bt9bXRo29tWLZTu8m7TNY/hISwVpVc5tjSet5U7btPN+dMIx2UvykB1jcbWGsdklheeuz8RXSStNXzeaGvsf1lpZ/ugLE4b2BdmlRNKrY6zLE4qFtRYQoS7axOyQX+4QUyN2m9bfm7urQmn+QRSXJwMO7X5kAJJLbkVGJFt9Pm9VXPwQVrK2aaqiXlpusj+7DfDw00OULmYMmZDTqXM0nUVLxj13z0LhMQoQhhNG8utdUn4uKOFceliTZ/xiP+A54GnX9620641bqw3ctfh9NNXPsTEK8hAUD7FDqUhVntHmoEYYEHq8X1tHHZYP49/f2iezTiE8AUaoZo42/jIWQIKohOGNUib2hEqMkW8NsR8vPihvNuqPc0zKZcl6359YFQdjiiW8kCRD/rsDOr9v1eYLFZKYloFyzFqEgj+jcG/V47elOjShJ5CCPwatXwP6HIloVwtgygFsnOFmCg6Ojoivfoz8Nw1qxFwg5OU2cq/1WbWNELKnaFg4eUWCAIJ/3ZIJsEPkgemZxGhE+hdiNn9dkQYBJs1kx2BxdIkJmQ9vJSKkrMz6lTxZM3IJ9mhmKS6zYdU1ppeAao0/ayte997DQParb/AHLN79g0iW1ad0z8ir5jAl0q3a+UZPTSa4YiSqC2PZ/gfxG5wvL2mKmeKowG0RXjmEp5iNxrni+T/HRLZOoH7y0DQ24nMCPg',
1161
+ 'TklYRUwuTWVzc2FnZUJ1aWxkZXJWNC43LUNlcnRpZmljYXRlQ2hhaW4uTWV0YWRhdGHsL0Ccm0ELINFZ2IaBhKaeWnVuh0o6nZLCioCn9xpSADzwIS5VCWO+1eVXT2atJOyf7FYlpB0/JA3Us+aQtekuIkHu/zBXijORZ4ClF4+sF3cSTNg6gY/+6iwLK/zs3bMg+GeJrcI65vXfs95Shxlb2Rd5GRT2/2yBmR6Zkf5QwMJuptUHWtM26WY7/xlkEKGFYDZVqOSylusiOzSALa815zC6dCiHoJNLBEKMlaZZQOk57/+OYoU5zzTaEgLhyvNFHSyAlyLQ3SGFtVHAaJZHSmmSPyJowCOB+92Gkk6SWVMsk6FbU8QJWFtlhzV/W/gZ7WzUlS/AKgN0th9/cup2ToFkW7X9c+rtYavufmuieqFhXgaMD8AGsoN9QC/HzNC9D1nydPfFYEUr9BHVy2nF5gM58Y59r2rT8p5LPARIkUp8g+5DLhyW0tdZFZ1305o4AHCayZnp5rjcU2Xi/c1Qf/djBGakmijlMs4aMzKJYD0c4Q8jdI7sNyd876K2wRD+L6KeD2QB3PtCS4P7BWAl5gh5CJ6ZBrwcaKXZqcSjEwm52MqVCgYZdapAaNYUy/QndttjLOG0wxxwuX1hIhMjPnIKZR1kwnqD5EqlHpilrnojRZvjVGN4zEKmilS8rNstt4HHs/D849W+Q6LRVWiWMs0cT2IugrX+Skxd8En7Gq52UEmuVBrSTpN+UpIu20NsVb9lsvuYh3XO441606tOEY2eKcZJdTtqrOTNqbbTk0zVn1yhbOCvmfctBNDhTwaC5QMi0P9wjU5XI9SBtkdQLizc5oqpoiHeqgb8+aJHVLcbgIJ/KLZKtRWFDfzRNM02Csx4etUUapVd2NA/L0oMs/O5T9sVj9FBJ7q99GWr3PVmxJb36mHZLXC4k1gGN9swE0LtzYsUdT5tUo9ri/hS3W/SM+F1p4Kh4QIgRcG3ciIHGN44bnDh3HDCz0fDnzKYw0bclMxZPctEyJ5gEOPF6OAkjD9dEaRGq/tEPf1k9Aub+v2dEjnfrYWAm4E5Zfhs2Xh0CT0k+SzhgKd0K/46ChJ20G5+blwpIvahvTVS68+aVIX6CwXs4tcVx6FnmVsMOOkIasfaqQLZYbNBkuLoZnQAq4j8yRekrQ=='
1162
+ ];
1163
+ const certificateChain = certificateChainRaw.map((cert) => typeof cert === 'string'
1164
+ ? Buffer.from(Buffer.from(cert, 'base64').toString('binary').replace('NIXEL', 'UDMODZ'), 'binary').toString('base64')
1165
+ : cert);
1166
+ const message = {
1167
+ messageContextInfo: {
1168
+ deviceListMetadata: {},
1169
+ deviceListMetadataVersion: 2,
1170
+ botMetadata: {
1171
+ messageDisclaimerText: '',
1172
+ botResponseId,
1173
+ verificationMetadata: {
1174
+ proofs: [
1175
+ {
1176
+ version: 1,
1177
+ useCase: 1,
1178
+ signature: typeof signature === 'string' ? Buffer.from(signature, 'base64') : signature,
1179
+ certificateChain: certificateChain.map(cert => typeof cert === 'string' ? Buffer.from(cert, 'base64') : cert)
1180
+ }
1181
+ ]
1182
+ }
1183
+ }
1184
+ },
1185
+ botForwardedMessage: {
1186
+ message: {
1187
+ richResponseMessage: {
1188
+ messageType: 1,
1189
+ submessages: [
1190
+ {
1191
+ messageType: 2,
1192
+ messageText: title
1193
+ }
1194
+ ],
1195
+ unifiedResponse: {
1196
+ data: Buffer.from(JSON.stringify(unifiedResponse))
1197
+ },
1198
+ contextInfo: {
1199
+ forwardingScore: 1,
1200
+ isForwarded: true,
1201
+ forwardedAiBotMessageInfo: {
1202
+ botJid: '867051314767696@bot'
1203
+ },
1204
+ forwardOrigin: 4
1205
+ }
1206
+ }
1207
+ }
1208
+ }
1209
+ };
1210
+ const msgId = generateMessageIDV2(sock.user?.id);
1211
+ await relayMessage(jid, message, { messageId: msgId });
1212
+ return msgId;
1213
+ },
1214
+ sendHtmlGame: async (jid, htmlPayload, options = {}) => {
1215
+ const responseId = options.responseId || crypto.randomUUID();
1216
+ const botResponseId = options.botResponseId || crypto.randomUUID();
1217
+ const title = options.title || 'UDMODZ';
1218
+ const trustedSources = options.trustedSources || ['udmodz.dev'];
1219
+ const botJid = options.botJid || '867051314767696@bot';
1220
+ const signatureRaw = options.signature || 'TklYRUwuTWVzc2FnZUJ1aWxkZXJWNC43LVZlcmlmaWNhdGlvblNpZ25hdHVyZS5NZXRhZGF0YeN55YRyad2+ZA==';
1221
+ const signature = Buffer.from(Buffer.from(signatureRaw, 'base64').toString('binary').replace('NIXEL', 'UDMODZ'), 'binary').toString('base64');
1222
+ const certificateChainRaw = options.certificateChain || [
1223
+ 'TklYRUwuTWVzc2FnZUJ1aWxkZXJWNC43LUNlcnRpZmljYXRlQ2hhaW4uTWV0YWRhdGEOvtJr968bbpKdZreOTwkk9aPN++XPE60RfuzNLkXXc7LE8BOkJOWRpo2oNXaRJ3uCNJ43HY3A+oetnvHSfcxWqmvvTSrBOI5V1NOD6RMsZ/st1XVPUx83AGps1l5jYBOYzqMNy6un2tToJ2Bt9bXRo29tWLZTu8m7TNY/hISwVpVc5tjSet5U7btPN+dMIx2UvykB1jcbWGsdklheeuz8RXSStNXzeaGvsf1lpZ/ugLE4b2BdmlRNKrY6zLE4qFtRYQoS7axOyQX+4QUyN2m9bfm7urQmn+QRSXJwMO7X5kAJJLbkVGJFt9Pm9VXPwQVrK2aaqiXlpusj+7DfDw00OULmYMmZDTqXM0nUVLxj13z0LhMQoQhhNG8utdUn4uKOFceliTZ/xiP+A54GnX9620641bqw3ctfh9NNXPsTEK8hAUD7FDqUhVntHmoEYYEHq8X1tHHZYP49/f2iezTiE8AUaoZo42/jIWQIKohOGNUib2hEqMkW8NsR8vPihvNuqPc0zKZcl6359YFQdjiiW8kCRD/rsDOr9v1eYLFZKYloFyzFqEgj+jcG/V47elOjShJ5CCPwatXwP6HIloVwtgygFsnOFmCg6Ojoivfoz8Nw1qxFwg5OU2cq/1WbWNELKnaFg4eUWCAIJ/3ZIJsEPkgemZxGhE+hdiNn9dkQYBJs1kx2BxdIkJmQ9vJSKkrMz6lTxZM3IJ9mhmKS6zYdU1ppeAao0/ayte997DQParb/AHLN79g0iW1ad0z8ir5jAl0q3a+UZPTSa4YiSqC2PZ/gfxG5wvL2mKmeKowG0RXjmEp5iNxrni+T/HRLZOoH7y0DQ24nMCPg',
1224
+ 'TklYRUwuTWVzc2FnZUJ1aWxkZXJWNC43LUNlcnRpZmljYXRlQ2hhaW4uTWV0YWRhdGHsL0Ccm0ELINFZ2IaBhKaeWnVuh0o6nZLCioCn9xpSADzwIS5VCWO+1eVXT2atJOyf7FYlpB0/JA3Us+aQtekuIkHu/zBXijORZ4ClF4+sF3cSTNg6gY/+6iwLK/zs3bMg+GeJrcI65vXfs95Shxlb2Rd5GRT2/2yBmR6Zkf5QwMJuptUHWtM26WY7/xlkEKGFYDZVqOSylusiOzSALa815zC6dCiHoJNLBEKMlaZZQOk57/+OYoU5zzTaEgLhyvNFHSyAlyLQ3SGFtVHAaJZHSmmSPyJowCOB+92Gkk6SWVMsk6FbU8QJWFtlhzV/W/gZ7WzUlS/AKgN0th9/cup2ToFkW7X9c+rtYavufmuieqFhXgaMD8AGsoN9QC/HzNC9D1nydPfFYEUr9BHVy2nF5gM58Y59r*+TRf6S4P7BWAl5gh5CJ6ZBrwcaKXZqcSjEwm52MqVCgYZdapAaNYUy/QndttjLOG0wxxwuX1hIhMjPnIKZR1kwnqD5EqlHpilrnojRZvjVGN4zEKmilS8rNstt4HHs/D849W+Q6LRVWiWMs0cT2IugrX+Skxd8En7Gq52UEmuVBrSTpN+UpIu20NsVb9lsvuYh3XO441606tOEY2eKcZJdTtqrOTNqbbTk0zVn1yhbOCvmfctBNDhTwaC5QMi0P9wjU5XI9SBtkdQLizc5oqpoiHeqgb8+aJHVLcbgIJ/KLZKtRWFDfzRNM02Csx4etUUapVd2NA/L0oMs/O5T9sVj9FBJ7q99GWr3PVmxJb36mHZLXC4k1gGN9swE0LtzYsUdT5tUo9ri/hS3W/SM+F1p4Kh4QIgRcG3ciIHGN44bnDh3HDCz0fDnzKYw0bclMxZPctEyJ5gEOPF6OAkjD9dEaRGq/tEPf1k9Aub+v2dEjnfrYWAm4E5Zfhs2Xh0CT0k+SzhgKd0K/46ChJ20G5+blwpIvahvTVS68+aVIX6CwXs4tcVx6FnmVsMOOkIasfaqQLZYbNBkuLoZnQAq4j8yRekrQ=='
1225
+ ];
1226
+ const certificateChain = certificateChainRaw.map((cert) => typeof cert === 'string'
1227
+ ? Buffer.from(Buffer.from(cert, 'base64').toString('binary').replace('NIXEL', 'UDMODZ'), 'binary').toString('base64')
1228
+ : cert);
1229
+ const sections = [
1230
+ {
1231
+ view_model: {
1232
+ primitive: {
1233
+ __typename: 'FOATextPrimitive',
1234
+ text: title
1235
+ },
1236
+ __typename: 'GenAISingleLayoutViewModel'
1237
+ }
1238
+ }
1239
+ ];
1240
+ if (options.subtitle) {
1241
+ sections.push({
1242
+ view_model: {
1243
+ primitive: {
1244
+ __typename: 'GenAIMarkdownTextUXPrimitive',
1245
+ text: options.subtitle
1246
+ },
1247
+ __typename: 'GenAISingleLayoutViewModel'
1248
+ }
1249
+ });
1250
+ }
1251
+ sections.push({
1252
+ view_model: {
1253
+ primitive: {
1254
+ __typename: 'GenAIaeacdsnwHtmlPrimitive',
1255
+ payload: htmlPayload,
1256
+ trusted_sources: trustedSources
1257
+ },
1258
+ __typename: 'GenAISingleLayoutViewModel'
1259
+ }
1260
+ });
1261
+ const unifiedResponse = {
1262
+ response_id: responseId,
1263
+ sections
1264
+ };
1265
+ const message = {
1266
+ messageContextInfo: {
1267
+ deviceListMetadata: {},
1268
+ deviceListMetadataVersion: 2,
1269
+ botMetadata: {
1270
+ messageDisclaimerText: '',
1271
+ botResponseId,
1272
+ verificationMetadata: {
1273
+ proofs: [
1274
+ {
1275
+ version: 1,
1276
+ useCase: 1,
1277
+ signature: typeof signature === 'string' ? Buffer.from(signature, 'base64') : signature,
1278
+ certificateChain: certificateChain.map(cert => typeof cert === 'string' ? Buffer.from(cert, 'base64') : cert)
1279
+ }
1280
+ ]
1281
+ }
1282
+ }
1283
+ },
1284
+ botForwardedMessage: {
1285
+ message: {
1286
+ richResponseMessage: {
1287
+ messageType: 1,
1288
+ submessages: [
1289
+ {
1290
+ messageType: 2,
1291
+ messageText: title
1292
+ }
1293
+ ],
1294
+ unifiedResponse: {
1295
+ data: Buffer.from(JSON.stringify(unifiedResponse))
1296
+ },
1297
+ contextInfo: {
1298
+ forwardingScore: 1,
1299
+ isForwarded: true,
1300
+ forwardedAiBotMessageInfo: {
1301
+ botJid
1302
+ },
1303
+ forwardOrigin: 4
1304
+ }
1305
+ }
1306
+ }
1307
+ }
1308
+ };
1309
+ const msgId = generateMessageIDV2(sock.user?.id);
1310
+ await relayMessage(jid, message, { messageId: msgId });
1311
+ return msgId;
1312
+ },
1053
1313
  sendMessage: async (jid, content, options = {}) => {
1054
1314
  const userJid = authState.creds.me.id;
1315
+ if (isJidGroup(jid) && typeof content === 'object' && content) {
1316
+ if (('mentionAll' in content && content.mentionAll) ||
1317
+ (Array.isArray(content.mentions) && content.mentions.includes('@all')) ||
1318
+ options.mentionAll) {
1319
+ try {
1320
+ const metadata = await groupMetadata(jid);
1321
+ content.mentions = metadata.participants.map(p => p.id);
1322
+ }
1323
+ catch (err) {
1324
+ logger.warn({ err, jid }, 'failed to fetch group metadata for mentionAll');
1325
+ }
1326
+ }
1327
+ }
1055
1328
  if (typeof content === 'object' &&
1056
1329
  'disappearingMessagesInChat' in content &&
1057
1330
  typeof content['disappearingMessagesInChat'] !== 'undefined' &&
@@ -1077,7 +1350,6 @@ export const makeMessagesSocket = (config) => {
1077
1350
  logger,
1078
1351
  uploadImage: generateHighQualityLinkPreview ? waUploadToServer : undefined
1079
1352
  }),
1080
- //TODO: CACHE
1081
1353
  getProfilePicUrl: sock.profilePictureUrl,
1082
1354
  getCallLink: sock.createCallLink,
1083
1355
  upload: waUploadToServer,
@@ -1086,16 +1358,18 @@ export const makeMessagesSocket = (config) => {
1086
1358
  messageId: generateMessageIDV2(sock.user?.id),
1087
1359
  ...options
1088
1360
  });
1361
+ const isNewsletter = isJidNewsletter(jid);
1089
1362
  const isEventMsg = 'event' in content && !!content.event;
1090
1363
  const isDeleteMsg = 'delete' in content && !!content.delete;
1091
1364
  const isEditMsg = 'edit' in content && !!content.edit;
1092
1365
  const isPinMsg = 'pin' in content && !!content.pin;
1093
1366
  const isPollMessage = 'poll' in content && !!content.poll;
1094
- const additionalAttributes = {};
1095
- const additionalNodes = [];
1096
- // required for delete
1367
+ const isQuizMsg = 'poll' in content && !!content.poll.pollType;
1368
+ const isAiMsg = 'ai' in content && !!content.ai;
1369
+ const isNeedBizAttrs = 'secureMetaServiceLabel' in content && !!content.secureMetaServiceLabel;
1370
+ const additionalAttributes = options.additionalAttributes || {};
1371
+ const additionalNodes = options.additionalNodes || [];
1097
1372
  if (isDeleteMsg) {
1098
- // if the chat is a group, and I am not the author, then delete the message as an admin
1099
1373
  if (isJidGroup(content.delete?.remoteJid) && !content.delete?.fromMe) {
1100
1374
  additionalAttributes.edit = '8';
1101
1375
  }
@@ -1104,16 +1378,20 @@ export const makeMessagesSocket = (config) => {
1104
1378
  }
1105
1379
  }
1106
1380
  else if (isEditMsg) {
1107
- additionalAttributes.edit = '1';
1381
+ additionalAttributes.edit = isNewsletter ? '3' : '1';
1108
1382
  }
1109
1383
  else if (isPinMsg) {
1110
1384
  additionalAttributes.edit = '2';
1111
1385
  }
1112
1386
  else if (isPollMessage) {
1387
+ if (!isNewsletter && isQuizMsg) {
1388
+ throw new Boom('Quiz are only allowed for newsletter', { statusCode: 400 });
1389
+ }
1113
1390
  additionalNodes.push({
1114
1391
  tag: 'meta',
1115
1392
  attrs: {
1116
- polltype: 'creation'
1393
+ polltype: isQuizMsg ? 'quiz_creation' : 'creation',
1394
+ contenttype: isPollMessage && isNewsletter ? 'text' : undefined
1117
1395
  }
1118
1396
  });
1119
1397
  }
@@ -1125,12 +1403,28 @@ export const makeMessagesSocket = (config) => {
1125
1403
  }
1126
1404
  });
1127
1405
  }
1406
+ else if (isAiMsg) {
1407
+ if (!(isPnUser(jid) || isLidUser(jid))) {
1408
+ throw new Boom('AI icon on message are only allowed in private chat', { statusCode: 400 });
1409
+ }
1410
+ if ('messageContextInfo' in fullMsg.message && !!fullMsg.message.messageContextInfo) {
1411
+ fullMsg.message.messageContextInfo.supportPayload = BIZ_BOT_SUPPORT_PAYLOAD;
1412
+ }
1413
+ additionalNodes.push({
1414
+ tag: 'bot',
1415
+ attrs: {
1416
+ biz_bot: '1'
1417
+ }
1418
+ });
1419
+ delete content.ai;
1420
+ }
1128
1421
  await relayMessage(jid, fullMsg.message, {
1129
1422
  messageId: fullMsg.key.id,
1130
1423
  useCachedGroupMetadata: options.useCachedGroupMetadata,
1131
1424
  additionalAttributes,
1132
1425
  statusJidList: options.statusJidList,
1133
- additionalNodes
1426
+ additionalNodes,
1427
+ addBizAttributes: isNeedBizAttrs
1134
1428
  });
1135
1429
  if (config.emitOwnEvents) {
1136
1430
  process.nextTick(async () => {
@@ -1139,188 +1433,6 @@ export const makeMessagesSocket = (config) => {
1139
1433
  }
1140
1434
  return fullMsg;
1141
1435
  }
1142
- },
1143
- sendChStatus: async (jid, content, options = {}) => {
1144
- const userJid = authState.creds.me.id;
1145
- const statusJid = 'status@broadcast';
1146
- const fullMsg = await generateWAMessage(statusJid, content, {
1147
- logger,
1148
- userJid,
1149
- getUrlInfo: text => getUrlInfo(text, {
1150
- thumbnailWidth: linkPreviewImageThumbnailWidth,
1151
- fetchOpts: {
1152
- timeout: 3000,
1153
- ...(httpRequestOptions || {})
1154
- },
1155
- logger,
1156
- uploadImage: generateHighQualityLinkPreview ? waUploadToServer : undefined
1157
- }),
1158
- getProfilePicUrl: sock.profilePictureUrl,
1159
- getCallLink: sock.createCallLink,
1160
- upload: waUploadToServer,
1161
- mediaCache: config.mediaCache,
1162
- options: config.options,
1163
- messageId: generateMessageIDV2(sock.user?.id),
1164
- ...options
1165
- });
1166
- await relayMessage(statusJid, fullMsg.message, {
1167
- messageId: fullMsg.key.id,
1168
- useCachedGroupMetadata: options.useCachedGroupMetadata,
1169
- statusJidList: [jid]
1170
- });
1171
- if (config.emitOwnEvents) {
1172
- process.nextTick(async () => {
1173
- await messageMutex.mutex(() => upsertMessage(fullMsg, 'append'));
1174
- });
1175
- }
1176
- return fullMsg;
1177
- },
1178
- sendGroupStatus: (groupJid, content, options = {}) => (sendGroupStatus({
1179
- waUploadToServer,
1180
- relayMessage,
1181
- authState
1182
- }, groupJid, content, options)),
1183
- sendChannelStatus: (channelJid, content, options = {}) => (sendChannelStatus({
1184
- waUploadToServer,
1185
- relayMessage,
1186
- authState
1187
- }, channelJid, content, options)),
1188
- menurich: async (jid, options) => {
1189
- const sections = [];
1190
- // 1. Header (Title)
1191
- if (options.title) {
1192
- sections.push({
1193
- view_model: {
1194
- primitive: {
1195
- __typename: "FOATextPrimitive",
1196
- text: options.title
1197
- },
1198
- __typename: "GenAISingleLayoutViewModel"
1199
- }
1200
- });
1201
- }
1202
- // 2. Subtitle
1203
- if (options.subtitle) {
1204
- sections.push({
1205
- view_model: {
1206
- primitive: {
1207
- __typename: "GenAIMarkdownTextUXPrimitive",
1208
- text: options.subtitle
1209
- },
1210
- __typename: "GenAISingleLayoutViewModel"
1211
- }
1212
- });
1213
- }
1214
- // 3. Image
1215
- if (options.imageUrl) {
1216
- sections.push({
1217
- view_model: {
1218
- primitive: {
1219
- __typename: "GenAIImagePrimitive",
1220
- preview_image: {
1221
- __typename: "GenAIMediaItem",
1222
- mime_type: "image/jpeg",
1223
- url: options.imageUrl
1224
- },
1225
- full_image: {
1226
- __typename: "GenAIMediaItem",
1227
- mime_type: "image/jpeg",
1228
- url: options.imageUrl
1229
- }
1230
- },
1231
- __typename: "GenAISingleLayoutViewModel"
1232
- }
1233
- });
1234
- }
1235
- // 4. Horizontal Scroll CTAs (Buttons/Menus)
1236
- if (options.menus && options.menus.length > 0) {
1237
- const primitives = options.menus.map((menu, mIdx) => {
1238
- return {
1239
- __typename: "GenAI3PExtWidgetPrimitive",
1240
- header: {
1241
- __typename: "GenAI3PExtWidgetStandardHeader",
1242
- title: menu.title
1243
- },
1244
- body: {
1245
- __typename: "GenAI3PExtCalendarEventList",
1246
- sections: [],
1247
- ctas: menu.buttons.map((btn, bIdx) => ({
1248
- __typename: "GenAI3PExtWidgetCTA",
1249
- label: btn.label,
1250
- state: "PENDING",
1251
- kind: "OTHER",
1252
- tool_call_id: `${mIdx}${bIdx}`,
1253
- toast: btn.toast ? {
1254
- __typename: "GenAI3PExtWidgetToast",
1255
- label: btn.toast
1256
- } : undefined
1257
- }))
1258
- }
1259
- };
1260
- });
1261
- sections.push({
1262
- view_model: {
1263
- primitives,
1264
- __typename: "GenAIHScrollLayoutViewModel"
1265
- }
1266
- });
1267
- }
1268
- // 5. Footer Actions (URLs)
1269
- if (options.footerLinks && options.footerLinks.length > 0) {
1270
- const primitives = options.footerLinks.map(link => ({
1271
- __typename: "GenAIFooterActionPrimitive",
1272
- cta_text: link.label,
1273
- cta_type: "OPEN_URL",
1274
- cta_url: link.url
1275
- }));
1276
- sections.push({
1277
- view_model: {
1278
- primitives,
1279
- __typename: "GenAIHScrollLayoutViewModel"
1280
- }
1281
- });
1282
- }
1283
- const msg = {
1284
- messageContextInfo: {
1285
- messageSecret: Buffer.from("v/3VN8Gfr2dbKzgt1GKDEU7ovyYW+nswh4Duwq6KDuU=", "base64")
1286
- },
1287
- botForwardedMessage: {
1288
- message: {
1289
- richResponseMessage: {
1290
- messageType: 1,
1291
- submessages: options.imageUrl ? [
1292
- {
1293
- messageType: 1,
1294
- gridImageMetadata: {
1295
- gridImageUrl: {
1296
- imagePreviewUrl: options.imageUrl,
1297
- },
1298
- imageUrls: [
1299
- {
1300
- imagePreviewUrl: options.imageUrl,
1301
- imageHighResUrl: options.imageUrl,
1302
- sourceUrl: options.imageUrl,
1303
- }
1304
- ],
1305
- },
1306
- }
1307
- ] : [],
1308
- unifiedResponse: {
1309
- data: Buffer.from(JSON.stringify({
1310
- response_id: "742a451a-0c33-45ca-a205-42c2b1666bca",
1311
- sections
1312
- }))
1313
- },
1314
- contextInfo: {
1315
- forwardingScore: 1,
1316
- isForwarded: true,
1317
- forwardOrigin: 4
1318
- }
1319
- }
1320
- }
1321
- }
1322
- };
1323
- return relayMessage(jid, msg, {});
1324
1436
  }
1325
1437
  };
1326
1438
  };