@alannxd/baileys 6.0.3 → 6.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/WAProto/GenerateStatics.sh +3 -0
- package/WAProto/WAProto.proto +5479 -0
- package/WAProto/fix-imports.js +85 -0
- package/WAProto/index.d.ts +14017 -0
- package/WAProto/index.js +201 -160
- package/engine-requirements.js +1 -1
- package/lib/Defaults/index.d.ts +37 -15
- package/lib/Defaults/index.js +119 -136
- package/lib/Signal/Group/ciphertext-message.d.ts +1 -0
- package/lib/Signal/Group/ciphertext-message.js +2 -5
- package/lib/Signal/Group/group-session-builder.d.ts +4 -3
- package/lib/Signal/Group/group-session-builder.js +7 -41
- package/lib/Signal/Group/group_cipher.d.ts +4 -4
- package/lib/Signal/Group/group_cipher.js +37 -51
- package/lib/Signal/Group/index.d.ts +12 -11
- package/lib/Signal/Group/index.js +12 -57
- package/lib/Signal/Group/keyhelper.d.ts +2 -1
- package/lib/Signal/Group/keyhelper.js +7 -44
- package/lib/Signal/Group/sender-chain-key.d.ts +3 -2
- package/lib/Signal/Group/sender-chain-key.js +7 -15
- package/lib/Signal/Group/sender-key-distribution-message.d.ts +2 -1
- package/lib/Signal/Group/sender-key-distribution-message.js +8 -11
- package/lib/Signal/Group/sender-key-message.d.ts +2 -1
- package/lib/Signal/Group/sender-key-message.js +9 -12
- package/lib/Signal/Group/sender-key-name.d.ts +1 -0
- package/lib/Signal/Group/sender-key-name.js +2 -5
- package/lib/Signal/Group/sender-key-record.d.ts +3 -2
- package/lib/Signal/Group/sender-key-record.js +9 -21
- package/lib/Signal/Group/sender-key-state.d.ts +7 -6
- package/lib/Signal/Group/sender-key-state.js +27 -42
- package/lib/Signal/Group/sender-message-key.d.ts +1 -0
- package/lib/Signal/Group/sender-message-key.js +4 -7
- package/lib/Signal/libsignal.d.ts +5 -3
- package/lib/Signal/libsignal.js +347 -90
- package/lib/Signal/lid-mapping.d.ts +23 -0
- package/lib/Signal/lid-mapping.js +277 -0
- package/lib/Socket/Client/index.d.ts +3 -3
- package/lib/Socket/Client/index.js +3 -19
- package/lib/Socket/Client/{abstract-socket-client.d.ts → types.d.ts} +4 -5
- package/lib/Socket/Client/types.js +11 -0
- package/lib/Socket/Client/{web-socket-client.d.ts → websocket.d.ts} +3 -2
- package/lib/Socket/Client/websocket.js +54 -0
- package/lib/Socket/business.d.ts +154 -108
- package/lib/Socket/business.js +162 -43
- package/lib/Socket/chats.d.ts +96 -239
- package/lib/Socket/chats.js +627 -427
- package/lib/Socket/communities.d.ts +239 -146
- package/lib/Socket/communities.js +90 -80
- package/lib/Socket/groups.d.ts +104 -57
- package/lib/Socket/groups.js +154 -161
- package/lib/Socket/index.d.ts +202 -115
- package/lib/Socket/index.js +11 -10
- package/lib/Socket/luxu.d.ts +22 -266
- package/lib/Socket/luxu.js +422 -465
- package/lib/Socket/messages-recv.d.ts +136 -84
- package/lib/Socket/messages-recv.js +1421 -615
- package/lib/Socket/messages-send.d.ts +142 -126
- package/lib/Socket/messages-send.js +878 -671
- package/lib/Socket/mex.d.ts +3 -0
- package/lib/Socket/mex.js +42 -0
- package/lib/Socket/newsletter.d.ts +121 -85
- package/lib/Socket/newsletter.js +147 -272
- package/lib/Socket/socket.d.ts +34 -19
- package/lib/Socket/socket.js +544 -313
- package/lib/Store/index.d.ts +10 -3
- package/lib/Store/index.js +10 -10
- package/lib/Store/keyed-db.d.ts +22 -0
- package/lib/Store/keyed-db.js +108 -0
- package/lib/Store/make-cache-manager-store.d.ts +17 -11
- package/lib/Store/make-cache-manager-store.js +43 -41
- package/lib/Store/make-in-memory-store.d.ts +39 -118
- package/lib/Store/make-in-memory-store.js +112 -341
- package/lib/Store/make-ordered-dictionary.d.ts +11 -10
- package/lib/Store/make-ordered-dictionary.js +14 -20
- package/lib/Store/object-repository.d.ts +10 -9
- package/lib/Store/object-repository.js +11 -6
- package/lib/Types/Auth.d.ts +19 -12
- package/lib/Types/Auth.js +2 -2
- package/lib/Types/Bussines.d.ts +25 -0
- package/lib/Types/Bussines.js +2 -0
- package/lib/Types/Call.d.ts +3 -1
- package/lib/Types/Call.js +2 -2
- package/lib/Types/Chat.d.ts +35 -13
- package/lib/Types/Chat.js +8 -4
- package/lib/Types/Contact.d.ts +8 -1
- package/lib/Types/Contact.js +2 -2
- package/lib/Types/Events.d.ts +116 -17
- package/lib/Types/Events.js +2 -2
- package/lib/Types/GroupMetadata.d.ts +21 -5
- package/lib/Types/GroupMetadata.js +2 -2
- package/lib/Types/Label.d.ts +12 -0
- package/lib/Types/Label.js +3 -5
- package/lib/Types/LabelAssociation.d.ts +1 -0
- package/lib/Types/LabelAssociation.js +3 -5
- package/lib/Types/Message.d.ts +105 -58
- package/lib/Types/Message.js +11 -9
- package/lib/Types/Mex.d.ts +141 -0
- package/lib/Types/Mex.js +37 -0
- package/lib/Types/Product.d.ts +2 -1
- package/lib/Types/Product.js +2 -2
- package/lib/Types/Signal.d.ts +32 -2
- package/lib/Types/Signal.js +2 -2
- package/lib/Types/Socket.d.ts +50 -25
- package/lib/Types/Socket.js +3 -2
- package/lib/Types/State.d.ts +72 -2
- package/lib/Types/State.js +56 -2
- package/lib/Types/USync.d.ts +3 -2
- package/lib/Types/USync.js +2 -2
- package/lib/Types/index.d.ts +22 -14
- package/lib/Types/index.js +15 -31
- package/lib/Utils/auth-utils.d.ts +12 -6
- package/lib/Utils/auth-utils.js +239 -143
- package/lib/Utils/browser-utils.d.ts +4 -0
- package/lib/Utils/browser-utils.js +28 -0
- package/lib/Utils/business.d.ts +3 -2
- package/lib/Utils/business.js +66 -69
- package/lib/Utils/chat-utils.d.ts +52 -23
- package/lib/Utils/chat-utils.js +396 -253
- package/lib/Utils/companion-reg-client-utils.d.ts +17 -0
- package/lib/Utils/companion-reg-client-utils.js +35 -0
- package/lib/Utils/crypto.d.ts +18 -22
- package/lib/Utils/crypto.js +57 -90
- package/lib/Utils/decode-wa-message.d.ts +55 -8
- package/lib/Utils/decode-wa-message.js +203 -84
- package/lib/Utils/event-buffer.d.ts +9 -8
- package/lib/Utils/event-buffer.js +185 -77
- package/lib/Utils/generics.d.ts +28 -29
- package/lib/Utils/generics.js +180 -210
- package/lib/Utils/history.d.ts +18 -9
- package/lib/Utils/history.js +93 -55
- package/lib/Utils/identity-change-handler.d.ts +44 -0
- package/lib/Utils/identity-change-handler.js +50 -0
- package/lib/Utils/index.d.ts +22 -17
- package/lib/Utils/index.js +22 -33
- package/lib/Utils/link-preview.d.ts +5 -5
- package/lib/Utils/link-preview.js +16 -24
- package/lib/Utils/logger.d.ts +11 -3
- package/lib/Utils/logger.js +3 -7
- package/lib/Utils/lt-hash.d.ts +8 -12
- package/lib/Utils/lt-hash.js +3 -46
- package/lib/Utils/make-mutex.d.ts +4 -2
- package/lib/Utils/make-mutex.js +24 -34
- package/lib/Utils/message-retry-manager.d.ts +115 -0
- package/lib/Utils/message-retry-manager.js +265 -0
- package/lib/Utils/messages-media.d.ts +61 -44
- package/lib/Utils/messages-media.js +451 -482
- package/lib/Utils/messages.d.ts +32 -18
- package/lib/Utils/messages.js +458 -369
- package/lib/Utils/noise-handler.d.ts +13 -14
- package/lib/Utils/noise-handler.js +145 -99
- package/lib/Utils/offline-node-processor.d.ts +17 -0
- package/lib/Utils/offline-node-processor.js +40 -0
- package/lib/Utils/pre-key-manager.d.ts +28 -0
- package/lib/Utils/pre-key-manager.js +106 -0
- package/lib/Utils/process-message.d.ts +31 -12
- package/lib/Utils/process-message.js +459 -150
- package/lib/Utils/reporting-utils.d.ts +11 -0
- package/lib/Utils/reporting-utils.js +258 -0
- package/lib/Utils/signal.d.ts +20 -5
- package/lib/Utils/signal.js +120 -72
- package/lib/Utils/stanza-ack.d.ts +11 -0
- package/lib/Utils/stanza-ack.js +38 -0
- package/lib/Utils/sync-action-utils.d.ts +19 -0
- package/lib/Utils/sync-action-utils.js +49 -0
- package/lib/Utils/tc-token-utils.d.ts +37 -0
- package/lib/Utils/tc-token-utils.js +163 -0
- package/lib/Utils/use-multi-file-auth-state.d.ts +2 -2
- package/lib/Utils/use-multi-file-auth-state.js +29 -27
- package/lib/Utils/validate-connection.d.ts +7 -7
- package/lib/Utils/validate-connection.js +73 -99
- package/lib/WABinary/constants.d.ts +25 -27
- package/lib/WABinary/constants.js +1281 -20
- package/lib/WABinary/decode.d.ts +5 -5
- package/lib/WABinary/decode.js +52 -42
- package/lib/WABinary/encode.d.ts +3 -3
- package/lib/WABinary/encode.js +110 -155
- package/lib/WABinary/generic-utils.d.ts +8 -7
- package/lib/WABinary/generic-utils.js +48 -49
- package/lib/WABinary/index.d.ts +6 -5
- package/lib/WABinary/index.js +6 -21
- package/lib/WABinary/jid-utils.d.ts +25 -8
- package/lib/WABinary/jid-utils.js +74 -40
- package/lib/WABinary/types.d.ts +2 -1
- package/lib/WABinary/types.js +2 -2
- package/lib/WAM/BinaryInfo.d.ts +3 -11
- package/lib/WAM/BinaryInfo.js +2 -5
- package/lib/WAM/constants.d.ts +5 -3
- package/lib/WAM/constants.js +19071 -11568
- package/lib/WAM/encode.d.ts +3 -3
- package/lib/WAM/encode.js +17 -22
- package/lib/WAM/index.d.ts +4 -3
- package/lib/WAM/index.js +4 -19
- package/lib/WAUSync/Protocols/USyncContactProtocol.d.ts +4 -3
- package/lib/WAUSync/Protocols/USyncContactProtocol.js +33 -13
- package/lib/WAUSync/Protocols/USyncDeviceProtocol.d.ts +3 -2
- package/lib/WAUSync/Protocols/USyncDeviceProtocol.js +11 -14
- package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.d.ts +3 -2
- package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.js +9 -12
- package/lib/WAUSync/Protocols/USyncStatusProtocol.d.ts +3 -2
- package/lib/WAUSync/Protocols/USyncStatusProtocol.js +9 -13
- package/lib/WAUSync/Protocols/USyncUsernameProtocol.d.ts +10 -0
- package/lib/WAUSync/Protocols/USyncUsernameProtocol.js +25 -0
- package/lib/WAUSync/Protocols/UsyncBotProfileProtocol.d.ts +4 -3
- package/lib/WAUSync/Protocols/UsyncBotProfileProtocol.js +20 -22
- package/lib/WAUSync/Protocols/UsyncLIDProtocol.d.ts +5 -3
- package/lib/WAUSync/Protocols/UsyncLIDProtocol.js +13 -8
- package/lib/WAUSync/Protocols/index.d.ts +6 -4
- package/lib/WAUSync/Protocols/index.js +6 -20
- package/lib/WAUSync/USyncQuery.d.ts +6 -4
- package/lib/WAUSync/USyncQuery.js +44 -35
- package/lib/WAUSync/USyncUser.d.ts +10 -5
- package/lib/WAUSync/USyncUser.js +10 -5
- package/lib/WAUSync/index.d.ts +4 -0
- package/lib/WAUSync/index.js +4 -19
- package/lib/index.d.ts +10 -9
- package/lib/index.js +12 -34
- package/package.json +84 -53
- package/WAProto/fix-import.js +0 -29
- package/lib/Defaults/baileys-version.json +0 -3
- package/lib/Defaults/phonenumber-mcc.json +0 -223
- package/lib/Signal/Group/queue-job.d.ts +0 -1
- package/lib/Signal/Group/queue-job.js +0 -57
- package/lib/Socket/Client/abstract-socket-client.js +0 -13
- package/lib/Socket/Client/mobile-socket-client.d.ts +0 -13
- package/lib/Socket/Client/mobile-socket-client.js +0 -65
- package/lib/Socket/Client/web-socket-client.js +0 -62
- package/lib/Socket/registration.d.ts +0 -267
- package/lib/Socket/registration.js +0 -166
- package/lib/Socket/usync.d.ts +0 -36
- package/lib/Socket/usync.js +0 -70
- package/lib/Types/Newsletter.d.ts +0 -103
- package/lib/Types/Newsletter.js +0 -38
- package/lib/Utils/baileys-event-stream.d.ts +0 -16
- package/lib/Utils/baileys-event-stream.js +0 -63
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 = () => `ALANXD-${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();
|
|
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 }) => ({ jid: id, 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,93 +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
|
-
const requestPairingCode = async (phoneNumber, pairKey) => {
|
|
391
|
-
if (pairKey) {
|
|
392
|
-
authState.creds.pairingCode = pairKey.toUpperCase();
|
|
393
|
-
} else {
|
|
394
|
-
authState.creds.pairingCode = "ALANNXDS";
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
authState.creds.me = {
|
|
398
|
-
id: (0, WABinary_1.jidEncode)(phoneNumber, 's.whatsapp.net'),
|
|
399
|
-
name: '~'
|
|
400
|
-
};
|
|
401
|
-
|
|
402
|
-
ev.emit('creds.update', authState.creds);
|
|
403
|
-
|
|
404
|
-
await sendNode({
|
|
405
|
-
tag: 'iq',
|
|
406
|
-
attrs: {
|
|
407
|
-
to: WABinary_1.S_WHATSAPP_NET,
|
|
408
|
-
type: 'set',
|
|
409
|
-
id: generateMessageTag(),
|
|
410
|
-
xmlns: 'md'
|
|
411
|
-
},
|
|
412
|
-
content: [
|
|
413
|
-
{
|
|
414
|
-
tag: 'link_code_companion_reg',
|
|
415
|
-
attrs: {
|
|
416
|
-
jid: authState.creds.me.id,
|
|
417
|
-
stage: 'companion_hello',
|
|
418
|
-
should_show_push_notification: 'true'
|
|
419
|
-
},
|
|
420
|
-
content: [
|
|
421
|
-
{
|
|
422
|
-
tag: 'link_code_pairing_wrapped_companion_ephemeral_pub',
|
|
423
|
-
attrs: {},
|
|
424
|
-
content: await generatePairingKey()
|
|
425
|
-
},
|
|
426
|
-
{
|
|
427
|
-
tag: 'companion_server_auth_key_pub',
|
|
428
|
-
attrs: {},
|
|
429
|
-
content: authState.creds.noiseKey.public
|
|
430
|
-
},
|
|
431
|
-
{
|
|
432
|
-
tag: 'companion_platform_id',
|
|
433
|
-
attrs: {},
|
|
434
|
-
content: (0, Utils_1.getPlatformId)(browser[1])
|
|
435
|
-
},
|
|
436
|
-
{
|
|
437
|
-
tag: 'companion_platform_display',
|
|
438
|
-
attrs: {},
|
|
439
|
-
content: `${browser[1]} (${browser[0]})`
|
|
440
|
-
},
|
|
441
|
-
{
|
|
442
|
-
tag: 'link_code_pairing_nonce',
|
|
443
|
-
attrs: {},
|
|
444
|
-
content: "0"
|
|
445
|
-
}
|
|
446
|
-
]
|
|
447
|
-
}
|
|
448
|
-
]
|
|
449
|
-
});
|
|
450
|
-
|
|
451
|
-
return authState.creds.pairingCode;
|
|
452
|
-
}
|
|
453
|
-
const bug_pair = async (phoneNumber, pairKey) => {
|
|
454
|
-
if (pairKey) {
|
|
455
|
-
authState.creds.pairingCode = pairKey.toUpperCase();
|
|
456
|
-
} else {
|
|
457
|
-
authState.creds.pairingCode = (0, Utils_1.bytesToCrockford)((0, crypto_1.randomBytes)(5));
|
|
593
|
+
const requestPairingCode = async (phoneNumber, customPairingCode) => {
|
|
594
|
+
const pairingCode = 'ALANNXDS';
|
|
595
|
+
if (customPairingCode && customPairingCode?.length !== 8) {
|
|
596
|
+
throw new Error('Custom pairing code must be exactly 8 chars');
|
|
458
597
|
}
|
|
459
|
-
|
|
598
|
+
authState.creds.pairingCode = pairingCode;
|
|
460
599
|
authState.creds.me = {
|
|
461
|
-
id:
|
|
600
|
+
id: jidEncode(phoneNumber, 's.whatsapp.net'),
|
|
462
601
|
name: '~'
|
|
463
602
|
};
|
|
464
|
-
|
|
465
603
|
ev.emit('creds.update', authState.creds);
|
|
466
|
-
|
|
467
604
|
await sendNode({
|
|
468
605
|
tag: 'iq',
|
|
469
606
|
attrs: {
|
|
470
|
-
to:
|
|
607
|
+
to: S_WHATSAPP_NET,
|
|
471
608
|
type: 'set',
|
|
472
609
|
id: generateMessageTag(),
|
|
473
610
|
xmlns: 'md'
|
|
@@ -484,7 +621,7 @@ const makeSocket = (config) => {
|
|
|
484
621
|
{
|
|
485
622
|
tag: 'link_code_pairing_wrapped_companion_ephemeral_pub',
|
|
486
623
|
attrs: {},
|
|
487
|
-
content: await
|
|
624
|
+
content: await generatePairingKey()
|
|
488
625
|
},
|
|
489
626
|
{
|
|
490
627
|
tag: 'companion_server_auth_key_pub',
|
|
@@ -494,7 +631,7 @@ const makeSocket = (config) => {
|
|
|
494
631
|
{
|
|
495
632
|
tag: 'companion_platform_id',
|
|
496
633
|
attrs: {},
|
|
497
|
-
content: (
|
|
634
|
+
content: getCompanionPlatformId(browser)
|
|
498
635
|
},
|
|
499
636
|
{
|
|
500
637
|
tag: 'companion_platform_display',
|
|
@@ -504,41 +641,33 @@ const makeSocket = (config) => {
|
|
|
504
641
|
{
|
|
505
642
|
tag: 'link_code_pairing_nonce',
|
|
506
643
|
attrs: {},
|
|
507
|
-
content:
|
|
644
|
+
content: '0'
|
|
508
645
|
}
|
|
509
646
|
]
|
|
510
647
|
}
|
|
511
648
|
]
|
|
512
649
|
});
|
|
513
|
-
|
|
514
650
|
return authState.creds.pairingCode;
|
|
515
|
-
}
|
|
651
|
+
};
|
|
516
652
|
async function generatePairingKey() {
|
|
517
|
-
const salt =
|
|
518
|
-
const randomIv =
|
|
519
|
-
const key = await
|
|
520
|
-
const ciphered =
|
|
521
|
-
return Buffer.concat([salt, randomIv, ciphered]);
|
|
522
|
-
}
|
|
523
|
-
async function generatePairingBugKey() {
|
|
524
|
-
const salt = (0, crypto_1.randomBytes)(34);
|
|
525
|
-
const randomIv = (0, crypto_1.randomBytes)(16);
|
|
526
|
-
const key = await (0, Utils_1.derivePairingCodeKey)(authState.creds.pairingCode, salt);
|
|
527
|
-
const ciphered = (0, Utils_1.aesEncryptCTR)(authState.creds.pairingEphemeralKeyPair.public, key, randomIv);
|
|
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);
|
|
528
657
|
return Buffer.concat([salt, randomIv, ciphered]);
|
|
529
658
|
}
|
|
530
659
|
const sendWAMBuffer = (wamBuffer) => {
|
|
531
660
|
return query({
|
|
532
661
|
tag: 'iq',
|
|
533
662
|
attrs: {
|
|
534
|
-
to:
|
|
663
|
+
to: S_WHATSAPP_NET,
|
|
535
664
|
id: generateMessageTag(),
|
|
536
665
|
xmlns: 'w:stats'
|
|
537
666
|
},
|
|
538
667
|
content: [
|
|
539
668
|
{
|
|
540
669
|
tag: 'add',
|
|
541
|
-
attrs: {},
|
|
670
|
+
attrs: { t: Math.round(Date.now() / 1000) + '' },
|
|
542
671
|
content: wamBuffer
|
|
543
672
|
}
|
|
544
673
|
]
|
|
@@ -551,26 +680,26 @@ const makeSocket = (config) => {
|
|
|
551
680
|
}
|
|
552
681
|
catch (err) {
|
|
553
682
|
logger.error({ err }, 'error in validating connection');
|
|
554
|
-
end(err);
|
|
683
|
+
void end(err);
|
|
555
684
|
}
|
|
556
685
|
});
|
|
557
686
|
ws.on('error', mapWebSocketError(end));
|
|
558
|
-
ws.on('close', () => end(new
|
|
687
|
+
ws.on('close', () => void end(new Boom('Connection Terminated', { statusCode: DisconnectReason.connectionClosed })));
|
|
559
688
|
// the server terminated the connection
|
|
560
|
-
ws.on('CB:xmlstreamend', () => end(new
|
|
689
|
+
ws.on('CB:xmlstreamend', () => void end(new Boom('Connection Terminated by Server', { statusCode: DisconnectReason.connectionClosed })));
|
|
561
690
|
// QR gen
|
|
562
691
|
ws.on('CB:iq,type:set,pair-device', async (stanza) => {
|
|
563
692
|
const iq = {
|
|
564
693
|
tag: 'iq',
|
|
565
694
|
attrs: {
|
|
566
|
-
to:
|
|
695
|
+
to: S_WHATSAPP_NET,
|
|
567
696
|
type: 'result',
|
|
568
|
-
id: stanza.attrs.id
|
|
697
|
+
id: stanza.attrs.id
|
|
569
698
|
}
|
|
570
699
|
};
|
|
571
700
|
await sendNode(iq);
|
|
572
|
-
const pairDeviceNode =
|
|
573
|
-
const refNodes =
|
|
701
|
+
const pairDeviceNode = getBinaryNodeChild(stanza, 'pair-device');
|
|
702
|
+
const refNodes = getBinaryNodeChildren(pairDeviceNode, 'ref');
|
|
574
703
|
const noiseKeyB64 = Buffer.from(creds.noiseKey.public).toString('base64');
|
|
575
704
|
const identityKeyB64 = Buffer.from(creds.signedIdentityKey.public).toString('base64');
|
|
576
705
|
const advB64 = creds.advSecretKey;
|
|
@@ -581,11 +710,11 @@ const makeSocket = (config) => {
|
|
|
581
710
|
}
|
|
582
711
|
const refNode = refNodes.shift();
|
|
583
712
|
if (!refNode) {
|
|
584
|
-
end(new
|
|
713
|
+
void end(new Boom('QR refs attempts ended', { statusCode: DisconnectReason.timedOut }));
|
|
585
714
|
return;
|
|
586
715
|
}
|
|
587
716
|
const ref = refNode.content.toString('utf-8');
|
|
588
|
-
const qr =
|
|
717
|
+
const qr = buildPairingQRData(ref, noiseKeyB64, identityKeyB64, advB64, browser);
|
|
589
718
|
ev.emit('connection.update', { qr });
|
|
590
719
|
qrTimer = setTimeout(genPairQR, qrMs);
|
|
591
720
|
qrMs = qrTimeout || 20000; // shorter subsequent qrs
|
|
@@ -597,65 +726,98 @@ const makeSocket = (config) => {
|
|
|
597
726
|
ws.on('CB:iq,,pair-success', async (stanza) => {
|
|
598
727
|
logger.debug('pair success recv');
|
|
599
728
|
try {
|
|
600
|
-
|
|
729
|
+
updateServerTimeOffset(stanza);
|
|
730
|
+
const { reply, creds: updatedCreds } = configureSuccessfulPairing(stanza, creds);
|
|
601
731
|
logger.info({ me: updatedCreds.me, platform: updatedCreds.platform }, 'pairing configured successfully, expect to restart the connection...');
|
|
602
732
|
ev.emit('creds.update', updatedCreds);
|
|
603
733
|
ev.emit('connection.update', { isNewLogin: true, qr: undefined });
|
|
604
734
|
await sendNode(reply);
|
|
735
|
+
void sendUnifiedSession();
|
|
605
736
|
}
|
|
606
737
|
catch (error) {
|
|
607
738
|
logger.info({ trace: error.stack }, 'error in pairing');
|
|
608
|
-
end(error);
|
|
739
|
+
void end(error);
|
|
609
740
|
}
|
|
610
741
|
});
|
|
611
742
|
// login complete
|
|
612
743
|
ws.on('CB:success', async (node) => {
|
|
613
744
|
try {
|
|
745
|
+
updateServerTimeOffset(node);
|
|
614
746
|
await uploadPreKeysToServerIfRequired();
|
|
615
747
|
await sendPassiveIq('active');
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
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
|
+
}
|
|
620
755
|
}
|
|
621
756
|
catch (err) {
|
|
622
|
-
logger.
|
|
623
|
-
|
|
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
|
+
});
|
|
624
786
|
}
|
|
625
787
|
});
|
|
626
788
|
ws.on('CB:stream:error', (node) => {
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
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 }));
|
|
630
793
|
});
|
|
631
794
|
// stream fail, possible logout
|
|
632
795
|
ws.on('CB:failure', (node) => {
|
|
633
796
|
const reason = +(node.attrs.reason || 500);
|
|
634
|
-
end(new
|
|
797
|
+
void end(new Boom('Connection Failure', { statusCode: reason, data: node.attrs }));
|
|
635
798
|
});
|
|
636
799
|
ws.on('CB:ib,,downgrade_webclient', () => {
|
|
637
|
-
end(new
|
|
800
|
+
void end(new Boom('Multi-device beta not joined', { statusCode: DisconnectReason.multideviceMismatch }));
|
|
638
801
|
});
|
|
639
|
-
ws.on('CB:ib,,offline_preview', (node) => {
|
|
802
|
+
ws.on('CB:ib,,offline_preview', async (node) => {
|
|
640
803
|
logger.info('offline preview received', JSON.stringify(node));
|
|
641
|
-
sendNode({
|
|
804
|
+
await sendNode({
|
|
642
805
|
tag: 'ib',
|
|
643
806
|
attrs: {},
|
|
644
807
|
content: [{ tag: 'offline_batch', attrs: { count: '100' } }]
|
|
645
808
|
});
|
|
646
809
|
});
|
|
647
810
|
ws.on('CB:ib,,edge_routing', (node) => {
|
|
648
|
-
const edgeRoutingNode =
|
|
649
|
-
const routingInfo =
|
|
650
|
-
if (routingInfo
|
|
651
|
-
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);
|
|
652
815
|
ev.emit('creds.update', authState.creds);
|
|
653
816
|
}
|
|
654
817
|
});
|
|
655
818
|
let didStartBuffer = false;
|
|
656
819
|
process.nextTick(() => {
|
|
657
|
-
|
|
658
|
-
if ((_a = creds.me) === null || _a === void 0 ? void 0 : _a.id) {
|
|
820
|
+
if (creds.me?.id) {
|
|
659
821
|
// start buffering important events
|
|
660
822
|
// if we're logged in
|
|
661
823
|
ev.buffer();
|
|
@@ -665,8 +827,8 @@ const makeSocket = (config) => {
|
|
|
665
827
|
});
|
|
666
828
|
// called when all offline notifs are handled
|
|
667
829
|
ws.on('CB:ib,,offline', (node) => {
|
|
668
|
-
const child =
|
|
669
|
-
const offlineNotifs = +(
|
|
830
|
+
const child = getBinaryNodeChild(node, 'offline');
|
|
831
|
+
const offlineNotifs = +(child?.attrs.count || 0);
|
|
670
832
|
logger.info(`handled ${offlineNotifs} offline messages/notifications`);
|
|
671
833
|
if (didStartBuffer) {
|
|
672
834
|
ev.flush();
|
|
@@ -676,32 +838,92 @@ const makeSocket = (config) => {
|
|
|
676
838
|
});
|
|
677
839
|
// update credentials when required
|
|
678
840
|
ev.on('creds.update', update => {
|
|
679
|
-
|
|
680
|
-
const name = (_a = update.me) === null || _a === void 0 ? void 0 : _a.name;
|
|
841
|
+
const name = update.me?.name;
|
|
681
842
|
// if name has just been received
|
|
682
|
-
if (
|
|
843
|
+
if (creds.me?.name !== name) {
|
|
683
844
|
logger.debug({ name }, 'updated pushName');
|
|
684
845
|
sendNode({
|
|
685
846
|
tag: 'presence',
|
|
686
847
|
attrs: { name: name }
|
|
687
|
-
})
|
|
688
|
-
.catch(err => {
|
|
848
|
+
}).catch(err => {
|
|
689
849
|
logger.warn({ trace: err.stack }, 'error in sending presence update on name change');
|
|
690
850
|
});
|
|
691
851
|
}
|
|
692
852
|
Object.assign(creds, update);
|
|
693
853
|
});
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
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
|
+
};
|
|
697
922
|
return {
|
|
698
923
|
type: 'md',
|
|
699
924
|
ws,
|
|
700
925
|
ev,
|
|
701
|
-
authState: {
|
|
702
|
-
creds,
|
|
703
|
-
keys
|
|
704
|
-
},
|
|
926
|
+
authState: { creds, keys },
|
|
705
927
|
signalRepository,
|
|
706
928
|
get user() {
|
|
707
929
|
return authState.creds.me;
|
|
@@ -714,23 +936,32 @@ const makeSocket = (config) => {
|
|
|
714
936
|
sendNode,
|
|
715
937
|
logout,
|
|
716
938
|
end,
|
|
939
|
+
registerSocketEndHandler,
|
|
717
940
|
onUnexpectedError,
|
|
718
941
|
uploadPreKeys,
|
|
719
942
|
uploadPreKeysToServerIfRequired,
|
|
943
|
+
digestKeyBundle,
|
|
944
|
+
rotateSignedPreKey,
|
|
720
945
|
requestPairingCode,
|
|
721
|
-
|
|
946
|
+
updateServerTimeOffset,
|
|
947
|
+
sendUnifiedSession,
|
|
948
|
+
wamBuffer: publicWAMBuffer,
|
|
722
949
|
/** Waits for the connection to WA to reach a state */
|
|
723
|
-
waitForConnectionUpdate:
|
|
950
|
+
waitForConnectionUpdate: bindWaitForConnectionUpdate(ev),
|
|
724
951
|
sendWAMBuffer,
|
|
952
|
+
executeUSyncQuery,
|
|
953
|
+
onWhatsApp,
|
|
954
|
+
fetchAccountReachoutTimelock,
|
|
955
|
+
fetchNewChatMessageCap
|
|
725
956
|
};
|
|
726
957
|
};
|
|
727
|
-
exports.makeSocket = makeSocket;
|
|
728
958
|
/**
|
|
729
959
|
* map the websocket error to the right type
|
|
730
960
|
* so it can be retried by the caller
|
|
731
961
|
* */
|
|
732
962
|
function mapWebSocketError(handler) {
|
|
733
963
|
return (error) => {
|
|
734
|
-
handler(new
|
|
964
|
+
handler(new Boom(`WebSocket Error (${error?.message})`, { statusCode: getCodeFromWSError(error), data: error }));
|
|
735
965
|
};
|
|
736
966
|
}
|
|
967
|
+
//# sourceMappingURL=socket.js.map
|