@badzz88/baileys 8.5.3 → 8.5.4
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 +1 -1
- package/WAProto/WAProto.proto +1256 -75
- package/WAProto/index.d.ts +36042 -19649
- package/WAProto/index.js +181990 -135645
- package/engine-requirements.js +7 -15
- package/lib/Defaults/index.js +22 -17
- package/lib/Signal/Group/group_cipher.js +5 -5
- package/lib/Signal/Group/keyhelper.js +4 -4
- package/lib/Signal/Group/sender-chain-key.js +2 -2
- package/lib/Signal/Group/sender-key-distribution-message.js +1 -1
- package/lib/Signal/Group/sender-key-message.js +3 -3
- package/lib/Signal/Group/sender-key-record.js +1 -1
- package/lib/Signal/Group/sender-message-key.js +5 -5
- package/lib/Signal/libsignal.js +39 -59
- package/lib/Signal/lid-mapping.js +28 -10
- package/lib/Socket/aigroups.js +5 -5
- package/lib/Socket/business.js +3 -11
- package/lib/Socket/chats.js +668 -319
- package/lib/Socket/communities.js +85 -89
- package/lib/Socket/graphql.js +435 -228
- package/lib/Socket/groups.js +132 -211
- package/lib/Socket/index.js +9 -7
- package/lib/Socket/interactive-handler.js +117 -60
- package/lib/Socket/interop.js +56 -175
- package/lib/Socket/managed-account.js +79 -48
- package/lib/Socket/messages-recv.js +898 -340
- package/lib/Socket/messages-send.js +462 -160
- package/lib/Socket/mex.js +40 -53
- package/lib/Socket/newsletter.js +291 -295
- package/lib/Socket/privacy.js +77 -265
- package/lib/Socket/registration.js +164 -157
- package/lib/Socket/socket.js +181 -199
- package/lib/Socket/username.js +144 -227
- package/lib/Store/make-cache-manager-store.js +2 -2
- package/lib/Store/make-in-memory-store.js +25 -141
- package/lib/Types/Auth.js +3 -3
- package/lib/Types/Label.js +1 -1
- package/lib/Types/LabelAssociation.js +1 -1
- package/lib/Types/Newsletter.js +96 -65
- package/lib/Types/State.js +4 -4
- package/lib/Types/index.js +1 -1
- package/lib/Utils/auth-utils.js +52 -57
- package/lib/Utils/browser-utils.js +1 -1
- package/lib/Utils/business.js +2 -6
- package/lib/Utils/chat-utils.js +102 -38
- package/lib/Utils/crypto.js +54 -84
- package/lib/Utils/decode-wa-message.js +316 -83
- package/lib/Utils/event-buffer.js +37 -61
- package/lib/Utils/generics.js +157 -103
- package/lib/Utils/group-history.js +3 -12
- package/lib/Utils/history.js +15 -15
- package/lib/Utils/index.js +1 -5
- package/lib/Utils/link-preview.js +3 -8
- package/lib/Utils/lt-hash.js +2 -35
- package/lib/Utils/make-mutex.js +1 -1
- package/lib/Utils/message-inspect.js +393 -0
- package/lib/Utils/message-retry-manager.js +27 -92
- package/lib/Utils/messages-media.js +159 -130
- package/lib/Utils/messages.js +144 -566
- package/lib/Utils/meta-ai-msmsg.js +62 -202
- package/lib/Utils/noise-handler.js +19 -21
- package/lib/Utils/offline-node-processor.js +3 -8
- package/lib/Utils/pre-key-manager.js +13 -23
- package/lib/Utils/process-message.js +197 -106
- package/lib/Utils/reporting-utils.js +2 -2
- package/lib/Utils/signal.js +28 -63
- package/lib/Utils/stanza-ack.js +39 -12
- package/lib/Utils/sync-action-utils.js +4 -7
- package/lib/Utils/tc-token-utils.js +20 -25
- package/lib/Utils/use-multi-file-auth-state.js +30 -14
- package/lib/Utils/validate-connection.js +59 -19
- package/lib/WABinary/decode.js +50 -16
- package/lib/WABinary/encode.js +54 -224
- package/lib/WABinary/generic-utils.js +7 -97
- package/lib/WABinary/jid-utils.js +27 -14
- package/lib/WAM/encode.js +7 -7
- package/lib/WAUSync/Protocols/USyncBusinessProtocol.js +58 -1
- package/lib/WAUSync/Protocols/USyncContactProtocol.js +7 -1
- package/lib/WAUSync/Protocols/USyncDeviceProtocol.js +13 -5
- package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.js +1 -4
- package/lib/WAUSync/Protocols/USyncFeatureProtocol.js +20 -7
- package/lib/WAUSync/Protocols/USyncNewsletterProtocol.js +865 -0
- package/lib/WAUSync/Protocols/USyncPictureProtocol.js +1 -1
- package/lib/WAUSync/Protocols/USyncSidelistProtocol.js +2 -2
- package/lib/WAUSync/Protocols/USyncStatusProtocol.js +7 -2
- package/lib/WAUSync/Protocols/USyncTextStatusProtocol.js +1 -1
- package/lib/WAUSync/Protocols/USyncUsernameProtocol.js +2 -1
- package/lib/WAUSync/Protocols/index.js +1 -0
- package/lib/WAUSync/USyncBackoff.js +26 -0
- package/lib/WAUSync/USyncQuery.js +77 -6
- package/lib/WAUSync/USyncUser.js +16 -8
- package/lib/WAUSync/index.js +1 -0
- package/lib/antiban.js +608 -854
- package/lib/index.js +1 -1
- package/package.json +16 -20
- package/lib/Socket/luxu.js +0 -387
- package/lib/Socket/text-router.js +0 -83
- package/lib/Store/keyed-db.js +0 -108
- package/lib/Types/Mex.js +0 -112
- package/lib/Utils/command-loader.d.ts +0 -31
- package/lib/Utils/command-loader.js +0 -100
- package/lib/Utils/companion-reg-client-utils.js +0 -42
- package/lib/Utils/curve25519-js.js +0 -275
- package/lib/Utils/native-bridge.js +0 -82
- package/lib/Utils/session-pool.d.ts +0 -16
- package/lib/Utils/session-pool.js +0 -94
- package/lib/Utils/sticker.d.ts +0 -17
- package/lib/Utils/sticker.js +0 -145
- package/lib/Utils/view-once-cache.d.ts +0 -9
- package/lib/Utils/view-once-cache.js +0 -79
|
@@ -1,262 +1,122 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const
|
|
3
|
+
const rb = require('whatsapp-rust-bridge')
|
|
4
4
|
const { proto } = require('../../WAProto')
|
|
5
5
|
|
|
6
6
|
const BOT_MESSAGE_INFO = 'Bot Message'
|
|
7
7
|
const KEY_LENGTH = 32
|
|
8
|
-
const AUTH_TAG_LENGTH = 16
|
|
9
|
-
|
|
10
|
-
const MSG_ID_HEX_RE = /^[0-9A-Fa-f]{32}$/
|
|
11
8
|
|
|
12
9
|
const unpadRandomMax16 = value => {
|
|
13
10
|
const bytes = new Uint8Array(value)
|
|
14
11
|
if (!bytes.length) {
|
|
15
12
|
throw new Error('unpadPkcs7 given empty bytes')
|
|
16
13
|
}
|
|
17
|
-
|
|
18
14
|
const padLength = bytes[bytes.length - 1]
|
|
19
15
|
if (padLength > bytes.length) {
|
|
20
16
|
throw new Error(`unpad given ${bytes.length} bytes, but pad is ${padLength}`)
|
|
21
17
|
}
|
|
22
|
-
|
|
23
18
|
return new Uint8Array(bytes.buffer, bytes.byteOffset, bytes.length - padLength)
|
|
24
19
|
}
|
|
25
20
|
|
|
26
21
|
const toBuffer = value => {
|
|
27
|
-
if (Buffer.isBuffer(value))
|
|
28
|
-
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
if (value instanceof Uint8Array) {
|
|
32
|
-
return Buffer.from(value.buffer, value.byteOffset, value.byteLength)
|
|
33
|
-
}
|
|
34
|
-
|
|
22
|
+
if (Buffer.isBuffer(value)) return value
|
|
23
|
+
if (value instanceof Uint8Array) return Buffer.from(value.buffer, value.byteOffset, value.byteLength)
|
|
35
24
|
return Buffer.from(value)
|
|
36
25
|
}
|
|
37
26
|
|
|
38
|
-
const normalizeLidJid = jid => {
|
|
39
|
-
if (!jid || !jid.endsWith('@lid') || !jid.includes(':')) {
|
|
40
|
-
return jid
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
return `${jid.split(':')[0]}@lid`
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
const buildMessageIdRepresentations = messageId => {
|
|
47
|
-
const ascii = Buffer.from(messageId)
|
|
48
|
-
const binary = MSG_ID_HEX_RE.test(messageId) ? Buffer.from(messageId, 'hex') : ascii
|
|
49
|
-
return [
|
|
50
|
-
{ label: 'msgIdAscii', value: ascii },
|
|
51
|
-
...(binary.equals(ascii) ? [] : [{ label: 'msgIdBinary', value: binary }])
|
|
52
|
-
]
|
|
53
|
-
}
|
|
54
27
|
|
|
55
|
-
const pushUnique = (items, seen, item) => {
|
|
56
|
-
const key = JSON.stringify([
|
|
57
|
-
item.messageId,
|
|
58
|
-
item.idSource,
|
|
59
|
-
item.idSources,
|
|
60
|
-
item.infoSource,
|
|
61
|
-
item.aadSource,
|
|
62
|
-
item.info.toString('hex'),
|
|
63
|
-
item.aad.toString('hex')
|
|
64
|
-
])
|
|
65
28
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
}
|
|
29
|
+
const normalizeLidJid = jid => {
|
|
30
|
+
if (!jid || !jid.endsWith('@lid') || !jid.includes(':')) return jid
|
|
31
|
+
return `${jid.split(':')[0]}@lid`
|
|
70
32
|
}
|
|
71
33
|
|
|
72
|
-
const getCandidateIds = messageKey => {
|
|
73
|
-
const orderedCandidates = [
|
|
74
|
-
messageKey?.botType === 'full'
|
|
75
|
-
? { source: 'stanzaId', messageId: messageKey?.stanzaId }
|
|
76
|
-
: { source: 'botEditTargetId', messageId: messageKey?.botEditTargetId },
|
|
77
|
-
{ source: 'targetId', messageId: messageKey?.targetId },
|
|
78
|
-
{ source: 'metaTargetId', messageId: messageKey?.metaTargetId },
|
|
79
|
-
{ source: 'stanzaId', messageId: messageKey?.stanzaId }
|
|
80
|
-
]
|
|
81
34
|
|
|
82
|
-
const targetIdCandidates = Array.isArray(messageKey?.targetIdCandidates) ? messageKey.targetIdCandidates : []
|
|
83
|
-
for (let index = 0; index < targetIdCandidates.length; index += 1) {
|
|
84
|
-
orderedCandidates.push({
|
|
85
|
-
source: `targetIdCandidates[${index}]`,
|
|
86
|
-
messageId: targetIdCandidates[index]
|
|
87
|
-
})
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
const grouped = new Map()
|
|
91
|
-
for (const candidate of orderedCandidates) {
|
|
92
|
-
if (!candidate.messageId) {
|
|
93
|
-
continue
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
const messageId = String(candidate.messageId)
|
|
97
|
-
const existing = grouped.get(messageId)
|
|
98
|
-
if (existing) {
|
|
99
|
-
if (!existing.idSources.includes(candidate.source)) {
|
|
100
|
-
existing.idSources.push(candidate.source)
|
|
101
|
-
}
|
|
102
|
-
} else {
|
|
103
|
-
grouped.set(messageId, {
|
|
104
|
-
messageId,
|
|
105
|
-
idSource: candidate.source,
|
|
106
|
-
idSources: [candidate.source]
|
|
107
|
-
})
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
return Array.from(grouped.values())
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
const getJidCandidates = messageKey => {
|
|
115
|
-
const ordered = [
|
|
116
|
-
{ source: 'meId', jid: messageKey?.meId },
|
|
117
|
-
{ source: 'conversationJid', jid: messageKey?.conversationJid },
|
|
118
|
-
{ source: 'senderJid', jid: messageKey?.senderJid },
|
|
119
|
-
{ source: 'meLidNormalized', jid: normalizeLidJid(messageKey?.meLid) }
|
|
120
|
-
]
|
|
121
35
|
|
|
36
|
+
const selectMsgIdCandidates = messageKey => {
|
|
122
37
|
const seen = new Set()
|
|
123
|
-
const
|
|
124
|
-
for (const
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
const jid = String(candidate.jid)
|
|
130
|
-
if (!seen.has(jid)) {
|
|
131
|
-
seen.add(jid)
|
|
132
|
-
candidates.push({ source: candidate.source, jid, value: Buffer.from(jid) })
|
|
38
|
+
const result = []
|
|
39
|
+
for (const id of [messageKey?.botEditTargetId, messageKey?.stanzaId, messageKey?.metaTargetId]) {
|
|
40
|
+
const s = id ? String(id) : ''
|
|
41
|
+
if (s && !seen.has(s)) {
|
|
42
|
+
seen.add(s)
|
|
43
|
+
result.push(s)
|
|
133
44
|
}
|
|
134
45
|
}
|
|
135
|
-
|
|
136
|
-
return candidates
|
|
46
|
+
return result
|
|
137
47
|
}
|
|
138
48
|
|
|
139
|
-
const buildMsmsgDecryptionStrategies = messageKey => {
|
|
140
|
-
const botJid = String(messageKey?.participant || '')
|
|
141
|
-
const botJidBuffer = Buffer.from(botJid)
|
|
142
|
-
const targetIds = getCandidateIds(messageKey)
|
|
143
|
-
const jidCandidates = getJidCandidates(messageKey)
|
|
144
|
-
const primaryJid = jidCandidates[0]
|
|
145
|
-
const alternateJid = jidCandidates.find(
|
|
146
|
-
candidate => candidate.source !== primaryJid?.source && candidate.jid !== botJid
|
|
147
|
-
)
|
|
148
|
-
const strategies = []
|
|
149
|
-
const seen = new Set()
|
|
150
49
|
|
|
151
|
-
for (const idCandidate of targetIds) {
|
|
152
|
-
const idForms = buildMessageIdRepresentations(idCandidate.messageId)
|
|
153
|
-
for (const idForm of idForms) {
|
|
154
|
-
pushUnique(strategies, seen, {
|
|
155
|
-
mode: '2step',
|
|
156
|
-
idSource: idCandidate.idSource,
|
|
157
|
-
idSources: idCandidate.idSources,
|
|
158
|
-
infoSource: `${idForm.label}+meId+botJid`,
|
|
159
|
-
aadSource: `${idForm.label}+0+botJid`,
|
|
160
|
-
authTagLayout: 'trailing',
|
|
161
|
-
messageId: idCandidate.messageId,
|
|
162
|
-
info: Buffer.concat([idForm.value, primaryJid.value, botJidBuffer, Buffer.alloc(0)]),
|
|
163
|
-
aad: Buffer.concat([idForm.value, Buffer.from([0]), botJidBuffer]),
|
|
164
|
-
attemptLabel: `${idCandidate.idSource}:${idForm.label}:primary`
|
|
165
|
-
})
|
|
166
50
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
messageId: idCandidate.messageId,
|
|
176
|
-
info: Buffer.concat([idForm.value, alternateJid.value, botJidBuffer, Buffer.alloc(0)]),
|
|
177
|
-
aad: Buffer.concat([idForm.value, Buffer.from([0]), alternateJid.value]),
|
|
178
|
-
attemptLabel: `${idCandidate.idSource}:${idForm.label}:${alternateJid.source}`
|
|
179
|
-
})
|
|
180
|
-
}
|
|
51
|
+
const selectTargetJidCandidates = messageKey => {
|
|
52
|
+
const seen = new Set()
|
|
53
|
+
const result = []
|
|
54
|
+
for (const jid of [normalizeLidJid(messageKey?.meId), normalizeLidJid(messageKey?.meLid)]) {
|
|
55
|
+
const s = jid ? String(jid) : ''
|
|
56
|
+
if (s && !seen.has(s)) {
|
|
57
|
+
seen.add(s)
|
|
58
|
+
result.push(s)
|
|
181
59
|
}
|
|
182
60
|
}
|
|
183
|
-
|
|
184
|
-
return strategies.slice(0, 12)
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
const assertRequired = (value, label) => {
|
|
188
|
-
if (
|
|
189
|
-
!value ||
|
|
190
|
-
(Buffer.isBuffer(value) && value.length === 0) ||
|
|
191
|
-
(value instanceof Uint8Array && value.byteLength === 0)
|
|
192
|
-
) {
|
|
193
|
-
throw new Error(`Missing required ${label} for msmsg decryption`)
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
const decryptWithStrategy = (messageSecret, msMsg, strategy) => {
|
|
198
|
-
const baseSecret = Buffer.from(Crypto_1.hkdf(toBuffer(messageSecret), KEY_LENGTH, { info: BOT_MESSAGE_INFO }))
|
|
199
|
-
const key = Buffer.from(Crypto_1.hkdf(baseSecret, KEY_LENGTH, { info: strategy.info }))
|
|
200
|
-
const payload = toBuffer(msMsg.encPayload)
|
|
201
|
-
// ciphertext = payload without last 16 bytes (auth tag) + auth tag appended → standard GCM layout
|
|
202
|
-
const ciphertextWithTag = Buffer.concat([payload.slice(0, -AUTH_TAG_LENGTH), payload.slice(-AUTH_TAG_LENGTH)])
|
|
203
|
-
return Buffer.from(Crypto_1.aesDecryptGCM(ciphertextWithTag, key, toBuffer(msMsg.encIv), strategy.aad))
|
|
61
|
+
return result
|
|
204
62
|
}
|
|
205
63
|
|
|
206
64
|
const decodeDecryptedMsmsgMessage = decrypted => {
|
|
207
|
-
const
|
|
208
|
-
|
|
65
|
+
const buf = toBuffer(decrypted)
|
|
209
66
|
try {
|
|
210
|
-
const unpadded = Buffer.from(unpadRandomMax16(
|
|
67
|
+
const unpadded = Buffer.from(unpadRandomMax16(buf))
|
|
211
68
|
const decoded = proto.Message.decode(unpadded)
|
|
212
|
-
const hasContent = Object.keys(decoded).some(
|
|
213
|
-
if (hasContent)
|
|
214
|
-
return decoded
|
|
215
|
-
}
|
|
69
|
+
const hasContent = Object.keys(decoded).some(k => k !== 'messageContextInfo' && decoded[k] != null)
|
|
70
|
+
if (hasContent) return decoded
|
|
216
71
|
} catch {}
|
|
217
|
-
|
|
218
|
-
return proto.Message.decode(messageBuffer)
|
|
72
|
+
return proto.Message.decode(buf)
|
|
219
73
|
}
|
|
220
74
|
|
|
75
|
+
|
|
221
76
|
const decryptMsmsgBotMessage = async (messageSecret, messageKey, msMsg) => {
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
assertRequired(messageKey?.meId, 'meId')
|
|
225
|
-
assertRequired(msMsg?.encIv, 'encIv')
|
|
226
|
-
assertRequired(msMsg?.encPayload, 'encPayload')
|
|
227
|
-
if (getCandidateIds(messageKey).length === 0) {
|
|
228
|
-
throw new Error('Missing required target message id for msmsg decryption')
|
|
77
|
+
if (!messageSecret || (messageSecret instanceof Uint8Array && !messageSecret.byteLength)) {
|
|
78
|
+
throw new Error('Missing required messageSecret for msmsg decryption')
|
|
229
79
|
}
|
|
80
|
+
if (!messageKey?.participant) throw new Error('Missing required participant for msmsg decryption')
|
|
81
|
+
if (!messageKey?.meId) throw new Error('Missing required meId for msmsg decryption')
|
|
82
|
+
if (!msMsg?.encIv) throw new Error('Missing required encIv for msmsg decryption')
|
|
83
|
+
if (!msMsg?.encPayload) throw new Error('Missing required encPayload for msmsg decryption')
|
|
230
84
|
|
|
231
|
-
const
|
|
232
|
-
|
|
233
|
-
let lastError
|
|
85
|
+
const msgIdCandidates = selectMsgIdCandidates(messageKey)
|
|
86
|
+
if (!msgIdCandidates.length) throw new Error('Missing required target message id for msmsg decryption')
|
|
234
87
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
messageId: strategy.messageId
|
|
242
|
-
}
|
|
88
|
+
const targetJidCandidates = selectTargetJidCandidates(messageKey)
|
|
89
|
+
if (!targetJidCandidates.length) throw new Error('Missing required target JID for msmsg decryption')
|
|
90
|
+
|
|
91
|
+
const botJidBuf = Buffer.from(String(messageKey.participant))
|
|
92
|
+
const payload = toBuffer(msMsg.encPayload)
|
|
93
|
+
const iv = toBuffer(msMsg.encIv)
|
|
243
94
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
95
|
+
|
|
96
|
+
const baseKey = Buffer.from(rb.hkdf(toBuffer(messageSecret), KEY_LENGTH, { info: BOT_MESSAGE_INFO }))
|
|
97
|
+
|
|
98
|
+
let lastError
|
|
99
|
+
for (const msgId of msgIdCandidates) {
|
|
100
|
+
|
|
101
|
+
const idBuf = Buffer.from(msgId)
|
|
102
|
+
for (const targetJid of targetJidCandidates) {
|
|
103
|
+
const info = Buffer.concat([idBuf, Buffer.from(targetJid), botJidBuf])
|
|
104
|
+
const key = Buffer.from(rb.hkdf(baseKey, KEY_LENGTH, { info }))
|
|
105
|
+
const aad = Buffer.concat([idBuf, Buffer.from([0x00]), botJidBuf])
|
|
106
|
+
try {
|
|
107
|
+
return Buffer.from(rb.aesDecryptGCM(payload, key, iv, aad))
|
|
108
|
+
} catch (e) {
|
|
109
|
+
lastError = e
|
|
110
|
+
}
|
|
249
111
|
}
|
|
250
112
|
}
|
|
251
113
|
|
|
252
|
-
const
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
throw error
|
|
114
|
+
const err = new Error('msmsg decryption failed: all key derivation candidates exhausted')
|
|
115
|
+
err.cause = lastError
|
|
116
|
+
throw err
|
|
256
117
|
}
|
|
257
118
|
|
|
258
119
|
module.exports = {
|
|
259
|
-
buildMsmsgDecryptionStrategies,
|
|
260
120
|
decodeDecryptedMsmsgMessage,
|
|
261
121
|
decryptMsmsgBotMessage
|
|
262
122
|
}
|
|
@@ -4,15 +4,15 @@ exports.makeNoiseHandler = void 0
|
|
|
4
4
|
const boom_1 = require('@hapi/boom')
|
|
5
5
|
const index_js_1 = require('../../WAProto/index.js')
|
|
6
6
|
const Defaults_1 = require('../Defaults')
|
|
7
|
-
const rb = require('
|
|
7
|
+
const rb = require('whatsapp-rust-bridge')
|
|
8
|
+
|
|
8
9
|
|
|
9
|
-
// rustNodeToJs mirrors the one in decode.js — converts InternalBinaryNode to plain JS object
|
|
10
10
|
const rustNodeToJs = node => {
|
|
11
11
|
if (!node || typeof node !== 'object') return node
|
|
12
12
|
const result = { tag: node.tag, attrs: node.attrs || {} }
|
|
13
13
|
const content = node.content
|
|
14
14
|
if (content === undefined || content === null) {
|
|
15
|
-
|
|
15
|
+
|
|
16
16
|
} else if (content instanceof Uint8Array) {
|
|
17
17
|
result.content = Buffer.from(content)
|
|
18
18
|
} else if (typeof content === 'string') {
|
|
@@ -33,14 +33,6 @@ const makeNoiseHandler = ({
|
|
|
33
33
|
}) => {
|
|
34
34
|
logger = logger.child({ class: 'ns' })
|
|
35
35
|
|
|
36
|
-
if (!rb) {
|
|
37
|
-
throw new Error(
|
|
38
|
-
'Establishing a WhatsApp connection requires the "whatsapp-rust-bridge" native module ' +
|
|
39
|
-
'(Noise protocol handshake + session), which failed to load. Make sure it is installed: ' +
|
|
40
|
-
'npm install whatsapp-rust-bridge@0.5.4'
|
|
41
|
-
)
|
|
42
|
-
}
|
|
43
|
-
|
|
44
36
|
const session = new rb.NoiseSession(
|
|
45
37
|
publicKey instanceof Uint8Array ? publicKey : new Uint8Array(publicKey),
|
|
46
38
|
NOISE_HEADER instanceof Uint8Array ? NOISE_HEADER : new Uint8Array(NOISE_HEADER),
|
|
@@ -52,7 +44,7 @@ const makeNoiseHandler = ({
|
|
|
52
44
|
let pendingBytes = null
|
|
53
45
|
|
|
54
46
|
const processFrames = async onFrame => {
|
|
55
|
-
|
|
47
|
+
|
|
56
48
|
const frames = session.decodeFrame(new Uint8Array(0))
|
|
57
49
|
for (let i = 0; i < frames.length; i++) {
|
|
58
50
|
const item = frames[i]
|
|
@@ -93,7 +85,7 @@ const makeNoiseHandler = ({
|
|
|
93
85
|
} else {
|
|
94
86
|
try {
|
|
95
87
|
frame = rustNodeToJs(item.toJSON())
|
|
96
|
-
} catch
|
|
88
|
+
} catch {
|
|
97
89
|
continue
|
|
98
90
|
}
|
|
99
91
|
}
|
|
@@ -119,7 +111,7 @@ const makeNoiseHandler = ({
|
|
|
119
111
|
},
|
|
120
112
|
finishInit,
|
|
121
113
|
processHandshake: ({ serverHello }, noiseKey) => {
|
|
122
|
-
|
|
114
|
+
|
|
123
115
|
const certPayload = session.processHandshakeInit(
|
|
124
116
|
serverHello.ephemeral,
|
|
125
117
|
serverHello.static,
|
|
@@ -127,7 +119,7 @@ const makeNoiseHandler = ({
|
|
|
127
119
|
privateKey instanceof Uint8Array ? privateKey : new Uint8Array(privateKey)
|
|
128
120
|
)
|
|
129
121
|
|
|
130
|
-
|
|
122
|
+
|
|
131
123
|
const { intermediate: certIntermediate, leaf } = index_js_1.proto.CertChain.decode(certPayload)
|
|
132
124
|
if (!leaf?.details || !leaf?.signature) {
|
|
133
125
|
throw new boom_1.Boom('invalid noise leaf certificate', { statusCode: 400 })
|
|
@@ -148,13 +140,20 @@ const makeNoiseHandler = ({
|
|
|
148
140
|
throw new boom_1.Boom('certification match failed', { statusCode: 400 })
|
|
149
141
|
}
|
|
150
142
|
|
|
151
|
-
|
|
143
|
+
|
|
144
|
+
let serverStaticPub
|
|
145
|
+
try {
|
|
146
|
+
const leafDetails = index_js_1.proto.CertChain.NoiseCertificate.Details.decode(leaf.details)
|
|
147
|
+
if (leafDetails.key?.length === 32) serverStaticPub = Buffer.from(leafDetails.key)
|
|
148
|
+
} catch {}
|
|
149
|
+
|
|
150
|
+
|
|
152
151
|
const keyEnc = session.processHandshakeFinish(
|
|
153
152
|
noiseKey.public instanceof Uint8Array ? noiseKey.public : new Uint8Array(noiseKey.public),
|
|
154
153
|
noiseKey.private instanceof Uint8Array ? noiseKey.private : new Uint8Array(noiseKey.private),
|
|
155
154
|
serverHello.ephemeral
|
|
156
155
|
)
|
|
157
|
-
return Buffer.from(keyEnc)
|
|
156
|
+
return { keyEnc: Buffer.from(keyEnc), serverStaticPub }
|
|
158
157
|
},
|
|
159
158
|
encodeFrame: data => {
|
|
160
159
|
const u8 = data instanceof Uint8Array ? data : new Uint8Array(data.buffer, data.byteOffset, data.byteLength)
|
|
@@ -162,14 +161,13 @@ const makeNoiseHandler = ({
|
|
|
162
161
|
},
|
|
163
162
|
decodeFrame: async (newData, onFrame) => {
|
|
164
163
|
if (isWaitingForTransport) {
|
|
165
|
-
pendingBytes = pendingBytes
|
|
166
|
-
? Buffer.concat([pendingBytes, Buffer.from(newData)])
|
|
167
|
-
: Buffer.from(newData)
|
|
164
|
+
pendingBytes = pendingBytes ? Buffer.concat([pendingBytes, Buffer.from(newData)]) : Buffer.from(newData)
|
|
168
165
|
pendingOnFrame = onFrame
|
|
169
166
|
return
|
|
170
167
|
}
|
|
171
168
|
|
|
172
|
-
const u8 =
|
|
169
|
+
const u8 =
|
|
170
|
+
newData instanceof Uint8Array ? newData : new Uint8Array(newData.buffer, newData.byteOffset, newData.byteLength)
|
|
173
171
|
const frames = session.decodeFrame(u8)
|
|
174
172
|
|
|
175
173
|
for (let i = 0; i < frames.length; i++) {
|
|
@@ -1,12 +1,7 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
Object.defineProperty(exports, '__esModule', { value: true })
|
|
3
3
|
exports.makeOfflineNodeProcessor = makeOfflineNodeProcessor
|
|
4
|
-
|
|
5
|
-
* Creates a processor for offline stanza nodes that:
|
|
6
|
-
* - Queues nodes for sequential processing
|
|
7
|
-
* - Yields to the event loop periodically to avoid blocking
|
|
8
|
-
* - Catches handler errors to prevent the processing loop from crashing
|
|
9
|
-
*/
|
|
4
|
+
|
|
10
5
|
function makeOfflineNodeProcessor(nodeProcessorMap, deps, batchSize = 10) {
|
|
11
6
|
const nodes = []
|
|
12
7
|
let isProcessing = false
|
|
@@ -27,8 +22,8 @@ function makeOfflineNodeProcessor(nodeProcessorMap, deps, batchSize = 10) {
|
|
|
27
22
|
}
|
|
28
23
|
await nodeProcessor(node).catch(err => deps.onUnexpectedError(err, `processing offline ${type}`))
|
|
29
24
|
processedInBatch++
|
|
30
|
-
|
|
31
|
-
|
|
25
|
+
|
|
26
|
+
|
|
32
27
|
if (processedInBatch >= batchSize) {
|
|
33
28
|
processedInBatch = 0
|
|
34
29
|
await deps.yieldToEventLoop()
|
|
@@ -7,35 +7,29 @@ var __importDefault =
|
|
|
7
7
|
Object.defineProperty(exports, '__esModule', { value: true })
|
|
8
8
|
exports.PreKeyManager = void 0
|
|
9
9
|
const p_queue_1 = __importDefault(require('p-queue'))
|
|
10
|
-
|
|
11
|
-
* Manages pre-key operations with proper concurrency control
|
|
12
|
-
*/
|
|
10
|
+
|
|
13
11
|
class PreKeyManager {
|
|
14
12
|
constructor(store, logger) {
|
|
15
13
|
this.store = store
|
|
16
14
|
this.logger = logger
|
|
17
15
|
this.queues = new Map()
|
|
18
16
|
}
|
|
19
|
-
|
|
20
|
-
* Get or create a queue for a specific key type
|
|
21
|
-
*/
|
|
17
|
+
|
|
22
18
|
getQueue(keyType) {
|
|
23
19
|
if (!this.queues.has(keyType)) {
|
|
24
20
|
this.queues.set(keyType, new p_queue_1.default({ concurrency: 1 }))
|
|
25
21
|
}
|
|
26
22
|
return this.queues.get(keyType)
|
|
27
23
|
}
|
|
28
|
-
|
|
29
|
-
* Process pre-key operations (updates and deletions)
|
|
30
|
-
*/
|
|
24
|
+
|
|
31
25
|
async processOperations(data, keyType, transactionCache, mutations, isInTransaction) {
|
|
32
26
|
const keyData = data[keyType]
|
|
33
27
|
if (!keyData) return
|
|
34
28
|
return this.getQueue(keyType).add(async () => {
|
|
35
|
-
|
|
29
|
+
|
|
36
30
|
transactionCache[keyType] = transactionCache[keyType] || {}
|
|
37
31
|
mutations[keyType] = mutations[keyType] || {}
|
|
38
|
-
|
|
32
|
+
|
|
39
33
|
const deletions = []
|
|
40
34
|
const updates = {}
|
|
41
35
|
for (const keyId in keyData) {
|
|
@@ -45,23 +39,21 @@ class PreKeyManager {
|
|
|
45
39
|
updates[keyId] = keyData[keyId]
|
|
46
40
|
}
|
|
47
41
|
}
|
|
48
|
-
|
|
42
|
+
|
|
49
43
|
if (Object.keys(updates).length > 0) {
|
|
50
44
|
Object.assign(transactionCache[keyType], updates)
|
|
51
45
|
Object.assign(mutations[keyType], updates)
|
|
52
46
|
}
|
|
53
|
-
|
|
47
|
+
|
|
54
48
|
if (deletions.length > 0) {
|
|
55
49
|
await this.processDeletions(keyType, deletions, transactionCache, mutations, isInTransaction)
|
|
56
50
|
}
|
|
57
51
|
})
|
|
58
52
|
}
|
|
59
|
-
|
|
60
|
-
* Process deletions with validation
|
|
61
|
-
*/
|
|
53
|
+
|
|
62
54
|
async processDeletions(keyType, ids, transactionCache, mutations, isInTransaction) {
|
|
63
55
|
if (isInTransaction) {
|
|
64
|
-
|
|
56
|
+
|
|
65
57
|
for (const keyId of ids) {
|
|
66
58
|
if (transactionCache[keyType]?.[keyId]) {
|
|
67
59
|
transactionCache[keyType][keyId] = null
|
|
@@ -71,7 +63,7 @@ class PreKeyManager {
|
|
|
71
63
|
}
|
|
72
64
|
}
|
|
73
65
|
} else {
|
|
74
|
-
|
|
66
|
+
|
|
75
67
|
const existingKeys = await this.store.get(keyType, ids)
|
|
76
68
|
for (const keyId of ids) {
|
|
77
69
|
if (existingKeys[keyId]) {
|
|
@@ -83,17 +75,15 @@ class PreKeyManager {
|
|
|
83
75
|
}
|
|
84
76
|
}
|
|
85
77
|
}
|
|
86
|
-
|
|
87
|
-
* Validate and process pre-key deletions outside transactions
|
|
88
|
-
*/
|
|
78
|
+
|
|
89
79
|
async validateDeletions(data, keyType) {
|
|
90
80
|
const keyData = data[keyType]
|
|
91
81
|
if (!keyData) return
|
|
92
82
|
return this.getQueue(keyType).add(async () => {
|
|
93
|
-
|
|
83
|
+
|
|
94
84
|
const deletionIds = Object.keys(keyData).filter(id => keyData[id] === null)
|
|
95
85
|
if (deletionIds.length === 0) return
|
|
96
|
-
|
|
86
|
+
|
|
97
87
|
const existingKeys = await this.store.get(keyType, deletionIds)
|
|
98
88
|
for (const keyId of deletionIds) {
|
|
99
89
|
if (!existingKeys[keyId]) {
|