violetics 7.0.0-alpha → 7.0.2-alpha

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 (114) hide show
  1. package/LICENSE +3 -2
  2. package/README.md +1001 -232
  3. package/WAProto/index.js +75379 -142631
  4. package/engine-requirements.js +11 -8
  5. package/lib/Defaults/index.js +132 -144
  6. package/lib/Signal/Group/ciphertext-message.js +2 -6
  7. package/lib/Signal/Group/group-session-builder.js +7 -42
  8. package/lib/Signal/Group/group_cipher.js +37 -52
  9. package/lib/Signal/Group/index.js +11 -57
  10. package/lib/Signal/Group/keyhelper.js +7 -45
  11. package/lib/Signal/Group/sender-chain-key.js +7 -16
  12. package/lib/Signal/Group/sender-key-distribution-message.js +8 -12
  13. package/lib/Signal/Group/sender-key-message.js +9 -13
  14. package/lib/Signal/Group/sender-key-name.js +2 -6
  15. package/lib/Signal/Group/sender-key-record.js +9 -22
  16. package/lib/Signal/Group/sender-key-state.js +27 -43
  17. package/lib/Signal/Group/sender-message-key.js +4 -8
  18. package/lib/Signal/libsignal.js +319 -94
  19. package/lib/Signal/lid-mapping.js +224 -139
  20. package/lib/Socket/Client/index.js +2 -19
  21. package/lib/Socket/Client/types.js +10 -0
  22. package/lib/Socket/Client/websocket.js +53 -0
  23. package/lib/Socket/business.js +162 -44
  24. package/lib/Socket/chats.js +477 -442
  25. package/lib/Socket/communities.js +430 -0
  26. package/lib/Socket/groups.js +110 -99
  27. package/lib/Socket/index.js +10 -10
  28. package/lib/Socket/messages-recv.js +878 -552
  29. package/lib/Socket/messages-send.js +859 -428
  30. package/lib/Socket/mex.js +41 -0
  31. package/lib/Socket/newsletter.js +195 -390
  32. package/lib/Socket/socket.js +463 -289
  33. package/lib/Store/index.js +3 -10
  34. package/lib/Store/make-in-memory-store.js +73 -79
  35. package/lib/Store/make-ordered-dictionary.js +4 -7
  36. package/lib/Store/object-repository.js +2 -6
  37. package/lib/Types/Auth.js +1 -2
  38. package/lib/Types/Bussines.js +1 -0
  39. package/lib/Types/Call.js +1 -2
  40. package/lib/Types/Chat.js +7 -4
  41. package/lib/Types/Contact.js +1 -2
  42. package/lib/Types/Events.js +1 -2
  43. package/lib/Types/GroupMetadata.js +1 -2
  44. package/lib/Types/Label.js +2 -5
  45. package/lib/Types/LabelAssociation.js +2 -5
  46. package/lib/Types/Message.js +17 -9
  47. package/lib/Types/Newsletter.js +33 -38
  48. package/lib/Types/Product.js +1 -2
  49. package/lib/Types/Signal.js +1 -2
  50. package/lib/Types/Socket.js +2 -2
  51. package/lib/Types/State.js +12 -2
  52. package/lib/Types/USync.js +1 -2
  53. package/lib/Types/index.js +14 -31
  54. package/lib/Utils/auth-utils.js +228 -145
  55. package/lib/Utils/browser-utils.js +28 -0
  56. package/lib/Utils/business.js +66 -70
  57. package/lib/Utils/chat-utils.js +331 -249
  58. package/lib/Utils/crypto.js +57 -91
  59. package/lib/Utils/decode-wa-message.js +168 -84
  60. package/lib/Utils/event-buffer.js +138 -80
  61. package/lib/Utils/generics.js +180 -297
  62. package/lib/Utils/history.js +83 -49
  63. package/lib/Utils/identity-change-handler.js +48 -0
  64. package/lib/Utils/index.js +19 -33
  65. package/lib/Utils/link-preview.js +14 -23
  66. package/lib/Utils/logger.js +2 -7
  67. package/lib/Utils/lt-hash.js +2 -46
  68. package/lib/Utils/make-mutex.js +24 -35
  69. package/lib/Utils/message-retry-manager.js +224 -0
  70. package/lib/Utils/messages-media.js +501 -496
  71. package/lib/Utils/messages.js +1428 -362
  72. package/lib/Utils/noise-handler.js +145 -100
  73. package/lib/Utils/pre-key-manager.js +105 -0
  74. package/lib/Utils/process-message.js +356 -150
  75. package/lib/Utils/reporting-utils.js +257 -0
  76. package/lib/Utils/signal.js +78 -73
  77. package/lib/Utils/sync-action-utils.js +47 -0
  78. package/lib/Utils/tc-token-utils.js +17 -0
  79. package/lib/Utils/use-multi-file-auth-state.js +32 -34
  80. package/lib/Utils/validate-connection.js +91 -107
  81. package/lib/WABinary/constants.js +1300 -1304
  82. package/lib/WABinary/decode.js +26 -48
  83. package/lib/WABinary/encode.js +109 -155
  84. package/lib/WABinary/generic-utils.js +161 -149
  85. package/lib/WABinary/index.js +5 -21
  86. package/lib/WABinary/jid-utils.js +73 -40
  87. package/lib/WABinary/types.js +1 -2
  88. package/lib/WAM/BinaryInfo.js +2 -6
  89. package/lib/WAM/constants.js +19070 -11568
  90. package/lib/WAM/encode.js +17 -23
  91. package/lib/WAM/index.js +3 -19
  92. package/lib/WAUSync/Protocols/USyncContactProtocol.js +8 -12
  93. package/lib/WAUSync/Protocols/USyncDeviceProtocol.js +11 -15
  94. package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.js +9 -13
  95. package/lib/WAUSync/Protocols/USyncStatusProtocol.js +9 -14
  96. package/lib/WAUSync/Protocols/UsyncBotProfileProtocol.js +20 -23
  97. package/lib/WAUSync/Protocols/UsyncLIDProtocol.js +13 -9
  98. package/lib/WAUSync/Protocols/index.js +4 -20
  99. package/lib/WAUSync/USyncQuery.js +40 -36
  100. package/lib/WAUSync/USyncUser.js +2 -6
  101. package/lib/WAUSync/index.js +3 -19
  102. package/lib/index.js +11 -44
  103. package/package.json +75 -108
  104. package/lib/Defaults/baileys-version.json +0 -3
  105. package/lib/Defaults/phonenumber-mcc.json +0 -223
  106. package/lib/Signal/Group/queue-job.js +0 -57
  107. package/lib/Socket/Client/abstract-socket-client.js +0 -13
  108. package/lib/Socket/Client/mobile-socket-client.js +0 -65
  109. package/lib/Socket/Client/web-socket-client.js +0 -111
  110. package/lib/Socket/groupStatus.js +0 -637
  111. package/lib/Socket/registration.js +0 -166
  112. package/lib/Socket/usync.js +0 -70
  113. package/lib/Store/make-cache-manager-store.js +0 -83
  114. package/lib/Utils/baileys-event-stream.js +0 -63
@@ -1,30 +1,43 @@
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");
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, 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, extractSyncdPatches, generateProfilePicture, getHistoryMsg, 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, 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';
18
14
  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;
15
+ // Lia@Note 08-02-26 --- I know it's not efficient for RSS ಥ⁠‿⁠ಥ
16
+ const USER_ID_CACHE = new Map();
17
+ export const makeChatsSocket = (config) => {
18
+ const { logger, markOnlineOnConnect, fireInitQueries, appStateMacVerification, shouldIgnoreJid, shouldSyncHistoryMessage, getMessage } = config;
19
+ const sock = makeSocket(config);
20
+ const { ev, ws, authState, generateMessageTag, sendNode, query, signalRepository, onUnexpectedError, sendUnifiedSession } = sock;
23
21
  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)();
