@periskope/baileys 7.0.0-alpha-5 → 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 (145) 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 +2 -6
  32. package/lib/Signal/libsignal.d.ts.map +1 -1
  33. package/lib/Signal/libsignal.js +92 -137
  34. package/lib/Signal/libsignal.js.map +1 -1
  35. package/lib/Signal/lid-mapping.d.ts +2 -11
  36. package/lib/Signal/lid-mapping.d.ts.map +1 -1
  37. package/lib/Signal/lid-mapping.js +34 -91
  38. package/lib/Signal/lid-mapping.js.map +1 -1
  39. package/lib/Socket/business.d.ts +7 -8
  40. package/lib/Socket/business.d.ts.map +1 -1
  41. package/lib/Socket/chats.d.ts +7 -7
  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 +7 -8
  46. package/lib/Socket/communities.d.ts.map +1 -1
  47. package/lib/Socket/groups.d.ts +7 -7
  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 +7 -8
  52. package/lib/Socket/index.d.ts.map +1 -1
  53. package/lib/Socket/messages-recv.d.ts +7 -8
  54. package/lib/Socket/messages-recv.d.ts.map +1 -1
  55. package/lib/Socket/messages-recv.js +254 -357
  56. package/lib/Socket/messages-recv.js.map +1 -1
  57. package/lib/Socket/messages-send.d.ts +7 -9
  58. package/lib/Socket/messages-send.d.ts.map +1 -1
  59. package/lib/Socket/messages-send.js +110 -94
  60. package/lib/Socket/messages-send.js.map +1 -1
  61. package/lib/Socket/newsletter.d.ts +10 -9
  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 +1 -8
  66. package/lib/Socket/socket.d.ts.map +1 -1
  67. package/lib/Socket/socket.js +52 -125
  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 +5 -13
  85. package/lib/Types/Signal.d.ts.map +1 -1
  86. package/lib/Types/Socket.d.ts +7 -23
  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 +2 -8
  100. package/lib/Utils/decode-wa-message.d.ts.map +1 -1
  101. package/lib/Utils/decode-wa-message.js +28 -51
  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 +0 -1
  108. package/lib/Utils/generics.d.ts.map +1 -1
  109. package/lib/Utils/generics.js +8 -24
  110. package/lib/Utils/generics.js.map +1 -1
  111. package/lib/Utils/history.d.ts.map +1 -1
  112. package/lib/Utils/history.js +2 -1
  113. package/lib/Utils/history.js.map +1 -1
  114. package/lib/Utils/index.d.ts +0 -1
  115. package/lib/Utils/index.d.ts.map +1 -1
  116. package/lib/Utils/index.js +0 -1
  117. package/lib/Utils/index.js.map +1 -1
  118. package/lib/Utils/messages-media.d.ts +2 -3
  119. package/lib/Utils/messages-media.d.ts.map +1 -1
  120. package/lib/Utils/messages-media.js.map +1 -1
  121. package/lib/Utils/messages.d.ts.map +1 -1
  122. package/lib/Utils/messages.js +3 -3
  123. package/lib/Utils/messages.js.map +1 -1
  124. package/lib/Utils/process-message.d.ts +2 -3
  125. package/lib/Utils/process-message.d.ts.map +1 -1
  126. package/lib/Utils/process-message.js +3 -13
  127. package/lib/Utils/process-message.js.map +1 -1
  128. package/lib/Utils/signal.d.ts +2 -2
  129. package/lib/Utils/signal.d.ts.map +1 -1
  130. package/lib/Utils/signal.js.map +1 -1
  131. package/lib/Utils/validate-connection.d.ts.map +1 -1
  132. package/lib/Utils/validate-connection.js +2 -3
  133. package/lib/Utils/validate-connection.js.map +1 -1
  134. package/lib/WABinary/jid-utils.d.ts +5 -6
  135. package/lib/WABinary/jid-utils.d.ts.map +1 -1
  136. package/lib/WABinary/jid-utils.js +5 -11
  137. package/lib/WABinary/jid-utils.js.map +1 -1
  138. package/lib/WAM/encode.d.ts.map +1 -1
  139. package/lib/WAM/encode.js +1 -0
  140. package/lib/WAM/encode.js.map +1 -1
  141. package/lib/WAUSync/Protocols/UsyncLIDProtocol.d.ts +1 -2
  142. package/lib/WAUSync/Protocols/UsyncLIDProtocol.d.ts.map +1 -1
  143. package/lib/WAUSync/Protocols/UsyncLIDProtocol.js +2 -10
  144. package/lib/WAUSync/Protocols/UsyncLIDProtocol.js.map +1 -1
  145. 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,36 +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.lidMapping.storeLIDPNMappings([{ 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
- if (altServer === 'lid') {
982
- if (typeof (await signalRepository.lidMapping.getPNForLID(alt)) === 'string') {
983
- await signalRepository.lidMapping.storeLIDPNMappings([
984
- { lid: alt, pn: msg.key.participant || msg.key.remoteJid }
985
- ]);
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
+ }
986
714
  }
987
715
  }
988
- else {
989
- if (typeof (await signalRepository.lidMapping.getLIDForPN(alt)) === 'string') {
990
- await signalRepository.lidMapping.storeLIDPNMappings([
991
- { lid: msg.key.participant || msg.key.remoteJid, pn: alt }
992
- ]);
993
- }
716
+ catch (error) {
717
+ logger.error({ error }, 'Failed to process LID migration sync message');
994
718
  }
995
719
  }
996
- if (msg.key?.remoteJid && msg.key?.id && messageRetryManager) {
997
- messageRetryManager.addRecentMessage(msg.key.remoteJid, msg.key.id, msg.message);
998
- logger.debug({
999
- jid: msg.key.remoteJid,
1000
- id: msg.key.id
1001
- }, 'Added message to recent cache for retry receipts');
1002
- }
1003
720
  try {
1004
721
  await Promise.all([
1005
722
  processingMutex.mutex(async () => {
@@ -1011,7 +728,7 @@ export const makeMessagesRecvSocket = (config) => {
1011
728
  }
1012
729
  const errorMessage = msg?.messageStubParameters?.[0] || '';
1013
730
  const isPreKeyError = errorMessage.includes('PreKey');
1014
- logger.debug(`[handleMessage] Attempting retry request for failed decryption`);
731
+ console.debug(`[handleMessage] Attempting retry request for failed decryption`);
1015
732
  // Handle both pre-key and normal retries in single mutex
1016
733
  retryMutex.mutex(async () => {
1017
734
  try {
@@ -1067,8 +784,8 @@ export const makeMessagesRecvSocket = (config) => {
1067
784
  // message was sent by us from a different device
1068
785
  type = 'sender';
1069
786
  // need to specially handle this case
1070
- if (isLidUser(msg.key.remoteJid) || isLidUser(msg.key.remoteJidAlt)) {
1071
- participant = author; // TODO: investigate sending receipts to LIDs and not PNs
787
+ if (isJidUser(msg.key.remoteJid)) {
788
+ participant = author;
1072
789
  }
1073
790
  }
1074
791
  else if (!sendActiveReceipts) {
@@ -1092,6 +809,54 @@ export const makeMessagesRecvSocket = (config) => {
1092
809
  logger.error({ error, node }, 'error in handling message');
1093
810
  }
1094
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
+ };
1095
860
  const handleCall = async (node) => {
1096
861
  let status;
1097
862
  const { attrs } = node;
@@ -1103,7 +868,7 @@ export const makeMessagesRecvSocket = (config) => {
1103
868
  const from = infoChild.attrs.from || infoChild.attrs['call-creator'];
1104
869
  status = getCallStatusFromNode(infoChild);
1105
870
  if (isLidUser(from) && infoChild.tag === 'relaylatency') {
1106
- const verify = await callOfferCache.get(callId);
871
+ const verify = callOfferCache.get(callId);
1107
872
  if (!verify) {
1108
873
  status = 'offer';
1109
874
  const callLid = {
@@ -1114,7 +879,7 @@ export const makeMessagesRecvSocket = (config) => {
1114
879
  offline: !!attrs.offline,
1115
880
  status
1116
881
  };
1117
- await callOfferCache.set(callId, callLid);
882
+ callOfferCache.set(callId, callLid);
1118
883
  }
1119
884
  }
1120
885
  const call = {
@@ -1129,9 +894,9 @@ export const makeMessagesRecvSocket = (config) => {
1129
894
  call.isVideo = !!getBinaryNodeChild(infoChild, 'video');
1130
895
  call.isGroup = infoChild.attrs.type === 'group' || !!infoChild.attrs['group-jid'];
1131
896
  call.groupJid = infoChild.attrs['group-jid'];
1132
- await callOfferCache.set(call.id, call);
897
+ callOfferCache.set(call.id, call);
1133
898
  }
1134
- const existingCall = await callOfferCache.get(call.id);
899
+ const existingCall = callOfferCache.get(call.id);
1135
900
  // use existing call info to populate this event
1136
901
  if (existingCall) {
1137
902
  call.isVideo = existingCall.isVideo;
@@ -1139,7 +904,7 @@ export const makeMessagesRecvSocket = (config) => {
1139
904
  }
1140
905
  // delete data once call has ended
1141
906
  if (status === 'reject' || status === 'accept' || status === 'timeout' || status === 'terminate') {
1142
- await callOfferCache.del(call.id);
907
+ callOfferCache.del(call.id);
1143
908
  }
1144
909
  ev.emit('call', [call]);
1145
910
  await sendMessageAck(node);
@@ -1225,6 +990,139 @@ export const makeMessagesRecvSocket = (config) => {
1225
990
  processNodeWithBuffer(node, identifier, exec);
1226
991
  }
1227
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
+ }
1228
1126
  // recv a message
1229
1127
  ws.on('CB:message', (node) => {
1230
1128
  processNode('message', node, 'processing message', handleMessage);
@@ -1284,8 +1182,7 @@ export const makeMessagesRecvSocket = (config) => {
1284
1182
  sendRetryRequest,
1285
1183
  rejectCall,
1286
1184
  fetchMessageHistory,
1287
- requestPlaceholderResend,
1288
- messageRetryManager
1185
+ requestPlaceholderResend
1289
1186
  };
1290
1187
  };
1291
1188
  //# sourceMappingURL=messages-recv.js.map