whatsapp-store-db 1.3.67 → 1.3.69
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.
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "whatsapp-store-db",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.69",
|
|
4
4
|
"description": "Minimal Baileys data storage for your favorite DBMS built with Prisma",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"typescript": "^4.9.3"
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
|
-
"baileys": "7.0.0-
|
|
33
|
+
"baileys": "7.0.0-rc13",
|
|
34
34
|
"patch-package": "^8.0.0",
|
|
35
35
|
"tiny-invariant": "^1.3.1"
|
|
36
36
|
},
|
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
--- a/node_modules/baileys/lib/Socket/messages-recv.js
|
|
2
|
+
+++ b/node_modules/baileys/lib/Socket/messages-recv.js
|
|
3
|
+
@@ -54,11 +54,12 @@
|
|
4
|
+
return sendPeerDataOperationMessage(pdoMessage);
|
|
5
|
+
};
|
|
6
|
+
const requestPlaceholderResend = async (messageKey, msgData) => {
|
|
7
|
+
+ const sessionJid = authState.creds.me?.id || 'unknown';
|
|
8
|
+
if (!authState.creds.me?.id) {
|
|
9
|
+
throw new Boom('Not authenticated');
|
|
10
|
+
}
|
|
11
|
+
if (await placeholderResendCache.get(messageKey?.id)) {
|
|
12
|
+
- logger.debug({ messageKey }, 'already requested resend');
|
|
13
|
+
+ logger.error({ session: sessionJid, msgId: messageKey?.id, remoteJid: messageKey?.remoteJid }, 'CTWA_TRACE: Placeholder resend already requested (dedup), skipping');
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
else {
|
|
17
|
+
@@ -66,9 +67,10 @@
|
|
18
|
+
// metadata (LID details, timestamps, etc.) that the phone may omit
|
|
19
|
+
await placeholderResendCache.set(messageKey?.id, msgData || true);
|
|
20
|
+
}
|
|
21
|
+
+ logger.error({ session: sessionJid, msgId: messageKey?.id, remoteJid: messageKey?.remoteJid }, 'CTWA_TRACE: Waiting 2s before sending PDO request');
|
|
22
|
+
await delay(2000);
|
|
23
|
+
if (!(await placeholderResendCache.get(messageKey?.id))) {
|
|
24
|
+
- logger.debug({ messageKey }, 'message received while resend requested');
|
|
25
|
+
+ logger.error({ session: sessionJid, msgId: messageKey?.id, remoteJid: messageKey?.remoteJid }, 'CTWA_TRACE: Message arrived during 2s wait, no PDO needed');
|
|
26
|
+
return 'RESOLVED';
|
|
27
|
+
}
|
|
28
|
+
const pdoMessage = {
|
|
29
|
+
@@ -79,13 +81,43 @@
|
|
30
|
+
],
|
|
31
|
+
peerDataOperationRequestType: proto.Message.PeerDataOperationRequestType.PLACEHOLDER_MESSAGE_RESEND
|
|
32
|
+
};
|
|
33
|
+
- setTimeout(async () => {
|
|
34
|
+
- if (await placeholderResendCache.get(messageKey?.id)) {
|
|
35
|
+
- logger.debug({ messageKey }, 'PDO message without response after 8 seconds. Phone possibly offline');
|
|
36
|
+
- await placeholderResendCache.del(messageKey?.id);
|
|
37
|
+
+ // PDO retry loop — up to 3 attempts, 15s wait each (~47s total with 2s initial delay)
|
|
38
|
+
+ const MAX_PDO_ATTEMPTS = 3;
|
|
39
|
+
+ let stanzaId;
|
|
40
|
+
+ for (let attempt = 1; attempt <= MAX_PDO_ATTEMPTS; attempt++) {
|
|
41
|
+
+ try {
|
|
42
|
+
+ const currentStanzaId = await sendPeerDataOperationMessage(pdoMessage);
|
|
43
|
+
+ if (attempt === 1)
|
|
44
|
+
+ stanzaId = currentStanzaId;
|
|
45
|
+
+ // Store mapping: stanzaId → messageKey.id so PDO response can clear the right cache entry
|
|
46
|
+
+ if (currentStanzaId) {
|
|
47
|
+
+ await placeholderResendCache.set(`pdo_${currentStanzaId}`, messageKey?.id);
|
|
48
|
+
+ }
|
|
49
|
+
+ logger.error({ session: sessionJid, msgId: messageKey?.id, remoteJid: messageKey?.remoteJid, attempt, maxAttempts: MAX_PDO_ATTEMPTS }, 'CTWA: Placeholder resend PDO request sent');
|
|
50
|
+
+ // Wait 15s for response
|
|
51
|
+
+ await delay(15000);
|
|
52
|
+
+ // Check if response arrived (cache entry cleared by process-message.js handler)
|
|
53
|
+
+ if (!(await placeholderResendCache.get(messageKey?.id))) {
|
|
54
|
+
+ logger.error({ session: sessionJid, msgId: messageKey?.id, remoteJid: messageKey?.remoteJid, attempt }, 'CTWA_TRACE: PDO response received within 15s');
|
|
55
|
+
+ return stanzaId;
|
|
56
|
+
+ }
|
|
57
|
+
+ // No response yet
|
|
58
|
+
+ if (attempt < MAX_PDO_ATTEMPTS) {
|
|
59
|
+
+ logger.error({ session: sessionJid, msgId: messageKey?.id, remoteJid: messageKey?.remoteJid, attempt, maxAttempts: MAX_PDO_ATTEMPTS }, 'CTWA: PDO no response after 15s, retrying...');
|
|
60
|
+
+ }
|
|
61
|
+
}
|
|
62
|
+
- }, 8000);
|
|
63
|
+
- return sendPeerDataOperationMessage(pdoMessage);
|
|
64
|
+
+ catch (err) {
|
|
65
|
+
+ logger.error({ session: sessionJid, msgId: messageKey?.id, error: err, attempt }, 'CTWA: PDO request failed');
|
|
66
|
+
+ if (attempt >= MAX_PDO_ATTEMPTS) {
|
|
67
|
+
+ break;
|
|
68
|
+
+ }
|
|
69
|
+
+ }
|
|
70
|
+
+ }
|
|
71
|
+
+ // All attempts exhausted — message lost
|
|
72
|
+
+ logger.error({ session: sessionJid, msgId: messageKey?.id, remoteJid: messageKey?.remoteJid, attempts: MAX_PDO_ATTEMPTS }, 'CTWA: PDO no response after all retries - phone possibly offline, message LOST');
|
|
73
|
+
+ ev.emit('ctwa.failure', { messageKey, msgData, session: sessionJid });
|
|
74
|
+
+ await placeholderResendCache.del(messageKey?.id);
|
|
75
|
+
+ return stanzaId;
|
|
76
|
+
};
|
|
77
|
+
const handleMexNotification = async (node) => {
|
|
78
|
+
const updateNode = getBinaryNodeChild(node, 'update');
|
|
79
|
+
@@ -1296,68 +1328,104 @@
|
|
80
|
+
messageRetryManager.addRecentMessage(msg.key.remoteJid, msg.key.id, msg.message);
|
|
81
|
+
}
|
|
82
|
+
// message failed to decrypt
|
|
83
|
+
- if (msg.messageStubType === proto.WebMessageInfo.StubType.CIPHERTEXT && msg.category !== 'peer') {
|
|
84
|
+
- if (msg?.messageStubParameters?.[0] === MISSING_KEYS_ERROR_TEXT) {
|
|
85
|
+
- acked = true;
|
|
86
|
+
- return sendMessageAck(node, NACK_REASONS.ParsingError);
|
|
87
|
+
- }
|
|
88
|
+
- if (msg.messageStubParameters?.[0] === NO_MESSAGE_FOUND_ERROR_TEXT) {
|
|
89
|
+
- // Message arrived without encryption (e.g. CTWA ads messages).
|
|
90
|
+
- // Check if this is eligible for placeholder resend (matching WA Web filters).
|
|
91
|
+
+ if (msg.messageStubType === proto.WebMessageInfo.StubType.CIPHERTEXT) {
|
|
92
|
+
+ const sessionJid = authState.creds.me?.id || 'unknown';
|
|
93
|
+
+ const stubParam = msg?.messageStubParameters?.[0] || '';
|
|
94
|
+
+ logger.error({ session: sessionJid, msgId: msg.key?.id, remoteJid: msg.key?.remoteJid, fromMe: msg.key?.fromMe, stubParam, category, msgCategory: msg.category }, 'CTWA_TRACE: CIPHERTEXT stub received');
|
|
95
|
+
+ // CTWA placeholder recovery — handle BEFORE category gate.
|
|
96
|
+
+ // "Message absent from node" means the message content was never sent to this
|
|
97
|
+
+ // linked device (common for CTWA ad messages). We must attempt PDO recovery
|
|
98
|
+
+ // regardless of message category, because CTWA messages may arrive with any
|
|
99
|
+
+ // category value including 'peer'.
|
|
100
|
+
+ if (stubParam === NO_MESSAGE_FOUND_ERROR_TEXT) {
|
|
101
|
+
+ // Skip newsletter/channel messages — these are not CTWA ads
|
|
102
|
+
+ if (isJidNewsletter(msg.key?.remoteJid || node.attrs.from || '')) {
|
|
103
|
+
+ logger.error({ session: sessionJid, msgId: msg.key?.id, remoteJid: msg.key?.remoteJid }, 'CTWA_TRACE: Skipping placeholder resend for newsletter/channel message');
|
|
104
|
+
+ acked = true;
|
|
105
|
+
+ return sendMessageAck(node);
|
|
106
|
+
+ }
|
|
107
|
+
const unavailableNode = getBinaryNodeChild(node, 'unavailable');
|
|
108
|
+
const unavailableType = unavailableNode?.attrs?.type;
|
|
109
|
+
if (unavailableType === 'bot_unavailable_fanout' ||
|
|
110
|
+
unavailableType === 'hosted_unavailable_fanout' ||
|
|
111
|
+
unavailableType === 'view_once_unavailable_fanout') {
|
|
112
|
+
- logger.debug({ msgId: msg.key.id, unavailableType }, 'skipping placeholder resend for excluded unavailable type');
|
|
113
|
+
+ logger.error({ session: sessionJid, msgId: msg.key?.id, unavailableType }, 'CTWA_TRACE: Skipping placeholder resend for excluded unavailable type');
|
|
114
|
+
acked = true;
|
|
115
|
+
return sendMessageAck(node);
|
|
116
|
+
}
|
|
117
|
+
const messageAge = unixTimestampSeconds() - toNumber(msg.messageTimestamp);
|
|
118
|
+
if (messageAge > PLACEHOLDER_MAX_AGE_SECONDS) {
|
|
119
|
+
- logger.debug({ msgId: msg.key.id, messageAge }, 'skipping placeholder resend for old message');
|
|
120
|
+
+ logger.error({ session: sessionJid, msgId: msg.key?.id, messageAge }, 'CTWA_TRACE: Skipping placeholder resend for old message (>3 days)');
|
|
121
|
+
acked = true;
|
|
122
|
+
return sendMessageAck(node);
|
|
123
|
+
}
|
|
124
|
+
- // Request the real content from the phone via placeholder resend PDO.
|
|
125
|
+
- // Upsert the CIPHERTEXT stub as a placeholder (like WA Web's processPlaceholderMsg),
|
|
126
|
+
- // and store the requestId in stubParameters[1] so users can correlate
|
|
127
|
+
- // with the incoming PDO response event.
|
|
128
|
+
+ logger.error({ session: sessionJid, msgId: msg.key?.id, remoteJid: msg.key?.remoteJid, fromMe: msg.key?.fromMe, category, msgCategory: msg.category }, 'CTWA: Message absent from node detected, requesting placeholder resend from phone');
|
|
129
|
+
+ // Build clean key and cache original metadata so PDO response handler
|
|
130
|
+
+ // can preserve LID details, timestamps, pushName, etc.
|
|
131
|
+
const cleanKey = {
|
|
132
|
+
remoteJid: msg.key.remoteJid,
|
|
133
|
+
fromMe: msg.key.fromMe,
|
|
134
|
+
id: msg.key.id,
|
|
135
|
+
participant: msg.key.participant
|
|
136
|
+
};
|
|
137
|
+
- // Cache the original message metadata so the PDO response handler
|
|
138
|
+
- // can preserve key fields (LID details etc.) that the phone may omit
|
|
139
|
+
+ // Extract CTWA ad metadata from raw node for fallback message construction
|
|
140
|
+
+ // (used by src/wa.ts ctwa.failure handler when PDO never recovers content)
|
|
141
|
+
+ const senderPn = node.attrs?.sender_pn || '';
|
|
142
|
+
+ const urlTextNode = getBinaryNodeChild(node, 'url_text');
|
|
143
|
+
+ const urlNumberNode = getBinaryNodeChild(node, 'url_number');
|
|
144
|
+
+ const ctwaUrlText = urlTextNode?.content instanceof Uint8Array
|
|
145
|
+
+ ? Buffer.from(urlTextNode.content).toString('utf-8')
|
|
146
|
+
+ : (typeof urlTextNode?.content === 'string' ? urlTextNode.content : '');
|
|
147
|
+
+ const ctwaUrlNumber = urlNumberNode?.content instanceof Uint8Array
|
|
148
|
+
+ ? Buffer.from(urlNumberNode.content).toString('utf-8')
|
|
149
|
+
+ : (typeof urlNumberNode?.content === 'string' ? urlNumberNode.content : '');
|
|
150
|
+
+ const reportingNode = getBinaryNodeChild(node, 'reporting');
|
|
151
|
+
+ const reportingTag = reportingNode?.attrs?.reporting_tag || '';
|
|
152
|
+
const msgData = {
|
|
153
|
+
key: msg.key,
|
|
154
|
+
messageTimestamp: msg.messageTimestamp,
|
|
155
|
+
pushName: msg.pushName,
|
|
156
|
+
participant: msg.participant,
|
|
157
|
+
- verifiedBizName: msg.verifiedBizName
|
|
158
|
+
+ verifiedBizName: msg.verifiedBizName,
|
|
159
|
+
+ ctwaContext: {
|
|
160
|
+
+ senderPn: senderPn,
|
|
161
|
+
+ urlText: ctwaUrlText,
|
|
162
|
+
+ urlNumber: ctwaUrlNumber,
|
|
163
|
+
+ reportingTag: reportingTag
|
|
164
|
+
+ }
|
|
165
|
+
};
|
|
166
|
+
requestPlaceholderResend(cleanKey, msgData)
|
|
167
|
+
- .then(requestId => {
|
|
168
|
+
- if (requestId && requestId !== 'RESOLVED') {
|
|
169
|
+
- logger.debug({ msgId: msg.key.id, requestId }, 'requested placeholder resend for unavailable message');
|
|
170
|
+
+ .then(result => {
|
|
171
|
+
+ if (result === 'RESOLVED') {
|
|
172
|
+
+ logger.error({ session: sessionJid, msgId: msg.key.id, remoteJid: msg.key.remoteJid }, 'CTWA: Message received during resend delay');
|
|
173
|
+
+ }
|
|
174
|
+
+ else if (result) {
|
|
175
|
+
+ logger.error({ session: sessionJid, msgId: msg.key.id, remoteJid: msg.key.remoteJid, requestId: result }, 'CTWA: Placeholder resend PDO request sent');
|
|
176
|
+
ev.emit('messages.update', [
|
|
177
|
+
{
|
|
178
|
+
key: msg.key,
|
|
179
|
+
- update: { messageStubParameters: [NO_MESSAGE_FOUND_ERROR_TEXT, requestId] }
|
|
180
|
+
+ update: { messageStubParameters: [NO_MESSAGE_FOUND_ERROR_TEXT, result] }
|
|
181
|
+
}
|
|
182
|
+
]);
|
|
183
|
+
}
|
|
184
|
+
+ else {
|
|
185
|
+
+ logger.error({ session: sessionJid, msgId: msg.key.id, remoteJid: msg.key.remoteJid }, 'CTWA: Placeholder resend skipped (already requested or dedup)');
|
|
186
|
+
+ }
|
|
187
|
+
})
|
|
188
|
+
- .catch(err => {
|
|
189
|
+
- logger.warn({ err, msgId: msg.key.id }, 'failed to request placeholder resend for unavailable message');
|
|
190
|
+
+ .catch(error => {
|
|
191
|
+
+ logger.error({ session: sessionJid, error, msgId: msg.key.id }, 'CTWA: Failed to request placeholder resend');
|
|
192
|
+
});
|
|
193
|
+
acked = true;
|
|
194
|
+
await sendMessageAck(node);
|
|
195
|
+
// Don't return — fall through to upsertMessage so the stub is emitted
|
|
196
|
+
}
|
|
197
|
+
- else {
|
|
198
|
+
+ else if (msg.category !== 'peer') {
|
|
199
|
+
+ // For all other CIPHERTEXT errors, apply the category gate
|
|
200
|
+
+ // (peer category messages use a different retry mechanism)
|
|
201
|
+
+ if (stubParam === MISSING_KEYS_ERROR_TEXT) {
|
|
202
|
+
+ acked = true;
|
|
203
|
+
+ return sendMessageAck(node, NACK_REASONS.ParsingError);
|
|
204
|
+
+ }
|
|
205
|
+
// Skip retry for expired status messages (>24h old)
|
|
206
|
+
if (isJidStatusBroadcast(msg.key.remoteJid)) {
|
|
207
|
+
const messageAge = unixTimestampSeconds() - toNumber(msg.messageTimestamp);
|
|
208
|
+
--- a/node_modules/baileys/lib/Utils/process-message.js
|
|
209
|
+
+++ b/node_modules/baileys/lib/Utils/process-message.js
|
|
210
|
+
@@ -333,6 +333,9 @@
|
|
211
|
+
//eslint-disable-next-line max-depth
|
|
212
|
+
if (msgId) {
|
|
213
|
+
await placeholderResendCache?.del(msgId);
|
|
214
|
+
+ // Also clean up pdo_ mapping entries written by requestPlaceholderResend
|
|
215
|
+
+ await placeholderResendCache?.del(`pdo_${response.stanzaId}`);
|
|
216
|
+
+ logger?.error({ session: meId, stanzaId: response.stanzaId, msgId }, 'CTWA_TRACE: Cleared cache for PDO response');
|
|
217
|
+
}
|
|
218
|
+
let finalMsg;
|
|
219
|
+
//eslint-disable-next-line max-depth
|
|
220
|
+
@@ -344,11 +347,53 @@
|
|
221
|
+
cachedData.messageTimestamp = webMessageInfo.messageTimestamp;
|
|
222
|
+
}
|
|
223
|
+
finalMsg = cachedData;
|
|
224
|
+
+ logger?.error({ session: meId, msgId }, 'CTWA_TRACE: Merged PDO content with cached metadata');
|
|
225
|
+
}
|
|
226
|
+
else {
|
|
227
|
+
finalMsg = webMessageInfo;
|
|
228
|
+
}
|
|
229
|
+
- logger?.debug({ msgId, requestId: response.stanzaId }, 'received placeholder resend');
|
|
230
|
+
+ // Apply LID resolution and JID normalization so recovered messages
|
|
231
|
+
+ // surface under their phone-number JID (matches src/wa.ts expectations)
|
|
232
|
+
+ //eslint-disable-next-line max-depth
|
|
233
|
+
+ if (finalMsg.key?.remoteJid) {
|
|
234
|
+
+ const rawJid = finalMsg.key.remoteJid;
|
|
235
|
+
+ //eslint-disable-next-line max-depth
|
|
236
|
+
+ if (isLidUser(rawJid)) {
|
|
237
|
+
+ try {
|
|
238
|
+
+ const pn = await signalRepository.lidMapping.getPNForLID(rawJid);
|
|
239
|
+
+ //eslint-disable-next-line max-depth
|
|
240
|
+
+ if (pn) {
|
|
241
|
+
+ const phoneJid = jidNormalizedUser(pn.includes('@') ? pn : `${pn}@s.whatsapp.net`);
|
|
242
|
+
+ logger?.error({ session: meId, lid: rawJid, resolved: phoneJid }, 'CTWA: Resolved LID to phone number for recovered message');
|
|
243
|
+
+ finalMsg.key.remoteJidAlt = rawJid;
|
|
244
|
+
+ finalMsg.key.remoteJid = phoneJid;
|
|
245
|
+
+ }
|
|
246
|
+
+ else {
|
|
247
|
+
+ logger?.error({ session: meId, lid: rawJid }, 'CTWA: Could not resolve LID to phone number - no mapping found');
|
|
248
|
+
+ }
|
|
249
|
+
+ }
|
|
250
|
+
+ catch (e) {
|
|
251
|
+
+ logger?.error({ session: meId, lid: rawJid, error: e }, 'CTWA: Error resolving LID to phone number');
|
|
252
|
+
+ }
|
|
253
|
+
+ }
|
|
254
|
+
+ else {
|
|
255
|
+
+ const normalized = jidNormalizedUser(rawJid);
|
|
256
|
+
+ //eslint-disable-next-line max-depth
|
|
257
|
+
+ if (normalized !== rawJid) {
|
|
258
|
+
+ logger?.error({ session: meId, rawJid, normalized }, 'CTWA: Normalized remoteJid (stripped device suffix)');
|
|
259
|
+
+ finalMsg.key.remoteJid = normalized;
|
|
260
|
+
+ }
|
|
261
|
+
+ }
|
|
262
|
+
+ }
|
|
263
|
+
+ //eslint-disable-next-line max-depth
|
|
264
|
+
+ if (finalMsg.key?.participant) {
|
|
265
|
+
+ const normalizedParticipant = jidNormalizedUser(finalMsg.key.participant);
|
|
266
|
+
+ //eslint-disable-next-line max-depth
|
|
267
|
+
+ if (normalizedParticipant !== finalMsg.key.participant) {
|
|
268
|
+
+ finalMsg.key.participant = normalizedParticipant;
|
|
269
|
+
+ }
|
|
270
|
+
+ }
|
|
271
|
+
+ logger?.error({ session: meId, msgId: finalMsg.key?.id, remoteJid: finalMsg.key?.remoteJid, requestId: response.stanzaId }, 'CTWA: Message recovered from phone via PDO');
|
|
272
|
+
ev.emit('messages.upsert', {
|
|
273
|
+
messages: [finalMsg],
|
|
274
|
+
type: 'notify',
|
|
275
|
+
@@ -356,7 +401,7 @@
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
catch (err) {
|
|
279
|
+
- logger?.warn({ err, stanzaId: response.stanzaId }, 'failed to decode placeholder resend response');
|
|
280
|
+
+ logger?.error({ session: meId, err, stanzaId: response.stanzaId }, 'CTWA: Failed to decode placeholder resend response');
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
--- a/node_modules/baileys/lib/Utils/validate-connection.js
|
|
285
|
+
+++ b/node_modules/baileys/lib/Utils/validate-connection.js
|
|
286
|
+
@@ -13,7 +13,7 @@
|
|
287
|
+
secondary: config.version[1],
|
|
288
|
+
tertiary: config.version[2]
|
|
289
|
+
},
|
|
290
|
+
- platform: proto.ClientPayload.UserAgent.Platform.WEB,
|
|
291
|
+
+ platform: proto.ClientPayload.UserAgent.Platform.MACOS,
|
|
292
|
+
releaseChannel: proto.ClientPayload.UserAgent.ReleaseChannel.RELEASE,
|
|
293
|
+
osVersion: '0.1',
|
|
294
|
+
device: 'Desktop',
|
|
@@ -1,389 +0,0 @@
|
|
|
1
|
-
diff --git a/node_modules/baileys/lib/Socket/messages-recv.js b/node_modules/baileys/lib/Socket/messages-recv.js
|
|
2
|
-
index f2e2e10..1535b62 100644
|
|
3
|
-
--- a/node_modules/baileys/lib/Socket/messages-recv.js
|
|
4
|
-
+++ b/node_modules/baileys/lib/Socket/messages-recv.js
|
|
5
|
-
@@ -50,20 +50,24 @@ export const makeMessagesRecvSocket = (config) => {
|
|
6
|
-
};
|
|
7
|
-
return sendPeerDataOperationMessage(pdoMessage);
|
|
8
|
-
};
|
|
9
|
-
- const requestPlaceholderResend = async (messageKey) => {
|
|
10
|
-
+ const requestPlaceholderResend = async (messageKey, msgData) => {
|
|
11
|
-
+ const sessionJid = authState.creds.me?.id || 'unknown';
|
|
12
|
-
if (!authState.creds.me?.id) {
|
|
13
|
-
throw new Boom('Not authenticated');
|
|
14
|
-
}
|
|
15
|
-
if (placeholderResendCache.get(messageKey?.id)) {
|
|
16
|
-
- logger.debug({ messageKey }, 'already requested resend');
|
|
17
|
-
+ logger.error({ session: sessionJid, msgId: messageKey?.id, remoteJid: messageKey?.remoteJid }, 'CTWA_TRACE: Placeholder resend already requested (dedup), skipping');
|
|
18
|
-
return;
|
|
19
|
-
}
|
|
20
|
-
else {
|
|
21
|
-
- await placeholderResendCache.set(messageKey?.id, true);
|
|
22
|
-
+ // Store original message data so PDO response handler can preserve
|
|
23
|
-
+ // metadata (LID details, timestamps, pushName, etc.) that the phone may omit
|
|
24
|
-
+ await placeholderResendCache.set(messageKey?.id, msgData || true);
|
|
25
|
-
}
|
|
26
|
-
- await delay(5000);
|
|
27
|
-
+ logger.error({ session: sessionJid, msgId: messageKey?.id, remoteJid: messageKey?.remoteJid }, 'CTWA_TRACE: Waiting 2s before sending PDO request');
|
|
28
|
-
+ await delay(2000);
|
|
29
|
-
if (!placeholderResendCache.get(messageKey?.id)) {
|
|
30
|
-
- logger.debug({ messageKey }, 'message received while resend requested');
|
|
31
|
-
+ logger.error({ session: sessionJid, msgId: messageKey?.id, remoteJid: messageKey?.remoteJid }, 'CTWA_TRACE: Message arrived during 5s wait, no PDO needed');
|
|
32
|
-
return 'RESOLVED';
|
|
33
|
-
}
|
|
34
|
-
const pdoMessage = {
|
|
35
|
-
@@ -74,13 +78,51 @@ export const makeMessagesRecvSocket = (config) => {
|
|
36
|
-
],
|
|
37
|
-
peerDataOperationRequestType: proto.Message.PeerDataOperationRequestType.PLACEHOLDER_MESSAGE_RESEND
|
|
38
|
-
};
|
|
39
|
-
- setTimeout(async () => {
|
|
40
|
-
- if (placeholderResendCache.get(messageKey?.id)) {
|
|
41
|
-
- logger.debug({ messageKey }, 'PDO message without response after 15 seconds. Phone possibly offline');
|
|
42
|
-
- await placeholderResendCache.del(messageKey?.id);
|
|
43
|
-
- }
|
|
44
|
-
- }, 15000);
|
|
45
|
-
- return sendPeerDataOperationMessage(pdoMessage);
|
|
46
|
-
+ // Send first PDO attempt
|
|
47
|
-
+ const stanzaId = await sendPeerDataOperationMessage(pdoMessage);
|
|
48
|
-
+ // Store mapping: stanzaId → messageKey.id so PDO response can clear the right cache entry
|
|
49
|
-
+ if (stanzaId) {
|
|
50
|
-
+ await placeholderResendCache.set(`pdo_${stanzaId}`, messageKey?.id);
|
|
51
|
-
+ }
|
|
52
|
-
+ // Wait 15s for response, then retry once if no response
|
|
53
|
-
+ await new Promise((resolve) => {
|
|
54
|
-
+ setTimeout(async () => {
|
|
55
|
-
+ if (!placeholderResendCache.get(messageKey?.id)) {
|
|
56
|
-
+ logger.error({ session: sessionJid, msgId: messageKey?.id, remoteJid: messageKey?.remoteJid }, 'CTWA_TRACE: PDO response received within 15s (first attempt)');
|
|
57
|
-
+ resolve(undefined);
|
|
58
|
-
+ return;
|
|
59
|
-
+ }
|
|
60
|
-
+ // First attempt failed — retry once
|
|
61
|
-
+ logger.error({ session: sessionJid, msgId: messageKey?.id, remoteJid: messageKey?.remoteJid }, 'CTWA: PDO no response after 15s, retrying once...');
|
|
62
|
-
+ try {
|
|
63
|
-
+ const retryStanzaId = await sendPeerDataOperationMessage(pdoMessage);
|
|
64
|
-
+ if (retryStanzaId) {
|
|
65
|
-
+ await placeholderResendCache.set(`pdo_${retryStanzaId}`, messageKey?.id);
|
|
66
|
-
+ }
|
|
67
|
-
+ // Wait another 15s for retry response
|
|
68
|
-
+ setTimeout(async () => {
|
|
69
|
-
+ if (placeholderResendCache.get(messageKey?.id)) {
|
|
70
|
-
+ logger.error({ session: sessionJid, msgId: messageKey?.id, remoteJid: messageKey?.remoteJid }, 'CTWA: PDO no response after retry - phone possibly offline, message LOST');
|
|
71
|
-
+ // Emit failure event so wa.ts can notify backend
|
|
72
|
-
+ ev.emit('ctwa.failure', { messageKey, session: sessionJid });
|
|
73
|
-
+ await placeholderResendCache.del(messageKey?.id);
|
|
74
|
-
+ }
|
|
75
|
-
+ else {
|
|
76
|
-
+ logger.error({ session: sessionJid, msgId: messageKey?.id, remoteJid: messageKey?.remoteJid }, 'CTWA_TRACE: PDO response received within 15s (retry attempt)');
|
|
77
|
-
+ }
|
|
78
|
-
+ resolve(undefined);
|
|
79
|
-
+ }, 15000);
|
|
80
|
-
+ }
|
|
81
|
-
+ catch (retryErr) {
|
|
82
|
-
+ logger.error({ session: sessionJid, msgId: messageKey?.id, error: retryErr }, 'CTWA: PDO retry request failed');
|
|
83
|
-
+ // Emit failure event
|
|
84
|
-
+ ev.emit('ctwa.failure', { messageKey, session: sessionJid });
|
|
85
|
-
+ await placeholderResendCache.del(messageKey?.id);
|
|
86
|
-
+ resolve(undefined);
|
|
87
|
-
+ }
|
|
88
|
-
+ }, 15000);
|
|
89
|
-
+ });
|
|
90
|
-
+ return stanzaId;
|
|
91
|
-
};
|
|
92
|
-
// Handles mex newsletter notifications
|
|
93
|
-
const handleMexNewsletterNotification = async (node) => {
|
|
94
|
-
@@ -987,53 +1029,125 @@ export const makeMessagesRecvSocket = (config) => {
|
|
95
|
-
await processingMutex.mutex(async () => {
|
|
96
|
-
await decrypt();
|
|
97
|
-
// message failed to decrypt
|
|
98
|
-
- if (msg.messageStubType === proto.WebMessageInfo.StubType.CIPHERTEXT && msg.category !== 'peer') {
|
|
99
|
-
- if (msg?.messageStubParameters?.[0] === MISSING_KEYS_ERROR_TEXT ||
|
|
100
|
-
- msg.messageStubParameters?.[0] === NO_MESSAGE_FOUND_ERROR_TEXT) {
|
|
101
|
-
- return sendMessageAck(node);
|
|
102
|
-
+ if (msg.messageStubType === proto.WebMessageInfo.StubType.CIPHERTEXT) {
|
|
103
|
-
+ const sessionJid = authState.creds.me?.id || 'unknown';
|
|
104
|
-
+ const stubParam = msg?.messageStubParameters?.[0] || '';
|
|
105
|
-
+ logger.error({ session: sessionJid, msgId: msg.key?.id, remoteJid: msg.key?.remoteJid, fromMe: msg.key?.fromMe, stubParam, category, msgCategory: msg.category }, 'CTWA_TRACE: CIPHERTEXT stub received');
|
|
106
|
-
+ // CTWA placeholder recovery — handle BEFORE category gate
|
|
107
|
-
+ // "Message absent from node" means the message content was never sent to this
|
|
108
|
-
+ // linked device (common for CTWA ad messages). We must attempt PDO recovery
|
|
109
|
-
+ // regardless of message category, because CTWA messages may arrive with any
|
|
110
|
-
+ // category value including 'peer'.
|
|
111
|
-
+ if (stubParam === NO_MESSAGE_FOUND_ERROR_TEXT) {
|
|
112
|
-
+ // Filter out unavailable types that shouldn't trigger PDO (from official fix)
|
|
113
|
-
+ const unavailableNode = getBinaryNodeChild(node, 'unavailable');
|
|
114
|
-
+ const unavailableType = unavailableNode?.attrs?.type;
|
|
115
|
-
+ if (
|
|
116
|
-
+ unavailableType === 'bot_unavailable_fanout' ||
|
|
117
|
-
+ unavailableType === 'hosted_unavailable_fanout' ||
|
|
118
|
-
+ unavailableType === 'view_once_unavailable_fanout'
|
|
119
|
-
+ ) {
|
|
120
|
-
+ logger.error({ session: sessionJid, msgId: msg.key?.id, unavailableType }, 'CTWA_TRACE: Skipping placeholder resend for excluded unavailable type');
|
|
121
|
-
+ return sendMessageAck(node);
|
|
122
|
-
+ }
|
|
123
|
-
+ // Skip PDO for messages older than 14 days (WA Web enforces this limit)
|
|
124
|
-
+ const PLACEHOLDER_MAX_AGE_SECONDS = 14 * 24 * 60 * 60;
|
|
125
|
-
+ const messageAge = unixTimestampSeconds() - (typeof msg.messageTimestamp === 'number' ? msg.messageTimestamp : Number(msg.messageTimestamp || 0));
|
|
126
|
-
+ if (messageAge > PLACEHOLDER_MAX_AGE_SECONDS) {
|
|
127
|
-
+ logger.error({ session: sessionJid, msgId: msg.key?.id, messageAge }, 'CTWA_TRACE: Skipping placeholder resend for old message (>14 days)');
|
|
128
|
-
+ return sendMessageAck(node);
|
|
129
|
-
+ }
|
|
130
|
-
+ if (msg.key) {
|
|
131
|
-
+ logger.error({ session: sessionJid, msgId: msg.key.id, remoteJid: msg.key.remoteJid, fromMe: msg.key.fromMe, category, msgCategory: msg.category }, 'CTWA: Message absent from node detected, requesting placeholder resend from phone');
|
|
132
|
-
+ // Build clean key and cache original metadata (from official fix)
|
|
133
|
-
+ // so PDO response handler can preserve LID details, timestamps, etc.
|
|
134
|
-
+ const cleanKey = {
|
|
135
|
-
+ remoteJid: msg.key.remoteJid,
|
|
136
|
-
+ fromMe: msg.key.fromMe,
|
|
137
|
-
+ id: msg.key.id,
|
|
138
|
-
+ participant: msg.key.participant
|
|
139
|
-
+ };
|
|
140
|
-
+ const msgData = {
|
|
141
|
-
+ key: msg.key,
|
|
142
|
-
+ messageTimestamp: msg.messageTimestamp,
|
|
143
|
-
+ pushName: msg.pushName,
|
|
144
|
-
+ participant: msg.participant,
|
|
145
|
-
+ verifiedBizName: msg.verifiedBizName
|
|
146
|
-
+ };
|
|
147
|
-
+ requestPlaceholderResend(cleanKey, msgData)
|
|
148
|
-
+ .then((result) => {
|
|
149
|
-
+ if (result === 'RESOLVED') {
|
|
150
|
-
+ logger.error({ session: sessionJid, msgId: msg.key.id, remoteJid: msg.key.remoteJid }, 'CTWA: Message received during resend delay');
|
|
151
|
-
+ }
|
|
152
|
-
+ else if (result) {
|
|
153
|
-
+ logger.error({ session: sessionJid, msgId: msg.key.id, remoteJid: msg.key.remoteJid, requestId: result }, 'CTWA: Placeholder resend PDO request sent');
|
|
154
|
-
+ // Store requestId in stubParameters for correlation (from official fix)
|
|
155
|
-
+ ev.emit('messages.update', [{
|
|
156
|
-
+ key: msg.key,
|
|
157
|
-
+ update: { messageStubParameters: [NO_MESSAGE_FOUND_ERROR_TEXT, result] }
|
|
158
|
-
+ }]);
|
|
159
|
-
+ }
|
|
160
|
-
+ else {
|
|
161
|
-
+ logger.error({ session: sessionJid, msgId: msg.key.id, remoteJid: msg.key.remoteJid }, 'CTWA: Placeholder resend skipped (already requested or dedup)');
|
|
162
|
-
+ }
|
|
163
|
-
+ })
|
|
164
|
-
+ .catch((error) => {
|
|
165
|
-
+ logger.error({ session: sessionJid, error, msgId: msg.key.id }, 'CTWA: Failed to request placeholder resend');
|
|
166
|
-
+ });
|
|
167
|
-
+ }
|
|
168
|
-
+ // ACK but DON'T return — fall through to upsertMessage so the
|
|
169
|
-
+ // CIPHERTEXT stub is emitted as a placeholder (from official fix)
|
|
170
|
-
+ await sendMessageAck(node);
|
|
171
|
-
}
|
|
172
|
-
- const errorMessage = msg?.messageStubParameters?.[0] || '';
|
|
173
|
-
- const isPreKeyError = errorMessage.includes('PreKey');
|
|
174
|
-
- logger.debug(`[handleMessage] Attempting retry request for failed decryption`);
|
|
175
|
-
- // Handle both pre-key and normal retries in single mutex
|
|
176
|
-
- await retryMutex.mutex(async () => {
|
|
177
|
-
- try {
|
|
178
|
-
- if (!ws.isOpen) {
|
|
179
|
-
- logger.debug({ node }, 'Connection closed, skipping retry');
|
|
180
|
-
- return;
|
|
181
|
-
- }
|
|
182
|
-
- // Handle pre-key errors with upload and delay
|
|
183
|
-
- if (isPreKeyError) {
|
|
184
|
-
- logger.info({ error: errorMessage }, 'PreKey error detected, uploading and retrying');
|
|
185
|
-
- try {
|
|
186
|
-
- logger.debug('Uploading pre-keys for error recovery');
|
|
187
|
-
- await uploadPreKeys(5);
|
|
188
|
-
- logger.debug('Waiting for server to process new pre-keys');
|
|
189
|
-
- await delay(1000);
|
|
190
|
-
- }
|
|
191
|
-
- catch (uploadErr) {
|
|
192
|
-
- logger.error({ uploadErr }, 'Pre-key upload failed, proceeding with retry anyway');
|
|
193
|
-
- }
|
|
194
|
-
- }
|
|
195
|
-
- const encNode = getBinaryNodeChild(node, 'enc');
|
|
196
|
-
- await sendRetryRequest(node, !encNode);
|
|
197
|
-
- if (retryRequestDelayMs) {
|
|
198
|
-
- await delay(retryRequestDelayMs);
|
|
199
|
-
- }
|
|
200
|
-
+ // For all other CIPHERTEXT errors, apply the category gate
|
|
201
|
-
+ // (peer category messages use a different retry mechanism)
|
|
202
|
-
+ else if (msg.category !== 'peer') {
|
|
203
|
-
+ if (stubParam === MISSING_KEYS_ERROR_TEXT) {
|
|
204
|
-
+ return sendMessageAck(node);
|
|
205
|
-
}
|
|
206
|
-
- catch (err) {
|
|
207
|
-
- logger.error({ err, isPreKeyError }, 'Failed to handle retry, attempting basic retry');
|
|
208
|
-
- // Still attempt retry even if pre-key upload failed
|
|
209
|
-
+ const errorMessage = msg?.messageStubParameters?.[0] || '';
|
|
210
|
-
+ const isPreKeyError = errorMessage.includes('PreKey');
|
|
211
|
-
+ logger.debug(`[handleMessage] Attempting retry request for failed decryption`);
|
|
212
|
-
+ // Handle both pre-key and normal retries in single mutex
|
|
213
|
-
+ await retryMutex.mutex(async () => {
|
|
214
|
-
try {
|
|
215
|
-
+ if (!ws.isOpen) {
|
|
216
|
-
+ logger.debug({ node }, 'Connection closed, skipping retry');
|
|
217
|
-
+ return;
|
|
218
|
-
+ }
|
|
219
|
-
+ // Handle pre-key errors with upload and delay
|
|
220
|
-
+ if (isPreKeyError) {
|
|
221
|
-
+ logger.info({ error: errorMessage }, 'PreKey error detected, uploading and retrying');
|
|
222
|
-
+ try {
|
|
223
|
-
+ logger.debug('Uploading pre-keys for error recovery');
|
|
224
|
-
+ await uploadPreKeys(5);
|
|
225
|
-
+ logger.debug('Waiting for server to process new pre-keys');
|
|
226
|
-
+ await delay(1000);
|
|
227
|
-
+ }
|
|
228
|
-
+ catch (uploadErr) {
|
|
229
|
-
+ logger.error({ uploadErr }, 'Pre-key upload failed, proceeding with retry anyway');
|
|
230
|
-
+ }
|
|
231
|
-
+ }
|
|
232
|
-
const encNode = getBinaryNodeChild(node, 'enc');
|
|
233
|
-
await sendRetryRequest(node, !encNode);
|
|
234
|
-
+ if (retryRequestDelayMs) {
|
|
235
|
-
+ await delay(retryRequestDelayMs);
|
|
236
|
-
+ }
|
|
237
|
-
}
|
|
238
|
-
- catch (retryErr) {
|
|
239
|
-
- logger.error({ retryErr }, 'Failed to send retry after error handling');
|
|
240
|
-
+ catch (err) {
|
|
241
|
-
+ logger.error({ err, isPreKeyError }, 'Failed to handle retry, attempting basic retry');
|
|
242
|
-
+ // Still attempt retry even if pre-key upload failed
|
|
243
|
-
+ try {
|
|
244
|
-
+ const encNode = getBinaryNodeChild(node, 'enc');
|
|
245
|
-
+ await sendRetryRequest(node, !encNode);
|
|
246
|
-
+ }
|
|
247
|
-
+ catch (retryErr) {
|
|
248
|
-
+ logger.error({ retryErr }, 'Failed to send retry after error handling');
|
|
249
|
-
+ }
|
|
250
|
-
}
|
|
251
|
-
- }
|
|
252
|
-
- await sendMessageAck(node, NACK_REASONS.UnhandledError);
|
|
253
|
-
- });
|
|
254
|
-
+ await sendMessageAck(node, NACK_REASONS.UnhandledError);
|
|
255
|
-
+ });
|
|
256
|
-
+ }
|
|
257
|
-
}
|
|
258
|
-
else {
|
|
259
|
-
const isNewsletter = isJidNewsletter(msg.key.remoteJid);
|
|
260
|
-
diff --git a/node_modules/baileys/lib/Utils/process-message.js b/node_modules/baileys/lib/Utils/process-message.js
|
|
261
|
-
index 7927483..7111219 100644
|
|
262
|
-
--- a/node_modules/baileys/lib/Utils/process-message.js
|
|
263
|
-
+++ b/node_modules/baileys/lib/Utils/process-message.js
|
|
264
|
-
@@ -215,23 +215,99 @@ const processMessage = async (message, { shouldProcessHistoryMsg, placeholderRes
|
|
265
|
-
case proto.Message.ProtocolMessage.Type.PEER_DATA_OPERATION_REQUEST_RESPONSE_MESSAGE:
|
|
266
|
-
const response = protocolMsg.peerDataOperationRequestResponseMessage;
|
|
267
|
-
if (response) {
|
|
268
|
-
- await placeholderResendCache?.del(response.stanzaId);
|
|
269
|
-
// TODO: IMPLEMENT HISTORY SYNC ETC (sticker uploads etc.).
|
|
270
|
-
- const { peerDataOperationResult } = response;
|
|
271
|
-
+ const peerDataOperationResult = response.peerDataOperationResult || [];
|
|
272
|
-
for (const result of peerDataOperationResult) {
|
|
273
|
-
- const { placeholderMessageResendResponse: retryResponse } = result;
|
|
274
|
-
+ const retryResponse = result?.placeholderMessageResendResponse;
|
|
275
|
-
//eslint-disable-next-line max-depth
|
|
276
|
-
- if (retryResponse) {
|
|
277
|
-
+ if (!retryResponse?.webMessageInfoBytes) {
|
|
278
|
-
+ continue;
|
|
279
|
-
+ }
|
|
280
|
-
+ //eslint-disable-next-line max-depth
|
|
281
|
-
+ try {
|
|
282
|
-
const webMessageInfo = proto.WebMessageInfo.decode(retryResponse.webMessageInfoBytes);
|
|
283
|
-
- // wait till another upsert event is available, don't want it to be part of the PDO response message
|
|
284
|
-
- // TODO: parse through proper message handling utilities (to add relevant key fields)
|
|
285
|
-
- setTimeout(() => {
|
|
286
|
-
- ev.emit('messages.upsert', {
|
|
287
|
-
- messages: [webMessageInfo],
|
|
288
|
-
- type: 'notify',
|
|
289
|
-
- requestId: response.stanzaId
|
|
290
|
-
- });
|
|
291
|
-
- }, 500);
|
|
292
|
-
+ const msgId = webMessageInfo.key?.id;
|
|
293
|
-
+ // Retrieve cached original message data (from official fix)
|
|
294
|
-
+ // Preserves LID details, timestamps, pushName, etc. that the phone may omit
|
|
295
|
-
+ const cachedData = msgId ? placeholderResendCache?.get(msgId) : undefined;
|
|
296
|
-
+ //eslint-disable-next-line max-depth
|
|
297
|
-
+ if (msgId) {
|
|
298
|
-
+ await placeholderResendCache?.del(msgId);
|
|
299
|
-
+ // Also clean up pdo_ mapping entries
|
|
300
|
-
+ await placeholderResendCache?.del(`pdo_${response.stanzaId}`);
|
|
301
|
-
+ logger.error({ session: meId, stanzaId: response.stanzaId, msgId }, 'CTWA_TRACE: Cleared cache for PDO response');
|
|
302
|
-
+ }
|
|
303
|
-
+ let finalMsg;
|
|
304
|
-
+ //eslint-disable-next-line max-depth
|
|
305
|
-
+ if (cachedData && typeof cachedData === 'object') {
|
|
306
|
-
+ // Apply decoded message content onto cached metadata (from official fix)
|
|
307
|
-
+ // This preserves LID details, timestamps, pushName, etc.
|
|
308
|
-
+ cachedData.message = webMessageInfo.message;
|
|
309
|
-
+ if (webMessageInfo.messageTimestamp) {
|
|
310
|
-
+ cachedData.messageTimestamp = webMessageInfo.messageTimestamp;
|
|
311
|
-
+ }
|
|
312
|
-
+ finalMsg = cachedData;
|
|
313
|
-
+ logger.error({ session: meId, msgId }, 'CTWA_TRACE: Merged PDO content with cached metadata');
|
|
314
|
-
+ }
|
|
315
|
-
+ else {
|
|
316
|
-
+ finalMsg = webMessageInfo;
|
|
317
|
-
+ }
|
|
318
|
-
+ // Apply LID resolution and JID normalization (our enhancement)
|
|
319
|
-
+ if (finalMsg.key?.remoteJid) {
|
|
320
|
-
+ const rawJid = finalMsg.key.remoteJid;
|
|
321
|
-
+ if (isLidUser(rawJid)) {
|
|
322
|
-
+ try {
|
|
323
|
-
+ const pn = await signalRepository.lidMapping.getPNForLID(rawJid);
|
|
324
|
-
+ if (pn) {
|
|
325
|
-
+ const phoneJid = jidNormalizedUser(pn.includes('@') ? pn : `${pn}@s.whatsapp.net`);
|
|
326
|
-
+ logger.error({ session: meId, lid: rawJid, resolved: phoneJid }, 'CTWA: Resolved LID to phone number for recovered message');
|
|
327
|
-
+ finalMsg.key.remoteJidAlt = rawJid;
|
|
328
|
-
+ finalMsg.key.remoteJid = phoneJid;
|
|
329
|
-
+ }
|
|
330
|
-
+ else {
|
|
331
|
-
+ logger.error({ session: meId, lid: rawJid }, 'CTWA: Could not resolve LID to phone number - no mapping found');
|
|
332
|
-
+ }
|
|
333
|
-
+ }
|
|
334
|
-
+ catch (e) {
|
|
335
|
-
+ logger.error({ session: meId, lid: rawJid, error: e }, 'CTWA: Error resolving LID to phone number');
|
|
336
|
-
+ }
|
|
337
|
-
+ }
|
|
338
|
-
+ else {
|
|
339
|
-
+ const normalized = jidNormalizedUser(rawJid);
|
|
340
|
-
+ if (normalized !== rawJid) {
|
|
341
|
-
+ logger.error({ session: meId, rawJid, normalized }, 'CTWA: Normalized remoteJid (stripped device suffix)');
|
|
342
|
-
+ finalMsg.key.remoteJid = normalized;
|
|
343
|
-
+ }
|
|
344
|
-
+ }
|
|
345
|
-
+ }
|
|
346
|
-
+ // Normalize participant JID if present
|
|
347
|
-
+ if (finalMsg.key?.participant) {
|
|
348
|
-
+ const normalizedParticipant = jidNormalizedUser(finalMsg.key.participant);
|
|
349
|
-
+ if (normalizedParticipant !== finalMsg.key.participant) {
|
|
350
|
-
+ finalMsg.key.participant = normalizedParticipant;
|
|
351
|
-
+ }
|
|
352
|
-
+ }
|
|
353
|
-
+ const recoveredContent = finalMsg.message?.conversation
|
|
354
|
-
+ || finalMsg.message?.extendedTextMessage?.text
|
|
355
|
-
+ || finalMsg.message?.imageMessage?.caption
|
|
356
|
-
+ || finalMsg.message?.videoMessage?.caption
|
|
357
|
-
+ || (finalMsg.message?.imageMessage ? '[image]' : '')
|
|
358
|
-
+ || (finalMsg.message?.videoMessage ? '[video]' : '')
|
|
359
|
-
+ || (finalMsg.message?.audioMessage ? '[audio]' : '')
|
|
360
|
-
+ || (finalMsg.message?.documentMessage ? '[document]' : '')
|
|
361
|
-
+ || (finalMsg.message?.stickerMessage ? '[sticker]' : '')
|
|
362
|
-
+ || (finalMsg.message?.contactMessage ? '[contact]' : '')
|
|
363
|
-
+ || (finalMsg.message?.locationMessage ? '[location]' : '')
|
|
364
|
-
+ || '[unknown type]';
|
|
365
|
-
+ logger.error({ session: meId, msgId: finalMsg.key?.id, remoteJid: finalMsg.key?.remoteJid, recoveredContent, requestId: response.stanzaId }, 'CTWA: Message recovered from phone via PDO');
|
|
366
|
-
+ ev.emit('messages.upsert', {
|
|
367
|
-
+ messages: [finalMsg],
|
|
368
|
-
+ type: 'notify',
|
|
369
|
-
+ requestId: response.stanzaId
|
|
370
|
-
+ });
|
|
371
|
-
+ }
|
|
372
|
-
+ catch (err) {
|
|
373
|
-
+ logger.error({ session: meId, err, stanzaId: response.stanzaId }, 'CTWA: Failed to decode placeholder resend response');
|
|
374
|
-
}
|
|
375
|
-
}
|
|
376
|
-
}
|
|
377
|
-
diff --git a/node_modules/baileys/lib/Utils/validate-connection.js b/node_modules/baileys/lib/Utils/validate-connection.js
|
|
378
|
-
index 42fb902..38a2e0c 100644
|
|
379
|
-
--- a/node_modules/baileys/lib/Utils/validate-connection.js
|
|
380
|
-
+++ b/node_modules/baileys/lib/Utils/validate-connection.js
|
|
381
|
-
@@ -13,7 +13,7 @@ const getUserAgent = (config) => {
|
|
382
|
-
secondary: config.version[1],
|
|
383
|
-
tertiary: config.version[2]
|
|
384
|
-
},
|
|
385
|
-
- platform: proto.ClientPayload.UserAgent.Platform.WEB,
|
|
386
|
-
+ platform: proto.ClientPayload.UserAgent.Platform.MACOS,
|
|
387
|
-
releaseChannel: proto.ClientPayload.UserAgent.ReleaseChannel.RELEASE,
|
|
388
|
-
osVersion: '0.1',
|
|
389
|
-
device: 'Desktop',
|