@twsxtd/baileys 7.0.0-rc.9.commit.71ed75d4aae0 → 7.0.0-rc.9.commit.8d1e795328c7

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.
Files changed (139) hide show
  1. package/WAProto/fix-imports.js +22 -18
  2. package/WAProto/index.js +22 -18
  3. package/lib/Defaults/index.d.ts +2 -0
  4. package/lib/Defaults/index.d.ts.map +1 -1
  5. package/lib/Defaults/index.js +3 -1
  6. package/lib/Defaults/index.js.map +1 -1
  7. package/lib/Signal/libsignal.d.ts.map +1 -1
  8. package/lib/Signal/libsignal.js +11 -3
  9. package/lib/Signal/libsignal.js.map +1 -1
  10. package/lib/Signal/lid-mapping.d.ts +3 -1
  11. package/lib/Signal/lid-mapping.d.ts.map +1 -1
  12. package/lib/Signal/lid-mapping.js +34 -14
  13. package/lib/Signal/lid-mapping.js.map +1 -1
  14. package/lib/Socket/Client/websocket.d.ts.map +1 -1
  15. package/lib/Socket/Client/websocket.js +3 -5
  16. package/lib/Socket/Client/websocket.js.map +1 -1
  17. package/lib/Socket/business.d.ts +7 -4
  18. package/lib/Socket/business.d.ts.map +1 -1
  19. package/lib/Socket/chats.d.ts +9 -3
  20. package/lib/Socket/chats.d.ts.map +1 -1
  21. package/lib/Socket/chats.js +252 -35
  22. package/lib/Socket/chats.js.map +1 -1
  23. package/lib/Socket/communities.d.ts +7 -4
  24. package/lib/Socket/communities.d.ts.map +1 -1
  25. package/lib/Socket/groups.d.ts +8 -4
  26. package/lib/Socket/groups.d.ts.map +1 -1
  27. package/lib/Socket/groups.js +20 -0
  28. package/lib/Socket/groups.js.map +1 -1
  29. package/lib/Socket/index.d.ts +7 -4
  30. package/lib/Socket/index.d.ts.map +1 -1
  31. package/lib/Socket/messages-recv.d.ts +7 -4
  32. package/lib/Socket/messages-recv.d.ts.map +1 -1
  33. package/lib/Socket/messages-recv.js +289 -61
  34. package/lib/Socket/messages-recv.js.map +1 -1
  35. package/lib/Socket/messages-send.d.ts +7 -4
  36. package/lib/Socket/messages-send.d.ts.map +1 -1
  37. package/lib/Socket/messages-send.js +106 -12
  38. package/lib/Socket/messages-send.js.map +1 -1
  39. package/lib/Socket/newsletter.d.ts +6 -3
  40. package/lib/Socket/newsletter.d.ts.map +1 -1
  41. package/lib/Socket/newsletter.js +2 -2
  42. package/lib/Socket/newsletter.js.map +1 -1
  43. package/lib/Socket/socket.d.ts +0 -2
  44. package/lib/Socket/socket.d.ts.map +1 -1
  45. package/lib/Socket/socket.js +15 -8
  46. package/lib/Socket/socket.js.map +1 -1
  47. package/lib/Types/Auth.d.ts +2 -0
  48. package/lib/Types/Auth.d.ts.map +1 -1
  49. package/lib/Types/Call.d.ts +1 -1
  50. package/lib/Types/Call.d.ts.map +1 -1
  51. package/lib/Types/Contact.d.ts +2 -0
  52. package/lib/Types/Contact.d.ts.map +1 -1
  53. package/lib/Types/Events.d.ts +16 -0
  54. package/lib/Types/Events.d.ts.map +1 -1
  55. package/lib/Types/GroupMetadata.d.ts +4 -0
  56. package/lib/Types/GroupMetadata.d.ts.map +1 -1
  57. package/lib/Types/Message.d.ts +15 -2
  58. package/lib/Types/Message.d.ts.map +1 -1
  59. package/lib/Types/Message.js.map +1 -1
  60. package/lib/Types/Newsletter.d.ts +4 -2
  61. package/lib/Types/Newsletter.d.ts.map +1 -1
  62. package/lib/Types/Newsletter.js +4 -2
  63. package/lib/Types/Newsletter.js.map +1 -1
  64. package/lib/Types/Signal.d.ts +1 -1
  65. package/lib/Types/Signal.d.ts.map +1 -1
  66. package/lib/Utils/auth-utils.d.ts +5 -0
  67. package/lib/Utils/auth-utils.d.ts.map +1 -1
  68. package/lib/Utils/auth-utils.js +45 -0
  69. package/lib/Utils/auth-utils.js.map +1 -1
  70. package/lib/Utils/chat-utils.d.ts +30 -0
  71. package/lib/Utils/chat-utils.d.ts.map +1 -1
  72. package/lib/Utils/chat-utils.js +34 -8
  73. package/lib/Utils/chat-utils.js.map +1 -1
  74. package/lib/Utils/companion-reg-client-utils.d.ts +17 -0
  75. package/lib/Utils/companion-reg-client-utils.d.ts.map +1 -0
  76. package/lib/Utils/companion-reg-client-utils.js +41 -0
  77. package/lib/Utils/companion-reg-client-utils.js.map +1 -0
  78. package/lib/Utils/decode-wa-message.d.ts +12 -0
  79. package/lib/Utils/decode-wa-message.d.ts.map +1 -1
  80. package/lib/Utils/decode-wa-message.js +22 -0
  81. package/lib/Utils/decode-wa-message.js.map +1 -1
  82. package/lib/Utils/event-buffer.d.ts +0 -2
  83. package/lib/Utils/event-buffer.d.ts.map +1 -1
  84. package/lib/Utils/event-buffer.js +3 -16
  85. package/lib/Utils/event-buffer.js.map +1 -1
  86. package/lib/Utils/generics.d.ts +1 -0
  87. package/lib/Utils/generics.d.ts.map +1 -1
  88. package/lib/Utils/generics.js +22 -1
  89. package/lib/Utils/generics.js.map +1 -1
  90. package/lib/Utils/history.d.ts.map +1 -1
  91. package/lib/Utils/history.js +11 -9
  92. package/lib/Utils/history.js.map +1 -1
  93. package/lib/Utils/identity-change-handler.d.ts +7 -0
  94. package/lib/Utils/identity-change-handler.d.ts.map +1 -1
  95. package/lib/Utils/identity-change-handler.js +1 -0
  96. package/lib/Utils/identity-change-handler.js.map +1 -1
  97. package/lib/Utils/index.d.ts +1 -0
  98. package/lib/Utils/index.d.ts.map +1 -1
  99. package/lib/Utils/index.js +1 -0
  100. package/lib/Utils/index.js.map +1 -1
  101. package/lib/Utils/message-retry-manager.d.ts +1 -0
  102. package/lib/Utils/message-retry-manager.d.ts.map +1 -1
  103. package/lib/Utils/message-retry-manager.js +10 -0
  104. package/lib/Utils/message-retry-manager.js.map +1 -1
  105. package/lib/Utils/messages-media.js +1 -1
  106. package/lib/Utils/messages-media.js.map +1 -1
  107. package/lib/Utils/messages.d.ts.map +1 -1
  108. package/lib/Utils/messages.js +22 -1
  109. package/lib/Utils/messages.js.map +1 -1
  110. package/lib/Utils/process-message.d.ts.map +1 -1
  111. package/lib/Utils/process-message.js +70 -1
  112. package/lib/Utils/process-message.js.map +1 -1
  113. package/lib/Utils/sync-action-utils.d.ts.map +1 -1
  114. package/lib/Utils/sync-action-utils.js +1 -0
  115. package/lib/Utils/sync-action-utils.js.map +1 -1
  116. package/lib/Utils/tc-token-utils.d.ts +26 -1
  117. package/lib/Utils/tc-token-utils.d.ts.map +1 -1
  118. package/lib/Utils/tc-token-utils.js +149 -4
  119. package/lib/Utils/tc-token-utils.js.map +1 -1
  120. package/lib/WAUSync/Protocols/USyncContactProtocol.d.ts.map +1 -1
  121. package/lib/WAUSync/Protocols/USyncContactProtocol.js +26 -3
  122. package/lib/WAUSync/Protocols/USyncContactProtocol.js.map +1 -1
  123. package/lib/WAUSync/Protocols/USyncUsernameProtocol.d.ts +10 -0
  124. package/lib/WAUSync/Protocols/USyncUsernameProtocol.d.ts.map +1 -0
  125. package/lib/WAUSync/Protocols/USyncUsernameProtocol.js +25 -0
  126. package/lib/WAUSync/Protocols/USyncUsernameProtocol.js.map +1 -0
  127. package/lib/WAUSync/Protocols/index.d.ts +1 -0
  128. package/lib/WAUSync/Protocols/index.d.ts.map +1 -1
  129. package/lib/WAUSync/Protocols/index.js +1 -0
  130. package/lib/WAUSync/Protocols/index.js.map +1 -1
  131. package/lib/WAUSync/USyncQuery.d.ts +1 -0
  132. package/lib/WAUSync/USyncQuery.d.ts.map +1 -1
  133. package/lib/WAUSync/USyncQuery.js +5 -1
  134. package/lib/WAUSync/USyncQuery.js.map +1 -1
  135. package/lib/WAUSync/USyncUser.d.ts +4 -0
  136. package/lib/WAUSync/USyncUser.d.ts.map +1 -1
  137. package/lib/WAUSync/USyncUser.js +8 -0
  138. package/lib/WAUSync/USyncUser.js.map +1 -1
  139. package/package.json +3 -3
