@nexustechpro/baileys 2.0.2 → 2.0.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/LICENSE +1 -1
- package/README.md +924 -1299
- package/WAProto/index.js +22 -18
- package/lib/Defaults/baileys-version.json +6 -2
- package/lib/Defaults/index.js +173 -172
- package/lib/Signal/libsignal.js +395 -292
- package/lib/Signal/lid-mapping.js +264 -171
- package/lib/Socket/Client/index.js +2 -2
- package/lib/Socket/Client/types.js +10 -10
- package/lib/Socket/Client/websocket.js +45 -310
- package/lib/Socket/business.js +375 -375
- package/lib/Socket/chats.js +916 -963
- package/lib/Socket/communities.js +430 -430
- package/lib/Socket/groups.js +342 -342
- package/lib/Socket/index.js +21 -22
- package/lib/Socket/messages-recv.js +963 -743
- package/lib/Socket/messages-send.js +273 -321
- package/lib/Socket/mex.js +50 -50
- package/lib/Socket/newsletter.js +148 -148
- package/lib/Socket/nexus-handler.js +296 -247
- package/lib/Socket/registration.js +50 -33
- package/lib/Socket/socket.js +872 -1201
- package/lib/Store/index.js +5 -5
- package/lib/Store/make-cache-manager-store.js +81 -81
- package/lib/Store/make-in-memory-store.js +416 -416
- package/lib/Store/make-ordered-dictionary.js +81 -81
- package/lib/Store/object-repository.js +30 -30
- package/lib/Types/Auth.js +1 -1
- package/lib/Types/Bussines.js +1 -1
- package/lib/Types/Call.js +1 -1
- package/lib/Types/Chat.js +7 -7
- package/lib/Types/Contact.js +1 -1
- package/lib/Types/Events.js +1 -1
- package/lib/Types/GroupMetadata.js +1 -1
- package/lib/Types/Label.js +24 -24
- package/lib/Types/LabelAssociation.js +6 -6
- package/lib/Types/Message.js +10 -10
- package/lib/Types/Newsletter.js +37 -29
- package/lib/Types/Product.js +1 -1
- package/lib/Types/Signal.js +1 -1
- package/lib/Types/Socket.js +2 -2
- package/lib/Types/State.js +55 -12
- package/lib/Types/USync.js +1 -1
- package/lib/Types/index.js +25 -25
- package/lib/Utils/auth-utils.js +264 -256
- package/lib/Utils/baileys-event-stream.js +55 -55
- package/lib/Utils/browser-utils.js +27 -27
- package/lib/Utils/business.js +228 -230
- package/lib/Utils/chat-utils.js +726 -764
- package/lib/Utils/companion-reg-client-utils.js +34 -0
- package/lib/Utils/crypto.js +109 -135
- package/lib/Utils/decode-wa-message.js +342 -314
- package/lib/Utils/event-buffer.js +547 -547
- package/lib/Utils/generics.js +295 -297
- package/lib/Utils/history.js +91 -83
- package/lib/Utils/index.js +25 -20
- package/lib/Utils/key-store.js +17 -0
- package/lib/Utils/link-preview.js +107 -98
- package/lib/Utils/logger.js +2 -2
- package/lib/Utils/lt-hash.js +47 -47
- package/lib/Utils/make-mutex.js +39 -39
- package/lib/Utils/message-retry-manager.js +148 -148
- package/lib/Utils/messages-media.js +579 -535
- package/lib/Utils/messages.js +821 -706
- package/lib/Utils/noise-handler.js +255 -255
- package/lib/Utils/pre-key-manager.js +105 -105
- package/lib/Utils/process-message.js +430 -412
- package/lib/Utils/reporting-utils.js +155 -0
- package/lib/Utils/signal.js +191 -159
- package/lib/Utils/sync-action-utils.js +33 -0
- package/lib/Utils/tc-token-utils.js +162 -0
- package/lib/Utils/use-multi-file-auth-state.js +120 -120
- package/lib/Utils/validate-connection.js +194 -194
- package/lib/WABinary/constants.js +1306 -1300
- package/lib/WABinary/decode.js +237 -237
- package/lib/WABinary/encode.js +232 -232
- package/lib/WABinary/generic-utils.js +252 -211
- package/lib/WABinary/index.js +6 -5
- package/lib/WABinary/jid-utils.js +279 -95
- package/lib/WABinary/types.js +1 -1
- package/lib/WAM/BinaryInfo.js +9 -9
- package/lib/WAM/constants.js +22852 -22852
- package/lib/WAM/encode.js +149 -149
- package/lib/WAM/index.js +3 -3
- package/lib/WAUSync/Protocols/USyncContactProtocol.js +28 -28
- package/lib/WAUSync/Protocols/USyncDeviceProtocol.js +53 -53
- package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.js +26 -26
- package/lib/WAUSync/Protocols/USyncStatusProtocol.js +37 -37
- package/lib/WAUSync/Protocols/UsyncBotProfileProtocol.js +50 -50
- package/lib/WAUSync/Protocols/UsyncLIDProtocol.js +28 -28
- package/lib/WAUSync/Protocols/index.js +4 -4
- package/lib/WAUSync/USyncQuery.js +93 -93
- package/lib/WAUSync/USyncUser.js +22 -22
- package/lib/WAUSync/index.js +3 -3
- package/lib/index.js +65 -66
- package/package.json +172 -143
- package/lib/Signal/Group/ciphertext-message.js +0 -12
- package/lib/Signal/Group/group-session-builder.js +0 -30
- package/lib/Signal/Group/group_cipher.js +0 -100
- package/lib/Signal/Group/index.js +0 -12
- package/lib/Signal/Group/keyhelper.js +0 -18
- package/lib/Signal/Group/sender-chain-key.js +0 -26
- package/lib/Signal/Group/sender-key-distribution-message.js +0 -63
- package/lib/Signal/Group/sender-key-message.js +0 -66
- package/lib/Signal/Group/sender-key-name.js +0 -48
- package/lib/Signal/Group/sender-key-record.js +0 -41
- package/lib/Signal/Group/sender-key-state.js +0 -84
- package/lib/Signal/Group/sender-message-key.js +0 -26
package/lib/Signal/libsignal.js
CHANGED
|
@@ -1,293 +1,396 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
const
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
const sessionBatch =
|
|
218
|
-
const
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
1
|
+
import {
|
|
2
|
+
SessionCipher,
|
|
3
|
+
SessionBuilder,
|
|
4
|
+
SessionRecord,
|
|
5
|
+
ProtocolAddress,
|
|
6
|
+
GroupCipher,
|
|
7
|
+
GroupSessionBuilder,
|
|
8
|
+
SenderKeyName,
|
|
9
|
+
SenderKeyDistributionMessage,
|
|
10
|
+
} from 'whatsapp-rust-bridge'
|
|
11
|
+
import { LRUCache } from 'lru-cache'
|
|
12
|
+
import { generateSignalPubKey, migrateIndexKey } from '../Utils/index.js'
|
|
13
|
+
import { isHostedLidUser, isHostedPnUser, isLidUser, isPnUser, jidDecode, transferDevice, WAJIDDomains } from '../WABinary/index.js'
|
|
14
|
+
import { LIDMappingStore } from './lid-mapping.js'
|
|
15
|
+
|
|
16
|
+
// ─── Address Helpers ──────────────────────────────────────────────────────────
|
|
17
|
+
|
|
18
|
+
const jidToAddr = (jid) => {
|
|
19
|
+
const { user, device, server, domainType } = jidDecode(jid)
|
|
20
|
+
if (!user) throw new Error(`Invalid JID: "${jid}"`)
|
|
21
|
+
if (device === 99 && server !== 'hosted' && server !== 'hosted.lid') throw new Error('Invalid device 99: ' + jid)
|
|
22
|
+
const signalUser = domainType !== WAJIDDomains.WHATSAPP ? `${user}_${domainType}` : user
|
|
23
|
+
return new ProtocolAddress(signalUser, device || 0)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const jidToSenderKeyName = (group, user) => new SenderKeyName(group, jidToAddr(user))
|
|
27
|
+
|
|
28
|
+
const v2Key = (addr) => `${addr}:v2`
|
|
29
|
+
|
|
30
|
+
// ─── Identity Extraction ──────────────────────────────────────────────────────
|
|
31
|
+
|
|
32
|
+
function extractIdentityFromPkmsg(ciphertext) {
|
|
33
|
+
try {
|
|
34
|
+
if (!ciphertext || ciphertext.length < 2) return undefined
|
|
35
|
+
if ((ciphertext[0] & 0xf) !== 3) return undefined
|
|
36
|
+
const buf = ciphertext.slice(1)
|
|
37
|
+
let i = 0
|
|
38
|
+
while (i < buf.length) {
|
|
39
|
+
const tag = buf[i++]
|
|
40
|
+
const fieldNum = tag >> 3
|
|
41
|
+
const wireType = tag & 0x7
|
|
42
|
+
if (wireType === 2) {
|
|
43
|
+
let len = 0, shift = 0
|
|
44
|
+
while (i < buf.length) { const b = buf[i++]; len |= (b & 0x7f) << shift; if (!(b & 0x80)) break; shift += 7 }
|
|
45
|
+
if (fieldNum === 4 && len === 33) return new Uint8Array(buf.slice(i, i + len))
|
|
46
|
+
i += len
|
|
47
|
+
} else if (wireType === 0) {
|
|
48
|
+
while (i < buf.length && buf[i++] & 0x80) { }
|
|
49
|
+
} else if (wireType === 5) { i += 4 }
|
|
50
|
+
else if (wireType === 1) { i += 8 }
|
|
51
|
+
else break
|
|
52
|
+
}
|
|
53
|
+
} catch { }
|
|
54
|
+
return undefined
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// ─── Buffer Utils ─────────────────────────────────────────────────────────────
|
|
58
|
+
|
|
59
|
+
const toBuffer = (raw) => {
|
|
60
|
+
if (!raw) return null
|
|
61
|
+
if (raw instanceof Uint8Array) return raw
|
|
62
|
+
if (Buffer.isBuffer(raw)) return raw
|
|
63
|
+
if (raw?.type === 'Buffer' && Array.isArray(raw?.data)) return Buffer.from(raw.data)
|
|
64
|
+
if (Array.isArray(raw)) return Buffer.from(raw)
|
|
65
|
+
if (typeof raw === 'string') return Buffer.from(raw, 'base64')
|
|
66
|
+
if (raw?.data) return Buffer.from(raw.data)
|
|
67
|
+
return null
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const isOldJson = (raw) => {
|
|
71
|
+
if (!raw || raw instanceof Uint8Array || Buffer.isBuffer(raw)) return false
|
|
72
|
+
if (typeof raw === 'object') return 'version' in raw || '_sessions' in raw
|
|
73
|
+
if (typeof raw === 'string') { try { const p = JSON.parse(raw); return 'version' in p || '_sessions' in p } catch { return false } }
|
|
74
|
+
return false
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// ─── Main Factory ─────────────────────────────────────────────────────────────
|
|
78
|
+
|
|
79
|
+
export function makeLibSignalRepository(auth, logger, pnToLIDFunc) {
|
|
80
|
+
const lidMapping = new LIDMappingStore(auth.keys, logger, pnToLIDFunc)
|
|
81
|
+
const storage = signalStorage(auth, lidMapping, logger)
|
|
82
|
+
const parsedKeys = auth.keys
|
|
83
|
+
const migratedCache = new LRUCache({ ttl: 7 * 24 * 60 * 60 * 1000, ttlAutopurge: true, updateAgeOnGet: true })
|
|
84
|
+
const txn = (fn, key) => parsedKeys.transaction(fn, key)
|
|
85
|
+
|
|
86
|
+
return {
|
|
87
|
+
decryptGroupMessage({ group, authorJid, msg }) {
|
|
88
|
+
return txn(() => new GroupCipher(storage, group, jidToAddr(authorJid)).decrypt(msg), group)
|
|
89
|
+
},
|
|
90
|
+
|
|
91
|
+
async processSenderKeyDistributionMessage({ item, authorJid }) {
|
|
92
|
+
if (!item.groupId) throw new Error('Group ID required')
|
|
93
|
+
const senderName = jidToSenderKeyName(item.groupId, authorJid)
|
|
94
|
+
const senderMsg = SenderKeyDistributionMessage.deserialize(item.axolotlSenderKeyDistributionMessage)
|
|
95
|
+
return txn(() => new GroupSessionBuilder(storage).process(senderName, senderMsg), item.groupId)
|
|
96
|
+
},
|
|
97
|
+
|
|
98
|
+
async encryptGroupMessage({ group, meId, data }) {
|
|
99
|
+
const senderName = jidToSenderKeyName(group, meId)
|
|
100
|
+
const skdm = await new GroupSessionBuilder(storage).create(senderName)
|
|
101
|
+
const plaintext = data instanceof Uint8Array && data.constructor === Uint8Array
|
|
102
|
+
? data
|
|
103
|
+
: new Uint8Array(data.buffer, data.byteOffset, data.byteLength)
|
|
104
|
+
const ciphertext = await new GroupCipher(storage, group, jidToAddr(meId)).encrypt(plaintext)
|
|
105
|
+
return { ciphertext, senderKeyDistributionMessage: skdm.serialize() }
|
|
106
|
+
},
|
|
107
|
+
|
|
108
|
+
async getSenderKeyDistributionMessage({ group, meId }) {
|
|
109
|
+
const senderName = jidToSenderKeyName(group, meId)
|
|
110
|
+
const skdm = await new GroupSessionBuilder(storage).create(senderName)
|
|
111
|
+
return skdm.serialize()
|
|
112
|
+
},
|
|
113
|
+
|
|
114
|
+
async hasSenderKey({ group, meId }) {
|
|
115
|
+
const name = jidToSenderKeyName(group, meId).toString()
|
|
116
|
+
const { [name]: key } = await parsedKeys.get('sender-key', [name])
|
|
117
|
+
return !!toBuffer(key)
|
|
118
|
+
},
|
|
119
|
+
|
|
120
|
+
async decryptMessage({ jid, type, ciphertext }) {
|
|
121
|
+
const addr = jidToAddr(jid)
|
|
122
|
+
const cipher = new SessionCipher(storage, addr)
|
|
123
|
+
if (type === 'pkmsg') {
|
|
124
|
+
const identityKey = extractIdentityFromPkmsg(ciphertext)
|
|
125
|
+
if (identityKey) {
|
|
126
|
+
const changed = await storage.saveIdentity(addr.toString(), identityKey)
|
|
127
|
+
if (changed) logger?.info?.({ jid }, '[Signal] Identity key changed, session cleared')
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
try {
|
|
131
|
+
return await txn(() => {
|
|
132
|
+
if (type === 'pkmsg') return cipher.decryptPreKeyWhisperMessage(ciphertext)
|
|
133
|
+
if (type === 'msg') return cipher.decryptWhisperMessage(ciphertext)
|
|
134
|
+
throw new Error(`Unknown type: ${type}`)
|
|
135
|
+
}, jid)
|
|
136
|
+
} catch (e) {
|
|
137
|
+
if (e?.message?.includes('DuplicatedMessage')) { logger?.debug?.({ jid }, '[Signal] Duplicate message ignored'); return null }
|
|
138
|
+
throw e
|
|
139
|
+
}
|
|
140
|
+
},
|
|
141
|
+
|
|
142
|
+
encryptMessage({ jid, data }) {
|
|
143
|
+
return txn(async () => {
|
|
144
|
+
const { type: sigType, body } = await new SessionCipher(storage, jidToAddr(jid)).encrypt(data)
|
|
145
|
+
return { type: sigType === 3 ? 'pkmsg' : 'msg', ciphertext: Buffer.from(body) }
|
|
146
|
+
}, jid)
|
|
147
|
+
},
|
|
148
|
+
|
|
149
|
+
injectE2ESession({ jid, session }) {
|
|
150
|
+
return txn(() => new SessionBuilder(storage, jidToAddr(jid)).processPreKeyBundle(session), jid)
|
|
151
|
+
},
|
|
152
|
+
|
|
153
|
+
jidToSignalProtocolAddress: jid => jidToAddr(jid).toString(),
|
|
154
|
+
|
|
155
|
+
lidMapping,
|
|
156
|
+
|
|
157
|
+
async validateSession(jid) {
|
|
158
|
+
try {
|
|
159
|
+
const addr = jidToAddr(jid).toString()
|
|
160
|
+
const batch = await migrateIndexKey(parsedKeys, 'session')
|
|
161
|
+
const raw = toBuffer(batch[v2Key(addr)]) || toBuffer(batch[addr])
|
|
162
|
+
if (!raw || isOldJson(raw)) return { exists: false, reason: 'no session' }
|
|
163
|
+
const sess = SessionRecord.deserialize(raw)
|
|
164
|
+
if (!sess.haveOpenSession()) return { exists: false, reason: 'no open session' }
|
|
165
|
+
return { exists: true }
|
|
166
|
+
} catch { return { exists: false, reason: 'error' } }
|
|
167
|
+
},
|
|
168
|
+
|
|
169
|
+
async deleteSession(jids) {
|
|
170
|
+
if (!jids.length) return
|
|
171
|
+
return txn(async () => {
|
|
172
|
+
const batch = await migrateIndexKey(parsedKeys, 'session')
|
|
173
|
+
for (const jid of jids) { const addr = jidToAddr(jid).toString(); delete batch[addr]; delete batch[v2Key(addr)] }
|
|
174
|
+
await parsedKeys.set({ session: { index: batch } })
|
|
175
|
+
}, `del-${jids.length}`)
|
|
176
|
+
},
|
|
177
|
+
|
|
178
|
+
async migrateSession(fromJid, toJid) {
|
|
179
|
+
if (!fromJid || (!isLidUser(toJid) && !isHostedLidUser(toJid))) return { migrated: 0, skipped: 0, total: 0 }
|
|
180
|
+
if (!isPnUser(fromJid) && !isHostedPnUser(fromJid)) return { migrated: 0, skipped: 0, total: 1 }
|
|
181
|
+
const { user } = jidDecode(fromJid)
|
|
182
|
+
const deviceListBatch = await migrateIndexKey(parsedKeys, 'device-list')
|
|
183
|
+
const userDevices = deviceListBatch[user]
|
|
184
|
+
if (!userDevices?.length) return { migrated: 0, skipped: 0, total: 0 }
|
|
185
|
+
const fromDeviceStr = jidDecode(fromJid).device?.toString() || '0'
|
|
186
|
+
if (!userDevices.includes(fromDeviceStr)) userDevices.push(fromDeviceStr)
|
|
187
|
+
const uncachedDevices = userDevices.filter(d => !migratedCache.has(`${user}.${d}`))
|
|
188
|
+
const sessionBatch = await migrateIndexKey(parsedKeys, 'session')
|
|
189
|
+
const deviceJids = uncachedDevices.map(d => {
|
|
190
|
+
const num = parseInt(d)
|
|
191
|
+
return { addr: `${user}.${d || 0}`, jid: num === 99 ? `${user}:99@hosted` : num === 0 ? `${user}@s.whatsapp.net` : `${user}:${num}@s.whatsapp.net` }
|
|
192
|
+
}).filter(({ addr }) => sessionBatch[v2Key(addr)] || sessionBatch[addr])
|
|
193
|
+
return txn(async () => {
|
|
194
|
+
const updated = { ...sessionBatch }
|
|
195
|
+
let migrated = 0
|
|
196
|
+
for (const { jid } of deviceJids) {
|
|
197
|
+
const pnAddr = jidToAddr(jid).toString()
|
|
198
|
+
const lidAddr = jidToAddr(transferDevice(jid, toJid)).toString()
|
|
199
|
+
const raw = toBuffer(updated[v2Key(pnAddr)]) || toBuffer(updated[pnAddr])
|
|
200
|
+
if (!raw || isOldJson(raw)) continue
|
|
201
|
+
const sess = SessionRecord.deserialize(raw)
|
|
202
|
+
if (!sess.haveOpenSession()) continue
|
|
203
|
+
updated[v2Key(lidAddr)] = sess.serialize()
|
|
204
|
+
updated[lidAddr] = { version: 'v1', _sessions: {} }
|
|
205
|
+
delete updated[v2Key(pnAddr)]
|
|
206
|
+
delete updated[pnAddr]
|
|
207
|
+
migrated++
|
|
208
|
+
migratedCache.set(`${user}.${jidDecode(jid).device || 0}`, true)
|
|
209
|
+
}
|
|
210
|
+
if (migrated > 0) await parsedKeys.set({ session: { index: updated } })
|
|
211
|
+
return { migrated, skipped: deviceJids.length - migrated, total: deviceJids.length }
|
|
212
|
+
}, `migrate-${deviceJids.length}`)
|
|
213
|
+
},
|
|
214
|
+
|
|
215
|
+
async migrateAllPNSessionsToLID() {
|
|
216
|
+
return txn(async () => {
|
|
217
|
+
const sessionBatch = await migrateIndexKey(parsedKeys, 'session')
|
|
218
|
+
const sessionKeys = Object.keys(sessionBatch)
|
|
219
|
+
if (!sessionKeys.length) return 0
|
|
220
|
+
const pnAddrs = sessionKeys.filter(addr => {
|
|
221
|
+
if (addr.endsWith(':v2') || !addr.includes('.')) return false
|
|
222
|
+
const [, dt] = addr.split('.')[0].split('_')
|
|
223
|
+
const domainType = parseInt(dt || '0')
|
|
224
|
+
return domainType === WAJIDDomains.WHATSAPP || domainType === WAJIDDomains.HOSTED
|
|
225
|
+
})
|
|
226
|
+
if (!pnAddrs.length) return 0
|
|
227
|
+
const pnUserSet = new Set(pnAddrs.map(addr => addr.split('.')[0].split('_')[0]))
|
|
228
|
+
const stored = await parsedKeys.get('lid-mapping', [...pnUserSet])
|
|
229
|
+
const pnToLidUserMap = new Map()
|
|
230
|
+
for (const pnUser of pnUserSet) {
|
|
231
|
+
const lidUser = stored[pnUser]
|
|
232
|
+
if (lidUser && typeof lidUser === 'string') pnToLidUserMap.set(pnUser, lidUser)
|
|
233
|
+
}
|
|
234
|
+
if (!pnToLidUserMap.size) return 0
|
|
235
|
+
let migrated = 0
|
|
236
|
+
const updated = { ...sessionBatch }
|
|
237
|
+
for (const addr of pnAddrs) {
|
|
238
|
+
const [deviceId, device] = addr.split('.')
|
|
239
|
+
const [user, dt] = deviceId.split('_')
|
|
240
|
+
const domainType = parseInt(dt || '0')
|
|
241
|
+
const lidUser = pnToLidUserMap.get(user)
|
|
242
|
+
if (!lidUser) continue
|
|
243
|
+
const lidDomainType = domainType === WAJIDDomains.HOSTED ? WAJIDDomains.HOSTED_LID : WAJIDDomains.LID
|
|
244
|
+
const lidAddr = `${lidUser}_${lidDomainType}.${device}`
|
|
245
|
+
if (updated[v2Key(lidAddr)]) continue
|
|
246
|
+
const raw = toBuffer(updated[v2Key(addr)]) || toBuffer(updated[addr])
|
|
247
|
+
if (!raw || isOldJson(raw)) continue
|
|
248
|
+
const sess = SessionRecord.deserialize(raw)
|
|
249
|
+
if (!sess.haveOpenSession()) continue
|
|
250
|
+
updated[v2Key(lidAddr)] = sess.serialize()
|
|
251
|
+
updated[lidAddr] = { version: 'v1', _sessions: {} }
|
|
252
|
+
delete updated[v2Key(addr)]
|
|
253
|
+
delete updated[addr]
|
|
254
|
+
migrated++
|
|
255
|
+
migratedCache.set(`${user}.${device}`, true)
|
|
256
|
+
}
|
|
257
|
+
if (migrated > 0) {
|
|
258
|
+
await parsedKeys.set({ session: { index: updated } })
|
|
259
|
+
logger?.info?.({ migrated, totalPN: pnAddrs.length, mappingsFound: pnToLidUserMap.size }, '[Signal] Batch-migrated PN sessions to LID on connect')
|
|
260
|
+
}
|
|
261
|
+
return migrated
|
|
262
|
+
}, 'migrate-all-pn-to-lid')
|
|
263
|
+
},
|
|
264
|
+
|
|
265
|
+
deleteSenderKey(group, authorJid) {
|
|
266
|
+
const senderName = jidToSenderKeyName(group, authorJid).toString()
|
|
267
|
+
return parsedKeys.set({ 'sender-key': { [senderName]: null } })
|
|
268
|
+
},
|
|
269
|
+
|
|
270
|
+
close() {
|
|
271
|
+
migratedCache.clear()
|
|
272
|
+
lidMapping.close?.()
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// ─── Storage Adapter ──────────────────────────────────────────────────────────
|
|
278
|
+
|
|
279
|
+
function signalStorage({ creds, keys }, lidMapping, logger) {
|
|
280
|
+
const lidCache = new LRUCache({ max: 500, ttl: 5 * 60 * 1000 })
|
|
281
|
+
|
|
282
|
+
const resolveLID = async (id) => {
|
|
283
|
+
if (!id.includes('.')) return id
|
|
284
|
+
const cached = lidCache.get(id)
|
|
285
|
+
if (cached) return cached
|
|
286
|
+
const [deviceId, device] = id.split('.')
|
|
287
|
+
const [user, dt] = deviceId.split('_')
|
|
288
|
+
const domainType = parseInt(dt || '0')
|
|
289
|
+
if (domainType === WAJIDDomains.LID || domainType === WAJIDDomains.HOSTED_LID) return id
|
|
290
|
+
const pnJid = `${user}${device !== '0' ? `:${device}` : ''}@${domainType === WAJIDDomains.HOSTED ? 'hosted' : 's.whatsapp.net'}`
|
|
291
|
+
const lid = await lidMapping.getLIDForPN(pnJid)
|
|
292
|
+
const result = lid ? jidToAddr(lid).toString() : id
|
|
293
|
+
lidCache.set(id, result)
|
|
294
|
+
return result
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
const getIndex = () => migrateIndexKey(keys, 'session')
|
|
298
|
+
const setIndex = (batch) => keys.set({ session: { index: batch } })
|
|
299
|
+
|
|
300
|
+
return {
|
|
301
|
+
loadSession: async (id) => {
|
|
302
|
+
try {
|
|
303
|
+
const addr = await resolveLID(id)
|
|
304
|
+
const batch = await getIndex()
|
|
305
|
+
const v2 = batch[v2Key(addr)]
|
|
306
|
+
if (v2) {
|
|
307
|
+
if (isOldJson(v2)) { logger?.debug?.(`[Signal] Corrupt v2 for ${addr}, will fresh handshake`); return null }
|
|
308
|
+
const buf = toBuffer(v2)
|
|
309
|
+
if (buf) return buf
|
|
310
|
+
}
|
|
311
|
+
const plain = batch[addr]
|
|
312
|
+
if (!plain || isOldJson(plain)) {
|
|
313
|
+
if (plain) logger?.debug?.(`[Signal] Old JSON session for ${addr}, will fresh handshake`)
|
|
314
|
+
return null
|
|
315
|
+
}
|
|
316
|
+
return toBuffer(plain)
|
|
317
|
+
} catch (e) { logger?.error?.(`[Signal] loadSession error: ${e.message}`); return null }
|
|
318
|
+
},
|
|
319
|
+
|
|
320
|
+
storeSession: async (id, session) => {
|
|
321
|
+
const addr = await resolveLID(id)
|
|
322
|
+
const batch = await getIndex()
|
|
323
|
+
batch[v2Key(addr)] = session.serialize()
|
|
324
|
+
batch[addr] = { version: 'v1', _sessions: {} }
|
|
325
|
+
await setIndex(batch)
|
|
326
|
+
},
|
|
327
|
+
|
|
328
|
+
isTrustedIdentity: () => true,
|
|
329
|
+
|
|
330
|
+
loadIdentityKey: async (id) => {
|
|
331
|
+
const addr = await resolveLID(id)
|
|
332
|
+
const { [addr]: key } = await keys.get('identity-key', [addr])
|
|
333
|
+
const buf = toBuffer(key)
|
|
334
|
+
return buf ? new Uint8Array(buf) : undefined
|
|
335
|
+
},
|
|
336
|
+
|
|
337
|
+
saveIdentity: async (id, identityKey) => {
|
|
338
|
+
const addr = await resolveLID(id)
|
|
339
|
+
const { [addr]: raw } = await keys.get('identity-key', [addr])
|
|
340
|
+
const buf = toBuffer(raw)
|
|
341
|
+
const existing = buf ? new Uint8Array(buf) : null
|
|
342
|
+
const match = existing && existing.length === identityKey.length && existing.every((b, i) => b === identityKey[i])
|
|
343
|
+
if (existing && !match) {
|
|
344
|
+
await keys.set({ session: { [addr]: null }, 'identity-key': { [addr]: identityKey } })
|
|
345
|
+
return true
|
|
346
|
+
}
|
|
347
|
+
if (!existing) {
|
|
348
|
+
await keys.set({ 'identity-key': { [addr]: identityKey } })
|
|
349
|
+
return true
|
|
350
|
+
}
|
|
351
|
+
return false
|
|
352
|
+
},
|
|
353
|
+
|
|
354
|
+
loadPreKey: async (id) => {
|
|
355
|
+
const { [id.toString()]: key } = await keys.get('pre-key', [id.toString()])
|
|
356
|
+
if (!key) return null
|
|
357
|
+
return { pubKey: new Uint8Array(Buffer.from(key.public)), privKey: new Uint8Array(Buffer.from(key.private)) }
|
|
358
|
+
},
|
|
359
|
+
|
|
360
|
+
removePreKey: (id) => keys.set({ 'pre-key': { [id]: null } }),
|
|
361
|
+
|
|
362
|
+
loadSignedPreKey: () => {
|
|
363
|
+
const { signedPreKey: key } = creds
|
|
364
|
+
return {
|
|
365
|
+
keyId: key.keyId,
|
|
366
|
+
keyPair: { pubKey: new Uint8Array(Buffer.from(key.keyPair.public)), privKey: new Uint8Array(Buffer.from(key.keyPair.private)) },
|
|
367
|
+
signature: new Uint8Array(Buffer.from(key.signature))
|
|
368
|
+
}
|
|
369
|
+
},
|
|
370
|
+
|
|
371
|
+
loadSenderKey: async (keyId) => {
|
|
372
|
+
try {
|
|
373
|
+
const id = keyId.toString()
|
|
374
|
+
const { [id]: key } = await keys.get('sender-key', [id])
|
|
375
|
+
return toBuffer(key)
|
|
376
|
+
} catch (e) { logger?.error?.(`[Signal] loadSenderKey error: ${e.message}`); return null }
|
|
377
|
+
},
|
|
378
|
+
|
|
379
|
+
storeSenderKey: async (keyId, record) => {
|
|
380
|
+
const id = keyId.toString()
|
|
381
|
+
await keys.set({ 'sender-key': { [id]: Buffer.from(record) } })
|
|
382
|
+
},
|
|
383
|
+
|
|
384
|
+
getOurRegistrationId: () => creds.registrationId,
|
|
385
|
+
|
|
386
|
+
getOurIdentity: () => {
|
|
387
|
+
const { signedIdentityKey } = creds
|
|
388
|
+
return {
|
|
389
|
+
pubKey: new Uint8Array(generateSignalPubKey(Buffer.from(signedIdentityKey.public))),
|
|
390
|
+
privKey: new Uint8Array(Buffer.from(signedIdentityKey.private))
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
|
|
293
396
|
export default makeLibSignalRepository
|