@nexustechpro/baileys 2.0.6 → 2.1.2
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 +72 -1
- package/lib/Defaults/index.js +3 -2
- package/lib/Signal/libsignal.js +552 -358
- package/lib/Socket/chats.js +192 -273
- package/lib/Socket/messages-recv.js +4 -2
- package/lib/Socket/messages-send.js +42 -18
- package/lib/Socket/newsletter.js +87 -36
- package/lib/Socket/nexus-handler.js +44 -42
- package/lib/Socket/socket.js +23 -10
- package/lib/Store/make-in-memory-store.js +29 -20
- package/lib/Utils/auth-utils.js +2 -2
- package/lib/Utils/chat-utils.js +319 -620
- package/lib/Utils/decode-wa-message.js +11 -26
- package/lib/Utils/key-store.js +1 -1
- package/lib/Utils/link-preview.js +134 -71
- package/lib/Utils/messages-media.js +97 -26
- package/lib/Utils/messages.js +723 -820
- package/lib/Utils/use-multi-file-auth-state.js +183 -90
- package/lib/index.js +1 -1
- package/package.json +10 -8
|
@@ -1,121 +1,214 @@
|
|
|
1
|
-
import { Mutex } from 'async-mutex'
|
|
2
|
-
import { mkdir, readFile, stat, unlink, writeFile } from 'fs/promises'
|
|
3
|
-
import { join } from 'path'
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
//
|
|
10
|
-
|
|
11
|
-
const
|
|
12
|
-
|
|
1
|
+
import { Mutex } from 'async-mutex'
|
|
2
|
+
import { mkdir, readFile, rename, stat, unlink, writeFile, readdir } from 'fs/promises'
|
|
3
|
+
import { join } from 'path'
|
|
4
|
+
import { createHash } from 'crypto'
|
|
5
|
+
import { proto } from '../../WAProto/index.js'
|
|
6
|
+
import { initAuthCreds } from './auth-utils.js'
|
|
7
|
+
import { BufferJSON } from './generics.js'
|
|
8
|
+
|
|
9
|
+
// ─── Constants ────────────────────────────────────────────────────────────────
|
|
10
|
+
const CURRENT_VERSION = 1
|
|
11
|
+
const DEFAULT_PREKEY_RETENTION = 150
|
|
12
|
+
const DEFAULT_CLEANUP_THRESHOLD = 50
|
|
13
|
+
const CLEANUP_INTERVAL_MS = 10 * 60 * 1000
|
|
14
|
+
|
|
15
|
+
// ─── File Lock Registry ───────────────────────────────────────────────────────
|
|
16
|
+
const fileLocks = new Map()
|
|
13
17
|
const getFileLock = (path) => {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
if (!fileLocks.has(path)) fileLocks.set(path, new Mutex())
|
|
19
|
+
return fileLocks.get(path)
|
|
20
|
+
}
|
|
21
|
+
const releaseFileLock = (path) => {
|
|
22
|
+
if (fileLocks.has(path) && !fileLocks.get(path).isLocked()) fileLocks.delete(path)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// ─── Checksum ─────────────────────────────────────────────────────────────────
|
|
26
|
+
const computeChecksum = (data) => createHash('sha256').update(data).digest('hex')
|
|
27
|
+
|
|
21
28
|
/**
|
|
22
|
-
*
|
|
23
|
-
*
|
|
29
|
+
* Production-grade multi-file auth state for Baileys.
|
|
30
|
+
* Atomic writes, checksum integrity, and smart prekey cleanup.
|
|
31
|
+
* Compatible with standard Baileys auth folder format.
|
|
24
32
|
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
|
|
29
|
-
|
|
33
|
+
* @param {string} folder - Directory to store auth files
|
|
34
|
+
* @param {object} [options] - Configuration options
|
|
35
|
+
* @param {number} [options.preKeyRetention=150] - How many recent prekeys to keep
|
|
36
|
+
* @param {number} [options.cleanupThreshold=50] - How many new prekeys trigger a cleanup
|
|
37
|
+
* @param {object} [options.logger] - Optional logger with .info/.warn methods
|
|
38
|
+
*/
|
|
39
|
+
export const useMultiFileAuthState = async (folder, options = {}) => {
|
|
40
|
+
const {
|
|
41
|
+
preKeyRetention = DEFAULT_PREKEY_RETENTION,
|
|
42
|
+
cleanupThreshold = DEFAULT_CLEANUP_THRESHOLD,
|
|
43
|
+
logger,
|
|
44
|
+
} = options
|
|
45
|
+
const fixFileName = (file) => file?.replace(/\//g, '__')?.replace(/:/g, '-')
|
|
46
|
+
const filePath = (file) => join(folder, fixFileName(file))
|
|
47
|
+
const tmpPath = (file) => filePath(file) + '.tmp'
|
|
48
|
+
|
|
49
|
+
// ─── Folder Bootstrap ───────────────────────────────────────────────────────
|
|
50
|
+
const folderInfo = await stat(folder).catch(() => null)
|
|
51
|
+
if (folderInfo) {
|
|
52
|
+
if (!folderInfo.isDirectory()) throw new Error(`Path exists but is not a directory: ${folder}`)
|
|
53
|
+
} else {
|
|
54
|
+
await mkdir(folder, { recursive: true })
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// ─── Atomic Write ────────────────────────────────────────────────────────────
|
|
30
58
|
const writeData = async (data, file) => {
|
|
31
|
-
const
|
|
32
|
-
const
|
|
33
|
-
|
|
59
|
+
const fp = filePath(file)
|
|
60
|
+
const tp = tmpPath(file)
|
|
61
|
+
const mutex = getFileLock(fp)
|
|
62
|
+
const release = await mutex.acquire()
|
|
63
|
+
try {
|
|
64
|
+
const serialized = JSON.stringify(data, BufferJSON.replacer)
|
|
65
|
+
const checksum = computeChecksum(serialized)
|
|
66
|
+
const payload = JSON.stringify({ data: JSON.parse(serialized), __checksum: checksum })
|
|
67
|
+
await writeFile(tp, payload)
|
|
34
68
|
try {
|
|
35
|
-
await
|
|
69
|
+
await rename(tp, fp)
|
|
70
|
+
} catch {
|
|
71
|
+
await writeFile(fp, payload)
|
|
72
|
+
await unlink(tp).catch(() => { })
|
|
36
73
|
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
}
|
|
41
|
-
}
|
|
74
|
+
} finally {
|
|
75
|
+
release()
|
|
76
|
+
releaseFileLock(fp)
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// ─── Read with Checksum Verification ─────────────────────────────────────────
|
|
42
81
|
const readData = async (file) => {
|
|
82
|
+
const fp = filePath(file)
|
|
83
|
+
const mutex = getFileLock(fp)
|
|
84
|
+
const release = await mutex.acquire()
|
|
43
85
|
try {
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
86
|
+
const raw = await readFile(fp, { encoding: 'utf-8' }).catch(() => null)
|
|
87
|
+
if (!raw) return null
|
|
88
|
+
try {
|
|
89
|
+
const parsed = JSON.parse(raw)
|
|
90
|
+
if (parsed.__checksum) {
|
|
91
|
+
const expected = computeChecksum(JSON.stringify(parsed.data))
|
|
92
|
+
if (expected !== parsed.__checksum) {
|
|
93
|
+
logger?.warn({ file }, 'checksum mismatch — reinitializing file')
|
|
94
|
+
await writeFile(fp, JSON.stringify({ data: parsed.data, __checksum: computeChecksum(JSON.stringify(parsed.data)) })).catch(() => { })
|
|
95
|
+
return JSON.parse(JSON.stringify(parsed.data), BufferJSON.reviver)
|
|
96
|
+
}
|
|
97
|
+
return JSON.parse(JSON.stringify(parsed.data), BufferJSON.reviver)
|
|
53
98
|
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
99
|
+
// legacy file — rewrite with checksum
|
|
100
|
+
const reserialized = JSON.stringify(JSON.parse(raw, BufferJSON.reviver), BufferJSON.replacer)
|
|
101
|
+
const checksum = computeChecksum(reserialized)
|
|
102
|
+
await writeFile(fp, JSON.stringify({ data: JSON.parse(reserialized), __checksum: checksum })).catch(() => { })
|
|
103
|
+
return JSON.parse(raw, BufferJSON.reviver)
|
|
104
|
+
} catch (err) {
|
|
105
|
+
logger?.warn({ file, err: err.message }, 'failed to read auth file — reinitializing')
|
|
106
|
+
await unlink(fp).catch(() => { })
|
|
107
|
+
return null
|
|
108
|
+
}
|
|
109
|
+
} finally { release(); releaseFileLock(fp) }
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// ─── Remove File ────────────────────────────────────────────────────────────
|
|
60
113
|
const removeData = async (file) => {
|
|
114
|
+
const fp = filePath(file)
|
|
115
|
+
const mutex = getFileLock(fp)
|
|
116
|
+
const release = await mutex.acquire()
|
|
61
117
|
try {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
await unlink(filePath);
|
|
67
|
-
}
|
|
68
|
-
catch {
|
|
69
|
-
}
|
|
70
|
-
finally {
|
|
71
|
-
release();
|
|
72
|
-
}
|
|
73
|
-
});
|
|
118
|
+
await unlink(fp).catch(() => { })
|
|
119
|
+
} finally {
|
|
120
|
+
release()
|
|
121
|
+
releaseFileLock(fp)
|
|
74
122
|
}
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// ─── Credentials Bootstrap ──────────────────────────────────────────────────
|
|
126
|
+
let creds = (await readData('creds.json')) || initAuthCreds()
|
|
127
|
+
if (!creds.__version) {
|
|
128
|
+
creds.__version = CURRENT_VERSION
|
|
129
|
+
} else if (creds.__version < CURRENT_VERSION) {
|
|
130
|
+
creds.__version = CURRENT_VERSION
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// ─── Prekey Cleanup ─────────────────────────────────────────────────────────
|
|
134
|
+
let cleanupRunning = false
|
|
135
|
+
let lastCleanupAt = 0
|
|
136
|
+
let lastCleanedPreKeyId = creds.nextPreKeyId
|
|
137
|
+
|
|
138
|
+
const cleanOldPreKeys = async () => {
|
|
139
|
+
const now = Date.now()
|
|
140
|
+
if (cleanupRunning) return
|
|
141
|
+
if (now - lastCleanupAt < CLEANUP_INTERVAL_MS) return
|
|
142
|
+
cleanupRunning = true
|
|
143
|
+
try {
|
|
144
|
+
const minId = creds.nextPreKeyId - preKeyRetention
|
|
145
|
+
if (minId <= 0) return
|
|
146
|
+
const files = await readdir(folder)
|
|
147
|
+
const targets = []
|
|
148
|
+
for (const file of files) {
|
|
149
|
+
const match = file.match(/^pre-key-(\d+)\.json(\.tmp)?$/)
|
|
150
|
+
if (!match) continue
|
|
151
|
+
if (parseInt(match[1], 10) < minId) targets.push(join(folder, file))
|
|
152
|
+
}
|
|
153
|
+
if (!targets.length) return
|
|
154
|
+
await Promise.all(targets.map(f => unlink(f).catch(() => { })))
|
|
155
|
+
lastCleanupAt = Date.now()
|
|
156
|
+
lastCleanedPreKeyId = creds.nextPreKeyId
|
|
157
|
+
logger?.info({ deleted: targets.length, minId }, 'prekey cleanup complete')
|
|
158
|
+
} catch (err) {
|
|
159
|
+
logger?.warn({ err }, 'prekey cleanup failed')
|
|
160
|
+
} finally {
|
|
161
|
+
cleanupRunning = false
|
|
81
162
|
}
|
|
82
163
|
}
|
|
83
|
-
|
|
84
|
-
|
|
164
|
+
|
|
165
|
+
cleanOldPreKeys().catch(() => { })
|
|
166
|
+
|
|
167
|
+
// ─── Stats ──────────────────────────────────────────────────────────────────
|
|
168
|
+
const getStats = async () => {
|
|
169
|
+
const files = await readdir(folder).catch(() => [])
|
|
170
|
+
const preKeyFiles = files.filter(f => /^pre-key-\d+\.json$/.test(f))
|
|
171
|
+
return {
|
|
172
|
+
totalFiles: files.length,
|
|
173
|
+
preKeyCount: preKeyFiles.length,
|
|
174
|
+
nextPreKeyId: creds.nextPreKeyId,
|
|
175
|
+
lastCleanupAt: lastCleanupAt ? new Date(lastCleanupAt).toISOString() : null,
|
|
176
|
+
}
|
|
85
177
|
}
|
|
86
|
-
|
|
87
|
-
const creds = (await readData('creds.json')) || initAuthCreds();
|
|
178
|
+
|
|
88
179
|
return {
|
|
89
180
|
state: {
|
|
90
181
|
creds,
|
|
91
182
|
keys: {
|
|
92
183
|
get: async (type, ids) => {
|
|
93
|
-
const data = {}
|
|
184
|
+
const data = {}
|
|
94
185
|
await Promise.all(ids.map(async (id) => {
|
|
95
|
-
let value = await readData(`${type}-${id}.json`)
|
|
186
|
+
let value = await readData(`${type}-${id}.json`)
|
|
96
187
|
if (type === 'app-state-sync-key' && value) {
|
|
97
|
-
value = proto.Message.AppStateSyncKeyData.fromObject(value)
|
|
188
|
+
value = proto.Message.AppStateSyncKeyData.fromObject(value)
|
|
98
189
|
}
|
|
99
|
-
data[id] = value
|
|
100
|
-
}))
|
|
101
|
-
return data
|
|
190
|
+
data[id] = value
|
|
191
|
+
}))
|
|
192
|
+
return data
|
|
102
193
|
},
|
|
103
194
|
set: async (data) => {
|
|
104
|
-
const tasks = []
|
|
195
|
+
const tasks = []
|
|
105
196
|
for (const category in data) {
|
|
106
197
|
for (const id in data[category]) {
|
|
107
|
-
const value = data[category][id]
|
|
108
|
-
|
|
109
|
-
tasks.push(value ? writeData(value, file) : removeData(file));
|
|
198
|
+
const value = data[category][id]
|
|
199
|
+
tasks.push(value ? writeData(value, `${category}-${id}.json`) : removeData(`${category}-${id}.json`))
|
|
110
200
|
}
|
|
111
201
|
}
|
|
112
|
-
await Promise.all(tasks)
|
|
113
|
-
}
|
|
114
|
-
}
|
|
202
|
+
await Promise.all(tasks)
|
|
203
|
+
},
|
|
204
|
+
},
|
|
115
205
|
},
|
|
116
206
|
saveCreds: async () => {
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
207
|
+
if (creds.nextPreKeyId - lastCleanedPreKeyId >= cleanupThreshold) {
|
|
208
|
+
cleanOldPreKeys().catch(() => { })
|
|
209
|
+
}
|
|
210
|
+
return writeData(creds, 'creds.json')
|
|
211
|
+
},
|
|
212
|
+
getStats,
|
|
213
|
+
}
|
|
214
|
+
}
|
package/lib/index.js
CHANGED
|
@@ -31,7 +31,7 @@ const banner = `
|
|
|
31
31
|
const info = `
|
|
32
32
|
┌───────────────────────────────────────────────────────────────────────┐
|
|
33
33
|
│ 📦 Package: @nexustechpro/baileys │
|
|
34
|
-
│ 🔖 Version: 2.
|
|
34
|
+
│ 🔖 Version: 2.1.2 │
|
|
35
35
|
│ ⚡ Status: Production Ready │
|
|
36
36
|
├───────────────────────────────────────────────────────────────────────┤
|
|
37
37
|
│ 🚀 Advanced WhatsApp Web API Client │
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"$schema": "https://json.schemastore.org/package.json",
|
|
3
3
|
"name": "@nexustechpro/baileys",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"version": "2.
|
|
5
|
+
"version": "2.1.2",
|
|
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
|
"whatsapp",
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
"events",
|
|
50
50
|
"payments",
|
|
51
51
|
"polls",
|
|
52
|
-
"
|
|
52
|
+
"albums",
|
|
53
53
|
"sticker-pack",
|
|
54
54
|
"nodejs",
|
|
55
55
|
"typescript",
|
|
@@ -98,27 +98,29 @@
|
|
|
98
98
|
"engine-requirements.js"
|
|
99
99
|
],
|
|
100
100
|
"dependencies": {
|
|
101
|
+
"@adiwajshing/keyed-db": "^0.2.4",
|
|
101
102
|
"@cacheable/node-cache": "^1.4.0",
|
|
102
103
|
"@hapi/boom": "^9.1.3",
|
|
104
|
+
"@microlink/mql": "^0.16.1",
|
|
103
105
|
"async-mutex": "^0.5.0",
|
|
104
106
|
"axios": "^1.6.0",
|
|
105
107
|
"cache-manager": "latest",
|
|
106
108
|
"chalk": "^4.1.2",
|
|
107
109
|
"fflate": "^0.8.2",
|
|
108
|
-
"ffmpeg-static": "^5.2.0",
|
|
109
110
|
"gradient-string": "^2.0.2",
|
|
110
111
|
"libphonenumber-js": "^1.13.3",
|
|
111
|
-
"link-preview-js": "
|
|
112
|
+
"link-preview-js": "^4.0.3",
|
|
112
113
|
"lru-cache": "^11.1.0",
|
|
113
114
|
"music-metadata": "^11.7.0",
|
|
114
115
|
"p-queue": "^9.0.0",
|
|
115
116
|
"pino": "^9.6",
|
|
116
117
|
"protobufjs": "latest",
|
|
118
|
+
"qrcode-terminal": "^0.12.0",
|
|
117
119
|
"sharp": "^0.32.0",
|
|
120
|
+
"url-regex-safe": "^4.0.0",
|
|
118
121
|
"uuid": "latest",
|
|
119
122
|
"whatsapp-rust-bridge": "latest",
|
|
120
|
-
"ws": "latest"
|
|
121
|
-
"yarn": "^1.22.22"
|
|
123
|
+
"ws": "latest"
|
|
122
124
|
},
|
|
123
125
|
"devDependencies": {
|
|
124
126
|
"@eslint/eslintrc": "^3.3.1",
|
|
@@ -139,7 +141,7 @@
|
|
|
139
141
|
"jimp": "^1.6.0",
|
|
140
142
|
"jiti": "^2.4.2",
|
|
141
143
|
"json": "^11.0.0",
|
|
142
|
-
"link-preview-js": "^
|
|
144
|
+
"link-preview-js": "^4.0.3",
|
|
143
145
|
"lru-cache": "^11.1.0",
|
|
144
146
|
"open": "^8.4.2",
|
|
145
147
|
"pino-pretty": "^13.1.1",
|
|
@@ -156,7 +158,7 @@
|
|
|
156
158
|
"peerDependencies": {
|
|
157
159
|
"audio-decode": "^2.1.3",
|
|
158
160
|
"jimp": "^1.6.0",
|
|
159
|
-
"link-preview-js": "^
|
|
161
|
+
"link-preview-js": "^4.0.3"
|
|
160
162
|
},
|
|
161
163
|
"peerDependenciesMeta": {
|
|
162
164
|
"audio-decode": {
|