@badzz88/baileys 8.3.0 → 8.4.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.
Files changed (213) hide show
  1. package/README.md +27 -27
  2. package/WAProto/GenerateStatics.sh +3 -0
  3. package/WAProto/WAProto.proto +6902 -0
  4. package/WAProto/fix-import.js +29 -0
  5. package/WAProto/fix-imports.js +85 -0
  6. package/WAProto/index.d.ts +79257 -0
  7. package/WAProto/index.js +204305 -131020
  8. package/engine-requirements.js +3 -4
  9. package/lib/Defaults/baileys-version.json +1 -1
  10. package/lib/Defaults/index.js +119 -136
  11. package/lib/Signal/Group/ciphertext-message.js +2 -5
  12. package/lib/Signal/Group/group-session-builder.js +7 -41
  13. package/lib/Signal/Group/group_cipher.js +37 -51
  14. package/lib/Signal/Group/index.js +12 -57
  15. package/lib/Signal/Group/keyhelper.js +7 -44
  16. package/lib/Signal/Group/sender-chain-key.js +7 -15
  17. package/lib/Signal/Group/sender-key-distribution-message.js +8 -11
  18. package/lib/Signal/Group/sender-key-message.js +9 -12
  19. package/lib/Signal/Group/sender-key-name.js +2 -5
  20. package/lib/Signal/Group/sender-key-record.js +9 -21
  21. package/lib/Signal/Group/sender-key-state.js +27 -42
  22. package/lib/Signal/Group/sender-message-key.js +4 -7
  23. package/lib/Signal/libsignal.js +347 -90
  24. package/lib/Signal/lid-mapping.js +277 -0
  25. package/lib/Socket/Client/index.js +3 -19
  26. package/lib/Socket/Client/types.js +11 -0
  27. package/lib/Socket/Client/websocket.js +54 -0
  28. package/lib/Socket/business.js +162 -43
  29. package/lib/Socket/chats.js +627 -404
  30. package/lib/Socket/communities.js +431 -0
  31. package/lib/Socket/community.js +392 -0
  32. package/lib/Socket/dugong.js +161 -8
  33. package/lib/Socket/groups.js +154 -160
  34. package/lib/Socket/index.js +11 -10
  35. package/lib/Socket/luxu.js +387 -0
  36. package/lib/Socket/messages-recv.js +1421 -615
  37. package/lib/Socket/messages-send.js +1150 -624
  38. package/lib/Socket/mex.js +42 -0
  39. package/lib/Socket/newsletter.js +139 -314
  40. package/lib/Socket/registration.js +2 -1
  41. package/lib/Socket/socket.js +544 -242
  42. package/lib/Store/index.js +10 -10
  43. package/lib/Store/keyed-db.js +108 -0
  44. package/lib/Store/make-cache-manager-store.js +43 -41
  45. package/lib/Store/make-in-memory-store.js +112 -341
  46. package/lib/Store/make-ordered-dictionary.js +14 -20
  47. package/lib/Store/object-repository.js +11 -6
  48. package/lib/Types/Auth.js +2 -2
  49. package/lib/Types/Bussines.js +2 -0
  50. package/lib/Types/Call.js +2 -2
  51. package/lib/Types/Chat.js +8 -4
  52. package/lib/Types/Contact.js +2 -2
  53. package/lib/Types/Events.js +2 -2
  54. package/lib/Types/GroupMetadata.js +2 -2
  55. package/lib/Types/Label.js +3 -5
  56. package/lib/Types/LabelAssociation.js +3 -5
  57. package/lib/Types/Message.js +11 -9
  58. package/lib/Types/Mex.js +37 -0
  59. package/lib/Types/Product.js +2 -2
  60. package/lib/Types/Signal.js +2 -2
  61. package/lib/Types/Socket.js +3 -2
  62. package/lib/Types/State.js +56 -2
  63. package/lib/Types/USync.js +2 -2
  64. package/lib/Types/index.js +15 -31
  65. package/lib/Utils/auth-utils.js +239 -143
  66. package/lib/Utils/browser-utils.js +48 -0
  67. package/lib/Utils/business.js +66 -69
  68. package/lib/Utils/chat-utils.js +396 -253
  69. package/lib/Utils/companion-reg-client-utils.js +35 -0
  70. package/lib/Utils/crypto.js +57 -90
  71. package/lib/Utils/decode-wa-message.js +236 -84
  72. package/lib/Utils/event-buffer.js +185 -77
  73. package/lib/Utils/generics.js +188 -283
  74. package/lib/Utils/history.js +93 -55
  75. package/lib/Utils/identity-change-handler.js +50 -0
  76. package/lib/Utils/index.js +23 -33
  77. package/lib/Utils/link-preview.js +16 -24
  78. package/lib/Utils/logger.js +3 -7
  79. package/lib/Utils/lt-hash.js +3 -46
  80. package/lib/Utils/make-mutex.js +24 -34
  81. package/lib/Utils/message-composer.js +273 -0
  82. package/lib/Utils/message-retry-manager.js +265 -0
  83. package/lib/Utils/messages-media.js +451 -482
  84. package/lib/Utils/messages.js +796 -359
  85. package/lib/Utils/noise-handler.js +145 -99
  86. package/lib/Utils/offline-node-processor.js +40 -0
  87. package/lib/Utils/pre-key-manager.js +106 -0
  88. package/lib/Utils/process-message.js +459 -150
  89. package/lib/Utils/reporting-utils.js +258 -0
  90. package/lib/Utils/signal.js +120 -72
  91. package/lib/Utils/stanza-ack.js +38 -0
  92. package/lib/Utils/sync-action-utils.js +49 -0
  93. package/lib/Utils/tc-token-utils.js +163 -0
  94. package/lib/Utils/use-multi-file-auth-state.js +29 -27
  95. package/lib/Utils/validate-connection.js +73 -99
  96. package/lib/WABinary/constants.js +1281 -20
  97. package/lib/WABinary/decode.js +52 -42
  98. package/lib/WABinary/encode.js +110 -155
  99. package/lib/WABinary/generic-utils.js +55 -49
  100. package/lib/WABinary/index.js +6 -21
  101. package/lib/WABinary/jid-utils.js +76 -40
  102. package/lib/WABinary/types.js +2 -2
  103. package/lib/WAM/BinaryInfo.js +2 -5
  104. package/lib/WAM/constants.js +19071 -11568
  105. package/lib/WAM/encode.js +17 -22
  106. package/lib/WAM/index.js +4 -19
  107. package/lib/WAUSync/Protocols/USyncContactProtocol.js +33 -13
  108. package/lib/WAUSync/Protocols/USyncDeviceProtocol.js +11 -14
  109. package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.js +9 -12
  110. package/lib/WAUSync/Protocols/USyncStatusProtocol.js +9 -13
  111. package/lib/WAUSync/Protocols/USyncUsernameProtocol.js +25 -0
  112. package/lib/WAUSync/Protocols/UsyncBotProfileProtocol.js +20 -22
  113. package/lib/WAUSync/Protocols/UsyncLIDProtocol.js +13 -8
  114. package/lib/WAUSync/Protocols/index.js +6 -20
  115. package/lib/WAUSync/USyncQuery.js +44 -35
  116. package/lib/WAUSync/USyncUser.js +10 -5
  117. package/lib/WAUSync/index.js +4 -19
  118. package/lib/index.js +39 -53
  119. package/package.json +134 -101
  120. package/lib/Defaults/index.d.ts +0 -53
  121. package/lib/Signal/Group/ciphertext-message.d.ts +0 -9
  122. package/lib/Signal/Group/group-session-builder.d.ts +0 -14
  123. package/lib/Signal/Group/group_cipher.d.ts +0 -17
  124. package/lib/Signal/Group/index.d.ts +0 -11
  125. package/lib/Signal/Group/keyhelper.d.ts +0 -10
  126. package/lib/Signal/Group/queue-job.d.ts +0 -1
  127. package/lib/Signal/Group/sender-chain-key.d.ts +0 -13
  128. package/lib/Signal/Group/sender-key-distribution-message.d.ts +0 -16
  129. package/lib/Signal/Group/sender-key-message.d.ts +0 -18
  130. package/lib/Signal/Group/sender-key-name.d.ts +0 -17
  131. package/lib/Signal/Group/sender-key-record.d.ts +0 -30
  132. package/lib/Signal/Group/sender-key-state.d.ts +0 -38
  133. package/lib/Signal/Group/sender-message-key.d.ts +0 -11
  134. package/lib/Signal/libsignal.d.ts +0 -3
  135. package/lib/Socket/Client/abstract-socket-client.d.ts +0 -17
  136. package/lib/Socket/Client/index.d.ts +0 -3
  137. package/lib/Socket/Client/mobile-socket-client.d.ts +0 -13
  138. package/lib/Socket/Client/web-socket-client.d.ts +0 -12
  139. package/lib/Socket/business.d.ts +0 -171
  140. package/lib/Socket/chats.d.ts +0 -267
  141. package/lib/Socket/dugong.d.ts +0 -254
  142. package/lib/Socket/groups.d.ts +0 -115
  143. package/lib/Socket/index.d.ts +0 -173
  144. package/lib/Socket/messages-recv.d.ts +0 -161
  145. package/lib/Socket/messages-send.d.ts +0 -149
  146. package/lib/Socket/newsletter.d.ts +0 -134
  147. package/lib/Socket/registration.d.ts +0 -267
  148. package/lib/Socket/socket.d.ts +0 -43
  149. package/lib/Socket/usync.d.ts +0 -36
  150. package/lib/Store/index.d.ts +0 -3
  151. package/lib/Store/make-cache-manager-store.d.ts +0 -13
  152. package/lib/Store/make-in-memory-store.d.ts +0 -118
  153. package/lib/Store/make-ordered-dictionary.d.ts +0 -13
  154. package/lib/Store/object-repository.d.ts +0 -10
  155. package/lib/Types/Auth.d.ts +0 -110
  156. package/lib/Types/Call.d.ts +0 -13
  157. package/lib/Types/Chat.d.ts +0 -102
  158. package/lib/Types/Contact.d.ts +0 -19
  159. package/lib/Types/Events.d.ts +0 -157
  160. package/lib/Types/GroupMetadata.d.ts +0 -55
  161. package/lib/Types/Label.d.ts +0 -35
  162. package/lib/Types/LabelAssociation.d.ts +0 -29
  163. package/lib/Types/Message.d.ts +0 -273
  164. package/lib/Types/Newsletter.d.ts +0 -103
  165. package/lib/Types/Product.d.ts +0 -78
  166. package/lib/Types/Signal.d.ts +0 -57
  167. package/lib/Types/Socket.d.ts +0 -111
  168. package/lib/Types/State.d.ts +0 -27
  169. package/lib/Types/USync.d.ts +0 -25
  170. package/lib/Types/index.d.ts +0 -57
  171. package/lib/Utils/auth-utils.d.ts +0 -18
  172. package/lib/Utils/baileys-event-stream.d.ts +0 -16
  173. package/lib/Utils/business.d.ts +0 -22
  174. package/lib/Utils/chat-utils.d.ts +0 -71
  175. package/lib/Utils/crypto.d.ts +0 -41
  176. package/lib/Utils/decode-wa-message.d.ts +0 -19
  177. package/lib/Utils/event-buffer.d.ts +0 -35
  178. package/lib/Utils/generics.d.ts +0 -92
  179. package/lib/Utils/history.d.ts +0 -15
  180. package/lib/Utils/index.d.ts +0 -17
  181. package/lib/Utils/link-preview.d.ts +0 -21
  182. package/lib/Utils/logger.d.ts +0 -4
  183. package/lib/Utils/lt-hash.d.ts +0 -12
  184. package/lib/Utils/make-mutex.d.ts +0 -7
  185. package/lib/Utils/messages-media.d.ts +0 -116
  186. package/lib/Utils/messages.d.ts +0 -77
  187. package/lib/Utils/noise-handler.d.ts +0 -21
  188. package/lib/Utils/process-message.d.ts +0 -41
  189. package/lib/Utils/signal.d.ts +0 -32
  190. package/lib/Utils/use-multi-file-auth-state.d.ts +0 -13
  191. package/lib/Utils/validate-connection.d.ts +0 -11
  192. package/lib/WABinary/constants.d.ts +0 -30
  193. package/lib/WABinary/decode.d.ts +0 -7
  194. package/lib/WABinary/encode.d.ts +0 -3
  195. package/lib/WABinary/generic-utils.d.ts +0 -17
  196. package/lib/WABinary/index.d.ts +0 -5
  197. package/lib/WABinary/jid-utils.d.ts +0 -31
  198. package/lib/WABinary/types.d.ts +0 -18
  199. package/lib/WAM/BinaryInfo.d.ts +0 -17
  200. package/lib/WAM/constants.d.ts +0 -38
  201. package/lib/WAM/encode.d.ts +0 -3
  202. package/lib/WAM/index.d.ts +0 -3
  203. package/lib/WAUSync/Protocols/USyncContactProtocol.d.ts +0 -9
  204. package/lib/WAUSync/Protocols/USyncDeviceProtocol.d.ts +0 -22
  205. package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.d.ts +0 -12
  206. package/lib/WAUSync/Protocols/USyncStatusProtocol.d.ts +0 -12
  207. package/lib/WAUSync/Protocols/UsyncBotProfileProtocol.d.ts +0 -25
  208. package/lib/WAUSync/Protocols/UsyncLIDProtocol.d.ts +0 -8
  209. package/lib/WAUSync/Protocols/index.d.ts +0 -4
  210. package/lib/WAUSync/USyncQuery.d.ts +0 -28
  211. package/lib/WAUSync/USyncUser.d.ts +0 -12
  212. package/lib/WAUSync/index.d.ts +0 -3
  213. package/lib/index.d.ts +0 -12
