@crysnovax/baileys 2.5.2 → 2.5.6
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 +1430 -285
- package/WAProto/index.js +3578 -14789
- package/lib/Defaults/index.js +11 -19
- package/lib/Signal/libsignal.js +18 -42
- package/lib/Socket/chats.js +91 -246
- package/lib/Socket/messages-recv.js +322 -618
- package/lib/Socket/messages-send.js +74 -174
- package/lib/Socket/newsletter.js +2 -2
- package/lib/Socket/socket.js +20 -12
- package/lib/Types/index.js +0 -1
- package/lib/Utils/event-buffer.js +589 -0
- package/lib/Utils/index.js +0 -1
- package/lib/Utils/messages.js +114 -148
- package/lib/WABinary/constants.js +5 -99
- package/package.json +9 -43
- package/lib/Socket/Client//342/234/230 +0 -1
- package/lib/Socket//342/230/201/357/270/216 +0 -1
- package/lib/Types/Mex.js +0 -39
- package/lib/Types//342/234/206 +0 -1
- package/lib/Utils/use-sqlite-auth-state.js +0 -109
- package/lib/Utils//342/232/211 +0 -1
- package/lib/WABinary//342/216/231 +0 -1
package/lib/Socket/chats.js
CHANGED
|
@@ -1,31 +1,24 @@
|
|
|
1
1
|
import NodeCache from '@cacheable/node-cache';
|
|
2
2
|
import { Boom } from '@hapi/boom';
|
|
3
3
|
import { proto } from '../../WAProto/index.js';
|
|
4
|
-
import { DEFAULT_CACHE_TTLS,
|
|
4
|
+
import { DEFAULT_CACHE_TTLS, PROCESSABLE_HISTORY_TYPES } from '../Defaults/index.js';
|
|
5
5
|
import { ALL_WA_PATCH_NAMES } from '../Types/index.js';
|
|
6
6
|
import { SyncState } from '../Types/State.js';
|
|
7
|
-
import { chatModificationToAppPatch, decodePatches, decodeSyncdSnapshot, encodeSyncdPatch,
|
|
7
|
+
import { chatModificationToAppPatch, decodePatches, decodeSyncdSnapshot, encodeSyncdPatch, extractSyncdPatches, generateProfilePicture, getHistoryMsg, newLTHashState, processSyncAction } from '../Utils/index.js';
|
|
8
8
|
import { makeMutex } from '../Utils/make-mutex.js';
|
|
9
9
|
import processMessage from '../Utils/process-message.js';
|
|
10
10
|
import { buildTcTokenFromJid } from '../Utils/tc-token-utils.js';
|
|
11
|
-
import { getBinaryNodeChild, getBinaryNodeChildren,
|
|
11
|
+
import { getBinaryNodeChild, getBinaryNodeChildren, isLidUser, isPnUser, jidDecode, jidNormalizedUser, reduceBinaryNodeToDictionary, S_WHATSAPP_NET } from '../WABinary/index.js';
|
|
12
12
|
import { USyncQuery, USyncUser } from '../WAUSync/index.js';
|
|
13
13
|
import { makeSocket } from './socket.js';
|
|
14
|
+
const MAX_SYNC_ATTEMPTS = 2;
|
|
15
|
+
// Lia@Note 08-02-26 --- I know it's not efficient for RSS ಥ‿ಥ
|
|
16
|
+
const USER_ID_CACHE = new Map();
|
|
14
17
|
export const makeChatsSocket = (config) => {
|
|
15
18
|
const { logger, markOnlineOnConnect, fireInitQueries, appStateMacVerification, shouldIgnoreJid, shouldSyncHistoryMessage, getMessage } = config;
|
|
16
19
|
const sock = makeSocket(config);
|
|
17
20
|
const { ev, ws, authState, generateMessageTag, sendNode, query, signalRepository, onUnexpectedError, sendUnifiedSession, registerSocketEndHandler } = sock;
|
|
18
|
-
const getLIDForPN = signalRepository.lidMapping.getLIDForPN.bind(signalRepository.lidMapping);
|
|
19
21
|
let privacySettings;
|
|
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
22
|
let syncState = SyncState.Connecting;
|
|
30
23
|
/** this mutex ensures that messages are processed in order */
|
|
31
24
|
const messageMutex = makeMutex();
|
|
@@ -37,20 +30,14 @@ export const makeChatsSocket = (config) => {
|
|
|
37
30
|
const notificationMutex = makeMutex();
|
|
38
31
|
// Timeout for AwaitingInitialSync state
|
|
39
32
|
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
33
|
const placeholderResendCache = config.placeholderResendCache ||
|
|
50
34
|
new NodeCache({
|
|
51
35
|
stdTTL: DEFAULT_CACHE_TTLS.MSG_RETRY, // 1 hour
|
|
52
36
|
useClones: false
|
|
53
37
|
});
|
|
38
|
+
// if (!config.placeholderResendCache) {
|
|
39
|
+
// config.placeholderResendCache = placeholderResendCache;
|
|
40
|
+
// }
|
|
54
41
|
/** helper function to fetch the given app state sync key */
|
|
55
42
|
const getAppStateSyncKey = async (keyId) => {
|
|
56
43
|
const { [keyId]: key } = await authState.keys.get('app-state-sync-key', [keyId]);
|
|
@@ -187,24 +174,37 @@ export const makeChatsSocket = (config) => {
|
|
|
187
174
|
return result.list;
|
|
188
175
|
}
|
|
189
176
|
};
|
|
190
|
-
//
|
|
177
|
+
// Lia@Note 06-02-26 --- Quick helper only. This function exists solely for faster lookup with caching.
|
|
191
178
|
const findUserId = async (pnLid) => {
|
|
192
|
-
const
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
};
|
|
197
|
-
if (isPnUser(
|
|
198
|
-
userId.phoneNumber =
|
|
199
|
-
userId.lid =
|
|
179
|
+
const cachedId = USER_ID_CACHE.get(pnLid);
|
|
180
|
+
if (cachedId) {
|
|
181
|
+
return cachedId;
|
|
182
|
+
}
|
|
183
|
+
const userId = {};
|
|
184
|
+
if (isPnUser(pnLid)) {
|
|
185
|
+
userId.phoneNumber = pnLid;
|
|
186
|
+
userId.lid = (await signalRepository.lidMapping.getLIDsForPNs([pnLid]))?.[0]?.lid;
|
|
187
|
+
if (!userId.lid) {
|
|
188
|
+
userId.lid = 'id-not-found';
|
|
189
|
+
return userId; // Lia@Note 06-02-26 --- Early return to skip caching for non-existent ID
|
|
190
|
+
}
|
|
200
191
|
}
|
|
201
|
-
else if (isLidUser(
|
|
202
|
-
userId.lid =
|
|
203
|
-
userId.phoneNumber =
|
|
192
|
+
else if (isLidUser(pnLid)) {
|
|
193
|
+
userId.lid = pnLid;
|
|
194
|
+
userId.phoneNumber = (await signalRepository.lidMapping.getPNsForLIDs([pnLid]))?.[0]?.pn;
|
|
195
|
+
if (!userId.phoneNumber) {
|
|
196
|
+
userId.phoneNumber = 'id-not-found';
|
|
197
|
+
return userId; // Lia@Note 06-02-26 --- Early return to skip caching for non-existent ID
|
|
198
|
+
}
|
|
204
199
|
}
|
|
205
200
|
else {
|
|
206
201
|
throw new Boom('Invalid id input to find user ids', { statusCode: 400 });
|
|
207
202
|
}
|
|
203
|
+
userId.phoneNumber = jidNormalizedUser(userId.phoneNumber);
|
|
204
|
+
userId.lid = jidNormalizedUser(userId.lid);
|
|
205
|
+
// Lia@Note 06-02-26 --- I know... it's dirty (╯︵╰,)
|
|
206
|
+
USER_ID_CACHE.set(userId.phoneNumber, userId);
|
|
207
|
+
USER_ID_CACHE.set(userId.lid, userId);
|
|
208
208
|
return userId;
|
|
209
209
|
};
|
|
210
210
|
/** update the profile picture for yourself or a group */
|
|
@@ -293,42 +293,6 @@ export const makeChatsSocket = (config) => {
|
|
|
293
293
|
return getBinaryNodeChildren(listNode, 'item').map(n => n.attrs.jid);
|
|
294
294
|
};
|
|
295
295
|
const updateBlockStatus = async (jid, action) => {
|
|
296
|
-
const normalizedJid = jidNormalizedUser(jid);
|
|
297
|
-
let lid;
|
|
298
|
-
let pn_jid;
|
|
299
|
-
if (isLidUser(normalizedJid) || isHostedLidUser(normalizedJid)) {
|
|
300
|
-
lid = normalizedJid;
|
|
301
|
-
if (action === 'block') {
|
|
302
|
-
const pn = (await findUserId(normalizedJid)).phoneNumber;
|
|
303
|
-
if (!pn) {
|
|
304
|
-
throw new Boom(`Unable to resolve PN JID for LID: ${jid}`, { statusCode: 400 });
|
|
305
|
-
}
|
|
306
|
-
pn_jid = jidNormalizedUser(pn);
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
else if (isPnUser(normalizedJid) || isHostedPnUser(normalizedJid)) {
|
|
310
|
-
const mapped = (await findUserId(normalizedJid)).lid;
|
|
311
|
-
if (!mapped) {
|
|
312
|
-
throw new Boom(`Unable to resolve LID for PN JID: ${jid}`, { statusCode: 400 });
|
|
313
|
-
}
|
|
314
|
-
lid = mapped;
|
|
315
|
-
if (action === 'block') {
|
|
316
|
-
pn_jid = jidNormalizedUser(normalizedJid);
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
else {
|
|
320
|
-
throw new Boom(`Invalid jid: ${jid}`, { statusCode: 400 });
|
|
321
|
-
}
|
|
322
|
-
const itemAttrs = {
|
|
323
|
-
action,
|
|
324
|
-
jid: lid
|
|
325
|
-
};
|
|
326
|
-
if (action === 'block') {
|
|
327
|
-
if (!pn_jid) {
|
|
328
|
-
throw new Boom(`pn_jid required for block: ${jid}`, { statusCode: 400 });
|
|
329
|
-
}
|
|
330
|
-
itemAttrs.pn_jid = pn_jid;
|
|
331
|
-
}
|
|
332
296
|
await query({
|
|
333
297
|
tag: 'iq',
|
|
334
298
|
attrs: {
|
|
@@ -339,7 +303,10 @@ export const makeChatsSocket = (config) => {
|
|
|
339
303
|
content: [
|
|
340
304
|
{
|
|
341
305
|
tag: 'item',
|
|
342
|
-
attrs:
|
|
306
|
+
attrs: {
|
|
307
|
+
action,
|
|
308
|
+
jid
|
|
309
|
+
}
|
|
343
310
|
}
|
|
344
311
|
]
|
|
345
312
|
});
|
|
@@ -438,9 +405,6 @@ export const makeChatsSocket = (config) => {
|
|
|
438
405
|
const collectionsToHandle = new Set(collections);
|
|
439
406
|
// in case something goes wrong -- ensure we don't enter a loop that cannot be exited from
|
|
440
407
|
const attemptsMap = {};
|
|
441
|
-
// collections that failed and need a full snapshot on retry
|
|
442
|
-
// mirrors WA Web's ErrorFatal -> force snapshot behavior
|
|
443
|
-
const forceSnapshotCollections = new Set();
|
|
444
408
|
// keep executing till all collections are done
|
|
445
409
|
// sometimes a single patch request will not return all the patches (God knows why)
|
|
446
410
|
// so we fetch till they're all done (this is determined by the "has_more_patches" flag)
|
|
@@ -451,7 +415,6 @@ export const makeChatsSocket = (config) => {
|
|
|
451
415
|
const result = await authState.keys.get('app-state-sync-version', [name]);
|
|
452
416
|
let state = result[name];
|
|
453
417
|
if (state) {
|
|
454
|
-
state = ensureLTHashStateVersion(state);
|
|
455
418
|
if (typeof initialVersionMap[name] === 'undefined') {
|
|
456
419
|
initialVersionMap[name] = state.version;
|
|
457
420
|
}
|
|
@@ -460,18 +423,14 @@ export const makeChatsSocket = (config) => {
|
|
|
460
423
|
state = newLTHashState();
|
|
461
424
|
}
|
|
462
425
|
states[name] = state;
|
|
463
|
-
|
|
464
|
-
if (shouldForceSnapshot) {
|
|
465
|
-
forceSnapshotCollections.delete(name);
|
|
466
|
-
}
|
|
467
|
-
logger.info(`resyncing ${name} from v${state.version}${shouldForceSnapshot ? ' (forcing snapshot)' : ''}`);
|
|
426
|
+
logger.info(`resyncing ${name} from v${state.version}`);
|
|
468
427
|
nodes.push({
|
|
469
428
|
tag: 'collection',
|
|
470
429
|
attrs: {
|
|
471
430
|
name,
|
|
472
431
|
version: state.version.toString(),
|
|
473
|
-
// return snapshot if
|
|
474
|
-
return_snapshot: (
|
|
432
|
+
// return snapshot if being synced from scratch
|
|
433
|
+
return_snapshot: (!state.version).toString()
|
|
475
434
|
}
|
|
476
435
|
});
|
|
477
436
|
}
|
|
@@ -497,7 +456,7 @@ export const makeChatsSocket = (config) => {
|
|
|
497
456
|
const { patches, hasMorePatches, snapshot } = decoded[name];
|
|
498
457
|
try {
|
|
499
458
|
if (snapshot) {
|
|
500
|
-
const { state: newState, mutationMap } = await decodeSyncdSnapshot(name, snapshot, getCachedAppStateSyncKey, initialVersionMap[name], appStateMacVerification.snapshot
|
|
459
|
+
const { state: newState, mutationMap } = await decodeSyncdSnapshot(name, snapshot, getCachedAppStateSyncKey, initialVersionMap[name], appStateMacVerification.snapshot);
|
|
501
460
|
states[name] = newState;
|
|
502
461
|
Object.assign(globalMutationMap, mutationMap);
|
|
503
462
|
logger.info(`restored state of ${name} from snapshot to v${newState.version} with mutations`);
|
|
@@ -520,37 +479,19 @@ export const makeChatsSocket = (config) => {
|
|
|
520
479
|
}
|
|
521
480
|
}
|
|
522
481
|
catch (error) {
|
|
482
|
+
// if retry attempts overshoot
|
|
483
|
+
// or key not found
|
|
484
|
+
const isIrrecoverableError = attemptsMap[name] >= MAX_SYNC_ATTEMPTS ||
|
|
485
|
+
error.output?.statusCode === 404 ||
|
|
486
|
+
error.name === 'TypeError';
|
|
487
|
+
logger.info({ name, error: error.stack }, `failed to sync state from version${isIrrecoverableError ? '' : ', removing and trying from scratch'}`);
|
|
488
|
+
await authState.keys.set({ 'app-state-sync-version': { [name]: null } });
|
|
489
|
+
// increment number of retries
|
|
523
490
|
attemptsMap[name] = (attemptsMap[name] || 0) + 1;
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
attempt: attemptsMap[name],
|
|
527
|
-
version: states[name].version,
|
|
528
|
-
statusCode: error.output?.statusCode,
|
|
529
|
-
errorType: error.name,
|
|
530
|
-
error: error.stack
|
|
531
|
-
};
|
|
532
|
-
if (isMissingKeyError(error) && attemptsMap[name] >= MAX_SYNC_ATTEMPTS) {
|
|
533
|
-
// WA Web treats missing keys as "Blocked" — park the collection
|
|
534
|
-
// until the key arrives via APP_STATE_SYNC_KEY_SHARE.
|
|
535
|
-
logger.warn(logData, `${name} blocked on missing key from v${states[name].version}, parking after ${attemptsMap[name]} attempts`);
|
|
536
|
-
blockedCollections.add(name);
|
|
491
|
+
if (isIrrecoverableError) {
|
|
492
|
+
// stop retrying
|
|
537
493
|
collectionsToHandle.delete(name);
|
|
538
494
|
}
|
|
539
|
-
else if (isMissingKeyError(error)) {
|
|
540
|
-
// Retry with a snapshot which may use a different key.
|
|
541
|
-
logger.info(logData, `${name} blocked on missing key from v${states[name].version}, retrying with snapshot`);
|
|
542
|
-
forceSnapshotCollections.add(name);
|
|
543
|
-
}
|
|
544
|
-
else if (isAppStateSyncIrrecoverable(error, attemptsMap[name])) {
|
|
545
|
-
logger.warn(logData, `failed to sync ${name} from v${states[name].version}, giving up`);
|
|
546
|
-
collectionsToHandle.delete(name);
|
|
547
|
-
}
|
|
548
|
-
else {
|
|
549
|
-
logger.info(logData, `failed to sync ${name} from v${states[name].version}, forcing snapshot retry`);
|
|
550
|
-
// force a full snapshot on retry to recover from
|
|
551
|
-
// corrupted local state (e.g. LTHash MAC mismatch)
|
|
552
|
-
forceSnapshotCollections.add(name);
|
|
553
|
-
}
|
|
554
495
|
}
|
|
555
496
|
}
|
|
556
497
|
}
|
|
@@ -565,25 +506,25 @@ export const makeChatsSocket = (config) => {
|
|
|
565
506
|
* type = "preview" for a low res picture
|
|
566
507
|
* type = "image for the high res picture"
|
|
567
508
|
*/
|
|
568
|
-
const profilePictureUrl = async (jid, type = 'image', timeoutMs
|
|
569
|
-
|
|
570
|
-
// WA Web only includes tctoken for user JIDs (not groups/newsletters)
|
|
571
|
-
// and never for own profile pic (Chat model for self has no tcToken).
|
|
572
|
-
// Including tctoken for own JID causes the server to never respond.
|
|
573
|
-
const normalizedJid = jidNormalizedUser(jid);
|
|
574
|
-
const isUserJid = isPnUser(normalizedJid) || isLidUser(normalizedJid);
|
|
575
|
-
const me = authState.creds.me;
|
|
576
|
-
const isSelf = me && (normalizedJid === jidNormalizedUser(me.id) || (me.lid && normalizedJid === jidNormalizedUser(me.lid)));
|
|
577
|
-
let content = baseContent;
|
|
578
|
-
if (shouldIncludeTcToken && serverProps.profilePicPrivacyToken && isUserJid && !isSelf) {
|
|
579
|
-
content = await buildTcTokenFromJid({
|
|
580
|
-
authState,
|
|
581
|
-
jid: normalizedJid,
|
|
582
|
-
baseContent,
|
|
583
|
-
getLIDForPN
|
|
584
|
-
});
|
|
585
|
-
}
|
|
509
|
+
const profilePictureUrl = async (jid, type = 'image', timeoutMs) => {
|
|
510
|
+
// Lia@Changes 06-02-26 --- Refactor profilePictureUrl() to use tctoken and adjust error handling
|
|
586
511
|
jid = jidNormalizedUser(jid);
|
|
512
|
+
const baseContent = {
|
|
513
|
+
tag: 'picture',
|
|
514
|
+
attrs: {
|
|
515
|
+
type,
|
|
516
|
+
query: 'url'
|
|
517
|
+
}
|
|
518
|
+
};
|
|
519
|
+
const tcTokenData = await authState.keys.get('tctoken', [jid]);
|
|
520
|
+
const tcTokenBuffer = tcTokenData?.[jid]?.token
|
|
521
|
+
if (tcTokenBuffer) {
|
|
522
|
+
baseContent.content = [{
|
|
523
|
+
tag: 'tctoken',
|
|
524
|
+
attrs: {},
|
|
525
|
+
content: tcTokenBuffer
|
|
526
|
+
}];
|
|
527
|
+
}
|
|
587
528
|
const result = await query({
|
|
588
529
|
tag: 'iq',
|
|
589
530
|
attrs: {
|
|
@@ -592,9 +533,16 @@ export const makeChatsSocket = (config) => {
|
|
|
592
533
|
type: 'get',
|
|
593
534
|
xmlns: 'w:profile:picture'
|
|
594
535
|
},
|
|
595
|
-
content
|
|
536
|
+
content: [baseContent]
|
|
596
537
|
}, timeoutMs);
|
|
597
538
|
const child = getBinaryNodeChild(result, 'picture');
|
|
539
|
+
if (!child) {
|
|
540
|
+
throw new Boom('Picture node missing', { statusCode: 404 });
|
|
541
|
+
}
|
|
542
|
+
const status = child.attrs?.status;
|
|
543
|
+
if (status === '404' || status === '204') {
|
|
544
|
+
throw new Boom('Profile picture not set', { statusCode: 404 });
|
|
545
|
+
}
|
|
598
546
|
return child?.attrs?.url;
|
|
599
547
|
};
|
|
600
548
|
const createCallLink = async (type, event, timeoutMs) => {
|
|
@@ -658,12 +606,7 @@ export const makeChatsSocket = (config) => {
|
|
|
658
606
|
* @param tcToken token for subscription, use if present
|
|
659
607
|
*/
|
|
660
608
|
const presenceSubscribe = async (toJid) => {
|
|
661
|
-
|
|
662
|
-
const normalizedToJid = jidNormalizedUser(toJid);
|
|
663
|
-
const isUserJid = isPnUser(normalizedToJid) || isLidUser(normalizedToJid);
|
|
664
|
-
const tcTokenContent = isUserJid
|
|
665
|
-
? await buildTcTokenFromJid({ authState, jid: normalizedToJid, getLIDForPN })
|
|
666
|
-
: undefined;
|
|
609
|
+
const tcTokenContent = await buildTcTokenFromJid({ authState, jid: toJid });
|
|
667
610
|
return sendNode({
|
|
668
611
|
tag: 'presence',
|
|
669
612
|
attrs: {
|
|
@@ -684,8 +627,7 @@ export const makeChatsSocket = (config) => {
|
|
|
684
627
|
if (tag === 'presence') {
|
|
685
628
|
presence = {
|
|
686
629
|
lastKnownPresence: attrs.type === 'unavailable' ? 'unavailable' : 'available',
|
|
687
|
-
lastSeen: attrs.last && attrs.last !== 'deny' ? +attrs.last : undefined
|
|
688
|
-
groupOnlineCount: attrs.count ? +attrs.count : undefined
|
|
630
|
+
lastSeen: attrs.last && attrs.last !== 'deny' ? +attrs.last : undefined
|
|
689
631
|
};
|
|
690
632
|
}
|
|
691
633
|
else if (Array.isArray(content)) {
|
|
@@ -719,7 +661,7 @@ export const makeChatsSocket = (config) => {
|
|
|
719
661
|
logger.debug({ patch: patchCreate }, 'applying app patch');
|
|
720
662
|
await resyncAppState([name], false);
|
|
721
663
|
const { [name]: currentSyncVersion } = await authState.keys.get('app-state-sync-version', [name]);
|
|
722
|
-
initial = currentSyncVersion
|
|
664
|
+
initial = currentSyncVersion || newLTHashState();
|
|
723
665
|
encodeResult = await encodeSyncdPatch(patchCreate, myAppStateKeyId, initial, getAppStateSyncKey);
|
|
724
666
|
const { patch, state } = encodeResult;
|
|
725
667
|
const node = {
|
|
@@ -765,21 +707,22 @@ export const makeChatsSocket = (config) => {
|
|
|
765
707
|
}
|
|
766
708
|
}
|
|
767
709
|
};
|
|
768
|
-
/**
|
|
710
|
+
/** sending non-abt props may fix QR scan fail if server expects */
|
|
769
711
|
const fetchProps = async () => {
|
|
712
|
+
//TODO: implement both protocol 1 and protocol 2 prop fetching, specially for abKey for WM
|
|
770
713
|
const resultNode = await query({
|
|
771
714
|
tag: 'iq',
|
|
772
715
|
attrs: {
|
|
773
716
|
to: S_WHATSAPP_NET,
|
|
774
|
-
xmlns: '
|
|
717
|
+
xmlns: 'w',
|
|
775
718
|
type: 'get'
|
|
776
719
|
},
|
|
777
720
|
content: [
|
|
778
721
|
{
|
|
779
722
|
tag: 'props',
|
|
780
723
|
attrs: {
|
|
781
|
-
protocol: '
|
|
782
|
-
|
|
724
|
+
protocol: '2',
|
|
725
|
+
hash: authState?.creds?.lastPropHash || ''
|
|
783
726
|
}
|
|
784
727
|
}
|
|
785
728
|
]
|
|
@@ -794,20 +737,7 @@ export const makeChatsSocket = (config) => {
|
|
|
794
737
|
}
|
|
795
738
|
props = reduceBinaryNodeToDictionary(propsNode, 'prop');
|
|
796
739
|
}
|
|
797
|
-
|
|
798
|
-
const privacyTokenProp = props['10518'] ?? props['privacy_token_sending_on_all_1_on_1_messages'];
|
|
799
|
-
if (privacyTokenProp !== undefined) {
|
|
800
|
-
serverProps.privacyTokenOn1to1 = privacyTokenProp === 'true' || privacyTokenProp === '1';
|
|
801
|
-
}
|
|
802
|
-
const profilePicProp = props['9666'] ?? props['profile_scraping_privacy_token_in_photo_iq'];
|
|
803
|
-
if (profilePicProp !== undefined) {
|
|
804
|
-
serverProps.profilePicPrivacyToken = profilePicProp === 'true' || profilePicProp === '1';
|
|
805
|
-
}
|
|
806
|
-
const lidIssueProp = props['14303'] ?? props['lid_trusted_token_issue_to_lid'];
|
|
807
|
-
if (lidIssueProp !== undefined) {
|
|
808
|
-
serverProps.lidTrustedTokenIssueToLid = lidIssueProp === 'true' || lidIssueProp === '1';
|
|
809
|
-
}
|
|
810
|
-
logger.debug({ serverProps }, 'fetched props');
|
|
740
|
+
logger.debug('fetched props');
|
|
811
741
|
return props;
|
|
812
742
|
};
|
|
813
743
|
/**
|
|
@@ -947,47 +877,6 @@ export const makeChatsSocket = (config) => {
|
|
|
947
877
|
? shouldSyncHistoryMessage(historyMsg) &&
|
|
948
878
|
PROCESSABLE_HISTORY_TYPES.includes(historyMsg.syncType)
|
|
949
879
|
: false;
|
|
950
|
-
if (historyMsg && shouldProcessHistoryMsg) {
|
|
951
|
-
const syncType = historyMsg.syncType;
|
|
952
|
-
// INITIAL_BOOTSTRAP — fire immediately, no progress check (same as WA Web K function)
|
|
953
|
-
if (syncType === proto.HistorySync.HistorySyncType.INITIAL_BOOTSTRAP &&
|
|
954
|
-
!historySyncStatus.initialBootstrapComplete) {
|
|
955
|
-
historySyncStatus.initialBootstrapComplete = true;
|
|
956
|
-
ev.emit('messaging-history.status', {
|
|
957
|
-
syncType,
|
|
958
|
-
status: 'complete',
|
|
959
|
-
explicit: true
|
|
960
|
-
});
|
|
961
|
-
}
|
|
962
|
-
// RECENT with progress === 100 — explicit completion
|
|
963
|
-
if (syncType === proto.HistorySync.HistorySyncType.RECENT &&
|
|
964
|
-
historyMsg.progress === 100 &&
|
|
965
|
-
!historySyncStatus.recentSyncComplete) {
|
|
966
|
-
historySyncStatus.recentSyncComplete = true;
|
|
967
|
-
clearTimeout(historySyncPausedTimeout);
|
|
968
|
-
historySyncPausedTimeout = undefined;
|
|
969
|
-
ev.emit('messaging-history.status', {
|
|
970
|
-
syncType,
|
|
971
|
-
status: 'complete',
|
|
972
|
-
explicit: true
|
|
973
|
-
});
|
|
974
|
-
}
|
|
975
|
-
// Reset 120s paused timeout on any RECENT chunk (like WA Web's handleChunkProgress)
|
|
976
|
-
if (syncType === proto.HistorySync.HistorySyncType.RECENT && !historySyncStatus.recentSyncComplete) {
|
|
977
|
-
clearTimeout(historySyncPausedTimeout);
|
|
978
|
-
historySyncPausedTimeout = setTimeout(() => {
|
|
979
|
-
if (!historySyncStatus.recentSyncComplete) {
|
|
980
|
-
historySyncStatus.recentSyncComplete = true;
|
|
981
|
-
ev.emit('messaging-history.status', {
|
|
982
|
-
syncType: proto.HistorySync.HistorySyncType.RECENT,
|
|
983
|
-
status: 'paused',
|
|
984
|
-
explicit: false
|
|
985
|
-
});
|
|
986
|
-
}
|
|
987
|
-
historySyncPausedTimeout = undefined;
|
|
988
|
-
}, HISTORY_SYNC_PAUSED_TIMEOUT_MS);
|
|
989
|
-
}
|
|
990
|
-
}
|
|
991
880
|
// State machine: decide on sync and flush
|
|
992
881
|
if (historyMsg && syncState === SyncState.AwaitingInitialSync) {
|
|
993
882
|
if (awaitingSyncTimeout) {
|
|
@@ -1007,8 +896,6 @@ export const makeChatsSocket = (config) => {
|
|
|
1007
896
|
}
|
|
1008
897
|
const doAppStateSync = async () => {
|
|
1009
898
|
if (syncState === SyncState.Syncing) {
|
|
1010
|
-
// All collections will be synced, so clear any blocked ones
|
|
1011
|
-
blockedCollections.clear();
|
|
1012
899
|
logger.info('Doing app state sync');
|
|
1013
900
|
await resyncAppState(ALL_WA_PATCH_NAMES, true);
|
|
1014
901
|
// Sync is complete, go online and flush everything
|
|
@@ -1068,11 +955,6 @@ export const makeChatsSocket = (config) => {
|
|
|
1068
955
|
}
|
|
1069
956
|
});
|
|
1070
957
|
ev.on('connection.update', ({ connection, receivedPendingNotifications }) => {
|
|
1071
|
-
if (connection === 'close') {
|
|
1072
|
-
blockedCollections.clear();
|
|
1073
|
-
clearTimeout(historySyncPausedTimeout);
|
|
1074
|
-
historySyncPausedTimeout = undefined;
|
|
1075
|
-
}
|
|
1076
958
|
if (connection === 'open') {
|
|
1077
959
|
if (fireInitQueries) {
|
|
1078
960
|
executeInitQueries().catch(error => onUnexpectedError(error, 'init queries'));
|
|
@@ -1082,10 +964,6 @@ export const makeChatsSocket = (config) => {
|
|
|
1082
964
|
if (!receivedPendingNotifications || syncState !== SyncState.Connecting) {
|
|
1083
965
|
return;
|
|
1084
966
|
}
|
|
1085
|
-
historySyncStatus.initialBootstrapComplete = false;
|
|
1086
|
-
historySyncStatus.recentSyncComplete = false;
|
|
1087
|
-
clearTimeout(historySyncPausedTimeout);
|
|
1088
|
-
historySyncPausedTimeout = undefined;
|
|
1089
967
|
syncState = SyncState.AwaitingInitialSync;
|
|
1090
968
|
logger.info('Connection is now AwaitingInitialSync, buffering events');
|
|
1091
969
|
ev.buffer();
|
|
@@ -1098,49 +976,19 @@ export const makeChatsSocket = (config) => {
|
|
|
1098
976
|
setTimeout(() => ev.flush(), 0);
|
|
1099
977
|
return;
|
|
1100
978
|
}
|
|
1101
|
-
|
|
1102
|
-
// history sync notifications — the device already has its data.
|
|
1103
|
-
// Skip the 20s wait and go online immediately.
|
|
1104
|
-
if (authState.creds.accountSyncCounter > 0) {
|
|
1105
|
-
logger.info('Reconnection with existing sync data, skipping history sync wait. Transitioning to Online.');
|
|
1106
|
-
syncState = SyncState.Online;
|
|
1107
|
-
setTimeout(() => ev.flush(), 0);
|
|
1108
|
-
return;
|
|
1109
|
-
}
|
|
1110
|
-
logger.info('First connection, awaiting history sync notification with a 20s timeout.');
|
|
979
|
+
logger.info('History sync is enabled, awaiting notification with a 20s timeout.');
|
|
1111
980
|
if (awaitingSyncTimeout) {
|
|
1112
981
|
clearTimeout(awaitingSyncTimeout);
|
|
1113
982
|
}
|
|
1114
983
|
awaitingSyncTimeout = setTimeout(() => {
|
|
1115
984
|
if (syncState === SyncState.AwaitingInitialSync) {
|
|
985
|
+
// TODO: investigate
|
|
1116
986
|
logger.warn('Timeout in AwaitingInitialSync, forcing state to Online and flushing buffer');
|
|
1117
987
|
syncState = SyncState.Online;
|
|
1118
988
|
ev.flush();
|
|
1119
|
-
// Increment so subsequent reconnections skip the 20s wait.
|
|
1120
|
-
// Late-arriving history is still processed via processMessage
|
|
1121
|
-
// regardless of the state machine phase.
|
|
1122
|
-
const accountSyncCounter = (authState.creds.accountSyncCounter || 0) + 1;
|
|
1123
|
-
ev.emit('creds.update', { accountSyncCounter });
|
|
1124
989
|
}
|
|
1125
990
|
}, 20000);
|
|
1126
991
|
});
|
|
1127
|
-
// When an app state sync key arrives (myAppStateKeyId is set) and there are
|
|
1128
|
-
// collections blocked on a missing key, trigger a re-sync for just those collections.
|
|
1129
|
-
// This mirrors WA Web's Blocked → retry-on-key-arrival behavior.
|
|
1130
|
-
ev.on('creds.update', ({ myAppStateKeyId }) => {
|
|
1131
|
-
if (!myAppStateKeyId || blockedCollections.size === 0) {
|
|
1132
|
-
return;
|
|
1133
|
-
}
|
|
1134
|
-
// If we're in the middle of a full sync, doAppStateSync handles all collections
|
|
1135
|
-
if (syncState === SyncState.Syncing) {
|
|
1136
|
-
blockedCollections.clear();
|
|
1137
|
-
return;
|
|
1138
|
-
}
|
|
1139
|
-
const collections = [...blockedCollections];
|
|
1140
|
-
blockedCollections.clear();
|
|
1141
|
-
logger.info({ collections }, 'app state sync key arrived, re-syncing blocked collections');
|
|
1142
|
-
resyncAppState(collections, false).catch(error => onUnexpectedError(error, 'blocked collections resync'));
|
|
1143
|
-
});
|
|
1144
992
|
ev.on('lid-mapping.update', async ({ lid, pn }) => {
|
|
1145
993
|
try {
|
|
1146
994
|
await signalRepository.lidMapping.storeLIDPNMappings([{ lid, pn }]);
|
|
@@ -1162,8 +1010,6 @@ export const makeChatsSocket = (config) => {
|
|
|
1162
1010
|
});
|
|
1163
1011
|
return {
|
|
1164
1012
|
...sock,
|
|
1165
|
-
findUserId,
|
|
1166
|
-
serverProps,
|
|
1167
1013
|
createCallLink,
|
|
1168
1014
|
getBotListV2,
|
|
1169
1015
|
messageMutex,
|
|
@@ -1179,6 +1025,7 @@ export const makeChatsSocket = (config) => {
|
|
|
1179
1025
|
fetchBlocklist,
|
|
1180
1026
|
fetchStatus,
|
|
1181
1027
|
fetchDisappearingDuration,
|
|
1028
|
+
findUserId,
|
|
1182
1029
|
updateProfilePicture,
|
|
1183
1030
|
removeProfilePicture,
|
|
1184
1031
|
updateProfileStatus,
|
|
@@ -1200,7 +1047,6 @@ export const makeChatsSocket = (config) => {
|
|
|
1200
1047
|
cleanDirtyBits,
|
|
1201
1048
|
addOrEditContact,
|
|
1202
1049
|
removeContact,
|
|
1203
|
-
placeholderResendCache,
|
|
1204
1050
|
addLabel,
|
|
1205
1051
|
addChatLabel,
|
|
1206
1052
|
removeChatLabel,
|
|
@@ -1210,5 +1056,4 @@ export const makeChatsSocket = (config) => {
|
|
|
1210
1056
|
addOrEditQuickReply,
|
|
1211
1057
|
removeQuickReply
|
|
1212
1058
|
};
|
|
1213
|
-
};
|
|
1214
|
-
//# sourceMappingURL=chats.js.map
|
|
1059
|
+
};
|