@nexustechpro/baileys 2.1.0 → 2.1.3
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 +1 -1
- package/lib/Defaults/index.js +1 -0
- package/lib/Socket/chats.js +180 -272
- package/lib/Socket/messages-recv.js +8 -5
- package/lib/Socket/messages-send.js +16 -15
- package/lib/Utils/chat-utils.js +318 -619
- package/lib/Utils/key-store.js +10 -5
- package/lib/Utils/lt-hash.js +2 -43
- package/lib/Utils/messages.js +89 -64
- package/lib/Utils/process-message.js +234 -215
- package/lib/Utils/tc-token-utils.js +38 -73
- package/lib/Utils/use-multi-file-auth-state.js +18 -8
- package/lib/index.js +1 -1
- package/package.json +2 -2
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import { getBinaryNodeChild, getBinaryNodeChildren, isHostedLidUser, isHostedPnUser, isJidMetaAI, isLidUser, isPnUser, jidNormalizedUser } from '../WABinary/index.js';
|
|
2
|
+
import { migrateIndexKey, TC_TOKEN_INDEX_KEY } from '../Utils/index.js';
|
|
3
|
+
|
|
2
4
|
// Same phone-number pattern as WABinary's isJidBot, applied against the user
|
|
3
5
|
// part so the check is invariant to @c.us ↔ @s.whatsapp.net normalization.
|
|
4
6
|
const BOT_PHONE_REGEX = /^1313555\d{4}$|^131655500\d{2}$/;
|
|
7
|
+
|
|
5
8
|
/**
|
|
6
9
|
* Mirrors WA Web's `Wid.isRegularUser()` (user ∧ ¬PSA ∧ ¬Bot). Used to gate tctoken
|
|
7
10
|
* storage against malformed notifications — WA Web filters server-side but we
|
|
@@ -9,93 +12,73 @@ const BOT_PHONE_REGEX = /^1313555\d{4}$|^131655500\d{2}$/;
|
|
|
9
12
|
* Works for both pre- and post-normalized JIDs (`@c.us` vs `@s.whatsapp.net`).
|
|
10
13
|
*/
|
|
11
14
|
function isRegularUser(jid) {
|
|
12
|
-
if (!jid)
|
|
13
|
-
return false;
|
|
15
|
+
if (!jid) return false;
|
|
14
16
|
const user = jid.split('@')[0] ?? '';
|
|
15
|
-
if (user === '0')
|
|
16
|
-
|
|
17
|
-
if (
|
|
18
|
-
return false; // Bot by phone pattern
|
|
19
|
-
if (isJidMetaAI(jid))
|
|
20
|
-
return false; // MetaAI (@bot server)
|
|
17
|
+
if (user === '0') return false; // PSA
|
|
18
|
+
if (BOT_PHONE_REGEX.test(user)) return false; // Bot by phone pattern
|
|
19
|
+
if (isJidMetaAI(jid)) return false; // MetaAI (@bot server)
|
|
21
20
|
return !!(isPnUser(jid) || isLidUser(jid) || isHostedPnUser(jid) || isHostedLidUser(jid) || jid.endsWith('@c.us'));
|
|
22
21
|
}
|
|
22
|
+
|
|
23
23
|
const TC_TOKEN_BUCKET_DURATION = 604800; // 7 days
|
|
24
24
|
const TC_TOKEN_NUM_BUCKETS = 4; // ~28-day rolling window
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
/** Read the persisted tctoken JID index and return its entries (never contains the sentinel key itself). */
|
|
25
|
+
|
|
26
|
+
/** Read the persisted tctoken JID index and return its entries. */
|
|
28
27
|
export async function readTcTokenIndex(keys) {
|
|
29
|
-
const
|
|
30
|
-
|
|
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
|
-
}
|
|
28
|
+
const batch = await migrateIndexKey(keys, 'tctoken');
|
|
29
|
+
return Object.keys(batch).filter(j => j && j !== TC_TOKEN_INDEX_KEY);
|
|
42
30
|
}
|
|
43
|
-
|
|
31
|
+
|
|
32
|
+
/** Build a SignalDataSet fragment that writes the merged index (persisted ∪ added) under the index key. */
|
|
44
33
|
export async function buildMergedTcTokenIndexWrite(keys, addedJids) {
|
|
45
|
-
const
|
|
46
|
-
const merged =
|
|
47
|
-
for (const jid of addedJids) {
|
|
48
|
-
|
|
49
|
-
merged.add(jid);
|
|
50
|
-
}
|
|
51
|
-
return {
|
|
52
|
-
[TC_TOKEN_INDEX_KEY]: { token: Buffer.from(JSON.stringify([...merged])) }
|
|
53
|
-
};
|
|
34
|
+
const batch = await migrateIndexKey(keys, 'tctoken');
|
|
35
|
+
const merged = { ...batch };
|
|
36
|
+
for (const jid of addedJids) { if (jid && jid !== TC_TOKEN_INDEX_KEY) merged[jid] = merged[jid] || true; }
|
|
37
|
+
return { [TC_TOKEN_INDEX_KEY]: merged };
|
|
54
38
|
}
|
|
39
|
+
|
|
55
40
|
// WA Web has separate sender/receiver AB props for these but they're identical today
|
|
56
41
|
export function isTcTokenExpired(timestamp) {
|
|
57
|
-
if (timestamp === null || timestamp === undefined)
|
|
58
|
-
return true;
|
|
42
|
+
if (timestamp === null || timestamp === undefined) return true;
|
|
59
43
|
const ts = typeof timestamp === 'string' ? parseInt(timestamp) : timestamp;
|
|
60
|
-
if (isNaN(ts))
|
|
61
|
-
return true;
|
|
44
|
+
if (isNaN(ts)) return true;
|
|
62
45
|
const now = Math.floor(Date.now() / 1000);
|
|
63
46
|
const currentBucket = Math.floor(now / TC_TOKEN_BUCKET_DURATION);
|
|
64
47
|
const cutoffBucket = currentBucket - (TC_TOKEN_NUM_BUCKETS - 1);
|
|
65
48
|
const cutoffTimestamp = cutoffBucket * TC_TOKEN_BUCKET_DURATION;
|
|
66
49
|
return ts < cutoffTimestamp;
|
|
67
50
|
}
|
|
51
|
+
|
|
68
52
|
export function shouldSendNewTcToken(senderTimestamp) {
|
|
69
|
-
if (senderTimestamp === undefined)
|
|
70
|
-
return true;
|
|
53
|
+
if (senderTimestamp === undefined) return true;
|
|
71
54
|
const now = Math.floor(Date.now() / 1000);
|
|
72
55
|
const currentBucket = Math.floor(now / TC_TOKEN_BUCKET_DURATION);
|
|
73
56
|
const senderBucket = Math.floor(senderTimestamp / TC_TOKEN_BUCKET_DURATION);
|
|
74
57
|
return currentBucket > senderBucket;
|
|
75
58
|
}
|
|
59
|
+
|
|
76
60
|
/** Resolve JID to LID for tctoken storage (WA Web stores under LID) */
|
|
77
61
|
export async function resolveTcTokenJid(jid, getLIDForPN) {
|
|
78
|
-
if (isLidUser(jid))
|
|
79
|
-
return jid;
|
|
62
|
+
if (isLidUser(jid)) return jid;
|
|
80
63
|
const lid = await getLIDForPN(jid);
|
|
81
64
|
return lid ?? jid;
|
|
82
65
|
}
|
|
66
|
+
|
|
83
67
|
/** Resolve target JID for issuing privacy token based on AB prop 14303 */
|
|
84
68
|
export async function resolveIssuanceJid(jid, issueToLid, getLIDForPN, getPNForLID) {
|
|
85
69
|
if (issueToLid) {
|
|
86
|
-
if (isLidUser(jid))
|
|
87
|
-
return jid;
|
|
70
|
+
if (isLidUser(jid)) return jid;
|
|
88
71
|
const lid = await getLIDForPN(jid);
|
|
89
72
|
return lid ?? jid;
|
|
90
73
|
}
|
|
91
|
-
if (!isLidUser(jid))
|
|
92
|
-
return jid;
|
|
74
|
+
if (!isLidUser(jid)) return jid;
|
|
93
75
|
if (getPNForLID) {
|
|
94
76
|
const pn = await getPNForLID(jid);
|
|
95
77
|
return pn ?? jid;
|
|
96
78
|
}
|
|
97
79
|
return jid;
|
|
98
80
|
}
|
|
81
|
+
|
|
99
82
|
export async function buildTcTokenFromJid({ authState, jid, baseContent = [], getLIDForPN }) {
|
|
100
83
|
try {
|
|
101
84
|
const storageJid = await resolveTcTokenJid(jid, getLIDForPN);
|
|
@@ -114,49 +97,31 @@ export async function buildTcTokenFromJid({ authState, jid, baseContent = [], ge
|
|
|
114
97
|
}
|
|
115
98
|
return baseContent.length > 0 ? baseContent : undefined;
|
|
116
99
|
}
|
|
117
|
-
baseContent.push({
|
|
118
|
-
tag: 'tctoken',
|
|
119
|
-
attrs: {},
|
|
120
|
-
content: tcTokenBuffer
|
|
121
|
-
});
|
|
100
|
+
baseContent.push({ tag: 'tctoken', attrs: {}, content: tcTokenBuffer });
|
|
122
101
|
return baseContent;
|
|
123
|
-
}
|
|
124
|
-
catch (error) {
|
|
102
|
+
} catch (error) {
|
|
125
103
|
return baseContent.length > 0 ? baseContent : undefined;
|
|
126
104
|
}
|
|
127
105
|
}
|
|
106
|
+
|
|
128
107
|
export async function storeTcTokensFromIqResult({ result, fallbackJid, keys, getLIDForPN, onNewJidStored }) {
|
|
129
108
|
const tokensNode = getBinaryNodeChild(result, 'tokens');
|
|
130
|
-
if (!tokensNode)
|
|
131
|
-
return;
|
|
109
|
+
if (!tokensNode) return;
|
|
132
110
|
const tokenNodes = getBinaryNodeChildren(tokensNode, 'token');
|
|
133
111
|
for (const tokenNode of tokenNodes) {
|
|
134
|
-
if (tokenNode.attrs.type !== 'trusted_contact' || !(tokenNode.content instanceof Uint8Array))
|
|
135
|
-
continue;
|
|
136
|
-
}
|
|
112
|
+
if (tokenNode.attrs.type !== 'trusted_contact' || !(tokenNode.content instanceof Uint8Array)) continue;
|
|
137
113
|
// In notifications tokenNode.attrs.jid is your own device JID, not the sender's
|
|
138
114
|
const rawJid = jidNormalizedUser(fallbackJid || tokenNode.attrs.jid);
|
|
139
|
-
if (!isRegularUser(rawJid))
|
|
140
|
-
continue;
|
|
115
|
+
if (!isRegularUser(rawJid)) continue;
|
|
141
116
|
const storageJid = await resolveTcTokenJid(rawJid, getLIDForPN);
|
|
142
117
|
const existingTcData = await keys.get('tctoken', [storageJid]);
|
|
143
118
|
const existingEntry = existingTcData[storageJid];
|
|
144
119
|
const existingTs = existingEntry?.timestamp ? Number(existingEntry.timestamp) : 0;
|
|
145
120
|
const incomingTs = tokenNode.attrs.t ? Number(tokenNode.attrs.t) : 0;
|
|
146
121
|
// timestamp-less tokens would be immediately expired
|
|
147
|
-
if (!incomingTs)
|
|
148
|
-
|
|
149
|
-
|
|
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
|
-
});
|
|
122
|
+
if (!incomingTs) continue;
|
|
123
|
+
if (existingTs > 0 && existingTs > incomingTs) continue;
|
|
124
|
+
await keys.set({ tctoken: { [storageJid]: { ...existingEntry, token: Buffer.from(tokenNode.content), timestamp: tokenNode.attrs.t } } });
|
|
160
125
|
onNewJidStored?.(storageJid);
|
|
161
126
|
}
|
|
162
127
|
}
|
|
@@ -65,7 +65,12 @@ export const useMultiFileAuthState = async (folder, options = {}) => {
|
|
|
65
65
|
const checksum = computeChecksum(serialized)
|
|
66
66
|
const payload = JSON.stringify({ data: JSON.parse(serialized), __checksum: checksum })
|
|
67
67
|
await writeFile(tp, payload)
|
|
68
|
-
|
|
68
|
+
try {
|
|
69
|
+
await rename(tp, fp)
|
|
70
|
+
} catch {
|
|
71
|
+
await writeFile(fp, payload)
|
|
72
|
+
await unlink(tp).catch(() => { })
|
|
73
|
+
}
|
|
69
74
|
} finally {
|
|
70
75
|
release()
|
|
71
76
|
releaseFileLock(fp)
|
|
@@ -84,19 +89,24 @@ export const useMultiFileAuthState = async (folder, options = {}) => {
|
|
|
84
89
|
const parsed = JSON.parse(raw)
|
|
85
90
|
if (parsed.__checksum) {
|
|
86
91
|
const expected = computeChecksum(JSON.stringify(parsed.data))
|
|
87
|
-
if (expected !== parsed.__checksum)
|
|
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
|
+
}
|
|
88
97
|
return JSON.parse(JSON.stringify(parsed.data), BufferJSON.reviver)
|
|
89
98
|
}
|
|
90
|
-
// legacy file
|
|
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(() => { })
|
|
91
103
|
return JSON.parse(raw, BufferJSON.reviver)
|
|
92
104
|
} catch (err) {
|
|
93
|
-
logger?.warn({ file, err: err.message }, 'failed to read auth file')
|
|
105
|
+
logger?.warn({ file, err: err.message }, 'failed to read auth file — reinitializing')
|
|
106
|
+
await unlink(fp).catch(() => { })
|
|
94
107
|
return null
|
|
95
108
|
}
|
|
96
|
-
} finally {
|
|
97
|
-
release()
|
|
98
|
-
releaseFileLock(fp)
|
|
99
|
-
}
|
|
109
|
+
} finally { release(); releaseFileLock(fp) }
|
|
100
110
|
}
|
|
101
111
|
|
|
102
112
|
// ─── Remove File ────────────────────────────────────────────────────────────
|
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.1.
|
|
34
|
+
│ 🔖 Version: 2.1.3 │
|
|
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.1.
|
|
5
|
+
"version": "2.1.3",
|
|
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",
|
|
@@ -100,8 +100,8 @@
|
|
|
100
100
|
"dependencies": {
|
|
101
101
|
"@adiwajshing/keyed-db": "^0.2.4",
|
|
102
102
|
"@cacheable/node-cache": "^1.4.0",
|
|
103
|
-
"@microlink/mql": "^0.16.1",
|
|
104
103
|
"@hapi/boom": "^9.1.3",
|
|
104
|
+
"@microlink/mql": "^0.16.1",
|
|
105
105
|
"async-mutex": "^0.5.0",
|
|
106
106
|
"axios": "^1.6.0",
|
|
107
107
|
"cache-manager": "latest",
|