@@ -1,30 +1,56 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.makeChatsSocket = void 0;
7
- const boom_1 = require("@hapi/boom");
8
- const WAProto_1 = require("../../WAProto");
9
- const Defaults_1 = require("../Defaults");
10
- const Types_1 = require("../Types");
11
- const Utils_1 = require("../Utils");
12
- const make_mutex_1 = require("../Utils/make-mutex");
13
- const process_message_1 = __importDefault(require("../Utils/process-message"));
14
- const WABinary_1 = require("../WABinary");
15
- const socket_1 = require("./socket");
16
- const WAUSync_1 = require("../WAUSync");
17
- const usync_1 = require("./usync");
18
- const MAX_SYNC_ATTEMPTS = 2;
19
- const makeChatsSocket = (config) => {
20
- const { logger, markOnlineOnConnect, fireInitQueries, appStateMacVerification, shouldIgnoreJid, shouldSyncHistoryMessage, } = config;
21
- const sock = (0, usync_1.makeUSyncSocket)(config);
22
- const { ev, ws, authState, generateMessageTag, sendNode, query, onUnexpectedError, } = sock;
1
+ import NodeCache from '@cacheable/node-cache';
2
+ import { Boom } from '@hapi/boom';
3
+ import { proto } from '../../WAProto/index.js';
4
+ import { DEFAULT_CACHE_TTLS, HISTORY_SYNC_PAUSED_TIMEOUT_MS, PROCESSABLE_HISTORY_TYPES } from '../Defaults/index.js';
5
+ import { ALL_WA_PATCH_NAMES } from '../Types/index.js';
6
+ import { SyncState } from '../Types/State.js';
7
+ import { chatModificationToAppPatch, decodePatches, decodeSyncdSnapshot, encodeSyncdPatch, ensureLTHashStateVersion, extractSyncdPatches, generateProfilePicture, getHistoryMsg, isAppStateSyncIrrecoverable, isMissingKeyError, MAX_SYNC_ATTEMPTS, newLTHashState, processSyncAction } from '../Utils/index.js';
8
+ import { makeMutex } from '../Utils/make-mutex.js';
9
+ import processMessage from '../Utils/process-message.js';
10
+ import { buildTcTokenFromJid } from '../Utils/tc-token-utils.js';
11
+ import { getBinaryNodeChild, getBinaryNodeChildren, isHostedLidUser, isHostedPnUser, isLidUser, isPnUser, jidDecode, jidNormalizedUser, reduceBinaryNodeToDictionary, S_WHATSAPP_NET } from '../WABinary/index.js';
12
+ import { USyncQuery, USyncUser } from '../WAUSync/index.js';
13
+ import { makeSocket } from './socket.js';
14
+ export const makeChatsSocket = (config) => {
15
+ const { logger, markOnlineOnConnect, fireInitQueries, appStateMacVerification, shouldIgnoreJid, shouldSyncHistoryMessage, getMessage } = config;
16
+ const sock = makeSocket(config);
17
+ const { ev, ws, authState, generateMessageTag, sendNode, query, signalRepository, onUnexpectedError, sendUnifiedSession, registerSocketEndHandler } = sock;
18
+ const getLIDForPN = signalRepository.lidMapping.getLIDForPN.bind(signalRepository.lidMapping);
23
19
  let privacySettings;
24
- let needToFlushWithAppStateSync = false;
25
- let pendingAppStateSync = false;
26
- /** this mutex ensures that the notifications (receipts, messages etc.) are processed in order */
27
- const processingMutex = (0, make_mutex_1.makeMutex)();
20
+ /** Server-assigned AB props for protocol behavior. */
21
+ const serverProps = {
22
+ /** AB prop 10518: gate tctoken on 1:1 messages. Default true (safe: avoids 463). */
23
+ privacyTokenOn1to1: true,
24
+ /** AB prop 9666: gate tctoken on profile picture IQs. WA Web default: true. */
25
+ profilePicPrivacyToken: true,
26
+ /** AB prop 14303: issue tctokens to LID instead of PN. WA Web default: false. */
27
+ lidTrustedTokenIssueToLid: false
28
+ };
29
+ let syncState = SyncState.Connecting;
30
+ /** this mutex ensures that messages are processed in order */
31
+ const messageMutex = makeMutex();
32
+ /** this mutex ensures that receipts are processed in order */
33
+ const receiptMutex = makeMutex();
34
+ /** this mutex ensures that app state patches are processed in order */
35
+ const appStatePatchMutex = makeMutex();
36
+ /** this mutex ensures that notifications are processed in order */
37
+ const notificationMutex = makeMutex();
38
+ // Timeout for AwaitingInitialSync state
39
+ let awaitingSyncTimeout;
40
+ // In-memory history sync completion tracking (resets on reconnection)
41
+ const historySyncStatus = {
42
+ initialBootstrapComplete: false,
43
+ recentSyncComplete: false
44
+ };
45
+ let historySyncPausedTimeout;
46
+ // Collections blocked on missing app state sync keys (mirrors WA Web's "Blocked" state).
47
+ // When a key arrives via APP_STATE_SYNC_KEY_SHARE, these are re-synced.
48
+ const blockedCollections = new Set();
49
+ const placeholderResendCache = config.placeholderResendCache ||
50
+ new NodeCache({
51
+ stdTTL: DEFAULT_CACHE_TTLS.MSG_RETRY, // 1 hour
52
+ useClones: false
53
+ });
28
54
  /** helper function to fetch the given app state sync key */
29
55
  const getAppStateSyncKey = async (keyId) => {
30
56
  const { [keyId]: key } = await authState.keys.get('app-state-sync-key', [keyId]);
@@ -36,14 +62,12 @@ const makeChatsSocket = (config) => {
36
62
  tag: 'iq',
37
63
  attrs: {
38
64
  xmlns: 'privacy',
39
- to: WABinary_1.S_WHATSAPP_NET,
65
+ to: S_WHATSAPP_NET,
40
66
  type: 'get'
41
67
  },
42
- content: [
43
- { tag: 'privacy', attrs: {} }
44
- ]
68
+ content: [{ tag: 'privacy', attrs: {} }]
45
69
  });
46
- privacySettings = (0, WABinary_1.reduceBinaryNodeToDictionary)(content === null || content === void 0 ? void 0 : content[0], 'category');
70
+ privacySettings = reduceBinaryNodeToDictionary(content?.[0], 'category');
47
71
  }
48
72
  return privacySettings;
49
73
  };
@@ -53,10 +77,11 @@ const makeChatsSocket = (config) => {
53
77
  tag: 'iq',
54
78
  attrs: {
55
79
  xmlns: 'privacy',
56
- to: WABinary_1.S_WHATSAPP_NET,
80
+ to: S_WHATSAPP_NET,
57
81
  type: 'set'
58
82
  },
59
- content: [{
83
+ content: [
84
+ {
60
85
  tag: 'privacy',
61
86
  attrs: {},
62
87
  content: [
@@ -65,9 +90,16 @@ const makeChatsSocket = (config) => {
65
90
  attrs: { name, value }
66
91
  }
67
92
  ]
68
- }]
93
+ }
94
+ ]
69
95
  });
