@zervonex/baileys 3.0.7 → 3.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +44 -10
- package/lib/Utils/use-multi-file-auth-state.js +95 -50
- package/lib/index.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -375,13 +375,17 @@ import { rm } from 'fs/promises'
|
|
|
375
375
|
await rm('auth_session', { recursive: true, force: true })
|
|
376
376
|
```
|
|
377
377
|
|
|
378
|
+
---
|
|
379
|
+
|
|
378
380
|
### Keyv-based (production)
|
|
379
381
|
|
|
380
|
-
Universal adapter for any database backend via [Keyv](https://www.npmjs.com/package/keyv). Pass a connection string — the matching backend driver is resolved and cached automatically
|
|
382
|
+
Universal adapter for any database backend via [Keyv](https://www.npmjs.com/package/keyv). Pass a connection string — the matching backend driver is resolved, instantiated, and **cached automatically per session** — no manual adapter imports needed.
|
|
381
383
|
|
|
382
384
|
Both adapters use the same `BufferJSON` serialization format and auto-detect legacy `{ value: { value: ... } }` double-wrapping from older versions — no manual data migration needed when upgrading.
|
|
383
385
|
|
|
384
|
-
The first argument is the **session ID** — equivalent to the folder name in `useMultiFileAuthState`. It is used as a Keyv namespace so multiple bots can safely share one backend without key collisions. The second argument is a connection string, a pre-built `Keyv` instance, or a raw `KeyvStoreAdapter
|
|
386
|
+
The first argument is the **session ID** — equivalent to the folder name in `useMultiFileAuthState`. It is used as a Keyv namespace so multiple bots can safely share one backend without key collisions. The second argument is a connection string, a pre-built `Keyv` instance, or a raw `KeyvStoreAdapter`.
|
|
387
|
+
|
|
388
|
+
#### Basic usage
|
|
385
389
|
|
|
386
390
|
```javascript
|
|
387
391
|
import { useKeyvAuthState } from '@zervonex/baileys'
|
|
@@ -413,13 +417,25 @@ const { state, saveCreds } = await useKeyvAuthState('test')
|
|
|
413
417
|
|
|
414
418
|
Supported protocols: `redis:`/`rediss:`, `mongodb:`/`mongodb+srv:`, `postgres:`/`postgresql:`, `mysql:`, `sqlite:`, `etcd:`, `memcache:`/`memcached:`. All matching driver packages ship as dependencies — nothing extra to install.
|
|
415
419
|
|
|
416
|
-
|
|
420
|
+
#### How adapter caching works
|
|
421
|
+
|
|
422
|
+
Connection strings are cached **per `conn::collection::sessionId`** — each session gets its own isolated adapter instance so `clearSession()` and namespace operations never bleed across sessions. Underlying database drivers (MongoDB, pg, mysql2, ioredis) manage their own internal connection pools, so multiple adapter instances pointing at the same URI still share one real connection — no connection explosion across 50+ sessions.
|
|
423
|
+
|
|
424
|
+
```javascript
|
|
425
|
+
// these two calls resolve to separate adapter instances but share one MongoDB connection internally
|
|
426
|
+
const bot1 = await useKeyvAuthState('session_111', 'mongodb://localhost/baileys')
|
|
427
|
+
const bot2 = await useKeyvAuthState('session_222', 'mongodb://localhost/baileys')
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
#### Sharing a pre-built adapter
|
|
431
|
+
|
|
432
|
+
For backends without a connection string (e.g. DynamoDB), or when you need full control over the connection, pass a pre-built adapter or `Keyv` instance directly:
|
|
417
433
|
|
|
418
434
|
```javascript
|
|
419
435
|
import KeyvMongo from '@keyv/mongo'
|
|
420
436
|
import KeyvDynamo from '@keyv/dynamo'
|
|
421
437
|
|
|
422
|
-
// share one
|
|
438
|
+
// share one adapter instance across sessions
|
|
423
439
|
const mongo = new KeyvMongo('mongodb://localhost/baileys')
|
|
424
440
|
const { state: s1 } = await useKeyvAuthState('bot1', mongo)
|
|
425
441
|
const { state: s2 } = await useKeyvAuthState('bot2', mongo)
|
|
@@ -429,7 +445,7 @@ const dynamo = new KeyvDynamo({ region: 'us-east-1', table: 'baileys_auth' })
|
|
|
429
445
|
const { state, saveCreds } = await useKeyvAuthState('bot1', dynamo)
|
|
430
446
|
```
|
|
431
447
|
|
|
432
|
-
|
|
448
|
+
#### Custom collection/table name
|
|
433
449
|
|
|
434
450
|
By default, Mongo/Postgres/MySQL/SQLite store everything in a collection/table named `keyv`. Pass `collectionName` to use your own:
|
|
435
451
|
|
|
@@ -439,17 +455,35 @@ const { state, saveCreds } = await useKeyvAuthState('bot1', 'mongodb://localhost
|
|
|
439
455
|
})
|
|
440
456
|
```
|
|
441
457
|
|
|
442
|
-
|
|
458
|
+
#### Multi-backend routing
|
|
459
|
+
|
|
460
|
+
Route different key types to different backends — e.g. keep creds on Postgres and prekeys on Redis:
|
|
461
|
+
|
|
462
|
+
```javascript
|
|
463
|
+
const { state, saveCreds, clearSession } = await useKeyvAuthState('bot1', {
|
|
464
|
+
'creds': 'postgresql://user:pass@localhost/db',
|
|
465
|
+
'pre-key': 'redis://localhost:6379',
|
|
466
|
+
'session': 'redis://localhost:6379',
|
|
467
|
+
'app-state-sync-key': 'mongodb://localhost/baileys',
|
|
468
|
+
'app-state-sync-version': 'mongodb://localhost/baileys',
|
|
469
|
+
'sender-key': 'redis://localhost:6379',
|
|
470
|
+
'sender-key-memory': 'redis://localhost:6379',
|
|
471
|
+
})
|
|
472
|
+
```
|
|
473
|
+
|
|
474
|
+
If a key type isn't listed in the map, the **first entry's backend** is used as the default. Backends that appear more than once in the map are deduplicated — one adapter instance, one connection.
|
|
475
|
+
|
|
476
|
+
`clearSession()` on a multi-backend session clears all backends in the map, each scoped to that session's namespace only.
|
|
443
477
|
|
|
444
478
|
#### Clearing a session (logout)
|
|
445
479
|
|
|
446
480
|
```javascript
|
|
447
481
|
const { state, saveCreds, clearSession } = await useKeyvAuthState('bot1', 'redis://localhost:6379')
|
|
448
482
|
|
|
449
|
-
await clearSession() // wipes all keys for this session
|
|
483
|
+
await clearSession() // wipes all keys for this session only — other sessions untouched
|
|
450
484
|
```
|
|
451
485
|
|
|
452
|
-
`clearSession()` removes every key scoped to that session's namespace — creds, prekeys, sessions, sender keys, everything.
|
|
486
|
+
`clearSession()` removes every key scoped to that session's namespace — creds, prekeys, sessions, sender keys, app state, everything. Sessions sharing the same backend are never affected.
|
|
453
487
|
|
|
454
488
|
#### Checking auth without loading full state
|
|
455
489
|
|
|
@@ -460,7 +494,7 @@ const valid = await hasKeyvAuth('bot1', backend)
|
|
|
460
494
|
// true if creds exist and contain noiseKey + signedIdentityKey
|
|
461
495
|
```
|
|
462
496
|
|
|
463
|
-
Lightweight —
|
|
497
|
+
Lightweight — does not initialize full auth state, just checks for valid creds under the session's namespace. Accepts the same backend types as `useKeyvAuthState` including multi-backend maps (checks the first entry where creds live).
|
|
464
498
|
|
|
465
499
|
#### Options
|
|
466
500
|
|
|
@@ -468,7 +502,7 @@ Lightweight — opens no full auth state, just checks for valid creds under the
|
|
|
468
502
|
|--------|------|---------|-------------|
|
|
469
503
|
| `preKeyRetention` | `number` | `150` | Number of recent prekeys to keep |
|
|
470
504
|
| `cleanupThreshold` | `number` | `50` | New prekeys generated before cleanup runs |
|
|
471
|
-
| `collectionName` | `string` | `"
|
|
505
|
+
| `collectionName` | `string` | `"zervonex"` | Custom collection/table name — ignored for in-memory, pre-built instances, and backends without collection support (redis, etcd, memcache) |support (redis, etcd, memcache) |
|
|
472
506
|
|
|
473
507
|
#### Return values
|
|
474
508
|
|
|
@@ -10,6 +10,7 @@ import logger from './logger.js'
|
|
|
10
10
|
const DEFAULT_PREKEY_RETENTION = 150
|
|
11
11
|
const DEFAULT_CLEANUP_THRESHOLD = 50
|
|
12
12
|
const CLEANUP_INTERVAL_MS = 10 * 60 * 1000
|
|
13
|
+
const DEFAULT_COLLECTION_NAME = 'zervonex'
|
|
13
14
|
|
|
14
15
|
// ── Helpers ───────────────────────────────────────────────────────────────────
|
|
15
16
|
|
|
@@ -19,9 +20,11 @@ const ser = v => v == null ? null : JSON.parse(JSON.stringify(v, BufferJSON.repl
|
|
|
19
20
|
const deser = raw => {
|
|
20
21
|
if (raw == null) return null
|
|
21
22
|
let p = typeof raw === 'string'
|
|
22
|
-
? (() => { try { return JSON.parse(raw) } catch
|
|
23
|
+
? (() => { try { return JSON.parse(raw) } catch { return raw } })() // ← return raw string on parse fail, don't warn
|
|
23
24
|
: raw
|
|
24
|
-
|
|
25
|
+
// some adapters double-wrap in { value: { value: ... } } — unwrap up to 3 levels
|
|
26
|
+
let depth = 0
|
|
27
|
+
while (depth++ < 3 && p && typeof p === 'object' && !Array.isArray(p) && Object.keys(p).length === 1 && 'value' in p) p = p.value
|
|
25
28
|
if (p == null) return null
|
|
26
29
|
if (typeof p === 'object' && '__checksum' in p && 'data' in p) p = p.data
|
|
27
30
|
return JSON.parse(JSON.stringify(p), BufferJSON.reviver)
|
|
@@ -29,14 +32,17 @@ const deser = raw => {
|
|
|
29
32
|
|
|
30
33
|
// ── Adapter resolution ────────────────────────────────────────────────────────
|
|
31
34
|
|
|
32
|
-
// Only adapters
|
|
35
|
+
// Only these adapters accept a named collection/table — redis, valkey, etcd etc do not
|
|
33
36
|
const COLLECTION_KEY = { '@keyv/mongo': 'collection', '@keyv/postgres': 'table', '@keyv/mysql': 'table', '@keyv/sqlite': 'table' }
|
|
34
37
|
|
|
38
|
+
// Cached per conn::col::namespace — each session gets its own adapter instance so
|
|
39
|
+
// clear() never bleeds across sessions. Underlying drivers manage their own connection
|
|
40
|
+
// pools so multiple adapters on the same URI still share one real connection.
|
|
35
41
|
const _adapterCache = new Map()
|
|
36
42
|
export const clearAdapterCache = () => _adapterCache.clear()
|
|
37
43
|
|
|
38
|
-
const resolveAdapter = async (conn, col) => {
|
|
39
|
-
const ck =
|
|
44
|
+
const resolveAdapter = async (conn, col, namespace) => {
|
|
45
|
+
const ck = `${conn}::${col ?? ''}::${namespace ?? ''}`
|
|
40
46
|
if (_adapterCache.has(ck)) return _adapterCache.get(ck)
|
|
41
47
|
let protocol
|
|
42
48
|
try { protocol = new URL(conn).protocol } catch { throw new Error(`[Auth] Invalid connection string: "${conn}"`) }
|
|
@@ -51,23 +57,21 @@ const resolveAdapter = async (conn, col) => {
|
|
|
51
57
|
const optKey = COLLECTION_KEY[pkg]
|
|
52
58
|
const adapter = col && optKey ? new Adapter({ url: conn, uri: conn, [optKey]: col }) : new Adapter(conn)
|
|
53
59
|
adapter.setMaxListeners?.(0)
|
|
60
|
+
if (namespace) adapter.namespace = namespace
|
|
54
61
|
_adapterCache.set(ck, adapter)
|
|
55
62
|
return adapter
|
|
56
63
|
}
|
|
57
64
|
|
|
58
|
-
// Object.create scoping only needed for mapLike stores — keyvStorage and asyncMap have namespace managed by Keyv/bridge
|
|
59
65
|
const resolveStore = async (backend, col, namespace) => {
|
|
60
66
|
if (backend == null) return null
|
|
61
|
-
if (typeof backend === 'string') return resolveAdapter(backend, col)
|
|
67
|
+
if (typeof backend === 'string') return resolveAdapter(backend, col, namespace)
|
|
62
68
|
if (backend instanceof Keyv) return backend.opts?.store ?? backend.store ?? null
|
|
63
|
-
const { compatible
|
|
69
|
+
const { compatible } = detectKeyvStorage(backend)
|
|
64
70
|
if (!compatible) throw new Error('[Auth] backend must be a connection string, Keyv instance, or KeyvStoreAdapter')
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
}
|
|
70
|
-
return backend
|
|
71
|
+
// scope namespace via prototype so we don't mutate the original store instance
|
|
72
|
+
const scoped = Object.create(backend)
|
|
73
|
+
if (namespace) scoped.namespace = namespace
|
|
74
|
+
return scoped
|
|
71
75
|
}
|
|
72
76
|
|
|
73
77
|
const makeKeyv = (store, namespace) => {
|
|
@@ -76,6 +80,40 @@ const makeKeyv = (store, namespace) => {
|
|
|
76
80
|
return keyv
|
|
77
81
|
}
|
|
78
82
|
|
|
83
|
+
// ── Multi-backend support ─────────────────────────────────────────────────────
|
|
84
|
+
|
|
85
|
+
// Multi-backend map: { 'creds': pgBackend, 'pre-key': mongoBackend, ... }
|
|
86
|
+
// Anything else (string, Keyv, raw store adapter) is treated as single backend
|
|
87
|
+
const isMultiBackend = backend =>
|
|
88
|
+
backend !== null
|
|
89
|
+
&& typeof backend === 'object'
|
|
90
|
+
&& !Array.isArray(backend)
|
|
91
|
+
&& !(backend instanceof Keyv)
|
|
92
|
+
&& !detectKeyvStorage(backend).compatible
|
|
93
|
+
|
|
94
|
+
// Returns { get(type) → Keyv, all → Keyv[] }
|
|
95
|
+
// get(type) falls back to first entry if type not in map
|
|
96
|
+
// all is deduplicated — shared backends only clear once
|
|
97
|
+
const resolveBackendMap = async (backend, sessionId, collectionName) => {
|
|
98
|
+
if (!isMultiBackend(backend)) {
|
|
99
|
+
const store = await resolveStore(backend, collectionName, sessionId)
|
|
100
|
+
const keyv = makeKeyv(store, sessionId)
|
|
101
|
+
return { get: () => keyv, all: [keyv] }
|
|
102
|
+
}
|
|
103
|
+
const entries = Object.entries(backend)
|
|
104
|
+
if (!entries.length) throw new Error('[Auth] backend map must have at least one entry')
|
|
105
|
+
const resolved = new Map()
|
|
106
|
+
for (const [, b] of entries) {
|
|
107
|
+
if (!resolved.has(b)) {
|
|
108
|
+
const store = await resolveStore(b, collectionName, sessionId)
|
|
109
|
+
resolved.set(b, makeKeyv(store, sessionId))
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
const typeMap = Object.fromEntries(entries.map(([type, b]) => [type, resolved.get(b)]))
|
|
113
|
+
const defaultKeyv = resolved.get(entries[0][1])
|
|
114
|
+
return { get: type => typeMap[type] ?? defaultKeyv, all: [...new Set(resolved.values())] }
|
|
115
|
+
}
|
|
116
|
+
|
|
79
117
|
// ── File-based auth state ─────────────────────────────────────────────────────
|
|
80
118
|
|
|
81
119
|
export const useMultiFileAuthState = async (folder, opts = {}) => {
|
|
@@ -87,7 +125,6 @@ export const useMultiFileAuthState = async (folder, opts = {}) => {
|
|
|
87
125
|
if (info) { if (!info.isDirectory()) throw new Error(`Not a directory: ${folder}`) }
|
|
88
126
|
else await mkdir(folder, { recursive: true })
|
|
89
127
|
|
|
90
|
-
// Atomic write: tmp → rename, fallback to direct write
|
|
91
128
|
const write = async (data, file) => {
|
|
92
129
|
const [p, t, payload] = [fp(file), fp(file) + '.tmp', JSON.stringify(data, BufferJSON.replacer)]
|
|
93
130
|
await writeFile(t, payload)
|
|
@@ -108,22 +145,24 @@ export const useMultiFileAuthState = async (folder, opts = {}) => {
|
|
|
108
145
|
const remove = f => unlink(fp(f)).catch(() => { })
|
|
109
146
|
|
|
110
147
|
let creds = bootstrapCreds(await read('creds.json'))
|
|
111
|
-
let
|
|
148
|
+
let cleanupPromise = null, lastCleanupAt = 0, lastCleanedId = creds.nextPreKeyId
|
|
112
149
|
|
|
113
150
|
const cleanOldPreKeys = async () => {
|
|
114
|
-
if (
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
151
|
+
if (cleanupPromise || Date.now() - lastCleanupAt < CLEANUP_INTERVAL_MS) return
|
|
152
|
+
cleanupPromise = (async () => {
|
|
153
|
+
try {
|
|
154
|
+
const minId = creds.nextPreKeyId - preKeyRetention
|
|
155
|
+
if (minId <= 0) return
|
|
156
|
+
const targets = (await readdir(folder))
|
|
157
|
+
.map(f => f.match(/^pre-key-(\d+)\.json(\.tmp)?$/))
|
|
158
|
+
.filter(m => m && parseInt(m[1], 10) < minId)
|
|
159
|
+
.map(m => join(folder, m[0]))
|
|
160
|
+
if (targets.length) await Promise.all(targets.map(f => unlink(f).catch(() => { })))
|
|
161
|
+
lastCleanupAt = Date.now(); lastCleanedId = creds.nextPreKeyId
|
|
162
|
+
} catch (e) { logger.warn({ err: e.message }, 'prekey cleanup failed') }
|
|
163
|
+
finally { cleanupPromise = null }
|
|
164
|
+
})()
|
|
165
|
+
await cleanupPromise
|
|
127
166
|
}
|
|
128
167
|
cleanOldPreKeys().catch(() => { })
|
|
129
168
|
|
|
@@ -158,14 +197,14 @@ export const useMultiFileAuthState = async (folder, opts = {}) => {
|
|
|
158
197
|
// ── Keyv-backed auth state ────────────────────────────────────────────────────
|
|
159
198
|
|
|
160
199
|
export const useKeyvAuthState = async (sessionId, backend, opts = {}) => {
|
|
161
|
-
const { preKeyRetention = DEFAULT_PREKEY_RETENTION, cleanupThreshold = DEFAULT_CLEANUP_THRESHOLD, collectionName } = opts
|
|
162
|
-
const
|
|
163
|
-
const
|
|
164
|
-
|
|
200
|
+
const { preKeyRetention = DEFAULT_PREKEY_RETENTION, cleanupThreshold = DEFAULT_CLEANUP_THRESHOLD, collectionName = DEFAULT_COLLECTION_NAME } = opts
|
|
201
|
+
const bmap = await resolveBackendMap(backend, sessionId, collectionName)
|
|
202
|
+
const credsKeyv = bmap.get('creds')
|
|
203
|
+
credsKeyv.on('error', err => logger.warn({ err: err?.message, sessionId }, '[Auth] store error'))
|
|
165
204
|
|
|
166
205
|
const kn = (type, id) => `${type}-${id}`
|
|
167
206
|
const credsKey = 'creds'
|
|
168
|
-
let creds = bootstrapCreds(deser(await
|
|
207
|
+
let creds = bootstrapCreds(deser(await credsKeyv.get(credsKey)))
|
|
169
208
|
let cleanupRunning = false, lastCleanupAt = 0, lastCleanedId = creds.nextPreKeyId
|
|
170
209
|
|
|
171
210
|
const cleanOldPreKeys = async () => {
|
|
@@ -176,7 +215,7 @@ export const useKeyvAuthState = async (sessionId, backend, opts = {}) => {
|
|
|
176
215
|
if (minId <= 0) return
|
|
177
216
|
const ids = []
|
|
178
217
|
for (let id = Math.max(0, lastCleanedId - preKeyRetention); id < minId; id++) ids.push(kn('pre-key', id))
|
|
179
|
-
if (ids.length) await
|
|
218
|
+
if (ids.length) await bmap.get('pre-key').deleteMany(ids)
|
|
180
219
|
lastCleanupAt = Date.now(); lastCleanedId = creds.nextPreKeyId
|
|
181
220
|
} catch (e) { logger.warn({ err: e.message, sessionId }, '[Auth] prekey cleanup failed') }
|
|
182
221
|
finally { cleanupRunning = false }
|
|
@@ -186,43 +225,49 @@ export const useKeyvAuthState = async (sessionId, backend, opts = {}) => {
|
|
|
186
225
|
const keys = {
|
|
187
226
|
get: async (type, ids) => {
|
|
188
227
|
if (!ids.length) return {}
|
|
228
|
+
const keyv = bmap.get(type)
|
|
189
229
|
const raws = await keyv.getMany(ids.map(id => kn(type, id)))
|
|
190
230
|
return Object.fromEntries(ids.map((id, i) => [id, patchAppStateKey(type, deser(raws[i]))]))
|
|
191
231
|
},
|
|
192
232
|
set: async data => {
|
|
193
|
-
|
|
194
|
-
|
|
233
|
+
// group by Keyv instance to batch writes per backend
|
|
234
|
+
const byKeyv = new Map()
|
|
235
|
+
for (const type in data) {
|
|
236
|
+
const keyv = bmap.get(type)
|
|
237
|
+
if (!byKeyv.has(keyv)) byKeyv.set(keyv, { sets: [], dels: [] })
|
|
238
|
+
const bucket = byKeyv.get(keyv)
|
|
195
239
|
for (const id in data[type]) {
|
|
196
240
|
const v = data[type][id], k = kn(type, id)
|
|
197
|
-
if (v != null) sets.push({ key: k, value: ser(v) })
|
|
198
|
-
else dels.push(k)
|
|
241
|
+
if (v != null) bucket.sets.push({ key: k, value: ser(v) })
|
|
242
|
+
else bucket.dels.push(k)
|
|
199
243
|
}
|
|
200
|
-
|
|
244
|
+
}
|
|
245
|
+
await Promise.all([...byKeyv.entries()].map(([keyv, { sets, dels }]) =>
|
|
246
|
+
Promise.all([sets.length ? keyv.setMany(sets) : null, dels.length ? keyv.deleteMany(dels) : null])
|
|
247
|
+
))
|
|
201
248
|
}
|
|
202
249
|
}
|
|
203
250
|
|
|
204
251
|
const saveCreds = async () => {
|
|
205
252
|
if (creds.nextPreKeyId - lastCleanedId >= cleanupThreshold) cleanOldPreKeys().catch(() => { })
|
|
206
|
-
await
|
|
253
|
+
await credsKeyv.set(credsKey, ser(creds))
|
|
207
254
|
}
|
|
208
255
|
|
|
209
256
|
const clearSession = async () => {
|
|
210
|
-
try {
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
isolated.removeAllListeners?.()
|
|
214
|
-
} catch (e) { logger.warn({ err: e.message, sessionId }, '[Auth] clearSession failed') }
|
|
215
|
-
keyv.removeAllListeners?.()
|
|
257
|
+
try { await Promise.all(bmap.all.map(keyv => keyv.clear())) }
|
|
258
|
+
catch (e) { logger.warn({ err: e.message, sessionId }, '[Auth] clearSession failed') }
|
|
259
|
+
bmap.all.forEach(keyv => keyv.removeAllListeners?.())
|
|
216
260
|
}
|
|
217
261
|
|
|
218
262
|
return { state: { creds, keys }, saveCreds, clearSession }
|
|
219
263
|
}
|
|
220
264
|
|
|
221
|
-
//
|
|
265
|
+
// Lightweight check — reads only the creds key without full state init
|
|
222
266
|
export const hasKeyvAuth = async (sessionId, backend, opts = {}) => {
|
|
223
267
|
try {
|
|
224
|
-
const
|
|
225
|
-
const
|
|
268
|
+
const b = isMultiBackend(backend) ? Object.values(backend)[0] : backend
|
|
269
|
+
const bmap = await resolveBackendMap(b, sessionId, opts.collectionName ?? DEFAULT_COLLECTION_NAME)
|
|
270
|
+
const parsed = deser(await bmap.get('creds').get('creds'))
|
|
226
271
|
return !!(parsed?.noiseKey && parsed?.signedIdentityKey)
|
|
227
272
|
} catch { return false }
|
|
228
273
|
}
|
package/lib/index.js
CHANGED
|
@@ -29,7 +29,7 @@ const info = [
|
|
|
29
29
|
'╭──────────────────────────────────────────────────────────────╮',
|
|
30
30
|
'│ │',
|
|
31
31
|
'│ ◆ Package › @zervonex/baileys │',
|
|
32
|
-
'│ ◆ Version › 3.0.
|
|
32
|
+
'│ ◆ Version › 3.0.8 │',
|
|
33
33
|
'│ ◆ Status › Production Ready │',
|
|
34
34
|
'│ │',
|
|
35
35
|
'├──────────────────────────── ◇◆◇ ─────────────────────────────┤',
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"$schema": "https://json.schemastore.org/package.json",
|
|
3
3
|
"name": "@zervonex/baileys",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"version": "3.0.
|
|
5
|
+
"version": "3.0.8",
|
|
6
6
|
"description": "Advanced WhatsApp Web API built on Baileys — interactive messages, buttons, carousels, products, events, payments, polls, albums, sticker packs and more.",
|
|
7
7
|
"keywords": [
|
|
8
8
|
"baileys",
|