@d0v3riz/baileys 6.7.24 → 7.0.0-rc.6

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 (208) hide show
  1. package/README.md +23 -8
  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/business.d.ts +29 -17
  47. package/lib/Socket/business.d.ts.map +1 -1
  48. package/lib/Socket/business.js +122 -1
  49. package/lib/Socket/business.js.map +1 -1
  50. package/lib/Socket/chats.d.ts +15 -9
  51. package/lib/Socket/chats.d.ts.map +1 -1
  52. package/lib/Socket/chats.js +49 -20
  53. package/lib/Socket/chats.js.map +1 -1
  54. package/lib/Socket/communities.d.ts +39 -14
  55. package/lib/Socket/communities.d.ts.map +1 -1
  56. package/lib/Socket/communities.js +82 -2
  57. package/lib/Socket/communities.js.map +1 -1
  58. package/lib/Socket/groups.d.ts +14 -9
  59. package/lib/Socket/groups.d.ts.map +1 -1
  60. package/lib/Socket/groups.js +14 -11
  61. package/lib/Socket/groups.js.map +1 -1
  62. package/lib/Socket/index.d.ts +39 -14
  63. package/lib/Socket/index.d.ts.map +1 -1
  64. package/lib/Socket/index.js +1 -0
  65. package/lib/Socket/index.js.map +1 -1
  66. package/lib/Socket/messages-recv.d.ts +24 -16
  67. package/lib/Socket/messages-recv.d.ts.map +1 -1
  68. package/lib/Socket/messages-recv.js +463 -319
  69. package/lib/Socket/messages-recv.js.map +1 -1
  70. package/lib/Socket/messages-send.d.ts +25 -16
  71. package/lib/Socket/messages-send.d.ts.map +1 -1
  72. package/lib/Socket/messages-send.js +365 -125
  73. package/lib/Socket/messages-send.js.map +1 -1
  74. package/lib/Socket/newsletter.d.ts +16 -12
  75. package/lib/Socket/newsletter.d.ts.map +1 -1
  76. package/lib/Socket/newsletter.js +3 -1
  77. package/lib/Socket/newsletter.js.map +1 -1
  78. package/lib/Socket/socket.d.ts +11 -3
  79. package/lib/Socket/socket.d.ts.map +1 -1
  80. package/lib/Socket/socket.js +305 -71
  81. package/lib/Socket/socket.js.map +1 -1
  82. package/lib/Types/Auth.d.ts +8 -1
  83. package/lib/Types/Auth.d.ts.map +1 -1
  84. package/lib/Types/Bussines.d.ts +25 -0
  85. package/lib/Types/Bussines.d.ts.map +1 -0
  86. package/lib/Types/Bussines.js +2 -0
  87. package/lib/Types/Bussines.js.map +1 -0
  88. package/lib/Types/Chat.d.ts +5 -0
  89. package/lib/Types/Chat.d.ts.map +1 -1
  90. package/lib/Types/Chat.js.map +1 -1
  91. package/lib/Types/Contact.d.ts +4 -4
  92. package/lib/Types/Contact.d.ts.map +1 -1
  93. package/lib/Types/Events.d.ts +8 -5
  94. package/lib/Types/Events.d.ts.map +1 -1
  95. package/lib/Types/GroupMetadata.d.ts +7 -4
  96. package/lib/Types/GroupMetadata.d.ts.map +1 -1
  97. package/lib/Types/Message.d.ts +38 -12
  98. package/lib/Types/Message.d.ts.map +1 -1
  99. package/lib/Types/Message.js +5 -1
  100. package/lib/Types/Message.js.map +1 -1
  101. package/lib/Types/Signal.d.ts +18 -0
  102. package/lib/Types/Signal.d.ts.map +1 -1
  103. package/lib/Types/Socket.d.ts +24 -13
  104. package/lib/Types/Socket.d.ts.map +1 -1
  105. package/lib/Types/Socket.js.map +1 -1
  106. package/lib/Utils/auth-utils.d.ts +2 -2
  107. package/lib/Utils/auth-utils.d.ts.map +1 -1
  108. package/lib/Utils/auth-utils.js +178 -111
  109. package/lib/Utils/auth-utils.js.map +1 -1
  110. package/lib/Utils/browser-utils.d.ts +4 -0
  111. package/lib/Utils/browser-utils.d.ts.map +1 -0
  112. package/lib/Utils/browser-utils.js +28 -0
  113. package/lib/Utils/browser-utils.js.map +1 -0
  114. package/lib/Utils/chat-utils.d.ts +6 -7
  115. package/lib/Utils/chat-utils.d.ts.map +1 -1
  116. package/lib/Utils/chat-utils.js +19 -2
  117. package/lib/Utils/chat-utils.js.map +1 -1
  118. package/lib/Utils/crypto.d.ts +6 -6
  119. package/lib/Utils/crypto.d.ts.map +1 -1
  120. package/lib/Utils/crypto.js +16 -12
  121. package/lib/Utils/crypto.js.map +1 -1
  122. package/lib/Utils/decode-wa-message.d.ts +14 -2
  123. package/lib/Utils/decode-wa-message.d.ts.map +1 -1
  124. package/lib/Utils/decode-wa-message.js +99 -14
  125. package/lib/Utils/decode-wa-message.js.map +1 -1
  126. package/lib/Utils/event-buffer.d.ts +0 -1
  127. package/lib/Utils/event-buffer.d.ts.map +1 -1
  128. package/lib/Utils/event-buffer.js +63 -18
  129. package/lib/Utils/event-buffer.js.map +1 -1
  130. package/lib/Utils/generics.d.ts +5 -7
  131. package/lib/Utils/generics.d.ts.map +1 -1
  132. package/lib/Utils/generics.js +58 -50
  133. package/lib/Utils/generics.js.map +1 -1
  134. package/lib/Utils/history.d.ts +8 -9
  135. package/lib/Utils/history.d.ts.map +1 -1
  136. package/lib/Utils/history.js +8 -3
  137. package/lib/Utils/history.js.map +1 -1
  138. package/lib/Utils/index.d.ts +2 -0
  139. package/lib/Utils/index.d.ts.map +1 -1
  140. package/lib/Utils/index.js +2 -0
  141. package/lib/Utils/index.js.map +1 -1
  142. package/lib/Utils/link-preview.d.ts +1 -2
  143. package/lib/Utils/link-preview.d.ts.map +1 -1
  144. package/lib/Utils/link-preview.js +1 -1
  145. package/lib/Utils/link-preview.js.map +1 -1
  146. package/lib/Utils/message-retry-manager.d.ts +82 -0
  147. package/lib/Utils/message-retry-manager.d.ts.map +1 -0
  148. package/lib/Utils/message-retry-manager.js +149 -0
  149. package/lib/Utils/message-retry-manager.js.map +1 -0
  150. package/lib/Utils/messages-media.d.ts +14 -12
  151. package/lib/Utils/messages-media.d.ts.map +1 -1
  152. package/lib/Utils/messages-media.js +47 -23
  153. package/lib/Utils/messages-media.js.map +1 -1
  154. package/lib/Utils/messages.d.ts +4 -4
  155. package/lib/Utils/messages.d.ts.map +1 -1
  156. package/lib/Utils/messages.js +55 -21
  157. package/lib/Utils/messages.js.map +1 -1
  158. package/lib/Utils/noise-handler.js +2 -1
  159. package/lib/Utils/noise-handler.js.map +1 -1
  160. package/lib/Utils/pre-key-manager.d.ts +28 -0
  161. package/lib/Utils/pre-key-manager.d.ts.map +1 -0
  162. package/lib/Utils/pre-key-manager.js +106 -0
  163. package/lib/Utils/pre-key-manager.js.map +1 -0
  164. package/lib/Utils/process-message.d.ts +8 -8
  165. package/lib/Utils/process-message.d.ts.map +1 -1
  166. package/lib/Utils/process-message.js +68 -23
  167. package/lib/Utils/process-message.js.map +1 -1
  168. package/lib/Utils/signal.d.ts +4 -4
  169. package/lib/Utils/signal.d.ts.map +1 -1
  170. package/lib/Utils/signal.js +21 -12
  171. package/lib/Utils/signal.js.map +1 -1
  172. package/lib/Utils/validate-connection.d.ts.map +1 -1
  173. package/lib/Utils/validate-connection.js +50 -14
  174. package/lib/Utils/validate-connection.js.map +1 -1
  175. package/lib/WABinary/decode.d.ts.map +1 -1
  176. package/lib/WABinary/decode.js +12 -2
  177. package/lib/WABinary/decode.js.map +1 -1
  178. package/lib/WABinary/generic-utils.js +1 -1
  179. package/lib/WABinary/generic-utils.js.map +1 -1
  180. package/lib/WABinary/jid-utils.d.ts +18 -6
  181. package/lib/WABinary/jid-utils.d.ts.map +1 -1
  182. package/lib/WABinary/jid-utils.js +52 -8
  183. package/lib/WABinary/jid-utils.js.map +1 -1
  184. package/lib/WAM/constants.d.ts +1 -1
  185. package/lib/WAM/constants.d.ts.map +1 -1
  186. package/lib/WAM/constants.js +18516 -10904
  187. package/lib/WAM/constants.js.map +1 -1
  188. package/lib/WAM/encode.d.ts.map +1 -1
  189. package/lib/WAM/encode.js +0 -1
  190. package/lib/WAM/encode.js.map +1 -1
  191. package/lib/WAUSync/Protocols/UsyncLIDProtocol.d.ts +2 -1
  192. package/lib/WAUSync/Protocols/UsyncLIDProtocol.d.ts.map +1 -1
  193. package/lib/WAUSync/Protocols/UsyncLIDProtocol.js +10 -2
  194. package/lib/WAUSync/Protocols/UsyncLIDProtocol.js.map +1 -1
  195. package/lib/WAUSync/USyncQuery.d.ts +1 -1
  196. package/lib/WAUSync/USyncQuery.d.ts.map +1 -1
  197. package/lib/WAUSync/USyncQuery.js +23 -20
  198. package/lib/WAUSync/USyncQuery.js.map +1 -1
  199. package/package.json +16 -11
  200. package/lib/Defaults/baileys-version.json +0 -3
  201. package/lib/Signal/Group/queue-job.d.ts +0 -2
  202. package/lib/Signal/Group/queue-job.d.ts.map +0 -1
  203. package/lib/Signal/Group/queue-job.js +0 -54
  204. package/lib/Signal/Group/queue-job.js.map +0 -1
  205. package/lib/Socket/usync.d.ts +0 -37
  206. package/lib/Socket/usync.d.ts.map +0 -1
  207. package/lib/Socket/usync.js +0 -63
  208. package/lib/Socket/usync.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, 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 ||
