n4lyx 3.0.2 → 3.0.3

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.
@@ -4,835 +4,685 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.makeChatsSocket = void 0;
7
- const boom_1 = require("@hapi/boom");
8
- const node_cache_1 = __importDefault(require("@cacheable/node-cache"));
9
- const WAProto_1 = require("../../WAProto");
10
- const Defaults_1 = require("../Defaults");
11
- const Types_1 = require("../Types");
12
- const Utils_1 = require("../Utils");
13
- const make_mutex_1 = require("../Utils/make-mutex");
14
- const process_message_1 = __importDefault(require("../Utils/process-message"));
15
- const WABinary_1 = require("../WABinary");
16
- const WAUSync_1 = require("../WAUSync");
17
- const usync_1 = require("./usync");
18
- const chalk = require('chalk');
7
+
8
+ const boom_1 = require("@hapi/boom");
9
+ const node_cache_1 = __importDefault(require("@cacheable/node-cache"));
10
+ const WAProto_1 = require("../../WAProto");
11
+ const Defaults_1 = require("../Defaults");
12
+ const Types_1 = require("../Types");
13
+ const Utils_1 = require("../Utils");
14
+ const make_mutex_1 = require("../Utils/make-mutex");
15
+ const process_message_1 = __importDefault(require("../Utils/process-message"));
16
+ const WABinary_1 = require("../WABinary");
17
+ const WAUSync_1 = require("../WAUSync");
18
+ const usync_1 = require("./usync");
19
+
20
+ // chalk is optional — graceful fallback if not installed
21
+ let chalk;
22
+ try { chalk = require("chalk"); } catch { chalk = { gray: s => s, yellow: s => s }; }
23
+
19
24
  const MAX_SYNC_ATTEMPTS = 2;
25
+
20
26
  const SyncState = {
21
- Connecting: 'connecting',
22
- AwaitingInitialSync: 'awaiting_initial_sync',
23
- Syncing: 'syncing',
24
- Online: 'online'
27
+ Connecting: "connecting",
28
+ AwaitingInitialSync: "awaiting_initial_sync",
29
+ Syncing: "syncing",
30
+ Online: "online",
25
31
  };
32
+
26
33
  const makeChatsSocket = (config) => {
27
- const { logger, markOnlineOnConnect, fireInitQueries, appStateMacVerification, shouldIgnoreJid, shouldSyncHistoryMessage, } = config;
34
+ const {
35
+ logger,
36
+ markOnlineOnConnect,
37
+ fireInitQueries,
38
+ appStateMacVerification,
39
+ shouldIgnoreJid,
40
+ shouldSyncHistoryMessage,
41
+ } = config;
42
+
28
43
  const sock = (0, usync_1.makeUSyncSocket)(config);
29
- const { ev, ws, authState, generateMessageTag, sendNode, query, signalRepository, onUnexpectedError, } = sock;
44
+ const {
45
+ ev, ws, authState, generateMessageTag,
46
+ sendNode, query, signalRepository, onUnexpectedError,
47
+ } = sock;
48
+
30
49
  let privacySettings;
31
- let syncState = SyncState.Connecting;
50
+ let syncState = SyncState.Connecting;
32
51
  let needToFlushWithAppStateSync = false;
33
- let pendingAppStateSync = false;
52
+ let pendingAppStateSync = false;
34
53
  let awaitingSyncTimeout;
54
+
35
55
  const processingMutex = (0, make_mutex_1.makeMutex)();
56
+
36
57
  const placeholderResendCache = config.placeholderResendCache || new node_cache_1.default({
37
- stdTTL: Defaults_1.DEFAULT_CACHE_TTLS.MSG_RETRY,
38
- useClones: false
58
+ stdTTL: Defaults_1.DEFAULT_CACHE_TTLS.MSG_RETRY,
59
+ useClones: false,
39
60
  });
40
61
  const receivedMessageDedupCache = new node_cache_1.default({
41
- stdTTL: 3 * 60,
42
- useClones: false
62
+ stdTTL: 3 * 60,
63
+ useClones: false,
43
64
  });
44
- if (!config.placeholderResendCache) {
45
- config.placeholderResendCache = placeholderResendCache;
46
- }
65
+ if (!config.placeholderResendCache) config.placeholderResendCache = placeholderResendCache;
66
+
67
+ // ── App state sync key ────────────────────────────────────────────────────
47
68
  const getAppStateSyncKey = async (keyId) => {
48
- const { [keyId]: key } = await authState.keys.get('app-state-sync-key', [keyId]);
69
+ const { [keyId]: key } = await authState.keys.get("app-state-sync-key", [keyId]);
49
70
  return key;
50
71
  };
72
+
73
+ // ── Privacy ───────────────────────────────────────────────────────────────
51
74
  const fetchPrivacySettings = async (force = false) => {
52
75
  if (!privacySettings || force) {
53
76
  const { content } = await query({
54
- tag: 'iq',
55
- attrs: {
56
- xmlns: 'privacy',
57
- to: WABinary_1.S_WHATSAPP_NET,
58
- type: 'get'
59
- },
60
- content: [
61
- { tag: 'privacy', attrs: {} }
62
- ]
77
+ tag: "iq",
78
+ attrs: { xmlns: "privacy", to: WABinary_1.S_WHATSAPP_NET, type: "get" },
79
+ content: [{ tag: "privacy", attrs: {} }],
63
80
  });
64
- privacySettings = (0, WABinary_1.reduceBinaryNodeToDictionary)(content === null || content === void 0 ? void 0 : content[0], 'category');
81
+ privacySettings = (0, WABinary_1.reduceBinaryNodeToDictionary)(content?.[0], "category");
65
82
  }
66
83
  return privacySettings;
67
84
  };
85
+
68
86
  const privacyQuery = async (name, value) => {
69
87
  await query({
70
- tag: 'iq',
71
- attrs: {
72
- xmlns: 'privacy',
73
- to: WABinary_1.S_WHATSAPP_NET,
74
- type: 'set'
75
- },
88
+ tag: "iq",
89
+ attrs: { xmlns: "privacy", to: WABinary_1.S_WHATSAPP_NET, type: "set" },
76
90
  content: [{
77
- tag: 'privacy',
78
- attrs: {},
79
- content: [
80
- {
81
- tag: 'category',
82
- attrs: { name, value }
83
- }
84
- ]
85
- }]
91
+ tag: "privacy",
92
+ attrs: {},
93
+ content: [{ tag: "category", attrs: { name, value } }],
94
+ }],
86
95
  });
87
96
  };
88
- const updateMessagesPrivacy = async (value) => {
89
- await privacyQuery('messages', value);
90
- };
91
- const updateCallPrivacy = async (value) => {
92
- await privacyQuery('calladd', value);
93
- };
94
- const updateLastSeenPrivacy = async (value) => {
95
- await privacyQuery('last', value);
96
- };
97
- const updateOnlinePrivacy = async (value) => {
98
- await privacyQuery('online', value);
99
- };
100
- const updateProfilePicturePrivacy = async (value) => {
101
- await privacyQuery('profile', value);
102
- };
103
- const updateStatusPrivacy = async (value) => {
104
- await privacyQuery('status', value);
105
- };
106
- const updateReadReceiptsPrivacy = async (value) => {
107
- await privacyQuery('readreceipts', value);
108
- };
109
- const updateGroupsAddPrivacy = async (value) => {
110
- await privacyQuery('groupadd', value);
111
- };
112
- const updateDisableLinkPreviewsPrivacy = async (isPreviewsDisabled) => {
113
- return chatModify({ disableLinkPreviews: { isPreviewsDisabled } }, '');
114
- };
97
+
98
+ const updateMessagesPrivacy = async (value) => privacyQuery("messages", value);
99
+ const updateCallPrivacy = async (value) => privacyQuery("calladd", value);
100
+ const updateLastSeenPrivacy = async (value) => privacyQuery("last", value);
101
+ const updateOnlinePrivacy = async (value) => privacyQuery("online", value);
102
+ const updateProfilePicturePrivacy = async (value) => privacyQuery("profile", value);
103
+ const updateStatusPrivacy = async (value) => privacyQuery("status", value);
104
+ const updateReadReceiptsPrivacy = async (value) => privacyQuery("readreceipts", value);
105
+ const updateGroupsAddPrivacy = async (value) => privacyQuery("groupadd", value);
106
+
107
+ const updateDisableLinkPreviewsPrivacy = async (isPreviewsDisabled) =>
108
+ chatModify({ disableLinkPreviews: { isPreviewsDisabled } }, "");
109
+
115
110
  const updateDefaultDisappearingMode = async (duration) => {
116
111
  await query({
117
- tag: 'iq',
118
- attrs: {
119
- xmlns: 'disappearing_mode',
120
- to: WABinary_1.S_WHATSAPP_NET,
121
- type: 'set'
122
- },
112
+ tag: "iq",
113
+ attrs: { xmlns: "disappearing_mode", to: WABinary_1.S_WHATSAPP_NET, type: "set" },
123
114
  content: [{
124
- tag: 'disappearing_mode',
125
- attrs: {
126
- duration: duration.toString()
127
- }
128
- }]
115
+ tag: "disappearing_mode",
116
+ attrs: { duration: duration.toString() },
117
+ }],
129
118
  });
