@ryuu-reinzz/baileys 3.5.1 → 5.0.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.
Files changed (41) hide show
  1. package/README.md +30 -25
  2. package/WAProto/fix-imports.js +22 -18
  3. package/WAProto/index.js +22 -18
  4. package/lib/Defaults/index.js +10 -9
  5. package/lib/Signal/libsignal.js +46 -19
  6. package/lib/Signal/lid-mapping.js +6 -0
  7. package/lib/Socket/chats.js +241 -39
  8. package/lib/Socket/groups.js +20 -0
  9. package/lib/Socket/messages-recv.js +736 -314
  10. package/lib/Socket/messages-send.js +279 -129
  11. package/lib/Socket/newsletter.js +2 -2
  12. package/lib/Socket/socket.js +56 -25
  13. package/lib/Types/{Newsletter.js → Mex.js} +9 -3
  14. package/lib/Types/State.js +43 -0
  15. package/lib/Types/index.js +1 -1
  16. package/lib/Utils/auth-utils.js +12 -0
  17. package/lib/Utils/chat-utils.js +80 -20
  18. package/lib/Utils/companion-reg-client-utils.js +35 -0
  19. package/lib/Utils/decode-wa-message.js +34 -0
  20. package/lib/Utils/event-buffer.js +49 -1
  21. package/lib/Utils/generics.js +12 -3
  22. package/lib/Utils/history.js +12 -9
  23. package/lib/Utils/identity-change-handler.js +1 -0
  24. package/lib/Utils/index.js +3 -1
  25. package/lib/Utils/link-preview.js +2 -2
  26. package/lib/Utils/message-retry-manager.js +40 -0
  27. package/lib/Utils/messages-media.js +21 -7
  28. package/lib/Utils/messages.js +28 -5
  29. package/lib/Utils/offline-node-processor.js +40 -0
  30. package/lib/Utils/process-message.js +103 -1
  31. package/lib/Utils/signal.js +42 -0
  32. package/lib/Utils/stanza-ack.js +38 -0
  33. package/lib/Utils/sync-action-utils.js +1 -0
  34. package/lib/Utils/tc-token-utils.js +149 -4
  35. package/lib/Utils/validate-connection.js +3 -0
  36. package/lib/WAUSync/Protocols/USyncContactProtocol.js +26 -3
  37. package/lib/WAUSync/Protocols/USyncUsernameProtocol.js +25 -0
  38. package/lib/WAUSync/Protocols/index.js +1 -0
  39. package/lib/WAUSync/USyncQuery.js +6 -2
  40. package/lib/WAUSync/USyncUser.js +8 -0
  41. package/package.json +39 -12
