@periskope/baileys 7.0.0-alpha-5 → 7.0.0-beta-2

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 (146) hide show
  1. package/README.md +0 -6
  2. package/lib/Defaults/index.d.ts +1 -7
  3. package/lib/Defaults/index.d.ts.map +1 -1
  4. package/lib/Defaults/index.js +4 -11
  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/sender-chain-key.d.ts +1 -1
  14. package/lib/Signal/Group/sender-chain-key.d.ts.map +1 -1
  15. package/lib/Signal/Group/sender-chain-key.js +3 -9
  16. package/lib/Signal/Group/sender-chain-key.js.map +1 -1
  17. package/lib/Signal/Group/sender-key-message.d.ts.map +1 -1
  18. package/lib/Signal/Group/sender-key-message.js +1 -0
  19. package/lib/Signal/Group/sender-key-message.js.map +1 -1
  20. package/lib/Signal/Group/sender-key-state.d.ts +4 -4
  21. package/lib/Signal/Group/sender-key-state.d.ts.map +1 -1
  22. package/lib/Signal/Group/sender-key-state.js +19 -47
  23. package/lib/Signal/Group/sender-key-state.js.map +1 -1
  24. package/lib/Signal/Group/sender-message-key.d.ts.map +1 -1
  25. package/lib/Signal/Group/sender-message-key.js +1 -0
  26. package/lib/Signal/Group/sender-message-key.js.map +1 -1
  27. package/lib/Signal/libsignal.d.ts +2 -6
  28. package/lib/Signal/libsignal.d.ts.map +1 -1
  29. package/lib/Signal/libsignal.js +41 -224
  30. package/lib/Signal/libsignal.js.map +1 -1
  31. package/lib/Signal/lid-mapping.d.ts +2 -11
  32. package/lib/Signal/lid-mapping.d.ts.map +1 -1
  33. package/lib/Signal/lid-mapping.js +34 -91
  34. package/lib/Signal/lid-mapping.js.map +1 -1
  35. package/lib/Socket/business.d.ts +14 -26
  36. package/lib/Socket/business.d.ts.map +1 -1
  37. package/lib/Socket/business.js +1 -122
  38. package/lib/Socket/business.js.map +1 -1
  39. package/lib/Socket/chats.d.ts +9 -15
  40. package/lib/Socket/chats.d.ts.map +1 -1
  41. package/lib/Socket/chats.js +20 -53
  42. package/lib/Socket/chats.js.map +1 -1
  43. package/lib/Socket/communities.d.ts +11 -25
  44. package/lib/Socket/communities.d.ts.map +1 -1
  45. package/lib/Socket/communities.js +0 -44
  46. package/lib/Socket/communities.js.map +1 -1
  47. package/lib/Socket/groups.d.ts +9 -14
  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 +11 -25
  52. package/lib/Socket/index.d.ts.map +1 -1
  53. package/lib/Socket/messages-recv.d.ts +11 -19
  54. package/lib/Socket/messages-recv.d.ts.map +1 -1
  55. package/lib/Socket/messages-recv.js +230 -396
  56. package/lib/Socket/messages-recv.js.map +1 -1
  57. package/lib/Socket/messages-send.d.ts +11 -20
  58. package/lib/Socket/messages-send.d.ts.map +1 -1
  59. package/lib/Socket/messages-send.js +71 -443
  60. package/lib/Socket/messages-send.js.map +1 -1
  61. package/lib/Socket/newsletter.d.ts +12 -16
  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 +3 -10
  66. package/lib/Socket/socket.d.ts.map +1 -1
  67. package/lib/Socket/socket.js +69 -264
  68. package/lib/Socket/socket.js.map +1 -1
  69. package/lib/Types/Auth.d.ts +1 -2
  70. package/lib/Types/Auth.d.ts.map +1 -1
  71. package/lib/Types/Chat.d.ts +0 -5
  72. package/lib/Types/Chat.d.ts.map +1 -1
  73. package/lib/Types/Chat.js.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 +7 -28
  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 -28
  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 -19
  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 -13
  100. package/lib/Utils/decode-wa-message.d.ts.map +1 -1
  101. package/lib/Utils/decode-wa-message.js +13 -84
  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 +1 -4
  121. package/lib/Utils/messages-media.js.map +1 -1
  122. package/lib/Utils/messages.d.ts.map +1 -1
  123. package/lib/Utils/messages.js +4 -24
  124. package/lib/Utils/messages.js.map +1 -1
  125. package/lib/Utils/process-message.d.ts +2 -3
  126. package/lib/Utils/process-message.d.ts.map +1 -1
  127. package/lib/Utils/process-message.js +3 -13
  128. package/lib/Utils/process-message.js.map +1 -1
  129. package/lib/Utils/signal.d.ts +2 -2
  130. package/lib/Utils/signal.d.ts.map +1 -1
  131. package/lib/Utils/signal.js.map +1 -1
  132. package/lib/Utils/validate-connection.d.ts.map +1 -1
  133. package/lib/Utils/validate-connection.js +2 -3
  134. package/lib/Utils/validate-connection.js.map +1 -1
  135. package/lib/WABinary/jid-utils.d.ts +5 -6
  136. package/lib/WABinary/jid-utils.d.ts.map +1 -1
  137. package/lib/WABinary/jid-utils.js +5 -11
  138. package/lib/WABinary/jid-utils.js.map +1 -1
  139. package/lib/WAM/encode.d.ts.map +1 -1
  140. package/lib/WAM/encode.js +1 -0
  141. package/lib/WAM/encode.js.map +1 -1
  142. package/lib/WAUSync/Protocols/UsyncLIDProtocol.d.ts +1 -2
  143. package/lib/WAUSync/Protocols/UsyncLIDProtocol.d.ts.map +1 -1
  144. package/lib/WAUSync/Protocols/UsyncLIDProtocol.js +2 -10
  145. package/lib/WAUSync/Protocols/UsyncLIDProtocol.js.map +1 -1
  146. package/package.json +8 -12
