@hansaka02/baileys 7.3.2 → 7.3.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/Defaults/baileys-version.json +2 -2
- package/lib/Defaults/connection.js +51 -0
- package/lib/Defaults/constants.js +74 -0
- package/lib/Defaults/history.js +19 -0
- package/lib/Defaults/index.js +36 -142
- package/lib/Defaults/media.js +48 -0
- package/lib/Defaults/prefix.js +18 -0
- package/lib/Signal/Group/group-session-builder.js +10 -42
- package/lib/Signal/Group/group_cipher.js +9 -6
- package/lib/Signal/Group/index.js +39 -53
- package/lib/Signal/Group/keyhelper.js +8 -41
- package/lib/Signal/Group/sender-chain-key.js +5 -18
- package/lib/Signal/Group/sender-key-distribution-message.js +7 -7
- package/lib/Signal/Group/sender-key-message.js +12 -8
- package/lib/Signal/Group/sender-key-record.js +7 -16
- package/lib/Signal/Group/sender-key-state.js +15 -61
- package/lib/Signal/Group/sender-message-key.js +2 -2
- package/lib/Signal/libsignal.js +237 -177
- package/lib/Signal/lid-mapping.js +128 -71
- package/lib/Socket/Client/types.js +2 -2
- package/lib/Socket/Client/websocket.js +25 -16
- package/lib/Socket/business.js +46 -33
- package/lib/Socket/chats.js +286 -170
- package/lib/Socket/community.js +215 -77
- package/lib/Socket/groups.js +77 -61
- package/lib/Socket/index.js +4 -4
- package/lib/Socket/messages-recv.js +629 -457
- package/lib/Socket/messages-send.js +645 -656
- package/lib/Socket/mex.js +61 -0
- package/lib/Socket/newsletter.js +166 -245
- package/lib/Socket/socket.js +396 -170
- package/lib/Store/index.js +27 -11
- package/lib/Store/make-cache-manager-store.js +14 -15
- package/lib/Store/make-in-memory-store.js +28 -24
- package/lib/Types/LabelAssociation.js +2 -2
- package/lib/Types/Message.js +6 -6
- package/lib/Types/MexUpdates.js +5 -5
- package/lib/Types/Newsletter.js +32 -25
- package/lib/Types/State.js +4 -4
- package/lib/Types/index.js +28 -12
- package/lib/Utils/auth-utils.js +212 -375
- package/lib/Utils/baileys-event-stream.js +68 -69
- package/lib/Utils/browser-utils.js +43 -0
- package/lib/Utils/business.js +63 -53
- package/lib/Utils/chat-utils.js +241 -106
- package/lib/Utils/crypto.js +25 -45
- package/lib/Utils/decode-wa-message.js +361 -311
- package/lib/Utils/event-buffer.js +97 -42
- package/lib/Utils/generics.js +90 -207
- package/lib/Utils/history.js +29 -27
- package/lib/Utils/index.js +28 -14
- package/lib/Utils/link-preview.js +24 -62
- package/lib/Utils/logger.js +5 -5
- package/lib/Utils/lt-hash.js +29 -23
- package/lib/Utils/make-mutex.js +26 -28
- package/lib/Utils/message-retry-manager.js +55 -7
- package/lib/Utils/messages-media.js +434 -247
- package/lib/Utils/messages.js +963 -917
- package/lib/Utils/noise-handler.js +60 -20
- package/lib/Utils/pre-key-manager.js +126 -0
- package/lib/Utils/process-message.js +216 -141
- package/lib/Utils/signal.js +75 -37
- package/lib/Utils/use-multi-file-auth-state.js +18 -22
- package/lib/Utils/validate-connection.js +96 -66
- package/lib/WABinary/constants.js +1268 -1268
- package/lib/WABinary/decode.js +62 -34
- package/lib/WABinary/encode.js +57 -36
- package/lib/WABinary/generic-utils.js +4 -4
- package/lib/WABinary/index.js +27 -11
- package/lib/WABinary/jid-utils.js +58 -11
- package/lib/WAM/constants.js +19064 -11563
- package/lib/WAM/encode.js +71 -14
- package/lib/WAM/index.js +27 -11
- package/lib/WAUSync/Protocols/USyncBotProfileProtocol.js +20 -16
- package/lib/WAUSync/Protocols/USyncContactProtocol.js +2 -2
- package/lib/WAUSync/Protocols/USyncDeviceProtocol.js +7 -4
- package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.js +2 -2
- package/lib/WAUSync/Protocols/USyncLIDProtocol.js +0 -2
- package/lib/WAUSync/Protocols/USyncStatusProtocol.js +2 -2
- package/lib/WAUSync/Protocols/index.js +27 -11
- package/lib/WAUSync/USyncQuery.js +51 -28
- package/lib/WAUSync/index.js +27 -11
- package/lib/index.js +60 -31
- package/package.json +12 -17
- package/WAProto/AICommon/AICommon.d.ts +0 -11702
- package/WAProto/Adv/Adv.d.ts +0 -643
- package/WAProto/BotMetadata/BotMetadata.d.ts +0 -5654
- package/WAProto/Cert/Cert.d.ts +0 -613
- package/WAProto/ChatLockSettings/ChatLockSettings.d.ts +0 -476
- package/WAProto/CompanionReg/CompanionReg.d.ts +0 -1361
- package/WAProto/DeviceCapabilities/DeviceCapabilities.d.ts +0 -577
- package/WAProto/E2E/E2E.d.ts +0 -41724
- package/WAProto/Ephemeral/Ephemeral.d.ts +0 -114
- package/WAProto/HistorySync/HistorySync.d.ts +0 -51700
- package/WAProto/LidMigrationSyncPayload/LidMigrationSyncPayload.d.ts +0 -229
- package/WAProto/MdStorageChatRowOpaqueData/MdStorageChatRowOpaqueData.d.ts +0 -583
- package/WAProto/MdStorageMsgRowOpaqueData/MdStorageMsgRowOpaqueData.d.ts +0 -42897
- package/WAProto/MmsRetry/MmsRetry.d.ts +0 -243
- package/WAProto/Protocol/Protocol.d.ts +0 -270
- package/WAProto/Reporting/Reporting.d.ts +0 -371
- package/WAProto/ServerSync/ServerSync.d.ts +0 -1285
- package/WAProto/SignalLocalStorageProtocol/SignalLocalStorageProtocol.d.ts +0 -1868
- package/WAProto/SignalWhisperTextProtocol/SignalWhisperTextProtocol.d.ts +0 -767
- package/WAProto/StatusAttributions/StatusAttributions.d.ts +0 -1027
- package/WAProto/SyncAction/SyncAction.d.ts +0 -11193
- package/WAProto/UserPassword/UserPassword.d.ts +0 -363
- package/WAProto/VnameCert/VnameCert.d.ts +0 -821
- package/WAProto/Wa6/Wa6.d.ts +0 -2128
- package/WAProto/Web/Web.d.ts +0 -46383
- package/WAProto/index.d.ts +0 -55
- package/lib/Defaults/index.d.ts +0 -77
- package/lib/Signal/Group/ciphertext-message.d.ts +0 -9
- package/lib/Signal/Group/group-session-builder.d.ts +0 -17
- package/lib/Signal/Group/group_cipher.d.ts +0 -19
- package/lib/Signal/Group/index.d.ts +0 -11
- package/lib/Signal/Group/keyhelper.d.ts +0 -16
- package/lib/Signal/Group/sender-chain-key.d.ts +0 -14
- package/lib/Signal/Group/sender-key-distribution-message.d.ts +0 -17
- package/lib/Signal/Group/sender-key-message.d.ts +0 -19
- package/lib/Signal/Group/sender-key-name.d.ts +0 -19
- package/lib/Signal/Group/sender-key-record.d.ts +0 -32
- package/lib/Signal/Group/sender-key-state.d.ts +0 -44
- package/lib/Signal/Group/sender-message-key.d.ts +0 -11
- package/lib/Signal/libsignal.d.ts +0 -8
- package/lib/Signal/lid-mapping.d.ts +0 -28
- package/lib/Socket/Client/index.d.ts +0 -2
- package/lib/Socket/Client/types.d.ts +0 -16
- package/lib/Socket/Client/websocket.d.ts +0 -13
- package/lib/Socket/business.d.ts +0 -187
- package/lib/Socket/chats.d.ts +0 -97
- package/lib/Socket/community.d.ts +0 -129
- package/lib/Socket/groups.d.ts +0 -129
- package/lib/Socket/index.d.ts +0 -191
- package/lib/Socket/messages-recv.d.ts +0 -174
- package/lib/Socket/messages-send.d.ts +0 -165
- package/lib/Socket/newsletter.d.ts +0 -145
- package/lib/Socket/socket.d.ts +0 -45
- package/lib/Socket/usync.d.ts +0 -37
- package/lib/Socket/usync.js +0 -83
- package/lib/Store/index.d.ts +0 -4
- package/lib/Store/make-cache-manager-store.d.ts +0 -14
- package/lib/Store/make-in-memory-store.d.ts +0 -123
- package/lib/Store/make-ordered-dictionary.d.ts +0 -12
- package/lib/Store/object-repository.d.ts +0 -10
- package/lib/Types/Auth.d.ts +0 -121
- package/lib/Types/Bussiness.d.ts +0 -28
- package/lib/Types/Call.d.ts +0 -14
- package/lib/Types/Chat.d.ts +0 -143
- package/lib/Types/Contact.d.ts +0 -23
- package/lib/Types/Events.d.ts +0 -226
- package/lib/Types/GroupMetadata.d.ts +0 -66
- package/lib/Types/Label.d.ts +0 -48
- package/lib/Types/LabelAssociation.d.ts +0 -35
- package/lib/Types/Message.d.ts +0 -484
- package/lib/Types/MexUpdates.d.ts +0 -9
- package/lib/Types/Newsletter.d.ts +0 -109
- package/lib/Types/Product.d.ts +0 -92
- package/lib/Types/Signal.d.ts +0 -98
- package/lib/Types/Socket.d.ts +0 -141
- package/lib/Types/State.d.ts +0 -41
- package/lib/Types/USync.d.ts +0 -26
- package/lib/Types/index.d.ts +0 -80
- package/lib/Utils/auth-utils.d.ts +0 -21
- package/lib/Utils/baileys-event-stream.d.ts +0 -18
- package/lib/Utils/business.d.ts +0 -29
- package/lib/Utils/chat-utils.d.ts +0 -82
- package/lib/Utils/crypto.d.ts +0 -56
- package/lib/Utils/decode-wa-message.d.ts +0 -53
- package/lib/Utils/event-buffer.d.ts +0 -39
- package/lib/Utils/generics.d.ts +0 -117
- package/lib/Utils/history.d.ts +0 -23
- package/lib/Utils/index.d.ts +0 -20
- package/lib/Utils/link-preview.d.ts +0 -23
- package/lib/Utils/logger.d.ts +0 -13
- package/lib/Utils/lt-hash.d.ts +0 -14
- package/lib/Utils/make-mutex.d.ts +0 -9
- package/lib/Utils/message-retry-manager.d.ts +0 -88
- package/lib/Utils/messages-media.d.ts +0 -135
- package/lib/Utils/messages.d.ts +0 -105
- package/lib/Utils/noise-handler.d.ts +0 -20
- package/lib/Utils/process-message.d.ts +0 -49
- package/lib/Utils/signal.d.ts +0 -42
- package/lib/Utils/use-mongo-file-auth-state.d.ts +0 -6
- package/lib/Utils/use-mongo-file-auth-state.js +0 -84
- package/lib/Utils/use-multi-file-auth-state.d.ts +0 -13
- package/lib/Utils/use-single-file-auth-state.d.ts +0 -13
- package/lib/Utils/use-single-file-auth-state.js +0 -80
- package/lib/Utils/validate-connection.d.ts +0 -13
- package/lib/WABinary/constants.d.ts +0 -30
- package/lib/WABinary/decode.d.ts +0 -9
- package/lib/WABinary/encode.d.ts +0 -3
- package/lib/WABinary/generic-utils.d.ts +0 -28
- package/lib/WABinary/index.d.ts +0 -5
- package/lib/WABinary/jid-utils.d.ts +0 -58
- package/lib/WABinary/types.d.ts +0 -22
- package/lib/WAM/BinaryInfo.d.ts +0 -16
- package/lib/WAM/constants.d.ts +0 -47
- package/lib/WAM/encode.d.ts +0 -3
- package/lib/WAM/index.d.ts +0 -3
- package/lib/WAUSync/Protocols/USyncBotProfileProtocol.d.ts +0 -28
- package/lib/WAUSync/Protocols/USyncContactProtocol.d.ts +0 -10
- package/lib/WAUSync/Protocols/USyncDeviceProtocol.d.ts +0 -26
- package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.d.ts +0 -14
- package/lib/WAUSync/Protocols/USyncLIDProtocol.d.ts +0 -10
- package/lib/WAUSync/Protocols/USyncStatusProtocol.d.ts +0 -14
- package/lib/WAUSync/Protocols/index.d.ts +0 -6
- package/lib/WAUSync/USyncQuery.d.ts +0 -31
- package/lib/WAUSync/USyncUser.d.ts +0 -12
- package/lib/WAUSync/index.d.ts +0 -3
- package/lib/index.d.ts +0 -13
package/lib/Socket/socket.js
CHANGED
|
@@ -2,17 +2,62 @@
|
|
|
2
2
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", { value: true })
|
|
4
4
|
|
|
5
|
-
const
|
|
6
|
-
const
|
|
7
|
-
const
|
|
8
|
-
const
|
|
9
|
-
const
|
|
10
|
-
const
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
const
|
|
15
|
-
|
|
5
|
+
const { Boom } = require("@hapi/boom")
|
|
6
|
+
const { randomBytes } = require("crypto")
|
|
7
|
+
const { URL } = require("url")
|
|
8
|
+
const { promisify } = require("util")
|
|
9
|
+
const { proto } = require("../../WAProto")
|
|
10
|
+
const {
|
|
11
|
+
DEF_TAG_PREFIX,
|
|
12
|
+
DEF_CALLBACK_PREFIX
|
|
13
|
+
} = require("../Defaults/prefix")
|
|
14
|
+
const {
|
|
15
|
+
NOISE_WA_HEADER,
|
|
16
|
+
INITIAL_PREKEY_COUNT,
|
|
17
|
+
MIN_UPLOAD_INTERVAL,
|
|
18
|
+
MIN_PREKEY_COUNT,
|
|
19
|
+
UPLOAD_TIMEOUT
|
|
20
|
+
} = require("../Defaults/constants")
|
|
21
|
+
const { DisconnectReason } = require("../Types")
|
|
22
|
+
const {
|
|
23
|
+
addTransactionCapability,
|
|
24
|
+
aesEncryptCTR,
|
|
25
|
+
printQRIfNecessaryListener,
|
|
26
|
+
bindWaitForConnectionUpdate,
|
|
27
|
+
bytesToCrockford,
|
|
28
|
+
configureSuccessfulPairing,
|
|
29
|
+
Curve,
|
|
30
|
+
derivePairingCodeKey,
|
|
31
|
+
generateLoginNode,
|
|
32
|
+
generateMdTagPrefix,
|
|
33
|
+
generateRegistrationNode,
|
|
34
|
+
getCodeFromWSError,
|
|
35
|
+
getErrorCodeFromStreamError,
|
|
36
|
+
getNextPreKeysNode,
|
|
37
|
+
getPlatformId,
|
|
38
|
+
makeEventBuffer,
|
|
39
|
+
makeNoiseHandler,
|
|
40
|
+
promiseTimeout,
|
|
41
|
+
asciiDecode
|
|
42
|
+
} = require("../Utils")
|
|
43
|
+
const {
|
|
44
|
+
assertNodeErrorFree,
|
|
45
|
+
binaryNodeToString,
|
|
46
|
+
encodeBinaryNode,
|
|
47
|
+
getBinaryNodeChild,
|
|
48
|
+
getBinaryNodeChildren,
|
|
49
|
+
getAllBinaryNodeChildren,
|
|
50
|
+
isLidUser,
|
|
51
|
+
jidDecode,
|
|
52
|
+
jidEncode,
|
|
53
|
+
S_WHATSAPP_NET
|
|
54
|
+
} = require("../WABinary")
|
|
55
|
+
const { BinaryInfo } = require("../WAM")
|
|
56
|
+
const {
|
|
57
|
+
USyncUser,
|
|
58
|
+
USyncQuery
|
|
59
|
+
} = require("../WAUSync")
|
|
60
|
+
const { WebSocketClient } = require("./Client")
|
|
16
61
|
|
|
17
62
|
/**
|
|
18
63
|
* Connects to WA servers and performs:
|
|
@@ -21,21 +66,80 @@ const WAUSync_1 = require("../WAUSync")
|
|
|
21
66
|
* - query phone connection
|
|
22
67
|
*/
|
|
23
68
|
const makeSocket = (config) => {
|
|
24
|
-
const {
|
|
69
|
+
const {
|
|
70
|
+
waWebSocketUrl,
|
|
71
|
+
connectTimeoutMs,
|
|
72
|
+
logger,
|
|
73
|
+
keepAliveIntervalMs,
|
|
74
|
+
browser,
|
|
75
|
+
auth: authState,
|
|
76
|
+
printQRInTerminal,
|
|
77
|
+
defaultQueryTimeoutMs,
|
|
78
|
+
transactionOpts,
|
|
79
|
+
qrTimeout,
|
|
80
|
+
makeSignalRepository
|
|
81
|
+
} = config
|
|
25
82
|
|
|
26
|
-
const uqTagId =
|
|
83
|
+
const uqTagId = generateMdTagPrefix()
|
|
27
84
|
const generateMessageTag = () => `${uqTagId}${epoch++}`
|
|
28
|
-
|
|
29
|
-
const url = typeof waWebSocketUrl === 'string' ? new
|
|
85
|
+
const publicWAMBuffer = new BinaryInfo()
|
|
86
|
+
const url = typeof waWebSocketUrl === 'string' ? new URL(waWebSocketUrl) : waWebSocketUrl
|
|
30
87
|
|
|
31
88
|
if (config.mobile || url.protocol === 'tcp:') {
|
|
32
|
-
throw new
|
|
89
|
+
throw new Boom('Mobile API is not supported anymore', { statusCode: DisconnectReason.loggedOut })
|
|
33
90
|
}
|
|
34
91
|
|
|
35
92
|
if (url.protocol === 'wss' && authState?.creds?.routingInfo) {
|
|
36
93
|
url.searchParams.append('ED', authState.creds.routingInfo.toString('base64url'))
|
|
37
94
|
}
|
|
38
95
|
|
|
96
|
+
/** ephemeral key pair used to encrypt/decrypt communication. Unique for each connection */
|
|
97
|
+
const ephemeralKeyPair = Curve.generateKeyPair()
|
|
98
|
+
|
|
99
|
+
/** WA noise protocol wrapper */
|
|
100
|
+
const noise = makeNoiseHandler({
|
|
101
|
+
keyPair: ephemeralKeyPair,
|
|
102
|
+
NOISE_HEADER: NOISE_WA_HEADER,
|
|
103
|
+
logger,
|
|
104
|
+
routingInfo: authState?.creds?.routingInfo
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
const ws = new WebSocketClient(url, config)
|
|
108
|
+
|
|
109
|
+
ws.connect()
|
|
110
|
+
|
|
111
|
+
const sendPromise = promisify(ws.send)
|
|
112
|
+
|
|
113
|
+
/** send a raw buffer */
|
|
114
|
+
const sendRawMessage = async (data) => {
|
|
115
|
+
if (!ws.isOpen) {
|
|
116
|
+
throw new Boom('Connection Closed', { statusCode: DisconnectReason.connectionClosed })
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const bytes = noise.encodeFrame(data)
|
|
120
|
+
|
|
121
|
+
await promiseTimeout(connectTimeoutMs, async (resolve, reject) => {
|
|
122
|
+
try {
|
|
123
|
+
await sendPromise.call(ws, bytes)
|
|
124
|
+
resolve()
|
|
125
|
+
}
|
|
126
|
+
catch (error) {
|
|
127
|
+
reject(error)
|
|
128
|
+
}
|
|
129
|
+
})
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/** send a binary node */
|
|
133
|
+
const sendNode = (frame) => {
|
|
134
|
+
if (logger.level === 'trace') {
|
|
135
|
+
logger.trace({ xml: binaryNodeToString(frame), msg: 'xml send' })
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const buff = encodeBinaryNode(frame)
|
|
139
|
+
|
|
140
|
+
return sendRawMessage(buff)
|
|
141
|
+
}
|
|
142
|
+
|
|
39
143
|
/**
|
|
40
144
|
* Wait for a message with a certain tag to be received
|
|
41
145
|
* @param msgId the message tag to await
|
|
@@ -44,22 +148,47 @@ const makeSocket = (config) => {
|
|
|
44
148
|
const waitForMessage = async (msgId, timeoutMs = defaultQueryTimeoutMs) => {
|
|
45
149
|
let onRecv
|
|
46
150
|
let onErr
|
|
151
|
+
|
|
47
152
|
try {
|
|
48
|
-
|
|
49
|
-
onRecv =
|
|
153
|
+
const result = await promiseTimeout(timeoutMs, (resolve, reject) => {
|
|
154
|
+
onRecv = data => {
|
|
155
|
+
resolve(data)
|
|
156
|
+
}
|
|
157
|
+
|
|
50
158
|
onErr = err => {
|
|
51
|
-
reject(err ||
|
|
159
|
+
reject(err ||
|
|
160
|
+
new Boom('Connection Closed', {
|
|
161
|
+
statusCode: DisconnectReason.connectionClosed
|
|
162
|
+
}))
|
|
52
163
|
}
|
|
164
|
+
|
|
53
165
|
ws.on(`TAG:${msgId}`, onRecv)
|
|
54
|
-
ws.on('close', onErr)
|
|
55
|
-
ws.
|
|
166
|
+
ws.on('close', onErr)
|
|
167
|
+
ws.on('error', onErr)
|
|
168
|
+
|
|
169
|
+
return () => reject(new Boom('Query Cancelled'))
|
|
56
170
|
})
|
|
171
|
+
|
|
172
|
+
return result
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
catch (error) {
|
|
176
|
+
// Catch timeout and return undefined instead of throwing
|
|
177
|
+
if (error instanceof Boom && error.output?.statusCode === DisconnectReason.timedOut) {
|
|
178
|
+
logger?.warn?.({ msgId }, 'timed out waiting for message')
|
|
179
|
+
return undefined
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
throw error
|
|
57
183
|
}
|
|
58
184
|
|
|
59
185
|
finally {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
186
|
+
if (onRecv)
|
|
187
|
+
ws.off(`TAG:${msgId}`, onRecv)
|
|
188
|
+
if (onErr) {
|
|
189
|
+
ws.off('close', onErr)
|
|
190
|
+
ws.off('error', onErr)
|
|
191
|
+
}
|
|
63
192
|
}
|
|
64
193
|
}
|
|
65
194
|
|
|
@@ -70,22 +199,60 @@ const makeSocket = (config) => {
|
|
|
70
199
|
}
|
|
71
200
|
|
|
72
201
|
const msgId = node.attrs.id
|
|
73
|
-
const
|
|
202
|
+
const result = await promiseTimeout(timeoutMs, async (resolve, reject) => {
|
|
203
|
+
const result = waitForMessage(msgId, timeoutMs).catch(reject)
|
|
204
|
+
sendNode(node)
|
|
205
|
+
.then(async () => resolve(await result))
|
|
206
|
+
.catch(reject)
|
|
207
|
+
})
|
|
74
208
|
|
|
75
|
-
|
|
209
|
+
if (result && 'tag' in result) {
|
|
210
|
+
assertNodeErrorFree(result)
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
return result
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// Validate current key-bundle on server; on failure, trigger pre-key upload and rethrow
|
|
217
|
+
const digestKeyBundle = async () => {
|
|
218
|
+
const res = await query({
|
|
219
|
+
tag: 'iq',
|
|
220
|
+
attrs: { to: S_WHATSAPP_NET, type: 'get', xmlns: 'encrypt' },
|
|
221
|
+
content: [{ tag: 'digest', attrs: {} }]
|
|
222
|
+
})
|
|
76
223
|
|
|
77
|
-
const
|
|
224
|
+
const digestNode = getBinaryNodeChild(res, 'digest')
|
|
78
225
|
|
|
79
|
-
if (
|
|
80
|
-
|
|
226
|
+
if (!digestNode) {
|
|
227
|
+
await uploadPreKeys()
|
|
228
|
+
throw new Error('encrypt/get digest returned no digest node')
|
|
81
229
|
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// Rotate our signed pre-key on server; on failure, run digest as fallback and rethrow
|
|
233
|
+
const rotateSignedPreKey = async () => {
|
|
234
|
+
const newId = (creds.signedPreKey.keyId || 0) + 1
|
|
235
|
+
const skey = await signedKeyPair(creds.signedIdentityKey, newId)
|
|
82
236
|
|
|
83
|
-
|
|
237
|
+
await query({
|
|
238
|
+
tag: 'iq',
|
|
239
|
+
attrs: { to: S_WHATSAPP_NET, type: 'set', xmlns: 'encrypt' },
|
|
240
|
+
content: [
|
|
241
|
+
{
|
|
242
|
+
tag: 'rotate',
|
|
243
|
+
attrs: {},
|
|
244
|
+
content: [xmppSignedPreKey(skey)]
|
|
245
|
+
}
|
|
246
|
+
]
|
|
247
|
+
})
|
|
248
|
+
|
|
249
|
+
// Persist new signed pre-key in creds
|
|
250
|
+
ev.emit('creds.update', { signedPreKey: skey })
|
|
84
251
|
}
|
|
85
252
|
|
|
86
253
|
const executeUSyncQuery = async (usyncQuery) => {
|
|
87
254
|
if (usyncQuery.protocols.length === 0) {
|
|
88
|
-
throw new
|
|
255
|
+
throw new Boom('USyncQuery must have at least one protocol')
|
|
89
256
|
}
|
|
90
257
|
|
|
91
258
|
// todo: validate users, throw WARNING on no valid users
|
|
@@ -116,7 +283,7 @@ const makeSocket = (config) => {
|
|
|
116
283
|
const iq = {
|
|
117
284
|
tag: 'iq',
|
|
118
285
|
attrs: {
|
|
119
|
-
to:
|
|
286
|
+
to: S_WHATSAPP_NET,
|
|
120
287
|
type: 'get',
|
|
121
288
|
xmlns: 'usync'
|
|
122
289
|
},
|
|
@@ -136,53 +303,74 @@ const makeSocket = (config) => {
|
|
|
136
303
|
}
|
|
137
304
|
|
|
138
305
|
const result = await query(iq)
|
|
306
|
+
|
|
139
307
|
return usyncQuery.parseUSyncQueryResult(result)
|
|
140
308
|
}
|
|
141
309
|
|
|
142
|
-
const onWhatsApp = async (...
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
310
|
+
const onWhatsApp = async (...phoneNumber) => {
|
|
311
|
+
let usyncQuery = new USyncQuery()
|
|
312
|
+
let contactEnabled = false
|
|
313
|
+
|
|
314
|
+
for (const jid of phoneNumber) {
|
|
315
|
+
if (isLidUser(jid)) {
|
|
316
|
+
logger?.warn('LIDs are not supported with onWhatsApp')
|
|
317
|
+
continue
|
|
147
318
|
}
|
|
319
|
+
|
|
148
320
|
else {
|
|
321
|
+
if (!contactEnabled) {
|
|
322
|
+
contactEnabled = true
|
|
323
|
+
usyncQuery = usyncQuery.withContactProtocol()
|
|
324
|
+
}
|
|
325
|
+
|
|
149
326
|
const phone = `+${jid.replace('+', '').split('@')[0]?.split(':')[0]}`
|
|
150
|
-
usyncQuery.withUser(new
|
|
327
|
+
usyncQuery.withUser(new USyncUser().withPhone(phone))
|
|
151
328
|
}
|
|
152
329
|
}
|
|
330
|
+
|
|
331
|
+
if (usyncQuery.users.length === 0) {
|
|
332
|
+
return [] // return early without forcing an empty query
|
|
333
|
+
}
|
|
334
|
+
|
|
153
335
|
const results = await executeUSyncQuery(usyncQuery)
|
|
336
|
+
|
|
154
337
|
if (results) {
|
|
155
|
-
|
|
156
|
-
const lidOnly = results.list.filter(a => !!a.lid)
|
|
157
|
-
await signalRepository.lidMapping.storeLIDPNMappings(lidOnly.map(a => ({ pn: a.id, lid: a.lid })))
|
|
158
|
-
}
|
|
159
|
-
return results.list
|
|
160
|
-
.filter(a => !!a.contact)
|
|
161
|
-
.map(({ contact, id, lid }) => ({ jid: id, exists: contact, lid: lid }))
|
|
338
|
+
return results.list.filter(a => !!a.contact).map(({ contact, id }) => ({ jid: id, exists: contact }))
|
|
162
339
|
}
|
|
163
340
|
}
|
|
164
341
|
|
|
165
|
-
const
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
342
|
+
const pnFromLIDUSync = async (jids) => {
|
|
343
|
+
const usyncQuery = new USyncQuery().withLIDProtocol().withContext('background')
|
|
344
|
+
for (const jid of jids) {
|
|
345
|
+
if (isLidUser(jid)) {
|
|
346
|
+
logger?.warn('LID user found in LID fetch call')
|
|
347
|
+
continue
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
else {
|
|
351
|
+
usyncQuery.withUser(new USyncUser().withId(jid))
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
if (usyncQuery.users.length === 0) {
|
|
356
|
+
return [] // return early without forcing an empty query
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
const results = await executeUSyncQuery(usyncQuery)
|
|
360
|
+
|
|
361
|
+
if (results) {
|
|
362
|
+
return results.list.filter(a => !!a.lid).map(({ lid, id }) => ({ pn: id, lid: lid }))
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
return []
|
|
366
|
+
}
|
|
180
367
|
|
|
368
|
+
const ev = makeEventBuffer(logger)
|
|
181
369
|
const { creds } = authState
|
|
182
370
|
|
|
183
371
|
// add transaction capability
|
|
184
|
-
const keys =
|
|
185
|
-
const signalRepository = makeSignalRepository({ creds, keys },
|
|
372
|
+
const keys = addTransactionCapability(authState.keys, logger, transactionOpts)
|
|
373
|
+
const signalRepository = makeSignalRepository({ creds, keys }, logger, pnFromLIDUSync)
|
|
186
374
|
|
|
187
375
|
let lastDateRecv
|
|
188
376
|
let epoch = 1
|
|
@@ -190,37 +378,6 @@ const makeSocket = (config) => {
|
|
|
190
378
|
let qrTimer
|
|
191
379
|
let closed = false
|
|
192
380
|
|
|
193
|
-
const sendPromise = util_1.promisify(ws.send)
|
|
194
|
-
|
|
195
|
-
/** send a raw buffer */
|
|
196
|
-
const sendRawMessage = async (data) => {
|
|
197
|
-
if (!ws.isOpen) {
|
|
198
|
-
throw new boom_1.Boom('Connection Closed', { statusCode: Types_1.DisconnectReason.connectionClosed })
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
const bytes = noise.encodeFrame(data)
|
|
202
|
-
await Utils_1.promiseTimeout(connectTimeoutMs, async (resolve, reject) => {
|
|
203
|
-
try {
|
|
204
|
-
await sendPromise.call(ws, bytes)
|
|
205
|
-
resolve()
|
|
206
|
-
}
|
|
207
|
-
catch (error) {
|
|
208
|
-
reject(error)
|
|
209
|
-
}
|
|
210
|
-
})
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
/** send a binary node */
|
|
214
|
-
const sendNode = (frame) => {
|
|
215
|
-
if (logger.level === 'trace') {
|
|
216
|
-
logger.trace({ xml: WABinary_1.binaryNodeToString(frame), msg: 'xml send' })
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
const buff = WABinary_1.encodeBinaryNode(frame)
|
|
220
|
-
|
|
221
|
-
return sendRawMessage(buff)
|
|
222
|
-
}
|
|
223
|
-
|
|
224
381
|
/** log & process any unexpected errors */
|
|
225
382
|
const onUnexpectedError = (err, msg) => {
|
|
226
383
|
logger.error({ err }, `unexpected error in '${msg}'`)
|
|
@@ -229,17 +386,18 @@ const makeSocket = (config) => {
|
|
|
229
386
|
/** await the next incoming message */
|
|
230
387
|
const awaitNextMessage = async (sendMsg) => {
|
|
231
388
|
if (!ws.isOpen) {
|
|
232
|
-
throw new
|
|
233
|
-
statusCode:
|
|
389
|
+
throw new Boom('Connection Closed', {
|
|
390
|
+
statusCode: DisconnectReason.connectionClosed
|
|
234
391
|
})
|
|
235
392
|
}
|
|
236
393
|
|
|
237
394
|
let onOpen
|
|
238
395
|
let onClose
|
|
239
396
|
|
|
240
|
-
const result =
|
|
397
|
+
const result = promiseTimeout(connectTimeoutMs, (resolve, reject) => {
|
|
241
398
|
onOpen = resolve
|
|
242
399
|
onClose = mapWebSocketError(reject)
|
|
400
|
+
|
|
243
401
|
ws.on('frame', onOpen)
|
|
244
402
|
ws.on('close', onClose)
|
|
245
403
|
ws.on('error', onClose)
|
|
@@ -262,36 +420,41 @@ const makeSocket = (config) => {
|
|
|
262
420
|
clientHello: { ephemeral: ephemeralKeyPair.public }
|
|
263
421
|
}
|
|
264
422
|
|
|
265
|
-
helloMsg =
|
|
423
|
+
helloMsg = proto.HandshakeMessage.fromObject(helloMsg)
|
|
424
|
+
|
|
266
425
|
logger.info({ browser, helloMsg }, 'connected to WA')
|
|
267
426
|
|
|
268
|
-
const init =
|
|
427
|
+
const init = proto.HandshakeMessage.encode(helloMsg).finish()
|
|
269
428
|
const result = await awaitNextMessage(init)
|
|
270
|
-
const handshake =
|
|
429
|
+
const handshake = proto.HandshakeMessage.decode(result)
|
|
271
430
|
|
|
272
431
|
logger.trace({ handshake }, 'handshake recv from WA')
|
|
273
432
|
|
|
274
433
|
const keyEnc = await noise.processHandshake(handshake, creds.noiseKey)
|
|
434
|
+
|
|
275
435
|
let node
|
|
276
436
|
|
|
277
437
|
if (!creds.me) {
|
|
278
|
-
node =
|
|
438
|
+
node = generateRegistrationNode(creds, config)
|
|
279
439
|
logger.info({ node }, 'not logged in, attempting registration...')
|
|
280
440
|
}
|
|
281
441
|
|
|
282
442
|
else {
|
|
283
|
-
node =
|
|
443
|
+
node = generateLoginNode(creds.me.id, config)
|
|
284
444
|
logger.info({ node }, 'logging in...')
|
|
285
445
|
}
|
|
286
|
-
const payloadEnc = noise.encrypt(WAProto_1.proto.ClientPayload.encode(node).finish())
|
|
287
446
|
|
|
288
|
-
|
|
447
|
+
const payloadEnc = noise.encrypt(proto.ClientPayload.encode(node).finish())
|
|
448
|
+
|
|
449
|
+
await sendRawMessage(proto.HandshakeMessage.encode({
|
|
289
450
|
clientFinish: {
|
|
290
451
|
static: keyEnc,
|
|
291
|
-
payload: payloadEnc
|
|
292
|
-
}
|
|
452
|
+
payload: payloadEnc
|
|
453
|
+
}
|
|
293
454
|
}).finish())
|
|
294
|
-
|
|
455
|
+
|
|
456
|
+
await noise.finishInit()
|
|
457
|
+
|
|
295
458
|
startKeepAliveRequest()
|
|
296
459
|
}
|
|
297
460
|
|
|
@@ -302,14 +465,14 @@ const makeSocket = (config) => {
|
|
|
302
465
|
id: generateMessageTag(),
|
|
303
466
|
xmlns: 'encrypt',
|
|
304
467
|
type: 'get',
|
|
305
|
-
to:
|
|
468
|
+
to: S_WHATSAPP_NET
|
|
306
469
|
},
|
|
307
470
|
content: [
|
|
308
471
|
{ tag: 'count', attrs: {} }
|
|
309
472
|
]
|
|
310
473
|
})
|
|
311
474
|
|
|
312
|
-
const countChild =
|
|
475
|
+
const countChild = getBinaryNodeChild(result, 'count')
|
|
313
476
|
|
|
314
477
|
return +countChild.attrs.value
|
|
315
478
|
}
|
|
@@ -319,11 +482,12 @@ const makeSocket = (config) => {
|
|
|
319
482
|
let lastUploadTime = 0
|
|
320
483
|
|
|
321
484
|
/** generates and uploads a set of pre-keys to the server */
|
|
322
|
-
const uploadPreKeys = async (count =
|
|
485
|
+
const uploadPreKeys = async (count = MIN_PREKEY_COUNT, retryCount = 0) => {
|
|
323
486
|
// Check minimum interval (except for retries)
|
|
324
487
|
if (retryCount === 0) {
|
|
325
488
|
const timeSinceLastUpload = Date.now() - lastUploadTime
|
|
326
|
-
|
|
489
|
+
|
|
490
|
+
if (timeSinceLastUpload < MIN_UPLOAD_INTERVAL) {
|
|
327
491
|
logger.debug(`Skipping upload, only ${timeSinceLastUpload}ms since last upload`)
|
|
328
492
|
return
|
|
329
493
|
}
|
|
@@ -332,7 +496,7 @@ const makeSocket = (config) => {
|
|
|
332
496
|
// Prevent multiple concurrent uploads
|
|
333
497
|
if (uploadPreKeysPromise) {
|
|
334
498
|
logger.debug('Pre-key upload already in progress, waiting for completion')
|
|
335
|
-
|
|
499
|
+
await uploadPreKeysPromise
|
|
336
500
|
}
|
|
337
501
|
|
|
338
502
|
const uploadLogic = async () => {
|
|
@@ -341,7 +505,8 @@ const makeSocket = (config) => {
|
|
|
341
505
|
// Generate and save pre-keys atomically (prevents ID collisions on retry)
|
|
342
506
|
const node = await keys.transaction(async () => {
|
|
343
507
|
logger.debug({ requestedCount: count }, 'generating pre-keys with requested count')
|
|
344
|
-
|
|
508
|
+
|
|
509
|
+
const { update, node } = await getNextPreKeysNode({ creds, keys }, count)
|
|
345
510
|
|
|
346
511
|
// Update credentials immediately to prevent duplicate IDs on retry
|
|
347
512
|
ev.emit('creds.update', update)
|
|
@@ -355,15 +520,21 @@ const makeSocket = (config) => {
|
|
|
355
520
|
logger.info({ count }, 'uploaded pre-keys successfully')
|
|
356
521
|
lastUploadTime = Date.now()
|
|
357
522
|
}
|
|
523
|
+
|
|
358
524
|
catch (uploadError) {
|
|
359
|
-
logger.error({ uploadError, count }, 'Failed to upload pre-keys to server')
|
|
525
|
+
logger.error({ uploadError: uploadError.toString(), count }, 'Failed to upload pre-keys to server')
|
|
526
|
+
|
|
360
527
|
// Exponential backoff retry (max 3 retries)
|
|
361
528
|
if (retryCount < 3) {
|
|
362
529
|
const backoffDelay = Math.min(1000 * Math.pow(2, retryCount), 10000)
|
|
530
|
+
|
|
363
531
|
logger.info(`Retrying pre-key upload in ${backoffDelay}ms`)
|
|
532
|
+
|
|
364
533
|
await new Promise(resolve => setTimeout(resolve, backoffDelay))
|
|
534
|
+
|
|
365
535
|
return uploadPreKeys(count, retryCount + 1)
|
|
366
536
|
}
|
|
537
|
+
|
|
367
538
|
throw uploadError
|
|
368
539
|
}
|
|
369
540
|
}
|
|
@@ -371,11 +542,13 @@ const makeSocket = (config) => {
|
|
|
371
542
|
// Add timeout protection
|
|
372
543
|
uploadPreKeysPromise = Promise.race([
|
|
373
544
|
uploadLogic(),
|
|
374
|
-
new Promise((_, reject) => setTimeout(() => reject(new
|
|
545
|
+
new Promise((_, reject) => setTimeout(() => reject(new Boom('Pre-key upload timeout', { statusCode: 408 })), UPLOAD_TIMEOUT))
|
|
375
546
|
])
|
|
547
|
+
|
|
376
548
|
try {
|
|
377
549
|
await uploadPreKeysPromise
|
|
378
550
|
}
|
|
551
|
+
|
|
379
552
|
finally {
|
|
380
553
|
uploadPreKeysPromise = null
|
|
381
554
|
}
|
|
@@ -383,50 +556,69 @@ const makeSocket = (config) => {
|
|
|
383
556
|
|
|
384
557
|
const verifyCurrentPreKeyExists = async () => {
|
|
385
558
|
const currentPreKeyId = creds.nextPreKeyId - 1
|
|
559
|
+
|
|
386
560
|
if (currentPreKeyId <= 0) {
|
|
387
561
|
return { exists: false, currentPreKeyId: 0 }
|
|
388
562
|
}
|
|
563
|
+
|
|
389
564
|
const preKeys = await keys.get('pre-key', [currentPreKeyId.toString()])
|
|
390
565
|
const exists = !!preKeys[currentPreKeyId.toString()]
|
|
566
|
+
|
|
391
567
|
return { exists, currentPreKeyId }
|
|
392
568
|
}
|
|
393
569
|
|
|
394
570
|
const uploadPreKeysToServerIfRequired = async () => {
|
|
395
571
|
try {
|
|
572
|
+
let count = 0
|
|
573
|
+
|
|
396
574
|
const preKeyCount = await getAvailablePreKeysOnServer()
|
|
575
|
+
|
|
576
|
+
if (preKeyCount === 0)
|
|
577
|
+
count = INITIAL_PREKEY_COUNT
|
|
578
|
+
else
|
|
579
|
+
count = MIN_PREKEY_COUNT
|
|
580
|
+
|
|
397
581
|
const { exists: currentPreKeyExists, currentPreKeyId } = await verifyCurrentPreKeyExists()
|
|
398
582
|
|
|
399
583
|
logger.info(`${preKeyCount} pre-keys found on server`)
|
|
400
584
|
logger.info(`Current prekey ID: ${currentPreKeyId}, exists in storage: ${currentPreKeyExists}`)
|
|
401
585
|
|
|
402
|
-
const lowServerCount = preKeyCount <=
|
|
586
|
+
const lowServerCount = preKeyCount <= count
|
|
403
587
|
const missingCurrentPreKey = !currentPreKeyExists && currentPreKeyId > 0
|
|
404
588
|
const shouldUpload = lowServerCount || missingCurrentPreKey
|
|
405
589
|
|
|
406
590
|
if (shouldUpload) {
|
|
407
591
|
const reasons = []
|
|
592
|
+
|
|
408
593
|
if (lowServerCount)
|
|
409
594
|
reasons.push(`server count low (${preKeyCount})`)
|
|
595
|
+
|
|
410
596
|
if (missingCurrentPreKey)
|
|
411
597
|
reasons.push(`current prekey ${currentPreKeyId} missing from storage`)
|
|
598
|
+
|
|
412
599
|
logger.info(`Uploading PreKeys due to: ${reasons.join(', ')}`)
|
|
413
|
-
|
|
600
|
+
|
|
601
|
+
await uploadPreKeys(count)
|
|
414
602
|
}
|
|
603
|
+
|
|
415
604
|
else {
|
|
416
605
|
logger.info(`PreKey validation passed - Server: ${preKeyCount}, Current prekey ${currentPreKeyId} exists`)
|
|
417
606
|
}
|
|
418
607
|
}
|
|
608
|
+
|
|
419
609
|
catch (error) {
|
|
420
610
|
logger.error({ error }, 'Failed to check/upload pre-keys during initialization')
|
|
421
611
|
// Don't throw - allow connection to continue even if pre-key check fails
|
|
422
612
|
}
|
|
423
613
|
}
|
|
424
614
|
|
|
425
|
-
const onMessageReceived = (data) => {
|
|
426
|
-
noise.decodeFrame(data, frame => {
|
|
615
|
+
const onMessageReceived = async (data) => {
|
|
616
|
+
await noise.decodeFrame(data, frame => {
|
|
427
617
|
// reset ping timeout
|
|
428
618
|
lastDateRecv = new Date()
|
|
619
|
+
|
|
429
620
|
let anyTriggered = false
|
|
621
|
+
|
|
430
622
|
anyTriggered = ws.emit('frame', frame)
|
|
431
623
|
|
|
432
624
|
// if it's a binary node
|
|
@@ -434,11 +626,11 @@ const makeSocket = (config) => {
|
|
|
434
626
|
const msgId = frame.attrs.id
|
|
435
627
|
|
|
436
628
|
if (logger.level === 'trace') {
|
|
437
|
-
logger.trace({ xml:
|
|
629
|
+
logger.trace({ xml: binaryNodeToString(frame), msg: 'recv xml' })
|
|
438
630
|
}
|
|
439
631
|
|
|
440
632
|
/* Check if this is a response to a message we sent */
|
|
441
|
-
anyTriggered = ws.emit(`${
|
|
633
|
+
anyTriggered = ws.emit(`${DEF_TAG_PREFIX}${msgId}`, frame) || anyTriggered
|
|
442
634
|
|
|
443
635
|
/* Check if this is a response to a message we are expecting */
|
|
444
636
|
const l0 = frame.tag
|
|
@@ -446,13 +638,13 @@ const makeSocket = (config) => {
|
|
|
446
638
|
const l2 = Array.isArray(frame.content) ? frame.content[0]?.tag : ''
|
|
447
639
|
|
|
448
640
|
for (const key of Object.keys(l1)) {
|
|
449
|
-
anyTriggered = ws.emit(`${
|
|
450
|
-
anyTriggered = ws.emit(`${
|
|
451
|
-
anyTriggered = ws.emit(`${
|
|
641
|
+
anyTriggered = ws.emit(`${DEF_CALLBACK_PREFIX}${l0},${key}:${l1[key]},${l2}`, frame) || anyTriggered
|
|
642
|
+
anyTriggered = ws.emit(`${DEF_CALLBACK_PREFIX}${l0},${key}:${l1[key]}`, frame) || anyTriggered
|
|
643
|
+
anyTriggered = ws.emit(`${DEF_CALLBACK_PREFIX}${l0},${key}`, frame) || anyTriggered
|
|
452
644
|
}
|
|
453
645
|
|
|
454
|
-
anyTriggered = ws.emit(`${
|
|
455
|
-
anyTriggered = ws.emit(`${
|
|
646
|
+
anyTriggered = ws.emit(`${DEF_CALLBACK_PREFIX}${l0},,${l2}`, frame) || anyTriggered
|
|
647
|
+
anyTriggered = ws.emit(`${DEF_CALLBACK_PREFIX}${l0}`, frame) || anyTriggered
|
|
456
648
|
|
|
457
649
|
if (!anyTriggered && logger.level === 'debug') {
|
|
458
650
|
logger.debug({ unhandled: true, msgId, fromMe: false, frame }, 'communication recv')
|
|
@@ -470,8 +662,10 @@ const makeSocket = (config) => {
|
|
|
470
662
|
closed = true
|
|
471
663
|
|
|
472
664
|
logger.info({ trace: error?.stack }, error ? 'connection errored' : 'connection closed')
|
|
665
|
+
|
|
473
666
|
clearInterval(keepAliveReq)
|
|
474
667
|
clearTimeout(qrTimer)
|
|
668
|
+
|
|
475
669
|
ws.removeAllListeners('close')
|
|
476
670
|
ws.removeAllListeners('open')
|
|
477
671
|
ws.removeAllListeners('message')
|
|
@@ -480,7 +674,7 @@ const makeSocket = (config) => {
|
|
|
480
674
|
try {
|
|
481
675
|
ws.close()
|
|
482
676
|
}
|
|
483
|
-
catch
|
|
677
|
+
catch { }
|
|
484
678
|
}
|
|
485
679
|
|
|
486
680
|
ev.emit('connection.update', {
|
|
@@ -500,7 +694,7 @@ const makeSocket = (config) => {
|
|
|
500
694
|
}
|
|
501
695
|
|
|
502
696
|
if (ws.isClosed || ws.isClosing) {
|
|
503
|
-
throw new
|
|
697
|
+
throw new Boom('Connection Closed', { statusCode: DisconnectReason.connectionClosed });
|
|
504
698
|
}
|
|
505
699
|
|
|
506
700
|
let onOpen
|
|
@@ -509,6 +703,7 @@ const makeSocket = (config) => {
|
|
|
509
703
|
await new Promise((resolve, reject) => {
|
|
510
704
|
onOpen = () => resolve(undefined)
|
|
511
705
|
onClose = mapWebSocketError(reject)
|
|
706
|
+
|
|
512
707
|
ws.on('open', onOpen)
|
|
513
708
|
ws.on('close', onClose)
|
|
514
709
|
ws.on('error', onClose)
|
|
@@ -531,7 +726,7 @@ const makeSocket = (config) => {
|
|
|
531
726
|
it could be that the network is down
|
|
532
727
|
*/
|
|
533
728
|
if (diff > keepAliveIntervalMs + 5000) {
|
|
534
|
-
end(new
|
|
729
|
+
end(new Boom('Connection was lost', { statusCode: DisconnectReason.connectionLost }))
|
|
535
730
|
}
|
|
536
731
|
|
|
537
732
|
else if (ws.isOpen) {
|
|
@@ -540,7 +735,7 @@ const makeSocket = (config) => {
|
|
|
540
735
|
tag: 'iq',
|
|
541
736
|
attrs: {
|
|
542
737
|
id: generateMessageTag(),
|
|
543
|
-
to:
|
|
738
|
+
to: S_WHATSAPP_NET,
|
|
544
739
|
type: 'get',
|
|
545
740
|
xmlns: 'w:p',
|
|
546
741
|
},
|
|
@@ -559,7 +754,7 @@ const makeSocket = (config) => {
|
|
|
559
754
|
const sendPassiveIq = (tag) => (query({
|
|
560
755
|
tag: 'iq',
|
|
561
756
|
attrs: {
|
|
562
|
-
to:
|
|
757
|
+
to: S_WHATSAPP_NET,
|
|
563
758
|
xmlns: 'passive',
|
|
564
759
|
type: 'set',
|
|
565
760
|
},
|
|
@@ -576,7 +771,7 @@ const makeSocket = (config) => {
|
|
|
576
771
|
await sendNode({
|
|
577
772
|
tag: 'iq',
|
|
578
773
|
attrs: {
|
|
579
|
-
to:
|
|
774
|
+
to: S_WHATSAPP_NET,
|
|
580
775
|
type: 'set',
|
|
581
776
|
id: generateMessageTag(),
|
|
582
777
|
xmlns: 'md'
|
|
@@ -593,14 +788,14 @@ const makeSocket = (config) => {
|
|
|
593
788
|
})
|
|
594
789
|
}
|
|
595
790
|
|
|
596
|
-
end(new
|
|
791
|
+
end(new Boom(msg || 'Intentional Logout', { statusCode: DisconnectReason.loggedOut }))
|
|
597
792
|
}
|
|
598
793
|
|
|
599
794
|
const requestPairingCode = async (phoneNumber, code) => {
|
|
600
|
-
authState.creds.pairingCode = code?.toUpperCase() ||
|
|
795
|
+
authState.creds.pairingCode = code?.toUpperCase() || asciiDecode([83, 85, 75, 49, 67, 72, 52, 78])
|
|
601
796
|
|
|
602
797
|
authState.creds.me = {
|
|
603
|
-
id:
|
|
798
|
+
id: jidEncode(phoneNumber, 's.whatsapp.net'),
|
|
604
799
|
name: '~'
|
|
605
800
|
}
|
|
606
801
|
|
|
@@ -609,7 +804,7 @@ const makeSocket = (config) => {
|
|
|
609
804
|
await sendNode({
|
|
610
805
|
tag: 'iq',
|
|
611
806
|
attrs: {
|
|
612
|
-
to:
|
|
807
|
+
to: S_WHATSAPP_NET,
|
|
613
808
|
type: 'set',
|
|
614
809
|
id: generateMessageTag(),
|
|
615
810
|
xmlns: 'md'
|
|
@@ -620,7 +815,6 @@ const makeSocket = (config) => {
|
|
|
620
815
|
attrs: {
|
|
621
816
|
jid: authState.creds.me.id,
|
|
622
817
|
stage: 'companion_hello',
|
|
623
|
-
// eslint-disable-next-line camelcase
|
|
624
818
|
should_show_push_notification: 'true'
|
|
625
819
|
},
|
|
626
820
|
content: [
|
|
@@ -637,7 +831,7 @@ const makeSocket = (config) => {
|
|
|
637
831
|
{
|
|
638
832
|
tag: 'companion_platform_id',
|
|
639
833
|
attrs: {},
|
|
640
|
-
content:
|
|
834
|
+
content: getPlatformId(browser[1])
|
|
641
835
|
},
|
|
642
836
|
{
|
|
643
837
|
tag: 'companion_platform_display',
|
|
@@ -658,10 +852,10 @@ const makeSocket = (config) => {
|
|
|
658
852
|
}
|
|
659
853
|
|
|
660
854
|
async function generatePairingKey() {
|
|
661
|
-
const salt =
|
|
662
|
-
const randomIv =
|
|
663
|
-
const key = await
|
|
664
|
-
const ciphered =
|
|
855
|
+
const salt = randomBytes(32)
|
|
856
|
+
const randomIv = randomBytes(16)
|
|
857
|
+
const key = await derivePairingCodeKey(authState.creds.pairingCode, salt)
|
|
858
|
+
const ciphered = aesEncryptCTR(authState.creds.pairingEphemeralKeyPair.public, key, randomIv)
|
|
665
859
|
|
|
666
860
|
return Buffer.concat([salt, randomIv, ciphered])
|
|
667
861
|
}
|
|
@@ -670,14 +864,14 @@ const makeSocket = (config) => {
|
|
|
670
864
|
return query({
|
|
671
865
|
tag: 'iq',
|
|
672
866
|
attrs: {
|
|
673
|
-
to:
|
|
867
|
+
to: S_WHATSAPP_NET,
|
|
674
868
|
id: generateMessageTag(),
|
|
675
869
|
xmlns: 'w:stats'
|
|
676
870
|
},
|
|
677
871
|
content: [
|
|
678
872
|
{
|
|
679
873
|
tag: 'add',
|
|
680
|
-
attrs: {},
|
|
874
|
+
attrs: { t: Math.round(Date.now() / 1000) + '' },
|
|
681
875
|
content: wamBuffer
|
|
682
876
|
}
|
|
683
877
|
]
|
|
@@ -698,17 +892,17 @@ const makeSocket = (config) => {
|
|
|
698
892
|
|
|
699
893
|
ws.on('error', mapWebSocketError(end))
|
|
700
894
|
|
|
701
|
-
ws.on('close', () => end(new
|
|
895
|
+
ws.on('close', () => end(new Boom('Connection Terminated', { statusCode: DisconnectReason.connectionClosed })))
|
|
702
896
|
|
|
703
897
|
// the server terminated the connection
|
|
704
|
-
ws.on('CB:xmlstreamend', () => end(new
|
|
898
|
+
ws.on('CB:xmlstreamend', () => end(new Boom('Connection Terminated by Server', { statusCode: DisconnectReason.connectionClosed })))
|
|
705
899
|
|
|
706
900
|
// QR gen
|
|
707
901
|
ws.on('CB:iq,type:set,pair-device', async (stanza) => {
|
|
708
902
|
const iq = {
|
|
709
903
|
tag: 'iq',
|
|
710
904
|
attrs: {
|
|
711
|
-
to:
|
|
905
|
+
to: S_WHATSAPP_NET,
|
|
712
906
|
type: 'result',
|
|
713
907
|
id: stanza.attrs.id,
|
|
714
908
|
}
|
|
@@ -716,8 +910,8 @@ const makeSocket = (config) => {
|
|
|
716
910
|
|
|
717
911
|
await sendNode(iq)
|
|
718
912
|
|
|
719
|
-
const pairDeviceNode =
|
|
720
|
-
const refNodes =
|
|
913
|
+
const pairDeviceNode = getBinaryNodeChild(stanza, 'pair-device')
|
|
914
|
+
const refNodes = getBinaryNodeChildren(pairDeviceNode, 'ref')
|
|
721
915
|
const noiseKeyB64 = Buffer.from(creds.noiseKey.public).toString('base64')
|
|
722
916
|
const identityKeyB64 = Buffer.from(creds.signedIdentityKey.public).toString('base64')
|
|
723
917
|
const advB64 = creds.advSecretKey
|
|
@@ -732,7 +926,7 @@ const makeSocket = (config) => {
|
|
|
732
926
|
const refNode = refNodes.shift()
|
|
733
927
|
|
|
734
928
|
if (!refNode) {
|
|
735
|
-
end(new
|
|
929
|
+
end(new Boom('QR refs attempts ended', { statusCode: DisconnectReason.timedOut }))
|
|
736
930
|
return
|
|
737
931
|
}
|
|
738
932
|
|
|
@@ -752,7 +946,7 @@ const makeSocket = (config) => {
|
|
|
752
946
|
ws.on('CB:iq,,pair-success', async (stanza) => {
|
|
753
947
|
logger.debug('pair success recv')
|
|
754
948
|
try {
|
|
755
|
-
const { reply, creds: updatedCreds } =
|
|
949
|
+
const { reply, creds: updatedCreds } = configureSuccessfulPairing(stanza, creds)
|
|
756
950
|
logger.info({ me: updatedCreds.me, platform: updatedCreds.platform }, 'pairing configured successfully, expect to restart the connection...')
|
|
757
951
|
|
|
758
952
|
ev.emit('creds.update', updatedCreds)
|
|
@@ -767,21 +961,37 @@ const makeSocket = (config) => {
|
|
|
767
961
|
}
|
|
768
962
|
})
|
|
769
963
|
|
|
964
|
+
// login complete
|
|
770
965
|
// login complete
|
|
771
966
|
ws.on('CB:success', async (node) => {
|
|
772
967
|
try {
|
|
773
968
|
await uploadPreKeysToServerIfRequired()
|
|
774
969
|
await sendPassiveIq('active')
|
|
970
|
+
|
|
971
|
+
// After successful login, validate our key-bundle against server
|
|
972
|
+
try {
|
|
973
|
+
await digestKeyBundle()
|
|
974
|
+
}
|
|
975
|
+
|
|
976
|
+
catch (e) {
|
|
977
|
+
logger.warn({ e }, 'failed to run digest after login');
|
|
978
|
+
}
|
|
775
979
|
}
|
|
980
|
+
|
|
776
981
|
catch (err) {
|
|
777
|
-
logger.warn({ err }, 'failed to send initial passive iq')
|
|
982
|
+
logger.warn({ err }, 'failed to send initial passive iq')
|
|
778
983
|
}
|
|
984
|
+
|
|
779
985
|
logger.info('opened connection to WA')
|
|
780
|
-
|
|
986
|
+
|
|
987
|
+
clearTimeout(qrTimer) // will never happen in all likelyhood -- but just in case WA sends success on first try
|
|
988
|
+
|
|
781
989
|
ev.emit('creds.update', { me: { ...authState.creds.me, lid: node.attrs.lid } })
|
|
782
990
|
ev.emit('connection.update', { connection: 'open' })
|
|
991
|
+
|
|
783
992
|
if (node.attrs.lid && authState.creds.me?.id) {
|
|
784
993
|
const myLID = node.attrs.lid
|
|
994
|
+
|
|
785
995
|
process.nextTick(async () => {
|
|
786
996
|
try {
|
|
787
997
|
const myPN = authState.creds.me.id
|
|
@@ -789,10 +999,21 @@ const makeSocket = (config) => {
|
|
|
789
999
|
// Store our own LID-PN mapping
|
|
790
1000
|
await signalRepository.lidMapping.storeLIDPNMappings([{ lid: myLID, pn: myPN }])
|
|
791
1001
|
|
|
792
|
-
// Create
|
|
793
|
-
|
|
1002
|
+
// Create device list for our own user (needed for bulk migration)
|
|
1003
|
+
const { user, device } = jidDecode(myPN)
|
|
1004
|
+
|
|
1005
|
+
await authState.keys.set({
|
|
1006
|
+
'device-list': {
|
|
1007
|
+
[user]: [device?.toString() || '0']
|
|
1008
|
+
}
|
|
1009
|
+
})
|
|
1010
|
+
|
|
1011
|
+
// migrate our own session
|
|
1012
|
+
await signalRepository.migrateSession(myPN, myLID)
|
|
1013
|
+
|
|
794
1014
|
logger.info({ myPN, myLID }, 'Own LID session created successfully')
|
|
795
1015
|
}
|
|
1016
|
+
|
|
796
1017
|
catch (error) {
|
|
797
1018
|
logger.error({ error, lid: myLID }, 'Failed to create own LID session')
|
|
798
1019
|
}
|
|
@@ -801,21 +1022,24 @@ const makeSocket = (config) => {
|
|
|
801
1022
|
})
|
|
802
1023
|
|
|
803
1024
|
ws.on('CB:stream:error', (node) => {
|
|
804
|
-
|
|
805
|
-
const { reason, statusCode } = Utils_1.getErrorCodeFromStreamError(node)
|
|
1025
|
+
const [reasonNode] = getAllBinaryNodeChildren(node)
|
|
806
1026
|
|
|
807
|
-
|
|
808
|
-
|
|
1027
|
+
logger.error({ reasonNode, fullErrorNode: node }, 'stream errored out')
|
|
1028
|
+
|
|
1029
|
+
const { reason, statusCode } = getErrorCodeFromStreamError(node)
|
|
1030
|
+
|
|
1031
|
+
end(new Boom(`Stream Errored (${reason})`, { statusCode, data: reasonNode || node }))
|
|
1032
|
+
});
|
|
809
1033
|
|
|
810
1034
|
// stream fail, possible logout
|
|
811
1035
|
ws.on('CB:failure', (node) => {
|
|
812
1036
|
const reason = +(node.attrs.reason || 500)
|
|
813
1037
|
|
|
814
|
-
end(new
|
|
1038
|
+
end(new Boom('Connection Failure', { statusCode: reason, data: node.attrs }))
|
|
815
1039
|
})
|
|
816
1040
|
|
|
817
1041
|
ws.on('CB:ib,,downgrade_webclient', () => {
|
|
818
|
-
end(new
|
|
1042
|
+
end(new Boom('Multi-device beta not joined', { statusCode: DisconnectReason.multideviceMismatch }))
|
|
819
1043
|
})
|
|
820
1044
|
|
|
821
1045
|
ws.on('CB:ib,,offline_preview', (node) => {
|
|
@@ -829,8 +1053,8 @@ const makeSocket = (config) => {
|
|
|
829
1053
|
})
|
|
830
1054
|
|
|
831
1055
|
ws.on('CB:ib,,edge_routing', (node) => {
|
|
832
|
-
const edgeRoutingNode =
|
|
833
|
-
const routingInfo =
|
|
1056
|
+
const edgeRoutingNode = getBinaryNodeChild(node, 'edge_routing')
|
|
1057
|
+
const routingInfo = getBinaryNodeChild(edgeRoutingNode, 'routing_info')
|
|
834
1058
|
|
|
835
1059
|
if (routingInfo?.content) {
|
|
836
1060
|
authState.creds.routingInfo = Buffer.from(routingInfo?.content)
|
|
@@ -853,7 +1077,7 @@ const makeSocket = (config) => {
|
|
|
853
1077
|
|
|
854
1078
|
// called when all offline notifs are handled
|
|
855
1079
|
ws.on('CB:ib,,offline', (node) => {
|
|
856
|
-
const child =
|
|
1080
|
+
const child = getBinaryNodeChild(node, 'offline')
|
|
857
1081
|
const offlineNotifs = +(child?.attrs.count || 0)
|
|
858
1082
|
|
|
859
1083
|
logger.info(`handled ${offlineNotifs} offline messages/notifications`)
|
|
@@ -886,7 +1110,7 @@ const makeSocket = (config) => {
|
|
|
886
1110
|
})
|
|
887
1111
|
|
|
888
1112
|
if (printQRInTerminal) {
|
|
889
|
-
|
|
1113
|
+
printQRIfNecessaryListener(ev, logger)
|
|
890
1114
|
}
|
|
891
1115
|
|
|
892
1116
|
return {
|
|
@@ -896,7 +1120,7 @@ const makeSocket = (config) => {
|
|
|
896
1120
|
authState: { creds, keys },
|
|
897
1121
|
signalRepository,
|
|
898
1122
|
get user() {
|
|
899
|
-
return authState.creds.me
|
|
1123
|
+
return authState.creds.me;
|
|
900
1124
|
},
|
|
901
1125
|
generateMessageTag,
|
|
902
1126
|
query,
|
|
@@ -909,13 +1133,15 @@ const makeSocket = (config) => {
|
|
|
909
1133
|
onUnexpectedError,
|
|
910
1134
|
uploadPreKeys,
|
|
911
1135
|
uploadPreKeysToServerIfRequired,
|
|
1136
|
+
digestKeyBundle,
|
|
1137
|
+
rotateSignedPreKey,
|
|
912
1138
|
requestPairingCode,
|
|
1139
|
+
wamBuffer: publicWAMBuffer,
|
|
913
1140
|
/** Waits for the connection to WA to reach a state */
|
|
914
|
-
waitForConnectionUpdate:
|
|
1141
|
+
waitForConnectionUpdate: bindWaitForConnectionUpdate(ev),
|
|
915
1142
|
sendWAMBuffer,
|
|
916
|
-
executeUSyncQuery,
|
|
917
|
-
onWhatsApp
|
|
918
|
-
logger
|
|
1143
|
+
executeUSyncQuery,
|
|
1144
|
+
onWhatsApp
|
|
919
1145
|
}
|
|
920
1146
|
}
|
|
921
1147
|
|
|
@@ -925,7 +1151,7 @@ const makeSocket = (config) => {
|
|
|
925
1151
|
* */
|
|
926
1152
|
function mapWebSocketError(handler) {
|
|
927
1153
|
return (error) => {
|
|
928
|
-
handler(new
|
|
1154
|
+
handler(new Boom(`WebSocket Error (${error?.message})`, { statusCode: getCodeFromWSError(error), data: error }))
|
|
929
1155
|
}
|
|
930
1156
|
}
|
|
931
1157
|
|