@badzz88/baileys 8.5.6 → 8.5.8
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 +337 -1055
- package/WAProto/WAProto.proto +28 -1189
- package/WAProto/fix-imports.js +79 -69
- package/WAProto/index.d.ts +71491 -15254
- package/WAProto/index.js +126436 -19814
- package/engine-requirements.js +8 -15
- package/lib/Defaults/index.js +130 -0
- package/{src → lib}/Signal/Group/ciphertext-message.js +2 -5
- package/lib/Signal/Group/group-session-builder.js +30 -0
- package/{src → lib}/Signal/Group/group_cipher.js +11 -11
- package/lib/Signal/Group/index.js +12 -0
- package/lib/Signal/Group/keyhelper.js +18 -0
- package/{src → lib}/Signal/Group/sender-chain-key.js +6 -9
- package/{src → lib}/Signal/Group/sender-key-distribution-message.js +6 -9
- package/{src → lib}/Signal/Group/sender-key-message.js +9 -12
- package/{src → lib}/Signal/Group/sender-key-name.js +2 -5
- package/{src → lib}/Signal/Group/sender-key-record.js +8 -11
- package/{src → lib}/Signal/Group/sender-key-state.js +6 -9
- package/lib/Signal/Group/sender-message-key.js +26 -0
- package/{src → lib}/Signal/libsignal.js +131 -155
- package/{src → lib}/Signal/lid-mapping.js +27 -23
- package/lib/Socket/Client/index.js +3 -0
- package/lib/Socket/Client/types.js +11 -0
- package/{src → lib}/Socket/Client/websocket.js +11 -18
- package/{src → lib}/Socket/business.js +35 -67
- package/{src → lib}/Socket/chats.js +294 -550
- package/{src → lib}/Socket/communities.js +82 -114
- package/lib/Socket/graphql.js +716 -0
- package/lib/Socket/groups.js +374 -0
- package/lib/Socket/index.js +18 -0
- package/lib/Socket/interop.js +463 -0
- package/{src → lib}/Socket/luxu.js +120 -82
- package/lib/Socket/messages-recv.js +1916 -0
- package/lib/Socket/messages-send.js +1436 -0
- package/lib/Socket/mex.js +54 -0
- package/lib/Socket/newsletter.js +757 -0
- package/lib/Socket/privacy.js +318 -0
- package/{src → lib}/Socket/socket.js +356 -244
- package/lib/Socket/username.js +236 -0
- package/lib/Store/index.js +10 -0
- package/{src → lib}/Store/keyed-db.js +11 -21
- package/lib/Store/make-cache-manager-store.js +85 -0
- package/lib/Store/make-in-memory-store.js +244 -0
- package/{src → lib}/Store/make-ordered-dictionary.js +19 -25
- package/{src → lib}/Store/object-repository.js +11 -5
- package/lib/Types/Auth.js +2 -0
- package/lib/Types/Bussines.js +2 -0
- package/lib/Types/Call.js +2 -0
- package/lib/Types/Chat.js +8 -0
- package/lib/Types/Contact.js +2 -0
- package/lib/Types/Events.js +2 -0
- package/lib/Types/GroupMetadata.js +2 -0
- package/lib/Types/Label.js +25 -0
- package/lib/Types/LabelAssociation.js +7 -0
- package/lib/Types/Message.js +11 -0
- package/lib/Types/Mex.js +111 -0
- package/lib/Types/Product.js +2 -0
- package/lib/Types/Signal.js +2 -0
- package/lib/Types/Socket.js +3 -0
- package/lib/Types/State.js +56 -0
- package/lib/Types/USync.js +2 -0
- package/lib/Types/index.js +26 -0
- package/{src → lib}/Utils/auth-utils.js +91 -43
- package/lib/Utils/browser-utils.js +48 -0
- package/lib/Utils/business.js +231 -0
- package/lib/Utils/chat-utils.js +872 -0
- package/lib/Utils/companion-reg-client-utils.js +35 -0
- package/lib/Utils/crypto.js +118 -0
- package/lib/Utils/decode-wa-message.js +350 -0
- package/{src → lib}/Utils/event-buffer.js +81 -39
- package/lib/Utils/generics.js +403 -0
- package/lib/Utils/history.js +134 -0
- package/{src → lib}/Utils/identity-change-handler.js +9 -11
- package/lib/Utils/index.js +23 -0
- package/lib/Utils/link-preview.js +85 -0
- package/lib/Utils/logger.js +3 -0
- package/lib/Utils/lt-hash.js +8 -0
- package/{src → lib}/Utils/make-mutex.js +7 -10
- package/lib/Utils/message-composer.js +273 -0
- package/{src → lib}/Utils/message-retry-manager.js +76 -28
- package/lib/Utils/messages-media.js +870 -0
- package/lib/Utils/messages.js +1337 -0
- package/lib/Utils/noise-handler.js +201 -0
- package/{src → lib}/Utils/offline-node-processor.js +10 -4
- package/{src → lib}/Utils/pre-key-manager.js +27 -11
- package/lib/Utils/process-message.js +630 -0
- package/{src → lib}/Utils/reporting-utils.js +12 -15
- package/lib/Utils/signal.js +201 -0
- package/{src → lib}/Utils/stanza-ack.js +12 -4
- package/lib/Utils/sticker.js +110 -0
- package/{src → lib}/Utils/sync-action-utils.js +14 -10
- package/{src → lib}/Utils/tc-token-utils.js +48 -43
- package/{src → lib}/Utils/use-multi-file-auth-state.js +35 -25
- package/lib/Utils/validate-connection.js +203 -0
- package/{src → lib}/WABinary/constants.js +9 -12
- package/{src → lib}/WABinary/decode.js +38 -118
- package/{src → lib}/WABinary/encode.js +6 -11
- package/lib/WABinary/generic-utils.js +204 -0
- package/lib/WABinary/index.js +6 -0
- package/lib/WABinary/jid-utils.js +98 -0
- package/lib/WABinary/types.js +2 -0
- package/{src → lib}/WAM/BinaryInfo.js +2 -5
- package/{src → lib}/WAM/constants.js +4 -16635
- package/{src → lib}/WAM/encode.js +18 -17
- package/lib/WAM/index.js +4 -0
- package/lib/WAUSync/Protocols/USyncBotProfileProtocol.js +53 -0
- package/lib/WAUSync/Protocols/USyncBusinessProtocol.js +43 -0
- package/lib/WAUSync/Protocols/USyncContactProtocol.js +54 -0
- package/lib/WAUSync/Protocols/USyncDeviceProtocol.js +57 -0
- package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.js +30 -0
- package/lib/WAUSync/Protocols/USyncFeatureProtocol.js +58 -0
- package/lib/WAUSync/Protocols/USyncLIDProtocol.js +30 -0
- package/lib/WAUSync/Protocols/USyncPictureProtocol.js +32 -0
- package/lib/WAUSync/Protocols/USyncSidelistProtocol.js +28 -0
- package/lib/WAUSync/Protocols/USyncStatusProtocol.js +39 -0
- package/lib/WAUSync/Protocols/USyncTextStatusProtocol.js +38 -0
- package/lib/WAUSync/Protocols/USyncUsernameProtocol.js +27 -0
- package/lib/WAUSync/Protocols/index.js +12 -0
- package/lib/WAUSync/USyncQuery.js +151 -0
- package/lib/WAUSync/USyncUser.js +56 -0
- package/lib/WAUSync/index.js +3 -0
- package/{src → lib}/index.js +16 -51
- package/package.json +129 -99
- package/LICENSE +0 -21
- package/src/Defaults/index.js +0 -186
- package/src/Defaults/phonenumber-mcc.json +0 -223
- package/src/Signal/Group/group-session-builder.js +0 -86
- package/src/Signal/Group/index.js +0 -140
- package/src/Signal/Group/keyhelper.js +0 -77
- package/src/Signal/Group/sender-message-key.js +0 -29
- package/src/Socket/Client/index.js +0 -31
- package/src/Socket/Client/types.js +0 -13
- package/src/Socket/aigroups.js +0 -221
- package/src/Socket/graphql.js +0 -523
- package/src/Socket/groups.js +0 -516
- package/src/Socket/index.js +0 -31
- package/src/Socket/interactive-handler.js +0 -527
- package/src/Socket/interop.js +0 -339
- package/src/Socket/managed-account.js +0 -98
- package/src/Socket/messages-recv.js +0 -2685
- package/src/Socket/messages-send.js +0 -2047
- package/src/Socket/mex.js +0 -57
- package/src/Socket/newsletter.js +0 -455
- package/src/Socket/privacy.js +0 -125
- package/src/Socket/registration.js +0 -236
- package/src/Socket/text-router.js +0 -65
- package/src/Socket/username.js +0 -130
- package/src/Store/index.js +0 -33
- package/src/Store/make-cache-manager-store.js +0 -84
- package/src/Store/make-in-memory-store.js +0 -551
- package/src/Types/Auth.js +0 -31
- package/src/Types/Bussines.js +0 -2
- package/src/Types/Call.js +0 -2
- package/src/Types/Chat.js +0 -4
- package/src/Types/Contact.js +0 -2
- package/src/Types/Events.js +0 -2
- package/src/Types/GroupMetadata.js +0 -2
- package/src/Types/Label.js +0 -26
- package/src/Types/LabelAssociation.js +0 -8
- package/src/Types/Message.js +0 -93
- package/src/Types/Mex.js +0 -112
- package/src/Types/Newsletter.js +0 -109
- package/src/Types/Product.js +0 -2
- package/src/Types/Signal.js +0 -2
- package/src/Types/Socket.js +0 -2
- package/src/Types/State.js +0 -62
- package/src/Types/USync.js +0 -2
- package/src/Types/index.js +0 -56
- package/src/Utils/browser-utils.js +0 -112
- package/src/Utils/business.js +0 -240
- package/src/Utils/chat-utils.js +0 -1230
- package/src/Utils/command-loader.d.ts +0 -27
- package/src/Utils/command-loader.js +0 -89
- package/src/Utils/companion-reg-client-utils.js +0 -40
- package/src/Utils/consumer-application.js +0 -105
- package/src/Utils/crypto.js +0 -118
- package/src/Utils/curve25519-js.js +0 -242
- package/src/Utils/decode-wa-message.js +0 -529
- package/src/Utils/generics.js +0 -483
- package/src/Utils/group-history.js +0 -44
- package/src/Utils/history.js +0 -232
- package/src/Utils/index.js +0 -58
- package/src/Utils/jid-display-normalization.js +0 -195
- package/src/Utils/link-preview.js +0 -135
- package/src/Utils/logger.js +0 -8
- package/src/Utils/lt-hash.js +0 -25
- package/src/Utils/message-composer.js +0 -471
- package/src/Utils/messages-media.js +0 -843
- package/src/Utils/messages.js +0 -2817
- package/src/Utils/meta-ai-msmsg.js +0 -222
- package/src/Utils/native-bridge.js +0 -68
- package/src/Utils/noise-handler.js +0 -169
- package/src/Utils/process-message.js +0 -950
- package/src/Utils/session-pool.d.ts +0 -14
- package/src/Utils/session-pool.js +0 -70
- package/src/Utils/signal.js +0 -217
- package/src/Utils/sticker.d.ts +0 -13
- package/src/Utils/sticker.js +0 -123
- package/src/Utils/validate-connection.js +0 -209
- package/src/Utils/view-once-cache.d.ts +0 -12
- package/src/Utils/view-once-cache.js +0 -63
- package/src/Utils/voip-rekey.js +0 -22
- package/src/WABinary/generic-utils.js +0 -238
- package/src/WABinary/index.js +0 -34
- package/src/WABinary/jid-utils.js +0 -351
- package/src/WABinary/types.js +0 -2
- package/src/WAM/index.js +0 -32
- package/src/WAUSync/Protocols/USyncBotProfileProtocol.js +0 -53
- package/src/WAUSync/Protocols/USyncBusinessProtocol.js +0 -44
- package/src/WAUSync/Protocols/USyncContactProtocol.js +0 -55
- package/src/WAUSync/Protocols/USyncDeviceProtocol.js +0 -55
- package/src/WAUSync/Protocols/USyncDisappearingModeProtocol.js +0 -31
- package/src/WAUSync/Protocols/USyncFeatureProtocol.js +0 -54
- package/src/WAUSync/Protocols/USyncLIDProtocol.js +0 -32
- package/src/WAUSync/Protocols/USyncNewsletterProtocol.js +0 -473
- package/src/WAUSync/Protocols/USyncPictureProtocol.js +0 -34
- package/src/WAUSync/Protocols/USyncSidelistProtocol.js +0 -29
- package/src/WAUSync/Protocols/USyncStatusProtocol.js +0 -42
- package/src/WAUSync/Protocols/USyncTextStatusProtocol.js +0 -38
- package/src/WAUSync/Protocols/USyncUsernameProtocol.js +0 -28
- package/src/WAUSync/Protocols/index.js +0 -40
- package/src/WAUSync/USyncQuery.js +0 -126
- package/src/WAUSync/USyncUser.js +0 -50
- package/src/WAUSync/index.js +0 -32
- package/src/antiban.js +0 -4152
|
@@ -1,55 +1,57 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
const
|
|
15
|
-
const make_mutex_1 = require('../Utils/make-mutex');
|
|
16
|
-
const process_message_1 = __importDefault(require('../Utils/process-message'));
|
|
17
|
-
const tc_token_utils_1 = require('../Utils/tc-token-utils');
|
|
18
|
-
const WABinary_1 = require('../WABinary');
|
|
19
|
-
const WAUSync_1 = require('../WAUSync');
|
|
20
|
-
const socket_js_1 = require('./socket.js');
|
|
21
|
-
const interop_js_1 = require('./interop.js');
|
|
22
|
-
const makeChatsSocket = config => {
|
|
1
|
+
import NodeCache from '@cacheable/node-cache';
|
|
2
|
+
import { Boom } from '@hapi/boom';
|
|
3
|
+
import { proto } from '../../WAProto/index.js';
|
|
4
|
+
import { DEFAULT_CACHE_TTLS, HISTORY_SYNC_PAUSED_TIMEOUT_MS, PROCESSABLE_HISTORY_TYPES } from '../Defaults/index.js';
|
|
5
|
+
import { ALL_WA_PATCH_NAMES } from '../Types/index.js';
|
|
6
|
+
import { SyncState } from '../Types/State.js';
|
|
7
|
+
import { chatModificationToAppPatch, decodePatches, decodeSyncdSnapshot, encodeSyncdPatch, ensureLTHashStateVersion, extractSyncdPatches, generateProfilePicture, getHistoryMsg, isAppStateSyncIrrecoverable, isMissingKeyError, MAX_SYNC_ATTEMPTS, newLTHashState, processSyncAction } from '../Utils/index.js';
|
|
8
|
+
import { makeMutex } from '../Utils/make-mutex.js';
|
|
9
|
+
import processMessage from '../Utils/process-message.js';
|
|
10
|
+
import { buildTcTokenFromJid } from '../Utils/tc-token-utils.js';
|
|
11
|
+
import { getBinaryNodeChild, getBinaryNodeChildren, isHostedLidUser, isHostedPnUser, isLidUser, isPnUser, jidDecode, jidNormalizedUser, reduceBinaryNodeToDictionary, S_WHATSAPP_NET } from '../WABinary/index.js';
|
|
12
|
+
import { USyncQuery, USyncUser } from '../WAUSync/index.js';
|
|
13
|
+
import { makeSocket } from './socket.js';
|
|
14
|
+
export const makeChatsSocket = (config) => {
|
|
23
15
|
const { logger, markOnlineOnConnect, fireInitQueries, appStateMacVerification, shouldIgnoreJid, shouldSyncHistoryMessage, getMessage } = config;
|
|
24
|
-
const sock =
|
|
25
|
-
const { ev, ws, authState, generateMessageTag, sendNode, query, signalRepository, onUnexpectedError, sendUnifiedSession,
|
|
16
|
+
const sock = makeSocket(config);
|
|
17
|
+
const { ev, ws, authState, generateMessageTag, sendNode, query, signalRepository, onUnexpectedError, sendUnifiedSession, registerSocketEndHandler } = sock;
|
|
26
18
|
const getLIDForPN = signalRepository.lidMapping.getLIDForPN.bind(signalRepository.lidMapping);
|
|
27
19
|
let privacySettings;
|
|
20
|
+
/** Server-assigned AB props for protocol behavior. */
|
|
28
21
|
const serverProps = {
|
|
22
|
+
/** AB prop 10518: gate tctoken on 1:1 messages. Default true (safe: avoids 463). */
|
|
29
23
|
privacyTokenOn1to1: true,
|
|
24
|
+
/** AB prop 9666: gate tctoken on profile picture IQs. WA Web default: true. */
|
|
30
25
|
profilePicPrivacyToken: true,
|
|
26
|
+
/** AB prop 14303: issue tctokens to LID instead of PN. WA Web default: false. */
|
|
31
27
|
lidTrustedTokenIssueToLid: false
|
|
32
28
|
};
|
|
33
|
-
let syncState =
|
|
34
|
-
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
const
|
|
29
|
+
let syncState = SyncState.Connecting;
|
|
30
|
+
/** this mutex ensures that messages are processed in order */
|
|
31
|
+
const messageMutex = makeMutex();
|
|
32
|
+
/** this mutex ensures that receipts are processed in order */
|
|
33
|
+
const receiptMutex = makeMutex();
|
|
34
|
+
/** this mutex ensures that app state patches are processed in order */
|
|
35
|
+
const appStatePatchMutex = makeMutex();
|
|
36
|
+
/** this mutex ensures that notifications are processed in order */
|
|
37
|
+
const notificationMutex = makeMutex();
|
|
38
|
+
// Timeout for AwaitingInitialSync state
|
|
38
39
|
let awaitingSyncTimeout;
|
|
40
|
+
// In-memory history sync completion tracking (resets on reconnection)
|
|
39
41
|
const historySyncStatus = {
|
|
40
42
|
initialBootstrapComplete: false,
|
|
41
43
|
recentSyncComplete: false
|
|
42
44
|
};
|
|
43
45
|
let historySyncPausedTimeout;
|
|
46
|
+
// Collections blocked on missing app state sync keys (mirrors WA Web's "Blocked" state).
|
|
47
|
+
// When a key arrives via APP_STATE_SYNC_KEY_SHARE, these are re-synced.
|
|
44
48
|
const blockedCollections = new Set();
|
|
45
49
|
const placeholderResendCache = config.placeholderResendCache ||
|
|
46
|
-
new
|
|
47
|
-
stdTTL:
|
|
50
|
+
new NodeCache({
|
|
51
|
+
stdTTL: DEFAULT_CACHE_TTLS.MSG_RETRY, // 1 hour
|
|
48
52
|
useClones: false
|
|
49
53
|
});
|
|
50
|
-
|
|
51
|
-
config.placeholderResendCache = placeholderResendCache;
|
|
52
|
-
}
|
|
54
|
+
/** helper function to fetch the given app state sync key */
|
|
53
55
|
const getAppStateSyncKey = async (keyId) => {
|
|
54
56
|
const { [keyId]: key } = await authState.keys.get('app-state-sync-key', [keyId]);
|
|
55
57
|
return key;
|
|
@@ -60,21 +62,22 @@ const makeChatsSocket = config => {
|
|
|
60
62
|
tag: 'iq',
|
|
61
63
|
attrs: {
|
|
62
64
|
xmlns: 'privacy',
|
|
63
|
-
to:
|
|
65
|
+
to: S_WHATSAPP_NET,
|
|
64
66
|
type: 'get'
|
|
65
67
|
},
|
|
66
68
|
content: [{ tag: 'privacy', attrs: {} }]
|
|
67
69
|
});
|
|
68
|
-
privacySettings =
|
|
70
|
+
privacySettings = reduceBinaryNodeToDictionary(content?.[0], 'category');
|
|
69
71
|
}
|
|
70
72
|
return privacySettings;
|
|
71
73
|
};
|
|
74
|
+
/** helper function to run a privacy IQ query */
|
|
72
75
|
const privacyQuery = async (name, value) => {
|
|
73
76
|
await query({
|
|
74
77
|
tag: 'iq',
|
|
75
78
|
attrs: {
|
|
76
79
|
xmlns: 'privacy',
|
|
77
|
-
to:
|
|
80
|
+
to: S_WHATSAPP_NET,
|
|
78
81
|
type: 'set'
|
|
79
82
|
},
|
|
80
83
|
content: [
|
|
@@ -109,59 +112,6 @@ const makeChatsSocket = config => {
|
|
|
109
112
|
const updateStatusPrivacy = async (value) => {
|
|
110
113
|
await privacyQuery('status', value);
|
|
111
114
|
};
|
|
112
|
-
const getStatusPrivacy = async () => {
|
|
113
|
-
const result = await query({
|
|
114
|
-
tag: 'iq',
|
|
115
|
-
attrs: {
|
|
116
|
-
xmlns: 'status',
|
|
117
|
-
to: WABinary_1.S_WHATSAPP_NET,
|
|
118
|
-
type: 'get'
|
|
119
|
-
},
|
|
120
|
-
content: [{ tag: 'privacy', attrs: {} }]
|
|
121
|
-
});
|
|
122
|
-
const privacyNode = result?.content?.[0];
|
|
123
|
-
if (!privacyNode)
|
|
124
|
-
return null;
|
|
125
|
-
const lists = [];
|
|
126
|
-
for (const listNode of (privacyNode.content || [])) {
|
|
127
|
-
const { type, id, listname, emoji, selected, deleted } = listNode.attrs || {};
|
|
128
|
-
const members = (listNode.content || []).map(u => u.attrs?.jid).filter(Boolean);
|
|
129
|
-
lists.push({ type, id, listname, emoji, selected: selected === 'true', deleted: deleted === 'true', members });
|
|
130
|
-
}
|
|
131
|
-
return lists;
|
|
132
|
-
};
|
|
133
|
-
const setStatusPrivacy = async (type, jids = [], customLists = []) => {
|
|
134
|
-
const content = [];
|
|
135
|
-
const mainList = {
|
|
136
|
-
tag: 'list',
|
|
137
|
-
attrs: { type },
|
|
138
|
-
content: jids.map(jid => ({ tag: 'user', attrs: { jid }, content: [] }))
|
|
139
|
-
};
|
|
140
|
-
content.push(mainList);
|
|
141
|
-
for (const cl of customLists) {
|
|
142
|
-
const attrs = { type: 'customlist', id: cl.id, listname: cl.listname };
|
|
143
|
-
if (cl.emoji)
|
|
144
|
-
attrs.emoji = cl.emoji;
|
|
145
|
-
if (cl.selected)
|
|
146
|
-
attrs.selected = 'true';
|
|
147
|
-
if (cl.deleted)
|
|
148
|
-
attrs.deleted = 'true';
|
|
149
|
-
content.push({
|
|
150
|
-
tag: 'list',
|
|
151
|
-
attrs,
|
|
152
|
-
content: (cl.members || []).map(jid => ({ tag: 'user', attrs: { jid }, content: [] }))
|
|
153
|
-
});
|
|
154
|
-
}
|
|
155
|
-
await query({
|
|
156
|
-
tag: 'iq',
|
|
157
|
-
attrs: {
|
|
158
|
-
xmlns: 'status',
|
|
159
|
-
to: WABinary_1.S_WHATSAPP_NET,
|
|
160
|
-
type: 'set'
|
|
161
|
-
},
|
|
162
|
-
content: [{ tag: 'privacy', attrs: {}, content }]
|
|
163
|
-
});
|
|
164
|
-
};
|
|
165
115
|
const updateReadReceiptsPrivacy = async (value) => {
|
|
166
116
|
await privacyQuery('readreceipts', value);
|
|
167
117
|
};
|
|
@@ -173,7 +123,7 @@ const makeChatsSocket = config => {
|
|
|
173
123
|
tag: 'iq',
|
|
174
124
|
attrs: {
|
|
175
125
|
xmlns: 'disappearing_mode',
|
|
176
|
-
to:
|
|
126
|
+
to: S_WHATSAPP_NET,
|
|
177
127
|
type: 'set'
|
|
178
128
|
},
|
|
179
129
|
content: [
|
|
@@ -186,35 +136,12 @@ const makeChatsSocket = config => {
|
|
|
186
136
|
]
|
|
187
137
|
});
|
|
188
138
|
};
|
|
189
|
-
const fetchBroadcastListQuota = async () => {
|
|
190
|
-
const result = await query({
|
|
191
|
-
tag: 'iq',
|
|
192
|
-
attrs: {
|
|
193
|
-
xmlns: 'w:biz',
|
|
194
|
-
to: WABinary_1.S_WHATSAPP_NET,
|
|
195
|
-
type: 'get'
|
|
196
|
-
},
|
|
197
|
-
content: [{ tag: 'broadcast_list_quota', attrs: {}, content: [] }]
|
|
198
|
-
}, 32000);
|
|
199
|
-
const limitsNode = (0, WABinary_1.getBinaryNodeChild)(result, 'limits');
|
|
200
|
-
const timeframeNode = (0, WABinary_1.getBinaryNodeChild)(result, 'timeframe');
|
|
201
|
-
if (!limitsNode)
|
|
202
|
-
return null;
|
|
203
|
-
return {
|
|
204
|
-
messagesLeft: parseInt(limitsNode.attrs?.messages_left ?? (0, WABinary_1.getBinaryNodeChild)(limitsNode, 'messages_left')?.content ?? '0'),
|
|
205
|
-
totalLimit: parseInt(limitsNode.attrs?.total_limit ?? (0, WABinary_1.getBinaryNodeChild)(limitsNode, 'total_limit')?.content ?? '0'),
|
|
206
|
-
isHeavySender: (limitsNode.attrs?.is_heavy_sender ?? (0, WABinary_1.getBinaryNodeChild)(limitsNode, 'is_heavy_sender')?.content) === 'true',
|
|
207
|
-
startTs: parseInt(timeframeNode?.attrs?.start_ts_s ?? (0, WABinary_1.getBinaryNodeChild)(timeframeNode, 'start_ts_s')?.content ?? '0'),
|
|
208
|
-
endTs: parseInt(timeframeNode?.attrs?.end_ts_s ?? (0, WABinary_1.getBinaryNodeChild)(timeframeNode, 'end_ts_s')?.content ?? '0'),
|
|
209
|
-
resetTs: parseInt(timeframeNode?.attrs?.reset_ts_s ?? (0, WABinary_1.getBinaryNodeChild)(timeframeNode, 'reset_ts_s')?.content ?? '0')
|
|
210
|
-
};
|
|
211
|
-
};
|
|
212
139
|
const getBotListV2 = async () => {
|
|
213
140
|
const resp = await query({
|
|
214
141
|
tag: 'iq',
|
|
215
142
|
attrs: {
|
|
216
143
|
xmlns: 'bot',
|
|
217
|
-
to:
|
|
144
|
+
to: S_WHATSAPP_NET,
|
|
218
145
|
type: 'get'
|
|
219
146
|
},
|
|
220
147
|
content: [
|
|
@@ -226,11 +153,11 @@ const makeChatsSocket = config => {
|
|
|
226
153
|
}
|
|
227
154
|
]
|
|
228
155
|
});
|
|
229
|
-
const botNode =
|
|
156
|
+
const botNode = getBinaryNodeChild(resp, 'bot');
|
|
230
157
|
const botList = [];
|
|
231
|
-
for (const section of
|
|
158
|
+
for (const section of getBinaryNodeChildren(botNode, 'section')) {
|
|
232
159
|
if (section.attrs.type === 'all') {
|
|
233
|
-
for (const bot of
|
|
160
|
+
for (const bot of getBinaryNodeChildren(section, 'bot')) {
|
|
234
161
|
botList.push({
|
|
235
162
|
jid: bot.attrs.jid,
|
|
236
163
|
personaId: bot.attrs['persona_id']
|
|
@@ -240,66 +167,10 @@ const makeChatsSocket = config => {
|
|
|
240
167
|
}
|
|
241
168
|
return botList;
|
|
242
169
|
};
|
|
243
|
-
const getChatBlockingStatus = async () => {
|
|
244
|
-
const result = await query({
|
|
245
|
-
tag: 'iq',
|
|
246
|
-
attrs: {
|
|
247
|
-
to: WABinary_1.S_WHATSAPP_NET,
|
|
248
|
-
xmlns: 'w:comms:chat',
|
|
249
|
-
type: 'get'
|
|
250
|
-
},
|
|
251
|
-
content: [{ tag: 'query', attrs: {}, content: [{ tag: 'blocking_status', attrs: {} }] }]
|
|
252
|
-
});
|
|
253
|
-
const blocking = (0, WABinary_1.getBinaryNodeChild)(result, 'blocking') ||
|
|
254
|
-
(0, WABinary_1.getBinaryNodeChild)((0, WABinary_1.getBinaryNodeChild)(result, 'query'), 'blocking');
|
|
255
|
-
return blocking?.attrs?.status;
|
|
256
|
-
};
|
|
257
|
-
const updateChatBlockingStatus = async (action) => {
|
|
258
|
-
const result = await query({
|
|
259
|
-
tag: 'iq',
|
|
260
|
-
attrs: {
|
|
261
|
-
to: WABinary_1.S_WHATSAPP_NET,
|
|
262
|
-
xmlns: 'w:comms:chat',
|
|
263
|
-
type: 'set'
|
|
264
|
-
},
|
|
265
|
-
content: [{ tag: 'blocking', attrs: { action } }]
|
|
266
|
-
});
|
|
267
|
-
const blocking = (0, WABinary_1.getBinaryNodeChild)(result, 'blocking');
|
|
268
|
-
return blocking?.attrs?.status;
|
|
269
|
-
};
|
|
270
|
-
const getUserDisclosures = async (t = 0) => {
|
|
271
|
-
const result = await query({
|
|
272
|
-
tag: 'iq',
|
|
273
|
-
attrs: { to: WABinary_1.S_WHATSAPP_NET, xmlns: 'tos', type: 'get' },
|
|
274
|
-
content: [{ tag: 'get_user_disclosures', attrs: { t: String(t) } }]
|
|
275
|
-
});
|
|
276
|
-
return (0, WABinary_1.getBinaryNodeChildren)(result, 'notice').map(n => ({ ...n.attrs }));
|
|
277
|
-
};
|
|
278
|
-
const getOptOutList = async () => {
|
|
279
|
-
return query({
|
|
280
|
-
tag: 'iq',
|
|
281
|
-
attrs: { to: WABinary_1.S_WHATSAPP_NET, xmlns: 'optoutlist', type: 'get' }
|
|
282
|
-
});
|
|
283
|
-
};
|
|
284
|
-
const getPushConfig = async () => {
|
|
285
|
-
const result = await query({
|
|
286
|
-
tag: 'iq',
|
|
287
|
-
attrs: { to: WABinary_1.S_WHATSAPP_NET, xmlns: 'urn:xmpp:whatsapp:push', type: 'get' },
|
|
288
|
-
content: [{ tag: 'settings', attrs: {} }]
|
|
289
|
-
});
|
|
290
|
-
return (0, WABinary_1.getBinaryNodeChild)(result, 'settings');
|
|
291
|
-
};
|
|
292
|
-
const setPushConfig = async (config) => {
|
|
293
|
-
await query({
|
|
294
|
-
tag: 'iq',
|
|
295
|
-
attrs: { to: WABinary_1.S_WHATSAPP_NET, xmlns: 'urn:xmpp:whatsapp:push', type: 'set' },
|
|
296
|
-
content: [{ tag: 'config', attrs: config }]
|
|
297
|
-
});
|
|
298
|
-
};
|
|
299
170
|
const fetchStatus = async (...jids) => {
|
|
300
|
-
const usyncQuery = new
|
|
171
|
+
const usyncQuery = new USyncQuery().withStatusProtocol();
|
|
301
172
|
for (const jid of jids) {
|
|
302
|
-
usyncQuery.withUser(new
|
|
173
|
+
usyncQuery.withUser(new USyncUser().withId(jid));
|
|
303
174
|
}
|
|
304
175
|
const result = await sock.executeUSyncQuery(usyncQuery);
|
|
305
176
|
if (result) {
|
|
@@ -307,31 +178,32 @@ const makeChatsSocket = config => {
|
|
|
307
178
|
}
|
|
308
179
|
};
|
|
309
180
|
const fetchDisappearingDuration = async (...jids) => {
|
|
310
|
-
const usyncQuery = new
|
|
181
|
+
const usyncQuery = new USyncQuery().withDisappearingModeProtocol();
|
|
311
182
|
for (const jid of jids) {
|
|
312
|
-
usyncQuery.withUser(new
|
|
183
|
+
usyncQuery.withUser(new USyncUser().withId(jid));
|
|
313
184
|
}
|
|
314
185
|
const result = await sock.executeUSyncQuery(usyncQuery);
|
|
315
186
|
if (result) {
|
|
316
187
|
return result.list;
|
|
317
188
|
}
|
|
318
189
|
};
|
|
190
|
+
/** update the profile picture for yourself or a group */
|
|
319
191
|
const updateProfilePicture = async (jid, content, dimensions) => {
|
|
320
192
|
let targetJid;
|
|
321
193
|
if (!jid) {
|
|
322
|
-
throw new
|
|
194
|
+
throw new Boom('Illegal no-jid profile update. Please specify either your ID or the ID of the chat you wish to update');
|
|
323
195
|
}
|
|
324
|
-
if (
|
|
325
|
-
targetJid =
|
|
196
|
+
if (jidNormalizedUser(jid) !== jidNormalizedUser(authState.creds.me.id)) {
|
|
197
|
+
targetJid = jidNormalizedUser(jid); // in case it is someone other than us
|
|
326
198
|
}
|
|
327
199
|
else {
|
|
328
200
|
targetJid = undefined;
|
|
329
201
|
}
|
|
330
|
-
const { img } = await
|
|
202
|
+
const { img } = await generateProfilePicture(content, dimensions);
|
|
331
203
|
await query({
|
|
332
204
|
tag: 'iq',
|
|
333
205
|
attrs: {
|
|
334
|
-
to:
|
|
206
|
+
to: S_WHATSAPP_NET,
|
|
335
207
|
type: 'set',
|
|
336
208
|
xmlns: 'w:profile:picture',
|
|
337
209
|
...(targetJid ? { target: targetJid } : {})
|
|
@@ -345,13 +217,14 @@ const makeChatsSocket = config => {
|
|
|
345
217
|
]
|
|
346
218
|
});
|
|
347
219
|
};
|
|
220
|
+
/** remove the profile picture for yourself or a group */
|
|
348
221
|
const removeProfilePicture = async (jid) => {
|
|
349
222
|
let targetJid;
|
|
350
223
|
if (!jid) {
|
|
351
|
-
throw new
|
|
224
|
+
throw new Boom('Illegal no-jid profile update. Please specify either your ID or the ID of the chat you wish to update');
|
|
352
225
|
}
|
|
353
|
-
if (
|
|
354
|
-
targetJid =
|
|
226
|
+
if (jidNormalizedUser(jid) !== jidNormalizedUser(authState.creds.me.id)) {
|
|
227
|
+
targetJid = jidNormalizedUser(jid); // in case it is someone other than us
|
|
355
228
|
}
|
|
356
229
|
else {
|
|
357
230
|
targetJid = undefined;
|
|
@@ -359,18 +232,19 @@ const makeChatsSocket = config => {
|
|
|
359
232
|
await query({
|
|
360
233
|
tag: 'iq',
|
|
361
234
|
attrs: {
|
|
362
|
-
to:
|
|
235
|
+
to: S_WHATSAPP_NET,
|
|
363
236
|
type: 'set',
|
|
364
237
|
xmlns: 'w:profile:picture',
|
|
365
238
|
...(targetJid ? { target: targetJid } : {})
|
|
366
239
|
}
|
|
367
240
|
});
|
|
368
241
|
};
|
|
242
|
+
/** update the profile status for yourself */
|
|
369
243
|
const updateProfileStatus = async (status) => {
|
|
370
244
|
await query({
|
|
371
245
|
tag: 'iq',
|
|
372
246
|
attrs: {
|
|
373
|
-
to:
|
|
247
|
+
to: S_WHATSAPP_NET,
|
|
374
248
|
type: 'set',
|
|
375
249
|
xmlns: 'status'
|
|
376
250
|
},
|
|
@@ -391,28 +265,61 @@ const makeChatsSocket = config => {
|
|
|
391
265
|
tag: 'iq',
|
|
392
266
|
attrs: {
|
|
393
267
|
xmlns: 'blocklist',
|
|
394
|
-
to:
|
|
268
|
+
to: S_WHATSAPP_NET,
|
|
395
269
|
type: 'get'
|
|
396
270
|
}
|
|
397
271
|
});
|
|
398
|
-
const listNode =
|
|
399
|
-
return
|
|
272
|
+
const listNode = getBinaryNodeChild(result, 'list');
|
|
273
|
+
return getBinaryNodeChildren(listNode, 'item').map(n => n.attrs.jid);
|
|
400
274
|
};
|
|
401
275
|
const updateBlockStatus = async (jid, action) => {
|
|
276
|
+
const normalizedJid = jidNormalizedUser(jid);
|
|
277
|
+
let lid;
|
|
278
|
+
let pn_jid;
|
|
279
|
+
if (isLidUser(normalizedJid) || isHostedLidUser(normalizedJid)) {
|
|
280
|
+
lid = normalizedJid;
|
|
281
|
+
if (action === 'block') {
|
|
282
|
+
const pn = await signalRepository.lidMapping.getPNForLID(normalizedJid);
|
|
283
|
+
if (!pn) {
|
|
284
|
+
throw new Boom(`Unable to resolve PN JID for LID: ${jid}`, { statusCode: 400 });
|
|
285
|
+
}
|
|
286
|
+
pn_jid = jidNormalizedUser(pn);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
else if (isPnUser(normalizedJid) || isHostedPnUser(normalizedJid)) {
|
|
290
|
+
const mapped = await signalRepository.lidMapping.getLIDForPN(normalizedJid);
|
|
291
|
+
if (!mapped) {
|
|
292
|
+
throw new Boom(`Unable to resolve LID for PN JID: ${jid}`, { statusCode: 400 });
|
|
293
|
+
}
|
|
294
|
+
lid = mapped;
|
|
295
|
+
if (action === 'block') {
|
|
296
|
+
pn_jid = jidNormalizedUser(normalizedJid);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
else {
|
|
300
|
+
throw new Boom(`Invalid jid: ${jid}`, { statusCode: 400 });
|
|
301
|
+
}
|
|
302
|
+
const itemAttrs = {
|
|
303
|
+
action,
|
|
304
|
+
jid: lid
|
|
305
|
+
};
|
|
306
|
+
if (action === 'block') {
|
|
307
|
+
if (!pn_jid) {
|
|
308
|
+
throw new Boom(`pn_jid required for block: ${jid}`, { statusCode: 400 });
|
|
309
|
+
}
|
|
310
|
+
itemAttrs.pn_jid = pn_jid;
|
|
311
|
+
}
|
|
402
312
|
await query({
|
|
403
313
|
tag: 'iq',
|
|
404
314
|
attrs: {
|
|
405
315
|
xmlns: 'blocklist',
|
|
406
|
-
to:
|
|
316
|
+
to: S_WHATSAPP_NET,
|
|
407
317
|
type: 'set'
|
|
408
318
|
},
|
|
409
319
|
content: [
|
|
410
320
|
{
|
|
411
321
|
tag: 'item',
|
|
412
|
-
attrs:
|
|
413
|
-
action,
|
|
414
|
-
jid
|
|
415
|
-
}
|
|
322
|
+
attrs: itemAttrs
|
|
416
323
|
}
|
|
417
324
|
]
|
|
418
325
|
});
|
|
@@ -438,17 +345,17 @@ const makeChatsSocket = config => {
|
|
|
438
345
|
}
|
|
439
346
|
]
|
|
440
347
|
});
|
|
441
|
-
const profileNode =
|
|
442
|
-
const profiles =
|
|
348
|
+
const profileNode = getBinaryNodeChild(results, 'business_profile');
|
|
349
|
+
const profiles = getBinaryNodeChild(profileNode, 'profile');
|
|
443
350
|
if (profiles) {
|
|
444
|
-
const address =
|
|
445
|
-
const description =
|
|
446
|
-
const website =
|
|
447
|
-
const email =
|
|
448
|
-
const category =
|
|
449
|
-
const businessHours =
|
|
351
|
+
const address = getBinaryNodeChild(profiles, 'address');
|
|
352
|
+
const description = getBinaryNodeChild(profiles, 'description');
|
|
353
|
+
const website = getBinaryNodeChild(profiles, 'website');
|
|
354
|
+
const email = getBinaryNodeChild(profiles, 'email');
|
|
355
|
+
const category = getBinaryNodeChild(getBinaryNodeChild(profiles, 'categories'), 'category');
|
|
356
|
+
const businessHours = getBinaryNodeChild(profiles, 'business_hours');
|
|
450
357
|
const businessHoursConfig = businessHours
|
|
451
|
-
?
|
|
358
|
+
? getBinaryNodeChildren(businessHours, 'business_hours_config')
|
|
452
359
|
: undefined;
|
|
453
360
|
const websiteStr = website?.content?.toString();
|
|
454
361
|
return {
|
|
@@ -470,7 +377,7 @@ const makeChatsSocket = config => {
|
|
|
470
377
|
await sendNode({
|
|
471
378
|
tag: 'iq',
|
|
472
379
|
attrs: {
|
|
473
|
-
to:
|
|
380
|
+
to: S_WHATSAPP_NET,
|
|
474
381
|
type: 'set',
|
|
475
382
|
xmlns: 'urn:xmpp:whatsapp:dirty',
|
|
476
383
|
id: generateMessageTag()
|
|
@@ -486,11 +393,10 @@ const makeChatsSocket = config => {
|
|
|
486
393
|
]
|
|
487
394
|
});
|
|
488
395
|
};
|
|
489
|
-
const newAppStateChunkHandler = isInitialSync => {
|
|
396
|
+
const newAppStateChunkHandler = (isInitialSync) => {
|
|
490
397
|
return {
|
|
491
398
|
onMutation(mutation) {
|
|
492
|
-
;
|
|
493
|
-
(0, Utils_1.processSyncAction)(mutation, ev, authState.creds.me, isInitialSync ? { accountSettings: authState.creds.accountSettings } : undefined, logger);
|
|
399
|
+
processSyncAction(mutation, ev, authState.creds.me, isInitialSync ? { accountSettings: authState.creds.accountSettings } : undefined, logger);
|
|
494
400
|
}
|
|
495
401
|
};
|
|
496
402
|
};
|
|
@@ -504,12 +410,20 @@ const makeChatsSocket = config => {
|
|
|
504
410
|
appStateSyncKeyCache.set(keyId, key ?? null);
|
|
505
411
|
return key;
|
|
506
412
|
};
|
|
413
|
+
// we use this to determine which events to fire
|
|
414
|
+
// otherwise when we resync from scratch -- all notifications will fire
|
|
507
415
|
const initialVersionMap = {};
|
|
508
416
|
const globalMutationMap = {};
|
|
509
417
|
await authState.keys.transaction(async () => {
|
|
510
418
|
const collectionsToHandle = new Set(collections);
|
|
419
|
+
// in case something goes wrong -- ensure we don't enter a loop that cannot be exited from
|
|
511
420
|
const attemptsMap = {};
|
|
421
|
+
// collections that failed and need a full snapshot on retry
|
|
422
|
+
// mirrors WA Web's ErrorFatal -> force snapshot behavior
|
|
512
423
|
const forceSnapshotCollections = new Set();
|
|
424
|
+
// keep executing till all collections are done
|
|
425
|
+
// sometimes a single patch request will not return all the patches (God knows why)
|
|
426
|
+
// so we fetch till they're all done (this is determined by the "has_more_patches" flag)
|
|
513
427
|
while (collectionsToHandle.size) {
|
|
514
428
|
const states = {};
|
|
515
429
|
const nodes = [];
|
|
@@ -517,12 +431,13 @@ const makeChatsSocket = config => {
|
|
|
517
431
|
const result = await authState.keys.get('app-state-sync-version', [name]);
|
|
518
432
|
let state = result[name];
|
|
519
433
|
if (state) {
|
|
434
|
+
state = ensureLTHashStateVersion(state);
|
|
520
435
|
if (typeof initialVersionMap[name] === 'undefined') {
|
|
521
436
|
initialVersionMap[name] = state.version;
|
|
522
437
|
}
|
|
523
438
|
}
|
|
524
439
|
else {
|
|
525
|
-
state =
|
|
440
|
+
state = newLTHashState();
|
|
526
441
|
}
|
|
527
442
|
states[name] = state;
|
|
528
443
|
const shouldForceSnapshot = forceSnapshotCollections.has(name);
|
|
@@ -535,6 +450,7 @@ const makeChatsSocket = config => {
|
|
|
535
450
|
attrs: {
|
|
536
451
|
name,
|
|
537
452
|
version: state.version.toString(),
|
|
453
|
+
// return snapshot if syncing from scratch or forcing after a failed attempt
|
|
538
454
|
return_snapshot: (shouldForceSnapshot || !state.version).toString()
|
|
539
455
|
}
|
|
540
456
|
});
|
|
@@ -542,7 +458,7 @@ const makeChatsSocket = config => {
|
|
|
542
458
|
const result = await query({
|
|
543
459
|
tag: 'iq',
|
|
544
460
|
attrs: {
|
|
545
|
-
to:
|
|
461
|
+
to: S_WHATSAPP_NET,
|
|
546
462
|
xmlns: 'w:sync:app:state',
|
|
547
463
|
type: 'set'
|
|
548
464
|
},
|
|
@@ -554,20 +470,22 @@ const makeChatsSocket = config => {
|
|
|
554
470
|
}
|
|
555
471
|
]
|
|
556
472
|
});
|
|
557
|
-
|
|
473
|
+
// extract from binary node
|
|
474
|
+
const decoded = await extractSyncdPatches(result, config?.options);
|
|
558
475
|
for (const key in decoded) {
|
|
559
476
|
const name = key;
|
|
560
477
|
const { patches, hasMorePatches, snapshot } = decoded[name];
|
|
561
478
|
try {
|
|
562
479
|
if (snapshot) {
|
|
563
|
-
const { state: newState, mutationMap } = await
|
|
480
|
+
const { state: newState, mutationMap } = await decodeSyncdSnapshot(name, snapshot, getCachedAppStateSyncKey, initialVersionMap[name], appStateMacVerification.snapshot, logger);
|
|
564
481
|
states[name] = newState;
|
|
565
482
|
Object.assign(globalMutationMap, mutationMap);
|
|
566
483
|
logger.info(`restored state of ${name} from snapshot to v${newState.version} with mutations`);
|
|
567
484
|
await authState.keys.set({ 'app-state-sync-version': { [name]: newState } });
|
|
568
485
|
}
|
|
486
|
+
// only process if there are syncd patches
|
|
569
487
|
if (patches.length) {
|
|
570
|
-
const { state: newState, mutationMap } = await
|
|
488
|
+
const { state: newState, mutationMap } = await decodePatches(name, patches, states[name], getCachedAppStateSyncKey, config.options, initialVersionMap[name], logger, appStateMacVerification.patch);
|
|
571
489
|
await authState.keys.set({ 'app-state-sync-version': { [name]: newState } });
|
|
572
490
|
logger.info(`synced ${name} to v${newState.version}`);
|
|
573
491
|
initialVersionMap[name] = newState.version;
|
|
@@ -577,6 +495,7 @@ const makeChatsSocket = config => {
|
|
|
577
495
|
logger.info(`${name} has more patches...`);
|
|
578
496
|
}
|
|
579
497
|
else {
|
|
498
|
+
// collection is done with sync
|
|
580
499
|
collectionsToHandle.delete(name);
|
|
581
500
|
}
|
|
582
501
|
}
|
|
@@ -590,21 +509,26 @@ const makeChatsSocket = config => {
|
|
|
590
509
|
errorType: error.name,
|
|
591
510
|
error: error.stack
|
|
592
511
|
};
|
|
593
|
-
if (
|
|
512
|
+
if (isMissingKeyError(error) && attemptsMap[name] >= MAX_SYNC_ATTEMPTS) {
|
|
513
|
+
// WA Web treats missing keys as "Blocked" — park the collection
|
|
514
|
+
// until the key arrives via APP_STATE_SYNC_KEY_SHARE.
|
|
594
515
|
logger.warn(logData, `${name} blocked on missing key from v${states[name].version}, parking after ${attemptsMap[name]} attempts`);
|
|
595
516
|
blockedCollections.add(name);
|
|
596
517
|
collectionsToHandle.delete(name);
|
|
597
518
|
}
|
|
598
|
-
else if (
|
|
519
|
+
else if (isMissingKeyError(error)) {
|
|
520
|
+
// Retry with a snapshot which may use a different key.
|
|
599
521
|
logger.info(logData, `${name} blocked on missing key from v${states[name].version}, retrying with snapshot`);
|
|
600
522
|
forceSnapshotCollections.add(name);
|
|
601
523
|
}
|
|
602
|
-
else if (
|
|
524
|
+
else if (isAppStateSyncIrrecoverable(error, attemptsMap[name])) {
|
|
603
525
|
logger.warn(logData, `failed to sync ${name} from v${states[name].version}, giving up`);
|
|
604
526
|
collectionsToHandle.delete(name);
|
|
605
527
|
}
|
|
606
528
|
else {
|
|
607
529
|
logger.info(logData, `failed to sync ${name} from v${states[name].version}, forcing snapshot retry`);
|
|
530
|
+
// force a full snapshot on retry to recover from
|
|
531
|
+
// corrupted local state (e.g. LTHash MAC mismatch)
|
|
608
532
|
forceSnapshotCollections.add(name);
|
|
609
533
|
}
|
|
610
534
|
}
|
|
@@ -616,36 +540,41 @@ const makeChatsSocket = config => {
|
|
|
616
540
|
onMutation(globalMutationMap[key]);
|
|
617
541
|
}
|
|
618
542
|
});
|
|
543
|
+
/**
|
|
544
|
+
* fetch the profile picture of a user/group
|
|
545
|
+
* type = "preview" for a low res picture
|
|
546
|
+
* type = "image for the high res picture"
|
|
547
|
+
*/
|
|
619
548
|
const profilePictureUrl = async (jid, type = 'preview', timeoutMs) => {
|
|
620
|
-
const
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
549
|
+
const baseContent = [{ tag: 'picture', attrs: { type, query: 'url' } }];
|
|
550
|
+
// WA Web only includes tctoken for user JIDs (not groups/newsletters)
|
|
551
|
+
// and never for own profile pic (Chat model for self has no tcToken).
|
|
552
|
+
// Including tctoken for own JID causes the server to never respond.
|
|
553
|
+
const normalizedJid = jidNormalizedUser(jid);
|
|
554
|
+
const isUserJid = isPnUser(normalizedJid) || isLidUser(normalizedJid);
|
|
624
555
|
const me = authState.creds.me;
|
|
625
|
-
const isSelf = me &&
|
|
626
|
-
(normalizedJid === (0, WABinary_1.jidNormalizedUser)(me.id) ||
|
|
627
|
-
(me.lid && normalizedJid === (0, WABinary_1.jidNormalizedUser)(me.lid)));
|
|
556
|
+
const isSelf = me && (normalizedJid === jidNormalizedUser(me.id) || (me.lid && normalizedJid === jidNormalizedUser(me.lid)));
|
|
628
557
|
let content = baseContent;
|
|
629
558
|
if (serverProps.profilePicPrivacyToken && isUserJid && !isSelf) {
|
|
630
|
-
content = await
|
|
559
|
+
content = await buildTcTokenFromJid({
|
|
631
560
|
authState,
|
|
632
561
|
jid: normalizedJid,
|
|
633
562
|
baseContent,
|
|
634
563
|
getLIDForPN
|
|
635
564
|
});
|
|
636
565
|
}
|
|
637
|
-
jid =
|
|
566
|
+
jid = jidNormalizedUser(jid);
|
|
638
567
|
const result = await query({
|
|
639
568
|
tag: 'iq',
|
|
640
569
|
attrs: {
|
|
641
570
|
target: jid,
|
|
642
|
-
to:
|
|
571
|
+
to: S_WHATSAPP_NET,
|
|
643
572
|
type: 'get',
|
|
644
573
|
xmlns: 'w:profile:picture'
|
|
645
574
|
},
|
|
646
575
|
content
|
|
647
576
|
}, timeoutMs);
|
|
648
|
-
const child =
|
|
577
|
+
const child = getBinaryNodeChild(result, 'picture');
|
|
649
578
|
return child?.attrs?.url;
|
|
650
579
|
};
|
|
651
580
|
const createCallLink = async (type, event, timeoutMs) => {
|
|
@@ -663,23 +592,9 @@ const makeChatsSocket = config => {
|
|
|
663
592
|
}
|
|
664
593
|
]
|
|
665
594
|
}, timeoutMs);
|
|
666
|
-
const child =
|
|
595
|
+
const child = getBinaryNodeChild(result, 'link_create');
|
|
667
596
|
return child?.attrs?.token;
|
|
668
597
|
};
|
|
669
|
-
const toggleCallLinkWaitingRoom = async (linkToken, enabled, media = 'audio') => {
|
|
670
|
-
const result = await query({
|
|
671
|
-
tag: 'call',
|
|
672
|
-
attrs: { id: generateMessageTag(), to: '@call' },
|
|
673
|
-
content: [
|
|
674
|
-
{
|
|
675
|
-
tag: 'waiting_room_toggle',
|
|
676
|
-
attrs: { enabled: enabled ? '1' : '0', 'link-token': linkToken, media }
|
|
677
|
-
}
|
|
678
|
-
]
|
|
679
|
-
});
|
|
680
|
-
const child = (0, WABinary_1.getBinaryNodeChild)(result, 'waiting_room_toggle');
|
|
681
|
-
return child?.attrs;
|
|
682
|
-
};
|
|
683
598
|
const sendPresenceUpdate = async (type, toJid) => {
|
|
684
599
|
const me = authState.creds.me;
|
|
685
600
|
const isAvailableType = type === 'available';
|
|
@@ -701,7 +616,7 @@ const makeChatsSocket = config => {
|
|
|
701
616
|
});
|
|
702
617
|
}
|
|
703
618
|
else {
|
|
704
|
-
const { server } =
|
|
619
|
+
const { server } = jidDecode(toJid);
|
|
705
620
|
const isLid = server === 'lid';
|
|
706
621
|
await sendNode({
|
|
707
622
|
tag: 'chatstate',
|
|
@@ -718,11 +633,16 @@ const makeChatsSocket = config => {
|
|
|
718
633
|
});
|
|
719
634
|
}
|
|
720
635
|
};
|
|
636
|
+
/**
|
|
637
|
+
* @param toJid the jid to subscribe to
|
|
638
|
+
* @param tcToken token for subscription, use if present
|
|
639
|
+
*/
|
|
721
640
|
const presenceSubscribe = async (toJid) => {
|
|
722
|
-
|
|
723
|
-
const
|
|
641
|
+
// Only include tctoken for user JIDs — groups/newsletters don't use tctokens
|
|
642
|
+
const normalizedToJid = jidNormalizedUser(toJid);
|
|
643
|
+
const isUserJid = isPnUser(normalizedToJid) || isLidUser(normalizedToJid);
|
|
724
644
|
const tcTokenContent = isUserJid
|
|
725
|
-
? await
|
|
645
|
+
? await buildTcTokenFromJid({ authState, jid: normalizedToJid, getLIDForPN })
|
|
726
646
|
: undefined;
|
|
727
647
|
return sendNode({
|
|
728
648
|
tag: 'presence',
|
|
@@ -738,13 +658,14 @@ const makeChatsSocket = config => {
|
|
|
738
658
|
let presence;
|
|
739
659
|
const jid = attrs.from;
|
|
740
660
|
const participant = attrs.participant || attrs.from;
|
|
741
|
-
if (shouldIgnoreJid(jid) && jid !==
|
|
661
|
+
if (shouldIgnoreJid(jid) && jid !== S_WHATSAPP_NET) {
|
|
742
662
|
return;
|
|
743
663
|
}
|
|
744
664
|
if (tag === 'presence') {
|
|
745
665
|
presence = {
|
|
746
666
|
lastKnownPresence: attrs.type === 'unavailable' ? 'unavailable' : 'available',
|
|
747
|
-
lastSeen: attrs.last && attrs.last !== 'deny' ? +attrs.last : undefined
|
|
667
|
+
lastSeen: attrs.last && attrs.last !== 'deny' ? +attrs.last : undefined,
|
|
668
|
+
groupOnlineCount: attrs.count ? +attrs.count : undefined
|
|
748
669
|
};
|
|
749
670
|
}
|
|
750
671
|
else if (Array.isArray(content)) {
|
|
@@ -769,7 +690,7 @@ const makeChatsSocket = config => {
|
|
|
769
690
|
const name = patchCreate.type;
|
|
770
691
|
const myAppStateKeyId = authState.creds.myAppStateKeyId;
|
|
771
692
|
if (!myAppStateKeyId) {
|
|
772
|
-
throw new
|
|
693
|
+
throw new Boom('App state key not present!', { statusCode: 400 });
|
|
773
694
|
}
|
|
774
695
|
let initial;
|
|
775
696
|
let encodeResult;
|
|
@@ -778,13 +699,13 @@ const makeChatsSocket = config => {
|
|
|
778
699
|
logger.debug({ patch: patchCreate }, 'applying app patch');
|
|
779
700
|
await resyncAppState([name], false);
|
|
780
701
|
const { [name]: currentSyncVersion } = await authState.keys.get('app-state-sync-version', [name]);
|
|
781
|
-
initial = currentSyncVersion
|
|
782
|
-
encodeResult = await
|
|
702
|
+
initial = currentSyncVersion ? ensureLTHashStateVersion(currentSyncVersion) : newLTHashState();
|
|
703
|
+
encodeResult = await encodeSyncdPatch(patchCreate, myAppStateKeyId, initial, getAppStateSyncKey);
|
|
783
704
|
const { patch, state } = encodeResult;
|
|
784
705
|
const node = {
|
|
785
706
|
tag: 'iq',
|
|
786
707
|
attrs: {
|
|
787
|
-
to:
|
|
708
|
+
to: S_WHATSAPP_NET,
|
|
788
709
|
type: 'set',
|
|
789
710
|
xmlns: 'w:sync:app:state'
|
|
790
711
|
},
|
|
@@ -804,7 +725,7 @@ const makeChatsSocket = config => {
|
|
|
804
725
|
{
|
|
805
726
|
tag: 'patch',
|
|
806
727
|
attrs: {},
|
|
807
|
-
content:
|
|
728
|
+
content: proto.SyncdPatch.encode(patch).finish()
|
|
808
729
|
}
|
|
809
730
|
]
|
|
810
731
|
}
|
|
@@ -818,39 +739,42 @@ const makeChatsSocket = config => {
|
|
|
818
739
|
});
|
|
819
740
|
if (config.emitOwnEvents) {
|
|
820
741
|
const { onMutation } = newAppStateChunkHandler(false);
|
|
821
|
-
const { mutationMap } = await
|
|
742
|
+
const { mutationMap } = await decodePatches(name, [{ ...encodeResult.patch, version: { version: encodeResult.state.version } }], initial, getAppStateSyncKey, config.options, undefined, logger);
|
|
822
743
|
for (const key in mutationMap) {
|
|
823
744
|
onMutation(mutationMap[key]);
|
|
824
745
|
}
|
|
825
746
|
}
|
|
826
747
|
};
|
|
748
|
+
/** fetch AB props */
|
|
827
749
|
const fetchProps = async () => {
|
|
828
750
|
const resultNode = await query({
|
|
829
751
|
tag: 'iq',
|
|
830
752
|
attrs: {
|
|
831
|
-
to:
|
|
832
|
-
xmlns: '
|
|
753
|
+
to: S_WHATSAPP_NET,
|
|
754
|
+
xmlns: 'abt',
|
|
833
755
|
type: 'get'
|
|
834
756
|
},
|
|
835
757
|
content: [
|
|
836
758
|
{
|
|
837
759
|
tag: 'props',
|
|
838
760
|
attrs: {
|
|
839
|
-
protocol: '
|
|
840
|
-
hash: authState
|
|
761
|
+
protocol: '1',
|
|
762
|
+
...(authState?.creds?.lastPropHash ? { hash: authState.creds.lastPropHash } : {})
|
|
841
763
|
}
|
|
842
764
|
}
|
|
843
765
|
]
|
|
844
766
|
});
|
|
845
|
-
const propsNode =
|
|
767
|
+
const propsNode = getBinaryNodeChild(resultNode, 'props');
|
|
846
768
|
let props = {};
|
|
847
769
|
if (propsNode) {
|
|
848
770
|
if (propsNode.attrs?.hash) {
|
|
771
|
+
// on some clients, the hash is returning as undefined
|
|
849
772
|
authState.creds.lastPropHash = propsNode?.attrs?.hash;
|
|
850
773
|
ev.emit('creds.update', authState.creds);
|
|
851
774
|
}
|
|
852
|
-
props =
|
|
775
|
+
props = reduceBinaryNodeToDictionary(propsNode, 'prop');
|
|
853
776
|
}
|
|
777
|
+
// Extract protocol-relevant AB props (only the ones we need)
|
|
854
778
|
const privacyTokenProp = props['10518'] ?? props['privacy_token_sending_on_all_1_on_1_messages'];
|
|
855
779
|
if (privacyTokenProp !== undefined) {
|
|
856
780
|
serverProps.privacyTokenOn1to1 = privacyTokenProp === 'true' || privacyTokenProp === '1';
|
|
@@ -866,15 +790,26 @@ const makeChatsSocket = config => {
|
|
|
866
790
|
logger.debug({ serverProps }, 'fetched props');
|
|
867
791
|
return props;
|
|
868
792
|
};
|
|
793
|
+
/**
|
|
794
|
+
* modify a chat -- mark unread, read etc.
|
|
795
|
+
* lastMessages must be sorted in reverse chronologically
|
|
796
|
+
* requires the last messages till the last message received; required for archive & unread
|
|
797
|
+
*/
|
|
869
798
|
const chatModify = (mod, jid) => {
|
|
870
|
-
const patch =
|
|
799
|
+
const patch = chatModificationToAppPatch(mod, jid);
|
|
871
800
|
return appPatch(patch);
|
|
872
801
|
};
|
|
873
|
-
|
|
802
|
+
/**
|
|
803
|
+
* Enable/Disable link preview privacy, not related to baileys link preview generation
|
|
804
|
+
*/
|
|
805
|
+
const updateDisableLinkPreviewsPrivacy = (isPreviewsDisabled) => {
|
|
874
806
|
return chatModify({
|
|
875
807
|
disableLinkPreviews: { isPreviewsDisabled }
|
|
876
808
|
}, '');
|
|
877
809
|
};
|
|
810
|
+
/**
|
|
811
|
+
* Star or Unstar a message
|
|
812
|
+
*/
|
|
878
813
|
const star = (jid, messages, star) => {
|
|
879
814
|
return chatModify({
|
|
880
815
|
star: {
|
|
@@ -883,16 +818,25 @@ const makeChatsSocket = config => {
|
|
|
883
818
|
}
|
|
884
819
|
}, jid);
|
|
885
820
|
};
|
|
821
|
+
/**
|
|
822
|
+
* Add or Edit Contact
|
|
823
|
+
*/
|
|
886
824
|
const addOrEditContact = (jid, contact) => {
|
|
887
825
|
return chatModify({
|
|
888
826
|
contact
|
|
889
827
|
}, jid);
|
|
890
828
|
};
|
|
891
|
-
|
|
829
|
+
/**
|
|
830
|
+
* Remove Contact
|
|
831
|
+
*/
|
|
832
|
+
const removeContact = (jid) => {
|
|
892
833
|
return chatModify({
|
|
893
834
|
contact: null
|
|
894
835
|
}, jid);
|
|
895
836
|
};
|
|
837
|
+
/**
|
|
838
|
+
* Adds label
|
|
839
|
+
*/
|
|
896
840
|
const addLabel = (jid, labels) => {
|
|
897
841
|
return chatModify({
|
|
898
842
|
addLabel: {
|
|
@@ -900,6 +844,9 @@ const makeChatsSocket = config => {
|
|
|
900
844
|
}
|
|
901
845
|
}, jid);
|
|
902
846
|
};
|
|
847
|
+
/**
|
|
848
|
+
* Adds label for the chats
|
|
849
|
+
*/
|
|
903
850
|
const addChatLabel = (jid, labelId) => {
|
|
904
851
|
return chatModify({
|
|
905
852
|
addChatLabel: {
|
|
@@ -907,6 +854,9 @@ const makeChatsSocket = config => {
|
|
|
907
854
|
}
|
|
908
855
|
}, jid);
|
|
909
856
|
};
|
|
857
|
+
/**
|
|
858
|
+
* Removes label for the chat
|
|
859
|
+
*/
|
|
910
860
|
const removeChatLabel = (jid, labelId) => {
|
|
911
861
|
return chatModify({
|
|
912
862
|
removeChatLabel: {
|
|
@@ -914,6 +864,9 @@ const makeChatsSocket = config => {
|
|
|
914
864
|
}
|
|
915
865
|
}, jid);
|
|
916
866
|
};
|
|
867
|
+
/**
|
|
868
|
+
* Adds label for the message
|
|
869
|
+
*/
|
|
917
870
|
const addMessageLabel = (jid, messageId, labelId) => {
|
|
918
871
|
return chatModify({
|
|
919
872
|
addMessageLabel: {
|
|
@@ -922,6 +875,9 @@ const makeChatsSocket = config => {
|
|
|
922
875
|
}
|
|
923
876
|
}, jid);
|
|
924
877
|
};
|
|
878
|
+
/**
|
|
879
|
+
* Removes label for the message
|
|
880
|
+
*/
|
|
925
881
|
const removeMessageLabel = (jid, messageId, labelId) => {
|
|
926
882
|
return chatModify({
|
|
927
883
|
removeMessageLabel: {
|
|
@@ -930,238 +886,51 @@ const makeChatsSocket = config => {
|
|
|
930
886
|
}
|
|
931
887
|
}, jid);
|
|
932
888
|
};
|
|
933
|
-
|
|
889
|
+
/**
|
|
890
|
+
* Add or Edit Quick Reply
|
|
891
|
+
*/
|
|
892
|
+
const addOrEditQuickReply = (quickReply) => {
|
|
934
893
|
return chatModify({
|
|
935
894
|
quickReply
|
|
936
895
|
}, '');
|
|
937
896
|
};
|
|
938
|
-
|
|
897
|
+
/**
|
|
898
|
+
* Remove Quick Reply
|
|
899
|
+
*/
|
|
900
|
+
const removeQuickReply = (timestamp) => {
|
|
939
901
|
return chatModify({
|
|
940
902
|
quickReply: { timestamp, deleted: true }
|
|
941
903
|
}, '');
|
|
942
904
|
};
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
return chatModify({ threadPin: { pinned, messageId } }, jid);
|
|
948
|
-
};
|
|
949
|
-
const updatePrivateProcessingSetting = enabled => {
|
|
950
|
-
return chatModify({ privateProcessingSetting: enabled ? 'enabled' : 'disabled' }, '');
|
|
951
|
-
};
|
|
952
|
-
const updateBioPrivacy = async (value) => {
|
|
953
|
-
await privacyQuery('about', value);
|
|
954
|
-
};
|
|
955
|
-
const blockBot = async (botJid) => {
|
|
956
|
-
await query({
|
|
957
|
-
tag: 'iq',
|
|
958
|
-
attrs: {
|
|
959
|
-
xmlns: 'disappearing_mode',
|
|
960
|
-
to: WABinary_1.S_WHATSAPP_NET,
|
|
961
|
-
type: 'set'
|
|
962
|
-
},
|
|
963
|
-
content: [
|
|
964
|
-
{
|
|
965
|
-
tag: 'block',
|
|
966
|
-
attrs: { jid: botJid }
|
|
967
|
-
}
|
|
968
|
-
]
|
|
969
|
-
});
|
|
970
|
-
};
|
|
971
|
-
const unblockBot = async (botJid) => {
|
|
972
|
-
await updateBlockStatus(botJid, 'unblock');
|
|
973
|
-
};
|
|
974
|
-
const muteContactStatus = (jid, muted = true) => {
|
|
975
|
-
return chatModify({ muteStatus: { muted } }, jid);
|
|
976
|
-
};
|
|
977
|
-
const toggleFavorite = (jid, isFavorite = true) => {
|
|
978
|
-
return chatModify({ favorite: { isFavorite } }, jid);
|
|
979
|
-
};
|
|
980
|
-
const reorderLabels = sortedLabelIds => {
|
|
981
|
-
return chatModify({ reorderLabel: { sortedLabelIds } }, '');
|
|
982
|
-
};
|
|
983
|
-
const deleteCallLog = (callId, jid = '') => {
|
|
984
|
-
return chatModify({ deleteCallLog: { callId } }, jid);
|
|
985
|
-
};
|
|
986
|
-
const setChatNote = (jid, note) => {
|
|
987
|
-
return chatModify({ noteEdit: { note } }, jid);
|
|
988
|
-
};
|
|
989
|
-
const deleteChatNote = jid => {
|
|
990
|
-
return chatModify({ noteEdit: { note: '', deleted: true } }, jid);
|
|
991
|
-
};
|
|
992
|
-
const markChatAsUnread = (jid, lastMessages) => {
|
|
993
|
-
return chatModify({ markAsUnread: true, lastMessages }, jid);
|
|
994
|
-
};
|
|
995
|
-
const setChatEphemeral = (jid, duration) => {
|
|
996
|
-
return chatModify({ setChatEphemeral: duration }, jid);
|
|
997
|
-
};
|
|
998
|
-
const silenceChat = (jid, silent = true, until = null) => {
|
|
999
|
-
return chatModify({ silenceChat: { silent, until } }, jid);
|
|
1000
|
-
};
|
|
1001
|
-
const fetchBotProfiles = async (jids) => {
|
|
1002
|
-
const { USyncQuery, USyncUser, USyncBotProfileProtocol } = require('../WAUSync');
|
|
1003
|
-
const q = new USyncQuery();
|
|
1004
|
-
q.protocols.push(new USyncBotProfileProtocol());
|
|
1005
|
-
for (const jid of jids) {
|
|
1006
|
-
q.withUser(new USyncUser().withId(jid));
|
|
1007
|
-
}
|
|
1008
|
-
const result = await sock.executeUSyncQuery(q);
|
|
1009
|
-
return result?.list || [];
|
|
1010
|
-
};
|
|
1011
|
-
const updateChatLock = (jid, locked) => {
|
|
1012
|
-
return chatModify({ chatLock: { locked } }, jid);
|
|
1013
|
-
};
|
|
1014
|
-
const updateChatWallpaper = (jid, wallpaper) => {
|
|
1015
|
-
if (!wallpaper) {
|
|
1016
|
-
return chatModify({ wallpaper: { remove: true } }, jid);
|
|
1017
|
-
}
|
|
1018
|
-
return chatModify({ wallpaper }, jid);
|
|
1019
|
-
};
|
|
1020
|
-
const updateChatMediaVisibility = (jid, visibility) => {
|
|
1021
|
-
return chatModify({ mediaVisibility: visibility }, jid);
|
|
1022
|
-
};
|
|
1023
|
-
const getBotProfile = async (botJid) => {
|
|
1024
|
-
const resp = await query({
|
|
1025
|
-
tag: 'iq',
|
|
1026
|
-
attrs: {
|
|
1027
|
-
xmlns: 'bot',
|
|
1028
|
-
to: WABinary_1.S_WHATSAPP_NET,
|
|
1029
|
-
type: 'get'
|
|
1030
|
-
},
|
|
1031
|
-
content: [
|
|
1032
|
-
{
|
|
1033
|
-
tag: 'bot',
|
|
1034
|
-
attrs: { v: '2', jid: botJid }
|
|
1035
|
-
}
|
|
1036
|
-
]
|
|
1037
|
-
});
|
|
1038
|
-
const botNode = (0, WABinary_1.getBinaryNodeChild)(resp, 'bot');
|
|
1039
|
-
if (!botNode)
|
|
1040
|
-
return null;
|
|
1041
|
-
return {
|
|
1042
|
-
jid: botNode.attrs.jid,
|
|
1043
|
-
personaId: botNode.attrs['persona_id'],
|
|
1044
|
-
name: botNode.attrs.name,
|
|
1045
|
-
description: botNode.attrs.description
|
|
1046
|
-
};
|
|
1047
|
-
};
|
|
1048
|
-
const fetchABProps = async (protocol = '2', hash = '', refreshId = null, group = null) => {
|
|
1049
|
-
const propAttrs = { protocol };
|
|
1050
|
-
if (hash)
|
|
1051
|
-
propAttrs.hash = hash;
|
|
1052
|
-
if (refreshId != null)
|
|
1053
|
-
propAttrs.refresh_id = String(refreshId);
|
|
1054
|
-
if (group != null)
|
|
1055
|
-
propAttrs.group = String(group);
|
|
1056
|
-
const result = await query({
|
|
1057
|
-
tag: 'iq',
|
|
1058
|
-
attrs: { to: WABinary_1.S_WHATSAPP_NET, xmlns: 'abt', type: 'get' },
|
|
1059
|
-
content: [{ tag: 'props', attrs: propAttrs }]
|
|
1060
|
-
});
|
|
1061
|
-
const propsNode = (0, WABinary_1.getBinaryNodeChild)(result, 'props');
|
|
1062
|
-
if (!propsNode)
|
|
1063
|
-
return {};
|
|
1064
|
-
return (0, WABinary_1.reduceBinaryNodeToDictionary)(propsNode, 'prop');
|
|
1065
|
-
};
|
|
1066
|
-
const removeCompanionDevice = async (keyIndex, reason = 'user_initiated') => {
|
|
1067
|
-
await query({
|
|
1068
|
-
tag: 'iq',
|
|
1069
|
-
attrs: { to: WABinary_1.S_WHATSAPP_NET, xmlns: 'md', type: 'set' },
|
|
1070
|
-
content: [
|
|
1071
|
-
{
|
|
1072
|
-
tag: 'remove-companion-device',
|
|
1073
|
-
attrs: { platform: 'true', reason, id: String(keyIndex) }
|
|
1074
|
-
}
|
|
1075
|
-
]
|
|
1076
|
-
});
|
|
1077
|
-
};
|
|
1078
|
-
const updateKeyIndexList = async (ts, content) => {
|
|
1079
|
-
await query({
|
|
1080
|
-
tag: 'iq',
|
|
1081
|
-
attrs: { to: WABinary_1.S_WHATSAPP_NET, xmlns: 'md', type: 'set' },
|
|
1082
|
-
content: [
|
|
1083
|
-
{
|
|
1084
|
-
tag: 'key-index-list',
|
|
1085
|
-
attrs: { ts: String(ts) },
|
|
1086
|
-
content
|
|
1087
|
-
}
|
|
1088
|
-
]
|
|
1089
|
-
});
|
|
1090
|
-
};
|
|
1091
|
-
const fetchMediaConn = async (lastId = null) => {
|
|
1092
|
-
const attrs = {};
|
|
1093
|
-
if (lastId != null)
|
|
1094
|
-
attrs.last_id = String(lastId);
|
|
1095
|
-
const result = await query({
|
|
1096
|
-
tag: 'iq',
|
|
1097
|
-
attrs: { to: WABinary_1.S_WHATSAPP_NET, xmlns: 'w:m', type: 'set' },
|
|
1098
|
-
content: [{ tag: 'media_conn', attrs }]
|
|
1099
|
-
});
|
|
1100
|
-
return (0, WABinary_1.getBinaryNodeChild)(result, 'media_conn') || null;
|
|
1101
|
-
};
|
|
1102
|
-
const deleteBroadcastList = async (listId) => {
|
|
1103
|
-
await query({
|
|
1104
|
-
tag: 'iq',
|
|
1105
|
-
attrs: { to: WABinary_1.S_WHATSAPP_NET, xmlns: 'w:b', type: 'set' },
|
|
1106
|
-
content: [
|
|
1107
|
-
{
|
|
1108
|
-
tag: 'delete',
|
|
1109
|
-
attrs: {},
|
|
1110
|
-
content: [{ tag: 'list', attrs: { id: String(listId) } }]
|
|
1111
|
-
}
|
|
1112
|
-
]
|
|
1113
|
-
});
|
|
1114
|
-
};
|
|
1115
|
-
const fetchQRCode = async (code, addressingMode = null) => {
|
|
1116
|
-
const attrs = { code };
|
|
1117
|
-
if (addressingMode)
|
|
1118
|
-
attrs.addressing_mode = addressingMode;
|
|
1119
|
-
const result = await query({
|
|
1120
|
-
tag: 'iq',
|
|
1121
|
-
attrs: { to: WABinary_1.S_WHATSAPP_NET, xmlns: 'w:qr', type: 'get' },
|
|
1122
|
-
content: [{ tag: 'qr', attrs }]
|
|
1123
|
-
});
|
|
1124
|
-
return (0, WABinary_1.getBinaryNodeChild)(result, 'qr') || null;
|
|
1125
|
-
};
|
|
1126
|
-
const confirmDeviceLogout = async (id, approve = true) => {
|
|
1127
|
-
await query({
|
|
1128
|
-
tag: 'iq',
|
|
1129
|
-
attrs: {
|
|
1130
|
-
to: WABinary_1.S_WHATSAPP_NET,
|
|
1131
|
-
xmlns: 'w:account_defence',
|
|
1132
|
-
type: 'set',
|
|
1133
|
-
smax_id: '87'
|
|
1134
|
-
},
|
|
1135
|
-
content: [
|
|
1136
|
-
{
|
|
1137
|
-
tag: 'device_logout',
|
|
1138
|
-
attrs: { approve: approve ? 'true' : 'false', id: String(id) }
|
|
1139
|
-
}
|
|
1140
|
-
]
|
|
1141
|
-
});
|
|
1142
|
-
};
|
|
905
|
+
/**
|
|
906
|
+
* queries need to be fired on connection open
|
|
907
|
+
* help ensure parity with WA Web
|
|
908
|
+
* */
|
|
1143
909
|
const executeInitQueries = async () => {
|
|
1144
|
-
await Promise.all([fetchProps(), fetchBlocklist(), fetchPrivacySettings()
|
|
910
|
+
await Promise.all([fetchProps(), fetchBlocklist(), fetchPrivacySettings()]);
|
|
1145
911
|
};
|
|
1146
912
|
const upsertMessage = ev.createBufferedFunction(async (msg, type) => {
|
|
1147
913
|
ev.emit('messages.upsert', { messages: [msg], type });
|
|
1148
914
|
if (!!msg.pushName) {
|
|
1149
915
|
let jid = msg.key.fromMe ? authState.creds.me.id : msg.key.participant || msg.key.remoteJid;
|
|
1150
|
-
jid =
|
|
916
|
+
jid = jidNormalizedUser(jid);
|
|
1151
917
|
if (!msg.key.fromMe) {
|
|
1152
918
|
ev.emit('contacts.update', [{ id: jid, notify: msg.pushName, verifiedName: msg.verifiedBizName }]);
|
|
1153
919
|
}
|
|
920
|
+
// update our pushname too
|
|
1154
921
|
if (msg.key.fromMe && msg.pushName && authState.creds.me?.name !== msg.pushName) {
|
|
1155
922
|
ev.emit('creds.update', { me: { ...authState.creds.me, name: msg.pushName } });
|
|
1156
923
|
}
|
|
1157
924
|
}
|
|
1158
|
-
const historyMsg =
|
|
925
|
+
const historyMsg = getHistoryMsg(msg.message);
|
|
1159
926
|
const shouldProcessHistoryMsg = historyMsg
|
|
1160
|
-
? shouldSyncHistoryMessage(historyMsg) &&
|
|
927
|
+
? shouldSyncHistoryMessage(historyMsg) &&
|
|
928
|
+
PROCESSABLE_HISTORY_TYPES.includes(historyMsg.syncType)
|
|
1161
929
|
: false;
|
|
1162
930
|
if (historyMsg && shouldProcessHistoryMsg) {
|
|
1163
931
|
const syncType = historyMsg.syncType;
|
|
1164
|
-
|
|
932
|
+
// INITIAL_BOOTSTRAP — fire immediately, no progress check (same as WA Web K function)
|
|
933
|
+
if (syncType === proto.HistorySync.HistorySyncType.INITIAL_BOOTSTRAP &&
|
|
1165
934
|
!historySyncStatus.initialBootstrapComplete) {
|
|
1166
935
|
historySyncStatus.initialBootstrapComplete = true;
|
|
1167
936
|
ev.emit('messaging-history.status', {
|
|
@@ -1170,7 +939,8 @@ const makeChatsSocket = config => {
|
|
|
1170
939
|
explicit: true
|
|
1171
940
|
});
|
|
1172
941
|
}
|
|
1173
|
-
|
|
942
|
+
// RECENT with progress === 100 — explicit completion
|
|
943
|
+
if (syncType === proto.HistorySync.HistorySyncType.RECENT &&
|
|
1174
944
|
historyMsg.progress === 100 &&
|
|
1175
945
|
!historySyncStatus.recentSyncComplete) {
|
|
1176
946
|
historySyncStatus.recentSyncComplete = true;
|
|
@@ -1182,42 +952,47 @@ const makeChatsSocket = config => {
|
|
|
1182
952
|
explicit: true
|
|
1183
953
|
});
|
|
1184
954
|
}
|
|
1185
|
-
|
|
955
|
+
// Reset 120s paused timeout on any RECENT chunk (like WA Web's handleChunkProgress)
|
|
956
|
+
if (syncType === proto.HistorySync.HistorySyncType.RECENT && !historySyncStatus.recentSyncComplete) {
|
|
1186
957
|
clearTimeout(historySyncPausedTimeout);
|
|
1187
958
|
historySyncPausedTimeout = setTimeout(() => {
|
|
1188
959
|
if (!historySyncStatus.recentSyncComplete) {
|
|
1189
960
|
historySyncStatus.recentSyncComplete = true;
|
|
1190
961
|
ev.emit('messaging-history.status', {
|
|
1191
|
-
syncType:
|
|
962
|
+
syncType: proto.HistorySync.HistorySyncType.RECENT,
|
|
1192
963
|
status: 'paused',
|
|
1193
964
|
explicit: false
|
|
1194
965
|
});
|
|
1195
966
|
}
|
|
1196
967
|
historySyncPausedTimeout = undefined;
|
|
1197
|
-
},
|
|
968
|
+
}, HISTORY_SYNC_PAUSED_TIMEOUT_MS);
|
|
1198
969
|
}
|
|
1199
970
|
}
|
|
1200
|
-
|
|
971
|
+
// State machine: decide on sync and flush
|
|
972
|
+
if (historyMsg && syncState === SyncState.AwaitingInitialSync) {
|
|
1201
973
|
if (awaitingSyncTimeout) {
|
|
1202
974
|
clearTimeout(awaitingSyncTimeout);
|
|
1203
975
|
awaitingSyncTimeout = undefined;
|
|
1204
976
|
}
|
|
1205
977
|
if (shouldProcessHistoryMsg) {
|
|
1206
|
-
syncState =
|
|
978
|
+
syncState = SyncState.Syncing;
|
|
1207
979
|
logger.info('Transitioned to Syncing state');
|
|
980
|
+
// Let doAppStateSync handle the final flush after it's done
|
|
1208
981
|
}
|
|
1209
982
|
else {
|
|
1210
|
-
syncState =
|
|
983
|
+
syncState = SyncState.Online;
|
|
1211
984
|
logger.info('History sync skipped, transitioning to Online state and flushing buffer');
|
|
1212
985
|
ev.flush();
|
|
1213
986
|
}
|
|
1214
987
|
}
|
|
1215
988
|
const doAppStateSync = async () => {
|
|
1216
|
-
if (syncState ===
|
|
989
|
+
if (syncState === SyncState.Syncing) {
|
|
990
|
+
// All collections will be synced, so clear any blocked ones
|
|
1217
991
|
blockedCollections.clear();
|
|
1218
992
|
logger.info('Doing app state sync');
|
|
1219
|
-
await resyncAppState(
|
|
1220
|
-
|
|
993
|
+
await resyncAppState(ALL_WA_PATCH_NAMES, true);
|
|
994
|
+
// Sync is complete, go online and flush everything
|
|
995
|
+
syncState = SyncState.Online;
|
|
1221
996
|
logger.info('App state sync complete, transitioning to Online state and flushing buffer');
|
|
1222
997
|
ev.flush();
|
|
1223
998
|
const accountSyncCounter = (authState.creds.accountSyncCounter || 0) + 1;
|
|
@@ -1230,7 +1005,7 @@ const makeChatsSocket = config => {
|
|
|
1230
1005
|
await doAppStateSync();
|
|
1231
1006
|
}
|
|
1232
1007
|
})(),
|
|
1233
|
-
(
|
|
1008
|
+
processMessage(msg, {
|
|
1234
1009
|
signalRepository,
|
|
1235
1010
|
shouldProcessHistoryMsg,
|
|
1236
1011
|
placeholderResendCache,
|
|
@@ -1242,7 +1017,8 @@ const makeChatsSocket = config => {
|
|
|
1242
1017
|
getMessage
|
|
1243
1018
|
})
|
|
1244
1019
|
]);
|
|
1245
|
-
|
|
1020
|
+
// If the app state key arrives and we are waiting to sync, trigger the sync now.
|
|
1021
|
+
if (msg.message?.protocolMessage?.appStateSyncKeyShare && syncState === SyncState.Syncing) {
|
|
1246
1022
|
logger.info('App state sync key arrived, triggering app state sync');
|
|
1247
1023
|
await doAppStateSync();
|
|
1248
1024
|
}
|
|
@@ -1250,7 +1026,7 @@ const makeChatsSocket = config => {
|
|
|
1250
1026
|
ws.on('CB:presence', handlePresenceUpdate);
|
|
1251
1027
|
ws.on('CB:chatstate', handlePresenceUpdate);
|
|
1252
1028
|
ws.on('CB:ib,,dirty', async (node) => {
|
|
1253
|
-
const { attrs } =
|
|
1029
|
+
const { attrs } = getBinaryNodeChild(node, 'dirty');
|
|
1254
1030
|
const type = attrs.type;
|
|
1255
1031
|
switch (type) {
|
|
1256
1032
|
case 'account_sync':
|
|
@@ -1264,6 +1040,7 @@ const makeChatsSocket = config => {
|
|
|
1264
1040
|
}
|
|
1265
1041
|
break;
|
|
1266
1042
|
case 'groups':
|
|
1043
|
+
// handled in groups.ts
|
|
1267
1044
|
break;
|
|
1268
1045
|
default:
|
|
1269
1046
|
logger.info({ node }, 'received unknown sync');
|
|
@@ -1282,28 +1059,31 @@ const makeChatsSocket = config => {
|
|
|
1282
1059
|
}
|
|
1283
1060
|
sendPresenceUpdate(markOnlineOnConnect ? 'available' : 'unavailable').catch(error => onUnexpectedError(error, 'presence update requests'));
|
|
1284
1061
|
}
|
|
1285
|
-
if (!receivedPendingNotifications || syncState !==
|
|
1062
|
+
if (!receivedPendingNotifications || syncState !== SyncState.Connecting) {
|
|
1286
1063
|
return;
|
|
1287
1064
|
}
|
|
1288
1065
|
historySyncStatus.initialBootstrapComplete = false;
|
|
1289
1066
|
historySyncStatus.recentSyncComplete = false;
|
|
1290
1067
|
clearTimeout(historySyncPausedTimeout);
|
|
1291
1068
|
historySyncPausedTimeout = undefined;
|
|
1292
|
-
syncState =
|
|
1069
|
+
syncState = SyncState.AwaitingInitialSync;
|
|
1293
1070
|
logger.info('Connection is now AwaitingInitialSync, buffering events');
|
|
1294
1071
|
ev.buffer();
|
|
1295
|
-
const willSyncHistory = shouldSyncHistoryMessage(
|
|
1296
|
-
syncType:
|
|
1072
|
+
const willSyncHistory = shouldSyncHistoryMessage(proto.Message.HistorySyncNotification.create({
|
|
1073
|
+
syncType: proto.HistorySync.HistorySyncType.RECENT
|
|
1297
1074
|
}));
|
|
1298
1075
|
if (!willSyncHistory) {
|
|
1299
1076
|
logger.info('History sync is disabled by config, not waiting for notification. Transitioning to Online.');
|
|
1300
|
-
syncState =
|
|
1077
|
+
syncState = SyncState.Online;
|
|
1301
1078
|
setTimeout(() => ev.flush(), 0);
|
|
1302
1079
|
return;
|
|
1303
1080
|
}
|
|
1081
|
+
// On reconnection (accountSyncCounter > 0), the server does not push
|
|
1082
|
+
// history sync notifications — the device already has its data.
|
|
1083
|
+
// Skip the 20s wait and go online immediately.
|
|
1304
1084
|
if (authState.creds.accountSyncCounter > 0) {
|
|
1305
1085
|
logger.info('Reconnection with existing sync data, skipping history sync wait. Transitioning to Online.');
|
|
1306
|
-
syncState =
|
|
1086
|
+
syncState = SyncState.Online;
|
|
1307
1087
|
setTimeout(() => ev.flush(), 0);
|
|
1308
1088
|
return;
|
|
1309
1089
|
}
|
|
@@ -1312,20 +1092,27 @@ const makeChatsSocket = config => {
|
|
|
1312
1092
|
clearTimeout(awaitingSyncTimeout);
|
|
1313
1093
|
}
|
|
1314
1094
|
awaitingSyncTimeout = setTimeout(() => {
|
|
1315
|
-
if (syncState ===
|
|
1095
|
+
if (syncState === SyncState.AwaitingInitialSync) {
|
|
1316
1096
|
logger.warn('Timeout in AwaitingInitialSync, forcing state to Online and flushing buffer');
|
|
1317
|
-
syncState =
|
|
1097
|
+
syncState = SyncState.Online;
|
|
1318
1098
|
ev.flush();
|
|
1099
|
+
// Increment so subsequent reconnections skip the 20s wait.
|
|
1100
|
+
// Late-arriving history is still processed via processMessage
|
|
1101
|
+
// regardless of the state machine phase.
|
|
1319
1102
|
const accountSyncCounter = (authState.creds.accountSyncCounter || 0) + 1;
|
|
1320
1103
|
ev.emit('creds.update', { accountSyncCounter });
|
|
1321
1104
|
}
|
|
1322
1105
|
}, 20000);
|
|
1323
1106
|
});
|
|
1107
|
+
// When an app state sync key arrives (myAppStateKeyId is set) and there are
|
|
1108
|
+
// collections blocked on a missing key, trigger a re-sync for just those collections.
|
|
1109
|
+
// This mirrors WA Web's Blocked → retry-on-key-arrival behavior.
|
|
1324
1110
|
ev.on('creds.update', ({ myAppStateKeyId }) => {
|
|
1325
1111
|
if (!myAppStateKeyId || blockedCollections.size === 0) {
|
|
1326
1112
|
return;
|
|
1327
1113
|
}
|
|
1328
|
-
|
|
1114
|
+
// If we're in the middle of a full sync, doAppStateSync handles all collections
|
|
1115
|
+
if (syncState === SyncState.Syncing) {
|
|
1329
1116
|
blockedCollections.clear();
|
|
1330
1117
|
return;
|
|
1331
1118
|
}
|
|
@@ -1342,18 +1129,22 @@ const makeChatsSocket = config => {
|
|
|
1342
1129
|
logger.warn({ lid, pn, error }, 'Failed to store LID-PN mapping');
|
|
1343
1130
|
}
|
|
1344
1131
|
});
|
|
1132
|
+
registerSocketEndHandler(() => {
|
|
1133
|
+
if (awaitingSyncTimeout) {
|
|
1134
|
+
clearTimeout(awaitingSyncTimeout);
|
|
1135
|
+
awaitingSyncTimeout = undefined;
|
|
1136
|
+
}
|
|
1137
|
+
if (!config.placeholderResendCache && placeholderResendCache.close) {
|
|
1138
|
+
placeholderResendCache.close();
|
|
1139
|
+
}
|
|
1140
|
+
syncState = SyncState.Connecting;
|
|
1141
|
+
privacySettings = undefined;
|
|
1142
|
+
});
|
|
1345
1143
|
return {
|
|
1346
1144
|
...sock,
|
|
1347
1145
|
serverProps,
|
|
1348
1146
|
createCallLink,
|
|
1349
|
-
toggleCallLinkWaitingRoom,
|
|
1350
1147
|
getBotListV2,
|
|
1351
|
-
getChatBlockingStatus,
|
|
1352
|
-
updateChatBlockingStatus,
|
|
1353
|
-
getUserDisclosures,
|
|
1354
|
-
getOptOutList,
|
|
1355
|
-
getPushConfig,
|
|
1356
|
-
setPushConfig,
|
|
1357
1148
|
messageMutex,
|
|
1358
1149
|
receiptMutex,
|
|
1359
1150
|
appStatePatchMutex,
|
|
@@ -1379,18 +1170,16 @@ const makeChatsSocket = config => {
|
|
|
1379
1170
|
updateOnlinePrivacy,
|
|
1380
1171
|
updateProfilePicturePrivacy,
|
|
1381
1172
|
updateStatusPrivacy,
|
|
1382
|
-
getStatusPrivacy,
|
|
1383
|
-
setStatusPrivacy,
|
|
1384
1173
|
updateReadReceiptsPrivacy,
|
|
1385
1174
|
updateGroupsAddPrivacy,
|
|
1386
1175
|
updateDefaultDisappearingMode,
|
|
1387
|
-
fetchBroadcastListQuota,
|
|
1388
1176
|
getBusinessProfile,
|
|
1389
1177
|
resyncAppState,
|
|
1390
1178
|
chatModify,
|
|
1391
1179
|
cleanDirtyBits,
|
|
1392
1180
|
addOrEditContact,
|
|
1393
1181
|
removeContact,
|
|
1182
|
+
placeholderResendCache,
|
|
1394
1183
|
addLabel,
|
|
1395
1184
|
addChatLabel,
|
|
1396
1185
|
removeChatLabel,
|
|
@@ -1398,52 +1187,7 @@ const makeChatsSocket = config => {
|
|
|
1398
1187
|
removeMessageLabel,
|
|
1399
1188
|
star,
|
|
1400
1189
|
addOrEditQuickReply,
|
|
1401
|
-
removeQuickReply
|
|
1402
|
-
renameAIThread,
|
|
1403
|
-
pinThreadMessage,
|
|
1404
|
-
updatePrivateProcessingSetting,
|
|
1405
|
-
updateBioPrivacy,
|
|
1406
|
-
blockBot,
|
|
1407
|
-
unblockBot,
|
|
1408
|
-
getBotProfile,
|
|
1409
|
-
muteContactStatus,
|
|
1410
|
-
toggleFavorite,
|
|
1411
|
-
reorderLabels,
|
|
1412
|
-
deleteCallLog,
|
|
1413
|
-
setChatNote,
|
|
1414
|
-
deleteChatNote,
|
|
1415
|
-
markChatAsUnread,
|
|
1416
|
-
setChatEphemeral,
|
|
1417
|
-
silenceChat,
|
|
1418
|
-
fetchBotProfiles,
|
|
1419
|
-
updateChatLock,
|
|
1420
|
-
updateChatWallpaper,
|
|
1421
|
-
updateChatMediaVisibility,
|
|
1422
|
-
fetchIntegrators,
|
|
1423
|
-
acceptInteropTOS,
|
|
1424
|
-
optInIntegrators,
|
|
1425
|
-
optOutIntegrators,
|
|
1426
|
-
resolveInteropUser,
|
|
1427
|
-
resolveInteropUsers,
|
|
1428
|
-
getReachabilitySettings,
|
|
1429
|
-
setReachabilitySettings,
|
|
1430
|
-
blockInteropUser,
|
|
1431
|
-
unblockInteropUser,
|
|
1432
|
-
reportInteropSpam,
|
|
1433
|
-
trustInteropContact,
|
|
1434
|
-
initInterop,
|
|
1435
|
-
createInteropGroup,
|
|
1436
|
-
leaveInteropGroup,
|
|
1437
|
-
getInteropGroupAddPrivacy,
|
|
1438
|
-
INTEGRATOR_BIRDYCHAT,
|
|
1439
|
-
INTEGRATOR_HAIKET,
|
|
1440
|
-
fetchABProps,
|
|
1441
|
-
removeCompanionDevice,
|
|
1442
|
-
updateKeyIndexList,
|
|
1443
|
-
fetchMediaConn,
|
|
1444
|
-
deleteBroadcastList,
|
|
1445
|
-
fetchQRCode,
|
|
1446
|
-
confirmDeviceLogout
|
|
1190
|
+
removeQuickReply
|
|
1447
1191
|
};
|
|
1448
1192
|
};
|
|
1449
|
-
|
|
1193
|
+
//# sourceMappingURL=chats.js.map
|