@fer2809fl/baileys 7.0.2 → 7.0.4

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 (222) hide show
  1. package/README.md +100 -20
  2. package/lib/Defaults/index.js +2 -1
  3. package/lib/Signal/libsignal.js +19 -0
  4. package/lib/Socket/business.d.ts +213 -532
  5. package/lib/Socket/business.js +359 -401
  6. package/lib/Socket/chats.d.ts +16 -2
  7. package/lib/Socket/chats.js +247 -40
  8. package/lib/Socket/groups.d.ts +16 -0
  9. package/lib/Socket/groups.js +101 -18
  10. package/lib/Socket/messages-recv.js +18 -1
  11. package/lib/Socket/newsletter.js +3 -3
  12. package/lib/Socket/socket.js +50 -6
  13. package/lib/Types/Newsletter.d.ts +13 -4
  14. package/lib/Types/Newsletter.js +12 -2
  15. package/lib/Utils/chat-utils.d.ts +34 -4
  16. package/lib/Utils/chat-utils.js +80 -20
  17. package/lib/Utils/companion-reg-client-utils.d.ts +17 -0
  18. package/lib/Utils/companion-reg-client-utils.js +35 -0
  19. package/lib/Utils/event-buffer.js +49 -1
  20. package/lib/Utils/index.js +1 -0
  21. package/lib/Utils/message-retry-manager.js +40 -0
  22. package/lib/WAUSync/Protocols/USyncUsernameProtocol.d.ts +10 -0
  23. package/lib/WAUSync/Protocols/USyncUsernameProtocol.js +25 -0
  24. package/lib/WAUSync/Protocols/index.js +1 -0
  25. package/lib/WAUSync/USyncQuery.js +5 -1
  26. package/package.json +6 -6
  27. package/lib/Defaults/index.d.ts.map +0 -1
  28. package/lib/Defaults/index.js.map +0 -1
  29. package/lib/Signal/Group/ciphertext-message.d.ts.map +0 -1
  30. package/lib/Signal/Group/ciphertext-message.js.map +0 -1
  31. package/lib/Signal/Group/group-session-builder.d.ts.map +0 -1
  32. package/lib/Signal/Group/group-session-builder.js.map +0 -1
  33. package/lib/Signal/Group/group_cipher.d.ts.map +0 -1
  34. package/lib/Signal/Group/group_cipher.js.map +0 -1
  35. package/lib/Signal/Group/index.d.ts.map +0 -1
  36. package/lib/Signal/Group/index.js.map +0 -1
  37. package/lib/Signal/Group/keyhelper.d.ts.map +0 -1
  38. package/lib/Signal/Group/keyhelper.js.map +0 -1
  39. package/lib/Signal/Group/sender-chain-key.d.ts.map +0 -1
  40. package/lib/Signal/Group/sender-chain-key.js.map +0 -1
  41. package/lib/Signal/Group/sender-key-distribution-message.d.ts.map +0 -1
  42. package/lib/Signal/Group/sender-key-distribution-message.js.map +0 -1
  43. package/lib/Signal/Group/sender-key-message.d.ts.map +0 -1
  44. package/lib/Signal/Group/sender-key-message.js.map +0 -1
  45. package/lib/Signal/Group/sender-key-name.d.ts.map +0 -1
  46. package/lib/Signal/Group/sender-key-name.js.map +0 -1
  47. package/lib/Signal/Group/sender-key-record.d.ts.map +0 -1
  48. package/lib/Signal/Group/sender-key-record.js.map +0 -1
  49. package/lib/Signal/Group/sender-key-state.d.ts.map +0 -1
  50. package/lib/Signal/Group/sender-key-state.js.map +0 -1
  51. package/lib/Signal/Group/sender-message-key.d.ts.map +0 -1
  52. package/lib/Signal/Group/sender-message-key.js.map +0 -1
  53. package/lib/Signal/libsignal.d.ts.map +0 -1
  54. package/lib/Signal/libsignal.js.map +0 -1
  55. package/lib/Signal/lid-mapping.d.ts.map +0 -1
  56. package/lib/Signal/lid-mapping.js.map +0 -1
  57. package/lib/Socket/Client/index.d.ts.map +0 -1
  58. package/lib/Socket/Client/index.js.map +0 -1
  59. package/lib/Socket/Client/types.d.ts.map +0 -1
  60. package/lib/Socket/Client/types.js.map +0 -1
  61. package/lib/Socket/Client/websocket.d.ts.map +0 -1
  62. package/lib/Socket/Client/websocket.js.map +0 -1
  63. package/lib/Socket/business.d.ts.map +0 -1
  64. package/lib/Socket/business.js.map +0 -1
  65. package/lib/Socket/chats.d.ts.map +0 -1
  66. package/lib/Socket/chats.js.map +0 -1
  67. package/lib/Socket/communities.d.ts.map +0 -1
  68. package/lib/Socket/communities.js.map +0 -1
  69. package/lib/Socket/dugong.d.ts.map +0 -1
  70. package/lib/Socket/dugong.js.map +0 -1
  71. package/lib/Socket/groups.d.ts.map +0 -1
  72. package/lib/Socket/groups.js.map +0 -1
  73. package/lib/Socket/index.d.ts.map +0 -1
  74. package/lib/Socket/index.js.map +0 -1
  75. package/lib/Socket/messages-recv.d.ts.map +0 -1
  76. package/lib/Socket/messages-recv.js.map +0 -1
  77. package/lib/Socket/messages-send.d.ts.map +0 -1
  78. package/lib/Socket/messages-send.js.map +0 -1
  79. package/lib/Socket/mex.d.ts.map +0 -1
  80. package/lib/Socket/mex.js.map +0 -1
  81. package/lib/Socket/newsletter.d.ts.map +0 -1
  82. package/lib/Socket/newsletter.js.map +0 -1
  83. package/lib/Socket/socket.d.ts.map +0 -1
  84. package/lib/Socket/socket.js.map +0 -1
  85. package/lib/Types/Auth.d.ts.map +0 -1
  86. package/lib/Types/Auth.js.map +0 -1
  87. package/lib/Types/Bussines.d.ts.map +0 -1
  88. package/lib/Types/Bussines.js.map +0 -1
  89. package/lib/Types/Call.d.ts.map +0 -1
  90. package/lib/Types/Call.js.map +0 -1
  91. package/lib/Types/Chat.d.ts.map +0 -1
  92. package/lib/Types/Chat.js.map +0 -1
  93. package/lib/Types/Contact.d.ts.map +0 -1
  94. package/lib/Types/Contact.js.map +0 -1
  95. package/lib/Types/Events.d.ts.map +0 -1
  96. package/lib/Types/Events.js.map +0 -1
  97. package/lib/Types/GroupMetadata.d.ts.map +0 -1
  98. package/lib/Types/GroupMetadata.js.map +0 -1
  99. package/lib/Types/Label.d.ts.map +0 -1
  100. package/lib/Types/Label.js.map +0 -1
  101. package/lib/Types/LabelAssociation.d.ts.map +0 -1
  102. package/lib/Types/LabelAssociation.js.map +0 -1
  103. package/lib/Types/Message.d.ts.map +0 -1
  104. package/lib/Types/Message.js.map +0 -1
  105. package/lib/Types/Newsletter.d.ts.map +0 -1
  106. package/lib/Types/Newsletter.js.map +0 -1
  107. package/lib/Types/Product.d.ts.map +0 -1
  108. package/lib/Types/Product.js.map +0 -1
  109. package/lib/Types/Signal.d.ts.map +0 -1
  110. package/lib/Types/Signal.js.map +0 -1
  111. package/lib/Types/Socket.d.ts.map +0 -1
  112. package/lib/Types/Socket.js.map +0 -1
  113. package/lib/Types/State.d.ts.map +0 -1
  114. package/lib/Types/State.js.map +0 -1
  115. package/lib/Types/USync.d.ts.map +0 -1
  116. package/lib/Types/USync.js.map +0 -1
  117. package/lib/Types/index.d.ts.map +0 -1
  118. package/lib/Types/index.js.map +0 -1
  119. package/lib/Utils/auth-utils.d.ts.map +0 -1
  120. package/lib/Utils/auth-utils.js.map +0 -1
  121. package/lib/Utils/browser-utils.d.ts.map +0 -1
  122. package/lib/Utils/browser-utils.js.map +0 -1
  123. package/lib/Utils/business.d.ts.map +0 -1
  124. package/lib/Utils/business.js.map +0 -1
  125. package/lib/Utils/chat-utils.d.ts.map +0 -1
  126. package/lib/Utils/chat-utils.js.map +0 -1
  127. package/lib/Utils/crypto.d.ts.map +0 -1
  128. package/lib/Utils/crypto.js.map +0 -1
  129. package/lib/Utils/decode-wa-message.d.ts.map +0 -1
  130. package/lib/Utils/decode-wa-message.js.map +0 -1
  131. package/lib/Utils/event-buffer.d.ts.map +0 -1
  132. package/lib/Utils/event-buffer.js.map +0 -1
  133. package/lib/Utils/generics.d.ts.map +0 -1
  134. package/lib/Utils/generics.js.map +0 -1
  135. package/lib/Utils/history.d.ts.map +0 -1
  136. package/lib/Utils/history.js.map +0 -1
  137. package/lib/Utils/identity-change-handler.d.ts.map +0 -1
  138. package/lib/Utils/identity-change-handler.js.map +0 -1
  139. package/lib/Utils/index.d.ts.map +0 -1
  140. package/lib/Utils/index.js.map +0 -1
  141. package/lib/Utils/link-preview.d.ts.map +0 -1
  142. package/lib/Utils/link-preview.js.map +0 -1
  143. package/lib/Utils/logger.d.ts.map +0 -1
  144. package/lib/Utils/logger.js.map +0 -1
  145. package/lib/Utils/lt-hash.d.ts.map +0 -1
  146. package/lib/Utils/lt-hash.js.map +0 -1
  147. package/lib/Utils/make-mutex.d.ts.map +0 -1
  148. package/lib/Utils/make-mutex.js.map +0 -1
  149. package/lib/Utils/message-retry-manager.d.ts.map +0 -1
  150. package/lib/Utils/message-retry-manager.js.map +0 -1
  151. package/lib/Utils/messages-media.d.ts.map +0 -1
  152. package/lib/Utils/messages-media.js.map +0 -1
  153. package/lib/Utils/messages.d.ts.map +0 -1
  154. package/lib/Utils/messages.js.map +0 -1
  155. package/lib/Utils/noise-handler.d.ts.map +0 -1
  156. package/lib/Utils/noise-handler.js.map +0 -1
  157. package/lib/Utils/offline-node-processor.d.ts.map +0 -1
  158. package/lib/Utils/offline-node-processor.js.map +0 -1
  159. package/lib/Utils/pre-key-manager.d.ts.map +0 -1
  160. package/lib/Utils/pre-key-manager.js.map +0 -1
  161. package/lib/Utils/process-message.d.ts.map +0 -1
  162. package/lib/Utils/process-message.js.map +0 -1
  163. package/lib/Utils/reporting-utils.d.ts.map +0 -1
  164. package/lib/Utils/reporting-utils.js.map +0 -1
  165. package/lib/Utils/rich-messages.d.ts.map +0 -1
  166. package/lib/Utils/rich-messages.js.map +0 -1
  167. package/lib/Utils/signal.d.ts.map +0 -1
  168. package/lib/Utils/signal.js.map +0 -1
  169. package/lib/Utils/stanza-ack.d.ts.map +0 -1
  170. package/lib/Utils/stanza-ack.js.map +0 -1
  171. package/lib/Utils/sync-action-utils.d.ts.map +0 -1
  172. package/lib/Utils/sync-action-utils.js.map +0 -1
  173. package/lib/Utils/tc-token-utils.d.ts.map +0 -1
  174. package/lib/Utils/tc-token-utils.js.map +0 -1
  175. package/lib/Utils/use-multi-file-auth-state.d.ts.map +0 -1
  176. package/lib/Utils/use-multi-file-auth-state.js.map +0 -1
  177. package/lib/Utils/validate-connection.d.ts.map +0 -1
  178. package/lib/Utils/validate-connection.js.map +0 -1
  179. package/lib/WABinary/constants.d.ts.map +0 -1
  180. package/lib/WABinary/constants.js.map +0 -1
  181. package/lib/WABinary/decode.d.ts.map +0 -1
  182. package/lib/WABinary/decode.js.map +0 -1
  183. package/lib/WABinary/encode.d.ts.map +0 -1
  184. package/lib/WABinary/encode.js.map +0 -1
  185. package/lib/WABinary/generic-utils.d.ts.map +0 -1
  186. package/lib/WABinary/generic-utils.js.map +0 -1
  187. package/lib/WABinary/index.d.ts.map +0 -1
  188. package/lib/WABinary/index.js.map +0 -1
  189. package/lib/WABinary/jid-utils.d.ts.map +0 -1
  190. package/lib/WABinary/jid-utils.js.map +0 -1
  191. package/lib/WABinary/types.d.ts.map +0 -1
  192. package/lib/WABinary/types.js.map +0 -1
  193. package/lib/WAM/BinaryInfo.d.ts.map +0 -1
  194. package/lib/WAM/BinaryInfo.js.map +0 -1
  195. package/lib/WAM/constants.d.ts.map +0 -1
  196. package/lib/WAM/constants.js.map +0 -1
  197. package/lib/WAM/encode.d.ts.map +0 -1
  198. package/lib/WAM/encode.js.map +0 -1
  199. package/lib/WAM/index.d.ts.map +0 -1
  200. package/lib/WAM/index.js.map +0 -1
  201. package/lib/WAUSync/Protocols/USyncContactProtocol.d.ts.map +0 -1
  202. package/lib/WAUSync/Protocols/USyncContactProtocol.js.map +0 -1
  203. package/lib/WAUSync/Protocols/USyncDeviceProtocol.d.ts.map +0 -1
  204. package/lib/WAUSync/Protocols/USyncDeviceProtocol.js.map +0 -1
  205. package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.d.ts.map +0 -1
  206. package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.js.map +0 -1
  207. package/lib/WAUSync/Protocols/USyncStatusProtocol.d.ts.map +0 -1
  208. package/lib/WAUSync/Protocols/USyncStatusProtocol.js.map +0 -1
  209. package/lib/WAUSync/Protocols/UsyncBotProfileProtocol.d.ts.map +0 -1
  210. package/lib/WAUSync/Protocols/UsyncBotProfileProtocol.js.map +0 -1
  211. package/lib/WAUSync/Protocols/UsyncLIDProtocol.d.ts.map +0 -1
  212. package/lib/WAUSync/Protocols/UsyncLIDProtocol.js.map +0 -1
  213. package/lib/WAUSync/Protocols/index.d.ts.map +0 -1
  214. package/lib/WAUSync/Protocols/index.js.map +0 -1
  215. package/lib/WAUSync/USyncQuery.d.ts.map +0 -1
  216. package/lib/WAUSync/USyncQuery.js.map +0 -1
  217. package/lib/WAUSync/USyncUser.d.ts.map +0 -1
  218. package/lib/WAUSync/USyncUser.js.map +0 -1
  219. package/lib/WAUSync/index.d.ts.map +0 -1
  220. package/lib/WAUSync/index.js.map +0 -1
  221. package/lib/index.d.ts.map +0 -1
  222. package/lib/index.js.map +0 -1
