@badzz88/baileys 8.3.0 → 8.4.1
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 +27 -27
- package/WAProto/GenerateStatics.sh +3 -0
- package/WAProto/WAProto.proto +6902 -0
- package/WAProto/fix-import.js +29 -0
- package/WAProto/fix-imports.js +85 -0
- package/WAProto/index.d.ts +79257 -0
- package/WAProto/index.js +204305 -131020
- package/engine-requirements.js +3 -4
- package/lib/Defaults/baileys-version.json +1 -1
- package/lib/Defaults/index.js +119 -136
- package/lib/Signal/Group/ciphertext-message.js +2 -5
- package/lib/Signal/Group/group-session-builder.js +7 -41
- package/lib/Signal/Group/group_cipher.js +37 -51
- package/lib/Signal/Group/index.js +12 -57
- package/lib/Signal/Group/keyhelper.js +7 -44
- package/lib/Signal/Group/sender-chain-key.js +7 -15
- package/lib/Signal/Group/sender-key-distribution-message.js +8 -11
- package/lib/Signal/Group/sender-key-message.js +9 -12
- package/lib/Signal/Group/sender-key-name.js +2 -5
- package/lib/Signal/Group/sender-key-record.js +9 -21
- package/lib/Signal/Group/sender-key-state.js +27 -42
- package/lib/Signal/Group/sender-message-key.js +4 -7
- package/lib/Signal/libsignal.js +347 -90
- package/lib/Signal/lid-mapping.js +277 -0
- package/lib/Socket/Client/index.js +3 -19
- package/lib/Socket/Client/types.js +11 -0
- package/lib/Socket/Client/websocket.js +54 -0
- package/lib/Socket/business.js +162 -43
- package/lib/Socket/chats.js +627 -404
- package/lib/Socket/communities.js +431 -0
- package/lib/Socket/community.js +392 -0
- package/lib/Socket/dugong.js +161 -8
- package/lib/Socket/groups.js +154 -160
- package/lib/Socket/index.js +11 -10
- package/lib/Socket/luxu.js +387 -0
- package/lib/Socket/messages-recv.js +1421 -615
- package/lib/Socket/messages-send.js +1150 -624
- package/lib/Socket/mex.js +42 -0
- package/lib/Socket/newsletter.js +139 -314
- package/lib/Socket/registration.js +2 -1
- package/lib/Socket/socket.js +544 -242
- package/lib/Store/index.js +10 -10
- package/lib/Store/keyed-db.js +108 -0
- package/lib/Store/make-cache-manager-store.js +43 -41
- package/lib/Store/make-in-memory-store.js +112 -341
- package/lib/Store/make-ordered-dictionary.js +14 -20
- package/lib/Store/object-repository.js +11 -6
- package/lib/Types/Auth.js +2 -2
- package/lib/Types/Bussines.js +2 -0
- package/lib/Types/Call.js +2 -2
- package/lib/Types/Chat.js +8 -4
- package/lib/Types/Contact.js +2 -2
- package/lib/Types/Events.js +2 -2
- package/lib/Types/GroupMetadata.js +2 -2
- package/lib/Types/Label.js +3 -5
- package/lib/Types/LabelAssociation.js +3 -5
- package/lib/Types/Message.js +11 -9
- package/lib/Types/Mex.js +37 -0
- package/lib/Types/Product.js +2 -2
- package/lib/Types/Signal.js +2 -2
- package/lib/Types/Socket.js +3 -2
- package/lib/Types/State.js +56 -2
- package/lib/Types/USync.js +2 -2
- package/lib/Types/index.js +15 -31
- package/lib/Utils/auth-utils.js +239 -143
- package/lib/Utils/browser-utils.js +48 -0
- package/lib/Utils/business.js +66 -69
- package/lib/Utils/chat-utils.js +396 -253
- package/lib/Utils/companion-reg-client-utils.js +35 -0
- package/lib/Utils/crypto.js +57 -90
- package/lib/Utils/decode-wa-message.js +236 -84
- package/lib/Utils/event-buffer.js +185 -77
- package/lib/Utils/generics.js +188 -283
- package/lib/Utils/history.js +93 -55
- package/lib/Utils/identity-change-handler.js +50 -0
- package/lib/Utils/index.js +23 -33
- package/lib/Utils/link-preview.js +16 -24
- package/lib/Utils/logger.js +3 -7
- package/lib/Utils/lt-hash.js +3 -46
- package/lib/Utils/make-mutex.js +24 -34
- package/lib/Utils/message-composer.js +273 -0
- package/lib/Utils/message-retry-manager.js +265 -0
- package/lib/Utils/messages-media.js +451 -482
- package/lib/Utils/messages.js +796 -359
- package/lib/Utils/noise-handler.js +145 -99
- package/lib/Utils/offline-node-processor.js +40 -0
- package/lib/Utils/pre-key-manager.js +106 -0
- package/lib/Utils/process-message.js +459 -150
- package/lib/Utils/reporting-utils.js +258 -0
- package/lib/Utils/signal.js +120 -72
- package/lib/Utils/stanza-ack.js +38 -0
- package/lib/Utils/sync-action-utils.js +49 -0
- package/lib/Utils/tc-token-utils.js +163 -0
- package/lib/Utils/use-multi-file-auth-state.js +29 -27
- package/lib/Utils/validate-connection.js +73 -99
- package/lib/WABinary/constants.js +1281 -20
- package/lib/WABinary/decode.js +52 -42
- package/lib/WABinary/encode.js +110 -155
- package/lib/WABinary/generic-utils.js +55 -49
- package/lib/WABinary/index.js +6 -21
- package/lib/WABinary/jid-utils.js +76 -40
- package/lib/WABinary/types.js +2 -2
- package/lib/WAM/BinaryInfo.js +2 -5
- package/lib/WAM/constants.js +19071 -11568
- package/lib/WAM/encode.js +17 -22
- package/lib/WAM/index.js +4 -19
- package/lib/WAUSync/Protocols/USyncContactProtocol.js +33 -13
- package/lib/WAUSync/Protocols/USyncDeviceProtocol.js +11 -14
- package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.js +9 -12
- package/lib/WAUSync/Protocols/USyncStatusProtocol.js +9 -13
- package/lib/WAUSync/Protocols/USyncUsernameProtocol.js +25 -0
- package/lib/WAUSync/Protocols/UsyncBotProfileProtocol.js +20 -22
- package/lib/WAUSync/Protocols/UsyncLIDProtocol.js +13 -8
- package/lib/WAUSync/Protocols/index.js +6 -20
- package/lib/WAUSync/USyncQuery.js +44 -35
- package/lib/WAUSync/USyncUser.js +10 -5
- package/lib/WAUSync/index.js +4 -19
- package/lib/index.js +39 -53
- package/package.json +134 -101
- package/lib/Defaults/index.d.ts +0 -53
- package/lib/Signal/Group/ciphertext-message.d.ts +0 -9
- package/lib/Signal/Group/group-session-builder.d.ts +0 -14
- package/lib/Signal/Group/group_cipher.d.ts +0 -17
- package/lib/Signal/Group/index.d.ts +0 -11
- package/lib/Signal/Group/keyhelper.d.ts +0 -10
- package/lib/Signal/Group/queue-job.d.ts +0 -1
- package/lib/Signal/Group/sender-chain-key.d.ts +0 -13
- package/lib/Signal/Group/sender-key-distribution-message.d.ts +0 -16
- package/lib/Signal/Group/sender-key-message.d.ts +0 -18
- package/lib/Signal/Group/sender-key-name.d.ts +0 -17
- package/lib/Signal/Group/sender-key-record.d.ts +0 -30
- package/lib/Signal/Group/sender-key-state.d.ts +0 -38
- package/lib/Signal/Group/sender-message-key.d.ts +0 -11
- package/lib/Signal/libsignal.d.ts +0 -3
- package/lib/Socket/Client/abstract-socket-client.d.ts +0 -17
- package/lib/Socket/Client/index.d.ts +0 -3
- package/lib/Socket/Client/mobile-socket-client.d.ts +0 -13
- package/lib/Socket/Client/web-socket-client.d.ts +0 -12
- package/lib/Socket/business.d.ts +0 -171
- package/lib/Socket/chats.d.ts +0 -267
- package/lib/Socket/dugong.d.ts +0 -254
- package/lib/Socket/groups.d.ts +0 -115
- package/lib/Socket/index.d.ts +0 -173
- package/lib/Socket/messages-recv.d.ts +0 -161
- package/lib/Socket/messages-send.d.ts +0 -149
- package/lib/Socket/newsletter.d.ts +0 -134
- package/lib/Socket/registration.d.ts +0 -267
- package/lib/Socket/socket.d.ts +0 -43
- package/lib/Socket/usync.d.ts +0 -36
- package/lib/Store/index.d.ts +0 -3
- package/lib/Store/make-cache-manager-store.d.ts +0 -13
- package/lib/Store/make-in-memory-store.d.ts +0 -118
- package/lib/Store/make-ordered-dictionary.d.ts +0 -13
- package/lib/Store/object-repository.d.ts +0 -10
- package/lib/Types/Auth.d.ts +0 -110
- package/lib/Types/Call.d.ts +0 -13
- package/lib/Types/Chat.d.ts +0 -102
- package/lib/Types/Contact.d.ts +0 -19
- package/lib/Types/Events.d.ts +0 -157
- package/lib/Types/GroupMetadata.d.ts +0 -55
- package/lib/Types/Label.d.ts +0 -35
- package/lib/Types/LabelAssociation.d.ts +0 -29
- package/lib/Types/Message.d.ts +0 -273
- package/lib/Types/Newsletter.d.ts +0 -103
- package/lib/Types/Product.d.ts +0 -78
- package/lib/Types/Signal.d.ts +0 -57
- package/lib/Types/Socket.d.ts +0 -111
- package/lib/Types/State.d.ts +0 -27
- package/lib/Types/USync.d.ts +0 -25
- package/lib/Types/index.d.ts +0 -57
- package/lib/Utils/auth-utils.d.ts +0 -18
- package/lib/Utils/baileys-event-stream.d.ts +0 -16
- package/lib/Utils/business.d.ts +0 -22
- package/lib/Utils/chat-utils.d.ts +0 -71
- package/lib/Utils/crypto.d.ts +0 -41
- package/lib/Utils/decode-wa-message.d.ts +0 -19
- package/lib/Utils/event-buffer.d.ts +0 -35
- package/lib/Utils/generics.d.ts +0 -92
- package/lib/Utils/history.d.ts +0 -15
- package/lib/Utils/index.d.ts +0 -17
- package/lib/Utils/link-preview.d.ts +0 -21
- package/lib/Utils/logger.d.ts +0 -4
- package/lib/Utils/lt-hash.d.ts +0 -12
- package/lib/Utils/make-mutex.d.ts +0 -7
- package/lib/Utils/messages-media.d.ts +0 -116
- package/lib/Utils/messages.d.ts +0 -77
- package/lib/Utils/noise-handler.d.ts +0 -21
- package/lib/Utils/process-message.d.ts +0 -41
- package/lib/Utils/signal.d.ts +0 -32
- package/lib/Utils/use-multi-file-auth-state.d.ts +0 -13
- package/lib/Utils/validate-connection.d.ts +0 -11
- package/lib/WABinary/constants.d.ts +0 -30
- package/lib/WABinary/decode.d.ts +0 -7
- package/lib/WABinary/encode.d.ts +0 -3
- package/lib/WABinary/generic-utils.d.ts +0 -17
- package/lib/WABinary/index.d.ts +0 -5
- package/lib/WABinary/jid-utils.d.ts +0 -31
- package/lib/WABinary/types.d.ts +0 -18
- package/lib/WAM/BinaryInfo.d.ts +0 -17
- package/lib/WAM/constants.d.ts +0 -38
- package/lib/WAM/encode.d.ts +0 -3
- package/lib/WAM/index.d.ts +0 -3
- package/lib/WAUSync/Protocols/USyncContactProtocol.d.ts +0 -9
- package/lib/WAUSync/Protocols/USyncDeviceProtocol.d.ts +0 -22
- package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.d.ts +0 -12
- package/lib/WAUSync/Protocols/USyncStatusProtocol.d.ts +0 -12
- package/lib/WAUSync/Protocols/UsyncBotProfileProtocol.d.ts +0 -25
- package/lib/WAUSync/Protocols/UsyncLIDProtocol.d.ts +0 -8
- package/lib/WAUSync/Protocols/index.d.ts +0 -4
- package/lib/WAUSync/USyncQuery.d.ts +0 -28
- package/lib/WAUSync/USyncUser.d.ts +0 -12
- package/lib/WAUSync/index.d.ts +0 -3
- package/lib/index.d.ts +0 -12
package/lib/Socket/chats.js
CHANGED
|
@@ -1,30 +1,56 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
};
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
const
|
|
15
|
-
const
|
|
16
|
-
const
|
|
17
|
-
const
|
|
18
|
-
const
|
|
19
|
-
const makeChatsSocket = (config) => {
|
|
20
|
-
const { logger, markOnlineOnConnect, fireInitQueries, appStateMacVerification, shouldIgnoreJid, shouldSyncHistoryMessage, } = config;
|
|
21
|
-
const sock = (0, usync_1.makeUSyncSocket)(config);
|
|
22
|
-
const { ev, ws, authState, generateMessageTag, sendNode, query, onUnexpectedError, } = sock;
|
|
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) => {
|
|
15
|
+
const { logger, markOnlineOnConnect, fireInitQueries, appStateMacVerification, shouldIgnoreJid, shouldSyncHistoryMessage, getMessage } = config;
|
|
16
|
+
const sock = makeSocket(config);
|
|
17
|
+
const { ev, ws, authState, generateMessageTag, sendNode, query, signalRepository, onUnexpectedError, sendUnifiedSession, registerSocketEndHandler } = sock;
|
|
18
|
+
const getLIDForPN = signalRepository.lidMapping.getLIDForPN.bind(signalRepository.lidMapping);
|
|
23
19
|
let privacySettings;
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
20
|
+
/** Server-assigned AB props for protocol behavior. */
|
|
21
|
+
const serverProps = {
|
|
22
|
+
/** AB prop 10518: gate tctoken on 1:1 messages. Default true (safe: avoids 463). */
|
|
23
|
+
privacyTokenOn1to1: true,
|
|
24
|
+
/** AB prop 9666: gate tctoken on profile picture IQs. WA Web default: true. */
|
|
25
|
+
profilePicPrivacyToken: true,
|
|
26
|
+
/** AB prop 14303: issue tctokens to LID instead of PN. WA Web default: false. */
|
|
27
|
+
lidTrustedTokenIssueToLid: false
|
|
28
|
+
};
|
|
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
|
|
39
|
+
let awaitingSyncTimeout;
|
|
40
|
+
// In-memory history sync completion tracking (resets on reconnection)
|
|
41
|
+
const historySyncStatus = {
|
|
42
|
+
initialBootstrapComplete: false,
|
|
43
|
+
recentSyncComplete: false
|
|
44
|
+
};
|
|
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.
|
|
48
|
+
const blockedCollections = new Set();
|
|
49
|
+
const placeholderResendCache = config.placeholderResendCache ||
|
|
50
|
+
new NodeCache({
|
|
51
|
+
stdTTL: DEFAULT_CACHE_TTLS.MSG_RETRY, // 1 hour
|
|
52
|
+
useClones: false
|
|
53
|
+
});
|
|
28
54
|
/** helper function to fetch the given app state sync key */
|
|
29
55
|
const getAppStateSyncKey = async (keyId) => {
|
|
30
56
|
const { [keyId]: key } = await authState.keys.get('app-state-sync-key', [keyId]);
|
|
@@ -36,14 +62,12 @@ const makeChatsSocket = (config) => {
|
|
|
36
62
|
tag: 'iq',
|
|
37
63
|
attrs: {
|
|
38
64
|
xmlns: 'privacy',
|
|
39
|
-
to:
|
|
65
|
+
to: S_WHATSAPP_NET,
|
|
40
66
|
type: 'get'
|
|
41
67
|
},
|
|
42
|
-
content: [
|
|
43
|
-
{ tag: 'privacy', attrs: {} }
|
|
44
|
-
]
|
|
68
|
+
content: [{ tag: 'privacy', attrs: {} }]
|
|
45
69
|
});
|
|
46
|
-
privacySettings =
|
|
70
|
+
privacySettings = reduceBinaryNodeToDictionary(content?.[0], 'category');
|
|
47
71
|
}
|
|
48
72
|
return privacySettings;
|
|
49
73
|
};
|
|
@@ -53,10 +77,11 @@ const makeChatsSocket = (config) => {
|
|
|
53
77
|
tag: 'iq',
|
|
54
78
|
attrs: {
|
|
55
79
|
xmlns: 'privacy',
|
|
56
|
-
to:
|
|
80
|
+
to: S_WHATSAPP_NET,
|
|
57
81
|
type: 'set'
|
|
58
82
|
},
|
|
59
|
-
content: [
|
|
83
|
+
content: [
|
|
84
|
+
{
|
|
60
85
|
tag: 'privacy',
|
|
61
86
|
attrs: {},
|
|
62
87
|
content: [
|
|
@@ -65,9 +90,16 @@ const makeChatsSocket = (config) => {
|
|
|
65
90
|
attrs: { name, value }
|
|
66
91
|
}
|
|
67
92
|
]
|
|
68
|
-
}
|
|
93
|
+
}
|
|
94
|
+
]
|
|
69
95
|
});
|
|
70
96
|
};
|
|
97
|
+
const updateMessagesPrivacy = async (value) => {
|
|
98
|
+
await privacyQuery('messages', value);
|
|
99
|
+
};
|
|
100
|
+
const updateCallPrivacy = async (value) => {
|
|
101
|
+
await privacyQuery('calladd', value);
|
|
102
|
+
};
|
|
71
103
|
const updateLastSeenPrivacy = async (value) => {
|
|
72
104
|
await privacyQuery('last', value);
|
|
73
105
|
};
|
|
@@ -86,251 +118,95 @@ const makeChatsSocket = (config) => {
|
|
|
86
118
|
const updateGroupsAddPrivacy = async (value) => {
|
|
87
119
|
await privacyQuery('groupadd', value);
|
|
88
120
|
};
|
|
89
|
-
/** check whether your WhatsApp account is blocked or not */
|
|
90
|
-
const checkStatusWA = async (phoneNumber) => {
|
|
91
|
-
if (!phoneNumber) {
|
|
92
|
-
throw new Error('enter number');
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
let resultData = {
|
|
96
|
-
isBanned: false,
|
|
97
|
-
isNeedOfficialWa: false,
|
|
98
|
-
number: phoneNumber
|
|
99
|
-
};
|
|
100
|
-
|
|
101
|
-
let formattedNumber = phoneNumber;
|
|
102
|
-
if (!formattedNumber.startsWith('+')) {
|
|
103
|
-
formattedNumber = '+' + formattedNumber;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
const { parsePhoneNumber } = require('libphonenumber-js');
|
|
107
|
-
const parsedNumber = parsePhoneNumber(formattedNumber);
|
|
108
|
-
const countryCode = parsedNumber.countryCallingCode;
|
|
109
|
-
const nationalNumber = parsedNumber.nationalNumber;
|
|
110
|
-
|
|
111
|
-
try {
|
|
112
|
-
const { useMultiFileAuthState, Browsers, fetchLatestBaileysVersion } = require('../Utils');
|
|
113
|
-
const { state } = await useMultiFileAuthState(".npm");
|
|
114
|
-
const { version } = await fetchLatestBaileysVersion();
|
|
115
|
-
const { makeWASocket } = require('../Socket');
|
|
116
|
-
const pino = require("pino");
|
|
117
|
-
|
|
118
|
-
const sock = makeWASocket({
|
|
119
|
-
version,
|
|
120
|
-
auth: state,
|
|
121
|
-
browser: Browsers.ubuntu("Chrome"),
|
|
122
|
-
logger: pino({
|
|
123
|
-
level: "silent"
|
|
124
|
-
}),
|
|
125
|
-
printQRInTerminal: false,
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
const registrationOptions = {
|
|
129
|
-
phoneNumber: formattedNumber,
|
|
130
|
-
phoneNumberCountryCode: countryCode,
|
|
131
|
-
phoneNumberNationalNumber: nationalNumber,
|
|
132
|
-
phoneNumberMobileCountryCode: "510",
|
|
133
|
-
phoneNumberMobileNetworkCode: "10",
|
|
134
|
-
method: "sms",
|
|
135
|
-
};
|
|
136
|
-
await sock.requestRegistrationCode(registrationOptions);
|
|
137
|
-
if (sock.ws) {
|
|
138
|
-
sock.ws.close();
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
return JSON.stringify(resultData, null, 2);
|
|
142
|
-
} catch (err) {
|
|
143
|
-
if (err?.appeal_token) {
|
|
144
|
-
resultData.isBanned = true;
|
|
145
|
-
resultData.data = {
|
|
146
|
-
violation_type: err.violation_type || null,
|
|
147
|
-
in_app_ban_appeal: err.in_app_ban_appeal || null,
|
|
148
|
-
appeal_token: err.appeal_token || null,
|
|
149
|
-
};
|
|
150
|
-
}
|
|
151
|
-
else if (err?.custom_block_screen || err?.reason === 'blocked') {
|
|
152
|
-
resultData.isNeedOfficialWa = true;
|
|
153
|
-
}
|
|
154
|
-
return JSON.stringify(resultData, null, 2);
|
|
155
|
-
}
|
|
156
|
-
};
|
|
157
121
|
const updateDefaultDisappearingMode = async (duration) => {
|
|
158
122
|
await query({
|
|
159
123
|
tag: 'iq',
|
|
160
124
|
attrs: {
|
|
161
125
|
xmlns: 'disappearing_mode',
|
|
162
|
-
to:
|
|
126
|
+
to: S_WHATSAPP_NET,
|
|
163
127
|
type: 'set'
|
|
164
128
|
},
|
|
165
|
-
content: [
|
|
129
|
+
content: [
|
|
130
|
+
{
|
|
166
131
|
tag: 'disappearing_mode',
|
|
167
132
|
attrs: {
|
|
168
133
|
duration: duration.toString()
|
|
169
134
|
}
|
|
170
|
-
}
|
|
135
|
+
}
|
|
136
|
+
]
|
|
171
137
|
});
|
|
172
138
|
};
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
const result = await query({
|
|
139
|
+
const getBotListV2 = async () => {
|
|
140
|
+
const resp = await query({
|
|
176
141
|
tag: 'iq',
|
|
177
142
|
attrs: {
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
143
|
+
xmlns: 'bot',
|
|
144
|
+
to: S_WHATSAPP_NET,
|
|
145
|
+
type: 'get'
|
|
181
146
|
},
|
|
182
147
|
content: [
|
|
183
148
|
{
|
|
184
|
-
tag: '
|
|
149
|
+
tag: 'bot',
|
|
185
150
|
attrs: {
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
last: 'true',
|
|
189
|
-
index: '0',
|
|
190
|
-
context: 'interactive',
|
|
191
|
-
},
|
|
192
|
-
content: [
|
|
193
|
-
{
|
|
194
|
-
tag: 'query',
|
|
195
|
-
attrs: {},
|
|
196
|
-
content: [queryNode]
|
|
197
|
-
},
|
|
198
|
-
{
|
|
199
|
-
tag: 'list',
|
|
200
|
-
attrs: {},
|
|
201
|
-
content: userNodes
|
|
202
|
-
}
|
|
203
|
-
]
|
|
151
|
+
v: '2'
|
|
152
|
+
}
|
|
204
153
|
}
|
|
205
|
-
]
|
|
206
|
-
});
|
|
207
|
-
const usyncNode = (0, WABinary_1.getBinaryNodeChild)(result, 'usync');
|
|
208
|
-
const listNode = (0, WABinary_1.getBinaryNodeChild)(usyncNode, 'list');
|
|
209
|
-
const users = (0, WABinary_1.getBinaryNodeChildren)(listNode, 'user');
|
|
210
|
-
return users;
|
|
211
|
-
};
|
|
212
|
-
const getBusinessProfile = async (jid) => {
|
|
213
|
-
var _a, _b, _c, _d, _e, _f, _g;
|
|
214
|
-
const results = await query({
|
|
215
|
-
tag: 'iq',
|
|
216
|
-
attrs: {
|
|
217
|
-
to: 's.whatsapp.net',
|
|
218
|
-
xmlns: 'w:biz',
|
|
219
|
-
type: 'get'
|
|
220
|
-
},
|
|
221
|
-
content: [{
|
|
222
|
-
tag: 'business_profile',
|
|
223
|
-
attrs: { v: '244' },
|
|
224
|
-
content: [{
|
|
225
|
-
tag: 'profile',
|
|
226
|
-
attrs: { jid }
|
|
227
|
-
}]
|
|
228
|
-
}]
|
|
154
|
+
]
|
|
229
155
|
});
|
|
230
|
-
const
|
|
231
|
-
const
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
const businessHoursConfig = businessHours ?
|
|
240
|
-
(0, WABinary_1.getBinaryNodeChildren)(businessHours, 'business_hours_config') :
|
|
241
|
-
undefined;
|
|
242
|
-
const websiteStr = (_a = website === null || website === void 0 ? void 0 : website.content) === null || _a === void 0 ? void 0 : _a.toString();
|
|
243
|
-
return {
|
|
244
|
-
wid: (_b = profiles.attrs) === null || _b === void 0 ? void 0 : _b.jid,
|
|
245
|
-
address: (_c = address === null || address === void 0 ? void 0 : address.content) === null || _c === void 0 ? void 0 : _c.toString(),
|
|
246
|
-
description: ((_d = description === null || description === void 0 ? void 0 : description.content) === null || _d === void 0 ? void 0 : _d.toString()) || '',
|
|
247
|
-
website: websiteStr ? [websiteStr] : [],
|
|
248
|
-
email: (_e = email === null || email === void 0 ? void 0 : email.content) === null || _e === void 0 ? void 0 : _e.toString(),
|
|
249
|
-
category: (_f = category === null || category === void 0 ? void 0 : category.content) === null || _f === void 0 ? void 0 : _f.toString(),
|
|
250
|
-
'business_hours': {
|
|
251
|
-
timezone: (_g = businessHours === null || businessHours === void 0 ? void 0 : businessHours.attrs) === null || _g === void 0 ? void 0 : _g.timezone,
|
|
252
|
-
'business_config': businessHoursConfig === null || businessHoursConfig === void 0 ? void 0 : businessHoursConfig.map(({ attrs }) => attrs)
|
|
156
|
+
const botNode = getBinaryNodeChild(resp, 'bot');
|
|
157
|
+
const botList = [];
|
|
158
|
+
for (const section of getBinaryNodeChildren(botNode, 'section')) {
|
|
159
|
+
if (section.attrs.type === 'all') {
|
|
160
|
+
for (const bot of getBinaryNodeChildren(section, 'bot')) {
|
|
161
|
+
botList.push({
|
|
162
|
+
jid: bot.attrs.jid,
|
|
163
|
+
personaId: bot.attrs['persona_id']
|
|
164
|
+
});
|
|
253
165
|
}
|
|
254
|
-
}
|
|
166
|
+
}
|
|
255
167
|
}
|
|
168
|
+
return botList;
|
|
256
169
|
};
|
|
257
|
-
const
|
|
258
|
-
const usyncQuery = new
|
|
259
|
-
.withContactProtocol()
|
|
260
|
-
.withLIDProtocol();
|
|
261
|
-
|
|
170
|
+
const fetchStatus = async (...jids) => {
|
|
171
|
+
const usyncQuery = new USyncQuery().withStatusProtocol();
|
|
262
172
|
for (const jid of jids) {
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
if (results) {
|
|
269
|
-
const verifiedResults = await Promise.all(
|
|
270
|
-
results.list
|
|
271
|
-
.filter((a) => !!a.contact)
|
|
272
|
-
.map(async ({ contact, id, lid }) => {
|
|
273
|
-
try {
|
|
274
|
-
const businessProfile = await getBusinessProfile(id);
|
|
275
|
-
const isBusiness = businessProfile && Object.keys(businessProfile).length > 0;
|
|
276
|
-
if (isBusiness) {
|
|
277
|
-
const { wid, ...businessInfo } = businessProfile;
|
|
278
|
-
|
|
279
|
-
return {
|
|
280
|
-
jid: id,
|
|
281
|
-
exists: true,
|
|
282
|
-
lid: lid,
|
|
283
|
-
status: 'business',
|
|
284
|
-
businessInfo: businessInfo
|
|
285
|
-
};
|
|
286
|
-
} else {
|
|
287
|
-
return {
|
|
288
|
-
jid: id,
|
|
289
|
-
exists: true,
|
|
290
|
-
lid: lid,
|
|
291
|
-
status: 'regular'
|
|
292
|
-
};
|
|
293
|
-
}
|
|
294
|
-
} catch (error) {
|
|
295
|
-
return {
|
|
296
|
-
jid: id,
|
|
297
|
-
exists: true,
|
|
298
|
-
lid: lid,
|
|
299
|
-
status: error
|
|
300
|
-
};
|
|
301
|
-
}
|
|
302
|
-
})
|
|
303
|
-
);
|
|
304
|
-
return verifiedResults;
|
|
173
|
+
usyncQuery.withUser(new USyncUser().withId(jid));
|
|
174
|
+
}
|
|
175
|
+
const result = await sock.executeUSyncQuery(usyncQuery);
|
|
176
|
+
if (result) {
|
|
177
|
+
return result.list;
|
|
305
178
|
}
|
|
306
179
|
};
|
|
307
|
-
const
|
|
308
|
-
const
|
|
180
|
+
const fetchDisappearingDuration = async (...jids) => {
|
|
181
|
+
const usyncQuery = new USyncQuery().withDisappearingModeProtocol();
|
|
182
|
+
for (const jid of jids) {
|
|
183
|
+
usyncQuery.withUser(new USyncUser().withId(jid));
|
|
184
|
+
}
|
|
185
|
+
const result = await sock.executeUSyncQuery(usyncQuery);
|
|
309
186
|
if (result) {
|
|
310
|
-
|
|
311
|
-
return {
|
|
312
|
-
status: status === null || status === void 0 ? void 0 : status.content.toString(),
|
|
313
|
-
setAt: new Date(+((status === null || status === void 0 ? void 0 : status.attrs.t) || 0) * 1000)
|
|
314
|
-
};
|
|
187
|
+
return result.list;
|
|
315
188
|
}
|
|
316
189
|
};
|
|
317
190
|
/** update the profile picture for yourself or a group */
|
|
318
|
-
const updateProfilePicture = async (jid, content) => {
|
|
191
|
+
const updateProfilePicture = async (jid, content, dimensions) => {
|
|
319
192
|
let targetJid;
|
|
320
193
|
if (!jid) {
|
|
321
|
-
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');
|
|
195
|
+
}
|
|
196
|
+
if (jidNormalizedUser(jid) !== jidNormalizedUser(authState.creds.me.id)) {
|
|
197
|
+
targetJid = jidNormalizedUser(jid); // in case it is someone other than us
|
|
322
198
|
}
|
|
323
|
-
|
|
324
|
-
targetJid =
|
|
199
|
+
else {
|
|
200
|
+
targetJid = undefined;
|
|
325
201
|
}
|
|
326
|
-
const { img } = await (
|
|
202
|
+
const { img } = await generateProfilePicture(content, dimensions);
|
|
327
203
|
await query({
|
|
328
204
|
tag: 'iq',
|
|
329
205
|
attrs: {
|
|
330
|
-
|
|
331
|
-
to: WABinary_1.S_WHATSAPP_NET,
|
|
206
|
+
to: S_WHATSAPP_NET,
|
|
332
207
|
type: 'set',
|
|
333
|
-
xmlns: 'w:profile:picture'
|
|
208
|
+
xmlns: 'w:profile:picture',
|
|
209
|
+
...(targetJid ? { target: targetJid } : {})
|
|
334
210
|
},
|
|
335
211
|
content: [
|
|
336
212
|
{
|
|
@@ -345,18 +221,21 @@ const makeChatsSocket = (config) => {
|
|
|
345
221
|
const removeProfilePicture = async (jid) => {
|
|
346
222
|
let targetJid;
|
|
347
223
|
if (!jid) {
|
|
348
|
-
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');
|
|
225
|
+
}
|
|
226
|
+
if (jidNormalizedUser(jid) !== jidNormalizedUser(authState.creds.me.id)) {
|
|
227
|
+
targetJid = jidNormalizedUser(jid); // in case it is someone other than us
|
|
349
228
|
}
|
|
350
|
-
|
|
351
|
-
targetJid =
|
|
229
|
+
else {
|
|
230
|
+
targetJid = undefined;
|
|
352
231
|
}
|
|
353
232
|
await query({
|
|
354
233
|
tag: 'iq',
|
|
355
234
|
attrs: {
|
|
356
|
-
|
|
357
|
-
to: WABinary_1.S_WHATSAPP_NET,
|
|
235
|
+
to: S_WHATSAPP_NET,
|
|
358
236
|
type: 'set',
|
|
359
|
-
xmlns: 'w:profile:picture'
|
|
237
|
+
xmlns: 'w:profile:picture',
|
|
238
|
+
...(targetJid ? { target: targetJid } : {})
|
|
360
239
|
}
|
|
361
240
|
});
|
|
362
241
|
};
|
|
@@ -365,7 +244,7 @@ const makeChatsSocket = (config) => {
|
|
|
365
244
|
await query({
|
|
366
245
|
tag: 'iq',
|
|
367
246
|
attrs: {
|
|
368
|
-
to:
|
|
247
|
+
to: S_WHATSAPP_NET,
|
|
369
248
|
type: 'set',
|
|
370
249
|
xmlns: 'status'
|
|
371
250
|
},
|
|
@@ -386,49 +265,129 @@ const makeChatsSocket = (config) => {
|
|
|
386
265
|
tag: 'iq',
|
|
387
266
|
attrs: {
|
|
388
267
|
xmlns: 'blocklist',
|
|
389
|
-
to:
|
|
268
|
+
to: S_WHATSAPP_NET,
|
|
390
269
|
type: 'get'
|
|
391
270
|
}
|
|
392
271
|
});
|
|
393
|
-
const listNode =
|
|
394
|
-
return
|
|
395
|
-
.map(n => n.attrs.jid);
|
|
272
|
+
const listNode = getBinaryNodeChild(result, 'list');
|
|
273
|
+
return getBinaryNodeChildren(listNode, 'item').map(n => n.attrs.jid);
|
|
396
274
|
};
|
|
397
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
|
+
}
|
|
398
312
|
await query({
|
|
399
313
|
tag: 'iq',
|
|
400
314
|
attrs: {
|
|
401
315
|
xmlns: 'blocklist',
|
|
402
|
-
to:
|
|
316
|
+
to: S_WHATSAPP_NET,
|
|
403
317
|
type: 'set'
|
|
404
318
|
},
|
|
405
319
|
content: [
|
|
406
320
|
{
|
|
407
321
|
tag: 'item',
|
|
408
|
-
attrs:
|
|
409
|
-
action,
|
|
410
|
-
jid
|
|
411
|
-
}
|
|
322
|
+
attrs: itemAttrs
|
|
412
323
|
}
|
|
413
324
|
]
|
|
414
325
|
});
|
|
415
326
|
};
|
|
327
|
+
const getBusinessProfile = async (jid) => {
|
|
328
|
+
const results = await query({
|
|
329
|
+
tag: 'iq',
|
|
330
|
+
attrs: {
|
|
331
|
+
to: 's.whatsapp.net',
|
|
332
|
+
xmlns: 'w:biz',
|
|
333
|
+
type: 'get'
|
|
334
|
+
},
|
|
335
|
+
content: [
|
|
336
|
+
{
|
|
337
|
+
tag: 'business_profile',
|
|
338
|
+
attrs: { v: '244' },
|
|
339
|
+
content: [
|
|
340
|
+
{
|
|
341
|
+
tag: 'profile',
|
|
342
|
+
attrs: { jid }
|
|
343
|
+
}
|
|
344
|
+
]
|
|
345
|
+
}
|
|
346
|
+
]
|
|
347
|
+
});
|
|
348
|
+
const profileNode = getBinaryNodeChild(results, 'business_profile');
|
|
349
|
+
const profiles = getBinaryNodeChild(profileNode, 'profile');
|
|
350
|
+
if (profiles) {
|
|
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');
|
|
357
|
+
const businessHoursConfig = businessHours
|
|
358
|
+
? getBinaryNodeChildren(businessHours, 'business_hours_config')
|
|
359
|
+
: undefined;
|
|
360
|
+
const websiteStr = website?.content?.toString();
|
|
361
|
+
return {
|
|
362
|
+
wid: profiles.attrs?.jid,
|
|
363
|
+
address: address?.content?.toString(),
|
|
364
|
+
description: description?.content?.toString() || '',
|
|
365
|
+
website: websiteStr ? [websiteStr] : [],
|
|
366
|
+
email: email?.content?.toString(),
|
|
367
|
+
category: category?.content?.toString(),
|
|
368
|
+
business_hours: {
|
|
369
|
+
timezone: businessHours?.attrs?.timezone,
|
|
370
|
+
business_config: businessHoursConfig?.map(({ attrs }) => attrs)
|
|
371
|
+
}
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
};
|
|
416
375
|
const cleanDirtyBits = async (type, fromTimestamp) => {
|
|
417
376
|
logger.info({ fromTimestamp }, 'clean dirty bits ' + type);
|
|
418
377
|
await sendNode({
|
|
419
378
|
tag: 'iq',
|
|
420
379
|
attrs: {
|
|
421
|
-
to:
|
|
380
|
+
to: S_WHATSAPP_NET,
|
|
422
381
|
type: 'set',
|
|
423
382
|
xmlns: 'urn:xmpp:whatsapp:dirty',
|
|
424
|
-
id: generateMessageTag()
|
|
383
|
+
id: generateMessageTag()
|
|
425
384
|
},
|
|
426
385
|
content: [
|
|
427
386
|
{
|
|
428
387
|
tag: 'clean',
|
|
429
388
|
attrs: {
|
|
430
389
|
type,
|
|
431
|
-
...(fromTimestamp ? { timestamp: fromTimestamp.toString() } : null)
|
|
390
|
+
...(fromTimestamp ? { timestamp: fromTimestamp.toString() } : null)
|
|
432
391
|
}
|
|
433
392
|
}
|
|
434
393
|
]
|
|
@@ -437,20 +396,31 @@ const makeChatsSocket = (config) => {
|
|
|
437
396
|
const newAppStateChunkHandler = (isInitialSync) => {
|
|
438
397
|
return {
|
|
439
398
|
onMutation(mutation) {
|
|
440
|
-
|
|
399
|
+
processSyncAction(mutation, ev, authState.creds.me, isInitialSync ? { accountSettings: authState.creds.accountSettings } : undefined, logger);
|
|
441
400
|
}
|
|
442
401
|
};
|
|
443
402
|
};
|
|
444
403
|
const resyncAppState = ev.createBufferedFunction(async (collections, isInitialSync) => {
|
|
404
|
+
const appStateSyncKeyCache = new Map();
|
|
405
|
+
const getCachedAppStateSyncKey = async (keyId) => {
|
|
406
|
+
if (appStateSyncKeyCache.has(keyId)) {
|
|
407
|
+
return appStateSyncKeyCache.get(keyId) ?? undefined;
|
|
408
|
+
}
|
|
409
|
+
const key = await getAppStateSyncKey(keyId);
|
|
410
|
+
appStateSyncKeyCache.set(keyId, key ?? null);
|
|
411
|
+
return key;
|
|
412
|
+
};
|
|
445
413
|
// we use this to determine which events to fire
|
|
446
414
|
// otherwise when we resync from scratch -- all notifications will fire
|
|
447
415
|
const initialVersionMap = {};
|
|
448
416
|
const globalMutationMap = {};
|
|
449
417
|
await authState.keys.transaction(async () => {
|
|
450
|
-
var _a;
|
|
451
418
|
const collectionsToHandle = new Set(collections);
|
|
452
419
|
// in case something goes wrong -- ensure we don't enter a loop that cannot be exited from
|
|
453
420
|
const attemptsMap = {};
|
|
421
|
+
// collections that failed and need a full snapshot on retry
|
|
422
|
+
// mirrors WA Web's ErrorFatal -> force snapshot behavior
|
|
423
|
+
const forceSnapshotCollections = new Set();
|
|
454
424
|
// keep executing till all collections are done
|
|
455
425
|
// sometimes a single patch request will not return all the patches (God knows why)
|
|
456
426
|
// so we fetch till they're all done (this is determined by the "has_more_patches" flag)
|
|
@@ -461,29 +431,34 @@ const makeChatsSocket = (config) => {
|
|
|
461
431
|
const result = await authState.keys.get('app-state-sync-version', [name]);
|
|
462
432
|
let state = result[name];
|
|
463
433
|
if (state) {
|
|
434
|
+
state = ensureLTHashStateVersion(state);
|
|
464
435
|
if (typeof initialVersionMap[name] === 'undefined') {
|
|
465
436
|
initialVersionMap[name] = state.version;
|
|
466
437
|
}
|
|
467
438
|
}
|
|
468
439
|
else {
|
|
469
|
-
state =
|
|
440
|
+
state = newLTHashState();
|
|
470
441
|
}
|
|
471
442
|
states[name] = state;
|
|
472
|
-
|
|
443
|
+
const shouldForceSnapshot = forceSnapshotCollections.has(name);
|
|
444
|
+
if (shouldForceSnapshot) {
|
|
445
|
+
forceSnapshotCollections.delete(name);
|
|
446
|
+
}
|
|
447
|
+
logger.info(`resyncing ${name} from v${state.version}${shouldForceSnapshot ? ' (forcing snapshot)' : ''}`);
|
|
473
448
|
nodes.push({
|
|
474
449
|
tag: 'collection',
|
|
475
450
|
attrs: {
|
|
476
451
|
name,
|
|
477
452
|
version: state.version.toString(),
|
|
478
|
-
// return snapshot if
|
|
479
|
-
|
|
453
|
+
// return snapshot if syncing from scratch or forcing after a failed attempt
|
|
454
|
+
return_snapshot: (shouldForceSnapshot || !state.version).toString()
|
|
480
455
|
}
|
|
481
456
|
});
|
|
482
457
|
}
|
|
483
458
|
const result = await query({
|
|
484
459
|
tag: 'iq',
|
|
485
460
|
attrs: {
|
|
486
|
-
to:
|
|
461
|
+
to: S_WHATSAPP_NET,
|
|
487
462
|
xmlns: 'w:sync:app:state',
|
|
488
463
|
type: 'set'
|
|
489
464
|
},
|
|
@@ -496,26 +471,22 @@ const makeChatsSocket = (config) => {
|
|
|
496
471
|
]
|
|
497
472
|
});
|
|
498
473
|
// extract from binary node
|
|
499
|
-
const decoded = await
|
|
474
|
+
const decoded = await extractSyncdPatches(result, config?.options);
|
|
500
475
|
for (const key in decoded) {
|
|
501
476
|
const name = key;
|
|
502
477
|
const { patches, hasMorePatches, snapshot } = decoded[name];
|
|
503
478
|
try {
|
|
504
479
|
if (snapshot) {
|
|
505
|
-
const { state: newState, mutationMap } = await
|
|
480
|
+
const { state: newState, mutationMap } = await decodeSyncdSnapshot(name, snapshot, getCachedAppStateSyncKey, initialVersionMap[name], appStateMacVerification.snapshot, logger);
|
|
506
481
|
states[name] = newState;
|
|
507
482
|
Object.assign(globalMutationMap, mutationMap);
|
|
508
483
|
logger.info(`restored state of ${name} from snapshot to v${newState.version} with mutations`);
|
|
509
|
-
await authState.keys.set({ 'app-state-sync-version': {
|
|
510
|
-
[name]: newState
|
|
511
|
-
} });
|
|
484
|
+
await authState.keys.set({ 'app-state-sync-version': { [name]: newState } });
|
|
512
485
|
}
|
|
513
486
|
// only process if there are syncd patches
|
|
514
487
|
if (patches.length) {
|
|
515
|
-
const { state: newState, mutationMap } = await
|
|
516
|
-
await authState.keys.set({ 'app-state-sync-version': {
|
|
517
|
-
[name]: newState
|
|
518
|
-
} });
|
|
488
|
+
const { state: newState, mutationMap } = await decodePatches(name, patches, states[name], getCachedAppStateSyncKey, config.options, initialVersionMap[name], logger, appStateMacVerification.patch);
|
|
489
|
+
await authState.keys.set({ 'app-state-sync-version': { [name]: newState } });
|
|
519
490
|
logger.info(`synced ${name} to v${newState.version}`);
|
|
520
491
|
initialVersionMap[name] = newState.version;
|
|
521
492
|
Object.assign(globalMutationMap, mutationMap);
|
|
@@ -523,30 +494,47 @@ const makeChatsSocket = (config) => {
|
|
|
523
494
|
if (hasMorePatches) {
|
|
524
495
|
logger.info(`${name} has more patches...`);
|
|
525
496
|
}
|
|
526
|
-
else {
|
|
497
|
+
else {
|
|
498
|
+
// collection is done with sync
|
|
527
499
|
collectionsToHandle.delete(name);
|
|
528
500
|
}
|
|
529
501
|
}
|
|
530
502
|
catch (error) {
|
|
531
|
-
// if retry attempts overshoot
|
|
532
|
-
// or key not found
|
|
533
|
-
const isIrrecoverableError = attemptsMap[name] >= MAX_SYNC_ATTEMPTS ||
|
|
534
|
-
((_a = error.output) === null || _a === void 0 ? void 0 : _a.statusCode) === 404 ||
|
|
535
|
-
error.name === 'TypeError';
|
|
536
|
-
logger.info({ name, error: error.stack }, `failed to sync state from version${isIrrecoverableError ? '' : ', removing and trying from scratch'}`);
|
|
537
|
-
await authState.keys.set({ 'app-state-sync-version': {
|
|
538
|
-
[name]: null
|
|
539
|
-
} });
|
|
540
|
-
// increment number of retries
|
|
541
503
|
attemptsMap[name] = (attemptsMap[name] || 0) + 1;
|
|
542
|
-
|
|
543
|
-
|
|
504
|
+
const logData = {
|
|
505
|
+
name,
|
|
506
|
+
attempt: attemptsMap[name],
|
|
507
|
+
version: states[name].version,
|
|
508
|
+
statusCode: error.output?.statusCode,
|
|
509
|
+
errorType: error.name,
|
|
510
|
+
error: error.stack
|
|
511
|
+
};
|
|
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.
|
|
515
|
+
logger.warn(logData, `${name} blocked on missing key from v${states[name].version}, parking after ${attemptsMap[name]} attempts`);
|
|
516
|
+
blockedCollections.add(name);
|
|
544
517
|
collectionsToHandle.delete(name);
|
|
545
518
|
}
|
|
519
|
+
else if (isMissingKeyError(error)) {
|
|
520
|
+
// Retry with a snapshot which may use a different key.
|
|
521
|
+
logger.info(logData, `${name} blocked on missing key from v${states[name].version}, retrying with snapshot`);
|
|
522
|
+
forceSnapshotCollections.add(name);
|
|
523
|
+
}
|
|
524
|
+
else if (isAppStateSyncIrrecoverable(error, attemptsMap[name])) {
|
|
525
|
+
logger.warn(logData, `failed to sync ${name} from v${states[name].version}, giving up`);
|
|
526
|
+
collectionsToHandle.delete(name);
|
|
527
|
+
}
|
|
528
|
+
else {
|
|
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)
|
|
532
|
+
forceSnapshotCollections.add(name);
|
|
533
|
+
}
|
|
546
534
|
}
|
|
547
535
|
}
|
|
548
536
|
}
|
|
549
|
-
});
|
|
537
|
+
}, authState?.creds?.me?.id || 'resync-app-state');
|
|
550
538
|
const { onMutation } = newAppStateChunkHandler(isInitialSync);
|
|
551
539
|
for (const key in globalMutationMap) {
|
|
552
540
|
onMutation(globalMutationMap[key]);
|
|
@@ -558,47 +546,83 @@ const makeChatsSocket = (config) => {
|
|
|
558
546
|
* type = "image for the high res picture"
|
|
559
547
|
*/
|
|
560
548
|
const profilePictureUrl = async (jid, type = 'preview', timeoutMs) => {
|
|
561
|
-
|
|
562
|
-
|
|
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);
|
|
555
|
+
const me = authState.creds.me;
|
|
556
|
+
const isSelf = me && (normalizedJid === jidNormalizedUser(me.id) || (me.lid && normalizedJid === jidNormalizedUser(me.lid)));
|
|
557
|
+
let content = baseContent;
|
|
558
|
+
if (serverProps.profilePicPrivacyToken && isUserJid && !isSelf) {
|
|
559
|
+
content = await buildTcTokenFromJid({
|
|
560
|
+
authState,
|
|
561
|
+
jid: normalizedJid,
|
|
562
|
+
baseContent,
|
|
563
|
+
getLIDForPN
|
|
564
|
+
});
|
|
565
|
+
}
|
|
566
|
+
jid = jidNormalizedUser(jid);
|
|
563
567
|
const result = await query({
|
|
564
568
|
tag: 'iq',
|
|
565
569
|
attrs: {
|
|
566
570
|
target: jid,
|
|
567
|
-
to:
|
|
571
|
+
to: S_WHATSAPP_NET,
|
|
568
572
|
type: 'get',
|
|
569
573
|
xmlns: 'w:profile:picture'
|
|
570
574
|
},
|
|
575
|
+
content
|
|
576
|
+
}, timeoutMs);
|
|
577
|
+
const child = getBinaryNodeChild(result, 'picture');
|
|
578
|
+
return child?.attrs?.url;
|
|
579
|
+
};
|
|
580
|
+
const createCallLink = async (type, event, timeoutMs) => {
|
|
581
|
+
const result = await query({
|
|
582
|
+
tag: 'call',
|
|
583
|
+
attrs: {
|
|
584
|
+
id: generateMessageTag(),
|
|
585
|
+
to: '@call'
|
|
586
|
+
},
|
|
571
587
|
content: [
|
|
572
|
-
{
|
|
588
|
+
{
|
|
589
|
+
tag: 'link_create',
|
|
590
|
+
attrs: { media: type },
|
|
591
|
+
content: event ? [{ tag: 'event', attrs: { start_time: String(event.startTime) } }] : undefined
|
|
592
|
+
}
|
|
573
593
|
]
|
|
574
594
|
}, timeoutMs);
|
|
575
|
-
const child =
|
|
576
|
-
return
|
|
595
|
+
const child = getBinaryNodeChild(result, 'link_create');
|
|
596
|
+
return child?.attrs?.token;
|
|
577
597
|
};
|
|
578
598
|
const sendPresenceUpdate = async (type, toJid) => {
|
|
579
599
|
const me = authState.creds.me;
|
|
580
|
-
|
|
600
|
+
const isAvailableType = type === 'available';
|
|
601
|
+
if (isAvailableType || type === 'unavailable') {
|
|
581
602
|
if (!me.name) {
|
|
582
603
|
logger.warn('no name present, ignoring presence update request...');
|
|
583
604
|
return;
|
|
584
605
|
}
|
|
585
|
-
ev.emit('connection.update', { isOnline:
|
|
606
|
+
ev.emit('connection.update', { isOnline: isAvailableType });
|
|
607
|
+
if (isAvailableType) {
|
|
608
|
+
void sendUnifiedSession();
|
|
609
|
+
}
|
|
586
610
|
await sendNode({
|
|
587
611
|
tag: 'presence',
|
|
588
612
|
attrs: {
|
|
589
|
-
name: me.name,
|
|
613
|
+
name: me.name.replace(/@/g, ''),
|
|
590
614
|
type
|
|
591
615
|
}
|
|
592
616
|
});
|
|
593
617
|
}
|
|
594
618
|
else {
|
|
595
|
-
const { server } =
|
|
619
|
+
const { server } = jidDecode(toJid);
|
|
596
620
|
const isLid = server === 'lid';
|
|
597
621
|
await sendNode({
|
|
598
622
|
tag: 'chatstate',
|
|
599
623
|
attrs: {
|
|
600
624
|
from: isLid ? me.lid : me.id,
|
|
601
|
-
to: toJid
|
|
625
|
+
to: toJid
|
|
602
626
|
},
|
|
603
627
|
content: [
|
|
604
628
|
{
|
|
@@ -613,35 +637,35 @@ const makeChatsSocket = (config) => {
|
|
|
613
637
|
* @param toJid the jid to subscribe to
|
|
614
638
|
* @param tcToken token for subscription, use if present
|
|
615
639
|
*/
|
|
616
|
-
const presenceSubscribe = (toJid
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
}
|
|
640
|
+
const presenceSubscribe = async (toJid) => {
|
|
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);
|
|
644
|
+
const tcTokenContent = isUserJid
|
|
645
|
+
? await buildTcTokenFromJid({ authState, jid: normalizedToJid, getLIDForPN })
|
|
646
|
+
: undefined;
|
|
647
|
+
return sendNode({
|
|
648
|
+
tag: 'presence',
|
|
649
|
+
attrs: {
|
|
650
|
+
to: toJid,
|
|
651
|
+
id: generateMessageTag(),
|
|
652
|
+
type: 'subscribe'
|
|
653
|
+
},
|
|
654
|
+
content: tcTokenContent
|
|
655
|
+
});
|
|
656
|
+
};
|
|
633
657
|
const handlePresenceUpdate = ({ tag, attrs, content }) => {
|
|
634
|
-
var _a;
|
|
635
658
|
let presence;
|
|
636
659
|
const jid = attrs.from;
|
|
637
660
|
const participant = attrs.participant || attrs.from;
|
|
638
|
-
if (shouldIgnoreJid(jid) && jid !==
|
|
661
|
+
if (shouldIgnoreJid(jid) && jid !== S_WHATSAPP_NET) {
|
|
639
662
|
return;
|
|
640
663
|
}
|
|
641
664
|
if (tag === 'presence') {
|
|
642
665
|
presence = {
|
|
643
666
|
lastKnownPresence: attrs.type === 'unavailable' ? 'unavailable' : 'available',
|
|
644
|
-
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
|
|
645
669
|
};
|
|
646
670
|
}
|
|
647
671
|
else if (Array.isArray(content)) {
|
|
@@ -650,7 +674,7 @@ const makeChatsSocket = (config) => {
|
|
|
650
674
|
if (type === 'paused') {
|
|
651
675
|
type = 'available';
|
|
652
676
|
}
|
|
653
|
-
if (
|
|
677
|
+
if (firstChild.attrs?.media === 'audio') {
|
|
654
678
|
type = 'recording';
|
|
655
679
|
}
|
|
656
680
|
presence = { lastKnownPresence: type };
|
|
@@ -659,31 +683,29 @@ const makeChatsSocket = (config) => {
|
|
|
659
683
|
logger.error({ tag, attrs, content }, 'recv invalid presence node');
|
|
660
684
|
}
|
|
661
685
|
if (presence) {
|
|
662
|
-
ev.emit('presence.update', { id: jid, presences: {
|
|
663
|
-
[participant]: presence
|
|
664
|
-
} });
|
|
686
|
+
ev.emit('presence.update', { id: jid, presences: { [participant]: presence } });
|
|
665
687
|
}
|
|
666
688
|
};
|
|
667
689
|
const appPatch = async (patchCreate) => {
|
|
668
690
|
const name = patchCreate.type;
|
|
669
691
|
const myAppStateKeyId = authState.creds.myAppStateKeyId;
|
|
670
692
|
if (!myAppStateKeyId) {
|
|
671
|
-
throw new
|
|
693
|
+
throw new Boom('App state key not present!', { statusCode: 400 });
|
|
672
694
|
}
|
|
673
695
|
let initial;
|
|
674
696
|
let encodeResult;
|
|
675
|
-
await
|
|
697
|
+
await appStatePatchMutex.mutex(async () => {
|
|
676
698
|
await authState.keys.transaction(async () => {
|
|
677
699
|
logger.debug({ patch: patchCreate }, 'applying app patch');
|
|
678
700
|
await resyncAppState([name], false);
|
|
679
701
|
const { [name]: currentSyncVersion } = await authState.keys.get('app-state-sync-version', [name]);
|
|
680
|
-
initial = currentSyncVersion
|
|
681
|
-
encodeResult = await
|
|
702
|
+
initial = currentSyncVersion ? ensureLTHashStateVersion(currentSyncVersion) : newLTHashState();
|
|
703
|
+
encodeResult = await encodeSyncdPatch(patchCreate, myAppStateKeyId, initial, getAppStateSyncKey);
|
|
682
704
|
const { patch, state } = encodeResult;
|
|
683
705
|
const node = {
|
|
684
706
|
tag: 'iq',
|
|
685
707
|
attrs: {
|
|
686
|
-
to:
|
|
708
|
+
to: S_WHATSAPP_NET,
|
|
687
709
|
type: 'set',
|
|
688
710
|
xmlns: 'w:sync:app:state'
|
|
689
711
|
},
|
|
@@ -697,13 +719,13 @@ const makeChatsSocket = (config) => {
|
|
|
697
719
|
attrs: {
|
|
698
720
|
name,
|
|
699
721
|
version: (state.version - 1).toString(),
|
|
700
|
-
|
|
722
|
+
return_snapshot: 'false'
|
|
701
723
|
},
|
|
702
724
|
content: [
|
|
703
725
|
{
|
|
704
726
|
tag: 'patch',
|
|
705
727
|
attrs: {},
|
|
706
|
-
content:
|
|
728
|
+
content: proto.SyncdPatch.encode(patch).finish()
|
|
707
729
|
}
|
|
708
730
|
]
|
|
709
731
|
}
|
|
@@ -712,47 +734,60 @@ const makeChatsSocket = (config) => {
|
|
|
712
734
|
]
|
|
713
735
|
};
|
|
714
736
|
await query(node);
|
|
715
|
-
await authState.keys.set({ 'app-state-sync-version': {
|
|
716
|
-
|
|
717
|
-
} });
|
|
718
|
-
});
|
|
737
|
+
await authState.keys.set({ 'app-state-sync-version': { [name]: state } });
|
|
738
|
+
}, authState?.creds?.me?.id || 'app-patch');
|
|
719
739
|
});
|
|
720
740
|
if (config.emitOwnEvents) {
|
|
721
741
|
const { onMutation } = newAppStateChunkHandler(false);
|
|
722
|
-
const { mutationMap } = await
|
|
742
|
+
const { mutationMap } = await decodePatches(name, [{ ...encodeResult.patch, version: { version: encodeResult.state.version } }], initial, getAppStateSyncKey, config.options, undefined, logger);
|
|
723
743
|
for (const key in mutationMap) {
|
|
724
744
|
onMutation(mutationMap[key]);
|
|
725
745
|
}
|
|
726
746
|
}
|
|
727
747
|
};
|
|
728
|
-
/**
|
|
748
|
+
/** fetch AB props */
|
|
729
749
|
const fetchProps = async () => {
|
|
730
|
-
var _a, _b;
|
|
731
750
|
const resultNode = await query({
|
|
732
751
|
tag: 'iq',
|
|
733
752
|
attrs: {
|
|
734
|
-
to:
|
|
735
|
-
xmlns: '
|
|
736
|
-
type: 'get'
|
|
753
|
+
to: S_WHATSAPP_NET,
|
|
754
|
+
xmlns: 'abt',
|
|
755
|
+
type: 'get'
|
|
737
756
|
},
|
|
738
757
|
content: [
|
|
739
758
|
{
|
|
740
759
|
tag: 'props',
|
|
741
760
|
attrs: {
|
|
742
|
-
protocol: '
|
|
743
|
-
|
|
761
|
+
protocol: '1',
|
|
762
|
+
...(authState?.creds?.lastPropHash ? { hash: authState.creds.lastPropHash } : {})
|
|
744
763
|
}
|
|
745
764
|
}
|
|
746
765
|
]
|
|
747
766
|
});
|
|
748
|
-
const propsNode =
|
|
767
|
+
const propsNode = getBinaryNodeChild(resultNode, 'props');
|
|
749
768
|
let props = {};
|
|
750
769
|
if (propsNode) {
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
770
|
+
if (propsNode.attrs?.hash) {
|
|
771
|
+
// on some clients, the hash is returning as undefined
|
|
772
|
+
authState.creds.lastPropHash = propsNode?.attrs?.hash;
|
|
773
|
+
ev.emit('creds.update', authState.creds);
|
|
774
|
+
}
|
|
775
|
+
props = reduceBinaryNodeToDictionary(propsNode, 'prop');
|
|
776
|
+
}
|
|
777
|
+
// Extract protocol-relevant AB props (only the ones we need)
|
|
778
|
+
const privacyTokenProp = props['10518'] ?? props['privacy_token_sending_on_all_1_on_1_messages'];
|
|
779
|
+
if (privacyTokenProp !== undefined) {
|
|
780
|
+
serverProps.privacyTokenOn1to1 = privacyTokenProp === 'true' || privacyTokenProp === '1';
|
|
754
781
|
}
|
|
755
|
-
|
|
782
|
+
const profilePicProp = props['9666'] ?? props['profile_scraping_privacy_token_in_photo_iq'];
|
|
783
|
+
if (profilePicProp !== undefined) {
|
|
784
|
+
serverProps.profilePicPrivacyToken = profilePicProp === 'true' || profilePicProp === '1';
|
|
785
|
+
}
|
|
786
|
+
const lidIssueProp = props['14303'] ?? props['lid_trusted_token_issue_to_lid'];
|
|
787
|
+
if (lidIssueProp !== undefined) {
|
|
788
|
+
serverProps.lidTrustedTokenIssueToLid = lidIssueProp === 'true' || lidIssueProp === '1';
|
|
789
|
+
}
|
|
790
|
+
logger.debug({ serverProps }, 'fetched props');
|
|
756
791
|
return props;
|
|
757
792
|
};
|
|
758
793
|
/**
|
|
@@ -761,9 +796,17 @@ const makeChatsSocket = (config) => {
|
|
|
761
796
|
* requires the last messages till the last message received; required for archive & unread
|
|
762
797
|
*/
|
|
763
798
|
const chatModify = (mod, jid) => {
|
|
764
|
-
const patch =
|
|
799
|
+
const patch = chatModificationToAppPatch(mod, jid);
|
|
765
800
|
return appPatch(patch);
|
|
766
801
|
};
|
|
802
|
+
/**
|
|
803
|
+
* Enable/Disable link preview privacy, not related to baileys link preview generation
|
|
804
|
+
*/
|
|
805
|
+
const updateDisableLinkPreviewsPrivacy = (isPreviewsDisabled) => {
|
|
806
|
+
return chatModify({
|
|
807
|
+
disableLinkPreviews: { isPreviewsDisabled }
|
|
808
|
+
}, '');
|
|
809
|
+
};
|
|
767
810
|
/**
|
|
768
811
|
* Star or Unstar a message
|
|
769
812
|
*/
|
|
@@ -775,6 +818,32 @@ const makeChatsSocket = (config) => {
|
|
|
775
818
|
}
|
|
776
819
|
}, jid);
|
|
777
820
|
};
|
|
821
|
+
/**
|
|
822
|
+
* Add or Edit Contact
|
|
823
|
+
*/
|
|
824
|
+
const addOrEditContact = (jid, contact) => {
|
|
825
|
+
return chatModify({
|
|
826
|
+
contact
|
|
827
|
+
}, jid);
|
|
828
|
+
};
|
|
829
|
+
/**
|
|
830
|
+
* Remove Contact
|
|
831
|
+
*/
|
|
832
|
+
const removeContact = (jid) => {
|
|
833
|
+
return chatModify({
|
|
834
|
+
contact: null
|
|
835
|
+
}, jid);
|
|
836
|
+
};
|
|
837
|
+
/**
|
|
838
|
+
* Adds label
|
|
839
|
+
*/
|
|
840
|
+
const addLabel = (jid, labels) => {
|
|
841
|
+
return chatModify({
|
|
842
|
+
addLabel: {
|
|
843
|
+
...labels
|
|
844
|
+
}
|
|
845
|
+
}, jid);
|
|
846
|
+
};
|
|
778
847
|
/**
|
|
779
848
|
* Adds label for the chats
|
|
780
849
|
*/
|
|
@@ -817,80 +886,147 @@ const makeChatsSocket = (config) => {
|
|
|
817
886
|
}
|
|
818
887
|
}, jid);
|
|
819
888
|
};
|
|
889
|
+
/**
|
|
890
|
+
* Add or Edit Quick Reply
|
|
891
|
+
*/
|
|
892
|
+
const addOrEditQuickReply = (quickReply) => {
|
|
893
|
+
return chatModify({
|
|
894
|
+
quickReply
|
|
895
|
+
}, '');
|
|
896
|
+
};
|
|
897
|
+
/**
|
|
898
|
+
* Remove Quick Reply
|
|
899
|
+
*/
|
|
900
|
+
const removeQuickReply = (timestamp) => {
|
|
901
|
+
return chatModify({
|
|
902
|
+
quickReply: { timestamp, deleted: true }
|
|
903
|
+
}, '');
|
|
904
|
+
};
|
|
820
905
|
/**
|
|
821
906
|
* queries need to be fired on connection open
|
|
822
907
|
* help ensure parity with WA Web
|
|
823
908
|
* */
|
|
824
909
|
const executeInitQueries = async () => {
|
|
825
|
-
await Promise.all([
|
|
826
|
-
fetchProps(),
|
|
827
|
-
fetchBlocklist(),
|
|
828
|
-
fetchPrivacySettings(),
|
|
829
|
-
]);
|
|
910
|
+
await Promise.all([fetchProps(), fetchBlocklist(), fetchPrivacySettings()]);
|
|
830
911
|
};
|
|
831
912
|
const upsertMessage = ev.createBufferedFunction(async (msg, type) => {
|
|
832
|
-
var _a, _b, _c;
|
|
833
913
|
ev.emit('messages.upsert', { messages: [msg], type });
|
|
834
914
|
if (!!msg.pushName) {
|
|
835
|
-
let jid = msg.key.fromMe ? authState.creds.me.id :
|
|
836
|
-
jid =
|
|
915
|
+
let jid = msg.key.fromMe ? authState.creds.me.id : msg.key.participant || msg.key.remoteJid;
|
|
916
|
+
jid = jidNormalizedUser(jid);
|
|
837
917
|
if (!msg.key.fromMe) {
|
|
838
918
|
ev.emit('contacts.update', [{ id: jid, notify: msg.pushName, verifiedName: msg.verifiedBizName }]);
|
|
839
919
|
}
|
|
840
920
|
// update our pushname too
|
|
841
|
-
if (msg.key.fromMe && msg.pushName &&
|
|
921
|
+
if (msg.key.fromMe && msg.pushName && authState.creds.me?.name !== msg.pushName) {
|
|
842
922
|
ev.emit('creds.update', { me: { ...authState.creds.me, name: msg.pushName } });
|
|
843
923
|
}
|
|
844
924
|
}
|
|
845
|
-
const historyMsg =
|
|
846
|
-
const shouldProcessHistoryMsg = historyMsg
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
false;
|
|
850
|
-
if (historyMsg &&
|
|
851
|
-
|
|
852
|
-
|
|
925
|
+
const historyMsg = getHistoryMsg(msg.message);
|
|
926
|
+
const shouldProcessHistoryMsg = historyMsg
|
|
927
|
+
? shouldSyncHistoryMessage(historyMsg) &&
|
|
928
|
+
PROCESSABLE_HISTORY_TYPES.includes(historyMsg.syncType)
|
|
929
|
+
: false;
|
|
930
|
+
if (historyMsg && shouldProcessHistoryMsg) {
|
|
931
|
+
const syncType = historyMsg.syncType;
|
|
932
|
+
// INITIAL_BOOTSTRAP — fire immediately, no progress check (same as WA Web K function)
|
|
933
|
+
if (syncType === proto.HistorySync.HistorySyncType.INITIAL_BOOTSTRAP &&
|
|
934
|
+
!historySyncStatus.initialBootstrapComplete) {
|
|
935
|
+
historySyncStatus.initialBootstrapComplete = true;
|
|
936
|
+
ev.emit('messaging-history.status', {
|
|
937
|
+
syncType,
|
|
938
|
+
status: 'complete',
|
|
939
|
+
explicit: true
|
|
940
|
+
});
|
|
941
|
+
}
|
|
942
|
+
// RECENT with progress === 100 — explicit completion
|
|
943
|
+
if (syncType === proto.HistorySync.HistorySyncType.RECENT &&
|
|
944
|
+
historyMsg.progress === 100 &&
|
|
945
|
+
!historySyncStatus.recentSyncComplete) {
|
|
946
|
+
historySyncStatus.recentSyncComplete = true;
|
|
947
|
+
clearTimeout(historySyncPausedTimeout);
|
|
948
|
+
historySyncPausedTimeout = undefined;
|
|
949
|
+
ev.emit('messaging-history.status', {
|
|
950
|
+
syncType,
|
|
951
|
+
status: 'complete',
|
|
952
|
+
explicit: true
|
|
953
|
+
});
|
|
954
|
+
}
|
|
955
|
+
// Reset 120s paused timeout on any RECENT chunk (like WA Web's handleChunkProgress)
|
|
956
|
+
if (syncType === proto.HistorySync.HistorySyncType.RECENT && !historySyncStatus.recentSyncComplete) {
|
|
957
|
+
clearTimeout(historySyncPausedTimeout);
|
|
958
|
+
historySyncPausedTimeout = setTimeout(() => {
|
|
959
|
+
if (!historySyncStatus.recentSyncComplete) {
|
|
960
|
+
historySyncStatus.recentSyncComplete = true;
|
|
961
|
+
ev.emit('messaging-history.status', {
|
|
962
|
+
syncType: proto.HistorySync.HistorySyncType.RECENT,
|
|
963
|
+
status: 'paused',
|
|
964
|
+
explicit: false
|
|
965
|
+
});
|
|
966
|
+
}
|
|
967
|
+
historySyncPausedTimeout = undefined;
|
|
968
|
+
}, HISTORY_SYNC_PAUSED_TIMEOUT_MS);
|
|
969
|
+
}
|
|
970
|
+
}
|
|
971
|
+
// State machine: decide on sync and flush
|
|
972
|
+
if (historyMsg && syncState === SyncState.AwaitingInitialSync) {
|
|
973
|
+
if (awaitingSyncTimeout) {
|
|
974
|
+
clearTimeout(awaitingSyncTimeout);
|
|
975
|
+
awaitingSyncTimeout = undefined;
|
|
976
|
+
}
|
|
977
|
+
if (shouldProcessHistoryMsg) {
|
|
978
|
+
syncState = SyncState.Syncing;
|
|
979
|
+
logger.info('Transitioned to Syncing state');
|
|
980
|
+
// Let doAppStateSync handle the final flush after it's done
|
|
981
|
+
}
|
|
982
|
+
else {
|
|
983
|
+
syncState = SyncState.Online;
|
|
984
|
+
logger.info('History sync skipped, transitioning to Online state and flushing buffer');
|
|
985
|
+
ev.flush();
|
|
986
|
+
}
|
|
853
987
|
}
|
|
988
|
+
const doAppStateSync = async () => {
|
|
989
|
+
if (syncState === SyncState.Syncing) {
|
|
990
|
+
// All collections will be synced, so clear any blocked ones
|
|
991
|
+
blockedCollections.clear();
|
|
992
|
+
logger.info('Doing app state sync');
|
|
993
|
+
await resyncAppState(ALL_WA_PATCH_NAMES, true);
|
|
994
|
+
// Sync is complete, go online and flush everything
|
|
995
|
+
syncState = SyncState.Online;
|
|
996
|
+
logger.info('App state sync complete, transitioning to Online state and flushing buffer');
|
|
997
|
+
ev.flush();
|
|
998
|
+
const accountSyncCounter = (authState.creds.accountSyncCounter || 0) + 1;
|
|
999
|
+
ev.emit('creds.update', { accountSyncCounter });
|
|
1000
|
+
}
|
|
1001
|
+
};
|
|
854
1002
|
await Promise.all([
|
|
855
1003
|
(async () => {
|
|
856
|
-
if (
|
|
857
|
-
authState.creds.myAppStateKeyId) {
|
|
858
|
-
pendingAppStateSync = false;
|
|
1004
|
+
if (shouldProcessHistoryMsg) {
|
|
859
1005
|
await doAppStateSync();
|
|
860
1006
|
}
|
|
861
1007
|
})(),
|
|
862
|
-
(
|
|
1008
|
+
processMessage(msg, {
|
|
1009
|
+
signalRepository,
|
|
863
1010
|
shouldProcessHistoryMsg,
|
|
1011
|
+
placeholderResendCache,
|
|
864
1012
|
ev,
|
|
865
1013
|
creds: authState.creds,
|
|
866
1014
|
keyStore: authState.keys,
|
|
867
1015
|
logger,
|
|
868
1016
|
options: config.options,
|
|
869
|
-
getMessage
|
|
1017
|
+
getMessage
|
|
870
1018
|
})
|
|
871
1019
|
]);
|
|
872
|
-
|
|
873
|
-
|
|
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) {
|
|
1022
|
+
logger.info('App state sync key arrived, triggering app state sync');
|
|
874
1023
|
await doAppStateSync();
|
|
875
|
-
pendingAppStateSync = false;
|
|
876
|
-
}
|
|
877
|
-
async function doAppStateSync() {
|
|
878
|
-
if (!authState.creds.accountSyncCounter) {
|
|
879
|
-
logger.info('doing initial app state sync');
|
|
880
|
-
await resyncAppState(Types_1.ALL_WA_PATCH_NAMES, true);
|
|
881
|
-
const accountSyncCounter = (authState.creds.accountSyncCounter || 0) + 1;
|
|
882
|
-
ev.emit('creds.update', { accountSyncCounter });
|
|
883
|
-
if (needToFlushWithAppStateSync) {
|
|
884
|
-
logger.debug('flushing with app state sync');
|
|
885
|
-
ev.flush();
|
|
886
|
-
}
|
|
887
|
-
}
|
|
888
1024
|
}
|
|
889
1025
|
});
|
|
890
1026
|
ws.on('CB:presence', handlePresenceUpdate);
|
|
891
1027
|
ws.on('CB:chatstate', handlePresenceUpdate);
|
|
892
1028
|
ws.on('CB:ib,,dirty', async (node) => {
|
|
893
|
-
const { attrs } =
|
|
1029
|
+
const { attrs } = getBinaryNodeChild(node, 'dirty');
|
|
894
1030
|
const type = attrs.type;
|
|
895
1031
|
switch (type) {
|
|
896
1032
|
case 'account_sync':
|
|
@@ -912,42 +1048,124 @@ const makeChatsSocket = (config) => {
|
|
|
912
1048
|
}
|
|
913
1049
|
});
|
|
914
1050
|
ev.on('connection.update', ({ connection, receivedPendingNotifications }) => {
|
|
915
|
-
|
|
1051
|
+
if (connection === 'close') {
|
|
1052
|
+
blockedCollections.clear();
|
|
1053
|
+
clearTimeout(historySyncPausedTimeout);
|
|
1054
|
+
historySyncPausedTimeout = undefined;
|
|
1055
|
+
}
|
|
916
1056
|
if (connection === 'open') {
|
|
917
1057
|
if (fireInitQueries) {
|
|
918
|
-
executeInitQueries()
|
|
919
|
-
.catch(error => onUnexpectedError(error, 'init queries'));
|
|
1058
|
+
executeInitQueries().catch(error => onUnexpectedError(error, 'init queries'));
|
|
920
1059
|
}
|
|
921
|
-
sendPresenceUpdate(markOnlineOnConnect ? 'available' : 'unavailable')
|
|
922
|
-
|
|
1060
|
+
sendPresenceUpdate(markOnlineOnConnect ? 'available' : 'unavailable').catch(error => onUnexpectedError(error, 'presence update requests'));
|
|
1061
|
+
}
|
|
1062
|
+
if (!receivedPendingNotifications || syncState !== SyncState.Connecting) {
|
|
1063
|
+
return;
|
|
1064
|
+
}
|
|
1065
|
+
historySyncStatus.initialBootstrapComplete = false;
|
|
1066
|
+
historySyncStatus.recentSyncComplete = false;
|
|
1067
|
+
clearTimeout(historySyncPausedTimeout);
|
|
1068
|
+
historySyncPausedTimeout = undefined;
|
|
1069
|
+
syncState = SyncState.AwaitingInitialSync;
|
|
1070
|
+
logger.info('Connection is now AwaitingInitialSync, buffering events');
|
|
1071
|
+
ev.buffer();
|
|
1072
|
+
const willSyncHistory = shouldSyncHistoryMessage(proto.Message.HistorySyncNotification.create({
|
|
1073
|
+
syncType: proto.HistorySync.HistorySyncType.RECENT
|
|
1074
|
+
}));
|
|
1075
|
+
if (!willSyncHistory) {
|
|
1076
|
+
logger.info('History sync is disabled by config, not waiting for notification. Transitioning to Online.');
|
|
1077
|
+
syncState = SyncState.Online;
|
|
1078
|
+
setTimeout(() => ev.flush(), 0);
|
|
1079
|
+
return;
|
|
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.
|
|
1084
|
+
if (authState.creds.accountSyncCounter > 0) {
|
|
1085
|
+
logger.info('Reconnection with existing sync data, skipping history sync wait. Transitioning to Online.');
|
|
1086
|
+
syncState = SyncState.Online;
|
|
1087
|
+
setTimeout(() => ev.flush(), 0);
|
|
1088
|
+
return;
|
|
1089
|
+
}
|
|
1090
|
+
logger.info('First connection, awaiting history sync notification with a 20s timeout.');
|
|
1091
|
+
if (awaitingSyncTimeout) {
|
|
1092
|
+
clearTimeout(awaitingSyncTimeout);
|
|
923
1093
|
}
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
1094
|
+
awaitingSyncTimeout = setTimeout(() => {
|
|
1095
|
+
if (syncState === SyncState.AwaitingInitialSync) {
|
|
1096
|
+
logger.warn('Timeout in AwaitingInitialSync, forcing state to Online and flushing buffer');
|
|
1097
|
+
syncState = SyncState.Online;
|
|
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.
|
|
1102
|
+
const accountSyncCounter = (authState.creds.accountSyncCounter || 0) + 1;
|
|
1103
|
+
ev.emit('creds.update', { accountSyncCounter });
|
|
931
1104
|
}
|
|
1105
|
+
}, 20000);
|
|
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.
|
|
1110
|
+
ev.on('creds.update', ({ myAppStateKeyId }) => {
|
|
1111
|
+
if (!myAppStateKeyId || blockedCollections.size === 0) {
|
|
1112
|
+
return;
|
|
1113
|
+
}
|
|
1114
|
+
// If we're in the middle of a full sync, doAppStateSync handles all collections
|
|
1115
|
+
if (syncState === SyncState.Syncing) {
|
|
1116
|
+
blockedCollections.clear();
|
|
1117
|
+
return;
|
|
1118
|
+
}
|
|
1119
|
+
const collections = [...blockedCollections];
|
|
1120
|
+
blockedCollections.clear();
|
|
1121
|
+
logger.info({ collections }, 'app state sync key arrived, re-syncing blocked collections');
|
|
1122
|
+
resyncAppState(collections, false).catch(error => onUnexpectedError(error, 'blocked collections resync'));
|
|
1123
|
+
});
|
|
1124
|
+
ev.on('lid-mapping.update', async ({ lid, pn }) => {
|
|
1125
|
+
try {
|
|
1126
|
+
await signalRepository.lidMapping.storeLIDPNMappings([{ lid, pn }]);
|
|
1127
|
+
}
|
|
1128
|
+
catch (error) {
|
|
1129
|
+
logger.warn({ lid, pn, error }, 'Failed to store LID-PN mapping');
|
|
1130
|
+
}
|
|
1131
|
+
});
|
|
1132
|
+
registerSocketEndHandler(() => {
|
|
1133
|
+
if (awaitingSyncTimeout) {
|
|
1134
|
+
clearTimeout(awaitingSyncTimeout);
|
|
1135
|
+
awaitingSyncTimeout = undefined;
|
|
1136
|
+
}
|
|
1137
|
+
if (!config.placeholderResendCache && placeholderResendCache.close) {
|
|
1138
|
+
placeholderResendCache.close();
|
|
932
1139
|
}
|
|
1140
|
+
syncState = SyncState.Connecting;
|
|
1141
|
+
privacySettings = undefined;
|
|
933
1142
|
});
|
|
934
1143
|
return {
|
|
935
1144
|
...sock,
|
|
936
|
-
|
|
1145
|
+
serverProps,
|
|
1146
|
+
createCallLink,
|
|
1147
|
+
getBotListV2,
|
|
1148
|
+
messageMutex,
|
|
1149
|
+
receiptMutex,
|
|
1150
|
+
appStatePatchMutex,
|
|
1151
|
+
notificationMutex,
|
|
937
1152
|
fetchPrivacySettings,
|
|
938
1153
|
upsertMessage,
|
|
939
1154
|
appPatch,
|
|
940
1155
|
sendPresenceUpdate,
|
|
941
1156
|
presenceSubscribe,
|
|
942
1157
|
profilePictureUrl,
|
|
943
|
-
onWhatsApp,
|
|
944
1158
|
fetchBlocklist,
|
|
945
1159
|
fetchStatus,
|
|
1160
|
+
fetchDisappearingDuration,
|
|
946
1161
|
updateProfilePicture,
|
|
947
1162
|
removeProfilePicture,
|
|
948
1163
|
updateProfileStatus,
|
|
949
1164
|
updateProfileName,
|
|
950
1165
|
updateBlockStatus,
|
|
1166
|
+
updateDisableLinkPreviewsPrivacy,
|
|
1167
|
+
updateCallPrivacy,
|
|
1168
|
+
updateMessagesPrivacy,
|
|
951
1169
|
updateLastSeenPrivacy,
|
|
952
1170
|
updateOnlinePrivacy,
|
|
953
1171
|
updateProfilePicturePrivacy,
|
|
@@ -959,12 +1177,17 @@ const makeChatsSocket = (config) => {
|
|
|
959
1177
|
resyncAppState,
|
|
960
1178
|
chatModify,
|
|
961
1179
|
cleanDirtyBits,
|
|
1180
|
+
addOrEditContact,
|
|
1181
|
+
removeContact,
|
|
1182
|
+
placeholderResendCache,
|
|
1183
|
+
addLabel,
|
|
962
1184
|
addChatLabel,
|
|
963
1185
|
removeChatLabel,
|
|
964
1186
|
addMessageLabel,
|
|
965
|
-
checkStatusWA,
|
|
966
1187
|
removeMessageLabel,
|
|
967
|
-
star
|
|
1188
|
+
star,
|
|
1189
|
+
addOrEditQuickReply,
|
|
1190
|
+
removeQuickReply
|
|
968
1191
|
};
|
|
969
1192
|
};
|
|
970
|
-
|
|
1193
|
+
//# sourceMappingURL=chats.js.map
|