@alannxd/baileys 6.0.6 → 6.0.9
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/LICENSE +1 -1
- package/README.md +341 -286
- package/WAProto/GenerateStatics.sh +3 -0
- package/WAProto/WAProto.proto +6902 -0
- package/WAProto/fix-imports.js +85 -0
- package/WAProto/index.d.ts +79257 -0
- package/WAProto/index.js +205861 -60565
- package/engine-requirements.js +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 -427
- package/lib/Socket/communities.js +90 -80
- package/lib/Socket/groups.js +154 -161
- package/lib/Socket/index.js +11 -10
- package/lib/Socket/luxu.js +315 -469
- package/lib/Socket/messages-recv.js +1421 -615
- package/lib/Socket/messages-send.js +1150 -799
- package/lib/Socket/mex.js +42 -0
- package/lib/Socket/newsletter.js +152 -204
- package/lib/Socket/socket.js +544 -313
- 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 +189 -209
- 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 +795 -369
- 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 +13 -36
- package/package.json +85 -51
- package/WAProto/fix-import.js +0 -29
- package/lib/Defaults/baileys-version.json +0 -3
- package/lib/Defaults/index.d.ts +0 -53
- package/lib/Defaults/phonenumber-mcc.json +0 -223
- 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/queue-job.js +0 -57
- 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/abstract-socket-client.js +0 -13
- package/lib/Socket/Client/index.d.ts +0 -3
- package/lib/Socket/Client/mobile-socket-client.d.ts +0 -13
- package/lib/Socket/Client/mobile-socket-client.js +0 -65
- package/lib/Socket/Client/web-socket-client.d.ts +0 -12
- package/lib/Socket/Client/web-socket-client.js +0 -62
- package/lib/Socket/business.d.ts +0 -171
- package/lib/Socket/chats.d.ts +0 -267
- package/lib/Socket/communities.d.ts +0 -180
- package/lib/Socket/groups.d.ts +0 -115
- package/lib/Socket/index.d.ts +0 -173
- package/lib/Socket/luxu.d.ts +0 -266
- package/lib/Socket/messages-recv.d.ts +0 -161
- package/lib/Socket/messages-send.d.ts +0 -183
- package/lib/Socket/newsletter.d.ts +0 -134
- package/lib/Socket/registration.d.ts +0 -267
- package/lib/Socket/registration.js +0 -166
- package/lib/Socket/socket.d.ts +0 -44
- package/lib/Socket/usync.d.ts +0 -36
- package/lib/Socket/usync.js +0 -70
- 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/Newsletter.js +0 -38
- 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/baileys-event-stream.js +0 -63
- 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/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,259 +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
|
-
const results = await sock.executeUSyncQuery(usyncQuery);
|
|
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;
|
|
305
|
-
}
|
|
306
|
-
};
|
|
307
|
-
const toLid = async (jid) => {
|
|
308
|
-
const result = await onWhatsApp(jid);
|
|
309
|
-
let lidResult = result[0].lid;
|
|
310
|
-
if (!result[0].exist) {
|
|
311
|
-
lidResult = jid + "doesnt exist";
|
|
312
|
-
}
|
|
313
|
-
return lidResult
|
|
314
|
-
}
|
|
315
|
-
const fetchStatus = async (jid) => {
|
|
316
|
-
const [result] = await interactiveQuery([{ tag: 'user', attrs: { jid } }], { tag: 'status', attrs: {} });
|
|
173
|
+
usyncQuery.withUser(new USyncUser().withId(jid));
|
|
174
|
+
}
|
|
175
|
+
const result = await sock.executeUSyncQuery(usyncQuery);
|
|
317
176
|
if (result) {
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
177
|
+
return result.list;
|
|
178
|
+
}
|
|
179
|
+
};
|
|
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);
|
|
186
|
+
if (result) {
|
|
187
|
+
return result.list;
|
|
323
188
|
}
|
|
324
189
|
};
|
|
325
190
|
/** update the profile picture for yourself or a group */
|
|
326
|
-
const updateProfilePicture = async (jid, content) => {
|
|
191
|
+
const updateProfilePicture = async (jid, content, dimensions) => {
|
|
327
192
|
let targetJid;
|
|
328
193
|
if (!jid) {
|
|
329
|
-
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');
|
|
330
195
|
}
|
|
331
|
-
if (
|
|
332
|
-
targetJid =
|
|
196
|
+
if (jidNormalizedUser(jid) !== jidNormalizedUser(authState.creds.me.id)) {
|
|
197
|
+
targetJid = jidNormalizedUser(jid); // in case it is someone other than us
|
|
333
198
|
}
|
|
334
|
-
|
|
199
|
+
else {
|
|
200
|
+
targetJid = undefined;
|
|
201
|
+
}
|
|
202
|
+
const { img } = await generateProfilePicture(content, dimensions);
|
|
335
203
|
await query({
|
|
336
204
|
tag: 'iq',
|
|
337
205
|
attrs: {
|
|
338
|
-
|
|
339
|
-
to: WABinary_1.S_WHATSAPP_NET,
|
|
206
|
+
to: S_WHATSAPP_NET,
|
|
340
207
|
type: 'set',
|
|
341
|
-
xmlns: 'w:profile:picture'
|
|
208
|
+
xmlns: 'w:profile:picture',
|
|
209
|
+
...(targetJid ? { target: targetJid } : {})
|
|
342
210
|
},
|
|
343
211
|
content: [
|
|
344
212
|
{
|
|
@@ -353,18 +221,21 @@ const makeChatsSocket = (config) => {
|
|
|
353
221
|
const removeProfilePicture = async (jid) => {
|
|
354
222
|
let targetJid;
|
|
355
223
|
if (!jid) {
|
|
356
|
-
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');
|
|
357
225
|
}
|
|
358
|
-
if (
|
|
359
|
-
targetJid =
|
|
226
|
+
if (jidNormalizedUser(jid) !== jidNormalizedUser(authState.creds.me.id)) {
|
|
227
|
+
targetJid = jidNormalizedUser(jid); // in case it is someone other than us
|
|
228
|
+
}
|
|
229
|
+
else {
|
|
230
|
+
targetJid = undefined;
|
|
360
231
|
}
|
|
361
232
|
await query({
|
|
362
233
|
tag: 'iq',
|
|
363
234
|
attrs: {
|
|
364
|
-
|
|
365
|
-
to: WABinary_1.S_WHATSAPP_NET,
|
|
235
|
+
to: S_WHATSAPP_NET,
|
|
366
236
|
type: 'set',
|
|
367
|
-
xmlns: 'w:profile:picture'
|
|
237
|
+
xmlns: 'w:profile:picture',
|
|
238
|
+
...(targetJid ? { target: targetJid } : {})
|
|
368
239
|
}
|
|
369
240
|
});
|
|
370
241
|
};
|
|
@@ -373,7 +244,7 @@ const makeChatsSocket = (config) => {
|
|
|
373
244
|
await query({
|
|
374
245
|
tag: 'iq',
|
|
375
246
|
attrs: {
|
|
376
|
-
to:
|
|
247
|
+
to: S_WHATSAPP_NET,
|
|
377
248
|
type: 'set',
|
|
378
249
|
xmlns: 'status'
|
|
379
250
|
},
|
|
@@ -394,49 +265,129 @@ const makeChatsSocket = (config) => {
|
|
|
394
265
|
tag: 'iq',
|
|
395
266
|
attrs: {
|
|
396
267
|
xmlns: 'blocklist',
|
|
397
|
-
to:
|
|
268
|
+
to: S_WHATSAPP_NET,
|
|
398
269
|
type: 'get'
|
|
399
270
|
}
|
|
400
271
|
});
|
|
401
|
-
const listNode =
|
|
402
|
-
return
|
|
403
|
-
.map(n => n.attrs.jid);
|
|
272
|
+
const listNode = getBinaryNodeChild(result, 'list');
|
|
273
|
+
return getBinaryNodeChildren(listNode, 'item').map(n => n.attrs.jid);
|
|
404
274
|
};
|
|
405
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
|
+
}
|
|
406
312
|
await query({
|
|
407
313
|
tag: 'iq',
|
|
408
314
|
attrs: {
|
|
409
315
|
xmlns: 'blocklist',
|
|
410
|
-
to:
|
|
316
|
+
to: S_WHATSAPP_NET,
|
|
411
317
|
type: 'set'
|
|
412
318
|
},
|
|
413
319
|
content: [
|
|
414
320
|
{
|
|
415
321
|
tag: 'item',
|
|
416
|
-
attrs:
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
322
|
+
attrs: itemAttrs
|
|
323
|
+
}
|
|
324
|
+
]
|
|
325
|
+
});
|
|
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
|
+
]
|
|
420
345
|
}
|
|
421
346
|
]
|
|
422
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
|
+
}
|
|
423
374
|
};
|
|
424
375
|
const cleanDirtyBits = async (type, fromTimestamp) => {
|
|
425
376
|
logger.info({ fromTimestamp }, 'clean dirty bits ' + type);
|
|
426
377
|
await sendNode({
|
|
427
378
|
tag: 'iq',
|
|
428
379
|
attrs: {
|
|
429
|
-
to:
|
|
380
|
+
to: S_WHATSAPP_NET,
|
|
430
381
|
type: 'set',
|
|
431
382
|
xmlns: 'urn:xmpp:whatsapp:dirty',
|
|
432
|
-
id: generateMessageTag()
|
|
383
|
+
id: generateMessageTag()
|
|
433
384
|
},
|
|
434
385
|
content: [
|
|
435
386
|
{
|
|
436
387
|
tag: 'clean',
|
|
437
388
|
attrs: {
|
|
438
389
|
type,
|
|
439
|
-
...(fromTimestamp ? { timestamp: fromTimestamp.toString() } : null)
|
|
390
|
+
...(fromTimestamp ? { timestamp: fromTimestamp.toString() } : null)
|
|
440
391
|
}
|
|
441
392
|
}
|
|
442
393
|
]
|
|
@@ -445,20 +396,31 @@ const makeChatsSocket = (config) => {
|
|
|
445
396
|
const newAppStateChunkHandler = (isInitialSync) => {
|
|
446
397
|
return {
|
|
447
398
|
onMutation(mutation) {
|
|
448
|
-
|
|
399
|
+
processSyncAction(mutation, ev, authState.creds.me, isInitialSync ? { accountSettings: authState.creds.accountSettings } : undefined, logger);
|
|
449
400
|
}
|
|
450
401
|
};
|
|
451
402
|
};
|
|
452
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
|
+
};
|
|
453
413
|
// we use this to determine which events to fire
|
|
454
414
|
// otherwise when we resync from scratch -- all notifications will fire
|
|
455
415
|
const initialVersionMap = {};
|
|
456
416
|
const globalMutationMap = {};
|
|
457
417
|
await authState.keys.transaction(async () => {
|
|
458
|
-
var _a;
|
|
459
418
|
const collectionsToHandle = new Set(collections);
|
|
460
419
|
// in case something goes wrong -- ensure we don't enter a loop that cannot be exited from
|
|
461
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();
|
|
462
424
|
// keep executing till all collections are done
|
|
463
425
|
// sometimes a single patch request will not return all the patches (God knows why)
|
|
464
426
|
// so we fetch till they're all done (this is determined by the "has_more_patches" flag)
|
|
@@ -469,29 +431,34 @@ const makeChatsSocket = (config) => {
|
|
|
469
431
|
const result = await authState.keys.get('app-state-sync-version', [name]);
|
|
470
432
|
let state = result[name];
|
|
471
433
|
if (state) {
|
|
434
|
+
state = ensureLTHashStateVersion(state);
|
|
472
435
|
if (typeof initialVersionMap[name] === 'undefined') {
|
|
473
436
|
initialVersionMap[name] = state.version;
|
|
474
437
|
}
|
|
475
438
|
}
|
|
476
439
|
else {
|
|
477
|
-
state =
|
|
440
|
+
state = newLTHashState();
|
|
478
441
|
}
|
|
479
442
|
states[name] = state;
|
|
480
|
-
|
|
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)' : ''}`);
|
|
481
448
|
nodes.push({
|
|
482
449
|
tag: 'collection',
|
|
483
450
|
attrs: {
|
|
484
451
|
name,
|
|
485
452
|
version: state.version.toString(),
|
|
486
|
-
// return snapshot if
|
|
487
|
-
|
|
453
|
+
// return snapshot if syncing from scratch or forcing after a failed attempt
|
|
454
|
+
return_snapshot: (shouldForceSnapshot || !state.version).toString()
|
|
488
455
|
}
|
|
489
456
|
});
|
|
490
457
|
}
|
|
491
458
|
const result = await query({
|
|
492
459
|
tag: 'iq',
|
|
493
460
|
attrs: {
|
|
494
|
-
to:
|
|
461
|
+
to: S_WHATSAPP_NET,
|
|
495
462
|
xmlns: 'w:sync:app:state',
|
|
496
463
|
type: 'set'
|
|
497
464
|
},
|
|
@@ -504,26 +471,22 @@ const makeChatsSocket = (config) => {
|
|
|
504
471
|
]
|
|
505
472
|
});
|
|
506
473
|
// extract from binary node
|
|
507
|
-
const decoded = await
|
|
474
|
+
const decoded = await extractSyncdPatches(result, config?.options);
|
|
508
475
|
for (const key in decoded) {
|
|
509
476
|
const name = key;
|
|
510
477
|
const { patches, hasMorePatches, snapshot } = decoded[name];
|
|
511
478
|
try {
|
|
512
479
|
if (snapshot) {
|
|
513
|
-
const { state: newState, mutationMap } = await
|
|
480
|
+
const { state: newState, mutationMap } = await decodeSyncdSnapshot(name, snapshot, getCachedAppStateSyncKey, initialVersionMap[name], appStateMacVerification.snapshot, logger);
|
|
514
481
|
states[name] = newState;
|
|
515
482
|
Object.assign(globalMutationMap, mutationMap);
|
|
516
483
|
logger.info(`restored state of ${name} from snapshot to v${newState.version} with mutations`);
|
|
517
|
-
await authState.keys.set({ 'app-state-sync-version': {
|
|
518
|
-
[name]: newState
|
|
519
|
-
} });
|
|
484
|
+
await authState.keys.set({ 'app-state-sync-version': { [name]: newState } });
|
|
520
485
|
}
|
|
521
486
|
// only process if there are syncd patches
|
|
522
487
|
if (patches.length) {
|
|
523
|
-
const { state: newState, mutationMap } = await
|
|
524
|
-
await authState.keys.set({ 'app-state-sync-version': {
|
|
525
|
-
[name]: newState
|
|
526
|
-
} });
|
|
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 } });
|
|
527
490
|
logger.info(`synced ${name} to v${newState.version}`);
|
|
528
491
|
initialVersionMap[name] = newState.version;
|
|
529
492
|
Object.assign(globalMutationMap, mutationMap);
|
|
@@ -531,30 +494,47 @@ const makeChatsSocket = (config) => {
|
|
|
531
494
|
if (hasMorePatches) {
|
|
532
495
|
logger.info(`${name} has more patches...`);
|
|
533
496
|
}
|
|
534
|
-
else {
|
|
497
|
+
else {
|
|
498
|
+
// collection is done with sync
|
|
535
499
|
collectionsToHandle.delete(name);
|
|
536
500
|
}
|
|
537
501
|
}
|
|
538
502
|
catch (error) {
|
|
539
|
-
// if retry attempts overshoot
|
|
540
|
-
// or key not found
|
|
541
|
-
const isIrrecoverableError = attemptsMap[name] >= MAX_SYNC_ATTEMPTS ||
|
|
542
|
-
((_a = error.output) === null || _a === void 0 ? void 0 : _a.statusCode) === 404 ||
|
|
543
|
-
error.name === 'TypeError';
|
|
544
|
-
logger.info({ name, error: error.stack }, `failed to sync state from version${isIrrecoverableError ? '' : ', removing and trying from scratch'}`);
|
|
545
|
-
await authState.keys.set({ 'app-state-sync-version': {
|
|
546
|
-
[name]: null
|
|
547
|
-
} });
|
|
548
|
-
// increment number of retries
|
|
549
503
|
attemptsMap[name] = (attemptsMap[name] || 0) + 1;
|
|
550
|
-
|
|
551
|
-
|
|
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);
|
|
517
|
+
collectionsToHandle.delete(name);
|
|
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`);
|
|
552
526
|
collectionsToHandle.delete(name);
|
|
553
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
|
+
}
|
|
554
534
|
}
|
|
555
535
|
}
|
|
556
536
|
}
|
|
557
|
-
});
|
|
537
|
+
}, authState?.creds?.me?.id || 'resync-app-state');
|
|
558
538
|
const { onMutation } = newAppStateChunkHandler(isInitialSync);
|
|
559
539
|
for (const key in globalMutationMap) {
|
|
560
540
|
onMutation(globalMutationMap[key]);
|
|
@@ -566,47 +546,83 @@ const makeChatsSocket = (config) => {
|
|
|
566
546
|
* type = "image for the high res picture"
|
|
567
547
|
*/
|
|
568
548
|
const profilePictureUrl = async (jid, type = 'preview', timeoutMs) => {
|
|
569
|
-
|
|
570
|
-
|
|
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);
|
|
571
567
|
const result = await query({
|
|
572
568
|
tag: 'iq',
|
|
573
569
|
attrs: {
|
|
574
570
|
target: jid,
|
|
575
|
-
to:
|
|
571
|
+
to: S_WHATSAPP_NET,
|
|
576
572
|
type: 'get',
|
|
577
573
|
xmlns: 'w:profile:picture'
|
|
578
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
|
+
},
|
|
579
587
|
content: [
|
|
580
|
-
{
|
|
588
|
+
{
|
|
589
|
+
tag: 'link_create',
|
|
590
|
+
attrs: { media: type },
|
|
591
|
+
content: event ? [{ tag: 'event', attrs: { start_time: String(event.startTime) } }] : undefined
|
|
592
|
+
}
|
|
581
593
|
]
|
|
582
594
|
}, timeoutMs);
|
|
583
|
-
const child =
|
|
584
|
-
return
|
|
595
|
+
const child = getBinaryNodeChild(result, 'link_create');
|
|
596
|
+
return child?.attrs?.token;
|
|
585
597
|
};
|
|
586
598
|
const sendPresenceUpdate = async (type, toJid) => {
|
|
587
599
|
const me = authState.creds.me;
|
|
588
|
-
|
|
600
|
+
const isAvailableType = type === 'available';
|
|
601
|
+
if (isAvailableType || type === 'unavailable') {
|
|
589
602
|
if (!me.name) {
|
|
590
603
|
logger.warn('no name present, ignoring presence update request...');
|
|
591
604
|
return;
|
|
592
605
|
}
|
|
593
|
-
ev.emit('connection.update', { isOnline:
|
|
606
|
+
ev.emit('connection.update', { isOnline: isAvailableType });
|
|
607
|
+
if (isAvailableType) {
|
|
608
|
+
void sendUnifiedSession();
|
|
609
|
+
}
|
|
594
610
|
await sendNode({
|
|
595
611
|
tag: 'presence',
|
|
596
612
|
attrs: {
|
|
597
|
-
name: me.name,
|
|
613
|
+
name: me.name.replace(/@/g, ''),
|
|
598
614
|
type
|
|
599
615
|
}
|
|
600
616
|
});
|
|
601
617
|
}
|
|
602
618
|
else {
|
|
603
|
-
const { server } =
|
|
619
|
+
const { server } = jidDecode(toJid);
|
|
604
620
|
const isLid = server === 'lid';
|
|
605
621
|
await sendNode({
|
|
606
622
|
tag: 'chatstate',
|
|
607
623
|
attrs: {
|
|
608
624
|
from: isLid ? me.lid : me.id,
|
|
609
|
-
to: toJid
|
|
625
|
+
to: toJid
|
|
610
626
|
},
|
|
611
627
|
content: [
|
|
612
628
|
{
|
|
@@ -621,35 +637,35 @@ const makeChatsSocket = (config) => {
|
|
|
621
637
|
* @param toJid the jid to subscribe to
|
|
622
638
|
* @param tcToken token for subscription, use if present
|
|
623
639
|
*/
|
|
624
|
-
const presenceSubscribe = (toJid
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
}
|
|
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
|
+
};
|
|
641
657
|
const handlePresenceUpdate = ({ tag, attrs, content }) => {
|
|
642
|
-
var _a;
|
|
643
658
|
let presence;
|
|
644
659
|
const jid = attrs.from;
|
|
645
660
|
const participant = attrs.participant || attrs.from;
|
|
646
|
-
if (shouldIgnoreJid(jid) && jid !==
|
|
661
|
+
if (shouldIgnoreJid(jid) && jid !== S_WHATSAPP_NET) {
|
|
647
662
|
return;
|
|
648
663
|
}
|
|
649
664
|
if (tag === 'presence') {
|
|
650
665
|
presence = {
|
|
651
666
|
lastKnownPresence: attrs.type === 'unavailable' ? 'unavailable' : 'available',
|
|
652
|
-
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
|
|
653
669
|
};
|
|
654
670
|
}
|
|
655
671
|
else if (Array.isArray(content)) {
|
|
@@ -658,7 +674,7 @@ const makeChatsSocket = (config) => {
|
|
|
658
674
|
if (type === 'paused') {
|
|
659
675
|
type = 'available';
|
|
660
676
|
}
|
|
661
|
-
if (
|
|
677
|
+
if (firstChild.attrs?.media === 'audio') {
|
|
662
678
|
type = 'recording';
|
|
663
679
|
}
|
|
664
680
|
presence = { lastKnownPresence: type };
|
|
@@ -667,31 +683,29 @@ const makeChatsSocket = (config) => {
|
|
|
667
683
|
logger.error({ tag, attrs, content }, 'recv invalid presence node');
|
|
668
684
|
}
|
|
669
685
|
if (presence) {
|
|
670
|
-
ev.emit('presence.update', { id: jid, presences: {
|
|
671
|
-
[participant]: presence
|
|
672
|
-
} });
|
|
686
|
+
ev.emit('presence.update', { id: jid, presences: { [participant]: presence } });
|
|
673
687
|
}
|
|
674
688
|
};
|
|
675
689
|
const appPatch = async (patchCreate) => {
|
|
676
690
|
const name = patchCreate.type;
|
|
677
691
|
const myAppStateKeyId = authState.creds.myAppStateKeyId;
|
|
678
692
|
if (!myAppStateKeyId) {
|
|
679
|
-
throw new
|
|
693
|
+
throw new Boom('App state key not present!', { statusCode: 400 });
|
|
680
694
|
}
|
|
681
695
|
let initial;
|
|
682
696
|
let encodeResult;
|
|
683
|
-
await
|
|
697
|
+
await appStatePatchMutex.mutex(async () => {
|
|
684
698
|
await authState.keys.transaction(async () => {
|
|
685
699
|
logger.debug({ patch: patchCreate }, 'applying app patch');
|
|
686
700
|
await resyncAppState([name], false);
|
|
687
701
|
const { [name]: currentSyncVersion } = await authState.keys.get('app-state-sync-version', [name]);
|
|
688
|
-
initial = currentSyncVersion
|
|
689
|
-
encodeResult = await
|
|
702
|
+
initial = currentSyncVersion ? ensureLTHashStateVersion(currentSyncVersion) : newLTHashState();
|
|
703
|
+
encodeResult = await encodeSyncdPatch(patchCreate, myAppStateKeyId, initial, getAppStateSyncKey);
|
|
690
704
|
const { patch, state } = encodeResult;
|
|
691
705
|
const node = {
|
|
692
706
|
tag: 'iq',
|
|
693
707
|
attrs: {
|
|
694
|
-
to:
|
|
708
|
+
to: S_WHATSAPP_NET,
|
|
695
709
|
type: 'set',
|
|
696
710
|
xmlns: 'w:sync:app:state'
|
|
697
711
|
},
|
|
@@ -705,13 +719,13 @@ const makeChatsSocket = (config) => {
|
|
|
705
719
|
attrs: {
|
|
706
720
|
name,
|
|
707
721
|
version: (state.version - 1).toString(),
|
|
708
|
-
|
|
722
|
+
return_snapshot: 'false'
|
|
709
723
|
},
|
|
710
724
|
content: [
|
|
711
725
|
{
|
|
712
726
|
tag: 'patch',
|
|
713
727
|
attrs: {},
|
|
714
|
-
content:
|
|
728
|
+
content: proto.SyncdPatch.encode(patch).finish()
|
|
715
729
|
}
|
|
716
730
|
]
|
|
717
731
|
}
|
|
@@ -720,47 +734,60 @@ const makeChatsSocket = (config) => {
|
|
|
720
734
|
]
|
|
721
735
|
};
|
|
722
736
|
await query(node);
|
|
723
|
-
await authState.keys.set({ 'app-state-sync-version': {
|
|
724
|
-
|
|
725
|
-
} });
|
|
726
|
-
});
|
|
737
|
+
await authState.keys.set({ 'app-state-sync-version': { [name]: state } });
|
|
738
|
+
}, authState?.creds?.me?.id || 'app-patch');
|
|
727
739
|
});
|
|
728
740
|
if (config.emitOwnEvents) {
|
|
729
741
|
const { onMutation } = newAppStateChunkHandler(false);
|
|
730
|
-
const { mutationMap } = await
|
|
742
|
+
const { mutationMap } = await decodePatches(name, [{ ...encodeResult.patch, version: { version: encodeResult.state.version } }], initial, getAppStateSyncKey, config.options, undefined, logger);
|
|
731
743
|
for (const key in mutationMap) {
|
|
732
744
|
onMutation(mutationMap[key]);
|
|
733
745
|
}
|
|
734
746
|
}
|
|
735
747
|
};
|
|
736
|
-
/**
|
|
748
|
+
/** fetch AB props */
|
|
737
749
|
const fetchProps = async () => {
|
|
738
|
-
var _a, _b;
|
|
739
750
|
const resultNode = await query({
|
|
740
751
|
tag: 'iq',
|
|
741
752
|
attrs: {
|
|
742
|
-
to:
|
|
743
|
-
xmlns: '
|
|
744
|
-
type: 'get'
|
|
753
|
+
to: S_WHATSAPP_NET,
|
|
754
|
+
xmlns: 'abt',
|
|
755
|
+
type: 'get'
|
|
745
756
|
},
|
|
746
757
|
content: [
|
|
747
758
|
{
|
|
748
759
|
tag: 'props',
|
|
749
760
|
attrs: {
|
|
750
|
-
protocol: '
|
|
751
|
-
|
|
761
|
+
protocol: '1',
|
|
762
|
+
...(authState?.creds?.lastPropHash ? { hash: authState.creds.lastPropHash } : {})
|
|
752
763
|
}
|
|
753
764
|
}
|
|
754
765
|
]
|
|
755
766
|
});
|
|
756
|
-
const propsNode =
|
|
767
|
+
const propsNode = getBinaryNodeChild(resultNode, 'props');
|
|
757
768
|
let props = {};
|
|
758
769
|
if (propsNode) {
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
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';
|
|
781
|
+
}
|
|
782
|
+
const profilePicProp = props['9666'] ?? props['profile_scraping_privacy_token_in_photo_iq'];
|
|
783
|
+
if (profilePicProp !== undefined) {
|
|
784
|
+
serverProps.profilePicPrivacyToken = profilePicProp === 'true' || profilePicProp === '1';
|
|
762
785
|
}
|
|
763
|
-
|
|
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');
|
|
764
791
|
return props;
|
|
765
792
|
};
|
|
766
793
|
/**
|
|
@@ -769,9 +796,17 @@ const makeChatsSocket = (config) => {
|
|
|
769
796
|
* requires the last messages till the last message received; required for archive & unread
|
|
770
797
|
*/
|
|
771
798
|
const chatModify = (mod, jid) => {
|
|
772
|
-
const patch =
|
|
799
|
+
const patch = chatModificationToAppPatch(mod, jid);
|
|
773
800
|
return appPatch(patch);
|
|
774
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
|
+
};
|
|
775
810
|
/**
|
|
776
811
|
* Star or Unstar a message
|
|
777
812
|
*/
|
|
@@ -783,6 +818,32 @@ const makeChatsSocket = (config) => {
|
|
|
783
818
|
}
|
|
784
819
|
}, jid);
|
|
785
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
|
+
};
|
|
786
847
|
/**
|
|
787
848
|
* Adds label for the chats
|
|
788
849
|
*/
|
|
@@ -826,92 +887,146 @@ const makeChatsSocket = (config) => {
|
|
|
826
887
|
}, jid);
|
|
827
888
|
};
|
|
828
889
|
/**
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
const deleteChat = async (jid, key) => {
|
|
890
|
+
* Add or Edit Quick Reply
|
|
891
|
+
*/
|
|
892
|
+
const addOrEditQuickReply = (quickReply) => {
|
|
833
893
|
return chatModify({
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
894
|
+
quickReply
|
|
895
|
+
}, '');
|
|
896
|
+
};
|
|
897
|
+
/**
|
|
898
|
+
* Remove Quick Reply
|
|
899
|
+
*/
|
|
900
|
+
const removeQuickReply = (timestamp) => {
|
|
901
|
+
return chatModify({
|
|
902
|
+
quickReply: { timestamp, deleted: true }
|
|
903
|
+
}, '');
|
|
904
|
+
};
|
|
841
905
|
/**
|
|
842
906
|
* queries need to be fired on connection open
|
|
843
907
|
* help ensure parity with WA Web
|
|
844
908
|
* */
|
|
845
909
|
const executeInitQueries = async () => {
|
|
846
|
-
await Promise.all([
|
|
847
|
-
fetchProps(),
|
|
848
|
-
fetchBlocklist(),
|
|
849
|
-
fetchPrivacySettings(),
|
|
850
|
-
]);
|
|
910
|
+
await Promise.all([fetchProps(), fetchBlocklist(), fetchPrivacySettings()]);
|
|
851
911
|
};
|
|
852
912
|
const upsertMessage = ev.createBufferedFunction(async (msg, type) => {
|
|
853
|
-
var _a, _b, _c;
|
|
854
913
|
ev.emit('messages.upsert', { messages: [msg], type });
|
|
855
914
|
if (!!msg.pushName) {
|
|
856
|
-
let jid = msg.key.fromMe ? authState.creds.me.id :
|
|
857
|
-
jid =
|
|
915
|
+
let jid = msg.key.fromMe ? authState.creds.me.id : msg.key.participant || msg.key.remoteJid;
|
|
916
|
+
jid = jidNormalizedUser(jid);
|
|
858
917
|
if (!msg.key.fromMe) {
|
|
859
918
|
ev.emit('contacts.update', [{ id: jid, notify: msg.pushName, verifiedName: msg.verifiedBizName }]);
|
|
860
919
|
}
|
|
861
920
|
// update our pushname too
|
|
862
|
-
if (msg.key.fromMe && msg.pushName &&
|
|
921
|
+
if (msg.key.fromMe && msg.pushName && authState.creds.me?.name !== msg.pushName) {
|
|
863
922
|
ev.emit('creds.update', { me: { ...authState.creds.me, name: msg.pushName } });
|
|
864
923
|
}
|
|
865
924
|
}
|
|
866
|
-
const historyMsg =
|
|
867
|
-
const shouldProcessHistoryMsg = historyMsg
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
false;
|
|
871
|
-
if (historyMsg &&
|
|
872
|
-
|
|
873
|
-
|
|
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
|
+
}
|
|
874
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
|
+
};
|
|
875
1002
|
await Promise.all([
|
|
876
1003
|
(async () => {
|
|
877
|
-
if (
|
|
878
|
-
authState.creds.myAppStateKeyId) {
|
|
879
|
-
pendingAppStateSync = false;
|
|
1004
|
+
if (shouldProcessHistoryMsg) {
|
|
880
1005
|
await doAppStateSync();
|
|
881
1006
|
}
|
|
882
1007
|
})(),
|
|
883
|
-
(
|
|
1008
|
+
processMessage(msg, {
|
|
1009
|
+
signalRepository,
|
|
884
1010
|
shouldProcessHistoryMsg,
|
|
1011
|
+
placeholderResendCache,
|
|
885
1012
|
ev,
|
|
886
1013
|
creds: authState.creds,
|
|
887
1014
|
keyStore: authState.keys,
|
|
888
1015
|
logger,
|
|
889
1016
|
options: config.options,
|
|
890
|
-
getMessage
|
|
1017
|
+
getMessage
|
|
891
1018
|
})
|
|
892
1019
|
]);
|
|
893
|
-
|
|
894
|
-
|
|
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');
|
|
895
1023
|
await doAppStateSync();
|
|
896
|
-
pendingAppStateSync = false;
|
|
897
|
-
}
|
|
898
|
-
async function doAppStateSync() {
|
|
899
|
-
if (!authState.creds.accountSyncCounter) {
|
|
900
|
-
logger.info('doing initial app state sync');
|
|
901
|
-
await resyncAppState(Types_1.ALL_WA_PATCH_NAMES, true);
|
|
902
|
-
const accountSyncCounter = (authState.creds.accountSyncCounter || 0) + 1;
|
|
903
|
-
ev.emit('creds.update', { accountSyncCounter });
|
|
904
|
-
if (needToFlushWithAppStateSync) {
|
|
905
|
-
logger.debug('flushing with app state sync');
|
|
906
|
-
ev.flush();
|
|
907
|
-
}
|
|
908
|
-
}
|
|
909
1024
|
}
|
|
910
1025
|
});
|
|
911
1026
|
ws.on('CB:presence', handlePresenceUpdate);
|
|
912
1027
|
ws.on('CB:chatstate', handlePresenceUpdate);
|
|
913
1028
|
ws.on('CB:ib,,dirty', async (node) => {
|
|
914
|
-
const { attrs } =
|
|
1029
|
+
const { attrs } = getBinaryNodeChild(node, 'dirty');
|
|
915
1030
|
const type = attrs.type;
|
|
916
1031
|
switch (type) {
|
|
917
1032
|
case 'account_sync':
|
|
@@ -933,43 +1048,124 @@ const makeChatsSocket = (config) => {
|
|
|
933
1048
|
}
|
|
934
1049
|
});
|
|
935
1050
|
ev.on('connection.update', ({ connection, receivedPendingNotifications }) => {
|
|
936
|
-
|
|
1051
|
+
if (connection === 'close') {
|
|
1052
|
+
blockedCollections.clear();
|
|
1053
|
+
clearTimeout(historySyncPausedTimeout);
|
|
1054
|
+
historySyncPausedTimeout = undefined;
|
|
1055
|
+
}
|
|
937
1056
|
if (connection === 'open') {
|
|
938
1057
|
if (fireInitQueries) {
|
|
939
|
-
executeInitQueries()
|
|
940
|
-
.catch(error => onUnexpectedError(error, 'init queries'));
|
|
1058
|
+
executeInitQueries().catch(error => onUnexpectedError(error, 'init queries'));
|
|
941
1059
|
}
|
|
942
|
-
sendPresenceUpdate(markOnlineOnConnect ? 'available' : 'unavailable')
|
|
943
|
-
|
|
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;
|
|
944
1089
|
}
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
1090
|
+
logger.info('First connection, awaiting history sync notification with a 20s timeout.');
|
|
1091
|
+
if (awaitingSyncTimeout) {
|
|
1092
|
+
clearTimeout(awaitingSyncTimeout);
|
|
1093
|
+
}
|
|
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 });
|
|
952
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();
|
|
953
1139
|
}
|
|
1140
|
+
syncState = SyncState.Connecting;
|
|
1141
|
+
privacySettings = undefined;
|
|
954
1142
|
});
|
|
955
1143
|
return {
|
|
956
1144
|
...sock,
|
|
957
|
-
|
|
1145
|
+
serverProps,
|
|
1146
|
+
createCallLink,
|
|
1147
|
+
getBotListV2,
|
|
1148
|
+
messageMutex,
|
|
1149
|
+
receiptMutex,
|
|
1150
|
+
appStatePatchMutex,
|
|
1151
|
+
notificationMutex,
|
|
958
1152
|
fetchPrivacySettings,
|
|
959
1153
|
upsertMessage,
|
|
960
1154
|
appPatch,
|
|
961
1155
|
sendPresenceUpdate,
|
|
962
1156
|
presenceSubscribe,
|
|
963
1157
|
profilePictureUrl,
|
|
964
|
-
onWhatsApp,
|
|
965
|
-
toLid,
|
|
966
1158
|
fetchBlocklist,
|
|
967
1159
|
fetchStatus,
|
|
1160
|
+
fetchDisappearingDuration,
|
|
968
1161
|
updateProfilePicture,
|
|
969
1162
|
removeProfilePicture,
|
|
970
1163
|
updateProfileStatus,
|
|
971
1164
|
updateProfileName,
|
|
972
1165
|
updateBlockStatus,
|
|
1166
|
+
updateDisableLinkPreviewsPrivacy,
|
|
1167
|
+
updateCallPrivacy,
|
|
1168
|
+
updateMessagesPrivacy,
|
|
973
1169
|
updateLastSeenPrivacy,
|
|
974
1170
|
updateOnlinePrivacy,
|
|
975
1171
|
updateProfilePicturePrivacy,
|
|
@@ -981,13 +1177,17 @@ const makeChatsSocket = (config) => {
|
|
|
981
1177
|
resyncAppState,
|
|
982
1178
|
chatModify,
|
|
983
1179
|
cleanDirtyBits,
|
|
1180
|
+
addOrEditContact,
|
|
1181
|
+
removeContact,
|
|
1182
|
+
placeholderResendCache,
|
|
1183
|
+
addLabel,
|
|
984
1184
|
addChatLabel,
|
|
985
1185
|
removeChatLabel,
|
|
986
1186
|
addMessageLabel,
|
|
987
|
-
deleteChat,
|
|
988
|
-
checkStatusWA,
|
|
989
1187
|
removeMessageLabel,
|
|
990
|
-
star
|
|
1188
|
+
star,
|
|
1189
|
+
addOrEditQuickReply,
|
|
1190
|
+
removeQuickReply
|
|
991
1191
|
};
|
|
992
1192
|
};
|
|
993
|
-
|
|
1193
|
+
//# sourceMappingURL=chats.js.map
|