@@ -1,11 +1,20 @@
1
1
  import { Boom } from '@hapi/boom';
2
2
  import { proto } from '../../WAProto/index.js';
3
- import type { BotListInfo, ChatModification, MessageUpsertType, SocketConfig, WABusinessProfile, WAMediaUpload, WAMessage, WAPatchCreate, WAPresence, WAPrivacyCallValue, WAPrivacyGroupAddValue, WAPrivacyMessagesValue, WAPrivacyOnlineValue, WAPrivacyValue, WAReadReceiptsValue } from '../Types/index.js';
3
+ import type { BotListInfo, CacheStore, ChatModification, MessageUpsertType, SocketConfig, WABusinessProfile, WAMediaUpload, WAMessage, WAPatchCreate, WAPresence, WAPrivacyCallValue, WAPrivacyGroupAddValue, WAPrivacyMessagesValue, WAPrivacyOnlineValue, WAPrivacyValue, WAReadReceiptsValue } from '../Types/index.js';
4
4
  import type { QuickReplyAction } from '../Types/Bussines.js';
5
5
  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
+ export declare const buildProfilePictureQueryContent: (type: "preview" | "image", tcTokenContent?: BinaryNode[]) => BinaryNode[];
8
9
  export declare const makeChatsSocket: (config: SocketConfig) => {
10
+ serverProps: {
11
+ /** AB prop 10518: gate tctoken on 1:1 messages. Default true (safe: avoids 463). */
12
+ privacyTokenOn1to1: boolean;
13
+ /** AB prop 9666: gate tctoken on profile picture IQs. WA Web default: true. */
14
+ profilePicPrivacyToken: boolean;
15
+ /** AB prop 14303: issue tctokens to LID instead of PN. WA Web default: false. */
16
+ lidTrustedTokenIssueToLid: boolean;
17
+ };
9
18
  createCallLink: (type: "audio" | "video", event?: {
10
19
  startTime: number;
11
20
  }, timeoutMs?: number) => Promise<string | undefined>;
@@ -57,6 +66,7 @@ export declare const makeChatsSocket: (config: SocketConfig) => {
57
66
  cleanDirtyBits: (type: "account_sync" | "groups", fromTimestamp?: number | string) => Promise<void>;
58
67
  addOrEditContact: (jid: string, contact: proto.SyncActionValue.IContactAction) => Promise<void>;
59
68
  removeContact: (jid: string) => Promise<void>;
69
+ placeholderResendCache: CacheStore;
60
70
  addLabel: (jid: string, labels: LabelActionBody) => Promise<void>;
61
71
  addChatLabel: (jid: string, labelId: string) => Promise<void>;
62
72
  removeChatLabel: (jid: string, labelId: string) => Promise<void>;
@@ -76,6 +86,7 @@ export declare const makeChatsSocket: (config: SocketConfig) => {
76
86
  createBufferedFunction<A extends any[], T>(work: (...args: A) => Promise<T>): (...args: A) => Promise<T>;
77
87
  flush(): boolean;
78
88
  isBuffering(): boolean;
89
+ destroy(): void;
79
90
  };
80
91
  authState: {
81
92
  creds: import("../Types/index.js").AuthenticationCreds;
@@ -91,8 +102,9 @@ export declare const makeChatsSocket: (config: SocketConfig) => {
91
102
  sendNode: (frame: BinaryNode) => Promise<void>;
92
103
  logout: (msg?: string) => Promise<void>;
93
104
  end: (error: Error | undefined) => Promise<void>;
105
+ registerSocketEndHandler: (handler: (error: Error | undefined) => void | Promise<void>) => void;
94
106
  onUnexpectedError: (err: Error | Boom, msg: string) => void;
95
- uploadPreKeys: (count?: number, retryCount?: number) => Promise<void>;
107
+ uploadPreKeys: (count?: number) => Promise<void>;
96
108
  uploadPreKeysToServerIfRequired: () => Promise<void>;
97
109
  digestKeyBundle: () => Promise<void>;
98
110
  rotateSignedPreKey: () => Promise<void>;
@@ -107,5 +119,7 @@ export declare const makeChatsSocket: (config: SocketConfig) => {
107
119
  jid: string;
108
120
  exists: boolean;
109
121
  }[] | undefined>;
122
+ fetchAccountReachoutTimelock: () => Promise<import("../Types/index.js").ReachoutTimelockState>;
123
+ fetchNewChatMessageCap: () => Promise<import("../Types/index.js").NewChatMessageCapInfo>;
110
124
  };
111
125
  //# sourceMappingURL=chats.d.ts.map
@@ -1,22 +1,38 @@
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
- import { getBinaryNodeChild, getBinaryNodeChildren, jidDecode, jidNormalizedUser, reduceBinaryNodeToDictionary, S_WHATSAPP_NET } from '../WABinary/index.js';
11
+ import { getBinaryNodeChild, getBinaryNodeChildren, isHostedLidUser, isHostedPnUser, 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;
14
+ export const buildProfilePictureQueryContent = (type, tcTokenContent) => {
15
+ const picture = { tag: 'picture', attrs: { type, query: 'url' } };
16
+ if (tcTokenContent?.length) {
17
+ picture.content = tcTokenContent;
18
+ }
19
+ return [picture];
20
+ };
15
21
  export const makeChatsSocket = (config) => {
16
22
  const { logger, markOnlineOnConnect, fireInitQueries, appStateMacVerification, shouldIgnoreJid, shouldSyncHistoryMessage, getMessage } = config;
17
23
  const sock = makeSocket(config);
18
- const { ev, ws, authState, generateMessageTag, sendNode, query, signalRepository, onUnexpectedError, sendUnifiedSession } = sock;
24
+ const { ev, ws, authState, generateMessageTag, sendNode, query, signalRepository, onUnexpectedError, sendUnifiedSession, registerSocketEndHandler } = sock;
25
+ const getLIDForPN = signalRepository.lidMapping.getLIDForPN.bind(signalRepository.lidMapping);
19
26
  let privacySettings;
27
+ /** Server-assigned AB props for protocol behavior. */
28
+ const serverProps = {
29
+ /** AB prop 10518: gate tctoken on 1:1 messages. Default true (safe: avoids 463). */
30
+ privacyTokenOn1to1: true,
31
+ /** AB prop 9666: gate tctoken on profile picture IQs. WA Web default: true. */
32
+ profilePicPrivacyToken: true,
33
+ /** AB prop 14303: issue tctokens to LID instead of PN. WA Web default: false. */
34
+ lidTrustedTokenIssueToLid: false
35
+ };
20
36
  let syncState = SyncState.Connecting;
21
37
  /** this mutex ensures that messages are processed in order */
22
38
  const messageMutex = makeMutex();
@@ -28,14 +44,20 @@ export const makeChatsSocket = (config) => {
28
44
  const notificationMutex = makeMutex();
29
45
  // Timeout for AwaitingInitialSync state
30
46
  let awaitingSyncTimeout;
47
+ // In-memory history sync completion tracking (resets on reconnection)
48
+ const historySyncStatus = {
49
+ initialBootstrapComplete: false,
50
+ recentSyncComplete: false
51
+ };
52
+ let historySyncPausedTimeout;
53
+ // Collections blocked on missing app state sync keys (mirrors WA Web's "Blocked" state).
54
+ // When a key arrives via APP_STATE_SYNC_KEY_SHARE, these are re-synced.
55
+ const blockedCollections = new Set();
31
56
  const placeholderResendCache = config.placeholderResendCache ||
32
57
  new NodeCache({
33
58
  stdTTL: DEFAULT_CACHE_TTLS.MSG_RETRY, // 1 hour
34
59
  useClones: false
35
60
  });
36
- if (!config.placeholderResendCache) {
37
- config.placeholderResendCache = placeholderResendCache;
38
- }
39
61
  /** helper function to fetch the given app state sync key */
40
62
  const getAppStateSyncKey = async (keyId) => {
41
63
  const { [keyId]: key } = await authState.keys.get('app-state-sync-key', [keyId]);
@@ -258,6 +280,42 @@ export const makeChatsSocket = (config) => {
258
280
  return getBinaryNodeChildren(listNode, 'item').map(n => n.attrs.jid);
259
281
  };
260
282
  const updateBlockStatus = async (jid, action) => {
283
+ const normalizedJid = jidNormalizedUser(jid);
284
+ let lid;
285
+ let pn_jid;
286
+ if (isLidUser(normalizedJid) || isHostedLidUser(normalizedJid)) {
287
+ lid = normalizedJid;
288
+ if (action === 'block') {
289
+ const pn = await signalRepository.lidMapping.getPNForLID(normalizedJid);
290
+ if (!pn) {
291
+ throw new Boom(`Unable to resolve PN JID for LID: ${jid}`, { statusCode: 400 });
292
+ }
293
+ pn_jid = jidNormalizedUser(pn);
294
+ }
295
+ }
296
+ else if (isPnUser(normalizedJid) || isHostedPnUser(normalizedJid)) {
297
+ const mapped = await signalRepository.lidMapping.getLIDForPN(normalizedJid);
298
+ if (!mapped) {
299
+ throw new Boom(`Unable to resolve LID for PN JID: ${jid}`, { statusCode: 400 });
300
+ }
301
+ lid = mapped;
302
+ if (action === 'block') {
303
+ pn_jid = jidNormalizedUser(normalizedJid);
304
+ }
305
+ }
306
+ else {
307
+ throw new Boom(`Invalid jid: ${jid}`, { statusCode: 400 });
308
+ }
309
+ const itemAttrs = {
310
+ action,
311
+ jid: lid
312
+ };
313
+ if (action === 'block') {
314
+ if (!pn_jid) {
315
+ throw new Boom(`pn_jid required for block: ${jid}`, { statusCode: 400 });
316
+ }
317
+ itemAttrs.pn_jid = pn_jid;
318
+ }
261
319
  await query({
262
320
  tag: 'iq',
263
321
  attrs: {
@@ -268,10 +326,7 @@ export const makeChatsSocket = (config) => {
268
326
  content: [
269
327
  {
270
328
  tag: 'item',
271
- attrs: {
272
- action,
273
- jid
274
- }
329
+ attrs: itemAttrs
275
330
  }
276
331
  ]
277
332
  });
@@ -370,6 +425,9 @@ export const makeChatsSocket = (config) => {
370
425
  const collectionsToHandle = new Set(collections);
371
426
  // in case something goes wrong -- ensure we don't enter a loop that cannot be exited from
372
427
  const attemptsMap = {};
428
+ // collections that failed and need a full snapshot on retry
429
+ // mirrors WA Web's ErrorFatal -> force snapshot behavior
430
+ const forceSnapshotCollections = new Set();
373
431
  // keep executing till all collections are done
374
432
  // sometimes a single patch request will not return all the patches (God knows why)
375
433
  // so we fetch till they're all done (this is determined by the "has_more_patches" flag)
@@ -380,6 +438,7 @@ export const makeChatsSocket = (config) => {
380
438
  const result = await authState.keys.get('app-state-sync-version', [name]);
381
439
  let state = result[name];
382
440
  if (state) {
441
+ state = ensureLTHashStateVersion(state);
383
442
  if (typeof initialVersionMap[name] === 'undefined') {
384
443
  initialVersionMap[name] = state.version;
385
444
  }
@@ -388,14 +447,18 @@ export const makeChatsSocket = (config) => {
388
447
  state = newLTHashState();
389
448
  }
390
449
  states[name] = state;
391
- logger.info(`resyncing ${name} from v${state.version}`);
450
+ const shouldForceSnapshot = forceSnapshotCollections.has(name);
451
+ if (shouldForceSnapshot) {
452
+ forceSnapshotCollections.delete(name);
453
+ }
454
+ logger.info(`resyncing ${name} from v${state.version}${shouldForceSnapshot ? ' (forcing snapshot)' : ''}`);
392
455
  nodes.push({
393
456
  tag: 'collection',
394
457
  attrs: {
395
458
  name,
396
459
  version: state.version.toString(),
397
- // return snapshot if being synced from scratch
398
- return_snapshot: (!state.version).toString()
460
+ // return snapshot if syncing from scratch or forcing after a failed attempt
461
+ return_snapshot: (shouldForceSnapshot || !state.version).toString()
399
462
  }
400
463
  });
401
464
  }
@@ -421,7 +484,7 @@ export const makeChatsSocket = (config) => {
421
484
  const { patches, hasMorePatches, snapshot } = decoded[name];
422
485
  try {
423
486
  if (snapshot) {
424
- const { state: newState, mutationMap } = await decodeSyncdSnapshot(name, snapshot, getCachedAppStateSyncKey, initialVersionMap[name], appStateMacVerification.snapshot);
487
+ const { state: newState, mutationMap } = await decodeSyncdSnapshot(name, snapshot, getCachedAppStateSyncKey, initialVersionMap[name], appStateMacVerification.snapshot, logger);
425
488
  states[name] = newState;
426
489
  Object.assign(globalMutationMap, mutationMap);
427
490
  logger.info(`restored state of ${name} from snapshot to v${newState.version} with mutations`);
@@ -444,19 +507,37 @@ export const makeChatsSocket = (config) => {
444
507
  }
445
508
  }
446
509
  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
510
  attemptsMap[name] = (attemptsMap[name] || 0) + 1;
456
- if (isIrrecoverableError) {
457
- // stop retrying
511
+ const logData = {
512
+ name,
513
+ attempt: attemptsMap[name],
514
+ version: states[name].version,
515
+ statusCode: error.output?.statusCode,
516
+ errorType: error.name,
517
+ error: error.stack
518
+ };
519
+ if (isMissingKeyError(error) && attemptsMap[name] >= MAX_SYNC_ATTEMPTS) {
520
+ // WA Web treats missing keys as "Blocked" — park the collection
521
+ // until the key arrives via APP_STATE_SYNC_KEY_SHARE.
522
+ logger.warn(logData, `${name} blocked on missing key from v${states[name].version}, parking after ${attemptsMap[name]} attempts`);
523
+ blockedCollections.add(name);
524
+ collectionsToHandle.delete(name);
525
+ }
526
+ else if (isMissingKeyError(error)) {
527
+ // Retry with a snapshot which may use a different key.
528
+ logger.info(logData, `${name} blocked on missing key from v${states[name].version}, retrying with snapshot`);
529
+ forceSnapshotCollections.add(name);
530
+ }
531
+ else if (isAppStateSyncIrrecoverable(error, attemptsMap[name])) {
532
+ logger.warn(logData, `failed to sync ${name} from v${states[name].version}, giving up`);
458
533
  collectionsToHandle.delete(name);
459
534
  }
535
+ else {
536
+ logger.info(logData, `failed to sync ${name} from v${states[name].version}, forcing snapshot retry`);
537
+ // force a full snapshot on retry to recover from
538
+ // corrupted local state (e.g. LTHash MAC mismatch)
539
+ forceSnapshotCollections.add(name);
540
+ }
460
541
  }
461
542
  }
462
543
  }
@@ -472,8 +553,21 @@ export const makeChatsSocket = (config) => {
472
553
  * type = "image for the high res picture"
473
554
  */
474
555
  const profilePictureUrl = async (jid, type = 'preview', timeoutMs) => {
475
- const baseContent = [{ tag: 'picture', attrs: { type, query: 'url' } }];
476
- const tcTokenContent = await buildTcTokenFromJid({ authState, jid, baseContent });
556
+ // WA Web only includes tctoken for user JIDs (not groups/newsletters)
557
+ // and never for own profile pic (Chat model for self has no tcToken).
558
+ // Including tctoken for own JID causes the server to never respond.
559
+ const normalizedJid = jidNormalizedUser(jid);
560
+ const isUserJid = isPnUser(normalizedJid) || isLidUser(normalizedJid);
561
+ const me = authState.creds.me;
562
+ const isSelf = me && (normalizedJid === jidNormalizedUser(me.id) || (me.lid && normalizedJid === jidNormalizedUser(me.lid)));
563
+ let tcTokenContent;
564
+ if (serverProps.profilePicPrivacyToken && isUserJid && !isSelf) {
565
+ tcTokenContent = await buildTcTokenFromJid({
566
+ authState,
567
+ jid: normalizedJid,
568
+ getLIDForPN
569
+ });
570
+ }
477
571
  jid = jidNormalizedUser(jid);
478
572
  const result = await query({
479
573
  tag: 'iq',
@@ -483,7 +577,7 @@ export const makeChatsSocket = (config) => {
483
577
  type: 'get',
484
578
  xmlns: 'w:profile:picture'
485
579
  },
486
- content: tcTokenContent
580
+ content: buildProfilePictureQueryContent(type, tcTokenContent)
487
581
  }, timeoutMs);
488
582
  const child = getBinaryNodeChild(result, 'picture');
489
583
  return child?.attrs?.url;
@@ -549,7 +643,12 @@ export const makeChatsSocket = (config) => {
549
643
  * @param tcToken token for subscription, use if present
550
644
  */
551
645
  const presenceSubscribe = async (toJid) => {
552
- const tcTokenContent = await buildTcTokenFromJid({ authState, jid: toJid });
646
+ // Only include tctoken for user JIDs groups/newsletters don't use tctokens
647
+ const normalizedToJid = jidNormalizedUser(toJid);
648
+ const isUserJid = isPnUser(normalizedToJid) || isLidUser(normalizedToJid);
649
+ const tcTokenContent = isUserJid
650
+ ? await buildTcTokenFromJid({ authState, jid: normalizedToJid, getLIDForPN })
651
+ : undefined;
553
652
  return sendNode({
554
653
  tag: 'presence',
555
654
  attrs: {
@@ -570,7 +669,8 @@ export const makeChatsSocket = (config) => {
570
669
  if (tag === 'presence') {
571
670
  presence = {
572
671
  lastKnownPresence: attrs.type === 'unavailable' ? 'unavailable' : 'available',
573
- lastSeen: attrs.last && attrs.last !== 'deny' ? +attrs.last : undefined
672
+ lastSeen: attrs.last && attrs.last !== 'deny' ? +attrs.last : undefined,
673
+ groupOnlineCount: attrs.count ? +attrs.count : undefined
574
674
  };
575
675
  }
576
676
  else if (Array.isArray(content)) {
@@ -604,7 +704,7 @@ export const makeChatsSocket = (config) => {
604
704
  logger.debug({ patch: patchCreate }, 'applying app patch');
605
705
  await resyncAppState([name], false);
606
706
  const { [name]: currentSyncVersion } = await authState.keys.get('app-state-sync-version', [name]);
607
- initial = currentSyncVersion || newLTHashState();
707
+ initial = currentSyncVersion ? ensureLTHashStateVersion(currentSyncVersion) : newLTHashState();
608
708
  encodeResult = await encodeSyncdPatch(patchCreate, myAppStateKeyId, initial, getAppStateSyncKey);
609
709
  const { patch, state } = encodeResult;
610
710
  const node = {
@@ -650,22 +750,21 @@ export const makeChatsSocket = (config) => {
650
750
  }
651
751
  }
652
752
  };
653
- /** sending non-abt props may fix QR scan fail if server expects */
753
+ /** fetch AB props */
654
754
  const fetchProps = async () => {
655
- //TODO: implement both protocol 1 and protocol 2 prop fetching, specially for abKey for WM
656
755
  const resultNode = await query({
657
756
  tag: 'iq',
658
757
  attrs: {
659
758
  to: S_WHATSAPP_NET,
660
- xmlns: 'w',
759
+ xmlns: 'abt',
661
760
  type: 'get'
662
761
  },
663
762
  content: [
664
763
  {
665
764
  tag: 'props',
666
765
  attrs: {
667
- protocol: '2',
668
- hash: authState?.creds?.lastPropHash || ''
766
+ protocol: '1',
767
+ ...(authState?.creds?.lastPropHash ? { hash: authState.creds.lastPropHash } : {})
669
768
  }
670
769
  }
671
770
  ]
@@ -680,7 +779,20 @@ export const makeChatsSocket = (config) => {
680
779
  }
681
780
  props = reduceBinaryNodeToDictionary(propsNode, 'prop');
682
781
  }
683
- logger.debug('fetched props');
782
+ // Extract protocol-relevant AB props (only the ones we need)
783
+ const privacyTokenProp = props['10518'] ?? props['privacy_token_sending_on_all_1_on_1_messages'];
784
+ if (privacyTokenProp !== undefined) {
785
+ serverProps.privacyTokenOn1to1 = privacyTokenProp === 'true' || privacyTokenProp === '1';
786
+ }
787
+ const profilePicProp = props['9666'] ?? props['profile_scraping_privacy_token_in_photo_iq'];
788
+ if (profilePicProp !== undefined) {
789
+ serverProps.profilePicPrivacyToken = profilePicProp === 'true' || profilePicProp === '1';
790
+ }
791
+ const lidIssueProp = props['14303'] ?? props['lid_trusted_token_issue_to_lid'];
792
+ if (lidIssueProp !== undefined) {
793
+ serverProps.lidTrustedTokenIssueToLid = lidIssueProp === 'true' || lidIssueProp === '1';
794
+ }
795
+ logger.debug({ serverProps }, 'fetched props');
684
796
  return props;
685
797
  };
686
798
  /**
@@ -820,6 +932,47 @@ export const makeChatsSocket = (config) => {
820
932
  ? shouldSyncHistoryMessage(historyMsg) &&
821
933
  PROCESSABLE_HISTORY_TYPES.includes(historyMsg.syncType)
822
934
  : false;
935
+ if (historyMsg && shouldProcessHistoryMsg) {
936
+ const syncType = historyMsg.syncType;
937
+ // INITIAL_BOOTSTRAP — fire immediately, no progress check (same as WA Web K function)
938
+ if (syncType === proto.HistorySync.HistorySyncType.INITIAL_BOOTSTRAP &&
939
+ !historySyncStatus.initialBootstrapComplete) {
940
+ historySyncStatus.initialBootstrapComplete = true;
941
+ ev.emit('messaging-history.status', {
942
+ syncType,
943
+ status: 'complete',
944
+ explicit: true
945
+ });
946
+ }
947
+ // RECENT with progress === 100 — explicit completion
948
+ if (syncType === proto.HistorySync.HistorySyncType.RECENT &&
949
+ historyMsg.progress === 100 &&
950
+ !historySyncStatus.recentSyncComplete) {
951
+ historySyncStatus.recentSyncComplete = true;
952
+ clearTimeout(historySyncPausedTimeout);
953
+ historySyncPausedTimeout = undefined;
954
+ ev.emit('messaging-history.status', {
955
+ syncType,
956
+ status: 'complete',
957
+ explicit: true
958
+ });
959
+ }
960
+ // Reset 120s paused timeout on any RECENT chunk (like WA Web's handleChunkProgress)
961
+ if (syncType === proto.HistorySync.HistorySyncType.RECENT && !historySyncStatus.recentSyncComplete) {
962
+ clearTimeout(historySyncPausedTimeout);
963
+ historySyncPausedTimeout = setTimeout(() => {
964
+ if (!historySyncStatus.recentSyncComplete) {
965
+ historySyncStatus.recentSyncComplete = true;
966
+ ev.emit('messaging-history.status', {
967
+ syncType: proto.HistorySync.HistorySyncType.RECENT,
968
+ status: 'paused',
969
+ explicit: false
970
+ });
971
+ }
972
+ historySyncPausedTimeout = undefined;
973
+ }, HISTORY_SYNC_PAUSED_TIMEOUT_MS);
974
+ }
975
+ }
823
976
  // State machine: decide on sync and flush
824
977
  if (historyMsg && syncState === SyncState.AwaitingInitialSync) {
825
978
  if (awaitingSyncTimeout) {
@@ -839,6 +992,8 @@ export const makeChatsSocket = (config) => {
839
992
  }
840
993
  const doAppStateSync = async () => {
841
994
  if (syncState === SyncState.Syncing) {
995
+ // All collections will be synced, so clear any blocked ones
996
+ blockedCollections.clear();
842
997
  logger.info('Doing app state sync');
843
998
  await resyncAppState(ALL_WA_PATCH_NAMES, true);
844
999
  // Sync is complete, go online and flush everything
@@ -898,6 +1053,11 @@ export const makeChatsSocket = (config) => {
898
1053
  }
899
1054
  });
900
1055
  ev.on('connection.update', ({ connection, receivedPendingNotifications }) => {
1056
+ if (connection === 'close') {
1057
+ blockedCollections.clear();
1058
+ clearTimeout(historySyncPausedTimeout);
1059
+ historySyncPausedTimeout = undefined;
1060
+ }
901
1061
  if (connection === 'open') {
902
1062
  if (fireInitQueries) {
903
1063
  executeInitQueries().catch(error => onUnexpectedError(error, 'init queries'));
@@ -907,6 +1067,10 @@ export const makeChatsSocket = (config) => {
907
1067
  if (!receivedPendingNotifications || syncState !== SyncState.Connecting) {
908
1068
  return;
909
1069
  }
1070
+ historySyncStatus.initialBootstrapComplete = false;
1071
+ historySyncStatus.recentSyncComplete = false;
1072
+ clearTimeout(historySyncPausedTimeout);
1073
+ historySyncPausedTimeout = undefined;
910
1074
  syncState = SyncState.AwaitingInitialSync;
911
1075
  logger.info('Connection is now AwaitingInitialSync, buffering events');
912
1076
  ev.buffer();
@@ -919,19 +1083,49 @@ export const makeChatsSocket = (config) => {
919
1083
  setTimeout(() => ev.flush(), 0);
920
1084
  return;
921
1085
  }
922
- logger.info('History sync is enabled, awaiting notification with a 20s timeout.');
1086
+ // On reconnection (accountSyncCounter > 0), the server does not push
1087
+ // history sync notifications — the device already has its data.
1088
+ // Skip the 20s wait and go online immediately.
1089
+ if (authState.creds.accountSyncCounter > 0) {
1090
+ logger.info('Reconnection with existing sync data, skipping history sync wait. Transitioning to Online.');
1091
+ syncState = SyncState.Online;
1092
+ setTimeout(() => ev.flush(), 0);
1093
+ return;
1094
+ }
1095
+ logger.info('First connection, awaiting history sync notification with a 20s timeout.');
923
1096
  if (awaitingSyncTimeout) {
924
1097
  clearTimeout(awaitingSyncTimeout);
925
1098
  }
926
1099
  awaitingSyncTimeout = setTimeout(() => {
927
1100
  if (syncState === SyncState.AwaitingInitialSync) {
928
- // TODO: investigate
929
1101
  logger.warn('Timeout in AwaitingInitialSync, forcing state to Online and flushing buffer');
930
1102
  syncState = SyncState.Online;
931
1103
  ev.flush();
1104
+ // Increment so subsequent reconnections skip the 20s wait.
1105
+ // Late-arriving history is still processed via processMessage
1106
+ // regardless of the state machine phase.
1107
+ const accountSyncCounter = (authState.creds.accountSyncCounter || 0) + 1;
1108
+ ev.emit('creds.update', { accountSyncCounter });
932
1109
  }
933
1110
  }, 20000);
934
1111
  });
1112
+ // When an app state sync key arrives (myAppStateKeyId is set) and there are
1113
+ // collections blocked on a missing key, trigger a re-sync for just those collections.
1114
+ // This mirrors WA Web's Blocked → retry-on-key-arrival behavior.
1115
+ ev.on('creds.update', ({ myAppStateKeyId }) => {
1116
+ if (!myAppStateKeyId || blockedCollections.size === 0) {
1117
+ return;
1118
+ }
1119
+ // If we're in the middle of a full sync, doAppStateSync handles all collections
1120
+ if (syncState === SyncState.Syncing) {
1121
+ blockedCollections.clear();
1122
+ return;
1123
+ }
1124
+ const collections = [...blockedCollections];
1125
+ blockedCollections.clear();
1126
+ logger.info({ collections }, 'app state sync key arrived, re-syncing blocked collections');
1127
+ resyncAppState(collections, false).catch(error => onUnexpectedError(error, 'blocked collections resync'));
1128
+ });
935
1129
  ev.on('lid-mapping.update', async ({ lid, pn }) => {
936
1130
  try {
937
1131
  await signalRepository.lidMapping.storeLIDPNMappings([{ lid, pn }]);
@@ -940,8 +1134,20 @@ export const makeChatsSocket = (config) => {
940
1134
  logger.warn({ lid, pn, error }, 'Failed to store LID-PN mapping');
941
1135
  }
942
1136
  });
1137
+ registerSocketEndHandler(() => {
1138
+ if (awaitingSyncTimeout) {
1139
+ clearTimeout(awaitingSyncTimeout);
1140
+ awaitingSyncTimeout = undefined;
1141
+ }
1142
+ if (!config.placeholderResendCache && placeholderResendCache.close) {
1143
+ placeholderResendCache.close();
1144
+ }
1145
+ syncState = SyncState.Connecting;
1146
+ privacySettings = undefined;
1147
+ });
943
1148
  return {
944
1149
  ...sock,
1150
+ serverProps,
945
1151
  createCallLink,
946
1152
  getBotListV2,
947
1153
  messageMutex,
@@ -978,6 +1184,7 @@ export const makeChatsSocket = (config) => {
978
1184
  cleanDirtyBits,
979
1185
  addOrEditContact,
980
1186
  removeContact,
1187
+ placeholderResendCache,
981
1188
  addLabel,
982
1189
  addChatLabel,
983
1190
  removeChatLabel,
@@ -3,6 +3,22 @@ import type { GroupMetadata, ParticipantAction, SocketConfig, WAMessageKey } fro
3
3
  import { type BinaryNode } from '../WABinary/index.js';
4
4
  export declare const makeGroupsSocket: (config: SocketConfig) => {
5
5
  groupMetadata: (jid: string) => Promise<GroupMetadata>;
6
+ /** groupMetadata con cache de 30s, invalidado automáticamente ante cambios reales de participantes. */
7
+ groupMetadataCached: (jid: string) => Promise<GroupMetadata>;
8
+ /**
9
+ * Resuelve un jid/@lid a su jid de número de teléfono real usando primero los participantes
10
+ * del grupo y, si no hay coincidencia, el mapeo LID↔PN nativo de Baileys (signalRepository).
11
+ * @param jidOrChatId el chatId del grupo, o directamente el array de participantes ya obtenido
12
+ * @param targetJid el jid/lid a resolver
13
+ */
14
+ resolveParticipantJid: (jidOrChatId: string | GroupMetadata["participants"], targetJid: string) => Promise<string>;
15
+ /** true si el participante (jid o @lid) es admin/superadmin del grupo. */
16
+ isGroupAdmin: (jid: string, participantJid: string) => Promise<boolean>;
17
+ /** Lista de admins del grupo ya resueltos a jid de número de teléfono. */
18
+ getGroupAdmins: (jid: string) => Promise<{
19
+ jid: string;
20
+ admin: "admin" | "superadmin" | null;
21
+ }[]>;
6
22
  groupCreate: (subject: string, participants: string[]) => Promise<GroupMetadata>;
7
23
  groupLeave: (id: string) => Promise<void>;
8
24
  groupUpdateSubject: (jid: string, subject: string) => Promise<void>;