@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 +33 -3
- package/WAProto/WAProtoCompile.js +1 -0
- package/WAProto/fetcher.js +17 -33
- package/WAProto/index.js +34 -26
- package/WAProto/parser.js +333 -312
- package/lib/Builders/AIRich.js +14 -14
- package/lib/Builders/base.js +3 -3
- package/lib/Builders/builders.js +460 -448
- package/lib/Defaults/index.js +2 -3
- package/lib/Socket/messages-recv.js +1 -1
- package/lib/Socket/messages-send.js +1 -1
- package/lib/Socket/newsletter.js +9 -7
- package/lib/Socket/socket.js +4 -3
- package/lib/Socket/zervonex-handler.js +1 -1
- package/lib/Utils/browser-utils.js +8 -4
- package/lib/Utils/chat-utils.js +1 -1
- package/lib/Utils/logger.js +2 -1
- package/lib/Utils/messages.js +91 -40
- package/lib/Utils/use-multi-file-auth-state.js +155 -207
- package/lib/Utils/validate-connection.js +19 -28
- package/lib/WABinary/generic-utils.js +43 -19
- package/lib/index.js +1 -1
- package/package.json +62 -97
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
|
|
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 —
|
|
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('.'))
|
package/WAProto/fetcher.js
CHANGED
|
@@ -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
|
-
|
|
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]
|
|
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
|
-
|
|
43
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
17
|
-
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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'
|