@d0v3riz/baileys 6.7.25 → 7.0.0-rc.8

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 (218) hide show
  1. package/README.md +53 -50
  2. package/WAProto/GenerateStatics.sh +2 -2
  3. package/WAProto/WAProto.proto +1339 -493
  4. package/WAProto/fix-imports.js +1 -1
  5. package/WAProto/index.d.ts +4585 -44041
  6. package/WAProto/index.js +80831 -148208
  7. package/lib/Defaults/index.d.ts +13 -3
  8. package/lib/Defaults/index.d.ts.map +1 -1
  9. package/lib/Defaults/index.js +27 -13
  10. package/lib/Defaults/index.js.map +1 -1
  11. package/lib/Signal/Group/group_cipher.d.ts +1 -2
  12. package/lib/Signal/Group/group_cipher.d.ts.map +1 -1
  13. package/lib/Signal/Group/group_cipher.js +29 -41
  14. package/lib/Signal/Group/group_cipher.js.map +1 -1
  15. package/lib/Signal/Group/keyhelper.d.ts.map +1 -1
  16. package/lib/Signal/Group/keyhelper.js +0 -1
  17. package/lib/Signal/Group/keyhelper.js.map +1 -1
  18. package/lib/Signal/Group/sender-chain-key.d.ts +1 -1
  19. package/lib/Signal/Group/sender-chain-key.d.ts.map +1 -1
  20. package/lib/Signal/Group/sender-chain-key.js +1 -7
  21. package/lib/Signal/Group/sender-chain-key.js.map +1 -1
  22. package/lib/Signal/Group/sender-key-distribution-message.js +2 -2
  23. package/lib/Signal/Group/sender-key-distribution-message.js.map +1 -1
  24. package/lib/Signal/Group/sender-key-message.d.ts.map +1 -1
  25. package/lib/Signal/Group/sender-key-message.js +0 -1
  26. package/lib/Signal/Group/sender-key-message.js.map +1 -1
  27. package/lib/Signal/Group/sender-key-record.d.ts +1 -1
  28. package/lib/Signal/Group/sender-key-record.d.ts.map +1 -1
  29. package/lib/Signal/Group/sender-key-record.js +2 -11
  30. package/lib/Signal/Group/sender-key-record.js.map +1 -1
  31. package/lib/Signal/Group/sender-key-state.d.ts +4 -4
  32. package/lib/Signal/Group/sender-key-state.d.ts.map +1 -1
  33. package/lib/Signal/Group/sender-key-state.js +21 -33
  34. package/lib/Signal/Group/sender-key-state.js.map +1 -1
  35. package/lib/Signal/Group/sender-message-key.d.ts.map +1 -1
  36. package/lib/Signal/Group/sender-message-key.js +0 -1
  37. package/lib/Signal/Group/sender-message-key.js.map +1 -1
  38. package/lib/Signal/libsignal.d.ts +4 -3
  39. package/lib/Signal/libsignal.d.ts.map +1 -1
  40. package/lib/Signal/libsignal.js +243 -42
  41. package/lib/Signal/libsignal.js.map +1 -1
  42. package/lib/Signal/lid-mapping.d.ts +23 -0
  43. package/lib/Signal/lid-mapping.d.ts.map +1 -0
  44. package/lib/Signal/lid-mapping.js +171 -0
  45. package/lib/Signal/lid-mapping.js.map +1 -0
  46. package/lib/Socket/Client/types.d.ts +2 -2
  47. package/lib/Socket/Client/types.d.ts.map +1 -1
  48. package/lib/Socket/Client/websocket.d.ts +2 -2
  49. package/lib/Socket/Client/websocket.d.ts.map +1 -1
  50. package/lib/Socket/Client/websocket.js +2 -2
  51. package/lib/Socket/Client/websocket.js.map +1 -1
  52. package/lib/Socket/business.d.ts +31 -17
  53. package/lib/Socket/business.d.ts.map +1 -1
  54. package/lib/Socket/business.js +122 -1
  55. package/lib/Socket/business.js.map +1 -1
  56. package/lib/Socket/chats.d.ts +17 -9
  57. package/lib/Socket/chats.d.ts.map +1 -1
  58. package/lib/Socket/chats.js +54 -23
  59. package/lib/Socket/chats.js.map +1 -1
  60. package/lib/Socket/communities.d.ts +41 -14
  61. package/lib/Socket/communities.d.ts.map +1 -1
  62. package/lib/Socket/communities.js +83 -3
  63. package/lib/Socket/communities.js.map +1 -1
  64. package/lib/Socket/groups.d.ts +16 -9
  65. package/lib/Socket/groups.d.ts.map +1 -1
  66. package/lib/Socket/groups.js +14 -11
  67. package/lib/Socket/groups.js.map +1 -1
  68. package/lib/Socket/index.d.ts +41 -14
  69. package/lib/Socket/index.d.ts.map +1 -1
  70. package/lib/Socket/index.js +1 -0
  71. package/lib/Socket/index.js.map +1 -1
  72. package/lib/Socket/messages-recv.d.ts +26 -16
  73. package/lib/Socket/messages-recv.d.ts.map +1 -1
  74. package/lib/Socket/messages-recv.js +512 -311
  75. package/lib/Socket/messages-recv.js.map +1 -1
  76. package/lib/Socket/messages-send.d.ts +27 -16
  77. package/lib/Socket/messages-send.d.ts.map +1 -1
  78. package/lib/Socket/messages-send.js +394 -133
  79. package/lib/Socket/messages-send.js.map +1 -1
  80. package/lib/Socket/newsletter.d.ts +18 -12
  81. package/lib/Socket/newsletter.d.ts.map +1 -1
  82. package/lib/Socket/newsletter.js +3 -1
  83. package/lib/Socket/newsletter.js.map +1 -1
  84. package/lib/Socket/socket.d.ts +13 -3
  85. package/lib/Socket/socket.d.ts.map +1 -1
  86. package/lib/Socket/socket.js +353 -78
  87. package/lib/Socket/socket.js.map +1 -1
  88. package/lib/Types/Auth.d.ts +12 -1
  89. package/lib/Types/Auth.d.ts.map +1 -1
  90. package/lib/Types/Bussines.d.ts +25 -0
  91. package/lib/Types/Bussines.d.ts.map +1 -0
  92. package/lib/Types/Bussines.js +2 -0
  93. package/lib/Types/Bussines.js.map +1 -0
  94. package/lib/Types/Chat.d.ts +5 -0
  95. package/lib/Types/Chat.d.ts.map +1 -1
  96. package/lib/Types/Chat.js.map +1 -1
  97. package/lib/Types/Contact.d.ts +4 -4
  98. package/lib/Types/Contact.d.ts.map +1 -1
  99. package/lib/Types/Events.d.ts +8 -5
  100. package/lib/Types/Events.d.ts.map +1 -1
  101. package/lib/Types/GroupMetadata.d.ts +7 -4
  102. package/lib/Types/GroupMetadata.d.ts.map +1 -1
  103. package/lib/Types/Message.d.ts +38 -12
  104. package/lib/Types/Message.d.ts.map +1 -1
  105. package/lib/Types/Message.js +5 -1
  106. package/lib/Types/Message.js.map +1 -1
  107. package/lib/Types/Signal.d.ts +18 -0
  108. package/lib/Types/Signal.d.ts.map +1 -1
  109. package/lib/Types/Socket.d.ts +24 -13
  110. package/lib/Types/Socket.d.ts.map +1 -1
  111. package/lib/Types/Socket.js.map +1 -1
  112. package/lib/Utils/auth-utils.d.ts +2 -2
  113. package/lib/Utils/auth-utils.d.ts.map +1 -1
  114. package/lib/Utils/auth-utils.js +178 -111
  115. package/lib/Utils/auth-utils.js.map +1 -1
  116. package/lib/Utils/browser-utils.d.ts +4 -0
  117. package/lib/Utils/browser-utils.d.ts.map +1 -0
  118. package/lib/Utils/browser-utils.js +28 -0
  119. package/lib/Utils/browser-utils.js.map +1 -0
  120. package/lib/Utils/chat-utils.d.ts +6 -7
  121. package/lib/Utils/chat-utils.d.ts.map +1 -1
  122. package/lib/Utils/chat-utils.js +19 -2
  123. package/lib/Utils/chat-utils.js.map +1 -1
  124. package/lib/Utils/crypto.d.ts +6 -6
  125. package/lib/Utils/crypto.d.ts.map +1 -1
  126. package/lib/Utils/crypto.js +16 -12
  127. package/lib/Utils/crypto.js.map +1 -1
  128. package/lib/Utils/decode-wa-message.d.ts +14 -2
  129. package/lib/Utils/decode-wa-message.d.ts.map +1 -1
  130. package/lib/Utils/decode-wa-message.js +104 -15
  131. package/lib/Utils/decode-wa-message.js.map +1 -1
  132. package/lib/Utils/event-buffer.d.ts +0 -1
  133. package/lib/Utils/event-buffer.d.ts.map +1 -1
  134. package/lib/Utils/event-buffer.js +65 -20
  135. package/lib/Utils/event-buffer.js.map +1 -1
  136. package/lib/Utils/generics.d.ts +5 -7
  137. package/lib/Utils/generics.d.ts.map +1 -1
  138. package/lib/Utils/generics.js +59 -51
  139. package/lib/Utils/generics.js.map +1 -1
  140. package/lib/Utils/history.d.ts +8 -9
  141. package/lib/Utils/history.d.ts.map +1 -1
  142. package/lib/Utils/history.js +8 -3
  143. package/lib/Utils/history.js.map +1 -1
  144. package/lib/Utils/index.d.ts +2 -1
  145. package/lib/Utils/index.d.ts.map +1 -1
  146. package/lib/Utils/index.js +2 -1
  147. package/lib/Utils/index.js.map +1 -1
  148. package/lib/Utils/link-preview.d.ts +1 -2
  149. package/lib/Utils/link-preview.d.ts.map +1 -1
  150. package/lib/Utils/link-preview.js +1 -1
  151. package/lib/Utils/link-preview.js.map +1 -1
  152. package/lib/Utils/message-retry-manager.d.ts +82 -0
  153. package/lib/Utils/message-retry-manager.d.ts.map +1 -0
  154. package/lib/Utils/message-retry-manager.js +172 -0
  155. package/lib/Utils/message-retry-manager.js.map +1 -0
  156. package/lib/Utils/messages-media.d.ts +14 -12
  157. package/lib/Utils/messages-media.d.ts.map +1 -1
  158. package/lib/Utils/messages-media.js +48 -24
  159. package/lib/Utils/messages-media.js.map +1 -1
  160. package/lib/Utils/messages.d.ts +17 -4
  161. package/lib/Utils/messages.d.ts.map +1 -1
  162. package/lib/Utils/messages.js +85 -21
  163. package/lib/Utils/messages.js.map +1 -1
  164. package/lib/Utils/noise-handler.js +2 -1
  165. package/lib/Utils/noise-handler.js.map +1 -1
  166. package/lib/Utils/pre-key-manager.d.ts +28 -0
  167. package/lib/Utils/pre-key-manager.d.ts.map +1 -0
  168. package/lib/Utils/pre-key-manager.js +106 -0
  169. package/lib/Utils/pre-key-manager.js.map +1 -0
  170. package/lib/Utils/process-message.d.ts +26 -8
  171. package/lib/Utils/process-message.d.ts.map +1 -1
  172. package/lib/Utils/process-message.js +141 -24
  173. package/lib/Utils/process-message.js.map +1 -1
  174. package/lib/Utils/signal.d.ts +4 -4
  175. package/lib/Utils/signal.d.ts.map +1 -1
  176. package/lib/Utils/signal.js +21 -12
  177. package/lib/Utils/signal.js.map +1 -1
  178. package/lib/Utils/validate-connection.d.ts.map +1 -1
  179. package/lib/Utils/validate-connection.js +50 -14
  180. package/lib/Utils/validate-connection.js.map +1 -1
  181. package/lib/WABinary/decode.d.ts.map +1 -1
  182. package/lib/WABinary/decode.js +12 -2
  183. package/lib/WABinary/decode.js.map +1 -1
  184. package/lib/WABinary/generic-utils.js +1 -1
  185. package/lib/WABinary/generic-utils.js.map +1 -1
  186. package/lib/WABinary/jid-utils.d.ts +18 -6
  187. package/lib/WABinary/jid-utils.d.ts.map +1 -1
  188. package/lib/WABinary/jid-utils.js +52 -8
  189. package/lib/WABinary/jid-utils.js.map +1 -1
  190. package/lib/WAM/constants.d.ts +1 -1
  191. package/lib/WAM/constants.d.ts.map +1 -1
  192. package/lib/WAM/constants.js +18516 -10904
  193. package/lib/WAM/constants.js.map +1 -1
  194. package/lib/WAM/encode.d.ts.map +1 -1
  195. package/lib/WAM/encode.js +0 -1
  196. package/lib/WAM/encode.js.map +1 -1
  197. package/lib/WAUSync/Protocols/UsyncLIDProtocol.d.ts +2 -1
  198. package/lib/WAUSync/Protocols/UsyncLIDProtocol.d.ts.map +1 -1
  199. package/lib/WAUSync/Protocols/UsyncLIDProtocol.js +10 -2
  200. package/lib/WAUSync/Protocols/UsyncLIDProtocol.js.map +1 -1
  201. package/lib/WAUSync/USyncQuery.d.ts +1 -1
  202. package/lib/WAUSync/USyncQuery.d.ts.map +1 -1
  203. package/lib/WAUSync/USyncQuery.js +23 -20
  204. package/lib/WAUSync/USyncQuery.js.map +1 -1
  205. package/package.json +16 -11
  206. package/lib/Defaults/baileys-version.json +0 -3
  207. package/lib/Signal/Group/queue-job.d.ts +0 -2
  208. package/lib/Signal/Group/queue-job.d.ts.map +0 -1
  209. package/lib/Signal/Group/queue-job.js +0 -54
  210. package/lib/Signal/Group/queue-job.js.map +0 -1
  211. package/lib/Socket/usync.d.ts +0 -37
  212. package/lib/Socket/usync.d.ts.map +0 -1
  213. package/lib/Socket/usync.js +0 -63
  214. package/lib/Socket/usync.js.map +0 -1
  215. package/lib/Utils/baileys-event-stream.d.ts +0 -17
  216. package/lib/Utils/baileys-event-stream.d.ts.map +0 -1
  217. package/lib/Utils/baileys-event-stream.js +0 -56
  218. package/lib/Utils/baileys-event-stream.js.map +0 -1
