@zervonex/baileys 3.0.3 → 3.0.5

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 CHANGED
@@ -73,6 +73,7 @@
73
73
  - [AI Rich Messages](#ai-rich-messages)
74
74
  - [Status & Stories](#personal-status--story-statusbroadcast)
75
75
  - [Sticker Pack](#sticker-pack)
76
+ - [Premium Stickers](#premium-stickers)
76
77
  - [Shorthand Wrappers](#shorthand-wrappers)
77
78
  - [Message Modifications](#️-message-modifications)
78
79
  - [Media Download](#-media-download)
@@ -343,7 +344,6 @@ autoFollowNewsletter: false, // default: true
343
344
  })
344
345
  ```
345
346
  ---
346
-
347
347
  ## 💾 Session Management
348
348
 
349
349
  Two adapters are available. They do **not** share an identical return shape — see the differences noted below each.
@@ -377,7 +377,7 @@ Universal adapter for any database backend via [Keyv](https://github.com/jaredwr
377
377
 
378
378
  Storage format matches `useMultiFileAuthState` exactly: every value is wrapped as `{ data, __checksum }` with a sha256 integrity check, so corruption is detected and self-healed the same way on both adapters. Reads also auto-detect and repair values that were written by an older/buggy version of this adapter (including arbitrarily nested `{ value: { value: ... } }` wrapping) — no manual data-fixing needed if you're upgrading from an earlier release.
379
379
 
380
- The first argument is the **session ID** — equivalent to the folder name in `useMultiFileAuthState`. It's used as the Keyv namespace, so multiple bots can safely share one backend. The second argument is a connection string, a pre-built `Keyv` instance, or a raw `KeyvStoreAdapter`:
380
+ The first argument is the **session ID** — equivalent to the folder name in `useMultiFileAuthState`. It's used as a key prefix (`sessionId::key`), so multiple bots can safely share one backend. The second argument is a connection string, a pre-built `Keyv` instance, or a raw `KeyvStoreAdapter`:
381
381
 
382
382
  ```javascript
383
383
  import { useKeyvAuthState } from '@zervonex/baileys'
@@ -440,7 +440,19 @@ const { state, saveCreds, clearSession } = await useKeyvAuthState('bot1', 'redis
440
440
  await clearSession()
441
441
  ```
442
442
 
443
- `clearSession()` wipes every key belonging to that `sessionId` — creds, prekeys, sessions, sender keys, everything — using each backend's native namespace-scoped bulk delete (a real `deleteMany`/`DELETE WHERE`/`SCAN`+delete under the hood, not a per-key loop). It only affects this session; other sessions sharing the same backend are untouched. There's no equivalent method on `useMultiFileAuthState` — delete the session folder instead.
443
+ `clearSession()` wipes every key belonging to that `sessionId` — creds, prekeys, sessions, sender keys, everything — by iterating the store filtering by the `sessionId::` prefix and calling `deleteMany`. Only this session's keys are removed; other sessions sharing the same backend are untouched. There is no equivalent method on `useMultiFileAuthState` — delete the session folder instead.
444
+
445
+ #### Checking auth without loading full state
446
+
447
+ ```javascript
448
+ import { hasKeyvAuth } from '@zervonex/baileys'
449
+
450
+ // Check if a session has valid auth (lightweight — no full state load)
451
+ const valid = await hasKeyvAuth('bot1', backend)
452
+ // true if creds exist and contain noiseKey + signedIdentityKey
453
+ ```
454
+
455
+ `hasKeyvAuth()` queries the backend using the correct key prefix — the same way `useKeyvAuthState` does internally — so it always returns the right answer regardless of your backend. Useful for checking session ownership before deciding whether to restore a connection, without the overhead of initializing a full auth state.
444
456
 
445
457
  #### Options
446
458
 
@@ -1363,6 +1375,23 @@ await sock.stickerPackMessage(jid, {
1363
1375
  }, { quoted: message })
1364
1376
  ```
1365
1377
 
1378
+ ### Premium Stickers
1379
+
1380
+ Pass `premium` on a single sticker:
1381
+ ```js
1382
+ await sock.sendMessage(jid, { sticker: buffer, premium: 1 })
1383
+ ```
1384
+
1385
+ Per-sticker in a pack:
1386
+ ```js
1387
+ await sock.sendMessage(jid, { stickerPack: { stickers: [{ data: buffer, emojis: ['🔥'], premium: 1 }] } })
1388
+ ```
1389
+
1390
+ Whole pack as premium:
1391
+ ```js
1392
+ await sock.sendMessage(jid, { stickerPack: { premium: 1, stickers: [...] } })
1393
+ ```
1394
+
1366
1395
  **Per-sticker options:**
1367
1396
 
1368
1397
  | Field | Type | Default | Description |
@@ -2961,6 +2990,7 @@ import {
2961
2990
  makeWASocket,
2962
2991
  useMultiFileAuthState,
2963
2992
  useKeyvAuthState,
2993
+ hasKeyvAuth,
2964
2994
  makeCacheableSignalKeyStore,
2965
2995
  Browsers,
2966
2996
  DisconnectReason,
@@ -255,6 +255,7 @@ function makeProto(tablePath) {
255
255
  node.toObject = o => o ?? {}
256
256
  node.verify = () => null
257
257
  node.name = full.split('.').pop()
258
+ node.fields = Object.fromEntries(TABLE[full].order.map(f => [f.name, { type: f.s ?? f.k, id: f.id, repeated: f.rep ?? false }]))
258
259
  }
259
260
  for (const [full, values] of Object.entries(e)) {
260
261
  const node = nodeFor(full.replace(/^proto\./, '').split('.'))
@@ -1,69 +1,53 @@
1
1
  import { fetch, Agent } from 'undici'
2
2
  import { createHash } from 'crypto'
3
- import { readFileSync, writeFileSync, existsSync } from 'fs'
3
+ import { readFileSync, writeFileSync, existsSync, createWriteStream, unlinkSync } from 'fs'
4
+ import { pipeline } from 'stream/promises'
4
5
  import { join, dirname } from 'path'
5
6
  import { fileURLToPath } from 'url'
6
7
 
7
8
  const __dirname = dirname(fileURLToPath(import.meta.url))
8
9
  const CACHE_FILE = join(__dirname, '.cache.json')
9
10
  const PROTO_FILE = join(__dirname, 'WAProto.proto')
10
-
11
+ const BUNDLE_TMP = join(__dirname, 'WAProto.bundle.tmp')
11
12
  const GITHUB_RAW = 'https://raw.githubusercontent.com/nexustechpro2/baileys/wa-cache'
12
13
  const CACHE_TTL = 24 * 60 * 60 * 1000
13
-
14
- const agent = new Agent({ connect: { family: 4 } })
14
+ const agent = new Agent({ connect: { family: 4, timeout: 60000 } })
15
15
 
16
16
  const isCacheValid = (cache) => cache.version && cache.timestamp && (Date.now() - cache.timestamp) < CACHE_TTL
17
17
  const readCache = () => { try { return existsSync(CACHE_FILE) ? JSON.parse(readFileSync(CACHE_FILE, 'utf8')) : {} } catch { return {} } }
18
18
  const writeCache = (data) => writeFileSync(CACHE_FILE, JSON.stringify({ ...data, timestamp: Date.now() }, null, 2), 'utf8')
19
+ const removeTmp = () => { try { if (existsSync(BUNDLE_TMP)) unlinkSync(BUNDLE_TMP) } catch { } }
19
20
 
20
21
  async function fetchFromGitHub(file) {
21
22
  const res = await fetch(`${GITHUB_RAW}/${file}`, { method: 'GET', dispatcher: agent })
22
23
  if (!res.ok) throw new Error(`[WAProto] GitHub fetch failed: ${res.status} for ${file}`)
23
- return file.endsWith('.json') ? res.json() : res.text()
24
+ if (file.endsWith('.json')) return res.json()
25
+ const hash = createHash('sha256')
26
+ const writer = createWriteStream(BUNDLE_TMP)
27
+ await pipeline(res.body, async function* (source) { for await (const chunk of source) { hash.update(chunk); yield chunk } }, writer)
28
+ return hash.digest('hex')
24
29
  }
25
30
 
26
31
  async function fetchClientRevision() {
27
32
  const data = await fetchFromGitHub('wa-version.json')
28
- if (!data?.version) throw new Error('[WAProto] GitHub wa-version.json missing version field')
33
+ if (!data?.version) throw new Error('[WAProto] wa-version.json missing version field')
29
34
  return String(data.version)
30
35
  }
31
36
 
32
- async function fetchCombinedBundle() {
33
- return fetchFromGitHub('WAProto.bundle.js')
34
- }
35
-
36
37
  export async function fetchProtoBundle() {
37
38
  const cache = readCache()
38
39
  if (isCacheValid(cache) && existsSync(PROTO_FILE)) return { changed: false, bundle: null, version: cache.version }
39
-
40
40
  const version = await fetchClientRevision()
41
-
42
- if (cache.version === version && existsSync(PROTO_FILE)) {
43
- writeCache({ ...cache, version })
44
- return { changed: false, bundle: null, version }
45
- }
46
-
47
- let bundle = await fetchCombinedBundle()
48
- const hash = createHash('sha256').update(bundle).digest('hex')
49
-
50
- if (cache.hash === hash) {
51
- writeCache({ version, hash })
52
- return { changed: false, bundle: null, version }
53
- }
54
-
41
+ if (cache.version === version && existsSync(PROTO_FILE)) { writeCache({ ...cache, version }); return { changed: false, bundle: null, version } }
42
+ const hash = await fetchFromGitHub('WAProto.bundle.js')
43
+ if (cache.hash === hash) { writeCache({ version, hash }); removeTmp(); return { changed: false, bundle: null, version } }
55
44
  writeCache({ version, hash })
56
- const result = { changed: true, bundle, version }
57
- bundle = null
58
- return result
45
+ return { changed: true, bundlePath: BUNDLE_TMP, version }
59
46
  }
60
47
 
61
48
  export async function getWAVersion() {
62
49
  const cache = readCache()
63
50
  if (isCacheValid(cache) && cache.version) return [2, 3000, parseInt(cache.version)]
64
-
65
- const data = await fetchFromGitHub('wa-version.json')
66
- if (!data?.version) throw new Error('[WAProto] GitHub wa-version.json missing version field')
67
-
68
- return [2, 3000, parseInt(data.version)]
51
+ const version = await fetchClientRevision()
52
+ return [2, 3000, parseInt(version)]
69
53
  }
package/WAProto/index.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { existsSync, unlinkSync, readFileSync } from 'fs'
2
2
  import { join, dirname } from 'path'
3
3
  import { fileURLToPath } from 'url'
4
+ import { Worker, isMainThread, workerData, parentPort } from 'worker_threads'
4
5
  import logger from '../lib/Utils/logger.js'
5
6
  import { fetchProtoBundle } from './fetcher.js'
6
7
  import { generateTable, _reloadProto } from './WAProtoCompile.js'
@@ -9,37 +10,33 @@ import { parseBundle } from './parser.js'
9
10
  const __dirname = dirname(fileURLToPath(import.meta.url))
10
11
  const PROTO_FILE = join(__dirname, 'WAProto.proto')
11
12
  const TABLE_FILE = join(__dirname, 'WAProto.json')
13
+ const SELF = new URL(import.meta.url)
12
14
 
13
15
  const log = msg => logger.info(msg)
14
16
  const err = msg => logger.error(msg)
17
+ const del = (...files) => { for (const f of files) { try { if (existsSync(f)) unlinkSync(f) } catch { } } }
18
+ const isJsonValid = () => { try { const { m, t } = JSON.parse(readFileSync(TABLE_FILE, 'utf8')); return Array.isArray(m) && m.length > 0 && typeof t === 'object' && Object.keys(t).length > 0 } catch { return false } }
15
19
 
16
- const isJsonValid = () => {
17
- try {
18
- const { m, t } = JSON.parse(readFileSync(TABLE_FILE, 'utf8'))
19
- return Array.isArray(m) && m.length > 0 && typeof t === 'object' && Object.keys(t).length > 0
20
- } catch { return false }
20
+ function runInWorker(data) {
21
+ return new Promise((resolve, reject) => {
22
+ const w = new Worker(SELF, { workerData: data })
23
+ w.once('message', msg => { w.terminate(); msg.ok ? resolve() : reject(new Error(msg.error)) })
24
+ w.once('error', reject)
25
+ })
21
26
  }
22
27
 
23
- const del = (...files) => { for (const f of files) { try { if (existsSync(f)) unlinkSync(f) } catch { } } }
24
-
25
28
  async function regenerateFromProto() {
26
29
  log('Regenerating WAProto.json from existing WAProto.proto...')
27
- await generateTable(PROTO_FILE, TABLE_FILE)
30
+ await runInWorker({ bundlePath: null, version: null, protoPath: PROTO_FILE, tablePath: TABLE_FILE })
28
31
  _reloadProto()
29
32
  log('Regenerated successfully')
30
33
  }
31
34
 
32
35
  async function refetch() {
33
36
  log('Fetching proto bundle from GitHub...')
34
- const { bundle, version } = await fetchProtoBundle()
35
- if (!bundle) throw new Error('fetchProtoBundle returned no bundle')
36
- const protoText = parseBundle(bundle, version)
37
- const { writeFileSync } = await import('fs')
38
- const tmp = PROTO_FILE + '.tmp'
39
- writeFileSync(tmp, protoText, 'utf8')
40
- const { renameSync } = await import('fs')
41
- renameSync(tmp, PROTO_FILE)
42
- await generateTable(PROTO_FILE, TABLE_FILE)
37
+ const { bundle, bundlePath, version } = await fetchProtoBundle()
38
+ if (!bundle && !bundlePath) throw new Error('fetchProtoBundle returned no bundle')
39
+ await runInWorker({ bundlePath: bundlePath ?? null, bundle: bundle ?? null, version, protoPath: PROTO_FILE, tablePath: TABLE_FILE })
43
40
  _reloadProto()
44
41
  log(`Fetched and compiled — WA version ${version}`)
45
42
  }
@@ -59,7 +56,6 @@ async function boot() {
59
56
  }
60
57
  return
61
58
  }
62
-
63
59
  if (hasProto && !hasJson) {
64
60
  log('WAProto.json missing — generating from existing proto...')
65
61
  try { await regenerateFromProto(); return } catch (e) {
@@ -69,19 +65,31 @@ async function boot() {
69
65
  }
70
66
  return
71
67
  }
72
-
73
- if (hasJson && !hasProto) {
74
- err('WAProto.proto missing but WAProto.json exists — cannot trust JSON, refetching...')
75
- del(TABLE_FILE)
76
- await refetch()
77
- return
78
- }
68
+ if (hasJson && !hasProto) { err('WAProto.proto missing but WAProto.json exists — cannot trust JSON, refetching...'); del(TABLE_FILE); await refetch(); return }
79
69
 
80
70
  log('No proto files found — fetching from GitHub...')
81
71
  await refetch()
82
72
  }
83
73
 
84
- await boot()
74
+ if (!isMainThread) {
75
+ const { bundlePath, bundle, version, protoPath, tablePath } = workerData
76
+ try {
77
+ if (bundlePath || bundle) {
78
+ const src = bundlePath ? readFileSync(bundlePath, 'utf8') : bundle
79
+ if (bundlePath) unlinkSync(bundlePath)
80
+ const { writeFileSync, renameSync } = await import('fs')
81
+ const tmp = protoPath + '.tmp'
82
+ writeFileSync(tmp, parseBundle(src, version), 'utf8')
83
+ renameSync(tmp, protoPath)
84
+ }
85
+ await generateTable(protoPath, tablePath)
86
+ parentPort.postMessage({ ok: true })
87
+ } catch (e) {
88
+ parentPort.postMessage({ ok: false, error: e.message })
89
+ }
90
+ } else {
91
+ await boot()
92
+ }
85
93
 
86
94
  export { proto, codec, generateTable, _reloadProto } from './WAProtoCompile.js'
87
95
  export { getWAVersion, fetchProtoBundle } from './fetcher.js'