@periskope/baileys 7.0.0-alpha-4 → 7.0.0-beta-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 (137) hide show
  1. package/README.md +0 -6
  2. package/lib/Defaults/index.d.ts +1 -2
  3. package/lib/Defaults/index.d.ts.map +1 -1
  4. package/lib/Defaults/index.js +2 -3
  5. package/lib/Defaults/index.js.map +1 -1
  6. package/lib/Signal/Group/group_cipher.d.ts +1 -0
  7. package/lib/Signal/Group/group_cipher.d.ts.map +1 -1
  8. package/lib/Signal/Group/group_cipher.js +37 -28
  9. package/lib/Signal/Group/group_cipher.js.map +1 -1
  10. package/lib/Signal/Group/keyhelper.d.ts.map +1 -1
  11. package/lib/Signal/Group/keyhelper.js +1 -0
  12. package/lib/Signal/Group/keyhelper.js.map +1 -1
  13. package/lib/Signal/Group/queue-job.d.ts +1 -0
  14. package/lib/Signal/Group/queue-job.d.ts.map +1 -1
  15. package/lib/Signal/Group/queue-job.js +3 -0
  16. package/lib/Signal/Group/queue-job.js.map +1 -1
  17. package/lib/Signal/Group/sender-chain-key.d.ts +1 -1
  18. package/lib/Signal/Group/sender-chain-key.d.ts.map +1 -1
  19. package/lib/Signal/Group/sender-chain-key.js +3 -9
  20. package/lib/Signal/Group/sender-chain-key.js.map +1 -1
  21. package/lib/Signal/Group/sender-key-message.d.ts.map +1 -1
  22. package/lib/Signal/Group/sender-key-message.js +1 -0
  23. package/lib/Signal/Group/sender-key-message.js.map +1 -1
  24. package/lib/Signal/Group/sender-key-state.d.ts +4 -4
  25. package/lib/Signal/Group/sender-key-state.d.ts.map +1 -1
  26. package/lib/Signal/Group/sender-key-state.js +24 -47
  27. package/lib/Signal/Group/sender-key-state.js.map +1 -1
  28. package/lib/Signal/Group/sender-message-key.d.ts.map +1 -1
  29. package/lib/Signal/Group/sender-message-key.js +1 -0
  30. package/lib/Signal/Group/sender-message-key.js.map +1 -1
  31. package/lib/Signal/libsignal.d.ts +1 -5
  32. package/lib/Signal/libsignal.d.ts.map +1 -1
  33. package/lib/Signal/libsignal.js +38 -70
  34. package/lib/Signal/libsignal.js.map +1 -1
  35. package/lib/Signal/lid-mapping.d.ts +1 -13
  36. package/lib/Signal/lid-mapping.d.ts.map +1 -1
  37. package/lib/Signal/lid-mapping.js +25 -45
  38. package/lib/Signal/lid-mapping.js.map +1 -1
  39. package/lib/Socket/business.d.ts +6 -7
  40. package/lib/Socket/business.d.ts.map +1 -1
  41. package/lib/Socket/chats.d.ts +6 -6
  42. package/lib/Socket/chats.d.ts.map +1 -1
  43. package/lib/Socket/chats.js +19 -15
  44. package/lib/Socket/chats.js.map +1 -1
  45. package/lib/Socket/communities.d.ts +6 -7
  46. package/lib/Socket/communities.d.ts.map +1 -1
  47. package/lib/Socket/groups.d.ts +6 -6
  48. package/lib/Socket/groups.d.ts.map +1 -1
  49. package/lib/Socket/groups.js +10 -12
  50. package/lib/Socket/groups.js.map +1 -1
  51. package/lib/Socket/index.d.ts +6 -7
  52. package/lib/Socket/index.d.ts.map +1 -1
  53. package/lib/Socket/messages-recv.d.ts +6 -7
  54. package/lib/Socket/messages-recv.d.ts.map +1 -1
  55. package/lib/Socket/messages-recv.js +254 -354
  56. package/lib/Socket/messages-recv.js.map +1 -1
  57. package/lib/Socket/messages-send.d.ts +6 -8
  58. package/lib/Socket/messages-send.d.ts.map +1 -1
  59. package/lib/Socket/messages-send.js +21 -40
  60. package/lib/Socket/messages-send.js.map +1 -1
  61. package/lib/Socket/newsletter.d.ts +9 -8
  62. package/lib/Socket/newsletter.d.ts.map +1 -1
  63. package/lib/Socket/newsletter.js +1 -3
  64. package/lib/Socket/newsletter.js.map +1 -1
  65. package/lib/Socket/socket.d.ts +0 -7
  66. package/lib/Socket/socket.d.ts.map +1 -1
  67. package/lib/Socket/socket.js +50 -124
  68. package/lib/Socket/socket.js.map +1 -1
  69. package/lib/Socket/usync.d.ts +2 -2
  70. package/lib/Types/Auth.d.ts +1 -1
  71. package/lib/Types/Auth.d.ts.map +1 -1
  72. package/lib/Types/Chat.d.ts +0 -2
  73. package/lib/Types/Chat.d.ts.map +1 -1
  74. package/lib/Types/Contact.d.ts +4 -4
  75. package/lib/Types/Contact.d.ts.map +1 -1
  76. package/lib/Types/Events.d.ts +2 -2
  77. package/lib/Types/Events.d.ts.map +1 -1
  78. package/lib/Types/GroupMetadata.d.ts +4 -6
  79. package/lib/Types/GroupMetadata.d.ts.map +1 -1
  80. package/lib/Types/Message.d.ts +6 -7
  81. package/lib/Types/Message.d.ts.map +1 -1
  82. package/lib/Types/Message.js +1 -5
  83. package/lib/Types/Message.js.map +1 -1
  84. package/lib/Types/Signal.d.ts +0 -4
  85. package/lib/Types/Signal.d.ts.map +1 -1
  86. package/lib/Types/Socket.d.ts +6 -22
  87. package/lib/Types/Socket.d.ts.map +1 -1
  88. package/lib/Utils/auth-utils.d.ts.map +1 -1
  89. package/lib/Utils/auth-utils.js +79 -363
  90. package/lib/Utils/auth-utils.js.map +1 -1
  91. package/lib/Utils/chat-utils.d.ts +2 -2
  92. package/lib/Utils/chat-utils.d.ts.map +1 -1
  93. package/lib/Utils/chat-utils.js +2 -2
  94. package/lib/Utils/chat-utils.js.map +1 -1
  95. package/lib/Utils/crypto.d.ts +3 -3
  96. package/lib/Utils/crypto.d.ts.map +1 -1
  97. package/lib/Utils/crypto.js +12 -16
  98. package/lib/Utils/crypto.js.map +1 -1
  99. package/lib/Utils/decode-wa-message.d.ts +0 -5
  100. package/lib/Utils/decode-wa-message.d.ts.map +1 -1
  101. package/lib/Utils/decode-wa-message.js +27 -50
  102. package/lib/Utils/decode-wa-message.js.map +1 -1
  103. package/lib/Utils/event-buffer.d.ts +1 -0
  104. package/lib/Utils/event-buffer.d.ts.map +1 -1
  105. package/lib/Utils/event-buffer.js +4 -48
  106. package/lib/Utils/event-buffer.js.map +1 -1
  107. package/lib/Utils/generics.d.ts.map +1 -1
  108. package/lib/Utils/generics.js +8 -17
  109. package/lib/Utils/generics.js.map +1 -1
  110. package/lib/Utils/history.d.ts.map +1 -1
  111. package/lib/Utils/history.js +2 -1
  112. package/lib/Utils/history.js.map +1 -1
  113. package/lib/Utils/index.d.ts +0 -1
  114. package/lib/Utils/index.d.ts.map +1 -1
  115. package/lib/Utils/index.js +0 -1
  116. package/lib/Utils/index.js.map +1 -1
  117. package/lib/Utils/messages-media.d.ts +2 -3
  118. package/lib/Utils/messages-media.d.ts.map +1 -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 +3 -3
  122. package/lib/Utils/messages.js.map +1 -1
  123. package/lib/Utils/process-message.d.ts +2 -3
  124. package/lib/Utils/process-message.d.ts.map +1 -1
  125. package/lib/Utils/process-message.js +3 -14
  126. package/lib/Utils/process-message.js.map +1 -1
  127. package/lib/Utils/validate-connection.d.ts.map +1 -1
  128. package/lib/Utils/validate-connection.js +2 -3
  129. package/lib/Utils/validate-connection.js.map +1 -1
  130. package/lib/WABinary/jid-utils.d.ts +5 -6
  131. package/lib/WABinary/jid-utils.d.ts.map +1 -1
  132. package/lib/WABinary/jid-utils.js +5 -11
  133. package/lib/WABinary/jid-utils.js.map +1 -1
  134. package/lib/WAM/encode.d.ts.map +1 -1
  135. package/lib/WAM/encode.js +1 -0
  136. package/lib/WAM/encode.js.map +1 -1
  137. package/package.json +8 -10