@@ -5,15 +5,15 @@ import Long from 'long';
5
5
  import { proto } from '../../WAProto/index.js';
6
6
  import { DEFAULT_CACHE_TTLS, KEY_BUNDLE_TYPE, MIN_PREKEY_COUNT } from '../Defaults/index.js';
7
7
  import { WAMessageStatus, WAMessageStubType } from '../Types/index.js';
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';
8
+ import { aesDecryptCTR, aesEncryptGCM, cleanMessage, Curve, decodeMediaRetryNode, decodeMessageNode, decryptMessageNode, delay, derivePairingCodeKey, encodeBigEndian, encodeSignedDeviceIdentity, extractAddressingContext, getCallStatusFromNode, getHistoryMsg, getNextPreKeys, getStatusFromReceiptType, hkdf, MISSING_KEYS_ERROR_TEXT, NACK_REASONS, unixTimestampSeconds, xmppPreKey, xmppSignedPreKey } from '../Utils/index.js';
9
9
  import { makeMutex } from '../Utils/make-mutex.js';
10
- import { areJidsSameUser, getAllBinaryNodeChildren, getBinaryNodeChild, getBinaryNodeChildBuffer, getBinaryNodeChildren, getBinaryNodeChildString, isJidGroup, isJidStatusBroadcast, isJidUser, isLidUser, jidDecode, jidNormalizedUser, S_WHATSAPP_NET } from '../WABinary/index.js';
10
+ import { areJidsSameUser, binaryNodeToString, getAllBinaryNodeChildren, getBinaryNodeChild, getBinaryNodeChildBuffer, getBinaryNodeChildren, getBinaryNodeChildString, isJidGroup, isJidNewsletter, isJidStatusBroadcast, isLidUser, isPnUser, jidDecode, jidNormalizedUser, S_WHATSAPP_NET } from '../WABinary/index.js';
11
11
  import { extractGroupMetadata } from './groups.js';
12
12
  import { makeMessagesSocket } from './messages-send.js';
13
13
  export const makeMessagesRecvSocket = (config) => {
14
- const { logger, retryRequestDelayMs, maxMsgRetryCount, getMessage, shouldIgnoreJid } = config;
14
+ const { logger, retryRequestDelayMs, maxMsgRetryCount, getMessage, shouldIgnoreJid, enableAutoSessionRecreation } = config;
15
15
  const sock = makeMessagesSocket(config);
16
- const { ev, authState, ws, processingMutex, signalRepository, query, upsertMessage, resyncAppState, onUnexpectedError, assertSessions, sendNode, relayMessage, sendReceipt, uploadPreKeys, sendPeerDataOperationMessage } = sock;
16
+ const { ev, authState, ws, processingMutex, signalRepository, query, upsertMessage, resyncAppState, onUnexpectedError, assertSessions, sendNode, relayMessage, sendReceipt, uploadPreKeys, sendPeerDataOperationMessage, messageRetryManager } = sock;
17
17
  /** this mutex ensures that each retryRequest will wait for the previous one to finish */
18
18
  const retryMutex = makeMutex();
19
19
  const msgRetryCache = config.msgRetryCounterCache ||
@@ -31,7 +31,190 @@ export const makeMessagesRecvSocket = (config) => {
31
31
  stdTTL: DEFAULT_CACHE_TTLS.MSG_RETRY, // 1 hour
32
32
  useClones: false
33
33
  });
