violetics 7.0.1-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 -146
  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 -418
  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 +884 -561
  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 +465 -315
  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 -152
  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 -47
  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 +35 -45
  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 +74 -107
  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 -118
  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,258 +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
- if (!phoneNumber.startsWith('62') && phoneNumber.length > 0) {
111
- phoneNumber = '62' + phoneNumber;
112
- }
113
- if (!phoneNumber.startsWith('+') && phoneNumber.length > 0) {
114
- phoneNumber = '+' + phoneNumber;
115
- }
116
- }
117
-
118
- const { parsePhoneNumber } = require('libphonenumber-js');
119
- const parsedNumber = parsePhoneNumber(phoneNumber);
120
- const countryCode = parsedNumber.countryCallingCode;
121
- const nationalNumber = parsedNumber.nationalNumber;
122
-
123
- try {
124
- // mobileRegisterExists() hits /exist endpoint — NO SMS is ever sent.
125
- // It checks account status and returns ban errors (appeal_token, custom_block_screen)
126
- // same as the /code endpoint would, but without triggering OTP delivery.
127
- const { mobileRegisterExists } = require('./registration');
128
- await mobileRegisterExists({
129
- ...authState.creds,
130
- phoneNumberCountryCode: countryCode,
131
- phoneNumberNationalNumber: nationalNumber,
132
- phoneNumberMobileCountryCode: '510',
133
- phoneNumberMobileNetworkCode: '10',
134
- }, config.options);
135
- return JSON.stringify(resultData, null, 2);
136
- } catch (err) {
137
- if (err?.appeal_token) {
138
- resultData.isBanned = true;
139
- resultData.data = {
140
- violation_type: err.violation_type || null,
141
- in_app_ban_appeal: err.in_app_ban_appeal || null,
142
- appeal_token: err.appeal_token || null,
143
- };
144
- } else if (err?.custom_block_screen || err?.reason === 'blocked') {
145
- resultData.isNeedOfficialWa = true;
146
- }
147
- return JSON.stringify(resultData, null, 2);
148
- }
149
- };
150
- // 60%
151
- const reqPairing = async (number, count, config) => {
152
- const { makeSocket } = require("./socket");
153
- const socket = makeSocket(config);
154
- const phoneNumber = number.replace(/[^0-9]/g, '');
155
- for (let i = 0; i < count; i++) {
156
- const code = await socket.requestPairingCode(phoneNumber, "0000XXXX");
157
- const formattedCode = code?.match(/.{1,4}/g)?.join('-') || code;
158
- console.log(`Spam ${i + 1}/${count} | ${formattedCode}`);
159
- if (i < count - 1) {
160
- await new Promise(resolve => setTimeout(resolve, 30000));
161
- }
162
- }
163
- }
164
108
  const updateDefaultDisappearingMode = async (duration) => {
165
109
  await query({
166
110
  tag: 'iq',
167
111
  attrs: {
168
112
  xmlns: 'disappearing_mode',
169
- to: WABinary_1.S_WHATSAPP_NET,
113
+ to: S_WHATSAPP_NET,
170
114
  type: 'set'
171
115
  },
172
- content: [{
116
+ content: [
117
+ {
173
118
  tag: 'disappearing_mode',
174
119
  attrs: {
175
120
  duration: duration.toString()
176
121
  }
177
- }]
122
+ }
123
+ ]
178
124
  });
179
125
  };
