@periskope/baileys 6.7.19 → 7.0.0-alpha-1

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 (139) hide show
  1. package/README.md +6 -0
  2. package/WAProto/GenerateStatics.sh +2 -2
  3. package/WAProto/index.d.ts +2793 -45578
  4. package/WAProto/index.js +19344 -147065
  5. package/lib/Defaults/index.d.ts +7 -1
  6. package/lib/Defaults/index.d.ts.map +1 -1
  7. package/lib/Defaults/index.js +11 -4
  8. package/lib/Defaults/index.js.map +1 -1
  9. package/lib/Signal/Group/group_cipher.d.ts +0 -1
  10. package/lib/Signal/Group/group_cipher.d.ts.map +1 -1
  11. package/lib/Signal/Group/group_cipher.js +28 -36
  12. package/lib/Signal/Group/group_cipher.js.map +1 -1
  13. package/lib/Signal/Group/sender-key-distribution-message.d.ts.map +1 -1
  14. package/lib/Signal/Group/sender-key-distribution-message.js +1 -1
  15. package/lib/Signal/Group/sender-key-distribution-message.js.map +1 -1
  16. package/lib/Signal/Group/sender-key-message.d.ts.map +1 -1
  17. package/lib/Signal/Group/sender-key-message.js +1 -1
  18. package/lib/Signal/Group/sender-key-message.js.map +1 -1
  19. package/lib/Signal/Group/sender-key-state.d.ts.map +1 -1
  20. package/lib/Signal/Group/sender-key-state.js +6 -1
  21. package/lib/Signal/Group/sender-key-state.js.map +1 -1
  22. package/lib/Signal/libsignal.d.ts +5 -1
  23. package/lib/Signal/libsignal.d.ts.map +1 -1
  24. package/lib/Signal/libsignal.js +211 -38
  25. package/lib/Signal/libsignal.js.map +1 -1
  26. package/lib/Signal/lid-mapping.d.ts +14 -3
  27. package/lib/Signal/lid-mapping.d.ts.map +1 -0
  28. package/lib/Signal/lid-mapping.js +53 -65
  29. package/lib/Signal/lid-mapping.js.map +1 -0
  30. package/lib/Socket/business.d.ts +25 -13
  31. package/lib/Socket/business.d.ts.map +1 -1
  32. package/lib/Socket/business.js +122 -1
  33. package/lib/Socket/business.js.map +1 -1
  34. package/lib/Socket/chats.d.ts +14 -8
  35. package/lib/Socket/chats.d.ts.map +1 -1
  36. package/lib/Socket/chats.js +54 -21
  37. package/lib/Socket/chats.js.map +1 -1
  38. package/lib/Socket/communities.d.ts +24 -10
  39. package/lib/Socket/communities.d.ts.map +1 -1
  40. package/lib/Socket/communities.js +45 -1
  41. package/lib/Socket/communities.js.map +1 -1
  42. package/lib/Socket/groups.d.ts +13 -8
  43. package/lib/Socket/groups.d.ts.map +1 -1
  44. package/lib/Socket/groups.js +12 -11
  45. package/lib/Socket/groups.js.map +1 -1
  46. package/lib/Socket/index.d.ts +24 -10
  47. package/lib/Socket/index.d.ts.map +1 -1
  48. package/lib/Socket/messages-recv.d.ts +18 -10
  49. package/lib/Socket/messages-recv.d.ts.map +1 -1
  50. package/lib/Socket/messages-recv.js +383 -220
  51. package/lib/Socket/messages-recv.js.map +1 -1
  52. package/lib/Socket/messages-send.d.ts +19 -10
  53. package/lib/Socket/messages-send.d.ts.map +1 -1
  54. package/lib/Socket/messages-send.js +454 -62
  55. package/lib/Socket/messages-send.js.map +1 -1
  56. package/lib/Socket/newsletter.d.ts +15 -11
  57. package/lib/Socket/newsletter.d.ts.map +1 -1
  58. package/lib/Socket/newsletter.js +3 -1
  59. package/lib/Socket/newsletter.js.map +1 -1
  60. package/lib/Socket/socket.d.ts +9 -2
  61. package/lib/Socket/socket.d.ts.map +1 -1
  62. package/lib/Socket/socket.js +266 -70
  63. package/lib/Socket/socket.js.map +1 -1
  64. package/lib/Types/Auth.d.ts +2 -1
  65. package/lib/Types/Auth.d.ts.map +1 -1
  66. package/lib/Types/Bussines.d.ts +25 -0
  67. package/lib/Types/Bussines.d.ts.map +1 -0
  68. package/lib/Types/Bussines.js +2 -0
  69. package/lib/Types/Bussines.js.map +1 -0
  70. package/lib/Types/Chat.d.ts +5 -0
  71. package/lib/Types/Chat.d.ts.map +1 -1
  72. package/lib/Types/Chat.js.map +1 -1
  73. package/lib/Types/Contact.d.ts +4 -4
  74. package/lib/Types/Contact.d.ts.map +1 -1
  75. package/lib/Types/Events.d.ts +2 -2
  76. package/lib/Types/Events.d.ts.map +1 -1
  77. package/lib/Types/GroupMetadata.d.ts +6 -4
  78. package/lib/Types/GroupMetadata.d.ts.map +1 -1
  79. package/lib/Types/Message.d.ts +28 -7
  80. package/lib/Types/Message.d.ts.map +1 -1
  81. package/lib/Types/Message.js +5 -1
  82. package/lib/Types/Message.js.map +1 -1
  83. package/lib/Types/Signal.d.ts +24 -0
  84. package/lib/Types/Signal.d.ts.map +1 -1
  85. package/lib/Types/Socket.d.ts +9 -1
  86. package/lib/Types/Socket.d.ts.map +1 -1
  87. package/lib/Utils/auth-utils.d.ts.map +1 -1
  88. package/lib/Utils/auth-utils.js +362 -78
  89. package/lib/Utils/auth-utils.js.map +1 -1
  90. package/lib/Utils/chat-utils.d.ts +2 -2
  91. package/lib/Utils/chat-utils.d.ts.map +1 -1
  92. package/lib/Utils/chat-utils.js +20 -3
  93. package/lib/Utils/chat-utils.js.map +1 -1
  94. package/lib/Utils/decode-wa-message.d.ts +10 -0
  95. package/lib/Utils/decode-wa-message.d.ts.map +1 -1
  96. package/lib/Utils/decode-wa-message.js +84 -13
  97. package/lib/Utils/decode-wa-message.js.map +1 -1
  98. package/lib/Utils/event-buffer.d.ts +0 -1
  99. package/lib/Utils/event-buffer.d.ts.map +1 -1
  100. package/lib/Utils/event-buffer.js +48 -4
  101. package/lib/Utils/event-buffer.js.map +1 -1
  102. package/lib/Utils/generics.d.ts.map +1 -1
  103. package/lib/Utils/generics.js +17 -8
  104. package/lib/Utils/generics.js.map +1 -1
  105. package/lib/Utils/history.d.ts.map +1 -1
  106. package/lib/Utils/history.js +1 -2
  107. package/lib/Utils/history.js.map +1 -1
  108. package/lib/Utils/index.d.ts +1 -0
  109. package/lib/Utils/index.d.ts.map +1 -1
  110. package/lib/Utils/index.js +1 -0
  111. package/lib/Utils/index.js.map +1 -1
  112. package/lib/Utils/message-retry-manager.d.ts +82 -0
  113. package/lib/Utils/message-retry-manager.d.ts.map +1 -0
  114. package/lib/Utils/message-retry-manager.js +149 -0
  115. package/lib/Utils/message-retry-manager.js.map +1 -0
  116. package/lib/Utils/messages-media.d.ts +3 -2
  117. package/lib/Utils/messages-media.d.ts.map +1 -1
  118. package/lib/Utils/messages-media.js +4 -1
  119. package/lib/Utils/messages-media.js.map +1 -1
  120. package/lib/Utils/messages.d.ts.map +1 -1
  121. package/lib/Utils/messages.js +35 -15
  122. package/lib/Utils/messages.js.map +1 -1
  123. package/lib/Utils/process-message.d.ts +3 -2
  124. package/lib/Utils/process-message.d.ts.map +1 -1
  125. package/lib/Utils/process-message.js +13 -2
  126. package/lib/Utils/process-message.js.map +1 -1
  127. package/lib/Utils/use-multi-file-auth-state.js +1 -1
  128. package/lib/Utils/use-multi-file-auth-state.js.map +1 -1
  129. package/lib/Utils/validate-connection.d.ts.map +1 -1
  130. package/lib/Utils/validate-connection.js +5 -4
  131. package/lib/Utils/validate-connection.js.map +1 -1
  132. package/lib/WABinary/jid-utils.d.ts +6 -5
  133. package/lib/WABinary/jid-utils.d.ts.map +1 -1
  134. package/lib/WABinary/jid-utils.js +11 -5
  135. package/lib/WABinary/jid-utils.js.map +1 -1
  136. package/lib/WAM/encode.d.ts.map +1 -1
  137. package/lib/WAM/encode.js +0 -1
  138. package/lib/WAM/encode.js.map +1 -1
  139. package/package.json +12 -8