34
+ // Debounce identity-change session refreshes per JID to avoid bursts
35
+ const identityAssertDebounce = new NodeCache({ stdTTL: 5, useClones: false });
34
36
  let sendActiveReceipts = false;
37
+ const fetchMessageHistory = async (count, oldestMsgKey, oldestMsgTimestamp) => {
38
+ if (!authState.creds.me?.id) {
39
+ throw new Boom('Not authenticated');
40
+ }
41
+ const pdoMessage = {
42
+ historySyncOnDemandRequest: {
43
+ chatJid: oldestMsgKey.remoteJid,
44
+ oldestMsgFromMe: oldestMsgKey.fromMe,
45
+ oldestMsgId: oldestMsgKey.id,
46
+ oldestMsgTimestampMs: oldestMsgTimestamp,
47
+ onDemandMsgCount: count
48
+ },
49
+ peerDataOperationRequestType: proto.Message.PeerDataOperationRequestType.HISTORY_SYNC_ON_DEMAND
50
+ };
51
+ return sendPeerDataOperationMessage(pdoMessage);
52
+ };
53
+ const requestPlaceholderResend = async (messageKey) => {
54
+ if (!authState.creds.me?.id) {
55
+ throw new Boom('Not authenticated');
56
+ }
57
+ if (placeholderResendCache.get(messageKey?.id)) {
58
+ logger.debug({ messageKey }, 'already requested resend');
59
+ return;
60
+ }
61
+ else {
62
+ await placeholderResendCache.set(messageKey?.id, true);
63
+ }
64
+ await delay(5000);
65
+ if (!placeholderResendCache.get(messageKey?.id)) {
66
+ logger.debug({ messageKey }, 'message received while resend requested');
67
+ return 'RESOLVED';
68
+ }
69
+ const pdoMessage = {
70
+ placeholderMessageResendRequest: [
71
+ {
72
+ messageKey
73
+ }
74
+ ],
75
+ peerDataOperationRequestType: proto.Message.PeerDataOperationRequestType.PLACEHOLDER_MESSAGE_RESEND
76
+ };
77
+ setTimeout(async () => {
78
+ if (placeholderResendCache.get(messageKey?.id)) {
79
+ logger.debug({ messageKey }, 'PDO message without response after 15 seconds. Phone possibly offline');
80
+ await placeholderResendCache.del(messageKey?.id);
81
+ }
82
+ }, 15000);
83
+ return sendPeerDataOperationMessage(pdoMessage);
84
+ };
85
+ // Handles mex newsletter notifications
86
+ const handleMexNewsletterNotification = async (node) => {
87
+ const mexNode = getBinaryNodeChild(node, 'mex');
88
+ if (!mexNode?.content) {
89
+ logger.warn({ node }, 'Invalid mex newsletter notification');
90
+ return;
91
+ }
92
+ let data;
93
+ try {
94
+ data = JSON.parse(mexNode.content.toString());
95
+ }
96
+ catch (error) {
97
+ logger.error({ err: error, node }, 'Failed to parse mex newsletter notification');
98
+ return;
99
+ }
100
+ const operation = data?.operation;
101
+ const updates = data?.updates;
102
+ if (!updates || !operation) {
103
+ logger.warn({ data }, 'Invalid mex newsletter notification content');
104
+ return;
105
+ }
106
+ logger.info({ operation, updates }, 'got mex newsletter notification');
107
+ switch (operation) {
108
+ case 'NotificationNewsletterUpdate':
109
+ for (const update of updates) {
110
+ if (update.jid && update.settings && Object.keys(update.settings).length > 0) {
111
+ ev.emit('newsletter-settings.update', {
112
+ id: update.jid,
113
+ update: update.settings
114
+ });
115
+ }
116
+ }
117
+ break;
118
+ case 'NotificationNewsletterAdminPromote':
119
+ for (const update of updates) {
120
+ if (update.jid && update.user) {
121
+ ev.emit('newsletter-participants.update', {
122
+ id: update.jid,
123
+ author: node.attrs.from,
124
+ user: update.user,
125
+ new_role: 'ADMIN',
126
+ action: 'promote'
127
+ });
128
+ }
129
+ }
130
+ break;
131
+ default:
132
+ logger.info({ operation, data }, 'Unhandled mex newsletter notification');
133
+ break;
134
+ }
135
+ };
136
+ // Handles newsletter notifications
137
+ const handleNewsletterNotification = async (node) => {
138
+ const from = node.attrs.from;
139
+ const child = getAllBinaryNodeChildren(node)[0];
140
+ const author = node.attrs.participant;
141
+ logger.info({ from, child }, 'got newsletter notification');
142
+ switch (child.tag) {
143
+ case 'reaction':
144
+ const reactionUpdate = {
145
+ id: from,
146
+ server_id: child.attrs.message_id,
147
+ reaction: {
148
+ code: getBinaryNodeChildString(child, 'reaction'),
149
+ count: 1
150
+ }
151
+ };
152
+ ev.emit('newsletter.reaction', reactionUpdate);
153
+ break;
154
+ case 'view':
155
+ const viewUpdate = {
156
+ id: from,
157
+ server_id: child.attrs.message_id,
158
+ count: parseInt(child.content?.toString() || '0', 10)
159
+ };
160
+ ev.emit('newsletter.view', viewUpdate);
161
+ break;
162
+ case 'participant':
163
+ const participantUpdate = {
164
+ id: from,
165
+ author,
166
+ user: child.attrs.jid,
167
+ action: child.attrs.action,
168
+ new_role: child.attrs.role
169
+ };
170
+ ev.emit('newsletter-participants.update', participantUpdate);
171
+ break;
172
+ case 'update':
173
+ const settingsNode = getBinaryNodeChild(child, 'settings');
174
+ if (settingsNode) {
175
+ const update = {};
176
+ const nameNode = getBinaryNodeChild(settingsNode, 'name');
177
+ if (nameNode?.content)
178
+ update.name = nameNode.content.toString();
179
+ const descriptionNode = getBinaryNodeChild(settingsNode, 'description');
180
+ if (descriptionNode?.content)
181
+ update.description = descriptionNode.content.toString();
182
+ ev.emit('newsletter-settings.update', {
183
+ id: from,
184
+ update
185
+ });
186
+ }
187
+ break;
188
+ case 'message':
189
+ const plaintextNode = getBinaryNodeChild(child, 'plaintext');
190
+ if (plaintextNode?.content) {
191
+ try {
192
+ const contentBuf = typeof plaintextNode.content === 'string'
193
+ ? Buffer.from(plaintextNode.content, 'binary')
194
+ : Buffer.from(plaintextNode.content);
195
+ const messageProto = proto.Message.decode(contentBuf).toJSON();
196
+ const fullMessage = proto.WebMessageInfo.fromObject({
197
+ key: {
198
+ remoteJid: from,
199
+ id: child.attrs.message_id || child.attrs.server_id,
200
+ fromMe: false // TODO: is this really true though
201
+ },
202
+ message: messageProto,
203
+ messageTimestamp: +child.attrs.t
204
+ }).toJSON();
205
+ await upsertMessage(fullMessage, 'append');
206
+ logger.info('Processed plaintext newsletter message');
207
+ }
208
+ catch (error) {
209
+ logger.error({ error }, 'Failed to decode plaintext newsletter message');
210
+ }
211
+ }
212
+ break;
213
+ default:
214
+ logger.warn({ node }, 'Unknown newsletter notification');
215
+ break;
216
+ }
217
+ };
35
218
  const sendMessageAck = async ({ tag, attrs, content }, errorCode) => {
36
219
  const stanza = {
37
220
  tag: 'ack',
@@ -85,20 +268,76 @@ export const makeMessagesRecvSocket = (config) => {
85
268
  const { fullMessage } = decodeMessageNode(node, authState.creds.me.id, authState.creds.me.lid || '');
86
269
  const { key: msgKey } = fullMessage;
87
270
  const msgId = msgKey.id;
88
- const key = `${msgId}:${msgKey?.participant}`;
89
- let retryCount = msgRetryCache.get(key) || 0;
90
- if (retryCount >= maxMsgRetryCount) {
91
- logger.debug({ retryCount, msgId }, 'reached retry limit, clearing');
92
- msgRetryCache.del(key);
93
- return;
271
+ if (messageRetryManager) {
272
+ // Check if we've exceeded max retries using the new system
273
+ if (messageRetryManager.hasExceededMaxRetries(msgId)) {
274
+ logger.debug({ msgId }, 'reached retry limit with new retry manager, clearing');
275
+ messageRetryManager.markRetryFailed(msgId);
276
+ return;
277
+ }
278
+ // Increment retry count using new system
279
+ const retryCount = messageRetryManager.incrementRetryCount(msgId);
280
+ // Use the new retry count for the rest of the logic
281
+ const key = `${msgId}:${msgKey?.participant}`;
282
+ await msgRetryCache.set(key, retryCount);
94
283
  }
95
- retryCount += 1;
96
- msgRetryCache.set(key, retryCount);
284
+ else {
285
+ // Fallback to old system
286
+ const key = `${msgId}:${msgKey?.participant}`;
287
+ let retryCount = (await msgRetryCache.get(key)) || 0;
288
+ if (retryCount >= maxMsgRetryCount) {
289
+ logger.debug({ retryCount, msgId }, 'reached retry limit, clearing');
290
+ await msgRetryCache.del(key);
291
+ return;
292
+ }
293
+ retryCount += 1;
294
+ await msgRetryCache.set(key, retryCount);
295
+ }
296
+ const key = `${msgId}:${msgKey?.participant}`;
297
+ const retryCount = (await msgRetryCache.get(key)) || 1;
97
298
  const { account, signedPreKey, signedIdentityKey: identityKey } = authState.creds;
98
- if (retryCount === 1) {
99
- //request a resend via phone
100
- const msgId = await requestPlaceholderResend(msgKey);
101
- logger.debug(`sendRetryRequest: requested placeholder resend for message ${msgId}`);
299
+ const fromJid = node.attrs.from;
300
+ // Check if we should recreate the session
301
+ let shouldRecreateSession = false;
302
+ let recreateReason = '';
303
+ if (enableAutoSessionRecreation && messageRetryManager) {
304
+ try {
305
+ // Check if we have a session with this JID
306
+ const sessionId = signalRepository.jidToSignalProtocolAddress(fromJid);
307
+ const hasSession = await signalRepository.validateSession(fromJid);
308
+ const result = messageRetryManager.shouldRecreateSession(fromJid, retryCount, hasSession.exists);
309
+ shouldRecreateSession = result.recreate;
310
+ recreateReason = result.reason;
311
+ if (shouldRecreateSession) {
312
+ logger.debug({ fromJid, retryCount, reason: recreateReason }, 'recreating session for retry');
313
+ // Delete existing session to force recreation
314
+ await authState.keys.set({ session: { [sessionId]: null } });
315
+ forceIncludeKeys = true;
316
+ }
317
+ }
318
+ catch (error) {
319
+ logger.warn({ error, fromJid }, 'failed to check session recreation');
320
+ }
321
+ }
322
+ if (retryCount <= 2) {
323
+ // Use new retry manager for phone requests if available
324
+ if (messageRetryManager) {
325
+ // Schedule phone request with delay (like whatsmeow)
326
+ messageRetryManager.schedulePhoneRequest(msgId, async () => {
327
+ try {
328
+ const requestId = await requestPlaceholderResend(msgKey);
329
+ logger.debug(`sendRetryRequest: requested placeholder resend (${requestId}) for message ${msgId} (scheduled)`);
330
+ }
331
+ catch (error) {
332
+ logger.warn({ error, msgId }, 'failed to send scheduled phone request');
333
+ }
334
+ });
335
+ }
336
+ else {
337
+ // Fallback to immediate request
338
+ const msgId = await requestPlaceholderResend(msgKey);
339
+ logger.debug(`sendRetryRequest: requested placeholder resend for message ${msgId}`);
340
+ }
102
341
  }
103
342
  const deviceIdentity = encodeSignedDeviceIdentity(account, true);
104
343
  await authState.keys.transaction(async () => {
@@ -116,7 +355,9 @@ export const makeMessagesRecvSocket = (config) => {
116
355
  count: retryCount.toString(),
117
356
  id: node.attrs.id,
118
357
  t: node.attrs.t,
119
- v: '1'
358
+ v: '1',
359
+ // ADD ERROR FIELD
360
+ error: '0'
120
361
  }
121
362
  },
122
363
  {
@@ -132,7 +373,7 @@ export const makeMessagesRecvSocket = (config) => {
132
373
  if (node.attrs.participant) {
133
374
  receipt.attrs.participant = node.attrs.participant;
134
375
  }
135
- if (retryCount > 1 || forceIncludeKeys) {
376
+ if (retryCount > 1 || forceIncludeKeys || shouldRecreateSession) {
136
377
  const { update, preKeys } = await getNextPreKeys(authState, 1);
137
378
  const [keyId] = Object.keys(preKeys);
138
379
  const key = preKeys[+keyId];
@@ -152,7 +393,7 @@ export const makeMessagesRecvSocket = (config) => {
152
393
  }
153
394
  await sendNode(receipt);
154
395
  logger.info({ msgAttrs: node.attrs, retryCount }, 'sent retry receipt');
155
- });
396
+ }, authState?.creds?.me?.id || 'sendRetryRequest');
156
397
  };
157
398
  const handleEncryptNotification = async (node) => {
158
399
  const from = node.attrs.from;
@@ -169,22 +410,35 @@ export const makeMessagesRecvSocket = (config) => {
169
410
  const identityNode = getBinaryNodeChild(node, 'identity');
170
411
  if (identityNode) {
171
412
  logger.info({ jid: from }, 'identity changed');
172
- // not handling right now
173
- // signal will override new identity anyway
413
+ if (identityAssertDebounce.get(from)) {
414
+ logger.debug({ jid: from }, 'skipping identity assert (debounced)');
415
+ return;
416
+ }
417
+ identityAssertDebounce.set(from, true);
418
+ try {
419
+ await assertSessions([from], true);
420
+ }
421
+ catch (error) {
422
+ logger.warn({ error, jid: from }, 'failed to assert sessions after identity change');
423
+ }
174
424
  }
175
425
  else {
176
426
  logger.info({ node }, 'unknown encrypt notification');
177
427
  }
178
428
  }
179
429
  };
180
- const handleGroupNotification = (participant, child, msg) => {
181
- const participantJid = getBinaryNodeChild(child, 'participant')?.attrs?.jid || participant;
430
+ const handleGroupNotification = (fullNode, child, msg) => {
431
+ // TODO: Support PN/LID (Here is only LID now)
432
+ const actingParticipantLid = fullNode.attrs.participant;
433
+ const actingParticipantPn = fullNode.attrs.participant_pn;
434
+ const affectedParticipantLid = getBinaryNodeChild(child, 'participant')?.attrs?.jid || actingParticipantLid;
435
+ const affectedParticipantPn = getBinaryNodeChild(child, 'participant')?.attrs?.phone_number || actingParticipantPn;
182
436
  switch (child?.tag) {
183
437
  case 'create':
184
438
  const metadata = extractGroupMetadata(child);
185
439
  msg.messageStubType = WAMessageStubType.GROUP_CREATE;
186
440
  msg.messageStubParameters = [metadata.subject];
187
- msg.key = { participant: metadata.owner };
441
+ msg.key = { participant: metadata.owner, participantAlt: metadata.ownerPn };
188
442
  ev.emit('chats.upsert', [
189
443
  {
190
444
  id: metadata.id,
@@ -195,7 +449,8 @@ export const makeMessagesRecvSocket = (config) => {
195
449
  ev.emit('groups.upsert', [
196
450
  {
197
451
  ...metadata,
198
- author: participant
452
+ author: actingParticipantLid,
453
+ authorPn: actingParticipantPn
199
454
  }
200
455
  ]);
201
456
  break;
@@ -220,15 +475,24 @@ export const makeMessagesRecvSocket = (config) => {
220
475
  case 'leave':
221
476
  const stubType = `GROUP_PARTICIPANT_${child.tag.toUpperCase()}`;
222
477
  msg.messageStubType = WAMessageStubType[stubType];
223
- const participants = getBinaryNodeChildren(child, 'participant').map(p => p.attrs.jid);
478
+ const participants = getBinaryNodeChildren(child, 'participant').map(({ attrs }) => {
479
+ // TODO: Store LID MAPPINGS
480
+ return {
481
+ id: attrs.jid,
482
+ phoneNumber: isLidUser(attrs.jid) && isPnUser(attrs.phone_number) ? attrs.phone_number : undefined,
483
+ lid: isPnUser(attrs.jid) && isLidUser(attrs.lid) ? attrs.lid : undefined,
484
+ admin: (attrs.type || null)
485
+ };
486
+ });
224
487
  if (participants.length === 1 &&
225
488
  // if recv. "remove" message and sender removed themselves
226
489
  // mark as left
227
- areJidsSameUser(participants[0], participant) &&
490
+ (areJidsSameUser(participants[0].id, actingParticipantLid) ||
491
+ areJidsSameUser(participants[0].id, actingParticipantPn)) &&
228
492
  child.tag === 'remove') {
229
493
  msg.messageStubType = WAMessageStubType.GROUP_PARTICIPANT_LEAVE;
230
494
  }
231
- msg.messageStubParameters = participants;
495
+ msg.messageStubParameters = participants.map(a => JSON.stringify(a));
232
496
  break;
233
497
  case 'subject':
234
498
  msg.messageStubType = WAMessageStubType.GROUP_CHANGE_SUBJECT;
@@ -269,12 +533,20 @@ export const makeMessagesRecvSocket = (config) => {
269
533
  break;
270
534
  case 'created_membership_requests':
271
535
  msg.messageStubType = WAMessageStubType.GROUP_MEMBERSHIP_JOIN_APPROVAL_REQUEST_NON_ADMIN_ADD;
272
- msg.messageStubParameters = [participantJid, 'created', child.attrs.request_method];
536
+ msg.messageStubParameters = [
537
+ JSON.stringify({ lid: affectedParticipantLid, pn: affectedParticipantPn }),
538
+ 'created',
539
+ child.attrs.request_method
540
+ ];
273
541
  break;
274
542
  case 'revoked_membership_requests':
275
- const isDenied = areJidsSameUser(participantJid, participant);
543
+ const isDenied = areJidsSameUser(affectedParticipantLid, actingParticipantLid);
544
+ // TODO: LIDMAPPING SUPPORT
276
545
  msg.messageStubType = WAMessageStubType.GROUP_MEMBERSHIP_JOIN_APPROVAL_REQUEST_NON_ADMIN_ADD;
277
- msg.messageStubParameters = [participantJid, isDenied ? 'revoked' : 'rejected'];
546
+ msg.messageStubParameters = [
547
+ JSON.stringify({ lid: affectedParticipantLid, pn: affectedParticipantPn }),
548
+ isDenied ? 'revoked' : 'rejected'
549
+ ];
278
550
  break;
279
551
  }
280
552
  };
@@ -304,7 +576,8 @@ export const makeMessagesRecvSocket = (config) => {
304
576
  await handleMexNewsletterNotification(node);
305
577
  break;
306
578
  case 'w:gp2':
307
- handleGroupNotification(node.attrs.participant, child, result);
579
+ // TODO: HANDLE PARTICIPANT_PN
580
+ handleGroupNotification(node, child, result);
308
581
  break;
309
582
  case 'mediaretry':
310
583
  const event = decodeMediaRetryNode(node);
@@ -315,10 +588,12 @@ export const makeMessagesRecvSocket = (config) => {
315
588
  break;
316
589
  case 'devices':
317
590
  const devices = getBinaryNodeChildren(child, 'device');
318
- if (areJidsSameUser(child.attrs.jid, authState.creds.me.id)) {
319
- const deviceJids = devices.map(d => d.attrs.jid);
320
- logger.info({ deviceJids }, 'got my own devices');
591
+ if (areJidsSameUser(child.attrs.jid, authState.creds.me.id) ||
592
+ areJidsSameUser(child.attrs.lid, authState.creds.me.lid)) {
593
+ const deviceData = devices.map(d => ({ id: d.attrs.jid, lid: d.attrs.lid }));
594
+ logger.info({ deviceData }, 'my own devices changed');
321
595
  }
596
+ //TODO: drop a new event, add hashes
322
597
  break;
323
598
  case 'server_sync':
324
599
  const update = getBinaryNodeChild(node, 'collection');
@@ -434,11 +709,37 @@ export const makeMessagesRecvSocket = (config) => {
434
709
  });
435
710
  authState.creds.registered = true;
436
711
  ev.emit('creds.update', authState.creds);
712
+ break;
713
+ case 'privacy_token':
714
+ await handlePrivacyTokenNotification(node);
715
+ break;
437
716
  }
438
717
  if (Object.keys(result).length) {
439
718
  return result;
440
719
  }
441
720
  };
721
+ const handlePrivacyTokenNotification = async (node) => {
722
+ const tokensNode = getBinaryNodeChild(node, 'tokens');
723
+ const from = jidNormalizedUser(node.attrs.from);
724
+ if (!tokensNode)
725
+ return;
726
+ const tokenNodes = getBinaryNodeChildren(tokensNode, 'token');
727
+ for (const tokenNode of tokenNodes) {
728
+ const { attrs, content } = tokenNode;
729
+ const type = attrs.type;
730
+ const timestamp = attrs.t;
731
+ if (type === 'trusted_contact' && content instanceof Buffer) {
732
+ logger.debug({
733
+ from,
734
+ timestamp,
735
+ tcToken: content
736
+ }, 'received trusted contact token');
737
+ await authState.keys.set({
738
+ tctoken: { [from]: { token: content, timestamp } }
739
+ });
740
+ }
741
+ }
742
+ };
442
743
  async function decipherLinkPublicKey(data) {
443
744
  const buffer = toRequiredBuffer(data);
444
745
  const salt = buffer.slice(0, 32);
@@ -453,33 +754,80 @@ export const makeMessagesRecvSocket = (config) => {
453
754
  }
454
755
  return data instanceof Buffer ? data : Buffer.from(data);
455
756
  }
456
- const willSendMessageAgain = (id, participant) => {
757
+ const willSendMessageAgain = async (id, participant) => {
457
758
  const key = `${id}:${participant}`;
458
- const retryCount = msgRetryCache.get(key) || 0;
759
+ const retryCount = (await msgRetryCache.get(key)) || 0;
459
760
  return retryCount < maxMsgRetryCount;
460
761
  };
461
- const updateSendMessageAgainCount = (id, participant) => {
762
+ const updateSendMessageAgainCount = async (id, participant) => {
462
763
  const key = `${id}:${participant}`;
463
- const newValue = (msgRetryCache.get(key) || 0) + 1;
464
- msgRetryCache.set(key, newValue);
764
+ const newValue = ((await msgRetryCache.get(key)) || 0) + 1;
765
+ await msgRetryCache.set(key, newValue);
465
766
  };
466
767
  const sendMessagesAgain = async (key, ids, retryNode) => {
467
- // todo: implement a cache to store the last 256 sent messages (copy whatsmeow)
468
- const msgs = await Promise.all(ids.map(id => getMessage({ ...key, id })));
469
768
  const remoteJid = key.remoteJid;
470
769
  const participant = key.participant || remoteJid;
770
+ const retryCount = +retryNode.attrs.count || 1;
771
+ // Try to get messages from cache first, then fallback to getMessage
772
+ const msgs = [];
773
+ for (const id of ids) {
774
+ let msg;
775
+ // Try to get from retry cache first if enabled
776
+ if (messageRetryManager) {
777
+ const cachedMsg = messageRetryManager.getRecentMessage(remoteJid, id);
778
+ if (cachedMsg) {
779
+ msg = cachedMsg.message;
780
+ logger.debug({ jid: remoteJid, id }, 'found message in retry cache');
781
+ // Mark retry as successful since we found the message
782
+ messageRetryManager.markRetrySuccess(id);
783
+ }
784
+ }
785
+ // Fallback to getMessage if not found in cache
786
+ if (!msg) {
787
+ msg = await getMessage({ ...key, id });
788
+ if (msg) {
789
+ logger.debug({ jid: remoteJid, id }, 'found message via getMessage');
790
+ // Also mark as successful if found via getMessage
791
+ if (messageRetryManager) {
792
+ messageRetryManager.markRetrySuccess(id);
793
+ }
794
+ }
795
+ }
796
+ msgs.push(msg);
797
+ }
471
798
  // if it's the primary jid sending the request
472
799
  // just re-send the message to everyone
473
800
  // prevents the first message decryption failure
474
801
  const sendToAll = !jidDecode(participant)?.device;
802
+ // Check if we should recreate session for this retry
803
+ let shouldRecreateSession = false;
804
+ let recreateReason = '';
805
+ if (enableAutoSessionRecreation && messageRetryManager) {
806
+ try {
807
+ const sessionId = signalRepository.jidToSignalProtocolAddress(participant);
808
+ const hasSession = await signalRepository.validateSession(participant);
809
+ const result = messageRetryManager.shouldRecreateSession(participant, retryCount, hasSession.exists);
810
+ shouldRecreateSession = result.recreate;
811
+ recreateReason = result.reason;
812
+ if (shouldRecreateSession) {
813
+ logger.debug({ participant, retryCount, reason: recreateReason }, 'recreating session for outgoing retry');
814
+ await authState.keys.set({ session: { [sessionId]: null } });
815
+ }
816
+ }
817
+ catch (error) {
818
+ logger.warn({ error, participant }, 'failed to check session recreation for outgoing retry');
819
+ }
820
+ }
475
821
  await assertSessions([participant], true);
476
822
  if (isJidGroup(remoteJid)) {
477
823
  await authState.keys.set({ 'sender-key-memory': { [remoteJid]: null } });
478
824
  }
479
- logger.debug({ participant, sendToAll }, 'forced new session for retry recp');
825
+ logger.debug({ participant, sendToAll, shouldRecreateSession, recreateReason }, 'forced new session for retry recp');
480
826
  for (const [i, msg] of msgs.entries()) {
481
- if (msg) {
482
- updateSendMessageAgainCount(ids[i], participant);
827
+ if (!ids[i])
828
+ continue;
829
+ if (msg && (await willSendMessageAgain(ids[i], participant))) {
830
+ await updateSendMessageAgainCount(ids[i], participant);
483
831
  const msgRelayOpts = { messageId: ids[i] };
484
832
  if (sendToAll) {
485
833
  msgRelayOpts.useUserDevicesCache = false;
@@ -509,7 +857,7 @@ export const makeMessagesRecvSocket = (config) => {
509
857
  fromMe,
510
858
  participant: attrs.participant
511
859
  };
512
- if (shouldIgnoreJid(remoteJid) && remoteJid !== '@s.whatsapp.net') {
860
+ if (shouldIgnoreJid(remoteJid) && remoteJid !== S_WHATSAPP_NET) {
513
861
  logger.debug({ remoteJid }, 'ignoring receipt from jid');
514
862
  await sendMessageAck(node);
515
863
  return;
@@ -550,14 +898,15 @@ export const makeMessagesRecvSocket = (config) => {
550
898
  // correctly set who is asking for the retry
551
899
  key.participant = key.participant || attrs.from;
552
900
  const retryNode = getBinaryNodeChild(node, 'retry');
553
- if (willSendMessageAgain(ids[0], key.participant)) {
901
+ if (ids[0] && key.participant && (await willSendMessageAgain(ids[0], key.participant))) {
554
902
  if (key.fromMe) {
555
903
  try {
904
+ await updateSendMessageAgainCount(ids[0], key.participant);
556
905
  logger.debug({ attrs, key }, 'recv retry request');
557
906
  await sendMessagesAgain(key, ids, retryNode);
558
907
  }
559
908
  catch (error) {
560
- logger.error({ key, ids, trace: error.stack }, 'error in sending message again');
909
+ logger.error({ key, ids, trace: error instanceof Error ? error.stack : 'Unknown error' }, 'error in sending message again');
561
910
  }
562
911
  }
563
912
  else {
@@ -577,7 +926,7 @@ export const makeMessagesRecvSocket = (config) => {
577
926
  };
578
927
  const handleNotification = async (node) => {
579
928
  const remoteJid = node.attrs.from;
580
- if (shouldIgnoreJid(remoteJid) && remoteJid !== '@s.whatsapp.net') {
929
+ if (shouldIgnoreJid(remoteJid) && remoteJid !== S_WHATSAPP_NET) {
581
930
  logger.debug({ remoteJid, id: node.attrs.id }, 'ignored notification');
582
931
  await sendMessageAck(node);
583
932
  return;
@@ -588,10 +937,13 @@ export const makeMessagesRecvSocket = (config) => {
588
937
  const msg = await processNotification(node);
589
938
  if (msg) {
590
939
  const fromMe = areJidsSameUser(node.attrs.participant || remoteJid, authState.creds.me.id);
940
+ const { senderAlt: participantAlt, addressingMode } = extractAddressingContext(node);
591
941
  msg.key = {
592
942
  remoteJid,
593
943
  fromMe,
594
944
  participant: node.attrs.participant,
945
+ participantAlt,
946
+ addressingMode,
595
947
  id: node.attrs.id,
596
948
  ...(msg.key || {})
597
949
  };
@@ -608,67 +960,97 @@ export const makeMessagesRecvSocket = (config) => {
608
960
  }
609
961
  };
610
962
  const handleMessage = async (node) => {
611
- if (shouldIgnoreJid(node.attrs.from) && node.attrs.from !== '@s.whatsapp.net') {
963
+ if (shouldIgnoreJid(node.attrs.from) && node.attrs.from !== S_WHATSAPP_NET) {
612
964
  logger.debug({ key: node.attrs.key }, 'ignored message');
613
- await sendMessageAck(node);
965
+ await sendMessageAck(node, NACK_REASONS.UnhandledError);
614
966
  return;
615
967
  }
616
968
  const encNode = getBinaryNodeChild(node, 'enc');
617
969
  // TODO: temporary fix for crashes and issues resulting of failed msmsg decryption
618
970
  if (encNode && encNode.attrs.type === 'msmsg') {
619
971
  logger.debug({ key: node.attrs.key }, 'ignored msmsg');
620
- await sendMessageAck(node);
972
+ await sendMessageAck(node, NACK_REASONS.MissingMessageSecret);
621
973
  return;
622
974
  }
623
- let response;
624
- if (getBinaryNodeChild(node, 'unavailable') && !encNode) {
625
- await sendMessageAck(node);
626
- const { key } = decodeMessageNode(node, authState.creds.me.id, authState.creds.me.lid || '').fullMessage;
627
- response = await requestPlaceholderResend(key);
628
- if (response === 'RESOLVED') {
629
- return;
975
+ const { fullMessage: msg, category, author, decrypt } = decryptMessageNode(node, authState.creds.me.id, authState.creds.me.lid || '', signalRepository, logger);
976
+ const alt = msg.key.participantAlt || msg.key.remoteJidAlt;
977
+ // store new mappings we didn't have before
978
+ if (!!alt) {
979
+ const altServer = jidDecode(alt)?.server;
980
+ const primaryJid = msg.key.participant || msg.key.remoteJid;
981
+ if (altServer === 'lid') {
982
+ if (!(await signalRepository.lidMapping.getPNForLID(alt))) {
983
+ await signalRepository.lidMapping.storeLIDPNMappings([{ lid: alt, pn: primaryJid }]);
984
+ await signalRepository.migrateSession(primaryJid, alt);
985
+ }
630
986
  }
631
- logger.debug('received unavailable message, acked and requested resend from phone');
632
- }
633
- else {
634
- if (placeholderResendCache.get(node.attrs.id)) {
635
- placeholderResendCache.del(node.attrs.id);
987
+ else {
988
+ await signalRepository.lidMapping.storeLIDPNMappings([{ lid: primaryJid, pn: alt }]);
989
+ await signalRepository.migrateSession(alt, primaryJid);
636
990
  }
637
991
  }
638
- const { fullMessage: msg, category, author, decrypt } = decryptMessageNode(node, authState.creds.me.id, authState.creds.me.lid || '', signalRepository, logger);
639
- if (response && msg?.messageStubParameters?.[0] === NO_MESSAGE_FOUND_ERROR_TEXT) {
640
- msg.messageStubParameters = [NO_MESSAGE_FOUND_ERROR_TEXT, response];
641
- }
642
- if (msg.message?.protocolMessage?.type === proto.Message.ProtocolMessage.Type.SHARE_PHONE_NUMBER &&
643
- node.attrs.sender_pn) {
644
- ev.emit('chats.phoneNumberShare', { lid: node.attrs.from, jid: node.attrs.sender_pn });
992
+ if (msg.key?.remoteJid && msg.key?.id && messageRetryManager) {
993
+ messageRetryManager.addRecentMessage(msg.key.remoteJid, msg.key.id, msg.message);
994
+ logger.debug({
995
+ jid: msg.key.remoteJid,
996
+ id: msg.key.id
997
+ }, 'Added message to recent cache for retry receipts');
645
998
  }
646
999
  try {
647
- await Promise.all([
648
- processingMutex.mutex(async () => {
649
- await decrypt();
650
- // message failed to decrypt
651
- if (msg.messageStubType === proto.WebMessageInfo.StubType.CIPHERTEXT) {
652
- if (msg?.messageStubParameters?.[0] === MISSING_KEYS_ERROR_TEXT) {
653
- return sendMessageAck(node, NACK_REASONS.ParsingError);
654
- }
655
- retryMutex.mutex(async () => {
656
- if (ws.isOpen) {
657
- if (getBinaryNodeChild(node, 'unavailable')) {
658
- return;
1000
+ await processingMutex.mutex(async () => {
1001
+ await decrypt();
1002
+ // message failed to decrypt
1003
+ if (msg.messageStubType === proto.WebMessageInfo.StubType.CIPHERTEXT && msg.category !== 'peer') {
1004
+ if (msg?.messageStubParameters?.[0] === MISSING_KEYS_ERROR_TEXT) {
1005
+ return sendMessageAck(node, NACK_REASONS.ParsingError);
1006
+ }
1007
+ const errorMessage = msg?.messageStubParameters?.[0] || '';
1008
+ const isPreKeyError = errorMessage.includes('PreKey');
1009
+ logger.debug(`[handleMessage] Attempting retry request for failed decryption`);
1010
+ // Handle both pre-key and normal retries in single mutex
1011
+ await retryMutex.mutex(async () => {
1012
+ try {
1013
+ if (!ws.isOpen) {
1014
+ logger.debug({ node }, 'Connection closed, skipping retry');
1015
+ return;
1016
+ }
1017
+ // Handle pre-key errors with upload and delay
1018
+ if (isPreKeyError) {
1019
+ logger.info({ error: errorMessage }, 'PreKey error detected, uploading and retrying');
1020
+ try {
1021
+ logger.debug('Uploading pre-keys for error recovery');
1022
+ await uploadPreKeys(5);
1023
+ logger.debug('Waiting for server to process new pre-keys');
1024
+ await delay(1000);
1025
+ }
1026
+ catch (uploadErr) {
1027
+ logger.error({ uploadErr }, 'Pre-key upload failed, proceeding with retry anyway');
659
1028
  }
1029
+ }
1030
+ const encNode = getBinaryNodeChild(node, 'enc');
1031
+ await sendRetryRequest(node, !encNode);
1032
+ if (retryRequestDelayMs) {
1033
+ await delay(retryRequestDelayMs);
1034
+ }
1035
+ }
1036
+ catch (err) {
1037
+ logger.error({ err, isPreKeyError }, 'Failed to handle retry, attempting basic retry');
1038
+ // Still attempt retry even if pre-key upload failed
1039
+ try {
660
1040
  const encNode = getBinaryNodeChild(node, 'enc');
661
1041
  await sendRetryRequest(node, !encNode);
662
- if (retryRequestDelayMs) {
663
- await delay(retryRequestDelayMs);
664
- }
665
1042
  }
666
- else {
667
- logger.debug({ node }, 'connection closed, ignoring retry req');
1043
+ catch (retryErr) {
1044
+ logger.error({ retryErr }, 'Failed to send retry after error handling');
668
1045
  }
669
- });
670
- }
671
- else {
1046
+ }
1047
+ await sendMessageAck(node, NACK_REASONS.UnhandledError);
1048
+ });
1049
+ }
1050
+ else {
1051
+ await sendMessageAck(node);
1052
+ const isNewsletter = isJidNewsletter(msg.key.remoteJid);
1053
+ if (!isNewsletter) {
672
1054
  // no type in the receipt => message delivered
673
1055
  let type = undefined;
674
1056
  let participant = msg.key.participant;
@@ -680,8 +1062,8 @@ export const makeMessagesRecvSocket = (config) => {
680
1062
  // message was sent by us from a different device
681
1063
  type = 'sender';
682
1064
  // need to specially handle this case
683
- if (isJidUser(msg.key.remoteJid)) {
684
- participant = author;
1065
+ if (isLidUser(msg.key.remoteJid) || isLidUser(msg.key.remoteJidAlt)) {
1066
+ participant = author; // TODO: investigate sending receipts to LIDs and not PNs
685
1067
  }
686
1068
  }
687
1069
  else if (!sendActiveReceipts) {
@@ -695,89 +1077,27 @@ export const makeMessagesRecvSocket = (config) => {
695
1077
  await sendReceipt(jid, undefined, [msg.key.id], 'hist_sync');
696
1078
  }
697
1079
  }
698
- cleanMessage(msg, authState.creds.me.id);
699
- await sendMessageAck(node);
700
- await upsertMessage(msg, node.attrs.offline ? 'append' : 'notify');
701
- })
702
- ]);
1080
+ else {
1081
+ logger.debug({ key: msg.key }, 'processed newsletter message without receipts');
1082
+ }
1083
+ }
1084
+ cleanMessage(msg, authState.creds.me.id, authState.creds.me.lid);
1085
+ await upsertMessage(msg, node.attrs.offline ? 'append' : 'notify');
1086
+ });
703
1087
  }
704
1088
  catch (error) {
705
- logger.error({ error, node }, 'error in handling message');
1089
+ logger.error({ error, node: binaryNodeToString(node) }, 'error in handling message');
706
1090
  }
707
1091
  };
708
- const fetchMessageHistory = async (count, oldestMsgKey, oldestMsgTimestamp) => {
709
- if (!authState.creds.me?.id) {
710
- throw new Boom('Not authenticated');
711
- }
712
- const pdoMessage = {
713
- historySyncOnDemandRequest: {
714
- chatJid: oldestMsgKey.remoteJid,
715
- oldestMsgFromMe: oldestMsgKey.fromMe,
716
- oldestMsgId: oldestMsgKey.id,
717
- oldestMsgTimestampMs: oldestMsgTimestamp,
718
- onDemandMsgCount: count
719
- },
720
- peerDataOperationRequestType: proto.Message.PeerDataOperationRequestType.HISTORY_SYNC_ON_DEMAND
721
- };
722
- return sendPeerDataOperationMessage(pdoMessage);
723
- };
724
- const requestPlaceholderResend = async (messageKey) => {
725
- if (!authState.creds.me?.id) {
726
- throw new Boom('Not authenticated');
727
- }
728
- if (placeholderResendCache.get(messageKey?.id)) {
729
- logger.debug({ messageKey }, 'already requested resend');
730
- return;
731
- }
732
- else {
733
- placeholderResendCache.set(messageKey?.id, true);
734
- }
735
- await delay(5000);
736
- if (!placeholderResendCache.get(messageKey?.id)) {
737
- logger.debug({ messageKey }, 'message received while resend requested');
738
- return 'RESOLVED';
739
- }
740
- const pdoMessage = {
741
- placeholderMessageResendRequest: [
742
- {
743
- messageKey
744
- }
745
- ],
746
- peerDataOperationRequestType: proto.Message.PeerDataOperationRequestType.PLACEHOLDER_MESSAGE_RESEND
747
- };
748
- setTimeout(() => {
749
- if (placeholderResendCache.get(messageKey?.id)) {
750
- logger.debug({ messageKey }, 'PDO message without response after 15 seconds. Phone possibly offline');
751
- placeholderResendCache.del(messageKey?.id);
752
- }
753
- }, 15000);
754
- return sendPeerDataOperationMessage(pdoMessage);
755
- };
756
1092
  const handleCall = async (node) => {
757
- let status;
758
1093
  const { attrs } = node;
759
1094
  const [infoChild] = getAllBinaryNodeChildren(node);
1095
+ const status = getCallStatusFromNode(infoChild);
760
1096
  if (!infoChild) {
761
1097
  throw new Boom('Missing call info in call node');
762
1098
  }
763
1099
  const callId = infoChild.attrs['call-id'];
764
1100
  const from = infoChild.attrs.from || infoChild.attrs['call-creator'];
765
- status = getCallStatusFromNode(infoChild);
766
- if (isLidUser(from) && infoChild.tag === 'relaylatency') {
767
- const verify = callOfferCache.get(callId);
768
- if (!verify) {
769
- status = 'offer';
770
- const callLid = {
771
- chatId: attrs.from,
772
- from,
773
- id: callId,
774
- date: new Date(+attrs.t * 1000),
775
- offline: !!attrs.offline,
776
- status
777
- };
778
- callOfferCache.set(callId, callLid);
779
- }
780
- }
781
1101
  const call = {
782
1102
  chatId: attrs.from,
783
1103
  from,
@@ -790,9 +1110,9 @@ export const makeMessagesRecvSocket = (config) => {
790
1110
  call.isVideo = !!getBinaryNodeChild(infoChild, 'video');
791
1111
  call.isGroup = infoChild.attrs.type === 'group' || !!infoChild.attrs['group-jid'];
792
1112
  call.groupJid = infoChild.attrs['group-jid'];
793
- callOfferCache.set(call.id, call);
1113
+ await callOfferCache.set(call.id, call);
794
1114
  }
795
- const existingCall = callOfferCache.get(call.id);
1115
+ const existingCall = await callOfferCache.get(call.id);
796
1116
  // use existing call info to populate this event
797
1117
  if (existingCall) {
798
1118
  call.isVideo = existingCall.isVideo;
@@ -800,7 +1120,7 @@ export const makeMessagesRecvSocket = (config) => {
800
1120
  }
801
1121
  // delete data once call has ended
802
1122
  if (status === 'reject' || status === 'accept' || status === 'timeout' || status === 'terminate') {
803
- callOfferCache.del(call.id);
1123
+ await callOfferCache.del(call.id);
804
1124
  }
805
1125
  ev.emit('call', [call]);
806
1126
  await sendMessageAck(node);
@@ -833,6 +1153,19 @@ export const makeMessagesRecvSocket = (config) => {
833
1153
  }
834
1154
  }
835
1155
  ]);
1156
+ // resend the message with device_fanout=false, use at your own risk
1157
+ // if (attrs.error === '475') {
1158
+ // const msg = await getMessage(key)
1159
+ // if (msg) {
1160
+ // await relayMessage(key.remoteJid!, msg, {
1161
+ // messageId: key.id!,
1162
+ // useUserDevicesCache: false,
1163
+ // additionalAttributes: {
1164
+ // device_fanout: 'false'
1165
+ // }
1166
+ // })
1167
+ // }
1168
+ // }
836
1169
  }
837
1170
  };
838
1171
  /// processes a node with the given function
@@ -877,165 +1210,32 @@ export const makeMessagesRecvSocket = (config) => {
877
1210
  return { enqueue };
878
1211
  };
879
1212
  const offlineNodeProcessor = makeOfflineNodeProcessor();
880
- const processNode = (type, node, identifier, exec) => {
1213
+ const processNode = async (type, node, identifier, exec) => {
881
1214
  const isOffline = !!node.attrs.offline;
882
1215
  if (isOffline) {
883
1216
  offlineNodeProcessor.enqueue(type, node);
884
1217
  }
885
1218
  else {
886
- processNodeWithBuffer(node, identifier, exec);
1219
+ await processNodeWithBuffer(node, identifier, exec);
887
1220
  }
888
1221
  };
889
- // Handles newsletter notifications
890
- async function handleNewsletterNotification(node) {
891
- const from = node.attrs.from;
892
- const child = getAllBinaryNodeChildren(node)[0];
893
- const author = node.attrs.participant;
894
- logger.info({ from, child }, 'got newsletter notification');
895
- switch (child.tag) {
896
- case 'reaction':
897
- const reactionUpdate = {
898
- id: from,
899
- server_id: child.attrs.message_id,
900
- reaction: {
901
- code: getBinaryNodeChildString(child, 'reaction'),
902
- count: 1
903
- }
904
- };
905
- ev.emit('newsletter.reaction', reactionUpdate);
906
- break;
907
- case 'view':
908
- const viewUpdate = {
909
- id: from,
910
- server_id: child.attrs.message_id,
911
- count: parseInt(child.content?.toString() || '0', 10)
912
- };
913
- ev.emit('newsletter.view', viewUpdate);
914
- break;
915
- case 'participant':
916
- const participantUpdate = {
917
- id: from,
918
- author,
919
- user: child.attrs.jid,
920
- action: child.attrs.action,
921
- new_role: child.attrs.role
922
- };
923
- ev.emit('newsletter-participants.update', participantUpdate);
924
- break;
925
- case 'update':
926
- const settingsNode = getBinaryNodeChild(child, 'settings');
927
- if (settingsNode) {
928
- const update = {};
929
- const nameNode = getBinaryNodeChild(settingsNode, 'name');
930
- if (nameNode?.content)
931
- update.name = nameNode.content.toString();
932
- const descriptionNode = getBinaryNodeChild(settingsNode, 'description');
933
- if (descriptionNode?.content)
934
- update.description = descriptionNode.content.toString();
935
- ev.emit('newsletter-settings.update', {
936
- id: from,
937
- update
938
- });
939
- }
940
- break;
941
- case 'message':
942
- const plaintextNode = getBinaryNodeChild(child, 'plaintext');
943
- if (plaintextNode?.content) {
944
- try {
945
- const contentBuf = typeof plaintextNode.content === 'string'
946
- ? Buffer.from(plaintextNode.content, 'binary')
947
- : Buffer.from(plaintextNode.content);
948
- const messageProto = proto.Message.decode(contentBuf);
949
- const fullMessage = proto.WebMessageInfo.fromObject({
950
- key: {
951
- remoteJid: from,
952
- id: child.attrs.message_id || child.attrs.server_id,
953
- fromMe: false
954
- },
955
- message: messageProto,
956
- messageTimestamp: +child.attrs.t
957
- });
958
- await upsertMessage(fullMessage, 'append');
959
- logger.info('Processed plaintext newsletter message');
960
- }
961
- catch (error) {
962
- logger.error({ error }, 'Failed to decode plaintext newsletter message');
963
- }
964
- }
965
- break;
966
- default:
967
- logger.warn({ node }, 'Unknown newsletter notification');
968
- break;
969
- }
970
- }
971
- // Handles mex newsletter notifications
972
- async function handleMexNewsletterNotification(node) {
973
- const mexNode = getBinaryNodeChild(node, 'mex');
974
- if (!mexNode?.content) {
975
- logger.warn({ node }, 'Invalid mex newsletter notification');
976
- return;
977
- }
978
- let data;
979
- try {
980
- data = JSON.parse(mexNode.content.toString());
981
- }
982
- catch (error) {
983
- logger.error({ err: error, node }, 'Failed to parse mex newsletter notification');
984
- return;
985
- }
986
- const operation = data?.operation;
987
- const updates = data?.updates;
988
- if (!updates || !operation) {
989
- logger.warn({ data }, 'Invalid mex newsletter notification content');
990
- return;
991
- }
992
- logger.info({ operation, updates }, 'got mex newsletter notification');
993
- switch (operation) {
994
- case 'NotificationNewsletterUpdate':
995
- for (const update of updates) {
996
- if (update.jid && update.settings && Object.keys(update.settings).length > 0) {
997
- ev.emit('newsletter-settings.update', {
998
- id: update.jid,
999
- update: update.settings
1000
- });
1001
- }
1002
- }
1003
- break;
1004
- case 'NotificationNewsletterAdminPromote':
1005
- for (const update of updates) {
1006
- if (update.jid && update.user) {
1007
- ev.emit('newsletter-participants.update', {
1008
- id: update.jid,
1009
- author: node.attrs.from,
1010
- user: update.user,
1011
- new_role: 'ADMIN',
1012
- action: 'promote'
1013
- });
1014
- }
1015
- }
1016
- break;
1017
- default:
1018
- logger.info({ operation, data }, 'Unhandled mex newsletter notification');
1019
- break;
1020
- }
1021
- }
1022
1222
  // recv a message
1023
- ws.on('CB:message', (node) => {
1024
- processNode('message', node, 'processing message', handleMessage);
1223
+ ws.on('CB:message', async (node) => {
1224
+ await processNode('message', node, 'processing message', handleMessage);
1025
1225
  });
1026
1226
  ws.on('CB:call', async (node) => {
1027
- processNode('call', node, 'handling call', handleCall);
1227
+ await processNode('call', node, 'handling call', handleCall);
1028
1228
  });
1029
- ws.on('CB:receipt', node => {
1030
- processNode('receipt', node, 'handling receipt', handleReceipt);
1229
+ ws.on('CB:receipt', async (node) => {
1230
+ await processNode('receipt', node, 'handling receipt', handleReceipt);
1031
1231
  });
1032
1232
  ws.on('CB:notification', async (node) => {
1033
- processNode('notification', node, 'handling notification', handleNotification);
1233
+ await processNode('notification', node, 'handling notification', handleNotification);
1034
1234
  });
1035
1235
  ws.on('CB:ack,class:message', (node) => {
1036
1236
  handleBadAck(node).catch(error => onUnexpectedError(error, 'handling bad ack'));
1037
1237
  });
1038
- ev.on('call', ([call]) => {
1238
+ ev.on('call', async ([call]) => {
1039
1239
  if (!call) {
1040
1240
  return;
1041
1241
  }
@@ -1063,7 +1263,7 @@ export const makeMessagesRecvSocket = (config) => {
1063
1263
  msg.message = { call: { callKey: Buffer.from(call.id) } };
1064
1264
  }
1065
1265
  const protoMsg = proto.WebMessageInfo.fromObject(msg);
1066
- upsertMessage(protoMsg, call.offline ? 'append' : 'notify');
1266
+ await upsertMessage(protoMsg, call.offline ? 'append' : 'notify');
1067
1267
  }
1068
1268
  });
1069
1269
  ev.on('connection.update', ({ isOnline }) => {
@@ -1078,7 +1278,8 @@ export const makeMessagesRecvSocket = (config) => {
1078
1278
  sendRetryRequest,
1079
1279
  rejectCall,
1080
1280
  fetchMessageHistory,
1081
- requestPlaceholderResend
1281
+ requestPlaceholderResend,
1282
+ messageRetryManager
1082
1283
  };
1083
1284
  };
1084
1285
  //# sourceMappingURL=messages-recv.js.map