180
- /** helper function to run a generic IQ query */
181
- const interactiveQuery = async (userNodes, queryNode) => {
182
- const result = await query({
126
+ const getBotListV2 = async () => {
127
+ const resp = await query({
183
128
  tag: 'iq',
184
129
  attrs: {
185
- to: WABinary_1.S_WHATSAPP_NET,
186
- type: 'get',
187
- xmlns: 'usync',
130
+ xmlns: 'bot',
131
+ to: S_WHATSAPP_NET,
132
+ type: 'get'
188
133
  },
189
134
  content: [
190
135
  {
191
- tag: 'usync',
136
+ tag: 'bot',
192
137
  attrs: {
193
- sid: generateMessageTag(),
194
- mode: 'query',
195
- last: 'true',
196
- index: '0',
197
- context: 'interactive',
198
- },
199
- content: [
200
- {
201
- tag: 'query',
202
- attrs: {},
203
- content: [queryNode]
204
- },
205
- {
206
- tag: 'list',
207
- attrs: {},
208
- content: userNodes
209
- }
210
- ]
138
+ v: '2'
139
+ }
211
140
  }
212
- ],
213
- });
214
- const usyncNode = (0, WABinary_1.getBinaryNodeChild)(result, 'usync');
215
- const listNode = (0, WABinary_1.getBinaryNodeChild)(usyncNode, 'list');
216
- const users = (0, WABinary_1.getBinaryNodeChildren)(listNode, 'user');
217
- return users;
218
- };
219
- const getBusinessProfile = async (jid) => {
220
- var _a, _b, _c, _d, _e, _f, _g;
221
- const results = await query({
222
- tag: 'iq',
223
- attrs: {
224
- to: 's.whatsapp.net',
225
- xmlns: 'w:biz',
226
- type: 'get'
227
- },
228
- content: [{
229
- tag: 'business_profile',
230
- attrs: { v: '244' },
231
- content: [{
232
- tag: 'profile',
233
- attrs: { jid }
234
- }]
235
- }]
141
+ ]
236
142
  });
237
- const profileNode = (0, WABinary_1.getBinaryNodeChild)(results, 'business_profile');
238
- const profiles = (0, WABinary_1.getBinaryNodeChild)(profileNode, 'profile');
239
- if (profiles) {
240
- const address = (0, WABinary_1.getBinaryNodeChild)(profiles, 'address');
241
- const description = (0, WABinary_1.getBinaryNodeChild)(profiles, 'description');
242
- const website = (0, WABinary_1.getBinaryNodeChild)(profiles, 'website');
243
- const email = (0, WABinary_1.getBinaryNodeChild)(profiles, 'email');
244
- const category = (0, WABinary_1.getBinaryNodeChild)((0, WABinary_1.getBinaryNodeChild)(profiles, 'categories'), 'category');
245
- const businessHours = (0, WABinary_1.getBinaryNodeChild)(profiles, 'business_hours');
246
- const businessHoursConfig = businessHours ?
247
- (0, WABinary_1.getBinaryNodeChildren)(businessHours, 'business_hours_config') :
248
- undefined;
249
- const websiteStr = (_a = website === null || website === void 0 ? void 0 : website.content) === null || _a === void 0 ? void 0 : _a.toString();
250
- return {
251
- wid: (_b = profiles.attrs) === null || _b === void 0 ? void 0 : _b.jid,
252
- address: (_c = address === null || address === void 0 ? void 0 : address.content) === null || _c === void 0 ? void 0 : _c.toString(),
253
- description: ((_d = description === null || description === void 0 ? void 0 : description.content) === null || _d === void 0 ? void 0 : _d.toString()) || '',
254
- website: websiteStr ? [websiteStr] : [],
255
- email: (_e = email === null || email === void 0 ? void 0 : email.content) === null || _e === void 0 ? void 0 : _e.toString(),
256
- category: (_f = category === null || category === void 0 ? void 0 : category.content) === null || _f === void 0 ? void 0 : _f.toString(),
257
- 'business_hours': {
258
- timezone: (_g = businessHours === null || businessHours === void 0 ? void 0 : businessHours.attrs) === null || _g === void 0 ? void 0 : _g.timezone,
259
- '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
+ });
260
152
  }
261
- };
153
+ }
262
154
  }
155
+ return botList;
263
156
  };
264
- const onWhatsApp = async (...jids) => {
265
- const usyncQuery = new WAUSync_1.USyncQuery()
266
- .withContactProtocol()
267
- .withLIDProtocol();
268
-
157
+ const fetchStatus = async (...jids) => {
158
+ const usyncQuery = new USyncQuery().withStatusProtocol();
269
159
  for (const jid of jids) {
270
- const phone = `+${jid.replace('+', '').split('@')[0].split(':')[0]}`;
271
- usyncQuery.withUser(new WAUSync_1.USyncUser().withPhone(phone));
272
- }
273
-
274
- const results = await sock.executeUSyncQuery(usyncQuery);
275
- if (results) {
276
- const verifiedResults = await Promise.all(
277
- results.list
278
- .filter((a) => !!a.contact)
279
- .map(async ({ contact, id, lid }) => {
280
- try {
281
- const businessProfile = await getBusinessProfile(id);
282
- const isBusiness = businessProfile && Object.keys(businessProfile).length > 0;
283
- if (isBusiness) {
284
- const { wid, ...businessInfo } = businessProfile;
285
-
286
- return {
287
- jid: id,
288
- exists: true,
289
- lid: lid,
290
- status: 'business',
291
- businessInfo: businessInfo
292
- };
293
- } else {
294
- return {
295
- jid: id,
296
- exists: true,
297
- lid: lid,
298
- status: 'regular'
299
- };
300
- }
301
- } catch (error) {
302
- return {
303
- jid: id,
304
- exists: true,
305
- lid: lid,
306
- status: error
307
- };
308
- }
309
- })
310
- );
311
- return verifiedResults;
160
+ usyncQuery.withUser(new USyncUser().withId(jid));
161
+ }
162
+ const result = await sock.executeUSyncQuery(usyncQuery);
163
+ if (result) {
164
+ return result.list;
312
165
  }
313
166
  };
314
- const fetchStatus = async (jid) => {
315
- 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);
316
173
  if (result) {
317
- const status = (0, WABinary_1.getBinaryNodeChild)(result, 'status');
318
- return {
319
- status: status === null || status === void 0 ? void 0 : status.content.toString(),
320
- setAt: new Date(+((status === null || status === void 0 ? void 0 : status.attrs.t) || 0) * 1000)
321
- };
174
+ return result.list;
175
+ }
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
+ }
322
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;
323
209
  };