@@ -13,8 +13,10 @@ import {
13
13
  import {
14
14
  aggregateMessageKeysNotFromMe,
15
15
  assertMediaContent,
16
+ assertMeId,
16
17
  bindWaitForEvent,
17
18
  decryptMediaRetryData,
19
+ DEF_MEDIA_HOST,
18
20
  encodeNewsletterMessage,
19
21
  encodeSignedDeviceIdentity,
20
22
  encodeWAMessage,
@@ -36,29 +38,41 @@ import {
36
38
  downloadContentFromMessage,
37
39
  prepareWAMessageMedia,
38
40
  generateMessageID
39
- } from '@ryuu-reinzz/baileys';
41
+ } from '../index.js';
40
42
  import {
41
43
  getUrlInfo
42
44
  } from '../Utils/link-preview.js';
43
45
  import {
44
- makeKeyedMutex
46
+ makeKeyedMutex,
47
+ makeMutex
45
48
  } from '../Utils/make-mutex.js';
46
49
  import {
47
50
  getMessageReportingToken,
48
51
  shouldIncludeReportingToken
49
52
  } from '../Utils/reporting-utils.js';
53
+ import {
54
+ buildMergedTcTokenIndexWrite,
55
+ isTcTokenExpired,
56
+ resolveIssuanceJid,
57
+ resolveTcTokenJid,
58
+ shouldSendNewTcToken,
59
+ storeTcTokensFromIqResult
60
+ } from '../Utils/tc-token-utils.js';
50
61
  import {
51
62
  areJidsSameUser,
52
63
  getBinaryNodeChild,
53
64
  getBinaryNodeChildren,
54
65
  isHostedLidUser,
55
66
  isHostedPnUser,
67
+ isJidBot,
56
68
  isJidGroup,
69
+ isJidMetaAI,
57
70
  isLidUser,
58
71
  isPnUser,
59
72
  jidDecode,
60
73
  jidEncode,
61
74
  jidNormalizedUser,
75
+ PSA_WID,
62
76
  S_WHATSAPP_NET
63
77
  } from '../WABinary/index.js';
64
78
  import {
@@ -100,22 +114,30 @@ export const makeMessagesSocket = (config) => {
100
114
  fetchPrivacySettings,
101
115
  sendNode,
102
116
  groupMetadata,
103
- groupToggleEphemeral
117
+ groupToggleEphemeral,
118
+ registerSocketEndHandler
104
119
  } = sock;
120
+ const getLIDForPN = signalRepository.lidMapping.getLIDForPN.bind(signalRepository.lidMapping);
121
+ /**
122
+ * Set of tctoken storage JIDs with a fire-and-forget `issuePrivacyTokens` IQ in flight.
123
+ * Prevents duplicate IQs from rapid back-to-back sends before `senderTimestamp` persists.
124
+ * Entries are always removed in `.finally()`, so the set is bounded by concurrency.
125
+ */
126
+ const inFlightTcTokenIssuance = new Set();
105
127
  const userDevicesCache = config.userDevicesCache ||
106
128
  new NodeCache({
107
129
  stdTTL: DEFAULT_CACHE_TTLS.USER_DEVICES, // 5 minutes
108
130
  useClones: false
109
131
  });
110
- const peerSessionsCache = new NodeCache({
111
- stdTTL: DEFAULT_CACHE_TTLS.USER_DEVICES,
112
- useClones: false
113
- });
132
+ /** Serializes writes to userDevicesCache across USync refresh and device-notification handling. */
133
+ const devicesMutex = makeMutex();
114
134
  // Initialize message retry manager if enabled
115
135
  const messageRetryManager = enableRecentMessageCache ? new MessageRetryManager(logger, maxMsgRetryCount) : null;
116
136
  // Prevent race conditions in Signal session encryption by user
117
137
  const encryptionMutex = makeKeyedMutex();
118
138
  let mediaConn;
139
+ /** Per-socket media host; updated whenever media_conn is fetched. Defaults to the public WhatsApp host. */
140
+ let mediaHost = DEF_MEDIA_HOST;
119
141
  const refreshMediaConn = async (forceGet = false) => {
120
142
  const media = await mediaConn;
121
143
  if (!media || forceGet || new Date().getTime() - media.fetchDate.getTime() > media.ttl * 1000) {
@@ -146,6 +168,9 @@ export const makeMessagesSocket = (config) => {
146
168
  fetchDate: new Date()
147
169
  };
148
170
  logger.debug('fetched media conn');
171
+ if (node.hosts[0]) {
172
+ mediaHost = node.hosts[0].hostname;
173
+ }
149
174
  return node;
150
175
  })();
151
176
  }
@@ -340,18 +365,20 @@ export const makeMessagesSocket = (config) => {
340
365
  }, 'Processed device with LID priority');
341
366
  }
342
367
  }