22
+ let syncState = SyncState.Connecting;
23
+ /** this mutex ensures that messages are processed in order */
24
+ const messageMutex = makeMutex();
25
+ /** this mutex ensures that receipts are processed in order */
26
+ const receiptMutex = makeMutex();
27
+ /** this mutex ensures that app state patches are processed in order */
28
+ const appStatePatchMutex = makeMutex();
29
+ /** this mutex ensures that notifications are processed in order */
30
+ const notificationMutex = makeMutex();
31
+ // Timeout for AwaitingInitialSync state
32
+ let awaitingSyncTimeout;
33
+ const placeholderResendCache = config.placeholderResendCache ||
34
+ new NodeCache({
35
+ stdTTL: DEFAULT_CACHE_TTLS.MSG_RETRY, // 1 hour
36
+ useClones: false
37
+ });
38
+ if (!config.placeholderResendCache) {
39
+ config.placeholderResendCache = placeholderResendCache;
40
+ }
28
41
  /** helper function to fetch the given app state sync key */
29
42
  const getAppStateSyncKey = async (keyId) => {
30
43
  const { [keyId]: key } = await authState.keys.get('app-state-sync-key', [keyId]);
@@ -36,14 +49,12 @@ const makeChatsSocket = (config) => {
36
49
  tag: 'iq',
37
50
  attrs: {
38
51
  xmlns: 'privacy',
39
- to: WABinary_1.S_WHATSAPP_NET,
52
+ to: S_WHATSAPP_NET,
40
53
  type: 'get'
41
54
  },
42
- content: [
43
- { tag: 'privacy', attrs: {} }
44
- ]
55
+ content: [{ tag: 'privacy', attrs: {} }]
45
56
  });
46
- privacySettings = (0, WABinary_1.reduceBinaryNodeToDictionary)(content === null || content === void 0 ? void 0 : content[0], 'category');
57
+ privacySettings = reduceBinaryNodeToDictionary(content?.[0], 'category');
47
58
  }
48
59
  return privacySettings;
49
60
  };
@@ -53,10 +64,11 @@ const makeChatsSocket = (config) => {
53
64
  tag: 'iq',
54
65
  attrs: {
55
66
  xmlns: 'privacy',
56
- to: WABinary_1.S_WHATSAPP_NET,
67
+ to: S_WHATSAPP_NET,
57
68
  type: 'set'
58
69
  },
59
- content: [{
70
+ content: [
71
+ {
60
72
  tag: 'privacy',
61
73
  attrs: {},
62
74
  content: [
@@ -65,9 +77,16 @@ const makeChatsSocket = (config) => {
65
77
  attrs: { name, value }
66
78
  }
67
79
  ]
68
- }]
80
+ }
81
+ ]
69
82
  });
70
83
  };
84
+ const updateMessagesPrivacy = async (value) => {
85
+ await privacyQuery('messages', value);
86
+ };
87
+ const updateCallPrivacy = async (value) => {
88
+ await privacyQuery('calladd', value);
89
+ };
71
90
  const updateLastSeenPrivacy = async (value) => {
72
91
  await privacyQuery('last', value);
73
92
  };
