@alannxd/baileys 6.0.5 → 6.0.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.
- package/WAProto/fix-import.js +29 -0
- package/WAProto/index.js +160 -201
- package/engine-requirements.js +1 -1
- package/lib/Defaults/baileys-version.json +3 -0
- package/lib/Defaults/index.d.ts +15 -37
- package/lib/Defaults/index.js +136 -119
- package/lib/Defaults/phonenumber-mcc.json +223 -0
- package/lib/Signal/Group/ciphertext-message.d.ts +0 -1
- package/lib/Signal/Group/ciphertext-message.js +5 -2
- package/lib/Signal/Group/group-session-builder.d.ts +3 -4
- package/lib/Signal/Group/group-session-builder.js +41 -7
- package/lib/Signal/Group/group_cipher.d.ts +4 -4
- package/lib/Signal/Group/group_cipher.js +51 -37
- package/lib/Signal/Group/index.d.ts +11 -12
- package/lib/Signal/Group/index.js +57 -12
- package/lib/Signal/Group/keyhelper.d.ts +1 -2
- package/lib/Signal/Group/keyhelper.js +44 -7
- package/lib/Signal/Group/queue-job.d.ts +1 -0
- package/lib/Signal/Group/queue-job.js +57 -0
- package/lib/Signal/Group/sender-chain-key.d.ts +2 -3
- package/lib/Signal/Group/sender-chain-key.js +15 -7
- package/lib/Signal/Group/sender-key-distribution-message.d.ts +1 -2
- package/lib/Signal/Group/sender-key-distribution-message.js +11 -8
- package/lib/Signal/Group/sender-key-message.d.ts +1 -2
- package/lib/Signal/Group/sender-key-message.js +12 -9
- package/lib/Signal/Group/sender-key-name.d.ts +0 -1
- package/lib/Signal/Group/sender-key-name.js +5 -2
- package/lib/Signal/Group/sender-key-record.d.ts +2 -3
- package/lib/Signal/Group/sender-key-record.js +21 -9
- package/lib/Signal/Group/sender-key-state.d.ts +6 -7
- package/lib/Signal/Group/sender-key-state.js +42 -27
- package/lib/Signal/Group/sender-message-key.d.ts +0 -1
- package/lib/Signal/Group/sender-message-key.js +7 -4
- package/lib/Signal/libsignal.d.ts +3 -5
- package/lib/Signal/libsignal.js +90 -347
- package/lib/Socket/Client/{types.d.ts → abstract-socket-client.d.ts} +5 -4
- package/lib/Socket/Client/abstract-socket-client.js +13 -0
- package/lib/Socket/Client/index.d.ts +3 -3
- package/lib/Socket/Client/index.js +19 -3
- package/lib/Socket/Client/mobile-socket-client.d.ts +13 -0
- package/lib/Socket/Client/mobile-socket-client.js +65 -0
- package/lib/Socket/Client/{websocket.d.ts → web-socket-client.d.ts} +2 -3
- package/lib/Socket/Client/web-socket-client.js +62 -0
- package/lib/Socket/business.d.ts +108 -154
- package/lib/Socket/business.js +43 -162
- package/lib/Socket/chats.d.ts +239 -96
- package/lib/Socket/chats.js +427 -627
- package/lib/Socket/communities.d.ts +146 -239
- package/lib/Socket/communities.js +80 -90
- package/lib/Socket/groups.d.ts +57 -104
- package/lib/Socket/groups.js +161 -154
- package/lib/Socket/index.d.ts +115 -202
- package/lib/Socket/index.js +10 -11
- package/lib/Socket/luxu.d.ts +266 -22
- package/lib/Socket/luxu.js +465 -422
- package/lib/Socket/messages-recv.d.ts +84 -136
- package/lib/Socket/messages-recv.js +615 -1421
- package/lib/Socket/messages-send.d.ts +126 -142
- package/lib/Socket/messages-send.js +671 -878
- package/lib/Socket/newsletter.d.ts +85 -121
- package/lib/Socket/newsletter.js +272 -147
- package/lib/Socket/registration.d.ts +267 -0
- package/lib/Socket/registration.js +166 -0
- package/lib/Socket/socket.d.ts +19 -34
- package/lib/Socket/socket.js +313 -544
- package/lib/Socket/usync.d.ts +36 -0
- package/lib/Socket/usync.js +70 -0
- package/lib/Store/index.d.ts +3 -10
- package/lib/Store/index.js +10 -10
- package/lib/Store/make-cache-manager-store.d.ts +11 -17
- package/lib/Store/make-cache-manager-store.js +41 -43
- package/lib/Store/make-in-memory-store.d.ts +118 -39
- package/lib/Store/make-in-memory-store.js +341 -112
- package/lib/Store/make-ordered-dictionary.d.ts +10 -11
- package/lib/Store/make-ordered-dictionary.js +20 -14
- package/lib/Store/object-repository.d.ts +9 -10
- package/lib/Store/object-repository.js +6 -11
- package/lib/Types/Auth.d.ts +12 -19
- package/lib/Types/Auth.js +2 -2
- package/lib/Types/Call.d.ts +1 -3
- package/lib/Types/Call.js +2 -2
- package/lib/Types/Chat.d.ts +13 -35
- package/lib/Types/Chat.js +4 -8
- package/lib/Types/Contact.d.ts +1 -8
- package/lib/Types/Contact.js +2 -2
- package/lib/Types/Events.d.ts +17 -116
- package/lib/Types/Events.js +2 -2
- package/lib/Types/GroupMetadata.d.ts +5 -21
- package/lib/Types/GroupMetadata.js +2 -2
- package/lib/Types/Label.d.ts +0 -12
- package/lib/Types/Label.js +5 -3
- package/lib/Types/LabelAssociation.d.ts +0 -1
- package/lib/Types/LabelAssociation.js +5 -3
- package/lib/Types/Message.d.ts +58 -105
- package/lib/Types/Message.js +9 -11
- package/lib/Types/Newsletter.d.ts +103 -0
- package/lib/Types/Newsletter.js +38 -0
- package/lib/Types/Product.d.ts +1 -2
- package/lib/Types/Product.js +2 -2
- package/lib/Types/Signal.d.ts +2 -32
- package/lib/Types/Signal.js +2 -2
- package/lib/Types/Socket.d.ts +25 -50
- package/lib/Types/Socket.js +2 -3
- package/lib/Types/State.d.ts +2 -72
- package/lib/Types/State.js +2 -56
- package/lib/Types/USync.d.ts +2 -3
- package/lib/Types/USync.js +2 -2
- package/lib/Types/index.d.ts +14 -22
- package/lib/Types/index.js +31 -15
- package/lib/Utils/auth-utils.d.ts +6 -12
- package/lib/Utils/auth-utils.js +143 -239
- package/lib/Utils/baileys-event-stream.d.ts +16 -0
- package/lib/Utils/baileys-event-stream.js +63 -0
- package/lib/Utils/business.d.ts +2 -3
- package/lib/Utils/business.js +69 -66
- package/lib/Utils/chat-utils.d.ts +23 -52
- package/lib/Utils/chat-utils.js +253 -396
- package/lib/Utils/crypto.d.ts +22 -18
- package/lib/Utils/crypto.js +90 -57
- package/lib/Utils/decode-wa-message.d.ts +8 -55
- package/lib/Utils/decode-wa-message.js +84 -203
- package/lib/Utils/event-buffer.d.ts +8 -9
- package/lib/Utils/event-buffer.js +77 -185
- package/lib/Utils/generics.d.ts +29 -28
- package/lib/Utils/generics.js +210 -180
- package/lib/Utils/history.d.ts +9 -18
- package/lib/Utils/history.js +55 -93
- package/lib/Utils/index.d.ts +17 -22
- package/lib/Utils/index.js +33 -22
- package/lib/Utils/link-preview.d.ts +5 -5
- package/lib/Utils/link-preview.js +24 -16
- package/lib/Utils/logger.d.ts +3 -11
- package/lib/Utils/logger.js +7 -3
- package/lib/Utils/lt-hash.d.ts +12 -8
- package/lib/Utils/lt-hash.js +46 -3
- package/lib/Utils/make-mutex.d.ts +2 -4
- package/lib/Utils/make-mutex.js +34 -24
- package/lib/Utils/messages-media.d.ts +44 -61
- package/lib/Utils/messages-media.js +482 -451
- package/lib/Utils/messages.d.ts +18 -32
- package/lib/Utils/messages.js +369 -458
- package/lib/Utils/noise-handler.d.ts +14 -13
- package/lib/Utils/noise-handler.js +99 -145
- package/lib/Utils/process-message.d.ts +12 -31
- package/lib/Utils/process-message.js +150 -459
- package/lib/Utils/signal.d.ts +5 -20
- package/lib/Utils/signal.js +72 -120
- package/lib/Utils/use-multi-file-auth-state.d.ts +2 -2
- package/lib/Utils/use-multi-file-auth-state.js +27 -29
- package/lib/Utils/validate-connection.d.ts +7 -7
- package/lib/Utils/validate-connection.js +99 -73
- package/lib/WABinary/constants.d.ts +27 -25
- package/lib/WABinary/constants.js +20 -1281
- package/lib/WABinary/decode.d.ts +5 -5
- package/lib/WABinary/decode.js +42 -52
- package/lib/WABinary/encode.d.ts +3 -3
- package/lib/WABinary/encode.js +155 -110
- package/lib/WABinary/generic-utils.d.ts +7 -8
- package/lib/WABinary/generic-utils.js +49 -48
- package/lib/WABinary/index.d.ts +5 -6
- package/lib/WABinary/index.js +21 -6
- package/lib/WABinary/jid-utils.d.ts +8 -25
- package/lib/WABinary/jid-utils.js +40 -74
- package/lib/WABinary/types.d.ts +1 -2
- package/lib/WABinary/types.js +2 -2
- package/lib/WAM/BinaryInfo.d.ts +11 -3
- package/lib/WAM/BinaryInfo.js +5 -2
- package/lib/WAM/constants.d.ts +3 -5
- package/lib/WAM/constants.js +11958 -19461
- package/lib/WAM/encode.d.ts +3 -3
- package/lib/WAM/encode.js +22 -17
- package/lib/WAM/index.d.ts +3 -4
- package/lib/WAM/index.js +19 -4
- package/lib/WAUSync/Protocols/USyncContactProtocol.d.ts +3 -4
- package/lib/WAUSync/Protocols/USyncContactProtocol.js +13 -33
- package/lib/WAUSync/Protocols/USyncDeviceProtocol.d.ts +2 -3
- package/lib/WAUSync/Protocols/USyncDeviceProtocol.js +14 -11
- package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.d.ts +2 -3
- package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.js +12 -9
- package/lib/WAUSync/Protocols/USyncStatusProtocol.d.ts +2 -3
- package/lib/WAUSync/Protocols/USyncStatusProtocol.js +13 -9
- package/lib/WAUSync/Protocols/UsyncBotProfileProtocol.d.ts +3 -4
- package/lib/WAUSync/Protocols/UsyncBotProfileProtocol.js +22 -20
- package/lib/WAUSync/Protocols/UsyncLIDProtocol.d.ts +3 -5
- package/lib/WAUSync/Protocols/UsyncLIDProtocol.js +8 -13
- package/lib/WAUSync/Protocols/index.d.ts +4 -6
- package/lib/WAUSync/Protocols/index.js +20 -6
- package/lib/WAUSync/USyncQuery.d.ts +4 -6
- package/lib/WAUSync/USyncQuery.js +35 -44
- package/lib/WAUSync/USyncUser.d.ts +5 -10
- package/lib/WAUSync/USyncUser.js +5 -10
- package/lib/WAUSync/index.js +19 -4
- package/lib/index.d.ts +9 -10
- package/lib/index.js +34 -12
- package/package.json +50 -83
- package/WAProto/GenerateStatics.sh +0 -3
- package/WAProto/WAProto.proto +0 -5479
- package/WAProto/fix-imports.js +0 -85
- package/WAProto/index.d.ts +0 -14017
- package/lib/Signal/lid-mapping.d.ts +0 -23
- package/lib/Signal/lid-mapping.js +0 -277
- package/lib/Socket/Client/types.js +0 -11
- package/lib/Socket/Client/websocket.js +0 -54
- package/lib/Socket/mex.d.ts +0 -3
- package/lib/Socket/mex.js +0 -42
- package/lib/Store/keyed-db.d.ts +0 -22
- package/lib/Store/keyed-db.js +0 -108
- package/lib/Types/Bussines.d.ts +0 -25
- package/lib/Types/Bussines.js +0 -2
- package/lib/Types/Mex.d.ts +0 -141
- package/lib/Types/Mex.js +0 -37
- package/lib/Utils/browser-utils.d.ts +0 -4
- package/lib/Utils/browser-utils.js +0 -28
- package/lib/Utils/companion-reg-client-utils.d.ts +0 -17
- package/lib/Utils/companion-reg-client-utils.js +0 -35
- package/lib/Utils/identity-change-handler.d.ts +0 -44
- package/lib/Utils/identity-change-handler.js +0 -50
- package/lib/Utils/message-retry-manager.d.ts +0 -115
- package/lib/Utils/message-retry-manager.js +0 -265
- package/lib/Utils/offline-node-processor.d.ts +0 -17
- package/lib/Utils/offline-node-processor.js +0 -40
- package/lib/Utils/pre-key-manager.d.ts +0 -28
- package/lib/Utils/pre-key-manager.js +0 -106
- package/lib/Utils/reporting-utils.d.ts +0 -11
- package/lib/Utils/reporting-utils.js +0 -258
- package/lib/Utils/stanza-ack.d.ts +0 -11
- package/lib/Utils/stanza-ack.js +0 -38
- package/lib/Utils/sync-action-utils.d.ts +0 -19
- package/lib/Utils/sync-action-utils.js +0 -49
- package/lib/Utils/tc-token-utils.d.ts +0 -37
- package/lib/Utils/tc-token-utils.js +0 -163
- package/lib/WAUSync/Protocols/USyncUsernameProtocol.d.ts +0 -10
- package/lib/WAUSync/Protocols/USyncUsernameProtocol.js +0 -25
- package/lib/WAUSync/index.d.ts +0 -4
|
@@ -1,599 +1,219 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.makeMessagesRecvSocket = void 0;
|
|
7
|
+
const boom_1 = require("@hapi/boom");
|
|
8
|
+
const crypto_1 = require("crypto");
|
|
9
|
+
const node_cache_1 = __importDefault(require("@cacheable/node-cache"));
|
|
10
|
+
const WAProto_1 = require("../../WAProto");
|
|
11
|
+
const Defaults_1 = require("../Defaults");
|
|
12
|
+
const Types_1 = require("../Types");
|
|
13
|
+
const Utils_1 = require("../Utils");
|
|
14
|
+
const make_mutex_1 = require("../Utils/make-mutex");
|
|
15
|
+
const WABinary_1 = require("../WABinary");
|
|
16
|
+
const groups_1 = require("./groups");
|
|
17
|
+
const messages_send_1 = require("./messages-send");
|
|
18
|
+
const makeMessagesRecvSocket = (config) => {
|
|
19
|
+
const {
|
|
20
|
+
logger,
|
|
21
|
+
retryRequestDelayMs,
|
|
22
|
+
maxMsgRetryCount,
|
|
23
|
+
getMessage,
|
|
24
|
+
shouldIgnoreJid
|
|
25
|
+
} = config;
|
|
26
|
+
const sock = (0, messages_send_1.makeMessagesSocket)(config);
|
|
27
|
+
const {
|
|
28
|
+
ev,
|
|
29
|
+
authState,
|
|
30
|
+
ws,
|
|
31
|
+
processingMutex,
|
|
32
|
+
signalRepository,
|
|
33
|
+
query,
|
|
34
|
+
upsertMessage,
|
|
35
|
+
resyncAppState,
|
|
36
|
+
groupMetadata,
|
|
37
|
+
onUnexpectedError,
|
|
38
|
+
assertSessions,
|
|
39
|
+
sendNode,
|
|
40
|
+
relayMessage,
|
|
41
|
+
sendReceipt,
|
|
42
|
+
uploadPreKeys,
|
|
43
|
+
createParticipantNodes,
|
|
44
|
+
getUSyncDevices,
|
|
45
|
+
sendPeerDataOperationMessage
|
|
46
|
+
} = sock;
|
|
25
47
|
/** this mutex ensures that each retryRequest will wait for the previous one to finish */
|
|
26
|
-
const retryMutex = makeMutex();
|
|
27
|
-
const msgRetryCache = config.msgRetryCounterCache ||
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
48
|
+
const retryMutex = (0, make_mutex_1.makeMutex)();
|
|
49
|
+
const msgRetryCache = config.msgRetryCounterCache || new node_cache_1.default({
|
|
50
|
+
stdTTL: Defaults_1.DEFAULT_CACHE_TTLS.MSG_RETRY, // 1 hour
|
|
51
|
+
useClones: false
|
|
52
|
+
});
|
|
53
|
+
const callOfferCache = config.callOfferCache || new node_cache_1.default({
|
|
54
|
+
stdTTL: Defaults_1.DEFAULT_CACHE_TTLS.CALL_OFFER, // 5 mins
|
|
55
|
+
useClones: false
|
|
56
|
+
});
|
|
57
|
+
const placeholderResendCache = config.placeholderResendCache || new node_cache_1.default({
|
|
58
|
+
stdTTL: Defaults_1.DEFAULT_CACHE_TTLS.MSG_RETRY, // 1 hour
|
|
59
|
+
useClones: false
|
|
60
|
+
});
|
|
39
61
|
let sendActiveReceipts = false;
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
|
|
62
|
+
const sendMessageAck = async ({ tag, attrs, content }, errorCode) => {
|
|
63
|
+
const stanza = {
|
|
64
|
+
tag: 'ack',
|
|
65
|
+
attrs: {
|
|
66
|
+
id: attrs.id,
|
|
67
|
+
to: attrs.from,
|
|
68
|
+
class: tag
|
|
69
|
+
}
|
|
43
70
|
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
chatJid: oldestMsgKey.remoteJid,
|
|
47
|
-
oldestMsgFromMe: oldestMsgKey.fromMe,
|
|
48
|
-
oldestMsgId: oldestMsgKey.id,
|
|
49
|
-
oldestMsgTimestampMs: oldestMsgTimestamp,
|
|
50
|
-
onDemandMsgCount: count
|
|
51
|
-
},
|
|
52
|
-
peerDataOperationRequestType: proto.Message.PeerDataOperationRequestType.HISTORY_SYNC_ON_DEMAND
|
|
53
|
-
};
|
|
54
|
-
return sendPeerDataOperationMessage(pdoMessage);
|
|
55
|
-
};
|
|
56
|
-
const requestPlaceholderResend = async (messageKey, msgData) => {
|
|
57
|
-
if (!authState.creds.me?.id) {
|
|
58
|
-
throw new Boom('Not authenticated');
|
|
71
|
+
if (!!errorCode) {
|
|
72
|
+
stanza.attrs.error = errorCode.toString();
|
|
59
73
|
}
|
|
60
|
-
if (
|
|
61
|
-
|
|
62
|
-
return;
|
|
74
|
+
if (!!attrs.participant) {
|
|
75
|
+
stanza.attrs.participant = attrs.participant;
|
|
63
76
|
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
// metadata (LID details, timestamps, etc.) that the phone may omit
|
|
67
|
-
await placeholderResendCache.set(messageKey?.id, msgData || true);
|
|
77
|
+
if (!!attrs.recipient) {
|
|
78
|
+
stanza.attrs.recipient = attrs.recipient;
|
|
68
79
|
}
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
logger.debug({ messageKey }, 'message received while resend requested');
|
|
72
|
-
return 'RESOLVED';
|
|
80
|
+
if (!!attrs.type && (tag !== 'message' || (0, WABinary_1.getBinaryNodeChild)({ tag, attrs, content }, 'unavailable') || errorCode !== 0)) {
|
|
81
|
+
stanza.attrs.type = attrs.type;
|
|
73
82
|
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
{
|
|
77
|
-
messageKey
|
|
78
|
-
}
|
|
79
|
-
],
|
|
80
|
-
peerDataOperationRequestType: proto.Message.PeerDataOperationRequestType.PLACEHOLDER_MESSAGE_RESEND
|
|
81
|
-
};
|
|
82
|
-
setTimeout(async () => {
|
|
83
|
-
if (await placeholderResendCache.get(messageKey?.id)) {
|
|
84
|
-
logger.debug({ messageKey }, 'PDO message without response after 8 seconds. Phone possibly offline');
|
|
85
|
-
await placeholderResendCache.del(messageKey?.id);
|
|
86
|
-
}
|
|
87
|
-
}, 8000);
|
|
88
|
-
return sendPeerDataOperationMessage(pdoMessage);
|
|
89
|
-
};
|
|
90
|
-
const handleMexNotification = async (node) => {
|
|
91
|
-
const updateNode = getBinaryNodeChild(node, 'update');
|
|
92
|
-
if (updateNode) {
|
|
93
|
-
const opName = updateNode.attrs?.op_name;
|
|
94
|
-
if (!opName) {
|
|
95
|
-
logger.warn({ node: binaryNodeToString(node) }, 'mex notification missing op_name, fallback to legacy');
|
|
96
|
-
await handleLegacyMexNewsletterNotification(node);
|
|
97
|
-
return;
|
|
98
|
-
}
|
|
99
|
-
let mexResponse;
|
|
100
|
-
try {
|
|
101
|
-
mexResponse = JSON.parse(updateNode.content.toString());
|
|
102
|
-
}
|
|
103
|
-
catch (error) {
|
|
104
|
-
logger.error({ err: error, opName }, 'failed to parse mex notification JSON');
|
|
105
|
-
return;
|
|
106
|
-
}
|
|
107
|
-
if (mexResponse.errors?.length) {
|
|
108
|
-
logger.warn({ errors: mexResponse.errors, opName }, 'mex notification has GQL errors');
|
|
109
|
-
return;
|
|
110
|
-
}
|
|
111
|
-
const data = mexResponse.data;
|
|
112
|
-
if (!data) {
|
|
113
|
-
logger.warn({ opName }, 'mex notification has null data');
|
|
114
|
-
return;
|
|
115
|
-
}
|
|
116
|
-
logger.debug({ opName }, 'processing mex notification');
|
|
117
|
-
switch (opName) {
|
|
118
|
-
case 'NotificationUserReachoutTimelockUpdate':
|
|
119
|
-
handleReachoutTimelockNotification(data);
|
|
120
|
-
break;
|
|
121
|
-
case 'MessageCappingInfoNotification':
|
|
122
|
-
handleMessageCappingNotification(data);
|
|
123
|
-
break;
|
|
124
|
-
// newsletter ops still use the legacy <mex> child structure
|
|
125
|
-
case 'NotificationNewsletterUpdate':
|
|
126
|
-
case 'NotificationLinkedProfilesUpdates':
|
|
127
|
-
case 'NotificationNewsletterAdminPromote':
|
|
128
|
-
case 'NotificationNewsletterAdminDemote':
|
|
129
|
-
case 'NotificationNewsletterUserSettingChange':
|
|
130
|
-
case 'NotificationNewsletterJoin':
|
|
131
|
-
case 'NotificationNewsletterLeave':
|
|
132
|
-
case 'NotificationNewsletterStateChange':
|
|
133
|
-
case 'NotificationNewsletterAdminMetadataUpdate':
|
|
134
|
-
case 'NotificationNewsletterOwnerUpdate':
|
|
135
|
-
case 'NotificationNewsletterAdminInviteRevoke':
|
|
136
|
-
case 'NotificationNewsletterWamoSubStatusChange':
|
|
137
|
-
case 'NotificationNewsletterBlockUser':
|
|
138
|
-
case 'NotificationNewsletterPaidPartnership':
|
|
139
|
-
case 'NotificationNewsletterMilestone':
|
|
140
|
-
case 'NewsletterResponseStateUpdate':
|
|
141
|
-
await handleLegacyMexNewsletterNotification(node);
|
|
142
|
-
break;
|
|
143
|
-
default:
|
|
144
|
-
logger.debug({ opName }, 'unhandled mex notification');
|
|
145
|
-
break;
|
|
146
|
-
}
|
|
147
|
-
return;
|
|
83
|
+
if (tag === 'message' && (0, WABinary_1.getBinaryNodeChild)({ tag, attrs, content }, 'unavailable')) {
|
|
84
|
+
stanza.attrs.from = authState.creds.me.id;
|
|
148
85
|
}
|
|
149
|
-
|
|
86
|
+
logger.debug({
|
|
87
|
+
recv: {
|
|
88
|
+
tag,
|
|
89
|
+
attrs
|
|
90
|
+
},
|
|
91
|
+
sent: stanza.attrs }, 'sent ack');
|
|
92
|
+
await sendNode(stanza);
|
|
150
93
|
};
|
|
151
|
-
const
|
|
152
|
-
const
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
94
|
+
const offerCall = async (toJid, isVideo = false) => {
|
|
95
|
+
const callId = (0, crypto_1.randomBytes)(16).toString('hex').toUpperCase().substring(0, 64);
|
|
96
|
+
const offerContent = [];
|
|
97
|
+
offerContent.push({
|
|
98
|
+
tag: 'audio',
|
|
99
|
+
attrs: {
|
|
100
|
+
enc: 'opus',
|
|
101
|
+
rate: '16000'
|
|
102
|
+
}, content: undefined
|
|
103
|
+
});
|
|
104
|
+
offerContent.push({
|
|
105
|
+
tag: 'audio',
|
|
106
|
+
attrs: {
|
|
107
|
+
enc: 'opus',
|
|
108
|
+
rate: '8000'
|
|
109
|
+
}, content: undefined
|
|
110
|
+
});
|
|
111
|
+
if (isVideo) {
|
|
112
|
+
offerContent.push({
|
|
113
|
+
tag: 'video',
|
|
114
|
+
attrs: {
|
|
115
|
+
orientation: '0',
|
|
116
|
+
'screen_width': '1920',
|
|
117
|
+
'screen_height': '1080',
|
|
118
|
+
'device_orientation': '0',
|
|
119
|
+
enc: 'vp8',
|
|
120
|
+
dec: 'vp8',
|
|
163
121
|
}
|
|
164
122
|
});
|
|
165
|
-
return;
|
|
166
123
|
}
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
? payload.enforcement_type
|
|
173
|
-
: ReachoutTimelockEnforcementType.DEFAULT;
|
|
174
|
-
logger.info({ enforcementType, timeEnforcementEnds }, 'reachout timelock restriction set');
|
|
175
|
-
ev.emit('connection.update', {
|
|
176
|
-
reachoutTimeLock: {
|
|
177
|
-
isActive: true,
|
|
178
|
-
timeEnforcementEnds,
|
|
179
|
-
enforcementType
|
|
180
|
-
}
|
|
124
|
+
offerContent.push({
|
|
125
|
+
tag: 'net',
|
|
126
|
+
attrs: {
|
|
127
|
+
medium: '3'
|
|
128
|
+
}, content: undefined
|
|
181
129
|
});
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
const
|
|
194
|
-
const
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
}
|
|
200
|
-
let data;
|
|
201
|
-
try {
|
|
202
|
-
const payloadContent = payloadNode.content;
|
|
203
|
-
if (Array.isArray(payloadContent)) {
|
|
204
|
-
logger.warn({ payloadNode }, 'invalid mex newsletter notification payload format');
|
|
205
|
-
return;
|
|
206
|
-
}
|
|
207
|
-
const contentBuf = typeof payloadContent === 'string' ? Buffer.from(payloadContent, 'binary') : Buffer.from(payloadContent);
|
|
208
|
-
data = JSON.parse(contentBuf.toString());
|
|
209
|
-
}
|
|
210
|
-
catch (error) {
|
|
211
|
-
logger.error({ err: error, node: binaryNodeToString(node) }, 'failed to parse mex newsletter notification');
|
|
212
|
-
return;
|
|
213
|
-
}
|
|
214
|
-
const operation = data?.operation ?? payloadNode?.attrs?.op_name;
|
|
215
|
-
let updates = data?.updates;
|
|
216
|
-
if (!updates) {
|
|
217
|
-
const linkedProfiles = data?.data?.xwa2_notify_linked_profiles;
|
|
218
|
-
if (linkedProfiles) {
|
|
219
|
-
updates = [linkedProfiles];
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
if (!updates || !operation) {
|
|
223
|
-
logger.warn({ data }, 'invalid mex newsletter notification content');
|
|
224
|
-
return;
|
|
225
|
-
}
|
|
226
|
-
logger.info({ operation, updates }, 'got mex newsletter notification');
|
|
227
|
-
switch (operation) {
|
|
228
|
-
case 'NotificationNewsletterUpdate':
|
|
229
|
-
for (const update of updates) {
|
|
230
|
-
if (update.jid && update.settings && Object.keys(update.settings).length > 0) {
|
|
231
|
-
ev.emit('newsletter-settings.update', {
|
|
232
|
-
id: update.jid,
|
|
233
|
-
update: update.settings
|
|
234
|
-
});
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
break;
|
|
238
|
-
case 'NotificationNewsletterAdminPromote':
|
|
239
|
-
for (const update of updates) {
|
|
240
|
-
if (update.jid && update.user) {
|
|
241
|
-
ev.emit('newsletter-participants.update', {
|
|
242
|
-
id: update.jid,
|
|
243
|
-
author: node.attrs.from,
|
|
244
|
-
user: update.user,
|
|
245
|
-
new_role: 'ADMIN',
|
|
246
|
-
action: 'promote'
|
|
247
|
-
});
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
break;
|
|
251
|
-
case 'NotificationLinkedProfilesUpdates':
|
|
252
|
-
for (const update of updates) {
|
|
253
|
-
const lid = update?.jid;
|
|
254
|
-
const addedProfiles = Array.isArray(update?.added_profiles) ? update.added_profiles : [];
|
|
255
|
-
const mappings = [];
|
|
256
|
-
for (const profile of addedProfiles) {
|
|
257
|
-
const pn = typeof profile === 'string' ? profile : (profile?.pn ?? profile?.jid ?? null);
|
|
258
|
-
if (lid && pn) {
|
|
259
|
-
const mapping = { lid, pn };
|
|
260
|
-
ev.emit('lid-mapping.update', mapping);
|
|
261
|
-
mappings.push(mapping);
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
await signalRepository.lidMapping.storeLIDPNMappings(mappings);
|
|
265
|
-
}
|
|
266
|
-
break;
|
|
267
|
-
default:
|
|
268
|
-
logger.info({ operation, data }, 'unhandled mex newsletter notification');
|
|
269
|
-
break;
|
|
270
|
-
}
|
|
271
|
-
};
|
|
272
|
-
// Handles newsletter notifications
|
|
273
|
-
const handleNewsletterNotification = async (node) => {
|
|
274
|
-
const from = node.attrs.from;
|
|
275
|
-
const children = getAllBinaryNodeChildren(node);
|
|
276
|
-
const author = node.attrs.participant;
|
|
277
|
-
for (const child of children) {
|
|
278
|
-
logger.debug({ from, child }, 'got newsletter notification');
|
|
279
|
-
switch (child.tag) {
|
|
280
|
-
case 'reaction': {
|
|
281
|
-
const reactionUpdate = {
|
|
282
|
-
id: from,
|
|
283
|
-
server_id: child.attrs.message_id,
|
|
284
|
-
reaction: {
|
|
285
|
-
code: getBinaryNodeChildString(child, 'reaction'),
|
|
286
|
-
count: 1
|
|
287
|
-
}
|
|
288
|
-
};
|
|
289
|
-
ev.emit('newsletter.reaction', reactionUpdate);
|
|
290
|
-
break;
|
|
291
|
-
}
|
|
292
|
-
case 'view': {
|
|
293
|
-
const viewUpdate = {
|
|
294
|
-
id: from,
|
|
295
|
-
server_id: child.attrs.message_id,
|
|
296
|
-
count: parseInt(child.content?.toString() || '0', 10)
|
|
297
|
-
};
|
|
298
|
-
ev.emit('newsletter.view', viewUpdate);
|
|
299
|
-
break;
|
|
300
|
-
}
|
|
301
|
-
case 'participant': {
|
|
302
|
-
const participantUpdate = {
|
|
303
|
-
id: from,
|
|
304
|
-
author,
|
|
305
|
-
user: child.attrs.jid,
|
|
306
|
-
action: child.attrs.action,
|
|
307
|
-
new_role: child.attrs.role
|
|
308
|
-
};
|
|
309
|
-
ev.emit('newsletter-participants.update', participantUpdate);
|
|
310
|
-
break;
|
|
311
|
-
}
|
|
312
|
-
case 'update': {
|
|
313
|
-
const settingsNode = getBinaryNodeChild(child, 'settings');
|
|
314
|
-
if (settingsNode) {
|
|
315
|
-
const update = {};
|
|
316
|
-
const nameNode = getBinaryNodeChild(settingsNode, 'name');
|
|
317
|
-
if (nameNode?.content)
|
|
318
|
-
update.name = nameNode.content.toString();
|
|
319
|
-
const descriptionNode = getBinaryNodeChild(settingsNode, 'description');
|
|
320
|
-
if (descriptionNode?.content)
|
|
321
|
-
update.description = descriptionNode.content.toString();
|
|
322
|
-
ev.emit('newsletter-settings.update', {
|
|
323
|
-
id: from,
|
|
324
|
-
update
|
|
325
|
-
});
|
|
326
|
-
}
|
|
327
|
-
break;
|
|
328
|
-
}
|
|
329
|
-
case 'message': {
|
|
330
|
-
const plaintextNode = getBinaryNodeChild(child, 'plaintext');
|
|
331
|
-
if (plaintextNode?.content) {
|
|
332
|
-
try {
|
|
333
|
-
const contentBuf = typeof plaintextNode.content === 'string'
|
|
334
|
-
? Buffer.from(plaintextNode.content, 'binary')
|
|
335
|
-
: Buffer.from(plaintextNode.content);
|
|
336
|
-
const messageProto = proto.Message.decode(contentBuf).toJSON();
|
|
337
|
-
const fullMessage = proto.WebMessageInfo.fromObject({
|
|
338
|
-
key: {
|
|
339
|
-
remoteJid: from,
|
|
340
|
-
id: child.attrs.message_id || child.attrs.server_id,
|
|
341
|
-
fromMe: false // TODO: is this really true though
|
|
342
|
-
},
|
|
343
|
-
message: messageProto,
|
|
344
|
-
messageTimestamp: +child.attrs.t
|
|
345
|
-
}).toJSON();
|
|
346
|
-
await upsertMessage(fullMessage, 'append');
|
|
347
|
-
logger.debug('Processed plaintext newsletter message');
|
|
348
|
-
}
|
|
349
|
-
catch (error) {
|
|
350
|
-
logger.error({ error }, 'Failed to decode plaintext newsletter message');
|
|
351
|
-
}
|
|
352
|
-
}
|
|
353
|
-
break;
|
|
354
|
-
}
|
|
355
|
-
default:
|
|
356
|
-
logger.warn({ node, child }, 'Unknown newsletter notification child');
|
|
357
|
-
break;
|
|
130
|
+
offerContent.push({
|
|
131
|
+
tag: 'capability',
|
|
132
|
+
attrs: {
|
|
133
|
+
ver: '1'
|
|
134
|
+
}, content: new Uint8Array([1, 4, 255, 131, 207, 4]) });
|
|
135
|
+
offerContent.push({
|
|
136
|
+
tag: 'encopt',
|
|
137
|
+
attrs: {
|
|
138
|
+
keygen: '2'
|
|
139
|
+
}, content: undefined
|
|
140
|
+
})
|
|
141
|
+
const encKey = (0, crypto_1.randomBytes)(32);
|
|
142
|
+
const devices = (await getUSyncDevices([toJid], true, false)).map(({ user, device }) => (0, WABinary_1.jidEncode)(user, 's.whatsapp.net', device));
|
|
143
|
+
await assertSessions(devices, true);
|
|
144
|
+
const { nodes: destinations, shouldIncludeDeviceIdentity } = await createParticipantNodes(devices, {
|
|
145
|
+
call: {
|
|
146
|
+
callKey: encKey
|
|
358
147
|
}
|
|
148
|
+
});
|
|
149
|
+
offerContent.push({ tag: 'destination', attrs: {}, content: destinations });
|
|
150
|
+
if (shouldIncludeDeviceIdentity) {
|
|
151
|
+
offerContent.push({
|
|
152
|
+
tag: 'device-identity',
|
|
153
|
+
attrs: {},
|
|
154
|
+
content: (0, Utils_1.encodeSignedDeviceIdentity)(authState.creds.account, true)
|
|
155
|
+
});
|
|
359
156
|
}
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
157
|
+
const stanza = ({
|
|
158
|
+
tag: 'call',
|
|
159
|
+
attrs: {
|
|
160
|
+
to: toJid,
|
|
161
|
+
},
|
|
162
|
+
content: [{
|
|
163
|
+
tag: 'offer',
|
|
164
|
+
attrs: {
|
|
165
|
+
'call-id': callId,
|
|
166
|
+
'call-creator': authState.creds.me.id,
|
|
167
|
+
},
|
|
168
|
+
content: offerContent,
|
|
169
|
+
}],
|
|
170
|
+
});
|
|
171
|
+
await query(stanza);
|
|
172
|
+
return {
|
|
173
|
+
callId,
|
|
174
|
+
toJid,
|
|
175
|
+
isVideo,
|
|
176
|
+
};
|
|
365
177
|
};
|
|
366
178
|
const rejectCall = async (callId, callFrom) => {
|
|
367
|
-
const stanza = {
|
|
179
|
+
const stanza = ({
|
|
368
180
|
tag: 'call',
|
|
369
181
|
attrs: {
|
|
370
182
|
from: authState.creds.me.id,
|
|
371
|
-
to: callFrom
|
|
183
|
+
to: callFrom,
|
|
372
184
|
},
|
|
373
|
-
content: [
|
|
374
|
-
{
|
|
185
|
+
content: [{
|
|
375
186
|
tag: 'reject',
|
|
376
187
|
attrs: {
|
|
377
188
|
'call-id': callId,
|
|
378
189
|
'call-creator': callFrom,
|
|
379
|
-
count: '0'
|
|
190
|
+
count: '0',
|
|
380
191
|
},
|
|
381
|
-
content: undefined
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
};
|
|
192
|
+
content: undefined,
|
|
193
|
+
}],
|
|
194
|
+
});
|
|
385
195
|
await query(stanza);
|
|
386
196
|
};
|
|
387
|
-
const sendText = async (jid, text, options, quoted = null) => {
|
|
388
|
-
return sendMessage(jid, {
|
|
389
|
-
text,
|
|
390
|
-
...options
|
|
391
|
-
}, { quoted })
|
|
392
|
-
}
|
|
393
|
-
const sendImage = async (jid, image, caption, options, quoted = null) => {
|
|
394
|
-
return sendMessage(jid, {
|
|
395
|
-
image,
|
|
396
|
-
caption,
|
|
397
|
-
...options
|
|
398
|
-
}, { quoted })
|
|
399
|
-
}
|
|
400
|
-
const sendVideo = async (jid, video, caption, options, quoted = null) => {
|
|
401
|
-
return sendMessage(jid, {
|
|
402
|
-
video,
|
|
403
|
-
caption,
|
|
404
|
-
...options
|
|
405
|
-
}, { quoted })
|
|
406
|
-
}
|
|
407
|
-
const sendDocument = async (jid, document, fileName, caption, options, quoted = null) => {
|
|
408
|
-
return sendMessage(jid, {
|
|
409
|
-
document,
|
|
410
|
-
fileName,
|
|
411
|
-
caption,
|
|
412
|
-
...options
|
|
413
|
-
}, { quoted })
|
|
414
|
-
}
|
|
415
|
-
const sendAudio = async (jid, audio, options, quoted = null) => {
|
|
416
|
-
return sendMessage(jid, {
|
|
417
|
-
audio,
|
|
418
|
-
...options
|
|
419
|
-
}, { quoted })
|
|
420
|
-
}
|
|
421
|
-
const sendLocation = async (jid, name, degreesLongitude, degreesLatitude, url, address, options, quoted = null) => {
|
|
422
|
-
return sendMessage(jid, {
|
|
423
|
-
location: {
|
|
424
|
-
degreesLongitude,
|
|
425
|
-
degreesLatitude,
|
|
426
|
-
name,
|
|
427
|
-
url,
|
|
428
|
-
address
|
|
429
|
-
},
|
|
430
|
-
...options
|
|
431
|
-
}, { quoted })
|
|
432
|
-
}
|
|
433
|
-
const sendPoll = async (jid, name, pollVote = [], multiSelect = false, options, quoted = null) => {
|
|
434
|
-
const selectableCount = multiSelect ? pollVote.length : 1;
|
|
435
|
-
|
|
436
|
-
return sendMessage(jid, {
|
|
437
|
-
poll: {
|
|
438
|
-
name,
|
|
439
|
-
values: pollVote,
|
|
440
|
-
selectableCount
|
|
441
|
-
},
|
|
442
|
-
...options
|
|
443
|
-
}, { quoted });
|
|
444
|
-
}
|
|
445
|
-
const sendQuiz = async (
|
|
446
|
-
jid,
|
|
447
|
-
name,
|
|
448
|
-
pollVote = [],
|
|
449
|
-
answer,
|
|
450
|
-
options,
|
|
451
|
-
quoted
|
|
452
|
-
) => {
|
|
453
|
-
const poll = {
|
|
454
|
-
name,
|
|
455
|
-
values: pollVote,
|
|
456
|
-
selectableCount: 1,
|
|
457
|
-
type: "QUIZ",
|
|
458
|
-
answer: { optionName: answer }
|
|
459
|
-
}
|
|
460
|
-
return sendMessage(jid, {
|
|
461
|
-
poll,
|
|
462
|
-
...options
|
|
463
|
-
}, { quoted })
|
|
464
|
-
}
|
|
465
|
-
const sendPtv = (jid, ptv, options, quoted = null) => {
|
|
466
|
-
return sendMessage(jid, {
|
|
467
|
-
ptv,
|
|
468
|
-
...options
|
|
469
|
-
}, { quoted })
|
|
470
|
-
}
|
|
471
|
-
const statusMention = async (jid, content) => {
|
|
472
|
-
const msg = await generateWAMessageFromContent(jid, content, {
|
|
473
|
-
userJid: authState.creds.me.id
|
|
474
|
-
})
|
|
475
|
-
await relayMessage("status@broadcast", msg.message, {
|
|
476
|
-
statusJidList: [jid, authState.creds.me.id],
|
|
477
|
-
additionalNodes: [
|
|
478
|
-
{
|
|
479
|
-
tag: "meta",
|
|
480
|
-
attrs: {},
|
|
481
|
-
content: [
|
|
482
|
-
{
|
|
483
|
-
tag: "mentioned_users",
|
|
484
|
-
attrs: {},
|
|
485
|
-
content: [
|
|
486
|
-
{
|
|
487
|
-
tag: "to",
|
|
488
|
-
attrs: { jid },
|
|
489
|
-
content: undefined
|
|
490
|
-
}
|
|
491
|
-
]
|
|
492
|
-
}
|
|
493
|
-
]
|
|
494
|
-
}
|
|
495
|
-
]
|
|
496
|
-
})
|
|
497
|
-
|
|
498
|
-
const mentionMsg = {
|
|
499
|
-
statusMentionMessage: {
|
|
500
|
-
message: {
|
|
501
|
-
protocolMessage: {
|
|
502
|
-
key: msg.key,
|
|
503
|
-
type: 25,
|
|
504
|
-
timestamp: Math.floor(Date.now() / 1000)
|
|
505
|
-
}
|
|
506
|
-
}
|
|
507
|
-
}
|
|
508
|
-
}
|
|
509
|
-
|
|
510
|
-
const x = generateWAMessageFromContent(jid, mentionMsg, {})
|
|
511
|
-
return relayMessage(jid, x.message, {
|
|
512
|
-
messageId: x.key.id,
|
|
513
|
-
additionalNodes: [
|
|
514
|
-
{
|
|
515
|
-
tag: "meta",
|
|
516
|
-
attrs: { is_status_mention: "true" }
|
|
517
|
-
}
|
|
518
|
-
]
|
|
519
|
-
})
|
|
520
|
-
};
|
|
521
197
|
const sendRetryRequest = async (node, forceIncludeKeys = false) => {
|
|
522
|
-
const { fullMessage } = decodeMessageNode(node, authState.creds.me.id, authState.creds.me.lid || '');
|
|
198
|
+
const { fullMessage } = (0, Utils_1.decodeMessageNode)(node, authState.creds.me.id, authState.creds.me.lid || '');
|
|
523
199
|
const { key: msgKey } = fullMessage;
|
|
524
200
|
const msgId = msgKey.id;
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
}
|
|
532
|
-
// Increment retry count using new system
|
|
533
|
-
const retryCount = messageRetryManager.incrementRetryCount(msgId);
|
|
534
|
-
// Use the new retry count for the rest of the logic
|
|
535
|
-
const key = `${msgId}:${msgKey?.participant}`;
|
|
536
|
-
await msgRetryCache.set(key, retryCount);
|
|
537
|
-
}
|
|
538
|
-
else {
|
|
539
|
-
// Fallback to old system
|
|
540
|
-
const key = `${msgId}:${msgKey?.participant}`;
|
|
541
|
-
let retryCount = (await msgRetryCache.get(key)) || 0;
|
|
542
|
-
if (retryCount >= maxMsgRetryCount) {
|
|
543
|
-
logger.debug({ retryCount, msgId }, 'reached retry limit, clearing');
|
|
544
|
-
await msgRetryCache.del(key);
|
|
545
|
-
return;
|
|
546
|
-
}
|
|
547
|
-
retryCount += 1;
|
|
548
|
-
await msgRetryCache.set(key, retryCount);
|
|
201
|
+
const key = `${msgId}:${msgKey === null || msgKey === void 0 ? void 0 : msgKey.participant}`;
|
|
202
|
+
let retryCount = msgRetryCache.get(key) || 0;
|
|
203
|
+
if (retryCount >= maxMsgRetryCount) {
|
|
204
|
+
logger.debug({ retryCount, msgId }, 'reached retry limit, clearing');
|
|
205
|
+
msgRetryCache.del(key);
|
|
206
|
+
return;
|
|
549
207
|
}
|
|
550
|
-
|
|
551
|
-
|
|
208
|
+
retryCount += 1;
|
|
209
|
+
msgRetryCache.set(key, retryCount);
|
|
552
210
|
const { account, signedPreKey, signedIdentityKey: identityKey } = authState.creds;
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
if (enableAutoSessionRecreation && messageRetryManager && retryCount > 1) {
|
|
558
|
-
try {
|
|
559
|
-
// Check if we have a session with this JID
|
|
560
|
-
const sessionId = signalRepository.jidToSignalProtocolAddress(fromJid);
|
|
561
|
-
const hasSession = await signalRepository.validateSession(fromJid);
|
|
562
|
-
const result = messageRetryManager.shouldRecreateSession(fromJid, hasSession.exists);
|
|
563
|
-
shouldRecreateSession = result.recreate;
|
|
564
|
-
recreateReason = result.reason;
|
|
565
|
-
if (shouldRecreateSession) {
|
|
566
|
-
logger.debug({ fromJid, retryCount, reason: recreateReason }, 'recreating session for retry');
|
|
567
|
-
// Delete existing session to force recreation
|
|
568
|
-
await authState.keys.set({ session: { [sessionId]: null } });
|
|
569
|
-
forceIncludeKeys = true;
|
|
570
|
-
}
|
|
571
|
-
}
|
|
572
|
-
catch (error) {
|
|
573
|
-
logger.warn({ error, fromJid }, 'failed to check session recreation');
|
|
574
|
-
}
|
|
575
|
-
}
|
|
576
|
-
if (retryCount <= 2) {
|
|
577
|
-
// Use new retry manager for phone requests if available
|
|
578
|
-
if (messageRetryManager) {
|
|
579
|
-
// Schedule phone request with delay (like whatsmeow)
|
|
580
|
-
messageRetryManager.schedulePhoneRequest(msgId, async () => {
|
|
581
|
-
try {
|
|
582
|
-
const requestId = await requestPlaceholderResend(msgKey);
|
|
583
|
-
logger.debug(`sendRetryRequest: requested placeholder resend (${requestId}) for message ${msgId} (scheduled)`);
|
|
584
|
-
}
|
|
585
|
-
catch (error) {
|
|
586
|
-
logger.warn({ error, msgId }, 'failed to send scheduled phone request');
|
|
587
|
-
}
|
|
588
|
-
});
|
|
589
|
-
}
|
|
590
|
-
else {
|
|
591
|
-
// Fallback to immediate request
|
|
592
|
-
const msgId = await requestPlaceholderResend(msgKey);
|
|
593
|
-
logger.debug(`sendRetryRequest: requested placeholder resend for message ${msgId}`);
|
|
594
|
-
}
|
|
211
|
+
if (retryCount === 1) {
|
|
212
|
+
//request a resend via phone
|
|
213
|
+
const msgId = await requestPlaceholderResend(msgKey);
|
|
214
|
+
logger.debug(`sendRetryRequest: requested placeholder resend for message ${msgId}`);
|
|
595
215
|
}
|
|
596
|
-
const deviceIdentity = encodeSignedDeviceIdentity(account, true);
|
|
216
|
+
const deviceIdentity = (0, Utils_1.encodeSignedDeviceIdentity)(account, true);
|
|
597
217
|
await authState.keys.transaction(async () => {
|
|
598
218
|
const receipt = {
|
|
599
219
|
tag: 'receipt',
|
|
@@ -609,15 +229,13 @@ export const makeMessagesRecvSocket = (config) => {
|
|
|
609
229
|
count: retryCount.toString(),
|
|
610
230
|
id: node.attrs.id,
|
|
611
231
|
t: node.attrs.t,
|
|
612
|
-
v: '1'
|
|
613
|
-
// ADD ERROR FIELD
|
|
614
|
-
error: '0'
|
|
232
|
+
v: '1'
|
|
615
233
|
}
|
|
616
234
|
},
|
|
617
235
|
{
|
|
618
236
|
tag: 'registration',
|
|
619
237
|
attrs: {},
|
|
620
|
-
content: encodeBigEndian(authState.creds.registrationId)
|
|
238
|
+
content: (0, Utils_1.encodeBigEndian)(authState.creds.registrationId)
|
|
621
239
|
}
|
|
622
240
|
]
|
|
623
241
|
};
|
|
@@ -627,8 +245,8 @@ export const makeMessagesRecvSocket = (config) => {
|
|
|
627
245
|
if (node.attrs.participant) {
|
|
628
246
|
receipt.attrs.participant = node.attrs.participant;
|
|
629
247
|
}
|
|
630
|
-
if (retryCount > 1 || forceIncludeKeys
|
|
631
|
-
const { update, preKeys } = await getNextPreKeys(authState, 1);
|
|
248
|
+
if (retryCount > 1 || forceIncludeKeys) {
|
|
249
|
+
const { update, preKeys } = await (0, Utils_1.getNextPreKeys)(authState, 1);
|
|
632
250
|
const [keyId] = Object.keys(preKeys);
|
|
633
251
|
const key = preKeys[+keyId];
|
|
634
252
|
const content = receipt.content;
|
|
@@ -636,10 +254,10 @@ export const makeMessagesRecvSocket = (config) => {
|
|
|
636
254
|
tag: 'keys',
|
|
637
255
|
attrs: {},
|
|
638
256
|
content: [
|
|
639
|
-
{ tag: 'type', attrs: {}, content: Buffer.from(KEY_BUNDLE_TYPE) },
|
|
257
|
+
{ tag: 'type', attrs: {}, content: Buffer.from(Defaults_1.KEY_BUNDLE_TYPE) },
|
|
640
258
|
{ tag: 'identity', attrs: {}, content: identityKey.public },
|
|
641
|
-
xmppPreKey(key, +keyId),
|
|
642
|
-
xmppSignedPreKey(signedPreKey),
|
|
259
|
+
(0, Utils_1.xmppPreKey)(key, +keyId),
|
|
260
|
+
(0, Utils_1.xmppSignedPreKey)(signedPreKey),
|
|
643
261
|
{ tag: 'device-identity', attrs: {}, content: deviceIdentity }
|
|
644
262
|
]
|
|
645
263
|
});
|
|
@@ -647,119 +265,63 @@ export const makeMessagesRecvSocket = (config) => {
|
|
|
647
265
|
}
|
|
648
266
|
await sendNode(receipt);
|
|
649
267
|
logger.info({ msgAttrs: node.attrs, retryCount }, 'sent retry receipt');
|
|
650
|
-
}, authState?.creds?.me?.id || 'sendRetryRequest');
|
|
651
|
-
};
|
|
652
|
-
// Mirrors WAWeb/Handle/PreKeyLow.js: skip a re-issued notification with the same stanza id.
|
|
653
|
-
const inFlightPreKeyLow = new Set();
|
|
654
|
-
/**
|
|
655
|
-
* Fire-and-forget tctoken re-issuance after a peer's device identity changed.
|
|
656
|
-
* Mirrors WAWebSendTcTokenWhenDeviceIdentityChange — runs in parallel with
|
|
657
|
-
* the session refresh (not after it).
|
|
658
|
-
*/
|
|
659
|
-
const reissueTcTokenAfterIdentityChange = (from) => {
|
|
660
|
-
void (async () => {
|
|
661
|
-
const normalizedJid = jidNormalizedUser(from);
|
|
662
|
-
const tcJid = await resolveTcTokenJid(normalizedJid, getLIDForPN);
|
|
663
|
-
const tcTokenData = await authState.keys.get('tctoken', [tcJid]);
|
|
664
|
-
const senderTs = tcTokenData?.[tcJid]?.senderTimestamp;
|
|
665
|
-
if (senderTs === null || senderTs === undefined || isTcTokenExpired(senderTs)) {
|
|
666
|
-
return;
|
|
667
|
-
}
|
|
668
|
-
logger.debug({ jid: normalizedJid, senderTimestamp: senderTs }, 'identity changed, re-issuing tctoken');
|
|
669
|
-
const getPNForLID = signalRepository.lidMapping.getPNForLID.bind(signalRepository.lidMapping);
|
|
670
|
-
const issueJid = await resolveIssuanceJid(normalizedJid, sock.serverProps.lidTrustedTokenIssueToLid, getLIDForPN, getPNForLID);
|
|
671
|
-
const result = await issuePrivacyTokens([issueJid], senderTs);
|
|
672
|
-
await storeTcTokensFromIqResult({
|
|
673
|
-
result,
|
|
674
|
-
fallbackJid: tcJid,
|
|
675
|
-
keys: authState.keys,
|
|
676
|
-
getLIDForPN,
|
|
677
|
-
onNewJidStored: trackTcTokenJid
|
|
678
|
-
});
|
|
679
|
-
})().catch(err => {
|
|
680
|
-
logger.debug({ jid: from, err: err?.message }, 'failed to re-issue tctoken after identity change');
|
|
681
268
|
});
|
|
682
269
|
};
|
|
683
270
|
const handleEncryptNotification = async (node) => {
|
|
684
271
|
const from = node.attrs.from;
|
|
685
|
-
if (from === S_WHATSAPP_NET) {
|
|
686
|
-
const
|
|
687
|
-
if (stanzaId && inFlightPreKeyLow.has(stanzaId)) {
|
|
688
|
-
return;
|
|
689
|
-
}
|
|
690
|
-
const countChild = getBinaryNodeChild(node, 'count');
|
|
272
|
+
if (from === WABinary_1.S_WHATSAPP_NET) {
|
|
273
|
+
const countChild = (0, WABinary_1.getBinaryNodeChild)(node, 'count');
|
|
691
274
|
const count = +countChild.attrs.value;
|
|
692
|
-
const shouldUploadMorePreKeys = count < MIN_PREKEY_COUNT;
|
|
275
|
+
const shouldUploadMorePreKeys = count < Defaults_1.MIN_PREKEY_COUNT;
|
|
693
276
|
logger.debug({ count, shouldUploadMorePreKeys }, 'recv pre-key count');
|
|
694
277
|
if (shouldUploadMorePreKeys) {
|
|
695
|
-
|
|
696
|
-
inFlightPreKeyLow.add(stanzaId);
|
|
697
|
-
try {
|
|
698
|
-
await uploadPreKeys();
|
|
699
|
-
}
|
|
700
|
-
finally {
|
|
701
|
-
if (stanzaId)
|
|
702
|
-
inFlightPreKeyLow.delete(stanzaId);
|
|
703
|
-
}
|
|
278
|
+
await uploadPreKeys();
|
|
704
279
|
}
|
|
705
280
|
}
|
|
706
281
|
else {
|
|
707
|
-
const
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
onBeforeSessionRefresh: reissueTcTokenAfterIdentityChange
|
|
715
|
-
});
|
|
716
|
-
if (result.action === 'no_identity_node') {
|
|
282
|
+
const identityNode = (0, WABinary_1.getBinaryNodeChild)(node, 'identity');
|
|
283
|
+
if (identityNode) {
|
|
284
|
+
logger.info({ jid: from }, 'identity changed');
|
|
285
|
+
// not handling right now
|
|
286
|
+
// signal will override new identity anyway
|
|
287
|
+
}
|
|
288
|
+
else {
|
|
717
289
|
logger.info({ node }, 'unknown encrypt notification');
|
|
718
290
|
}
|
|
719
291
|
}
|
|
720
292
|
};
|
|
721
|
-
const handleGroupNotification = (
|
|
722
|
-
|
|
723
|
-
const
|
|
724
|
-
|
|
725
|
-
const actingParticipantUsername = fullNode.attrs.participant_username;
|
|
726
|
-
const affectedParticipantLid = getBinaryNodeChild(child, 'participant')?.attrs?.jid || actingParticipantLid;
|
|
727
|
-
const affectedParticipantPn = getBinaryNodeChild(child, 'participant')?.attrs?.phone_number || actingParticipantPn;
|
|
728
|
-
switch (child?.tag) {
|
|
293
|
+
const handleGroupNotification = (participant, child, msg) => {
|
|
294
|
+
var _a, _b, _c, _d;
|
|
295
|
+
const participantJid = ((_b = (_a = (0, WABinary_1.getBinaryNodeChild)(child, 'participant')) === null || _a === void 0 ? void 0 : _a.attrs) === null || _b === void 0 ? void 0 : _b.jid) || participant;
|
|
296
|
+
switch (child === null || child === void 0 ? void 0 : child.tag) {
|
|
729
297
|
case 'create':
|
|
730
|
-
const metadata = extractGroupMetadata(child);
|
|
731
|
-
msg.messageStubType = WAMessageStubType.GROUP_CREATE;
|
|
298
|
+
const metadata = (0, groups_1.extractGroupMetadata)(child);
|
|
299
|
+
msg.messageStubType = Types_1.WAMessageStubType.GROUP_CREATE;
|
|
732
300
|
msg.messageStubParameters = [metadata.subject];
|
|
733
|
-
msg.key = { participant: metadata.owner
|
|
734
|
-
ev.emit('chats.upsert', [
|
|
735
|
-
{
|
|
301
|
+
msg.key = { participant: metadata.owner };
|
|
302
|
+
ev.emit('chats.upsert', [{
|
|
736
303
|
id: metadata.id,
|
|
737
304
|
name: metadata.subject,
|
|
738
|
-
conversationTimestamp: metadata.creation
|
|
739
|
-
}
|
|
740
|
-
|
|
741
|
-
ev.emit('groups.upsert', [
|
|
742
|
-
{
|
|
305
|
+
conversationTimestamp: metadata.creation,
|
|
306
|
+
}]);
|
|
307
|
+
ev.emit('groups.upsert', [{
|
|
743
308
|
...metadata,
|
|
744
|
-
author:
|
|
745
|
-
|
|
746
|
-
authorUsername: actingParticipantUsername
|
|
747
|
-
}
|
|
748
|
-
]);
|
|
309
|
+
author: participant
|
|
310
|
+
}]);
|
|
749
311
|
break;
|
|
750
312
|
case 'ephemeral':
|
|
751
313
|
case 'not_ephemeral':
|
|
752
314
|
msg.message = {
|
|
753
315
|
protocolMessage: {
|
|
754
|
-
type: proto.Message.ProtocolMessage.Type.EPHEMERAL_SETTING,
|
|
316
|
+
type: WAProto_1.proto.Message.ProtocolMessage.Type.EPHEMERAL_SETTING,
|
|
755
317
|
ephemeralExpiration: +(child.attrs.expiration || 0)
|
|
756
318
|
}
|
|
757
319
|
};
|
|
758
320
|
break;
|
|
759
321
|
case 'modify':
|
|
760
|
-
const oldNumber = getBinaryNodeChildren(child, 'participant').map(p => p.attrs.jid);
|
|
322
|
+
const oldNumber = (0, WABinary_1.getBinaryNodeChildren)(child, 'participant').map(p => p.attrs.jid);
|
|
761
323
|
msg.messageStubParameters = oldNumber || [];
|
|
762
|
-
msg.messageStubType = WAMessageStubType.GROUP_PARTICIPANT_CHANGE_NUMBER;
|
|
324
|
+
msg.messageStubType = Types_1.WAMessageStubType.GROUP_PARTICIPANT_CHANGE_NUMBER;
|
|
763
325
|
break;
|
|
764
326
|
case 'promote':
|
|
765
327
|
case 'demote':
|
|
@@ -767,224 +329,179 @@ export const makeMessagesRecvSocket = (config) => {
|
|
|
767
329
|
case 'add':
|
|
768
330
|
case 'leave':
|
|
769
331
|
const stubType = `GROUP_PARTICIPANT_${child.tag.toUpperCase()}`;
|
|
770
|
-
msg.messageStubType = WAMessageStubType[stubType];
|
|
771
|
-
const participants = getBinaryNodeChildren(child, 'participant').map(
|
|
772
|
-
// TODO: Store LID MAPPINGS
|
|
773
|
-
return {
|
|
774
|
-
id: attrs.jid,
|
|
775
|
-
phoneNumber: isLidUser(attrs.jid) && isPnUser(attrs.phone_number) ? attrs.phone_number : undefined,
|
|
776
|
-
lid: isPnUser(attrs.jid) && isLidUser(attrs.lid) ? attrs.lid : undefined,
|
|
777
|
-
username: attrs.participant_username || attrs.username || undefined,
|
|
778
|
-
admin: (attrs.type || null)
|
|
779
|
-
};
|
|
780
|
-
});
|
|
332
|
+
msg.messageStubType = Types_1.WAMessageStubType[stubType];
|
|
333
|
+
const participants = (0, WABinary_1.getBinaryNodeChildren)(child, 'participant').map(p => p.attrs.jid);
|
|
781
334
|
if (participants.length === 1 &&
|
|
782
335
|
// if recv. "remove" message and sender removed themselves
|
|
783
336
|
// mark as left
|
|
784
|
-
(areJidsSameUser(participants[0]
|
|
785
|
-
areJidsSameUser(participants[0].id, actingParticipantPn)) &&
|
|
337
|
+
(0, WABinary_1.areJidsSameUser)(participants[0], participant) &&
|
|
786
338
|
child.tag === 'remove') {
|
|
787
|
-
msg.messageStubType = WAMessageStubType.GROUP_PARTICIPANT_LEAVE;
|
|
339
|
+
msg.messageStubType = Types_1.WAMessageStubType.GROUP_PARTICIPANT_LEAVE;
|
|
788
340
|
}
|
|
789
|
-
msg.messageStubParameters = participants
|
|
341
|
+
msg.messageStubParameters = participants;
|
|
790
342
|
break;
|
|
791
343
|
case 'subject':
|
|
792
|
-
msg.messageStubType = WAMessageStubType.GROUP_CHANGE_SUBJECT;
|
|
344
|
+
msg.messageStubType = Types_1.WAMessageStubType.GROUP_CHANGE_SUBJECT;
|
|
793
345
|
msg.messageStubParameters = [child.attrs.subject];
|
|
794
346
|
break;
|
|
795
347
|
case 'description':
|
|
796
|
-
const description = getBinaryNodeChild(child, 'body')
|
|
797
|
-
msg.messageStubType = WAMessageStubType.GROUP_CHANGE_DESCRIPTION;
|
|
348
|
+
const description = (_d = (_c = (0, WABinary_1.getBinaryNodeChild)(child, 'body')) === null || _c === void 0 ? void 0 : _c.content) === null || _d === void 0 ? void 0 : _d.toString();
|
|
349
|
+
msg.messageStubType = Types_1.WAMessageStubType.GROUP_CHANGE_DESCRIPTION;
|
|
798
350
|
msg.messageStubParameters = description ? [description] : undefined;
|
|
799
351
|
break;
|
|
800
352
|
case 'announcement':
|
|
801
353
|
case 'not_announcement':
|
|
802
|
-
msg.messageStubType = WAMessageStubType.GROUP_CHANGE_ANNOUNCE;
|
|
803
|
-
msg.messageStubParameters = [child.tag === 'announcement' ? 'on' : 'off'];
|
|
354
|
+
msg.messageStubType = Types_1.WAMessageStubType.GROUP_CHANGE_ANNOUNCE;
|
|
355
|
+
msg.messageStubParameters = [(child.tag === 'announcement') ? 'on' : 'off'];
|
|
804
356
|
break;
|
|
805
357
|
case 'locked':
|
|
806
358
|
case 'unlocked':
|
|
807
|
-
msg.messageStubType = WAMessageStubType.GROUP_CHANGE_RESTRICT;
|
|
808
|
-
msg.messageStubParameters = [child.tag === 'locked' ? 'on' : 'off'];
|
|
359
|
+
msg.messageStubType = Types_1.WAMessageStubType.GROUP_CHANGE_RESTRICT;
|
|
360
|
+
msg.messageStubParameters = [(child.tag === 'locked') ? 'on' : 'off'];
|
|
809
361
|
break;
|
|
810
362
|
case 'invite':
|
|
811
|
-
msg.messageStubType = WAMessageStubType.GROUP_CHANGE_INVITE_LINK;
|
|
363
|
+
msg.messageStubType = Types_1.WAMessageStubType.GROUP_CHANGE_INVITE_LINK;
|
|
812
364
|
msg.messageStubParameters = [child.attrs.code];
|
|
813
365
|
break;
|
|
814
366
|
case 'member_add_mode':
|
|
815
367
|
const addMode = child.content;
|
|
816
368
|
if (addMode) {
|
|
817
|
-
msg.messageStubType = WAMessageStubType.GROUP_MEMBER_ADD_MODE;
|
|
369
|
+
msg.messageStubType = Types_1.WAMessageStubType.GROUP_MEMBER_ADD_MODE;
|
|
818
370
|
msg.messageStubParameters = [addMode.toString()];
|
|
819
371
|
}
|
|
820
372
|
break;
|
|
821
373
|
case 'membership_approval_mode':
|
|
822
|
-
const approvalMode = getBinaryNodeChild(child, 'group_join');
|
|
374
|
+
const approvalMode = (0, WABinary_1.getBinaryNodeChild)(child, 'group_join');
|
|
823
375
|
if (approvalMode) {
|
|
824
|
-
msg.messageStubType = WAMessageStubType.GROUP_MEMBERSHIP_JOIN_APPROVAL_MODE;
|
|
376
|
+
msg.messageStubType = Types_1.WAMessageStubType.GROUP_MEMBERSHIP_JOIN_APPROVAL_MODE;
|
|
825
377
|
msg.messageStubParameters = [approvalMode.attrs.state];
|
|
826
378
|
}
|
|
827
379
|
break;
|
|
828
380
|
case 'created_membership_requests':
|
|
829
|
-
msg.messageStubType = WAMessageStubType.GROUP_MEMBERSHIP_JOIN_APPROVAL_REQUEST_NON_ADMIN_ADD;
|
|
830
|
-
msg.messageStubParameters = [
|
|
831
|
-
JSON.stringify({ lid: affectedParticipantLid, pn: affectedParticipantPn }),
|
|
832
|
-
'created',
|
|
833
|
-
child.attrs.request_method
|
|
834
|
-
];
|
|
381
|
+
msg.messageStubType = Types_1.WAMessageStubType.GROUP_MEMBERSHIP_JOIN_APPROVAL_REQUEST_NON_ADMIN_ADD;
|
|
382
|
+
msg.messageStubParameters = [participantJid, 'created', child.attrs.request_method];
|
|
835
383
|
break;
|
|
836
384
|
case 'revoked_membership_requests':
|
|
837
|
-
const isDenied = areJidsSameUser(
|
|
838
|
-
|
|
839
|
-
msg.
|
|
840
|
-
|
|
841
|
-
JSON.stringify({ lid: affectedParticipantLid, pn: affectedParticipantPn }),
|
|
842
|
-
isDenied ? 'revoked' : 'rejected'
|
|
843
|
-
];
|
|
385
|
+
const isDenied = (0, WABinary_1.areJidsSameUser)(participantJid, participant);
|
|
386
|
+
msg.messageStubType = Types_1.WAMessageStubType.GROUP_MEMBERSHIP_JOIN_APPROVAL_REQUEST_NON_ADMIN_ADD;
|
|
387
|
+
msg.messageStubParameters = [participantJid, isDenied ? 'revoked' : 'rejected'];
|
|
388
|
+
break;
|
|
844
389
|
break;
|
|
390
|
+
default:
|
|
391
|
+
// console.log("BAILEYS-DEBUG:", JSON.stringify({ ...child, content: Buffer.isBuffer(child.content) ? child.content.toString() : child.content, participant }, null, 2))
|
|
845
392
|
}
|
|
846
393
|
};
|
|
847
|
-
const
|
|
848
|
-
const
|
|
849
|
-
const
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
if (areJidsSameUser(from, authState.creds.me.id) || areJidsSameUser(from, authState.creds.me.lid)) {
|
|
858
|
-
const deviceJids = devices.map(d => d.attrs.jid);
|
|
859
|
-
logger.info({ deviceJids }, 'got my own devices');
|
|
860
|
-
}
|
|
861
|
-
if (!devices.length) {
|
|
862
|
-
logger.debug({ from, tag }, 'no devices in notification, skipping');
|
|
863
|
-
return;
|
|
864
|
-
}
|
|
865
|
-
const decoded = [];
|
|
866
|
-
for (const d of devices) {
|
|
867
|
-
const jid = d.attrs.jid;
|
|
868
|
-
if (!jid)
|
|
869
|
-
continue;
|
|
870
|
-
const parts = jidDecode(jid);
|
|
871
|
-
if (!parts) {
|
|
872
|
-
logger.debug({ jid }, 'failed to decode device jid, skipping');
|
|
873
|
-
continue;
|
|
394
|
+
const handleNewsletterNotification = (id, node) => {
|
|
395
|
+
const messages = (0, WABinary_1.getBinaryNodeChild)(node, 'messages');
|
|
396
|
+
const message = (0, WABinary_1.getBinaryNodeChild)(messages, 'message');
|
|
397
|
+
const serverId = message.attrs.server_id;
|
|
398
|
+
const reactionsList = (0, WABinary_1.getBinaryNodeChild)(message, 'reactions');
|
|
399
|
+
const viewsList = (0, WABinary_1.getBinaryNodeChildren)(message, 'views_count');
|
|
400
|
+
if (reactionsList) {
|
|
401
|
+
const reactions = (0, WABinary_1.getBinaryNodeChildren)(reactionsList, 'reaction');
|
|
402
|
+
if (reactions.length === 0) {
|
|
403
|
+
ev.emit('newsletter.reaction', { id, 'server_id': serverId, reaction: { removed: true } });
|
|
874
404
|
}
|
|
875
|
-
|
|
405
|
+
reactions.forEach(item => {
|
|
406
|
+
var _a, _b;
|
|
407
|
+
ev.emit('newsletter.reaction', { id, 'server_id': serverId, reaction: { code: (_a = item.attrs) === null || _a === void 0 ? void 0 : _a.code, count: +((_b = item.attrs) === null || _b === void 0 ? void 0 : _b.count) } });
|
|
408
|
+
});
|
|
876
409
|
}
|
|
877
|
-
if (
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
410
|
+
if (viewsList.length) {
|
|
411
|
+
viewsList.forEach(item => {
|
|
412
|
+
ev.emit('newsletter.view', { id, 'server_id': serverId, count: +item.attrs.count });
|
|
413
|
+
});
|
|
414
|
+
}
|
|
415
|
+
};
|
|
416
|
+
const handleMexNewsletterNotification = (id, node) => {
|
|
417
|
+
var _a;
|
|
418
|
+
const operation = node === null || node === void 0 ? void 0 : node.attrs.op_name;
|
|
419
|
+
const content = JSON.parse((_a = node === null || node === void 0 ? void 0 : node.content) === null || _a === void 0 ? void 0 : _a.toString());
|
|
420
|
+
let contentPath;
|
|
421
|
+
if (operation === Types_1.MexOperations.PROMOTE || operation === Types_1.MexOperations.DEMOTE) {
|
|
422
|
+
let action;
|
|
423
|
+
if (operation === Types_1.MexOperations.PROMOTE) {
|
|
424
|
+
action = 'promote';
|
|
425
|
+
contentPath = content.data[Types_1.XWAPaths.PROMOTE];
|
|
885
426
|
}
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
await userDevicesCache?.del(user);
|
|
890
|
-
continue;
|
|
891
|
-
}
|
|
892
|
-
if (tag === 'remove') {
|
|
893
|
-
await signalRepository.deleteSession(entries.map(e => e.jid));
|
|
894
|
-
}
|
|
895
|
-
const existingCache = (await userDevicesCache?.get(user)) || [];
|
|
896
|
-
if (!existingCache.length) {
|
|
897
|
-
// No baseline yet; skip applying the delta so getUSyncDevices can
|
|
898
|
-
// later fetch the full device list. Caching just the notification
|
|
899
|
-
// entries would make a partial list look authoritative.
|
|
900
|
-
logger.debug({ user, tag }, 'device list not cached, deferring to USync refresh');
|
|
901
|
-
continue;
|
|
902
|
-
}
|
|
903
|
-
const affected = new Set(entries.map(e => e.device));
|
|
904
|
-
let updatedDevices;
|
|
905
|
-
switch (tag) {
|
|
906
|
-
case 'add':
|
|
907
|
-
logger.info({ deviceHash, count: entries.length }, 'devices added');
|
|
908
|
-
updatedDevices = [
|
|
909
|
-
...existingCache.filter(d => !affected.has(d.device)),
|
|
910
|
-
...entries.map(e => ({ user: e.user, server: e.server, device: e.device }))
|
|
911
|
-
];
|
|
912
|
-
break;
|
|
913
|
-
case 'remove':
|
|
914
|
-
logger.info({ deviceHash, count: entries.length }, 'devices removed');
|
|
915
|
-
updatedDevices = existingCache.filter(d => !affected.has(d.device));
|
|
916
|
-
break;
|
|
917
|
-
default:
|
|
918
|
-
logger.debug({ tag }, 'Unknown device list change tag');
|
|
919
|
-
continue;
|
|
920
|
-
}
|
|
921
|
-
if (updatedDevices.length === 0) {
|
|
922
|
-
await userDevicesCache?.del(user);
|
|
923
|
-
}
|
|
924
|
-
else {
|
|
925
|
-
await userDevicesCache?.set(user, updatedDevices);
|
|
926
|
-
}
|
|
427
|
+
if (operation === Types_1.MexOperations.DEMOTE) {
|
|
428
|
+
action = 'demote';
|
|
429
|
+
contentPath = content.data[Types_1.XWAPaths.DEMOTE];
|
|
927
430
|
}
|
|
928
|
-
|
|
431
|
+
ev.emit('newsletter-participants.update', { id, author: contentPath.actor.pn, user: contentPath.user.pn, new_role: contentPath.user_new_role, action });
|
|
432
|
+
}
|
|
433
|
+
if (operation === Types_1.MexOperations.UPDATE) {
|
|
434
|
+
contentPath = content.data[Types_1.XWAPaths.METADATA_UPDATE];
|
|
435
|
+
ev.emit('newsletter-settings.update', { id, update: contentPath.thread_metadata.settings });
|
|
436
|
+
}
|
|
929
437
|
};
|
|
930
438
|
const processNotification = async (node) => {
|
|
439
|
+
var _a, _b;
|
|
931
440
|
const result = {};
|
|
932
|
-
const [child] = getAllBinaryNodeChildren(node);
|
|
441
|
+
const [child] = (0, WABinary_1.getAllBinaryNodeChildren)(node);
|
|
933
442
|
const nodeType = node.attrs.type;
|
|
934
|
-
const from = jidNormalizedUser(node.attrs.from);
|
|
443
|
+
const from = (0, WABinary_1.jidNormalizedUser)(node.attrs.from);
|
|
935
444
|
switch (nodeType) {
|
|
445
|
+
case 'privacy_token':
|
|
446
|
+
const tokenList = (0, WABinary_1.getBinaryNodeChildren)(child, 'token');
|
|
447
|
+
for (const { attrs, content } of tokenList) {
|
|
448
|
+
const jid = attrs.jid;
|
|
449
|
+
ev.emit('chats.update', [
|
|
450
|
+
{
|
|
451
|
+
id: jid,
|
|
452
|
+
tcToken: content
|
|
453
|
+
}
|
|
454
|
+
]);
|
|
455
|
+
logger.debug({ jid }, 'got privacy token update');
|
|
456
|
+
}
|
|
457
|
+
break;
|
|
936
458
|
case 'newsletter':
|
|
937
|
-
|
|
459
|
+
handleNewsletterNotification(node.attrs.from, child);
|
|
938
460
|
break;
|
|
939
461
|
case 'mex':
|
|
940
|
-
|
|
462
|
+
handleMexNewsletterNotification(node.attrs.from, child);
|
|
941
463
|
break;
|
|
942
464
|
case 'w:gp2':
|
|
943
|
-
|
|
944
|
-
handleGroupNotification(node, child, result);
|
|
465
|
+
handleGroupNotification(node.attrs.participant, child, result);
|
|
945
466
|
break;
|
|
946
467
|
case 'mediaretry':
|
|
947
|
-
const event = decodeMediaRetryNode(node);
|
|
468
|
+
const event = (0, Utils_1.decodeMediaRetryNode)(node);
|
|
948
469
|
ev.emit('messages.media-update', [event]);
|
|
949
470
|
break;
|
|
950
471
|
case 'encrypt':
|
|
951
472
|
await handleEncryptNotification(node);
|
|
952
473
|
break;
|
|
953
474
|
case 'devices':
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
logger.error({ error, node }, 'failed to handle devices notification');
|
|
475
|
+
const devices = (0, WABinary_1.getBinaryNodeChildren)(child, 'device');
|
|
476
|
+
if ((0, WABinary_1.areJidsSameUser)(child.attrs.jid, authState.creds.me.id)) {
|
|
477
|
+
const deviceJids = devices.map(d => d.attrs.jid);
|
|
478
|
+
logger.info({ deviceJids }, 'got my own devices');
|
|
959
479
|
}
|
|
960
480
|
break;
|
|
961
481
|
case 'server_sync':
|
|
962
|
-
const update = getBinaryNodeChild(node, 'collection');
|
|
482
|
+
const update = (0, WABinary_1.getBinaryNodeChild)(node, 'collection');
|
|
963
483
|
if (update) {
|
|
964
484
|
const name = update.attrs.name;
|
|
965
485
|
await resyncAppState([name], false);
|
|
966
486
|
}
|
|
967
487
|
break;
|
|
968
488
|
case 'picture':
|
|
969
|
-
const setPicture = getBinaryNodeChild(node, 'set');
|
|
970
|
-
const delPicture = getBinaryNodeChild(node, 'delete');
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
{
|
|
974
|
-
id: jidNormalizedUser(node?.attrs?.from) || (setPicture || delPicture)?.attrs?.hash || '',
|
|
489
|
+
const setPicture = (0, WABinary_1.getBinaryNodeChild)(node, 'set');
|
|
490
|
+
const delPicture = (0, WABinary_1.getBinaryNodeChild)(node, 'delete');
|
|
491
|
+
ev.emit('contacts.update', [{
|
|
492
|
+
id: from || ((_b = (_a = (setPicture || delPicture)) === null || _a === void 0 ? void 0 : _a.attrs) === null || _b === void 0 ? void 0 : _b.hash) || '',
|
|
975
493
|
imgUrl: setPicture ? 'changed' : 'removed'
|
|
976
|
-
}
|
|
977
|
-
|
|
978
|
-
if (isJidGroup(from)) {
|
|
494
|
+
}]);
|
|
495
|
+
if ((0, WABinary_1.isJidGroup)(from)) {
|
|
979
496
|
const node = setPicture || delPicture;
|
|
980
|
-
result.messageStubType = WAMessageStubType.GROUP_CHANGE_ICON;
|
|
497
|
+
result.messageStubType = Types_1.WAMessageStubType.GROUP_CHANGE_ICON;
|
|
981
498
|
if (setPicture) {
|
|
982
499
|
result.messageStubParameters = [setPicture.attrs.id];
|
|
983
500
|
}
|
|
984
|
-
result.participant = node
|
|
501
|
+
result.participant = node === null || node === void 0 ? void 0 : node.attrs.author;
|
|
985
502
|
result.key = {
|
|
986
|
-
...
|
|
987
|
-
participant: setPicture
|
|
503
|
+
...result.key || {},
|
|
504
|
+
participant: setPicture === null || setPicture === void 0 ? void 0 : setPicture.attrs.author
|
|
988
505
|
};
|
|
989
506
|
}
|
|
990
507
|
break;
|
|
@@ -998,48 +515,44 @@ export const makeMessagesRecvSocket = (config) => {
|
|
|
998
515
|
...authState.creds.accountSettings,
|
|
999
516
|
defaultDisappearingMode: {
|
|
1000
517
|
ephemeralExpiration: newDuration,
|
|
1001
|
-
ephemeralSettingTimestamp: timestamp
|
|
1002
|
-
}
|
|
518
|
+
ephemeralSettingTimestamp: timestamp,
|
|
519
|
+
},
|
|
1003
520
|
}
|
|
1004
521
|
});
|
|
1005
522
|
}
|
|
1006
523
|
else if (child.tag === 'blocklist') {
|
|
1007
|
-
const blocklists = getBinaryNodeChildren(child, 'item');
|
|
524
|
+
const blocklists = (0, WABinary_1.getBinaryNodeChildren)(child, 'item');
|
|
1008
525
|
for (const { attrs } of blocklists) {
|
|
1009
526
|
const blocklist = [attrs.jid];
|
|
1010
|
-
const type = attrs.action === 'block' ? 'add' : 'remove';
|
|
527
|
+
const type = (attrs.action === 'block') ? 'add' : 'remove';
|
|
1011
528
|
ev.emit('blocklist.update', { blocklist, type });
|
|
1012
529
|
}
|
|
1013
530
|
}
|
|
1014
531
|
break;
|
|
1015
532
|
case 'link_code_companion_reg':
|
|
1016
|
-
const linkCodeCompanionReg = getBinaryNodeChild(node, 'link_code_companion_reg');
|
|
1017
|
-
const ref = toRequiredBuffer(getBinaryNodeChildBuffer(linkCodeCompanionReg, 'link_code_pairing_ref'));
|
|
1018
|
-
const primaryIdentityPublicKey = toRequiredBuffer(getBinaryNodeChildBuffer(linkCodeCompanionReg, 'primary_identity_pub'));
|
|
1019
|
-
const primaryEphemeralPublicKeyWrapped = toRequiredBuffer(getBinaryNodeChildBuffer(linkCodeCompanionReg, 'link_code_pairing_wrapped_primary_ephemeral_pub'));
|
|
533
|
+
const linkCodeCompanionReg = (0, WABinary_1.getBinaryNodeChild)(node, 'link_code_companion_reg');
|
|
534
|
+
const ref = toRequiredBuffer((0, WABinary_1.getBinaryNodeChildBuffer)(linkCodeCompanionReg, 'link_code_pairing_ref'));
|
|
535
|
+
const primaryIdentityPublicKey = toRequiredBuffer((0, WABinary_1.getBinaryNodeChildBuffer)(linkCodeCompanionReg, 'primary_identity_pub'));
|
|
536
|
+
const primaryEphemeralPublicKeyWrapped = toRequiredBuffer((0, WABinary_1.getBinaryNodeChildBuffer)(linkCodeCompanionReg, 'link_code_pairing_wrapped_primary_ephemeral_pub'));
|
|
1020
537
|
const codePairingPublicKey = await decipherLinkPublicKey(primaryEphemeralPublicKeyWrapped);
|
|
1021
|
-
const companionSharedKey = Curve.sharedKey(authState.creds.pairingEphemeralKeyPair.private, codePairingPublicKey);
|
|
1022
|
-
const random = randomBytes(32);
|
|
1023
|
-
const linkCodeSalt = randomBytes(32);
|
|
1024
|
-
const linkCodePairingExpanded = hkdf(companionSharedKey, 32, {
|
|
538
|
+
const companionSharedKey = Utils_1.Curve.sharedKey(authState.creds.pairingEphemeralKeyPair.private, codePairingPublicKey);
|
|
539
|
+
const random = (0, crypto_1.randomBytes)(32);
|
|
540
|
+
const linkCodeSalt = (0, crypto_1.randomBytes)(32);
|
|
541
|
+
const linkCodePairingExpanded = await (0, Utils_1.hkdf)(companionSharedKey, 32, {
|
|
1025
542
|
salt: linkCodeSalt,
|
|
1026
543
|
info: 'link_code_pairing_key_bundle_encryption_key'
|
|
1027
544
|
});
|
|
1028
|
-
const encryptPayload = Buffer.concat([
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
random
|
|
1032
|
-
]);
|
|
1033
|
-
const encryptIv = randomBytes(12);
|
|
1034
|
-
const encrypted = aesEncryptGCM(encryptPayload, linkCodePairingExpanded, encryptIv, Buffer.alloc(0));
|
|
545
|
+
const encryptPayload = Buffer.concat([Buffer.from(authState.creds.signedIdentityKey.public), primaryIdentityPublicKey, random]);
|
|
546
|
+
const encryptIv = (0, crypto_1.randomBytes)(12);
|
|
547
|
+
const encrypted = (0, Utils_1.aesEncryptGCM)(encryptPayload, linkCodePairingExpanded, encryptIv, Buffer.alloc(0));
|
|
1035
548
|
const encryptedPayload = Buffer.concat([linkCodeSalt, encryptIv, encrypted]);
|
|
1036
|
-
const identitySharedKey = Curve.sharedKey(authState.creds.signedIdentityKey.private, primaryIdentityPublicKey);
|
|
549
|
+
const identitySharedKey = Utils_1.Curve.sharedKey(authState.creds.signedIdentityKey.private, primaryIdentityPublicKey);
|
|
1037
550
|
const identityPayload = Buffer.concat([companionSharedKey, identitySharedKey, random]);
|
|
1038
|
-
authState.creds.advSecretKey =
|
|
551
|
+
authState.creds.advSecretKey = (await (0, Utils_1.hkdf)(identityPayload, 32, { info: 'adv_secret' })).toString('base64');
|
|
1039
552
|
await query({
|
|
1040
553
|
tag: 'iq',
|
|
1041
554
|
attrs: {
|
|
1042
|
-
to: S_WHATSAPP_NET,
|
|
555
|
+
to: WABinary_1.S_WHATSAPP_NET,
|
|
1043
556
|
type: 'set',
|
|
1044
557
|
id: sock.generateMessageTag(),
|
|
1045
558
|
xmlns: 'md'
|
|
@@ -1049,7 +562,7 @@ export const makeMessagesRecvSocket = (config) => {
|
|
|
1049
562
|
tag: 'link_code_companion_reg',
|
|
1050
563
|
attrs: {
|
|
1051
564
|
jid: authState.creds.me.id,
|
|
1052
|
-
stage: 'companion_finish'
|
|
565
|
+
stage: 'companion_finish',
|
|
1053
566
|
},
|
|
1054
567
|
content: [
|
|
1055
568
|
{
|
|
@@ -1073,208 +586,53 @@ export const makeMessagesRecvSocket = (config) => {
|
|
|
1073
586
|
});
|
|
1074
587
|
authState.creds.registered = true;
|
|
1075
588
|
ev.emit('creds.update', authState.creds);
|
|
1076
|
-
break;
|
|
1077
|
-
case 'privacy_token':
|
|
1078
|
-
await handlePrivacyTokenNotification(node);
|
|
1079
|
-
break;
|
|
1080
589
|
}
|
|
1081
590
|
if (Object.keys(result).length) {
|
|
1082
591
|
return result;
|
|
1083
592
|
}
|
|
1084
593
|
};
|
|
1085
|
-
/**
|
|
1086
|
-
* In-memory cache of storage JIDs with stored tctokens, seeded from the persisted index.
|
|
1087
|
-
* Used to coalesce writes during a session; pruning always re-reads the persisted index
|
|
1088
|
-
* to cover writes made by other layers (e.g. history sync).
|
|
1089
|
-
*/
|
|
1090
|
-
const tcTokenKnownJids = new Set();
|
|
1091
|
-
const tcTokenIndexLoaded = (async () => {
|
|
1092
|
-
try {
|
|
1093
|
-
const jids = await readTcTokenIndex(authState.keys);
|
|
1094
|
-
for (const jid of jids)
|
|
1095
|
-
tcTokenKnownJids.add(jid);
|
|
1096
|
-
logger.debug({ count: tcTokenKnownJids.size }, 'loaded tctoken index');
|
|
1097
|
-
}
|
|
1098
|
-
catch (err) {
|
|
1099
|
-
logger.warn({ err: err?.message }, 'failed to load tctoken index');
|
|
1100
|
-
}
|
|
1101
|
-
})();
|
|
1102
|
-
let tcTokenIndexTimer;
|
|
1103
|
-
async function flushTcTokenIndex() {
|
|
1104
|
-
if (tcTokenIndexTimer) {
|
|
1105
|
-
clearTimeout(tcTokenIndexTimer);
|
|
1106
|
-
tcTokenIndexTimer = undefined;
|
|
1107
|
-
}
|
|
1108
|
-
// Merge with whatever is already persisted so we don't clobber writes from other
|
|
1109
|
-
// paths (history sync, concurrent sessions on the same store).
|
|
1110
|
-
const write = await buildMergedTcTokenIndexWrite(authState.keys, tcTokenKnownJids);
|
|
1111
|
-
return authState.keys.set({ tctoken: write });
|
|
1112
|
-
}
|
|
1113
|
-
function scheduleTcTokenIndexSave() {
|
|
1114
|
-
if (tcTokenIndexTimer) {
|
|
1115
|
-
clearTimeout(tcTokenIndexTimer);
|
|
1116
|
-
}
|
|
1117
|
-
tcTokenIndexTimer = setTimeout(() => {
|
|
1118
|
-
tcTokenIndexTimer = undefined;
|
|
1119
|
-
flushTcTokenIndex().catch(err => {
|
|
1120
|
-
logger.warn({ err: err?.message }, 'failed to save tctoken index');
|
|
1121
|
-
});
|
|
1122
|
-
}, 5000);
|
|
1123
|
-
}
|
|
1124
|
-
function trackTcTokenJid(jid) {
|
|
1125
|
-
if (jid && jid !== TC_TOKEN_INDEX_KEY && !tcTokenKnownJids.has(jid)) {
|
|
1126
|
-
tcTokenKnownJids.add(jid);
|
|
1127
|
-
scheduleTcTokenIndexSave();
|
|
1128
|
-
}
|
|
1129
|
-
}
|
|
1130
|
-
const handlePrivacyTokenNotification = async (node) => {
|
|
1131
|
-
const tokensNode = getBinaryNodeChild(node, 'tokens');
|
|
1132
|
-
if (!tokensNode)
|
|
1133
|
-
return;
|
|
1134
|
-
const from = jidNormalizedUser(node.attrs.from);
|
|
1135
|
-
// WA Web uses: senderLid ?? toLid(from) for the storage key
|
|
1136
|
-
// The sender_lid attribute provides the LID directly when available
|
|
1137
|
-
const senderLid = node.attrs.sender_lid && isLidUser(jidNormalizedUser(node.attrs.sender_lid))
|
|
1138
|
-
? jidNormalizedUser(node.attrs.sender_lid)
|
|
1139
|
-
: undefined;
|
|
1140
|
-
const fallbackJid = senderLid ?? (await resolveTcTokenJid(from, getLIDForPN));
|
|
1141
|
-
logger.debug({ from, storageJid: fallbackJid }, 'processing privacy token notification');
|
|
1142
|
-
await storeTcTokensFromIqResult({
|
|
1143
|
-
result: node,
|
|
1144
|
-
fallbackJid,
|
|
1145
|
-
keys: authState.keys,
|
|
1146
|
-
getLIDForPN,
|
|
1147
|
-
onNewJidStored: trackTcTokenJid
|
|
1148
|
-
});
|
|
1149
|
-
};
|
|
1150
594
|
async function decipherLinkPublicKey(data) {
|
|
1151
595
|
const buffer = toRequiredBuffer(data);
|
|
1152
596
|
const salt = buffer.slice(0, 32);
|
|
1153
|
-
const secretKey = await derivePairingCodeKey(authState.creds.pairingCode, salt);
|
|
597
|
+
const secretKey = await (0, Utils_1.derivePairingCodeKey)(authState.creds.pairingCode, salt);
|
|
1154
598
|
const iv = buffer.slice(32, 48);
|
|
1155
599
|
const payload = buffer.slice(48, 80);
|
|
1156
|
-
return aesDecryptCTR(payload, secretKey, iv);
|
|
600
|
+
return (0, Utils_1.aesDecryptCTR)(payload, secretKey, iv);
|
|
1157
601
|
}
|
|
1158
602
|
function toRequiredBuffer(data) {
|
|
1159
603
|
if (data === undefined) {
|
|
1160
|
-
throw new Boom('Invalid buffer', { statusCode: 400 });
|
|
604
|
+
throw new boom_1.Boom('Invalid buffer', { statusCode: 400 });
|
|
1161
605
|
}
|
|
1162
606
|
return data instanceof Buffer ? data : Buffer.from(data);
|
|
1163
607
|
}
|
|
1164
|
-
const willSendMessageAgain =
|
|
608
|
+
const willSendMessageAgain = (id, participant) => {
|
|
1165
609
|
const key = `${id}:${participant}`;
|
|
1166
|
-
const retryCount =
|
|
610
|
+
const retryCount = msgRetryCache.get(key) || 0;
|
|
1167
611
|
return retryCount < maxMsgRetryCount;
|
|
1168
612
|
};
|
|
1169
|
-
const updateSendMessageAgainCount =
|
|
613
|
+
const updateSendMessageAgainCount = (id, participant) => {
|
|
1170
614
|
const key = `${id}:${participant}`;
|
|
1171
|
-
const newValue = (
|
|
1172
|
-
|
|
615
|
+
const newValue = (msgRetryCache.get(key) || 0) + 1;
|
|
616
|
+
msgRetryCache.set(key, newValue);
|
|
1173
617
|
};
|
|
1174
|
-
const sendMessagesAgain = async (key, ids, retryNode
|
|
618
|
+
const sendMessagesAgain = async (key, ids, retryNode) => {
|
|
619
|
+
var _a;
|
|
620
|
+
// todo: implement a cache to store the last 256 sent messages (copy whatsmeow)
|
|
621
|
+
const msgs = await Promise.all(ids.map(id => getMessage({ ...key, id })));
|
|
1175
622
|
const remoteJid = key.remoteJid;
|
|
1176
623
|
const participant = key.participant || remoteJid;
|
|
1177
|
-
const retryCount = +retryNode.attrs.count || 1;
|
|
1178
|
-
const msgId = ids[0];
|
|
1179
|
-
// Try to get messages from cache first, then fallback to getMessage
|
|
1180
|
-
const msgs = [];
|
|
1181
|
-
for (const id of ids) {
|
|
1182
|
-
let msg;
|
|
1183
|
-
// Try to get from retry cache first if enabled
|
|
1184
|
-
if (messageRetryManager) {
|
|
1185
|
-
const cachedMsg = messageRetryManager.getRecentMessage(remoteJid, id);
|
|
1186
|
-
if (cachedMsg) {
|
|
1187
|
-
msg = cachedMsg.message;
|
|
1188
|
-
logger.debug({ jid: remoteJid, id }, 'found message in retry cache');
|
|
1189
|
-
// Mark retry as successful since we found the message
|
|
1190
|
-
messageRetryManager.markRetrySuccess(id);
|
|
1191
|
-
}
|
|
1192
|
-
}
|
|
1193
|
-
// Fallback to getMessage if not found in cache
|
|
1194
|
-
if (!msg) {
|
|
1195
|
-
msg = await getMessage({ ...key, id });
|
|
1196
|
-
if (msg) {
|
|
1197
|
-
logger.debug({ jid: remoteJid, id }, 'found message via getMessage');
|
|
1198
|
-
// Also mark as successful if found via getMessage
|
|
1199
|
-
if (messageRetryManager) {
|
|
1200
|
-
messageRetryManager.markRetrySuccess(id);
|
|
1201
|
-
}
|
|
1202
|
-
}
|
|
1203
|
-
}
|
|
1204
|
-
msgs.push(msg);
|
|
1205
|
-
}
|
|
1206
624
|
// if it's the primary jid sending the request
|
|
1207
625
|
// just re-send the message to everyone
|
|
1208
626
|
// prevents the first message decryption failure
|
|
1209
|
-
const sendToAll = !jidDecode(participant)
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
const bundle = extractE2ESessionFromRetryReceipt(receiptNode);
|
|
1213
|
-
if (bundle) {
|
|
1214
|
-
try {
|
|
1215
|
-
await signalRepository.injectE2ESession({ jid: participant, session: bundle });
|
|
1216
|
-
injectedFromBundle = true;
|
|
1217
|
-
logger.debug({ participant, retryCount }, 'injected session from retry receipt key bundle');
|
|
1218
|
-
}
|
|
1219
|
-
catch (error) {
|
|
1220
|
-
logger.warn({ error, participant }, 'failed to inject session from retry receipt');
|
|
1221
|
-
}
|
|
1222
|
-
}
|
|
1223
|
-
if (!injectedFromBundle) {
|
|
1224
|
-
const receivedRegId = getBinaryNodeChildUInt(receiptNode, 'registration', 4);
|
|
1225
|
-
if (typeof receivedRegId === 'number' && Number.isInteger(receivedRegId)) {
|
|
1226
|
-
const info = await signalRepository.getSessionInfo(participant);
|
|
1227
|
-
if (info && info.registrationId !== 0 && info.registrationId !== receivedRegId) {
|
|
1228
|
-
logger.info({ participant, stored: info.registrationId, received: receivedRegId }, 'reg id mismatch on retry without bundle, deleting session');
|
|
1229
|
-
await authState.keys.set({ session: { [sessionId]: null } });
|
|
1230
|
-
}
|
|
1231
|
-
}
|
|
1232
|
-
}
|
|
1233
|
-
const BASE_KEY_CHECK_RETRY = 2;
|
|
1234
|
-
if (msgId && messageRetryManager) {
|
|
1235
|
-
const info = await signalRepository.getSessionInfo(participant);
|
|
1236
|
-
if (info) {
|
|
1237
|
-
if (retryCount === BASE_KEY_CHECK_RETRY) {
|
|
1238
|
-
messageRetryManager.saveBaseKey(sessionId, msgId, info.baseKey);
|
|
1239
|
-
}
|
|
1240
|
-
else if (retryCount > BASE_KEY_CHECK_RETRY) {
|
|
1241
|
-
if (messageRetryManager.hasSameBaseKey(sessionId, msgId, info.baseKey)) {
|
|
1242
|
-
logger.warn({ participant, retryCount }, 'base key collision on retry, forcing fresh session');
|
|
1243
|
-
await authState.keys.set({ session: { [sessionId]: null } });
|
|
1244
|
-
}
|
|
1245
|
-
messageRetryManager.deleteBaseKey(sessionId, msgId);
|
|
1246
|
-
}
|
|
1247
|
-
}
|
|
1248
|
-
}
|
|
1249
|
-
let shouldRecreateSession = false;
|
|
1250
|
-
let recreateReason = '';
|
|
1251
|
-
if (enableAutoSessionRecreation && messageRetryManager && retryCount > 1 && !injectedFromBundle) {
|
|
1252
|
-
try {
|
|
1253
|
-
const hasSession = await signalRepository.validateSession(participant);
|
|
1254
|
-
const result = messageRetryManager.shouldRecreateSession(participant, hasSession.exists);
|
|
1255
|
-
shouldRecreateSession = result.recreate;
|
|
1256
|
-
recreateReason = result.reason;
|
|
1257
|
-
if (shouldRecreateSession) {
|
|
1258
|
-
logger.debug({ participant, retryCount, reason: recreateReason }, 'recreating session for outgoing retry');
|
|
1259
|
-
await authState.keys.set({ session: { [sessionId]: null } });
|
|
1260
|
-
}
|
|
1261
|
-
}
|
|
1262
|
-
catch (error) {
|
|
1263
|
-
logger.warn({ error, participant }, 'failed to check session recreation for outgoing retry');
|
|
1264
|
-
}
|
|
1265
|
-
}
|
|
1266
|
-
if (!injectedFromBundle) {
|
|
1267
|
-
await assertSessions([participant], true);
|
|
1268
|
-
}
|
|
1269
|
-
if (isJidGroup(remoteJid)) {
|
|
627
|
+
const sendToAll = !((_a = (0, WABinary_1.jidDecode)(participant)) === null || _a === void 0 ? void 0 : _a.device);
|
|
628
|
+
await assertSessions([participant], true);
|
|
629
|
+
if ((0, WABinary_1.isJidGroup)(remoteJid)) {
|
|
1270
630
|
await authState.keys.set({ 'sender-key-memory': { [remoteJid]: null } });
|
|
1271
631
|
}
|
|
1272
|
-
logger.debug({ participant, sendToAll
|
|
632
|
+
logger.debug({ participant, sendToAll }, 'forced new session for retry recp');
|
|
1273
633
|
for (const [i, msg] of msgs.entries()) {
|
|
1274
|
-
if (
|
|
1275
|
-
|
|
1276
|
-
if (msg && (await willSendMessageAgain(ids[i], participant))) {
|
|
1277
|
-
await updateSendMessageAgainCount(ids[i], participant);
|
|
634
|
+
if (msg) {
|
|
635
|
+
updateSendMessageAgainCount(ids[i], participant);
|
|
1278
636
|
const msgRelayOpts = { messageId: ids[i] };
|
|
1279
637
|
if (sendToAll) {
|
|
1280
638
|
msgRelayOpts.useUserDevicesCache = false;
|
|
@@ -1293,10 +651,11 @@ export const makeMessagesRecvSocket = (config) => {
|
|
|
1293
651
|
}
|
|
1294
652
|
};
|
|
1295
653
|
const handleReceipt = async (node) => {
|
|
654
|
+
var _a, _b;
|
|
1296
655
|
const { attrs, content } = node;
|
|
1297
656
|
const isLid = attrs.from.includes('lid');
|
|
1298
|
-
const isNodeFromMe = areJidsSameUser(attrs.participant || attrs.from, isLid ? authState.creds.me
|
|
1299
|
-
const remoteJid = !isNodeFromMe || isJidGroup(attrs.from) ? attrs.from : attrs.recipient;
|
|
657
|
+
const isNodeFromMe = (0, WABinary_1.areJidsSameUser)(attrs.participant || attrs.from, isLid ? (_a = authState.creds.me) === null || _a === void 0 ? void 0 : _a.lid : (_b = authState.creds.me) === null || _b === void 0 ? void 0 : _b.id);
|
|
658
|
+
const remoteJid = !isNodeFromMe || (0, WABinary_1.isJidGroup)(attrs.from) ? attrs.from : attrs.recipient;
|
|
1300
659
|
const fromMe = !attrs.recipient || ((attrs.type === 'retry' || attrs.type === 'sender') && isNodeFromMe);
|
|
1301
660
|
const key = {
|
|
1302
661
|
remoteJid,
|
|
@@ -1304,26 +663,33 @@ export const makeMessagesRecvSocket = (config) => {
|
|
|
1304
663
|
fromMe,
|
|
1305
664
|
participant: attrs.participant
|
|
1306
665
|
};
|
|
666
|
+
if (shouldIgnoreJid(remoteJid) && remoteJid !== '@s.whatsapp.net') {
|
|
667
|
+
logger.debug({ remoteJid }, 'ignoring receipt from jid');
|
|
668
|
+
await sendMessageAck(node);
|
|
669
|
+
return;
|
|
670
|
+
}
|
|
1307
671
|
const ids = [attrs.id];
|
|
1308
672
|
if (Array.isArray(content)) {
|
|
1309
|
-
const items = getBinaryNodeChildren(content[0], 'item');
|
|
673
|
+
const items = (0, WABinary_1.getBinaryNodeChildren)(content[0], 'item');
|
|
1310
674
|
ids.push(...items.map(i => i.attrs.id));
|
|
1311
675
|
}
|
|
1312
676
|
try {
|
|
1313
677
|
await Promise.all([
|
|
1314
|
-
|
|
1315
|
-
const status = getStatusFromReceiptType(attrs.type);
|
|
678
|
+
processingMutex.mutex(async () => {
|
|
679
|
+
const status = (0, Utils_1.getStatusFromReceiptType)(attrs.type);
|
|
1316
680
|
if (typeof status !== 'undefined' &&
|
|
681
|
+
(
|
|
1317
682
|
// basically, we only want to know when a message from us has been delivered to/read by the other person
|
|
1318
683
|
// or another device of ours has read some messages
|
|
1319
|
-
|
|
1320
|
-
|
|
684
|
+
status >= WAProto_1.proto.WebMessageInfo.Status.SERVER_ACK ||
|
|
685
|
+
!isNodeFromMe)) {
|
|
686
|
+
if ((0, WABinary_1.isJidGroup)(remoteJid) || (0, WABinary_1.isJidStatusBroadcast)(remoteJid)) {
|
|
1321
687
|
if (attrs.participant) {
|
|
1322
|
-
const updateKey = status === proto.WebMessageInfo.Status.DELIVERY_ACK ? 'receiptTimestamp' : 'readTimestamp';
|
|
688
|
+
const updateKey = status === WAProto_1.proto.WebMessageInfo.Status.DELIVERY_ACK ? 'receiptTimestamp' : 'readTimestamp';
|
|
1323
689
|
ev.emit('message-receipt.update', ids.map(id => ({
|
|
1324
690
|
key: { ...key, id },
|
|
1325
691
|
receipt: {
|
|
1326
|
-
userJid: jidNormalizedUser(attrs.participant),
|
|
692
|
+
userJid: (0, WABinary_1.jidNormalizedUser)(attrs.participant),
|
|
1327
693
|
[updateKey]: +attrs.t
|
|
1328
694
|
}
|
|
1329
695
|
})));
|
|
@@ -1332,23 +698,22 @@ export const makeMessagesRecvSocket = (config) => {
|
|
|
1332
698
|
else {
|
|
1333
699
|
ev.emit('messages.update', ids.map(id => ({
|
|
1334
700
|
key: { ...key, id },
|
|
1335
|
-
update: { status
|
|
701
|
+
update: { status }
|
|
1336
702
|
})));
|
|
1337
703
|
}
|
|
1338
704
|
}
|
|
1339
705
|
if (attrs.type === 'retry') {
|
|
1340
706
|
// correctly set who is asking for the retry
|
|
1341
707
|
key.participant = key.participant || attrs.from;
|
|
1342
|
-
const retryNode = getBinaryNodeChild(node, 'retry');
|
|
1343
|
-
if (
|
|
708
|
+
const retryNode = (0, WABinary_1.getBinaryNodeChild)(node, 'retry');
|
|
709
|
+
if (willSendMessageAgain(ids[0], key.participant)) {
|
|
1344
710
|
if (key.fromMe) {
|
|
1345
711
|
try {
|
|
1346
|
-
await updateSendMessageAgainCount(ids[0], key.participant);
|
|
1347
712
|
logger.debug({ attrs, key }, 'recv retry request');
|
|
1348
|
-
await sendMessagesAgain(key, ids, retryNode
|
|
713
|
+
await sendMessagesAgain(key, ids, retryNode);
|
|
1349
714
|
}
|
|
1350
715
|
catch (error) {
|
|
1351
|
-
logger.error({ key, ids, trace: error
|
|
716
|
+
logger.error({ key, ids, trace: error.stack }, 'error in sending message again');
|
|
1352
717
|
}
|
|
1353
718
|
}
|
|
1354
719
|
else {
|
|
@@ -1363,338 +728,269 @@ export const makeMessagesRecvSocket = (config) => {
|
|
|
1363
728
|
]);
|
|
1364
729
|
}
|
|
1365
730
|
finally {
|
|
1366
|
-
await sendMessageAck(node)
|
|
731
|
+
await sendMessageAck(node);
|
|
1367
732
|
}
|
|
1368
733
|
};
|
|
1369
734
|
const handleNotification = async (node) => {
|
|
1370
735
|
const remoteJid = node.attrs.from;
|
|
736
|
+
if (shouldIgnoreJid(remoteJid) && remoteJid !== '@s.whatsapp.net') {
|
|
737
|
+
logger.debug({ remoteJid, id: node.attrs.id }, 'ignored notification');
|
|
738
|
+
await sendMessageAck(node);
|
|
739
|
+
return;
|
|
740
|
+
}
|
|
1371
741
|
try {
|
|
1372
742
|
await Promise.all([
|
|
1373
|
-
|
|
743
|
+
processingMutex.mutex(async () => {
|
|
744
|
+
var _a;
|
|
1374
745
|
const msg = await processNotification(node);
|
|
1375
746
|
if (msg) {
|
|
1376
|
-
const fromMe = areJidsSameUser(node.attrs.participant || remoteJid, authState.creds.me.id);
|
|
1377
|
-
const { senderAlt: participantAlt, addressingMode } = extractAddressingContext(node);
|
|
747
|
+
const fromMe = (0, WABinary_1.areJidsSameUser)(node.attrs.participant || remoteJid, authState.creds.me.id);
|
|
1378
748
|
msg.key = {
|
|
1379
749
|
remoteJid,
|
|
1380
750
|
fromMe,
|
|
1381
751
|
participant: node.attrs.participant,
|
|
1382
|
-
participantAlt,
|
|
1383
|
-
participantUsername: node.attrs.participant_username,
|
|
1384
|
-
addressingMode,
|
|
1385
752
|
id: node.attrs.id,
|
|
1386
753
|
...(msg.key || {})
|
|
1387
754
|
};
|
|
1388
|
-
msg.participant
|
|
755
|
+
(_a = msg.participant) !== null && _a !== void 0 ? _a : (msg.participant = node.attrs.participant);
|
|
1389
756
|
msg.messageTimestamp = +node.attrs.t;
|
|
1390
|
-
const fullMsg = proto.WebMessageInfo.fromObject(msg);
|
|
757
|
+
const fullMsg = WAProto_1.proto.WebMessageInfo.fromObject(msg);
|
|
1391
758
|
await upsertMessage(fullMsg, 'append');
|
|
1392
759
|
}
|
|
1393
760
|
})
|
|
1394
761
|
]);
|
|
1395
762
|
}
|
|
1396
763
|
finally {
|
|
1397
|
-
await sendMessageAck(node)
|
|
764
|
+
await sendMessageAck(node);
|
|
1398
765
|
}
|
|
1399
766
|
};
|
|
1400
767
|
const handleMessage = async (node) => {
|
|
1401
|
-
|
|
768
|
+
var _a, _b, _c;
|
|
769
|
+
if (shouldIgnoreJid(node.attrs.from) && node.attrs.from !== '@s.whatsapp.net') {
|
|
770
|
+
logger.debug({ key: node.attrs.key }, 'ignored message');
|
|
771
|
+
await sendMessageAck(node);
|
|
772
|
+
return;
|
|
773
|
+
}
|
|
774
|
+
const encNode = (0, WABinary_1.getBinaryNodeChild)(node, 'enc');
|
|
1402
775
|
// TODO: temporary fix for crashes and issues resulting of failed msmsg decryption
|
|
1403
|
-
if (encNode
|
|
776
|
+
if (encNode && encNode.attrs.type === 'msmsg') {
|
|
1404
777
|
logger.debug({ key: node.attrs.key }, 'ignored msmsg');
|
|
1405
|
-
await sendMessageAck(node
|
|
778
|
+
await sendMessageAck(node);
|
|
1406
779
|
return;
|
|
1407
780
|
}
|
|
1408
|
-
let
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
const
|
|
1412
|
-
|
|
1413
|
-
if (
|
|
1414
|
-
|
|
1415
|
-
const primaryJid = msg.key.participant || msg.key.remoteJid;
|
|
1416
|
-
if (altServer === 'lid') {
|
|
1417
|
-
if (!(await signalRepository.lidMapping.getPNForLID(alt))) {
|
|
1418
|
-
await signalRepository.lidMapping.storeLIDPNMappings([{ lid: alt, pn: primaryJid }]);
|
|
1419
|
-
await signalRepository.migrateSession(primaryJid, alt);
|
|
1420
|
-
}
|
|
1421
|
-
}
|
|
1422
|
-
else {
|
|
1423
|
-
await signalRepository.lidMapping.storeLIDPNMappings([{ lid: primaryJid, pn: alt }]);
|
|
1424
|
-
await signalRepository.migrateSession(alt, primaryJid);
|
|
1425
|
-
}
|
|
781
|
+
let response;
|
|
782
|
+
if ((0, WABinary_1.getBinaryNodeChild)(node, 'unavailable') && !encNode) {
|
|
783
|
+
await sendMessageAck(node);
|
|
784
|
+
const { key } = (0, Utils_1.decodeMessageNode)(node, authState.creds.me.id, authState.creds.me.lid || '').fullMessage;
|
|
785
|
+
response = await requestPlaceholderResend(key);
|
|
786
|
+
if (response === 'RESOLVED') {
|
|
787
|
+
return;
|
|
1426
788
|
}
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
const messageAge = unixTimestampSeconds() - toNumber(msg.messageTimestamp);
|
|
1451
|
-
if (messageAge > PLACEHOLDER_MAX_AGE_SECONDS) {
|
|
1452
|
-
logger.debug({ msgId: msg.key.id, messageAge }, 'skipping placeholder resend for old message');
|
|
1453
|
-
acked = true;
|
|
1454
|
-
return sendMessageAck(node);
|
|
1455
|
-
}
|
|
1456
|
-
// Request the real content from the phone via placeholder resend PDO.
|
|
1457
|
-
// Upsert the CIPHERTEXT stub as a placeholder (like WA Web's processPlaceholderMsg),
|
|
1458
|
-
// and store the requestId in stubParameters[1] so users can correlate
|
|
1459
|
-
// with the incoming PDO response event.
|
|
1460
|
-
const cleanKey = {
|
|
1461
|
-
remoteJid: msg.key.remoteJid,
|
|
1462
|
-
fromMe: msg.key.fromMe,
|
|
1463
|
-
id: msg.key.id,
|
|
1464
|
-
participant: msg.key.participant
|
|
1465
|
-
};
|
|
1466
|
-
// Cache the original message metadata so the PDO response handler
|
|
1467
|
-
// can preserve key fields (LID details etc.) that the phone may omit
|
|
1468
|
-
const msgData = {
|
|
1469
|
-
key: msg.key,
|
|
1470
|
-
messageTimestamp: msg.messageTimestamp,
|
|
1471
|
-
pushName: msg.pushName,
|
|
1472
|
-
participant: msg.participant,
|
|
1473
|
-
verifiedBizName: msg.verifiedBizName
|
|
1474
|
-
};
|
|
1475
|
-
requestPlaceholderResend(cleanKey, msgData)
|
|
1476
|
-
.then(requestId => {
|
|
1477
|
-
if (requestId && requestId !== 'RESOLVED') {
|
|
1478
|
-
logger.debug({ msgId: msg.key.id, requestId }, 'requested placeholder resend for unavailable message');
|
|
1479
|
-
ev.emit('messages.update', [
|
|
1480
|
-
{
|
|
1481
|
-
key: msg.key,
|
|
1482
|
-
update: { messageStubParameters: [NO_MESSAGE_FOUND_ERROR_TEXT, requestId] }
|
|
1483
|
-
}
|
|
1484
|
-
]);
|
|
1485
|
-
}
|
|
1486
|
-
})
|
|
1487
|
-
.catch(err => {
|
|
1488
|
-
logger.warn({ err, msgId: msg.key.id }, 'failed to request placeholder resend for unavailable message');
|
|
1489
|
-
});
|
|
1490
|
-
acked = true;
|
|
1491
|
-
await sendMessageAck(node);
|
|
1492
|
-
// Don't return — fall through to upsertMessage so the stub is emitted
|
|
1493
|
-
}
|
|
1494
|
-
else {
|
|
1495
|
-
// Skip retry for expired status messages (>24h old)
|
|
1496
|
-
if (isJidStatusBroadcast(msg.key.remoteJid)) {
|
|
1497
|
-
const messageAge = unixTimestampSeconds() - toNumber(msg.messageTimestamp);
|
|
1498
|
-
if (messageAge > STATUS_EXPIRY_SECONDS) {
|
|
1499
|
-
logger.debug({ msgId: msg.key.id, messageAge, remoteJid: msg.key.remoteJid }, 'skipping retry for expired status message');
|
|
1500
|
-
acked = true;
|
|
1501
|
-
return sendMessageAck(node);
|
|
1502
|
-
}
|
|
789
|
+
logger.debug('received unavailable message, acked and requested resend from phone');
|
|
790
|
+
}
|
|
791
|
+
else {
|
|
792
|
+
if (placeholderResendCache.get(node.attrs.id)) {
|
|
793
|
+
placeholderResendCache.del(node.attrs.id);
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
const { fullMessage: msg, category, author, decrypt } = (0, Utils_1.decryptMessageNode)(node, authState.creds.me.id, authState.creds.me.lid || '', signalRepository, logger);
|
|
797
|
+
if (response && ((_a = msg === null || msg === void 0 ? void 0 : msg.messageStubParameters) === null || _a === void 0 ? void 0 : _a[0]) === Utils_1.NO_MESSAGE_FOUND_ERROR_TEXT) {
|
|
798
|
+
msg.messageStubParameters = [Utils_1.NO_MESSAGE_FOUND_ERROR_TEXT, response];
|
|
799
|
+
}
|
|
800
|
+
if (((_c = (_b = msg.message) === null || _b === void 0 ? void 0 : _b.protocolMessage) === null || _c === void 0 ? void 0 : _c.type) === WAProto_1.proto.Message.ProtocolMessage.Type.SHARE_PHONE_NUMBER && node.attrs.sender_pn) {
|
|
801
|
+
ev.emit('chats.phoneNumberShare', { lid: node.attrs.from, jid: node.attrs.sender_pn });
|
|
802
|
+
}
|
|
803
|
+
try {
|
|
804
|
+
await Promise.all([
|
|
805
|
+
processingMutex.mutex(async () => {
|
|
806
|
+
var _a, _b, _c, _d, _e, _f;
|
|
807
|
+
await decrypt();
|
|
808
|
+
// message failed to decrypt
|
|
809
|
+
if (msg.messageStubType === WAProto_1.proto.WebMessageInfo.StubType.CIPHERTEXT) {
|
|
810
|
+
if (((_a = msg === null || msg === void 0 ? void 0 : msg.messageStubParameters) === null || _a === void 0 ? void 0 : _a[0]) === Utils_1.MISSING_KEYS_ERROR_TEXT) {
|
|
811
|
+
return sendMessageAck(node, Utils_1.NACK_REASONS.ParsingError);
|
|
1503
812
|
}
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
try {
|
|
1508
|
-
if (!ws.isOpen) {
|
|
1509
|
-
logger.debug({ node }, 'Connection closed, skipping retry');
|
|
813
|
+
retryMutex.mutex(async () => {
|
|
814
|
+
if (ws.isOpen) {
|
|
815
|
+
if ((0, WABinary_1.getBinaryNodeChild)(node, 'unavailable')) {
|
|
1510
816
|
return;
|
|
1511
817
|
}
|
|
1512
|
-
const encNode = getBinaryNodeChild(node, 'enc');
|
|
818
|
+
const encNode = (0, WABinary_1.getBinaryNodeChild)(node, 'enc');
|
|
1513
819
|
await sendRetryRequest(node, !encNode);
|
|
1514
820
|
if (retryRequestDelayMs) {
|
|
1515
|
-
await delay(retryRequestDelayMs);
|
|
821
|
+
await (0, Utils_1.delay)(retryRequestDelayMs);
|
|
1516
822
|
}
|
|
1517
823
|
}
|
|
1518
|
-
|
|
1519
|
-
logger.
|
|
824
|
+
else {
|
|
825
|
+
logger.debug({ node }, 'connection closed, ignoring retry req');
|
|
1520
826
|
}
|
|
1521
|
-
acked = true;
|
|
1522
|
-
await sendMessageAck(node, NACK_REASONS.UnhandledError);
|
|
1523
827
|
});
|
|
1524
828
|
}
|
|
1525
|
-
|
|
1526
|
-
else {
|
|
1527
|
-
if (messageRetryManager && msg.key.id) {
|
|
1528
|
-
messageRetryManager.cancelPendingPhoneRequest(msg.key.id);
|
|
1529
|
-
}
|
|
1530
|
-
const isNewsletter = isJidNewsletter(msg.key.remoteJid);
|
|
1531
|
-
if (!isNewsletter) {
|
|
829
|
+
else {
|
|
1532
830
|
// no type in the receipt => message delivered
|
|
1533
831
|
let type = undefined;
|
|
832
|
+
if ((_b = msg.key.participant) === null || _b === void 0 ? void 0 : _b.endsWith('@lid')) {
|
|
833
|
+
msg.key.participant = node.attrs.participant_pn || authState.creds.me.id;
|
|
834
|
+
}
|
|
835
|
+
if ((0, WABinary_1.isJidGroup)(msg.key.remoteJid) && ((_f = (_e = (_d = (_c = msg.message) === null || _c === void 0 ? void 0 : _c.extendedTextMessage) === null || _d === void 0 ? void 0 : _d.contextInfo) === null || _e === void 0 ? void 0 : _e.participant) === null || _f === void 0 ? void 0 : _f.endsWith('@lid'))) {
|
|
836
|
+
if (msg.message.extendedTextMessage.contextInfo) {
|
|
837
|
+
const metadata = await groupMetadata(msg.key.remoteJid);
|
|
838
|
+
const sender = msg.message.extendedTextMessage.contextInfo.participant;
|
|
839
|
+
const found = metadata.participants.find(p => p.id === sender);
|
|
840
|
+
msg.message.extendedTextMessage.contextInfo.participant = (found === null || found === void 0 ? void 0 : found.jid) || sender;
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
if (!(0, WABinary_1.isJidGroup)(msg.key.remoteJid) && (0, WABinary_1.isLidUser)(msg.key.remoteJid)) {
|
|
844
|
+
msg.key.remoteJid = node.attrs.sender_pn || node.attrs.peer_recipient_pn;
|
|
845
|
+
}
|
|
1534
846
|
let participant = msg.key.participant;
|
|
1535
|
-
if (category === 'peer') {
|
|
1536
|
-
// special peer message
|
|
847
|
+
if (category === 'peer') { // special peer message
|
|
1537
848
|
type = 'peer_msg';
|
|
1538
849
|
}
|
|
1539
|
-
else if (msg.key.fromMe) {
|
|
1540
|
-
// message was sent by us from a different device
|
|
850
|
+
else if (msg.key.fromMe) { // message was sent by us from a different device
|
|
1541
851
|
type = 'sender';
|
|
1542
852
|
// need to specially handle this case
|
|
1543
|
-
if (
|
|
1544
|
-
participant = author;
|
|
853
|
+
if ((0, WABinary_1.isJidUser)(msg.key.remoteJid)) {
|
|
854
|
+
participant = author;
|
|
1545
855
|
}
|
|
1546
856
|
}
|
|
1547
857
|
else if (!sendActiveReceipts) {
|
|
1548
858
|
type = 'inactive';
|
|
1549
859
|
}
|
|
1550
|
-
acked = true;
|
|
1551
860
|
await sendReceipt(msg.key.remoteJid, participant, [msg.key.id], type);
|
|
1552
861
|
// send ack for history message
|
|
1553
|
-
const isAnyHistoryMsg = getHistoryMsg(msg.message);
|
|
862
|
+
const isAnyHistoryMsg = (0, Utils_1.getHistoryMsg)(msg.message);
|
|
1554
863
|
if (isAnyHistoryMsg) {
|
|
1555
|
-
const jid = jidNormalizedUser(msg.key.remoteJid);
|
|
1556
|
-
await sendReceipt(jid, undefined, [msg.key.id], 'hist_sync');
|
|
864
|
+
const jid = (0, WABinary_1.jidNormalizedUser)(msg.key.remoteJid);
|
|
865
|
+
await sendReceipt(jid, undefined, [msg.key.id], 'hist_sync');
|
|
1557
866
|
}
|
|
1558
867
|
}
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
}
|
|
1565
|
-
cleanMessage(msg, authState.creds.me.id, authState.creds.me.lid);
|
|
1566
|
-
await upsertMessage(msg, node.attrs.offline ? 'append' : 'notify');
|
|
1567
|
-
});
|
|
868
|
+
(0, Utils_1.cleanMessage)(msg, authState.creds.me.id);
|
|
869
|
+
await sendMessageAck(node);
|
|
870
|
+
await upsertMessage(msg, node.attrs.offline ? 'append' : 'notify');
|
|
871
|
+
})
|
|
872
|
+
]);
|
|
1568
873
|
}
|
|
1569
874
|
catch (error) {
|
|
1570
|
-
logger.error({ error, node
|
|
1571
|
-
if (!acked) {
|
|
1572
|
-
await sendMessageAck(node, NACK_REASONS.UnhandledError).catch(ackErr => logger.error({ ackErr }, 'failed to ack message after error'));
|
|
1573
|
-
}
|
|
875
|
+
logger.error({ error, node }, 'error in handling message');
|
|
1574
876
|
}
|
|
1575
877
|
};
|
|
1576
|
-
const
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
if (!infoChild) {
|
|
1581
|
-
throw new Boom('Missing call info in call node');
|
|
1582
|
-
}
|
|
1583
|
-
const status = getCallStatusFromNode(infoChild);
|
|
1584
|
-
const callId = infoChild.attrs['call-id'];
|
|
1585
|
-
const from = infoChild.attrs.from || infoChild.attrs['call-creator'];
|
|
1586
|
-
const call = {
|
|
1587
|
-
chatId: attrs.from,
|
|
1588
|
-
from,
|
|
1589
|
-
callerPn: infoChild.attrs['caller_pn'],
|
|
1590
|
-
id: callId,
|
|
1591
|
-
date: new Date(+attrs.t * 1000),
|
|
1592
|
-
offline: !!attrs.offline,
|
|
1593
|
-
status
|
|
1594
|
-
};
|
|
1595
|
-
if (status === 'relaylatency') {
|
|
1596
|
-
const latencyValue = infoChild.attrs.latency || infoChild.attrs['latency_ms'] || infoChild.attrs['latency-ms'];
|
|
1597
|
-
const latencyMs = latencyValue ? Number(latencyValue) : undefined;
|
|
1598
|
-
if (Number.isFinite(latencyMs)) {
|
|
1599
|
-
call.latencyMs = latencyMs;
|
|
1600
|
-
}
|
|
1601
|
-
}
|
|
1602
|
-
if (status === 'offer') {
|
|
1603
|
-
call.isVideo = !!getBinaryNodeChild(infoChild, 'video');
|
|
1604
|
-
call.isGroup = infoChild.attrs.type === 'group' || !!infoChild.attrs['group-jid'];
|
|
1605
|
-
call.groupJid = infoChild.attrs['group-jid'];
|
|
1606
|
-
await callOfferCache.set(call.id, call);
|
|
1607
|
-
}
|
|
1608
|
-
const existingCall = await callOfferCache.get(call.id);
|
|
1609
|
-
// use existing call info to populate this event
|
|
1610
|
-
if (existingCall) {
|
|
1611
|
-
call.isVideo = existingCall.isVideo;
|
|
1612
|
-
call.isGroup = existingCall.isGroup;
|
|
1613
|
-
call.callerPn = call.callerPn || existingCall.callerPn;
|
|
1614
|
-
}
|
|
1615
|
-
// delete data once call has ended
|
|
1616
|
-
if (status === 'reject' || status === 'accept' || status === 'timeout' || status === 'terminate') {
|
|
1617
|
-
await callOfferCache.del(call.id);
|
|
1618
|
-
}
|
|
1619
|
-
ev.emit('call', [call]);
|
|
878
|
+
const fetchMessageHistory = async (count, oldestMsgKey, oldestMsgTimestamp) => {
|
|
879
|
+
var _a;
|
|
880
|
+
if (!((_a = authState.creds.me) === null || _a === void 0 ? void 0 : _a.id)) {
|
|
881
|
+
throw new boom_1.Boom('Not authenticated');
|
|
1620
882
|
}
|
|
1621
|
-
|
|
1622
|
-
|
|
883
|
+
const pdoMessage = {
|
|
884
|
+
historySyncOnDemandRequest: {
|
|
885
|
+
chatJid: oldestMsgKey.remoteJid,
|
|
886
|
+
oldestMsgFromMe: oldestMsgKey.fromMe,
|
|
887
|
+
oldestMsgId: oldestMsgKey.id,
|
|
888
|
+
oldestMsgTimestampMs: oldestMsgTimestamp,
|
|
889
|
+
onDemandMsgCount: count
|
|
890
|
+
},
|
|
891
|
+
peerDataOperationRequestType: WAProto_1.proto.Message.PeerDataOperationRequestType.HISTORY_SYNC_ON_DEMAND
|
|
892
|
+
};
|
|
893
|
+
return sendPeerDataOperationMessage(pdoMessage);
|
|
894
|
+
};
|
|
895
|
+
const requestPlaceholderResend = async (messageKey) => {
|
|
896
|
+
var _a;
|
|
897
|
+
if (!((_a = authState.creds.me) === null || _a === void 0 ? void 0 : _a.id)) {
|
|
898
|
+
throw new boom_1.Boom('Not authenticated');
|
|
1623
899
|
}
|
|
1624
|
-
|
|
1625
|
-
|
|
900
|
+
if (placeholderResendCache.get(messageKey === null || messageKey === void 0 ? void 0 : messageKey.id)) {
|
|
901
|
+
logger.debug({ messageKey }, 'already requested resend');
|
|
902
|
+
return;
|
|
903
|
+
}
|
|
904
|
+
else {
|
|
905
|
+
placeholderResendCache.set(messageKey === null || messageKey === void 0 ? void 0 : messageKey.id, true);
|
|
1626
906
|
}
|
|
907
|
+
await (0, Utils_1.delay)(5000);
|
|
908
|
+
if (!placeholderResendCache.get(messageKey === null || messageKey === void 0 ? void 0 : messageKey.id)) {
|
|
909
|
+
logger.debug({ messageKey }, 'message received while resend requested');
|
|
910
|
+
return 'RESOLVED';
|
|
911
|
+
}
|
|
912
|
+
const pdoMessage = {
|
|
913
|
+
placeholderMessageResendRequest: [{
|
|
914
|
+
messageKey
|
|
915
|
+
}],
|
|
916
|
+
peerDataOperationRequestType: WAProto_1.proto.Message.PeerDataOperationRequestType.PLACEHOLDER_MESSAGE_RESEND
|
|
917
|
+
};
|
|
918
|
+
setTimeout(() => {
|
|
919
|
+
if (placeholderResendCache.get(messageKey === null || messageKey === void 0 ? void 0 : messageKey.id)) {
|
|
920
|
+
logger.debug({ messageKey }, 'PDO message without response after 15 seconds. Phone possibly offline');
|
|
921
|
+
placeholderResendCache.del(messageKey === null || messageKey === void 0 ? void 0 : messageKey.id);
|
|
922
|
+
}
|
|
923
|
+
}, 15000);
|
|
924
|
+
return sendPeerDataOperationMessage(pdoMessage);
|
|
925
|
+
};
|
|
926
|
+
const handleCall = async (node) => {
|
|
927
|
+
const { attrs } = node;
|
|
928
|
+
const [infoChild] = (0, WABinary_1.getAllBinaryNodeChildren)(node);
|
|
929
|
+
const callId = infoChild.attrs['call-id'];
|
|
930
|
+
const from = infoChild.attrs.from || infoChild.attrs['call-creator'];
|
|
931
|
+
const status = (0, Utils_1.getCallStatusFromNode)(infoChild);
|
|
932
|
+
const call = {
|
|
933
|
+
chatId: attrs.from,
|
|
934
|
+
from,
|
|
935
|
+
id: callId,
|
|
936
|
+
date: new Date(+attrs.t * 1000),
|
|
937
|
+
offline: !!attrs.offline,
|
|
938
|
+
status,
|
|
939
|
+
};
|
|
940
|
+
if (status === 'offer') {
|
|
941
|
+
call.isVideo = !!(0, WABinary_1.getBinaryNodeChild)(infoChild, 'video');
|
|
942
|
+
call.isGroup = infoChild.attrs.type === 'group' || !!infoChild.attrs['group-jid'];
|
|
943
|
+
call.groupJid = infoChild.attrs['group-jid'];
|
|
944
|
+
callOfferCache.set(call.id, call);
|
|
945
|
+
}
|
|
946
|
+
const existingCall = callOfferCache.get(call.id);
|
|
947
|
+
// use existing call info to populate this event
|
|
948
|
+
if (existingCall) {
|
|
949
|
+
call.isVideo = existingCall.isVideo;
|
|
950
|
+
call.isGroup = existingCall.isGroup;
|
|
951
|
+
}
|
|
952
|
+
// delete data once call has ended
|
|
953
|
+
if (status === 'reject' || status === 'accept' || status === 'timeout' || status === 'terminate') {
|
|
954
|
+
callOfferCache.del(call.id);
|
|
955
|
+
}
|
|
956
|
+
ev.emit('call', [call]);
|
|
957
|
+
await sendMessageAck(node);
|
|
1627
958
|
};
|
|
1628
959
|
const handleBadAck = async ({ attrs }) => {
|
|
1629
|
-
const key = { remoteJid: attrs.from, fromMe: true, id: attrs.id };
|
|
1630
|
-
//
|
|
1631
|
-
//
|
|
1632
|
-
//
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
// logger.warn({ attrs }, 'could not send message again, as it was not found')
|
|
1641
|
-
// }
|
|
1642
|
-
// }
|
|
1643
|
-
// error in acknowledgement,
|
|
1644
|
-
// device could not display the message
|
|
1645
|
-
if (attrs.error) {
|
|
1646
|
-
const isReachoutTimelocked = attrs.error === String(NACK_REASONS.SenderReachoutTimelocked);
|
|
1647
|
-
if (attrs.error === SERVER_ERROR_CODES.MessageAccountRestriction) {
|
|
1648
|
-
// 463 = 1:1 message missing privacy token (tctoken). Usually means the
|
|
1649
|
-
// account is restricted: WhatsApp blocks starting new chats but preserves
|
|
1650
|
-
// existing ones, since established chats already carry a tctoken.
|
|
1651
|
-
// WA Web prevents this client-side (disables the compose bar).
|
|
1652
|
-
// No retry — retrying counts as another "reach out" and worsens the restriction.
|
|
1653
|
-
logger.warn({ msgId: attrs.id, from: attrs.from }, 'error 463: account restricted or missing tctoken for contact');
|
|
1654
|
-
const ackFrom = attrs.from;
|
|
1655
|
-
if (ackFrom && !inFlight463Recoveries.has(ackFrom)) {
|
|
1656
|
-
inFlight463Recoveries.add(ackFrom);
|
|
1657
|
-
void (async () => {
|
|
1658
|
-
try {
|
|
1659
|
-
const getPNForLID = signalRepository.lidMapping.getPNForLID.bind(signalRepository.lidMapping);
|
|
1660
|
-
const tcStorageJid = await resolveTcTokenJid(ackFrom, getLIDForPN);
|
|
1661
|
-
const issueJid = await resolveIssuanceJid(ackFrom, sock.serverProps.lidTrustedTokenIssueToLid, getLIDForPN, getPNForLID);
|
|
1662
|
-
const result = await issuePrivacyTokens([issueJid], unixTimestampSeconds());
|
|
1663
|
-
await storeTcTokensFromIqResult({
|
|
1664
|
-
result,
|
|
1665
|
-
fallbackJid: tcStorageJid,
|
|
1666
|
-
keys: authState.keys,
|
|
1667
|
-
getLIDForPN,
|
|
1668
|
-
onNewJidStored: trackTcTokenJid
|
|
1669
|
-
});
|
|
1670
|
-
logger.debug({ from: ackFrom }, 'completed 463 token recovery issuance');
|
|
1671
|
-
}
|
|
1672
|
-
catch (err) {
|
|
1673
|
-
logger.debug({ from: ackFrom, err: err?.message }, 'failed 463 token recovery issuance');
|
|
1674
|
-
}
|
|
1675
|
-
finally {
|
|
1676
|
-
inFlight463Recoveries.delete(ackFrom);
|
|
1677
|
-
}
|
|
1678
|
-
})();
|
|
1679
|
-
}
|
|
1680
|
-
}
|
|
1681
|
-
else if (attrs.error === SERVER_ERROR_CODES.SmaxInvalid) {
|
|
1682
|
-
logger.warn({ msgId: attrs.id, from: attrs.from }, 'smax-invalid (479): stanza rejected by server — likely stale device session or malformed addressing');
|
|
960
|
+
const key = { remoteJid: attrs.from, fromMe: true, id: attrs.id, 'server_id': attrs === null || attrs === void 0 ? void 0 : attrs.server_id };
|
|
961
|
+
// current hypothesis is that if pash is sent in the ack
|
|
962
|
+
// it means -- the message hasn't reached all devices yet
|
|
963
|
+
// we'll retry sending the message here
|
|
964
|
+
if (attrs.phash) {
|
|
965
|
+
logger.info({ attrs }, 'received phash in ack, resending message...');
|
|
966
|
+
const cacheKey = `${key.remoteJid}:${key.id}`;
|
|
967
|
+
if ((msgRetryCache.get(cacheKey) || 0) >= maxMsgRetryCount) {
|
|
968
|
+
logger.warn({ attrs }, 'reached max retry count, not sending message again');
|
|
969
|
+
msgRetryCache.del(cacheKey);
|
|
970
|
+
return;
|
|
1683
971
|
}
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
972
|
+
const retryCount = msgRetryCache.get(cacheKey) || 0;
|
|
973
|
+
const msg = await getMessage(key);
|
|
974
|
+
if (msg) {
|
|
975
|
+
await relayMessage(key.remoteJid, msg, { messageId: key.id, useUserDevicesCache: false });
|
|
976
|
+
msgRetryCache.set(cacheKey, retryCount + 1);
|
|
1688
977
|
}
|
|
1689
978
|
else {
|
|
1690
|
-
logger.warn({ attrs }, '
|
|
979
|
+
logger.warn({ attrs }, 'could not send message again, as it was not found');
|
|
1691
980
|
}
|
|
981
|
+
}
|
|
982
|
+
// error in acknowledgement,
|
|
983
|
+
// device could not display the message
|
|
984
|
+
if (attrs.error) {
|
|
985
|
+
logger.warn({ attrs }, 'received error in ack');
|
|
1692
986
|
ev.emit('messages.update', [
|
|
1693
987
|
{
|
|
1694
988
|
key,
|
|
1695
989
|
update: {
|
|
1696
|
-
status: WAMessageStatus.ERROR,
|
|
1697
|
-
messageStubParameters:
|
|
990
|
+
status: Types_1.WAMessageStatus.ERROR,
|
|
991
|
+
messageStubParameters: [
|
|
992
|
+
attrs.error
|
|
993
|
+
]
|
|
1698
994
|
}
|
|
1699
995
|
}
|
|
1700
996
|
]);
|
|
@@ -1707,61 +1003,69 @@ export const makeMessagesRecvSocket = (config) => {
|
|
|
1707
1003
|
await execTask();
|
|
1708
1004
|
ev.flush();
|
|
1709
1005
|
function execTask() {
|
|
1710
|
-
return exec(node, false)
|
|
1006
|
+
return exec(node, false)
|
|
1007
|
+
.catch(err => onUnexpectedError(err, identifier));
|
|
1711
1008
|
}
|
|
1712
1009
|
};
|
|
1713
|
-
const
|
|
1714
|
-
[
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
const
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1010
|
+
const makeOfflineNodeProcessor = () => {
|
|
1011
|
+
const nodeProcessorMap = new Map([
|
|
1012
|
+
['message', handleMessage],
|
|
1013
|
+
['call', handleCall],
|
|
1014
|
+
['receipt', handleReceipt],
|
|
1015
|
+
['notification', handleNotification]
|
|
1016
|
+
]);
|
|
1017
|
+
const nodes = [];
|
|
1018
|
+
let isProcessing = false;
|
|
1019
|
+
const enqueue = (type, node) => {
|
|
1020
|
+
nodes.push({ type, node });
|
|
1021
|
+
if (isProcessing) {
|
|
1022
|
+
return;
|
|
1023
|
+
}
|
|
1024
|
+
isProcessing = true;
|
|
1025
|
+
const promise = async () => {
|
|
1026
|
+
while (nodes.length && ws.isOpen) {
|
|
1027
|
+
const { type, node } = nodes.shift();
|
|
1028
|
+
const nodeProcessor = nodeProcessorMap.get(type);
|
|
1029
|
+
if (!nodeProcessor) {
|
|
1030
|
+
onUnexpectedError(new Error(`unknown offline node type: ${type}`), 'processing offline node');
|
|
1031
|
+
continue;
|
|
1032
|
+
}
|
|
1033
|
+
await nodeProcessor(node);
|
|
1034
|
+
}
|
|
1035
|
+
isProcessing = false;
|
|
1036
|
+
};
|
|
1037
|
+
promise().catch(error => onUnexpectedError(error, 'processing offline nodes'));
|
|
1038
|
+
};
|
|
1039
|
+
return { enqueue };
|
|
1040
|
+
};
|
|
1041
|
+
const offlineNodeProcessor = makeOfflineNodeProcessor();
|
|
1042
|
+
const processNode = (type, node, identifier, exec) => {
|
|
1737
1043
|
const isOffline = !!node.attrs.offline;
|
|
1738
1044
|
if (isOffline) {
|
|
1739
1045
|
offlineNodeProcessor.enqueue(type, node);
|
|
1740
1046
|
}
|
|
1741
1047
|
else {
|
|
1742
|
-
|
|
1048
|
+
processNodeWithBuffer(node, identifier, exec);
|
|
1743
1049
|
}
|
|
1744
1050
|
};
|
|
1745
1051
|
// recv a message
|
|
1746
|
-
ws.on('CB:message',
|
|
1747
|
-
|
|
1052
|
+
ws.on('CB:message', (node) => {
|
|
1053
|
+
processNode('message', node, 'processing message', handleMessage);
|
|
1748
1054
|
});
|
|
1749
1055
|
ws.on('CB:call', async (node) => {
|
|
1750
|
-
|
|
1056
|
+
processNode('call', node, 'handling call', handleCall);
|
|
1751
1057
|
});
|
|
1752
|
-
ws.on('CB:receipt',
|
|
1753
|
-
|
|
1058
|
+
ws.on('CB:receipt', node => {
|
|
1059
|
+
processNode('receipt', node, 'handling receipt', handleReceipt);
|
|
1754
1060
|
});
|
|
1755
1061
|
ws.on('CB:notification', async (node) => {
|
|
1756
|
-
|
|
1062
|
+
processNode('notification', node, 'handling notification', handleNotification);
|
|
1757
1063
|
});
|
|
1758
1064
|
ws.on('CB:ack,class:message', (node) => {
|
|
1759
|
-
handleBadAck(node)
|
|
1065
|
+
handleBadAck(node)
|
|
1066
|
+
.catch(error => onUnexpectedError(error, 'handling bad ack'));
|
|
1760
1067
|
});
|
|
1761
|
-
ev.on('call',
|
|
1762
|
-
if (!call) {
|
|
1763
|
-
return;
|
|
1764
|
-
}
|
|
1068
|
+
ev.on('call', ([call]) => {
|
|
1765
1069
|
// missed call + group call notification message generation
|
|
1766
1070
|
if (call.status === 'timeout' || (call.status === 'offer' && call.isGroup)) {
|
|
1767
1071
|
const msg = {
|
|
@@ -1770,147 +1074,37 @@ export const makeMessagesRecvSocket = (config) => {
|
|
|
1770
1074
|
id: call.id,
|
|
1771
1075
|
fromMe: false
|
|
1772
1076
|
},
|
|
1773
|
-
messageTimestamp: unixTimestampSeconds(call.date)
|
|
1077
|
+
messageTimestamp: (0, Utils_1.unixTimestampSeconds)(call.date),
|
|
1774
1078
|
};
|
|
1775
1079
|
if (call.status === 'timeout') {
|
|
1776
1080
|
if (call.isGroup) {
|
|
1777
|
-
msg.messageStubType = call.isVideo
|
|
1778
|
-
? WAMessageStubType.CALL_MISSED_GROUP_VIDEO
|
|
1779
|
-
: WAMessageStubType.CALL_MISSED_GROUP_VOICE;
|
|
1081
|
+
msg.messageStubType = call.isVideo ? Types_1.WAMessageStubType.CALL_MISSED_GROUP_VIDEO : Types_1.WAMessageStubType.CALL_MISSED_GROUP_VOICE;
|
|
1780
1082
|
}
|
|
1781
1083
|
else {
|
|
1782
|
-
msg.messageStubType = call.isVideo ? WAMessageStubType.CALL_MISSED_VIDEO : WAMessageStubType.CALL_MISSED_VOICE;
|
|
1084
|
+
msg.messageStubType = call.isVideo ? Types_1.WAMessageStubType.CALL_MISSED_VIDEO : Types_1.WAMessageStubType.CALL_MISSED_VOICE;
|
|
1783
1085
|
}
|
|
1784
1086
|
}
|
|
1785
1087
|
else {
|
|
1786
1088
|
msg.message = { call: { callKey: Buffer.from(call.id) } };
|
|
1787
1089
|
}
|
|
1788
|
-
const protoMsg = proto.WebMessageInfo.fromObject(msg);
|
|
1789
|
-
|
|
1090
|
+
const protoMsg = WAProto_1.proto.WebMessageInfo.fromObject(msg);
|
|
1091
|
+
upsertMessage(protoMsg, call.offline ? 'append' : 'notify');
|
|
1790
1092
|
}
|
|
1791
1093
|
});
|
|
1792
|
-
|
|
1793
|
-
let lastTcTokenPruneTs = 0;
|
|
1794
|
-
/** dedupe in-flight 463 recovery token issuance by target JID */
|
|
1795
|
-
const inFlight463Recoveries = new Set();
|
|
1796
|
-
ev.on('connection.update', ({ isOnline, connection }) => {
|
|
1094
|
+
ev.on('connection.update', ({ isOnline }) => {
|
|
1797
1095
|
if (typeof isOnline !== 'undefined') {
|
|
1798
1096
|
sendActiveReceipts = isOnline;
|
|
1799
1097
|
logger.trace(`sendActiveReceipts set to "${sendActiveReceipts}"`);
|
|
1800
1098
|
}
|
|
1801
|
-
// Flush pending tctoken index save on disconnect to avoid writing after close
|
|
1802
|
-
if (connection === 'close' && tcTokenIndexTimer) {
|
|
1803
|
-
clearTimeout(tcTokenIndexTimer);
|
|
1804
|
-
tcTokenIndexTimer = undefined;
|
|
1805
|
-
// Best-effort flush — may fail if store is already closed
|
|
1806
|
-
try {
|
|
1807
|
-
void Promise.resolve(flushTcTokenIndex()).catch(() => { });
|
|
1808
|
-
}
|
|
1809
|
-
catch {
|
|
1810
|
-
/* ignore sync errors */
|
|
1811
|
-
}
|
|
1812
|
-
}
|
|
1813
|
-
// Prune expired tctokens when coming online, at most once per 24 hours
|
|
1814
|
-
// Matches WA Web's CLEAN_TC_TOKENS task
|
|
1815
|
-
// Note: don't gate on tcTokenKnownJids.size — the index may still be loading
|
|
1816
|
-
if (isOnline) {
|
|
1817
|
-
const now = Date.now();
|
|
1818
|
-
const DAY_MS = 24 * 60 * 60 * 1000;
|
|
1819
|
-
if (now - lastTcTokenPruneTs >= DAY_MS) {
|
|
1820
|
-
lastTcTokenPruneTs = now;
|
|
1821
|
-
void pruneExpiredTcTokens();
|
|
1822
|
-
}
|
|
1823
|
-
}
|
|
1824
1099
|
});
|
|
1825
|
-
registerSocketEndHandler(() => {
|
|
1826
|
-
if (!config.msgRetryCounterCache && msgRetryCache.close) {
|
|
1827
|
-
msgRetryCache.close();
|
|
1828
|
-
}
|
|
1829
|
-
if (!config.callOfferCache && callOfferCache.close) {
|
|
1830
|
-
callOfferCache.close();
|
|
1831
|
-
}
|
|
1832
|
-
identityAssertDebounce.close();
|
|
1833
|
-
sendActiveReceipts = false;
|
|
1834
|
-
});
|
|
1835
|
-
async function pruneExpiredTcTokens() {
|
|
1836
|
-
try {
|
|
1837
|
-
await tcTokenIndexLoaded;
|
|
1838
|
-
// Union with the persisted index picks up JIDs added by other layers
|
|
1839
|
-
// (history sync) without needing inter-module wiring.
|
|
1840
|
-
const persisted = await readTcTokenIndex(authState.keys);
|
|
1841
|
-
const allJids = new Set(tcTokenKnownJids);
|
|
1842
|
-
for (const jid of persisted)
|
|
1843
|
-
allJids.add(jid);
|
|
1844
|
-
if (!allJids.size)
|
|
1845
|
-
return;
|
|
1846
|
-
const jids = [...allJids];
|
|
1847
|
-
const allTokens = await authState.keys.get('tctoken', jids);
|
|
1848
|
-
const writes = {};
|
|
1849
|
-
const survivors = new Set();
|
|
1850
|
-
let mutated = 0;
|
|
1851
|
-
for (const jid of jids) {
|
|
1852
|
-
const entry = allTokens[jid];
|
|
1853
|
-
if (!entry) {
|
|
1854
|
-
// Tracked but nothing in store — drop from index.
|
|
1855
|
-
mutated++;
|
|
1856
|
-
continue;
|
|
1857
|
-
}
|
|
1858
|
-
const hasPeerToken = !!entry.token?.length;
|
|
1859
|
-
const peerTokenExpired = hasPeerToken && isTcTokenExpired(entry.timestamp);
|
|
1860
|
-
const hasSenderTs = entry.senderTimestamp !== undefined;
|
|
1861
|
-
const senderTsExpired = hasSenderTs && isTcTokenExpired(entry.senderTimestamp);
|
|
1862
|
-
const keepPeerToken = hasPeerToken && !peerTokenExpired;
|
|
1863
|
-
const keepSenderTs = hasSenderTs && !senderTsExpired;
|
|
1864
|
-
if (!keepPeerToken && !keepSenderTs) {
|
|
1865
|
-
writes[jid] = null;
|
|
1866
|
-
mutated++;
|
|
1867
|
-
}
|
|
1868
|
-
else if (peerTokenExpired && keepSenderTs) {
|
|
1869
|
-
writes[jid] = { token: Buffer.alloc(0), senderTimestamp: entry.senderTimestamp };
|
|
1870
|
-
survivors.add(jid);
|
|
1871
|
-
mutated++;
|
|
1872
|
-
}
|
|
1873
|
-
else {
|
|
1874
|
-
survivors.add(jid);
|
|
1875
|
-
}
|
|
1876
|
-
}
|
|
1877
|
-
if (mutated === 0)
|
|
1878
|
-
return;
|
|
1879
|
-
await authState.keys.set({
|
|
1880
|
-
tctoken: {
|
|
1881
|
-
...writes,
|
|
1882
|
-
[TC_TOKEN_INDEX_KEY]: {
|
|
1883
|
-
token: Buffer.from(JSON.stringify([...survivors]))
|
|
1884
|
-
}
|
|
1885
|
-
}
|
|
1886
|
-
});
|
|
1887
|
-
tcTokenKnownJids.clear();
|
|
1888
|
-
for (const jid of survivors)
|
|
1889
|
-
tcTokenKnownJids.add(jid);
|
|
1890
|
-
logger.debug({ mutated, remaining: survivors.size }, 'pruned expired tctokens');
|
|
1891
|
-
}
|
|
1892
|
-
catch (err) {
|
|
1893
|
-
logger.warn({ err: err?.message }, 'failed to prune expired tctokens');
|
|
1894
|
-
}
|
|
1895
|
-
}
|
|
1896
1100
|
return {
|
|
1897
1101
|
...sock,
|
|
1898
1102
|
sendMessageAck,
|
|
1899
1103
|
sendRetryRequest,
|
|
1900
1104
|
rejectCall,
|
|
1105
|
+
offerCall,
|
|
1901
1106
|
fetchMessageHistory,
|
|
1902
1107
|
requestPlaceholderResend,
|
|
1903
|
-
messageRetryManager,
|
|
1904
|
-
sendText,
|
|
1905
|
-
sendImage,
|
|
1906
|
-
sendVideo,
|
|
1907
|
-
sendAudio,
|
|
1908
|
-
sendDocument,
|
|
1909
|
-
sendLocation,
|
|
1910
|
-
sendPoll,
|
|
1911
|
-
sendQuiz,
|
|
1912
|
-
sendPtv,
|
|
1913
|
-
statusMention
|
|
1914
1108
|
};
|
|
1915
1109
|
};
|
|
1916
|
-
|
|
1110
|
+
exports.makeMessagesRecvSocket = makeMessagesRecvSocket;
|