@ubedsockets/baileys 1.6.4 → 1.6.5

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 (172) hide show
  1. package/lib/Socket/Client/index.d.ts +3 -0
  2. package/lib/Socket/Client/index.d.ts.map +1 -0
  3. package/lib/Socket/Client/index.js +3 -18
  4. package/lib/Socket/Client/index.js.map +1 -0
  5. package/lib/Socket/Client/types.d.ts +16 -0
  6. package/lib/Socket/Client/types.d.ts.map +1 -0
  7. package/lib/Socket/Client/types.js +4 -6
  8. package/lib/Socket/Client/types.js.map +1 -0
  9. package/lib/Socket/Client/websocket.d.ts +13 -0
  10. package/lib/Socket/Client/websocket.d.ts.map +1 -0
  11. package/lib/Socket/Client/websocket.js +21 -78
  12. package/lib/Socket/Client/websocket.js.map +1 -0
  13. package/lib/Socket/business.d.ts +217 -0
  14. package/lib/Socket/business.d.ts.map +1 -0
  15. package/lib/Socket/business.js +162 -43
  16. package/lib/Socket/business.js.map +1 -0
  17. package/lib/Socket/chats.d.ts +124 -0
  18. package/lib/Socket/chats.d.ts.map +1 -0
  19. package/lib/Socket/chats.js +571 -249
  20. package/lib/Socket/chats.js.map +1 -0
  21. package/lib/Socket/communities.d.ts +273 -0
  22. package/lib/Socket/communities.d.ts.map +1 -0
  23. package/lib/Socket/communities.js +431 -0
  24. package/lib/Socket/communities.js.map +1 -0
  25. package/lib/Socket/groups.d.ts +161 -0
  26. package/lib/Socket/groups.d.ts.map +1 -0
  27. package/lib/Socket/groups.js +114 -99
  28. package/lib/Socket/groups.js.map +1 -0
  29. package/lib/Socket/index.d.ts +260 -0
  30. package/lib/Socket/index.d.ts.map +1 -0
  31. package/lib/Socket/index.js +11 -9
  32. package/lib/Socket/index.js.map +1 -0
  33. package/lib/Socket/messages-recv.d.ts +203 -0
  34. package/lib/Socket/messages-recv.d.ts.map +1 -0
  35. package/lib/Socket/messages-recv.js +1277 -559
  36. package/lib/Socket/messages-recv.js.map +1 -0
  37. package/lib/Socket/messages-send.d.ts +199 -0
  38. package/lib/Socket/messages-send.d.ts.map +1 -0
  39. package/lib/Socket/messages-send.js +674 -669
  40. package/lib/Socket/messages-send.js.map +1 -0
  41. package/lib/Socket/mex.d.ts +3 -0
  42. package/lib/Socket/mex.d.ts.map +1 -0
  43. package/lib/Socket/mex.js +42 -0
  44. package/lib/Socket/mex.js.map +1 -0
  45. package/lib/Socket/newsletter.d.ts +170 -0
  46. package/lib/Socket/newsletter.d.ts.map +1 -0
  47. package/lib/Socket/newsletter.js +149 -286
  48. package/lib/Socket/newsletter.js.map +1 -0
  49. package/lib/Socket/socket.d.ts +59 -0
  50. package/lib/Socket/socket.d.ts.map +1 -0
  51. package/lib/Socket/socket.js.map +1 -0
  52. package/lib/Utils/auth-utils.d.ts +24 -0
  53. package/lib/Utils/auth-utils.d.ts.map +1 -0
  54. package/lib/Utils/auth-utils.js +239 -136
  55. package/lib/Utils/auth-utils.js.map +1 -0
  56. package/lib/Utils/browser-utils.d.ts +4 -0
  57. package/lib/Utils/browser-utils.d.ts.map +1 -0
  58. package/lib/Utils/browser-utils.js +28 -0
  59. package/lib/Utils/browser-utils.js.map +1 -0
  60. package/lib/Utils/business.d.ts +23 -0
  61. package/lib/Utils/business.d.ts.map +1 -0
  62. package/lib/Utils/business.js +66 -69
  63. package/lib/Utils/business.js.map +1 -0
  64. package/lib/Utils/chat-utils.d.ts +100 -0
  65. package/lib/Utils/chat-utils.d.ts.map +1 -0
  66. package/lib/Utils/chat-utils.js +380 -238
  67. package/lib/Utils/chat-utils.js.map +1 -0
  68. package/lib/Utils/companion-reg-client-utils.d.ts +17 -0
  69. package/lib/Utils/companion-reg-client-utils.d.ts.map +1 -0
  70. package/lib/Utils/companion-reg-client-utils.js +35 -0
  71. package/lib/Utils/companion-reg-client-utils.js.map +1 -0
  72. package/lib/Utils/crypto.d.ts +37 -0
  73. package/lib/Utils/crypto.d.ts.map +1 -0
  74. package/lib/Utils/crypto.js +56 -127
  75. package/lib/Utils/crypto.js.map +1 -0
  76. package/lib/Utils/decode-wa-message.d.ts +66 -0
  77. package/lib/Utils/decode-wa-message.d.ts.map +1 -0
  78. package/lib/Utils/decode-wa-message.js +173 -63
  79. package/lib/Utils/decode-wa-message.js.map +1 -0
  80. package/lib/Utils/event-buffer.d.ts +36 -0
  81. package/lib/Utils/event-buffer.d.ts.map +1 -0
  82. package/lib/Utils/event-buffer.js +174 -70
  83. package/lib/Utils/event-buffer.js.map +1 -0
  84. package/lib/Utils/generics.d.ts +91 -0
  85. package/lib/Utils/generics.d.ts.map +1 -0
  86. package/lib/Utils/generics.js +159 -207
  87. package/lib/Utils/generics.js.map +1 -0
  88. package/lib/Utils/history.d.ts +24 -0
  89. package/lib/Utils/history.d.ts.map +1 -0
  90. package/lib/Utils/history.js +93 -53
  91. package/lib/Utils/history.js.map +1 -0
  92. package/lib/Utils/identity-change-handler.d.ts +44 -0
  93. package/lib/Utils/identity-change-handler.d.ts.map +1 -0
  94. package/lib/Utils/identity-change-handler.js +50 -0
  95. package/lib/Utils/identity-change-handler.js.map +1 -0
  96. package/lib/Utils/index.d.ts +22 -0
  97. package/lib/Utils/index.d.ts.map +1 -0
  98. package/lib/Utils/index.js +22 -33
  99. package/lib/Utils/index.js.map +1 -0
  100. package/lib/Utils/link-preview.d.ts +21 -0
  101. package/lib/Utils/link-preview.d.ts.map +1 -0
  102. package/lib/Utils/link-preview.js +17 -58
  103. package/lib/Utils/link-preview.js.map +1 -0
  104. package/lib/Utils/logger.d.ts +12 -0
  105. package/lib/Utils/logger.d.ts.map +1 -0
  106. package/lib/Utils/logger.js +3 -7
  107. package/lib/Utils/logger.js.map +1 -0
  108. package/lib/Utils/lt-hash.d.ts +8 -0
  109. package/lib/Utils/lt-hash.d.ts.map +1 -0
  110. package/lib/Utils/lt-hash.js +3 -46
  111. package/lib/Utils/lt-hash.js.map +1 -0
  112. package/lib/Utils/make-mutex.d.ts +9 -0
  113. package/lib/Utils/make-mutex.d.ts.map +1 -0
  114. package/lib/Utils/make-mutex.js +24 -34
  115. package/lib/Utils/make-mutex.js.map +1 -0
  116. package/lib/Utils/message-retry-manager.d.ts +115 -0
  117. package/lib/Utils/message-retry-manager.d.ts.map +1 -0
  118. package/lib/Utils/message-retry-manager.js +265 -0
  119. package/lib/Utils/message-retry-manager.js.map +1 -0
  120. package/lib/Utils/messages-media.d.ts +133 -0
  121. package/lib/Utils/messages-media.d.ts.map +1 -0
  122. package/lib/Utils/messages-media.js +483 -744
  123. package/lib/Utils/messages-media.js.map +1 -0
  124. package/lib/Utils/messages.d.ts +91 -0
  125. package/lib/Utils/messages.d.ts.map +1 -0
  126. package/lib/Utils/messages.js +411 -621
  127. package/lib/Utils/messages.js.map +1 -0
  128. package/lib/Utils/noise-handler.d.ts +20 -0
  129. package/lib/Utils/noise-handler.d.ts.map +1 -0
  130. package/lib/Utils/noise-handler.js +149 -99
  131. package/lib/Utils/noise-handler.js.map +1 -0
  132. package/lib/Utils/offline-node-processor.d.ts +17 -0
  133. package/lib/Utils/offline-node-processor.d.ts.map +1 -0
  134. package/lib/Utils/offline-node-processor.js +40 -0
  135. package/lib/Utils/offline-node-processor.js.map +1 -0
  136. package/lib/Utils/pre-key-manager.d.ts +28 -0
  137. package/lib/Utils/pre-key-manager.d.ts.map +1 -0
  138. package/lib/Utils/pre-key-manager.js +106 -0
  139. package/lib/Utils/pre-key-manager.js.map +1 -0
  140. package/lib/Utils/process-message.d.ts +60 -0
  141. package/lib/Utils/process-message.d.ts.map +1 -0
  142. package/lib/Utils/process-message.js +459 -150
  143. package/lib/Utils/process-message.js.map +1 -0
  144. package/lib/Utils/reporting-utils.d.ts +11 -0
  145. package/lib/Utils/reporting-utils.d.ts.map +1 -0
  146. package/lib/Utils/reporting-utils.js +258 -0
  147. package/lib/Utils/reporting-utils.js.map +1 -0
  148. package/lib/Utils/signal.d.ts +47 -0
  149. package/lib/Utils/signal.d.ts.map +1 -0
  150. package/lib/Utils/signal.js +112 -73
  151. package/lib/Utils/signal.js.map +1 -0
  152. package/lib/Utils/stanza-ack.d.ts +11 -0
  153. package/lib/Utils/stanza-ack.d.ts.map +1 -0
  154. package/lib/Utils/stanza-ack.js +38 -0
  155. package/lib/Utils/stanza-ack.js.map +1 -0
  156. package/lib/Utils/sync-action-utils.d.ts +19 -0
  157. package/lib/Utils/sync-action-utils.d.ts.map +1 -0
  158. package/lib/Utils/sync-action-utils.js +49 -0
  159. package/lib/Utils/sync-action-utils.js.map +1 -0
  160. package/lib/Utils/tc-token-utils.d.ts +37 -0
  161. package/lib/Utils/tc-token-utils.d.ts.map +1 -0
  162. package/lib/Utils/tc-token-utils.js +163 -0
  163. package/lib/Utils/tc-token-utils.js.map +1 -0
  164. package/lib/Utils/use-multi-file-auth-state.d.ts +13 -0
  165. package/lib/Utils/use-multi-file-auth-state.d.ts.map +1 -0
  166. package/lib/Utils/use-multi-file-auth-state.js +23 -27
  167. package/lib/Utils/use-multi-file-auth-state.js.map +1 -0
  168. package/lib/Utils/validate-connection.d.ts +11 -0
  169. package/lib/Utils/validate-connection.d.ts.map +1 -0
  170. package/lib/Utils/validate-connection.js +74 -104
  171. package/lib/Utils/validate-connection.js.map +1 -0
  172. package/package.json +1 -1
