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