@periskope/baileys 6.7.19-4 → 6.8.0-alpha-1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/WAProto/GenerateStatics.sh +2 -2
- package/WAProto/index.d.ts +2793 -45578
- package/WAProto/index.js +19344 -147065
- package/lib/Defaults/index.d.ts +5 -0
- package/lib/Defaults/index.d.ts.map +1 -1
- package/lib/Defaults/index.js +8 -2
- package/lib/Defaults/index.js.map +1 -1
- package/lib/Signal/Group/queue-job.d.ts +1 -0
- package/lib/Signal/Group/queue-job.d.ts.map +1 -1
- package/lib/Signal/Group/queue-job.js +3 -0
- package/lib/Signal/Group/queue-job.js.map +1 -1
- package/lib/Signal/Group/sender-key-distribution-message.d.ts.map +1 -1
- package/lib/Signal/Group/sender-key-distribution-message.js +1 -1
- package/lib/Signal/Group/sender-key-distribution-message.js.map +1 -1
- package/lib/Signal/Group/sender-key-message.d.ts.map +1 -1
- package/lib/Signal/Group/sender-key-message.js +1 -1
- package/lib/Signal/Group/sender-key-message.js.map +1 -1
- package/lib/Signal/Group/sender-key-state.d.ts.map +1 -1
- package/lib/Signal/Group/sender-key-state.js +6 -1
- package/lib/Signal/Group/sender-key-state.js.map +1 -1
- package/lib/Signal/libsignal.d.ts.map +1 -1
- package/lib/Signal/libsignal.js +5 -53
- package/lib/Signal/libsignal.js.map +1 -1
- package/lib/Signal/lid-mapping.d.ts +1 -3
- package/lib/Signal/lid-mapping.d.ts.map +1 -1
- package/lib/Signal/lid-mapping.js +3 -29
- package/lib/Signal/lid-mapping.js.map +1 -1
- package/lib/Socket/business.d.ts +14 -5
- package/lib/Socket/business.d.ts.map +1 -1
- package/lib/Socket/business.js +122 -1
- package/lib/Socket/business.js.map +1 -1
- package/lib/Socket/chats.d.ts +8 -2
- package/lib/Socket/chats.d.ts.map +1 -1
- package/lib/Socket/chats.js +39 -2
- package/lib/Socket/chats.js.map +1 -1
- package/lib/Socket/communities.d.ts +13 -2
- package/lib/Socket/communities.d.ts.map +1 -1
- package/lib/Socket/communities.js +45 -1
- package/lib/Socket/communities.js.map +1 -1
- package/lib/Socket/groups.d.ts +7 -2
- package/lib/Socket/groups.d.ts.map +1 -1
- package/lib/Socket/groups.js +1 -1
- package/lib/Socket/groups.js.map +1 -1
- package/lib/Socket/index.d.ts +13 -2
- package/lib/Socket/index.d.ts.map +1 -1
- package/lib/Socket/messages-recv.d.ts +7 -2
- package/lib/Socket/messages-recv.d.ts.map +1 -1
- package/lib/Socket/messages-recv.js +97 -303
- package/lib/Socket/messages-recv.js.map +1 -1
- package/lib/Socket/messages-send.d.ts +7 -2
- package/lib/Socket/messages-send.d.ts.map +1 -1
- package/lib/Socket/messages-send.js +34 -10
- package/lib/Socket/messages-send.js.map +1 -1
- package/lib/Socket/newsletter.d.ts +7 -2
- package/lib/Socket/newsletter.d.ts.map +1 -1
- package/lib/Socket/socket.d.ts +2 -2
- package/lib/Socket/socket.d.ts.map +1 -1
- package/lib/Socket/socket.js +131 -25
- package/lib/Socket/socket.js.map +1 -1
- package/lib/Socket/usync.d.ts +2 -2
- package/lib/Types/Auth.d.ts +0 -2
- package/lib/Types/Auth.d.ts.map +1 -1
- package/lib/Types/Bussines.d.ts +25 -0
- package/lib/Types/Bussines.d.ts.map +1 -0
- package/lib/Types/Bussines.js +2 -0
- package/lib/Types/Bussines.js.map +1 -0
- package/lib/Types/Chat.d.ts +3 -2
- package/lib/Types/Chat.d.ts.map +1 -1
- package/lib/Types/Chat.js.map +1 -1
- package/lib/Types/Message.d.ts +21 -1
- package/lib/Types/Message.d.ts.map +1 -1
- package/lib/Types/Signal.d.ts +0 -3
- package/lib/Types/Signal.d.ts.map +1 -1
- package/lib/Utils/chat-utils.d.ts.map +1 -1
- package/lib/Utils/chat-utils.js +18 -1
- package/lib/Utils/chat-utils.js.map +1 -1
- package/lib/Utils/decode-wa-message.d.ts +2 -57
- package/lib/Utils/decode-wa-message.d.ts.map +1 -1
- package/lib/Utils/decode-wa-message.js +28 -406
- package/lib/Utils/decode-wa-message.js.map +1 -1
- package/lib/Utils/event-buffer.d.ts +1 -1
- package/lib/Utils/event-buffer.d.ts.map +1 -1
- package/lib/Utils/event-buffer.js +5 -76
- package/lib/Utils/event-buffer.js.map +1 -1
- package/lib/Utils/message-retry-manager.d.ts +82 -0
- package/lib/Utils/message-retry-manager.d.ts.map +1 -0
- package/lib/Utils/message-retry-manager.js +149 -0
- package/lib/Utils/message-retry-manager.js.map +1 -0
- package/lib/Utils/messages-media.d.ts.map +1 -1
- package/lib/Utils/messages-media.js +4 -1
- package/lib/Utils/messages-media.js.map +1 -1
- package/lib/Utils/messages.d.ts.map +1 -1
- package/lib/Utils/messages.js +35 -15
- package/lib/Utils/messages.js.map +1 -1
- package/lib/Utils/signal.d.ts +0 -13
- package/lib/Utils/signal.d.ts.map +1 -1
- package/lib/Utils/signal.js +0 -47
- package/lib/Utils/signal.js.map +1 -1
- package/lib/Utils/use-multi-file-auth-state.js +1 -1
- package/lib/Utils/use-multi-file-auth-state.js.map +1 -1
- package/lib/Utils/validate-connection.js +2 -2
- package/lib/Utils/validate-connection.js.map +1 -1
- package/package.json +3 -2
|
@@ -5,7 +5,7 @@ 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 {
|
|
8
|
+
import { aesDecryptCTR, aesEncryptGCM, cleanMessage, Curve, decodeMediaRetryNode, decodeMessageNode, decryptMessageNode, delay, derivePairingCodeKey, encodeBigEndian, encodeSignedDeviceIdentity, getCallStatusFromNode, getHistoryMsg, getNextPreKeys, getStatusFromReceiptType, hkdf, MISSING_KEYS_ERROR_TEXT, NACK_REASONS, NO_MESSAGE_FOUND_ERROR_TEXT, unixTimestampSeconds, xmppPreKey, xmppSignedPreKey } from '../Utils/index.js';
|
|
9
9
|
import { makeMutex } from '../Utils/make-mutex.js';
|
|
10
10
|
import { areJidsSameUser, getAllBinaryNodeChildren, getBinaryNodeChild, getBinaryNodeChildBuffer, getBinaryNodeChildren, getBinaryNodeChildString, isJidGroup, isJidStatusBroadcast, isJidUser, isLidUser, jidDecode, jidNormalizedUser, S_WHATSAPP_NET } from '../WABinary/index.js';
|
|
11
11
|
import { extractGroupMetadata } from './groups.js';
|
|
@@ -14,9 +14,6 @@ export const makeMessagesRecvSocket = (config) => {
|
|
|
14
14
|
const { logger, retryRequestDelayMs, maxMsgRetryCount, getMessage, shouldIgnoreJid } = config;
|
|
15
15
|
const sock = makeMessagesSocket(config);
|
|
16
16
|
const { ev, authState, ws, processingMutex, signalRepository, query, upsertMessage, resyncAppState, onUnexpectedError, assertSessions, sendNode, relayMessage, sendReceipt, uploadPreKeys, sendPeerDataOperationMessage } = sock;
|
|
17
|
-
const PENDING_MESSAGE_DECRYPTIONS = new Map();
|
|
18
|
-
const MAX_DECRYPT_RETRY_COUNT = 3;
|
|
19
|
-
const DECRYPT_RETRY_DELAY = 2000;
|
|
20
17
|
/** this mutex ensures that each retryRequest will wait for the previous one to finish */
|
|
21
18
|
const retryMutex = makeMutex();
|
|
22
19
|
const msgRetryCache = config.msgRetryCounterCache ||
|
|
@@ -110,55 +107,20 @@ export const makeMessagesRecvSocket = (config) => {
|
|
|
110
107
|
const { fullMessage } = decodeMessageNode(node, authState.creds.me.id, authState.creds.me.lid || '');
|
|
111
108
|
const { key: msgKey } = fullMessage;
|
|
112
109
|
const msgId = msgKey.id;
|
|
113
|
-
// const senderJid = node.attrs.from || ''
|
|
114
|
-
// // Check internal retry counter (whatsmeow pattern - max 10 per sender/message)
|
|
115
|
-
// const internalRetryCount = incrementIncomingRetryCounter(senderJid, msgId)
|
|
116
|
-
// if (shouldDropRetryRequest(senderJid, msgId)) {
|
|
117
|
-
// logger.warn(
|
|
118
|
-
// {
|
|
119
|
-
// senderJid,
|
|
120
|
-
// msgId,
|
|
121
|
-
// internalRetryCount
|
|
122
|
-
// },
|
|
123
|
-
// 'Dropping retry request: internal retry counter exceeded limit (10)'
|
|
124
|
-
// )
|
|
125
|
-
// return
|
|
126
|
-
// }
|
|
127
110
|
const key = `${msgId}:${msgKey?.participant}`;
|
|
128
111
|
let retryCount = msgRetryCache.get(key) || 0;
|
|
129
|
-
// Enhanced retry limit check (whatsmeow uses 5 max retries)
|
|
130
112
|
if (retryCount >= maxMsgRetryCount) {
|
|
131
|
-
logger.
|
|
113
|
+
logger.debug({ retryCount, msgId }, 'reached retry limit, clearing');
|
|
132
114
|
msgRetryCache.del(key);
|
|
133
115
|
return;
|
|
134
116
|
}
|
|
135
117
|
retryCount += 1;
|
|
136
118
|
msgRetryCache.set(key, retryCount);
|
|
137
119
|
const { account, signedPreKey, signedIdentityKey: identityKey } = authState.creds;
|
|
138
|
-
// Enhanced retry logic inspired by whatsmeow
|
|
139
120
|
if (retryCount === 1) {
|
|
140
|
-
//
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
logger.debug({
|
|
144
|
-
retryCount,
|
|
145
|
-
msgId
|
|
146
|
-
// internalRetryCount
|
|
147
|
-
}, 'sendRetryRequest: requested placeholder resend for message (first retry)');
|
|
148
|
-
}
|
|
149
|
-
catch (error) {
|
|
150
|
-
logger.warn({
|
|
151
|
-
msgId,
|
|
152
|
-
error: error.message
|
|
153
|
-
}, 'Failed to request placeholder resend');
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
else {
|
|
157
|
-
logger.debug({
|
|
158
|
-
retryCount,
|
|
159
|
-
msgId
|
|
160
|
-
// internalRetryCount
|
|
161
|
-
}, 'sendRetryRequest: retry count > 1, skipping placeholder resend');
|
|
121
|
+
//request a resend via phone
|
|
122
|
+
const msgId = await requestPlaceholderResend(msgKey);
|
|
123
|
+
logger.debug(`sendRetryRequest: requested placeholder resend for message ${msgId}`);
|
|
162
124
|
}
|
|
163
125
|
const deviceIdentity = encodeSignedDeviceIdentity(account, true);
|
|
164
126
|
await authState.keys.transaction(async () => {
|
|
@@ -192,12 +154,7 @@ export const makeMessagesRecvSocket = (config) => {
|
|
|
192
154
|
if (node.attrs.participant) {
|
|
193
155
|
receipt.attrs.participant = node.attrs.participant;
|
|
194
156
|
}
|
|
195
|
-
|
|
196
|
-
// - Always include on first retry (retryCount === 1)
|
|
197
|
-
// - Include when forced (MAC errors, session errors)
|
|
198
|
-
// - Include when retry count > 1 (session recreation scenarios)
|
|
199
|
-
const shouldIncludeKeys = retryCount === 1 || forceIncludeKeys || retryCount > 1;
|
|
200
|
-
if (shouldIncludeKeys) {
|
|
157
|
+
if (retryCount > 1 || forceIncludeKeys) {
|
|
201
158
|
const { update, preKeys } = await getNextPreKeys(authState, 1);
|
|
202
159
|
const [keyId] = Object.keys(preKeys);
|
|
203
160
|
const key = preKeys[+keyId];
|
|
@@ -214,20 +171,6 @@ export const makeMessagesRecvSocket = (config) => {
|
|
|
214
171
|
]
|
|
215
172
|
});
|
|
216
173
|
ev.emit('creds.update', update);
|
|
217
|
-
logger.debug({
|
|
218
|
-
retryCount,
|
|
219
|
-
forceIncludeKeys,
|
|
220
|
-
shouldIncludeKeys
|
|
221
|
-
// internalRetryCount
|
|
222
|
-
}, 'Including keys and device identity in retry receipt');
|
|
223
|
-
}
|
|
224
|
-
else {
|
|
225
|
-
logger.debug({
|
|
226
|
-
retryCount,
|
|
227
|
-
forceIncludeKeys,
|
|
228
|
-
shouldIncludeKeys
|
|
229
|
-
// internalRetryCount
|
|
230
|
-
}, 'Not including keys in retry receipt');
|
|
231
174
|
}
|
|
232
175
|
await sendNode(receipt);
|
|
233
176
|
logger.info({ msgAttrs: node.attrs, retryCount }, 'sent retry receipt');
|
|
@@ -535,7 +478,7 @@ export const makeMessagesRecvSocket = (config) => {
|
|
|
535
478
|
const willSendMessageAgain = (id, participant) => {
|
|
536
479
|
const key = `${id}:${participant}`;
|
|
537
480
|
const retryCount = msgRetryCache.get(key) || 0;
|
|
538
|
-
return retryCount
|
|
481
|
+
return retryCount <= maxMsgRetryCount;
|
|
539
482
|
};
|
|
540
483
|
const updateSendMessageAgainCount = (id, participant) => {
|
|
541
484
|
const key = `${id}:${participant}`;
|
|
@@ -543,53 +486,23 @@ export const makeMessagesRecvSocket = (config) => {
|
|
|
543
486
|
msgRetryCache.set(key, newValue);
|
|
544
487
|
};
|
|
545
488
|
const sendMessagesAgain = async (key, ids, retryNode) => {
|
|
546
|
-
//
|
|
547
|
-
const msgs = await Promise.all(ids.map(id =>
|
|
489
|
+
// todo: implement a cache to store the last 256 sent messages (copy whatsmeow)
|
|
490
|
+
const msgs = await Promise.all(ids.map(id => getMessage({ ...key, id })));
|
|
548
491
|
const remoteJid = key.remoteJid;
|
|
549
492
|
const participant = key.participant || remoteJid;
|
|
550
493
|
// if it's the primary jid sending the request
|
|
551
494
|
// just re-send the message to everyone
|
|
552
495
|
// prevents the first message decryption failure
|
|
553
496
|
const sendToAll = !jidDecode(participant)?.device;
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
const sessionContext = {
|
|
557
|
-
authState,
|
|
558
|
-
logger,
|
|
559
|
-
signalRepository,
|
|
560
|
-
query
|
|
561
|
-
};
|
|
562
|
-
// Use the sophisticated session recreation logic from decode-wa-message.ts
|
|
563
|
-
const sessionResult = await shouldRecreateSession(participant, retryCount, sessionContext);
|
|
564
|
-
if (sessionResult.recreate && sessionResult.shouldFetchPreKeys) {
|
|
565
|
-
logger.warn({
|
|
566
|
-
participant,
|
|
567
|
-
retryCount,
|
|
568
|
-
reason: sessionResult.reason
|
|
569
|
-
}, 'Session recreation recommended for retry');
|
|
570
|
-
// Execute session recreation with prekey fetching (includes LID support)
|
|
571
|
-
const success = await executeSessionRecreation(participant, sessionContext);
|
|
572
|
-
if (!success) {
|
|
573
|
-
logger.warn({ participant }, 'Session recreation failed, proceeding with existing session');
|
|
574
|
-
}
|
|
575
|
-
}
|
|
576
|
-
// Ensure we have sessions (use force=false to allow LID migration logic)
|
|
577
|
-
await assertSessions([participant], false);
|
|
578
|
-
// Only clear sender key memory if session recreation was recommended and it's a group
|
|
579
|
-
// This prevents unnecessary decryption failures for other group members
|
|
580
|
-
if (isJidGroup(remoteJid) && sessionResult.recreate) {
|
|
497
|
+
await assertSessions([participant], true);
|
|
498
|
+
if (isJidGroup(remoteJid)) {
|
|
581
499
|
await authState.keys.set({ 'sender-key-memory': { [remoteJid]: null } });
|
|
582
|
-
logger.debug({ participant, retryCount, reason: sessionResult.reason }, 'cleared sender key memory due to session recreation');
|
|
583
500
|
}
|
|
584
|
-
logger.debug({
|
|
585
|
-
participant,
|
|
586
|
-
sendToAll,
|
|
587
|
-
retryCount,
|
|
588
|
-
sessionRecreated: sessionResult.recreate,
|
|
589
|
-
reason: sessionResult.reason
|
|
590
|
-
}, 'handling retry receipt');
|
|
501
|
+
logger.debug({ participant, sendToAll }, 'forced new session for retry recp');
|
|
591
502
|
for (const [i, msg] of msgs.entries()) {
|
|
592
|
-
if (
|
|
503
|
+
if (!ids[i])
|
|
504
|
+
continue;
|
|
505
|
+
if (msg && willSendMessageAgain(ids[i], participant)) {
|
|
593
506
|
updateSendMessageAgainCount(ids[i], participant);
|
|
594
507
|
const msgRelayOpts = { messageId: ids[i] };
|
|
595
508
|
if (sendToAll) {
|
|
@@ -637,7 +550,7 @@ export const makeMessagesRecvSocket = (config) => {
|
|
|
637
550
|
if (typeof status !== 'undefined' &&
|
|
638
551
|
// basically, we only want to know when a message from us has been delivered to/read by the other person
|
|
639
552
|
// or another device of ours has read some messages
|
|
640
|
-
status >= proto.WebMessageInfo.Status.SERVER_ACK) {
|
|
553
|
+
(status >= proto.WebMessageInfo.Status.SERVER_ACK)) {
|
|
641
554
|
if (isJidGroup(remoteJid) || isJidStatusBroadcast(remoteJid)) {
|
|
642
555
|
const updateKey = status === proto.WebMessageInfo.Status.DELIVERY_ACK ? 'receiptTimestamp' : 'readTimestamp';
|
|
643
556
|
if (attrs.participant) {
|
|
@@ -679,60 +592,24 @@ export const makeMessagesRecvSocket = (config) => {
|
|
|
679
592
|
// correctly set who is asking for the retry
|
|
680
593
|
key.participant = key.participant || attrs.from;
|
|
681
594
|
const retryNode = getBinaryNodeChild(node, 'retry');
|
|
682
|
-
|
|
683
|
-
// const senderJid = attrs.from || ''
|
|
684
|
-
const messageId = ids[0] || '';
|
|
685
|
-
// if (shouldDropRetryRequest(senderJid, messageId)) {
|
|
686
|
-
// logger.warn(
|
|
687
|
-
// {
|
|
688
|
-
// senderJid,
|
|
689
|
-
// messageId,
|
|
690
|
-
// attrs
|
|
691
|
-
// },
|
|
692
|
-
// 'Dropping retry receipt: internal retry counter exceeded limit (10)'
|
|
693
|
-
// )
|
|
694
|
-
// return
|
|
695
|
-
// }
|
|
696
|
-
// // Increment internal retry counter
|
|
697
|
-
// const internalRetryCount = incrementIncomingRetryCounter(senderJid, messageId)
|
|
698
|
-
if (willSendMessageAgain(ids[0], key.participant)) {
|
|
595
|
+
if (ids[0] && key.participant && willSendMessageAgain(ids[0], key.participant)) {
|
|
699
596
|
if (key.fromMe) {
|
|
700
597
|
try {
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
key
|
|
704
|
-
// internalRetryCount
|
|
705
|
-
}, 'recv retry request');
|
|
706
|
-
// Check if we have the message in recent cache (whatsmeow pattern)
|
|
707
|
-
const recentMessage = getRecentMessage(key.remoteJid || '', messageId);
|
|
708
|
-
//eslint-disable-next-line max-depth
|
|
709
|
-
if (recentMessage) {
|
|
710
|
-
logger.debug({
|
|
711
|
-
jid: key.remoteJid,
|
|
712
|
-
id: messageId
|
|
713
|
-
}, 'Found message in recent cache for retry');
|
|
714
|
-
}
|
|
598
|
+
updateSendMessageAgainCount(ids[0], key.participant);
|
|
599
|
+
logger.debug({ attrs, key }, 'recv retry request');
|
|
715
600
|
await sendMessagesAgain(key, ids, retryNode);
|
|
716
601
|
ev.emit('messages.retry', { key, ids, retryNode });
|
|
717
602
|
}
|
|
718
603
|
catch (error) {
|
|
719
|
-
logger.error({ key, ids, trace: error.stack }, 'error in sending message again');
|
|
604
|
+
logger.error({ key, ids, trace: error instanceof Error ? error.stack : 'Unknown error' }, 'error in sending message again');
|
|
720
605
|
}
|
|
721
606
|
}
|
|
722
607
|
else {
|
|
723
|
-
logger.info({
|
|
724
|
-
attrs,
|
|
725
|
-
key
|
|
726
|
-
// internalRetryCount
|
|
727
|
-
}, 'recv retry for not fromMe message');
|
|
608
|
+
logger.info({ attrs, key }, 'recv retry for not fromMe message');
|
|
728
609
|
}
|
|
729
610
|
}
|
|
730
611
|
else {
|
|
731
|
-
logger.info({
|
|
732
|
-
attrs,
|
|
733
|
-
key
|
|
734
|
-
// internalRetryCount
|
|
735
|
-
}, 'will not send message again, as sent too many times');
|
|
612
|
+
logger.info({ attrs, key }, 'will not send message again, as sent too many times');
|
|
736
613
|
}
|
|
737
614
|
}
|
|
738
615
|
})
|
|
@@ -756,15 +633,15 @@ export const makeMessagesRecvSocket = (config) => {
|
|
|
756
633
|
if (msg) {
|
|
757
634
|
const fromMe = areJidsSameUser(node.attrs.participant || remoteJid, authState.creds.me.id);
|
|
758
635
|
msg.key = {
|
|
759
|
-
...(msg.key || {}),
|
|
760
636
|
remoteJid,
|
|
761
637
|
fromMe,
|
|
762
638
|
participant: node.attrs.participant,
|
|
763
|
-
id: node.attrs.id
|
|
639
|
+
id: node.attrs.id,
|
|
640
|
+
...(msg.key || {})
|
|
764
641
|
};
|
|
765
642
|
msg.participant ?? (msg.participant = node.attrs.participant);
|
|
766
643
|
msg.messageTimestamp = +node.attrs.t;
|
|
767
|
-
const fullMsg = proto.WebMessageInfo.
|
|
644
|
+
const fullMsg = proto.WebMessageInfo.create(msg);
|
|
768
645
|
await upsertMessage(fullMsg, 'append');
|
|
769
646
|
}
|
|
770
647
|
})
|
|
@@ -802,15 +679,7 @@ export const makeMessagesRecvSocket = (config) => {
|
|
|
802
679
|
placeholderResendCache.del(node.attrs.id);
|
|
803
680
|
}
|
|
804
681
|
}
|
|
805
|
-
const
|
|
806
|
-
// Create session recreation context (whatsmeow-inspired)
|
|
807
|
-
const sessionContext = {
|
|
808
|
-
authState,
|
|
809
|
-
logger,
|
|
810
|
-
signalRepository,
|
|
811
|
-
query
|
|
812
|
-
};
|
|
813
|
-
const { fullMessage: msg, category, author, decrypt } = decryptMessageNode(node, authState.creds.me.id, authState.creds.me.lid || '', signalRepository, logger, sendRetryRequest, sessionContext);
|
|
682
|
+
const { fullMessage: msg, category, author, decrypt } = decryptMessageNode(node, authState.creds.me.id, authState.creds.me.lid || '', signalRepository, logger);
|
|
814
683
|
if (response && msg?.messageStubParameters?.[0] === NO_MESSAGE_FOUND_ERROR_TEXT) {
|
|
815
684
|
msg.messageStubParameters = [NO_MESSAGE_FOUND_ERROR_TEXT, response];
|
|
816
685
|
}
|
|
@@ -851,155 +720,95 @@ export const makeMessagesRecvSocket = (config) => {
|
|
|
851
720
|
try {
|
|
852
721
|
await Promise.all([
|
|
853
722
|
processingMutex.mutex(async () => {
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
if (
|
|
858
|
-
|
|
859
|
-
console.warn({ messageKey }, 'Message decrypted successfully after retry');
|
|
723
|
+
await decrypt();
|
|
724
|
+
// message failed to decrypt
|
|
725
|
+
if (msg.messageStubType === proto.WebMessageInfo.StubType.CIPHERTEXT) {
|
|
726
|
+
if (msg?.messageStubParameters?.[0] === MISSING_KEYS_ERROR_TEXT) {
|
|
727
|
+
return sendMessageAck(node, NACK_REASONS.ParsingError);
|
|
860
728
|
}
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
729
|
+
const errorMessage = msg?.messageStubParameters?.[0] || '';
|
|
730
|
+
const isPreKeyError = errorMessage.includes('PreKey');
|
|
731
|
+
console.debug(`[handleMessage] Attempting retry request for failed decryption`);
|
|
732
|
+
// Handle both pre-key and normal retries in single mutex
|
|
733
|
+
retryMutex.mutex(async () => {
|
|
734
|
+
try {
|
|
735
|
+
if (!ws.isOpen) {
|
|
736
|
+
logger.debug({ node }, 'Connection closed, skipping retry');
|
|
737
|
+
return;
|
|
738
|
+
}
|
|
739
|
+
if (getBinaryNodeChild(node, 'unavailable')) {
|
|
740
|
+
logger.debug('Message unavailable, skipping retry');
|
|
741
|
+
return;
|
|
742
|
+
}
|
|
743
|
+
// Handle pre-key errors with upload and delay
|
|
744
|
+
if (isPreKeyError) {
|
|
745
|
+
logger.info({ error: errorMessage }, 'PreKey error detected, uploading and retrying');
|
|
746
|
+
try {
|
|
747
|
+
logger.debug('Uploading pre-keys for error recovery');
|
|
748
|
+
await uploadPreKeys(5);
|
|
749
|
+
logger.debug('Waiting for server to process new pre-keys');
|
|
750
|
+
await delay(1000);
|
|
877
751
|
}
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
await sendRetryRequest(node, !encNode);
|
|
881
|
-
if (retryRequestDelayMs) {
|
|
882
|
-
await delay(retryRequestDelayMs);
|
|
752
|
+
catch (uploadErr) {
|
|
753
|
+
logger.error({ uploadErr }, 'Pre-key upload failed, proceeding with retry anyway');
|
|
883
754
|
}
|
|
884
755
|
}
|
|
885
|
-
|
|
886
|
-
|
|
756
|
+
const encNode = getBinaryNodeChild(node, 'enc');
|
|
757
|
+
await sendRetryRequest(node, !encNode);
|
|
758
|
+
if (retryRequestDelayMs) {
|
|
759
|
+
await delay(retryRequestDelayMs);
|
|
887
760
|
}
|
|
888
|
-
});
|
|
889
|
-
}
|
|
890
|
-
else {
|
|
891
|
-
let type = undefined;
|
|
892
|
-
let participant = msg.key.participant;
|
|
893
|
-
if (category === 'peer') {
|
|
894
|
-
type = 'peer_msg';
|
|
895
761
|
}
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
if
|
|
899
|
-
|
|
762
|
+
catch (err) {
|
|
763
|
+
logger.error({ err, isPreKeyError }, 'Failed to handle retry, attempting basic retry');
|
|
764
|
+
// Still attempt retry even if pre-key upload failed
|
|
765
|
+
try {
|
|
766
|
+
const encNode = getBinaryNodeChild(node, 'enc');
|
|
767
|
+
await sendRetryRequest(node, !encNode);
|
|
768
|
+
}
|
|
769
|
+
catch (retryErr) {
|
|
770
|
+
logger.error({ retryErr }, 'Failed to send retry after error handling');
|
|
900
771
|
}
|
|
901
772
|
}
|
|
902
|
-
|
|
903
|
-
type = 'inactive';
|
|
904
|
-
}
|
|
905
|
-
await sendReceipt(msg.key.remoteJid, participant, [msg.key.id], type);
|
|
906
|
-
const isAnyHistoryMsg = getHistoryMsg(msg.message);
|
|
907
|
-
if (isAnyHistoryMsg) {
|
|
908
|
-
const jid = jidNormalizedUser(msg.key.remoteJid);
|
|
909
|
-
await sendReceipt(jid, undefined, [msg.key.id], 'hist_sync');
|
|
910
|
-
}
|
|
911
|
-
}
|
|
912
|
-
cleanMessage(msg, authState.creds.me.id);
|
|
913
|
-
await sendMessageAck(node);
|
|
914
|
-
await upsertMessage(msg, node.attrs.offline ? 'append' : 'notify');
|
|
773
|
+
});
|
|
915
774
|
}
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
});
|
|
931
|
-
console.warn({ messageKey, error: decryptError.message }, 'Session not found, scheduling retry');
|
|
932
|
-
// Agenda retry
|
|
933
|
-
setTimeout(() => {
|
|
934
|
-
retryMessageDecryption(messageKey).catch(err => logger.error({ err, messageKey }, 'Error in retry decryption'));
|
|
935
|
-
}, DECRYPT_RETRY_DELAY);
|
|
936
|
-
// try to process the message as a ciphertext
|
|
937
|
-
msg.messageStubType = proto.WebMessageInfo.StubType.CIPHERTEXT;
|
|
938
|
-
msg.messageStubParameters = ['Session not ready, retrying...'];
|
|
939
|
-
cleanMessage(msg, authState.creds.me.id);
|
|
940
|
-
await sendMessageAck(node);
|
|
941
|
-
await upsertMessage(msg, node.attrs.offline ? 'append' : 'notify');
|
|
942
|
-
}
|
|
943
|
-
else if (pendingDecryption.retryCount < MAX_DECRYPT_RETRY_COUNT) {
|
|
944
|
-
// Retry
|
|
945
|
-
pendingDecryption.retryCount++;
|
|
946
|
-
console.warn({ messageKey, retryCount: pendingDecryption.retryCount }, 'Retrying message decryption');
|
|
947
|
-
setTimeout(() => {
|
|
948
|
-
retryMessageDecryption(messageKey).catch(err => console.error({ err, messageKey }, 'Error in retry decryption'));
|
|
949
|
-
}, DECRYPT_RETRY_DELAY * pendingDecryption.retryCount);
|
|
950
|
-
}
|
|
951
|
-
else {
|
|
952
|
-
// max retry count reached
|
|
953
|
-
PENDING_MESSAGE_DECRYPTIONS.delete(messageKey);
|
|
954
|
-
console.error({ messageKey }, 'Max retry count reached for message decryption');
|
|
955
|
-
msg.messageStubType = proto.WebMessageInfo.StubType.CIPHERTEXT;
|
|
956
|
-
msg.messageStubParameters = ['Decryption failed after retries'];
|
|
957
|
-
cleanMessage(msg, authState.creds.me.id);
|
|
958
|
-
await sendMessageAck(node);
|
|
959
|
-
await upsertMessage(msg, node.attrs.offline ? 'append' : 'notify');
|
|
775
|
+
else {
|
|
776
|
+
// no type in the receipt => message delivered
|
|
777
|
+
let type = undefined;
|
|
778
|
+
let participant = msg.key.participant;
|
|
779
|
+
if (category === 'peer') {
|
|
780
|
+
// special peer message
|
|
781
|
+
type = 'peer_msg';
|
|
782
|
+
}
|
|
783
|
+
else if (msg.key.fromMe) {
|
|
784
|
+
// message was sent by us from a different device
|
|
785
|
+
type = 'sender';
|
|
786
|
+
// need to specially handle this case
|
|
787
|
+
if (isJidUser(msg.key.remoteJid)) {
|
|
788
|
+
participant = author;
|
|
960
789
|
}
|
|
961
790
|
}
|
|
962
|
-
else {
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
791
|
+
else if (!sendActiveReceipts) {
|
|
792
|
+
type = 'inactive';
|
|
793
|
+
}
|
|
794
|
+
await sendReceipt(msg.key.remoteJid, participant, [msg.key.id], type);
|
|
795
|
+
// send ack for history message
|
|
796
|
+
const isAnyHistoryMsg = getHistoryMsg(msg.message);
|
|
797
|
+
if (isAnyHistoryMsg) {
|
|
798
|
+
const jid = jidNormalizedUser(msg.key.remoteJid);
|
|
799
|
+
await sendReceipt(jid, undefined, [msg.key.id], 'hist_sync');
|
|
966
800
|
}
|
|
967
801
|
}
|
|
802
|
+
cleanMessage(msg, authState.creds.me.id);
|
|
803
|
+
await sendMessageAck(node);
|
|
804
|
+
await upsertMessage(msg, node.attrs.offline ? 'append' : 'notify');
|
|
968
805
|
})
|
|
969
806
|
]);
|
|
970
807
|
}
|
|
971
808
|
catch (error) {
|
|
972
809
|
logger.error({ error, node }, 'error in handling message');
|
|
973
|
-
await sendMessageAck(node); // Ainda assim envia ack para evitar loops
|
|
974
|
-
}
|
|
975
|
-
};
|
|
976
|
-
const retryMessageDecryption = async (messageKey) => {
|
|
977
|
-
const pendingDecryption = PENDING_MESSAGE_DECRYPTIONS.get(messageKey);
|
|
978
|
-
if (!pendingDecryption) {
|
|
979
|
-
return;
|
|
980
|
-
}
|
|
981
|
-
const { node } = pendingDecryption;
|
|
982
|
-
try {
|
|
983
|
-
// try to decrypt the message again
|
|
984
|
-
await handleMessage(node);
|
|
985
|
-
}
|
|
986
|
-
catch (error) {
|
|
987
|
-
logger.error({ error, messageKey }, 'Retry decryption failed');
|
|
988
|
-
if (pendingDecryption.retryCount >= MAX_DECRYPT_RETRY_COUNT) {
|
|
989
|
-
PENDING_MESSAGE_DECRYPTIONS.delete(messageKey);
|
|
990
|
-
}
|
|
991
810
|
}
|
|
992
811
|
};
|
|
993
|
-
setInterval(() => {
|
|
994
|
-
const now = Date.now();
|
|
995
|
-
const maxAge = 5 * 60 * 1000; // 5 minutes
|
|
996
|
-
for (const [key, pending] of PENDING_MESSAGE_DECRYPTIONS.entries()) {
|
|
997
|
-
if (now - pending.timestamp > maxAge) {
|
|
998
|
-
PENDING_MESSAGE_DECRYPTIONS.delete(key);
|
|
999
|
-
logger.debug({ key }, 'Removed old pending decryption from queue');
|
|
1000
|
-
}
|
|
1001
|
-
}
|
|
1002
|
-
}, 60000);
|
|
1003
812
|
const fetchMessageHistory = async (count, oldestMsgKey, oldestMsgTimestamp) => {
|
|
1004
813
|
if (!authState.creds.me?.id) {
|
|
1005
814
|
throw new Boom('Not authenticated');
|
|
@@ -1241,7 +1050,7 @@ export const makeMessagesRecvSocket = (config) => {
|
|
|
1241
1050
|
? Buffer.from(plaintextNode.content, 'binary')
|
|
1242
1051
|
: Buffer.from(plaintextNode.content);
|
|
1243
1052
|
const messageProto = proto.Message.decode(contentBuf);
|
|
1244
|
-
const fullMessage = proto.WebMessageInfo.
|
|
1053
|
+
const fullMessage = proto.WebMessageInfo.create({
|
|
1245
1054
|
key: {
|
|
1246
1055
|
remoteJid: from,
|
|
1247
1056
|
id: child.attrs.message_id || child.attrs.server_id,
|
|
@@ -1357,7 +1166,7 @@ export const makeMessagesRecvSocket = (config) => {
|
|
|
1357
1166
|
else {
|
|
1358
1167
|
msg.message = { call: { callKey: Buffer.from(call.id) } };
|
|
1359
1168
|
}
|
|
1360
|
-
const protoMsg = proto.WebMessageInfo.
|
|
1169
|
+
const protoMsg = proto.WebMessageInfo.create(msg);
|
|
1361
1170
|
upsertMessage(protoMsg, call.offline ? 'append' : 'notify');
|
|
1362
1171
|
}
|
|
1363
1172
|
});
|
|
@@ -1367,21 +1176,6 @@ export const makeMessagesRecvSocket = (config) => {
|
|
|
1367
1176
|
logger.trace(`sendActiveReceipts set to "${sendActiveReceipts}"`);
|
|
1368
1177
|
}
|
|
1369
1178
|
});
|
|
1370
|
-
// Setup automatic cleanup of old retry states (inspired by whatsmeow)
|
|
1371
|
-
const cleanupInterval = setInterval(() => {
|
|
1372
|
-
try {
|
|
1373
|
-
cleanupOldRetryStates();
|
|
1374
|
-
}
|
|
1375
|
-
catch (error) {
|
|
1376
|
-
logger.warn({ error: error.message }, 'Failed to cleanup old retry states');
|
|
1377
|
-
}
|
|
1378
|
-
}, 60 * 60 * 1000); // Run every hour
|
|
1379
|
-
// Cleanup on socket close
|
|
1380
|
-
sock.ev.on('connection.update', ({ connection }) => {
|
|
1381
|
-
if (connection === 'close') {
|
|
1382
|
-
clearInterval(cleanupInterval);
|
|
1383
|
-
}
|
|
1384
|
-
});
|
|
1385
1179
|
return {
|
|
1386
1180
|
...sock,
|
|
1387
1181
|
sendMessageAck,
|