@@ -1,37 +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 node_cache_1 = __importDefault(require("@cacheable/node-cache"));
9
- const WAProto_1 = require("../../WAProto");
10
- const Defaults_1 = require("../Defaults");
11
- const Types_1 = require("../Types");
12
- const Utils_1 = require("../Utils");
13
- const make_mutex_1 = require("../Utils/make-mutex");
14
- const process_message_1 = __importDefault(require("../Utils/process-message"));
15
- const WABinary_1 = require("../WABinary");
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)();
28
- const placeholderResendCache = config.placeholderResendCache || new node_cache_1.default({
29
- stdTTL: Defaults_1.DEFAULT_CACHE_TTLS.MSG_RETRY, // 1 hour
30
- useClones: false
31
- });
32
- if (!config.placeholderResendCache) {
33
- config.placeholderResendCache = placeholderResendCache;
34
- }
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
+ });
35
54
  /** helper function to fetch the given app state sync key */
36
55
  const getAppStateSyncKey = async (keyId) => {
37
56
  const { [keyId]: key } = await authState.keys.get('app-state-sync-key', [keyId]);
@@ -43,14 +62,12 @@ const makeChatsSocket = (config) => {
43
62
  tag: 'iq',
44
63
  attrs: {
45
64
  xmlns: 'privacy',
46
- to: WABinary_1.S_WHATSAPP_NET,
65
+ to: S_WHATSAPP_NET,
47
66
  type: 'get'
48
67
  },
49
- content: [
50
- { tag: 'privacy', attrs: {} }
51
- ]
68
+ content: [{ tag: 'privacy', attrs: {} }]
52
69
  });
