@d0v3riz/baileys 6.7.25 → 7.0.0-rc.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +53 -50
- package/WAProto/GenerateStatics.sh +2 -2
- package/WAProto/WAProto.proto +1339 -493
- package/WAProto/fix-imports.js +1 -1
- package/WAProto/index.d.ts +4585 -44041
- package/WAProto/index.js +80831 -148208
- package/lib/Defaults/index.d.ts +13 -3
- package/lib/Defaults/index.d.ts.map +1 -1
- package/lib/Defaults/index.js +27 -13
- package/lib/Defaults/index.js.map +1 -1
- package/lib/Signal/Group/group_cipher.d.ts +1 -2
- package/lib/Signal/Group/group_cipher.d.ts.map +1 -1
- package/lib/Signal/Group/group_cipher.js +29 -41
- package/lib/Signal/Group/group_cipher.js.map +1 -1
- package/lib/Signal/Group/keyhelper.d.ts.map +1 -1
- package/lib/Signal/Group/keyhelper.js +0 -1
- package/lib/Signal/Group/keyhelper.js.map +1 -1
- package/lib/Signal/Group/sender-chain-key.d.ts +1 -1
- package/lib/Signal/Group/sender-chain-key.d.ts.map +1 -1
- package/lib/Signal/Group/sender-chain-key.js +1 -7
- package/lib/Signal/Group/sender-chain-key.js.map +1 -1
- package/lib/Signal/Group/sender-key-distribution-message.js +2 -2
- package/lib/Signal/Group/sender-key-distribution-message.js.map +1 -1
- package/lib/Signal/Group/sender-key-message.d.ts.map +1 -1
- package/lib/Signal/Group/sender-key-message.js +0 -1
- package/lib/Signal/Group/sender-key-message.js.map +1 -1
- package/lib/Signal/Group/sender-key-record.d.ts +1 -1
- package/lib/Signal/Group/sender-key-record.d.ts.map +1 -1
- package/lib/Signal/Group/sender-key-record.js +2 -11
- package/lib/Signal/Group/sender-key-record.js.map +1 -1
- package/lib/Signal/Group/sender-key-state.d.ts +4 -4
- package/lib/Signal/Group/sender-key-state.d.ts.map +1 -1
- package/lib/Signal/Group/sender-key-state.js +21 -33
- package/lib/Signal/Group/sender-key-state.js.map +1 -1
- package/lib/Signal/Group/sender-message-key.d.ts.map +1 -1
- package/lib/Signal/Group/sender-message-key.js +0 -1
- package/lib/Signal/Group/sender-message-key.js.map +1 -1
- package/lib/Signal/libsignal.d.ts +4 -3
- package/lib/Signal/libsignal.d.ts.map +1 -1
- package/lib/Signal/libsignal.js +243 -42
- package/lib/Signal/libsignal.js.map +1 -1
- 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 +171 -0
- package/lib/Signal/lid-mapping.js.map +1 -0
- package/lib/Socket/Client/types.d.ts +2 -2
- package/lib/Socket/Client/types.d.ts.map +1 -1
- package/lib/Socket/Client/websocket.d.ts +2 -2
- package/lib/Socket/Client/websocket.d.ts.map +1 -1
- package/lib/Socket/Client/websocket.js +2 -2
- package/lib/Socket/Client/websocket.js.map +1 -1
- package/lib/Socket/business.d.ts +31 -17
- package/lib/Socket/business.d.ts.map +1 -1
- package/lib/Socket/business.js +122 -1
- package/lib/Socket/business.js.map +1 -1
- package/lib/Socket/chats.d.ts +17 -9
- package/lib/Socket/chats.d.ts.map +1 -1
- package/lib/Socket/chats.js +54 -23
- package/lib/Socket/chats.js.map +1 -1
- package/lib/Socket/communities.d.ts +41 -14
- package/lib/Socket/communities.d.ts.map +1 -1
- package/lib/Socket/communities.js +83 -3
- package/lib/Socket/communities.js.map +1 -1
- package/lib/Socket/groups.d.ts +16 -9
- package/lib/Socket/groups.d.ts.map +1 -1
- package/lib/Socket/groups.js +14 -11
- package/lib/Socket/groups.js.map +1 -1
- package/lib/Socket/index.d.ts +41 -14
- package/lib/Socket/index.d.ts.map +1 -1
- package/lib/Socket/index.js +1 -0
- package/lib/Socket/index.js.map +1 -1
- package/lib/Socket/messages-recv.d.ts +26 -16
- package/lib/Socket/messages-recv.d.ts.map +1 -1
- package/lib/Socket/messages-recv.js +512 -311
- package/lib/Socket/messages-recv.js.map +1 -1
- package/lib/Socket/messages-send.d.ts +27 -16
- package/lib/Socket/messages-send.d.ts.map +1 -1
- package/lib/Socket/messages-send.js +394 -133
- package/lib/Socket/messages-send.js.map +1 -1
- package/lib/Socket/newsletter.d.ts +18 -12
- package/lib/Socket/newsletter.d.ts.map +1 -1
- package/lib/Socket/newsletter.js +3 -1
- package/lib/Socket/newsletter.js.map +1 -1
- package/lib/Socket/socket.d.ts +13 -3
- package/lib/Socket/socket.d.ts.map +1 -1
- package/lib/Socket/socket.js +353 -78
- package/lib/Socket/socket.js.map +1 -1
- package/lib/Types/Auth.d.ts +12 -1
- package/lib/Types/Auth.d.ts.map +1 -1
- package/lib/Types/Bussines.d.ts +25 -0
- package/lib/Types/Bussines.d.ts.map +1 -0
- package/lib/Types/Bussines.js +2 -0
- package/lib/Types/Bussines.js.map +1 -0
- package/lib/Types/Chat.d.ts +5 -0
- package/lib/Types/Chat.d.ts.map +1 -1
- package/lib/Types/Chat.js.map +1 -1
- package/lib/Types/Contact.d.ts +4 -4
- package/lib/Types/Contact.d.ts.map +1 -1
- package/lib/Types/Events.d.ts +8 -5
- package/lib/Types/Events.d.ts.map +1 -1
- package/lib/Types/GroupMetadata.d.ts +7 -4
- package/lib/Types/GroupMetadata.d.ts.map +1 -1
- package/lib/Types/Message.d.ts +38 -12
- package/lib/Types/Message.d.ts.map +1 -1
- package/lib/Types/Message.js +5 -1
- package/lib/Types/Message.js.map +1 -1
- package/lib/Types/Signal.d.ts +18 -0
- package/lib/Types/Signal.d.ts.map +1 -1
- package/lib/Types/Socket.d.ts +24 -13
- package/lib/Types/Socket.d.ts.map +1 -1
- package/lib/Types/Socket.js.map +1 -1
- package/lib/Utils/auth-utils.d.ts +2 -2
- package/lib/Utils/auth-utils.d.ts.map +1 -1
- package/lib/Utils/auth-utils.js +178 -111
- package/lib/Utils/auth-utils.js.map +1 -1
- package/lib/Utils/browser-utils.d.ts +4 -0
- package/lib/Utils/browser-utils.d.ts.map +1 -0
- package/lib/Utils/browser-utils.js +28 -0
- package/lib/Utils/browser-utils.js.map +1 -0
- package/lib/Utils/chat-utils.d.ts +6 -7
- package/lib/Utils/chat-utils.d.ts.map +1 -1
- package/lib/Utils/chat-utils.js +19 -2
- package/lib/Utils/chat-utils.js.map +1 -1
- package/lib/Utils/crypto.d.ts +6 -6
- package/lib/Utils/crypto.d.ts.map +1 -1
- package/lib/Utils/crypto.js +16 -12
- package/lib/Utils/crypto.js.map +1 -1
- package/lib/Utils/decode-wa-message.d.ts +14 -2
- package/lib/Utils/decode-wa-message.d.ts.map +1 -1
- package/lib/Utils/decode-wa-message.js +104 -15
- package/lib/Utils/decode-wa-message.js.map +1 -1
- package/lib/Utils/event-buffer.d.ts +0 -1
- package/lib/Utils/event-buffer.d.ts.map +1 -1
- package/lib/Utils/event-buffer.js +65 -20
- package/lib/Utils/event-buffer.js.map +1 -1
- package/lib/Utils/generics.d.ts +5 -7
- package/lib/Utils/generics.d.ts.map +1 -1
- package/lib/Utils/generics.js +59 -51
- package/lib/Utils/generics.js.map +1 -1
- package/lib/Utils/history.d.ts +8 -9
- package/lib/Utils/history.d.ts.map +1 -1
- package/lib/Utils/history.js +8 -3
- package/lib/Utils/history.js.map +1 -1
- package/lib/Utils/index.d.ts +2 -1
- package/lib/Utils/index.d.ts.map +1 -1
- package/lib/Utils/index.js +2 -1
- package/lib/Utils/index.js.map +1 -1
- package/lib/Utils/link-preview.d.ts +1 -2
- package/lib/Utils/link-preview.d.ts.map +1 -1
- package/lib/Utils/link-preview.js +1 -1
- package/lib/Utils/link-preview.js.map +1 -1
- package/lib/Utils/message-retry-manager.d.ts +82 -0
- package/lib/Utils/message-retry-manager.d.ts.map +1 -0
- package/lib/Utils/message-retry-manager.js +172 -0
- package/lib/Utils/message-retry-manager.js.map +1 -0
- package/lib/Utils/messages-media.d.ts +14 -12
- package/lib/Utils/messages-media.d.ts.map +1 -1
- package/lib/Utils/messages-media.js +48 -24
- package/lib/Utils/messages-media.js.map +1 -1
- package/lib/Utils/messages.d.ts +17 -4
- package/lib/Utils/messages.d.ts.map +1 -1
- package/lib/Utils/messages.js +85 -21
- package/lib/Utils/messages.js.map +1 -1
- package/lib/Utils/noise-handler.js +2 -1
- package/lib/Utils/noise-handler.js.map +1 -1
- package/lib/Utils/pre-key-manager.d.ts +28 -0
- package/lib/Utils/pre-key-manager.d.ts.map +1 -0
- package/lib/Utils/pre-key-manager.js +106 -0
- package/lib/Utils/pre-key-manager.js.map +1 -0
- package/lib/Utils/process-message.d.ts +26 -8
- package/lib/Utils/process-message.d.ts.map +1 -1
- package/lib/Utils/process-message.js +141 -24
- package/lib/Utils/process-message.js.map +1 -1
- package/lib/Utils/signal.d.ts +4 -4
- package/lib/Utils/signal.d.ts.map +1 -1
- package/lib/Utils/signal.js +21 -12
- package/lib/Utils/signal.js.map +1 -1
- package/lib/Utils/validate-connection.d.ts.map +1 -1
- package/lib/Utils/validate-connection.js +50 -14
- package/lib/Utils/validate-connection.js.map +1 -1
- package/lib/WABinary/decode.d.ts.map +1 -1
- package/lib/WABinary/decode.js +12 -2
- package/lib/WABinary/decode.js.map +1 -1
- package/lib/WABinary/generic-utils.js +1 -1
- package/lib/WABinary/generic-utils.js.map +1 -1
- package/lib/WABinary/jid-utils.d.ts +18 -6
- package/lib/WABinary/jid-utils.d.ts.map +1 -1
- package/lib/WABinary/jid-utils.js +52 -8
- package/lib/WABinary/jid-utils.js.map +1 -1
- package/lib/WAM/constants.d.ts +1 -1
- package/lib/WAM/constants.d.ts.map +1 -1
- package/lib/WAM/constants.js +18516 -10904
- package/lib/WAM/constants.js.map +1 -1
- package/lib/WAM/encode.d.ts.map +1 -1
- package/lib/WAM/encode.js +0 -1
- package/lib/WAM/encode.js.map +1 -1
- package/lib/WAUSync/Protocols/UsyncLIDProtocol.d.ts +2 -1
- package/lib/WAUSync/Protocols/UsyncLIDProtocol.d.ts.map +1 -1
- package/lib/WAUSync/Protocols/UsyncLIDProtocol.js +10 -2
- package/lib/WAUSync/Protocols/UsyncLIDProtocol.js.map +1 -1
- package/lib/WAUSync/USyncQuery.d.ts +1 -1
- package/lib/WAUSync/USyncQuery.d.ts.map +1 -1
- package/lib/WAUSync/USyncQuery.js +23 -20
- package/lib/WAUSync/USyncQuery.js.map +1 -1
- package/package.json +16 -11
- package/lib/Defaults/baileys-version.json +0 -3
- package/lib/Signal/Group/queue-job.d.ts +0 -2
- package/lib/Signal/Group/queue-job.d.ts.map +0 -1
- package/lib/Signal/Group/queue-job.js +0 -54
- package/lib/Signal/Group/queue-job.js.map +0 -1
- package/lib/Socket/usync.d.ts +0 -37
- package/lib/Socket/usync.d.ts.map +0 -1
- package/lib/Socket/usync.js +0 -63
- package/lib/Socket/usync.js.map +0 -1
- package/lib/Utils/baileys-event-stream.d.ts +0 -17
- package/lib/Utils/baileys-event-stream.d.ts.map +0 -1
- package/lib/Utils/baileys-event-stream.js +0 -56
- package/lib/Utils/baileys-event-stream.js.map +0 -1
package/lib/Socket/socket.js
CHANGED
|
@@ -3,10 +3,13 @@ import { randomBytes } from 'crypto';
|
|
|
3
3
|
import { URL } from 'url';
|
|
4
4
|
import { promisify } from 'util';
|
|
5
5
|
import { proto } from '../../WAProto/index.js';
|
|
6
|
-
import { DEF_CALLBACK_PREFIX, DEF_TAG_PREFIX, INITIAL_PREKEY_COUNT, MIN_PREKEY_COUNT, NOISE_WA_HEADER } from '../Defaults/index.js';
|
|
6
|
+
import { DEF_CALLBACK_PREFIX, DEF_TAG_PREFIX, INITIAL_PREKEY_COUNT, MIN_PREKEY_COUNT, MIN_UPLOAD_INTERVAL, NOISE_WA_HEADER, UPLOAD_TIMEOUT } from '../Defaults/index.js';
|
|
7
7
|
import { DisconnectReason } from '../Types/index.js';
|
|
8
|
-
import { addTransactionCapability, aesEncryptCTR, bindWaitForConnectionUpdate, bytesToCrockford, configureSuccessfulPairing, Curve, derivePairingCodeKey, generateLoginNode, generateMdTagPrefix, generateRegistrationNode, getCodeFromWSError, getErrorCodeFromStreamError, getNextPreKeysNode,
|
|
9
|
-
import {
|
|
8
|
+
import { addTransactionCapability, aesEncryptCTR, bindWaitForConnectionUpdate, bytesToCrockford, configureSuccessfulPairing, Curve, derivePairingCodeKey, generateLoginNode, generateMdTagPrefix, generateRegistrationNode, getCodeFromWSError, getErrorCodeFromStreamError, getNextPreKeysNode, makeEventBuffer, makeNoiseHandler, promiseTimeout, signedKeyPair, xmppSignedPreKey } from '../Utils/index.js';
|
|
9
|
+
import { getPlatformId } from '../Utils/browser-utils.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';
|
|
10
13
|
import { WebSocketClient } from './Client/index.js';
|
|
11
14
|
/**
|
|
12
15
|
* Connects to WA servers and performs:
|
|
@@ -16,6 +19,9 @@ import { WebSocketClient } from './Client/index.js';
|
|
|
16
19
|
*/
|
|
17
20
|
export const makeSocket = (config) => {
|
|
18
21
|
const { waWebSocketUrl, connectTimeoutMs, logger, keepAliveIntervalMs, browser, auth: authState, printQRInTerminal, defaultQueryTimeoutMs, transactionOpts, qrTimeout, makeSignalRepository } = config;
|
|
22
|
+
const publicWAMBuffer = new BinaryInfo();
|
|
23
|
+
const uqTagId = generateMdTagPrefix();
|
|
24
|
+
const generateMessageTag = () => `${uqTagId}${epoch++}`;
|
|
19
25
|
if (printQRInTerminal) {
|
|
20
26
|
console.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.');
|
|
21
27
|
}
|
|
@@ -26,9 +32,6 @@ export const makeSocket = (config) => {
|
|
|
26
32
|
if (url.protocol === 'wss' && authState?.creds?.routingInfo) {
|
|
27
33
|
url.searchParams.append('ED', authState.creds.routingInfo.toString('base64url'));
|
|
28
34
|
}
|
|
29
|
-
const ws = new WebSocketClient(url, config);
|
|
30
|
-
ws.connect();
|
|
31
|
-
const ev = makeEventBuffer(logger);
|
|
32
35
|
/** ephemeral key pair used to encrypt/decrypt communication. Unique for each connection */
|
|
33
36
|
const ephemeralKeyPair = Curve.generateKeyPair();
|
|
34
37
|
/** WA noise protocol wrapper */
|
|
@@ -38,17 +41,8 @@ export const makeSocket = (config) => {
|
|
|
38
41
|
logger,
|
|
39
42
|
routingInfo: authState?.creds?.routingInfo
|
|
40
43
|
});
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
const keys = addTransactionCapability(authState.keys, logger, transactionOpts);
|
|
44
|
-
const signalRepository = makeSignalRepository({ creds, keys });
|
|
45
|
-
let lastDateRecv;
|
|
46
|
-
let epoch = 1;
|
|
47
|
-
let keepAliveReq;
|
|
48
|
-
let qrTimer;
|
|
49
|
-
let closed = false;
|
|
50
|
-
const uqTagId = generateMdTagPrefix();
|
|
51
|
-
const generateMessageTag = () => `${uqTagId}${epoch++}`;
|
|
44
|
+
const ws = new WebSocketClient(url, config);
|
|
45
|
+
ws.connect();
|
|
52
46
|
const sendPromise = promisify(ws.send);
|
|
53
47
|
/** send a raw buffer */
|
|
54
48
|
const sendRawMessage = async (data) => {
|
|
@@ -74,35 +68,6 @@ export const makeSocket = (config) => {
|
|
|
74
68
|
const buff = encodeBinaryNode(frame);
|
|
75
69
|
return sendRawMessage(buff);
|
|
76
70
|
};
|
|
77
|
-
/** log & process any unexpected errors */
|
|
78
|
-
const onUnexpectedError = (err, msg) => {
|
|
79
|
-
logger.error({ err }, `unexpected error in '${msg}'`);
|
|
80
|
-
};
|
|
81
|
-
/** await the next incoming message */
|
|
82
|
-
const awaitNextMessage = async (sendMsg) => {
|
|
83
|
-
if (!ws.isOpen) {
|
|
84
|
-
throw new Boom('Connection Closed', {
|
|
85
|
-
statusCode: DisconnectReason.connectionClosed
|
|
86
|
-
});
|
|
87
|
-
}
|
|
88
|
-
let onOpen;
|
|
89
|
-
let onClose;
|
|
90
|
-
const result = promiseTimeout(connectTimeoutMs, (resolve, reject) => {
|
|
91
|
-
onOpen = resolve;
|
|
92
|
-
onClose = mapWebSocketError(reject);
|
|
93
|
-
ws.on('frame', onOpen);
|
|
94
|
-
ws.on('close', onClose);
|
|
95
|
-
ws.on('error', onClose);
|
|
96
|
-
}).finally(() => {
|
|
97
|
-
ws.off('frame', onOpen);
|
|
98
|
-
ws.off('close', onClose);
|
|
99
|
-
ws.off('error', onClose);
|
|
100
|
-
});
|
|
101
|
-
if (sendMsg) {
|
|
102
|
-
sendRawMessage(sendMsg).catch(onClose);
|
|
103
|
-
}
|
|
104
|
-
return result;
|
|
105
|
-
};
|
|
106
71
|
/**
|
|
107
72
|
* Wait for a message with a certain tag to be received
|
|
108
73
|
* @param msgId the message tag to await
|
|
@@ -113,20 +78,37 @@ export const makeSocket = (config) => {
|
|
|
113
78
|
let onErr;
|
|
114
79
|
try {
|
|
115
80
|
const result = await promiseTimeout(timeoutMs, (resolve, reject) => {
|
|
116
|
-
onRecv =
|
|
81
|
+
onRecv = data => {
|
|
82
|
+
resolve(data);
|
|
83
|
+
};
|
|
117
84
|
onErr = err => {
|
|
118
|
-
reject(err ||
|
|
85
|
+
reject(err ||
|
|
86
|
+
new Boom('Connection Closed', {
|
|
87
|
+
statusCode: DisconnectReason.connectionClosed
|
|
88
|
+
}));
|
|
119
89
|
};
|
|
120
90
|
ws.on(`TAG:${msgId}`, onRecv);
|
|
121
|
-
ws.on('close', onErr);
|
|
122
|
-
ws.
|
|
91
|
+
ws.on('close', onErr);
|
|
92
|
+
ws.on('error', onErr);
|
|
93
|
+
return () => reject(new Boom('Query Cancelled'));
|
|
123
94
|
});
|
|
124
95
|
return result;
|
|
125
96
|
}
|
|
97
|
+
catch (error) {
|
|
98
|
+
// Catch timeout and return undefined instead of throwing
|
|
99
|
+
if (error instanceof Boom && error.output?.statusCode === DisconnectReason.timedOut) {
|
|
100
|
+
logger?.warn?.({ msgId }, 'timed out waiting for message');
|
|
101
|
+
return undefined;
|
|
102
|
+
}
|
|
103
|
+
throw error;
|
|
104
|
+
}
|
|
126
105
|
finally {
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
106
|
+
if (onRecv)
|
|
107
|
+
ws.off(`TAG:${msgId}`, onRecv);
|
|
108
|
+
if (onErr) {
|
|
109
|
+
ws.off('close', onErr);
|
|
110
|
+
ws.off('error', onErr);
|
|
111
|
+
}
|
|
130
112
|
}
|
|
131
113
|
};
|
|
132
114
|
/** send a query, and wait for its response. auto-generates message ID if not provided */
|
|
@@ -135,12 +117,182 @@ export const makeSocket = (config) => {
|
|
|
135
117
|
node.attrs.id = generateMessageTag();
|
|
136
118
|
}
|
|
137
119
|
const msgId = node.attrs.id;
|
|
138
|
-
const
|
|
139
|
-
|
|
120
|
+
const result = await promiseTimeout(timeoutMs, async (resolve, reject) => {
|
|
121
|
+
const result = waitForMessage(msgId, timeoutMs).catch(reject);
|
|
122
|
+
sendNode(node)
|
|
123
|
+
.then(async () => resolve(await result))
|
|
124
|
+
.catch(reject);
|
|
125
|
+
});
|
|
126
|
+
if (result && 'tag' in result) {
|
|
140
127
|
assertNodeErrorFree(result);
|
|
141
128
|
}
|
|
142
129
|
return result;
|
|
143
130
|
};
|
|
131
|
+
// Validate current key-bundle on server; on failure, trigger pre-key upload and rethrow
|
|
132
|
+
const digestKeyBundle = async () => {
|
|
133
|
+
const res = await query({
|
|
134
|
+
tag: 'iq',
|
|
135
|
+
attrs: { to: S_WHATSAPP_NET, type: 'get', xmlns: 'encrypt' },
|
|
136
|
+
content: [{ tag: 'digest', attrs: {} }]
|
|
137
|
+
});
|
|
138
|
+
const digestNode = getBinaryNodeChild(res, 'digest');
|
|
139
|
+
if (!digestNode) {
|
|
140
|
+
await uploadPreKeys();
|
|
141
|
+
throw new Error('encrypt/get digest returned no digest node');
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
// Rotate our signed pre-key on server; on failure, run digest as fallback and rethrow
|
|
145
|
+
const rotateSignedPreKey = async () => {
|
|
146
|
+
const newId = (creds.signedPreKey.keyId || 0) + 1;
|
|
147
|
+
const skey = await signedKeyPair(creds.signedIdentityKey, newId);
|
|
148
|
+
await query({
|
|
149
|
+
tag: 'iq',
|
|
150
|
+
attrs: { to: S_WHATSAPP_NET, type: 'set', xmlns: 'encrypt' },
|
|
151
|
+
content: [
|
|
152
|
+
{
|
|
153
|
+
tag: 'rotate',
|
|
154
|
+
attrs: {},
|
|
155
|
+
content: [xmppSignedPreKey(skey)]
|
|
156
|
+
}
|
|
157
|
+
]
|
|
158
|
+
});
|
|
159
|
+
// Persist new signed pre-key in creds
|
|
160
|
+
ev.emit('creds.update', { signedPreKey: skey });
|
|
161
|
+
};
|
|
162
|
+
const executeUSyncQuery = async (usyncQuery) => {
|
|
163
|
+
if (usyncQuery.protocols.length === 0) {
|
|
164
|
+
throw new Boom('USyncQuery must have at least one protocol');
|
|
165
|
+
}
|
|
166
|
+
// todo: validate users, throw WARNING on no valid users
|
|
167
|
+
// variable below has only validated users
|
|
168
|
+
const validUsers = usyncQuery.users;
|
|
169
|
+
const userNodes = validUsers.map(user => {
|
|
170
|
+
return {
|
|
171
|
+
tag: 'user',
|
|
172
|
+
attrs: {
|
|
173
|
+
jid: !user.phone ? user.id : undefined
|
|
174
|
+
},
|
|
175
|
+
content: usyncQuery.protocols.map(a => a.getUserElement(user)).filter(a => a !== null)
|
|
176
|
+
};
|
|
177
|
+
});
|
|
178
|
+
const listNode = {
|
|
179
|
+
tag: 'list',
|
|
180
|
+
attrs: {},
|
|
181
|
+
content: userNodes
|
|
182
|
+
};
|
|
183
|
+
const queryNode = {
|
|
184
|
+
tag: 'query',
|
|
185
|
+
attrs: {},
|
|
186
|
+
content: usyncQuery.protocols.map(a => a.getQueryElement())
|
|
187
|
+
};
|
|
188
|
+
const iq = {
|
|
189
|
+
tag: 'iq',
|
|
190
|
+
attrs: {
|
|
191
|
+
to: S_WHATSAPP_NET,
|
|
192
|
+
type: 'get',
|
|
193
|
+
xmlns: 'usync'
|
|
194
|
+
},
|
|
195
|
+
content: [
|
|
196
|
+
{
|
|
197
|
+
tag: 'usync',
|
|
198
|
+
attrs: {
|
|
199
|
+
context: usyncQuery.context,
|
|
200
|
+
mode: usyncQuery.mode,
|
|
201
|
+
sid: generateMessageTag(),
|
|
202
|
+
last: 'true',
|
|
203
|
+
index: '0'
|
|
204
|
+
},
|
|
205
|
+
content: [queryNode, listNode]
|
|
206
|
+
}
|
|
207
|
+
]
|
|
208
|
+
};
|
|
209
|
+
const result = await query(iq);
|
|
210
|
+
return usyncQuery.parseUSyncQueryResult(result);
|
|
211
|
+
};
|
|
212
|
+
const onWhatsApp = async (...phoneNumber) => {
|
|
213
|
+
let usyncQuery = new USyncQuery();
|
|
214
|
+
let contactEnabled = false;
|
|
215
|
+
for (const jid of phoneNumber) {
|
|
216
|
+
if (isLidUser(jid)) {
|
|
217
|
+
logger?.warn('LIDs are not supported with onWhatsApp');
|
|
218
|
+
continue;
|
|
219
|
+
}
|
|
220
|
+
else {
|
|
221
|
+
if (!contactEnabled) {
|
|
222
|
+
contactEnabled = true;
|
|
223
|
+
usyncQuery = usyncQuery.withContactProtocol();
|
|
224
|
+
}
|
|
225
|
+
const phone = `+${jid.replace('+', '').split('@')[0]?.split(':')[0]}`;
|
|
226
|
+
usyncQuery.withUser(new USyncUser().withPhone(phone));
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
if (usyncQuery.users.length === 0) {
|
|
230
|
+
return []; // return early without forcing an empty query
|
|
231
|
+
}
|
|
232
|
+
const results = await executeUSyncQuery(usyncQuery);
|
|
233
|
+
if (results) {
|
|
234
|
+
return results.list.filter(a => !!a.contact).map(({ contact, id }) => ({ jid: id, exists: contact }));
|
|
235
|
+
}
|
|
236
|
+
};
|
|
237
|
+
const pnFromLIDUSync = async (jids) => {
|
|
238
|
+
const usyncQuery = new USyncQuery().withLIDProtocol().withContext('background');
|
|
239
|
+
for (const jid of jids) {
|
|
240
|
+
if (isLidUser(jid)) {
|
|
241
|
+
logger?.warn('LID user found in LID fetch call');
|
|
242
|
+
continue;
|
|
243
|
+
}
|
|
244
|
+
else {
|
|
245
|
+
usyncQuery.withUser(new USyncUser().withId(jid));
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
if (usyncQuery.users.length === 0) {
|
|
249
|
+
return []; // return early without forcing an empty query
|
|
250
|
+
}
|
|
251
|
+
const results = await executeUSyncQuery(usyncQuery);
|
|
252
|
+
if (results) {
|
|
253
|
+
return results.list.filter(a => !!a.lid).map(({ lid, id }) => ({ pn: id, lid: lid }));
|
|
254
|
+
}
|
|
255
|
+
return [];
|
|
256
|
+
};
|
|
257
|
+
const ev = makeEventBuffer(logger);
|
|
258
|
+
const { creds } = authState;
|
|
259
|
+
// add transaction capability
|
|
260
|
+
const keys = addTransactionCapability(authState.keys, logger, transactionOpts);
|
|
261
|
+
const signalRepository = makeSignalRepository({ creds, keys }, logger, pnFromLIDUSync);
|
|
262
|
+
let lastDateRecv;
|
|
263
|
+
let epoch = 1;
|
|
264
|
+
let keepAliveReq;
|
|
265
|
+
let qrTimer;
|
|
266
|
+
let closed = false;
|
|
267
|
+
/** log & process any unexpected errors */
|
|
268
|
+
const onUnexpectedError = (err, msg) => {
|
|
269
|
+
logger.error({ err }, `unexpected error in '${msg}'`);
|
|
270
|
+
};
|
|
271
|
+
/** await the next incoming message */
|
|
272
|
+
const awaitNextMessage = async (sendMsg) => {
|
|
273
|
+
if (!ws.isOpen) {
|
|
274
|
+
throw new Boom('Connection Closed', {
|
|
275
|
+
statusCode: DisconnectReason.connectionClosed
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
let onOpen;
|
|
279
|
+
let onClose;
|
|
280
|
+
const result = promiseTimeout(connectTimeoutMs, (resolve, reject) => {
|
|
281
|
+
onOpen = resolve;
|
|
282
|
+
onClose = mapWebSocketError(reject);
|
|
283
|
+
ws.on('frame', onOpen);
|
|
284
|
+
ws.on('close', onClose);
|
|
285
|
+
ws.on('error', onClose);
|
|
286
|
+
}).finally(() => {
|
|
287
|
+
ws.off('frame', onOpen);
|
|
288
|
+
ws.off('close', onClose);
|
|
289
|
+
ws.off('error', onClose);
|
|
290
|
+
});
|
|
291
|
+
if (sendMsg) {
|
|
292
|
+
sendRawMessage(sendMsg).catch(onClose);
|
|
293
|
+
}
|
|
294
|
+
return result;
|
|
295
|
+
};
|
|
144
296
|
/** connection handshake */
|
|
145
297
|
const validateConnection = async () => {
|
|
146
298
|
let helloMsg = {
|
|
@@ -169,7 +321,7 @@ export const makeSocket = (config) => {
|
|
|
169
321
|
payload: payloadEnc
|
|
170
322
|
}
|
|
171
323
|
}).finish());
|
|
172
|
-
noise.finishInit();
|
|
324
|
+
await noise.finishInit();
|
|
173
325
|
startKeepAliveRequest();
|
|
174
326
|
};
|
|
175
327
|
const getAvailablePreKeysOnServer = async () => {
|
|
@@ -186,25 +338,107 @@ export const makeSocket = (config) => {
|
|
|
186
338
|
const countChild = getBinaryNodeChild(result, 'count');
|
|
187
339
|
return +countChild.attrs.value;
|
|
188
340
|
};
|
|
341
|
+
// Pre-key upload state management
|
|
342
|
+
let uploadPreKeysPromise = null;
|
|
343
|
+
let lastUploadTime = 0;
|
|
189
344
|
/** generates and uploads a set of pre-keys to the server */
|
|
190
|
-
const uploadPreKeys = async (count =
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
const
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
345
|
+
const uploadPreKeys = async (count = MIN_PREKEY_COUNT, retryCount = 0) => {
|
|
346
|
+
// Check minimum interval (except for retries)
|
|
347
|
+
if (retryCount === 0) {
|
|
348
|
+
const timeSinceLastUpload = Date.now() - lastUploadTime;
|
|
349
|
+
if (timeSinceLastUpload < MIN_UPLOAD_INTERVAL) {
|
|
350
|
+
logger.debug(`Skipping upload, only ${timeSinceLastUpload}ms since last upload`);
|
|
351
|
+
return;
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
// Prevent multiple concurrent uploads
|
|
355
|
+
if (uploadPreKeysPromise) {
|
|
356
|
+
logger.debug('Pre-key upload already in progress, waiting for completion');
|
|
357
|
+
await uploadPreKeysPromise;
|
|
358
|
+
}
|
|
359
|
+
const uploadLogic = async () => {
|
|
360
|
+
logger.info({ count, retryCount }, 'uploading pre-keys');
|
|
361
|
+
// Generate and save pre-keys atomically (prevents ID collisions on retry)
|
|
362
|
+
const node = await keys.transaction(async () => {
|
|
363
|
+
logger.debug({ requestedCount: count }, 'generating pre-keys with requested count');
|
|
364
|
+
const { update, node } = await getNextPreKeysNode({ creds, keys }, count);
|
|
365
|
+
// Update credentials immediately to prevent duplicate IDs on retry
|
|
366
|
+
ev.emit('creds.update', update);
|
|
367
|
+
return node; // Only return node since update is already used
|
|
368
|
+
}, creds?.me?.id || 'upload-pre-keys');
|
|
369
|
+
// Upload to server (outside transaction, can fail without affecting local keys)
|
|
370
|
+
try {
|
|
371
|
+
await query(node);
|
|
372
|
+
logger.info({ count }, 'uploaded pre-keys successfully');
|
|
373
|
+
lastUploadTime = Date.now();
|
|
374
|
+
}
|
|
375
|
+
catch (uploadError) {
|
|
376
|
+
logger.error({ uploadError: uploadError.toString(), count }, 'Failed to upload pre-keys to server');
|
|
377
|
+
// Exponential backoff retry (max 3 retries)
|
|
378
|
+
if (retryCount < 3) {
|
|
379
|
+
const backoffDelay = Math.min(1000 * Math.pow(2, retryCount), 10000);
|
|
380
|
+
logger.info(`Retrying pre-key upload in ${backoffDelay}ms`);
|
|
381
|
+
await new Promise(resolve => setTimeout(resolve, backoffDelay));
|
|
382
|
+
return uploadPreKeys(count, retryCount + 1);
|
|
383
|
+
}
|
|
384
|
+
throw uploadError;
|
|
385
|
+
}
|
|
386
|
+
};
|
|
387
|
+
// Add timeout protection
|
|
388
|
+
uploadPreKeysPromise = Promise.race([
|
|
389
|
+
uploadLogic(),
|
|
390
|
+
new Promise((_, reject) => setTimeout(() => reject(new Boom('Pre-key upload timeout', { statusCode: 408 })), UPLOAD_TIMEOUT))
|
|
391
|
+
]);
|
|
392
|
+
try {
|
|
393
|
+
await uploadPreKeysPromise;
|
|
394
|
+
}
|
|
395
|
+
finally {
|
|
396
|
+
uploadPreKeysPromise = null;
|
|
397
|
+
}
|
|
398
|
+
};
|
|
399
|
+
const verifyCurrentPreKeyExists = async () => {
|
|
400
|
+
const currentPreKeyId = creds.nextPreKeyId - 1;
|
|
401
|
+
if (currentPreKeyId <= 0) {
|
|
402
|
+
return { exists: false, currentPreKeyId: 0 };
|
|
403
|
+
}
|
|
404
|
+
const preKeys = await keys.get('pre-key', [currentPreKeyId.toString()]);
|
|
405
|
+
const exists = !!preKeys[currentPreKeyId.toString()];
|
|
406
|
+
return { exists, currentPreKeyId };
|
|
198
407
|
};
|
|
199
408
|
const uploadPreKeysToServerIfRequired = async () => {
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
409
|
+
try {
|
|
410
|
+
let count = 0;
|
|
411
|
+
const preKeyCount = await getAvailablePreKeysOnServer();
|
|
412
|
+
if (preKeyCount === 0)
|
|
413
|
+
count = INITIAL_PREKEY_COUNT;
|
|
414
|
+
else
|
|
415
|
+
count = MIN_PREKEY_COUNT;
|
|
416
|
+
const { exists: currentPreKeyExists, currentPreKeyId } = await verifyCurrentPreKeyExists();
|
|
417
|
+
logger.info(`${preKeyCount} pre-keys found on server`);
|
|
418
|
+
logger.info(`Current prekey ID: ${currentPreKeyId}, exists in storage: ${currentPreKeyExists}`);
|
|
419
|
+
const lowServerCount = preKeyCount <= count;
|
|
420
|
+
const missingCurrentPreKey = !currentPreKeyExists && currentPreKeyId > 0;
|
|
421
|
+
const shouldUpload = lowServerCount || missingCurrentPreKey;
|
|
422
|
+
if (shouldUpload) {
|
|
423
|
+
const reasons = [];
|
|
424
|
+
if (lowServerCount)
|
|
425
|
+
reasons.push(`server count low (${preKeyCount})`);
|
|
426
|
+
if (missingCurrentPreKey)
|
|
427
|
+
reasons.push(`current prekey ${currentPreKeyId} missing from storage`);
|
|
428
|
+
logger.info(`Uploading PreKeys due to: ${reasons.join(', ')}`);
|
|
429
|
+
await uploadPreKeys(count);
|
|
430
|
+
}
|
|
431
|
+
else {
|
|
432
|
+
logger.info(`PreKey validation passed - Server: ${preKeyCount}, Current prekey ${currentPreKeyId} exists`);
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
catch (error) {
|
|
436
|
+
logger.error({ error }, 'Failed to check/upload pre-keys during initialization');
|
|
437
|
+
// Don't throw - allow connection to continue even if pre-key check fails
|
|
204
438
|
}
|
|
205
439
|
};
|
|
206
|
-
const onMessageReceived = (data) => {
|
|
207
|
-
noise.decodeFrame(data, frame => {
|
|
440
|
+
const onMessageReceived = async (data) => {
|
|
441
|
+
await noise.decodeFrame(data, frame => {
|
|
208
442
|
// reset ping timeout
|
|
209
443
|
lastDateRecv = new Date();
|
|
210
444
|
let anyTriggered = false;
|
|
@@ -425,7 +659,7 @@ export const makeSocket = (config) => {
|
|
|
425
659
|
content: [
|
|
426
660
|
{
|
|
427
661
|
tag: 'add',
|
|
428
|
-
attrs: {},
|
|
662
|
+
attrs: { t: Math.round(Date.now() / 1000) + '' },
|
|
429
663
|
content: wamBuffer
|
|
430
664
|
}
|
|
431
665
|
]
|
|
@@ -497,17 +731,53 @@ export const makeSocket = (config) => {
|
|
|
497
731
|
});
|
|
498
732
|
// login complete
|
|
499
733
|
ws.on('CB:success', async (node) => {
|
|
500
|
-
|
|
501
|
-
|
|
734
|
+
try {
|
|
735
|
+
await uploadPreKeysToServerIfRequired();
|
|
736
|
+
await sendPassiveIq('active');
|
|
737
|
+
// After successful login, validate our key-bundle against server
|
|
738
|
+
try {
|
|
739
|
+
await digestKeyBundle();
|
|
740
|
+
}
|
|
741
|
+
catch (e) {
|
|
742
|
+
logger.warn({ e }, 'failed to run digest after login');
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
catch (err) {
|
|
746
|
+
logger.warn({ err }, 'failed to send initial passive iq');
|
|
747
|
+
}
|
|
502
748
|
logger.info('opened connection to WA');
|
|
503
749
|
clearTimeout(qrTimer); // will never happen in all likelyhood -- but just in case WA sends success on first try
|
|
504
750
|
ev.emit('creds.update', { me: { ...authState.creds.me, lid: node.attrs.lid } });
|
|
505
751
|
ev.emit('connection.update', { connection: 'open' });
|
|
752
|
+
if (node.attrs.lid && authState.creds.me?.id) {
|
|
753
|
+
const myLID = node.attrs.lid;
|
|
754
|
+
process.nextTick(async () => {
|
|
755
|
+
try {
|
|
756
|
+
const myPN = authState.creds.me.id;
|
|
757
|
+
// Store our own LID-PN mapping
|
|
758
|
+
await signalRepository.lidMapping.storeLIDPNMappings([{ lid: myLID, pn: myPN }]);
|
|
759
|
+
// Create device list for our own user (needed for bulk migration)
|
|
760
|
+
const { user, device } = jidDecode(myPN);
|
|
761
|
+
await authState.keys.set({
|
|
762
|
+
'device-list': {
|
|
763
|
+
[user]: [device?.toString() || '0']
|
|
764
|
+
}
|
|
765
|
+
});
|
|
766
|
+
// migrate our own session
|
|
767
|
+
await signalRepository.migrateSession(myPN, myLID);
|
|
768
|
+
logger.info({ myPN, myLID }, 'Own LID session created successfully');
|
|
769
|
+
}
|
|
770
|
+
catch (error) {
|
|
771
|
+
logger.error({ error, lid: myLID }, 'Failed to create own LID session');
|
|
772
|
+
}
|
|
773
|
+
});
|
|
774
|
+
}
|
|
506
775
|
});
|
|
507
776
|
ws.on('CB:stream:error', (node) => {
|
|
508
|
-
|
|
777
|
+
const [reasonNode] = getAllBinaryNodeChildren(node);
|
|
778
|
+
logger.error({ reasonNode, fullErrorNode: node }, 'stream errored out');
|
|
509
779
|
const { reason, statusCode } = getErrorCodeFromStreamError(node);
|
|
510
|
-
end(new Boom(`Stream Errored (${reason})`, { statusCode, data: node }));
|
|
780
|
+
end(new Boom(`Stream Errored (${reason})`, { statusCode, data: reasonNode || node }));
|
|
511
781
|
});
|
|
512
782
|
// stream fail, possible logout
|
|
513
783
|
ws.on('CB:failure', (node) => {
|
|
@@ -517,9 +787,9 @@ export const makeSocket = (config) => {
|
|
|
517
787
|
ws.on('CB:ib,,downgrade_webclient', () => {
|
|
518
788
|
end(new Boom('Multi-device beta not joined', { statusCode: DisconnectReason.multideviceMismatch }));
|
|
519
789
|
});
|
|
520
|
-
ws.on('CB:ib,,offline_preview', (node) => {
|
|
790
|
+
ws.on('CB:ib,,offline_preview', async (node) => {
|
|
521
791
|
logger.info('offline preview received', JSON.stringify(node));
|
|
522
|
-
sendNode({
|
|
792
|
+
await sendNode({
|
|
523
793
|
tag: 'ib',
|
|
524
794
|
attrs: {},
|
|
525
795
|
content: [{ tag: 'offline_batch', attrs: { count: '100' } }]
|
|
@@ -589,10 +859,15 @@ export const makeSocket = (config) => {
|
|
|
589
859
|
onUnexpectedError,
|
|
590
860
|
uploadPreKeys,
|
|
591
861
|
uploadPreKeysToServerIfRequired,
|
|
862
|
+
digestKeyBundle,
|
|
863
|
+
rotateSignedPreKey,
|
|
592
864
|
requestPairingCode,
|
|
865
|
+
wamBuffer: publicWAMBuffer,
|
|
593
866
|
/** Waits for the connection to WA to reach a state */
|
|
594
867
|
waitForConnectionUpdate: bindWaitForConnectionUpdate(ev),
|
|
595
|
-
sendWAMBuffer
|
|
868
|
+
sendWAMBuffer,
|
|
869
|
+
executeUSyncQuery,
|
|
870
|
+
onWhatsApp
|
|
596
871
|
};
|
|
597
872
|
};
|
|
598
873
|
/**
|