@@ -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,32 @@ 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;
480
+ const retryCount = msgRetryCache.get(key) || 0;
721
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
- if (!ids[i])
789
- continue;
790
- if (msg && (await willSendMessageAgain(ids[i], participant))) {
503
+ if (msg) {
791
504
  updateSendMessageAgainCount(ids[i], participant);
792
505
  const msgRelayOpts = { messageId: ids[i] };
793
506
  if (sendToAll) {
@@ -835,7 +548,7 @@ export const makeMessagesRecvSocket = (config) => {
835
548
  if (typeof status !== 'undefined' &&
836
549
  // basically, we only want to know when a message from us has been delivered to/read by the other person
837
550
  // or another device of ours has read some messages
838
- status >= proto.WebMessageInfo.Status.SERVER_ACK) {
551
+ (status >= proto.WebMessageInfo.Status.SERVER_ACK)) {
839
552
  if (isJidGroup(remoteJid) || isJidStatusBroadcast(remoteJid)) {
840
553
  const updateKey = status === proto.WebMessageInfo.Status.DELIVERY_ACK ? 'receiptTimestamp' : 'readTimestamp';
841
554
  if (attrs.participant) {
@@ -877,16 +590,15 @@ export const makeMessagesRecvSocket = (config) => {
877
590
  // correctly set who is asking for the retry
878
591
  key.participant = key.participant || attrs.from;
879
592
  const retryNode = getBinaryNodeChild(node, 'retry');
880
- if (ids[0] && key.participant && (await willSendMessageAgain(ids[0], key.participant))) {
593
+ if (willSendMessageAgain(ids[0], key.participant)) {
881
594
  if (key.fromMe) {
882
595
  try {
883
- updateSendMessageAgainCount(ids[0], key.participant);
884
596
  logger.debug({ attrs, key }, 'recv retry request');
885
597
  await sendMessagesAgain(key, ids, retryNode);
886
598
  ev.emit('messages.retry', { key, ids, retryNode });
887
599
  }
888
600
  catch (error) {
889
- logger.error({ key, ids, trace: error instanceof Error ? error.stack : 'Unknown error' }, 'error in sending message again');
601
+ logger.error({ key, ids, trace: error.stack }, 'error in sending message again');
890
602
  }
891
603
  }
892
604
  else {
@@ -946,22 +658,22 @@ export const makeMessagesRecvSocket = (config) => {
946
658
  // TODO: temporary fix for crashes and issues resulting of failed msmsg decryption
947
659
  if (encNode && encNode.attrs.type === 'msmsg') {
948
660
  logger.debug({ key: node.attrs.key }, 'ignored msmsg');
949
- await sendMessageAck(node, NACK_REASONS.MissingMessageSecret);
661
+ await sendMessageAck(node);
950
662
  return;
951
663
  }
952
664
  let response;
953
665
  if (getBinaryNodeChild(node, 'unavailable') && !encNode) {
954
666
  await sendMessageAck(node);
955
667
  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
668
+ response = await requestPlaceholderResend(key);
957
669
  if (response === 'RESOLVED') {
958
670
  return;
959
671
  }
960
672
  logger.debug('received unavailable message, acked and requested resend from phone');
961
673
  }
962
674
  else {
963
- if (await placeholderResendCache.get(node.attrs.id)) {
964
- await placeholderResendCache.del(node.attrs.id);
675
+ if (placeholderResendCache.get(node.attrs.id)) {
676
+ placeholderResendCache.del(node.attrs.id);
965
677
  }
966
678
  }
967
679
  const { fullMessage: msg, category, author, decrypt } = decryptMessageNode(node, authState.creds.me.id, authState.creds.me.lid || '', signalRepository, logger);
@@ -970,35 +682,7 @@ export const makeMessagesRecvSocket = (config) => {
970
682
  }
971
683
  if (msg.message?.protocolMessage?.type === proto.Message.ProtocolMessage.Type.SHARE_PHONE_NUMBER &&
972
684
  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 }]);
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
- 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
- ]);
986
- }
987
- }
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
- }
994
- }
995
- }
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');
685
+ ev.emit('chats.phoneNumberShare', { lid: node.attrs.from, jid: node.attrs.sender_pn });
1002
686
  }
1003
687
  try {
1004
688
  await Promise.all([
@@ -1009,49 +693,19 @@ export const makeMessagesRecvSocket = (config) => {
1009
693
  if (msg?.messageStubParameters?.[0] === MISSING_KEYS_ERROR_TEXT) {
1010
694
  return sendMessageAck(node, NACK_REASONS.ParsingError);
1011
695
  }
1012
- const errorMessage = msg?.messageStubParameters?.[0] || '';
1013
- const isPreKeyError = errorMessage.includes('PreKey');
1014
- logger.debug(`[handleMessage] Attempting retry request for failed decryption`);
1015
- // Handle both pre-key and normal retries in single mutex
1016
696
  retryMutex.mutex(async () => {
1017
- try {
1018
- if (!ws.isOpen) {
1019
- logger.debug({ node }, 'Connection closed, skipping retry');
1020
- return;
1021
- }
697
+ if (ws.isOpen) {
1022
698
  if (getBinaryNodeChild(node, 'unavailable')) {
1023
- logger.debug('Message unavailable, skipping retry');
1024
699
  return;
1025
700
  }
1026
- // Handle pre-key errors with upload and delay
1027
- if (isPreKeyError) {
1028
- logger.info({ error: errorMessage }, 'PreKey error detected, uploading and retrying');
1029
- try {
1030
- logger.debug('Uploading pre-keys for error recovery');
1031
- await uploadPreKeys(5);
1032
- logger.debug('Waiting for server to process new pre-keys');
1033
- await delay(1000);
1034
- }
1035
- catch (uploadErr) {
1036
- logger.error({ uploadErr }, 'Pre-key upload failed, proceeding with retry anyway');
1037
- }
1038
- }
1039
701
  const encNode = getBinaryNodeChild(node, 'enc');
1040
702
  await sendRetryRequest(node, !encNode);
1041
703
  if (retryRequestDelayMs) {
1042
704
  await delay(retryRequestDelayMs);
1043
705
  }
1044
706
  }
1045
- catch (err) {
1046
- logger.error({ err, isPreKeyError }, 'Failed to handle retry, attempting basic retry');
1047
- // Still attempt retry even if pre-key upload failed
1048
- try {
1049
- const encNode = getBinaryNodeChild(node, 'enc');
1050
- await sendRetryRequest(node, !encNode);
1051
- }
1052
- catch (retryErr) {
1053
- logger.error({ retryErr }, 'Failed to send retry after error handling');
1054
- }
707
+ else {
708
+ logger.debug({ node }, 'connection closed, ignoring retry req');
1055
709
  }
1056
710
  });
1057
711
  }
@@ -1067,8 +721,8 @@ export const makeMessagesRecvSocket = (config) => {
1067
721
  // message was sent by us from a different device
1068
722
  type = 'sender';
1069
723
  // 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
724
+ if (isJidUser(msg.key.remoteJid)) {
725
+ participant = author;
1072
726
  }
1073
727
  }
1074
728
  else if (!sendActiveReceipts) {
@@ -1092,6 +746,54 @@ export const makeMessagesRecvSocket = (config) => {
1092
746
  logger.error({ error, node }, 'error in handling message');
1093
747
  }
1094
748
  };
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
+ };
1095
797
  const handleCall = async (node) => {
1096
798
  let status;
1097
799
  const { attrs } = node;
@@ -1103,7 +805,7 @@ export const makeMessagesRecvSocket = (config) => {
1103
805
  const from = infoChild.attrs.from || infoChild.attrs['call-creator'];
1104
806
  status = getCallStatusFromNode(infoChild);
1105
807
  if (isLidUser(from) && infoChild.tag === 'relaylatency') {
1106
- const verify = await callOfferCache.get(callId);
808
+ const verify = callOfferCache.get(callId);
1107
809
  if (!verify) {
1108
810
  status = 'offer';
1109
811
  const callLid = {
@@ -1114,7 +816,7 @@ export const makeMessagesRecvSocket = (config) => {
1114
816
  offline: !!attrs.offline,
1115
817
  status
1116
818
  };
1117
- await callOfferCache.set(callId, callLid);
819
+ callOfferCache.set(callId, callLid);
1118
820
  }
1119
821
  }
1120
822
  const call = {
@@ -1129,9 +831,9 @@ export const makeMessagesRecvSocket = (config) => {
1129
831
  call.isVideo = !!getBinaryNodeChild(infoChild, 'video');
1130
832
  call.isGroup = infoChild.attrs.type === 'group' || !!infoChild.attrs['group-jid'];
1131
833
  call.groupJid = infoChild.attrs['group-jid'];
1132
- await callOfferCache.set(call.id, call);
834
+ callOfferCache.set(call.id, call);
1133
835
  }
1134
- const existingCall = await callOfferCache.get(call.id);
836
+ const existingCall = callOfferCache.get(call.id);
1135
837
  // use existing call info to populate this event
1136
838
  if (existingCall) {
1137
839
  call.isVideo = existingCall.isVideo;
@@ -1139,7 +841,7 @@ export const makeMessagesRecvSocket = (config) => {
1139
841
  }
1140
842
  // delete data once call has ended
1141
843
  if (status === 'reject' || status === 'accept' || status === 'timeout' || status === 'terminate') {
1142
- await callOfferCache.del(call.id);
844
+ callOfferCache.del(call.id);
1143
845
  }
1144
846
  ev.emit('call', [call]);
1145
847
  await sendMessageAck(node);
@@ -1225,6 +927,139 @@ export const makeMessagesRecvSocket = (config) => {
1225
927
  processNodeWithBuffer(node, identifier, exec);
1226
928
  }
1227
929
  };
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.create({
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
+ }
1228
1063
  // recv a message
1229
1064
  ws.on('CB:message', (node) => {
1230
1065
  processNode('message', node, 'processing message', handleMessage);
@@ -1284,8 +1119,7 @@ export const makeMessagesRecvSocket = (config) => {
1284
1119
  sendRetryRequest,
1285
1120
  rejectCall,
1286
1121
  fetchMessageHistory,
1287
- requestPlaceholderResend,
1288
- messageRetryManager
1122
+ requestPlaceholderResend
1289
1123
  };
1290
1124
  };
1291
1125
  //# sourceMappingURL=messages-recv.js.map