53
- privacySettings = (0, WABinary_1.reduceBinaryNodeToDictionary)(content === null || content === void 0 ? void 0 : content[0], 'category');
70
+ privacySettings = reduceBinaryNodeToDictionary(content?.[0], 'category');
54
71
  }
55
72
  return privacySettings;
56
73
  };
@@ -60,10 +77,11 @@ const makeChatsSocket = (config) => {
60
77
  tag: 'iq',
61
78
  attrs: {
62
79
  xmlns: 'privacy',
63
- to: WABinary_1.S_WHATSAPP_NET,
80
+ to: S_WHATSAPP_NET,
64
81
  type: 'set'
65
82
  },
66
- content: [{
83
+ content: [
84
+ {
67
85
  tag: 'privacy',
68
86
  attrs: {},
69
87
  content: [
@@ -72,7 +90,8 @@ const makeChatsSocket = (config) => {
72
90
  attrs: { name, value }
73
91
  }
74
92
  ]
75
- }]
93
+ }
94
+ ]
76
95
  });
77
96
  };
78
97
  const updateMessagesPrivacy = async (value) => {
@@ -104,15 +123,17 @@ const makeChatsSocket = (config) => {
104
123
  tag: 'iq',
105
124
  attrs: {
106
125
  xmlns: 'disappearing_mode',
107
- to: WABinary_1.S_WHATSAPP_NET,
126
+ to: S_WHATSAPP_NET,
108
127
  type: 'set'
109
128
  },
110
- content: [{
129
+ content: [
130
+ {
111
131
  tag: 'disappearing_mode',
112
132
  attrs: {
113
133
  duration: duration.toString()
114
134
  }
115
- }]
135
+ }
136
+ ]
116
137
  });
117
138
  };
118
139
  const getBotListV2 = async () => {
@@ -120,21 +141,23 @@ const makeChatsSocket = (config) => {
120
141
  tag: 'iq',
121
142
  attrs: {
122
143
  xmlns: 'bot',
123
- to: WABinary_1.S_WHATSAPP_NET,
144
+ to: S_WHATSAPP_NET,
124
145
  type: 'get'
125
146
  },
126
- content: [{
147
+ content: [
148
+ {
127
149
  tag: 'bot',
128
150
  attrs: {
129
151
  v: '2'
130
152
  }
131
- }]
153
+ }
154
+ ]
132
155
  });
133
- const botNode = (0, WABinary_1.getBinaryNodeChild)(resp, 'bot');
156
+ const botNode = getBinaryNodeChild(resp, 'bot');
134
157
  const botList = [];
