@mrlegendbot/baileys 1.2.3 → 1.2.4
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.upstream.md +1312 -0
- package/WAProto/WAProto.json +1 -0
- package/WAProto/WAProto.proto +6275 -0
- package/WAProto/compiler.js +550 -0
- package/WAProto/index.js +2 -97691
- package/lib/Defaults/index.js +89 -92
- package/lib/Signal/Group/ciphertext-message.js +9 -10
- package/lib/Signal/Group/group-session-builder.js +21 -22
- package/lib/Signal/Group/group_cipher.js +75 -69
- package/lib/Signal/Group/index.js +0 -1
- package/lib/Signal/Group/keyhelper.js +9 -10
- package/lib/Signal/Group/sender-chain-key.js +21 -22
- package/lib/Signal/Group/sender-key-distribution-message.js +51 -60
- package/lib/Signal/Group/sender-key-message.js +56 -62
- package/lib/Signal/Group/sender-key-name.js +38 -40
- package/lib/Signal/Group/sender-key-record.js +33 -34
- package/lib/Signal/Group/sender-key-state.js +72 -76
- package/lib/Signal/Group/sender-message-key.js +22 -23
- package/lib/Signal/libsignal.js +506 -405
- package/lib/Signal/lid-mapping.js +276 -258
- package/lib/Socket/Client/index.js +0 -1
- package/lib/Socket/Client/types.js +6 -7
- package/lib/Socket/Client/websocket.js +48 -49
- package/lib/Socket/business.js +349 -369
- package/lib/Socket/chats.js +1172 -1148
- package/lib/Socket/communities.js +473 -413
- package/lib/Socket/groups.js +387 -331
- package/lib/Socket/index.js +6 -8
- package/lib/Socket/messages-recv.js +1782 -1719
- package/lib/Socket/messages-send.js +1503 -2450
- package/lib/Socket/mex.js +40 -35
- package/lib/Socket/newsletter.js +239 -191
- package/lib/Socket/socket.js +992 -923
- package/lib/Store/index.js +8 -0
- package/lib/Store/keyed-db.js +108 -0
- package/lib/Store/make-cache-manager-store.js +70 -0
- package/lib/Store/make-in-memory-store.js +417 -0
- package/lib/Store/make-ordered-dictionary.js +69 -0
- package/lib/Store/object-repository.js +27 -0
- package/lib/Types/Auth.js +0 -1
- package/lib/Types/Bussines.js +0 -1
- package/lib/Types/Call.js +0 -1
- package/lib/Types/Chat.js +1 -8
- package/lib/Types/Contact.js +0 -1
- package/lib/Types/Events.js +1 -2
- package/lib/Types/GroupMetadata.js +0 -1
- package/lib/Types/Label.js +20 -22
- package/lib/Types/LabelAssociation.js +2 -4
- package/lib/Types/Message.js +3 -5
- package/lib/Types/Mex.js +26 -31
- package/lib/Types/Product.js +0 -1
- package/lib/Types/Signal.js +1 -2
- package/lib/Types/Socket.js +2 -3
- package/lib/Types/State.js +4 -52
- package/lib/Types/USync.js +0 -1
- package/lib/Types/index.js +10 -11
- package/lib/Utils/auth-utils.js +240 -277
- package/lib/Utils/browser-utils.js +20 -23
- package/lib/Utils/business.js +200 -204
- package/lib/Utils/chat-utils.js +836 -810
- package/lib/Utils/companion-reg-client-utils.js +23 -25
- package/lib/Utils/crypto.js +70 -86
- package/lib/Utils/decode-wa-message.js +277 -284
- package/lib/Utils/event-buffer.js +592 -595
- package/lib/Utils/generics.js +312 -335
- package/lib/Utils/history.js +123 -115
- package/lib/Utils/index.js +1 -2
- package/lib/Utils/link-preview.js +74 -73
- package/lib/Utils/logger.js +3 -2
- package/lib/Utils/lt-hash.js +0 -6
- package/lib/Utils/make-mutex.js +27 -27
- package/lib/Utils/message-retry-manager.js +213 -254
- package/lib/Utils/messages-media.js +741 -715
- package/lib/Utils/messages.js +791 -827
- package/lib/Utils/noise-handler.js +215 -187
- package/lib/Utils/pre-key-manager.js +69 -98
- package/lib/Utils/process-message.js +617 -586
- package/lib/Utils/reporting-utils.js +553 -234
- package/lib/Utils/signal.js +217 -170
- package/lib/Utils/stanza-ack.js +27 -35
- package/lib/Utils/sync-action-utils.js +37 -41
- package/lib/Utils/tc-token-utils.js +120 -141
- package/lib/Utils/use-multi-file-auth-state.js +87 -105
- package/lib/Utils/use-sqlite-auth-state.js +162 -0
- package/lib/Utils/validate-connection.js +190 -188
- package/lib/WABinary/constants.js +28 -1294
- package/lib/WABinary/decode.js +245 -249
- package/lib/WABinary/encode.js +201 -206
- package/lib/WABinary/generic-utils.js +86 -89
- package/lib/WABinary/index.js +0 -1
- package/lib/WABinary/jid-utils.js +68 -77
- package/lib/WABinary/types.js +0 -1
- package/lib/WAM/BinaryInfo.js +7 -8
- package/lib/WAM/constants.js +19892 -22853
- package/lib/WAM/encode.js +121 -132
- package/lib/WAM/index.js +0 -1
- package/lib/WAUSync/Protocols/USyncContactProtocol.js +46 -43
- package/lib/WAUSync/Protocols/USyncDeviceProtocol.js +47 -48
- package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.js +22 -23
- package/lib/WAUSync/Protocols/USyncStatusProtocol.js +30 -33
- package/lib/WAUSync/Protocols/USyncUsernameProtocol.js +19 -20
- package/lib/WAUSync/Protocols/UsyncBotProfileProtocol.js +53 -44
- package/lib/WAUSync/Protocols/UsyncLIDProtocol.js +24 -24
- package/lib/WAUSync/Protocols/index.js +0 -1
- package/lib/WAUSync/USyncQuery.js +79 -88
- package/lib/WAUSync/USyncUser.js +28 -29
- package/lib/WAUSync/index.js +0 -1
- package/lib/index.js +2 -2
- package/package.json +22 -86
- package/WAProto/GenerateStatics.sh +0 -3
- package/WAProto/fix-imports.js +0 -85
- package/WAProto/index.d.ts +0 -14017
- package/lib/Defaults/index.d.ts +0 -75
- package/lib/Signal/Group/ciphertext-message.d.ts +0 -10
- package/lib/Signal/Group/group-session-builder.d.ts +0 -15
- package/lib/Signal/Group/group_cipher.d.ts +0 -17
- package/lib/Signal/Group/index.d.ts +0 -12
- package/lib/Signal/Group/keyhelper.d.ts +0 -11
- package/lib/Signal/Group/sender-chain-key.d.ts +0 -14
- package/lib/Signal/Group/sender-key-distribution-message.d.ts +0 -17
- package/lib/Signal/Group/sender-key-message.d.ts +0 -19
- package/lib/Signal/Group/sender-key-name.d.ts +0 -18
- package/lib/Signal/Group/sender-key-record.d.ts +0 -31
- package/lib/Signal/Group/sender-key-state.d.ts +0 -39
- package/lib/Signal/Group/sender-message-key.d.ts +0 -12
- package/lib/Signal/libsignal.d.ts +0 -5
- package/lib/Signal/lid-mapping.d.ts +0 -23
- package/lib/Socket/Client/index.d.ts +0 -3
- package/lib/Socket/Client/types.d.ts +0 -16
- package/lib/Socket/Client/websocket.d.ts +0 -13
- package/lib/Socket/business.d.ts +0 -217
- package/lib/Socket/chats.d.ts +0 -125
- package/lib/Socket/communities.d.ts +0 -273
- package/lib/Socket/groups.d.ts +0 -161
- package/lib/Socket/index.d.ts +0 -260
- package/lib/Socket/messages-recv.d.ts +0 -203
- package/lib/Socket/messages-send.d.ts +0 -199
- package/lib/Socket/mex.d.ts +0 -3
- package/lib/Socket/newsletter.d.ts +0 -170
- package/lib/Socket/socket.d.ts +0 -59
- package/lib/Types/Auth.d.ts +0 -117
- package/lib/Types/Bussines.d.ts +0 -25
- package/lib/Types/Call.d.ts +0 -15
- package/lib/Types/Chat.d.ts +0 -124
- package/lib/Types/Contact.d.ts +0 -26
- package/lib/Types/Events.d.ts +0 -256
- package/lib/Types/GroupMetadata.d.ts +0 -71
- package/lib/Types/Label.d.ts +0 -47
- package/lib/Types/LabelAssociation.d.ts +0 -30
- package/lib/Types/Message.d.ts +0 -320
- package/lib/Types/Mex.d.ts +0 -141
- package/lib/Types/Product.d.ts +0 -79
- package/lib/Types/Signal.d.ts +0 -87
- package/lib/Types/Socket.d.ts +0 -136
- package/lib/Types/State.d.ts +0 -97
- package/lib/Types/USync.d.ts +0 -26
- package/lib/Types/index.d.ts +0 -66
- package/lib/Utils/auth-utils.d.ts +0 -24
- package/lib/Utils/browser-utils.d.ts +0 -4
- package/lib/Utils/business.d.ts +0 -23
- package/lib/Utils/chat-utils.d.ts +0 -100
- package/lib/Utils/companion-reg-client-utils.d.ts +0 -17
- package/lib/Utils/crypto.d.ts +0 -37
- package/lib/Utils/decode-wa-message.d.ts +0 -66
- package/lib/Utils/event-buffer.d.ts +0 -36
- package/lib/Utils/generics.d.ts +0 -92
- package/lib/Utils/history.d.ts +0 -24
- package/lib/Utils/identity-change-handler.d.ts +0 -44
- package/lib/Utils/identity-change-handler.js +0 -50
- package/lib/Utils/index.d.ts +0 -22
- package/lib/Utils/link-preview.d.ts +0 -21
- package/lib/Utils/logger.d.ts +0 -12
- package/lib/Utils/lt-hash.d.ts +0 -8
- package/lib/Utils/make-mutex.d.ts +0 -9
- package/lib/Utils/message-retry-manager.d.ts +0 -115
- package/lib/Utils/messages-media.d.ts +0 -133
- package/lib/Utils/messages.d.ts +0 -91
- package/lib/Utils/noise-handler.d.ts +0 -20
- package/lib/Utils/offline-node-processor.d.ts +0 -17
- package/lib/Utils/offline-node-processor.js +0 -40
- package/lib/Utils/pre-key-manager.d.ts +0 -28
- package/lib/Utils/process-message.d.ts +0 -60
- package/lib/Utils/reporting-utils.d.ts +0 -11
- package/lib/Utils/signal.d.ts +0 -47
- package/lib/Utils/stanza-ack.d.ts +0 -11
- package/lib/Utils/sync-action-utils.d.ts +0 -19
- package/lib/Utils/tc-token-utils.d.ts +0 -37
- package/lib/Utils/use-multi-file-auth-state.d.ts +0 -13
- package/lib/Utils/validate-connection.d.ts +0 -11
- package/lib/WABinary/constants.d.ts +0 -28
- package/lib/WABinary/decode.d.ts +0 -7
- package/lib/WABinary/encode.d.ts +0 -3
- package/lib/WABinary/generic-utils.d.ts +0 -15
- package/lib/WABinary/index.d.ts +0 -6
- package/lib/WABinary/jid-utils.d.ts +0 -48
- package/lib/WABinary/types.d.ts +0 -19
- package/lib/WAM/BinaryInfo.d.ts +0 -9
- package/lib/WAM/constants.d.ts +0 -40
- package/lib/WAM/encode.d.ts +0 -3
- package/lib/WAM/index.d.ts +0 -4
- package/lib/WAUSync/Protocols/USyncContactProtocol.d.ts +0 -10
- package/lib/WAUSync/Protocols/USyncDeviceProtocol.d.ts +0 -23
- package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.d.ts +0 -13
- package/lib/WAUSync/Protocols/USyncStatusProtocol.d.ts +0 -13
- package/lib/WAUSync/Protocols/USyncUsernameProtocol.d.ts +0 -10
- package/lib/WAUSync/Protocols/UsyncBotProfileProtocol.d.ts +0 -26
- package/lib/WAUSync/Protocols/UsyncLIDProtocol.d.ts +0 -10
- package/lib/WAUSync/Protocols/index.d.ts +0 -6
- package/lib/WAUSync/USyncQuery.d.ts +0 -30
- package/lib/WAUSync/USyncUser.d.ts +0 -17
- package/lib/WAUSync/index.d.ts +0 -4
- package/lib/index.d.ts +0 -12
|
@@ -1,164 +1,143 @@
|
|
|
1
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
2
|
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
3
|
function isRegularUser(jid) {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
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'));
|
|
4
|
+
if (!jid) return false;
|
|
5
|
+
const user = jid.split('@')[0] ?? '';
|
|
6
|
+
if (user === '0') return false;
|
|
7
|
+
if (BOT_PHONE_REGEX.test(user)) return false;
|
|
8
|
+
if (isJidMetaAI(jid)) return false;
|
|
9
|
+
return !!(isPnUser(jid) || isLidUser(jid) || isHostedPnUser(jid) || isHostedLidUser(jid) || jid.endsWith('@c.us'));
|
|
22
10
|
}
|
|
23
|
-
const TC_TOKEN_BUCKET_DURATION = 604800;
|
|
24
|
-
const TC_TOKEN_NUM_BUCKETS = 4;
|
|
25
|
-
/** Sentinel key under `tctoken` store holding a JSON array of tracked storage JIDs for cross-session pruning. */
|
|
11
|
+
const TC_TOKEN_BUCKET_DURATION = 604800;
|
|
12
|
+
const TC_TOKEN_NUM_BUCKETS = 4;
|
|
26
13
|
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
14
|
export async function readTcTokenIndex(keys) {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
catch {
|
|
40
|
-
return [];
|
|
41
|
-
}
|
|
15
|
+
const data = await keys.get('tctoken', [TC_TOKEN_INDEX_KEY]);
|
|
16
|
+
const entry = data[TC_TOKEN_INDEX_KEY];
|
|
17
|
+
if (!entry?.token?.length) return [];
|
|
18
|
+
try {
|
|
19
|
+
const parsed = JSON.parse(Buffer.from(entry.token).toString());
|
|
20
|
+
if (!Array.isArray(parsed)) return [];
|
|
21
|
+
return parsed.filter(j => typeof j === 'string' && j.length > 0 && j !== TC_TOKEN_INDEX_KEY);
|
|
22
|
+
} catch {
|
|
23
|
+
return [];
|
|
24
|
+
}
|
|
42
25
|
}
|
|
43
|
-
/** Build a SignalDataSet fragment that writes the merged index (persisted ∪ added) under the sentinel key. */
|
|
44
26
|
export async function buildMergedTcTokenIndexWrite(keys, addedJids) {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
27
|
+
const persisted = await readTcTokenIndex(keys);
|
|
28
|
+
const merged = new Set(persisted);
|
|
29
|
+
for (const jid of addedJids) {
|
|
30
|
+
if (jid && jid !== TC_TOKEN_INDEX_KEY) merged.add(jid);
|
|
31
|
+
}
|
|
32
|
+
return {
|
|
33
|
+
[TC_TOKEN_INDEX_KEY]: {
|
|
34
|
+
token: Buffer.from(JSON.stringify([...merged]))
|
|
50
35
|
}
|
|
51
|
-
|
|
52
|
-
[TC_TOKEN_INDEX_KEY]: { token: Buffer.from(JSON.stringify([...merged])) }
|
|
53
|
-
};
|
|
36
|
+
};
|
|
54
37
|
}
|
|
55
|
-
// WA Web has separate sender/receiver AB props for these but they're identical today
|
|
56
38
|
export function isTcTokenExpired(timestamp) {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
const cutoffTimestamp = cutoffBucket * TC_TOKEN_BUCKET_DURATION;
|
|
66
|
-
return ts < cutoffTimestamp;
|
|
39
|
+
if (timestamp === null || timestamp === undefined) return true;
|
|
40
|
+
const ts = typeof timestamp === 'string' ? parseInt(timestamp) : timestamp;
|
|
41
|
+
if (isNaN(ts)) return true;
|
|
42
|
+
const now = Math.floor(Date.now() / 1000);
|
|
43
|
+
const currentBucket = Math.floor(now / TC_TOKEN_BUCKET_DURATION);
|
|
44
|
+
const cutoffBucket = currentBucket - (TC_TOKEN_NUM_BUCKETS - 1);
|
|
45
|
+
const cutoffTimestamp = cutoffBucket * TC_TOKEN_BUCKET_DURATION;
|
|
46
|
+
return ts < cutoffTimestamp;
|
|
67
47
|
}
|
|
68
48
|
export function shouldSendNewTcToken(senderTimestamp) {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
return currentBucket > senderBucket;
|
|
49
|
+
if (senderTimestamp === undefined) return true;
|
|
50
|
+
const now = Math.floor(Date.now() / 1000);
|
|
51
|
+
const currentBucket = Math.floor(now / TC_TOKEN_BUCKET_DURATION);
|
|
52
|
+
const senderBucket = Math.floor(senderTimestamp / TC_TOKEN_BUCKET_DURATION);
|
|
53
|
+
return currentBucket > senderBucket;
|
|
75
54
|
}
|
|
76
|
-
/** Resolve JID to LID for tctoken storage (WA Web stores under LID) */
|
|
77
55
|
export async function resolveTcTokenJid(jid, getLIDForPN) {
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
return lid ?? jid;
|
|
56
|
+
if (isLidUser(jid)) return jid;
|
|
57
|
+
const lid = await getLIDForPN(jid);
|
|
58
|
+
return lid ?? jid;
|
|
82
59
|
}
|
|
83
|
-
/** Resolve target JID for issuing privacy token based on AB prop 14303 */
|
|
84
60
|
export async function resolveIssuanceJid(jid, issueToLid, getLIDForPN, getPNForLID) {
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
}
|
|
97
|
-
return jid;
|
|
61
|
+
if (issueToLid) {
|
|
62
|
+
if (isLidUser(jid)) return jid;
|
|
63
|
+
const lid = await getLIDForPN(jid);
|
|
64
|
+
return lid ?? jid;
|
|
65
|
+
}
|
|
66
|
+
if (!isLidUser(jid)) return jid;
|
|
67
|
+
if (getPNForLID) {
|
|
68
|
+
const pn = await getPNForLID(jid);
|
|
69
|
+
return pn ?? jid;
|
|
70
|
+
}
|
|
71
|
+
return jid;
|
|
98
72
|
}
|
|
99
|
-
export async function buildTcTokenFromJid({
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
content: tcTokenBuffer
|
|
73
|
+
export async function buildTcTokenFromJid({
|
|
74
|
+
authState,
|
|
75
|
+
jid,
|
|
76
|
+
baseContent = [],
|
|
77
|
+
getLIDForPN
|
|
78
|
+
}) {
|
|
79
|
+
try {
|
|
80
|
+
const storageJid = await resolveTcTokenJid(jid, getLIDForPN);
|
|
81
|
+
const tcTokenData = await authState.keys.get('tctoken', [storageJid]);
|
|
82
|
+
const entry = tcTokenData?.[storageJid];
|
|
83
|
+
const tcTokenBuffer = entry?.token;
|
|
84
|
+
const timestamp = entry?.timestamp;
|
|
85
|
+
if (!tcTokenBuffer?.length || timestamp === undefined || isTcTokenExpired(timestamp)) {
|
|
86
|
+
if (tcTokenBuffer) {
|
|
87
|
+
const cleared = entry?.senderTimestamp !== undefined ? {
|
|
88
|
+
token: Buffer.alloc(0),
|
|
89
|
+
senderTimestamp: entry.senderTimestamp
|
|
90
|
+
} : null;
|
|
91
|
+
await authState.keys.set({
|
|
92
|
+
tctoken: {
|
|
93
|
+
[storageJid]: cleared
|
|
94
|
+
}
|
|
122
95
|
});
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
catch (error) {
|
|
126
|
-
return baseContent.length > 0 ? baseContent : undefined;
|
|
96
|
+
}
|
|
97
|
+
return baseContent.length > 0 ? baseContent : undefined;
|
|
127
98
|
}
|
|
99
|
+
baseContent.push({
|
|
100
|
+
tag: 'tctoken',
|
|
101
|
+
attrs: { t: String(timestamp) },
|
|
102
|
+
content: tcTokenBuffer
|
|
103
|
+
});
|
|
104
|
+
return baseContent;
|
|
105
|
+
} catch (error) {
|
|
106
|
+
return baseContent.length > 0 ? baseContent : undefined;
|
|
107
|
+
}
|
|
128
108
|
}
|
|
129
|
-
export async function storeTcTokensFromIqResult({
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
const storageJid = await resolveTcTokenJid(rawJid, getLIDForPN);
|
|
143
|
-
const existingTcData = await keys.get('tctoken', [storageJid]);
|
|
144
|
-
const existingEntry = existingTcData[storageJid];
|
|
145
|
-
const existingTs = existingEntry?.timestamp ? Number(existingEntry.timestamp) : 0;
|
|
146
|
-
const incomingTs = tokenNode.attrs.t ? Number(tokenNode.attrs.t) : 0;
|
|
147
|
-
// timestamp-less tokens would be immediately expired
|
|
148
|
-
if (!incomingTs)
|
|
149
|
-
continue;
|
|
150
|
-
if (existingTs > 0 && existingTs > incomingTs)
|
|
151
|
-
continue;
|
|
152
|
-
await keys.set({
|
|
153
|
-
tctoken: {
|
|
154
|
-
[storageJid]: {
|
|
155
|
-
...existingEntry,
|
|
156
|
-
token: Buffer.from(tokenNode.content),
|
|
157
|
-
timestamp: tokenNode.attrs.t
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
});
|
|
161
|
-
onNewJidStored?.(storageJid);
|
|
109
|
+
export async function storeTcTokensFromIqResult({
|
|
110
|
+
result,
|
|
111
|
+
fallbackJid,
|
|
112
|
+
keys,
|
|
113
|
+
getLIDForPN,
|
|
114
|
+
onNewJidStored
|
|
115
|
+
}) {
|
|
116
|
+
const tokensNode = getBinaryNodeChild(result, 'tokens');
|
|
117
|
+
if (!tokensNode) return;
|
|
118
|
+
const tokenNodes = getBinaryNodeChildren(tokensNode, 'token');
|
|
119
|
+
for (const tokenNode of tokenNodes) {
|
|
120
|
+
if (tokenNode.attrs.type !== 'trusted_contact' || !(tokenNode.content instanceof Uint8Array)) {
|
|
121
|
+
continue;
|
|
162
122
|
}
|
|
123
|
+
const rawJid = jidNormalizedUser(fallbackJid || tokenNode.attrs.jid);
|
|
124
|
+
if (!isRegularUser(rawJid)) continue;
|
|
125
|
+
const storageJid = await resolveTcTokenJid(rawJid, getLIDForPN);
|
|
126
|
+
const existingTcData = await keys.get('tctoken', [storageJid]);
|
|
127
|
+
const existingEntry = existingTcData[storageJid];
|
|
128
|
+
const existingTs = existingEntry?.timestamp ? Number(existingEntry.timestamp) : 0;
|
|
129
|
+
const incomingTs = tokenNode.attrs.t ? Number(tokenNode.attrs.t) : 0;
|
|
130
|
+
if (!incomingTs) continue;
|
|
131
|
+
if (existingTs > 0 && existingTs > incomingTs) continue;
|
|
132
|
+
await keys.set({
|
|
133
|
+
tctoken: {
|
|
134
|
+
[storageJid]: {
|
|
135
|
+
...existingEntry,
|
|
136
|
+
token: Buffer.from(tokenNode.content),
|
|
137
|
+
timestamp: tokenNode.attrs.t
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
onNewJidStored?.(storageJid);
|
|
142
|
+
}
|
|
163
143
|
}
|
|
164
|
-
//# sourceMappingURL=tc-token-utils.js.map
|
|
@@ -1,121 +1,103 @@
|
|
|
1
1
|
import { Mutex } from 'async-mutex';
|
|
2
2
|
import { mkdir, readFile, stat, unlink, writeFile } from 'fs/promises';
|
|
3
3
|
import { join } from 'path';
|
|
4
|
-
import { proto } from '../../WAProto/
|
|
4
|
+
import { proto } from '../../WAProto/compiler.js';
|
|
5
5
|
import { initAuthCreds } from './auth-utils.js';
|
|
6
6
|
import { BufferJSON } from './generics.js';
|
|
7
|
-
// We need to lock files due to the fact that we are using async functions to read and write files
|
|
8
|
-
// https://github.com/WhiskeySockets/Baileys/issues/794
|
|
9
|
-
// https://github.com/nodejs/node/issues/26338
|
|
10
|
-
// Use a Map to store mutexes for each file path
|
|
11
7
|
const fileLocks = new Map();
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
return mutex;
|
|
8
|
+
const getFileLock = path => {
|
|
9
|
+
let mutex = fileLocks.get(path);
|
|
10
|
+
if (!mutex) {
|
|
11
|
+
mutex = new Mutex();
|
|
12
|
+
fileLocks.set(path, mutex);
|
|
13
|
+
}
|
|
14
|
+
return mutex;
|
|
20
15
|
};
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
release();
|
|
39
|
-
}
|
|
40
|
-
});
|
|
41
|
-
};
|
|
42
|
-
const readData = async (file) => {
|
|
16
|
+
export const useMultiFileAuthState = async folder => {
|
|
17
|
+
const writeData = async (data, file) => {
|
|
18
|
+
const filePath = join(folder, fixFileName(file));
|
|
19
|
+
const mutex = getFileLock(filePath);
|
|
20
|
+
return mutex.acquire().then(async release => {
|
|
21
|
+
try {
|
|
22
|
+
await writeFile(filePath, JSON.stringify(data, BufferJSON.replacer));
|
|
23
|
+
} finally {
|
|
24
|
+
release();
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
};
|
|
28
|
+
const readData = async file => {
|
|
29
|
+
try {
|
|
30
|
+
const filePath = join(folder, fixFileName(file));
|
|
31
|
+
const mutex = getFileLock(filePath);
|
|
32
|
+
return await mutex.acquire().then(async release => {
|
|
43
33
|
try {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
}
|
|
51
|
-
finally {
|
|
52
|
-
release();
|
|
53
|
-
}
|
|
54
|
-
});
|
|
34
|
+
const data = await readFile(filePath, {
|
|
35
|
+
encoding: 'utf-8'
|
|
36
|
+
});
|
|
37
|
+
return JSON.parse(data, BufferJSON.reviver);
|
|
38
|
+
} finally {
|
|
39
|
+
release();
|
|
55
40
|
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
}
|
|
60
|
-
|
|
41
|
+
});
|
|
42
|
+
} catch (error) {
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
const removeData = async file => {
|
|
47
|
+
try {
|
|
48
|
+
const filePath = join(folder, fixFileName(file));
|
|
49
|
+
const mutex = getFileLock(filePath);
|
|
50
|
+
return mutex.acquire().then(async release => {
|
|
61
51
|
try {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
try {
|
|
66
|
-
await unlink(filePath);
|
|
67
|
-
}
|
|
68
|
-
catch {
|
|
69
|
-
}
|
|
70
|
-
finally {
|
|
71
|
-
release();
|
|
72
|
-
}
|
|
73
|
-
});
|
|
52
|
+
await unlink(filePath);
|
|
53
|
+
} catch {} finally {
|
|
54
|
+
release();
|
|
74
55
|
}
|
|
75
|
-
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
}
|
|
83
|
-
else {
|
|
84
|
-
await mkdir(folder, { recursive: true });
|
|
56
|
+
});
|
|
57
|
+
} catch {}
|
|
58
|
+
};
|
|
59
|
+
const folderInfo = await stat(folder).catch(() => {});
|
|
60
|
+
if (folderInfo) {
|
|
61
|
+
if (!folderInfo.isDirectory()) {
|
|
62
|
+
throw new Error(`found something that is not a directory at ${folder}, either delete it or specify a different location`);
|
|
85
63
|
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
set: async (data) => {
|
|
104
|
-
const tasks = [];
|
|
105
|
-
for (const category in data) {
|
|
106
|
-
for (const id in data[category]) {
|
|
107
|
-
const value = data[category][id];
|
|
108
|
-
const file = `${category}-${id}.json`;
|
|
109
|
-
tasks.push(value ? writeData(value, file) : removeData(file));
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
await Promise.all(tasks);
|
|
113
|
-
}
|
|
64
|
+
} else {
|
|
65
|
+
await mkdir(folder, {
|
|
66
|
+
recursive: true
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
const fixFileName = file => file?.replace(/\//g, '__')?.replace(/:/g, '-');
|
|
70
|
+
const creds = (await readData('creds.json')) || initAuthCreds();
|
|
71
|
+
return {
|
|
72
|
+
state: {
|
|
73
|
+
creds,
|
|
74
|
+
keys: {
|
|
75
|
+
get: async (type, ids) => {
|
|
76
|
+
const data = {};
|
|
77
|
+
await Promise.all(ids.map(async id => {
|
|
78
|
+
let value = await readData(`${type}-${id}.json`);
|
|
79
|
+
if (type === 'app-state-sync-key' && value) {
|
|
80
|
+
value = proto.Message.AppStateSyncKeyData.fromObject(value);
|
|
114
81
|
}
|
|
82
|
+
data[id] = value;
|
|
83
|
+
}));
|
|
84
|
+
return data;
|
|
115
85
|
},
|
|
116
|
-
|
|
117
|
-
|
|
86
|
+
set: async data => {
|
|
87
|
+
const tasks = [];
|
|
88
|
+
for (const category in data) {
|
|
89
|
+
for (const id in data[category]) {
|
|
90
|
+
const value = data[category][id];
|
|
91
|
+
const file = `${category}-${id}.json`;
|
|
92
|
+
tasks.push(value ? writeData(value, file) : removeData(file));
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
await Promise.all(tasks);
|
|
118
96
|
}
|
|
119
|
-
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
saveCreds: async () => {
|
|
100
|
+
return writeData(creds, 'creds.json');
|
|
101
|
+
}
|
|
102
|
+
};
|
|
120
103
|
};
|
|
121
|
-
//# sourceMappingURL=use-multi-file-auth-state.js.map
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { mkdir, readdir, readFile } from 'fs/promises';
|
|
2
|
+
import { dirname, join } from 'path';
|
|
3
|
+
import { proto } from '../../WAProto/compiler.js';
|
|
4
|
+
import { initAuthCreds } from './auth-utils.js';
|
|
5
|
+
export const useSqliteAuthState = async (pathOrFolder, options = {}) => {
|
|
6
|
+
const {
|
|
7
|
+
fileName = 'auth.db',
|
|
8
|
+
migrateFromFolder,
|
|
9
|
+
logger
|
|
10
|
+
} = options;
|
|
11
|
+
const dbPath = /\.(db|sqlite|sqlite3)$/i.test(pathOrFolder) ? pathOrFolder : join(pathOrFolder, fileName);
|
|
12
|
+
const bufReplacer = (_, value) => {
|
|
13
|
+
if (Buffer.isBuffer(value) || value instanceof Uint8Array || value?.type === 'Buffer') {
|
|
14
|
+
return {
|
|
15
|
+
type: 'Buffer',
|
|
16
|
+
data: Buffer.from(value?.data || value).toString('base64')
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
return value;
|
|
20
|
+
};
|
|
21
|
+
const bufReviver = (_, value) => {
|
|
22
|
+
if (value && typeof value === 'object' && value.type === 'Buffer' && typeof value.data === 'string') {
|
|
23
|
+
return Buffer.from(value.data, 'base64');
|
|
24
|
+
}
|
|
25
|
+
return value;
|
|
26
|
+
};
|
|
27
|
+
const encode = value => {
|
|
28
|
+
if (Buffer.isBuffer(value) || value instanceof Uint8Array) {
|
|
29
|
+
return Buffer.concat([Buffer.from([1]), Buffer.from(value)]);
|
|
30
|
+
}
|
|
31
|
+
return Buffer.concat([Buffer.from([0]), Buffer.from(JSON.stringify(value, bufReplacer), 'utf8')]);
|
|
32
|
+
};
|
|
33
|
+
const decode = blob => {
|
|
34
|
+
if (!blob || blob.length === 0) return null;
|
|
35
|
+
if (blob[0] === 1) return Buffer.from(blob.subarray(1));
|
|
36
|
+
return JSON.parse(Buffer.from(blob.subarray(1)).toString('utf8'), bufReviver);
|
|
37
|
+
};
|
|
38
|
+
const fixName = s => s?.replace(/\//g, '__')?.replace(/:/g, '-');
|
|
39
|
+
const keyOf = (category, id) => fixName(`${category}-${id}`);
|
|
40
|
+
let DatabaseSync;
|
|
41
|
+
try {
|
|
42
|
+
({
|
|
43
|
+
DatabaseSync
|
|
44
|
+
} = await import('node:sqlite'));
|
|
45
|
+
} catch (err) {
|
|
46
|
+
throw new Error("useSqliteAuthState needs the built-in 'node:sqlite' module (Node 22.5+). Upgrade Node, or use useMultiFileAuthState instead.");
|
|
47
|
+
}
|
|
48
|
+
await mkdir(dirname(dbPath), {
|
|
49
|
+
recursive: true
|
|
50
|
+
}).catch(() => {});
|
|
51
|
+
const db = new DatabaseSync(dbPath);
|
|
52
|
+
db.exec('PRAGMA journal_mode = TRUNCATE');
|
|
53
|
+
db.exec('PRAGMA synchronous = NORMAL');
|
|
54
|
+
db.exec('PRAGMA busy_timeout = 5000');
|
|
55
|
+
db.exec('CREATE TABLE IF NOT EXISTS auth_state (k TEXT PRIMARY KEY, v BLOB NOT NULL) WITHOUT ROWID');
|
|
56
|
+
const qGet = db.prepare('SELECT v FROM auth_state WHERE k = ?');
|
|
57
|
+
const qUpsert = db.prepare('INSERT INTO auth_state(k,v) VALUES(?,?) ON CONFLICT(k) DO UPDATE SET v=excluded.v');
|
|
58
|
+
const qDelete = db.prepare('DELETE FROM auth_state WHERE k = ?');
|
|
59
|
+
const runTx = fn => {
|
|
60
|
+
db.exec('BEGIN');
|
|
61
|
+
try {
|
|
62
|
+
const r = fn();
|
|
63
|
+
db.exec('COMMIT');
|
|
64
|
+
return r;
|
|
65
|
+
} catch (err) {
|
|
66
|
+
db.exec('ROLLBACK');
|
|
67
|
+
throw err;
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
const readRaw = k => {
|
|
71
|
+
const row = qGet.get(k);
|
|
72
|
+
if (!row) return null;
|
|
73
|
+
try {
|
|
74
|
+
return decode(row.v);
|
|
75
|
+
} catch (err) {
|
|
76
|
+
logger?.warn?.({
|
|
77
|
+
k,
|
|
78
|
+
err: err?.message
|
|
79
|
+
}, 'sqlite-auth: failed to decode row, treating as missing');
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
const runMigration = async folder => {
|
|
84
|
+
let files;
|
|
85
|
+
try {
|
|
86
|
+
files = await readdir(folder);
|
|
87
|
+
} catch {
|
|
88
|
+
return 0;
|
|
89
|
+
}
|
|
90
|
+
const rows = [];
|
|
91
|
+
for (const file of files) {
|
|
92
|
+
if (!file.endsWith('.json')) continue;
|
|
93
|
+
let value;
|
|
94
|
+
try {
|
|
95
|
+
value = JSON.parse(await readFile(join(folder, file), 'utf8'), bufReviver);
|
|
96
|
+
} catch {
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
if (value === null || value === undefined) continue;
|
|
100
|
+
rows.push([file.slice(0, -'.json'.length), encode(value)]);
|
|
101
|
+
}
|
|
102
|
+
runTx(() => {
|
|
103
|
+
for (const [k, v] of rows) qUpsert.run(k, v);
|
|
104
|
+
});
|
|
105
|
+
return rows.length;
|
|
106
|
+
};
|
|
107
|
+
const hasCreds = () => !!qGet.get('creds');
|
|
108
|
+
if (migrateFromFolder && !hasCreds()) {
|
|
109
|
+
const n = await runMigration(migrateFromFolder);
|
|
110
|
+
if (n > 0) logger?.info?.({
|
|
111
|
+
count: n,
|
|
112
|
+
from: migrateFromFolder
|
|
113
|
+
}, 'sqlite-auth: migrated legacy auth state');
|
|
114
|
+
}
|
|
115
|
+
let creds = readRaw('creds');
|
|
116
|
+
if (!creds) {
|
|
117
|
+
creds = initAuthCreds();
|
|
118
|
+
qUpsert.run('creds', encode(creds));
|
|
119
|
+
}
|
|
120
|
+
const keys = {
|
|
121
|
+
get: async (type, ids) => {
|
|
122
|
+
const data = {};
|
|
123
|
+
for (const id of ids) {
|
|
124
|
+
let value = readRaw(keyOf(type, id));
|
|
125
|
+
if (type === 'app-state-sync-key' && value) {
|
|
126
|
+
value = proto.Message.AppStateSyncKeyData.fromObject(value);
|
|
127
|
+
}
|
|
128
|
+
if (value !== null && value !== undefined) {
|
|
129
|
+
data[id] = value;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return data;
|
|
133
|
+
},
|
|
134
|
+
set: async data => {
|
|
135
|
+
const ops = [];
|
|
136
|
+
for (const category in data) {
|
|
137
|
+
for (const id in data[category]) {
|
|
138
|
+
ops.push([keyOf(category, id), data[category][id]]);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
runTx(() => {
|
|
142
|
+
for (const [k, value] of ops) {
|
|
143
|
+
if (value === null || value === undefined) qDelete.run(k);else qUpsert.run(k, encode(value));
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
},
|
|
147
|
+
clear: async () => {
|
|
148
|
+
db.prepare("DELETE FROM auth_state WHERE k <> 'creds'").run();
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
return {
|
|
152
|
+
state: {
|
|
153
|
+
creds,
|
|
154
|
+
keys
|
|
155
|
+
},
|
|
156
|
+
saveCreds: async () => {
|
|
157
|
+
qUpsert.run('creds', encode(creds));
|
|
158
|
+
},
|
|
159
|
+
db,
|
|
160
|
+
close: () => db.close()
|
|
161
|
+
};
|
|
162
|
+
};
|