@itsliaaa/baileys 0.1.33 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +109 -58
- package/lib/Defaults/index.js +2 -0
- package/lib/Socket/chats.js +185 -52
- package/lib/Socket/groups.js +6 -0
- package/lib/Socket/messages-recv.js +230 -53
- package/lib/Socket/messages-send.js +78 -7
- package/lib/Utils/chat-utils.js +34 -7
- package/lib/Utils/decode-wa-message.js +14 -0
- package/lib/Utils/event-buffer.js +2 -0
- package/lib/Utils/generics.js +9 -0
- package/lib/Utils/history.js +11 -9
- package/lib/Utils/identity-change-handler.js +1 -0
- package/lib/Utils/messages-media.js +1 -1
- package/lib/Utils/messages.js +14 -0
- package/lib/Utils/process-message.js +53 -1
- package/lib/Utils/rich-message-utils.js +1 -1
- package/lib/Utils/sync-action-utils.js +1 -0
- package/lib/Utils/tc-token-utils.js +151 -5
- package/lib/Utils/use-single-file-auth-state.js +19 -26
- package/lib/WAUSync/Protocols/USyncContactProtocol.js +26 -3
- package/lib/WAUSync/Protocols/USyncUsernameProtocol.js +22 -0
- package/lib/WAUSync/Protocols/index.js +2 -1
- package/lib/WAUSync/USyncQuery.js +5 -1
- package/lib/WAUSync/USyncUser.js +8 -0
- package/package.json +1 -1
package/lib/Socket/chats.js
CHANGED
|
@@ -1,24 +1,33 @@
|
|
|
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, PROCESSABLE_HISTORY_TYPES } from '../Defaults/index.js';
|
|
4
|
+
import { DEFAULT_CACHE_TTLS, HISTORY_SYNC_PAUSED_TIMEOUT_MS, 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, extractSyncdPatches, generateProfilePicture, getHistoryMsg, newLTHashState, processSyncAction } from '../Utils/index.js';
|
|
7
|
+
import { chatModificationToAppPatch, decodePatches, decodeSyncdSnapshot, encodeSyncdPatch, ensureLTHashStateVersion, extractSyncdPatches, generateProfilePicture, getHistoryMsg, isAppStateSyncIrrecoverable, isMissingKeyError, MAX_SYNC_ATTEMPTS, 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
11
|
import { getBinaryNodeChild, getBinaryNodeChildren, isPnUser, isLidUser, isHostedLidUser, isHostedPnUser, 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
14
|
// Lia@Note 08-02-26 --- I know it's not efficient for RSS ಥ‿ಥ
|
|
16
15
|
const USER_ID_CACHE = new Map();
|
|
17
16
|
export const makeChatsSocket = (config) => {
|
|
18
17
|
const { logger, markOnlineOnConnect, fireInitQueries, appStateMacVerification, shouldIgnoreJid, shouldSyncHistoryMessage, getMessage } = config;
|
|
19
18
|
const sock = makeSocket(config);
|
|
20
19
|
const { ev, ws, authState, generateMessageTag, sendNode, query, signalRepository, onUnexpectedError, sendUnifiedSession, registerSocketEndHandler } = sock;
|
|
20
|
+
const getLIDForPN = signalRepository.lidMapping.getLIDForPN.bind(signalRepository.lidMapping);
|
|
21
21
|
let privacySettings;
|
|
22
|
+
/** Server-assigned AB props for protocol behavior. */
|
|
23
|
+
const serverProps = {
|
|
24
|
+
/** AB prop 10518: gate tctoken on 1:1 messages. Default true (safe: avoids 463). */
|
|
25
|
+
privacyTokenOn1to1: true,
|
|
26
|
+
/** AB prop 9666: gate tctoken on profile picture IQs. WA Web default: true. */
|
|
27
|
+
profilePicPrivacyToken: true,
|
|
28
|
+
/** AB prop 14303: issue tctokens to LID instead of PN. WA Web default: false. */
|
|
29
|
+
lidTrustedTokenIssueToLid: false
|
|
30
|
+
};
|
|
22
31
|
let syncState = SyncState.Connecting;
|
|
23
32
|
/** this mutex ensures that messages are processed in order */
|
|
24
33
|
const messageMutex = makeMutex();
|
|
@@ -30,6 +39,15 @@ export const makeChatsSocket = (config) => {
|
|
|
30
39
|
const notificationMutex = makeMutex();
|
|
31
40
|
// Timeout for AwaitingInitialSync state
|
|
32
41
|
let awaitingSyncTimeout;
|
|
42
|
+
// In-memory history sync completion tracking (resets on reconnection)
|
|
43
|
+
const historySyncStatus = {
|
|
44
|
+
initialBootstrapComplete: false,
|
|
45
|
+
recentSyncComplete: false
|
|
46
|
+
};
|
|
47
|
+
let historySyncPausedTimeout;
|
|
48
|
+
// Collections blocked on missing app state sync keys (mirrors WA Web's "Blocked" state).
|
|
49
|
+
// When a key arrives via APP_STATE_SYNC_KEY_SHARE, these are re-synced.
|
|
50
|
+
const blockedCollections = new Set();
|
|
33
51
|
const placeholderResendCache = config.placeholderResendCache ||
|
|
34
52
|
new NodeCache({
|
|
35
53
|
stdTTL: DEFAULT_CACHE_TTLS.MSG_RETRY, // 1 hour
|
|
@@ -438,6 +456,9 @@ export const makeChatsSocket = (config) => {
|
|
|
438
456
|
const collectionsToHandle = new Set(collections);
|
|
439
457
|
// in case something goes wrong -- ensure we don't enter a loop that cannot be exited from
|
|
440
458
|
const attemptsMap = {};
|
|
459
|
+
// collections that failed and need a full snapshot on retry
|
|
460
|
+
// mirrors WA Web's ErrorFatal -> force snapshot behavior
|
|
461
|
+
const forceSnapshotCollections = new Set();
|
|
441
462
|
// keep executing till all collections are done
|
|
442
463
|
// sometimes a single patch request will not return all the patches (God knows why)
|
|
443
464
|
// so we fetch till they're all done (this is determined by the "has_more_patches" flag)
|
|
@@ -448,6 +469,7 @@ export const makeChatsSocket = (config) => {
|
|
|
448
469
|
const result = await authState.keys.get('app-state-sync-version', [name]);
|
|
449
470
|
let state = result[name];
|
|
450
471
|
if (state) {
|
|
472
|
+
state = ensureLTHashStateVersion(state);
|
|
451
473
|
if (typeof initialVersionMap[name] === 'undefined') {
|
|
452
474
|
initialVersionMap[name] = state.version;
|
|
453
475
|
}
|
|
@@ -456,14 +478,18 @@ export const makeChatsSocket = (config) => {
|
|
|
456
478
|
state = newLTHashState();
|
|
457
479
|
}
|
|
458
480
|
states[name] = state;
|
|
459
|
-
|
|
481
|
+
const shouldForceSnapshot = forceSnapshotCollections.has(name);
|
|
482
|
+
if (shouldForceSnapshot) {
|
|
483
|
+
forceSnapshotCollections.delete(name);
|
|
484
|
+
}
|
|
485
|
+
logger.info(`resyncing ${name} from v${state.version}${shouldForceSnapshot ? ' (forcing snapshot)' : ''}`);
|
|
460
486
|
nodes.push({
|
|
461
487
|
tag: 'collection',
|
|
462
488
|
attrs: {
|
|
463
489
|
name,
|
|
464
490
|
version: state.version.toString(),
|
|
465
|
-
// return snapshot if
|
|
466
|
-
return_snapshot: (!state.version).toString()
|
|
491
|
+
// return snapshot if syncing from scratch or forcing after a failed attempt
|
|
492
|
+
return_snapshot: (shouldForceSnapshot || !state.version).toString()
|
|
467
493
|
}
|
|
468
494
|
});
|
|
469
495
|
}
|
|
@@ -512,19 +538,37 @@ export const makeChatsSocket = (config) => {
|
|
|
512
538
|
}
|
|
513
539
|
}
|
|
514
540
|
catch (error) {
|
|
515
|
-
// if retry attempts overshoot
|
|
516
|
-
// or key not found
|
|
517
|
-
const isIrrecoverableError = attemptsMap[name] >= MAX_SYNC_ATTEMPTS ||
|
|
518
|
-
error.output?.statusCode === 404 ||
|
|
519
|
-
error.name === 'TypeError';
|
|
520
|
-
logger.info({ name, error: error.stack }, `failed to sync state from version${isIrrecoverableError ? '' : ', removing and trying from scratch'}`);
|
|
521
|
-
await authState.keys.set({ 'app-state-sync-version': { [name]: null } });
|
|
522
|
-
// increment number of retries
|
|
523
541
|
attemptsMap[name] = (attemptsMap[name] || 0) + 1;
|
|
524
|
-
|
|
525
|
-
|
|
542
|
+
const logData = {
|
|
543
|
+
name,
|
|
544
|
+
attempt: attemptsMap[name],
|
|
545
|
+
version: states[name].version,
|
|
546
|
+
statusCode: error.output?.statusCode,
|
|
547
|
+
errorType: error.name,
|
|
548
|
+
error: error.stack
|
|
549
|
+
};
|
|
550
|
+
if (isMissingKeyError(error) && attemptsMap[name] >= MAX_SYNC_ATTEMPTS) {
|
|
551
|
+
// WA Web treats missing keys as "Blocked" — park the collection
|
|
552
|
+
// until the key arrives via APP_STATE_SYNC_KEY_SHARE.
|
|
553
|
+
logger.warn(logData, `${name} blocked on missing key from v${states[name].version}, parking after ${attemptsMap[name]} attempts`);
|
|
554
|
+
blockedCollections.add(name);
|
|
555
|
+
collectionsToHandle.delete(name);
|
|
556
|
+
}
|
|
557
|
+
else if (isMissingKeyError(error)) {
|
|
558
|
+
// Retry with a snapshot which may use a different key.
|
|
559
|
+
logger.info(logData, `${name} blocked on missing key from v${states[name].version}, retrying with snapshot`);
|
|
560
|
+
forceSnapshotCollections.add(name);
|
|
561
|
+
}
|
|
562
|
+
else if (isAppStateSyncIrrecoverable(error, attemptsMap[name])) {
|
|
563
|
+
logger.warn(logData, `failed to sync ${name} from v${states[name].version}, giving up`);
|
|
526
564
|
collectionsToHandle.delete(name);
|
|
527
565
|
}
|
|
566
|
+
else {
|
|
567
|
+
logger.info(logData, `failed to sync ${name} from v${states[name].version}, forcing snapshot retry`);
|
|
568
|
+
// force a full snapshot on retry to recover from
|
|
569
|
+
// corrupted local state (e.g. LTHash MAC mismatch)
|
|
570
|
+
forceSnapshotCollections.add(name);
|
|
571
|
+
}
|
|
528
572
|
}
|
|
529
573
|
}
|
|
530
574
|
}
|
|
@@ -540,42 +584,34 @@ export const makeChatsSocket = (config) => {
|
|
|
540
584
|
* type = "image for the high res picture"
|
|
541
585
|
*/
|
|
542
586
|
const profilePictureUrl = async (jid, type = 'image', timeoutMs) => {
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
}];
|
|
587
|
+
const baseContent = [{ tag: 'picture', attrs: { type, query: 'url' } }];
|
|
588
|
+
// WA Web only includes tctoken for user JIDs (not groups/newsletters)
|
|
589
|
+
// and never for own profile pic (Chat model for self has no tcToken).
|
|
590
|
+
// Including tctoken for own JID causes the server to never respond.
|
|
591
|
+
const normalizedJid = jidNormalizedUser(jid);
|
|
592
|
+
const isUserJid = isPnUser(normalizedJid) || isLidUser(normalizedJid);
|
|
593
|
+
const me = authState.creds.me;
|
|
594
|
+
const isSelf = me && (normalizedJid === jidNormalizedUser(me.id) || (me.lid && normalizedJid === jidNormalizedUser(me.lid)));
|
|
595
|
+
let content = baseContent;
|
|
596
|
+
if (serverProps.profilePicPrivacyToken && isUserJid && !isSelf) {
|
|
597
|
+
content = await buildTcTokenFromJid({
|
|
598
|
+
authState,
|
|
599
|
+
jid: normalizedJid,
|
|
600
|
+
baseContent,
|
|
601
|
+
getLIDForPN
|
|
602
|
+
});
|
|
560
603
|
}
|
|
561
604
|
const result = await query({
|
|
562
605
|
tag: 'iq',
|
|
563
606
|
attrs: {
|
|
564
|
-
target:
|
|
607
|
+
target: normalizedJid,
|
|
565
608
|
to: S_WHATSAPP_NET,
|
|
566
609
|
type: 'get',
|
|
567
610
|
xmlns: 'w:profile:picture'
|
|
568
611
|
},
|
|
569
|
-
content
|
|
612
|
+
content
|
|
570
613
|
}, timeoutMs);
|
|
571
614
|
const child = getBinaryNodeChild(result, 'picture');
|
|
572
|
-
if (!child) {
|
|
573
|
-
throw new Boom('Picture node missing', { statusCode: 404 });
|
|
574
|
-
}
|
|
575
|
-
const status = child.attrs?.status;
|
|
576
|
-
if (status === '404' || status === '204') {
|
|
577
|
-
throw new Boom('Profile picture not set', { statusCode: 404 });
|
|
578
|
-
}
|
|
579
615
|
return child?.attrs?.url;
|
|
580
616
|
};
|
|
581
617
|
const createCallLink = async (type, event, timeoutMs) => {
|
|
@@ -639,7 +675,12 @@ export const makeChatsSocket = (config) => {
|
|
|
639
675
|
* @param tcToken token for subscription, use if present
|
|
640
676
|
*/
|
|
641
677
|
const presenceSubscribe = async (toJid) => {
|
|
642
|
-
|
|
678
|
+
// Only include tctoken for user JIDs — groups/newsletters don't use tctokens
|
|
679
|
+
const normalizedToJid = jidNormalizedUser(toJid);
|
|
680
|
+
const isUserJid = isPnUser(normalizedToJid) || isLidUser(normalizedToJid);
|
|
681
|
+
const tcTokenContent = isUserJid
|
|
682
|
+
? await buildTcTokenFromJid({ authState, jid: normalizedToJid, getLIDForPN })
|
|
683
|
+
: undefined;
|
|
643
684
|
return sendNode({
|
|
644
685
|
tag: 'presence',
|
|
645
686
|
attrs: {
|
|
@@ -694,7 +735,7 @@ export const makeChatsSocket = (config) => {
|
|
|
694
735
|
logger.debug({ patch: patchCreate }, 'applying app patch');
|
|
695
736
|
await resyncAppState([name], false);
|
|
696
737
|
const { [name]: currentSyncVersion } = await authState.keys.get('app-state-sync-version', [name]);
|
|
697
|
-
initial = currentSyncVersion
|
|
738
|
+
initial = currentSyncVersion ? ensureLTHashStateVersion(currentSyncVersion) : newLTHashState();
|
|
698
739
|
encodeResult = await encodeSyncdPatch(patchCreate, myAppStateKeyId, initial, getAppStateSyncKey);
|
|
699
740
|
const { patch, state } = encodeResult;
|
|
700
741
|
const node = {
|
|
@@ -740,22 +781,21 @@ export const makeChatsSocket = (config) => {
|
|
|
740
781
|
}
|
|
741
782
|
}
|
|
742
783
|
};
|
|
743
|
-
/**
|
|
784
|
+
/** fetch AB props */
|
|
744
785
|
const fetchProps = async () => {
|
|
745
|
-
//TODO: implement both protocol 1 and protocol 2 prop fetching, specially for abKey for WM
|
|
746
786
|
const resultNode = await query({
|
|
747
787
|
tag: 'iq',
|
|
748
788
|
attrs: {
|
|
749
789
|
to: S_WHATSAPP_NET,
|
|
750
|
-
xmlns: '
|
|
790
|
+
xmlns: 'abt',
|
|
751
791
|
type: 'get'
|
|
752
792
|
},
|
|
753
793
|
content: [
|
|
754
794
|
{
|
|
755
795
|
tag: 'props',
|
|
756
796
|
attrs: {
|
|
757
|
-
protocol: '
|
|
758
|
-
hash: authState
|
|
797
|
+
protocol: '1',
|
|
798
|
+
...(authState?.creds?.lastPropHash ? { hash: authState.creds.lastPropHash } : {})
|
|
759
799
|
}
|
|
760
800
|
}
|
|
761
801
|
]
|
|
@@ -770,7 +810,20 @@ export const makeChatsSocket = (config) => {
|
|
|
770
810
|
}
|
|
771
811
|
props = reduceBinaryNodeToDictionary(propsNode, 'prop');
|
|
772
812
|
}
|
|
773
|
-
|
|
813
|
+
// Extract protocol-relevant AB props (only the ones we need)
|
|
814
|
+
const privacyTokenProp = props['10518'] ?? props['privacy_token_sending_on_all_1_on_1_messages'];
|
|
815
|
+
if (privacyTokenProp !== undefined) {
|
|
816
|
+
serverProps.privacyTokenOn1to1 = privacyTokenProp === 'true' || privacyTokenProp === '1';
|
|
817
|
+
}
|
|
818
|
+
const profilePicProp = props['9666'] ?? props['profile_scraping_privacy_token_in_photo_iq'];
|
|
819
|
+
if (profilePicProp !== undefined) {
|
|
820
|
+
serverProps.profilePicPrivacyToken = profilePicProp === 'true' || profilePicProp === '1';
|
|
821
|
+
}
|
|
822
|
+
const lidIssueProp = props['14303'] ?? props['lid_trusted_token_issue_to_lid'];
|
|
823
|
+
if (lidIssueProp !== undefined) {
|
|
824
|
+
serverProps.lidTrustedTokenIssueToLid = lidIssueProp === 'true' || lidIssueProp === '1';
|
|
825
|
+
}
|
|
826
|
+
logger.debug({ serverProps }, 'fetched props');
|
|
774
827
|
return props;
|
|
775
828
|
};
|
|
776
829
|
/**
|
|
@@ -910,6 +963,44 @@ export const makeChatsSocket = (config) => {
|
|
|
910
963
|
? shouldSyncHistoryMessage(historyMsg) &&
|
|
911
964
|
PROCESSABLE_HISTORY_TYPES.includes(historyMsg.syncType)
|
|
912
965
|
: false;
|
|
966
|
+
if (historyMsg && shouldProcessHistoryMsg) {
|
|
967
|
+
const syncType = historyMsg.syncType;
|
|
968
|
+
// INITIAL_BOOTSTRAP — fire immediately, no progress check (same as WA Web K function)
|
|
969
|
+
if (syncType === proto.HistorySync.HistorySyncType.INITIAL_BOOTSTRAP && !historySyncStatus.initialBootstrapComplete) {
|
|
970
|
+
historySyncStatus.initialBootstrapComplete = true;
|
|
971
|
+
ev.emit('messaging-history.status', {
|
|
972
|
+
syncType,
|
|
973
|
+
status: 'complete',
|
|
974
|
+
explicit: true
|
|
975
|
+
});
|
|
976
|
+
}
|
|
977
|
+
// RECENT with progress === 100 — explicit completion
|
|
978
|
+
if (syncType === proto.HistorySync.HistorySyncType.RECENT && historyMsg.progress === 100 && !historySyncStatus.recentSyncComplete) {
|
|
979
|
+
historySyncStatus.recentSyncComplete = true;
|
|
980
|
+
clearTimeout(historySyncPausedTimeout);
|
|
981
|
+
historySyncPausedTimeout = undefined;
|
|
982
|
+
ev.emit('messaging-history.status', {
|
|
983
|
+
syncType,
|
|
984
|
+
status: 'complete',
|
|
985
|
+
explicit: true
|
|
986
|
+
});
|
|
987
|
+
}
|
|
988
|
+
// Reset 120s paused timeout on any RECENT chunk (like WA Web's handleChunkProgress)
|
|
989
|
+
if (syncType === proto.HistorySync.HistorySyncType.RECENT && !historySyncStatus.recentSyncComplete) {
|
|
990
|
+
clearTimeout(historySyncPausedTimeout);
|
|
991
|
+
historySyncPausedTimeout = setTimeout(() => {
|
|
992
|
+
if (!historySyncStatus.recentSyncComplete) {
|
|
993
|
+
historySyncStatus.recentSyncComplete = true;
|
|
994
|
+
ev.emit('messaging-history.status', {
|
|
995
|
+
syncType: proto.HistorySync.HistorySyncType.RECENT,
|
|
996
|
+
status: 'paused',
|
|
997
|
+
explicit: false
|
|
998
|
+
});
|
|
999
|
+
}
|
|
1000
|
+
historySyncPausedTimeout = undefined;
|
|
1001
|
+
}, HISTORY_SYNC_PAUSED_TIMEOUT_MS);
|
|
1002
|
+
}
|
|
1003
|
+
}
|
|
913
1004
|
// State machine: decide on sync and flush
|
|
914
1005
|
if (historyMsg && syncState === SyncState.AwaitingInitialSync) {
|
|
915
1006
|
if (awaitingSyncTimeout) {
|
|
@@ -929,6 +1020,8 @@ export const makeChatsSocket = (config) => {
|
|
|
929
1020
|
}
|
|
930
1021
|
const doAppStateSync = async () => {
|
|
931
1022
|
if (syncState === SyncState.Syncing) {
|
|
1023
|
+
// All collections will be synced, so clear any blocked ones
|
|
1024
|
+
blockedCollections.clear();
|
|
932
1025
|
logger.info('Doing app state sync');
|
|
933
1026
|
await resyncAppState(ALL_WA_PATCH_NAMES, true);
|
|
934
1027
|
// Sync is complete, go online and flush everything
|
|
@@ -988,6 +1081,11 @@ export const makeChatsSocket = (config) => {
|
|
|
988
1081
|
}
|
|
989
1082
|
});
|
|
990
1083
|
ev.on('connection.update', ({ connection, receivedPendingNotifications }) => {
|
|
1084
|
+
if (connection === 'close') {
|
|
1085
|
+
blockedCollections.clear();
|
|
1086
|
+
clearTimeout(historySyncPausedTimeout);
|
|
1087
|
+
historySyncPausedTimeout = undefined;
|
|
1088
|
+
}
|
|
991
1089
|
if (connection === 'open') {
|
|
992
1090
|
if (fireInitQueries) {
|
|
993
1091
|
executeInitQueries().catch(error => onUnexpectedError(error, 'init queries'));
|
|
@@ -997,6 +1095,10 @@ export const makeChatsSocket = (config) => {
|
|
|
997
1095
|
if (!receivedPendingNotifications || syncState !== SyncState.Connecting) {
|
|
998
1096
|
return;
|
|
999
1097
|
}
|
|
1098
|
+
historySyncStatus.initialBootstrapComplete = false;
|
|
1099
|
+
historySyncStatus.recentSyncComplete = false;
|
|
1100
|
+
clearTimeout(historySyncPausedTimeout);
|
|
1101
|
+
historySyncPausedTimeout = undefined;
|
|
1000
1102
|
syncState = SyncState.AwaitingInitialSync;
|
|
1001
1103
|
logger.info('Connection is now AwaitingInitialSync, buffering events');
|
|
1002
1104
|
ev.buffer();
|
|
@@ -1009,19 +1111,49 @@ export const makeChatsSocket = (config) => {
|
|
|
1009
1111
|
setTimeout(() => ev.flush(), 0);
|
|
1010
1112
|
return;
|
|
1011
1113
|
}
|
|
1012
|
-
|
|
1114
|
+
// On reconnection (accountSyncCounter > 0), the server does not push
|
|
1115
|
+
// history sync notifications — the device already has its data.
|
|
1116
|
+
// Skip the 20s wait and go online immediately.
|
|
1117
|
+
if (authState.creds.accountSyncCounter > 0) {
|
|
1118
|
+
logger.info('Reconnection with existing sync data, skipping history sync wait. Transitioning to Online.');
|
|
1119
|
+
syncState = SyncState.Online;
|
|
1120
|
+
setTimeout(() => ev.flush(), 0);
|
|
1121
|
+
return;
|
|
1122
|
+
}
|
|
1123
|
+
logger.info('First connection, awaiting history sync notification with a 20s timeout.');
|
|
1013
1124
|
if (awaitingSyncTimeout) {
|
|
1014
1125
|
clearTimeout(awaitingSyncTimeout);
|
|
1015
1126
|
}
|
|
1016
1127
|
awaitingSyncTimeout = setTimeout(() => {
|
|
1017
1128
|
if (syncState === SyncState.AwaitingInitialSync) {
|
|
1018
|
-
// TODO: investigate
|
|
1019
1129
|
logger.warn('Timeout in AwaitingInitialSync, forcing state to Online and flushing buffer');
|
|
1020
1130
|
syncState = SyncState.Online;
|
|
1021
1131
|
ev.flush();
|
|
1132
|
+
// Increment so subsequent reconnections skip the 20s wait.
|
|
1133
|
+
// Late-arriving history is still processed via processMessage
|
|
1134
|
+
// regardless of the state machine phase.
|
|
1135
|
+
const accountSyncCounter = (authState.creds.accountSyncCounter || 0) + 1;
|
|
1136
|
+
ev.emit('creds.update', { accountSyncCounter });
|
|
1022
1137
|
}
|
|
1023
1138
|
}, 20000);
|
|
1024
1139
|
});
|
|
1140
|
+
// When an app state sync key arrives (myAppStateKeyId is set) and there are
|
|
1141
|
+
// collections blocked on a missing key, trigger a re-sync for just those collections.
|
|
1142
|
+
// This mirrors WA Web's Blocked → retry-on-key-arrival behavior.
|
|
1143
|
+
ev.on('creds.update', ({ myAppStateKeyId }) => {
|
|
1144
|
+
if (!myAppStateKeyId || blockedCollections.size === 0) {
|
|
1145
|
+
return;
|
|
1146
|
+
}
|
|
1147
|
+
// If we're in the middle of a full sync, doAppStateSync handles all collections
|
|
1148
|
+
if (syncState === SyncState.Syncing) {
|
|
1149
|
+
blockedCollections.clear();
|
|
1150
|
+
return;
|
|
1151
|
+
}
|
|
1152
|
+
const collections = [...blockedCollections];
|
|
1153
|
+
blockedCollections.clear();
|
|
1154
|
+
logger.info({ collections }, 'app state sync key arrived, re-syncing blocked collections');
|
|
1155
|
+
resyncAppState(collections, false).catch(error => onUnexpectedError(error, 'blocked collections resync'));
|
|
1156
|
+
});
|
|
1025
1157
|
ev.on('lid-mapping.update', async ({ lid, pn }) => {
|
|
1026
1158
|
try {
|
|
1027
1159
|
await signalRepository.lidMapping.storeLIDPNMappings([{ lid, pn }]);
|
|
@@ -1043,6 +1175,7 @@ export const makeChatsSocket = (config) => {
|
|
|
1043
1175
|
});
|
|
1044
1176
|
return {
|
|
1045
1177
|
...sock,
|
|
1178
|
+
serverProps,
|
|
1046
1179
|
createCallLink,
|
|
1047
1180
|
getBotListV2,
|
|
1048
1181
|
messageMutex,
|
package/lib/Socket/groups.js
CHANGED
|
@@ -277,11 +277,13 @@ export const extractGroupMetadata = (result) => {
|
|
|
277
277
|
let descId;
|
|
278
278
|
let descOwner;
|
|
279
279
|
let descOwnerPn;
|
|
280
|
+
let descOwnerUsername;
|
|
280
281
|
let descTime;
|
|
281
282
|
if (descChild) {
|
|
282
283
|
desc = getBinaryNodeChildString(descChild, 'body');
|
|
283
284
|
descOwner = descChild.attrs.participant ? jidNormalizedUser(descChild.attrs.participant) : undefined;
|
|
284
285
|
descOwnerPn = descChild.attrs.participant_pn ? jidNormalizedUser(descChild.attrs.participant_pn) : undefined;
|
|
286
|
+
descOwnerUsername = descChild.attrs.participant_username || undefined;
|
|
285
287
|
descTime = +descChild.attrs.t;
|
|
286
288
|
descId = descChild.attrs.id;
|
|
287
289
|
}
|
|
@@ -295,16 +297,19 @@ export const extractGroupMetadata = (result) => {
|
|
|
295
297
|
subject: group.attrs.subject,
|
|
296
298
|
subjectOwner: group.attrs.s_o,
|
|
297
299
|
subjectOwnerPn: group.attrs.s_o_pn,
|
|
300
|
+
subjectOwnerUsername: group.attrs.s_o_username,
|
|
298
301
|
subjectTime: +group.attrs.s_t,
|
|
299
302
|
size: group.attrs.size ? +group.attrs.size : getBinaryNodeChildren(group, 'participant').length,
|
|
300
303
|
creation: +group.attrs.creation,
|
|
301
304
|
owner: group.attrs.creator ? jidNormalizedUser(group.attrs.creator) : undefined,
|
|
302
305
|
ownerPn: group.attrs.creator_pn ? jidNormalizedUser(group.attrs.creator_pn) : undefined,
|
|
306
|
+
ownerUsername: group.attrs.creator_username || undefined,
|
|
303
307
|
owner_country_code: group.attrs.creator_country_code,
|
|
304
308
|
desc,
|
|
305
309
|
descId,
|
|
306
310
|
descOwner,
|
|
307
311
|
descOwnerPn,
|
|
312
|
+
descOwnerUsername,
|
|
308
313
|
descTime,
|
|
309
314
|
linkedParent: getBinaryNodeChild(group, 'linked_parent')?.attrs.jid || undefined,
|
|
310
315
|
restrict: !!getBinaryNodeChild(group, 'locked'),
|
|
@@ -319,6 +324,7 @@ export const extractGroupMetadata = (result) => {
|
|
|
319
324
|
id: attrs.jid,
|
|
320
325
|
phoneNumber: isLidUser(attrs.jid) && isPnUser(attrs.phone_number) ? attrs.phone_number : undefined,
|
|
321
326
|
lid: isPnUser(attrs.jid) && isLidUser(attrs.lid) ? attrs.lid : undefined,
|
|
327
|
+
username: attrs.participant_username || attrs.username || undefined,
|
|
322
328
|
admin: (attrs.type || null)
|
|
323
329
|
};
|
|
324
330
|
}),
|