135
- for (const section of (0, WABinary_1.getBinaryNodeChildren)(botNode, 'section')) {
158
+ for (const section of getBinaryNodeChildren(botNode, 'section')) {
136
159
  if (section.attrs.type === 'all') {
137
- for (const bot of (0, WABinary_1.getBinaryNodeChildren)(section, 'bot')) {
160
+ for (const bot of getBinaryNodeChildren(section, 'bot')) {
138
161
  botList.push({
139
162
  jid: bot.attrs.jid,
140
163
  personaId: bot.attrs['persona_id']
@@ -144,24 +167,10 @@ const makeChatsSocket = (config) => {
144
167
  }
145
168
  return botList;
146
169
  };
147
- const onWhatsApp = async (...jids) => {
148
- const usyncQuery = new WAUSync_1.USyncQuery()
149
- .withContactProtocol()
150
- .withLIDProtocol();
151
- for (const jid of jids) {
152
- const phone = `+${jid.replace('+', '').split('@')[0].split(':')[0]}`;
153
- usyncQuery.withUser(new WAUSync_1.USyncUser().withPhone(phone));
154
- }
155
- const results = await sock.executeUSyncQuery(usyncQuery);
156
- if (results) {
157
- return results.list.filter((a) => !!a.contact).map(({ contact, id, lid }) => ({ jid: id, exists: contact, lid }));
158
- }
159
- };
160
170
  const fetchStatus = async (...jids) => {
161
- const usyncQuery = new WAUSync_1.USyncQuery()
162
- .withStatusProtocol();
171
+ const usyncQuery = new USyncQuery().withStatusProtocol();
163
172
  for (const jid of jids) {
164
- usyncQuery.withUser(new WAUSync_1.USyncUser().withId(jid));
173
+ usyncQuery.withUser(new USyncUser().withId(jid));
165
174
  }
166
175
  const result = await sock.executeUSyncQuery(usyncQuery);
167
176
  if (result) {
@@ -169,10 +178,9 @@ const makeChatsSocket = (config) => {
169
178
  }
170
179
  };
171
180
  const fetchDisappearingDuration = async (...jids) => {
172
- const usyncQuery = new WAUSync_1.USyncQuery()
173
- .withDisappearingModeProtocol();
181
+ const usyncQuery = new USyncQuery().withDisappearingModeProtocol();
174
182
  for (const jid of jids) {
175
- usyncQuery.withUser(new WAUSync_1.USyncUser().withId(jid));
183
+ usyncQuery.withUser(new USyncUser().withId(jid));
176
184
  }
177
185
  const result = await sock.executeUSyncQuery(usyncQuery);
178
186
  if (result) {
@@ -180,22 +188,25 @@ const makeChatsSocket = (config) => {
180
188
  }
181
189
  };
182
190
  /** update the profile picture for yourself or a group */
183
- const updateProfilePicture = async (jid, content) => {
191
+ const updateProfilePicture = async (jid, content, dimensions) => {
184
192
  let targetJid;
185
193
  if (!jid) {
186
- 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');
187
195
  }
188
- if ((0, WABinary_1.jidNormalizedUser)(jid) !== (0, WABinary_1.jidNormalizedUser)(authState.creds.me.id)) {
189
- targetJid = (0, WABinary_1.jidNormalizedUser)(jid); // in case it is someone other than us
196
+ if (jidNormalizedUser(jid) !== jidNormalizedUser(authState.creds.me.id)) {
197
+ targetJid = jidNormalizedUser(jid); // in case it is someone other than us
190
198
  }
191
- const { img } = await (0, Utils_1.generateProfilePicture)(content);
199
+ else {
200
+ targetJid = undefined;
201
+ }
202
+ const { img } = await generateProfilePicture(content, dimensions);
192
203
  await query({
193
204
  tag: 'iq',
194
205
  attrs: {
195
- target: targetJid,
196
- to: WABinary_1.S_WHATSAPP_NET,
206
+ to: S_WHATSAPP_NET,
197
207
  type: 'set',
198
- xmlns: 'w:profile:picture'
208
+ xmlns: 'w:profile:picture',
209
+ ...(targetJid ? { target: targetJid } : {})
199
210
  },
200
211
  content: [
201
212
  {
@@ -210,18 +221,21 @@ const makeChatsSocket = (config) => {
210
221
  const removeProfilePicture = async (jid) => {
211
222
  let targetJid;
212
223
  if (!jid) {
213
- 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');
214
225
  }
215
- if ((0, WABinary_1.jidNormalizedUser)(jid) !== (0, WABinary_1.jidNormalizedUser)(authState.creds.me.id)) {
216
- targetJid = (0, WABinary_1.jidNormalizedUser)(jid); // in case it is someone other than us
226
+ if (jidNormalizedUser(jid) !== jidNormalizedUser(authState.creds.me.id)) {
227
+ targetJid = jidNormalizedUser(jid); // in case it is someone other than us
228
+ }
229
+ else {
230
+ targetJid = undefined;
217
231
  }
218
232
  await query({
219
233
  tag: 'iq',
220
234
  attrs: {
221
- target: targetJid,
222
- to: WABinary_1.S_WHATSAPP_NET,
235
+ to: S_WHATSAPP_NET,
223
236
  type: 'set',
224
- xmlns: 'w:profile:picture'
237
+ xmlns: 'w:profile:picture',
238
+ ...(targetJid ? { target: targetJid } : {})
225
239
  }
226
240
  });
227
241
  };
@@ -230,7 +244,7 @@ const makeChatsSocket = (config) => {
230
244
  await query({
231
245
  tag: 'iq',
232
246
  attrs: {
233
- to: WABinary_1.S_WHATSAPP_NET,
247
+ to: S_WHATSAPP_NET,
234
248
  type: 'set',
235
249
  xmlns: 'status'
236
250
  },
@@ -251,35 +265,66 @@ const makeChatsSocket = (config) => {
251
265
  tag: 'iq',
252
266
  attrs: {
253
267
  xmlns: 'blocklist',
254
- to: WABinary_1.S_WHATSAPP_NET,
268
+ to: S_WHATSAPP_NET,
255
269
  type: 'get'
256
270
  }
257
271
  });
258
- const listNode = (0, WABinary_1.getBinaryNodeChild)(result, 'list');
259
- return (0, WABinary_1.getBinaryNodeChildren)(listNode, 'item')
260
- .map(n => n.attrs.jid);
272
+ const listNode = getBinaryNodeChild(result, 'list');
273
+ return getBinaryNodeChildren(listNode, 'item').map(n => n.attrs.jid);
261
274
  };
262
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
+ }
263
312
  await query({
264
313
  tag: 'iq',
265
314
  attrs: {
266
315
  xmlns: 'blocklist',
267
- to: WABinary_1.S_WHATSAPP_NET,
316
+ to: S_WHATSAPP_NET,
268
317
  type: 'set'
269
318
  },
270
319
  content: [
271
320
  {
272
321
  tag: 'item',
273
- attrs: {
274
- action,
275
- jid
276
- }
322
+ attrs: itemAttrs
277
323
  }
278
324
  ]
279
325
  });
280
326
  };
281
327
  const getBusinessProfile = async (jid) => {
282
- var _a, _b, _c, _d, _e, _f, _g;
283
328
  const results = await query({
284
329
  tag: 'iq',
285
330
  attrs: {
@@ -287,38 +332,42 @@ const makeChatsSocket = (config) => {
287
332
  xmlns: 'w:biz',
288
333
  type: 'get'
289
334
  },
290
- content: [{
335
+ content: [
336
+ {
291
337
  tag: 'business_profile',
292
338
  attrs: { v: '244' },
293
- content: [{
339
+ content: [
340
+ {
294
341
  tag: 'profile',
295
342
  attrs: { jid }
296
- }]
297
- }]
343
+ }
344
+ ]
345
+ }
346
+ ]
298
347
  });
299
- const profileNode = (0, WABinary_1.getBinaryNodeChild)(results, 'business_profile');
300
- const profiles = (0, WABinary_1.getBinaryNodeChild)(profileNode, 'profile');
348
+ const profileNode = getBinaryNodeChild(results, 'business_profile');
349
+ const profiles = getBinaryNodeChild(profileNode, 'profile');
301
350
  if (profiles) {
302
- const address = (0, WABinary_1.getBinaryNodeChild)(profiles, 'address');
303
- const description = (0, WABinary_1.getBinaryNodeChild)(profiles, 'description');
304
- const website = (0, WABinary_1.getBinaryNodeChild)(profiles, 'website');
305
- const email = (0, WABinary_1.getBinaryNodeChild)(profiles, 'email');
306
- const category = (0, WABinary_1.getBinaryNodeChild)((0, WABinary_1.getBinaryNodeChild)(profiles, 'categories'), 'category');
307
- const businessHours = (0, WABinary_1.getBinaryNodeChild)(profiles, 'business_hours');
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');
308
357
  const businessHoursConfig = businessHours
309
- ? (0, WABinary_1.getBinaryNodeChildren)(businessHours, 'business_hours_config')
358
+ ? getBinaryNodeChildren(businessHours, 'business_hours_config')
310
359
  : undefined;
311
- const websiteStr = (_a = website === null || website === void 0 ? void 0 : website.content) === null || _a === void 0 ? void 0 : _a.toString();
360
+ const websiteStr = website?.content?.toString();
312
361
  return {
313
- wid: (_b = profiles.attrs) === null || _b === void 0 ? void 0 : _b.jid,
314
- address: (_c = address === null || address === void 0 ? void 0 : address.content) === null || _c === void 0 ? void 0 : _c.toString(),
315
- description: ((_d = description === null || description === void 0 ? void 0 : description.content) === null || _d === void 0 ? void 0 : _d.toString()) || '',
362
+ wid: profiles.attrs?.jid,
363
+ address: address?.content?.toString(),
364
+ description: description?.content?.toString() || '',
316
365
  website: websiteStr ? [websiteStr] : [],
317
- email: (_e = email === null || email === void 0 ? void 0 : email.content) === null || _e === void 0 ? void 0 : _e.toString(),
318
- category: (_f = category === null || category === void 0 ? void 0 : category.content) === null || _f === void 0 ? void 0 : _f.toString(),
319
- 'business_hours': {
320
- timezone: (_g = businessHours === null || businessHours === void 0 ? void 0 : businessHours.attrs) === null || _g === void 0 ? void 0 : _g.timezone,
321
- 'business_config': businessHoursConfig === null || businessHoursConfig === void 0 ? void 0 : businessHoursConfig.map(({ attrs }) => attrs)
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)
322
371
  }
323
372
  };
324
373
  }
@@ -328,17 +377,17 @@ const makeChatsSocket = (config) => {
328
377
  await sendNode({
329
378
  tag: 'iq',
330
379
  attrs: {
331
- to: WABinary_1.S_WHATSAPP_NET,
380
+ to: S_WHATSAPP_NET,
332
381
  type: 'set',
333
382
  xmlns: 'urn:xmpp:whatsapp:dirty',
334
- id: generateMessageTag(),
383
+ id: generateMessageTag()
335
384
  },
336
385
  content: [
337
386
  {
338
387
  tag: 'clean',
339
388
  attrs: {
340
389
  type,
341
- ...(fromTimestamp ? { timestamp: fromTimestamp.toString() } : null),
390
+ ...(fromTimestamp ? { timestamp: fromTimestamp.toString() } : null)
342
391
  }
343
392
  }
344
393
  ]
@@ -347,20 +396,31 @@ const makeChatsSocket = (config) => {
347
396
  const newAppStateChunkHandler = (isInitialSync) => {
348
397
  return {
349
398
  onMutation(mutation) {
350
- (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);
351
400
  }
352
401
  };
353
402
  };
354
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
+ };
355
413
  // we use this to determine which events to fire
356
414
  // otherwise when we resync from scratch -- all notifications will fire
357
415
  const initialVersionMap = {};
358
416
  const globalMutationMap = {};
359
417
  await authState.keys.transaction(async () => {
360
- var _a;
361
418
  const collectionsToHandle = new Set(collections);
362
419
  // in case something goes wrong -- ensure we don't enter a loop that cannot be exited from
363
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();
364
424
  // keep executing till all collections are done
365
425
  // sometimes a single patch request will not return all the patches (God knows why)
366
426
  // so we fetch till they're all done (this is determined by the "has_more_patches" flag)
@@ -371,29 +431,34 @@ const makeChatsSocket = (config) => {
371
431
  const result = await authState.keys.get('app-state-sync-version', [name]);
372
432
  let state = result[name];
373
433
  if (state) {
434
+ state = ensureLTHashStateVersion(state);
374
435
  if (typeof initialVersionMap[name] === 'undefined') {
375
436
  initialVersionMap[name] = state.version;
376
437
  }
377
438
  }
378
439
  else {
379
- state = (0, Utils_1.newLTHashState)();
440
+ state = newLTHashState();
380
441
  }
381
442
  states[name] = state;
382
- 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)' : ''}`);
383
448
  nodes.push({
384
449
  tag: 'collection',
385
450
  attrs: {
386
451
  name,
387
452
  version: state.version.toString(),
388
- // return snapshot if being synced from scratch
389
- '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()
390
455
  }
391
456
  });
392
457
  }
393
458
  const result = await query({
394
459
  tag: 'iq',
395
460
  attrs: {
396
- to: WABinary_1.S_WHATSAPP_NET,
461
+ to: S_WHATSAPP_NET,
397
462
  xmlns: 'w:sync:app:state',
398
463
  type: 'set'
399
464
  },
@@ -406,13 +471,13 @@ const makeChatsSocket = (config) => {
406
471
  ]
407
472
  });
408
473
  // extract from binary node
409
- 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);
410
475
  for (const key in decoded) {
411
476
  const name = key;
412
477
  const { patches, hasMorePatches, snapshot } = decoded[name];
413
478
  try {
414
479
  if (snapshot) {
415
- 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);
416
481
  states[name] = newState;
417
482
  Object.assign(globalMutationMap, mutationMap);
418
483
  logger.info(`restored state of ${name} from snapshot to v${newState.version} with mutations`);
@@ -420,7 +485,7 @@ const makeChatsSocket = (config) => {
420
485
  }
421
486
  // only process if there are syncd patches
422
487
  if (patches.length) {
423
- const { state: newState, mutationMap } = await (0, Utils_1.decodePatches)(name, patches, states[name], getAppStateSyncKey, config.options, initialVersionMap[name], logger, appStateMacVerification.patch);
488
+ const { state: newState, mutationMap } = await decodePatches(name, patches, states[name], getCachedAppStateSyncKey, config.options, initialVersionMap[name], logger, appStateMacVerification.patch);
424
489
  await authState.keys.set({ 'app-state-sync-version': { [name]: newState } });
425
490
  logger.info(`synced ${name} to v${newState.version}`);
426
491
  initialVersionMap[name] = newState.version;
@@ -429,28 +494,47 @@ const makeChatsSocket = (config) => {
429
494
  if (hasMorePatches) {
430
495
  logger.info(`${name} has more patches...`);
431
496
  }
432
- else { // collection is done with sync
497
+ else {
498
+ // collection is done with sync
433
499
  collectionsToHandle.delete(name);
434
500
  }
435
501
  }
436
502
  catch (error) {
437
- // if retry attempts overshoot
438
- // or key not found
439
- const isIrrecoverableError = attemptsMap[name] >= MAX_SYNC_ATTEMPTS
440
- || ((_a = error.output) === null || _a === void 0 ? void 0 : _a.statusCode) === 404
441
- || error.name === 'TypeError';
442
- logger.info({ name, error: error.stack }, `failed to sync state from version${isIrrecoverableError ? '' : ', removing and trying from scratch'}`);
443
- await authState.keys.set({ 'app-state-sync-version': { [name]: null } });
444
- // increment number of retries
445
503
  attemptsMap[name] = (attemptsMap[name] || 0) + 1;
446
- if (isIrrecoverableError) {
447
- // 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);
517
+ collectionsToHandle.delete(name);
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`);
448
526
  collectionsToHandle.delete(name);
449
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
+ }
450
534
  }
451
535
  }
452
536
  }
453
- });
537
+ }, authState?.creds?.me?.id || 'resync-app-state');
454
538
  const { onMutation } = newAppStateChunkHandler(isInitialSync);
455
539
  for (const key in globalMutationMap) {
456
540
  onMutation(globalMutationMap[key]);
@@ -462,31 +546,67 @@ const makeChatsSocket = (config) => {
462
546
  * type = "image for the high res picture"
463
547
  */
464
548
  const profilePictureUrl = async (jid, type = 'preview', timeoutMs) => {
465
- var _a;
466
- 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);
467
567
  const result = await query({
468
568
  tag: 'iq',
469
569
  attrs: {
470
570
  target: jid,
471
- to: WABinary_1.S_WHATSAPP_NET,
571
+ to: S_WHATSAPP_NET,
472
572
  type: 'get',
473
573
  xmlns: 'w:profile:picture'
474
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
+ },
475
587
  content: [
476
- { 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
+ }
477
593
  ]
478
594
  }, timeoutMs);
479
- const child = (0, WABinary_1.getBinaryNodeChild)(result, 'picture');
480
- 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;
481
597
  };
482
598
  const sendPresenceUpdate = async (type, toJid) => {
483
599
  const me = authState.creds.me;
484
- if (type === 'available' || type === 'unavailable') {
600
+ const isAvailableType = type === 'available';
601
+ if (isAvailableType || type === 'unavailable') {
485
602
  if (!me.name) {
486
603
  logger.warn('no name present, ignoring presence update request...');
487
604
  return;
488
605
  }
489
- ev.emit('connection.update', { isOnline: type === 'available' });
606
+ ev.emit('connection.update', { isOnline: isAvailableType });
607
+ if (isAvailableType) {
608
+ void sendUnifiedSession();
609
+ }
490
610
  await sendNode({
491
611
  tag: 'presence',
492
612
  attrs: {
@@ -496,13 +616,13 @@ const makeChatsSocket = (config) => {
496
616
  });
497
617
  }
498
618
  else {
499
- const { server } = (0, WABinary_1.jidDecode)(toJid);
619
+ const { server } = jidDecode(toJid);
500
620
  const isLid = server === 'lid';
501
621
  await sendNode({
502
622
  tag: 'chatstate',
503
623
  attrs: {
504
624
  from: isLid ? me.lid : me.id,
505
- to: toJid,
625
+ to: toJid
506
626
  },
507
627
  content: [
508
628
  {
@@ -517,35 +637,35 @@ const makeChatsSocket = (config) => {
517
637
  * @param toJid the jid to subscribe to
518
638
  * @param tcToken token for subscription, use if present
519
639
  */
520
- const presenceSubscribe = (toJid, tcToken) => (sendNode({
521
- tag: 'presence',
522
- attrs: {
523
- to: toJid,
524
- id: generateMessageTag(),
525
- type: 'subscribe'
526
- },
527
- content: tcToken
528
- ? [
529
- {
530
- tag: 'tctoken',
531
- attrs: {},
532
- content: tcToken
533
- }
534
- ]
535
- : undefined
536
- }));
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
+ };
537
657
  const handlePresenceUpdate = ({ tag, attrs, content }) => {
538
- var _a;
539
658
  let presence;
540
659
  const jid = attrs.from;
541
660
  const participant = attrs.participant || attrs.from;
542
- if (shouldIgnoreJid(jid) && jid != '@s.whatsapp.net') {
661
+ if (shouldIgnoreJid(jid) && jid !== S_WHATSAPP_NET) {
543
662
  return;
544
663
  }
545
664
  if (tag === 'presence') {
546
665
  presence = {
547
666
  lastKnownPresence: attrs.type === 'unavailable' ? 'unavailable' : 'available',
548
- 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
549
669
  };
550
670
  }
551
671
  else if (Array.isArray(content)) {
@@ -554,7 +674,7 @@ const makeChatsSocket = (config) => {
554
674
  if (type === 'paused') {
555
675
  type = 'available';
556
676
  }
557
- if (((_a = firstChild.attrs) === null || _a === void 0 ? void 0 : _a.media) === 'audio') {
677
+ if (firstChild.attrs?.media === 'audio') {
558
678
  type = 'recording';
559
679
  }
560
680
  presence = { lastKnownPresence: type };
@@ -570,22 +690,22 @@ const makeChatsSocket = (config) => {
570
690
  const name = patchCreate.type;
571
691
  const myAppStateKeyId = authState.creds.myAppStateKeyId;
572
692
  if (!myAppStateKeyId) {
573
- throw new boom_1.Boom('App state key not present!', { statusCode: 400 });
693
+ throw new Boom('App state key not present!', { statusCode: 400 });
574
694
  }
575
695
  let initial;
576
696
  let encodeResult;
577
- await processingMutex.mutex(async () => {
697
+ await appStatePatchMutex.mutex(async () => {
578
698
  await authState.keys.transaction(async () => {
579
699
  logger.debug({ patch: patchCreate }, 'applying app patch');
580
700
  await resyncAppState([name], false);
581
701
  const { [name]: currentSyncVersion } = await authState.keys.get('app-state-sync-version', [name]);
582
- initial = currentSyncVersion || (0, Utils_1.newLTHashState)();
583
- encodeResult = await (0, Utils_1.encodeSyncdPatch)(patchCreate, myAppStateKeyId, initial, getAppStateSyncKey);
702
+ initial = currentSyncVersion ? ensureLTHashStateVersion(currentSyncVersion) : newLTHashState();
703
+ encodeResult = await encodeSyncdPatch(patchCreate, myAppStateKeyId, initial, getAppStateSyncKey);
584
704
  const { patch, state } = encodeResult;
585
705
  const node = {
586
706
  tag: 'iq',
587
707
  attrs: {
588
- to: WABinary_1.S_WHATSAPP_NET,
708
+ to: S_WHATSAPP_NET,
589
709
  type: 'set',
590
710
  xmlns: 'w:sync:app:state'
591
711
  },
@@ -599,13 +719,13 @@ const makeChatsSocket = (config) => {
599
719
  attrs: {
600
720
  name,
601
721
  version: (state.version - 1).toString(),
602
- 'return_snapshot': 'false'
722
+ return_snapshot: 'false'
603
723
  },
604
724
  content: [
605
725
  {
606
726
  tag: 'patch',
607
727
  attrs: {},
608
- content: WAProto_1.proto.SyncdPatch.encode(patch).finish()
728
+ content: proto.SyncdPatch.encode(patch).finish()
609
729
  }
610
730
  ]
611
731
  }
@@ -615,54 +735,78 @@ const makeChatsSocket = (config) => {
615
735
  };
616
736
  await query(node);
617
737
  await authState.keys.set({ 'app-state-sync-version': { [name]: state } });
618
- });
738
+ }, authState?.creds?.me?.id || 'app-patch');
619
739
  });
620
740
  if (config.emitOwnEvents) {
621
741
  const { onMutation } = newAppStateChunkHandler(false);
622
- 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);
623
743
  for (const key in mutationMap) {
624
744
  onMutation(mutationMap[key]);
625
745
  }
626
746
  }
627
747
  };
628
- /** sending non-abt props may fix QR scan fail if server expects */
748
+ /** fetch AB props */
629
749
  const fetchProps = async () => {
630
- var _a, _b, _c;
631
750
  const resultNode = await query({
632
751
  tag: 'iq',
633
752
  attrs: {
634
- to: WABinary_1.S_WHATSAPP_NET,
635
- xmlns: 'w',
636
- type: 'get',
753
+ to: S_WHATSAPP_NET,
754
+ xmlns: 'abt',
755
+ type: 'get'
637
756
  },
638
757
  content: [
639
- { tag: 'props', attrs: {
640
- protocol: '2',
641
- hash: ((_a = authState === null || authState === void 0 ? void 0 : authState.creds) === null || _a === void 0 ? void 0 : _a.lastPropHash) || ''
642
- } }
758
+ {
759
+ tag: 'props',
760
+ attrs: {
761
+ protocol: '1',
762
+ ...(authState?.creds?.lastPropHash ? { hash: authState.creds.lastPropHash } : {})
763
+ }
764
+ }
643
765
  ]
644
766
  });
645
- const propsNode = (0, WABinary_1.getBinaryNodeChild)(resultNode, 'props');
767
+ const propsNode = getBinaryNodeChild(resultNode, 'props');
646
768
  let props = {};
647
769
  if (propsNode) {
648
- if ((_b = propsNode.attrs) === null || _b === void 0 ? void 0 : _b.hash) { // on some clients, the hash is returning as undefined
649
- authState.creds.lastPropHash = (_c = propsNode === null || propsNode === void 0 ? void 0 : propsNode.attrs) === null || _c === void 0 ? void 0 : _c.hash;
770
+ if (propsNode.attrs?.hash) {
771
+ // on some clients, the hash is returning as undefined
772
+ authState.creds.lastPropHash = propsNode?.attrs?.hash;
650
773
  ev.emit('creds.update', authState.creds);
651
774
  }
652
- props = (0, WABinary_1.reduceBinaryNodeToDictionary)(propsNode, 'prop');
775
+ props = reduceBinaryNodeToDictionary(propsNode, 'prop');
653
776
  }
654
- logger.debug('fetched props');
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';
781
+ }
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');
655
791
  return props;
656
792
  };
657
793
  /**
658
794
  * modify a chat -- mark unread, read etc.
659
795
  * lastMessages must be sorted in reverse chronologically
660
796
  * requires the last messages till the last message received; required for archive & unread
661
- */
797
+ */
662
798
  const chatModify = (mod, jid) => {
663
- const patch = (0, Utils_1.chatModificationToAppPatch)(mod, jid);
799
+ const patch = chatModificationToAppPatch(mod, jid);
664
800
  return appPatch(patch);
665
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
+ };
666
810
  /**
667
811
  * Star or Unstar a message
668
812
  */
@@ -674,6 +818,32 @@ const makeChatsSocket = (config) => {
674
818
  }
675
819
  }, jid);
676
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
+ };
677
847
  /**
678
848
  * Adds label for the chats
679
849
  */
@@ -716,49 +886,127 @@ const makeChatsSocket = (config) => {
716
886
  }
717
887
  }, jid);
718
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
+ };
719
905
  /**
720
906
  * queries need to be fired on connection open
721
907
  * help ensure parity with WA Web
722
908
  * */
723
909
  const executeInitQueries = async () => {
724
- await Promise.all([
725
- fetchProps(),
726
- fetchBlocklist(),
727
- fetchPrivacySettings(),
728
- ]);
910
+ await Promise.all([fetchProps(), fetchBlocklist(), fetchPrivacySettings()]);
729
911
  };
730
912
  const upsertMessage = ev.createBufferedFunction(async (msg, type) => {
731
- var _a, _b, _c;
732
913
  ev.emit('messages.upsert', { messages: [msg], type });
733
914
  if (!!msg.pushName) {
734
- let jid = msg.key.fromMe ? authState.creds.me.id : (msg.key.participant || msg.key.remoteJid);
735
- 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);
736
917
  if (!msg.key.fromMe) {
737
918
  ev.emit('contacts.update', [{ id: jid, notify: msg.pushName, verifiedName: msg.verifiedBizName }]);
738
919
  }
739
920
  // update our pushname too
740
- 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) {
741
922
  ev.emit('creds.update', { me: { ...authState.creds.me, name: msg.pushName } });
742
923
  }
743
924
  }
744
- const historyMsg = (0, Utils_1.getHistoryMsg)(msg.message);
925
+ const historyMsg = getHistoryMsg(msg.message);
745
926
  const shouldProcessHistoryMsg = historyMsg
746
- ? (shouldSyncHistoryMessage(historyMsg)
747
- && Defaults_1.PROCESSABLE_HISTORY_TYPES.includes(historyMsg.syncType))
927
+ ? shouldSyncHistoryMessage(historyMsg) &&
928
+ PROCESSABLE_HISTORY_TYPES.includes(historyMsg.syncType)
748
929
  : false;
749
- if (historyMsg && !authState.creds.myAppStateKeyId) {
750
- logger.warn('skipping app state sync, as myAppStateKeyId is not set');
751
- pendingAppStateSync = true;
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
+ }
752
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
+ }
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
+ };
753
1002
  await Promise.all([
754
1003
  (async () => {
755
- if (historyMsg
756
- && authState.creds.myAppStateKeyId) {
757
- pendingAppStateSync = false;
1004
+ if (shouldProcessHistoryMsg) {
758
1005
  await doAppStateSync();
759
1006
  }
760
1007
  })(),
761
- (0, process_message_1.default)(msg, {
1008
+ processMessage(msg, {
1009
+ signalRepository,
762
1010
  shouldProcessHistoryMsg,
763
1011
  placeholderResendCache,
764
1012
  ev,
@@ -766,31 +1014,19 @@ const makeChatsSocket = (config) => {
766
1014
  keyStore: authState.keys,
767
1015
  logger,
768
1016
  options: config.options,
769
- getMessage: config.getMessage,
1017
+ getMessage
770
1018
  })
771
1019
  ]);
772
- if (((_c = (_b = msg.message) === null || _b === void 0 ? void 0 : _b.protocolMessage) === null || _c === void 0 ? void 0 : _c.appStateSyncKeyShare)
773
- && 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');
774
1023
  await doAppStateSync();
775
- pendingAppStateSync = false;
776
- }
777
- async function doAppStateSync() {
778
- if (!authState.creds.accountSyncCounter) {
779
- logger.info('doing initial app state sync');
780
- await resyncAppState(Types_1.ALL_WA_PATCH_NAMES, true);
781
- const accountSyncCounter = (authState.creds.accountSyncCounter || 0) + 1;
782
- ev.emit('creds.update', { accountSyncCounter });
783
- if (needToFlushWithAppStateSync) {
784
- logger.debug('flushing with app state sync');
785
- ev.flush();
786
- }
787
- }
788
1024
  }
789
1025
  });
790
1026
  ws.on('CB:presence', handlePresenceUpdate);
791
1027
  ws.on('CB:chatstate', handlePresenceUpdate);
792
1028
  ws.on('CB:ib,,dirty', async (node) => {
793
- const { attrs } = (0, WABinary_1.getBinaryNodeChild)(node, 'dirty');
1029
+ const { attrs } = getBinaryNodeChild(node, 'dirty');
794
1030
  const type = attrs.type;
795
1031
  switch (type) {
796
1032
  case 'account_sync':
@@ -812,42 +1048,122 @@ const makeChatsSocket = (config) => {
812
1048
  }
813
1049
  });
814
1050
  ev.on('connection.update', ({ connection, receivedPendingNotifications }) => {
815
- var _a;
1051
+ if (connection === 'close') {
1052
+ blockedCollections.clear();
1053
+ clearTimeout(historySyncPausedTimeout);
1054
+ historySyncPausedTimeout = undefined;
1055
+ }
816
1056
  if (connection === 'open') {
817
1057
  if (fireInitQueries) {
818
- executeInitQueries()
819
- .catch(error => onUnexpectedError(error, 'init queries'));
1058
+ executeInitQueries().catch(error => onUnexpectedError(error, 'init queries'));
820
1059
  }
821
- sendPresenceUpdate(markOnlineOnConnect ? 'available' : 'unavailable')
822
- .catch(error => onUnexpectedError(error, 'presence update requests'));
1060
+ sendPresenceUpdate(markOnlineOnConnect ? 'available' : 'unavailable').catch(error => onUnexpectedError(error, 'presence update requests'));
823
1061
  }
824
- if (receivedPendingNotifications && // if we don't have the app state key
825
- // we keep buffering events until we finally have
826
- // the key and can sync the messages
827
- !((_a = authState.creds) === null || _a === void 0 ? void 0 : _a.myAppStateKeyId)) {
828
- ev.buffer();
829
- needToFlushWithAppStateSync = true;
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);
1093
+ }
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 });
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');
830
1130
  }
831
1131
  });
1132
+ registerSocketEndHandler(() => {
1133
+ if (awaitingSyncTimeout) {
1134
+ clearTimeout(awaitingSyncTimeout);
1135
+ awaitingSyncTimeout = undefined;
1136
+ }
1137
+ if (!config.placeholderResendCache && placeholderResendCache.close) {
1138
+ placeholderResendCache.close();
1139
+ }
1140
+ syncState = SyncState.Connecting;
1141
+ privacySettings = undefined;
1142
+ });
832
1143
  return {
833
1144
  ...sock,
1145
+ serverProps,
1146
+ createCallLink,
834
1147
  getBotListV2,
835
- processingMutex,
1148
+ messageMutex,
1149
+ receiptMutex,
1150
+ appStatePatchMutex,
1151
+ notificationMutex,
836
1152
  fetchPrivacySettings,
837
1153
  upsertMessage,
838
1154
  appPatch,
839
1155
  sendPresenceUpdate,
840
1156
  presenceSubscribe,
841
1157
  profilePictureUrl,
842
- onWhatsApp,
843
1158
  fetchBlocklist,
844
- fetchDisappearingDuration,
845
1159
  fetchStatus,
1160
+ fetchDisappearingDuration,
846
1161
  updateProfilePicture,
847
1162
  removeProfilePicture,
848
1163
  updateProfileStatus,
849
1164
  updateProfileName,
850
1165
  updateBlockStatus,
1166
+ updateDisableLinkPreviewsPrivacy,
851
1167
  updateCallPrivacy,
852
1168
  updateMessagesPrivacy,
853
1169
  updateLastSeenPrivacy,
@@ -861,11 +1177,17 @@ const makeChatsSocket = (config) => {
861
1177
  resyncAppState,
862
1178
  chatModify,
863
1179
  cleanDirtyBits,
1180
+ addOrEditContact,
1181
+ removeContact,
1182
+ placeholderResendCache,
1183
+ addLabel,
864
1184
  addChatLabel,
865
1185
  removeChatLabel,
866
1186
  addMessageLabel,
867
1187
  removeMessageLabel,
868
- star
1188
+ star,
1189
+ addOrEditQuickReply,
1190
+ removeQuickReply
869
1191
  };
870
1192
  };
871
- exports.makeChatsSocket = makeChatsSocket;
1193
+ //# sourceMappingURL=chats.js.map