343
- if (userDevicesCache.mset) {
344
- // if the cache supports mset, we can set all devices in one go
345
- await userDevicesCache.mset(Object.entries(deviceMap).map(([key, value]) => ({
346
- key,
347
- value
348
- })));
349
- } else {
350
- for (const key in deviceMap) {
351
- if (deviceMap[key])
352
- await userDevicesCache.set(key, deviceMap[key]);
368
+ await devicesMutex.mutex(async () => {
369
+ if (userDevicesCache.mset) {
370
+ // if the cache supports mset, we can set all devices in one go
371
+ await userDevicesCache.mset(Object.entries(deviceMap).map(([key, value]) => ({
372
+ key,
373
+ value
374
+ })));
375
+ } else {
376
+ for (const key in deviceMap) {
377
+ if (deviceMap[key])
378
+ await userDevicesCache.set(key, deviceMap[key]);
379
+ }
353
380
  }
354
- }
381
+ });
355
382
  const userDeviceUpdates = {};
356
383
  for (const [userId, devices] of Object.entries(deviceMap)) {
357
384
  if (devices && devices.length > 0) {
@@ -400,24 +427,15 @@ export const makeMessagesSocket = (config) => {
400
427
  };
401
428
  const assertSessions = async (jids, force) => {
402
429
  let didFetchNewSession = false;
403
- const uniqueJids = [...new Set(jids)]; // Deduplicate JIDs
430
+ const uniqueJids = [...new Set(jids)];
404
431
  const jidsRequiringFetch = [];
405
432
  logger.debug({
406
433
  jids
407
434
  }, 'assertSessions call with jids');
408
- // Check peerSessionsCache and validate sessions using libsignal loadSession
409
435
  for (const jid of uniqueJids) {
410
- const signalId = signalRepository.jidToSignalProtocolAddress(jid);
411
- const cachedSession = peerSessionsCache.get(signalId);
412
- if (cachedSession !== undefined) {
413
- if (cachedSession && !force) {
414
- continue; // Session exists in cache
415
- }
416
- } else {
436
+ if (!force) {
417
437
  const sessionValidation = await signalRepository.validateSession(jid);
418
- const hasSession = sessionValidation.exists;
419
- peerSessionsCache.set(signalId, hasSession);
420
- if (hasSession && !force) {
438
+ if (sessionValidation.exists) {
421
439
  continue;
422
440
  }
423
441
  }
@@ -458,11 +476,6 @@ export const makeMessagesSocket = (config) => {
458
476
  });
459
477
  await parseAndInjectE2ESessions(result, signalRepository);
460
478
  didFetchNewSession = true;
461
- // Cache fetched sessions using wire JIDs
462
- for (const wireJid of wireJids) {
463
- const signalId = signalRepository.jidToSignalProtocolAddress(wireJid);
464
- peerSessionsCache.set(signalId, true);
465
- }
466
479
  }
467
480
  return didFetchNewSession;
468
481
  };
@@ -594,7 +607,7 @@ export const makeMessagesSocket = (config) => {
594
607
  useCachedGroupMetadata,
595
608
  statusJidList
596
609
  }) => {
597
- const meId = authState.creds.me.id;
610
+ const meId = assertMeId(authState.creds);
598
611
  const meLid = authState.creds.me?.lid;
599
612
  const isRetryResend = Boolean(participant?.jid);
600
613
  let shouldIncludeDeviceIdentity = isRetryResend;
@@ -889,14 +902,49 @@ export const makeMessagesSocket = (config) => {
889
902
  if (isRetryResend) {
890
903
  const isParticipantLid = isLidUser(participant.jid);
891
904
  const isMe = areJidsSameUser(participant.jid, isParticipantLid ? meLid : meId);
905
+ let messageToSend = message;
906
+ if (isGroupOrStatus) {
907
+ let groupSenderIdentity;
908
+ if (meLid && (await signalRepository.hasSenderKey({
909
+ group: destinationJid,
910
+ meId: meLid
911
+ }))) {
912
+ groupSenderIdentity = meLid;
913
+ } else if (await signalRepository.hasSenderKey({
914
+ group: destinationJid,
915
+ meId
916
+ })) {
917
+ groupSenderIdentity = meId;
918
+ }
919
+ if (groupSenderIdentity) {
920
+ try {
921
+ const skdm = await signalRepository.getSenderKeyDistributionMessage({
922
+ group: destinationJid,
923
+ meId: groupSenderIdentity
924
+ });
925
+ messageToSend = {
926
+ ...message,
927
+ senderKeyDistributionMessage: {
928
+ groupId: destinationJid,
929
+ axolotlSenderKeyDistributionMessage: skdm
930
+ }
931
+ };
932
+ } catch (err) {
933
+ logger.warn({
934
+ err,
935
+ jid: destinationJid
936
+ }, 'failed to build SKDM for retry, sending without it');
937
+ }
938
+ }
939
+ }
892
940
  const encodedMessageToSend = isMe ?
893
941
  encodeWAMessage({
894
942
  deviceSentMessage: {
895
943
  destinationJid,
896
- message
944
+ message: messageToSend
897
945
  }
898
946
  }) :
899
- encodeWAMessage(message);
947
+ encodeWAMessage(messageToSend);
900
948
  const {
901
949
  type,
902
950
  ciphertext: encryptedContent
@@ -992,9 +1040,41 @@ export const makeMessagesSocket = (config) => {
992
1040
  }, 'failed to attach reporting token');
993
1041
  }
994
1042
  }
995
- const contactTcTokenData = !isGroup && !isRetryResend && !isStatus ? await authState.keys.get('tctoken', [destinationJid]) : {};
996
- const tcTokenBuffer = contactTcTokenData[destinationJid]?.token;
997
- if (tcTokenBuffer) {
1043
+ // WA Web never attaches tctoken to peer (AppStateSync) messages server rejects with 479
1044
+ const isPeerMessage = additionalAttributes?.['category'] === 'peer';
1045
+ const is1on1Send = !isGroup && !isRetryResend && !isStatus && !isNewsletter && !isPeerMessage;
1046
+ // Resolve destination to LID for tctoken storage — matches Signal session key pattern
1047
+ const tcTokenJid = is1on1Send ? await resolveTcTokenJid(destinationJid, getLIDForPN) : destinationJid;
1048
+ const contactTcTokenData = is1on1Send ? await authState.keys.get('tctoken', [tcTokenJid]) : {};
1049
+ const existingTokenEntry = contactTcTokenData[tcTokenJid];
1050
+ let tcTokenBuffer = existingTokenEntry?.token;
1051
+ // Treat expired tokens the same as missing — clear from cache
1052
+ if (tcTokenBuffer?.length && isTcTokenExpired(existingTokenEntry?.timestamp)) {
1053
+ logger.debug({
1054
+ jid: destinationJid,
1055
+ timestamp: existingTokenEntry?.timestamp
1056
+ }, 'tctoken expired, clearing');
1057
+ tcTokenBuffer = undefined;
1058
+ // Preserve senderTimestamp so the fire-and-forget issuance dedupe survives cleanup.
1059
+ const cleared = existingTokenEntry?.senderTimestamp !== undefined ? {
1060
+ token: Buffer.alloc(0),
1061
+ senderTimestamp: existingTokenEntry.senderTimestamp
1062
+ } :
1063
+ null;
1064
+ try {
1065
+ await authState.keys.set({
1066
+ tctoken: {
1067
+ [tcTokenJid]: cleared
1068
+ }
1069
+ });
1070
+ } catch (err) {
1071
+ logger.debug({
1072
+ jid: destinationJid,
1073
+ err: err?.message
1074
+ }, 'failed to persist tctoken expiry cleanup');
1075
+ }
1076
+ }
1077
+ if (tcTokenBuffer?.length && sock.serverProps.privacyTokenOn1to1) {
998
1078
  ;
999
1079
  stanza.content.push({
1000
1080
  tag: 'tctoken',
@@ -1010,6 +1090,51 @@ export const makeMessagesSocket = (config) => {
1010
1090
  msgId
1011
1091
  }, `sending message to ${participants.length} devices`);
1012
1092
  await sendNode(stanza);
1093
+ // Fire-and-forget: issue our token to the contact AFTER message send.
1094
+ // WA Web skips protocol messages and PSA/bot contacts (TcTokenChatAction: isRegularUser)
1095
+ const isProtocolMsg = !!normalizeMessageContent(message)?.protocolMessage;
1096
+ const isBotOrPSA = destinationJid === PSA_WID || isJidBot(destinationJid) || isJidMetaAI(destinationJid);
1097
+ if (is1on1Send &&
1098
+ !isProtocolMsg &&
1099
+ !isBotOrPSA &&
1100
+ shouldSendNewTcToken(existingTokenEntry?.senderTimestamp) &&
1101
+ !inFlightTcTokenIssuance.has(tcTokenJid)) {
1102
+ inFlightTcTokenIssuance.add(tcTokenJid);
1103
+ const issueTimestamp = unixTimestampSeconds();
1104
+ const getPNForLID = signalRepository.lidMapping.getPNForLID.bind(signalRepository.lidMapping);
1105
+ resolveIssuanceJid(destinationJid, sock.serverProps.lidTrustedTokenIssueToLid, getLIDForPN, getPNForLID)
1106
+ .then(issueJid => issuePrivacyTokens([issueJid], issueTimestamp))
1107
+ .then(async (result) => {
1108
+ await storeTcTokensFromIqResult({
1109
+ result,
1110
+ fallbackJid: tcTokenJid,
1111
+ keys: authState.keys,
1112
+ getLIDForPN
1113
+ });
1114
+ const currentData = await authState.keys.get('tctoken', [tcTokenJid]);
1115
+ const currentEntry = currentData[tcTokenJid];
1116
+ const indexWrite = await buildMergedTcTokenIndexWrite(authState.keys, [tcTokenJid]);
1117
+ await authState.keys.set({
1118
+ tctoken: {
1119
+ [tcTokenJid]: {
1120
+ token: Buffer.alloc(0),
1121
+ ...currentEntry,
1122
+ senderTimestamp: issueTimestamp
1123
+ },
1124
+ ...indexWrite
1125
+ }
1126
+ });
1127
+ })
1128
+ .catch(err => {
1129
+ logger.debug({
1130
+ jid: destinationJid,
1131
+ err: err?.message
1132
+ }, 'fire-and-forget tctoken issuance failed');
1133
+ })
1134
+ .finally(() => {
1135
+ inFlightTcTokenIssuance.delete(tcTokenJid);
1136
+ });
1137
+ }
1013
1138
  // Add message to retry cache if enabled
1014
1139
  if (messageRetryManager && !participant) {
1015
1140
  messageRetryManager.addRecentMessage(destinationJid, msgId, message);
@@ -1072,8 +1197,8 @@ export const makeMessagesSocket = (config) => {
1072
1197
  }
1073
1198
  return '';
1074
1199
  };
1075
- const getPrivacyTokens = async (jids) => {
1076
- const t = unixTimestampSeconds().toString();
1200
+ const issuePrivacyTokens = async (jids, timestamp) => {
1201
+ const t = (timestamp ?? unixTimestampSeconds()).toString();
1077
1202
  const result = await query({
1078
1203
  tag: 'iq',
1079
1204
  attrs: {
@@ -1097,17 +1222,42 @@ export const makeMessagesSocket = (config) => {
1097
1222
  return result;
1098
1223
  };
1099
1224
  const waUploadToServer = getWAUploadToServer(config, refreshMediaConn);
1100
- const waitForMsgMediaUpdate = bindWaitForEvent(ev, 'messages.media-update');
1101
- sock.waUploadToServer = waUploadToServer;
1102
- sock.relayMessage = relayMessage;
1225
+ const waitForMsgMediaUpdate = bindWaitForEvent(ev, 'messages.media-update');
1226
+ Object.defineProperties(sock, {
1227
+ waUploadToServer: {
1228
+ value: waUploadToServer,
1229
+ writable: false,
1230
+ enumerable: false,
1231
+ configurable: true
1232
+ },
1233
+ relayMessage: {
1234
+ value: relayMessage,
1235
+ writable: false,
1236
+ enumerable: false,
1237
+ configurable: true
1238
+ }
1239
+ });
1240
+ registerSocketEndHandler(() => {
1241
+ if (!config.userDevicesCache && userDevicesCache.close) {
1242
+ userDevicesCache.close();
1243
+ }
1244
+ mediaConn = undefined;
1245
+ if (messageRetryManager) {
1246
+ messageRetryManager.clear();
1247
+ }
1248
+ });
1103
1249
  return {
1104
1250
  ...sock,
1105
- getPrivacyTokens,
1251
+ userDevicesCache,
1252
+ devicesMutex,
1253
+ issuePrivacyTokens,
1106
1254
  assertSessions,
1107
1255
  sendReceipt,
1108
1256
  sendReceipts,
1109
1257
  readMessages,
1110
1258
  refreshMediaConn,
1259
+ // Function (not getter) so the spread in chats.ts preserves the live closure binding.
1260
+ getMediaHost: () => mediaHost,
1111
1261
  fetchPrivacySettings,
1112
1262
  sendPeerDataOperationMessage,
1113
1263
  createParticipantNodes,
@@ -1138,7 +1288,7 @@ export const makeMessagesSocket = (config) => {
1138
1288
  });
1139
1289
  }
1140
1290
  content.directPath = media.directPath;
1141
- content.url = getUrlFromDirectPath(content.directPath);
1291
+ content.url = getUrlFromDirectPath(content.directPath, mediaHost);
1142
1292
  logger.debug({
1143
1293
  directPath: media.directPath,
1144
1294
  key: result.key
@@ -1163,92 +1313,92 @@ export const makeMessagesSocket = (config) => {
1163
1313
  return message;
1164
1314
  },
1165
1315
  sendMessage: async (jid, content, options = {}) => {
1166
- if (Array.isArray(content.buttons)) {
1167
- return await sock.sendButton(jid, content, options);
1168
- } else {
1169
- const userJid = authState.creds.me.id;
1170
- if (typeof content === 'object' &&
1171
- 'disappearingMessagesInChat' in content &&
1172
- typeof content['disappearingMessagesInChat'] !== 'undefined' &&
1173
- isJidGroup(jid)) {
1174
- const {
1175
- disappearingMessagesInChat
1176
- } = content;
1177
- const value = typeof disappearingMessagesInChat === 'boolean' ?
1178
- disappearingMessagesInChat ?
1179
- WA_DEFAULT_EPHEMERAL :
1180
- 0 :
1181
- disappearingMessagesInChat;
1182
- await groupToggleEphemeral(jid, value);
1316
+ if (Array.isArray(content.buttons)) {
1317
+ return await sock.sendButton(jid, content, options);
1183
1318
  } else {
1184
- const fullMsg = await generateWAMessage(jid, content, {
1185
- logger,
1186
- userJid,
1187
- getUrlInfo: text => getUrlInfo(text, {
1188
- thumbnailWidth: linkPreviewImageThumbnailWidth,
1189
- fetchOpts: {
1190
- timeout: 3000,
1191
- ...(httpRequestOptions || {})
1192
- },
1319
+ const userJid = authState.creds.me.id;
1320
+ if (typeof content === 'object' &&
1321
+ 'disappearingMessagesInChat' in content &&
1322
+ typeof content['disappearingMessagesInChat'] !== 'undefined' &&
1323
+ isJidGroup(jid)) {
1324
+ const {
1325
+ disappearingMessagesInChat
1326
+ } = content;
1327
+ const value = typeof disappearingMessagesInChat === 'boolean' ?
1328
+ disappearingMessagesInChat ?
1329
+ WA_DEFAULT_EPHEMERAL :
1330
+ 0 :
1331
+ disappearingMessagesInChat;
1332
+ await groupToggleEphemeral(jid, value);
1333
+ } else {
1334
+ const fullMsg = await generateWAMessage(jid, content, {
1193
1335
  logger,
1194
- uploadImage: generateHighQualityLinkPreview ? waUploadToServer : undefined
1195
- }),
1196
- //TODO: CACHE
1197
- getProfilePicUrl: sock.profilePictureUrl,
1198
- getCallLink: sock.createCallLink,
1199
- upload: waUploadToServer,
1200
- mediaCache: config.mediaCache,
1201
- options: config.options,
1202
- messageId: generateMessageIDV2(sock.user?.id),
1203
- ...options
1204
- });
1205
- const isEventMsg = 'event' in content && !!content.event;
1206
- const isDeleteMsg = 'delete' in content && !!content.delete;
1207
- const isEditMsg = 'edit' in content && !!content.edit;
1208
- const isPinMsg = 'pin' in content && !!content.pin;
1209
- const isPollMessage = 'poll' in content && !!content.poll;
1210
- const additionalAttributes = {};
1211
- const additionalNodes = [];
1212
- // required for delete
1213
- if (isDeleteMsg) {
1214
- // if the chat is a group, and I am not the author, then delete the message as an admin
1215
- if (isJidGroup(content.delete?.remoteJid) && !content.delete?.fromMe) {
1216
- additionalAttributes.edit = '8';
1217
- } else {
1218
- additionalAttributes.edit = '7';
1219
- }
1220
- } else if (isEditMsg) {
1221
- additionalAttributes.edit = '1';
1222
- } else if (isPinMsg) {
1223
- additionalAttributes.edit = '2';
1224
- } else if (isPollMessage) {
1225
- additionalNodes.push({
1226
- tag: 'meta',
1227
- attrs: {
1228
- polltype: 'creation'
1229
- }
1336
+ userJid,
1337
+ getUrlInfo: text => getUrlInfo(text, {
1338
+ thumbnailWidth: linkPreviewImageThumbnailWidth,
1339
+ fetchOpts: {
1340
+ timeout: 3000,
1341
+ ...(httpRequestOptions || {})
1342
+ },
1343
+ logger,
1344
+ uploadImage: generateHighQualityLinkPreview ? waUploadToServer : undefined
1345
+ }),
1346
+ //TODO: CACHE
1347
+ getProfilePicUrl: sock.profilePictureUrl,
1348
+ getCallLink: sock.createCallLink,
1349
+ upload: waUploadToServer,
1350
+ mediaCache: config.mediaCache,
1351
+ options: config.options,
1352
+ messageId: generateMessageIDV2(sock.user?.id),
1353
+ ...options
1230
1354
  });
1231
- } else if (isEventMsg) {
1232
- additionalNodes.push({
1233
- tag: 'meta',
1234
- attrs: {
1235
- event_type: 'creation'
1355
+ const isEventMsg = 'event' in content && !!content.event;
1356
+ const isDeleteMsg = 'delete' in content && !!content.delete;
1357
+ const isEditMsg = 'edit' in content && !!content.edit;
1358
+ const isPinMsg = 'pin' in content && !!content.pin;
1359
+ const isPollMessage = 'poll' in content && !!content.poll;
1360
+ const additionalAttributes = {};
1361
+ const additionalNodes = [];
1362
+ // required for delete
1363
+ if (isDeleteMsg) {
1364
+ // if the chat is a group, and I am not the author, then delete the message as an admin
1365
+ if (isJidGroup(content.delete?.remoteJid) && !content.delete?.fromMe) {
1366
+ additionalAttributes.edit = '8';
1367
+ } else {
1368
+ additionalAttributes.edit = '7';
1236
1369
  }
1370
+ } else if (isEditMsg) {
1371
+ additionalAttributes.edit = '1';
1372
+ } else if (isPinMsg) {
1373
+ additionalAttributes.edit = '2';
1374
+ } else if (isPollMessage) {
1375
+ additionalNodes.push({
1376
+ tag: 'meta',
1377
+ attrs: {
1378
+ polltype: 'creation'
1379
+ }
1380
+ });
1381
+ } else if (isEventMsg) {
1382
+ additionalNodes.push({
1383
+ tag: 'meta',
1384
+ attrs: {
1385
+ event_type: 'creation'
1386
+ }
1387
+ });
1388
+ }
1389
+ await relayMessage(jid, fullMsg.message, {
1390
+ messageId: fullMsg.key.id,
1391
+ useCachedGroupMetadata: options.useCachedGroupMetadata,
1392
+ additionalAttributes,
1393
+ statusJidList: options.statusJidList,
1394
+ additionalNodes
1237
1395
  });
1238
- }
1239
- await relayMessage(jid, fullMsg.message, {
1240
- messageId: fullMsg.key.id,
1241
- useCachedGroupMetadata: options.useCachedGroupMetadata,
1242
- additionalAttributes,
1243
- statusJidList: options.statusJidList,
1244
- additionalNodes
1245
- });
1246
- if (config.emitOwnEvents) {
1247
- process.nextTick(async () => {
1248
- await messageMutex.mutex(() => upsertMessage(fullMsg, 'append'));
1249
- });
1250
- }
1251
- return fullMsg;
1396
+ if (config.emitOwnEvents) {
1397
+ process.nextTick(async () => {
1398
+ await messageMutex.mutex(() => upsertMessage(fullMsg, 'append'));
1399
+ });
1400
+ }
1401
+ return fullMsg;
1252
1402
  }
1253
1403
  }
1254
1404
  }
@@ -79,10 +79,10 @@ export const makeNewsletterSocket = (config) => {
79
79
  return parseNewsletterMetadata(result);
80
80
  },
81
81
  newsletterFollow: (jid) => {
82
- return executeWMexQuery({ newsletter_id: jid }, QueryIds.FOLLOW, XWAPaths.xwa2_newsletter_follow);
82
+ return executeWMexQuery({ newsletter_id: jid }, QueryIds.FOLLOW, XWAPaths.xwa2_newsletter_join_v2);
83
83
  },
84
84
  newsletterUnfollow: (jid) => {
85
- return executeWMexQuery({ newsletter_id: jid }, QueryIds.UNFOLLOW, XWAPaths.xwa2_newsletter_unfollow);
85
+ return executeWMexQuery({ newsletter_id: jid }, QueryIds.UNFOLLOW, XWAPaths.xwa2_newsletter_leave_v2);
86
86
  },
87
87
  newsletterMute: (jid) => {
88
88
  return executeWMexQuery({ newsletter_id: jid }, QueryIds.MUTE, XWAPaths.xwa2_newsletter_mute_v2);