@@ -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, isLidUser, jidDecode, jidNormalizedUser, S_WHATSAPP_NET } from '../WABinary/index.js';
10
+ import { areJidsSameUser, getAllBinaryNodeChildren, getBinaryNodeChild, getBinaryNodeChildBuffer, getBinaryNodeChildren, getBinaryNodeChildString, isJidGroup, isJidStatusBroadcast, isJidUser, 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, enableAutoSessionRecreation } = config;
14
+ const { logger, retryRequestDelayMs, maxMsgRetryCount, getMessage, shouldIgnoreJid } = config;
15
15
  const sock = makeMessagesSocket(config);
16
- const { ev, authState, ws, processingMutex, signalRepository, query, upsertMessage, resyncAppState, onUnexpectedError, assertSessions, sendNode, relayMessage, sendReceipt, uploadPreKeys, sendPeerDataOperationMessage, messageRetryManager } = sock;
16
+ const { ev, authState, ws, processingMutex, signalRepository, query, upsertMessage, resyncAppState, onUnexpectedError, assertSessions, sendNode, relayMessage, sendReceipt, uploadPreKeys, sendPeerDataOperationMessage } = 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,187 +32,6 @@ 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
- };
216
35
  const sendMessageAck = async ({ tag, attrs, content }, errorCode) => {
217
36
  const stanza = {
218
37
  tag: 'ack',
@@ -288,76 +107,20 @@ export const makeMessagesRecvSocket = (config) => {
288
107
  const { fullMessage } = decodeMessageNode(node, authState.creds.me.id, authState.creds.me.lid || '');
289
108
  const { key: msgKey } = fullMessage;
290
109
  const msgId = msgKey.id;
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);
303
- }
304
- else {
305
- // Fallback to old system
306
- const key = `${msgId}:${msgKey?.participant}`;
307
- let retryCount = (await 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
- await msgRetryCache.set(key, retryCount);
315
- }
316
110
  const key = `${msgId}:${msgKey?.participant}`;
317
- const retryCount = (await msgRetryCache.get(key)) || 1;
318
- const { account, signedPreKey, signedIdentityKey: identityKey } = authState.creds;
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
- }
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;
341
116
  }
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
- }
117
+ retryCount += 1;
118
+ msgRetryCache.set(key, retryCount);
119
+ 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}`);
361
124
  }
362
125
  const deviceIdentity = encodeSignedDeviceIdentity(account, true);
363
126
  await authState.keys.transaction(async () => {
@@ -391,7 +154,7 @@ export const makeMessagesRecvSocket = (config) => {
391
154
  if (node.attrs.participant) {
392
155
  receipt.attrs.participant = node.attrs.participant;
393
156
  }
394
- if (retryCount > 1 || forceIncludeKeys || shouldRecreateSession) {
157
+ if (retryCount > 1 || forceIncludeKeys) {
395
158
  const { update, preKeys } = await getNextPreKeys(authState, 1);
396
159
  const [keyId] = Object.keys(preKeys);
397
160
  const key = preKeys[+keyId];
@@ -411,7 +174,7 @@ export const makeMessagesRecvSocket = (config) => {
411
174
  }
412
175
  await sendNode(receipt);
413
176
  logger.info({ msgAttrs: node.attrs, retryCount }, 'sent retry receipt');
414
- }, authState?.creds?.me?.id || 'sendRetryRequest');
177
+ });
415
178
  };
416
179
  const handleEncryptNotification = async (node) => {
417
180
  const from = node.attrs.from;
@@ -438,7 +201,6 @@ export const makeMessagesRecvSocket = (config) => {
438
201
  };
439
202
  const handleGroupNotification = (participant, child, msg) => {
440
203
  const participantJid = getBinaryNodeChild(child, 'participant')?.attrs?.jid || participant;
441
- // TODO: Add participant LID
442
204
  switch (child?.tag) {
443
205
  case 'create':
444
206
  const metadata = extractGroupMetadata(child);
@@ -575,12 +337,10 @@ export const makeMessagesRecvSocket = (config) => {
575
337
  break;
576
338
  case 'devices':
577
339
  const devices = getBinaryNodeChildren(child, 'device');
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');
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');
582
343
  }
583
- //TODO: drop a new event, add hashes
584
344
  break;
585
345
  case 'server_sync':
586
346
  const update = getBinaryNodeChild(node, 'collection');
@@ -715,79 +475,34 @@ export const makeMessagesRecvSocket = (config) => {
715
475
  }
716
476
  return data instanceof Buffer ? data : Buffer.from(data);
717
477
  }
718
- const willSendMessageAgain = async (id, participant) => {
478
+ const willSendMessageAgain = (id, participant) => {
719
479
  const key = `${id}:${participant}`;
720
- const retryCount = (await msgRetryCache.get(key)) || 0;
721
- return retryCount < maxMsgRetryCount;
480
+ const retryCount = msgRetryCache.get(key) || 0;
481
+ return retryCount <= maxMsgRetryCount;
722
482
  };
723
- const updateSendMessageAgainCount = async (id, participant) => {
483
+ const updateSendMessageAgainCount = (id, participant) => {
724
484
  const key = `${id}:${participant}`;
725
- const newValue = ((await msgRetryCache.get(key)) || 0) + 1;
726
- await msgRetryCache.set(key, newValue);
485
+ const newValue = (msgRetryCache.get(key) || 0) + 1;
486
+ msgRetryCache.set(key, newValue);
727
487
  };
728
488
  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 })));
729
491
  const remoteJid = key.remoteJid;
730
492
  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
- }
759
493
  // if it's the primary jid sending the request
760
494
  // just re-send the message to everyone
761
495
  // prevents the first message decryption failure
762
496
  const sendToAll = !jidDecode(participant)?.device;
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);
497
+ await assertSessions([participant], true);
783
498
  if (isJidGroup(remoteJid)) {
784
499
  await authState.keys.set({ 'sender-key-memory': { [remoteJid]: null } });
785
500
  }
786
- logger.debug({ participant, sendToAll, shouldRecreateSession, recreateReason }, 'forced new session for retry recp');
501
+ logger.debug({ participant, sendToAll }, 'forced new session for retry recp');
787
502
  for (const [i, msg] of msgs.entries()) {
788
503
  if (!ids[i])
789
504
  continue;
790
- if (msg && (await willSendMessageAgain(ids[i], participant))) {
505
+ if (msg && willSendMessageAgain(ids[i], participant)) {
791
506
  updateSendMessageAgainCount(ids[i], participant);
792
507
  const msgRelayOpts = { messageId: ids[i] };
793
508
  if (sendToAll) {
@@ -835,7 +550,7 @@ export const makeMessagesRecvSocket = (config) => {
835
550
  if (typeof status !== 'undefined' &&
836
551
  // basically, we only want to know when a message from us has been delivered to/read by the other person
837
552
  // or another device of ours has read some messages
838
- status >= proto.WebMessageInfo.Status.SERVER_ACK) {
553
+ (status >= proto.WebMessageInfo.Status.SERVER_ACK)) {
839
554
  if (isJidGroup(remoteJid) || isJidStatusBroadcast(remoteJid)) {
840
555
  const updateKey = status === proto.WebMessageInfo.Status.DELIVERY_ACK ? 'receiptTimestamp' : 'readTimestamp';
841
556
  if (attrs.participant) {
@@ -877,7 +592,7 @@ export const makeMessagesRecvSocket = (config) => {
877
592
  // correctly set who is asking for the retry
878
593
  key.participant = key.participant || attrs.from;
879
594
  const retryNode = getBinaryNodeChild(node, 'retry');
880
- if (ids[0] && key.participant && (await willSendMessageAgain(ids[0], key.participant))) {
595
+ if (ids[0] && key.participant && willSendMessageAgain(ids[0], key.participant)) {
881
596
  if (key.fromMe) {
882
597
  try {
883
598
  updateSendMessageAgainCount(ids[0], key.participant);
@@ -946,22 +661,22 @@ export const makeMessagesRecvSocket = (config) => {
946
661
  // TODO: temporary fix for crashes and issues resulting of failed msmsg decryption
947
662
  if (encNode && encNode.attrs.type === 'msmsg') {
948
663
  logger.debug({ key: node.attrs.key }, 'ignored msmsg');
949
- await sendMessageAck(node, NACK_REASONS.MissingMessageSecret);
664
+ await sendMessageAck(node);
950
665
  return;
951
666
  }
952
667
  let response;
953
668
  if (getBinaryNodeChild(node, 'unavailable') && !encNode) {
954
669
  await sendMessageAck(node);
955
670
  const { key } = decodeMessageNode(node, authState.creds.me.id, authState.creds.me.lid || '').fullMessage;
956
- response = await requestPlaceholderResend(key); // TODO: DEPRECATE THIS LOGIC AND PASS IT OFF TO THE RETRY MANAGER
671
+ response = await requestPlaceholderResend(key);
957
672
  if (response === 'RESOLVED') {
958
673
  return;
959
674
  }
960
675
  logger.debug('received unavailable message, acked and requested resend from phone');
961
676
  }
962
677
  else {
963
- if (await placeholderResendCache.get(node.attrs.id)) {
964
- await placeholderResendCache.del(node.attrs.id);
678
+ if (placeholderResendCache.get(node.attrs.id)) {
679
+ placeholderResendCache.del(node.attrs.id);
965
680
  }
966
681
  }
967
682
  const { fullMessage: msg, category, author, decrypt } = decryptMessageNode(node, authState.creds.me.id, authState.creds.me.lid || '', signalRepository, logger);
@@ -970,33 +685,38 @@ export const makeMessagesRecvSocket = (config) => {
970
685
  }
971
686
  if (msg.message?.protocolMessage?.type === proto.Message.ProtocolMessage.Type.SHARE_PHONE_NUMBER &&
972
687
  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);
688
+ ev.emit('chats.phoneNumberShare', { lid: node.attrs.from, jid: node.attrs.sender_pn });
976
689
  }
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 (typeof (await lidMapping.getPNForLID(alt)) === 'string') {
984
- await lidMapping.storeLIDPNMapping(alt, msg.key.participant || msg.key.remoteJid);
690
+ if (msg.message?.protocolMessage?.lidMigrationMappingSyncMessage?.encodedMappingPayload) {
691
+ try {
692
+ const payload = msg.message.protocolMessage.lidMigrationMappingSyncMessage.encodedMappingPayload;
693
+ const decoded = proto.LIDMigrationMappingSyncPayload.decode(payload);
694
+ logger.debug({
695
+ mappingCount: decoded.pnToLidMappings?.length || 0,
696
+ timestamp: decoded.chatDbMigrationTimestamp
697
+ }, 'Received LID migration sync message from server');
698
+ const lidMapping = signalRepository.getLIDMappingStore();
699
+ if (decoded.pnToLidMappings && decoded.pnToLidMappings.length > 0) {
700
+ for (const mapping of decoded.pnToLidMappings) {
701
+ const pn = `${mapping.pn}@s.whatsapp.net`;
702
+ // Use latestLid if available, otherwise assignedLid (proper LID refresh)
703
+ const lidValue = mapping.latestLid || mapping.assignedLid;
704
+ const lid = `${lidValue}@lid`;
705
+ await lidMapping.storeLIDPNMapping(lid, pn);
706
+ logger.debug({
707
+ pn,
708
+ lid,
709
+ assignedLid: mapping.assignedLid,
710
+ latestLid: mapping.latestLid,
711
+ usedLatest: !!mapping.latestLid
712
+ }, 'Stored server-provided PN-LID mapping');
713
+ }
985
714
  }
986
715
  }
987
- else {
988
- if (typeof (await lidMapping.getLIDForPN(alt)) === 'string') {
989
- await lidMapping.storeLIDPNMapping(msg.key.participant || msg.key.remoteJid, alt);
990
- }
716
+ catch (error) {
717
+ logger.error({ error }, 'Failed to process LID migration sync message');
991
718
  }
992
719
  }
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');
999
- }
1000
720
  try {
1001
721
  await Promise.all([
1002
722
  processingMutex.mutex(async () => {
@@ -1008,7 +728,7 @@ export const makeMessagesRecvSocket = (config) => {
1008
728
  }
1009
729
  const errorMessage = msg?.messageStubParameters?.[0] || '';
1010
730
  const isPreKeyError = errorMessage.includes('PreKey');
1011
- logger.debug(`[handleMessage] Attempting retry request for failed decryption`);
731
+ console.debug(`[handleMessage] Attempting retry request for failed decryption`);
1012
732
  // Handle both pre-key and normal retries in single mutex
1013
733
  retryMutex.mutex(async () => {
1014
734
  try {
@@ -1064,8 +784,8 @@ export const makeMessagesRecvSocket = (config) => {
1064
784
  // message was sent by us from a different device
1065
785
  type = 'sender';
1066
786
  // need to specially handle this case
1067
- if (isLidUser(msg.key.remoteJid) || isLidUser(msg.key.remoteJidAlt)) {
1068
- participant = author; // TODO: investigate sending receipts to LIDs and not PNs
787
+ if (isJidUser(msg.key.remoteJid)) {
788
+ participant = author;
1069
789
  }
1070
790
  }
1071
791
  else if (!sendActiveReceipts) {
@@ -1089,6 +809,54 @@ export const makeMessagesRecvSocket = (config) => {
1089
809
  logger.error({ error, node }, 'error in handling message');
1090
810
  }
1091
811
  };
812
+ const fetchMessageHistory = async (count, oldestMsgKey, oldestMsgTimestamp) => {
813
+ if (!authState.creds.me?.id) {
814
+ throw new Boom('Not authenticated');
815
+ }
816
+ const pdoMessage = {
817
+ historySyncOnDemandRequest: {
818
+ chatJid: oldestMsgKey.remoteJid,
819
+ oldestMsgFromMe: oldestMsgKey.fromMe,
820
+ oldestMsgId: oldestMsgKey.id,
821
+ oldestMsgTimestampMs: oldestMsgTimestamp,
822
+ onDemandMsgCount: count
823
+ },
824
+ peerDataOperationRequestType: proto.Message.PeerDataOperationRequestType.HISTORY_SYNC_ON_DEMAND
825
+ };
826
+ return sendPeerDataOperationMessage(pdoMessage);
827
+ };
828
+ const requestPlaceholderResend = async (messageKey) => {
829
+ if (!authState.creds.me?.id) {
830
+ throw new Boom('Not authenticated');
831
+ }
832
+ if (placeholderResendCache.get(messageKey?.id)) {
833
+ logger.debug({ messageKey }, 'already requested resend');
834
+ return;
835
+ }
836
+ else {
837
+ placeholderResendCache.set(messageKey?.id, true);
838
+ }
839
+ await delay(5000);
840
+ if (!placeholderResendCache.get(messageKey?.id)) {
841
+ logger.debug({ messageKey }, 'message received while resend requested');
842
+ return 'RESOLVED';
843
+ }
844
+ const pdoMessage = {
845
+ placeholderMessageResendRequest: [
846
+ {
847
+ messageKey
848
+ }
849
+ ],
850
+ peerDataOperationRequestType: proto.Message.PeerDataOperationRequestType.PLACEHOLDER_MESSAGE_RESEND
851
+ };
852
+ setTimeout(() => {
853
+ if (placeholderResendCache.get(messageKey?.id)) {
854
+ logger.debug({ messageKey }, 'PDO message without response after 15 seconds. Phone possibly offline');
855
+ placeholderResendCache.del(messageKey?.id);
856
+ }
857
+ }, 15000);
858
+ return sendPeerDataOperationMessage(pdoMessage);
859
+ };
1092
860
  const handleCall = async (node) => {
1093
861
  let status;
1094
862
  const { attrs } = node;
@@ -1100,7 +868,7 @@ export const makeMessagesRecvSocket = (config) => {
1100
868
  const from = infoChild.attrs.from || infoChild.attrs['call-creator'];
1101
869
  status = getCallStatusFromNode(infoChild);
1102
870
  if (isLidUser(from) && infoChild.tag === 'relaylatency') {
1103
- const verify = await callOfferCache.get(callId);
871
+ const verify = callOfferCache.get(callId);
1104
872
  if (!verify) {
1105
873
  status = 'offer';
1106
874
  const callLid = {
@@ -1111,7 +879,7 @@ export const makeMessagesRecvSocket = (config) => {
1111
879
  offline: !!attrs.offline,
1112
880
  status
1113
881
  };
1114
- await callOfferCache.set(callId, callLid);
882
+ callOfferCache.set(callId, callLid);
1115
883
  }
1116
884
  }
1117
885
  const call = {
@@ -1126,9 +894,9 @@ export const makeMessagesRecvSocket = (config) => {
1126
894
  call.isVideo = !!getBinaryNodeChild(infoChild, 'video');
1127
895
  call.isGroup = infoChild.attrs.type === 'group' || !!infoChild.attrs['group-jid'];
1128
896
  call.groupJid = infoChild.attrs['group-jid'];
1129
- await callOfferCache.set(call.id, call);
897
+ callOfferCache.set(call.id, call);
1130
898
  }
1131
- const existingCall = await callOfferCache.get(call.id);
899
+ const existingCall = callOfferCache.get(call.id);
1132
900
  // use existing call info to populate this event
1133
901
  if (existingCall) {
1134
902
  call.isVideo = existingCall.isVideo;
@@ -1136,7 +904,7 @@ export const makeMessagesRecvSocket = (config) => {
1136
904
  }
1137
905
  // delete data once call has ended
1138
906
  if (status === 'reject' || status === 'accept' || status === 'timeout' || status === 'terminate') {
1139
- await callOfferCache.del(call.id);
907
+ callOfferCache.del(call.id);
1140
908
  }
1141
909
  ev.emit('call', [call]);
1142
910
  await sendMessageAck(node);
@@ -1222,6 +990,139 @@ export const makeMessagesRecvSocket = (config) => {
1222
990
  processNodeWithBuffer(node, identifier, exec);
1223
991
  }
1224
992
  };
993
+ // Handles newsletter notifications
994
+ async function handleNewsletterNotification(node) {
995
+ const from = node.attrs.from;
996
+ const child = getAllBinaryNodeChildren(node)[0];
997
+ const author = node.attrs.participant;
998
+ logger.info({ from, child }, 'got newsletter notification');
999
+ switch (child.tag) {
1000
+ case 'reaction':
1001
+ const reactionUpdate = {
1002
+ id: from,
1003
+ server_id: child.attrs.message_id,
1004
+ reaction: {
1005
+ code: getBinaryNodeChildString(child, 'reaction'),
1006
+ count: 1
1007
+ }
1008
+ };
1009
+ ev.emit('newsletter.reaction', reactionUpdate);
1010
+ break;
1011
+ case 'view':
1012
+ const viewUpdate = {
1013
+ id: from,
1014
+ server_id: child.attrs.message_id,
1015
+ count: parseInt(child.content?.toString() || '0', 10)
1016
+ };
1017
+ ev.emit('newsletter.view', viewUpdate);
1018
+ break;
1019
+ case 'participant':
1020
+ const participantUpdate = {
1021
+ id: from,
1022
+ author,
1023
+ user: child.attrs.jid,
1024
+ action: child.attrs.action,
1025
+ new_role: child.attrs.role
1026
+ };
1027
+ ev.emit('newsletter-participants.update', participantUpdate);
1028
+ break;
1029
+ case 'update':
1030
+ const settingsNode = getBinaryNodeChild(child, 'settings');
1031
+ if (settingsNode) {
1032
+ const update = {};
1033
+ const nameNode = getBinaryNodeChild(settingsNode, 'name');
1034
+ if (nameNode?.content)
1035
+ update.name = nameNode.content.toString();
1036
+ const descriptionNode = getBinaryNodeChild(settingsNode, 'description');
1037
+ if (descriptionNode?.content)
1038
+ update.description = descriptionNode.content.toString();
1039
+ ev.emit('newsletter-settings.update', {
1040
+ id: from,
1041
+ update
1042
+ });
1043
+ }
1044
+ break;
1045
+ case 'message':
1046
+ const plaintextNode = getBinaryNodeChild(child, 'plaintext');
1047
+ if (plaintextNode?.content) {
1048
+ try {
1049
+ const contentBuf = typeof plaintextNode.content === 'string'
1050
+ ? Buffer.from(plaintextNode.content, 'binary')
1051
+ : Buffer.from(plaintextNode.content);
1052
+ const messageProto = proto.Message.decode(contentBuf);
1053
+ const fullMessage = proto.WebMessageInfo.create({
1054
+ key: {
1055
+ remoteJid: from,
1056
+ id: child.attrs.message_id || child.attrs.server_id,
1057
+ fromMe: false
1058
+ },
1059
+ message: messageProto,
1060
+ messageTimestamp: +child.attrs.t
1061
+ });
1062
+ await upsertMessage(fullMessage, 'append');
1063
+ logger.info('Processed plaintext newsletter message');
1064
+ }
1065
+ catch (error) {
1066
+ logger.error({ error }, 'Failed to decode plaintext newsletter message');
1067
+ }
1068
+ }
1069
+ break;
1070
+ default:
1071
+ logger.warn({ node }, 'Unknown newsletter notification');
1072
+ break;
1073
+ }
1074
+ }
1075
+ // Handles mex newsletter notifications
1076
+ async function handleMexNewsletterNotification(node) {
1077
+ const mexNode = getBinaryNodeChild(node, 'mex');
1078
+ if (!mexNode?.content) {
1079
+ logger.warn({ node }, 'Invalid mex newsletter notification');
1080
+ return;
1081
+ }
1082
+ let data;
1083
+ try {
1084
+ data = JSON.parse(mexNode.content.toString());
1085
+ }
1086
+ catch (error) {
1087
+ logger.error({ err: error, node }, 'Failed to parse mex newsletter notification');
1088
+ return;
1089
+ }
1090
+ const operation = data?.operation;
1091
+ const updates = data?.updates;
1092
+ if (!updates || !operation) {
1093
+ logger.warn({ data }, 'Invalid mex newsletter notification content');
1094
+ return;
1095
+ }
1096
+ logger.info({ operation, updates }, 'got mex newsletter notification');
1097
+ switch (operation) {
1098
+ case 'NotificationNewsletterUpdate':
1099
+ for (const update of updates) {
1100
+ if (update.jid && update.settings && Object.keys(update.settings).length > 0) {
1101
+ ev.emit('newsletter-settings.update', {
1102
+ id: update.jid,
1103
+ update: update.settings
1104
+ });
1105
+ }
1106
+ }
1107
+ break;
1108
+ case 'NotificationNewsletterAdminPromote':
1109
+ for (const update of updates) {
1110
+ if (update.jid && update.user) {
1111
+ ev.emit('newsletter-participants.update', {
1112
+ id: update.jid,
1113
+ author: node.attrs.from,
1114
+ user: update.user,
1115
+ new_role: 'ADMIN',
1116
+ action: 'promote'
1117
+ });
1118
+ }
1119
+ }
1120
+ break;
1121
+ default:
1122
+ logger.info({ operation, data }, 'Unhandled mex newsletter notification');
1123
+ break;
1124
+ }
1125
+ }
1225
1126
  // recv a message
1226
1127
  ws.on('CB:message', (node) => {
1227
1128
  processNode('message', node, 'processing message', handleMessage);
@@ -1281,8 +1182,7 @@ export const makeMessagesRecvSocket = (config) => {
1281
1182
  sendRetryRequest,
1282
1183
  rejectCall,
1283
1184
  fetchMessageHistory,
1284
- requestPlaceholderResend,
1285
- messageRetryManager
1185
+ requestPlaceholderResend
1286
1186
  };
1287
1187
  };
1288
1188
  //# sourceMappingURL=messages-recv.js.map