@@ -7,13 +7,13 @@ import { DEFAULT_CACHE_TTLS, KEY_BUNDLE_TYPE, MIN_PREKEY_COUNT } from '../Defaul
7
7
  import { WAMessageStatus, WAMessageStubType } from '../Types/index.js';
8
8
  import { aesDecryptCTR, aesEncryptGCM, cleanMessage, Curve, decodeMediaRetryNode, decodeMessageNode, decryptMessageNode, delay, derivePairingCodeKey, encodeBigEndian, encodeSignedDeviceIdentity, getCallStatusFromNode, getHistoryMsg, getNextPreKeys, getStatusFromReceiptType, hkdf, MISSING_KEYS_ERROR_TEXT, NACK_REASONS, NO_MESSAGE_FOUND_ERROR_TEXT, unixTimestampSeconds, xmppPreKey, xmppSignedPreKey } from '../Utils/index.js';
9
9
  import { makeMutex } from '../Utils/make-mutex.js';
10
- import { areJidsSameUser, getAllBinaryNodeChildren, getBinaryNodeChild, getBinaryNodeChildBuffer, getBinaryNodeChildren, getBinaryNodeChildString, isJidGroup, isJidStatusBroadcast, isJidUser, isLidUser, jidDecode, jidNormalizedUser, S_WHATSAPP_NET } from '../WABinary/index.js';
10
+ import { areJidsSameUser, getAllBinaryNodeChildren, getBinaryNodeChild, getBinaryNodeChildBuffer, getBinaryNodeChildren, getBinaryNodeChildString, isJidGroup, isJidStatusBroadcast, isLidUser, jidDecode, jidNormalizedUser, S_WHATSAPP_NET } from '../WABinary/index.js';
11
11
  import { extractGroupMetadata } from './groups.js';
12
12
  import { makeMessagesSocket } from './messages-send.js';
13
13
  export const makeMessagesRecvSocket = (config) => {
14
- const { logger, retryRequestDelayMs, maxMsgRetryCount, getMessage, shouldIgnoreJid } = config;
14
+ const { logger, retryRequestDelayMs, maxMsgRetryCount, getMessage, shouldIgnoreJid, enableAutoSessionRecreation } = config;
15
15
  const sock = makeMessagesSocket(config);
16
- const { ev, authState, ws, processingMutex, signalRepository, query, upsertMessage, resyncAppState, onUnexpectedError, assertSessions, sendNode, relayMessage, sendReceipt, uploadPreKeys, sendPeerDataOperationMessage } = sock;
16
+ const { ev, authState, ws, processingMutex, signalRepository, query, upsertMessage, resyncAppState, onUnexpectedError, assertSessions, sendNode, relayMessage, sendReceipt, uploadPreKeys, sendPeerDataOperationMessage, messageRetryManager } = sock;
17
17
  /** this mutex ensures that each retryRequest will wait for the previous one to finish */
18
18
  const retryMutex = makeMutex();
19
19
  const msgRetryCache = config.msgRetryCounterCache ||
@@ -32,6 +32,187 @@ export const makeMessagesRecvSocket = (config) => {
32
32
  useClones: false
33
33
  });