@@ -6,6 +6,15 @@ import type { LabelActionBody } from '../Types/Label.js';
6
6
  import { type BinaryNode } from '../WABinary/index.js';
7
7
  import { USyncQuery } from '../WAUSync/index.js';
8
8
  export declare const makeChatsSocket: (config: SocketConfig) => {
9
+ serverProps: {
10
+ /** AB prop 10518: gate tctoken on 1:1 messages. Default true (safe: avoids 463). */
11
+ privacyTokenOn1to1: boolean;
12
+ /** AB prop 9666: gate tctoken on profile picture IQs. WA Web default: true. */
13
+ profilePicPrivacyToken: boolean;
14
+ /** AB prop 14303: issue tctokens to LID instead of PN. WA Web default: false. */
15
+ lidTrustedTokenIssueToLid: boolean;
16
+ };
17
+ end: (error: Error | undefined) => Promise<void>;
9
18
  createCallLink: (type: "audio" | "video", event?: {
10
19
  startTime: number;
11
20
  }, timeoutMs?: number) => Promise<string | undefined>;
@@ -76,8 +85,6 @@ export declare const makeChatsSocket: (config: SocketConfig) => {
76
85
  createBufferedFunction<A extends any[], T>(work: (...args: A) => Promise<T>): (...args: A) => Promise<T>;
77
86
  flush(): boolean;
78
87
  isBuffering(): boolean;
79
- release(): void;
80
- removeAllListeners<T extends keyof import("../Types/index.js").BaileysEventMap>(event?: T): void;
81
88
  };
82
89
  authState: {
83
90
  creds: import("../Types/index.js").AuthenticationCreds;
@@ -92,7 +99,6 @@ export declare const makeChatsSocket: (config: SocketConfig) => {
92
99
  sendRawMessage: (data: Uint8Array | Buffer) => Promise<void>;
93
100
  sendNode: (frame: BinaryNode) => Promise<void>;
94
101
  logout: (msg?: string) => Promise<void>;
95
- end: (error: Error | undefined) => Promise<void>;
96
102
  onUnexpectedError: (err: Error | Boom, msg: string) => void;
97
103
  uploadPreKeys: (count?: number, retryCount?: number) => Promise<void>;
98
104
  uploadPreKeysToServerIfRequired: () => Promise<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"chats.d.ts","sourceRoot":"","sources":["../../src/Socket/chats.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAA;AACjC,OAAO,EAAE,KAAK,EAAE,MAAM,wBAAwB,CAAA;AAE9C,OAAO,KAAK,EACX,WAAW,EAEX,gBAAgB,EAGhB,iBAAiB,EAEjB,YAAY,EAEZ,iBAAiB,EACjB,aAAa,EACb,SAAS,EACT,aAAa,EAEb,UAAU,EACV,kBAAkB,EAClB,sBAAsB,EACtB,sBAAsB,EACtB,oBAAoB,EACpB,cAAc,EACd,mBAAmB,EACnB,MAAM,UAAU,CAAA;AAEjB,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAA;AAC5D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAA;AAiBrD,OAAO,EACN,KAAK,UAAU,EAOf,MAAM,aAAa,CAAA;AACpB,OAAO,EAAE,UAAU,EAAa,MAAM,YAAY,CAAA;AAIlD,eAAO,MAAM,eAAe,GAAI,QAAQ,YAAY;2BA0lBf,OAAO,GAAG,OAAO,UAAU;QAAE,SAAS,EAAE,MAAM,CAAA;KAAE,cAAc,MAAM;;;;;;;;;;;;;;;;;;4BAqHnE,aAAa;+BA/FV,UAAU,UAAU,MAAM;+BA8C1B,MAAM;6BA3FR,MAAM,SAAQ,SAAS,GAAG,OAAO,cAA0B,MAAM;;2BAzYnE,MAAM,EAAE;yCAaM,MAAM,EAAE;gCAepD,MAAM,WACF,aAAa,eACT;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE;gCAmCN,MAAM;kCA0BJ,MAAM;8BAkBV,MAAM;6BAkBP,MAAM,UAAU,OAAO,GAAG,SAAS;2DAshBX,OAAO;+BAvuB7B,kBAAkB;mCAJd,sBAAsB;mCAQtB,cAAc;iCAIhB,oBAAoB;yCAIZ,cAAc;iCAItB,cAAc;uCAIR,mBAAmB;oCAItB,sBAAsB;8CAIZ,MAAM;8BAyMtB,MAAM,KAAG,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC;;sBA0fxD,gBAAgB,OAAO,MAAM;2BAxclB,cAAc,GAAG,QAAQ,kBAAkB,MAAM,GAAG,MAAM;4BA2e/D,MAAM,WAAW,KAAK,CAAC,eAAe,CAAC,cAAc;yBAYxD,MAAM;oBAYX,MAAM,UAAU,eAAe;wBAc3B,MAAM,WAAW,MAAM;2BAcpB,MAAM,WAAW,MAAM;2BAcvB,MAAM,aAAa,MAAM,WAAW,MAAM;8BAevC,MAAM,aAAa,MAAM,WAAW,MAAM;gBAhGxD,MAAM,YAAY;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,OAAO,CAAA;KAAE,EAAE,QAAQ,OAAO;sCA+G7C,gBAAgB;kCAYpB,MAAM;;;;;;0DAj9BzC,GAAG,0BACD,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAsrCP,CAAA"}
1
+ {"version":3,"file":"chats.d.ts","sourceRoot":"","sources":["../../src/Socket/chats.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAA;AACjC,OAAO,EAAE,KAAK,EAAE,MAAM,wBAAwB,CAAA;AAE9C,OAAO,KAAK,EACX,WAAW,EAEX,gBAAgB,EAGhB,iBAAiB,EAEjB,YAAY,EAEZ,iBAAiB,EACjB,aAAa,EACb,SAAS,EACT,aAAa,EAEb,UAAU,EACV,kBAAkB,EAClB,sBAAsB,EACtB,sBAAsB,EACtB,oBAAoB,EACpB,cAAc,EACd,mBAAmB,EACnB,MAAM,UAAU,CAAA;AAEjB,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAA;AAC5D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAA;AAsBrD,OAAO,EACN,KAAK,UAAU,EAWf,MAAM,aAAa,CAAA;AACpB,OAAO,EAAE,UAAU,EAAa,MAAM,YAAY,CAAA;AAGlD,eAAO,MAAM,eAAe,GAAI,QAAQ,YAAY;;QA6BlD,oFAAoF;;QAEpF,+EAA+E;;QAE/E,iFAAiF;;;iBAg3C9D,KAAK,GAAG,SAAS;2BA7qBD,OAAO,GAAG,OAAO,UAAU;QAAE,SAAS,EAAE,MAAM,CAAA;KAAE,cAAc,MAAM;;;;;;;;;;;;;;;;;;4BA0HnE,aAAa;+BApGV,UAAU,UAAU,MAAM;+BA8C1B,MAAM;6BA5GR,MAAM,SAAQ,SAAS,GAAG,OAAO,cAA0B,MAAM;;2BAhdnE,MAAM,EAAE;yCAaM,MAAM,EAAE;gCAepD,MAAM,WACF,aAAa,eACT;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE;gCAmCN,MAAM;kCA0BJ,MAAM;8BAkBV,MAAM;6BAkBP,MAAM,UAAU,OAAO,GAAG,SAAS;2DAkoBX,OAAO;+BAn1B7B,kBAAkB;mCAJd,sBAAsB;mCAQtB,cAAc;iCAIhB,oBAAoB;yCAIZ,cAAc;iCAItB,cAAc;uCAIR,mBAAmB;oCAItB,sBAAsB;8CAIZ,MAAM;8BAqPtB,MAAM,KAAG,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC;;sBA0jBxD,gBAAgB,OAAO,MAAM;2BAxgBlB,cAAc,GAAG,QAAQ,kBAAkB,MAAM,GAAG,MAAM;4BA2iB/D,MAAM,WAAW,KAAK,CAAC,eAAe,CAAC,cAAc;yBAYxD,MAAM;oBAYX,MAAM,UAAU,eAAe;wBAc3B,MAAM,WAAW,MAAM;2BAcpB,MAAM,WAAW,MAAM;2BAcvB,MAAM,aAAa,MAAM,WAAW,MAAM;8BAevC,MAAM,aAAa,MAAM,WAAW,MAAM;gBAhGxD,MAAM,YAAY;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,OAAO,CAAA;KAAE,EAAE,QAAQ,OAAO;sCA+G7C,gBAAgB;kCAYpB,MAAM;;;;;;0DAvnClB,GAAG,0BACzB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAo8CN,CAAA"}
@@ -1,22 +1,31 @@
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, bindCleanupOnConnectionClose, 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
- import { getBinaryNodeChild, getBinaryNodeChildren, jidDecode, jidNormalizedUser, reduceBinaryNodeToDictionary, S_WHATSAPP_NET } from '../WABinary/index.js';
11
+ import { getBinaryNodeChild, getBinaryNodeChildren, isLidUser, isPnUser, jidDecode, jidNormalizedUser, isHostedLidUser, isHostedPnUser, 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
  export const makeChatsSocket = (config) => {
16
15
  const { logger, markOnlineOnConnect, fireInitQueries, appStateMacVerification, shouldIgnoreJid, shouldSyncHistoryMessage, getMessage } = config;
17
16
  const sock = makeSocket(config);
18
17
  const { ev, ws, authState, generateMessageTag, sendNode, query, signalRepository, onUnexpectedError, sendUnifiedSession } = sock;
18
+ const getLIDForPN = signalRepository.lidMapping.getLIDForPN.bind(signalRepository.lidMapping);
19
19
  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
+ };
20
29
  let syncState = SyncState.Connecting;
21
30
  /** this mutex ensures that messages are processed in order */
22
31
  const messageMutex = makeMutex();
@@ -28,14 +37,44 @@ export const makeChatsSocket = (config) => {
28
37
  const notificationMutex = makeMutex();
29
38
  // Timeout for AwaitingInitialSync state
30
39
  let awaitingSyncTimeout;
31
- const placeholderResendCache = config.placeholderResendCache ||
32
- new NodeCache({
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 internalPlaceholderResendCache = config.placeholderResendCache
50
+ ? undefined
51
+ : new NodeCache({
33
52
  stdTTL: DEFAULT_CACHE_TTLS.MSG_RETRY, // 1 hour
34
53
  useClones: false
35
54
  });
55
+ const placeholderResendCache = config.placeholderResendCache || internalPlaceholderResendCache;
36
56
  if (!config.placeholderResendCache) {
37
57
  config.placeholderResendCache = placeholderResendCache;
38
58
  }
59
+ let cleanedUp = false;
60
+ const cleanupInternalResources = async () => {
61
+ if (cleanedUp) {
62
+ return;
63
+ }
64
+ cleanedUp = true;
65
+ if (awaitingSyncTimeout) {
66
+ clearTimeout(awaitingSyncTimeout);
67
+ awaitingSyncTimeout = undefined;
68
+ }
69
+ if (historySyncPausedTimeout) {
70
+ clearTimeout(historySyncPausedTimeout);
71
+ historySyncPausedTimeout = undefined;
72
+ }
73
+ if (internalPlaceholderResendCache) {
74
+ await Promise.resolve(internalPlaceholderResendCache.close());
75
+ }
76
+ };
77
+ bindCleanupOnConnectionClose(ev, cleanupInternalResources);
39
78
  /** helper function to fetch the given app state sync key */
40
79
  const getAppStateSyncKey = async (keyId) => {
41
80
  const { [keyId]: key } = await authState.keys.get('app-state-sync-key', [keyId]);
@@ -258,6 +297,42 @@ export const makeChatsSocket = (config) => {
258
297
  return getBinaryNodeChildren(listNode, 'item').map(n => n.attrs.jid);
259
298
  };
260
299
  const updateBlockStatus = async (jid, action) => {
300
+ const normalizedJid = jidNormalizedUser(jid);
301
+ let lid;
302
+ let pn_jid;
303
+ if (isLidUser(normalizedJid) || isHostedLidUser(normalizedJid)) {
304
+ lid = normalizedJid;
305
+ if (action === 'block') {
306
+ const pn = await signalRepository.lidMapping.getPNForLID(normalizedJid);
307
+ if (!pn) {
308
+ throw new Boom(`Unable to resolve PN JID for LID: ${jid}`, { statusCode: 400 });
309
+ }
310
+ pn_jid = jidNormalizedUser(pn);
311
+ }
312
+ }
313
+ else if (isPnUser(normalizedJid) || isHostedPnUser(normalizedJid)) {
314
+ const mapped = await signalRepository.lidMapping.getLIDForPN(normalizedJid);
315
+ if (!mapped) {
316
+ throw new Boom(`Unable to resolve LID for PN JID: ${jid}`, { statusCode: 400 });
317
+ }
318
+ lid = mapped;
319
+ if (action === 'block') {
320
+ pn_jid = jidNormalizedUser(normalizedJid);
321
+ }
322
+ }
323
+ else {
324
+ throw new Boom(`Invalid jid: ${jid}`, { statusCode: 400 });
325
+ }
326
+ const itemAttrs = {
327
+ action,
328
+ jid: lid
329
+ };
330
+ if (action === 'block') {
331
+ if (!pn_jid) {
332
+ throw new Boom(`pn_jid required for block: ${jid}`, { statusCode: 400 });
333
+ }
334
+ itemAttrs.pn_jid = pn_jid;
335
+ }
261
336
  await query({
262
337
  tag: 'iq',
263
338
  attrs: {
@@ -268,10 +343,7 @@ export const makeChatsSocket = (config) => {
268
343
  content: [
269
344
  {
270
345
  tag: 'item',
271
- attrs: {
272
- action,
273
- jid
274
- }
346
+ attrs: itemAttrs
275
347
  }
276
348
  ]
277
349
  });
@@ -370,6 +442,9 @@ export const makeChatsSocket = (config) => {
370
442
  const collectionsToHandle = new Set(collections);
371
443
  // in case something goes wrong -- ensure we don't enter a loop that cannot be exited from
372
444
  const attemptsMap = {};
445
+ // collections that failed and need a full snapshot on retry
446
+ // mirrors WA Web's ErrorFatal -> force snapshot behavior
447
+ const forceSnapshotCollections = new Set();
373
448
  // keep executing till all collections are done
374
449
  // sometimes a single patch request will not return all the patches (God knows why)
375
450
  // so we fetch till they're all done (this is determined by the "has_more_patches" flag)
@@ -380,6 +455,7 @@ export const makeChatsSocket = (config) => {
380
455
  const result = await authState.keys.get('app-state-sync-version', [name]);
381
456
  let state = result[name];
382
457
  if (state) {
458
+ state = ensureLTHashStateVersion(state);
383
459
  if (typeof initialVersionMap[name] === 'undefined') {
384
460
  initialVersionMap[name] = state.version;
385
461
  }
@@ -388,14 +464,18 @@ export const makeChatsSocket = (config) => {
388
464
  state = newLTHashState();
389
465
  }
390
466
  states[name] = state;
391
- logger.info(`resyncing ${name} from v${state.version}`);
467
+ const shouldForceSnapshot = forceSnapshotCollections.has(name);
468
+ if (shouldForceSnapshot) {
469
+ forceSnapshotCollections.delete(name);
470
+ }
471
+ logger.info(`resyncing ${name} from v${state.version}${shouldForceSnapshot ? ' (forcing snapshot)' : ''}`);
392
472
  nodes.push({
393
473
  tag: 'collection',
394
474
  attrs: {
395
475
  name,
396
476
  version: state.version.toString(),
397
- // return snapshot if being synced from scratch
398
- return_snapshot: (!state.version).toString()
477
+ // return snapshot if syncing from scratch or forcing after a failed attempt
478
+ return_snapshot: (shouldForceSnapshot || !state.version).toString()
399
479
  }
400
480
  });
401
481
  }
@@ -444,19 +524,37 @@ export const makeChatsSocket = (config) => {
444
524
  }
445
525
  }
446
526
  catch (error) {
447
- // if retry attempts overshoot
448
- // or key not found
449
- const isIrrecoverableError = attemptsMap[name] >= MAX_SYNC_ATTEMPTS ||
450
- error.output?.statusCode === 404 ||
451
- error.name === 'TypeError';
452
- logger.info({ name, error: error.stack }, `failed to sync state from version${isIrrecoverableError ? '' : ', removing and trying from scratch'}`);
453
- await authState.keys.set({ 'app-state-sync-version': { [name]: null } });
454
- // increment number of retries
455
527
  attemptsMap[name] = (attemptsMap[name] || 0) + 1;
456
- if (isIrrecoverableError) {
457
- // stop retrying
528
+ const logData = {
529
+ name,
530
+ attempt: attemptsMap[name],
531
+ version: states[name].version,
532
+ statusCode: error.output?.statusCode,
533
+ errorType: error.name,
534
+ error: error.stack
535
+ };
536
+ if (isMissingKeyError(error) && attemptsMap[name] >= MAX_SYNC_ATTEMPTS) {
537
+ // WA Web treats missing keys as "Blocked" — park the collection
538
+ // until the key arrives via APP_STATE_SYNC_KEY_SHARE.
539
+ logger.warn(logData, `${name} blocked on missing key from v${states[name].version}, parking after ${attemptsMap[name]} attempts`);
540
+ blockedCollections.add(name);
541
+ collectionsToHandle.delete(name);
542
+ }
543
+ else if (isMissingKeyError(error)) {
544
+ // Retry with a snapshot which may use a different key.
545
+ logger.info(logData, `${name} blocked on missing key from v${states[name].version}, retrying with snapshot`);
546
+ forceSnapshotCollections.add(name);
547
+ }
548
+ else if (isAppStateSyncIrrecoverable(error, attemptsMap[name])) {
549
+ logger.warn(logData, `failed to sync ${name} from v${states[name].version}, giving up`);
458
550
  collectionsToHandle.delete(name);
459
551
  }
552
+ else {
553
+ logger.info(logData, `failed to sync ${name} from v${states[name].version}, forcing snapshot retry`);
554
+ // force a full snapshot on retry to recover from
555
+ // corrupted local state (e.g. LTHash MAC mismatch)
556
+ forceSnapshotCollections.add(name);
557
+ }
460
558
  }
461
559
  }
462
560
  }
@@ -473,7 +571,22 @@ export const makeChatsSocket = (config) => {
473
571
  */
474
572
  const profilePictureUrl = async (jid, type = 'preview', timeoutMs) => {
475
573
  const baseContent = [{ tag: 'picture', attrs: { type, query: 'url' } }];
476
- const tcTokenContent = await buildTcTokenFromJid({ authState, jid, baseContent });
574
+ // WA Web only includes tctoken for user JIDs (not groups/newsletters)
575
+ // and never for own profile pic (Chat model for self has no tcToken).
576
+ // Including tctoken for own JID causes the server to never respond.
577
+ const normalizedJid = jidNormalizedUser(jid);
578
+ const isUserJid = isPnUser(normalizedJid) || isLidUser(normalizedJid);
579
+ const me = authState.creds.me;
580
+ const isSelf = me && (normalizedJid === jidNormalizedUser(me.id) || (me.lid && normalizedJid === jidNormalizedUser(me.lid)));
581
+ let content = baseContent;
582
+ if (serverProps.profilePicPrivacyToken && isUserJid && !isSelf) {
583
+ content = await buildTcTokenFromJid({
584
+ authState,
585
+ jid: normalizedJid,
586
+ baseContent,
587
+ getLIDForPN
588
+ });
589
+ }
477
590
  jid = jidNormalizedUser(jid);
478
591
  const result = await query({
479
592
  tag: 'iq',
@@ -483,7 +596,7 @@ export const makeChatsSocket = (config) => {
483
596
  type: 'get',
484
597
  xmlns: 'w:profile:picture'
485
598
  },
486
- content: tcTokenContent
599
+ content
487
600
  }, timeoutMs);
488
601
  const child = getBinaryNodeChild(result, 'picture');
489
602
  return child?.attrs?.url;
@@ -549,7 +662,12 @@ export const makeChatsSocket = (config) => {
549
662
  * @param tcToken token for subscription, use if present
550
663
  */
551
664
  const presenceSubscribe = async (toJid) => {
552
- const tcTokenContent = await buildTcTokenFromJid({ authState, jid: toJid });
665
+ // Only include tctoken for user JIDs groups/newsletters don't use tctokens
666
+ const normalizedToJid = jidNormalizedUser(toJid);
667
+ const isUserJid = isPnUser(normalizedToJid) || isLidUser(normalizedToJid);
668
+ const tcTokenContent = isUserJid
669
+ ? await buildTcTokenFromJid({ authState, jid: normalizedToJid, getLIDForPN })
670
+ : undefined;
553
671
  return sendNode({
554
672
  tag: 'presence',
555
673
  attrs: {
@@ -604,7 +722,7 @@ export const makeChatsSocket = (config) => {
604
722
  logger.debug({ patch: patchCreate }, 'applying app patch');
605
723
  await resyncAppState([name], false);
606
724
  const { [name]: currentSyncVersion } = await authState.keys.get('app-state-sync-version', [name]);
607
- initial = currentSyncVersion || newLTHashState();
725
+ initial = currentSyncVersion ? ensureLTHashStateVersion(currentSyncVersion) : newLTHashState();
608
726
  encodeResult = await encodeSyncdPatch(patchCreate, myAppStateKeyId, initial, getAppStateSyncKey);
609
727
  const { patch, state } = encodeResult;
610
728
  const node = {
@@ -650,22 +768,21 @@ export const makeChatsSocket = (config) => {
650
768
  }
651
769
  }
652
770
  };
653
- /** sending non-abt props may fix QR scan fail if server expects */
771
+ /** fetch AB props */
654
772
  const fetchProps = async () => {
655
- //TODO: implement both protocol 1 and protocol 2 prop fetching, specially for abKey for WM
656
773
  const resultNode = await query({
657
774
  tag: 'iq',
658
775
  attrs: {
659
776
  to: S_WHATSAPP_NET,
660
- xmlns: 'w',
777
+ xmlns: 'abt',
661
778
  type: 'get'
662
779
  },
663
780
  content: [
664
781
  {
665
782
  tag: 'props',
666
783
  attrs: {
667
- protocol: '2',
668
- hash: authState?.creds?.lastPropHash || ''
784
+ protocol: '1',
785
+ ...(authState?.creds?.lastPropHash ? { hash: authState.creds.lastPropHash } : {})
669
786
  }
670
787
  }
671
788
  ]
@@ -680,7 +797,20 @@ export const makeChatsSocket = (config) => {
680
797
  }
681
798
  props = reduceBinaryNodeToDictionary(propsNode, 'prop');
682
799
  }
683
- logger.debug('fetched props');
800
+ // Extract protocol-relevant AB props (only the ones we need)
801
+ const privacyTokenProp = props['10518'] ?? props['privacy_token_sending_on_all_1_on_1_messages'];
802
+ if (privacyTokenProp !== undefined) {
803
+ serverProps.privacyTokenOn1to1 = privacyTokenProp === 'true' || privacyTokenProp === '1';
804
+ }
805
+ const profilePicProp = props['9666'] ?? props['profile_scraping_privacy_token_in_photo_iq'];
806
+ if (profilePicProp !== undefined) {
807
+ serverProps.profilePicPrivacyToken = profilePicProp === 'true' || profilePicProp === '1';
808
+ }
809
+ const lidIssueProp = props['14303'] ?? props['lid_trusted_token_issue_to_lid'];
810
+ if (lidIssueProp !== undefined) {
811
+ serverProps.lidTrustedTokenIssueToLid = lidIssueProp === 'true' || lidIssueProp === '1';
812
+ }
813
+ logger.debug({ serverProps }, 'fetched props');
684
814
  return props;
685
815
  };
686
816
  /**
@@ -820,6 +950,47 @@ export const makeChatsSocket = (config) => {
820
950
  ? shouldSyncHistoryMessage(historyMsg) &&
821
951
  PROCESSABLE_HISTORY_TYPES.includes(historyMsg.syncType)
822
952
  : false;
953
+ if (historyMsg && shouldProcessHistoryMsg) {
954
+ const syncType = historyMsg.syncType;
955
+ // INITIAL_BOOTSTRAP — fire immediately, no progress check (same as WA Web K function)
956
+ if (syncType === proto.HistorySync.HistorySyncType.INITIAL_BOOTSTRAP &&
957
+ !historySyncStatus.initialBootstrapComplete) {
958
+ historySyncStatus.initialBootstrapComplete = true;
959
+ ev.emit('messaging-history.status', {
960
+ syncType,
961
+ status: 'complete',
962
+ explicit: true
963
+ });
964
+ }
965
+ // RECENT with progress === 100 — explicit completion
966
+ if (syncType === proto.HistorySync.HistorySyncType.RECENT &&
967
+ historyMsg.progress === 100 &&
968
+ !historySyncStatus.recentSyncComplete) {
969
+ historySyncStatus.recentSyncComplete = true;
970
+ clearTimeout(historySyncPausedTimeout);
971
+ historySyncPausedTimeout = undefined;
972
+ ev.emit('messaging-history.status', {
973
+ syncType,
974
+ status: 'complete',
975
+ explicit: true
976
+ });
977
+ }
978
+ // Reset 120s paused timeout on any RECENT chunk (like WA Web's handleChunkProgress)
979
+ if (syncType === proto.HistorySync.HistorySyncType.RECENT && !historySyncStatus.recentSyncComplete) {
980
+ clearTimeout(historySyncPausedTimeout);
981
+ historySyncPausedTimeout = setTimeout(() => {
982
+ if (!historySyncStatus.recentSyncComplete) {
983
+ historySyncStatus.recentSyncComplete = true;
984
+ ev.emit('messaging-history.status', {
985
+ syncType: proto.HistorySync.HistorySyncType.RECENT,
986
+ status: 'paused',
987
+ explicit: false
988
+ });
989
+ }
990
+ historySyncPausedTimeout = undefined;
991
+ }, HISTORY_SYNC_PAUSED_TIMEOUT_MS);
992
+ }
993
+ }
823
994
  // State machine: decide on sync and flush
824
995
  if (historyMsg && syncState === SyncState.AwaitingInitialSync) {
825
996
  if (awaitingSyncTimeout) {
@@ -839,6 +1010,8 @@ export const makeChatsSocket = (config) => {
839
1010
  }
840
1011
  const doAppStateSync = async () => {
841
1012
  if (syncState === SyncState.Syncing) {
1013
+ // All collections will be synced, so clear any blocked ones
1014
+ blockedCollections.clear();
842
1015
  logger.info('Doing app state sync');
843
1016
  await resyncAppState(ALL_WA_PATCH_NAMES, true);
844
1017
  // Sync is complete, go online and flush everything
@@ -898,6 +1071,11 @@ export const makeChatsSocket = (config) => {
898
1071
  }
899
1072
  });
900
1073
  ev.on('connection.update', ({ connection, receivedPendingNotifications }) => {
1074
+ if (connection === 'close') {
1075
+ blockedCollections.clear();
1076
+ clearTimeout(historySyncPausedTimeout);
1077
+ historySyncPausedTimeout = undefined;
1078
+ }
901
1079
  if (connection === 'open') {
902
1080
  if (fireInitQueries) {
903
1081
  executeInitQueries().catch(error => onUnexpectedError(error, 'init queries'));
@@ -907,6 +1085,10 @@ export const makeChatsSocket = (config) => {
907
1085
  if (!receivedPendingNotifications || syncState !== SyncState.Connecting) {
908
1086
  return;
909
1087
  }
1088
+ historySyncStatus.initialBootstrapComplete = false;
1089
+ historySyncStatus.recentSyncComplete = false;
1090
+ clearTimeout(historySyncPausedTimeout);
1091
+ historySyncPausedTimeout = undefined;
910
1092
  syncState = SyncState.AwaitingInitialSync;
911
1093
  logger.info('Connection is now AwaitingInitialSync, buffering events');
912
1094
  ev.buffer();
@@ -919,19 +1101,49 @@ export const makeChatsSocket = (config) => {
919
1101
  setTimeout(() => ev.flush(), 0);
920
1102
  return;
921
1103
  }
922
- logger.info('History sync is enabled, awaiting notification with a 20s timeout.');
1104
+ // On reconnection (accountSyncCounter > 0), the server does not push
1105
+ // history sync notifications — the device already has its data.
1106
+ // Skip the 20s wait and go online immediately.
1107
+ if (authState.creds.accountSyncCounter > 0) {
1108
+ logger.info('Reconnection with existing sync data, skipping history sync wait. Transitioning to Online.');
1109
+ syncState = SyncState.Online;
1110
+ setTimeout(() => ev.flush(), 0);
1111
+ return;
1112
+ }
1113
+ logger.info('First connection, awaiting history sync notification with a 20s timeout.');
923
1114
  if (awaitingSyncTimeout) {
924
1115
  clearTimeout(awaitingSyncTimeout);
925
1116
  }
926
1117
  awaitingSyncTimeout = setTimeout(() => {
927
1118
  if (syncState === SyncState.AwaitingInitialSync) {
928
- // TODO: investigate
929
1119
  logger.warn('Timeout in AwaitingInitialSync, forcing state to Online and flushing buffer');
930
1120
  syncState = SyncState.Online;
931
1121
  ev.flush();
1122
+ // Increment so subsequent reconnections skip the 20s wait.
1123
+ // Late-arriving history is still processed via processMessage
1124
+ // regardless of the state machine phase.
1125
+ const accountSyncCounter = (authState.creds.accountSyncCounter || 0) + 1;
1126
+ ev.emit('creds.update', { accountSyncCounter });
932
1127
  }
933
1128
  }, 20000);
934
1129
  });
1130
+ // When an app state sync key arrives (myAppStateKeyId is set) and there are
1131
+ // collections blocked on a missing key, trigger a re-sync for just those collections.
1132
+ // This mirrors WA Web's Blocked → retry-on-key-arrival behavior.
1133
+ ev.on('creds.update', ({ myAppStateKeyId }) => {
1134
+ if (!myAppStateKeyId || blockedCollections.size === 0) {
1135
+ return;
1136
+ }
1137
+ // If we're in the middle of a full sync, doAppStateSync handles all collections
1138
+ if (syncState === SyncState.Syncing) {
1139
+ blockedCollections.clear();
1140
+ return;
1141
+ }
1142
+ const collections = [...blockedCollections];
1143
+ blockedCollections.clear();
1144
+ logger.info({ collections }, 'app state sync key arrived, re-syncing blocked collections');
1145
+ resyncAppState(collections, false).catch(error => onUnexpectedError(error, 'blocked collections resync'));
1146
+ });
935
1147
  ev.on('lid-mapping.update', async ({ lid, pn }) => {
936
1148
  try {
937
1149
  await signalRepository.lidMapping.storeLIDPNMappings([{ lid, pn }]);
@@ -942,6 +1154,11 @@ export const makeChatsSocket = (config) => {
942
1154
  });
943
1155
  return {
944
1156
  ...sock,
1157
+ serverProps,
1158
+ end: async (error) => {
1159
+ await cleanupInternalResources();
1160
+ await sock.end(error);
1161
+ },
945
1162
  createCallLink,
946
1163
  getBotListV2,
947
1164
  messageMutex,