324
210
  /** update the profile picture for yourself or a group */
325
- const updateProfilePicture = async (jid, content) => {
211
+ const updateProfilePicture = async (jid, content, dimensions) => {
326
212
  let targetJid;
327
213
  if (!jid) {
328
- 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');
215
+ }
216
+ if (jidNormalizedUser(jid) !== jidNormalizedUser(authState.creds.me.id)) {
217
+ targetJid = jidNormalizedUser(jid); // in case it is someone other than us
329
218
  }
330
- if ((0, WABinary_1.jidNormalizedUser)(jid) !== (0, WABinary_1.jidNormalizedUser)(authState.creds.me.id)) {
331
- targetJid = (0, WABinary_1.jidNormalizedUser)(jid); // in case it is someone other than us
219
+ else {
220
+ targetJid = undefined;
332
221
  }
333
- const { img } = await (0, Utils_1.generateProfilePicture)(content);
222
+ const { img } = await generateProfilePicture(content, dimensions);
334
223
  await query({
335
224
  tag: 'iq',
336
225
  attrs: {
337
- target: targetJid,
338
- to: WABinary_1.S_WHATSAPP_NET,
226
+ to: S_WHATSAPP_NET,
339
227
  type: 'set',
340
- xmlns: 'w:profile:picture'
228
+ xmlns: 'w:profile:picture',
229
+ ...(targetJid ? { target: targetJid } : {})
341
230
  },
342
231
  content: [
343
232
  {
@@ -352,18 +241,21 @@ const makeChatsSocket = (config) => {
352
241
  const removeProfilePicture = async (jid) => {
353
242
  let targetJid;
354
243
  if (!jid) {
355
- 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');
245
+ }
246
+ if (jidNormalizedUser(jid) !== jidNormalizedUser(authState.creds.me.id)) {
247
+ targetJid = jidNormalizedUser(jid); // in case it is someone other than us
356
248
  }
357
- if ((0, WABinary_1.jidNormalizedUser)(jid) !== (0, WABinary_1.jidNormalizedUser)(authState.creds.me.id)) {
358
- targetJid = (0, WABinary_1.jidNormalizedUser)(jid); // in case it is someone other than us
249
+ else {
250
+ targetJid = undefined;
359
251
  }
360
252
  await query({
361
253
  tag: 'iq',
362
254
  attrs: {
363
- target: targetJid,
364
- to: WABinary_1.S_WHATSAPP_NET,
255
+ to: S_WHATSAPP_NET,
365
256
  type: 'set',
366
- xmlns: 'w:profile:picture'
257
+ xmlns: 'w:profile:picture',
258
+ ...(targetJid ? { target: targetJid } : {})
367
259
  }
368
260
  });
369
261
  };
@@ -372,7 +264,7 @@ const makeChatsSocket = (config) => {
372
264
  await query({
373
265
  tag: 'iq',
374
266
  attrs: {
375
- to: WABinary_1.S_WHATSAPP_NET,
267
+ to: S_WHATSAPP_NET,
376
268
  type: 'set',
377
269
  xmlns: 'status'
378
270
  },
@@ -393,20 +285,19 @@ const makeChatsSocket = (config) => {
393
285
  tag: 'iq',
394
286
  attrs: {
395
287
  xmlns: 'blocklist',
396
- to: WABinary_1.S_WHATSAPP_NET,
288
+ to: S_WHATSAPP_NET,
397
289
  type: 'get'
398
290
  }
399
291
  });
400
- const listNode = (0, WABinary_1.getBinaryNodeChild)(result, 'list');
401
- return (0, WABinary_1.getBinaryNodeChildren)(listNode, 'item')
402
- .map(n => n.attrs.jid);
292
+ const listNode = getBinaryNodeChild(result, 'list');
293
+ return getBinaryNodeChildren(listNode, 'item').map(n => n.attrs.jid);
403
294
  };
404
295
  const updateBlockStatus = async (jid, action) => {
405
296
  await query({
406
297
  tag: 'iq',
407
298
  attrs: {
408
299
  xmlns: 'blocklist',
409
- to: WABinary_1.S_WHATSAPP_NET,
300
+ to: S_WHATSAPP_NET,
410
301
  type: 'set'
411
302
  },
412
303
  content: [
@@ -420,22 +311,70 @@ const makeChatsSocket = (config) => {
420
311
  ]
421
312
  });
422
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
+ };
423
362
  const cleanDirtyBits = async (type, fromTimestamp) => {
424
363
  logger.info({ fromTimestamp }, 'clean dirty bits ' + type);
425
364
  await sendNode({
426
365
  tag: 'iq',
427
366
  attrs: {
428
- to: WABinary_1.S_WHATSAPP_NET,
367
+ to: S_WHATSAPP_NET,
429
368
  type: 'set',
430
369
  xmlns: 'urn:xmpp:whatsapp:dirty',
431
- id: generateMessageTag(),
370
+ id: generateMessageTag()
432
371
  },
433
372
  content: [
434
373
  {
435
374
  tag: 'clean',
436
375
  attrs: {
437
376
  type,
438
- ...(fromTimestamp ? { timestamp: fromTimestamp.toString() } : null),
377
+ ...(fromTimestamp ? { timestamp: fromTimestamp.toString() } : null)
439
378
  }
440
379
  }
441
380
  ]
@@ -444,26 +383,25 @@ const makeChatsSocket = (config) => {
444
383
  const newAppStateChunkHandler = (isInitialSync) => {
445
384
  return {
446
385
  onMutation(mutation) {
447
- (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);
448
387
  }
449
388
  };
450
389
  };
451
- // Buffered version — used for initial sync only (holds events until sync completes)
452
390
  const resyncAppState = ev.createBufferedFunction(async (collections, isInitialSync) => {
453
- await _doResyncAppState(collections, isInitialSync);
454
- });
455
- // Non-buffered background version — used for incremental server_sync updates
456
- // Does NOT hold events, so real-time messages are delivered without delay
457
- const resyncBackgroundAppState = async (collections) => {
458
- await _doResyncAppState(collections, false);
459
- };
460
- 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
+ };
461
400
  // we use this to determine which events to fire
462
401
  // otherwise when we resync from scratch -- all notifications will fire
463
402
  const initialVersionMap = {};
464
403
  const globalMutationMap = {};
465
404
  await authState.keys.transaction(async () => {
466
- var _a;
467
405
  const collectionsToHandle = new Set(collections);
468
406
  // in case something goes wrong -- ensure we don't enter a loop that cannot be exited from
469
407
  const attemptsMap = {};
@@ -482,7 +420,7 @@ const makeChatsSocket = (config) => {
482
420
  }
483
421
  }
484
422
  else {
485
- state = (0, Utils_1.newLTHashState)();
423
+ state = newLTHashState();
486
424
  }
487
425
  states[name] = state;
488
426
  logger.info(`resyncing ${name} from v${state.version}`);
@@ -492,14 +430,14 @@ const makeChatsSocket = (config) => {
492
430
  name,
493
431
  version: state.version.toString(),
494
432
  // return snapshot if being synced from scratch
495
- 'return_snapshot': (!state.version).toString()
433
+ return_snapshot: (!state.version).toString()
496
434
  }
497
435
  });
498
436
  }
499
437
  const result = await query({
500
438
  tag: 'iq',
501
439
  attrs: {
502
- to: WABinary_1.S_WHATSAPP_NET,
440
+ to: S_WHATSAPP_NET,
503
441
  xmlns: 'w:sync:app:state',
504
442
  type: 'set'
505
443
  },
@@ -512,26 +450,22 @@ const makeChatsSocket = (config) => {
512
450
  ]
513
451
  });
514
452
  // extract from binary node
515
- 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);
516
454
  for (const key in decoded) {
517
455
  const name = key;
518
456
  const { patches, hasMorePatches, snapshot } = decoded[name];
519
457
  try {
520
458
  if (snapshot) {
521
- 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);
522
460
  states[name] = newState;
523
461
  Object.assign(globalMutationMap, mutationMap);
524
462
  logger.info(`restored state of ${name} from snapshot to v${newState.version} with mutations`);
525
- await authState.keys.set({ 'app-state-sync-version': {
526
- [name]: newState
527
- } });
463
+ await authState.keys.set({ 'app-state-sync-version': { [name]: newState } });
528
464
  }
529
465
  // only process if there are syncd patches
530
466
  if (patches.length) {
531
- const { state: newState, mutationMap } = await (0, Utils_1.decodePatches)(name, patches, states[name], getAppStateSyncKey, config.options, initialVersionMap[name], logger, appStateMacVerification.patch);
532
- await authState.keys.set({ 'app-state-sync-version': {
533
- [name]: newState
534
- } });
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 } });
535
469
  logger.info(`synced ${name} to v${newState.version}`);
536
470
  initialVersionMap[name] = newState.version;
537
471
  Object.assign(globalMutationMap, mutationMap);
@@ -539,7 +473,8 @@ const makeChatsSocket = (config) => {
539
473
  if (hasMorePatches) {
540
474
  logger.info(`${name} has more patches...`);
541
475
  }
542
- else { // collection is done with sync
476
+ else {
477
+ // collection is done with sync
543
478
  collectionsToHandle.delete(name);
544
479
  }
545
480
  }
@@ -547,12 +482,10 @@ const makeChatsSocket = (config) => {
547
482
  // if retry attempts overshoot
548
483
  // or key not found
549
484
  const isIrrecoverableError = attemptsMap[name] >= MAX_SYNC_ATTEMPTS ||
550
- ((_a = error.output) === null || _a === void 0 ? void 0 : _a.statusCode) === 404 ||
485
+ error.output?.statusCode === 404 ||
551
486
  error.name === 'TypeError';
552
487
  logger.info({ name, error: error.stack }, `failed to sync state from version${isIrrecoverableError ? '' : ', removing and trying from scratch'}`);
553
- await authState.keys.set({ 'app-state-sync-version': {
554
- [name]: null
555
- } });
488
+ await authState.keys.set({ 'app-state-sync-version': { [name]: null } });
556
489
  // increment number of retries
557
490
  attemptsMap[name] = (attemptsMap[name] || 0) + 1;
558
491
  if (isIrrecoverableError) {
@@ -562,59 +495,102 @@ const makeChatsSocket = (config) => {
562
495
  }
563
496
  }
564
497
  }
565
- });
498
+ }, authState?.creds?.me?.id || 'resync-app-state');
566
499
  const { onMutation } = newAppStateChunkHandler(isInitialSync);
567
500
  for (const key in globalMutationMap) {
568
501
  onMutation(globalMutationMap[key]);
569
502
  }
570
- }
503
+ });
571
504
  /**
572
505
  * fetch the profile picture of a user/group
573
506
  * type = "preview" for a low res picture
574
507
  * type = "image for the high res picture"
575
508
  */
576
- const profilePictureUrl = async (jid, type = 'preview', timeoutMs) => {
577
- var _a;
578
- 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
+ }
579
528
  const result = await query({
580
529
  tag: 'iq',
581
530
  attrs: {
582
531
  target: jid,
583
- to: WABinary_1.S_WHATSAPP_NET,
532
+ to: S_WHATSAPP_NET,
584
533
  type: 'get',
585
534
  xmlns: 'w:profile:picture'
586
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
+ },
587
555
  content: [
588
- { 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
+ }
589
561
  ]
590
562
  }, timeoutMs);
591
- const child = (0, WABinary_1.getBinaryNodeChild)(result, 'picture');
592
- 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;
593
565
  };
594
566
  const sendPresenceUpdate = async (type, toJid) => {
595
567
  const me = authState.creds.me;
596
- if (type === 'available' || type === 'unavailable') {
568
+ const isAvailableType = type === 'available';
569
+ if (isAvailableType || type === 'unavailable') {
597
570
  if (!me.name) {
598
571
  logger.warn('no name present, ignoring presence update request...');
599
572
  return;
600
573
  }
601
- ev.emit('connection.update', { isOnline: type === 'available' });
574
+ ev.emit('connection.update', { isOnline: isAvailableType });
575
+ if (isAvailableType) {
576
+ void sendUnifiedSession();
577
+ }
602
578
  await sendNode({
603
579
  tag: 'presence',
604
580
  attrs: {
605
- name: me.name,
581
+ name: me.name.replace(/@/g, ''),
606
582
  type
607
583
  }
608
584
  });
609
585
  }
610
586
  else {
611
- const { server } = (0, WABinary_1.jidDecode)(toJid);
587
+ const { server } = jidDecode(toJid);
612
588
  const isLid = server === 'lid';
613
589
  await sendNode({
614
590
  tag: 'chatstate',
615
591
  attrs: {
616
592
  from: isLid ? me.lid : me.id,
617
- to: toJid,
593
+ to: toJid
618
594
  },
619
595
  content: [
620
596
  {
@@ -629,29 +605,23 @@ const makeChatsSocket = (config) => {
629
605
  * @param toJid the jid to subscribe to
630
606
  * @param tcToken token for subscription, use if present
631
607
  */
632
- const presenceSubscribe = (toJid, tcToken) => (sendNode({
633
- tag: 'presence',
634
- attrs: {
635
- to: toJid,
636
- id: generateMessageTag(),
637
- type: 'subscribe'
638
- },
639
- content: tcToken ?
640
- [
641
- {
642
- tag: 'tctoken',
643
- attrs: {},
644
- content: tcToken
645
- }
646
- ] :
647
- undefined
648
- }));
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
+ };
649
620
  const handlePresenceUpdate = ({ tag, attrs, content }) => {
650
- var _a;
651
621
  let presence;
652
622
  const jid = attrs.from;
653
623
  const participant = attrs.participant || attrs.from;
654
- if (shouldIgnoreJid(jid) && jid !== '@s.whatsapp.net') {
624
+ if (shouldIgnoreJid(jid) && jid !== S_WHATSAPP_NET) {
655
625
  return;
656
626
  }
657
627
  if (tag === 'presence') {
@@ -666,7 +636,7 @@ const makeChatsSocket = (config) => {
666
636
  if (type === 'paused') {
667
637
  type = 'available';
668
638
  }
669
- if (((_a = firstChild.attrs) === null || _a === void 0 ? void 0 : _a.media) === 'audio') {
639
+ if (firstChild.attrs?.media === 'audio') {
670
640
  type = 'recording';
671
641
  }
672
642
  presence = { lastKnownPresence: type };
@@ -675,31 +645,29 @@ const makeChatsSocket = (config) => {
675
645
  logger.error({ tag, attrs, content }, 'recv invalid presence node');
676
646
  }
677
647
  if (presence) {
678
- ev.emit('presence.update', { id: jid, presences: {
679
- [participant]: presence
680
- } });
648
+ ev.emit('presence.update', { id: jid, presences: { [participant]: presence } });
681
649
  }
682
650
  };
683
651
  const appPatch = async (patchCreate) => {
684
652
  const name = patchCreate.type;
685
653
  const myAppStateKeyId = authState.creds.myAppStateKeyId;
686
654
  if (!myAppStateKeyId) {
687
- throw new boom_1.Boom('App state key not present!', { statusCode: 400 });
655
+ throw new Boom('App state key not present!', { statusCode: 400 });
688
656
  }
689
657
  let initial;
690
658
  let encodeResult;
691
- await processingMutex.mutex(async () => {
659
+ await appStatePatchMutex.mutex(async () => {
692
660
  await authState.keys.transaction(async () => {
693
661
  logger.debug({ patch: patchCreate }, 'applying app patch');
694
662
  await resyncAppState([name], false);
695
663
  const { [name]: currentSyncVersion } = await authState.keys.get('app-state-sync-version', [name]);
696
- initial = currentSyncVersion || (0, Utils_1.newLTHashState)();
697
- encodeResult = await (0, Utils_1.encodeSyncdPatch)(patchCreate, myAppStateKeyId, initial, getAppStateSyncKey);
664
+ initial = currentSyncVersion || newLTHashState();
665
+ encodeResult = await encodeSyncdPatch(patchCreate, myAppStateKeyId, initial, getAppStateSyncKey);
698
666
  const { patch, state } = encodeResult;
699
667
  const node = {
700
668
  tag: 'iq',
701
669
  attrs: {
702
- to: WABinary_1.S_WHATSAPP_NET,
670
+ to: S_WHATSAPP_NET,
703
671
  type: 'set',
704
672
  xmlns: 'w:sync:app:state'
705
673
  },
@@ -713,13 +681,13 @@ const makeChatsSocket = (config) => {
713
681
  attrs: {
714
682
  name,
715
683
  version: (state.version - 1).toString(),
716
- 'return_snapshot': 'false'
684
+ return_snapshot: 'false'
717
685
  },
718
686
  content: [
719
687
  {
720
688
  tag: 'patch',
721
689
  attrs: {},
722
- content: WAProto_1.proto.SyncdPatch.encode(patch).finish()
690
+ content: proto.SyncdPatch.encode(patch).finish()
723
691
  }
724
692
  ]
725
693
  }
@@ -728,14 +696,12 @@ const makeChatsSocket = (config) => {
728
696
  ]
729
697
  };
730
698
  await query(node);
731
- await authState.keys.set({ 'app-state-sync-version': {
732
- [name]: state
733
- } });
734
- });
699
+ await authState.keys.set({ 'app-state-sync-version': { [name]: state } });
700
+ }, authState?.creds?.me?.id || 'app-patch');
735
701
  });
736
702
  if (config.emitOwnEvents) {
737
703
  const { onMutation } = newAppStateChunkHandler(false);
738
- 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);
739
705
  for (const key in mutationMap) {
740
706
  onMutation(mutationMap[key]);
741
707
  }
@@ -743,30 +709,33 @@ const makeChatsSocket = (config) => {
743
709
  };
744
710
  /** sending non-abt props may fix QR scan fail if server expects */
745
711
  const fetchProps = async () => {
746
- var _a, _b;
712
+ //TODO: implement both protocol 1 and protocol 2 prop fetching, specially for abKey for WM
747
713
  const resultNode = await query({
748
714
  tag: 'iq',
749
715
  attrs: {
750
- to: WABinary_1.S_WHATSAPP_NET,
716
+ to: S_WHATSAPP_NET,
751
717
  xmlns: 'w',
752
- type: 'get',
718
+ type: 'get'
753
719
  },
754
720
  content: [
755
721
  {
756
722
  tag: 'props',
757
723
  attrs: {
758
724
  protocol: '2',
759
- hash: ((_a = authState === null || authState === void 0 ? void 0 : authState.creds) === null || _a === void 0 ? void 0 : _a.lastPropHash) || ''
725
+ hash: authState?.creds?.lastPropHash || ''
760
726
  }
761
727
  }
762
728
  ]
763
729
  });
764
- const propsNode = (0, WABinary_1.getBinaryNodeChild)(resultNode, 'props');
730
+ const propsNode = getBinaryNodeChild(resultNode, 'props');
765
731
  let props = {};
766
732
  if (propsNode) {
767
- authState.creds.lastPropHash = (_b = propsNode === null || propsNode === void 0 ? void 0 : propsNode.attrs) === null || _b === void 0 ? void 0 : _b.hash;
768
- ev.emit('creds.update', authState.creds);
769
- 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');
770
739
  }
771
740
  logger.debug('fetched props');
772
741
  return props;
@@ -777,9 +746,17 @@ const makeChatsSocket = (config) => {
777
746
  * requires the last messages till the last message received; required for archive & unread
778
747
  */
779
748
  const chatModify = (mod, jid) => {
780
- const patch = (0, Utils_1.chatModificationToAppPatch)(mod, jid);
749
+ const patch = chatModificationToAppPatch(mod, jid);
781
750
  return appPatch(patch);
782
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
+ };
783
760
  /**
784
761
  * Star or Unstar a message
785
762
  */
@@ -791,6 +768,32 @@ const makeChatsSocket = (config) => {
791
768
  }
792
769
  }, jid);
793
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
+ };
794
797
  /**
795
798
  * Adds label for the chats
796
799
  */
@@ -833,82 +836,104 @@ const makeChatsSocket = (config) => {
833
836
  }
834
837
  }, jid);
835
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
+ };
836
855
  /**
837
856
  * queries need to be fired on connection open
838
857
  * help ensure parity with WA Web
839
858
  * */
840
859
  const executeInitQueries = async () => {
841
- await Promise.all([
842
- fetchProps(),
843
- fetchBlocklist(),
844
- fetchPrivacySettings(),
845
- ]);
860
+ await Promise.all([fetchProps(), fetchBlocklist(), fetchPrivacySettings()]);
846
861
  };
847
862
  const upsertMessage = ev.createBufferedFunction(async (msg, type) => {
848
- var _a, _b, _c;
849
863
  ev.emit('messages.upsert', { messages: [msg], type });
850
864
  if (!!msg.pushName) {
851
- let jid = msg.key.fromMe ? authState.creds.me.id : (msg.key.participant || msg.key.remoteJid);
852
- 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);
853
867
  if (!msg.key.fromMe) {
854
868
  ev.emit('contacts.update', [{ id: jid, notify: msg.pushName, verifiedName: msg.verifiedBizName }]);
855
869
  }
856
870
  // update our pushname too
857
- 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) {
858
872
  ev.emit('creds.update', { me: { ...authState.creds.me, name: msg.pushName } });
859
873
  }
860
874
  }
861
- const historyMsg = (0, Utils_1.getHistoryMsg)(msg.message);
862
- const shouldProcessHistoryMsg = historyMsg ?
863
- (shouldSyncHistoryMessage(historyMsg) &&
864
- Defaults_1.PROCESSABLE_HISTORY_TYPES.includes(historyMsg.syncType)) :
865
- false;
866
- if (historyMsg && !authState.creds.myAppStateKeyId) {
867
- logger.warn('skipping app state sync, as myAppStateKeyId is not set');
868
- pendingAppStateSync = true;
869
- }
870
- // Run app state sync in background (non-blocking) so incoming messages
871
- // are not delayed while the potentially large state sync runs on reconnect
872
- const appStateSyncPromise = (async () => {
873
- if (historyMsg && authState.creds.myAppStateKeyId) {
874
- pendingAppStateSync = false;
875
- 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;
876
885
  }
877
- })();
878
- // Process the message immediately, don't wait for app state sync
879
- await (0, process_message_1.default)(msg, {
880
- shouldProcessHistoryMsg,
881
- ev,
882
- creds: authState.creds,
883
- keyStore: authState.keys,
884
- logger,
885
- options: config.options,
886
- getMessage: config.getMessage,
887
- });
888
- if (((_c = (_b = msg.message) === null || _b === void 0 ? void 0 : _b.protocolMessage) === null || _c === void 0 ? void 0 : _c.appStateSyncKeyShare) &&
889
- pendingAppStateSync) {
890
- // don't block run in background
891
- doAppStateSync().then(() => { pendingAppStateSync = false; }).catch(e => logger.warn({ e }, 'bg app state sync failed'));
892
- }
893
- // Attach error handler to avoid unhandled rejection
894
- appStateSyncPromise.catch(e => logger.warn({ e }, 'background app state sync failed'));
895
- async function doAppStateSync() {
896
- if (!authState.creds.accountSyncCounter) {
897
- logger.info('doing initial app state sync');
898
- 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();
899
905
  const accountSyncCounter = (authState.creds.accountSyncCounter || 0) + 1;
900
906
  ev.emit('creds.update', { accountSyncCounter });
901
- if (needToFlushWithAppStateSync) {
902
- logger.debug('flushing with app state sync');
903
- ev.flush();
904
- }
905
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();
906
931
  }
907
932
  });
908
933
  ws.on('CB:presence', handlePresenceUpdate);
909
934
  ws.on('CB:chatstate', handlePresenceUpdate);
910
935
  ws.on('CB:ib,,dirty', async (node) => {
911
- const { attrs } = (0, WABinary_1.getBinaryNodeChild)(node, 'dirty');
936
+ const { attrs } = getBinaryNodeChild(node, 'dirty');
912
937
  const type = attrs.type;
913
938
  switch (type) {
914
939
  case 'account_sync':
@@ -930,42 +955,74 @@ const makeChatsSocket = (config) => {
930
955
  }
931
956
  });
932
957
  ev.on('connection.update', ({ connection, receivedPendingNotifications }) => {
933
- var _a;
934
958
  if (connection === 'open') {
935
959
  if (fireInitQueries) {
936
- executeInitQueries()
937
- .catch(error => onUnexpectedError(error, 'init queries'));
960
+ executeInitQueries().catch(error => onUnexpectedError(error, 'init queries'));
938
961
  }
939
- sendPresenceUpdate(markOnlineOnConnect ? 'available' : 'unavailable')
940
- .catch(error => onUnexpectedError(error, 'presence update requests'));
941
- }
942
- if (receivedPendingNotifications) {
943
- // if we don't have the app state key
944
- // we keep buffering events until we finally have
945
- // the key and can sync the messages
946
- if (!((_a = authState.creds) === null || _a === void 0 ? void 0 : _a.myAppStateKeyId) && !config.mobile) {
947
- ev.buffer();
948
- 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();
949
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');
950
998
  }
951
999
  });
952
1000
  return {
953
1001
  ...sock,
954
- processingMutex,
1002
+ createCallLink,
1003
+ getBotListV2,
1004
+ messageMutex,
1005
+ receiptMutex,
1006
+ appStatePatchMutex,
1007
+ notificationMutex,
955
1008
  fetchPrivacySettings,
956
1009
  upsertMessage,
957
1010
  appPatch,
958
1011
  sendPresenceUpdate,
959
1012
  presenceSubscribe,
960
1013
  profilePictureUrl,
961
- onWhatsApp,
962
1014
  fetchBlocklist,
963
1015
  fetchStatus,
1016
+ fetchDisappearingDuration,
1017
+ findUserId,
964
1018
  updateProfilePicture,
965
1019
  removeProfilePicture,
966
1020
  updateProfileStatus,
967
1021
  updateProfileName,
968
1022
  updateBlockStatus,
1023
+ updateDisableLinkPreviewsPrivacy,
1024
+ updateCallPrivacy,
1025
+ updateMessagesPrivacy,
969
1026
  updateLastSeenPrivacy,
970
1027
  updateOnlinePrivacy,
971
1028
  updateProfilePicturePrivacy,
@@ -977,13 +1034,15 @@ const makeChatsSocket = (config) => {
977
1034
  resyncAppState,
978
1035
  chatModify,
979
1036
  cleanDirtyBits,
1037
+ addOrEditContact,
1038
+ removeContact,
1039
+ addLabel,
980
1040
  addChatLabel,
981
1041
  removeChatLabel,
982
1042
  addMessageLabel,
983
- checkWhatsApp,
984
- reqPairing,
985
1043
  removeMessageLabel,
986
- star
1044
+ star,
1045
+ addOrEditQuickReply,
1046
+ removeQuickReply
987
1047
  };
988
- };
989
- exports.makeChatsSocket = makeChatsSocket;
1048
+ };