@@ -32,6 +32,187 @@ export const makeMessagesRecvSocket = (config) => {
32
32
  useClones: false
33
33
  });
34
34
  let sendActiveReceipts = false;
35
+ const fetchMessageHistory = async (count, oldestMsgKey, oldestMsgTimestamp) => {
36
+ if (!authState.creds.me?.id) {
37
+ throw new Boom('Not authenticated');
38
+ }
39
+ const pdoMessage = {
40
+ historySyncOnDemandRequest: {
41
+ chatJid: oldestMsgKey.remoteJid,
42
+ oldestMsgFromMe: oldestMsgKey.fromMe,
43
+ oldestMsgId: oldestMsgKey.id,
44
+ oldestMsgTimestampMs: oldestMsgTimestamp,
45
+ onDemandMsgCount: count
46
+ },
47
+ peerDataOperationRequestType: proto.Message.PeerDataOperationRequestType.HISTORY_SYNC_ON_DEMAND
48
+ };
49
+ return sendPeerDataOperationMessage(pdoMessage);
50
+ };
51
+ const requestPlaceholderResend = async (messageKey) => {
52
+ if (!authState.creds.me?.id) {
53
+ throw new Boom('Not authenticated');
54
+ }
55
+ if (placeholderResendCache.get(messageKey?.id)) {
56
+ logger.debug({ messageKey }, 'already requested resend');
57
+ return;
58
+ }
59
+ else {
60
+ placeholderResendCache.set(messageKey?.id, true);
61
+ }
62
+ await delay(5000);
63
+ if (!placeholderResendCache.get(messageKey?.id)) {
64
+ logger.debug({ messageKey }, 'message received while resend requested');
65
+ return 'RESOLVED';
66
+ }
67
+ const pdoMessage = {
68
+ placeholderMessageResendRequest: [
69
+ {
70
+ messageKey
71
+ }
72
+ ],
73
+ peerDataOperationRequestType: proto.Message.PeerDataOperationRequestType.PLACEHOLDER_MESSAGE_RESEND
74
+ };
75
+ setTimeout(() => {
76
+ if (placeholderResendCache.get(messageKey?.id)) {
77
+ logger.debug({ messageKey }, 'PDO message without response after 15 seconds. Phone possibly offline');
78
+ placeholderResendCache.del(messageKey?.id);
79
+ }
80
+ }, 15000);
81
+ return sendPeerDataOperationMessage(pdoMessage);
82
+ };
83
+ // Handles mex newsletter notifications
84
+ const handleMexNewsletterNotification = async (node) => {
85
+ const mexNode = getBinaryNodeChild(node, 'mex');
86
+ if (!mexNode?.content) {
87
+ logger.warn({ node }, 'Invalid mex newsletter notification');
88
+ return;
89
+ }
90
+ let data;
91
+ try {
92
+ data = JSON.parse(mexNode.content.toString());
93
+ }
94
+ catch (error) {
95
+ logger.error({ err: error, node }, 'Failed to parse mex newsletter notification');
96
+ return;
97
+ }
98
+ const operation = data?.operation;
99
+ const updates = data?.updates;
100
+ if (!updates || !operation) {
101
+ logger.warn({ data }, 'Invalid mex newsletter notification content');
102
+ return;
103
+ }
104
+ logger.info({ operation, updates }, 'got mex newsletter notification');
105
+ switch (operation) {
106
+ case 'NotificationNewsletterUpdate':
107
+ for (const update of updates) {
108
+ if (update.jid && update.settings && Object.keys(update.settings).length > 0) {
109
+ ev.emit('newsletter-settings.update', {
110
+ id: update.jid,
111
+ update: update.settings
112
+ });
113
+ }
114
+ }
115
+ break;
116
+ case 'NotificationNewsletterAdminPromote':
117
+ for (const update of updates) {
118
+ if (update.jid && update.user) {
119
+ ev.emit('newsletter-participants.update', {
120
+ id: update.jid,
121
+ author: node.attrs.from,
122
+ user: update.user,
123
+ new_role: 'ADMIN',
124
+ action: 'promote'
125
+ });
126
+ }
127
+ }
128
+ break;
129
+ default:
130
+ logger.info({ operation, data }, 'Unhandled mex newsletter notification');
131
+ break;
132
+ }
133
+ };
134
+ // Handles newsletter notifications
135
+ const handleNewsletterNotification = async (node) => {
136
+ const from = node.attrs.from;
137
+ const child = getAllBinaryNodeChildren(node)[0];
138
+ const author = node.attrs.participant;
139
+ logger.info({ from, child }, 'got newsletter notification');
140
+ switch (child.tag) {
141
+ case 'reaction':
142
+ const reactionUpdate = {
143
+ id: from,
144
+ server_id: child.attrs.message_id,
145
+ reaction: {
146
+ code: getBinaryNodeChildString(child, 'reaction'),
147
+ count: 1
148
+ }
149
+ };
150
+ ev.emit('newsletter.reaction', reactionUpdate);
151
+ break;
152
+ case 'view':
153
+ const viewUpdate = {
154
+ id: from,
155
+ server_id: child.attrs.message_id,
156
+ count: parseInt(child.content?.toString() || '0', 10)
157
+ };
158
+ ev.emit('newsletter.view', viewUpdate);
159
+ break;
160
+ case 'participant':
161
+ const participantUpdate = {
162
+ id: from,
163
+ author,
164
+ user: child.attrs.jid,
165
+ action: child.attrs.action,
166
+ new_role: child.attrs.role
167
+ };
168
+ ev.emit('newsletter-participants.update', participantUpdate);
169
+ break;
170
+ case 'update':
171
+ const settingsNode = getBinaryNodeChild(child, 'settings');
172
+ if (settingsNode) {
173
+ const update = {};
174
+ const nameNode = getBinaryNodeChild(settingsNode, 'name');
175
+ if (nameNode?.content)
176
+ update.name = nameNode.content.toString();
177
+ const descriptionNode = getBinaryNodeChild(settingsNode, 'description');
178
+ if (descriptionNode?.content)
179
+ update.description = descriptionNode.content.toString();
180
+ ev.emit('newsletter-settings.update', {
181
+ id: from,
182
+ update
183
+ });
184
+ }
185
+ break;
186
+ case 'message':
187
+ const plaintextNode = getBinaryNodeChild(child, 'plaintext');
188
+ if (plaintextNode?.content) {
189
+ try {
190
+ const contentBuf = typeof plaintextNode.content === 'string'
191
+ ? Buffer.from(plaintextNode.content, 'binary')
192
+ : Buffer.from(plaintextNode.content);
193
+ const messageProto = proto.Message.decode(contentBuf).toJSON();
194
+ const fullMessage = proto.WebMessageInfo.fromObject({
195
+ key: {
196
+ remoteJid: from,
197
+ id: child.attrs.message_id || child.attrs.server_id,
198
+ fromMe: false // TODO: is this really true though
199
+ },
200
+ message: messageProto,
201
+ messageTimestamp: +child.attrs.t
202
+ }).toJSON();
203
+ await upsertMessage(fullMessage, 'append');
204
+ logger.info('Processed plaintext newsletter message');
205
+ }
206
+ catch (error) {
207
+ logger.error({ error }, 'Failed to decode plaintext newsletter message');
208
+ }
209
+ }
210
+ break;
211
+ default:
212
+ logger.warn({ node }, 'Unknown newsletter notification');
213
+ break;
214
+ }
215
+ };
35
216
  const sendMessageAck = async ({ tag, attrs, content }, errorCode) => {
36
217
  const stanza = {
37
218
  tag: 'ack',
@@ -85,20 +266,76 @@ export const makeMessagesRecvSocket = (config) => {
85
266
  const { fullMessage } = decodeMessageNode(node, authState.creds.me.id, authState.creds.me.lid || '');
86
267
  const { key: msgKey } = fullMessage;
87
268
  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;
269
+ if (messageRetryManager) {
270
+ // Check if we've exceeded max retries using the new system
271
+ if (messageRetryManager.hasExceededMaxRetries(msgId)) {
272
+ logger.debug({ msgId }, 'reached retry limit with new retry manager, clearing');
273
+ messageRetryManager.markRetryFailed(msgId);
274
+ return;
275
+ }
276
+ // Increment retry count using new system
277
+ const retryCount = messageRetryManager.incrementRetryCount(msgId);
278
+ // Use the new retry count for the rest of the logic
279
+ const key = `${msgId}:${msgKey?.participant}`;
280
+ msgRetryCache.set(key, retryCount);
94
281
  }
95
- retryCount += 1;
96
- msgRetryCache.set(key, retryCount);
282
+ else {
283
+ // Fallback to old system
284
+ const key = `${msgId}:${msgKey?.participant}`;
285
+ let retryCount = (await msgRetryCache.get(key)) || 0;
286
+ if (retryCount >= maxMsgRetryCount) {
287
+ logger.debug({ retryCount, msgId }, 'reached retry limit, clearing');
288
+ msgRetryCache.del(key);
289
+ return;
290
+ }
291
+ retryCount += 1;
292
+ await msgRetryCache.set(key, retryCount);
293
+ }
294
+ const key = `${msgId}:${msgKey?.participant}`;
295
+ const retryCount = (await msgRetryCache.get(key)) || 1;
97
296
  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}`);
297
+ const fromJid = node.attrs.from;
298
+ // Check if we should recreate the session
299
+ let shouldRecreateSession = false;
300
+ let recreateReason = '';
301
+ if (enableAutoSessionRecreation && messageRetryManager) {
302
+ try {
303
+ // Check if we have a session with this JID
304
+ const sessionId = signalRepository.jidToSignalProtocolAddress(fromJid);
305
+ const hasSession = await signalRepository.validateSession(fromJid);
306
+ const result = messageRetryManager.shouldRecreateSession(fromJid, retryCount, hasSession.exists);
307
+ shouldRecreateSession = result.recreate;
308
+ recreateReason = result.reason;
309
+ if (shouldRecreateSession) {
310
+ logger.debug({ fromJid, retryCount, reason: recreateReason }, 'recreating session for retry');
311
+ // Delete existing session to force recreation
312
+ await authState.keys.set({ session: { [sessionId]: null } });
313
+ forceIncludeKeys = true;
314
+ }
315
+ }
316
+ catch (error) {
317
+ logger.warn({ error, fromJid }, 'failed to check session recreation');
318
+ }
319
+ }
320
+ if (retryCount <= 2) {
321
+ // Use new retry manager for phone requests if available
322
+ if (messageRetryManager) {
323
+ // Schedule phone request with delay (like whatsmeow)
324
+ messageRetryManager.schedulePhoneRequest(msgId, async () => {
325
+ try {
326
+ const requestId = await requestPlaceholderResend(msgKey);
327
+ logger.debug(`sendRetryRequest: requested placeholder resend (${requestId}) for message ${msgId} (scheduled)`);
328
+ }
329
+ catch (error) {
330
+ logger.warn({ error, msgId }, 'failed to send scheduled phone request');
331
+ }
332
+ });
333
+ }
334
+ else {
335
+ // Fallback to immediate request
336
+ const msgId = await requestPlaceholderResend(msgKey);
337
+ logger.debug(`sendRetryRequest: requested placeholder resend for message ${msgId}`);
338
+ }
102
339
  }
103
340
  const deviceIdentity = encodeSignedDeviceIdentity(account, true);
104
341
  await authState.keys.transaction(async () => {
@@ -116,7 +353,9 @@ export const makeMessagesRecvSocket = (config) => {
116
353
  count: retryCount.toString(),
117
354
  id: node.attrs.id,
118
355
  t: node.attrs.t,
119
- v: '1'
356
+ v: '1',
357
+ // ADD ERROR FIELD
358
+ error: '0'
120
359
  }
121
360
  },
122
361
  {
@@ -132,7 +371,7 @@ export const makeMessagesRecvSocket = (config) => {
132
371
  if (node.attrs.participant) {
133
372
  receipt.attrs.participant = node.attrs.participant;
134
373
  }
135
- if (retryCount > 1 || forceIncludeKeys) {
374
+ if (retryCount > 1 || forceIncludeKeys || shouldRecreateSession) {
136
375
  const { update, preKeys } = await getNextPreKeys(authState, 1);
137
376
  const [keyId] = Object.keys(preKeys);
138
377
  const key = preKeys[+keyId];
@@ -152,7 +391,7 @@ export const makeMessagesRecvSocket = (config) => {
152
391
  }
153
392
  await sendNode(receipt);
154
393
  logger.info({ msgAttrs: node.attrs, retryCount }, 'sent retry receipt');
155
- });
394
+ }, authState?.creds?.me?.id || 'sendRetryRequest');
156
395
  };
157
396
  const handleEncryptNotification = async (node) => {
158
397
  const from = node.attrs.from;
@@ -177,14 +416,18 @@ export const makeMessagesRecvSocket = (config) => {
177
416
  }
178
417
  }
179
418
  };
180
- const handleGroupNotification = (participant, child, msg) => {
181
- const participantJid = getBinaryNodeChild(child, 'participant')?.attrs?.jid || participant;
419
+ const handleGroupNotification = (fullNode, child, msg) => {
420
+ // TODO: Support PN/LID (Here is only LID now)
421
+ const actingParticipantLid = fullNode.attrs.participant;
422
+ const actingParticipantPn = fullNode.attrs.participant_pn;
423
+ const affectedParticipantLid = getBinaryNodeChild(child, 'participant')?.attrs?.jid || actingParticipantLid;
424
+ const affectedParticipantPn = getBinaryNodeChild(child, 'participant')?.attrs?.phone_number || actingParticipantPn;
182
425
  switch (child?.tag) {
183
426
  case 'create':
184
427
  const metadata = extractGroupMetadata(child);
185
428
  msg.messageStubType = WAMessageStubType.GROUP_CREATE;
186
429
  msg.messageStubParameters = [metadata.subject];
187
- msg.key = { participant: metadata.owner };
430
+ msg.key = { participant: metadata.owner, participantAlt: metadata.ownerPn };
188
431
  ev.emit('chats.upsert', [
189
432
  {
190
433
  id: metadata.id,
@@ -195,7 +438,8 @@ export const makeMessagesRecvSocket = (config) => {
195
438
  ev.emit('groups.upsert', [
196
439
  {
197
440
  ...metadata,
198
- author: participant
441
+ author: actingParticipantLid,
442
+ authorPn: actingParticipantPn
199
443
  }
200
444
  ]);
201
445
  break;
@@ -220,15 +464,24 @@ export const makeMessagesRecvSocket = (config) => {
220
464
  case 'leave':
221
465
  const stubType = `GROUP_PARTICIPANT_${child.tag.toUpperCase()}`;
222
466
  msg.messageStubType = WAMessageStubType[stubType];
223
- const participants = getBinaryNodeChildren(child, 'participant').map(p => p.attrs.jid);
467
+ const participants = getBinaryNodeChildren(child, 'participant').map(({ attrs }) => {
468
+ // TODO: Store LID MAPPINGS
469
+ return {
470
+ id: attrs.jid,
471
+ phoneNumber: isLidUser(attrs.jid) && isPnUser(attrs.phone_number) ? attrs.phone_number : undefined,
472
+ lid: isPnUser(attrs.jid) && isLidUser(attrs.lid) ? attrs.lid : undefined,
473
+ admin: (attrs.type || null)
474
+ };
475
+ });
224
476
  if (participants.length === 1 &&
225
477
  // if recv. "remove" message and sender removed themselves
226
478
  // mark as left
227
- areJidsSameUser(participants[0], participant) &&
479
+ (areJidsSameUser(participants[0].id, actingParticipantLid) ||
480
+ areJidsSameUser(participants[0].id, actingParticipantPn)) &&
228
481
  child.tag === 'remove') {
229
482
  msg.messageStubType = WAMessageStubType.GROUP_PARTICIPANT_LEAVE;
230
483
  }
231
- msg.messageStubParameters = participants;
484
+ msg.messageStubParameters = participants.map(a => JSON.stringify(a));
232
485
  break;
233
486
  case 'subject':
234
487
  msg.messageStubType = WAMessageStubType.GROUP_CHANGE_SUBJECT;
@@ -269,12 +522,20 @@ export const makeMessagesRecvSocket = (config) => {
269
522
  break;
270
523
  case 'created_membership_requests':
271
524
  msg.messageStubType = WAMessageStubType.GROUP_MEMBERSHIP_JOIN_APPROVAL_REQUEST_NON_ADMIN_ADD;
272
- msg.messageStubParameters = [participantJid, 'created', child.attrs.request_method];
525
+ msg.messageStubParameters = [
526
+ JSON.stringify({ lid: affectedParticipantLid, pn: affectedParticipantPn }),
527
+ 'created',
528
+ child.attrs.request_method
529
+ ];
273
530
  break;
274
531
  case 'revoked_membership_requests':
275
- const isDenied = areJidsSameUser(participantJid, participant);
532
+ const isDenied = areJidsSameUser(affectedParticipantLid, actingParticipantLid);
533
+ // TODO: LIDMAPPING SUPPORT
276
534
  msg.messageStubType = WAMessageStubType.GROUP_MEMBERSHIP_JOIN_APPROVAL_REQUEST_NON_ADMIN_ADD;
277
- msg.messageStubParameters = [participantJid, isDenied ? 'revoked' : 'rejected'];
535
+ msg.messageStubParameters = [
536
+ JSON.stringify({ lid: affectedParticipantLid, pn: affectedParticipantPn }),
537
+ isDenied ? 'revoked' : 'rejected'
538
+ ];
278
539
  break;
279
540
  }
280
541
  };
@@ -304,7 +565,8 @@ export const makeMessagesRecvSocket = (config) => {
304
565
  await handleMexNewsletterNotification(node);
305
566
  break;
306
567
  case 'w:gp2':
307
- handleGroupNotification(node.attrs.participant, child, result);
568
+ // TODO: HANDLE PARTICIPANT_PN
569
+ handleGroupNotification(node, child, result);
308
570
  break;
309
571
  case 'mediaretry':
310
572
  const event = decodeMediaRetryNode(node);
@@ -315,10 +577,12 @@ export const makeMessagesRecvSocket = (config) => {
315
577
  break;
316
578
  case 'devices':
317
579
  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');
580
+ if (areJidsSameUser(child.attrs.jid, authState.creds.me.id) ||
581
+ areJidsSameUser(child.attrs.lid, authState.creds.me.lid)) {
582
+ const deviceData = devices.map(d => ({ id: d.attrs.jid, lid: d.attrs.lid }));
583
+ logger.info({ deviceData }, 'my own devices changed');
321
584
  }
585
+ //TODO: drop a new event, add hashes
322
586
  break;
323
587
  case 'server_sync':
324
588
  const update = getBinaryNodeChild(node, 'collection');
@@ -453,32 +717,79 @@ export const makeMessagesRecvSocket = (config) => {
453
717
  }
454
718
  return data instanceof Buffer ? data : Buffer.from(data);
455
719
  }
456
- const willSendMessageAgain = (id, participant) => {
720
+ const willSendMessageAgain = async (id, participant) => {
457
721
  const key = `${id}:${participant}`;
458
- const retryCount = msgRetryCache.get(key) || 0;
722
+ const retryCount = (await msgRetryCache.get(key)) || 0;
459
723
  return retryCount < maxMsgRetryCount;
460
724
  };
461
- const updateSendMessageAgainCount = (id, participant) => {
725
+ const updateSendMessageAgainCount = async (id, participant) => {
462
726
  const key = `${id}:${participant}`;
463
- const newValue = (msgRetryCache.get(key) || 0) + 1;
464
- msgRetryCache.set(key, newValue);
727
+ const newValue = ((await msgRetryCache.get(key)) || 0) + 1;
728
+ await msgRetryCache.set(key, newValue);
465
729
  };
466
730
  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
731
  const remoteJid = key.remoteJid;
470
732
  const participant = key.participant || remoteJid;
733
+ const retryCount = +retryNode.attrs.count || 1;
734
+ // Try to get messages from cache first, then fallback to getMessage
735
+ const msgs = [];
736
+ for (const id of ids) {
737
+ let msg;
738
+ // Try to get from retry cache first if enabled
739
+ if (messageRetryManager) {
740
+ const cachedMsg = messageRetryManager.getRecentMessage(remoteJid, id);
741
+ if (cachedMsg) {
742
+ msg = cachedMsg.message;
743
+ logger.debug({ jid: remoteJid, id }, 'found message in retry cache');
744
+ // Mark retry as successful since we found the message
745
+ messageRetryManager.markRetrySuccess(id);
746
+ }
747
+ }
748
+ // Fallback to getMessage if not found in cache
749
+ if (!msg) {
750
+ msg = await getMessage({ ...key, id });
751
+ if (msg) {
752
+ logger.debug({ jid: remoteJid, id }, 'found message via getMessage');
753
+ // Also mark as successful if found via getMessage
754
+ if (messageRetryManager) {
755
+ messageRetryManager.markRetrySuccess(id);
756
+ }
757
+ }
758
+ }
759
+ msgs.push(msg);
760
+ }
471
761
  // if it's the primary jid sending the request
472
762
  // just re-send the message to everyone
473
763
  // prevents the first message decryption failure
474
764
  const sendToAll = !jidDecode(participant)?.device;
475
- await assertSessions([participant], true);
765
+ // Check if we should recreate session for this retry
766
+ let shouldRecreateSession = false;
767
+ let recreateReason = '';
768
+ if (enableAutoSessionRecreation && messageRetryManager) {
769
+ try {
770
+ const sessionId = signalRepository.jidToSignalProtocolAddress(participant);
771
+ const hasSession = await signalRepository.validateSession(participant);
772
+ const result = messageRetryManager.shouldRecreateSession(participant, retryCount, hasSession.exists);
773
+ shouldRecreateSession = result.recreate;
774
+ recreateReason = result.reason;
775
+ if (shouldRecreateSession) {
776
+ logger.debug({ participant, retryCount, reason: recreateReason }, 'recreating session for outgoing retry');
777
+ await authState.keys.set({ session: { [sessionId]: null } });
778
+ }
779
+ }
780
+ catch (error) {
781
+ logger.warn({ error, participant }, 'failed to check session recreation for outgoing retry');
782
+ }
783
+ }
784
+ await assertSessions([participant]);
476
785
  if (isJidGroup(remoteJid)) {
477
786
  await authState.keys.set({ 'sender-key-memory': { [remoteJid]: null } });
478
787
  }
479
- logger.debug({ participant, sendToAll }, 'forced new session for retry recp');
788
+ logger.debug({ participant, sendToAll, shouldRecreateSession, recreateReason }, 'forced new session for retry recp');
480
789
  for (const [i, msg] of msgs.entries()) {
481
- if (msg) {
790
+ if (!ids[i])
791
+ continue;
792
+ if (msg && (await willSendMessageAgain(ids[i], participant))) {
482
793
  updateSendMessageAgainCount(ids[i], participant);
483
794
  const msgRelayOpts = { messageId: ids[i] };
484
795
  if (sendToAll) {
@@ -509,7 +820,7 @@ export const makeMessagesRecvSocket = (config) => {
509
820
  fromMe,
510
821
  participant: attrs.participant
511
822
  };
512
- if (shouldIgnoreJid(remoteJid) && remoteJid !== '@s.whatsapp.net') {
823
+ if (shouldIgnoreJid(remoteJid) && remoteJid !== S_WHATSAPP_NET) {
513
824
  logger.debug({ remoteJid }, 'ignoring receipt from jid');
514
825
  await sendMessageAck(node);
515
826
  return;
@@ -550,14 +861,15 @@ export const makeMessagesRecvSocket = (config) => {
550
861
  // correctly set who is asking for the retry
551
862
  key.participant = key.participant || attrs.from;
552
863
  const retryNode = getBinaryNodeChild(node, 'retry');
553
- if (willSendMessageAgain(ids[0], key.participant)) {
864
+ if (ids[0] && key.participant && (await willSendMessageAgain(ids[0], key.participant))) {
554
865
  if (key.fromMe) {
555
866
  try {
867
+ updateSendMessageAgainCount(ids[0], key.participant);
556
868
  logger.debug({ attrs, key }, 'recv retry request');
557
869
  await sendMessagesAgain(key, ids, retryNode);
558
870
  }
559
871
  catch (error) {
560
- logger.error({ key, ids, trace: error.stack }, 'error in sending message again');
872
+ logger.error({ key, ids, trace: error instanceof Error ? error.stack : 'Unknown error' }, 'error in sending message again');
561
873
  }
562
874
  }
563
875
  else {
@@ -577,7 +889,7 @@ export const makeMessagesRecvSocket = (config) => {
577
889
  };
578
890
  const handleNotification = async (node) => {
579
891
  const remoteJid = node.attrs.from;
580
- if (shouldIgnoreJid(remoteJid) && remoteJid !== '@s.whatsapp.net') {
892
+ if (shouldIgnoreJid(remoteJid) && remoteJid !== S_WHATSAPP_NET) {
581
893
  logger.debug({ remoteJid, id: node.attrs.id }, 'ignored notification');
582
894
  await sendMessageAck(node);
583
895
  return;
@@ -588,10 +900,13 @@ export const makeMessagesRecvSocket = (config) => {
588
900
  const msg = await processNotification(node);
589
901
  if (msg) {
590
902
  const fromMe = areJidsSameUser(node.attrs.participant || remoteJid, authState.creds.me.id);
903
+ const { senderAlt: participantAlt, addressingMode } = extractAddressingContext(node);
591
904
  msg.key = {
592
905
  remoteJid,
593
906
  fromMe,
594
907
  participant: node.attrs.participant,
908
+ participantAlt,
909
+ addressingMode,
595
910
  id: node.attrs.id,
596
911
  ...(msg.key || {})
597
912
  };
@@ -608,176 +923,137 @@ export const makeMessagesRecvSocket = (config) => {
608
923
  }
609
924
  };
610
925
  const handleMessage = async (node) => {
611
- if (shouldIgnoreJid(node.attrs.from) && node.attrs.from !== '@s.whatsapp.net') {
926
+ if (shouldIgnoreJid(node.attrs.from) && node.attrs.from !== S_WHATSAPP_NET) {
612
927
  logger.debug({ key: node.attrs.key }, 'ignored message');
613
- await sendMessageAck(node);
928
+ await sendMessageAck(node, NACK_REASONS.UnhandledError);
614
929
  return;
615
930
  }
616
931
  const encNode = getBinaryNodeChild(node, 'enc');
617
932
  // TODO: temporary fix for crashes and issues resulting of failed msmsg decryption
618
933
  if (encNode && encNode.attrs.type === 'msmsg') {
619
934
  logger.debug({ key: node.attrs.key }, 'ignored msmsg');
620
- await sendMessageAck(node);
935
+ await sendMessageAck(node, NACK_REASONS.MissingMessageSecret);
621
936
  return;
622
937
  }
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;
938
+ const { fullMessage: msg, category, author, decrypt } = decryptMessageNode(node, authState.creds.me.id, authState.creds.me.lid || '', signalRepository, logger);
939
+ const alt = msg.key.participantAlt || msg.key.remoteJidAlt;
940
+ // store new mappings we didn't have before
941
+ if (!!alt) {
942
+ const altServer = jidDecode(alt)?.server;
943
+ const primaryJid = msg.key.participant || msg.key.remoteJid;
944
+ if (altServer === 'lid') {
945
+ if (!(await signalRepository.lidMapping.getPNForLID(alt))) {
946
+ await signalRepository.lidMapping.storeLIDPNMappings([{ lid: alt, pn: primaryJid }]);
947
+ await signalRepository.migrateSession(primaryJid, alt);
948
+ }
630
949
  }
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);
950
+ else {
951
+ await signalRepository.lidMapping.storeLIDPNMappings([{ lid: primaryJid, pn: alt }]);
952
+ await signalRepository.migrateSession(alt, primaryJid);
636
953
  }
637
954
  }
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 });
955
+ if (msg.key?.remoteJid && msg.key?.id && messageRetryManager) {
956
+ messageRetryManager.addRecentMessage(msg.key.remoteJid, msg.key.id, msg.message);
957
+ logger.debug({
958
+ jid: msg.key.remoteJid,
959
+ id: msg.key.id
960
+ }, 'Added message to recent cache for retry receipts');
645
961
  }
646
962
  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;
963
+ await processingMutex.mutex(async () => {
964
+ await decrypt();
965
+ // message failed to decrypt
966
+ if (msg.messageStubType === proto.WebMessageInfo.StubType.CIPHERTEXT) {
967
+ if (msg?.messageStubParameters?.[0] === MISSING_KEYS_ERROR_TEXT) {
968
+ return sendMessageAck(node, NACK_REASONS.ParsingError);
969
+ }
970
+ const errorMessage = msg?.messageStubParameters?.[0] || '';
971
+ const isPreKeyError = errorMessage.includes('PreKey');
972
+ logger.debug(`[handleMessage] Attempting retry request for failed decryption`);
973
+ // Handle both pre-key and normal retries in single mutex
974
+ retryMutex.mutex(async () => {
975
+ try {
976
+ if (!ws.isOpen) {
977
+ logger.debug({ node }, 'Connection closed, skipping retry');
978
+ return;
979
+ }
980
+ // Handle pre-key errors with upload and delay
981
+ if (isPreKeyError) {
982
+ logger.info({ error: errorMessage }, 'PreKey error detected, uploading and retrying');
983
+ try {
984
+ logger.debug('Uploading pre-keys for error recovery');
985
+ await uploadPreKeys(5);
986
+ logger.debug('Waiting for server to process new pre-keys');
987
+ await delay(1000);
659
988
  }
660
- const encNode = getBinaryNodeChild(node, 'enc');
661
- await sendRetryRequest(node, !encNode);
662
- if (retryRequestDelayMs) {
663
- await delay(retryRequestDelayMs);
989
+ catch (uploadErr) {
990
+ logger.error({ uploadErr }, 'Pre-key upload failed, proceeding with retry anyway');
664
991
  }
665
992
  }
666
- else {
667
- logger.debug({ node }, 'connection closed, ignoring retry req');
993
+ const encNode = getBinaryNodeChild(node, 'enc');
994
+ await sendRetryRequest(node, !encNode);
995
+ if (retryRequestDelayMs) {
996
+ await delay(retryRequestDelayMs);
668
997
  }
669
- });
670
- }
671
- else {
672
- // no type in the receipt => message delivered
673
- let type = undefined;
674
- let participant = msg.key.participant;
675
- if (category === 'peer') {
676
- // special peer message
677
- type = 'peer_msg';
678
998
  }
679
- else if (msg.key.fromMe) {
680
- // message was sent by us from a different device
681
- type = 'sender';
682
- // need to specially handle this case
683
- if (isJidUser(msg.key.remoteJid)) {
684
- participant = author;
999
+ catch (err) {
1000
+ logger.error({ err, isPreKeyError }, 'Failed to handle retry, attempting basic retry');
1001
+ // Still attempt retry even if pre-key upload failed
1002
+ try {
1003
+ const encNode = getBinaryNodeChild(node, 'enc');
1004
+ await sendRetryRequest(node, !encNode);
1005
+ }
1006
+ catch (retryErr) {
1007
+ logger.error({ retryErr }, 'Failed to send retry after error handling');
685
1008
  }
686
1009
  }
687
- else if (!sendActiveReceipts) {
688
- type = 'inactive';
689
- }
690
- await sendReceipt(msg.key.remoteJid, participant, [msg.key.id], type);
691
- // send ack for history message
692
- const isAnyHistoryMsg = getHistoryMsg(msg.message);
693
- if (isAnyHistoryMsg) {
694
- const jid = jidNormalizedUser(msg.key.remoteJid);
695
- await sendReceipt(jid, undefined, [msg.key.id], 'hist_sync');
1010
+ await sendMessageAck(node, NACK_REASONS.UnhandledError);
1011
+ });
1012
+ }
1013
+ else {
1014
+ // no type in the receipt => message delivered
1015
+ let type = undefined;
1016
+ let participant = msg.key.participant;
1017
+ if (category === 'peer') {
1018
+ // special peer message
1019
+ type = 'peer_msg';
1020
+ }
1021
+ else if (msg.key.fromMe) {
1022
+ // message was sent by us from a different device
1023
+ type = 'sender';
1024
+ // need to specially handle this case
1025
+ if (isLidUser(msg.key.remoteJid) || isLidUser(msg.key.remoteJidAlt)) {
1026
+ participant = author; // TODO: investigate sending receipts to LIDs and not PNs
696
1027
  }
697
1028
  }
698
- cleanMessage(msg, authState.creds.me.id);
699
- await sendMessageAck(node);
700
- await upsertMessage(msg, node.attrs.offline ? 'append' : 'notify');
701
- })
702
- ]);
1029
+ else if (!sendActiveReceipts) {
1030
+ type = 'inactive';
1031
+ }
1032
+ await sendReceipt(msg.key.remoteJid, participant, [msg.key.id], type);
1033
+ // send ack for history message
1034
+ const isAnyHistoryMsg = getHistoryMsg(msg.message);
1035
+ if (isAnyHistoryMsg) {
1036
+ const jid = jidNormalizedUser(msg.key.remoteJid);
1037
+ await sendReceipt(jid, undefined, [msg.key.id], 'hist_sync');
1038
+ }
1039
+ }
1040
+ cleanMessage(msg, authState.creds.me.id, authState.creds.me.lid);
1041
+ await upsertMessage(msg, node.attrs.offline ? 'append' : 'notify');
1042
+ });
703
1043
  }
704
1044
  catch (error) {
705
- logger.error({ error, node }, 'error in handling message');
706
- }
707
- };
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');
1045
+ logger.error({ error, node: binaryNodeToString(node) }, 'error in handling message');
727
1046
  }
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
1047
  };
756
1048
  const handleCall = async (node) => {
757
- let status;
758
1049
  const { attrs } = node;
759
1050
  const [infoChild] = getAllBinaryNodeChildren(node);
1051
+ const status = getCallStatusFromNode(infoChild);
760
1052
  if (!infoChild) {
761
1053
  throw new Boom('Missing call info in call node');
762
1054
  }
763
1055
  const callId = infoChild.attrs['call-id'];
764
1056
  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
1057
  const call = {
782
1058
  chatId: attrs.from,
783
1059
  from,
@@ -790,9 +1066,9 @@ export const makeMessagesRecvSocket = (config) => {
790
1066
  call.isVideo = !!getBinaryNodeChild(infoChild, 'video');
791
1067
  call.isGroup = infoChild.attrs.type === 'group' || !!infoChild.attrs['group-jid'];
792
1068
  call.groupJid = infoChild.attrs['group-jid'];
793
- callOfferCache.set(call.id, call);
1069
+ await callOfferCache.set(call.id, call);
794
1070
  }
795
- const existingCall = callOfferCache.get(call.id);
1071
+ const existingCall = await callOfferCache.get(call.id);
796
1072
  // use existing call info to populate this event
797
1073
  if (existingCall) {
798
1074
  call.isVideo = existingCall.isVideo;
@@ -800,7 +1076,7 @@ export const makeMessagesRecvSocket = (config) => {
800
1076
  }
801
1077
  // delete data once call has ended
802
1078
  if (status === 'reject' || status === 'accept' || status === 'timeout' || status === 'terminate') {
803
- callOfferCache.del(call.id);
1079
+ await callOfferCache.del(call.id);
804
1080
  }
805
1081
  ev.emit('call', [call]);
806
1082
  await sendMessageAck(node);
@@ -886,139 +1162,6 @@ export const makeMessagesRecvSocket = (config) => {
886
1162
  processNodeWithBuffer(node, identifier, exec);
887
1163
  }
888
1164
  };
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
1165
  // recv a message
1023
1166
  ws.on('CB:message', (node) => {
1024
1167
  processNode('message', node, 'processing message', handleMessage);
@@ -1078,7 +1221,8 @@ export const makeMessagesRecvSocket = (config) => {
1078
1221
  sendRetryRequest,
1079
1222
  rejectCall,
1080
1223
  fetchMessageHistory,
1081
- requestPlaceholderResend
1224
+ requestPlaceholderResend,
1225
+ messageRetryManager
1082
1226
  };
1083
1227
  };
1084
1228
  //# sourceMappingURL=messages-recv.js.map