@nexustechpro/baileys 2.0.5 → 2.1.0
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 +71 -0
- package/WAProto/index.js +22 -18
- package/lib/Defaults/baileys-version.json +1 -1
- package/lib/Defaults/index.js +9 -8
- package/lib/Signal/libsignal.js +551 -342
- package/lib/Socket/chats.js +83 -65
- package/lib/Socket/index.js +2 -3
- package/lib/Socket/messages-recv.js +227 -41
- package/lib/Socket/messages-send.js +97 -117
- package/lib/Socket/newsletter.js +87 -36
- package/lib/Socket/nexus-handler.js +326 -89
- package/lib/Socket/registration.js +50 -33
- package/lib/Socket/socket.js +245 -69
- package/lib/Store/make-in-memory-store.js +29 -20
- package/lib/Types/Newsletter.js +37 -29
- package/lib/Types/State.js +43 -0
- package/lib/Utils/auth-utils.js +2 -2
- package/lib/Utils/chat-utils.js +48 -16
- package/lib/Utils/companion-reg-client-utils.js +34 -0
- package/lib/Utils/decode-wa-message.js +39 -22
- package/lib/Utils/generics.js +5 -7
- package/lib/Utils/index.js +4 -0
- package/lib/Utils/key-store.js +1 -1
- package/lib/Utils/link-preview.js +134 -61
- package/lib/Utils/messages-media.js +496 -381
- package/lib/Utils/messages.js +699 -706
- package/lib/Utils/process-message.js +53 -35
- package/lib/Utils/reporting-utils.js +155 -0
- package/lib/Utils/signal.js +134 -104
- 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 +174 -91
- package/lib/WABinary/constants.js +6 -0
- package/lib/WABinary/index.js +1 -0
- package/lib/index.js +2 -3
- package/package.json +13 -9
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { getBinaryNodeChild, getBinaryNodeChildren, isHostedLidUser, isHostedPnUser, isJidMetaAI, isLidUser, isPnUser, jidNormalizedUser } from '../WABinary/index.js';
|
|
2
|
+
// Same phone-number pattern as WABinary's isJidBot, applied against the user
|
|
3
|
+
// part so the check is invariant to @c.us ↔ @s.whatsapp.net normalization.
|
|
4
|
+
const BOT_PHONE_REGEX = /^1313555\d{4}$|^131655500\d{2}$/;
|
|
5
|
+
/**
|
|
6
|
+
* Mirrors WA Web's `Wid.isRegularUser()` (user ∧ ¬PSA ∧ ¬Bot). Used to gate tctoken
|
|
7
|
+
* storage against malformed notifications — WA Web filters server-side but we
|
|
8
|
+
* defend here for parity with `WAWebSetTcTokenChatAction.handleIncomingTcToken`.
|
|
9
|
+
* Works for both pre- and post-normalized JIDs (`@c.us` vs `@s.whatsapp.net`).
|
|
10
|
+
*/
|
|
11
|
+
function isRegularUser(jid) {
|
|
12
|
+
if (!jid)
|
|
13
|
+
return false;
|
|
14
|
+
const user = jid.split('@')[0] ?? '';
|
|
15
|
+
if (user === '0')
|
|
16
|
+
return false; // PSA
|
|
17
|
+
if (BOT_PHONE_REGEX.test(user))
|
|
18
|
+
return false; // Bot by phone pattern
|
|
19
|
+
if (isJidMetaAI(jid))
|
|
20
|
+
return false; // MetaAI (@bot server)
|
|
21
|
+
return !!(isPnUser(jid) || isLidUser(jid) || isHostedPnUser(jid) || isHostedLidUser(jid) || jid.endsWith('@c.us'));
|
|
22
|
+
}
|
|
23
|
+
const TC_TOKEN_BUCKET_DURATION = 604800; // 7 days
|
|
24
|
+
const TC_TOKEN_NUM_BUCKETS = 4; // ~28-day rolling window
|
|
25
|
+
/** Sentinel key under `tctoken` store holding a JSON array of tracked storage JIDs for cross-session pruning. */
|
|
26
|
+
export const TC_TOKEN_INDEX_KEY = '__index';
|
|
27
|
+
/** Read the persisted tctoken JID index and return its entries (never contains the sentinel key itself). */
|
|
28
|
+
export async function readTcTokenIndex(keys) {
|
|
29
|
+
const data = await keys.get('tctoken', [TC_TOKEN_INDEX_KEY]);
|
|
30
|
+
const entry = data[TC_TOKEN_INDEX_KEY];
|
|
31
|
+
if (!entry?.token?.length)
|
|
32
|
+
return [];
|
|
33
|
+
try {
|
|
34
|
+
const parsed = JSON.parse(Buffer.from(entry.token).toString());
|
|
35
|
+
if (!Array.isArray(parsed))
|
|
36
|
+
return [];
|
|
37
|
+
return parsed.filter((j) => typeof j === 'string' && j.length > 0 && j !== TC_TOKEN_INDEX_KEY);
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
return [];
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
/** Build a SignalDataSet fragment that writes the merged index (persisted ∪ added) under the sentinel key. */
|
|
44
|
+
export async function buildMergedTcTokenIndexWrite(keys, addedJids) {
|
|
45
|
+
const persisted = await readTcTokenIndex(keys);
|
|
46
|
+
const merged = new Set(persisted);
|
|
47
|
+
for (const jid of addedJids) {
|
|
48
|
+
if (jid && jid !== TC_TOKEN_INDEX_KEY)
|
|
49
|
+
merged.add(jid);
|
|
50
|
+
}
|
|
51
|
+
return {
|
|
52
|
+
[TC_TOKEN_INDEX_KEY]: { token: Buffer.from(JSON.stringify([...merged])) }
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
// WA Web has separate sender/receiver AB props for these but they're identical today
|
|
56
|
+
export function isTcTokenExpired(timestamp) {
|
|
57
|
+
if (timestamp === null || timestamp === undefined)
|
|
58
|
+
return true;
|
|
59
|
+
const ts = typeof timestamp === 'string' ? parseInt(timestamp) : timestamp;
|
|
60
|
+
if (isNaN(ts))
|
|
61
|
+
return true;
|
|
62
|
+
const now = Math.floor(Date.now() / 1000);
|
|
63
|
+
const currentBucket = Math.floor(now / TC_TOKEN_BUCKET_DURATION);
|
|
64
|
+
const cutoffBucket = currentBucket - (TC_TOKEN_NUM_BUCKETS - 1);
|
|
65
|
+
const cutoffTimestamp = cutoffBucket * TC_TOKEN_BUCKET_DURATION;
|
|
66
|
+
return ts < cutoffTimestamp;
|
|
67
|
+
}
|
|
68
|
+
export function shouldSendNewTcToken(senderTimestamp) {
|
|
69
|
+
if (senderTimestamp === undefined)
|
|
70
|
+
return true;
|
|
71
|
+
const now = Math.floor(Date.now() / 1000);
|
|
72
|
+
const currentBucket = Math.floor(now / TC_TOKEN_BUCKET_DURATION);
|
|
73
|
+
const senderBucket = Math.floor(senderTimestamp / TC_TOKEN_BUCKET_DURATION);
|
|
74
|
+
return currentBucket > senderBucket;
|
|
75
|
+
}
|
|
76
|
+
/** Resolve JID to LID for tctoken storage (WA Web stores under LID) */
|
|
77
|
+
export async function resolveTcTokenJid(jid, getLIDForPN) {
|
|
78
|
+
if (isLidUser(jid))
|
|
79
|
+
return jid;
|
|
80
|
+
const lid = await getLIDForPN(jid);
|
|
81
|
+
return lid ?? jid;
|
|
82
|
+
}
|
|
83
|
+
/** Resolve target JID for issuing privacy token based on AB prop 14303 */
|
|
84
|
+
export async function resolveIssuanceJid(jid, issueToLid, getLIDForPN, getPNForLID) {
|
|
85
|
+
if (issueToLid) {
|
|
86
|
+
if (isLidUser(jid))
|
|
87
|
+
return jid;
|
|
88
|
+
const lid = await getLIDForPN(jid);
|
|
89
|
+
return lid ?? jid;
|
|
90
|
+
}
|
|
91
|
+
if (!isLidUser(jid))
|
|
92
|
+
return jid;
|
|
93
|
+
if (getPNForLID) {
|
|
94
|
+
const pn = await getPNForLID(jid);
|
|
95
|
+
return pn ?? jid;
|
|
96
|
+
}
|
|
97
|
+
return jid;
|
|
98
|
+
}
|
|
99
|
+
export async function buildTcTokenFromJid({ authState, jid, baseContent = [], getLIDForPN }) {
|
|
100
|
+
try {
|
|
101
|
+
const storageJid = await resolveTcTokenJid(jid, getLIDForPN);
|
|
102
|
+
const tcTokenData = await authState.keys.get('tctoken', [storageJid]);
|
|
103
|
+
const entry = tcTokenData?.[storageJid];
|
|
104
|
+
const tcTokenBuffer = entry?.token;
|
|
105
|
+
if (!tcTokenBuffer?.length || isTcTokenExpired(entry?.timestamp)) {
|
|
106
|
+
if (tcTokenBuffer) {
|
|
107
|
+
// Preserve senderTimestamp so shouldSendNewTcToken() keeps its dedupe state
|
|
108
|
+
// after we drop the unusable peer token. Only wipe the record entirely when
|
|
109
|
+
// there's nothing worth keeping.
|
|
110
|
+
const cleared = entry?.senderTimestamp !== undefined
|
|
111
|
+
? { token: Buffer.alloc(0), senderTimestamp: entry.senderTimestamp }
|
|
112
|
+
: null;
|
|
113
|
+
await authState.keys.set({ tctoken: { [storageJid]: cleared } });
|
|
114
|
+
}
|
|
115
|
+
return baseContent.length > 0 ? baseContent : undefined;
|
|
116
|
+
}
|
|
117
|
+
baseContent.push({
|
|
118
|
+
tag: 'tctoken',
|
|
119
|
+
attrs: {},
|
|
120
|
+
content: tcTokenBuffer
|
|
121
|
+
});
|
|
122
|
+
return baseContent;
|
|
123
|
+
}
|
|
124
|
+
catch (error) {
|
|
125
|
+
return baseContent.length > 0 ? baseContent : undefined;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
export async function storeTcTokensFromIqResult({ result, fallbackJid, keys, getLIDForPN, onNewJidStored }) {
|
|
129
|
+
const tokensNode = getBinaryNodeChild(result, 'tokens');
|
|
130
|
+
if (!tokensNode)
|
|
131
|
+
return;
|
|
132
|
+
const tokenNodes = getBinaryNodeChildren(tokensNode, 'token');
|
|
133
|
+
for (const tokenNode of tokenNodes) {
|
|
134
|
+
if (tokenNode.attrs.type !== 'trusted_contact' || !(tokenNode.content instanceof Uint8Array)) {
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
// In notifications tokenNode.attrs.jid is your own device JID, not the sender's
|
|
138
|
+
const rawJid = jidNormalizedUser(fallbackJid || tokenNode.attrs.jid);
|
|
139
|
+
if (!isRegularUser(rawJid))
|
|
140
|
+
continue;
|
|
141
|
+
const storageJid = await resolveTcTokenJid(rawJid, getLIDForPN);
|
|
142
|
+
const existingTcData = await keys.get('tctoken', [storageJid]);
|
|
143
|
+
const existingEntry = existingTcData[storageJid];
|
|
144
|
+
const existingTs = existingEntry?.timestamp ? Number(existingEntry.timestamp) : 0;
|
|
145
|
+
const incomingTs = tokenNode.attrs.t ? Number(tokenNode.attrs.t) : 0;
|
|
146
|
+
// timestamp-less tokens would be immediately expired
|
|
147
|
+
if (!incomingTs)
|
|
148
|
+
continue;
|
|
149
|
+
if (existingTs > 0 && existingTs > incomingTs)
|
|
150
|
+
continue;
|
|
151
|
+
await keys.set({
|
|
152
|
+
tctoken: {
|
|
153
|
+
[storageJid]: {
|
|
154
|
+
...existingEntry,
|
|
155
|
+
token: Buffer.from(tokenNode.content),
|
|
156
|
+
timestamp: tokenNode.attrs.t
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
});
|
|
160
|
+
onNewJidStored?.(storageJid);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
@@ -1,121 +1,204 @@
|
|
|
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
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
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)
|
|
68
|
+
await rename(tp, fp)
|
|
69
|
+
} finally {
|
|
70
|
+
release()
|
|
71
|
+
releaseFileLock(fp)
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// ─── Read with Checksum Verification ─────────────────────────────────────────
|
|
42
76
|
const readData = async (file) => {
|
|
77
|
+
const fp = filePath(file)
|
|
78
|
+
const mutex = getFileLock(fp)
|
|
79
|
+
const release = await mutex.acquire()
|
|
43
80
|
try {
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
release();
|
|
81
|
+
const raw = await readFile(fp, { encoding: 'utf-8' }).catch(() => null)
|
|
82
|
+
if (!raw) return null
|
|
83
|
+
try {
|
|
84
|
+
const parsed = JSON.parse(raw)
|
|
85
|
+
if (parsed.__checksum) {
|
|
86
|
+
const expected = computeChecksum(JSON.stringify(parsed.data))
|
|
87
|
+
if (expected !== parsed.__checksum) throw new Error('checksum mismatch')
|
|
88
|
+
return JSON.parse(JSON.stringify(parsed.data), BufferJSON.reviver)
|
|
53
89
|
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
90
|
+
// legacy file without checksum — read as-is for compatibility
|
|
91
|
+
return JSON.parse(raw, BufferJSON.reviver)
|
|
92
|
+
} catch (err) {
|
|
93
|
+
logger?.warn({ file, err: err.message }, 'failed to read auth file')
|
|
94
|
+
return null
|
|
95
|
+
}
|
|
96
|
+
} finally {
|
|
97
|
+
release()
|
|
98
|
+
releaseFileLock(fp)
|
|
58
99
|
}
|
|
59
|
-
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// ─── Remove File ────────────────────────────────────────────────────────────
|
|
60
103
|
const removeData = async (file) => {
|
|
104
|
+
const fp = filePath(file)
|
|
105
|
+
const mutex = getFileLock(fp)
|
|
106
|
+
const release = await mutex.acquire()
|
|
61
107
|
try {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
await unlink(filePath);
|
|
67
|
-
}
|
|
68
|
-
catch {
|
|
69
|
-
}
|
|
70
|
-
finally {
|
|
71
|
-
release();
|
|
72
|
-
}
|
|
73
|
-
});
|
|
108
|
+
await unlink(fp).catch(() => { })
|
|
109
|
+
} finally {
|
|
110
|
+
release()
|
|
111
|
+
releaseFileLock(fp)
|
|
74
112
|
}
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// ─── Credentials Bootstrap ──────────────────────────────────────────────────
|
|
116
|
+
let creds = (await readData('creds.json')) || initAuthCreds()
|
|
117
|
+
if (!creds.__version) {
|
|
118
|
+
creds.__version = CURRENT_VERSION
|
|
119
|
+
} else if (creds.__version < CURRENT_VERSION) {
|
|
120
|
+
creds.__version = CURRENT_VERSION
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// ─── Prekey Cleanup ─────────────────────────────────────────────────────────
|
|
124
|
+
let cleanupRunning = false
|
|
125
|
+
let lastCleanupAt = 0
|
|
126
|
+
let lastCleanedPreKeyId = creds.nextPreKeyId
|
|
127
|
+
|
|
128
|
+
const cleanOldPreKeys = async () => {
|
|
129
|
+
const now = Date.now()
|
|
130
|
+
if (cleanupRunning) return
|
|
131
|
+
if (now - lastCleanupAt < CLEANUP_INTERVAL_MS) return
|
|
132
|
+
cleanupRunning = true
|
|
133
|
+
try {
|
|
134
|
+
const minId = creds.nextPreKeyId - preKeyRetention
|
|
135
|
+
if (minId <= 0) return
|
|
136
|
+
const files = await readdir(folder)
|
|
137
|
+
const targets = []
|
|
138
|
+
for (const file of files) {
|
|
139
|
+
const match = file.match(/^pre-key-(\d+)\.json(\.tmp)?$/)
|
|
140
|
+
if (!match) continue
|
|
141
|
+
if (parseInt(match[1], 10) < minId) targets.push(join(folder, file))
|
|
142
|
+
}
|
|
143
|
+
if (!targets.length) return
|
|
144
|
+
await Promise.all(targets.map(f => unlink(f).catch(() => { })))
|
|
145
|
+
lastCleanupAt = Date.now()
|
|
146
|
+
lastCleanedPreKeyId = creds.nextPreKeyId
|
|
147
|
+
logger?.info({ deleted: targets.length, minId }, 'prekey cleanup complete')
|
|
148
|
+
} catch (err) {
|
|
149
|
+
logger?.warn({ err }, 'prekey cleanup failed')
|
|
150
|
+
} finally {
|
|
151
|
+
cleanupRunning = false
|
|
81
152
|
}
|
|
82
153
|
}
|
|
83
|
-
|
|
84
|
-
|
|
154
|
+
|
|
155
|
+
cleanOldPreKeys().catch(() => { })
|
|
156
|
+
|
|
157
|
+
// ─── Stats ──────────────────────────────────────────────────────────────────
|
|
158
|
+
const getStats = async () => {
|
|
159
|
+
const files = await readdir(folder).catch(() => [])
|
|
160
|
+
const preKeyFiles = files.filter(f => /^pre-key-\d+\.json$/.test(f))
|
|
161
|
+
return {
|
|
162
|
+
totalFiles: files.length,
|
|
163
|
+
preKeyCount: preKeyFiles.length,
|
|
164
|
+
nextPreKeyId: creds.nextPreKeyId,
|
|
165
|
+
lastCleanupAt: lastCleanupAt ? new Date(lastCleanupAt).toISOString() : null,
|
|
166
|
+
}
|
|
85
167
|
}
|
|
86
|
-
|
|
87
|
-
const creds = (await readData('creds.json')) || initAuthCreds();
|
|
168
|
+
|
|
88
169
|
return {
|
|
89
170
|
state: {
|
|
90
171
|
creds,
|
|
91
172
|
keys: {
|
|
92
173
|
get: async (type, ids) => {
|
|
93
|
-
const data = {}
|
|
174
|
+
const data = {}
|
|
94
175
|
await Promise.all(ids.map(async (id) => {
|
|
95
|
-
let value = await readData(`${type}-${id}.json`)
|
|
176
|
+
let value = await readData(`${type}-${id}.json`)
|
|
96
177
|
if (type === 'app-state-sync-key' && value) {
|
|
97
|
-
value = proto.Message.AppStateSyncKeyData.fromObject(value)
|
|
178
|
+
value = proto.Message.AppStateSyncKeyData.fromObject(value)
|
|
98
179
|
}
|
|
99
|
-
data[id] = value
|
|
100
|
-
}))
|
|
101
|
-
return data
|
|
180
|
+
data[id] = value
|
|
181
|
+
}))
|
|
182
|
+
return data
|
|
102
183
|
},
|
|
103
184
|
set: async (data) => {
|
|
104
|
-
const tasks = []
|
|
185
|
+
const tasks = []
|
|
105
186
|
for (const category in data) {
|
|
106
187
|
for (const id in data[category]) {
|
|
107
|
-
const value = data[category][id]
|
|
108
|
-
|
|
109
|
-
tasks.push(value ? writeData(value, file) : removeData(file));
|
|
188
|
+
const value = data[category][id]
|
|
189
|
+
tasks.push(value ? writeData(value, `${category}-${id}.json`) : removeData(`${category}-${id}.json`))
|
|
110
190
|
}
|
|
111
191
|
}
|
|
112
|
-
await Promise.all(tasks)
|
|
113
|
-
}
|
|
114
|
-
}
|
|
192
|
+
await Promise.all(tasks)
|
|
193
|
+
},
|
|
194
|
+
},
|
|
115
195
|
},
|
|
116
196
|
saveCreds: async () => {
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
197
|
+
if (creds.nextPreKeyId - lastCleanedPreKeyId >= cleanupThreshold) {
|
|
198
|
+
cleanOldPreKeys().catch(() => { })
|
|
199
|
+
}
|
|
200
|
+
return writeData(creds, 'creds.json')
|
|
201
|
+
},
|
|
202
|
+
getStats,
|
|
203
|
+
}
|
|
204
|
+
}
|
|
@@ -17,6 +17,12 @@ export const TAGS = {
|
|
|
17
17
|
NIBBLE_8: 255,
|
|
18
18
|
PACKED_MAX: 127
|
|
19
19
|
};
|
|
20
|
+
export const VIOLATION_TYPES = {
|
|
21
|
+
'15': 'Spam/Bulk Messaging',
|
|
22
|
+
'16': 'Automation/Bot Usage',
|
|
23
|
+
'18': 'Terms of Service Violation',
|
|
24
|
+
'20': 'Illegal Content'
|
|
25
|
+
};
|
|
20
26
|
export const DOUBLE_BYTE_TOKENS = [
|
|
21
27
|
[
|
|
22
28
|
'read-self',
|
package/lib/WABinary/index.js
CHANGED
package/lib/index.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
import gradient from 'gradient-string';
|
|
3
3
|
import makeWASocket from './Socket/index.js';
|
|
4
|
-
import NexusHandler from './Socket/nexus-handler.js';
|
|
5
4
|
const banner = `
|
|
6
5
|
╔══════════════════════════════════════════════════════════════════╗
|
|
7
6
|
║ ║
|
|
@@ -32,7 +31,7 @@ const banner = `
|
|
|
32
31
|
const info = `
|
|
33
32
|
┌───────────────────────────────────────────────────────────────────────┐
|
|
34
33
|
│ 📦 Package: @nexustechpro/baileys │
|
|
35
|
-
│ 🔖 Version: 2.0
|
|
34
|
+
│ 🔖 Version: 2.1.0 │
|
|
36
35
|
│ ⚡ Status: Production Ready │
|
|
37
36
|
├───────────────────────────────────────────────────────────────────────┤
|
|
38
37
|
│ 🚀 Advanced WhatsApp Web API Client │
|
|
@@ -63,5 +62,5 @@ export * from './Defaults/index.js';
|
|
|
63
62
|
export * from './WABinary/index.js';
|
|
64
63
|
export * from './WAM/index.js';
|
|
65
64
|
export * from './WAUSync/index.js';
|
|
66
|
-
export
|
|
65
|
+
export * from './Socket/index.js';
|
|
67
66
|
export default makeWASocket;
|
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.0
|
|
5
|
+
"version": "2.1.0",
|
|
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,7 +98,9 @@
|
|
|
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",
|
|
103
|
+
"@microlink/mql": "^0.16.1",
|
|
102
104
|
"@hapi/boom": "^9.1.3",
|
|
103
105
|
"async-mutex": "^0.5.0",
|
|
104
106
|
"axios": "^1.6.0",
|
|
@@ -106,17 +108,19 @@
|
|
|
106
108
|
"chalk": "^4.1.2",
|
|
107
109
|
"fflate": "^0.8.2",
|
|
108
110
|
"gradient-string": "^2.0.2",
|
|
109
|
-
"
|
|
111
|
+
"libphonenumber-js": "^1.13.3",
|
|
112
|
+
"link-preview-js": "^4.0.3",
|
|
110
113
|
"lru-cache": "^11.1.0",
|
|
111
114
|
"music-metadata": "^11.7.0",
|
|
112
115
|
"p-queue": "^9.0.0",
|
|
113
116
|
"pino": "^9.6",
|
|
114
|
-
"uuid": "latest",
|
|
115
117
|
"protobufjs": "latest",
|
|
118
|
+
"qrcode-terminal": "^0.12.0",
|
|
116
119
|
"sharp": "^0.32.0",
|
|
120
|
+
"url-regex-safe": "^4.0.0",
|
|
121
|
+
"uuid": "latest",
|
|
117
122
|
"whatsapp-rust-bridge": "latest",
|
|
118
|
-
"ws": "latest"
|
|
119
|
-
"yarn": "^1.22.22"
|
|
123
|
+
"ws": "latest"
|
|
120
124
|
},
|
|
121
125
|
"devDependencies": {
|
|
122
126
|
"@eslint/eslintrc": "^3.3.1",
|
|
@@ -137,7 +141,7 @@
|
|
|
137
141
|
"jimp": "^1.6.0",
|
|
138
142
|
"jiti": "^2.4.2",
|
|
139
143
|
"json": "^11.0.0",
|
|
140
|
-
"link-preview-js": "^
|
|
144
|
+
"link-preview-js": "^4.0.3",
|
|
141
145
|
"lru-cache": "^11.1.0",
|
|
142
146
|
"open": "^8.4.2",
|
|
143
147
|
"pino-pretty": "^13.1.1",
|
|
@@ -154,7 +158,7 @@
|
|
|
154
158
|
"peerDependencies": {
|
|
155
159
|
"audio-decode": "^2.1.3",
|
|
156
160
|
"jimp": "^1.6.0",
|
|
157
|
-
"link-preview-js": "^
|
|
161
|
+
"link-preview-js": "^4.0.3"
|
|
158
162
|
},
|
|
159
163
|
"peerDependenciesMeta": {
|
|
160
164
|
"audio-decode": {
|
|
@@ -168,4 +172,4 @@
|
|
|
168
172
|
"engines": {
|
|
169
173
|
"node": ">=20.0.0"
|
|
170
174
|
}
|
|
171
|
-
}
|
|
175
|
+
}
|