@badzz88/baileys 8.3.0 → 8.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +27 -27
- package/WAProto/GenerateStatics.sh +3 -0
- package/WAProto/WAProto.proto +6902 -0
- package/WAProto/fix-import.js +29 -0
- package/WAProto/fix-imports.js +85 -0
- package/WAProto/index.d.ts +79257 -0
- package/WAProto/index.js +204305 -131020
- package/engine-requirements.js +3 -4
- package/lib/Defaults/baileys-version.json +1 -1
- package/lib/Defaults/index.js +119 -136
- package/lib/Signal/Group/ciphertext-message.js +2 -5
- package/lib/Signal/Group/group-session-builder.js +7 -41
- package/lib/Signal/Group/group_cipher.js +37 -51
- package/lib/Signal/Group/index.js +12 -57
- package/lib/Signal/Group/keyhelper.js +7 -44
- package/lib/Signal/Group/sender-chain-key.js +7 -15
- package/lib/Signal/Group/sender-key-distribution-message.js +8 -11
- package/lib/Signal/Group/sender-key-message.js +9 -12
- package/lib/Signal/Group/sender-key-name.js +2 -5
- package/lib/Signal/Group/sender-key-record.js +9 -21
- package/lib/Signal/Group/sender-key-state.js +27 -42
- package/lib/Signal/Group/sender-message-key.js +4 -7
- package/lib/Signal/libsignal.js +347 -90
- package/lib/Signal/lid-mapping.js +277 -0
- package/lib/Socket/Client/index.js +3 -19
- package/lib/Socket/Client/types.js +11 -0
- package/lib/Socket/Client/websocket.js +54 -0
- package/lib/Socket/business.js +162 -43
- package/lib/Socket/chats.js +627 -404
- package/lib/Socket/communities.js +431 -0
- package/lib/Socket/community.js +392 -0
- package/lib/Socket/dugong.js +161 -8
- package/lib/Socket/groups.js +154 -160
- package/lib/Socket/index.js +11 -10
- package/lib/Socket/luxu.js +387 -0
- package/lib/Socket/messages-recv.js +1421 -615
- package/lib/Socket/messages-send.js +1150 -624
- package/lib/Socket/mex.js +42 -0
- package/lib/Socket/newsletter.js +139 -314
- package/lib/Socket/registration.js +2 -1
- package/lib/Socket/socket.js +544 -242
- package/lib/Store/index.js +10 -10
- package/lib/Store/keyed-db.js +108 -0
- package/lib/Store/make-cache-manager-store.js +43 -41
- package/lib/Store/make-in-memory-store.js +112 -341
- package/lib/Store/make-ordered-dictionary.js +14 -20
- package/lib/Store/object-repository.js +11 -6
- package/lib/Types/Auth.js +2 -2
- package/lib/Types/Bussines.js +2 -0
- package/lib/Types/Call.js +2 -2
- package/lib/Types/Chat.js +8 -4
- package/lib/Types/Contact.js +2 -2
- package/lib/Types/Events.js +2 -2
- package/lib/Types/GroupMetadata.js +2 -2
- package/lib/Types/Label.js +3 -5
- package/lib/Types/LabelAssociation.js +3 -5
- package/lib/Types/Message.js +11 -9
- package/lib/Types/Mex.js +37 -0
- package/lib/Types/Product.js +2 -2
- package/lib/Types/Signal.js +2 -2
- package/lib/Types/Socket.js +3 -2
- package/lib/Types/State.js +56 -2
- package/lib/Types/USync.js +2 -2
- package/lib/Types/index.js +15 -31
- package/lib/Utils/auth-utils.js +239 -143
- package/lib/Utils/browser-utils.js +48 -0
- package/lib/Utils/business.js +66 -69
- package/lib/Utils/chat-utils.js +396 -253
- package/lib/Utils/companion-reg-client-utils.js +35 -0
- package/lib/Utils/crypto.js +57 -90
- package/lib/Utils/decode-wa-message.js +236 -84
- package/lib/Utils/event-buffer.js +185 -77
- package/lib/Utils/generics.js +188 -283
- package/lib/Utils/history.js +93 -55
- package/lib/Utils/identity-change-handler.js +50 -0
- package/lib/Utils/index.js +23 -33
- package/lib/Utils/link-preview.js +16 -24
- package/lib/Utils/logger.js +3 -7
- package/lib/Utils/lt-hash.js +3 -46
- package/lib/Utils/make-mutex.js +24 -34
- package/lib/Utils/message-composer.js +273 -0
- package/lib/Utils/message-retry-manager.js +265 -0
- package/lib/Utils/messages-media.js +451 -482
- package/lib/Utils/messages.js +796 -359
- package/lib/Utils/noise-handler.js +145 -99
- package/lib/Utils/offline-node-processor.js +40 -0
- package/lib/Utils/pre-key-manager.js +106 -0
- package/lib/Utils/process-message.js +459 -150
- package/lib/Utils/reporting-utils.js +258 -0
- package/lib/Utils/signal.js +120 -72
- package/lib/Utils/stanza-ack.js +38 -0
- package/lib/Utils/sync-action-utils.js +49 -0
- package/lib/Utils/tc-token-utils.js +163 -0
- package/lib/Utils/use-multi-file-auth-state.js +29 -27
- package/lib/Utils/validate-connection.js +73 -99
- package/lib/WABinary/constants.js +1281 -20
- package/lib/WABinary/decode.js +52 -42
- package/lib/WABinary/encode.js +110 -155
- package/lib/WABinary/generic-utils.js +55 -49
- package/lib/WABinary/index.js +6 -21
- package/lib/WABinary/jid-utils.js +76 -40
- package/lib/WABinary/types.js +2 -2
- package/lib/WAM/BinaryInfo.js +2 -5
- package/lib/WAM/constants.js +19071 -11568
- package/lib/WAM/encode.js +17 -22
- package/lib/WAM/index.js +4 -19
- package/lib/WAUSync/Protocols/USyncContactProtocol.js +33 -13
- package/lib/WAUSync/Protocols/USyncDeviceProtocol.js +11 -14
- package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.js +9 -12
- package/lib/WAUSync/Protocols/USyncStatusProtocol.js +9 -13
- package/lib/WAUSync/Protocols/USyncUsernameProtocol.js +25 -0
- package/lib/WAUSync/Protocols/UsyncBotProfileProtocol.js +20 -22
- package/lib/WAUSync/Protocols/UsyncLIDProtocol.js +13 -8
- package/lib/WAUSync/Protocols/index.js +6 -20
- package/lib/WAUSync/USyncQuery.js +44 -35
- package/lib/WAUSync/USyncUser.js +10 -5
- package/lib/WAUSync/index.js +4 -19
- package/lib/index.js +39 -53
- package/package.json +134 -101
- package/lib/Defaults/index.d.ts +0 -53
- package/lib/Signal/Group/ciphertext-message.d.ts +0 -9
- package/lib/Signal/Group/group-session-builder.d.ts +0 -14
- package/lib/Signal/Group/group_cipher.d.ts +0 -17
- package/lib/Signal/Group/index.d.ts +0 -11
- package/lib/Signal/Group/keyhelper.d.ts +0 -10
- package/lib/Signal/Group/queue-job.d.ts +0 -1
- package/lib/Signal/Group/sender-chain-key.d.ts +0 -13
- package/lib/Signal/Group/sender-key-distribution-message.d.ts +0 -16
- package/lib/Signal/Group/sender-key-message.d.ts +0 -18
- package/lib/Signal/Group/sender-key-name.d.ts +0 -17
- package/lib/Signal/Group/sender-key-record.d.ts +0 -30
- package/lib/Signal/Group/sender-key-state.d.ts +0 -38
- package/lib/Signal/Group/sender-message-key.d.ts +0 -11
- package/lib/Signal/libsignal.d.ts +0 -3
- package/lib/Socket/Client/abstract-socket-client.d.ts +0 -17
- package/lib/Socket/Client/index.d.ts +0 -3
- package/lib/Socket/Client/mobile-socket-client.d.ts +0 -13
- package/lib/Socket/Client/web-socket-client.d.ts +0 -12
- package/lib/Socket/business.d.ts +0 -171
- package/lib/Socket/chats.d.ts +0 -267
- package/lib/Socket/dugong.d.ts +0 -254
- package/lib/Socket/groups.d.ts +0 -115
- package/lib/Socket/index.d.ts +0 -173
- package/lib/Socket/messages-recv.d.ts +0 -161
- package/lib/Socket/messages-send.d.ts +0 -149
- package/lib/Socket/newsletter.d.ts +0 -134
- package/lib/Socket/registration.d.ts +0 -267
- package/lib/Socket/socket.d.ts +0 -43
- package/lib/Socket/usync.d.ts +0 -36
- package/lib/Store/index.d.ts +0 -3
- package/lib/Store/make-cache-manager-store.d.ts +0 -13
- package/lib/Store/make-in-memory-store.d.ts +0 -118
- package/lib/Store/make-ordered-dictionary.d.ts +0 -13
- package/lib/Store/object-repository.d.ts +0 -10
- package/lib/Types/Auth.d.ts +0 -110
- package/lib/Types/Call.d.ts +0 -13
- package/lib/Types/Chat.d.ts +0 -102
- package/lib/Types/Contact.d.ts +0 -19
- package/lib/Types/Events.d.ts +0 -157
- package/lib/Types/GroupMetadata.d.ts +0 -55
- package/lib/Types/Label.d.ts +0 -35
- package/lib/Types/LabelAssociation.d.ts +0 -29
- package/lib/Types/Message.d.ts +0 -273
- package/lib/Types/Newsletter.d.ts +0 -103
- package/lib/Types/Product.d.ts +0 -78
- package/lib/Types/Signal.d.ts +0 -57
- package/lib/Types/Socket.d.ts +0 -111
- package/lib/Types/State.d.ts +0 -27
- package/lib/Types/USync.d.ts +0 -25
- package/lib/Types/index.d.ts +0 -57
- package/lib/Utils/auth-utils.d.ts +0 -18
- package/lib/Utils/baileys-event-stream.d.ts +0 -16
- package/lib/Utils/business.d.ts +0 -22
- package/lib/Utils/chat-utils.d.ts +0 -71
- package/lib/Utils/crypto.d.ts +0 -41
- package/lib/Utils/decode-wa-message.d.ts +0 -19
- package/lib/Utils/event-buffer.d.ts +0 -35
- package/lib/Utils/generics.d.ts +0 -92
- package/lib/Utils/history.d.ts +0 -15
- package/lib/Utils/index.d.ts +0 -17
- package/lib/Utils/link-preview.d.ts +0 -21
- package/lib/Utils/logger.d.ts +0 -4
- package/lib/Utils/lt-hash.d.ts +0 -12
- package/lib/Utils/make-mutex.d.ts +0 -7
- package/lib/Utils/messages-media.d.ts +0 -116
- package/lib/Utils/messages.d.ts +0 -77
- package/lib/Utils/noise-handler.d.ts +0 -21
- package/lib/Utils/process-message.d.ts +0 -41
- package/lib/Utils/signal.d.ts +0 -32
- package/lib/Utils/use-multi-file-auth-state.d.ts +0 -13
- package/lib/Utils/validate-connection.d.ts +0 -11
- package/lib/WABinary/constants.d.ts +0 -30
- package/lib/WABinary/decode.d.ts +0 -7
- package/lib/WABinary/encode.d.ts +0 -3
- package/lib/WABinary/generic-utils.d.ts +0 -17
- package/lib/WABinary/index.d.ts +0 -5
- package/lib/WABinary/jid-utils.d.ts +0 -31
- package/lib/WABinary/types.d.ts +0 -18
- package/lib/WAM/BinaryInfo.d.ts +0 -17
- package/lib/WAM/constants.d.ts +0 -38
- package/lib/WAM/encode.d.ts +0 -3
- package/lib/WAM/index.d.ts +0 -3
- package/lib/WAUSync/Protocols/USyncContactProtocol.d.ts +0 -9
- package/lib/WAUSync/Protocols/USyncDeviceProtocol.d.ts +0 -22
- package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.d.ts +0 -12
- package/lib/WAUSync/Protocols/USyncStatusProtocol.d.ts +0 -12
- package/lib/WAUSync/Protocols/UsyncBotProfileProtocol.d.ts +0 -25
- package/lib/WAUSync/Protocols/UsyncLIDProtocol.d.ts +0 -8
- package/lib/WAUSync/Protocols/index.d.ts +0 -4
- package/lib/WAUSync/USyncQuery.d.ts +0 -28
- package/lib/WAUSync/USyncUser.d.ts +0 -12
- package/lib/WAUSync/index.d.ts +0 -3
- package/lib/index.d.ts +0 -12
package/lib/Socket/socket.js
CHANGED
|
@@ -1,65 +1,63 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
1
|
+
import { Boom } from '@hapi/boom';
|
|
2
|
+
import { randomBytes } from 'crypto';
|
|
3
|
+
import { URL } from 'url';
|
|
4
|
+
import { promisify } from 'util';
|
|
5
|
+
import { proto } from '../../WAProto/index.js';
|
|
6
|
+
import { DEF_CALLBACK_PREFIX, DEF_TAG_PREFIX, INITIAL_PREKEY_COUNT, MIN_PREKEY_COUNT, NOISE_WA_HEADER, PROCESSABLE_HISTORY_TYPES, TimeMs, UPLOAD_TIMEOUT } from '../Defaults/index.js';
|
|
7
|
+
import { QueryIds, ReachoutTimelockEnforcementType } from '../Types/index.js';
|
|
8
|
+
import { DisconnectReason, XWAPaths } from '../Types/index.js';
|
|
9
|
+
import { addTransactionCapability, aesEncryptCTR, bindWaitForConnectionUpdate, buildPairingQRData, bytesToCrockford, configureSuccessfulPairing, Curve, derivePairingCodeKey, generateLoginNode, generateMdTagPrefix, generateRegistrationNode, getCodeFromWSError, getCompanionPlatformId, getErrorCodeFromStreamError, getNextPreKeysNode, makeEventBuffer, makeNoiseHandler, promiseTimeout, signedKeyPair, xmppSignedPreKey } from '../Utils/index.js';
|
|
10
|
+
import { assertNodeErrorFree, binaryNodeToString, encodeBinaryNode, getAllBinaryNodeChildren, getBinaryNodeChild, getBinaryNodeChildren, isLidUser, jidDecode, jidEncode, S_WHATSAPP_NET } from '../WABinary/index.js';
|
|
11
|
+
import { BinaryInfo } from '../WAM/BinaryInfo.js';
|
|
12
|
+
import { USyncQuery, USyncUser } from '../WAUSync/index.js';
|
|
13
|
+
import { WebSocketClient } from './Client/index.js';
|
|
14
|
+
import { executeWMexQuery } from './mex.js';
|
|
14
15
|
/**
|
|
15
16
|
* Connects to WA servers and performs:
|
|
16
17
|
* - simple queries (no retry mechanism, wait for connection establishment)
|
|
17
18
|
* - listen to messages and emit events
|
|
18
19
|
* - query phone connection
|
|
19
20
|
*/
|
|
20
|
-
const makeSocket = (config) => {
|
|
21
|
-
|
|
22
|
-
const
|
|
23
|
-
|
|
21
|
+
export const makeSocket = (config) => {
|
|
22
|
+
const { waWebSocketUrl, connectTimeoutMs, logger, keepAliveIntervalMs, browser, auth: authState, printQRInTerminal, defaultQueryTimeoutMs, transactionOpts, qrTimeout, makeSignalRepository } = config;
|
|
23
|
+
const publicWAMBuffer = new BinaryInfo();
|
|
24
|
+
let serverTimeOffsetMs = 0;
|
|
25
|
+
const uqTagId = generateMdTagPrefix();
|
|
26
|
+
const generateMessageTag = () => `XZCYYX-${epoch++}`;
|
|
27
|
+
if (printQRInTerminal) {
|
|
28
|
+
logger.warn({}, '⚠️ The printQRInTerminal option has been deprecated. You will no longer receive QR codes in the terminal automatically. Please listen to the connection.update event yourself and handle the QR your way. You can remove this message by removing this opttion. This message will be removed in a future version.');
|
|
29
|
+
}
|
|
30
|
+
const syncDisabled = PROCESSABLE_HISTORY_TYPES.map(syncType => config.shouldSyncHistoryMessage({ syncType })).filter(x => x === false)
|
|
31
|
+
.length === PROCESSABLE_HISTORY_TYPES.length;
|
|
32
|
+
if (syncDisabled) {
|
|
33
|
+
logger.warn('⚠️ DANGER: DISABLING ALL SYNC BY shouldSyncHistoryMsg PREVENTS BAILEYS FROM ACCESSING INITIAL LID MAPPINGS, LEADING TO INSTABILIY AND SESSION ERRORS');
|
|
34
|
+
}
|
|
35
|
+
const url = typeof waWebSocketUrl === 'string' ? new URL(waWebSocketUrl) : waWebSocketUrl;
|
|
24
36
|
if (config.mobile || url.protocol === 'tcp:') {
|
|
25
|
-
throw new
|
|
26
|
-
statusCode: Types_1.DisconnectReason.loggedOut
|
|
27
|
-
});
|
|
37
|
+
throw new Boom('Mobile API is not supported anymore', { statusCode: DisconnectReason.loggedOut });
|
|
28
38
|
}
|
|
29
|
-
if (url.protocol === 'wss' &&
|
|
39
|
+
if (url.protocol === 'wss' && authState?.creds?.routingInfo) {
|
|
30
40
|
url.searchParams.append('ED', authState.creds.routingInfo.toString('base64url'));
|
|
31
41
|
}
|
|
32
|
-
const ws = new Client_1.WebSocketClient(url, config);
|
|
33
|
-
ws.connect();
|
|
34
|
-
const ev = (0, Utils_1.makeEventBuffer)(logger);
|
|
35
42
|
/** ephemeral key pair used to encrypt/decrypt communication. Unique for each connection */
|
|
36
|
-
const ephemeralKeyPair =
|
|
43
|
+
const ephemeralKeyPair = Curve.generateKeyPair();
|
|
37
44
|
/** WA noise protocol wrapper */
|
|
38
|
-
const noise =
|
|
45
|
+
const noise = makeNoiseHandler({
|
|
39
46
|
keyPair: ephemeralKeyPair,
|
|
40
|
-
NOISE_HEADER:
|
|
47
|
+
NOISE_HEADER: NOISE_WA_HEADER,
|
|
41
48
|
logger,
|
|
42
|
-
routingInfo:
|
|
49
|
+
routingInfo: authState?.creds?.routingInfo
|
|
43
50
|
});
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
const
|
|
47
|
-
const signalRepository = makeSignalRepository({ creds, keys });
|
|
48
|
-
let lastDateRecv;
|
|
49
|
-
let epoch = 1;
|
|
50
|
-
let keepAliveReq;
|
|
51
|
-
let qrTimer;
|
|
52
|
-
let closed = false;
|
|
53
|
-
const uqTagId = (0, Utils_1.generateMdTagPrefix)();
|
|
54
|
-
const generateMessageTag = () => `${uqTagId}${epoch++}`;
|
|
55
|
-
const sendPromise = (0, util_1.promisify)(ws.send);
|
|
51
|
+
const ws = new WebSocketClient(url, config);
|
|
52
|
+
ws.connect();
|
|
53
|
+
const sendPromise = promisify(ws.send);
|
|
56
54
|
/** send a raw buffer */
|
|
57
55
|
const sendRawMessage = async (data) => {
|
|
58
56
|
if (!ws.isOpen) {
|
|
59
|
-
throw new
|
|
57
|
+
throw new Boom('Connection Closed', { statusCode: DisconnectReason.connectionClosed });
|
|
60
58
|
}
|
|
61
59
|
const bytes = noise.encodeFrame(data);
|
|
62
|
-
await
|
|
60
|
+
await promiseTimeout(connectTimeoutMs, async (resolve, reject) => {
|
|
63
61
|
try {
|
|
64
62
|
await sendPromise.call(ws, bytes);
|
|
65
63
|
resolve();
|
|
@@ -72,60 +70,11 @@ const makeSocket = (config) => {
|
|
|
72
70
|
/** send a binary node */
|
|
73
71
|
const sendNode = (frame) => {
|
|
74
72
|
if (logger.level === 'trace') {
|
|
75
|
-
logger.trace({ xml:
|
|
73
|
+
logger.trace({ xml: binaryNodeToString(frame), msg: 'xml send' });
|
|
76
74
|
}
|
|
77
|
-
const buff =
|
|
75
|
+
const buff = encodeBinaryNode(frame);
|
|
78
76
|
return sendRawMessage(buff);
|
|
79
77
|
};
|
|
80
|
-
/** log & process any unexpected errors */
|
|
81
|
-
const onUnexpectedError = (err, msg) => {
|
|
82
|
-
logger.error({ err }, `unexpected error in '${msg}'`);
|
|
83
|
-
const message = (err && ((err.stack || err.message) || String(err))).toLowerCase();
|
|
84
|
-
// auto recover from cryptographic desyncs by re-uploading prekeys
|
|
85
|
-
if (message.includes('bad mac') || (message.includes('mac') && message.includes('invalid'))) {
|
|
86
|
-
try {
|
|
87
|
-
uploadPreKeysToServerIfRequired(true)
|
|
88
|
-
.catch(e => logger.warn({ e }, 'failed to re-upload prekeys after bad mac'));
|
|
89
|
-
}
|
|
90
|
-
catch (_e) {
|
|
91
|
-
// ignore
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
// gently back off when encountering rate limits (429)
|
|
95
|
-
if (message.includes('429') || message.includes('rate limit')) {
|
|
96
|
-
const wait = Math.min(30000, (config.backoffDelayMs || 5000));
|
|
97
|
-
logger.info({ wait }, 'backing off due to rate limit');
|
|
98
|
-
setTimeout(() => {
|
|
99
|
-
// intentionally empty; wait to delay further sends
|
|
100
|
-
}, wait);
|
|
101
|
-
}
|
|
102
|
-
};
|
|
103
|
-
/** await the next incoming message */
|
|
104
|
-
const awaitNextMessage = async (sendMsg) => {
|
|
105
|
-
if (!ws.isOpen) {
|
|
106
|
-
throw new boom_1.Boom('Connection Closed', {
|
|
107
|
-
statusCode: Types_1.DisconnectReason.connectionClosed
|
|
108
|
-
});
|
|
109
|
-
}
|
|
110
|
-
let onOpen;
|
|
111
|
-
let onClose;
|
|
112
|
-
const result = (0, Utils_1.promiseTimeout)(connectTimeoutMs, (resolve, reject) => {
|
|
113
|
-
onOpen = resolve;
|
|
114
|
-
onClose = mapWebSocketError(reject);
|
|
115
|
-
ws.on('frame', onOpen);
|
|
116
|
-
ws.on('close', onClose);
|
|
117
|
-
ws.on('error', onClose);
|
|
118
|
-
})
|
|
119
|
-
.finally(() => {
|
|
120
|
-
ws.off('frame', onOpen);
|
|
121
|
-
ws.off('close', onClose);
|
|
122
|
-
ws.off('error', onClose);
|
|
123
|
-
});
|
|
124
|
-
if (sendMsg) {
|
|
125
|
-
sendRawMessage(sendMsg).catch(onClose);
|
|
126
|
-
}
|
|
127
|
-
return result;
|
|
128
|
-
};
|
|
129
78
|
/**
|
|
130
79
|
* Wait for a message with a certain tag to be received
|
|
131
80
|
* @param msgId the message tag to await
|
|
@@ -135,21 +84,38 @@ const makeSocket = (config) => {
|
|
|
135
84
|
let onRecv;
|
|
136
85
|
let onErr;
|
|
137
86
|
try {
|
|
138
|
-
const result = await
|
|
139
|
-
onRecv =
|
|
87
|
+
const result = await promiseTimeout(timeoutMs, (resolve, reject) => {
|
|
88
|
+
onRecv = data => {
|
|
89
|
+
resolve(data);
|
|
90
|
+
};
|
|
140
91
|
onErr = err => {
|
|
141
|
-
reject(err ||
|
|
92
|
+
reject(err ||
|
|
93
|
+
new Boom('Connection Closed', {
|
|
94
|
+
statusCode: DisconnectReason.connectionClosed
|
|
95
|
+
}));
|
|
142
96
|
};
|
|
143
97
|
ws.on(`TAG:${msgId}`, onRecv);
|
|
144
|
-
ws.on('close', onErr);
|
|
145
|
-
ws.
|
|
98
|
+
ws.on('close', onErr);
|
|
99
|
+
ws.on('error', onErr);
|
|
100
|
+
return () => reject(new Boom('Query Cancelled'));
|
|
146
101
|
});
|
|
147
102
|
return result;
|
|
148
103
|
}
|
|
104
|
+
catch (error) {
|
|
105
|
+
// Catch timeout and return undefined instead of throwing
|
|
106
|
+
if (error instanceof Boom && error.output?.statusCode === DisconnectReason.timedOut) {
|
|
107
|
+
logger?.warn?.({ msgId }, 'timed out waiting for message');
|
|
108
|
+
return undefined;
|
|
109
|
+
}
|
|
110
|
+
throw error;
|
|
111
|
+
}
|
|
149
112
|
finally {
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
113
|
+
if (onRecv)
|
|
114
|
+
ws.off(`TAG:${msgId}`, onRecv);
|
|
115
|
+
if (onErr) {
|
|
116
|
+
ws.off('close', onErr);
|
|
117
|
+
ws.off('error', onErr);
|
|
118
|
+
}
|
|
153
119
|
}
|
|
154
120
|
};
|
|
155
121
|
/** send a query, and wait for its response. auto-generates message ID if not provided */
|
|
@@ -158,12 +124,180 @@ const makeSocket = (config) => {
|
|
|
158
124
|
node.attrs.id = generateMessageTag();
|
|
159
125
|
}
|
|
160
126
|
const msgId = node.attrs.id;
|
|
161
|
-
const
|
|
162
|
-
waitForMessage(msgId, timeoutMs)
|
|
127
|
+
const result = await promiseTimeout(timeoutMs, async (resolve, reject) => {
|
|
128
|
+
const result = waitForMessage(msgId, timeoutMs).catch(reject);
|
|
163
129
|
sendNode(node)
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
130
|
+
.then(async () => resolve(await result))
|
|
131
|
+
.catch(reject);
|
|
132
|
+
});
|
|
133
|
+
if (result && 'tag' in result) {
|
|
134
|
+
assertNodeErrorFree(result);
|
|
135
|
+
}
|
|
136
|
+
return result;
|
|
137
|
+
};
|
|
138
|
+
// Validate current key-bundle on server; on failure, trigger pre-key upload and rethrow
|
|
139
|
+
const digestKeyBundle = async () => {
|
|
140
|
+
const res = await query({
|
|
141
|
+
tag: 'iq',
|
|
142
|
+
attrs: { to: S_WHATSAPP_NET, type: 'get', xmlns: 'encrypt' },
|
|
143
|
+
content: [{ tag: 'digest', attrs: {} }]
|
|
144
|
+
});
|
|
145
|
+
const digestNode = getBinaryNodeChild(res, 'digest');
|
|
146
|
+
if (!digestNode) {
|
|
147
|
+
await uploadPreKeys();
|
|
148
|
+
throw new Error('encrypt/get digest returned no digest node');
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
// Rotate our signed pre-key on server; on failure, run digest as fallback and rethrow
|
|
152
|
+
const rotateSignedPreKey = async () => {
|
|
153
|
+
const newId = (creds.signedPreKey.keyId || 0) + 1;
|
|
154
|
+
const skey = await signedKeyPair(creds.signedIdentityKey, newId);
|
|
155
|
+
await query({
|
|
156
|
+
tag: 'iq',
|
|
157
|
+
attrs: { to: S_WHATSAPP_NET, type: 'set', xmlns: 'encrypt' },
|
|
158
|
+
content: [
|
|
159
|
+
{
|
|
160
|
+
tag: 'rotate',
|
|
161
|
+
attrs: {},
|
|
162
|
+
content: [xmppSignedPreKey(skey)]
|
|
163
|
+
}
|
|
164
|
+
]
|
|
165
|
+
});
|
|
166
|
+
// Persist new signed pre-key in creds
|
|
167
|
+
ev.emit('creds.update', { signedPreKey: skey });
|
|
168
|
+
};
|
|
169
|
+
const executeUSyncQuery = async (usyncQuery) => {
|
|
170
|
+
if (usyncQuery.protocols.length === 0) {
|
|
171
|
+
throw new Boom('USyncQuery must have at least one protocol');
|
|
172
|
+
}
|
|
173
|
+
// todo: validate users, throw WARNING on no valid users
|
|
174
|
+
// variable below has only validated users
|
|
175
|
+
const validUsers = usyncQuery.users;
|
|
176
|
+
const userNodes = validUsers.map(user => {
|
|
177
|
+
return {
|
|
178
|
+
tag: 'user',
|
|
179
|
+
attrs: {
|
|
180
|
+
jid: !user.phone ? user.id : undefined
|
|
181
|
+
},
|
|
182
|
+
content: usyncQuery.protocols.map(a => a.getUserElement(user)).filter(a => a !== null)
|
|
183
|
+
};
|
|
184
|
+
});
|
|
185
|
+
const listNode = {
|
|
186
|
+
tag: 'list',
|
|
187
|
+
attrs: {},
|
|
188
|
+
content: userNodes
|
|
189
|
+
};
|
|
190
|
+
const queryNode = {
|
|
191
|
+
tag: 'query',
|
|
192
|
+
attrs: {},
|
|
193
|
+
content: usyncQuery.protocols.map(a => a.getQueryElement())
|
|
194
|
+
};
|
|
195
|
+
const iq = {
|
|
196
|
+
tag: 'iq',
|
|
197
|
+
attrs: {
|
|
198
|
+
to: S_WHATSAPP_NET,
|
|
199
|
+
type: 'get',
|
|
200
|
+
xmlns: 'usync'
|
|
201
|
+
},
|
|
202
|
+
content: [
|
|
203
|
+
{
|
|
204
|
+
tag: 'usync',
|
|
205
|
+
attrs: {
|
|
206
|
+
context: usyncQuery.context,
|
|
207
|
+
mode: usyncQuery.mode,
|
|
208
|
+
sid: generateMessageTag(),
|
|
209
|
+
last: 'true',
|
|
210
|
+
index: '0'
|
|
211
|
+
},
|
|
212
|
+
content: [queryNode, listNode]
|
|
213
|
+
}
|
|
214
|
+
]
|
|
215
|
+
};
|
|
216
|
+
const result = await query(iq);
|
|
217
|
+
return usyncQuery.parseUSyncQueryResult(result);
|
|
218
|
+
};
|
|
219
|
+
const onWhatsApp = async (...phoneNumber) => {
|
|
220
|
+
let usyncQuery = new USyncQuery().withContactProtocol().withLIDProtocol();
|
|
221
|
+
let contactEnabled = false;
|
|
222
|
+
for (const jid of phoneNumber) {
|
|
223
|
+
if (isLidUser(jid)) {
|
|
224
|
+
logger?.warn('LIDs are not supported with onWhatsApp');
|
|
225
|
+
continue;
|
|
226
|
+
}
|
|
227
|
+
else {
|
|
228
|
+
if (!contactEnabled) {
|
|
229
|
+
contactEnabled = true;
|
|
230
|
+
usyncQuery = usyncQuery.withContactProtocol();
|
|
231
|
+
}
|
|
232
|
+
const phone = `+${jid.replace('+', '').split('@')[0]?.split(':')[0]}`;
|
|
233
|
+
usyncQuery.withUser(new USyncUser().withPhone(phone));
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
if (usyncQuery.users.length === 0) {
|
|
237
|
+
return []; // return early without forcing an empty query
|
|
238
|
+
}
|
|
239
|
+
const results = await executeUSyncQuery(usyncQuery);
|
|
240
|
+
if (results) {
|
|
241
|
+
return results.list.filter(a => !!a.contact).map(({ contact, id, lid }) => ({ jid: id, lid, exists: contact }));
|
|
242
|
+
}
|
|
243
|
+
};
|
|
244
|
+
const pnFromLIDUSync = async (jids) => {
|
|
245
|
+
const usyncQuery = new USyncQuery().withLIDProtocol().withContext('background');
|
|
246
|
+
for (const jid of jids) {
|
|
247
|
+
if (isLidUser(jid)) {
|
|
248
|
+
logger?.warn('LID user found in LID fetch call');
|
|
249
|
+
continue;
|
|
250
|
+
}
|
|
251
|
+
else {
|
|
252
|
+
usyncQuery.withUser(new USyncUser().withId(jid));
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
if (usyncQuery.users.length === 0) {
|
|
256
|
+
return []; // return early without forcing an empty query
|
|
257
|
+
}
|
|
258
|
+
const results = await executeUSyncQuery(usyncQuery);
|
|
259
|
+
if (results) {
|
|
260
|
+
return results.list.filter(a => !!a.lid).map(({ lid, id }) => ({ pn: id, lid: lid }));
|
|
261
|
+
}
|
|
262
|
+
return [];
|
|
263
|
+
};
|
|
264
|
+
const ev = makeEventBuffer(logger);
|
|
265
|
+
const { creds } = authState;
|
|
266
|
+
// add transaction capability
|
|
267
|
+
const keys = addTransactionCapability(authState.keys, logger, transactionOpts);
|
|
268
|
+
const signalRepository = makeSignalRepository({ creds, keys }, logger, pnFromLIDUSync);
|
|
269
|
+
let lastDateRecv;
|
|
270
|
+
let epoch = 1;
|
|
271
|
+
let keepAliveReq;
|
|
272
|
+
let qrTimer;
|
|
273
|
+
let closed = false;
|
|
274
|
+
const socketEndHandlers = [];
|
|
275
|
+
/** log & process any unexpected errors */
|
|
276
|
+
const onUnexpectedError = (err, msg) => {
|
|
277
|
+
logger.error({ err }, `unexpected error in '${msg}'`);
|
|
278
|
+
};
|
|
279
|
+
/** await the next incoming message */
|
|
280
|
+
const awaitNextMessage = async (sendMsg) => {
|
|
281
|
+
if (!ws.isOpen) {
|
|
282
|
+
throw new Boom('Connection Closed', {
|
|
283
|
+
statusCode: DisconnectReason.connectionClosed
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
let onOpen;
|
|
287
|
+
let onClose;
|
|
288
|
+
const result = promiseTimeout(connectTimeoutMs, (resolve, reject) => {
|
|
289
|
+
onOpen = resolve;
|
|
290
|
+
onClose = mapWebSocketError(reject);
|
|
291
|
+
ws.on('frame', onOpen);
|
|
292
|
+
ws.on('close', onClose);
|
|
293
|
+
ws.on('error', onClose);
|
|
294
|
+
}).finally(() => {
|
|
295
|
+
ws.off('frame', onOpen);
|
|
296
|
+
ws.off('close', onClose);
|
|
297
|
+
ws.off('error', onClose);
|
|
298
|
+
});
|
|
299
|
+
if (sendMsg) {
|
|
300
|
+
sendRawMessage(sendMsg).catch(onClose);
|
|
167
301
|
}
|
|
168
302
|
return result;
|
|
169
303
|
};
|
|
@@ -172,30 +306,30 @@ const makeSocket = (config) => {
|
|
|
172
306
|
let helloMsg = {
|
|
173
307
|
clientHello: { ephemeral: ephemeralKeyPair.public }
|
|
174
308
|
};
|
|
175
|
-
helloMsg =
|
|
309
|
+
helloMsg = proto.HandshakeMessage.fromObject(helloMsg);
|
|
176
310
|
logger.info({ browser, helloMsg }, 'connected to WA');
|
|
177
|
-
const init =
|
|
311
|
+
const init = proto.HandshakeMessage.encode(helloMsg).finish();
|
|
178
312
|
const result = await awaitNextMessage(init);
|
|
179
|
-
const handshake =
|
|
313
|
+
const handshake = proto.HandshakeMessage.decode(result);
|
|
180
314
|
logger.trace({ handshake }, 'handshake recv from WA');
|
|
181
|
-
const keyEnc =
|
|
315
|
+
const keyEnc = noise.processHandshake(handshake, creds.noiseKey);
|
|
182
316
|
let node;
|
|
183
317
|
if (!creds.me) {
|
|
184
|
-
node =
|
|
318
|
+
node = generateRegistrationNode(creds, config);
|
|
185
319
|
logger.info({ node }, 'not logged in, attempting registration...');
|
|
186
320
|
}
|
|
187
321
|
else {
|
|
188
|
-
node =
|
|
322
|
+
node = generateLoginNode(creds.me.id, config);
|
|
189
323
|
logger.info({ node }, 'logging in...');
|
|
190
324
|
}
|
|
191
|
-
const payloadEnc = noise.encrypt(
|
|
192
|
-
await sendRawMessage(
|
|
325
|
+
const payloadEnc = noise.encrypt(proto.ClientPayload.encode(node).finish());
|
|
326
|
+
await sendRawMessage(proto.HandshakeMessage.encode({
|
|
193
327
|
clientFinish: {
|
|
194
328
|
static: keyEnc,
|
|
195
|
-
payload: payloadEnc
|
|
196
|
-
}
|
|
329
|
+
payload: payloadEnc
|
|
330
|
+
}
|
|
197
331
|
}).finish());
|
|
198
|
-
noise.finishInit();
|
|
332
|
+
await noise.finishInit();
|
|
199
333
|
startKeepAliveRequest();
|
|
200
334
|
};
|
|
201
335
|
const getAvailablePreKeysOnServer = async () => {
|
|
@@ -205,35 +339,104 @@ const makeSocket = (config) => {
|
|
|
205
339
|
id: generateMessageTag(),
|
|
206
340
|
xmlns: 'encrypt',
|
|
207
341
|
type: 'get',
|
|
208
|
-
to:
|
|
342
|
+
to: S_WHATSAPP_NET
|
|
209
343
|
},
|
|
210
|
-
content: [
|
|
211
|
-
{ tag: 'count', attrs: {} }
|
|
212
|
-
]
|
|
344
|
+
content: [{ tag: 'count', attrs: {} }]
|
|
213
345
|
});
|
|
214
|
-
const countChild =
|
|
346
|
+
const countChild = getBinaryNodeChild(result, 'count');
|
|
215
347
|
return +countChild.attrs.value;
|
|
216
348
|
};
|
|
349
|
+
// WAWeb has no time throttle here; the server drives uploads via PreKeyLow notifications.
|
|
350
|
+
let uploadPreKeysPromise = null;
|
|
217
351
|
/** generates and uploads a set of pre-keys to the server */
|
|
218
|
-
const uploadPreKeys = async (count =
|
|
219
|
-
|
|
220
|
-
logger.
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
352
|
+
const uploadPreKeys = async (count = MIN_PREKEY_COUNT) => {
|
|
353
|
+
if (uploadPreKeysPromise) {
|
|
354
|
+
logger.debug('Pre-key upload already in progress, waiting for completion');
|
|
355
|
+
await uploadPreKeysPromise;
|
|
356
|
+
return;
|
|
357
|
+
}
|
|
358
|
+
const uploadLogic = async (retryCount) => {
|
|
359
|
+
logger.info({ count, retryCount }, 'uploading pre-keys');
|
|
360
|
+
// Generate and save pre-keys atomically (prevents ID collisions on retry)
|
|
361
|
+
const node = await keys.transaction(async () => {
|
|
362
|
+
logger.debug({ requestedCount: count }, 'generating pre-keys with requested count');
|
|
363
|
+
const { update, node } = await getNextPreKeysNode({ creds, keys }, count);
|
|
364
|
+
// Update credentials immediately to prevent duplicate IDs on retry
|
|
365
|
+
ev.emit('creds.update', update);
|
|
366
|
+
return node;
|
|
367
|
+
}, creds?.me?.id || 'upload-pre-keys');
|
|
368
|
+
// Upload to server (outside transaction, can fail without affecting local keys)
|
|
369
|
+
try {
|
|
370
|
+
await query(node);
|
|
371
|
+
logger.info({ count }, 'uploaded pre-keys successfully');
|
|
372
|
+
}
|
|
373
|
+
catch (uploadError) {
|
|
374
|
+
logger.error({ uploadError: uploadError.toString(), count }, 'Failed to upload pre-keys to server');
|
|
375
|
+
// Recurse into uploadLogic; calling uploadPreKeys would await its own in-flight promise.
|
|
376
|
+
if (retryCount < 3) {
|
|
377
|
+
const backoffDelay = Math.min(1000 * Math.pow(2, retryCount), 10000);
|
|
378
|
+
logger.info(`Retrying pre-key upload in ${backoffDelay}ms`);
|
|
379
|
+
await new Promise(resolve => setTimeout(resolve, backoffDelay));
|
|
380
|
+
return uploadLogic(retryCount + 1);
|
|
381
|
+
}
|
|
382
|
+
throw uploadError;
|
|
383
|
+
}
|
|
384
|
+
};
|
|
385
|
+
// Add timeout protection
|
|
386
|
+
uploadPreKeysPromise = Promise.race([
|
|
387
|
+
uploadLogic(0),
|
|
388
|
+
new Promise((_, reject) => setTimeout(() => reject(new Boom('Pre-key upload timeout', { statusCode: 408 })), UPLOAD_TIMEOUT))
|
|
389
|
+
]);
|
|
390
|
+
try {
|
|
391
|
+
await uploadPreKeysPromise;
|
|
392
|
+
}
|
|
393
|
+
finally {
|
|
394
|
+
uploadPreKeysPromise = null;
|
|
395
|
+
}
|
|
396
|
+
};
|
|
397
|
+
const verifyCurrentPreKeyExists = async () => {
|
|
398
|
+
const currentPreKeyId = creds.nextPreKeyId - 1;
|
|
399
|
+
if (currentPreKeyId <= 0) {
|
|
400
|
+
return { exists: false, currentPreKeyId: 0 };
|
|
401
|
+
}
|
|
402
|
+
const preKeys = await keys.get('pre-key', [currentPreKeyId.toString()]);
|
|
403
|
+
const exists = !!preKeys[currentPreKeyId.toString()];
|
|
404
|
+
return { exists, currentPreKeyId };
|
|
226
405
|
};
|
|
227
406
|
const uploadPreKeysToServerIfRequired = async () => {
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
407
|
+
try {
|
|
408
|
+
let count = 0;
|
|
409
|
+
const preKeyCount = await getAvailablePreKeysOnServer();
|
|
410
|
+
if (preKeyCount === 0)
|
|
411
|
+
count = INITIAL_PREKEY_COUNT;
|
|
412
|
+
else
|
|
413
|
+
count = MIN_PREKEY_COUNT;
|
|
414
|
+
const { exists: currentPreKeyExists, currentPreKeyId } = await verifyCurrentPreKeyExists();
|
|
415
|
+
logger.info(`${preKeyCount} pre-keys found on server`);
|
|
416
|
+
logger.info(`Current prekey ID: ${currentPreKeyId}, exists in storage: ${currentPreKeyExists}`);
|
|
417
|
+
const lowServerCount = preKeyCount <= count;
|
|
418
|
+
const missingCurrentPreKey = !currentPreKeyExists && currentPreKeyId > 0;
|
|
419
|
+
const shouldUpload = lowServerCount || missingCurrentPreKey;
|
|
420
|
+
if (shouldUpload) {
|
|
421
|
+
const reasons = [];
|
|
422
|
+
if (lowServerCount)
|
|
423
|
+
reasons.push(`server count low (${preKeyCount})`);
|
|
424
|
+
if (missingCurrentPreKey)
|
|
425
|
+
reasons.push(`current prekey ${currentPreKeyId} missing from storage`);
|
|
426
|
+
logger.info(`Uploading PreKeys due to: ${reasons.join(', ')}`);
|
|
427
|
+
await uploadPreKeys(count);
|
|
428
|
+
}
|
|
429
|
+
else {
|
|
430
|
+
logger.info(`PreKey validation passed - Server: ${preKeyCount}, Current prekey ${currentPreKeyId} exists`);
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
catch (error) {
|
|
434
|
+
logger.error({ error }, 'Failed to check/upload pre-keys during initialization');
|
|
435
|
+
// Don't throw - allow connection to continue even if pre-key check fails
|
|
232
436
|
}
|
|
233
437
|
};
|
|
234
|
-
const onMessageReceived = (data) => {
|
|
235
|
-
noise.decodeFrame(data, frame => {
|
|
236
|
-
var _a;
|
|
438
|
+
const onMessageReceived = async (data) => {
|
|
439
|
+
await noise.decodeFrame(data, frame => {
|
|
237
440
|
// reset ping timeout
|
|
238
441
|
lastDateRecv = new Date();
|
|
239
442
|
let anyTriggered = false;
|
|
@@ -242,45 +445,53 @@ const makeSocket = (config) => {
|
|
|
242
445
|
if (!(frame instanceof Uint8Array)) {
|
|
243
446
|
const msgId = frame.attrs.id;
|
|
244
447
|
if (logger.level === 'trace') {
|
|
245
|
-
logger.trace({ xml:
|
|
448
|
+
logger.trace({ xml: binaryNodeToString(frame), msg: 'recv xml' });
|
|
246
449
|
}
|
|
247
450
|
/* Check if this is a response to a message we sent */
|
|
248
|
-
anyTriggered = ws.emit(`${
|
|
451
|
+
anyTriggered = ws.emit(`${DEF_TAG_PREFIX}${msgId}`, frame) || anyTriggered;
|
|
249
452
|
/* Check if this is a response to a message we are expecting */
|
|
250
453
|
const l0 = frame.tag;
|
|
251
454
|
const l1 = frame.attrs || {};
|
|
252
|
-
const l2 = Array.isArray(frame.content) ?
|
|
455
|
+
const l2 = Array.isArray(frame.content) ? frame.content[0]?.tag : '';
|
|
253
456
|
for (const key of Object.keys(l1)) {
|
|
254
|
-
anyTriggered = ws.emit(`${
|
|
255
|
-
anyTriggered = ws.emit(`${
|
|
256
|
-
anyTriggered = ws.emit(`${
|
|
457
|
+
anyTriggered = ws.emit(`${DEF_CALLBACK_PREFIX}${l0},${key}:${l1[key]},${l2}`, frame) || anyTriggered;
|
|
458
|
+
anyTriggered = ws.emit(`${DEF_CALLBACK_PREFIX}${l0},${key}:${l1[key]}`, frame) || anyTriggered;
|
|
459
|
+
anyTriggered = ws.emit(`${DEF_CALLBACK_PREFIX}${l0},${key}`, frame) || anyTriggered;
|
|
257
460
|
}
|
|
258
|
-
anyTriggered = ws.emit(`${
|
|
259
|
-
anyTriggered = ws.emit(`${
|
|
461
|
+
anyTriggered = ws.emit(`${DEF_CALLBACK_PREFIX}${l0},,${l2}`, frame) || anyTriggered;
|
|
462
|
+
anyTriggered = ws.emit(`${DEF_CALLBACK_PREFIX}${l0}`, frame) || anyTriggered;
|
|
260
463
|
if (!anyTriggered && logger.level === 'debug') {
|
|
261
464
|
logger.debug({ unhandled: true, msgId, fromMe: false, frame }, 'communication recv');
|
|
262
465
|
}
|
|
263
466
|
}
|
|
264
467
|
});
|
|
265
468
|
};
|
|
266
|
-
const end = (error) => {
|
|
469
|
+
const end = async (error) => {
|
|
267
470
|
if (closed) {
|
|
268
|
-
logger.trace({ trace: error
|
|
471
|
+
logger.trace({ trace: error?.stack }, 'connection already closed');
|
|
269
472
|
return;
|
|
270
473
|
}
|
|
271
474
|
closed = true;
|
|
272
|
-
logger.info({ trace: error
|
|
475
|
+
logger.info({ trace: error?.stack }, error ? 'connection errored' : 'connection closed');
|
|
273
476
|
clearInterval(keepAliveReq);
|
|
274
477
|
clearTimeout(qrTimer);
|
|
275
478
|
ws.removeAllListeners('close');
|
|
276
|
-
ws.removeAllListeners('error');
|
|
277
479
|
ws.removeAllListeners('open');
|
|
278
480
|
ws.removeAllListeners('message');
|
|
481
|
+
signalRepository.close?.();
|
|
279
482
|
if (!ws.isClosed && !ws.isClosing) {
|
|
280
483
|
try {
|
|
281
|
-
ws.close();
|
|
484
|
+
await ws.close();
|
|
485
|
+
}
|
|
486
|
+
catch { }
|
|
487
|
+
}
|
|
488
|
+
for (const handler of socketEndHandlers) {
|
|
489
|
+
try {
|
|
490
|
+
await handler(error);
|
|
491
|
+
}
|
|
492
|
+
catch (err) {
|
|
493
|
+
logger.error({ err }, 'error in socket end handler');
|
|
282
494
|
}
|
|
283
|
-
catch (_a) { }
|
|
284
495
|
}
|
|
285
496
|
ev.emit('connection.update', {
|
|
286
497
|
connection: 'close',
|
|
@@ -290,13 +501,14 @@ const makeSocket = (config) => {
|
|
|
290
501
|
}
|
|
291
502
|
});
|
|
292
503
|
ev.removeAllListeners('connection.update');
|
|
504
|
+
ev.destroy();
|
|
293
505
|
};
|
|
294
506
|
const waitForSocketOpen = async () => {
|
|
295
507
|
if (ws.isOpen) {
|
|
296
508
|
return;
|
|
297
509
|
}
|
|
298
510
|
if (ws.isClosed || ws.isClosing) {
|
|
299
|
-
throw new
|
|
511
|
+
throw new Boom('Connection Closed', { statusCode: DisconnectReason.connectionClosed });
|
|
300
512
|
}
|
|
301
513
|
let onOpen;
|
|
302
514
|
let onClose;
|
|
@@ -306,8 +518,7 @@ const makeSocket = (config) => {
|
|
|
306
518
|
ws.on('open', onOpen);
|
|
307
519
|
ws.on('close', onClose);
|
|
308
520
|
ws.on('error', onClose);
|
|
309
|
-
})
|
|
310
|
-
.finally(() => {
|
|
521
|
+
}).finally(() => {
|
|
311
522
|
ws.off('open', onOpen);
|
|
312
523
|
ws.off('close', onClose);
|
|
313
524
|
ws.off('error', onClose);
|
|
@@ -323,7 +534,7 @@ const makeSocket = (config) => {
|
|
|
323
534
|
it could be that the network is down
|
|
324
535
|
*/
|
|
325
536
|
if (diff > keepAliveIntervalMs + 5000) {
|
|
326
|
-
end(new
|
|
537
|
+
void end(new Boom('Connection was lost', { statusCode: DisconnectReason.connectionLost }));
|
|
327
538
|
}
|
|
328
539
|
else if (ws.isOpen) {
|
|
329
540
|
// if its all good, send a keep alive request
|
|
@@ -331,13 +542,12 @@ const makeSocket = (config) => {
|
|
|
331
542
|
tag: 'iq',
|
|
332
543
|
attrs: {
|
|
333
544
|
id: generateMessageTag(),
|
|
334
|
-
to:
|
|
545
|
+
to: S_WHATSAPP_NET,
|
|
335
546
|
type: 'get',
|
|
336
|
-
xmlns: 'w:p'
|
|
547
|
+
xmlns: 'w:p'
|
|
337
548
|
},
|
|
338
549
|
content: [{ tag: 'ping', attrs: {} }]
|
|
339
|
-
})
|
|
340
|
-
.catch(err => {
|
|
550
|
+
}).catch(err => {
|
|
341
551
|
logger.error({ trace: err.stack }, 'error in sending keep alive');
|
|
342
552
|
});
|
|
343
553
|
}
|
|
@@ -346,26 +556,23 @@ const makeSocket = (config) => {
|
|
|
346
556
|
}
|
|
347
557
|
}, keepAliveIntervalMs));
|
|
348
558
|
/** i have no idea why this exists. pls enlighten me */
|
|
349
|
-
const sendPassiveIq = (tag) =>
|
|
559
|
+
const sendPassiveIq = (tag) => query({
|
|
350
560
|
tag: 'iq',
|
|
351
561
|
attrs: {
|
|
352
|
-
to:
|
|
562
|
+
to: S_WHATSAPP_NET,
|
|
353
563
|
xmlns: 'passive',
|
|
354
|
-
type: 'set'
|
|
564
|
+
type: 'set'
|
|
355
565
|
},
|
|
356
|
-
content: [
|
|
357
|
-
|
|
358
|
-
]
|
|
359
|
-
}));
|
|
566
|
+
content: [{ tag, attrs: {} }]
|
|
567
|
+
});
|
|
360
568
|
/** logout & invalidate connection */
|
|
361
569
|
const logout = async (msg) => {
|
|
362
|
-
|
|
363
|
-
const jid = (_a = authState.creds.me) === null || _a === void 0 ? void 0 : _a.id;
|
|
570
|
+
const jid = authState.creds.me?.id;
|
|
364
571
|
if (jid) {
|
|
365
572
|
await sendNode({
|
|
366
573
|
tag: 'iq',
|
|
367
574
|
attrs: {
|
|
368
|
-
to:
|
|
575
|
+
to: S_WHATSAPP_NET,
|
|
369
576
|
type: 'set',
|
|
370
577
|
id: generateMessageTag(),
|
|
371
578
|
xmlns: 'md'
|
|
@@ -381,30 +588,23 @@ const makeSocket = (config) => {
|
|
|
381
588
|
]
|
|
382
589
|
});
|
|
383
590
|
}
|
|
384
|
-
end(new
|
|
591
|
+
void end(new Boom(msg || 'Intentional Logout', { statusCode: DisconnectReason.loggedOut }));
|
|
385
592
|
};
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
authState.creds.pairingCode = pairKey.toUpperCase();
|
|
393
|
-
} else {
|
|
394
|
-
authState.creds.pairingCode = (0, Utils_1.bytesToCrockford)((0, crypto_1.randomBytes)(5));
|
|
395
|
-
}
|
|
396
|
-
|
|
593
|
+
const requestPairingCode = async (phoneNumber, customPairingCode) => {
|
|
594
|
+
const pairingCode = customPairingCode ?? bytesToCrockford(randomBytes(5));
|
|
595
|
+
if (customPairingCode && customPairingCode?.length !== 8) {
|
|
596
|
+
throw new Error('Custom pairing code must be exactly 8 chars');
|
|
597
|
+
}
|
|
598
|
+
authState.creds.pairingCode = pairingCode;
|
|
397
599
|
authState.creds.me = {
|
|
398
|
-
id:
|
|
600
|
+
id: jidEncode(phoneNumber, 's.whatsapp.net'),
|
|
399
601
|
name: '~'
|
|
400
602
|
};
|
|
401
|
-
|
|
402
603
|
ev.emit('creds.update', authState.creds);
|
|
403
|
-
|
|
404
604
|
await sendNode({
|
|
405
605
|
tag: 'iq',
|
|
406
606
|
attrs: {
|
|
407
|
-
to:
|
|
607
|
+
to: S_WHATSAPP_NET,
|
|
408
608
|
type: 'set',
|
|
409
609
|
id: generateMessageTag(),
|
|
410
610
|
xmlns: 'md'
|
|
@@ -431,7 +631,7 @@ const makeSocket = (config) => {
|
|
|
431
631
|
{
|
|
432
632
|
tag: 'companion_platform_id',
|
|
433
633
|
attrs: {},
|
|
434
|
-
content: (
|
|
634
|
+
content: getCompanionPlatformId(browser)
|
|
435
635
|
},
|
|
436
636
|
{
|
|
437
637
|
tag: 'companion_platform_display',
|
|
@@ -441,34 +641,33 @@ const makeSocket = (config) => {
|
|
|
441
641
|
{
|
|
442
642
|
tag: 'link_code_pairing_nonce',
|
|
443
643
|
attrs: {},
|
|
444
|
-
content:
|
|
644
|
+
content: '0'
|
|
445
645
|
}
|
|
446
646
|
]
|
|
447
647
|
}
|
|
448
648
|
]
|
|
449
649
|
});
|
|
450
|
-
|
|
451
650
|
return authState.creds.pairingCode;
|
|
452
|
-
}
|
|
651
|
+
};
|
|
453
652
|
async function generatePairingKey() {
|
|
454
|
-
const salt =
|
|
455
|
-
const randomIv =
|
|
456
|
-
const key = await
|
|
457
|
-
const ciphered =
|
|
653
|
+
const salt = randomBytes(32);
|
|
654
|
+
const randomIv = randomBytes(16);
|
|
655
|
+
const key = await derivePairingCodeKey(authState.creds.pairingCode, salt);
|
|
656
|
+
const ciphered = aesEncryptCTR(authState.creds.pairingEphemeralKeyPair.public, key, randomIv);
|
|
458
657
|
return Buffer.concat([salt, randomIv, ciphered]);
|
|
459
658
|
}
|
|
460
659
|
const sendWAMBuffer = (wamBuffer) => {
|
|
461
660
|
return query({
|
|
462
661
|
tag: 'iq',
|
|
463
662
|
attrs: {
|
|
464
|
-
to:
|
|
663
|
+
to: S_WHATSAPP_NET,
|
|
465
664
|
id: generateMessageTag(),
|
|
466
665
|
xmlns: 'w:stats'
|
|
467
666
|
},
|
|
468
667
|
content: [
|
|
469
668
|
{
|
|
470
669
|
tag: 'add',
|
|
471
|
-
attrs: {},
|
|
670
|
+
attrs: { t: Math.round(Date.now() / 1000) + '' },
|
|
472
671
|
content: wamBuffer
|
|
473
672
|
}
|
|
474
673
|
]
|
|
@@ -481,26 +680,26 @@ const makeSocket = (config) => {
|
|
|
481
680
|
}
|
|
482
681
|
catch (err) {
|
|
483
682
|
logger.error({ err }, 'error in validating connection');
|
|
484
|
-
end(err);
|
|
683
|
+
void end(err);
|
|
485
684
|
}
|
|
486
685
|
});
|
|
487
686
|
ws.on('error', mapWebSocketError(end));
|
|
488
|
-
ws.on('close', () => end(new
|
|
687
|
+
ws.on('close', () => void end(new Boom('Connection Terminated', { statusCode: DisconnectReason.connectionClosed })));
|
|
489
688
|
// the server terminated the connection
|
|
490
|
-
ws.on('CB:xmlstreamend', () => end(new
|
|
689
|
+
ws.on('CB:xmlstreamend', () => void end(new Boom('Connection Terminated by Server', { statusCode: DisconnectReason.connectionClosed })));
|
|
491
690
|
// QR gen
|
|
492
691
|
ws.on('CB:iq,type:set,pair-device', async (stanza) => {
|
|
493
692
|
const iq = {
|
|
494
693
|
tag: 'iq',
|
|
495
694
|
attrs: {
|
|
496
|
-
to:
|
|
695
|
+
to: S_WHATSAPP_NET,
|
|
497
696
|
type: 'result',
|
|
498
|
-
id: stanza.attrs.id
|
|
697
|
+
id: stanza.attrs.id
|
|
499
698
|
}
|
|
500
699
|
};
|
|
501
700
|
await sendNode(iq);
|
|
502
|
-
const pairDeviceNode =
|
|
503
|
-
const refNodes =
|
|
701
|
+
const pairDeviceNode = getBinaryNodeChild(stanza, 'pair-device');
|
|
702
|
+
const refNodes = getBinaryNodeChildren(pairDeviceNode, 'ref');
|
|
504
703
|
const noiseKeyB64 = Buffer.from(creds.noiseKey.public).toString('base64');
|
|
505
704
|
const identityKeyB64 = Buffer.from(creds.signedIdentityKey.public).toString('base64');
|
|
506
705
|
const advB64 = creds.advSecretKey;
|
|
@@ -511,11 +710,11 @@ const makeSocket = (config) => {
|
|
|
511
710
|
}
|
|
512
711
|
const refNode = refNodes.shift();
|
|
513
712
|
if (!refNode) {
|
|
514
|
-
end(new
|
|
713
|
+
void end(new Boom('QR refs attempts ended', { statusCode: DisconnectReason.timedOut }));
|
|
515
714
|
return;
|
|
516
715
|
}
|
|
517
716
|
const ref = refNode.content.toString('utf-8');
|
|
518
|
-
const qr =
|
|
717
|
+
const qr = buildPairingQRData(ref, noiseKeyB64, identityKeyB64, advB64, browser);
|
|
519
718
|
ev.emit('connection.update', { qr });
|
|
520
719
|
qrTimer = setTimeout(genPairQR, qrMs);
|
|
521
720
|
qrMs = qrTimeout || 20000; // shorter subsequent qrs
|
|
@@ -527,65 +726,98 @@ const makeSocket = (config) => {
|
|
|
527
726
|
ws.on('CB:iq,,pair-success', async (stanza) => {
|
|
528
727
|
logger.debug('pair success recv');
|
|
529
728
|
try {
|
|
530
|
-
|
|
729
|
+
updateServerTimeOffset(stanza);
|
|
730
|
+
const { reply, creds: updatedCreds } = configureSuccessfulPairing(stanza, creds);
|
|
531
731
|
logger.info({ me: updatedCreds.me, platform: updatedCreds.platform }, 'pairing configured successfully, expect to restart the connection...');
|
|
532
732
|
ev.emit('creds.update', updatedCreds);
|
|
533
733
|
ev.emit('connection.update', { isNewLogin: true, qr: undefined });
|
|
534
734
|
await sendNode(reply);
|
|
735
|
+
void sendUnifiedSession();
|
|
535
736
|
}
|
|
536
737
|
catch (error) {
|
|
537
738
|
logger.info({ trace: error.stack }, 'error in pairing');
|
|
538
|
-
end(error);
|
|
739
|
+
void end(error);
|
|
539
740
|
}
|
|
540
741
|
});
|
|
541
742
|
// login complete
|
|
542
743
|
ws.on('CB:success', async (node) => {
|
|
543
744
|
try {
|
|
745
|
+
updateServerTimeOffset(node);
|
|
544
746
|
await uploadPreKeysToServerIfRequired();
|
|
545
747
|
await sendPassiveIq('active');
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
748
|
+
// After successful login, validate our key-bundle against server
|
|
749
|
+
try {
|
|
750
|
+
await digestKeyBundle();
|
|
751
|
+
}
|
|
752
|
+
catch (e) {
|
|
753
|
+
logger.warn({ e }, 'failed to run digest after login');
|
|
754
|
+
}
|
|
550
755
|
}
|
|
551
756
|
catch (err) {
|
|
552
|
-
logger.
|
|
553
|
-
|
|
757
|
+
logger.warn({ err }, 'failed to send initial passive iq');
|
|
758
|
+
}
|
|
759
|
+
logger.info('opened connection to WA');
|
|
760
|
+
clearTimeout(qrTimer); // will never happen in all likelyhood -- but just in case WA sends success on first try
|
|
761
|
+
ev.emit('creds.update', { me: { ...authState.creds.me, lid: node.attrs.lid } });
|
|
762
|
+
ev.emit('connection.update', { connection: 'open' });
|
|
763
|
+
void sendUnifiedSession();
|
|
764
|
+
if (node.attrs.lid && authState.creds.me?.id) {
|
|
765
|
+
const myLID = node.attrs.lid;
|
|
766
|
+
process.nextTick(async () => {
|
|
767
|
+
try {
|
|
768
|
+
const myPN = authState.creds.me.id;
|
|
769
|
+
// Store our own LID-PN mapping
|
|
770
|
+
await signalRepository.lidMapping.storeLIDPNMappings([{ lid: myLID, pn: myPN }]);
|
|
771
|
+
// Create device list for our own user (needed for bulk migration)
|
|
772
|
+
const { user, device } = jidDecode(myPN);
|
|
773
|
+
await authState.keys.set({
|
|
774
|
+
'device-list': {
|
|
775
|
+
[user]: [device?.toString() || '0']
|
|
776
|
+
}
|
|
777
|
+
});
|
|
778
|
+
// migrate our own session
|
|
779
|
+
await signalRepository.migrateSession(myPN, myLID);
|
|
780
|
+
logger.info({ myPN, myLID }, 'Own LID session created successfully');
|
|
781
|
+
}
|
|
782
|
+
catch (error) {
|
|
783
|
+
logger.error({ error, lid: myLID }, 'Failed to create own LID session');
|
|
784
|
+
}
|
|
785
|
+
});
|
|
554
786
|
}
|
|
555
787
|
});
|
|
556
788
|
ws.on('CB:stream:error', (node) => {
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
789
|
+
const [reasonNode] = getAllBinaryNodeChildren(node);
|
|
790
|
+
logger.error({ reasonNode, fullErrorNode: node }, 'stream errored out');
|
|
791
|
+
const { reason, statusCode } = getErrorCodeFromStreamError(node);
|
|
792
|
+
void end(new Boom(`Stream Errored (${reason})`, { statusCode, data: reasonNode || node }));
|
|
560
793
|
});
|
|
561
794
|
// stream fail, possible logout
|
|
562
795
|
ws.on('CB:failure', (node) => {
|
|
563
796
|
const reason = +(node.attrs.reason || 500);
|
|
564
|
-
end(new
|
|
797
|
+
void end(new Boom('Connection Failure', { statusCode: reason, data: node.attrs }));
|
|
565
798
|
});
|
|
566
799
|
ws.on('CB:ib,,downgrade_webclient', () => {
|
|
567
|
-
end(new
|
|
800
|
+
void end(new Boom('Multi-device beta not joined', { statusCode: DisconnectReason.multideviceMismatch }));
|
|
568
801
|
});
|
|
569
|
-
ws.on('CB:ib,,offline_preview', (node) => {
|
|
802
|
+
ws.on('CB:ib,,offline_preview', async (node) => {
|
|
570
803
|
logger.info('offline preview received', JSON.stringify(node));
|
|
571
|
-
sendNode({
|
|
804
|
+
await sendNode({
|
|
572
805
|
tag: 'ib',
|
|
573
806
|
attrs: {},
|
|
574
807
|
content: [{ tag: 'offline_batch', attrs: { count: '100' } }]
|
|
575
808
|
});
|
|
576
809
|
});
|
|
577
810
|
ws.on('CB:ib,,edge_routing', (node) => {
|
|
578
|
-
const edgeRoutingNode =
|
|
579
|
-
const routingInfo =
|
|
580
|
-
if (routingInfo
|
|
581
|
-
authState.creds.routingInfo = Buffer.from(routingInfo
|
|
811
|
+
const edgeRoutingNode = getBinaryNodeChild(node, 'edge_routing');
|
|
812
|
+
const routingInfo = getBinaryNodeChild(edgeRoutingNode, 'routing_info');
|
|
813
|
+
if (routingInfo?.content) {
|
|
814
|
+
authState.creds.routingInfo = Buffer.from(routingInfo?.content);
|
|
582
815
|
ev.emit('creds.update', authState.creds);
|
|
583
816
|
}
|
|
584
817
|
});
|
|
585
818
|
let didStartBuffer = false;
|
|
586
819
|
process.nextTick(() => {
|
|
587
|
-
|
|
588
|
-
if ((_a = creds.me) === null || _a === void 0 ? void 0 : _a.id) {
|
|
820
|
+
if (creds.me?.id) {
|
|
589
821
|
// start buffering important events
|
|
590
822
|
// if we're logged in
|
|
591
823
|
ev.buffer();
|
|
@@ -595,8 +827,8 @@ const makeSocket = (config) => {
|
|
|
595
827
|
});
|
|
596
828
|
// called when all offline notifs are handled
|
|
597
829
|
ws.on('CB:ib,,offline', (node) => {
|
|
598
|
-
const child =
|
|
599
|
-
const offlineNotifs = +(
|
|
830
|
+
const child = getBinaryNodeChild(node, 'offline');
|
|
831
|
+
const offlineNotifs = +(child?.attrs.count || 0);
|
|
600
832
|
logger.info(`handled ${offlineNotifs} offline messages/notifications`);
|
|
601
833
|
if (didStartBuffer) {
|
|
602
834
|
ev.flush();
|
|
@@ -606,32 +838,92 @@ const makeSocket = (config) => {
|
|
|
606
838
|
});
|
|
607
839
|
// update credentials when required
|
|
608
840
|
ev.on('creds.update', update => {
|
|
609
|
-
|
|
610
|
-
const name = (_a = update.me) === null || _a === void 0 ? void 0 : _a.name;
|
|
841
|
+
const name = update.me?.name;
|
|
611
842
|
// if name has just been received
|
|
612
|
-
if (
|
|
843
|
+
if (creds.me?.name !== name) {
|
|
613
844
|
logger.debug({ name }, 'updated pushName');
|
|
614
845
|
sendNode({
|
|
615
846
|
tag: 'presence',
|
|
616
847
|
attrs: { name: name }
|
|
617
|
-
})
|
|
618
|
-
.catch(err => {
|
|
848
|
+
}).catch(err => {
|
|
619
849
|
logger.warn({ trace: err.stack }, 'error in sending presence update on name change');
|
|
620
850
|
});
|
|
621
851
|
}
|
|
622
852
|
Object.assign(creds, update);
|
|
623
853
|
});
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
854
|
+
const updateServerTimeOffset = ({ attrs }) => {
|
|
855
|
+
const tValue = attrs?.t;
|
|
856
|
+
if (!tValue) {
|
|
857
|
+
return;
|
|
858
|
+
}
|
|
859
|
+
const parsed = Number(tValue);
|
|
860
|
+
if (Number.isNaN(parsed) || parsed <= 0) {
|
|
861
|
+
return;
|
|
862
|
+
}
|
|
863
|
+
const localMs = Date.now();
|
|
864
|
+
serverTimeOffsetMs = parsed * 1000 - localMs;
|
|
865
|
+
logger.debug({ offset: serverTimeOffsetMs }, 'calculated server time offset');
|
|
866
|
+
};
|
|
867
|
+
const getUnifiedSessionId = () => {
|
|
868
|
+
const offsetMs = 3 * TimeMs.Day;
|
|
869
|
+
const now = Date.now() + serverTimeOffsetMs;
|
|
870
|
+
const id = (now + offsetMs) % TimeMs.Week;
|
|
871
|
+
return id.toString();
|
|
872
|
+
};
|
|
873
|
+
const sendUnifiedSession = async () => {
|
|
874
|
+
if (!ws.isOpen) {
|
|
875
|
+
return;
|
|
876
|
+
}
|
|
877
|
+
const node = {
|
|
878
|
+
tag: 'ib',
|
|
879
|
+
attrs: {},
|
|
880
|
+
content: [
|
|
881
|
+
{
|
|
882
|
+
tag: 'unified_session',
|
|
883
|
+
attrs: {
|
|
884
|
+
id: getUnifiedSessionId()
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
]
|
|
888
|
+
};
|
|
889
|
+
try {
|
|
890
|
+
await sendNode(node);
|
|
891
|
+
}
|
|
892
|
+
catch (error) {
|
|
893
|
+
logger.debug({ error }, 'failed to send unified_session telemetry');
|
|
894
|
+
}
|
|
895
|
+
};
|
|
896
|
+
const registerSocketEndHandler = (handler) => {
|
|
897
|
+
socketEndHandlers.push(handler);
|
|
898
|
+
};
|
|
899
|
+
/**
|
|
900
|
+
* Fetches your account's standing when it comes to restrictions.
|
|
901
|
+
* @returns Returns the state of the restrictions.
|
|
902
|
+
*/
|
|
903
|
+
const fetchAccountReachoutTimelock = async () => {
|
|
904
|
+
const queryResult = await executeWMexQuery({}, QueryIds.REACHOUT_TIMELOCK, XWAPaths.xwa2_fetch_account_reachout_timelock, query, generateMessageTag);
|
|
905
|
+
const result = {
|
|
906
|
+
isActive: !!queryResult?.is_active,
|
|
907
|
+
timeEnforcementEnds: queryResult?.time_enforcement_ends && queryResult?.time_enforcement_ends !== '0'
|
|
908
|
+
? new Date(parseInt(queryResult.time_enforcement_ends, 10) * 1000)
|
|
909
|
+
: undefined,
|
|
910
|
+
enforcementType: queryResult?.enforcement_type ?? ReachoutTimelockEnforcementType.DEFAULT
|
|
911
|
+
};
|
|
912
|
+
ev.emit('connection.update', { reachoutTimeLock: result });
|
|
913
|
+
return result;
|
|
914
|
+
};
|
|
915
|
+
/**
|
|
916
|
+
* Fetches your account's new chat limits.
|
|
917
|
+
* @returns Returns the quota and the usage.
|
|
918
|
+
*/
|
|
919
|
+
const fetchNewChatMessageCap = async () => {
|
|
920
|
+
return executeWMexQuery({ input: { type: 'INDIVIDUAL_NEW_CHAT_MSG' } }, QueryIds.MESSAGE_CAPPING_INFO, XWAPaths.xwa2_message_capping_info, query, generateMessageTag);
|
|
921
|
+
};
|
|
627
922
|
return {
|
|
628
923
|
type: 'md',
|
|
629
924
|
ws,
|
|
630
925
|
ev,
|
|
631
|
-
authState: {
|
|
632
|
-
creds,
|
|
633
|
-
keys
|
|
634
|
-
},
|
|
926
|
+
authState: { creds, keys },
|
|
635
927
|
signalRepository,
|
|
636
928
|
get user() {
|
|
637
929
|
return authState.creds.me;
|
|
@@ -644,22 +936,32 @@ const makeSocket = (config) => {
|
|
|
644
936
|
sendNode,
|
|
645
937
|
logout,
|
|
646
938
|
end,
|
|
939
|
+
registerSocketEndHandler,
|
|
647
940
|
onUnexpectedError,
|
|
648
941
|
uploadPreKeys,
|
|
649
942
|
uploadPreKeysToServerIfRequired,
|
|
943
|
+
digestKeyBundle,
|
|
944
|
+
rotateSignedPreKey,
|
|
650
945
|
requestPairingCode,
|
|
946
|
+
updateServerTimeOffset,
|
|
947
|
+
sendUnifiedSession,
|
|
948
|
+
wamBuffer: publicWAMBuffer,
|
|
651
949
|
/** Waits for the connection to WA to reach a state */
|
|
652
|
-
waitForConnectionUpdate:
|
|
950
|
+
waitForConnectionUpdate: bindWaitForConnectionUpdate(ev),
|
|
653
951
|
sendWAMBuffer,
|
|
952
|
+
executeUSyncQuery,
|
|
953
|
+
onWhatsApp,
|
|
954
|
+
fetchAccountReachoutTimelock,
|
|
955
|
+
fetchNewChatMessageCap
|
|
654
956
|
};
|
|
655
957
|
};
|
|
656
|
-
exports.makeSocket = makeSocket;
|
|
657
958
|
/**
|
|
658
959
|
* map the websocket error to the right type
|
|
659
960
|
* so it can be retried by the caller
|
|
660
961
|
* */
|
|
661
962
|
function mapWebSocketError(handler) {
|
|
662
963
|
return (error) => {
|
|
663
|
-
handler(new
|
|
964
|
+
handler(new Boom(`WebSocket Error (${error?.message})`, { statusCode: getCodeFromWSError(error), data: error }));
|
|
664
965
|
};
|
|
665
966
|
}
|
|
967
|
+
//# sourceMappingURL=socket.js.map
|