davexbaileys 2.5.21 → 2.5.23
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 +15 -179
- package/WAProto/GenerateStatics.sh +2 -0
- package/lib/Defaults/baileys-version.json +1 -1
- package/lib/Defaults/index.js +14 -3
- package/lib/Socket/chats.js +69 -14
- package/lib/Socket/communities.js +430 -0
- package/lib/Socket/groups.js +7 -1
- package/lib/Socket/index.js +11 -6
- package/lib/Socket/messages-recv.js +63 -30
- package/lib/Socket/messages-send.js +114 -3
- package/lib/Utils/browser-utils.js +33 -0
- package/lib/Utils/chat-utils.js +22 -12
- package/lib/Utils/decode-wa-message.js +7 -0
- package/lib/Utils/event-buffer.js +3 -1
- package/lib/Utils/generics.js +9 -0
- package/lib/Utils/history.js +11 -4
- package/lib/Utils/index.js +7 -0
- package/lib/Utils/message-retry-manager.js +151 -0
- package/lib/Utils/messages.js +31 -3
- package/lib/Utils/offline-node-processor.js +34 -0
- package/lib/Utils/pre-key-manager.js +95 -0
- package/lib/Utils/process-message.js +6 -4
- package/lib/Utils/stanza-ack.js +45 -0
- package/lib/Utils/sync-action-utils.js +23 -0
- package/lib/Utils/tc-token-utils.js +209 -0
- package/lib/WAUSync/Protocols/USyncContactProtocol.js +26 -3
- package/lib/WAUSync/Protocols/USyncUsernameProtocol.js +38 -0
- package/lib/WAUSync/Protocols/index.js +1 -0
- package/lib/WAUSync/USyncQuery.js +5 -0
- package/lib/WAUSync/USyncUser.js +8 -0
- package/package.json +15 -4
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PreKeyManager = void 0;
|
|
4
|
+
const p_queue_1 = require("p-queue");
|
|
5
|
+
|
|
6
|
+
class PreKeyManager {
|
|
7
|
+
constructor(store, logger) {
|
|
8
|
+
this.store = store;
|
|
9
|
+
this.logger = logger;
|
|
10
|
+
this.queues = new Map();
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
getQueue(keyType) {
|
|
14
|
+
if (!this.queues.has(keyType)) {
|
|
15
|
+
this.queues.set(keyType, new p_queue_1.default({ concurrency: 1 }));
|
|
16
|
+
}
|
|
17
|
+
return this.queues.get(keyType);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
async processOperations(data, keyType, transactionCache, mutations, isInTransaction) {
|
|
21
|
+
const keyData = data[keyType];
|
|
22
|
+
if (!keyData) return;
|
|
23
|
+
|
|
24
|
+
return this.getQueue(keyType).add(async () => {
|
|
25
|
+
transactionCache[keyType] = transactionCache[keyType] || {};
|
|
26
|
+
mutations[keyType] = mutations[keyType] || {};
|
|
27
|
+
|
|
28
|
+
const deletions = [];
|
|
29
|
+
const updates = {};
|
|
30
|
+
|
|
31
|
+
for (const keyId in keyData) {
|
|
32
|
+
if (keyData[keyId] === null) {
|
|
33
|
+
deletions.push(keyId);
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
updates[keyId] = keyData[keyId];
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (Object.keys(updates).length > 0) {
|
|
41
|
+
Object.assign(transactionCache[keyType], updates);
|
|
42
|
+
Object.assign(mutations[keyType], updates);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (deletions.length > 0) {
|
|
46
|
+
await this.processDeletions(keyType, deletions, transactionCache, mutations, isInTransaction);
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async processDeletions(keyType, ids, transactionCache, mutations, isInTransaction) {
|
|
52
|
+
if (isInTransaction) {
|
|
53
|
+
for (const keyId of ids) {
|
|
54
|
+
if (transactionCache[keyType] && transactionCache[keyType][keyId]) {
|
|
55
|
+
transactionCache[keyType][keyId] = null;
|
|
56
|
+
mutations[keyType][keyId] = null;
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
this.logger.warn(`Skipping deletion of non-existent ${keyType} in transaction: ${keyId}`);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
const existingKeys = await this.store.get(keyType, ids);
|
|
65
|
+
for (const keyId of ids) {
|
|
66
|
+
if (existingKeys[keyId]) {
|
|
67
|
+
transactionCache[keyType][keyId] = null;
|
|
68
|
+
mutations[keyType][keyId] = null;
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
this.logger.warn(`Skipping deletion of non-existent ${keyType}: ${keyId}`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
async validateDeletions(data, keyType) {
|
|
78
|
+
const keyData = data[keyType];
|
|
79
|
+
if (!keyData) return;
|
|
80
|
+
|
|
81
|
+
return this.getQueue(keyType).add(async () => {
|
|
82
|
+
const deletionIds = Object.keys(keyData).filter(id => keyData[id] === null);
|
|
83
|
+
if (deletionIds.length === 0) return;
|
|
84
|
+
|
|
85
|
+
const existingKeys = await this.store.get(keyType, deletionIds);
|
|
86
|
+
for (const keyId of deletionIds) {
|
|
87
|
+
if (!existingKeys[keyId]) {
|
|
88
|
+
this.logger.warn(`Skipping deletion of non-existent ${keyType}: ${keyId}`);
|
|
89
|
+
delete data[keyType][keyId];
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
exports.PreKeyManager = PreKeyManager;
|
|
@@ -149,7 +149,8 @@ const processMessage = async (message, { shouldProcessHistoryMsg, placeholderRes
|
|
|
149
149
|
ev.emit('messaging-history.set', {
|
|
150
150
|
...data,
|
|
151
151
|
isLatest: histNotification.syncType !== WAProto_1.proto.HistorySync.HistorySyncType.ON_DEMAND ? isLatest : undefined,
|
|
152
|
-
peerDataRequestSessionId: histNotification.peerDataRequestSessionId
|
|
152
|
+
peerDataRequestSessionId: histNotification.peerDataRequestSessionId,
|
|
153
|
+
chunkOrder: histNotification.chunkOrder !== undefined ? histNotification.chunkOrder : undefined
|
|
153
154
|
});
|
|
154
155
|
}
|
|
155
156
|
break;
|
|
@@ -248,13 +249,14 @@ const processMessage = async (message, { shouldProcessHistoryMsg, placeholderRes
|
|
|
248
249
|
const jid = (_f = message.key) === null || _f === void 0 ? void 0 : _f.remoteJid;
|
|
249
250
|
//let actor = whatsappID (message.participant)
|
|
250
251
|
let participants;
|
|
251
|
-
const
|
|
252
|
+
const authorUsername = message.key && message.key.participantUsername;
|
|
253
|
+
const emitParticipantsUpdate = (action) => ev.emit('group-participants.update', { id: jid, author: message.participant, authorUsername, participants, action });
|
|
252
254
|
const emitGroupUpdate = (update) => {
|
|
253
255
|
var _a;
|
|
254
|
-
ev.emit('groups.update', [{ id: jid, ...update, author: (_a = message.participant) !== null && _a !== void 0 ? _a : undefined }]);
|
|
256
|
+
ev.emit('groups.update', [{ id: jid, ...update, author: (_a = message.participant) !== null && _a !== void 0 ? _a : undefined, authorUsername }]);
|
|
255
257
|
};
|
|
256
258
|
const emitGroupRequestJoin = (participant, action, method) => {
|
|
257
|
-
ev.emit('group.join-request', { id: jid, author: message.participant, participant, action, method: method });
|
|
259
|
+
ev.emit('group.join-request', { id: jid, author: message.participant, authorUsername, participant, action, method: method });
|
|
258
260
|
};
|
|
259
261
|
const participantsIncludesMe = () => participants.find(jid => (0, WABinary_1.areJidsSameUser)(meId, jid));
|
|
260
262
|
switch (message.messageStubType) {
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.buildAckStanza = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Builds an ACK stanza for a received node.
|
|
6
|
+
* Pure function -- no I/O, no side effects.
|
|
7
|
+
*
|
|
8
|
+
* Mirrors WhatsApp Web's ACK construction:
|
|
9
|
+
* - WAWebHandleMsgSendAck.sendAck / sendNack
|
|
10
|
+
* - WAWebCreateNackFromStanza.createNackFromStanza
|
|
11
|
+
*
|
|
12
|
+
* @param node - the received binary node to acknowledge
|
|
13
|
+
* @param errorCode - optional NACK error code
|
|
14
|
+
* @param meId - own JID, required for message-class ACKs
|
|
15
|
+
*/
|
|
16
|
+
const buildAckStanza = (node, errorCode, meId) => {
|
|
17
|
+
const { tag, attrs } = node;
|
|
18
|
+
const stanza = {
|
|
19
|
+
tag: 'ack',
|
|
20
|
+
attrs: {
|
|
21
|
+
id: attrs.id,
|
|
22
|
+
to: attrs.from,
|
|
23
|
+
class: tag
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
if (errorCode) {
|
|
27
|
+
stanza.attrs.error = errorCode.toString();
|
|
28
|
+
}
|
|
29
|
+
if (attrs.participant) {
|
|
30
|
+
stanza.attrs.participant = attrs.participant;
|
|
31
|
+
}
|
|
32
|
+
if (attrs.recipient) {
|
|
33
|
+
stanza.attrs.recipient = attrs.recipient;
|
|
34
|
+
}
|
|
35
|
+
// WA Web always includes type when present
|
|
36
|
+
if (attrs.type) {
|
|
37
|
+
stanza.attrs.type = attrs.type;
|
|
38
|
+
}
|
|
39
|
+
// WA Web WAWebHandleMsgSendAck.sendAck/sendNack always includes `from` for message-class ACKs
|
|
40
|
+
if (tag === 'message' && meId) {
|
|
41
|
+
stanza.attrs.from = meId;
|
|
42
|
+
}
|
|
43
|
+
return stanza;
|
|
44
|
+
};
|
|
45
|
+
exports.buildAckStanza = buildAckStanza;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.isAppStateSyncIrrecoverable = exports.isMissingKeyError = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Returns true when the error indicates that an app-state sync key is missing.
|
|
6
|
+
* WA Web treats these as "Blocked" (waits for key arrival), not fatal.
|
|
7
|
+
* Errors are tagged via { data: { isMissingKey: true } } in the Boom payload.
|
|
8
|
+
*/
|
|
9
|
+
const isMissingKeyError = (error) => {
|
|
10
|
+
var _a;
|
|
11
|
+
return ((_a = error === null || error === void 0 ? void 0 : error.data) === null || _a === void 0 ? void 0 : _a.isMissingKey) === true;
|
|
12
|
+
};
|
|
13
|
+
exports.isMissingKeyError = isMissingKeyError;
|
|
14
|
+
/**
|
|
15
|
+
* Determines if an app state sync error is unrecoverable.
|
|
16
|
+
* TypeError indicates a WASM crash; otherwise we give up after MAX_SYNC_ATTEMPTS.
|
|
17
|
+
* Missing keys are NOT checked here — they are handled separately as "Blocked".
|
|
18
|
+
*/
|
|
19
|
+
const isAppStateSyncIrrecoverable = (error, attemptsForName, maxAttempts) => {
|
|
20
|
+
return (attemptsForName >= maxAttempts ||
|
|
21
|
+
(error === null || error === void 0 ? void 0 : error.name) === 'TypeError');
|
|
22
|
+
};
|
|
23
|
+
exports.isAppStateSyncIrrecoverable = isAppStateSyncIrrecoverable;
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.storeTcTokensFromIqResult = exports.buildTcTokenFromJid = exports.resolveIssuanceJid = exports.resolveTcTokenJid = exports.shouldSendNewTcToken = exports.isTcTokenExpired = exports.buildMergedTcTokenIndexWrite = exports.readTcTokenIndex = exports.TC_TOKEN_INDEX_KEY = exports.storeTcTokensFromHistorySync = exports.getTcTokenKey = void 0;
|
|
4
|
+
const WABinary_1 = require("../WABinary");
|
|
5
|
+
|
|
6
|
+
// Same phone-number pattern as WABinary's isJidBot, applied against the user
|
|
7
|
+
// part so the check is invariant to @c.us ↔ @s.whatsapp.net normalization.
|
|
8
|
+
const BOT_PHONE_REGEX = /^1313555\d{4}$|^131655500\d{2}$/;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Mirrors WA Web's `Wid.isRegularUser()` (user ∧ ¬PSA ∧ ¬Bot). Used to gate tctoken
|
|
12
|
+
* storage against malformed notifications — WA Web filters server-side but we
|
|
13
|
+
* defend here for parity with `WAWebSetTcTokenChatAction.handleIncomingTcToken`.
|
|
14
|
+
* Works for both pre- and post-normalized JIDs (`@c.us` vs `@s.whatsapp.net`).
|
|
15
|
+
*/
|
|
16
|
+
function isRegularUser(jid) {
|
|
17
|
+
if (!jid) return false;
|
|
18
|
+
const user = jid.split('@')[0] || '';
|
|
19
|
+
if (user === '0') return false;
|
|
20
|
+
if (BOT_PHONE_REGEX.test(user)) return false;
|
|
21
|
+
if ((0, WABinary_1.isJidMetaIa)(jid)) return false;
|
|
22
|
+
return !!(
|
|
23
|
+
(0, WABinary_1.isLidUser)(jid) ||
|
|
24
|
+
(0, WABinary_1.isJidUser)(jid) ||
|
|
25
|
+
jid.endsWith('@c.us')
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const TC_TOKEN_BUCKET_DURATION = 604800;
|
|
30
|
+
const TC_TOKEN_NUM_BUCKETS = 4;
|
|
31
|
+
|
|
32
|
+
/** Sentinel key under `tctoken` store holding a JSON array of tracked storage JIDs for cross-session pruning. */
|
|
33
|
+
exports.TC_TOKEN_INDEX_KEY = '__index';
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Returns the key used to store a tc-token for a given JID.
|
|
37
|
+
* @deprecated use resolveTcTokenJid instead
|
|
38
|
+
*/
|
|
39
|
+
const getTcTokenKey = (jid) => {
|
|
40
|
+
return `tc-token:${(0, WABinary_1.jidNormalizedUser)(jid)}`;
|
|
41
|
+
};
|
|
42
|
+
exports.getTcTokenKey = getTcTokenKey;
|
|
43
|
+
|
|
44
|
+
/** Read the persisted tctoken JID index and return its entries (never contains the sentinel key itself). */
|
|
45
|
+
const readTcTokenIndex = async (keys) => {
|
|
46
|
+
const data = await keys.get('tctoken', [exports.TC_TOKEN_INDEX_KEY]);
|
|
47
|
+
const entry = data[exports.TC_TOKEN_INDEX_KEY];
|
|
48
|
+
if (!(entry === null || entry === void 0 ? void 0 : entry.token) || !entry.token.length) return [];
|
|
49
|
+
try {
|
|
50
|
+
const parsed = JSON.parse(Buffer.from(entry.token).toString());
|
|
51
|
+
if (!Array.isArray(parsed)) return [];
|
|
52
|
+
return parsed.filter(j => typeof j === 'string' && j.length > 0 && j !== exports.TC_TOKEN_INDEX_KEY);
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
return [];
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
exports.readTcTokenIndex = readTcTokenIndex;
|
|
59
|
+
|
|
60
|
+
/** Build a SignalDataSet fragment that writes the merged index (persisted ∪ added) under the sentinel key. */
|
|
61
|
+
const buildMergedTcTokenIndexWrite = async (keys, addedJids) => {
|
|
62
|
+
const persisted = await (0, exports.readTcTokenIndex)(keys);
|
|
63
|
+
const merged = new Set(persisted);
|
|
64
|
+
for (const jid of addedJids) {
|
|
65
|
+
if (jid && jid !== exports.TC_TOKEN_INDEX_KEY) merged.add(jid);
|
|
66
|
+
}
|
|
67
|
+
return {
|
|
68
|
+
[exports.TC_TOKEN_INDEX_KEY]: { token: Buffer.from(JSON.stringify([...merged])) }
|
|
69
|
+
};
|
|
70
|
+
};
|
|
71
|
+
exports.buildMergedTcTokenIndexWrite = buildMergedTcTokenIndexWrite;
|
|
72
|
+
|
|
73
|
+
/** Returns true when a tc-token timestamp is past the rolling 28-day window. */
|
|
74
|
+
const isTcTokenExpired = (timestamp) => {
|
|
75
|
+
if (timestamp === null || timestamp === undefined) return true;
|
|
76
|
+
const ts = typeof timestamp === 'string' ? parseInt(timestamp) : timestamp;
|
|
77
|
+
if (isNaN(ts)) return true;
|
|
78
|
+
const now = Math.floor(Date.now() / 1000);
|
|
79
|
+
const currentBucket = Math.floor(now / TC_TOKEN_BUCKET_DURATION);
|
|
80
|
+
const cutoffBucket = currentBucket - (TC_TOKEN_NUM_BUCKETS - 1);
|
|
81
|
+
const cutoffTimestamp = cutoffBucket * TC_TOKEN_BUCKET_DURATION;
|
|
82
|
+
return ts < cutoffTimestamp;
|
|
83
|
+
};
|
|
84
|
+
exports.isTcTokenExpired = isTcTokenExpired;
|
|
85
|
+
|
|
86
|
+
/** Returns true if we should send a new tc-token to this contact (bucket-based deduplication). */
|
|
87
|
+
const shouldSendNewTcToken = (senderTimestamp) => {
|
|
88
|
+
if (senderTimestamp === undefined) return true;
|
|
89
|
+
const now = Math.floor(Date.now() / 1000);
|
|
90
|
+
const currentBucket = Math.floor(now / TC_TOKEN_BUCKET_DURATION);
|
|
91
|
+
const senderBucket = Math.floor(senderTimestamp / TC_TOKEN_BUCKET_DURATION);
|
|
92
|
+
return currentBucket > senderBucket;
|
|
93
|
+
};
|
|
94
|
+
exports.shouldSendNewTcToken = shouldSendNewTcToken;
|
|
95
|
+
|
|
96
|
+
/** Resolve JID to LID for tctoken storage (WA Web stores under LID) */
|
|
97
|
+
const resolveTcTokenJid = async (jid, getLIDForPN) => {
|
|
98
|
+
if ((0, WABinary_1.isLidUser)(jid)) return jid;
|
|
99
|
+
const lid = await getLIDForPN(jid);
|
|
100
|
+
return lid !== null && lid !== void 0 ? lid : jid;
|
|
101
|
+
};
|
|
102
|
+
exports.resolveTcTokenJid = resolveTcTokenJid;
|
|
103
|
+
|
|
104
|
+
/** Resolve target JID for issuing privacy token based on AB prop 14303 */
|
|
105
|
+
const resolveIssuanceJid = async (jid, issueToLid, getLIDForPN, getPNForLID) => {
|
|
106
|
+
if (issueToLid) {
|
|
107
|
+
if ((0, WABinary_1.isLidUser)(jid)) return jid;
|
|
108
|
+
const lid = await getLIDForPN(jid);
|
|
109
|
+
return lid !== null && lid !== void 0 ? lid : jid;
|
|
110
|
+
}
|
|
111
|
+
if (!(0, WABinary_1.isLidUser)(jid)) return jid;
|
|
112
|
+
if (getPNForLID) {
|
|
113
|
+
const pn = await getPNForLID(jid);
|
|
114
|
+
return pn !== null && pn !== void 0 ? pn : jid;
|
|
115
|
+
}
|
|
116
|
+
return jid;
|
|
117
|
+
};
|
|
118
|
+
exports.resolveIssuanceJid = resolveIssuanceJid;
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Build tc-token content array from a stored token for a given JID.
|
|
122
|
+
* Returns undefined if no valid token exists.
|
|
123
|
+
*/
|
|
124
|
+
const buildTcTokenFromJid = async ({ authState, jid, baseContent = [], getLIDForPN }) => {
|
|
125
|
+
try {
|
|
126
|
+
const storageJid = await (0, exports.resolveTcTokenJid)(jid, getLIDForPN);
|
|
127
|
+
const tcTokenData = await authState.keys.get('tctoken', [storageJid]);
|
|
128
|
+
const entry = tcTokenData === null || tcTokenData === void 0 ? void 0 : tcTokenData[storageJid];
|
|
129
|
+
const tcTokenBuffer = entry === null || entry === void 0 ? void 0 : entry.token;
|
|
130
|
+
if (!(tcTokenBuffer === null || tcTokenBuffer === void 0 ? void 0 : tcTokenBuffer.length) || (0, exports.isTcTokenExpired)(entry === null || entry === void 0 ? void 0 : entry.timestamp)) {
|
|
131
|
+
if (tcTokenBuffer) {
|
|
132
|
+
const cleared = (entry === null || entry === void 0 ? void 0 : entry.senderTimestamp) !== undefined
|
|
133
|
+
? { token: Buffer.alloc(0), senderTimestamp: entry.senderTimestamp }
|
|
134
|
+
: null;
|
|
135
|
+
await authState.keys.set({ tctoken: { [storageJid]: cleared } });
|
|
136
|
+
}
|
|
137
|
+
return baseContent.length > 0 ? baseContent : undefined;
|
|
138
|
+
}
|
|
139
|
+
baseContent.push({
|
|
140
|
+
tag: 'tctoken',
|
|
141
|
+
attrs: {},
|
|
142
|
+
content: tcTokenBuffer
|
|
143
|
+
});
|
|
144
|
+
return baseContent;
|
|
145
|
+
}
|
|
146
|
+
catch (error) {
|
|
147
|
+
return baseContent.length > 0 ? baseContent : undefined;
|
|
148
|
+
}
|
|
149
|
+
};
|
|
150
|
+
exports.buildTcTokenFromJid = buildTcTokenFromJid;
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Store tc-tokens received from a privacy_token IQ response.
|
|
154
|
+
*/
|
|
155
|
+
const storeTcTokensFromIqResult = async ({ result, fallbackJid, keys, getLIDForPN, onNewJidStored }) => {
|
|
156
|
+
const { getBinaryNodeChild, getBinaryNodeChildren } = require('../WABinary');
|
|
157
|
+
const tokensNode = getBinaryNodeChild(result, 'tokens');
|
|
158
|
+
if (!tokensNode) return;
|
|
159
|
+
const tokenNodes = getBinaryNodeChildren(tokensNode, 'token');
|
|
160
|
+
for (const tokenNode of tokenNodes) {
|
|
161
|
+
if (tokenNode.attrs.type !== 'trusted_contact' || !(tokenNode.content instanceof Uint8Array)) {
|
|
162
|
+
continue;
|
|
163
|
+
}
|
|
164
|
+
const rawJid = (0, WABinary_1.jidNormalizedUser)(fallbackJid || tokenNode.attrs.jid);
|
|
165
|
+
if (!isRegularUser(rawJid)) continue;
|
|
166
|
+
const storageJid = await (0, exports.resolveTcTokenJid)(rawJid, getLIDForPN);
|
|
167
|
+
const existingTcData = await keys.get('tctoken', [storageJid]);
|
|
168
|
+
const existingEntry = existingTcData[storageJid];
|
|
169
|
+
const existingTs = (existingEntry === null || existingEntry === void 0 ? void 0 : existingEntry.timestamp) ? Number(existingEntry.timestamp) : 0;
|
|
170
|
+
const incomingTs = tokenNode.attrs.t ? Number(tokenNode.attrs.t) : 0;
|
|
171
|
+
if (!incomingTs) continue;
|
|
172
|
+
if (existingTs > 0 && existingTs > incomingTs) continue;
|
|
173
|
+
await keys.set({
|
|
174
|
+
tctoken: {
|
|
175
|
+
[storageJid]: Object.assign(Object.assign({}, existingEntry), { token: Buffer.from(tokenNode.content), timestamp: tokenNode.attrs.t })
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
if (onNewJidStored) onNewJidStored(storageJid);
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
exports.storeTcTokensFromIqResult = storeTcTokensFromIqResult;
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Extracts and stores tc-tokens from a history sync payload.
|
|
185
|
+
* Conversations in the history sync may carry tcToken fields that should
|
|
186
|
+
* be stored so that presenceSubscribe / relayMessage can include them.
|
|
187
|
+
*/
|
|
188
|
+
const storeTcTokensFromHistorySync = async (ev, conversations) => {
|
|
189
|
+
if (!conversations || !conversations.length) {
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
const chatUpdates = [];
|
|
193
|
+
for (const conv of conversations) {
|
|
194
|
+
if (conv.id && conv.tcToken) {
|
|
195
|
+
chatUpdates.push({
|
|
196
|
+
id: conv.id,
|
|
197
|
+
tcToken: conv.tcToken instanceof Uint8Array
|
|
198
|
+
? Buffer.from(conv.tcToken)
|
|
199
|
+
: Buffer.isBuffer(conv.tcToken)
|
|
200
|
+
? conv.tcToken
|
|
201
|
+
: Buffer.from(Object.values(conv.tcToken))
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
if (chatUpdates.length) {
|
|
206
|
+
ev.emit('chats.update', chatUpdates);
|
|
207
|
+
}
|
|
208
|
+
};
|
|
209
|
+
exports.storeTcTokensFromHistorySync = storeTcTokensFromHistorySync;
|
|
@@ -13,11 +13,34 @@ class USyncContactProtocol {
|
|
|
13
13
|
};
|
|
14
14
|
}
|
|
15
15
|
getUserElement(user) {
|
|
16
|
-
|
|
16
|
+
if (user.phone) {
|
|
17
|
+
return {
|
|
18
|
+
tag: 'contact',
|
|
19
|
+
attrs: {},
|
|
20
|
+
content: user.phone
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
if (user.username) {
|
|
24
|
+
return {
|
|
25
|
+
tag: 'contact',
|
|
26
|
+
attrs: {
|
|
27
|
+
username: user.username,
|
|
28
|
+
...(user.usernameKey ? { pin: user.usernameKey } : {}),
|
|
29
|
+
...(user.lid ? { lid: user.lid } : {})
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
if (user.type) {
|
|
34
|
+
return {
|
|
35
|
+
tag: 'contact',
|
|
36
|
+
attrs: {
|
|
37
|
+
type: user.type
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
}
|
|
17
41
|
return {
|
|
18
42
|
tag: 'contact',
|
|
19
|
-
attrs: {}
|
|
20
|
-
content: user.phone
|
|
43
|
+
attrs: {}
|
|
21
44
|
};
|
|
22
45
|
}
|
|
23
46
|
parser(node) {
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.USyncUsernameProtocol = void 0;
|
|
4
|
+
class USyncUsernameProtocol {
|
|
5
|
+
constructor() {
|
|
6
|
+
this.name = 'username';
|
|
7
|
+
}
|
|
8
|
+
getQueryElement() {
|
|
9
|
+
return {
|
|
10
|
+
tag: 'username',
|
|
11
|
+
attrs: {}
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
getUserElement(user) {
|
|
15
|
+
if (user.usernameKey) {
|
|
16
|
+
return {
|
|
17
|
+
tag: 'username',
|
|
18
|
+
attrs: { key: user.usernameKey }
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
return {
|
|
22
|
+
tag: 'username',
|
|
23
|
+
attrs: {}
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
parser(node) {
|
|
27
|
+
var _a, _b;
|
|
28
|
+
if (node.tag === 'username') {
|
|
29
|
+
const username = (_a = node === null || node === void 0 ? void 0 : node.attrs) === null || _a === void 0 ? void 0 : _a.value;
|
|
30
|
+
const key = (_b = node === null || node === void 0 ? void 0 : node.attrs) === null || _b === void 0 ? void 0 : _b.key;
|
|
31
|
+
if (username) {
|
|
32
|
+
return { username, key };
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
exports.USyncUsernameProtocol = USyncUsernameProtocol;
|
|
@@ -18,3 +18,4 @@ __exportStar(require("./USyncDeviceProtocol"), exports);
|
|
|
18
18
|
__exportStar(require("./USyncContactProtocol"), exports);
|
|
19
19
|
__exportStar(require("./USyncStatusProtocol"), exports);
|
|
20
20
|
__exportStar(require("./USyncDisappearingModeProtocol"), exports);
|
|
21
|
+
__exportStar(require("./USyncUsernameProtocol"), exports);
|
|
@@ -5,6 +5,7 @@ const WABinary_1 = require("../WABinary");
|
|
|
5
5
|
const UsyncBotProfileProtocol_1 = require("./Protocols/UsyncBotProfileProtocol");
|
|
6
6
|
const UsyncLIDProtocol_1 = require("./Protocols/UsyncLIDProtocol");
|
|
7
7
|
const Protocols_1 = require("./Protocols");
|
|
8
|
+
const USyncUsernameProtocol_1 = require("./Protocols/USyncUsernameProtocol");
|
|
8
9
|
class USyncQuery {
|
|
9
10
|
constructor() {
|
|
10
11
|
this.protocols = [];
|
|
@@ -89,5 +90,9 @@ class USyncQuery {
|
|
|
89
90
|
this.protocols.push(new UsyncLIDProtocol_1.USyncLIDProtocol());
|
|
90
91
|
return this;
|
|
91
92
|
}
|
|
93
|
+
withUsernameProtocol() {
|
|
94
|
+
this.protocols.push(new USyncUsernameProtocol_1.USyncUsernameProtocol());
|
|
95
|
+
return this;
|
|
96
|
+
}
|
|
92
97
|
}
|
|
93
98
|
exports.USyncQuery = USyncQuery;
|
package/lib/WAUSync/USyncUser.js
CHANGED
|
@@ -22,5 +22,13 @@ class USyncUser {
|
|
|
22
22
|
this.personaId = personaId;
|
|
23
23
|
return this;
|
|
24
24
|
}
|
|
25
|
+
withUsername(username) {
|
|
26
|
+
this.username = username;
|
|
27
|
+
return this;
|
|
28
|
+
}
|
|
29
|
+
withUsernameKey(usernameKey) {
|
|
30
|
+
this.usernameKey = usernameKey;
|
|
31
|
+
return this;
|
|
32
|
+
}
|
|
25
33
|
}
|
|
26
34
|
exports.USyncUser = USyncUser;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "davexbaileys",
|
|
3
|
-
"version": "2.5.
|
|
3
|
+
"version": "2.5.23",
|
|
4
4
|
"description": "A lightweight, full-featured WhatsApp Web API library for Node.js — maintained by Dave Tech",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"types": "lib/index.d.ts",
|
|
@@ -20,7 +20,8 @@
|
|
|
20
20
|
"whatsapp-bot",
|
|
21
21
|
"messaging",
|
|
22
22
|
"chat",
|
|
23
|
-
"newsletter"
|
|
23
|
+
"newsletter",
|
|
24
|
+
"communities"
|
|
24
25
|
],
|
|
25
26
|
"dependencies": {
|
|
26
27
|
"@cacheable/node-cache": "^1.4.0",
|
|
@@ -29,10 +30,20 @@
|
|
|
29
30
|
"axios": "^1.6.0",
|
|
30
31
|
"libsignal": "github:WhiskeySockets/libsignal-node",
|
|
31
32
|
"lodash": "^4.17.21",
|
|
33
|
+
"lru-cache": "^11.3.5",
|
|
32
34
|
"music-metadata": "^7.12.3",
|
|
33
35
|
"node-fetch": "^2.6.1",
|
|
36
|
+
"p-queue": "^9.2.0",
|
|
34
37
|
"pino": "^9.6",
|
|
35
38
|
"protobufjs": "^7.2.4",
|
|
36
39
|
"ws": "^8.13.0"
|
|
37
|
-
}
|
|
38
|
-
|
|
40
|
+
},
|
|
41
|
+
"engines": {
|
|
42
|
+
"node": ">=20.0.0"
|
|
43
|
+
},
|
|
44
|
+
"files": [
|
|
45
|
+
"lib/**/*",
|
|
46
|
+
"WAProto/**/*",
|
|
47
|
+
"engine-requirements.js"
|
|
48
|
+
]
|
|
49
|
+
}
|