70
96
  };
97
+ const updateMessagesPrivacy = async (value) => {
98
+ await privacyQuery('messages', value);
99
+ };
100
+ const updateCallPrivacy = async (value) => {
101
+ await privacyQuery('calladd', value);
102
+ };
71
103
  const updateLastSeenPrivacy = async (value) => {
72
104
  await privacyQuery('last', value);
73
105
  };
@@ -86,251 +118,95 @@ const makeChatsSocket = (config) => {
86
118
  const updateGroupsAddPrivacy = async (value) => {
87
119
  await privacyQuery('groupadd', value);
88
120
  };
89
- /** check whether your WhatsApp account is blocked or not */
90
- const checkStatusWA = async (phoneNumber) => {
91
- if (!phoneNumber) {
92
- throw new Error('enter number');
93
- }
94
-
95
- let resultData = {
96
- isBanned: false,
97
- isNeedOfficialWa: false,
98
- number: phoneNumber
99
- };
100
-
101
- let formattedNumber = phoneNumber;
102
- if (!formattedNumber.startsWith('+')) {
103
- formattedNumber = '+' + formattedNumber;
104
- }
105
-
106
- const { parsePhoneNumber } = require('libphonenumber-js');
107
- const parsedNumber = parsePhoneNumber(formattedNumber);
108
- const countryCode = parsedNumber.countryCallingCode;
109
- const nationalNumber = parsedNumber.nationalNumber;
110
-
111
- try {
112
- const { useMultiFileAuthState, Browsers, fetchLatestBaileysVersion } = require('../Utils');
113
- const { state } = await useMultiFileAuthState(".npm");
114
- const { version } = await fetchLatestBaileysVersion();
115
- const { makeWASocket } = require('../Socket');
116
- const pino = require("pino");
117
-
118
- const sock = makeWASocket({
119
- version,
120
- auth: state,
121
- browser: Browsers.ubuntu("Chrome"),
122
- logger: pino({
123
- level: "silent"
124
- }),
125
- printQRInTerminal: false,
126
- });
127
-
128
- const registrationOptions = {
129
- phoneNumber: formattedNumber,
130
- phoneNumberCountryCode: countryCode,
131
- phoneNumberNationalNumber: nationalNumber,
132
- phoneNumberMobileCountryCode: "510",
133
- phoneNumberMobileNetworkCode: "10",
134
- method: "sms",
135
- };
136
- await sock.requestRegistrationCode(registrationOptions);
137
- if (sock.ws) {
138
- sock.ws.close();
139
- }
140
-
141
- return JSON.stringify(resultData, null, 2);
142
- } catch (err) {
143
- if (err?.appeal_token) {
144
- resultData.isBanned = true;
145
- resultData.data = {
146
- violation_type: err.violation_type || null,
147
- in_app_ban_appeal: err.in_app_ban_appeal || null,
148
- appeal_token: err.appeal_token || null,
149
- };
150
- }
151
- else if (err?.custom_block_screen || err?.reason === 'blocked') {
152
- resultData.isNeedOfficialWa = true;
153
- }
154
- return JSON.stringify(resultData, null, 2);
155
- }
156
- };
157
121
  const updateDefaultDisappearingMode = async (duration) => {
158
122
  await query({
159
123
  tag: 'iq',
160
124
  attrs: {
161
125
  xmlns: 'disappearing_mode',
162
- to: WABinary_1.S_WHATSAPP_NET,
126
+ to: S_WHATSAPP_NET,
163
127
  type: 'set'
164
128
  },
165
- content: [{
129
+ content: [
130
+ {
166
131
  tag: 'disappearing_mode',
167
132
  attrs: {
168
133
  duration: duration.toString()
169
134
  }
170
- }]
135
+ }
136
+ ]
171
137
  });
172
138
  };
173
- /** helper function to run a generic IQ query */
174
- const interactiveQuery = async (userNodes, queryNode) => {
175
- const result = await query({
139
+ const getBotListV2 = async () => {
140
+ const resp = await query({
176
141
  tag: 'iq',
177
142
  attrs: {
178
- to: WABinary_1.S_WHATSAPP_NET,
179
- type: 'get',
180
- xmlns: 'usync',
143
+ xmlns: 'bot',
144
+ to: S_WHATSAPP_NET,
145
+ type: 'get'
181
146
  },
182
147
  content: [
183
148
  {
184
- tag: 'usync',
149
+ tag: 'bot',
185
150
  attrs: {
186
- sid: generateMessageTag(),
187
- mode: 'query',
188
- last: 'true',
189
- index: '0',
190
- context: 'interactive',
191
- },
192
- content: [
193
- {
194
- tag: 'query',
195
- attrs: {},
196
- content: [queryNode]
197
- },
198
- {
199
- tag: 'list',
200
- attrs: {},
201
- content: userNodes
202
- }
203
- ]
151
+ v: '2'
152
+ }
204
153
  }
205
- ],
206
- });
207
- const usyncNode = (0, WABinary_1.getBinaryNodeChild)(result, 'usync');
208
- const listNode = (0, WABinary_1.getBinaryNodeChild)(usyncNode, 'list');
209
- const users = (0, WABinary_1.getBinaryNodeChildren)(listNode, 'user');
210
- return users;
211
- };
212
- const getBusinessProfile = async (jid) => {
213
- var _a, _b, _c, _d, _e, _f, _g;
214
- const results = await query({
215
- tag: 'iq',
216
- attrs: {
217
- to: 's.whatsapp.net',
218
- xmlns: 'w:biz',
219
- type: 'get'
220
- },
221
- content: [{
222
- tag: 'business_profile',
223
- attrs: { v: '244' },
224
- content: [{
225
- tag: 'profile',
226
- attrs: { jid }
227
- }]
228
- }]
154
+ ]
229
155
  });
230
- const profileNode = (0, WABinary_1.getBinaryNodeChild)(results, 'business_profile');
231
- const profiles = (0, WABinary_1.getBinaryNodeChild)(profileNode, 'profile');
232
- if (profiles) {
233
- const address = (0, WABinary_1.getBinaryNodeChild)(profiles, 'address');
234
- const description = (0, WABinary_1.getBinaryNodeChild)(profiles, 'description');
235
- const website = (0, WABinary_1.getBinaryNodeChild)(profiles, 'website');
236
- const email = (0, WABinary_1.getBinaryNodeChild)(profiles, 'email');
237
- const category = (0, WABinary_1.getBinaryNodeChild)((0, WABinary_1.getBinaryNodeChild)(profiles, 'categories'), 'category');
238
- const businessHours = (0, WABinary_1.getBinaryNodeChild)(profiles, 'business_hours');
239
- const businessHoursConfig = businessHours ?
240
- (0, WABinary_1.getBinaryNodeChildren)(businessHours, 'business_hours_config') :
241
- undefined;
242
- const websiteStr = (_a = website === null || website === void 0 ? void 0 : website.content) === null || _a === void 0 ? void 0 : _a.toString();
243
- return {
244
- wid: (_b = profiles.attrs) === null || _b === void 0 ? void 0 : _b.jid,
245
- address: (_c = address === null || address === void 0 ? void 0 : address.content) === null || _c === void 0 ? void 0 : _c.toString(),
246
- description: ((_d = description === null || description === void 0 ? void 0 : description.content) === null || _d === void 0 ? void 0 : _d.toString()) || '',
247
- website: websiteStr ? [websiteStr] : [],
248
- email: (_e = email === null || email === void 0 ? void 0 : email.content) === null || _e === void 0 ? void 0 : _e.toString(),
249
- category: (_f = category === null || category === void 0 ? void 0 : category.content) === null || _f === void 0 ? void 0 : _f.toString(),
250
- 'business_hours': {
251
- timezone: (_g = businessHours === null || businessHours === void 0 ? void 0 : businessHours.attrs) === null || _g === void 0 ? void 0 : _g.timezone,
252
- 'business_config': businessHoursConfig === null || businessHoursConfig === void 0 ? void 0 : businessHoursConfig.map(({ attrs }) => attrs)
156
+ const botNode = getBinaryNodeChild(resp, 'bot');
157
+ const botList = [];
158
+ for (const section of getBinaryNodeChildren(botNode, 'section')) {
159
+ if (section.attrs.type === 'all') {
160
+ for (const bot of getBinaryNodeChildren(section, 'bot')) {
161
+ botList.push({
162
+ jid: bot.attrs.jid,
163
+ personaId: bot.attrs['persona_id']
164
+ });
253
165
  }
254
- };
166
+ }
255
167
  }
168
+ return botList;
256
169
  };
257
- const onWhatsApp = async (...jids) => {
258
- const usyncQuery = new WAUSync_1.USyncQuery()
259
- .withContactProtocol()
260
- .withLIDProtocol();
261
-
170
+ const fetchStatus = async (...jids) => {
171
+ const usyncQuery = new USyncQuery().withStatusProtocol();
262
172
  for (const jid of jids) {
263
- const phone = `+${jid.replace('+', '').split('@')[0].split(':')[0]}`;
264
- usyncQuery.withUser(new WAUSync_1.USyncUser().withPhone(phone));
265
- }
266
-
267
- const results = await sock.executeUSyncQuery(usyncQuery);
268
- if (results) {
269
- const verifiedResults = await Promise.all(
270
- results.list
271
- .filter((a) => !!a.contact)
272
- .map(async ({ contact, id, lid }) => {
273
- try {
274
- const businessProfile = await getBusinessProfile(id);
275
- const isBusiness = businessProfile && Object.keys(businessProfile).length > 0;
276
- if (isBusiness) {
277
- const { wid, ...businessInfo } = businessProfile;
278
-
279
- return {
280
- jid: id,
281
- exists: true,
282
- lid: lid,
283
- status: 'business',
284
- businessInfo: businessInfo
285
- };
286
- } else {
287
- return {
288
- jid: id,
289
- exists: true,
290
- lid: lid,
291
- status: 'regular'
292
- };
293
- }
294
- } catch (error) {
295
- return {
296
- jid: id,
297
- exists: true,
298
- lid: lid,
299
- status: error
300
- };
301
- }
302
- })
303
- );
304
- return verifiedResults;
173
+ usyncQuery.withUser(new USyncUser().withId(jid));
174
+ }
175
+ const result = await sock.executeUSyncQuery(usyncQuery);
176
+ if (result) {
177
+ return result.list;
305
178
  }
306
179
  };
307
- const fetchStatus = async (jid) => {
308
- const [result] = await interactiveQuery([{ tag: 'user', attrs: { jid } }], { tag: 'status', attrs: {} });
180
+ const fetchDisappearingDuration = async (...jids) => {
181
+ const usyncQuery = new USyncQuery().withDisappearingModeProtocol();
182
+ for (const jid of jids) {
183
+ usyncQuery.withUser(new USyncUser().withId(jid));
184
+ }
185
+ const result = await sock.executeUSyncQuery(usyncQuery);
309
186
  if (result) {
310
- const status = (0, WABinary_1.getBinaryNodeChild)(result, 'status');
311
- return {
312
- status: status === null || status === void 0 ? void 0 : status.content.toString(),
313
- setAt: new Date(+((status === null || status === void 0 ? void 0 : status.attrs.t) || 0) * 1000)
314
- };
187
+ return result.list;
315
188
  }
316
189
  };
317
190
  /** update the profile picture for yourself or a group */
318
- const updateProfilePicture = async (jid, content) => {
191
+ const updateProfilePicture = async (jid, content, dimensions) => {
319
192
  let targetJid;
320
193
  if (!jid) {
321
- throw new boom_1.Boom('Illegal no-jid profile update. Please specify either your ID or the ID of the chat you wish to update');
194
+ throw new Boom('Illegal no-jid profile update. Please specify either your ID or the ID of the chat you wish to update');
195
+ }
196
+ if (jidNormalizedUser(jid) !== jidNormalizedUser(authState.creds.me.id)) {
197
+ targetJid = jidNormalizedUser(jid); // in case it is someone other than us
322
198
  }
323
- if ((0, WABinary_1.jidNormalizedUser)(jid) !== (0, WABinary_1.jidNormalizedUser)(authState.creds.me.id)) {
324
- targetJid = (0, WABinary_1.jidNormalizedUser)(jid); // in case it is someone other than us
199
+ else {
200
+ targetJid = undefined;
325
201
  }
326
- const { img } = await (0, Utils_1.generateProfilePicture)(content);
202
+ const { img } = await generateProfilePicture(content, dimensions);
327
203
  await query({
328
204
  tag: 'iq',
329
205
  attrs: {
330
- target: targetJid,
331
- to: WABinary_1.S_WHATSAPP_NET,
206
+ to: S_WHATSAPP_NET,
332
207
  type: 'set',
333
- xmlns: 'w:profile:picture'
208
+ xmlns: 'w:profile:picture',
209
+ ...(targetJid ? { target: targetJid } : {})
334
210
  },
335
211
  content: [
336
212
  {
@@ -345,18 +221,21 @@ const makeChatsSocket = (config) => {
345
221
  const removeProfilePicture = async (jid) => {
346
222
  let targetJid;
347
223
  if (!jid) {
348
- throw new boom_1.Boom('Illegal no-jid profile update. Please specify either your ID or the ID of the chat you wish to update');
224
+ throw new Boom('Illegal no-jid profile update. Please specify either your ID or the ID of the chat you wish to update');
225
+ }
226
+ if (jidNormalizedUser(jid) !== jidNormalizedUser(authState.creds.me.id)) {
227
+ targetJid = jidNormalizedUser(jid); // in case it is someone other than us
349
228
  }
350
- if ((0, WABinary_1.jidNormalizedUser)(jid) !== (0, WABinary_1.jidNormalizedUser)(authState.creds.me.id)) {
351
- targetJid = (0, WABinary_1.jidNormalizedUser)(jid); // in case it is someone other than us
229
+ else {
230
+ targetJid = undefined;
352
231
  }
353
232
  await query({
354
233
  tag: 'iq',
355
234
  attrs: {
356
- target: targetJid,
357
- to: WABinary_1.S_WHATSAPP_NET,
235
+ to: S_WHATSAPP_NET,
358
236
  type: 'set',
359
- xmlns: 'w:profile:picture'
237
+ xmlns: 'w:profile:picture',
238
+ ...(targetJid ? { target: targetJid } : {})
360
239
  }
361
240
  });
362
241
  };
@@ -365,7 +244,7 @@ const makeChatsSocket = (config) => {
365
244
  await query({
366
245
  tag: 'iq',
367
246
  attrs: {
368
- to: WABinary_1.S_WHATSAPP_NET,
247
+ to: S_WHATSAPP_NET,
369
248
  type: 'set',
370
249
  xmlns: 'status'
371
250
  },
@@ -386,49 +265,129 @@ const makeChatsSocket = (config) => {
386
265
  tag: 'iq',
387
266
  attrs: {
388
267
  xmlns: 'blocklist',
389
- to: WABinary_1.S_WHATSAPP_NET,
268
+ to: S_WHATSAPP_NET,
390
269
  type: 'get'
391
270
  }
392
271
  });
393
- const listNode = (0, WABinary_1.getBinaryNodeChild)(result, 'list');
394
- return (0, WABinary_1.getBinaryNodeChildren)(listNode, 'item')
395
- .map(n => n.attrs.jid);
272
+ const listNode = getBinaryNodeChild(result, 'list');
273
+ return getBinaryNodeChildren(listNode, 'item').map(n => n.attrs.jid);
396
274
  };
397
275
  const updateBlockStatus = async (jid, action) => {
276
+ const normalizedJid = jidNormalizedUser(jid);
277
+ let lid;
278
+ let pn_jid;
279
+ if (isLidUser(normalizedJid) || isHostedLidUser(normalizedJid)) {
280
+ lid = normalizedJid;
281
+ if (action === 'block') {
282
+ const pn = await signalRepository.lidMapping.getPNForLID(normalizedJid);
283
+ if (!pn) {
284
+ throw new Boom(`Unable to resolve PN JID for LID: ${jid}`, { statusCode: 400 });
285
+ }
286
+ pn_jid = jidNormalizedUser(pn);
287
+ }
288
+ }
289
+ else if (isPnUser(normalizedJid) || isHostedPnUser(normalizedJid)) {
290
+ const mapped = await signalRepository.lidMapping.getLIDForPN(normalizedJid);
291
+ if (!mapped) {
292
+ throw new Boom(`Unable to resolve LID for PN JID: ${jid}`, { statusCode: 400 });
293
+ }
294
+ lid = mapped;
295
+ if (action === 'block') {
296
+ pn_jid = jidNormalizedUser(normalizedJid);
297
+ }
298
+ }
299
+ else {
300
+ throw new Boom(`Invalid jid: ${jid}`, { statusCode: 400 });
301
+ }
302
+ const itemAttrs = {
303
+ action,
304
+ jid: lid
305
+ };
306
+ if (action === 'block') {
307
+ if (!pn_jid) {
308
+ throw new Boom(`pn_jid required for block: ${jid}`, { statusCode: 400 });
309
+ }
310
+ itemAttrs.pn_jid = pn_jid;
311
+ }
398
312
  await query({
399
313
  tag: 'iq',
400
314
  attrs: {
401
315
  xmlns: 'blocklist',
402
- to: WABinary_1.S_WHATSAPP_NET,
316
+ to: S_WHATSAPP_NET,
403
317
  type: 'set'
404
318
  },
405
319
  content: [
406
320
  {
407
321
  tag: 'item',
408
- attrs: {
409
- action,
410
- jid
411
- }
322
+ attrs: itemAttrs
412
323
  }
413
324
  ]
414
325
  });
415
326
  };
327
+ const getBusinessProfile = async (jid) => {
328
+ const results = await query({
329
+ tag: 'iq',
330
+ attrs: {
331
+ to: 's.whatsapp.net',
332
+ xmlns: 'w:biz',
333
+ type: 'get'
334
+ },
335
+ content: [
336
+ {
337
+ tag: 'business_profile',
338
+ attrs: { v: '244' },
339
+ content: [
340
+ {
341
+ tag: 'profile',
342
+ attrs: { jid }
343
+ }
344
+ ]
345
+ }
346
+ ]
347
+ });
348
+ const profileNode = getBinaryNodeChild(results, 'business_profile');
349
+ const profiles = getBinaryNodeChild(profileNode, 'profile');
350
+ if (profiles) {
351
+ const address = getBinaryNodeChild(profiles, 'address');
352
+ const description = getBinaryNodeChild(profiles, 'description');
353
+ const website = getBinaryNodeChild(profiles, 'website');
354
+ const email = getBinaryNodeChild(profiles, 'email');
355
+ const category = getBinaryNodeChild(getBinaryNodeChild(profiles, 'categories'), 'category');
356
+ const businessHours = getBinaryNodeChild(profiles, 'business_hours');
357
+ const businessHoursConfig = businessHours
358
+ ? getBinaryNodeChildren(businessHours, 'business_hours_config')
359
+ : undefined;
360
+ const websiteStr = website?.content?.toString();
361
+ return {
362
+ wid: profiles.attrs?.jid,
363
+ address: address?.content?.toString(),
364
+ description: description?.content?.toString() || '',
365
+ website: websiteStr ? [websiteStr] : [],
366
+ email: email?.content?.toString(),
367
+ category: category?.content?.toString(),
368
+ business_hours: {
369
+ timezone: businessHours?.attrs?.timezone,
370
+ business_config: businessHoursConfig?.map(({ attrs }) => attrs)
371
+ }
372
+ };
373
+ }
374
+ };
416
375
  const cleanDirtyBits = async (type, fromTimestamp) => {
417
376
  logger.info({ fromTimestamp }, 'clean dirty bits ' + type);
418
377
  await sendNode({
419
378
  tag: 'iq',
420
379
  attrs: {
421
- to: WABinary_1.S_WHATSAPP_NET,
380
+ to: S_WHATSAPP_NET,
422
381
  type: 'set',
423
382
  xmlns: 'urn:xmpp:whatsapp:dirty',
424
- id: generateMessageTag(),
383
+ id: generateMessageTag()
425
384
  },
426
385
  content: [
427
386
  {
428
387
  tag: 'clean',
429
388
  attrs: {
430
389
  type,
431
- ...(fromTimestamp ? { timestamp: fromTimestamp.toString() } : null),
390
+ ...(fromTimestamp ? { timestamp: fromTimestamp.toString() } : null)
432
391
  }
433
392
  }
434
393
  ]
@@ -437,20 +396,31 @@ const makeChatsSocket = (config) => {
437
396
  const newAppStateChunkHandler = (isInitialSync) => {
438
397
  return {
439
398
  onMutation(mutation) {
440
- (0, Utils_1.processSyncAction)(mutation, ev, authState.creds.me, isInitialSync ? { accountSettings: authState.creds.accountSettings } : undefined, logger);
399
+ processSyncAction(mutation, ev, authState.creds.me, isInitialSync ? { accountSettings: authState.creds.accountSettings } : undefined, logger);
441
400
  }
442
401
  };
443
402
  };
444
403
  const resyncAppState = ev.createBufferedFunction(async (collections, isInitialSync) => {
404
+ const appStateSyncKeyCache = new Map();
405
+ const getCachedAppStateSyncKey = async (keyId) => {
406
+ if (appStateSyncKeyCache.has(keyId)) {
407
+ return appStateSyncKeyCache.get(keyId) ?? undefined;
408
+ }
409
+ const key = await getAppStateSyncKey(keyId);
410
+ appStateSyncKeyCache.set(keyId, key ?? null);
411
+ return key;
412
+ };
445
413
  // we use this to determine which events to fire
446
414
  // otherwise when we resync from scratch -- all notifications will fire
447
415
  const initialVersionMap = {};
448
416
  const globalMutationMap = {};
449
417
  await authState.keys.transaction(async () => {
450
- var _a;
451
418
  const collectionsToHandle = new Set(collections);
452
419
  // in case something goes wrong -- ensure we don't enter a loop that cannot be exited from
453
420
  const attemptsMap = {};
421
+ // collections that failed and need a full snapshot on retry
422
+ // mirrors WA Web's ErrorFatal -> force snapshot behavior
423
+ const forceSnapshotCollections = new Set();
454
424
  // keep executing till all collections are done
455
425
  // sometimes a single patch request will not return all the patches (God knows why)
456
426
  // so we fetch till they're all done (this is determined by the "has_more_patches" flag)
@@ -461,29 +431,34 @@ const makeChatsSocket = (config) => {
461
431
  const result = await authState.keys.get('app-state-sync-version', [name]);
462
432
  let state = result[name];
463
433
  if (state) {
434
+ state = ensureLTHashStateVersion(state);
464
435
  if (typeof initialVersionMap[name] === 'undefined') {
465
436
  initialVersionMap[name] = state.version;
466
437
  }
467
438
  }
468
439
  else {
469
- state = (0, Utils_1.newLTHashState)();
440
+ state = newLTHashState();
470
441
  }
471
442
  states[name] = state;
472
- logger.info(`resyncing ${name} from v${state.version}`);
443
+ const shouldForceSnapshot = forceSnapshotCollections.has(name);
444
+ if (shouldForceSnapshot) {
445
+ forceSnapshotCollections.delete(name);
446
+ }
447
+ logger.info(`resyncing ${name} from v${state.version}${shouldForceSnapshot ? ' (forcing snapshot)' : ''}`);
473
448
  nodes.push({
474
449
  tag: 'collection',
475
450
  attrs: {
476
451
  name,
477
452
  version: state.version.toString(),
478
- // return snapshot if being synced from scratch
479
- 'return_snapshot': (!state.version).toString()
453
+ // return snapshot if syncing from scratch or forcing after a failed attempt
454
+ return_snapshot: (shouldForceSnapshot || !state.version).toString()
480
455
  }
481
456
  });
482
457
  }
483
458
  const result = await query({
484
459
  tag: 'iq',
485
460
  attrs: {
486
- to: WABinary_1.S_WHATSAPP_NET,
461
+ to: S_WHATSAPP_NET,
487
462
  xmlns: 'w:sync:app:state',
488
463
  type: 'set'
489
464
  },
@@ -496,26 +471,22 @@ const makeChatsSocket = (config) => {
496
471
  ]
497
472
  });
498
473
  // extract from binary node
499
- const decoded = await (0, Utils_1.extractSyncdPatches)(result, config === null || config === void 0 ? void 0 : config.options);
474
+ const decoded = await extractSyncdPatches(result, config?.options);
500
475
  for (const key in decoded) {
501
476
  const name = key;
502
477
  const { patches, hasMorePatches, snapshot } = decoded[name];
503
478
  try {
504
479
  if (snapshot) {
505
- const { state: newState, mutationMap } = await (0, Utils_1.decodeSyncdSnapshot)(name, snapshot, getAppStateSyncKey, initialVersionMap[name], appStateMacVerification.snapshot);
480
+ const { state: newState, mutationMap } = await decodeSyncdSnapshot(name, snapshot, getCachedAppStateSyncKey, initialVersionMap[name], appStateMacVerification.snapshot, logger);
506
481
  states[name] = newState;
507
482
  Object.assign(globalMutationMap, mutationMap);
508
483
  logger.info(`restored state of ${name} from snapshot to v${newState.version} with mutations`);
509
- await authState.keys.set({ 'app-state-sync-version': {
510
- [name]: newState
511
- } });
484
+ await authState.keys.set({ 'app-state-sync-version': { [name]: newState } });
512
485
  }
513
486
  // only process if there are syncd patches
514
487
  if (patches.length) {
515
- const { state: newState, mutationMap } = await (0, Utils_1.decodePatches)(name, patches, states[name], getAppStateSyncKey, config.options, initialVersionMap[name], logger, appStateMacVerification.patch);
516
- await authState.keys.set({ 'app-state-sync-version': {
517
- [name]: newState
518
- } });
488
+ const { state: newState, mutationMap } = await decodePatches(name, patches, states[name], getCachedAppStateSyncKey, config.options, initialVersionMap[name], logger, appStateMacVerification.patch);
489
+ await authState.keys.set({ 'app-state-sync-version': { [name]: newState } });
519
490
  logger.info(`synced ${name} to v${newState.version}`);
520
491
  initialVersionMap[name] = newState.version;
521
492
  Object.assign(globalMutationMap, mutationMap);
@@ -523,30 +494,47 @@ const makeChatsSocket = (config) => {
523
494
  if (hasMorePatches) {
524
495
  logger.info(`${name} has more patches...`);
525
496
  }
526
- else { // collection is done with sync
497
+ else {
498
+ // collection is done with sync
527
499
  collectionsToHandle.delete(name);
528
500
  }
529
501
  }
530
502
  catch (error) {
531
- // if retry attempts overshoot
532
- // or key not found
533
- const isIrrecoverableError = attemptsMap[name] >= MAX_SYNC_ATTEMPTS ||
534
- ((_a = error.output) === null || _a === void 0 ? void 0 : _a.statusCode) === 404 ||
535
- error.name === 'TypeError';
536
- logger.info({ name, error: error.stack }, `failed to sync state from version${isIrrecoverableError ? '' : ', removing and trying from scratch'}`);
537
- await authState.keys.set({ 'app-state-sync-version': {
538
- [name]: null
539
- } });
540
- // increment number of retries
541
503
  attemptsMap[name] = (attemptsMap[name] || 0) + 1;
542
- if (isIrrecoverableError) {
543
- // stop retrying
504
+ const logData = {
505
+ name,
506
+ attempt: attemptsMap[name],
507
+ version: states[name].version,
508
+ statusCode: error.output?.statusCode,
509
+ errorType: error.name,
510
+ error: error.stack
511
+ };
512
+ if (isMissingKeyError(error) && attemptsMap[name] >= MAX_SYNC_ATTEMPTS) {
513
+ // WA Web treats missing keys as "Blocked" — park the collection
514
+ // until the key arrives via APP_STATE_SYNC_KEY_SHARE.
515
+ logger.warn(logData, `${name} blocked on missing key from v${states[name].version}, parking after ${attemptsMap[name]} attempts`);
516
+ blockedCollections.add(name);
544
517
  collectionsToHandle.delete(name);
545
518
  }
519
+ else if (isMissingKeyError(error)) {
520
+ // Retry with a snapshot which may use a different key.
521
+ logger.info(logData, `${name} blocked on missing key from v${states[name].version}, retrying with snapshot`);
522
+ forceSnapshotCollections.add(name);
523
+ }
524
+ else if (isAppStateSyncIrrecoverable(error, attemptsMap[name])) {
525
+ logger.warn(logData, `failed to sync ${name} from v${states[name].version}, giving up`);
526
+ collectionsToHandle.delete(name);
527
+ }
528
+ else {
529
+ logger.info(logData, `failed to sync ${name} from v${states[name].version}, forcing snapshot retry`);
530
+ // force a full snapshot on retry to recover from
531
+ // corrupted local state (e.g. LTHash MAC mismatch)
532
+ forceSnapshotCollections.add(name);
533
+ }
546
534
  }
547
535
  }
548
536
  }
549
- });
537
+ }, authState?.creds?.me?.id || 'resync-app-state');
550
538
  const { onMutation } = newAppStateChunkHandler(isInitialSync);
551
539
  for (const key in globalMutationMap) {
552
540
  onMutation(globalMutationMap[key]);
@@ -558,47 +546,83 @@ const makeChatsSocket = (config) => {
558
546
  * type = "image for the high res picture"
559
547
  */
560
548
  const profilePictureUrl = async (jid, type = 'preview', timeoutMs) => {
561
- var _a;
562
- jid = (0, WABinary_1.jidNormalizedUser)(jid);
549
+ const baseContent = [{ tag: 'picture', attrs: { type, query: 'url' } }];
550
+ // WA Web only includes tctoken for user JIDs (not groups/newsletters)
551
+ // and never for own profile pic (Chat model for self has no tcToken).
552
+ // Including tctoken for own JID causes the server to never respond.
553
+ const normalizedJid = jidNormalizedUser(jid);
554
+ const isUserJid = isPnUser(normalizedJid) || isLidUser(normalizedJid);
555
+ const me = authState.creds.me;
556
+ const isSelf = me && (normalizedJid === jidNormalizedUser(me.id) || (me.lid && normalizedJid === jidNormalizedUser(me.lid)));
557
+ let content = baseContent;
558
+ if (serverProps.profilePicPrivacyToken && isUserJid && !isSelf) {
559
+ content = await buildTcTokenFromJid({
560
+ authState,
561
+ jid: normalizedJid,
562
+ baseContent,
563
+ getLIDForPN
564
+ });
565
+ }
566
+ jid = jidNormalizedUser(jid);
563
567
  const result = await query({
564
568
  tag: 'iq',
565
569
  attrs: {
566
570
  target: jid,
567
- to: WABinary_1.S_WHATSAPP_NET,
571
+ to: S_WHATSAPP_NET,
568
572
  type: 'get',
569
573
  xmlns: 'w:profile:picture'
570
574
  },
575
+ content
576
+ }, timeoutMs);
577
+ const child = getBinaryNodeChild(result, 'picture');
578
+ return child?.attrs?.url;
579
+ };
580
+ const createCallLink = async (type, event, timeoutMs) => {
581
+ const result = await query({
582
+ tag: 'call',
583
+ attrs: {
584
+ id: generateMessageTag(),
585
+ to: '@call'
586
+ },
571
587
  content: [
572
- { tag: 'picture', attrs: { type, query: 'url' } }
588
+ {
589
+ tag: 'link_create',
590
+ attrs: { media: type },
591
+ content: event ? [{ tag: 'event', attrs: { start_time: String(event.startTime) } }] : undefined
592
+ }
573
593
  ]
574
594
  }, timeoutMs);
575
- const child = (0, WABinary_1.getBinaryNodeChild)(result, 'picture');
576
- return (_a = child === null || child === void 0 ? void 0 : child.attrs) === null || _a === void 0 ? void 0 : _a.url;
595
+ const child = getBinaryNodeChild(result, 'link_create');
596
+ return child?.attrs?.token;
577
597
  };
578
598
  const sendPresenceUpdate = async (type, toJid) => {
579
599
  const me = authState.creds.me;
580
- if (type === 'available' || type === 'unavailable') {
600
+ const isAvailableType = type === 'available';
601
+ if (isAvailableType || type === 'unavailable') {
581
602
  if (!me.name) {
582
603
  logger.warn('no name present, ignoring presence update request...');
583
604
  return;
584
605
  }
585
- ev.emit('connection.update', { isOnline: type === 'available' });
606
+ ev.emit('connection.update', { isOnline: isAvailableType });
607
+ if (isAvailableType) {
608
+ void sendUnifiedSession();
609
+ }
586
610
  await sendNode({
587
611
  tag: 'presence',
588
612
  attrs: {
589
- name: me.name,
613
+ name: me.name.replace(/@/g, ''),
590
614
  type
591
615
  }
592
616
  });
593
617
  }
594
618
  else {
595
- const { server } = (0, WABinary_1.jidDecode)(toJid);
619
+ const { server } = jidDecode(toJid);
596
620
  const isLid = server === 'lid';
597
621
  await sendNode({
598
622
  tag: 'chatstate',
599
623
  attrs: {
600
624
  from: isLid ? me.lid : me.id,
601
- to: toJid,
625
+ to: toJid
602
626
  },
603
627
  content: [
604
628
  {
@@ -613,35 +637,35 @@ const makeChatsSocket = (config) => {
613
637
  * @param toJid the jid to subscribe to
614
638
  * @param tcToken token for subscription, use if present
615
639
  */
616
- const presenceSubscribe = (toJid, tcToken) => (sendNode({
617
- tag: 'presence',
618
- attrs: {
619
- to: toJid,
620
- id: generateMessageTag(),
621
- type: 'subscribe'
622
- },
623
- content: tcToken ?
624
- [
625
- {
626
- tag: 'tctoken',
627
- attrs: {},
628
- content: tcToken
629
- }
630
- ] :
631
- undefined
632
- }));
640
+ const presenceSubscribe = async (toJid) => {
641
+ // Only include tctoken for user JIDs — groups/newsletters don't use tctokens
642
+ const normalizedToJid = jidNormalizedUser(toJid);
643
+ const isUserJid = isPnUser(normalizedToJid) || isLidUser(normalizedToJid);
644
+ const tcTokenContent = isUserJid
645
+ ? await buildTcTokenFromJid({ authState, jid: normalizedToJid, getLIDForPN })
646
+ : undefined;
647
+ return sendNode({
648
+ tag: 'presence',
649
+ attrs: {
650
+ to: toJid,
651
+ id: generateMessageTag(),
652
+ type: 'subscribe'
653
+ },
654
+ content: tcTokenContent
655
+ });
656
+ };
633
657
  const handlePresenceUpdate = ({ tag, attrs, content }) => {
634
- var _a;
635
658
  let presence;
636
659
  const jid = attrs.from;
637
660
  const participant = attrs.participant || attrs.from;
638
- if (shouldIgnoreJid(jid) && jid !== '@s.whatsapp.net') {
661
+ if (shouldIgnoreJid(jid) && jid !== S_WHATSAPP_NET) {
639
662
  return;
640
663
  }
641
664
  if (tag === 'presence') {
642
665
  presence = {
643
666
  lastKnownPresence: attrs.type === 'unavailable' ? 'unavailable' : 'available',
644
- lastSeen: attrs.last && attrs.last !== 'deny' ? +attrs.last : undefined
667
+ lastSeen: attrs.last && attrs.last !== 'deny' ? +attrs.last : undefined,
668
+ groupOnlineCount: attrs.count ? +attrs.count : undefined
645
669
  };
646
670
  }
647
671
  else if (Array.isArray(content)) {
@@ -650,7 +674,7 @@ const makeChatsSocket = (config) => {
650
674
  if (type === 'paused') {
651
675
  type = 'available';
652
676
  }
653
- if (((_a = firstChild.attrs) === null || _a === void 0 ? void 0 : _a.media) === 'audio') {
677
+ if (firstChild.attrs?.media === 'audio') {
654
678
  type = 'recording';
655
679
  }
656
680
  presence = { lastKnownPresence: type };
@@ -659,31 +683,29 @@ const makeChatsSocket = (config) => {
659
683
  logger.error({ tag, attrs, content }, 'recv invalid presence node');
660
684
  }
661
685
  if (presence) {
662
- ev.emit('presence.update', { id: jid, presences: {
663
- [participant]: presence
664
- } });
686
+ ev.emit('presence.update', { id: jid, presences: { [participant]: presence } });
665
687
  }
666
688
  };
667
689
  const appPatch = async (patchCreate) => {
668
690
  const name = patchCreate.type;
669
691
  const myAppStateKeyId = authState.creds.myAppStateKeyId;
670
692
  if (!myAppStateKeyId) {
671
- throw new boom_1.Boom('App state key not present!', { statusCode: 400 });
693
+ throw new Boom('App state key not present!', { statusCode: 400 });
672
694
  }
673
695
  let initial;
674
696
  let encodeResult;
675
- await processingMutex.mutex(async () => {
697
+ await appStatePatchMutex.mutex(async () => {
676
698
  await authState.keys.transaction(async () => {
677
699
  logger.debug({ patch: patchCreate }, 'applying app patch');
678
700
  await resyncAppState([name], false);
679
701
  const { [name]: currentSyncVersion } = await authState.keys.get('app-state-sync-version', [name]);
680
- initial = currentSyncVersion || (0, Utils_1.newLTHashState)();
681
- encodeResult = await (0, Utils_1.encodeSyncdPatch)(patchCreate, myAppStateKeyId, initial, getAppStateSyncKey);
702
+ initial = currentSyncVersion ? ensureLTHashStateVersion(currentSyncVersion) : newLTHashState();
703
+ encodeResult = await encodeSyncdPatch(patchCreate, myAppStateKeyId, initial, getAppStateSyncKey);
682
704
  const { patch, state } = encodeResult;
683
705
  const node = {
684
706
  tag: 'iq',
685
707
  attrs: {
686
- to: WABinary_1.S_WHATSAPP_NET,
708
+ to: S_WHATSAPP_NET,
687
709
  type: 'set',
688
710
  xmlns: 'w:sync:app:state'
689
711
  },
@@ -697,13 +719,13 @@ const makeChatsSocket = (config) => {
697
719
  attrs: {
698
720
  name,
699
721
  version: (state.version - 1).toString(),
700
- 'return_snapshot': 'false'
722
+ return_snapshot: 'false'
701
723
  },
702
724
  content: [
703
725
  {
704
726
  tag: 'patch',
705
727
  attrs: {},
706
- content: WAProto_1.proto.SyncdPatch.encode(patch).finish()
728
+ content: proto.SyncdPatch.encode(patch).finish()
707
729
  }
708
730
  ]
709
731
  }
@@ -712,47 +734,60 @@ const makeChatsSocket = (config) => {
712
734
  ]
713
735
  };
714
736
  await query(node);
715
- await authState.keys.set({ 'app-state-sync-version': {
716
- [name]: state
717
- } });
718
- });
737
+ await authState.keys.set({ 'app-state-sync-version': { [name]: state } });
738
+ }, authState?.creds?.me?.id || 'app-patch');
719
739
  });
720
740
  if (config.emitOwnEvents) {
721
741
  const { onMutation } = newAppStateChunkHandler(false);
722
- const { mutationMap } = await (0, Utils_1.decodePatches)(name, [{ ...encodeResult.patch, version: { version: encodeResult.state.version }, }], initial, getAppStateSyncKey, config.options, undefined, logger);
742
+ const { mutationMap } = await decodePatches(name, [{ ...encodeResult.patch, version: { version: encodeResult.state.version } }], initial, getAppStateSyncKey, config.options, undefined, logger);
723
743
  for (const key in mutationMap) {
724
744
  onMutation(mutationMap[key]);
725
745
  }
726
746
  }
727
747
  };
728
- /** sending non-abt props may fix QR scan fail if server expects */
748
+ /** fetch AB props */
729
749
  const fetchProps = async () => {
730
- var _a, _b;
731
750
  const resultNode = await query({
732
751
  tag: 'iq',
733
752
  attrs: {
734
- to: WABinary_1.S_WHATSAPP_NET,
735
- xmlns: 'w',
736
- type: 'get',
753
+ to: S_WHATSAPP_NET,
754
+ xmlns: 'abt',
755
+ type: 'get'
737
756
  },
738
757
  content: [
739
758
  {
740
759
  tag: 'props',
741
760
  attrs: {
742
- protocol: '2',
743
- hash: ((_a = authState === null || authState === void 0 ? void 0 : authState.creds) === null || _a === void 0 ? void 0 : _a.lastPropHash) || ''
761
+ protocol: '1',
762
+ ...(authState?.creds?.lastPropHash ? { hash: authState.creds.lastPropHash } : {})
744
763
  }
745
764
  }
746
765
  ]
747
766
  });
748
- const propsNode = (0, WABinary_1.getBinaryNodeChild)(resultNode, 'props');
767
+ const propsNode = getBinaryNodeChild(resultNode, 'props');
749
768
  let props = {};
750
769
  if (propsNode) {
751
- authState.creds.lastPropHash = (_b = propsNode === null || propsNode === void 0 ? void 0 : propsNode.attrs) === null || _b === void 0 ? void 0 : _b.hash;
752
- ev.emit('creds.update', authState.creds);
753
- props = (0, WABinary_1.reduceBinaryNodeToDictionary)(propsNode, 'prop');
770
+ if (propsNode.attrs?.hash) {
771
+ // on some clients, the hash is returning as undefined
772
+ authState.creds.lastPropHash = propsNode?.attrs?.hash;
773
+ ev.emit('creds.update', authState.creds);
774
+ }
775
+ props = reduceBinaryNodeToDictionary(propsNode, 'prop');
776
+ }
777
+ // Extract protocol-relevant AB props (only the ones we need)
778
+ const privacyTokenProp = props['10518'] ?? props['privacy_token_sending_on_all_1_on_1_messages'];
779
+ if (privacyTokenProp !== undefined) {
780
+ serverProps.privacyTokenOn1to1 = privacyTokenProp === 'true' || privacyTokenProp === '1';
754
781
  }
755
- logger.debug('fetched props');
782
+ const profilePicProp = props['9666'] ?? props['profile_scraping_privacy_token_in_photo_iq'];
783
+ if (profilePicProp !== undefined) {
784
+ serverProps.profilePicPrivacyToken = profilePicProp === 'true' || profilePicProp === '1';
785
+ }
786
+ const lidIssueProp = props['14303'] ?? props['lid_trusted_token_issue_to_lid'];
787
+ if (lidIssueProp !== undefined) {
788
+ serverProps.lidTrustedTokenIssueToLid = lidIssueProp === 'true' || lidIssueProp === '1';
789
+ }
790
+ logger.debug({ serverProps }, 'fetched props');
756
791
  return props;
757
792
  };
758
793
  /**
@@ -761,9 +796,17 @@ const makeChatsSocket = (config) => {
761
796
  * requires the last messages till the last message received; required for archive & unread
762
797
  */
763
798
  const chatModify = (mod, jid) => {
764
- const patch = (0, Utils_1.chatModificationToAppPatch)(mod, jid);
799
+ const patch = chatModificationToAppPatch(mod, jid);
765
800
  return appPatch(patch);
766
801
  };
802
+ /**
803
+ * Enable/Disable link preview privacy, not related to baileys link preview generation
804
+ */
805
+ const updateDisableLinkPreviewsPrivacy = (isPreviewsDisabled) => {
806
+ return chatModify({
807
+ disableLinkPreviews: { isPreviewsDisabled }
808
+ }, '');
809
+ };
767
810
  /**
768
811
  * Star or Unstar a message
769
812
  */
@@ -775,6 +818,32 @@ const makeChatsSocket = (config) => {
775
818
  }
776
819
  }, jid);
777
820
  };
821
+ /**
822
+ * Add or Edit Contact
823
+ */
824
+ const addOrEditContact = (jid, contact) => {
825
+ return chatModify({
826
+ contact
827
+ }, jid);
828
+ };
829
+ /**
830
+ * Remove Contact
831
+ */
832
+ const removeContact = (jid) => {
833
+ return chatModify({
834
+ contact: null
835
+ }, jid);
836
+ };
837
+ /**
838
+ * Adds label
839
+ */
840
+ const addLabel = (jid, labels) => {
841
+ return chatModify({
842
+ addLabel: {
843
+ ...labels
844
+ }
845
+ }, jid);
846
+ };
778
847
  /**
779
848
  * Adds label for the chats
780
849
  */
@@ -817,80 +886,147 @@ const makeChatsSocket = (config) => {
817
886
  }
818
887
  }, jid);
819
888
  };
889
+ /**
890
+ * Add or Edit Quick Reply
891
+ */
892
+ const addOrEditQuickReply = (quickReply) => {
893
+ return chatModify({
894
+ quickReply
895
+ }, '');
896
+ };
897
+ /**
898
+ * Remove Quick Reply
899
+ */
900
+ const removeQuickReply = (timestamp) => {
901
+ return chatModify({
902
+ quickReply: { timestamp, deleted: true }
903
+ }, '');
904
+ };
820
905
  /**
821
906
  * queries need to be fired on connection open
822
907
  * help ensure parity with WA Web
823
908
  * */
824
909
  const executeInitQueries = async () => {
825
- await Promise.all([
826
- fetchProps(),
827
- fetchBlocklist(),
828
- fetchPrivacySettings(),
829
- ]);
910
+ await Promise.all([fetchProps(), fetchBlocklist(), fetchPrivacySettings()]);
830
911
  };
831
912
  const upsertMessage = ev.createBufferedFunction(async (msg, type) => {
832
- var _a, _b, _c;
833
913
  ev.emit('messages.upsert', { messages: [msg], type });
834
914
  if (!!msg.pushName) {
835
- let jid = msg.key.fromMe ? authState.creds.me.id : (msg.key.participant || msg.key.remoteJid);
836
- jid = (0, WABinary_1.jidNormalizedUser)(jid);
915
+ let jid = msg.key.fromMe ? authState.creds.me.id : msg.key.participant || msg.key.remoteJid;
916
+ jid = jidNormalizedUser(jid);
837
917
  if (!msg.key.fromMe) {
838
918
  ev.emit('contacts.update', [{ id: jid, notify: msg.pushName, verifiedName: msg.verifiedBizName }]);
839
919
  }
840
920
  // update our pushname too
841
- if (msg.key.fromMe && msg.pushName && ((_a = authState.creds.me) === null || _a === void 0 ? void 0 : _a.name) !== msg.pushName) {
921
+ if (msg.key.fromMe && msg.pushName && authState.creds.me?.name !== msg.pushName) {
842
922
  ev.emit('creds.update', { me: { ...authState.creds.me, name: msg.pushName } });
843
923
  }
844
924
  }
845
- const historyMsg = (0, Utils_1.getHistoryMsg)(msg.message);
846
- const shouldProcessHistoryMsg = historyMsg ?
847
- (shouldSyncHistoryMessage(historyMsg) &&
848
- Defaults_1.PROCESSABLE_HISTORY_TYPES.includes(historyMsg.syncType)) :
849
- false;
850
- if (historyMsg && !authState.creds.myAppStateKeyId) {
851
- logger.warn('skipping app state sync, as myAppStateKeyId is not set');
852
- pendingAppStateSync = true;
925
+ const historyMsg = getHistoryMsg(msg.message);
926
+ const shouldProcessHistoryMsg = historyMsg
927
+ ? shouldSyncHistoryMessage(historyMsg) &&
928
+ PROCESSABLE_HISTORY_TYPES.includes(historyMsg.syncType)
929
+ : false;
930
+ if (historyMsg && shouldProcessHistoryMsg) {
931
+ const syncType = historyMsg.syncType;
932
+ // INITIAL_BOOTSTRAP — fire immediately, no progress check (same as WA Web K function)
933
+ if (syncType === proto.HistorySync.HistorySyncType.INITIAL_BOOTSTRAP &&
934
+ !historySyncStatus.initialBootstrapComplete) {
935
+ historySyncStatus.initialBootstrapComplete = true;
936
+ ev.emit('messaging-history.status', {
937
+ syncType,
938
+ status: 'complete',
939
+ explicit: true
940
+ });
941
+ }
942
+ // RECENT with progress === 100 — explicit completion
943
+ if (syncType === proto.HistorySync.HistorySyncType.RECENT &&
944
+ historyMsg.progress === 100 &&
945
+ !historySyncStatus.recentSyncComplete) {
946
+ historySyncStatus.recentSyncComplete = true;
947
+ clearTimeout(historySyncPausedTimeout);
948
+ historySyncPausedTimeout = undefined;
949
+ ev.emit('messaging-history.status', {
950
+ syncType,
951
+ status: 'complete',
952
+ explicit: true
953
+ });
954
+ }
955
+ // Reset 120s paused timeout on any RECENT chunk (like WA Web's handleChunkProgress)
956
+ if (syncType === proto.HistorySync.HistorySyncType.RECENT && !historySyncStatus.recentSyncComplete) {
957
+ clearTimeout(historySyncPausedTimeout);
958
+ historySyncPausedTimeout = setTimeout(() => {
959
+ if (!historySyncStatus.recentSyncComplete) {
960
+ historySyncStatus.recentSyncComplete = true;
961
+ ev.emit('messaging-history.status', {
962
+ syncType: proto.HistorySync.HistorySyncType.RECENT,
963
+ status: 'paused',
964
+ explicit: false
965
+ });
966
+ }
967
+ historySyncPausedTimeout = undefined;
968
+ }, HISTORY_SYNC_PAUSED_TIMEOUT_MS);
969
+ }
970
+ }
971
+ // State machine: decide on sync and flush
972
+ if (historyMsg && syncState === SyncState.AwaitingInitialSync) {
973
+ if (awaitingSyncTimeout) {
974
+ clearTimeout(awaitingSyncTimeout);
975
+ awaitingSyncTimeout = undefined;
976
+ }
977
+ if (shouldProcessHistoryMsg) {
978
+ syncState = SyncState.Syncing;
979
+ logger.info('Transitioned to Syncing state');
980
+ // Let doAppStateSync handle the final flush after it's done
981
+ }
982
+ else {
983
+ syncState = SyncState.Online;
984
+ logger.info('History sync skipped, transitioning to Online state and flushing buffer');
985
+ ev.flush();
986
+ }
853
987
  }
988
+ const doAppStateSync = async () => {
989
+ if (syncState === SyncState.Syncing) {
990
+ // All collections will be synced, so clear any blocked ones
991
+ blockedCollections.clear();
992
+ logger.info('Doing app state sync');
993
+ await resyncAppState(ALL_WA_PATCH_NAMES, true);
994
+ // Sync is complete, go online and flush everything
995
+ syncState = SyncState.Online;
996
+ logger.info('App state sync complete, transitioning to Online state and flushing buffer');
997
+ ev.flush();
998
+ const accountSyncCounter = (authState.creds.accountSyncCounter || 0) + 1;
999
+ ev.emit('creds.update', { accountSyncCounter });
1000
+ }
1001
+ };
854
1002
  await Promise.all([
855
1003
  (async () => {
856
- if (historyMsg &&
857
- authState.creds.myAppStateKeyId) {
858
- pendingAppStateSync = false;
1004
+ if (shouldProcessHistoryMsg) {
859
1005
  await doAppStateSync();
860
1006
  }
861
1007
  })(),
862
- (0, process_message_1.default)(msg, {
1008
+ processMessage(msg, {
1009
+ signalRepository,
863
1010
  shouldProcessHistoryMsg,
1011
+ placeholderResendCache,
864
1012
  ev,
865
1013
  creds: authState.creds,
866
1014
  keyStore: authState.keys,
867
1015
  logger,
868
1016
  options: config.options,
869
- getMessage: config.getMessage,
1017
+ getMessage
870
1018
  })
871
1019
  ]);
872
- if (((_c = (_b = msg.message) === null || _b === void 0 ? void 0 : _b.protocolMessage) === null || _c === void 0 ? void 0 : _c.appStateSyncKeyShare) &&
873
- pendingAppStateSync) {
1020
+ // If the app state key arrives and we are waiting to sync, trigger the sync now.
1021
+ if (msg.message?.protocolMessage?.appStateSyncKeyShare && syncState === SyncState.Syncing) {
1022
+ logger.info('App state sync key arrived, triggering app state sync');
874
1023
  await doAppStateSync();
875
- pendingAppStateSync = false;
876
- }
877
- async function doAppStateSync() {
878
- if (!authState.creds.accountSyncCounter) {
879
- logger.info('doing initial app state sync');
880
- await resyncAppState(Types_1.ALL_WA_PATCH_NAMES, true);
881
- const accountSyncCounter = (authState.creds.accountSyncCounter || 0) + 1;
882
- ev.emit('creds.update', { accountSyncCounter });
883
- if (needToFlushWithAppStateSync) {
884
- logger.debug('flushing with app state sync');
885
- ev.flush();
886
- }
887
- }
888
1024
  }
889
1025
  });
890
1026
  ws.on('CB:presence', handlePresenceUpdate);
891
1027
  ws.on('CB:chatstate', handlePresenceUpdate);
892
1028
  ws.on('CB:ib,,dirty', async (node) => {
893
- const { attrs } = (0, WABinary_1.getBinaryNodeChild)(node, 'dirty');
1029
+ const { attrs } = getBinaryNodeChild(node, 'dirty');
894
1030
  const type = attrs.type;
895
1031
  switch (type) {
896
1032
  case 'account_sync':
@@ -912,42 +1048,124 @@ const makeChatsSocket = (config) => {
912
1048
  }
913
1049
  });
914
1050
  ev.on('connection.update', ({ connection, receivedPendingNotifications }) => {
915
- var _a;
1051
+ if (connection === 'close') {
1052
+ blockedCollections.clear();
1053
+ clearTimeout(historySyncPausedTimeout);
1054
+ historySyncPausedTimeout = undefined;
1055
+ }
916
1056
  if (connection === 'open') {
917
1057
  if (fireInitQueries) {
918
- executeInitQueries()
919
- .catch(error => onUnexpectedError(error, 'init queries'));
1058
+ executeInitQueries().catch(error => onUnexpectedError(error, 'init queries'));
920
1059
  }
921
- sendPresenceUpdate(markOnlineOnConnect ? 'available' : 'unavailable')
922
- .catch(error => onUnexpectedError(error, 'presence update requests'));
1060
+ sendPresenceUpdate(markOnlineOnConnect ? 'available' : 'unavailable').catch(error => onUnexpectedError(error, 'presence update requests'));
1061
+ }
1062
+ if (!receivedPendingNotifications || syncState !== SyncState.Connecting) {
1063
+ return;
1064
+ }
1065
+ historySyncStatus.initialBootstrapComplete = false;
1066
+ historySyncStatus.recentSyncComplete = false;
1067
+ clearTimeout(historySyncPausedTimeout);
1068
+ historySyncPausedTimeout = undefined;
1069
+ syncState = SyncState.AwaitingInitialSync;
1070
+ logger.info('Connection is now AwaitingInitialSync, buffering events');
1071
+ ev.buffer();
1072
+ const willSyncHistory = shouldSyncHistoryMessage(proto.Message.HistorySyncNotification.create({
1073
+ syncType: proto.HistorySync.HistorySyncType.RECENT
1074
+ }));
1075
+ if (!willSyncHistory) {
1076
+ logger.info('History sync is disabled by config, not waiting for notification. Transitioning to Online.');
1077
+ syncState = SyncState.Online;
1078
+ setTimeout(() => ev.flush(), 0);
1079
+ return;
1080
+ }
1081
+ // On reconnection (accountSyncCounter > 0), the server does not push
1082
+ // history sync notifications — the device already has its data.
1083
+ // Skip the 20s wait and go online immediately.
1084
+ if (authState.creds.accountSyncCounter > 0) {
1085
+ logger.info('Reconnection with existing sync data, skipping history sync wait. Transitioning to Online.');
1086
+ syncState = SyncState.Online;
1087
+ setTimeout(() => ev.flush(), 0);
1088
+ return;
1089
+ }
1090
+ logger.info('First connection, awaiting history sync notification with a 20s timeout.');
1091
+ if (awaitingSyncTimeout) {
1092
+ clearTimeout(awaitingSyncTimeout);
923
1093
  }
924
- if (receivedPendingNotifications) {
925
- // if we don't have the app state key
926
- // we keep buffering events until we finally have
927
- // the key and can sync the messages
928
- if (!((_a = authState.creds) === null || _a === void 0 ? void 0 : _a.myAppStateKeyId) && !config.mobile) {
929
- ev.buffer();
930
- needToFlushWithAppStateSync = true;
1094
+ awaitingSyncTimeout = setTimeout(() => {
1095
+ if (syncState === SyncState.AwaitingInitialSync) {
1096
+ logger.warn('Timeout in AwaitingInitialSync, forcing state to Online and flushing buffer');
1097
+ syncState = SyncState.Online;
1098
+ ev.flush();
1099
+ // Increment so subsequent reconnections skip the 20s wait.
1100
+ // Late-arriving history is still processed via processMessage
1101
+ // regardless of the state machine phase.
1102
+ const accountSyncCounter = (authState.creds.accountSyncCounter || 0) + 1;
1103
+ ev.emit('creds.update', { accountSyncCounter });
931
1104
  }
1105
+ }, 20000);
1106
+ });
1107
+ // When an app state sync key arrives (myAppStateKeyId is set) and there are
1108
+ // collections blocked on a missing key, trigger a re-sync for just those collections.
1109
+ // This mirrors WA Web's Blocked → retry-on-key-arrival behavior.
1110
+ ev.on('creds.update', ({ myAppStateKeyId }) => {
1111
+ if (!myAppStateKeyId || blockedCollections.size === 0) {
1112
+ return;
1113
+ }
1114
+ // If we're in the middle of a full sync, doAppStateSync handles all collections
1115
+ if (syncState === SyncState.Syncing) {
1116
+ blockedCollections.clear();
1117
+ return;
1118
+ }
1119
+ const collections = [...blockedCollections];
1120
+ blockedCollections.clear();
1121
+ logger.info({ collections }, 'app state sync key arrived, re-syncing blocked collections');
1122
+ resyncAppState(collections, false).catch(error => onUnexpectedError(error, 'blocked collections resync'));
1123
+ });
1124
+ ev.on('lid-mapping.update', async ({ lid, pn }) => {
1125
+ try {
1126
+ await signalRepository.lidMapping.storeLIDPNMappings([{ lid, pn }]);
1127
+ }
1128
+ catch (error) {
1129
+ logger.warn({ lid, pn, error }, 'Failed to store LID-PN mapping');
1130
+ }
1131
+ });
1132
+ registerSocketEndHandler(() => {
1133
+ if (awaitingSyncTimeout) {
1134
+ clearTimeout(awaitingSyncTimeout);
1135
+ awaitingSyncTimeout = undefined;
1136
+ }
1137
+ if (!config.placeholderResendCache && placeholderResendCache.close) {
1138
+ placeholderResendCache.close();
932
1139
  }
1140
+ syncState = SyncState.Connecting;
1141
+ privacySettings = undefined;
933
1142
  });
934
1143
  return {
935
1144
  ...sock,
936
- processingMutex,
1145
+ serverProps,
1146
+ createCallLink,
1147
+ getBotListV2,
1148
+ messageMutex,
1149
+ receiptMutex,
1150
+ appStatePatchMutex,
1151
+ notificationMutex,
937
1152
  fetchPrivacySettings,
938
1153
  upsertMessage,
939
1154
  appPatch,
940
1155
  sendPresenceUpdate,
941
1156
  presenceSubscribe,
942
1157
  profilePictureUrl,
943
- onWhatsApp,
944
1158
  fetchBlocklist,
945
1159
  fetchStatus,
1160
+ fetchDisappearingDuration,
946
1161
  updateProfilePicture,
947
1162
  removeProfilePicture,
948
1163
  updateProfileStatus,
949
1164
  updateProfileName,
950
1165
  updateBlockStatus,
1166
+ updateDisableLinkPreviewsPrivacy,
1167
+ updateCallPrivacy,
1168
+ updateMessagesPrivacy,
951
1169
  updateLastSeenPrivacy,
952
1170
  updateOnlinePrivacy,
953
1171
  updateProfilePicturePrivacy,
@@ -959,12 +1177,17 @@ const makeChatsSocket = (config) => {
959
1177
  resyncAppState,
960
1178
  chatModify,
961
1179
  cleanDirtyBits,
1180
+ addOrEditContact,
1181
+ removeContact,
1182
+ placeholderResendCache,
1183
+ addLabel,
962
1184
  addChatLabel,
963
1185
  removeChatLabel,
964
1186
  addMessageLabel,
965
- checkStatusWA,
966
1187
  removeMessageLabel,
967
- star
1188
+ star,
1189
+ addOrEditQuickReply,
1190
+ removeQuickReply
968
1191
  };
969
1192
  };
970
- exports.makeChatsSocket = makeChatsSocket;
1193
+ //# sourceMappingURL=chats.js.map