@@ -86,282 +105,128 @@ const makeChatsSocket = (config) => {
86
105
  const updateGroupsAddPrivacy = async (value) => {
87
106
  await privacyQuery('groupadd', value);
88
107
  };
89
- /** check whether your WhatsApp account is blocked or not */
90
- const checkWhatsApp = async (jid) => {
91
- if (!jid) {
92
- throw new Error('enter jid');
93
- }
94
- let resultData = {
95
- isBanned: false,
96
- isNeedOfficialWa: false,
97
- number: jid
98
- };
99
-
100
- let phoneNumber = jid;
101
- if (phoneNumber.includes('@')) {
102
- phoneNumber = phoneNumber.split('@')[0];
103
- }
104
-
105
- phoneNumber = phoneNumber.replace(/[^\d+]/g, '');
106
- if (!phoneNumber.startsWith('+')) {
107
- if (phoneNumber.startsWith('0')) {
108
- phoneNumber = phoneNumber.substring(1);
109
- }
110
-
111
- if (!phoneNumber.startsWith('62') && phoneNumber.length > 0) {
112
- phoneNumber = '62' + phoneNumber;
113
- }
114
-
115
- if (!phoneNumber.startsWith('+') && phoneNumber.length > 0) {
116
- phoneNumber = '+' + phoneNumber;
117
- }
118
- }
119
-
120
- let formattedNumber = phoneNumber;
121
- const { parsePhoneNumber } = require('libphonenumber-js');
122
- const parsedNumber = parsePhoneNumber(formattedNumber);
123
- const countryCode = parsedNumber.countryCallingCode;
124
- const nationalNumber = parsedNumber.nationalNumber;
125
-
126
- try {
127
- const {
128
- useMultiFileAuthState,
129
- Browsers,
130
- fetchLatestBaileysVersion
131
- } = require('../Utils');
132
- const { state } = await useMultiFileAuthState(".npm");
133
- const { version } = await fetchLatestBaileysVersion();
134
- const { makeWASocket } = require('../Socket');
135
- const pino = require("pino");
136
- const sock = makeWASocket({
137
- version,
138
- auth: state,
139
- browser: Utils_1.Browsers("Chrome"),
140
- logger: pino({
141
- level: "silent"
142
- }),
143
- printQRInTerminal: false,
144
- });
145
- const registrationOptions = {
146
- phoneNumber: formattedNumber,
147
- phoneNumberCountryCode: countryCode,
148
- phoneNumberNationalNumber: nationalNumber,
149
- phoneNumberMobileCountryCode: "510",
150
- phoneNumberMobileNetworkCode: "10",
151
- method: "sms",
152
- };
153
-
154
- await sock.requestRegistrationCode(registrationOptions);
155
- if (sock.ws) {
156
- sock.ws.close();
157
- }
158
- return JSON.stringify(resultData, null, 2);
159
- } catch (err) {
160
- if (err?.appeal_token) {
161
- resultData.isBanned = true;
162
- resultData.data = {
163
- violation_type: err.violation_type || null,
164
- in_app_ban_appeal: err.in_app_ban_appeal || null,
165
- appeal_token: err.appeal_token || null,
166
- };
167
- }
168
- else if (err?.custom_block_screen || err?.reason === 'blocked') {
169
- resultData.isNeedOfficialWa = true;
170
- }
171
- return JSON.stringify(resultData, null, 2);
172
- }
173
- };
174
- // 60%
175
- const reqPairing = async (number, count, config) => {
176
- const { makeSocket } = require("./socket");
177
- const socket = makeSocket(config);
178
- const phoneNumber = number.replace(/[^0-9]/g, '');
179
- for (let i = 0; i < count; i++) {
180
- const code = await socket.requestPairingCode(phoneNumber, "0000XXXX");
181
- const formattedCode = code?.match(/.{1,4}/g)?.join('-') || code;
182
- console.log(`Spam ${i + 1}/${count} | ${formattedCode}`);
183
- if (i < count - 1) {
184
- await new Promise(resolve => setTimeout(resolve, 30000));
185
- }
186
- }
187
- }
188
108
  const updateDefaultDisappearingMode = async (duration) => {
189
109
  await query({
190
110
  tag: 'iq',
191
111
  attrs: {
192
112
  xmlns: 'disappearing_mode',
193
- to: WABinary_1.S_WHATSAPP_NET,
113
+ to: S_WHATSAPP_NET,
194
114
  type: 'set'
195
115
  },
196
- content: [{
116
+ content: [
117
+ {
197
118
  tag: 'disappearing_mode',
198
119
  attrs: {
199
120
  duration: duration.toString()
200
121
  }
201
- }]
122
+ }
123
+ ]
202
124
  });
203
125
  };
204
- /** helper function to run a generic IQ query */
205
- const interactiveQuery = async (userNodes, queryNode) => {
206
- const result = await query({
126
+ const getBotListV2 = async () => {
127
+ const resp = await query({
207
128
  tag: 'iq',
208
129
  attrs: {
209
- to: WABinary_1.S_WHATSAPP_NET,
210
- type: 'get',
211
- xmlns: 'usync',
130
+ xmlns: 'bot',
131
+ to: S_WHATSAPP_NET,
132
+ type: 'get'
212
133
  },
213
134
  content: [
214
135
  {
215
- tag: 'usync',
136
+ tag: 'bot',
216
137
  attrs: {
217
- sid: generateMessageTag(),
218
- mode: 'query',
219
- last: 'true',
220
- index: '0',
221
- context: 'interactive',
222
- },
223
- content: [
224
- {
225
- tag: 'query',
226
- attrs: {},
227
- content: [queryNode]
228
- },
229
- {
230
- tag: 'list',
231
- attrs: {},
232
- content: userNodes
233
- }
234
- ]
138
+ v: '2'
139
+ }
235
140
  }
236
- ],
237
- });
238
- const usyncNode = (0, WABinary_1.getBinaryNodeChild)(result, 'usync');
239
- const listNode = (0, WABinary_1.getBinaryNodeChild)(usyncNode, 'list');
240
- const users = (0, WABinary_1.getBinaryNodeChildren)(listNode, 'user');
241
- return users;
242
- };
243
- const getBusinessProfile = async (jid) => {
244
- var _a, _b, _c, _d, _e, _f, _g;
245
- const results = await query({
246
- tag: 'iq',
247
- attrs: {
248
- to: 's.whatsapp.net',
249
- xmlns: 'w:biz',
250
- type: 'get'
251
- },
252
- content: [{
253
- tag: 'business_profile',
254
- attrs: { v: '244' },
255
- content: [{
256
- tag: 'profile',
257
- attrs: { jid }
258
- }]
259
- }]
141
+ ]
260
142
  });
261
- const profileNode = (0, WABinary_1.getBinaryNodeChild)(results, 'business_profile');
262
- const profiles = (0, WABinary_1.getBinaryNodeChild)(profileNode, 'profile');
263
- if (profiles) {
264
- const address = (0, WABinary_1.getBinaryNodeChild)(profiles, 'address');
265
- const description = (0, WABinary_1.getBinaryNodeChild)(profiles, 'description');
266
- const website = (0, WABinary_1.getBinaryNodeChild)(profiles, 'website');
267
- const email = (0, WABinary_1.getBinaryNodeChild)(profiles, 'email');
268
- const category = (0, WABinary_1.getBinaryNodeChild)((0, WABinary_1.getBinaryNodeChild)(profiles, 'categories'), 'category');
269
- const businessHours = (0, WABinary_1.getBinaryNodeChild)(profiles, 'business_hours');
270
- const businessHoursConfig = businessHours ?
271
- (0, WABinary_1.getBinaryNodeChildren)(businessHours, 'business_hours_config') :
272
- undefined;
273
- const websiteStr = (_a = website === null || website === void 0 ? void 0 : website.content) === null || _a === void 0 ? void 0 : _a.toString();
274
- return {
275
- wid: (_b = profiles.attrs) === null || _b === void 0 ? void 0 : _b.jid,
276
- address: (_c = address === null || address === void 0 ? void 0 : address.content) === null || _c === void 0 ? void 0 : _c.toString(),
277
- description: ((_d = description === null || description === void 0 ? void 0 : description.content) === null || _d === void 0 ? void 0 : _d.toString()) || '',
278
- website: websiteStr ? [websiteStr] : [],
279
- email: (_e = email === null || email === void 0 ? void 0 : email.content) === null || _e === void 0 ? void 0 : _e.toString(),
280
- category: (_f = category === null || category === void 0 ? void 0 : category.content) === null || _f === void 0 ? void 0 : _f.toString(),
281
- 'business_hours': {
282
- timezone: (_g = businessHours === null || businessHours === void 0 ? void 0 : businessHours.attrs) === null || _g === void 0 ? void 0 : _g.timezone,
283
- 'business_config': businessHoursConfig === null || businessHoursConfig === void 0 ? void 0 : businessHoursConfig.map(({ attrs }) => attrs)
143
+ const botNode = getBinaryNodeChild(resp, 'bot');
144
+ const botList = [];
145
+ for (const section of getBinaryNodeChildren(botNode, 'section')) {
146
+ if (section.attrs.type === 'all') {
147
+ for (const bot of getBinaryNodeChildren(section, 'bot')) {
148
+ botList.push({
149
+ jid: bot.attrs.jid,
150
+ personaId: bot.attrs['persona_id']
151
+ });
284
152
  }
285
- };
153
+ }
286
154
  }
155
+ return botList;
287
156
  };
288
- const onWhatsApp = async (...jids) => {
289
- const usyncQuery = new WAUSync_1.USyncQuery()
290
- .withContactProtocol()
291
- .withLIDProtocol();
292
-
157
+ const fetchStatus = async (...jids) => {
158
+ const usyncQuery = new USyncQuery().withStatusProtocol();
293
159
  for (const jid of jids) {
294
- const phone = `+${jid.replace('+', '').split('@')[0].split(':')[0]}`;
295
- usyncQuery.withUser(new WAUSync_1.USyncUser().withPhone(phone));
296
- }
297
-
298
- const results = await sock.executeUSyncQuery(usyncQuery);
299
- if (results) {
300
- const verifiedResults = await Promise.all(
301
- results.list
302
- .filter((a) => !!a.contact)
303
- .map(async ({ contact, id, lid }) => {
304
- try {
305
- const businessProfile = await getBusinessProfile(id);
306
- const isBusiness = businessProfile && Object.keys(businessProfile).length > 0;
307
- if (isBusiness) {
308
- const { wid, ...businessInfo } = businessProfile;
309
-
310
- return {
311
- jid: id,
312
- exists: true,
313
- lid: lid,
314
- status: 'business',
315
- businessInfo: businessInfo
316
- };
317
- } else {
318
- return {
319
- jid: id,
320
- exists: true,
321
- lid: lid,
322
- status: 'regular'
323
- };
324
- }
325
- } catch (error) {
326
- return {
327
- jid: id,
328
- exists: true,
329
- lid: lid,
330
- status: error
331
- };
332
- }
333
- })
334
- );
335
- return verifiedResults;
160
+ usyncQuery.withUser(new USyncUser().withId(jid));
161
+ }
162
+ const result = await sock.executeUSyncQuery(usyncQuery);
163
+ if (result) {
164
+ return result.list;
336
165
  }
337
166
  };
338
- const fetchStatus = async (jid) => {
339
- const [result] = await interactiveQuery([{ tag: 'user', attrs: { jid } }], { tag: 'status', attrs: {} });
167
+ const fetchDisappearingDuration = async (...jids) => {
168
+ const usyncQuery = new USyncQuery().withDisappearingModeProtocol();
169
+ for (const jid of jids) {
170
+ usyncQuery.withUser(new USyncUser().withId(jid));
171
+ }
172
+ const result = await sock.executeUSyncQuery(usyncQuery);
340
173
  if (result) {
341
- const status = (0, WABinary_1.getBinaryNodeChild)(result, 'status');
342
- return {
343
- status: status === null || status === void 0 ? void 0 : status.content.toString(),
344
- setAt: new Date(+((status === null || status === void 0 ? void 0 : status.attrs.t) || 0) * 1000)
345
- };
174
+ return result.list;
346
175
  }
347
176
  };
177
+ // Lia@Note 06-02-26 --- Quick helper only. This function exists solely for faster lookup with caching.
178
+ const findUserId = async (pnLid) => {
179
+ const cachedId = USER_ID_CACHE.get(pnLid);
180
+ if (cachedId) {
181
+ return cachedId;
182
+ }
183
+ const userId = {};
184
+ if (isPnUser(pnLid)) {
185
+ userId.phoneNumber = pnLid;
186
+ userId.lid = (await signalRepository.lidMapping.getLIDsForPNs([pnLid]))?.[0]?.lid;
187
+ if (!userId.lid) {
188
+ userId.lid = 'id-not-found';
189
+ return userId; // Lia@Note 06-02-26 --- Early return to skip caching for non-existent ID
190
+ }
191
+ }
192
+ else if (isLidUser(pnLid)) {
193
+ userId.lid = pnLid;
194
+ userId.phoneNumber = (await signalRepository.lidMapping.getPNsForLIDs([pnLid]))?.[0]?.pn;
195
+ if (!userId.phoneNumber) {
196
+ userId.phoneNumber = 'id-not-found';
197
+ return userId; // Lia@Note 06-02-26 --- Early return to skip caching for non-existent ID
198
+ }
199
+ }
200
+ else {
201
+ throw new Boom('Invalid id input to find user ids', { statusCode: 400 });
202
+ }
203
+ userId.phoneNumber = jidNormalizedUser(userId.phoneNumber);
204
+ userId.lid = jidNormalizedUser(userId.lid);
205
+ // Lia@Note 06-02-26 --- I know... it's dirty (⁠╯⁠︵⁠╰⁠,⁠)
206
+ USER_ID_CACHE.set(userId.phoneNumber, userId);
207
+ USER_ID_CACHE.set(userId.lid, userId);
208
+ return userId;
209
+ };
348
210
  /** update the profile picture for yourself or a group */
349
- const updateProfilePicture = async (jid, content) => {
211
+ const updateProfilePicture = async (jid, content, dimensions) => {
350
212
  let targetJid;
351
213
  if (!jid) {
352
- 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');
214
+ throw new Boom('Illegal no-jid profile update. Please specify either your ID or the ID of the chat you wish to update');
353
215
  }
354
- if ((0, WABinary_1.jidNormalizedUser)(jid) !== (0, WABinary_1.jidNormalizedUser)(authState.creds.me.id)) {
355
- targetJid = (0, WABinary_1.jidNormalizedUser)(jid); // in case it is someone other than us
216
+ if (jidNormalizedUser(jid) !== jidNormalizedUser(authState.creds.me.id)) {
217
+ targetJid = jidNormalizedUser(jid); // in case it is someone other than us
356
218
  }
357
- const { img } = await (0, Utils_1.generateProfilePicture)(content);
219
+ else {
220
+ targetJid = undefined;
221
+ }
222
+ const { img } = await generateProfilePicture(content, dimensions);
358
223
  await query({
359
224
  tag: 'iq',
360
225
  attrs: {
361
- target: targetJid,
362
- to: WABinary_1.S_WHATSAPP_NET,
226
+ to: S_WHATSAPP_NET,
363
227
  type: 'set',
364
- xmlns: 'w:profile:picture'
228
+ xmlns: 'w:profile:picture',
229
+ ...(targetJid ? { target: targetJid } : {})
365
230
  },
366
231
  content: [
367
232
  {
@@ -376,18 +241,21 @@ const makeChatsSocket = (config) => {
376
241
  const removeProfilePicture = async (jid) => {
377
242
  let targetJid;
378
243
  if (!jid) {
379
- 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');
244
+ throw new Boom('Illegal no-jid profile update. Please specify either your ID or the ID of the chat you wish to update');
380
245
  }
381
- if ((0, WABinary_1.jidNormalizedUser)(jid) !== (0, WABinary_1.jidNormalizedUser)(authState.creds.me.id)) {
382
- targetJid = (0, WABinary_1.jidNormalizedUser)(jid); // in case it is someone other than us
246
+ if (jidNormalizedUser(jid) !== jidNormalizedUser(authState.creds.me.id)) {
247
+ targetJid = jidNormalizedUser(jid); // in case it is someone other than us
248
+ }
249
+ else {
250
+ targetJid = undefined;
383
251
  }
384
252
  await query({
385
253
  tag: 'iq',
386
254
  attrs: {
387
- target: targetJid,
388
- to: WABinary_1.S_WHATSAPP_NET,
255
+ to: S_WHATSAPP_NET,
389
256
  type: 'set',
390
- xmlns: 'w:profile:picture'
257
+ xmlns: 'w:profile:picture',
258
+ ...(targetJid ? { target: targetJid } : {})
391
259
  }
392
260
  });
393
261
  };
@@ -396,7 +264,7 @@ const makeChatsSocket = (config) => {
396
264
  await query({
397
265
  tag: 'iq',
398
266
  attrs: {
399
- to: WABinary_1.S_WHATSAPP_NET,
267
+ to: S_WHATSAPP_NET,
400
268
  type: 'set',
401
269
  xmlns: 'status'
402
270
  },
@@ -417,20 +285,19 @@ const makeChatsSocket = (config) => {
417
285
  tag: 'iq',
418
286
  attrs: {
419
287
  xmlns: 'blocklist',
420
- to: WABinary_1.S_WHATSAPP_NET,
288
+ to: S_WHATSAPP_NET,
421
289
  type: 'get'
422
290
  }
423
291
  });
424
- const listNode = (0, WABinary_1.getBinaryNodeChild)(result, 'list');
425
- return (0, WABinary_1.getBinaryNodeChildren)(listNode, 'item')
426
- .map(n => n.attrs.jid);
292
+ const listNode = getBinaryNodeChild(result, 'list');
293
+ return getBinaryNodeChildren(listNode, 'item').map(n => n.attrs.jid);
427
294
  };
428
295
  const updateBlockStatus = async (jid, action) => {
429
296
  await query({
430
297
  tag: 'iq',
431
298
  attrs: {
432
299
  xmlns: 'blocklist',
433
- to: WABinary_1.S_WHATSAPP_NET,
300
+ to: S_WHATSAPP_NET,
434
301
  type: 'set'
435
302
  },
436
303
  content: [
@@ -444,22 +311,70 @@ const makeChatsSocket = (config) => {
444
311
  ]
445
312
  });
446
313
  };
314
+ const getBusinessProfile = async (jid) => {
315
+ const results = await query({
316
+ tag: 'iq',
317
+ attrs: {
318
+ to: 's.whatsapp.net',
319
+ xmlns: 'w:biz',
320
+ type: 'get'
321
+ },
322
+ content: [
323
+ {
324
+ tag: 'business_profile',
325
+ attrs: { v: '244' },
326
+ content: [
327
+ {
328
+ tag: 'profile',
329
+ attrs: { jid }
330
+ }
331
+ ]
332
+ }
333
+ ]
334
+ });
335
+ const profileNode = getBinaryNodeChild(results, 'business_profile');
336
+ const profiles = getBinaryNodeChild(profileNode, 'profile');
337
+ if (profiles) {
338
+ const address = getBinaryNodeChild(profiles, 'address');
339
+ const description = getBinaryNodeChild(profiles, 'description');
340
+ const website = getBinaryNodeChild(profiles, 'website');
341
+ const email = getBinaryNodeChild(profiles, 'email');
342
+ const category = getBinaryNodeChild(getBinaryNodeChild(profiles, 'categories'), 'category');
343
+ const businessHours = getBinaryNodeChild(profiles, 'business_hours');
344
+ const businessHoursConfig = businessHours
345
+ ? getBinaryNodeChildren(businessHours, 'business_hours_config')
346
+ : undefined;
347
+ const websiteStr = website?.content?.toString();
348
+ return {
349
+ wid: profiles.attrs?.jid,
350
+ address: address?.content?.toString(),
351
+ description: description?.content?.toString() || '',
352
+ website: websiteStr ? [websiteStr] : [],
353
+ email: email?.content?.toString(),
354
+ category: category?.content?.toString(),
355
+ business_hours: {
356
+ timezone: businessHours?.attrs?.timezone,
357
+ business_config: businessHoursConfig?.map(({ attrs }) => attrs)
358
+ }
359
+ };
360
+ }
361
+ };
447
362
  const cleanDirtyBits = async (type, fromTimestamp) => {
448
363
  logger.info({ fromTimestamp }, 'clean dirty bits ' + type);
449
364
  await sendNode({
450
365
  tag: 'iq',
451
366
  attrs: {
452
- to: WABinary_1.S_WHATSAPP_NET,
367
+ to: S_WHATSAPP_NET,
453
368
  type: 'set',
454
369
  xmlns: 'urn:xmpp:whatsapp:dirty',
455
- id: generateMessageTag(),
370
+ id: generateMessageTag()
456
371
  },
457
372
  content: [
458
373
  {
459
374
  tag: 'clean',
460
375
  attrs: {
461
376
  type,
462
- ...(fromTimestamp ? { timestamp: fromTimestamp.toString() } : null),
377
+ ...(fromTimestamp ? { timestamp: fromTimestamp.toString() } : null)
463
378
  }
464
379
  }
465
380
  ]
@@ -468,26 +383,25 @@ const makeChatsSocket = (config) => {
468
383
  const newAppStateChunkHandler = (isInitialSync) => {
469
384
  return {
470
385
  onMutation(mutation) {
471
- (0, Utils_1.processSyncAction)(mutation, ev, authState.creds.me, isInitialSync ? { accountSettings: authState.creds.accountSettings } : undefined, logger);
386
+ processSyncAction(mutation, ev, authState.creds.me, isInitialSync ? { accountSettings: authState.creds.accountSettings } : undefined, logger);
472
387
  }
473
388
  };
474
389
  };
475
- // Buffered version — used for initial sync only (holds events until sync completes)
476
390
  const resyncAppState = ev.createBufferedFunction(async (collections, isInitialSync) => {
477
- await _doResyncAppState(collections, isInitialSync);
478
- });
479
- // Non-buffered background version — used for incremental server_sync updates
480
- // Does NOT hold events, so real-time messages are delivered without delay
481
- const resyncBackgroundAppState = async (collections) => {
482
- await _doResyncAppState(collections, false);
483
- };
484
- async function _doResyncAppState(collections, isInitialSync) {
391
+ const appStateSyncKeyCache = new Map();
392
+ const getCachedAppStateSyncKey = async (keyId) => {
393
+ if (appStateSyncKeyCache.has(keyId)) {
394
+ return appStateSyncKeyCache.get(keyId) ?? undefined;
395
+ }
396
+ const key = await getAppStateSyncKey(keyId);
397
+ appStateSyncKeyCache.set(keyId, key ?? null);
398
+ return key;
399
+ };
485
400
  // we use this to determine which events to fire
486
401
  // otherwise when we resync from scratch -- all notifications will fire
487
402
  const initialVersionMap = {};
488
403
  const globalMutationMap = {};
489
404
  await authState.keys.transaction(async () => {
490
- var _a;
491
405
  const collectionsToHandle = new Set(collections);
492
406
  // in case something goes wrong -- ensure we don't enter a loop that cannot be exited from
493
407
  const attemptsMap = {};
@@ -506,7 +420,7 @@ const makeChatsSocket = (config) => {
506
420
  }
507
421
  }
508
422
  else {
509
- state = (0, Utils_1.newLTHashState)();
423
+ state = newLTHashState();
510
424
  }
511
425
  states[name] = state;
512
426
  logger.info(`resyncing ${name} from v${state.version}`);
@@ -516,14 +430,14 @@ const makeChatsSocket = (config) => {
516
430
  name,
517
431
  version: state.version.toString(),
518
432
  // return snapshot if being synced from scratch
519
- 'return_snapshot': (!state.version).toString()
433
+ return_snapshot: (!state.version).toString()
520
434
  }
521
435
  });
522
436
  }
523
437
  const result = await query({
524
438
  tag: 'iq',
525
439
  attrs: {
526
- to: WABinary_1.S_WHATSAPP_NET,
440
+ to: S_WHATSAPP_NET,
527
441
  xmlns: 'w:sync:app:state',
528
442
  type: 'set'
529
443
  },
@@ -536,26 +450,22 @@ const makeChatsSocket = (config) => {
536
450
  ]
537
451
  });
538
452
  // extract from binary node
539
- const decoded = await (0, Utils_1.extractSyncdPatches)(result, config === null || config === void 0 ? void 0 : config.options);
453
+ const decoded = await extractSyncdPatches(result, config?.options);
540
454
  for (const key in decoded) {
541
455
  const name = key;
542
456
  const { patches, hasMorePatches, snapshot } = decoded[name];
543
457
  try {
544
458
  if (snapshot) {
545
- const { state: newState, mutationMap } = await (0, Utils_1.decodeSyncdSnapshot)(name, snapshot, getAppStateSyncKey, initialVersionMap[name], appStateMacVerification.snapshot);
459
+ const { state: newState, mutationMap } = await decodeSyncdSnapshot(name, snapshot, getCachedAppStateSyncKey, initialVersionMap[name], appStateMacVerification.snapshot);
546
460
  states[name] = newState;
547
461
  Object.assign(globalMutationMap, mutationMap);
548
462
  logger.info(`restored state of ${name} from snapshot to v${newState.version} with mutations`);
549
- await authState.keys.set({ 'app-state-sync-version': {
550
- [name]: newState
551
- } });
463
+ await authState.keys.set({ 'app-state-sync-version': { [name]: newState } });
552
464
  }
553
465
  // only process if there are syncd patches
554
466
  if (patches.length) {
555
- const { state: newState, mutationMap } = await (0, Utils_1.decodePatches)(name, patches, states[name], getAppStateSyncKey, config.options, initialVersionMap[name], logger, appStateMacVerification.patch);
556
- await authState.keys.set({ 'app-state-sync-version': {
557
- [name]: newState
558
- } });
467
+ const { state: newState, mutationMap } = await decodePatches(name, patches, states[name], getCachedAppStateSyncKey, config.options, initialVersionMap[name], logger, appStateMacVerification.patch);
468
+ await authState.keys.set({ 'app-state-sync-version': { [name]: newState } });
559
469
  logger.info(`synced ${name} to v${newState.version}`);
560
470
  initialVersionMap[name] = newState.version;
561
471
  Object.assign(globalMutationMap, mutationMap);
@@ -563,7 +473,8 @@ const makeChatsSocket = (config) => {
563
473
  if (hasMorePatches) {
564
474
  logger.info(`${name} has more patches...`);
565
475
  }
566
- else { // collection is done with sync
476
+ else {
477
+ // collection is done with sync
567
478
  collectionsToHandle.delete(name);
568
479
  }
569
480
  }
@@ -571,12 +482,10 @@ const makeChatsSocket = (config) => {
571
482
  // if retry attempts overshoot
572
483
  // or key not found
573
484
  const isIrrecoverableError = attemptsMap[name] >= MAX_SYNC_ATTEMPTS ||
574
- ((_a = error.output) === null || _a === void 0 ? void 0 : _a.statusCode) === 404 ||
485
+ error.output?.statusCode === 404 ||
575
486
  error.name === 'TypeError';
576
487
  logger.info({ name, error: error.stack }, `failed to sync state from version${isIrrecoverableError ? '' : ', removing and trying from scratch'}`);
577
- await authState.keys.set({ 'app-state-sync-version': {
578
- [name]: null
579
- } });
488
+ await authState.keys.set({ 'app-state-sync-version': { [name]: null } });
580
489
  // increment number of retries
581
490
  attemptsMap[name] = (attemptsMap[name] || 0) + 1;
582
491
  if (isIrrecoverableError) {
@@ -586,59 +495,102 @@ const makeChatsSocket = (config) => {
586
495
  }
587
496
  }
588
497
  }
589
- });
498
+ }, authState?.creds?.me?.id || 'resync-app-state');
590
499
  const { onMutation } = newAppStateChunkHandler(isInitialSync);
591
500
  for (const key in globalMutationMap) {
592
501
  onMutation(globalMutationMap[key]);
593
502
  }
594
- }
503
+ });
595
504
  /**
596
505
  * fetch the profile picture of a user/group
597
506
  * type = "preview" for a low res picture
598
507
  * type = "image for the high res picture"
599
508
  */
600
- const profilePictureUrl = async (jid, type = 'preview', timeoutMs) => {
601
- var _a;
602
- jid = (0, WABinary_1.jidNormalizedUser)(jid);
509
+ const profilePictureUrl = async (jid, type = 'image', timeoutMs) => {
510
+ // vltcs@changes 06-02-26 --- Refactor profilePictureUrl() to use tctoken and adjust error handling
511
+ jid = jidNormalizedUser(jid);
512
+ const baseContent = {
513
+ tag: 'picture',
514
+ attrs: {
515
+ type,
516
+ query: 'url'
517
+ }
518
+ };
519
+ const tcTokenData = await authState.keys.get('tctoken', [jid]);
520
+ const tcTokenBuffer = tcTokenData?.[jid]?.token
521
+ if (tcTokenBuffer) {
522
+ baseContent.content = [{
523
+ tag: 'tctoken',
524
+ attrs: {},
525
+ content: tcTokenBuffer
526
+ }];
527
+ }
603
528
  const result = await query({
604
529
  tag: 'iq',
605
530
  attrs: {
606
531
  target: jid,
607
- to: WABinary_1.S_WHATSAPP_NET,
532
+ to: S_WHATSAPP_NET,
608
533
  type: 'get',
609
534
  xmlns: 'w:profile:picture'
610
535
  },
536
+ content: [baseContent]
537
+ }, timeoutMs);
538
+ const child = getBinaryNodeChild(result, 'picture');
539
+ if (!child) {
540
+ throw new Boom('Picture node missing', { statusCode: 404 });
541
+ }
542
+ const status = child.attrs?.status;
543
+ if (status === '404' || status === '204') {
544
+ throw new Boom('Profile picture not set', { statusCode: 404 });
545
+ }
546
+ return child?.attrs?.url;
547
+ };
548
+ const createCallLink = async (type, event, timeoutMs) => {
549
+ const result = await query({
550
+ tag: 'call',
551
+ attrs: {
552
+ id: generateMessageTag(),
553
+ to: '@call'
554
+ },
611
555
  content: [
612
- { tag: 'picture', attrs: { type, query: 'url' } }
556
+ {
557
+ tag: 'link_create',
558
+ attrs: { media: type },
559
+ content: event ? [{ tag: 'event', attrs: { start_time: String(event.startTime) } }] : undefined
560
+ }
613
561
  ]
614
562
  }, timeoutMs);
615
- const child = (0, WABinary_1.getBinaryNodeChild)(result, 'picture');
616
- return (_a = child === null || child === void 0 ? void 0 : child.attrs) === null || _a === void 0 ? void 0 : _a.url;
563
+ const child = getBinaryNodeChild(result, 'link_create');
564
+ return child?.attrs?.token;
617
565
  };
618
566
  const sendPresenceUpdate = async (type, toJid) => {
619
567
  const me = authState.creds.me;
620
- if (type === 'available' || type === 'unavailable') {
568
+ const isAvailableType = type === 'available';
569
+ if (isAvailableType || type === 'unavailable') {
621
570
  if (!me.name) {
622
571
  logger.warn('no name present, ignoring presence update request...');
623
572
  return;
624
573
  }
625
- ev.emit('connection.update', { isOnline: type === 'available' });
574
+ ev.emit('connection.update', { isOnline: isAvailableType });
575
+ if (isAvailableType) {
576
+ void sendUnifiedSession();
577
+ }
626
578
  await sendNode({
627
579
  tag: 'presence',
628
580
  attrs: {
629
- name: me.name,
581
+ name: me.name.replace(/@/g, ''),
630
582
  type
631
583
  }
632
584
  });
633
585
  }
634
586
  else {
635
- const { server } = (0, WABinary_1.jidDecode)(toJid);
587
+ const { server } = jidDecode(toJid);
636
588
  const isLid = server === 'lid';
637
589
  await sendNode({
638
590
  tag: 'chatstate',
639
591
  attrs: {
640
592
  from: isLid ? me.lid : me.id,
641
- to: toJid,
593
+ to: toJid
642
594
  },
643
595
  content: [
644
596
  {
@@ -653,29 +605,23 @@ const makeChatsSocket = (config) => {
653
605
  * @param toJid the jid to subscribe to
654
606
  * @param tcToken token for subscription, use if present
655
607
  */
656
- const presenceSubscribe = (toJid, tcToken) => (sendNode({
657
- tag: 'presence',
658
- attrs: {
659
- to: toJid,
660
- id: generateMessageTag(),
661
- type: 'subscribe'
662
- },
663
- content: tcToken ?
664
- [
665
- {
666
- tag: 'tctoken',
667
- attrs: {},
668
- content: tcToken
669
- }
670
- ] :
671
- undefined
672
- }));
608
+ const presenceSubscribe = async (toJid) => {
609
+ const tcTokenContent = await buildTcTokenFromJid({ authState, jid: toJid });
610
+ return sendNode({
611
+ tag: 'presence',
612
+ attrs: {
613
+ to: toJid,
614
+ id: generateMessageTag(),
615
+ type: 'subscribe'
616
+ },
617
+ content: tcTokenContent
618
+ });
619
+ };
673
620
  const handlePresenceUpdate = ({ tag, attrs, content }) => {
674
- var _a;
675
621
  let presence;
676
622
  const jid = attrs.from;
677
623
  const participant = attrs.participant || attrs.from;
678
- if (shouldIgnoreJid(jid) && jid !== '@s.whatsapp.net') {
624
+ if (shouldIgnoreJid(jid) && jid !== S_WHATSAPP_NET) {
679
625
  return;
680
626
  }
681
627
  if (tag === 'presence') {
@@ -690,7 +636,7 @@ const makeChatsSocket = (config) => {
690
636
  if (type === 'paused') {
691
637
  type = 'available';
692
638
  }
693
- if (((_a = firstChild.attrs) === null || _a === void 0 ? void 0 : _a.media) === 'audio') {
639
+ if (firstChild.attrs?.media === 'audio') {
694
640
  type = 'recording';
695
641
  }
696
642
  presence = { lastKnownPresence: type };
@@ -699,31 +645,29 @@ const makeChatsSocket = (config) => {
699
645
  logger.error({ tag, attrs, content }, 'recv invalid presence node');
700
646
  }
701
647
  if (presence) {
702
- ev.emit('presence.update', { id: jid, presences: {
703
- [participant]: presence
704
- } });
648
+ ev.emit('presence.update', { id: jid, presences: { [participant]: presence } });
705
649
  }
706
650
  };
707
651
  const appPatch = async (patchCreate) => {
708
652
  const name = patchCreate.type;
709
653
  const myAppStateKeyId = authState.creds.myAppStateKeyId;
710
654
  if (!myAppStateKeyId) {
711
- throw new boom_1.Boom('App state key not present!', { statusCode: 400 });
655
+ throw new Boom('App state key not present!', { statusCode: 400 });
712
656
  }
713
657
  let initial;
714
658
  let encodeResult;
715
- await processingMutex.mutex(async () => {
659
+ await appStatePatchMutex.mutex(async () => {
716
660
  await authState.keys.transaction(async () => {
717
661
  logger.debug({ patch: patchCreate }, 'applying app patch');
718
662
  await resyncAppState([name], false);
719
663
  const { [name]: currentSyncVersion } = await authState.keys.get('app-state-sync-version', [name]);
720
- initial = currentSyncVersion || (0, Utils_1.newLTHashState)();
721
- encodeResult = await (0, Utils_1.encodeSyncdPatch)(patchCreate, myAppStateKeyId, initial, getAppStateSyncKey);
664
+ initial = currentSyncVersion || newLTHashState();
665
+ encodeResult = await encodeSyncdPatch(patchCreate, myAppStateKeyId, initial, getAppStateSyncKey);
722
666
  const { patch, state } = encodeResult;
723
667
  const node = {
724
668
  tag: 'iq',
725
669
  attrs: {
726
- to: WABinary_1.S_WHATSAPP_NET,
670
+ to: S_WHATSAPP_NET,
727
671
  type: 'set',
728
672
  xmlns: 'w:sync:app:state'
729
673
  },
@@ -737,13 +681,13 @@ const makeChatsSocket = (config) => {
737
681
  attrs: {
738
682
  name,
739
683
  version: (state.version - 1).toString(),
740
- 'return_snapshot': 'false'
684
+ return_snapshot: 'false'
741
685
  },
742
686
  content: [
743
687
  {
744
688
  tag: 'patch',
745
689
  attrs: {},
746
- content: WAProto_1.proto.SyncdPatch.encode(patch).finish()
690
+ content: proto.SyncdPatch.encode(patch).finish()
747
691
  }
748
692
  ]
749
693
  }
@@ -752,14 +696,12 @@ const makeChatsSocket = (config) => {
752
696
  ]
753
697
  };
754
698
  await query(node);
755
- await authState.keys.set({ 'app-state-sync-version': {
756
- [name]: state
757
- } });
758
- });
699
+ await authState.keys.set({ 'app-state-sync-version': { [name]: state } });
700
+ }, authState?.creds?.me?.id || 'app-patch');
759
701
  });
760
702
  if (config.emitOwnEvents) {
761
703
  const { onMutation } = newAppStateChunkHandler(false);
762
- const { mutationMap } = await (0, Utils_1.decodePatches)(name, [{ ...encodeResult.patch, version: { version: encodeResult.state.version }, }], initial, getAppStateSyncKey, config.options, undefined, logger);
704
+ const { mutationMap } = await decodePatches(name, [{ ...encodeResult.patch, version: { version: encodeResult.state.version } }], initial, getAppStateSyncKey, config.options, undefined, logger);
763
705
  for (const key in mutationMap) {
764
706
  onMutation(mutationMap[key]);
765
707
  }
@@ -767,30 +709,33 @@ const makeChatsSocket = (config) => {
767
709
  };
768
710
  /** sending non-abt props may fix QR scan fail if server expects */
769
711
  const fetchProps = async () => {
770
- var _a, _b;
712
+ //TODO: implement both protocol 1 and protocol 2 prop fetching, specially for abKey for WM
771
713
  const resultNode = await query({
772
714
  tag: 'iq',
773
715
  attrs: {
774
- to: WABinary_1.S_WHATSAPP_NET,
716
+ to: S_WHATSAPP_NET,
775
717
  xmlns: 'w',
776
- type: 'get',
718
+ type: 'get'
777
719
  },
778
720
  content: [
779
721
  {
780
722
  tag: 'props',
781
723
  attrs: {
782
724
  protocol: '2',
783
- hash: ((_a = authState === null || authState === void 0 ? void 0 : authState.creds) === null || _a === void 0 ? void 0 : _a.lastPropHash) || ''
725
+ hash: authState?.creds?.lastPropHash || ''
784
726
  }
785
727
  }
786
728
  ]
787
729
  });
788
- const propsNode = (0, WABinary_1.getBinaryNodeChild)(resultNode, 'props');
730
+ const propsNode = getBinaryNodeChild(resultNode, 'props');
789
731
  let props = {};
790
732
  if (propsNode) {
791
- authState.creds.lastPropHash = (_b = propsNode === null || propsNode === void 0 ? void 0 : propsNode.attrs) === null || _b === void 0 ? void 0 : _b.hash;
792
- ev.emit('creds.update', authState.creds);
793
- props = (0, WABinary_1.reduceBinaryNodeToDictionary)(propsNode, 'prop');
733
+ if (propsNode.attrs?.hash) {
734
+ // on some clients, the hash is returning as undefined
735
+ authState.creds.lastPropHash = propsNode?.attrs?.hash;
736
+ ev.emit('creds.update', authState.creds);
737
+ }
738
+ props = reduceBinaryNodeToDictionary(propsNode, 'prop');
794
739
  }
795
740
  logger.debug('fetched props');
796
741
  return props;
@@ -801,9 +746,17 @@ const makeChatsSocket = (config) => {
801
746
  * requires the last messages till the last message received; required for archive & unread
802
747
  */
803
748
  const chatModify = (mod, jid) => {
804
- const patch = (0, Utils_1.chatModificationToAppPatch)(mod, jid);
749
+ const patch = chatModificationToAppPatch(mod, jid);
805
750
  return appPatch(patch);
806
751
  };
752
+ /**
753
+ * Enable/Disable link preview privacy, not related to baileys link preview generation
754
+ */
755
+ const updateDisableLinkPreviewsPrivacy = (isPreviewsDisabled) => {
756
+ return chatModify({
757
+ disableLinkPreviews: { isPreviewsDisabled }
758
+ }, '');
759
+ };
807
760
  /**
808
761
  * Star or Unstar a message
809
762
  */
@@ -815,6 +768,32 @@ const makeChatsSocket = (config) => {
815
768
  }
816
769
  }, jid);
817
770
  };
771
+ /**
772
+ * Add or Edit Contact
773
+ */
774
+ const addOrEditContact = (jid, contact) => {
775
+ return chatModify({
776
+ contact
777
+ }, jid);
778
+ };
779
+ /**
780
+ * Remove Contact
781
+ */
782
+ const removeContact = (jid) => {
783
+ return chatModify({
784
+ contact: null
785
+ }, jid);
786
+ };
787
+ /**
788
+ * Adds label
789
+ */
790
+ const addLabel = (jid, labels) => {
791
+ return chatModify({
792
+ addLabel: {
793
+ ...labels
794
+ }
795
+ }, jid);
796
+ };
818
797
  /**
819
798
  * Adds label for the chats
820
799
  */
@@ -857,82 +836,104 @@ const makeChatsSocket = (config) => {
857
836
  }
858
837
  }, jid);
859
838
  };
839
+ /**
840
+ * Add or Edit Quick Reply
841
+ */
842
+ const addOrEditQuickReply = (quickReply) => {
843
+ return chatModify({
844
+ quickReply
845
+ }, '');
846
+ };
847
+ /**
848
+ * Remove Quick Reply
849
+ */
850
+ const removeQuickReply = (timestamp) => {
851
+ return chatModify({
852
+ quickReply: { timestamp, deleted: true }
853
+ }, '');
854
+ };
860
855
  /**
861
856
  * queries need to be fired on connection open
862
857
  * help ensure parity with WA Web
863
858
  * */
864
859
  const executeInitQueries = async () => {
865
- await Promise.all([
866
- fetchProps(),
867
- fetchBlocklist(),
868
- fetchPrivacySettings(),
869
- ]);
860
+ await Promise.all([fetchProps(), fetchBlocklist(), fetchPrivacySettings()]);
870
861
  };
871
862
  const upsertMessage = ev.createBufferedFunction(async (msg, type) => {
872
- var _a, _b, _c;
873
863
  ev.emit('messages.upsert', { messages: [msg], type });
874
864
  if (!!msg.pushName) {
875
- let jid = msg.key.fromMe ? authState.creds.me.id : (msg.key.participant || msg.key.remoteJid);
876
- jid = (0, WABinary_1.jidNormalizedUser)(jid);
865
+ let jid = msg.key.fromMe ? authState.creds.me.id : msg.key.participant || msg.key.remoteJid;
866
+ jid = jidNormalizedUser(jid);
877
867
  if (!msg.key.fromMe) {
878
868
  ev.emit('contacts.update', [{ id: jid, notify: msg.pushName, verifiedName: msg.verifiedBizName }]);
879
869
  }
880
870
  // update our pushname too
881
- if (msg.key.fromMe && msg.pushName && ((_a = authState.creds.me) === null || _a === void 0 ? void 0 : _a.name) !== msg.pushName) {
871
+ if (msg.key.fromMe && msg.pushName && authState.creds.me?.name !== msg.pushName) {
882
872
  ev.emit('creds.update', { me: { ...authState.creds.me, name: msg.pushName } });
883
873
  }
884
874
  }
885
- const historyMsg = (0, Utils_1.getHistoryMsg)(msg.message);
886
- const shouldProcessHistoryMsg = historyMsg ?
887
- (shouldSyncHistoryMessage(historyMsg) &&
888
- Defaults_1.PROCESSABLE_HISTORY_TYPES.includes(historyMsg.syncType)) :
889
- false;
890
- if (historyMsg && !authState.creds.myAppStateKeyId) {
891
- logger.warn('skipping app state sync, as myAppStateKeyId is not set');
892
- pendingAppStateSync = true;
893
- }
894
- // Run app state sync in background (non-blocking) so incoming messages
895
- // are not delayed while the potentially large state sync runs on reconnect
896
- const appStateSyncPromise = (async () => {
897
- if (historyMsg && authState.creds.myAppStateKeyId) {
898
- pendingAppStateSync = false;
899
- await doAppStateSync();
875
+ const historyMsg = getHistoryMsg(msg.message);
876
+ const shouldProcessHistoryMsg = historyMsg
877
+ ? shouldSyncHistoryMessage(historyMsg) &&
878
+ PROCESSABLE_HISTORY_TYPES.includes(historyMsg.syncType)
879
+ : false;
880
+ // State machine: decide on sync and flush
881
+ if (historyMsg && syncState === SyncState.AwaitingInitialSync) {
882
+ if (awaitingSyncTimeout) {
883
+ clearTimeout(awaitingSyncTimeout);
884
+ awaitingSyncTimeout = undefined;
900
885
  }
901
- })();
902
- // Process the message immediately, don't wait for app state sync
903
- await (0, process_message_1.default)(msg, {
904
- shouldProcessHistoryMsg,
905
- ev,
906
- creds: authState.creds,
907
- keyStore: authState.keys,
908
- logger,
909
- options: config.options,
910
- getMessage: config.getMessage,
911
- });
912
- if (((_c = (_b = msg.message) === null || _b === void 0 ? void 0 : _b.protocolMessage) === null || _c === void 0 ? void 0 : _c.appStateSyncKeyShare) &&
913
- pendingAppStateSync) {
914
- // don't block run in background
915
- doAppStateSync().then(() => { pendingAppStateSync = false; }).catch(e => logger.warn({ e }, 'bg app state sync failed'));
916
- }
917
- // Attach error handler to avoid unhandled rejection
918
- appStateSyncPromise.catch(e => logger.warn({ e }, 'background app state sync failed'));
919
- async function doAppStateSync() {
920
- if (!authState.creds.accountSyncCounter) {
921
- logger.info('doing initial app state sync');
922
- await resyncAppState(Types_1.ALL_WA_PATCH_NAMES, true);
886
+ if (shouldProcessHistoryMsg) {
887
+ syncState = SyncState.Syncing;
888
+ logger.info('Transitioned to Syncing state');
889
+ // Let doAppStateSync handle the final flush after it's done
890
+ }
891
+ else {
892
+ syncState = SyncState.Online;
893
+ logger.info('History sync skipped, transitioning to Online state and flushing buffer');
894
+ ev.flush();
895
+ }
896
+ }
897
+ const doAppStateSync = async () => {
898
+ if (syncState === SyncState.Syncing) {
899
+ logger.info('Doing app state sync');
900
+ await resyncAppState(ALL_WA_PATCH_NAMES, true);
901
+ // Sync is complete, go online and flush everything
902
+ syncState = SyncState.Online;
903
+ logger.info('App state sync complete, transitioning to Online state and flushing buffer');
904
+ ev.flush();
923
905
  const accountSyncCounter = (authState.creds.accountSyncCounter || 0) + 1;
924
906
  ev.emit('creds.update', { accountSyncCounter });
925
- if (needToFlushWithAppStateSync) {
926
- logger.debug('flushing with app state sync');
927
- ev.flush();
928
- }
929
907
  }
908
+ };
909
+ await Promise.all([
910
+ (async () => {
911
+ if (shouldProcessHistoryMsg) {
912
+ await doAppStateSync();
913
+ }
914
+ })(),
915
+ processMessage(msg, {
916
+ signalRepository,
917
+ shouldProcessHistoryMsg,
918
+ placeholderResendCache,
919
+ ev,
920
+ creds: authState.creds,
921
+ keyStore: authState.keys,
922
+ logger,
923
+ options: config.options,
924
+ getMessage
925
+ })
926
+ ]);
927
+ // If the app state key arrives and we are waiting to sync, trigger the sync now.
928
+ if (msg.message?.protocolMessage?.appStateSyncKeyShare && syncState === SyncState.Syncing) {
929
+ logger.info('App state sync key arrived, triggering app state sync');
930
+ await doAppStateSync();
930
931
  }
931
932
  });
932
933
  ws.on('CB:presence', handlePresenceUpdate);
933
934
  ws.on('CB:chatstate', handlePresenceUpdate);
934
935
  ws.on('CB:ib,,dirty', async (node) => {
935
- const { attrs } = (0, WABinary_1.getBinaryNodeChild)(node, 'dirty');
936
+ const { attrs } = getBinaryNodeChild(node, 'dirty');
936
937
  const type = attrs.type;
937
938
  switch (type) {
938
939
  case 'account_sync':
@@ -954,42 +955,74 @@ const makeChatsSocket = (config) => {
954
955
  }
955
956
  });
956
957
  ev.on('connection.update', ({ connection, receivedPendingNotifications }) => {
957
- var _a;
958
958
  if (connection === 'open') {
959
959
  if (fireInitQueries) {
960
- executeInitQueries()
961
- .catch(error => onUnexpectedError(error, 'init queries'));
960
+ executeInitQueries().catch(error => onUnexpectedError(error, 'init queries'));
962
961
  }
963
- sendPresenceUpdate(markOnlineOnConnect ? 'available' : 'unavailable')
964
- .catch(error => onUnexpectedError(error, 'presence update requests'));
965
- }
966
- if (receivedPendingNotifications) {
967
- // if we don't have the app state key
968
- // we keep buffering events until we finally have
969
- // the key and can sync the messages
970
- if (!((_a = authState.creds) === null || _a === void 0 ? void 0 : _a.myAppStateKeyId) && !config.mobile) {
971
- ev.buffer();
972
- needToFlushWithAppStateSync = true;
962
+ sendPresenceUpdate(markOnlineOnConnect ? 'available' : 'unavailable').catch(error => onUnexpectedError(error, 'presence update requests'));
963
+ }
964
+ if (!receivedPendingNotifications || syncState !== SyncState.Connecting) {
965
+ return;
966
+ }
967
+ syncState = SyncState.AwaitingInitialSync;
968
+ logger.info('Connection is now AwaitingInitialSync, buffering events');
969
+ ev.buffer();
970
+ const willSyncHistory = shouldSyncHistoryMessage(proto.Message.HistorySyncNotification.create({
971
+ syncType: proto.HistorySync.HistorySyncType.RECENT
972
+ }));
973
+ if (!willSyncHistory) {
974
+ logger.info('History sync is disabled by config, not waiting for notification. Transitioning to Online.');
975
+ syncState = SyncState.Online;
976
+ setTimeout(() => ev.flush(), 0);
977
+ return;
978
+ }
979
+ logger.info('History sync is enabled, awaiting notification with a 20s timeout.');
980
+ if (awaitingSyncTimeout) {
981
+ clearTimeout(awaitingSyncTimeout);
982
+ }
983
+ awaitingSyncTimeout = setTimeout(() => {
984
+ if (syncState === SyncState.AwaitingInitialSync) {
985
+ // TODO: investigate
986
+ logger.warn('Timeout in AwaitingInitialSync, forcing state to Online and flushing buffer');
987
+ syncState = SyncState.Online;
988
+ ev.flush();
973
989
  }
990
+ }, 20000);
991
+ });
992
+ ev.on('lid-mapping.update', async ({ lid, pn }) => {
993
+ try {
994
+ await signalRepository.lidMapping.storeLIDPNMappings([{ lid, pn }]);
995
+ }
996
+ catch (error) {
997
+ logger.warn({ lid, pn, error }, 'Failed to store LID-PN mapping');
974
998
  }
975
999
  });
976
1000
  return {
977
1001
  ...sock,
978
- processingMutex,
1002
+ createCallLink,
1003
+ getBotListV2,
1004
+ messageMutex,
1005
+ receiptMutex,
1006
+ appStatePatchMutex,
1007
+ notificationMutex,
979
1008
  fetchPrivacySettings,
980
1009
  upsertMessage,
981
1010
  appPatch,
982
1011
  sendPresenceUpdate,
983
1012
  presenceSubscribe,
984
1013
  profilePictureUrl,
985
- onWhatsApp,
986
1014
  fetchBlocklist,
987
1015
  fetchStatus,
1016
+ fetchDisappearingDuration,
1017
+ findUserId,
988
1018
  updateProfilePicture,
989
1019
  removeProfilePicture,
990
1020
  updateProfileStatus,
991
1021
  updateProfileName,
992
1022
  updateBlockStatus,
1023
+ updateDisableLinkPreviewsPrivacy,
1024
+ updateCallPrivacy,
1025
+ updateMessagesPrivacy,
993
1026
  updateLastSeenPrivacy,
994
1027
  updateOnlinePrivacy,
995
1028
  updateProfilePicturePrivacy,
@@ -1001,13 +1034,15 @@ const makeChatsSocket = (config) => {
1001
1034
  resyncAppState,
1002
1035
  chatModify,
1003
1036
  cleanDirtyBits,
1037
+ addOrEditContact,
1038
+ removeContact,
1039
+ addLabel,
1004
1040
  addChatLabel,
1005
1041
  removeChatLabel,
1006
1042
  addMessageLabel,
1007
- checkWhatsApp,
1008
- reqPairing,
1009
1043
  removeMessageLabel,
1010
- star
1044
+ star,
1045
+ addOrEditQuickReply,
1046
+ removeQuickReply
1011
1047
  };
1012
- };
1013
- exports.makeChatsSocket = makeChatsSocket;
1048
+ };