130
119
  };
120
+
121
+ // ── Bot list ──────────────────────────────────────────────────────────────
131
122
  const getBotListV2 = async () => {
132
123
  const resp = await query({
133
- tag: 'iq',
134
- attrs: {
135
- xmlns: 'bot',
136
- to: WABinary_1.S_WHATSAPP_NET,
137
- type: 'get'
138
- },
139
- content: [{
140
- tag: 'bot',
141
- attrs: {
142
- v: '2'
143
- }
144
- }]
124
+ tag: "iq",
125
+ attrs: { xmlns: "bot", to: WABinary_1.S_WHATSAPP_NET, type: "get" },
126
+ content: [{ tag: "bot", attrs: { v: "2" } }],
145
127
  });
146
- const botNode = (0, WABinary_1.getBinaryNodeChild)(resp, 'bot');
128
+ const botNode = (0, WABinary_1.getBinaryNodeChild)(resp, "bot");
147
129
  const botList = [];
148
- for (const section of (0, WABinary_1.getBinaryNodeChildren)(botNode, 'section')) {
149
- if (section.attrs.type === 'all') {
150
- for (const bot of (0, WABinary_1.getBinaryNodeChildren)(section, 'bot')) {
151
- botList.push({
152
- jid: bot.attrs.jid,
153
- personaId: bot.attrs['persona_id']
154
- });
130
+ for (const section of (0, WABinary_1.getBinaryNodeChildren)(botNode, "section")) {
131
+ if (section.attrs.type === "all") {
132
+ for (const bot of (0, WABinary_1.getBinaryNodeChildren)(section, "bot")) {
133
+ botList.push({ jid: bot.attrs.jid, personaId: bot.attrs["persona_id"] });
155
134
  }
156
135
  }
157
136
  }
158
137
  return botList;
159
138
  };
139
+
140
+ // ── onWhatsApp / fetchStatus ──────────────────────────────────────────────
160
141
  const onWhatsApp = async (...jids) => {
161
142
  const usyncQuery = new WAUSync_1.USyncQuery()
162
143
  .withContactProtocol()
163
144
  .withLIDProtocol();
164
145
  for (const jid of jids) {
165
- const phone = `+${jid.replace('+', '').split('@')[0].split(':')[0]}`;
146
+ const phone = `+${jid.replace("+", "").split("@")[0].split(":")[0]}`;
166
147
  usyncQuery.withUser(new WAUSync_1.USyncUser().withPhone(phone));
167
148
  }
168
149
  const results = await sock.executeUSyncQuery(usyncQuery);
169
150
  if (results) {
170
- return results.list.filter((a) => !!a.contact).map(({ contact, id, lid }) => ({ jid: id, exists: contact, lid }));
151
+ return results.list
152
+ .filter(a => !!a.contact)
153
+ .map(({ contact, id, lid }) => ({ jid: id, exists: contact, lid }));
171
154
  }
155
+ return [];
172
156
  };
157
+
173
158
  const fetchStatus = async (...jids) => {
174
- const usyncQuery = new WAUSync_1.USyncQuery()
175
- .withStatusProtocol();
176
- for (const jid of jids) {
177
- usyncQuery.withUser(new WAUSync_1.USyncUser().withId(jid));
178
- }
159
+ const usyncQuery = new WAUSync_1.USyncQuery().withStatusProtocol();
160
+ for (const jid of jids) usyncQuery.withUser(new WAUSync_1.USyncUser().withId(jid));
179
161
  const result = await sock.executeUSyncQuery(usyncQuery);
180
- if (result) {
181
- return result.list;
182
- }
162
+ return result ? result.list : [];
183
163
  };
164
+
184
165
  const fetchDisappearingDuration = async (...jids) => {
185
- const usyncQuery = new WAUSync_1.USyncQuery()
186
- .withDisappearingModeProtocol();
187
- for (const jid of jids) {
188
- usyncQuery.withUser(new WAUSync_1.USyncUser().withId(jid));
189
- }
166
+ const usyncQuery = new WAUSync_1.USyncQuery().withDisappearingModeProtocol();
167
+ for (const jid of jids) usyncQuery.withUser(new WAUSync_1.USyncUser().withId(jid));
190
168
  const result = await sock.executeUSyncQuery(usyncQuery);
191
- if (result) {
192
- return result.list;
193
- }
169
+ return result ? result.list : [];
194
170
  };
171
+
172
+ // ── Profile picture ───────────────────────────────────────────────────────
195
173
  const updateProfilePicture = async (jid, content, dimensions) => {
174
+ if (!jid) throw new boom_1.Boom("Illegal no-jid profile update");
196
175
  let targetJid;
197
- if (!jid) {
198
- 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');
199
- }
200
176
  if ((0, WABinary_1.jidNormalizedUser)(jid) !== (0, WABinary_1.jidNormalizedUser)(authState.creds.me.id)) {
201
177
  targetJid = (0, WABinary_1.jidNormalizedUser)(jid);
202
178
  }
203
179
  const { img } = await (0, Utils_1.generateProfilePicture)(content, dimensions);
204
180
  await query({
205
- tag: 'iq',
206
- attrs: {
207
- target: targetJid,
208
- to: WABinary_1.S_WHATSAPP_NET,
209
- type: 'set',
210
- xmlns: 'w:profile:picture'
211
- },
212
- content: [
213
- {
214
- tag: 'picture',
215
- attrs: { type: 'image' },
216
- content: img
217
- }
218
- ]
181
+ tag: "iq",
182
+ attrs: { target: targetJid, to: WABinary_1.S_WHATSAPP_NET, type: "set", xmlns: "w:profile:picture" },
183
+ content: [{ tag: "picture", attrs: { type: "image" }, content: img }],
219
184
  });
220
185
  };
186
+
221
187
  const removeProfilePicture = async (jid) => {
188
+ if (!jid) throw new boom_1.Boom("Illegal no-jid profile update");
222
189
  let targetJid;
223
- if (!jid) {
224
- 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');
225
- }
226
190
  if ((0, WABinary_1.jidNormalizedUser)(jid) !== (0, WABinary_1.jidNormalizedUser)(authState.creds.me.id)) {
227
191
  targetJid = (0, WABinary_1.jidNormalizedUser)(jid);
228
192
  }
229
193
  await query({
230
- tag: 'iq',
231
- attrs: {
232
- target: targetJid,
233
- to: WABinary_1.S_WHATSAPP_NET,
234
- type: 'set',
235
- xmlns: 'w:profile:picture'
236
- }
194
+ tag: "iq",
195
+ attrs: { target: targetJid, to: WABinary_1.S_WHATSAPP_NET, type: "set", xmlns: "w:profile:picture" },
237
196
  });
238
197
  };
198
+
239
199
  const updateProfileStatus = async (status) => {
240
200
  await query({
241
- tag: 'iq',
242
- attrs: {
243
- to: WABinary_1.S_WHATSAPP_NET,
244
- type: 'set',
245
- xmlns: 'status'
246
- },
247
- content: [
248
- {
249
- tag: 'status',
250
- attrs: {},
251
- content: Buffer.from(status, 'utf-8')
252
- }
253
- ]
201
+ tag: "iq",
202
+ attrs: { to: WABinary_1.S_WHATSAPP_NET, type: "set", xmlns: "status" },
203
+ content: [{ tag: "status", attrs: {}, content: Buffer.from(status, "utf-8") }],
254
204
  });
255
205
  };
256
- const updateProfileName = async (name) => {
257
- await chatModify({ pushNameSetting: name }, '');
258
- };
206
+
207
+ const updateProfileName = async (name) => chatModify({ pushNameSetting: name }, "");
208
+
209
+ // ── Block list ────────────────────────────────────────────────────────────
259
210
  const fetchBlocklist = async () => {
260
- const result = await query({
261
- tag: 'iq',
262
- attrs: {
263
- xmlns: 'blocklist',
264
- to: WABinary_1.S_WHATSAPP_NET,
265
- type: 'get'
266
- }
211
+ const result = await query({
212
+ tag: "iq",
213
+ attrs: { xmlns: "blocklist", to: WABinary_1.S_WHATSAPP_NET, type: "get" },
267
214
  });
268
- const listNode = (0, WABinary_1.getBinaryNodeChild)(result, 'list');
269
- return (0, WABinary_1.getBinaryNodeChildren)(listNode, 'item')
270
- .map(n => n.attrs.jid);
215
+ const listNode = (0, WABinary_1.getBinaryNodeChild)(result, "list");
216
+ return (0, WABinary_1.getBinaryNodeChildren)(listNode, "item").map(n => n.attrs.jid);
271
217
  };
218
+
272
219
  const updateBlockStatus = async (jid, action) => {
273
220
  await query({
274
- tag: 'iq',
275
- attrs: {
276
- xmlns: 'blocklist',
277
- to: WABinary_1.S_WHATSAPP_NET,
278
- type: 'set'
279
- },
280
- content: [
281
- {
282
- tag: 'item',
283
- attrs: {
284
- action,
285
- jid
286
- }
287
- }
288
- ]
221
+ tag: "iq",
222
+ attrs: { xmlns: "blocklist", to: WABinary_1.S_WHATSAPP_NET, type: "set" },
223
+ content: [{ tag: "item", attrs: { action, jid } }],
289
224
  });
290
225
  };
226
+
227
+ // ── Business profile ──────────────────────────────────────────────────────
291
228
  const getBusinessProfile = async (jid) => {
292
- var _a, _b, _c, _d, _e, _f, _g;
293
229
  const results = await query({
294
- tag: 'iq',
295
- attrs: {
296
- to: 's.whatsapp.net',
297
- xmlns: 'w:biz',
298
- type: 'get'
299
- },
230
+ tag: "iq",
231
+ attrs: { to: "s.whatsapp.net", xmlns: "w:biz", type: "get" },
300
232
  content: [{
301
- tag: 'business_profile',
302
- attrs: { v: '244' },
303
- content: [{
304
- tag: 'profile',
305
- attrs: { jid }
306
- }]
307
- }]
233
+ tag: "business_profile",
234
+ attrs: { v: "244" },
235
+ content: [{ tag: "profile", attrs: { jid } }],
236
+ }],
308
237
  });
309
- const profileNode = (0, WABinary_1.getBinaryNodeChild)(results, 'business_profile');
310
- const profiles = (0, WABinary_1.getBinaryNodeChild)(profileNode, 'profile');
311
- if (profiles) {
312
- const address = (0, WABinary_1.getBinaryNodeChild)(profiles, 'address');
313
- const description = (0, WABinary_1.getBinaryNodeChild)(profiles, 'description');
314
- const website = (0, WABinary_1.getBinaryNodeChild)(profiles, 'website');
315
- const email = (0, WABinary_1.getBinaryNodeChild)(profiles, 'email');
316
- const category = (0, WABinary_1.getBinaryNodeChild)((0, WABinary_1.getBinaryNodeChild)(profiles, 'categories'), 'category');
317
- const businessHours = (0, WABinary_1.getBinaryNodeChild)(profiles, 'business_hours');
318
- const businessHoursConfig = businessHours
319
- ? (0, WABinary_1.getBinaryNodeChildren)(businessHours, 'business_hours_config')
320
- : undefined;
321
- const websiteStr = (_a = website === null || website === void 0 ? void 0 : website.content) === null || _a === void 0 ? void 0 : _a.toString();
322
- return {
323
- wid: (_b = profiles.attrs) === null || _b === void 0 ? void 0 : _b.jid,
324
- address: (_c = address === null || address === void 0 ? void 0 : address.content) === null || _c === void 0 ? void 0 : _c.toString(),
325
- description: ((_d = description === null || description === void 0 ? void 0 : description.content) === null || _d === void 0 ? void 0 : _d.toString()) || '',
326
- website: websiteStr ? [websiteStr] : [],
327
- email: (_e = email === null || email === void 0 ? void 0 : email.content) === null || _e === void 0 ? void 0 : _e.toString(),
328
- category: (_f = category === null || category === void 0 ? void 0 : category.content) === null || _f === void 0 ? void 0 : _f.toString(),
329
- 'business_hours': {
330
- timezone: (_g = businessHours === null || businessHours === void 0 ? void 0 : businessHours.attrs) === null || _g === void 0 ? void 0 : _g.timezone,
331
- 'business_config': businessHoursConfig === null || businessHoursConfig === void 0 ? void 0 : businessHoursConfig.map(({ attrs }) => attrs)
332
- }
333
- };
334
- }
238
+ const profileNode = (0, WABinary_1.getBinaryNodeChild)(results, "business_profile");
239
+ const profiles = (0, WABinary_1.getBinaryNodeChild)(profileNode, "profile");
240
+ if (!profiles) return null;
241
+
242
+ const address = (0, WABinary_1.getBinaryNodeChild)(profiles, "address");
243
+ const description = (0, WABinary_1.getBinaryNodeChild)(profiles, "description");
244
+ const website = (0, WABinary_1.getBinaryNodeChild)(profiles, "website");
245
+ const email = (0, WABinary_1.getBinaryNodeChild)(profiles, "email");
246
+ const category = (0, WABinary_1.getBinaryNodeChild)(
247
+ (0, WABinary_1.getBinaryNodeChild)(profiles, "categories"), "category"
248
+ );
249
+ const businessHours = (0, WABinary_1.getBinaryNodeChild)(profiles, "business_hours");
250
+ const businessHoursConfig = businessHours
251
+ ? (0, WABinary_1.getBinaryNodeChildren)(businessHours, "business_hours_config")
252
+ : undefined;
253
+ const websiteStr = website?.content?.toString();
254
+
255
+ return {
256
+ wid: profiles.attrs?.jid,
257
+ address: address?.content?.toString(),
258
+ description: description?.content?.toString() || "",
259
+ website: websiteStr ? [websiteStr] : [],
260
+ email: email?.content?.toString(),
261
+ category: category?.content?.toString(),
262
+ business_hours: {
263
+ timezone: businessHours?.attrs?.timezone,
264
+ business_config: businessHoursConfig?.map(({ attrs }) => attrs),
265
+ },
266
+ };
335
267
  };
268
+
269
+ // ── App state sync ────────────────────────────────────────────────────────
336
270
  const cleanDirtyBits = async (type, fromTimestamp) => {
337
- logger.info({ fromTimestamp }, 'clean dirty bits ' + type);
271
+ logger.info({ fromTimestamp }, "clean dirty bits " + type);
338
272
  await sendNode({
339
- tag: 'iq',
273
+ tag: "iq",
340
274
  attrs: {
341
- to: WABinary_1.S_WHATSAPP_NET,
342
- type: 'set',
343
- xmlns: 'urn:xmpp:whatsapp:dirty',
344
- id: generateMessageTag(),
275
+ to: WABinary_1.S_WHATSAPP_NET,
276
+ type: "set",
277
+ xmlns: "urn:xmpp:whatsapp:dirty",
278
+ id: generateMessageTag(),
345
279
  },
346
- content: [
347
- {
348
- tag: 'clean',
349
- attrs: {
350
- type,
351
- ...(fromTimestamp ? { timestamp: fromTimestamp.toString() } : null),
352
- }
353
- }
354
- ]
280
+ content: [{
281
+ tag: "clean",
282
+ attrs: { type, ...(fromTimestamp ? { timestamp: fromTimestamp.toString() } : null) },
283
+ }],
355
284
  });
356
285
  };
357
- const newAppStateChunkHandler = (isInitialSync) => {
358
- return {
359
- onMutation(mutation) {
360
- (0, Utils_1.processSyncAction)(mutation, ev, authState.creds.me, isInitialSync ? { accountSettings: authState.creds.accountSettings } : undefined, logger);
361
- }
362
- };
363
- };
286
+
287
+ const newAppStateChunkHandler = (isInitialSync) => ({
288
+ onMutation(mutation) {
289
+ (0, Utils_1.processSyncAction)(
290
+ mutation, ev, authState.creds.me,
291
+ isInitialSync ? { accountSettings: authState.creds.accountSettings } : undefined,
292
+ logger
293
+ );
294
+ },
295
+ });
296
+
364
297
  const resyncAppState = ev.createBufferedFunction(async (collections, isInitialSync) => {
365
298
  const initialVersionMap = {};
366
299
  const globalMutationMap = {};
300
+
367
301
  await authState.keys.transaction(async () => {
368
- var _a;
369
302
  const collectionsToHandle = new Set(collections);
370
- const attemptsMap = {};
303
+ const attemptsMap = {};
304
+
371
305
  while (collectionsToHandle.size) {
372
306
  const states = {};
373
- const nodes = [];
307
+ const nodes = [];
308
+
374
309
  for (const name of collectionsToHandle) {
375
- const result = await authState.keys.get('app-state-sync-version', [name]);
376
- let state = result[name];
310
+ const result = await authState.keys.get("app-state-sync-version", [name]);
311
+ let state = result[name];
377
312
  if (state) {
378
- if (typeof initialVersionMap[name] === 'undefined') {
313
+ if (typeof initialVersionMap[name] === "undefined") {
379
314
  initialVersionMap[name] = state.version;
380
315
  }
381
- }
382
- else {
316
+ } else {
383
317
  state = (0, Utils_1.newLTHashState)();
384
318
  }
385
319
  states[name] = state;
386
320
  logger.info(`resyncing ${name} from v${state.version}`);
387
321
  nodes.push({
388
- tag: 'collection',
322
+ tag: "collection",
389
323
  attrs: {
390
324
  name,
391
- version: state.version.toString(),
392
- 'return_snapshot': (!state.version).toString()
393
- }
325
+ version: state.version.toString(),
326
+ "return_snapshot": (!state.version).toString(),
327
+ },
394
328
  });
395
329
  }
396
- const result = await query({
397
- tag: 'iq',
398
- attrs: {
399
- to: WABinary_1.S_WHATSAPP_NET,
400
- xmlns: 'w:sync:app:state',
401
- type: 'set'
402
- },
403
- content: [
404
- {
405
- tag: 'sync',
406
- attrs: {},
407
- content: nodes
408
- }
409
- ]
330
+
331
+ const result = await query({
332
+ tag: "iq",
333
+ attrs: { to: WABinary_1.S_WHATSAPP_NET, xmlns: "w:sync:app:state", type: "set" },
334
+ content: [{ tag: "sync", attrs: {}, content: nodes }],
410
335
  });
411
- const decoded = await (0, Utils_1.extractSyncdPatches)(result, config === null || config === void 0 ? void 0 : config.options);
336
+ const decoded = await (0, Utils_1.extractSyncdPatches)(result, config?.options);
337
+
412
338
  for (const key in decoded) {
413
- const name = key;
339
+ const name = key;
414
340
  const { patches, hasMorePatches, snapshot } = decoded[name];
415
341
  try {
416
342
  if (snapshot) {
417
- const { state: newState, mutationMap } = await (0, Utils_1.decodeSyncdSnapshot)(name, snapshot, getAppStateSyncKey, initialVersionMap[name], appStateMacVerification.snapshot);
343
+ const { state: newState, mutationMap } = await (0, Utils_1.decodeSyncdSnapshot)(
344
+ name, snapshot, getAppStateSyncKey,
345
+ initialVersionMap[name], appStateMacVerification.snapshot
346
+ );
418
347
  states[name] = newState;
419
348
  Object.assign(globalMutationMap, mutationMap);
420
- logger.info(`restored state of ${name} from snapshot to v${newState.version} with mutations`);
421
- await authState.keys.set({ 'app-state-sync-version': { [name]: newState } });
349
+ logger.info(`restored state of ${name} from snapshot to v${newState.version}`);
350
+ await authState.keys.set({ "app-state-sync-version": { [name]: newState } });
422
351
  }
423
352
  if (patches.length) {
424
- const { state: newState, mutationMap } = await (0, Utils_1.decodePatches)(name, patches, states[name], getAppStateSyncKey, config.options, initialVersionMap[name], logger, appStateMacVerification.patch);
425
- await authState.keys.set({ 'app-state-sync-version': { [name]: newState } });
353
+ const { state: newState, mutationMap } = await (0, Utils_1.decodePatches)(
354
+ name, patches, states[name], getAppStateSyncKey,
355
+ config.options, initialVersionMap[name], logger,
356
+ appStateMacVerification.patch
357
+ );
358
+ await authState.keys.set({ "app-state-sync-version": { [name]: newState } });
426
359
  logger.info(`synced ${name} to v${newState.version}`);
427
360
  initialVersionMap[name] = newState.version;
428
361
  Object.assign(globalMutationMap, mutationMap);
429
362
  }
430
363
  if (hasMorePatches) {
431
364
  logger.info(`${name} has more patches...`);
432
- }
433
- else {
365
+ } else {
434
366
  collectionsToHandle.delete(name);
435
367
  }
436
- }
437
- catch (error) {
438
- const isIrrecoverableError = attemptsMap[name] >= MAX_SYNC_ATTEMPTS
439
- || ((_a = error.output) === null || _a === void 0 ? void 0 : _a.statusCode) === 404
440
- || error.name === 'TypeError';
441
- logger.info({ name, error: error.stack }, `failed to sync state from version${isIrrecoverableError ? '' : ', removing and trying from scratch'}`);
442
- await authState.keys.set({ 'app-state-sync-version': { [name]: null } });
368
+ } catch (error) {
369
+ const isIrrecoverable =
370
+ attemptsMap[name] >= MAX_SYNC_ATTEMPTS ||
371
+ error.output?.statusCode === 404 ||
372
+ error.name === "TypeError";
373
+ logger.info({ name, error: error.stack },
374
+ `failed to sync state${isIrrecoverable ? "" : ", removing and trying from scratch"}`);
375
+ await authState.keys.set({ "app-state-sync-version": { [name]: null } });
443
376
  attemptsMap[name] = (attemptsMap[name] || 0) + 1;
444
- if (isIrrecoverableError) {
445
- collectionsToHandle.delete(name);
446
- }
377
+ if (isIrrecoverable) collectionsToHandle.delete(name);
447
378
  }
448
379
  }
449
380
  }
450
381
  });
382
+
451
383
  const { onMutation } = newAppStateChunkHandler(isInitialSync);
452
- for (const key in globalMutationMap) {
453
- onMutation(globalMutationMap[key]);
454
- }
384
+ for (const key in globalMutationMap) onMutation(globalMutationMap[key]);
455
385
  });
456
- const profilePictureUrl = async (jid, type = 'preview', timeoutMs) => {
457
- var _a;
386
+
387
+ // ── profilePictureUrl — FIXED: no crash on 404 ────────────────────────────
388
+ const profilePictureUrl = async (jid, type = "preview", timeoutMs) => {
458
389
  jid = (0, WABinary_1.jidNormalizedUser)(jid);
459
390
  try {
460
391
  const result = await query({
461
- tag: 'iq',
462
- attrs: {
463
- target: jid,
464
- to: WABinary_1.S_WHATSAPP_NET,
465
- type: 'get',
466
- xmlns: 'w:profile:picture'
467
- },
468
- content: [
469
- { tag: 'picture', attrs: { type, query: 'url' } }
470
- ]
392
+ tag: "iq",
393
+ attrs: { target: jid, to: WABinary_1.S_WHATSAPP_NET, type: "get", xmlns: "w:profile:picture" },
394
+ content: [{ tag: "picture", attrs: { type, query: "url" } }],
471
395
  }, timeoutMs);
472
- const child = (0, WABinary_1.getBinaryNodeChild)(result, 'picture');
473
- return (_a = child === null || child === void 0 ? void 0 : child.attrs) === null || _a === void 0 ? void 0 : _a.url;
396
+ const child = (0, WABinary_1.getBinaryNodeChild)(result, "picture");
397
+ return child?.attrs?.url ?? null;
474
398
  } catch (error) {
475
- if (error.message?.includes('item-not-found') ||
476
- error.output?.payload?.statusCode === 404 ||
477
- error.statusCode === 404) {
478
- logger.info(chalk.gray(`[INFO] Profile pic not found for ${jid}, using fallback`));
479
- return {
480
- eurl: undefined,
481
- id: jid.split('@')[0],
482
- status: null,
483
- img: null
484
- };
399
+ const msg = error?.message || "";
400
+ const code = error?.output?.payload?.statusCode || error?.statusCode || 0;
401
+ // 404 / item-not-found / not-authorized = no picture, return null silently
402
+ if (
403
+ msg.includes("item-not-found") ||
404
+ msg.includes("not-authorized") ||
405
+ code === 404
406
+ ) {
407
+ logger.info(chalk.gray(`[profilePictureUrl] No picture for ${jid}`));
408
+ return null;
485
409
  }
410
+ // Re-throw anything unexpected
486
411
  throw error;
487
412
  }
488
413
  };
414
+
415
+ // ── Calls ─────────────────────────────────────────────────────────────────
489
416
  const createCallLink = async (type, event, timeoutMs) => {
490
417
  const result = await query({
491
- tag: 'call',
492
- attrs: {
493
- id: generateMessageTag(),
494
- to: '@call'
495
- },
496
- content: [
497
- {
498
- tag: 'link_create',
499
- attrs: { media: type },
500
- content: event ? [{ tag: 'event', attrs: { start_time: String(event.startTime) } }] : undefined
501
- }
502
- ]
418
+ tag: "call",
419
+ attrs: { id: generateMessageTag(), to: "@call" },
420
+ content: [{
421
+ tag: "link_create",
422
+ attrs: { media: type },
423
+ content: event
424
+ ? [{ tag: "event", attrs: { start_time: String(event.startTime) } }]
425
+ : undefined,
426
+ }],
503
427
  }, timeoutMs);
504
- const child = (0, WABinary_1.getBinaryNodeChild)(result, 'link_create');
505
- return child?.attrs?.token;
428
+ return (0, WABinary_1.getBinaryNodeChild)(result, "link_create")?.attrs?.token;
506
429
  };
430
+
431
+ // ── Presence ──────────────────────────────────────────────────────────────
507
432
  const sendPresenceUpdate = async (type, toJid) => {
508
433
  const me = authState.creds.me;
509
- if (type === 'available' || type === 'unavailable') {
434
+ if (type === "available" || type === "unavailable") {
510
435
  if (!me.name) {
511
- logger.warn('no name present, ignoring presence update request...');
436
+ logger.warn("no name present, ignoring presence update...");
512
437
  return;
513
438
  }
514
- ev.emit('connection.update', { isOnline: type === 'available' });
439
+ ev.emit("connection.update", { isOnline: type === "available" });
515
440
  await sendNode({
516
- tag: 'presence',
517
- attrs: {
518
- name: me.name.replace(/@/g, ''),
519
- type
520
- }
441
+ tag: "presence",
442
+ attrs: { name: me.name.replace(/@/g, ""), type },
521
443
  });
522
- }
523
- else {
444
+ } else {
524
445
  const { server } = (0, WABinary_1.jidDecode)(toJid);
525
- const isLid = server === 'lid';
446
+ const isLid = server === "lid";
526
447
  await sendNode({
527
- tag: 'chatstate',
528
- attrs: {
529
- from: isLid ? me.lid : me.id,
530
- to: toJid,
531
- },
532
- content: [
533
- {
534
- tag: type === 'recording' ? 'composing' : type,
535
- attrs: type === 'recording' ? { media: 'audio' } : {}
536
- }
537
- ]
448
+ tag: "chatstate",
449
+ attrs: { from: isLid ? me.lid : me.id, to: toJid },
450
+ content: [{
451
+ tag: type === "recording" ? "composing" : type,
452
+ attrs: type === "recording" ? { media: "audio" } : {},
453
+ }],
538
454
  });
539
455
  }
540
456
  };
541
- const presenceSubscribe = (toJid, tcToken) => (sendNode({
542
- tag: 'presence',
543
- attrs: {
544
- to: toJid,
545
- id: generateMessageTag(),
546
- type: 'subscribe'
547
- },
548
- content: tcToken
549
- ? [
550
- {
551
- tag: 'tctoken',
552
- attrs: {},
553
- content: tcToken
554
- }
555
- ]
556
- : undefined
557
- }));
457
+
458
+ const presenceSubscribe = (toJid, tcToken) => sendNode({
459
+ tag: "presence",
460
+ attrs: { to: toJid, id: generateMessageTag(), type: "subscribe" },
461
+ content: tcToken ? [{ tag: "tctoken", attrs: {}, content: tcToken }] : undefined,
462
+ });
463
+
558
464
  const handlePresenceUpdate = ({ tag, attrs, content }) => {
559
- var _a;
560
465
  let presence;
561
- const jid = attrs.from;
466
+ const jid = attrs.from;
562
467
  const participant = attrs.participant || attrs.from;
563
- if (shouldIgnoreJid(jid) && jid != '@s.whatsapp.net') {
564
- return;
565
- }
566
- if (tag === 'presence') {
468
+ if (shouldIgnoreJid(jid) && jid !== "@s.whatsapp.net") return;
469
+
470
+ if (tag === "presence") {
567
471
  presence = {
568
- lastKnownPresence: attrs.type === 'unavailable' ? 'unavailable' : 'available',
569
- lastSeen: attrs.last && attrs.last !== 'deny' ? +attrs.last : undefined
472
+ lastKnownPresence: attrs.type === "unavailable" ? "unavailable" : "available",
473
+ lastSeen: attrs.last && attrs.last !== "deny" ? +attrs.last : undefined,
570
474
  };
571
- }
572
- else if (Array.isArray(content)) {
475
+ } else if (Array.isArray(content)) {
573
476
  const [firstChild] = content;
574
477
  let type = firstChild.tag;
575
- if (type === 'paused') {
576
- type = 'available';
577
- }
578
- if (((_a = firstChild.attrs) === null || _a === void 0 ? void 0 : _a.media) === 'audio') {
579
- type = 'recording';
580
- }
478
+ if (type === "paused") type = "available";
479
+ if (firstChild.attrs?.media === "audio") type = "recording";
581
480
  presence = { lastKnownPresence: type };
481
+ } else {
482
+ logger.error({ tag, attrs, content }, "recv invalid presence node");
582
483
  }
583
- else {
584
- logger.error({ tag, attrs, content }, 'recv invalid presence node');
585
- }
586
- if (presence) {
587
- ev.emit('presence.update', { id: jid, presences: { [participant]: presence } });
588
- }
484
+ if (presence) ev.emit("presence.update", { id: jid, presences: { [participant]: presence } });
589
485
  };
486
+
487
+ // ── App patch ─────────────────────────────────────────────────────────────
590
488
  const appPatch = async (patchCreate) => {
591
- const name = patchCreate.type;
489
+ const name = patchCreate.type;
592
490
  const myAppStateKeyId = authState.creds.myAppStateKeyId;
593
- if (!myAppStateKeyId) {
594
- throw new boom_1.Boom('App state key not present!', { statusCode: 400 });
595
- }
596
- let initial;
597
- let encodeResult;
491
+ if (!myAppStateKeyId) throw new boom_1.Boom("App state key not present!", { statusCode: 400 });
492
+
493
+ let initial, encodeResult;
598
494
  await processingMutex.mutex(async () => {
599
495
  await authState.keys.transaction(async () => {
600
- logger.debug({ patch: patchCreate }, 'applying app patch');
496
+ logger.debug({ patch: patchCreate }, "applying app patch");
601
497
  await resyncAppState([name], false);
602
- const { [name]: currentSyncVersion } = await authState.keys.get('app-state-sync-version', [name]);
603
- initial = currentSyncVersion || (0, Utils_1.newLTHashState)();
498
+ const { [name]: currentSyncVersion } = await authState.keys.get("app-state-sync-version", [name]);
499
+ initial = currentSyncVersion || (0, Utils_1.newLTHashState)();
604
500
  encodeResult = await (0, Utils_1.encodeSyncdPatch)(patchCreate, myAppStateKeyId, initial, getAppStateSyncKey);
501
+
605
502
  const { patch, state } = encodeResult;
606
- const node = {
607
- tag: 'iq',
608
- attrs: {
609
- to: WABinary_1.S_WHATSAPP_NET,
610
- type: 'set',
611
- xmlns: 'w:sync:app:state'
612
- },
613
- content: [
614
- {
615
- tag: 'sync',
616
- attrs: {},
617
- content: [
618
- {
619
- tag: 'collection',
620
- attrs: {
621
- name,
622
- version: (state.version - 1).toString(),
623
- 'return_snapshot': 'false'
624
- },
625
- content: [
626
- {
627
- tag: 'patch',
628
- attrs: {},
629
- content: WAProto_1.proto.SyncdPatch.encode(patch).finish()
630
- }
631
- ]
632
- }
633
- ]
634
- }
635
- ]
636
- };
637
- await query(node);
638
- await authState.keys.set({ 'app-state-sync-version': { [name]: state } });
503
+ await query({
504
+ tag: "iq",
505
+ attrs: { to: WABinary_1.S_WHATSAPP_NET, type: "set", xmlns: "w:sync:app:state" },
506
+ content: [{
507
+ tag: "sync",
508
+ attrs: {},
509
+ content: [{
510
+ tag: "collection",
511
+ attrs: { name, version: (state.version - 1).toString(), "return_snapshot": "false" },
512
+ content: [{
513
+ tag: "patch",
514
+ attrs: {},
515
+ content: WAProto_1.proto.SyncdPatch.encode(patch).finish(),
516
+ }],
517
+ }],
518
+ }],
519
+ });
520
+ await authState.keys.set({ "app-state-sync-version": { [name]: state } });
639
521
  });
640
522
  });
523
+
641
524
  if (config.emitOwnEvents) {
642
525
  const { onMutation } = newAppStateChunkHandler(false);
643
- const { mutationMap } = await (0, Utils_1.decodePatches)(name, [{ ...encodeResult.patch, version: { version: encodeResult.state.version }, }], initial, getAppStateSyncKey, config.options, undefined, logger);
644
- for (const key in mutationMap) {
645
- onMutation(mutationMap[key]);
646
- }
526
+ const { mutationMap } = await (0, Utils_1.decodePatches)(
527
+ name,
528
+ [{ ...encodeResult.patch, version: { version: encodeResult.state.version } }],
529
+ initial, getAppStateSyncKey, config.options, undefined, logger
530
+ );
531
+ for (const key in mutationMap) onMutation(mutationMap[key]);
647
532
  }
648
533
  };
534
+
535
+ // ── fetchProps ────────────────────────────────────────────────────────────
649
536
  const fetchProps = async () => {
650
- var _a, _b, _c;
651
537
  const resultNode = await query({
652
- tag: 'iq',
653
- attrs: {
654
- to: WABinary_1.S_WHATSAPP_NET,
655
- xmlns: 'w',
656
- type: 'get',
657
- },
658
- content: [
659
- { tag: 'props', attrs: {
660
- protocol: '2',
661
- hash: ((_a = authState === null || authState === void 0 ? void 0 : authState.creds) === null || _a === void 0 ? void 0 : _a.lastPropHash) || ''
662
- } }
663
- ]
538
+ tag: "iq",
539
+ attrs: { to: WABinary_1.S_WHATSAPP_NET, xmlns: "w", type: "get" },
540
+ content: [{
541
+ tag: "props",
542
+ attrs: { protocol: "2", hash: authState?.creds?.lastPropHash || "" },
543
+ }],
664
544
  });
665
- const propsNode = (0, WABinary_1.getBinaryNodeChild)(resultNode, 'props');
545
+ const propsNode = (0, WABinary_1.getBinaryNodeChild)(resultNode, "props");
666
546
  let props = {};
667
547
  if (propsNode) {
668
- if ((_b = propsNode.attrs) === null || _b === void 0 ? void 0 : _b.hash) {
669
- authState.creds.lastPropHash = (_c = propsNode === null || propsNode === void 0 ? void 0 : propsNode.attrs) === null || _c === void 0 ? void 0 : _c.hash;
670
- ev.emit('creds.update', authState.creds);
548
+ if (propsNode.attrs?.hash) {
549
+ authState.creds.lastPropHash = propsNode?.attrs?.hash;
550
+ ev.emit("creds.update", authState.creds);
671
551
  }
672
- props = (0, WABinary_1.reduceBinaryNodeToDictionary)(propsNode, 'prop');
552
+ props = (0, WABinary_1.reduceBinaryNodeToDictionary)(propsNode, "prop");
673
553
  }
674
- logger.debug('fetched props');
554
+ logger.debug("fetched props");
675
555
  return props;
676
556
  };
557
+
558
+ // ── chatModify ────────────────────────────────────────────────────────────
677
559
  const chatModify = (mod, jid) => {
678
560
  const patch = (0, Utils_1.chatModificationToAppPatch)(mod, jid);
679
561
  return appPatch(patch);
680
562
  };
681
- const star = (jid, messages, star) => {
682
- return chatModify({
683
- star: {
684
- messages,
685
- star
686
- }
687
- }, jid);
688
- };
689
- const addOrEditContact = (jid, contact) => {
690
- return chatModify({ contact }, jid);
691
- };
692
- const removeContact = (jid) => {
693
- return chatModify({ contact: null }, jid);
694
- };
695
- const addLabel = (jid, labels) => {
696
- return chatModify({ addLabel: { ...labels } }, jid);
697
- };
698
- const addChatLabel = (jid, labelId) => {
699
- return chatModify({
700
- addChatLabel: {
701
- labelId
702
- }
703
- }, jid);
704
- };
705
- const removeChatLabel = (jid, labelId) => {
706
- return chatModify({
707
- removeChatLabel: {
708
- labelId
709
- }
710
- }, jid);
711
- };
712
- const addMessageLabel = (jid, messageId, labelId) => {
713
- return chatModify({
714
- addMessageLabel: {
715
- messageId,
716
- labelId
717
- }
718
- }, jid);
719
- };
720
- const removeMessageLabel = (jid, messageId, labelId) => {
721
- return chatModify({
722
- removeMessageLabel: {
723
- messageId,
724
- labelId
725
- }
726
- }, jid);
727
- };
563
+
564
+ const star = (jid, messages, star) => chatModify({ star: { messages, star } }, jid);
565
+ const addOrEditContact = (jid, contact) => chatModify({ contact }, jid);
566
+ const removeContact = (jid) => chatModify({ contact: null }, jid);
567
+ const addLabel = (jid, labels) => chatModify({ addLabel: { ...labels } }, jid);
568
+ const addChatLabel = (jid, labelId) => chatModify({ addChatLabel: { labelId } }, jid);
569
+ const removeChatLabel = (jid, labelId) => chatModify({ removeChatLabel: { labelId } }, jid);
570
+ const addMessageLabel = (jid, messageId, labelId) => chatModify({ addMessageLabel: { messageId, labelId } }, jid);
571
+ const removeMessageLabel = (jid, messageId, labelId) => chatModify({ removeMessageLabel: { messageId, labelId } }, jid);
572
+
573
+ // ── Init queries ──────────────────────────────────────────────────────────
728
574
  const executeInitQueries = async () => {
729
- await Promise.all([
730
- fetchProps(),
731
- fetchBlocklist(),
732
- fetchPrivacySettings(),
733
- ]);
575
+ await Promise.all([fetchProps(), fetchBlocklist(), fetchPrivacySettings()]);
734
576
  };
577
+
578
+ // ── JID utils ─────────────────────────────────────────────────────────────
735
579
  const canonicalizeUserJid = (jid) => {
736
- if (!jid) {
737
- return jid;
738
- }
739
- if ((0, WABinary_1.isLidUser)(jid)) {
740
- return (0, WABinary_1.lidToJid)(jid);
741
- }
580
+ if (!jid) return jid;
581
+ if ((0, WABinary_1.isLidUser)(jid)) return (0, WABinary_1.lidToJid)(jid);
742
582
  return jid;
743
583
  };
584
+
744
585
  const extractStanzaId = (msg) => {
745
- var _a, _b;
746
- const topLevelStanzaId = (_a = msg.messageContextInfo) === null || _a === void 0 ? void 0 : _a.stanzaId;
747
- if (topLevelStanzaId) {
748
- return topLevelStanzaId;
749
- }
750
- const messageContextStanzaId = (_b = msg.message) === null || _b === void 0 ? void 0 : _b.messageContextInfo;
751
- if (messageContextStanzaId === null || messageContextStanzaId === void 0 ? void 0 : messageContextStanzaId.stanzaId) {
752
- return messageContextStanzaId.stanzaId;
753
- }
754
- const normalizedContent = (0, Utils_1.normalizeMessageContent)(msg.message);
755
- const contentType = normalizedContent ? (0, Utils_1.getContentType)(normalizedContent) : undefined;
756
- const contentContextStanzaId = contentType
757
- ? normalizedContent[contentType].contextInfo && normalizedContent[contentType].contextInfo.stanzaId
586
+ const topLevel = msg.messageContextInfo?.stanzaId;
587
+ if (topLevel) return topLevel;
588
+ const ctxStanza = msg.message?.messageContextInfo?.stanzaId;
589
+ if (ctxStanza) return ctxStanza;
590
+ const normalized = (0, Utils_1.normalizeMessageContent)(msg.message);
591
+ const contentType = normalized ? (0, Utils_1.getContentType)(normalized) : undefined;
592
+ return contentType
593
+ ? normalized[contentType]?.contextInfo?.stanzaId
758
594
  : undefined;
759
- return contentContextStanzaId;
760
595
  };
596
+
761
597
  const buildMessageDedupKeys = (msg) => {
762
- const remoteJid = canonicalizeUserJid(msg.key.remoteJid);
598
+ const remoteJid = canonicalizeUserJid(msg.key.remoteJid);
763
599
  const participant = canonicalizeUserJid(msg.key.participant);
764
- const stanzaId = extractStanzaId(msg);
765
- const primaryKey = [remoteJid, participant, msg.key.id, msg.key.fromMe ? '1' : '0', stanzaId || ''].join('|');
766
- const keys = [primaryKey];
767
- // fallback alias key to collapse LID->PN duplicate deliveries for the same incoming user message ID
768
- if (!msg.key.fromMe && ((0, WABinary_1.isJidUser)(msg.key.remoteJid) || (0, WABinary_1.isLidUser)(msg.key.remoteJid))) {
769
- keys.push(`incoming-user-id|${msg.key.id}|${stanzaId || ''}`);
600
+ const stanzaId = extractStanzaId(msg);
601
+ const primaryKey = [remoteJid, participant, msg.key.id, msg.key.fromMe ? "1" : "0", stanzaId || ""].join("|");
602
+ const keys = [primaryKey];
603
+ if (!msg.key.fromMe && (
604
+ (0, WABinary_1.isJidUser)(msg.key.remoteJid) ||
605
+ (0, WABinary_1.isLidUser)(msg.key.remoteJid)
606
+ )) {
607
+ keys.push(`incoming-user-id|${msg.key.id}|${stanzaId || ""}`);
770
608
  }
771
609
  return keys;
772
610
  };
773
- const getDedupState = (msg) => msg.messageStubType === WAProto_1.proto.WebMessageInfo.StubType.CIPHERTEXT
774
- ? 'ciphertext'
775
- : 'final';
611
+
612
+ const getDedupState = (msg) =>
613
+ msg.messageStubType === WAProto_1.proto.WebMessageInfo.StubType.CIPHERTEXT
614
+ ? "ciphertext"
615
+ : "final";
616
+
617
+ // ── upsertMessage — FIXED: stable dedup + sync state ─────────────────────
776
618
  const upsertMessage = ev.createBufferedFunction(async (msg, type) => {
777
- var _a, _b, _c;
778
- const dedupKeys = buildMessageDedupKeys(msg);
779
- const dedupState = getDedupState(msg);
619
+ const dedupKeys = buildMessageDedupKeys(msg);
620
+ const dedupState = getDedupState(msg);
780
621
  const previousDedupState = dedupKeys
781
- .map(key => receivedMessageDedupCache.get(key))
782
- .find(state => !!state);
783
- if (previousDedupState === 'final' || (previousDedupState === 'ciphertext' && dedupState === 'ciphertext')) {
784
- logger.debug({ dedupKeys, messageId: msg.key.id, type, dedupState, previousDedupState }, 'skipping duplicate messages.upsert');
622
+ .map(key => { try { return receivedMessageDedupCache.get(key); } catch { return undefined; } })
623
+ .find(s => !!s);
624
+
625
+ if (
626
+ previousDedupState === "final" ||
627
+ (previousDedupState === "ciphertext" && dedupState === "ciphertext")
628
+ ) {
629
+ logger.debug({ dedupKeys, messageId: msg.key.id, type, dedupState, previousDedupState },
630
+ "skipping duplicate messages.upsert");
785
631
  return;
786
632
  }
787
- ev.emit('messages.upsert', { messages: [msg], type });
788
- if (!!msg.pushName) {
789
- let jid = msg.key.fromMe ? authState.creds.me.id : (msg.key.participant || msg.key.remoteJid);
633
+
634
+ ev.emit("messages.upsert", { messages: [msg], type });
635
+
636
+ if (msg.pushName) {
637
+ let jid = msg.key.fromMe
638
+ ? authState.creds.me.id
639
+ : (msg.key.participant || msg.key.remoteJid);
790
640
  jid = (0, WABinary_1.jidNormalizedUser)(jid);
791
641
  if (!msg.key.fromMe) {
792
- ev.emit('contacts.update', [{ id: jid, notify: msg.pushName, verifiedName: msg.verifiedBizName }]);
642
+ ev.emit("contacts.update", [{ id: jid, notify: msg.pushName, verifiedName: msg.verifiedBizName }]);
793
643
  }
794
- if (msg.key.fromMe && msg.pushName && ((_a = authState.creds.me) === null || _a === void 0 ? void 0 : _a.name) !== msg.pushName) {
795
- ev.emit('creds.update', { me: { ...authState.creds.me, name: msg.pushName } });
644
+ if (msg.key.fromMe && msg.pushName && authState.creds.me?.name !== msg.pushName) {
645
+ ev.emit("creds.update", { me: { ...authState.creds.me, name: msg.pushName } });
796
646
  }
797
647
  }
648
+
798
649
  const historyMsg = (0, Utils_1.getHistoryMsg)(msg.message);
799
650
  const shouldProcessHistoryMsg = historyMsg
800
- ? (shouldSyncHistoryMessage(historyMsg)
801
- && Defaults_1.PROCESSABLE_HISTORY_TYPES.includes(historyMsg.syncType))
651
+ ? shouldSyncHistoryMessage(historyMsg) && Defaults_1.PROCESSABLE_HISTORY_TYPES.includes(historyMsg.syncType)
802
652
  : false;
653
+
803
654
  if (historyMsg && syncState === SyncState.AwaitingInitialSync) {
804
- if (awaitingSyncTimeout) {
805
- clearTimeout(awaitingSyncTimeout);
806
- awaitingSyncTimeout = undefined;
807
- }
655
+ if (awaitingSyncTimeout) { clearTimeout(awaitingSyncTimeout); awaitingSyncTimeout = undefined; }
808
656
  if (shouldProcessHistoryMsg) {
809
657
  syncState = SyncState.Syncing;
810
- logger.info('Transitioned to Syncing state');
658
+ logger.info("Transitioned to Syncing state");
811
659
  } else {
812
660
  syncState = SyncState.Online;
813
- logger.info('History sync skipped, transitioning to Online state and flushing buffer');
661
+ logger.info("History sync skipped, transitioning to Online");
814
662
  ev.flush();
815
663
  }
816
664
  }
665
+
817
666
  const doAppStateSync = async () => {
818
667
  if (syncState === SyncState.Syncing) {
819
- logger.info('Doing app state sync');
668
+ logger.info("Doing app state sync");
820
669
  await resyncAppState(Types_1.ALL_WA_PATCH_NAMES, true);
821
670
  syncState = SyncState.Online;
822
- logger.info('App state sync complete, transitioning to Online state and flushing buffer');
671
+ logger.info("App state sync complete, transitioning to Online");
823
672
  ev.flush();
824
673
  const accountSyncCounter = (authState.creds.accountSyncCounter || 0) + 1;
825
- ev.emit('creds.update', { accountSyncCounter });
674
+ ev.emit("creds.update", { accountSyncCounter });
826
675
  }
827
676
  };
677
+
828
678
  if (historyMsg && !authState.creds.myAppStateKeyId) {
829
- logger.warn('skipping app state sync, as myAppStateKeyId is not set');
679
+ logger.warn("skipping app state sync, myAppStateKeyId not set");
830
680
  pendingAppStateSync = true;
831
681
  }
682
+
832
683
  await Promise.all([
833
684
  (async () => {
834
- if (historyMsg
835
- && authState.creds.myAppStateKeyId) {
685
+ if (historyMsg && authState.creds.myAppStateKeyId) {
836
686
  pendingAppStateSync = false;
837
687
  await doAppStateSync();
838
688
  }
@@ -842,89 +692,93 @@ const makeChatsSocket = (config) => {
842
692
  shouldProcessHistoryMsg,
843
693
  placeholderResendCache,
844
694
  ev,
845
- creds: authState.creds,
846
- keyStore: authState.keys,
695
+ creds: authState.creds,
696
+ keyStore: authState.keys,
847
697
  logger,
848
- options: config.options,
698
+ options: config.options,
849
699
  getMessage: config.getMessage,
850
- })
700
+ }),
851
701
  ]);
852
- if (((_c = (_b = msg.message) === null || _b === void 0 ? void 0 : _b.protocolMessage) === null || _c === void 0 ? void 0 : _c.appStateSyncKeyShare)
853
- && pendingAppStateSync) {
702
+
703
+ if (msg.message?.protocolMessage?.appStateSyncKeyShare && pendingAppStateSync) {
854
704
  await doAppStateSync();
855
705
  pendingAppStateSync = false;
856
706
  }
707
+
857
708
  for (const key of dedupKeys) {
858
- receivedMessageDedupCache.set(key, dedupState);
709
+ try { receivedMessageDedupCache.set(key, dedupState); } catch {}
859
710
  }
860
711
  });
861
- ws.on('CB:presence', handlePresenceUpdate);
862
- ws.on('CB:chatstate', handlePresenceUpdate);
863
- ws.on('CB:ib,,dirty', async (node) => {
864
- const { attrs } = (0, WABinary_1.getBinaryNodeChild)(node, 'dirty');
712
+
713
+ // ── WS event handlers ─────────────────────────────────────────────────────
714
+ ws.on("CB:presence", handlePresenceUpdate);
715
+ ws.on("CB:chatstate", handlePresenceUpdate);
716
+
717
+ ws.on("CB:ib,,dirty", async (node) => {
718
+ const { attrs } = (0, WABinary_1.getBinaryNodeChild)(node, "dirty");
865
719
  const type = attrs.type;
866
720
  switch (type) {
867
- case 'account_sync':
721
+ case "account_sync":
868
722
  if (attrs.timestamp) {
869
723
  let { lastAccountSyncTimestamp } = authState.creds;
870
724
  if (lastAccountSyncTimestamp) {
871
- await cleanDirtyBits('account_sync', lastAccountSyncTimestamp);
725
+ await cleanDirtyBits("account_sync", lastAccountSyncTimestamp);
872
726
  }
873
727
  lastAccountSyncTimestamp = +attrs.timestamp;
874
- ev.emit('creds.update', { lastAccountSyncTimestamp });
728
+ ev.emit("creds.update", { lastAccountSyncTimestamp });
875
729
  }
876
730
  break;
877
- case 'groups':
731
+ case "groups":
878
732
  break;
879
733
  default:
880
- logger.info({ node }, 'received unknown sync');
881
- break;
734
+ logger.info({ node }, "received unknown sync");
882
735
  }
883
736
  });
884
- ev.on('connection.update', ({ connection, receivedPendingNotifications }) => {
885
- var _a;
886
- if (connection === 'open') {
737
+
738
+ ev.on("connection.update", ({ connection, receivedPendingNotifications }) => {
739
+ if (connection === "open") {
887
740
  if (fireInitQueries) {
888
- executeInitQueries()
889
- .catch(error => onUnexpectedError(error, 'init queries'));
741
+ executeInitQueries().catch(error => onUnexpectedError(error, "init queries"));
890
742
  }
891
- sendPresenceUpdate(markOnlineOnConnect ? 'available' : 'unavailable')
892
- .catch(error => onUnexpectedError(error, 'presence update requests'));
743
+ sendPresenceUpdate(markOnlineOnConnect ? "available" : "unavailable")
744
+ .catch(error => onUnexpectedError(error, "presence update"));
893
745
  }
894
- if (receivedPendingNotifications &&
895
- !((_a = authState.creds) === null || _a === void 0 ? void 0 : _a.myAppStateKeyId)) {
746
+
747
+ if (receivedPendingNotifications && !authState.creds?.myAppStateKeyId) {
896
748
  ev.buffer();
897
749
  needToFlushWithAppStateSync = true;
898
750
  }
899
- if (!receivedPendingNotifications || syncState !== SyncState.Connecting) {
900
- return;
901
- }
751
+
752
+ if (!receivedPendingNotifications || syncState !== SyncState.Connecting) return;
753
+
902
754
  syncState = SyncState.AwaitingInitialSync;
903
- logger.info('Connection is now AwaitingInitialSync, buffering events');
755
+ logger.info("Connection is now AwaitingInitialSync, buffering events");
904
756
  ev.buffer();
757
+
905
758
  const willSyncHistory = shouldSyncHistoryMessage(
906
759
  WAProto_1.proto.Message.HistorySyncNotification.create({
907
- syncType: WAProto_1.proto.HistorySync.HistorySyncType.RECENT
760
+ syncType: WAProto_1.proto.HistorySync.HistorySyncType.RECENT,
908
761
  })
909
762
  );
763
+
910
764
  if (!willSyncHistory) {
911
- logger.info('History sync is disabled by config, not waiting for notification. Transitioning to Online.');
765
+ logger.info("History sync disabled by config, transitioning to Online.");
912
766
  syncState = SyncState.Online;
913
767
  setTimeout(() => ev.flush(), 0);
914
768
  return;
915
769
  }
916
- logger.info('History sync is enabled, awaiting notification with a 20s timeout.');
917
- if (awaitingSyncTimeout) {
918
- clearTimeout(awaitingSyncTimeout);
919
- }
770
+
771
+ logger.info("History sync enabled, awaiting notification with 20s timeout.");
772
+ if (awaitingSyncTimeout) clearTimeout(awaitingSyncTimeout);
920
773
  awaitingSyncTimeout = setTimeout(() => {
921
774
  if (syncState === SyncState.AwaitingInitialSync) {
922
- logger.warn('Timeout in AwaitingInitialSync, forcing state to Online and flushing buffer');
775
+ logger.warn("Timeout in AwaitingInitialSync, forcing to Online and flushing");
923
776
  syncState = SyncState.Online;
924
777
  ev.flush();
925
778
  }
926
779
  }, 20000);
927
780
  });
781
+
928
782
  return {
929
783
  ...sock,
930
784
  createCallLink,
@@ -966,7 +820,8 @@ const makeChatsSocket = (config) => {
966
820
  removeChatLabel,
967
821
  addMessageLabel,
968
822
  removeMessageLabel,
969
- star
823
+ star,
970
824
  };
971
825
  };
972
- exports.makeChatsSocket = makeChatsSocket;
826
+
827
+ exports.makeChatsSocket = makeChatsSocket;