34
34
  let sendActiveReceipts = false;
35
+ const fetchMessageHistory = async (count, oldestMsgKey, oldestMsgTimestamp) => {
36
+ if (!authState.creds.me?.id) {
37
+ throw new Boom('Not authenticated');
38
+ }
39
+ const pdoMessage = {
40
+ historySyncOnDemandRequest: {
41
+ chatJid: oldestMsgKey.remoteJid,
42
+ oldestMsgFromMe: oldestMsgKey.fromMe,
43
+ oldestMsgId: oldestMsgKey.id,
44
+ oldestMsgTimestampMs: oldestMsgTimestamp,
45
+ onDemandMsgCount: count
46
+ },
47
+ peerDataOperationRequestType: proto.Message.PeerDataOperationRequestType.HISTORY_SYNC_ON_DEMAND
48
+ };
49
+ return sendPeerDataOperationMessage(pdoMessage);
50
+ };
51
+ const requestPlaceholderResend = async (messageKey) => {
52
+ if (!authState.creds.me?.id) {
53
+ throw new Boom('Not authenticated');
54
+ }
55
+ if (placeholderResendCache.get(messageKey?.id)) {
56
+ logger.debug({ messageKey }, 'already requested resend');
57
+ return;
58
+ }
59
+ else {
60
+ placeholderResendCache.set(messageKey?.id, true);
61
+ }
62
+ await delay(5000);
63
+ if (!placeholderResendCache.get(messageKey?.id)) {
64
+ logger.debug({ messageKey }, 'message received while resend requested');
65
+ return 'RESOLVED';
66
+ }
67
+ const pdoMessage = {
68
+ placeholderMessageResendRequest: [
69
+ {
70
+ messageKey
71
+ }
72
+ ],
73
+ peerDataOperationRequestType: proto.Message.PeerDataOperationRequestType.PLACEHOLDER_MESSAGE_RESEND
74
+ };
75
+ setTimeout(() => {
76
+ if (placeholderResendCache.get(messageKey?.id)) {
77
+ logger.debug({ messageKey }, 'PDO message without response after 15 seconds. Phone possibly offline');
78
+ placeholderResendCache.del(messageKey?.id);
79
+ }
80
+ }, 15000);
81
+ return sendPeerDataOperationMessage(pdoMessage);
82
+ };
83
+ // Handles mex newsletter notifications
84
+ const handleMexNewsletterNotification = async (node) => {
85
+ const mexNode = getBinaryNodeChild(node, 'mex');
86
+ if (!mexNode?.content) {
87
+ logger.warn({ node }, 'Invalid mex newsletter notification');
88
+ return;
89
+ }
90
+ let data;
91
+ try {
92
+ data = JSON.parse(mexNode.content.toString());
93
+ }
94
+ catch (error) {
95
+ logger.error({ err: error, node }, 'Failed to parse mex newsletter notification');
96
+ return;
97
+ }
98
+ const operation = data?.operation;
99
+ const updates = data?.updates;
100
+ if (!updates || !operation) {
101
+ logger.warn({ data }, 'Invalid mex newsletter notification content');
102
+ return;
103
+ }
104
+ logger.info({ operation, updates }, 'got mex newsletter notification');
105
+ switch (operation) {
106
+ case 'NotificationNewsletterUpdate':
107
+ for (const update of updates) {
108
+ if (update.jid && update.settings && Object.keys(update.settings).length > 0) {
109
+ ev.emit('newsletter-settings.update', {
110
+ id: update.jid,
111
+ update: update.settings
112
+ });
113
+ }
114
+ }
115
+ break;
116
+ case 'NotificationNewsletterAdminPromote':
117
+ for (const update of updates) {
118
+ if (update.jid && update.user) {
119
+ ev.emit('newsletter-participants.update', {
120
+ id: update.jid,
121
+ author: node.attrs.from,
122
+ user: update.user,
123
+ new_role: 'ADMIN',
124
+ action: 'promote'
125
+ });
126
+ }
127
+ }
128
+ break;
129
+ default:
130
+ logger.info({ operation, data }, 'Unhandled mex newsletter notification');
131
+ break;
132
+ }
133
+ };
134
+ // Handles newsletter notifications
135
+ const handleNewsletterNotification = async (node) => {
136
+ const from = node.attrs.from;
137
+ const child = getAllBinaryNodeChildren(node)[0];
138
+ const author = node.attrs.participant;
139
+ logger.info({ from, child }, 'got newsletter notification');
140
+ switch (child.tag) {
141
+ case 'reaction':
142
+ const reactionUpdate = {
143
+ id: from,
144
+ server_id: child.attrs.message_id,
145
+ reaction: {
146
+ code: getBinaryNodeChildString(child, 'reaction'),
147
+ count: 1
148
+ }
149
+ };
150
+ ev.emit('newsletter.reaction', reactionUpdate);
151
+ break;
152
+ case 'view':
153
+ const viewUpdate = {
154
+ id: from,
155
+ server_id: child.attrs.message_id,
156
+ count: parseInt(child.content?.toString() || '0', 10)
157
+ };
158
+ ev.emit('newsletter.view', viewUpdate);
159
+ break;
160
+ case 'participant':
161
+ const participantUpdate = {
162
+ id: from,
163
+ author,
164
+ user: child.attrs.jid,
165
+ action: child.attrs.action,
166
+ new_role: child.attrs.role
167
+ };
168
+ ev.emit('newsletter-participants.update', participantUpdate);
169
+ break;
170
+ case 'update':
171
+ const settingsNode = getBinaryNodeChild(child, 'settings');
172
+ if (settingsNode) {
173
+ const update = {};
174
+ const nameNode = getBinaryNodeChild(settingsNode, 'name');
175
+ if (nameNode?.content)
176
+ update.name = nameNode.content.toString();
177
+ const descriptionNode = getBinaryNodeChild(settingsNode, 'description');
178
+ if (descriptionNode?.content)
179
+ update.description = descriptionNode.content.toString();
180
+ ev.emit('newsletter-settings.update', {
181
+ id: from,
182
+ update
183
+ });
184
+ }
185
+ break;
186
+ case 'message':
187
+ const plaintextNode = getBinaryNodeChild(child, 'plaintext');
188
+ if (plaintextNode?.content) {
189
+ try {
190
+ const contentBuf = typeof plaintextNode.content === 'string'
191
+ ? Buffer.from(plaintextNode.content, 'binary')
192
+ : Buffer.from(plaintextNode.content);
193
+ const messageProto = proto.Message.decode(contentBuf);
194
+ const fullMessage = proto.WebMessageInfo.create({
195
+ key: {
196
+ remoteJid: from,
197
+ id: child.attrs.message_id || child.attrs.server_id,
198
+ fromMe: false
199
+ },
200
+ message: messageProto,
201
+ messageTimestamp: +child.attrs.t
202
+ });
203
+ await upsertMessage(fullMessage, 'append');
204
+ logger.info('Processed plaintext newsletter message');
205
+ }
206
+ catch (error) {
207
+ logger.error({ error }, 'Failed to decode plaintext newsletter message');
208
+ }
209
+ }
210
+ break;
211
+ default:
212
+ logger.warn({ node }, 'Unknown newsletter notification');
213
+ break;
214
+ }
215
+ };
35
216
  const sendMessageAck = async ({ tag, attrs, content }, errorCode) => {
36
217
  const stanza = {
37
218
  tag: 'ack',
@@ -107,20 +288,76 @@ export const makeMessagesRecvSocket = (config) => {
107
288
  const { fullMessage } = decodeMessageNode(node, authState.creds.me.id, authState.creds.me.lid || '');
108
289
  const { key: msgKey } = fullMessage;
109
290
  const msgId = msgKey.id;
110
- const key = `${msgId}:${msgKey?.participant}`;
111
- let retryCount = msgRetryCache.get(key) || 0;
112
- if (retryCount >= maxMsgRetryCount) {
113
- logger.debug({ retryCount, msgId }, 'reached retry limit, clearing');
114
- msgRetryCache.del(key);
115
- return;
291
+ if (messageRetryManager) {
292
+ // Check if we've exceeded max retries using the new system
293
+ if (messageRetryManager.hasExceededMaxRetries(msgId)) {
294
+ logger.debug({ msgId }, 'reached retry limit with new retry manager, clearing');
295
+ messageRetryManager.markRetryFailed(msgId);
296
+ return;
297
+ }
298
+ // Increment retry count using new system
299
+ const retryCount = messageRetryManager.incrementRetryCount(msgId);
300
+ // Use the new retry count for the rest of the logic
301
+ const key = `${msgId}:${msgKey?.participant}`;
302
+ msgRetryCache.set(key, retryCount);
116
303
  }
117
- retryCount += 1;
118
- msgRetryCache.set(key, retryCount);
304
+ else {
305
+ // Fallback to old system
306
+ const key = `${msgId}:${msgKey?.participant}`;
307
+ let retryCount = msgRetryCache.get(key) || 0;
308
+ if (retryCount >= maxMsgRetryCount) {
309
+ logger.debug({ retryCount, msgId }, 'reached retry limit, clearing');
310
+ msgRetryCache.del(key);
311
+ return;
312
+ }
313
+ retryCount += 1;
314
+ msgRetryCache.set(key, retryCount);
315
+ }
316
+ const key = `${msgId}:${msgKey?.participant}`;
317
+ const retryCount = msgRetryCache.get(key) || 1;
119
318
  const { account, signedPreKey, signedIdentityKey: identityKey } = authState.creds;
120
- if (retryCount === 1) {
121
- //request a resend via phone
122
- const msgId = await requestPlaceholderResend(msgKey);
123
- logger.debug(`sendRetryRequest: requested placeholder resend for message ${msgId}`);
319
+ const fromJid = node.attrs.from;
320
+ // Check if we should recreate the session
321
+ let shouldRecreateSession = false;
322
+ let recreateReason = '';
323
+ if (enableAutoSessionRecreation && messageRetryManager) {
324
+ try {
325
+ // Check if we have a session with this JID
326
+ const sessionId = signalRepository.jidToSignalProtocolAddress(fromJid);
327
+ const hasSession = await signalRepository.validateSession(fromJid);
328
+ const result = messageRetryManager.shouldRecreateSession(fromJid, retryCount, hasSession.exists);
329
+ shouldRecreateSession = result.recreate;
330
+ recreateReason = result.reason;
331
+ if (shouldRecreateSession) {
332
+ logger.info({ fromJid, retryCount, reason: recreateReason }, 'recreating session for retry');
333
+ // Delete existing session to force recreation
334
+ await authState.keys.set({ session: { [sessionId]: null } });
335
+ forceIncludeKeys = true;
336
+ }
337
+ }
338
+ catch (error) {
339
+ logger.warn({ error, fromJid }, 'failed to check session recreation');
340
+ }
341
+ }
342
+ if (retryCount <= 2) {
343
+ // Use new retry manager for phone requests if available
344
+ if (messageRetryManager) {
345
+ // Schedule phone request with delay (like whatsmeow)
346
+ messageRetryManager.schedulePhoneRequest(msgId, async () => {
347
+ try {
348
+ const msgId = await requestPlaceholderResend(msgKey);
349
+ logger.debug(`sendRetryRequest: requested placeholder resend for message ${msgId} (scheduled)`);
350
+ }
351
+ catch (error) {
352
+ logger.warn({ error, msgId }, 'failed to send scheduled phone request');
353
+ }
354
+ });
355
+ }
356
+ else {
357
+ // Fallback to immediate request
358
+ const msgId = await requestPlaceholderResend(msgKey);
359
+ logger.debug(`sendRetryRequest: requested placeholder resend for message ${msgId}`);
360
+ }
124
361
  }
125
362
  const deviceIdentity = encodeSignedDeviceIdentity(account, true);
126
363
  await authState.keys.transaction(async () => {
@@ -154,7 +391,7 @@ export const makeMessagesRecvSocket = (config) => {
154
391
  if (node.attrs.participant) {
155
392
  receipt.attrs.participant = node.attrs.participant;
156
393
  }
157
- if (retryCount > 1 || forceIncludeKeys) {
394
+ if (retryCount > 1 || forceIncludeKeys || shouldRecreateSession) {
158
395
  const { update, preKeys } = await getNextPreKeys(authState, 1);
159
396
  const [keyId] = Object.keys(preKeys);
160
397
  const key = preKeys[+keyId];
@@ -174,7 +411,7 @@ export const makeMessagesRecvSocket = (config) => {
174
411
  }
175
412
  await sendNode(receipt);
176
413
  logger.info({ msgAttrs: node.attrs, retryCount }, 'sent retry receipt');
177
- });
414
+ }, authState?.creds?.me?.id || 'sendRetryRequest');
178
415
  };
179
416
  const handleEncryptNotification = async (node) => {
180
417
  const from = node.attrs.from;
@@ -201,6 +438,7 @@ export const makeMessagesRecvSocket = (config) => {
201
438
  };
202
439
  const handleGroupNotification = (participant, child, msg) => {
203
440
  const participantJid = getBinaryNodeChild(child, 'participant')?.attrs?.jid || participant;
441
+ // TODO: Add participant LID
204
442
  switch (child?.tag) {
205
443
  case 'create':
206
444
  const metadata = extractGroupMetadata(child);
@@ -337,10 +575,12 @@ export const makeMessagesRecvSocket = (config) => {
337
575
  break;
338
576
  case 'devices':
339
577
  const devices = getBinaryNodeChildren(child, 'device');
340
- if (areJidsSameUser(child.attrs.jid, authState.creds.me.id)) {
341
- const deviceJids = devices.map(d => d.attrs.jid);
342
- logger.info({ deviceJids }, 'got my own devices');
578
+ if (areJidsSameUser(child.attrs.jid, authState.creds.me.id) ||
579
+ areJidsSameUser(child.attrs.lid, authState.creds.me.lid)) {
580
+ const deviceData = devices.map(d => ({ id: d.attrs.jid, lid: d.attrs.lid }));
581
+ logger.info({ deviceData }, 'my own devices changed');
343
582
  }
583
+ //TODO: drop a new event, add hashes
344
584
  break;
345
585
  case 'server_sync':
346
586
  const update = getBinaryNodeChild(node, 'collection');
@@ -478,7 +718,7 @@ export const makeMessagesRecvSocket = (config) => {
478
718
  const willSendMessageAgain = (id, participant) => {
479
719
  const key = `${id}:${participant}`;
480
720
  const retryCount = msgRetryCache.get(key) || 0;
481
- return retryCount < maxMsgRetryCount;
721
+ return retryCount <= maxMsgRetryCount;
482
722
  };
483
723
  const updateSendMessageAgainCount = (id, participant) => {
484
724
  const key = `${id}:${participant}`;
@@ -486,21 +726,68 @@ export const makeMessagesRecvSocket = (config) => {
486
726
  msgRetryCache.set(key, newValue);
487
727
  };
488
728
  const sendMessagesAgain = async (key, ids, retryNode) => {
489
- // todo: implement a cache to store the last 256 sent messages (copy whatsmeow)
490
- const msgs = await Promise.all(ids.map(id => getMessage({ ...key, id })));
491
729
  const remoteJid = key.remoteJid;
492
730
  const participant = key.participant || remoteJid;
731
+ const retryCount = +retryNode.attrs.count || 1;
732
+ // Try to get messages from cache first, then fallback to getMessage
733
+ const msgs = [];
734
+ for (const id of ids) {
735
+ let msg;
736
+ // Try to get from retry cache first if enabled
737
+ if (messageRetryManager) {
738
+ const cachedMsg = messageRetryManager.getRecentMessage(remoteJid, id);
739
+ if (cachedMsg) {
740
+ msg = cachedMsg.message;
741
+ logger.debug({ jid: remoteJid, id }, 'found message in retry cache');
742
+ // Mark retry as successful since we found the message
743
+ messageRetryManager.markRetrySuccess(id);
744
+ }
745
+ }
746
+ // Fallback to getMessage if not found in cache
747
+ if (!msg) {
748
+ msg = await getMessage({ ...key, id });
749
+ if (msg) {
750
+ logger.debug({ jid: remoteJid, id }, 'found message via getMessage');
751
+ // Also mark as successful if found via getMessage
752
+ if (messageRetryManager) {
753
+ messageRetryManager.markRetrySuccess(id);
754
+ }
755
+ }
756
+ }
757
+ msgs.push(msg);
758
+ }
493
759
  // if it's the primary jid sending the request
494
760
  // just re-send the message to everyone
495
761
  // prevents the first message decryption failure
496
762
  const sendToAll = !jidDecode(participant)?.device;
497
- await assertSessions([participant], true);
763
+ // Check if we should recreate session for this retry
764
+ let shouldRecreateSession = false;
765
+ let recreateReason = '';
766
+ if (enableAutoSessionRecreation && messageRetryManager) {
767
+ try {
768
+ const sessionId = signalRepository.jidToSignalProtocolAddress(participant);
769
+ const hasSession = await signalRepository.validateSession(participant);
770
+ const result = messageRetryManager.shouldRecreateSession(participant, retryCount, hasSession.exists);
771
+ shouldRecreateSession = result.recreate;
772
+ recreateReason = result.reason;
773
+ if (shouldRecreateSession) {
774
+ logger.info({ participant, retryCount, reason: recreateReason }, 'recreating session for outgoing retry');
775
+ await authState.keys.set({ session: { [sessionId]: null } });
776
+ }
777
+ }
778
+ catch (error) {
779
+ logger.warn({ error, participant }, 'failed to check session recreation for outgoing retry');
780
+ }
781
+ }
782
+ await assertSessions([participant], shouldRecreateSession);
498
783
  if (isJidGroup(remoteJid)) {
499
784
  await authState.keys.set({ 'sender-key-memory': { [remoteJid]: null } });
500
785
  }
501
- logger.debug({ participant, sendToAll }, 'forced new session for retry recp');
786
+ logger.debug({ participant, sendToAll, shouldRecreateSession, recreateReason }, 'forced new session for retry recp');
502
787
  for (const [i, msg] of msgs.entries()) {
503
- if (msg) {
788
+ if (!ids[i])
789
+ continue;
790
+ if (msg && willSendMessageAgain(ids[i], participant)) {
504
791
  updateSendMessageAgainCount(ids[i], participant);
505
792
  const msgRelayOpts = { messageId: ids[i] };
506
793
  if (sendToAll) {
@@ -548,7 +835,7 @@ export const makeMessagesRecvSocket = (config) => {
548
835
  if (typeof status !== 'undefined' &&
549
836
  // basically, we only want to know when a message from us has been delivered to/read by the other person
550
837
  // or another device of ours has read some messages
551
- (status >= proto.WebMessageInfo.Status.SERVER_ACK)) {
838
+ status >= proto.WebMessageInfo.Status.SERVER_ACK) {
552
839
  if (isJidGroup(remoteJid) || isJidStatusBroadcast(remoteJid)) {
553
840
  const updateKey = status === proto.WebMessageInfo.Status.DELIVERY_ACK ? 'receiptTimestamp' : 'readTimestamp';
554
841
  if (attrs.participant) {
@@ -590,15 +877,16 @@ export const makeMessagesRecvSocket = (config) => {
590
877
  // correctly set who is asking for the retry
591
878
  key.participant = key.participant || attrs.from;
592
879
  const retryNode = getBinaryNodeChild(node, 'retry');
593
- if (willSendMessageAgain(ids[0], key.participant)) {
880
+ if (ids[0] && key.participant && willSendMessageAgain(ids[0], key.participant)) {
594
881
  if (key.fromMe) {
595
882
  try {
883
+ updateSendMessageAgainCount(ids[0], key.participant);
596
884
  logger.debug({ attrs, key }, 'recv retry request');
597
885
  await sendMessagesAgain(key, ids, retryNode);
598
886
  ev.emit('messages.retry', { key, ids, retryNode });
599
887
  }
600
888
  catch (error) {
601
- logger.error({ key, ids, trace: error.stack }, 'error in sending message again');
889
+ logger.error({ key, ids, trace: error instanceof Error ? error.stack : 'Unknown error' }, 'error in sending message again');
602
890
  }
603
891
  }
604
892
  else {
@@ -638,7 +926,7 @@ export const makeMessagesRecvSocket = (config) => {
638
926
  };
639
927
  msg.participant ?? (msg.participant = node.attrs.participant);
640
928
  msg.messageTimestamp = +node.attrs.t;
641
- const fullMsg = proto.WebMessageInfo.fromObject(msg);
929
+ const fullMsg = proto.WebMessageInfo.create(msg);
642
930
  await upsertMessage(fullMsg, 'append');
643
931
  }
644
932
  })
@@ -658,7 +946,7 @@ export const makeMessagesRecvSocket = (config) => {
658
946
  // TODO: temporary fix for crashes and issues resulting of failed msmsg decryption
659
947
  if (encNode && encNode.attrs.type === 'msmsg') {
660
948
  logger.debug({ key: node.attrs.key }, 'ignored msmsg');
661
- await sendMessageAck(node);
949
+ await sendMessageAck(node, NACK_REASONS.MissingMessageSecret);
662
950
  return;
663
951
  }
664
952
  let response;
@@ -682,7 +970,32 @@ export const makeMessagesRecvSocket = (config) => {
682
970
  }
683
971
  if (msg.message?.protocolMessage?.type === proto.Message.ProtocolMessage.Type.SHARE_PHONE_NUMBER &&
684
972
  node.attrs.sender_pn) {
685
- ev.emit('chats.phoneNumberShare', { lid: node.attrs.from, jid: node.attrs.sender_pn });
973
+ const lid = jidNormalizedUser(node.attrs.from), pn = jidNormalizedUser(node.attrs.sender_pn);
974
+ ev.emit('lid-mapping.update', { lid, pn });
975
+ await signalRepository.storeLIDPNMapping(lid, pn);
976
+ }
977
+ const alt = msg.key.participantAlt || msg.key.remoteJidAlt;
978
+ // store new mappings we didn't have before
979
+ if (!!alt) {
980
+ const altServer = jidDecode(alt)?.server;
981
+ const lidMapping = signalRepository.getLIDMappingStore();
982
+ if (altServer === 'lid') {
983
+ if (!(await lidMapping.getPNForLID(alt))) {
984
+ await lidMapping.storeLIDPNMapping(alt, msg.key.participant || msg.key.remoteJid);
985
+ }
986
+ }
987
+ else {
988
+ if (!(await lidMapping.getLIDForPN(alt))) {
989
+ await lidMapping.storeLIDPNMapping(msg.key.participant || msg.key.remoteJid, alt);
990
+ }
991
+ }
992
+ }
993
+ if (msg.key?.remoteJid && msg.key?.id && messageRetryManager) {
994
+ messageRetryManager.addRecentMessage(msg.key.remoteJid, msg.key.id, msg.message);
995
+ logger.debug({
996
+ jid: msg.key.remoteJid,
997
+ id: msg.key.id
998
+ }, 'Added message to recent cache for retry receipts');
686
999
  }
687
1000
  try {
688
1001
  await Promise.all([
@@ -693,19 +1006,49 @@ export const makeMessagesRecvSocket = (config) => {
693
1006
  if (msg?.messageStubParameters?.[0] === MISSING_KEYS_ERROR_TEXT) {
694
1007
  return sendMessageAck(node, NACK_REASONS.ParsingError);
695
1008
  }
1009
+ const errorMessage = msg?.messageStubParameters?.[0] || '';
1010
+ const isPreKeyError = errorMessage.includes('PreKey');
1011
+ logger.debug(`[handleMessage] Attempting retry request for failed decryption`);
1012
+ // Handle both pre-key and normal retries in single mutex
696
1013
  retryMutex.mutex(async () => {
697
- if (ws.isOpen) {
1014
+ try {
1015
+ if (!ws.isOpen) {
1016
+ logger.debug({ node }, 'Connection closed, skipping retry');
1017
+ return;
1018
+ }
698
1019
  if (getBinaryNodeChild(node, 'unavailable')) {
1020
+ logger.debug('Message unavailable, skipping retry');
699
1021
  return;
700
1022
  }
1023
+ // Handle pre-key errors with upload and delay
1024
+ if (isPreKeyError) {
1025
+ logger.info({ error: errorMessage }, 'PreKey error detected, uploading and retrying');
1026
+ try {
1027
+ logger.debug('Uploading pre-keys for error recovery');
1028
+ await uploadPreKeys(5);
1029
+ logger.debug('Waiting for server to process new pre-keys');
1030
+ await delay(1000);
1031
+ }
1032
+ catch (uploadErr) {
1033
+ logger.error({ uploadErr }, 'Pre-key upload failed, proceeding with retry anyway');
1034
+ }
1035
+ }
701
1036
  const encNode = getBinaryNodeChild(node, 'enc');
702
1037
  await sendRetryRequest(node, !encNode);
703
1038
  if (retryRequestDelayMs) {
704
1039
  await delay(retryRequestDelayMs);
705
1040
  }
706
1041
  }
707
- else {
708
- logger.debug({ node }, 'connection closed, ignoring retry req');
1042
+ catch (err) {
1043
+ logger.error({ err, isPreKeyError }, 'Failed to handle retry, attempting basic retry');
1044
+ // Still attempt retry even if pre-key upload failed
1045
+ try {
1046
+ const encNode = getBinaryNodeChild(node, 'enc');
1047
+ await sendRetryRequest(node, !encNode);
1048
+ }
1049
+ catch (retryErr) {
1050
+ logger.error({ retryErr }, 'Failed to send retry after error handling');
1051
+ }
709
1052
  }
710
1053
  });
711
1054
  }
@@ -721,8 +1064,8 @@ export const makeMessagesRecvSocket = (config) => {
721
1064
  // message was sent by us from a different device
722
1065
  type = 'sender';
723
1066
  // need to specially handle this case
724
- if (isJidUser(msg.key.remoteJid)) {
725
- participant = author;
1067
+ if (isLidUser(msg.key.remoteJid) || isLidUser(msg.key.remoteJidAlt)) {
1068
+ participant = author; // TODO: investigate sending receipts to LIDs and not PNs
726
1069
  }
727
1070
  }
728
1071
  else if (!sendActiveReceipts) {
@@ -746,54 +1089,6 @@ export const makeMessagesRecvSocket = (config) => {
746
1089
  logger.error({ error, node }, 'error in handling message');
747
1090
  }
748
1091
  };
749
- const fetchMessageHistory = async (count, oldestMsgKey, oldestMsgTimestamp) => {
750
- if (!authState.creds.me?.id) {
751
- throw new Boom('Not authenticated');
752
- }
753
- const pdoMessage = {
754
- historySyncOnDemandRequest: {
755
- chatJid: oldestMsgKey.remoteJid,
756
- oldestMsgFromMe: oldestMsgKey.fromMe,
757
- oldestMsgId: oldestMsgKey.id,
758
- oldestMsgTimestampMs: oldestMsgTimestamp,
759
- onDemandMsgCount: count
760
- },
761
- peerDataOperationRequestType: proto.Message.PeerDataOperationRequestType.HISTORY_SYNC_ON_DEMAND
762
- };
763
- return sendPeerDataOperationMessage(pdoMessage);
764
- };
765
- const requestPlaceholderResend = async (messageKey) => {
766
- if (!authState.creds.me?.id) {
767
- throw new Boom('Not authenticated');
768
- }
769
- if (placeholderResendCache.get(messageKey?.id)) {
770
- logger.debug({ messageKey }, 'already requested resend');
771
- return;
772
- }
773
- else {
774
- placeholderResendCache.set(messageKey?.id, true);
775
- }
776
- await delay(5000);
777
- if (!placeholderResendCache.get(messageKey?.id)) {
778
- logger.debug({ messageKey }, 'message received while resend requested');
779
- return 'RESOLVED';
780
- }
781
- const pdoMessage = {
782
- placeholderMessageResendRequest: [
783
- {
784
- messageKey
785
- }
786
- ],
787
- peerDataOperationRequestType: proto.Message.PeerDataOperationRequestType.PLACEHOLDER_MESSAGE_RESEND
788
- };
789
- setTimeout(() => {
790
- if (placeholderResendCache.get(messageKey?.id)) {
791
- logger.debug({ messageKey }, 'PDO message without response after 15 seconds. Phone possibly offline');
792
- placeholderResendCache.del(messageKey?.id);
793
- }
794
- }, 15000);
795
- return sendPeerDataOperationMessage(pdoMessage);
796
- };
797
1092
  const handleCall = async (node) => {
798
1093
  let status;
799
1094
  const { attrs } = node;
@@ -927,139 +1222,6 @@ export const makeMessagesRecvSocket = (config) => {
927
1222
  processNodeWithBuffer(node, identifier, exec);
928
1223
  }
929
1224
  };
930
- // Handles newsletter notifications
931
- async function handleNewsletterNotification(node) {
932
- const from = node.attrs.from;
933
- const child = getAllBinaryNodeChildren(node)[0];
934
- const author = node.attrs.participant;
935
- logger.info({ from, child }, 'got newsletter notification');
936
- switch (child.tag) {
937
- case 'reaction':
938
- const reactionUpdate = {
939
- id: from,
940
- server_id: child.attrs.message_id,
941
- reaction: {
942
- code: getBinaryNodeChildString(child, 'reaction'),
943
- count: 1
944
- }
945
- };
946
- ev.emit('newsletter.reaction', reactionUpdate);
947
- break;
948
- case 'view':
949
- const viewUpdate = {
950
- id: from,
951
- server_id: child.attrs.message_id,
952
- count: parseInt(child.content?.toString() || '0', 10)
953
- };
954
- ev.emit('newsletter.view', viewUpdate);
955
- break;
956
- case 'participant':
957
- const participantUpdate = {
958
- id: from,
959
- author,
960
- user: child.attrs.jid,
961
- action: child.attrs.action,
962
- new_role: child.attrs.role
963
- };
964
- ev.emit('newsletter-participants.update', participantUpdate);
965
- break;
966
- case 'update':
967
- const settingsNode = getBinaryNodeChild(child, 'settings');
968
- if (settingsNode) {
969
- const update = {};
970
- const nameNode = getBinaryNodeChild(settingsNode, 'name');
971
- if (nameNode?.content)
972
- update.name = nameNode.content.toString();
973
- const descriptionNode = getBinaryNodeChild(settingsNode, 'description');
974
- if (descriptionNode?.content)
975
- update.description = descriptionNode.content.toString();
976
- ev.emit('newsletter-settings.update', {
977
- id: from,
978
- update
979
- });
980
- }
981
- break;
982
- case 'message':
983
- const plaintextNode = getBinaryNodeChild(child, 'plaintext');
984
- if (plaintextNode?.content) {
985
- try {
986
- const contentBuf = typeof plaintextNode.content === 'string'
987
- ? Buffer.from(plaintextNode.content, 'binary')
988
- : Buffer.from(plaintextNode.content);
989
- const messageProto = proto.Message.decode(contentBuf);
990
- const fullMessage = proto.WebMessageInfo.fromObject({
991
- key: {
992
- remoteJid: from,
993
- id: child.attrs.message_id || child.attrs.server_id,
994
- fromMe: false
995
- },
996
- message: messageProto,
997
- messageTimestamp: +child.attrs.t
998
- });
999
- await upsertMessage(fullMessage, 'append');
1000
- logger.info('Processed plaintext newsletter message');
1001
- }
1002
- catch (error) {
1003
- logger.error({ error }, 'Failed to decode plaintext newsletter message');
1004
- }
1005
- }
1006
- break;
1007
- default:
1008
- logger.warn({ node }, 'Unknown newsletter notification');
1009
- break;
1010
- }
1011
- }
1012
- // Handles mex newsletter notifications
1013
- async function handleMexNewsletterNotification(node) {
1014
- const mexNode = getBinaryNodeChild(node, 'mex');
1015
- if (!mexNode?.content) {
1016
- logger.warn({ node }, 'Invalid mex newsletter notification');
1017
- return;
1018
- }
1019
- let data;
1020
- try {
1021
- data = JSON.parse(mexNode.content.toString());
1022
- }
1023
- catch (error) {
1024
- logger.error({ err: error, node }, 'Failed to parse mex newsletter notification');
1025
- return;
1026
- }
1027
- const operation = data?.operation;
1028
- const updates = data?.updates;
1029
- if (!updates || !operation) {
1030
- logger.warn({ data }, 'Invalid mex newsletter notification content');
1031
- return;
1032
- }
1033
- logger.info({ operation, updates }, 'got mex newsletter notification');
1034
- switch (operation) {
1035
- case 'NotificationNewsletterUpdate':
1036
- for (const update of updates) {
1037
- if (update.jid && update.settings && Object.keys(update.settings).length > 0) {
1038
- ev.emit('newsletter-settings.update', {
1039
- id: update.jid,
1040
- update: update.settings
1041
- });
1042
- }
1043
- }
1044
- break;
1045
- case 'NotificationNewsletterAdminPromote':
1046
- for (const update of updates) {
1047
- if (update.jid && update.user) {
1048
- ev.emit('newsletter-participants.update', {
1049
- id: update.jid,
1050
- author: node.attrs.from,
1051
- user: update.user,
1052
- new_role: 'ADMIN',
1053
- action: 'promote'
1054
- });
1055
- }
1056
- }
1057
- break;
1058
- default:
1059
- logger.info({ operation, data }, 'Unhandled mex newsletter notification');
1060
- break;
1061
- }
1062
- }
1063
1225
  // recv a message
1064
1226
  ws.on('CB:message', (node) => {
1065
1227
  processNode('message', node, 'processing message', handleMessage);
@@ -1103,7 +1265,7 @@ export const makeMessagesRecvSocket = (config) => {
1103
1265
  else {
1104
1266
  msg.message = { call: { callKey: Buffer.from(call.id) } };
1105
1267
  }
1106
- const protoMsg = proto.WebMessageInfo.fromObject(msg);
1268
+ const protoMsg = proto.WebMessageInfo.create(msg);
1107
1269
  upsertMessage(protoMsg, call.offline ? 'append' : 'notify');
1108
1270
  }
1109
1271
  });
@@ -1119,7 +1281,8 @@ export const makeMessagesRecvSocket = (config) => {
1119
1281
  sendRetryRequest,
1120
1282
  rejectCall,
1121
1283
  fetchMessageHistory,
1122
- requestPlaceholderResend
1284
+ requestPlaceholderResend,
1285
+ messageRetryManager
1123
1286
  };
1124
1287
  };
1125
1288
  //# sourceMappingURL=messages-recv.js.map