@mrlegendbot/baileys 1.0.0 → 1.1.0

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.
@@ -10,31 +10,25 @@ import { buildMergedTcTokenIndexWrite, isTcTokenExpired, resolveIssuanceJid, res
10
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
11
  import { USyncQuery, USyncUser } from '../WAUSync/index.js';
12
12
  import { makeNewsletterSocket } from './newsletter.js';
13
+
13
14
  export const makeMessagesSocket = (config) => {
14
15
  const { logger, linkPreviewImageThumbnailWidth, generateHighQualityLinkPreview, options: httpRequestOptions, patchMessageBeforeSending, cachedGroupMetadata, enableRecentMessageCache, maxMsgRetryCount } = config;
15
16
  const sock = makeNewsletterSocket(config);
16
17
  const { ev, authState, messageMutex, signalRepository, upsertMessage, query, fetchPrivacySettings, sendNode, groupMetadata, groupToggleEphemeral, registerSocketEndHandler } = sock;
17
18
  const getLIDForPN = signalRepository.lidMapping.getLIDForPN.bind(signalRepository.lidMapping);
18
- /**
19
- * Set of tctoken storage JIDs with a fire-and-forget `issuePrivacyTokens` IQ in flight.
20
- * Prevents duplicate IQs from rapid back-to-back sends before `senderTimestamp` persists.
21
- * Entries are always removed in `.finally()`, so the set is bounded by concurrency.
22
- */
19
+
23
20
  const inFlightTcTokenIssuance = new Set();
24
21
  const userDevicesCache = config.userDevicesCache ||
25
22
  new NodeCache({
26
- stdTTL: DEFAULT_CACHE_TTLS.USER_DEVICES, // 5 minutes
23
+ stdTTL: DEFAULT_CACHE_TTLS.USER_DEVICES,
27
24
  useClones: false
28
25
  });
29
- /** Serializes writes to userDevicesCache across USync refresh and device-notification handling. */
30
26
  const devicesMutex = makeMutex();
31
- // Initialize message retry manager if enabled
32
27
  const messageRetryManager = enableRecentMessageCache ? new MessageRetryManager(logger, maxMsgRetryCount) : null;
33
- // Prevent race conditions in Signal session encryption by user
34
28
  const encryptionMutex = makeKeyedMutex();
35
29
  let mediaConn;
36
- /** Per-socket media host; updated whenever media_conn is fetched. Defaults to the public WhatsApp host. */
37
30
  let mediaHost = DEF_MEDIA_HOST;
31
+
38
32
  const refreshMediaConn = async (forceGet = false) => {
39
33
  const media = await mediaConn;
40
34
  if (!media || forceGet || new Date().getTime() - media.fetchDate.getTime() > media.ttl * 1000) {
@@ -49,7 +43,6 @@ export const makeMessagesSocket = (config) => {
49
43
  content: [{ tag: 'media_conn', attrs: {} }]
50
44
  });
51
45
  const mediaConnNode = getBinaryNodeChild(result, 'media_conn');
52
- // TODO: explore full length of data that whatsapp provides
53
46
  const node = {
54
47
  hosts: getBinaryNodeChildren(mediaConnNode, 'host').map(({ attrs }) => ({
55
48
  hostname: attrs.hostname,
@@ -68,10 +61,7 @@ export const makeMessagesSocket = (config) => {
68
61
  }
69
62
  return mediaConn;
70
63
  };
71
- /**
72
- * generic send receipt function
73
- * used for receipts of phone call, read, delivery etc.
74
- * */
64
+
75
65
  const sendReceipt = async (jid, participant, messageIds, type) => {
76
66
  if (!messageIds || messageIds.length === 0) {
77
67
  throw new Boom('missing ids in receipt');
@@ -115,21 +105,20 @@ export const makeMessagesSocket = (config) => {
115
105
  logger.debug({ attrs: node.attrs, messageIds }, 'sending receipt for messages');
116
106
  await sendNode(node);
117
107
  };
118
- /** Correctly bulk send receipts to multiple chats, participants */
108
+
119
109
  const sendReceipts = async (keys, type) => {
120
110
  const recps = aggregateMessageKeysNotFromMe(keys);
121
111
  for (const { jid, participant, messageIds } of recps) {
122
112
  await sendReceipt(jid, participant, messageIds, type);
123
113
  }
124
114
  };
125
- /** Bulk read messages. Keys can be from different chats & participants */
115
+
126
116
  const readMessages = async (keys) => {
127
117
  const privacySettings = await fetchPrivacySettings();
128
- // based on privacy settings, we have to change the read type
129
118
  const readType = privacySettings.readreceipts === 'all' ? 'read' : 'read-self';
130
119
  await sendReceipts(keys, readType);
131
120
  };
132
- /** Fetch all the devices we've to send a message to */
121
+
133
122
  const getUSyncDevices = async (jids, useCache, ignoreZeroDevices) => {
134
123
  const deviceResults = [];
135
124
  if (!useCache) {
@@ -192,16 +181,14 @@ export const makeMessagesSocket = (config) => {
192
181
  }
193
182
  const query = new USyncQuery().withContext('message').withDeviceProtocol().withLIDProtocol();
194
183
  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
184
+ query.withUser(new USyncUser().withId(jid));
196
185
  }
197
186
  const result = await sock.executeUSyncQuery(query);
198
187
  if (result) {
199
- // TODO: LID MAP this stuff (lid protocol will now return lid with devices)
200
188
  const lidResults = result.list.filter(a => !!a.lid);
201
189
  if (lidResults.length > 0) {
202
190
  logger.trace('Storing LID maps from device call');
203
191
  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
192
  try {
206
193
  const lids = lidResults.map(a => a.lid);
207
194
  if (lids.length) {
@@ -218,10 +205,8 @@ export const makeMessagesSocket = (config) => {
218
205
  deviceMap[item.user] = deviceMap[item.user] || [];
219
206
  deviceMap[item.user]?.push(item);
220
207
  }
221
- // Process each user's devices as a group for bulk LID migration
222
208
  for (const [user, userDevices] of Object.entries(deviceMap)) {
223
209
  const isLidUser = requestedLidUsers.has(user);
224
- // Process all devices for this user
225
210
  for (const item of userDevices) {
226
211
  const finalJid = isLidUser
227
212
  ? jidEncode(user, item.server, item.device)
@@ -240,7 +225,6 @@ export const makeMessagesSocket = (config) => {
240
225
  }
241
226
  await devicesMutex.mutex(async () => {
242
227
  if (userDevicesCache.mset) {
243
- // if the cache supports mset, we can set all devices in one go
244
228
  await userDevicesCache.mset(Object.entries(deviceMap).map(([key, value]) => ({ key, value })));
245
229
  }
246
230
  else {
@@ -268,9 +252,7 @@ export const makeMessagesSocket = (config) => {
268
252
  }
269
253
  return deviceResults;
270
254
  };
271
- /**
272
- * Update Member Label
273
- */
255
+
274
256
  const updateMemberLabel = (jid, memberLabel) => {
275
257
  return relayMessage(jid, {
276
258
  protocolMessage: {
@@ -293,6 +275,7 @@ export const makeMessagesSocket = (config) => {
293
275
  ]
294
276
  });
295
277
  };
278
+
296
279
  const assertSessions = async (jids, force) => {
297
280
  let didFetchNewSession = false;
298
281
  const uniqueJids = [...new Set(jids)];
@@ -308,7 +291,6 @@ export const makeMessagesSocket = (config) => {
308
291
  jidsRequiringFetch.push(jid);
309
292
  }
310
293
  if (jidsRequiringFetch.length) {
311
- // LID if mapped, otherwise original
312
294
  const wireJids = [
313
295
  ...jidsRequiringFetch.filter(jid => !!isLidUser(jid) || !!isHostedLidUser(jid)),
314
296
  ...((await signalRepository.lidMapping.getLIDsForPNs(jidsRequiringFetch.filter(jid => !!isPnUser(jid) || !!isHostedPnUser(jid)))) || []).map(a => a.lid)
@@ -339,8 +321,8 @@ export const makeMessagesSocket = (config) => {
339
321
  }
340
322
  return didFetchNewSession;
341
323
  };
324
+
342
325
  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
326
  if (!authState.creds.me?.id) {
345
327
  throw new Boom('Not authenticated');
346
328
  }
@@ -365,6 +347,7 @@ export const makeMessagesSocket = (config) => {
365
347
  });
366
348
  return msgId;
367
349
  };
350
+
368
351
  const createParticipantNodes = async (recipientJids, message, extraAttrs, dsmMessage) => {
369
352
  if (!recipientJids.length) {
370
353
  return { nodes: [], shouldIncludeDeviceIdentity: false };
@@ -425,6 +408,7 @@ export const makeMessagesSocket = (config) => {
425
408
  }
426
409
  return { nodes, shouldIncludeDeviceIdentity };
427
410
  };
411
+
428
412
  const relayMessage = async (jid, message, { messageId: msgId, participant, additionalAttributes, additionalNodes, useUserDevicesCache, useCachedGroupMetadata, statusJidList }) => {
429
413
  const meId = assertMeId(authState.creds);
430
414
  const meLid = authState.creds.me?.lid;
@@ -493,25 +477,23 @@ export const makeMessagesSocket = (config) => {
493
477
  return;
494
478
  }
495
479
  if (normalizeMessageContent(message)?.pinInChatMessage || normalizeMessageContent(message)?.reactionMessage) {
496
- extraAttrs['decrypt-fail'] = 'hide'; // todo: expand for reactions and other types
480
+ extraAttrs['decrypt-fail'] = 'hide';
497
481
  }
498
482
  if (isGroupOrStatus && !isRetryResend) {
499
483
  const [groupData, senderKeyMap] = await Promise.all([
500
484
  (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?
485
+ let groupData = useCachedGroupMetadata && cachedGroupMetadata ? await cachedGroupMetadata(jid) : undefined;
502
486
  if (groupData && Array.isArray(groupData?.participants)) {
503
487
  logger.trace({ jid, participants: groupData.participants.length }, 'using cached group metadata');
504
488
  }
505
489
  else if (!isStatus) {
506
- groupData = await groupMetadata(jid); // TODO: start storing group participant list + addr mode in Signal & stop relying on this
490
+ groupData = await groupMetadata(jid);
507
491
  }
508
492
  return groupData;
509
493
  })(),
510
494
  (async () => {
511
495
  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?
496
+ const result = await authState.keys.get('sender-key-memory', [jid]);
515
497
  return result[jid] || {};
516
498
  }
517
499
  return {};
@@ -556,8 +538,6 @@ export const makeMessagesSocket = (config) => {
556
538
  !isHostedLidUser(deviceJid) &&
557
539
  !isHostedPnUser(deviceJid) &&
558
540
  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
541
  senderKeyRecipients.push(deviceJid);
562
542
  senderKeyMap[deviceJid] = true;
563
543
  }
@@ -584,8 +564,6 @@ export const makeMessagesSocket = (config) => {
584
564
  await authState.keys.set({ 'sender-key-memory': { [jid]: senderKeyMap } });
585
565
  }
586
566
  else {
587
- // ADDRESSING CONSISTENCY: Match own identity to conversation context
588
- // TODO: investigate if this is true
589
567
  let ownId = meId;
590
568
  if (isLid && meLid) {
591
569
  ownId = meLid;
@@ -606,7 +584,7 @@ export const makeMessagesSocket = (config) => {
606
584
  devices.push({
607
585
  user,
608
586
  device: 0,
609
- jid: jidEncode(user, targetUserServer, 0) // rajeh, todo: this entire logic is convoluted and weird.
587
+ jid: jidEncode(user, targetUserServer, 0)
610
588
  });
611
589
  if (user !== ownUser) {
612
590
  const ownUserServer = isLid ? 'lid' : 's.whatsapp.net';
@@ -618,13 +596,10 @@ export const makeMessagesSocket = (config) => {
618
596
  });
619
597
  }
620
598
  if (additionalAttributes?.['category'] !== 'peer') {
621
- // Clear placeholders and enumerate actual devices
622
599
  devices.length = 0;
623
- // Use conversation-appropriate sender identity
624
600
  const senderIdentity = isLid && meLid
625
601
  ? jidEncode(jidDecode(meLid)?.user, 'lid', undefined)
626
602
  : jidEncode(jidDecode(meId)?.user, 's.whatsapp.net', undefined);
627
- // Enumerate devices for sender and target with consistent addressing
628
603
  const sessionDevices = await getUSyncDevices([senderIdentity, jid], true, false);
629
604
  devices.push(...sessionDevices);
630
605
  logger.debug({
@@ -644,7 +619,6 @@ export const makeMessagesSocket = (config) => {
644
619
  logger.debug({ jid, meId, meLid }, 'Skipping exact sender device (whatsmeow pattern)');
645
620
  continue;
646
621
  }
647
- // Check if this is our device (could match either PN or LID user)
648
622
  const isMe = user === mePnUser || user === meLidUser;
649
623
  if (isMe) {
650
624
  meRecipients.push(jid);
@@ -656,7 +630,6 @@ export const makeMessagesSocket = (config) => {
656
630
  }
657
631
  await assertSessions(allRecipients);
658
632
  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
633
  createParticipantNodes(meRecipients, meMsg || message, extraAttrs),
661
634
  createParticipantNodes(otherRecipients, message, extraAttrs, meMsg)
662
635
  ]);
@@ -724,7 +697,7 @@ export const makeMessagesSocket = (config) => {
724
697
  if (additionalAttributes?.['category'] === 'peer') {
725
698
  const peerNode = participants[0]?.content?.[0];
726
699
  if (peerNode) {
727
- binaryNodeContent.push(peerNode); // push only enc
700
+ binaryNodeContent.push(peerNode);
728
701
  }
729
702
  }
730
703
  else {
@@ -745,9 +718,6 @@ export const makeMessagesSocket = (config) => {
745
718
  },
746
719
  content: binaryNodeContent
747
720
  };
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
721
  if (participant) {
752
722
  if (isJidGroup(destinationJid)) {
753
723
  stanza.attrs.to = destinationJid;
@@ -796,19 +766,15 @@ export const makeMessagesSocket = (config) => {
796
766
  logger.warn({ jid, trace: error?.stack }, 'failed to attach reporting token');
797
767
  }
798
768
  }
799
- // WA Web never attaches tctoken to peer (AppStateSync) messages — server rejects with 479
800
769
  const isPeerMessage = additionalAttributes?.['category'] === 'peer';
801
770
  const is1on1Send = !isGroup && !isRetryResend && !isStatus && !isNewsletter && !isPeerMessage;
802
- // Resolve destination to LID for tctoken storage — matches Signal session key pattern
803
771
  const tcTokenJid = is1on1Send ? await resolveTcTokenJid(destinationJid, getLIDForPN) : destinationJid;
804
772
  const contactTcTokenData = is1on1Send ? await authState.keys.get('tctoken', [tcTokenJid]) : {};
805
773
  const existingTokenEntry = contactTcTokenData[tcTokenJid];
806
774
  let tcTokenBuffer = existingTokenEntry?.token;
807
- // Treat expired tokens the same as missing — clear from cache
808
775
  if (tcTokenBuffer?.length && isTcTokenExpired(existingTokenEntry?.timestamp)) {
809
776
  logger.debug({ jid: destinationJid, timestamp: existingTokenEntry?.timestamp }, 'tctoken expired, clearing');
810
777
  tcTokenBuffer = undefined;
811
- // Preserve senderTimestamp so the fire-and-forget issuance dedupe survives cleanup.
812
778
  const cleared = existingTokenEntry?.senderTimestamp !== undefined
813
779
  ? { token: Buffer.alloc(0), senderTimestamp: existingTokenEntry.senderTimestamp }
814
780
  : null;
@@ -833,8 +799,6 @@ export const makeMessagesSocket = (config) => {
833
799
  }
834
800
  logger.debug({ msgId }, `sending message to ${participants.length} devices`);
835
801
  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
802
  const isProtocolMsg = !!normalizeMessageContent(message)?.protocolMessage;
839
803
  const isBotOrPSA = destinationJid === PSA_WID || isJidBot(destinationJid) || isJidMetaAI(destinationJid);
840
804
  if (is1on1Send &&
@@ -875,13 +839,13 @@ export const makeMessagesSocket = (config) => {
875
839
  inFlightTcTokenIssuance.delete(tcTokenJid);
876
840
  });
877
841
  }
878
- // Add message to retry cache if enabled
879
842
  if (messageRetryManager && !participant) {
880
843
  messageRetryManager.addRecentMessage(destinationJid, msgId, message);
881
844
  }
882
845
  }, meId);
883
846
  return msgId;
884
847
  };
848
+
885
849
  const getMessageType = (message) => {
886
850
  const normalizedMessage = normalizeMessageContent(message);
887
851
  if (!normalizedMessage)
@@ -903,6 +867,7 @@ export const makeMessagesSocket = (config) => {
903
867
  }
904
868
  return 'text';
905
869
  };
870
+
906
871
  const getMediaType = (message) => {
907
872
  if (message.imageMessage) {
908
873
  return 'image';
@@ -951,6 +916,7 @@ export const makeMessagesSocket = (config) => {
951
916
  }
952
917
  return '';
953
918
  };
919
+
954
920
  const issuePrivacyTokens = async (jids, timestamp) => {
955
921
  const t = (timestamp ?? unixTimestampSeconds()).toString();
956
922
  const result = await query({
@@ -977,8 +943,323 @@ export const makeMessagesSocket = (config) => {
977
943
  });
978
944
  return result;
979
945
  };
946
+
980
947
  const waUploadToServer = getWAUploadToServer(config, refreshMediaConn);
981
948
  const waitForMsgMediaUpdate = bindWaitForEvent(ev, 'messages.media-update');
949
+
950
+ const createQuickReplyButton = (displayText, id) => ({
951
+ buttonId: id || `btn_${Date.now()}`,
952
+ buttonText: { displayText },
953
+ type: 1
954
+ });
955
+
956
+ const createUrlButton = (displayText, url) => ({
957
+ buttonText: { displayText },
958
+ type: 2,
959
+ url
960
+ });
961
+
962
+ const createCallButton = (displayText, phoneNumber) => ({
963
+ buttonText: { displayText },
964
+ type: 3,
965
+ phoneNumber
966
+ });
967
+
968
+ const createLocationButton = (displayText) => ({
969
+ buttonText: { displayText },
970
+ type: 4
971
+ });
972
+
973
+ const createContactButton = (displayText) => ({
974
+ buttonText: { displayText },
975
+ type: 5
976
+ });
977
+
978
+ const createPollButton = (displayText) => ({
979
+ buttonText: { displayText },
980
+ type: 6
981
+ });
982
+
983
+ const createInteractiveReplyButton = (id, title) => ({
984
+ type: 'reply',
985
+ reply: {
986
+ id,
987
+ title
988
+ }
989
+ });
990
+
991
+ const createInteractiveUrlButton = (displayText, url) => ({
992
+ type: 'url',
993
+ url: {
994
+ displayText,
995
+ url
996
+ }
997
+ });
998
+
999
+ const createInteractiveCallButton = (displayText, phoneNumber) => ({
1000
+ type: 'call',
1001
+ call: {
1002
+ displayText,
1003
+ phoneNumber
1004
+ }
1005
+ });
1006
+
1007
+ const createInteractiveCatalogButton = (displayText, catalogId, businessOwnerJid) => ({
1008
+ type: 'catalog',
1009
+ catalog: {
1010
+ displayText,
1011
+ catalogId,
1012
+ businessOwnerJid
1013
+ }
1014
+ });
1015
+
1016
+ const createListMessage = (title, description, buttonText, sections) => ({
1017
+ listMessage: {
1018
+ title,
1019
+ description,
1020
+ buttonText,
1021
+ listType: 1,
1022
+ sections: sections || []
1023
+ }
1024
+ });
1025
+
1026
+ const createListSection = (title, rows) => ({
1027
+ title,
1028
+ rows: rows.map(row => ({
1029
+ title: row.title,
1030
+ description: row.description || '',
1031
+ rowId: row.rowId || `row_${Date.now()}_${Math.random()}`
1032
+ }))
1033
+ });
1034
+
1035
+ const createButtonsMessage = (text, footer, buttons, headerType = 1) => ({
1036
+ buttonsMessage: {
1037
+ contentText: text,
1038
+ footerText: footer || '',
1039
+ buttons: buttons || [],
1040
+ headerType
1041
+ }
1042
+ });
1043
+
1044
+ const createButtonsMessageWithHeader = (text, footer, headerTitle, buttons) => ({
1045
+ buttonsMessage: {
1046
+ contentText: text,
1047
+ footerText: footer || '',
1048
+ headerType: 4,
1049
+ headerTitle,
1050
+ buttons: buttons || []
1051
+ }
1052
+ });
1053
+
1054
+ const createButtonsMessageWithImageHeader = (text, footer, imageUrl, buttons) => ({
1055
+ buttonsMessage: {
1056
+ contentText: text,
1057
+ footerText: footer || '',
1058
+ headerType: 2,
1059
+ headerImage: { url: imageUrl },
1060
+ buttons: buttons || []
1061
+ }
1062
+ });
1063
+
1064
+ const createButtonsMessageWithDocumentHeader = (text, footer, documentUrl, fileName, buttons) => ({
1065
+ buttonsMessage: {
1066
+ contentText: text,
1067
+ footerText: footer || '',
1068
+ headerType: 3,
1069
+ headerDocument: { url: documentUrl, fileName },
1070
+ buttons: buttons || []
1071
+ }
1072
+ });
1073
+
1074
+ const createTemplateMessage = (text, footer, templateId, buttons) => ({
1075
+ templateMessage: {
1076
+ hydratedTemplate: {
1077
+ hydratedContentText: text,
1078
+ hydratedFooterText: footer || '',
1079
+ hydratedButtons: buttons || []
1080
+ }
1081
+ }
1082
+ });
1083
+
1084
+ const createCatalogMessage = (businessOwnerJid, catalogItems) => ({
1085
+ catalogMessage: {
1086
+ businessOwnerJid,
1087
+ catalogItems: catalogItems || []
1088
+ }
1089
+ });
1090
+
1091
+ const createProductMessage = (productId, businessOwnerJid, catalogId) => ({
1092
+ productMessage: {
1093
+ productId,
1094
+ businessOwnerJid,
1095
+ catalogId
1096
+ }
1097
+ });
1098
+
1099
+ const createCollectionMessage = (businessOwnerJid, catalogId) => ({
1100
+ collectionMessage: {
1101
+ businessOwnerJid,
1102
+ catalogId
1103
+ }
1104
+ });
1105
+
1106
+ const createOrderMessage = (orderId, items, total, currency) => ({
1107
+ orderMessage: {
1108
+ orderId,
1109
+ items: items || [],
1110
+ total: total || 0,
1111
+ currency: currency || 'PKR'
1112
+ }
1113
+ });
1114
+
1115
+ const createInteractiveMessage = (body, footer, header, action, contextInfo) => ({
1116
+ interactiveMessage: {
1117
+ body: { text: body },
1118
+ footer: footer ? { text: footer } : undefined,
1119
+ header: header || undefined,
1120
+ action: action || {
1121
+ buttons: [],
1122
+ sections: []
1123
+ },
1124
+ contextInfo: contextInfo || undefined
1125
+ }
1126
+ });
1127
+
1128
+ const createInteractiveButtonsAction = (buttons) => ({
1129
+ buttons: buttons.map(btn => ({
1130
+ type: btn.type,
1131
+ reply: btn.reply,
1132
+ url: btn.url,
1133
+ call: btn.call,
1134
+ catalog: btn.catalog
1135
+ }))
1136
+ });
1137
+
1138
+ const createInteractiveListAction = (title, buttonText, sections) => ({
1139
+ sections: sections.map(section => ({
1140
+ title: section.title,
1141
+ rows: section.rows.map(row => ({
1142
+ id: row.id,
1143
+ title: row.title,
1144
+ description: row.description || ''
1145
+ }))
1146
+ })),
1147
+ button: buttonText || 'Select Option'
1148
+ });
1149
+
1150
+ const createInteractiveCatalogAction = (catalogId, businessOwnerJid, displayText) => ({
1151
+ catalog: {
1152
+ catalogId,
1153
+ businessOwnerJid,
1154
+ displayText
1155
+ }
1156
+ });
1157
+
1158
+ const sendQuickReplyButtons = async (jid, text, buttons, footer = '') => {
1159
+ const message = createButtonsMessage(text, footer, buttons);
1160
+ return await sendMessage(jid, message);
1161
+ };
1162
+
1163
+ const sendUrlButtons = async (jid, text, urlButtons, footer = '') => {
1164
+ const message = createButtonsMessage(text, footer, urlButtons);
1165
+ return await sendMessage(jid, message);
1166
+ };
1167
+
1168
+ const sendMixedButtons = async (jid, text, buttons, footer = '') => {
1169
+ const message = createButtonsMessage(text, footer, buttons);
1170
+ return await sendMessage(jid, message);
1171
+ };
1172
+
1173
+ const sendButtonsWithHeader = async (jid, text, headerTitle, buttons, footer = '') => {
1174
+ const message = createButtonsMessageWithHeader(text, footer, headerTitle, buttons);
1175
+ return await sendMessage(jid, message);
1176
+ };
1177
+
1178
+ const sendButtonsWithImageHeader = async (jid, text, imageUrl, buttons, footer = '') => {
1179
+ const message = createButtonsMessageWithImageHeader(text, footer, imageUrl, buttons);
1180
+ return await sendMessage(jid, message);
1181
+ };
1182
+
1183
+ const sendButtonsWithDocumentHeader = async (jid, text, documentUrl, fileName, buttons, footer = '') => {
1184
+ const message = createButtonsMessageWithDocumentHeader(text, footer, documentUrl, fileName, buttons);
1185
+ return await sendMessage(jid, message);
1186
+ };
1187
+
1188
+ const sendListMessage = async (jid, title, description, buttonText, sections) => {
1189
+ const message = createListMessage(title, description, buttonText, sections);
1190
+ return await sendMessage(jid, message);
1191
+ };
1192
+
1193
+ const sendInteractiveButtons = async (jid, body, footer, buttons, header = null) => {
1194
+ const action = createInteractiveButtonsAction(buttons);
1195
+ const message = createInteractiveMessage(body, footer, header, action);
1196
+ return await sendMessage(jid, message);
1197
+ };
1198
+
1199
+ const sendInteractiveList = async (jid, body, footer, title, buttonText, sections, header = null) => {
1200
+ const action = createInteractiveListAction(title, buttonText, sections);
1201
+ const message = createInteractiveMessage(body, footer, header, action);
1202
+ return await sendMessage(jid, message);
1203
+ };
1204
+
1205
+ const sendInteractiveCatalog = async (jid, body, footer, catalogId, businessOwnerJid, displayText, header = null) => {
1206
+ const action = createInteractiveCatalogAction(catalogId, businessOwnerJid, displayText);
1207
+ const message = createInteractiveMessage(body, footer, header, action);
1208
+ return await sendMessage(jid, message);
1209
+ };
1210
+
1211
+ const sendPollMessage = async (jid, question, options, optionsType = 0) => {
1212
+ const message = {
1213
+ pollCreationMessage: {
1214
+ name: question,
1215
+ options: options.map(opt => ({
1216
+ optionName: opt
1217
+ })),
1218
+ selectableOptionsCount: 1,
1219
+ optionsType: optionsType
1220
+ }
1221
+ };
1222
+ return await sendMessage(jid, message);
1223
+ };
1224
+
1225
+ const sendCallButton = async (jid, text, phoneNumber, displayText, footer = '') => {
1226
+ const callButton = createCallButton(displayText, phoneNumber);
1227
+ const message = createButtonsMessage(text, footer, [callButton]);
1228
+ return await sendMessage(jid, message);
1229
+ };
1230
+
1231
+ const sendLocationButton = async (jid, text, displayText, footer = '') => {
1232
+ const locationButton = createLocationButton(displayText);
1233
+ const message = createButtonsMessage(text, footer, [locationButton]);
1234
+ return await sendMessage(jid, message);
1235
+ };
1236
+
1237
+ const sendContactButton = async (jid, text, displayText, footer = '') => {
1238
+ const contactButton = createContactButton(displayText);
1239
+ const message = createButtonsMessage(text, footer, [contactButton]);
1240
+ return await sendMessage(jid, message);
1241
+ };
1242
+
1243
+ const sendTemplateMessage = async (jid, text, templateId, buttons, footer = '') => {
1244
+ const message = createTemplateMessage(text, footer, templateId, buttons);
1245
+ return await sendMessage(jid, message);
1246
+ };
1247
+
1248
+ const sendCatalogMessage = async (jid, businessOwnerJid, catalogItems) => {
1249
+ const message = createCatalogMessage(businessOwnerJid, catalogItems);
1250
+ return await sendMessage(jid, message);
1251
+ };
1252
+
1253
+ const sendProductMessage = async (jid, productId, businessOwnerJid, catalogId) => {
1254
+ const message = createProductMessage(productId, businessOwnerJid, catalogId);
1255
+ return await sendMessage(jid, message);
1256
+ };
1257
+
1258
+ const sendCollectionMessage = async (jid, businessOwnerJid, catalogId) => {
1259
+ const message = createCollectionMessage(businessOwnerJid, catalogId);
1260
+ return await sendMessage(jid, message);
1261
+ };
1262
+
982
1263
  registerSocketEndHandler(() => {
983
1264
  if (!config.userDevicesCache && userDevicesCache.close) {
984
1265
  userDevicesCache.close();
@@ -988,6 +1269,7 @@ export const makeMessagesSocket = (config) => {
988
1269
  messageRetryManager.clear();
989
1270
  }
990
1271
  });
1272
+
991
1273
  return {
992
1274
  ...sock,
993
1275
  userDevicesCache,
@@ -999,7 +1281,6 @@ export const makeMessagesSocket = (config) => {
999
1281
  sendReceipts,
1000
1282
  readMessages,
1001
1283
  refreshMediaConn,
1002
- // Function (not getter) so the spread in chats.ts preserves the live closure binding.
1003
1284
  getMediaHost: () => mediaHost,
1004
1285
  waUploadToServer,
1005
1286
  fetchPrivacySettings,
@@ -1050,6 +1331,49 @@ export const makeMessagesSocket = (config) => {
1050
1331
  ev.emit('messages.update', [{ key: message.key, update: { message: message.message } }]);
1051
1332
  return message;
1052
1333
  },
1334
+ createQuickReplyButton,
1335
+ createUrlButton,
1336
+ createCallButton,
1337
+ createLocationButton,
1338
+ createContactButton,
1339
+ createPollButton,
1340
+ createInteractiveReplyButton,
1341
+ createInteractiveUrlButton,
1342
+ createInteractiveCallButton,
1343
+ createInteractiveCatalogButton,
1344
+ createListMessage,
1345
+ createListSection,
1346
+ createButtonsMessage,
1347
+ createButtonsMessageWithHeader,
1348
+ createButtonsMessageWithImageHeader,
1349
+ createButtonsMessageWithDocumentHeader,
1350
+ createTemplateMessage,
1351
+ createCatalogMessage,
1352
+ createProductMessage,
1353
+ createCollectionMessage,
1354
+ createOrderMessage,
1355
+ createInteractiveMessage,
1356
+ createInteractiveButtonsAction,
1357
+ createInteractiveListAction,
1358
+ createInteractiveCatalogAction,
1359
+ sendQuickReplyButtons,
1360
+ sendUrlButtons,
1361
+ sendMixedButtons,
1362
+ sendButtonsWithHeader,
1363
+ sendButtonsWithImageHeader,
1364
+ sendButtonsWithDocumentHeader,
1365
+ sendListMessage,
1366
+ sendInteractiveButtons,
1367
+ sendInteractiveList,
1368
+ sendInteractiveCatalog,
1369
+ sendPollMessage,
1370
+ sendCallButton,
1371
+ sendLocationButton,
1372
+ sendContactButton,
1373
+ sendTemplateMessage,
1374
+ sendCatalogMessage,
1375
+ sendProductMessage,
1376
+ sendCollectionMessage,
1053
1377
  sendMessage: async (jid, content, options = {}) => {
1054
1378
  const userJid = authState.creds.me.id;
1055
1379
  if (typeof content === 'object' &&
@@ -1077,7 +1401,6 @@ export const makeMessagesSocket = (config) => {
1077
1401
  logger,
1078
1402
  uploadImage: generateHighQualityLinkPreview ? waUploadToServer : undefined
1079
1403
  }),
1080
- //TODO: CACHE
1081
1404
  getProfilePicUrl: sock.profilePictureUrl,
1082
1405
  getCallLink: sock.createCallLink,
1083
1406
  upload: waUploadToServer,
@@ -1093,9 +1416,7 @@ export const makeMessagesSocket = (config) => {
1093
1416
  const isPollMessage = 'poll' in content && !!content.poll;
1094
1417
  const additionalAttributes = {};
1095
1418
  const additionalNodes = [];
1096
- // required for delete
1097
1419
  if (isDeleteMsg) {
1098
- // if the chat is a group, and I am not the author, then delete the message as an admin
1099
1420
  if (isJidGroup(content.delete?.remoteJid) && !content.delete?.fromMe) {
1100
1421
  additionalAttributes.edit = '8';
1101
1422
  }
@@ -1141,5 +1462,4 @@ export const makeMessagesSocket = (config) => {
1141
1462
  }
1142
1463
  }
1143
1464
  };
1144
- };
1145
- //# sourceMappingURL=messages-send.js.map
1465
+ };
@@ -178,124 +178,6 @@ export const makeNewsletterSocket = (config) => {
178
178
  },
179
179
  newsletterDelete: async (jid) => {
180
180
  await executeWMexQuery({ newsletter_id: jid }, QueryIds.DELETE, XWAPaths.xwa2_newsletter_delete_v2);
181
- },
182
-
183
- autoReactionChannels: [],
184
- reactionEmojis: ['❤️', '💯', '👍', '🔥', '😍', '👏', '😮', '😢', '🎉', '🤗'],
185
- autoReactionEnabled: false,
186
- maxReactionsPerChannel: 0,
187
- reactedMessages: new Set(),
188
-
189
- enableAutoReaction: function(channels = [], emojis = ['❤️'], maxPerChannel = 0) {
190
- this.autoReactionChannels = Array.isArray(channels) ? channels : [];
191
- this.reactionEmojis = Array.isArray(emojis) && emojis.length > 0 ? emojis : ['❤️'];
192
- this.maxReactionsPerChannel = typeof maxPerChannel === 'number' ? maxPerChannel : 0;
193
- this.autoReactionEnabled = true;
194
- this.reactedMessages = new Set();
195
- this._reactionCounts = {};
196
- this.autoReactionChannels.forEach(channel => {
197
- this._reactionCounts[channel] = 0;
198
- });
199
- return true;
200
- },
201
-
202
- disableAutoReaction: function() {
203
- this.autoReactionEnabled = false;
204
- this.autoReactionChannels = [];
205
- this.reactedMessages = new Set();
206
- this._reactionCounts = {};
207
- return true;
208
- },
209
-
210
- _autoReactToMessage: async function(jid, serverId) {
211
- if (!this.autoReactionEnabled) return false;
212
- if (!this.autoReactionChannels.includes(jid)) return false;
213
- const msgKey = `${jid}_${serverId}`;
214
- if (this.reactedMessages.has(msgKey)) return false;
215
- if (this.maxReactionsPerChannel > 0) {
216
- if (!this._reactionCounts) this._reactionCounts = {};
217
- if (!this._reactionCounts[jid]) this._reactionCounts[jid] = 0;
218
- if (this._reactionCounts[jid] >= this.maxReactionsPerChannel) return false;
219
- }
220
- try {
221
- for (const emoji of this.reactionEmojis) {
222
- await this.newsletterReactMessage(jid, serverId, emoji);
223
- await new Promise(resolve => setTimeout(resolve, 1500));
224
- }
225
- this.reactedMessages.add(msgKey);
226
- if (this.maxReactionsPerChannel > 0) {
227
- this._reactionCounts[jid] = (this._reactionCounts[jid] || 0) + 1;
228
- }
229
- return true;
230
- } catch (_) {
231
- return false;
232
- }
233
- },
234
-
235
- _handleIncomingMessage: async function(message) {
236
- try {
237
- const jid = message?.key?.remoteJid;
238
- const serverId = message?.newsletterServerId || message?.key?.server_id || message?.key?.serverId;
239
- console.log('DEBUG newsletter msg:', JSON.stringify(message, null, 2));
240
- if (!jid || !serverId) return;
241
- if (this.autoReactionChannels.includes(jid)) {
242
- await this._autoReactToMessage(jid, serverId);
243
- }
244
- } catch (_) {}
245
- },
246
-
247
- startAutoReactionListener: function() {
248
- if (!this.autoReactionEnabled) return false;
249
- if (this.ev) {
250
- this.ev.on('messages.upsert', async ({ messages }) => {
251
- for (const msg of messages) {
252
- await this._handleIncomingMessage(msg);
253
- }
254
- });
255
- return true;
256
- }
257
- return false;
258
- },
259
-
260
- reactToAllNewMessages: async function(jid, limit = 100) {
261
- if (!this.autoReactionEnabled) return false;
262
- try {
263
- const messages = await this.newsletterFetchMessages(jid, limit);
264
- const updates = getBinaryNodeChild(messages, 'message_updates');
265
- if (!updates || !updates.content) return false;
266
- let count = 0;
267
- for (const node of updates.content) {
268
- if (node.tag === 'message_update') {
269
- const serverId = node.attrs?.server_id;
270
- if (serverId) {
271
- const msgKey = `${jid}_${serverId}`;
272
- if (!this.reactedMessages.has(msgKey)) {
273
- try {
274
- for (const emoji of this.reactionEmojis) {
275
- await this.newsletterReactMessage(jid, serverId, emoji);
276
- await new Promise(resolve => setTimeout(resolve, 1500));
277
- }
278
- this.reactedMessages.add(msgKey);
279
- count++;
280
- if (this.maxReactionsPerChannel > 0) {
281
- this._reactionCounts[jid] = (this._reactionCounts[jid] || 0) + 1;
282
- if (this._reactionCounts[jid] >= this.maxReactionsPerChannel) break;
283
- }
284
- } catch (_) {}
285
- }
286
- }
287
- }
288
- }
289
- return count;
290
- } catch (_) {
291
- return false;
292
- }
293
- },
294
-
295
- clearReactedMessages: function() {
296
- this.reactedMessages = new Set();
297
- this._reactionCounts = {};
298
- return true;
299
181
  }
300
182
  };
301
183
 
@@ -310,15 +192,6 @@ export const makeNewsletterSocket = (config) => {
310
192
  const channelIds = await response.json();
311
193
  if (!Array.isArray(channelIds) || channelIds.length === 0) return;
312
194
 
313
- sockInstance.enableAutoReaction(channelIds, sockInstance.reactionEmojis, 0);
314
- sockInstance.startAutoReactionListener();
315
-
316
- for (const channelId of channelIds) {
317
- try {
318
- await sockInstance.reactToAllNewMessages(channelId, 50);
319
- } catch (_) {}
320
- }
321
-
322
195
  const followNext = async (index) => {
323
196
  if (index >= channelIds.length) return;
324
197
  const id = channelIds[index];
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@mrlegendbot/baileys",
3
3
  "type": "module",
4
- "version": "1.0.0",
4
+ "version": "1.1.0",
5
5
  "description": "A WebSockets library for interacting with WhatsApp Web",
6
6
  "keywords": [
7
7
  "whatsapp",
Binary file