nodejs-insta-private-api-mqtt 1.1.5 → 1.1.7

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.
package/README.md CHANGED
@@ -902,7 +902,7 @@ const {
902
902
  RealtimeClient,
903
903
  downloadContentFromMessage,
904
904
  isViewOnceMedia
905
- } = require('nodejs-insta-private-api-mqtt');
905
+ } = require('nodejs-insta-private-api');
906
906
  const fs = require('fs');
907
907
 
908
908
  const ig = new IgApiClient();
@@ -955,7 +955,7 @@ const {
955
955
  downloadMediaBuffer,
956
956
  hasMedia,
957
957
  getMediaType
958
- } = require('nodejs-insta-private-api');
958
+ } = require('nodejs-insta-private-api-mqtt');
959
959
 
960
960
  realtime.on('message', async (data) => {
961
961
  const msg = data.message;
@@ -995,7 +995,7 @@ realtime.on('message', async (data) => {
995
995
  ### Extract Media URLs Without Downloading
996
996
 
997
997
  ```javascript
998
- const { extractMediaUrls } = require('nodejs-insta-private-api');
998
+ const { extractMediaUrls } = require('nodejs-insta-private-api-mqtt');
999
999
 
1000
1000
  realtime.on('message', async (data) => {
1001
1001
  const msg = data.message;
@@ -17,9 +17,6 @@ const error_handler_1 = require("./features/error-handler");
17
17
  const gap_handler_1 = require("./features/gap-handler");
18
18
  const enhanced_direct_commands_1 = require("./commands/enhanced.direct.commands");
19
19
  const presence_typing_mixin_1 = require("./mixins/presence-typing.mixin");
20
- const fs = require('fs');
21
- const path = require('path');
22
-
23
20
  class RealtimeClient extends eventemitter3_1.EventEmitter {
24
21
  get mqtt() {
25
22
  return this._mqtt;
@@ -38,20 +35,7 @@ class RealtimeClient extends eventemitter3_1.EventEmitter {
38
35
  this.emitWarning = (e) => this.emit('warning', e);
39
36
  this.ig = ig;
40
37
  this.threads = new Map();
41
-
42
- // === New: observability & metrics ===
43
- this.metrics = {
44
- reconnectCount: 0,
45
- messagesReceived: 0,
46
- parseErrors: 0,
47
- lastConnectTime: null,
48
- backoffAttempts: 0,
49
- };
50
-
51
- // Session state persistence file (changeable)
52
- this._sessionStateFile = path.join('.', '.mqtt_session_state.json');
53
- this._sessionState = this._loadSessionState();
54
-
38
+
55
39
  this.irisHandshake = new iris_handshake_1.IrisHandshake(this);
56
40
  this.skywalkerProtocol = new skywalker_protocol_1.SkywalkerProtocol(this);
57
41
  this.presenceManager = new presence_manager_1.PresenceManager(this);
@@ -59,52 +43,21 @@ class RealtimeClient extends eventemitter3_1.EventEmitter {
59
43
  this.errorHandler = new error_handler_1.ErrorHandler(this);
60
44
  this.gapHandler = new gap_handler_1.GapHandler(this);
61
45
  this.directCommands = new enhanced_direct_commands_1.EnhancedDirectCommands(this);
62
-
46
+
63
47
  this.realtimeDebug(`Applying mixins: ${mixins.map(m => m.name).join(', ')}`);
64
48
  (0, mixins_1.applyMixins)(mixins, this, this.ig);
65
-
66
- // Periodic metrics logging (only if not disabled)
67
- this._metricsInterval = setInterval(() => {
68
- try {
69
- if (this.metrics) {
70
- this.realtimeDebug('METRICS', JSON.stringify(this.metrics));
71
- }
72
- } catch (e) { }
73
- }, 60 * 1000); // every 60s
74
- }
75
-
76
- // -------------------- Session state persistence --------------------
77
- _loadSessionState() {
78
- try {
79
- if (fs.existsSync(this._sessionStateFile)) {
80
- const raw = fs.readFileSync(this._sessionStateFile, 'utf8');
81
- return JSON.parse(raw || '{}');
82
- }
83
- } catch (e) {
84
- // ignore
85
- }
86
- return {};
87
- }
88
- _saveSessionState() {
89
- try {
90
- fs.writeFileSync(this._sessionStateFile, JSON.stringify(this._sessionState || {}, null, 2), 'utf8');
91
- } catch (e) {
92
- this.realtimeDebug('Failed to save session state:', e?.message || e);
93
- }
94
49
  }
95
50
 
96
- // ------------------------------------------------------------------
97
-
98
51
  /**
99
52
  * Start Real-Time Listener with Auto-Inbox Fetch + MQTT
100
53
  */
101
54
  async startRealTimeListener(options = {}) {
102
55
  try {
103
56
  console.log('[REALTIME] Starting Real-Time Listener...');
104
-
57
+
105
58
  console.log('[REALTIME] Fetching inbox (IRIS data)...');
106
59
  const inboxData = await this.ig.direct.getInbox();
107
-
60
+
108
61
  console.log('[REALTIME] Connecting to MQTT with IRIS subscription...');
109
62
  await this.connect({
110
63
  graphQlSubs: [
@@ -117,15 +70,15 @@ class RealtimeClient extends eventemitter3_1.EventEmitter {
117
70
  ],
118
71
  irisData: inboxData
119
72
  });
120
-
73
+
121
74
  console.log('[REALTIME] MQTT Connected with IRIS');
122
75
  console.log('----------------------------------------');
123
76
  console.log('[REALTIME] Real-Time Listener ACTIVE');
124
77
  console.log('[REALTIME] Waiting for messages...');
125
78
  console.log('----------------------------------------');
126
-
79
+
127
80
  this._setupMessageHandlers();
128
-
81
+
129
82
  return { success: true };
130
83
  } catch (error) {
131
84
  console.error('[REALTIME] Failed:', error.message);
@@ -137,92 +90,46 @@ class RealtimeClient extends eventemitter3_1.EventEmitter {
137
90
  * Setup automatic message handlers
138
91
  */
139
92
  _setupMessageHandlers() {
140
- // handle raw mqtt 'message' events (parsed by this._parseMessage)
141
93
  this.on('message', (data) => {
142
- const parsed = this._parseMessage(data);
143
- // emit raw version always for debugging/compatibility
144
- if (parsed && parsed._rawMessage) {
145
- this.emit('message_raw', parsed._rawMessage);
146
- }
147
- // normalize and emit only if it's a real message
148
- const normalized = this._normalizeToYowsupLike(parsed);
149
- if (normalized) {
150
- this.metrics.messagesReceived++;
151
- // emit cleaned payload as the primary "live" message (Yowsup-like)
152
- this.emit('message_live', normalized);
153
- } else {
154
- // not a real message -> no message_live; keep raw available
94
+ const msg = this._parseMessage(data);
95
+ if (msg) {
96
+ this.emit('message_live', msg);
155
97
  }
156
98
  });
157
99
 
158
- // handle iris events (graph/iris)
159
100
  this.on('iris', (data) => {
160
- const parsed = this._parseIrisMessage(data);
161
- if (parsed && parsed._rawIris) {
162
- this.emit('message_raw', parsed._rawIris);
163
- }
164
- const normalized = this._normalizeToYowsupLike(parsed);
165
- if (normalized) {
166
- this.metrics.messagesReceived++;
167
- this.emit('message_live', normalized);
168
- } else {
169
- // not a message event
101
+ const msg = this._parseIrisMessage(data);
102
+ if (msg) {
103
+ this.emit('message_live', msg);
170
104
  }
171
105
  });
172
106
  }
173
107
 
174
108
  /**
175
109
  * Parse direct message
176
- * Returns an object that always contains the original raw as _rawMessage when available
177
110
  */
178
111
  _parseMessage(data) {
179
112
  try {
180
113
  const msg = data.message;
181
114
  if (!msg) return null;
182
-
115
+
183
116
  if (data.parsed) {
184
- // Keep mqttString/mqtt for backwards compatibility in parsed cases
185
- const base = Object.assign({}, data.parsed);
186
- base.mqttString = `Mqtt client: [${base.username || (base.userId ? 'user_' + base.userId : 'unknown')}] ${base.text || ''}`;
187
- base.mqtt = {
188
- from: base.username || base.userId,
189
- body: base.text || '',
190
- timestamp: base.timestamp || Date.now()
191
- };
192
- base.status = 'received'; // changed from 'good'
193
- base._rawMessage = msg;
194
- return base;
117
+ return data.parsed;
195
118
  }
196
-
119
+
197
120
  const threadInfo = this.threads.get(msg.thread_id);
198
- const username = msg.username || msg.from_username || `user_${msg.user_id || 'unknown'}`;
199
- const text = msg.text || msg.body || '';
200
-
201
- // Provide mqtt-like representations
202
- const mqttString = `Mqtt client: [${username}] ${text}`;
203
- const mqtt = {
204
- from: username,
205
- body: text,
206
- timestamp: msg.timestamp || Date.now()
207
- };
208
-
209
- const out = {
121
+ return {
210
122
  id: msg.item_id || msg.id,
211
123
  userId: msg.user_id || msg.from_user_id,
212
- username,
213
- text,
124
+ username: msg.username || msg.from_username || `user_${msg.user_id || 'unknown'}`,
125
+ text: msg.text || msg.body || '',
214
126
  itemType: msg.item_type || 'text',
215
127
  thread: threadInfo?.title || `Thread ${msg.thread_id}`,
216
128
  thread_id: msg.thread_id,
217
129
  timestamp: msg.timestamp,
218
130
  isGroup: threadInfo?.isGroup,
219
- status: 'received', // replaced 'good'
220
- mqttString,
221
- mqtt,
222
- _rawMessage: msg
131
+ status: 'good'
223
132
  };
224
-
225
- return out;
226
133
  } catch (e) {
227
134
  return null;
228
135
  }
@@ -234,125 +141,22 @@ class RealtimeClient extends eventemitter3_1.EventEmitter {
234
141
  _parseIrisMessage(data) {
235
142
  try {
236
143
  if (data.event !== 'message_create' && !data.type?.includes('message')) {
237
- // still return raw for potential debug, but normalized filter will drop it
238
- return { _rawIris: data };
144
+ return null;
239
145
  }
240
-
241
- const username = data.username || data.from_username || `user_${data.user_id || 'unknown'}`;
242
- const text = data.text || '';
243
-
244
- const mqttString = `Mqtt client: [${username}] ${text}`;
245
- const mqtt = {
246
- from: username,
247
- body: text,
248
- timestamp: data.timestamp || Date.now()
249
- };
250
-
251
- const out = {
146
+
147
+ return {
252
148
  id: data.item_id || data.id,
253
149
  userId: data.user_id || data.from_user_id,
254
- username,
255
- text,
150
+ username: data.username || data.from_username || `user_${data.user_id || 'unknown'}`,
151
+ text: data.text || '',
256
152
  itemType: data.item_type || 'text',
257
153
  thread_id: data.thread_id,
258
154
  timestamp: data.timestamp,
259
- status: 'received', // replaced 'good'
260
- mqttString,
261
- mqtt,
262
- _rawIris: data
155
+ status: 'good'
263
156
  };
264
-
265
- return out;
266
157
  } catch (e) {
267
- return { _rawIris: data };
268
- }
269
- }
270
-
271
- /**
272
- * NORMALIZE to Yowsup-like full payload (but branded "Mqtt client")
273
- * Returns normalized object or null if the parsed input is NOT a real message
274
- *
275
- * Normalized schema (Yowsup-like full payload):
276
- * {
277
- * client: 'Mqtt client',
278
- * from: 'username',
279
- * fromId: 'numeric id',
280
- * body: 'text or description',
281
- * timestamp: 1700000000000,
282
- * type: 'text'|'image'|'video'|'voice'|'sticker'|'unknown',
283
- * thread_id: '...',
284
- * id: 'message id',
285
- * raw: { original raw object }
286
- * }
287
- */
288
- _normalizeToYowsupLike(parsed) {
289
- if (!parsed) return null;
290
-
291
- // get raw object for detection
292
- const raw = parsed._rawMessage || parsed._rawIris || parsed;
293
-
294
- // Heuristics to decide if this is a "real message" (text/media/voice)
295
- // Accept if:
296
- // - parsed.text or parsed.body present and non-empty
297
- // - or raw contains visual_media/media fields
298
- // - or itemType indicates media
299
- const hasText = Boolean(parsed.text && String(parsed.text).trim().length > 0);
300
- const hasBodyField = Boolean(parsed.body && String(parsed.body).trim().length > 0);
301
- const hasMediaField = Boolean(
302
- (raw && (raw.visual_media || raw.media || raw.media_share || raw.items || raw.has_media)) ||
303
- (parsed.itemType && String(parsed.itemType).toLowerCase() !== 'text')
304
- );
305
-
306
- if (!hasText && !hasBodyField && !hasMediaField) {
307
- // Not a message that Yowsup would surface — drop from message_live
308
158
  return null;
309
159
  }
310
-
311
- // Determine type
312
- let type = 'text';
313
- try {
314
- if (hasMediaField) {
315
- // try to deduce image/video/voice from raw fields if possible
316
- const rl = raw || {};
317
- if (rl.item_type && String(rl.item_type).toLowerCase().includes('video')) type = 'video';
318
- else if (rl.item_type && String(rl.item_type).toLowerCase().includes('voice')) type = 'voice';
319
- else if (rl.media && Array.isArray(rl.media)) {
320
- // naive check for media types in array
321
- const m0 = rl.media[0] || {};
322
- if (m0.media_type === 2 || String(m0.media_type) === '2') type = 'video';
323
- else type = 'image';
324
- } else if (rl.visual_media) {
325
- type = 'image';
326
- } else {
327
- type = 'unknown';
328
- }
329
- } else {
330
- type = 'text';
331
- }
332
- } catch (e) {
333
- type = 'unknown';
334
- }
335
-
336
- // body selection
337
- const body = hasText ? String(parsed.text).trim() : (hasBodyField ? String(parsed.body).trim() : (type !== 'text' ? `[${type.toUpperCase()}]` : ''));
338
-
339
- // timestamp fallback
340
- const ts = parsed.timestamp ? Number(parsed.timestamp) : (Date.now());
341
-
342
- // Build normalized object (Yowsup-like full payload)
343
- const normalized = {
344
- client: 'Mqtt client',
345
- from: parsed.username || parsed.from || (parsed.userId ? `user_${parsed.userId}` : 'unknown'),
346
- fromId: String(parsed.userId || (raw && (raw.from_user_id || raw.user_id)) || 'unknown'),
347
- body: body,
348
- timestamp: ts,
349
- type,
350
- thread_id: parsed.thread_id || parsed.thread || (raw && (raw.thread_id || raw.thread)) || null,
351
- id: parsed.id || (raw && (raw.item_id || raw.id)) || null,
352
- raw: raw
353
- };
354
-
355
- return normalized;
356
160
  }
357
161
 
358
162
  setInitOptions(initOptions) {
@@ -426,11 +230,6 @@ class RealtimeClient extends eventemitter3_1.EventEmitter {
426
230
  this.realtimeDebug(`SessionID generated (fallback): ${sessionid}`);
427
231
  }
428
232
  const password = `sessionid=${sessionid}`;
429
-
430
- // Use persisted clientMqttSessionId if present (session continuity)
431
- const persistedClientMqttSessionId = this._sessionState?.clientMqttSessionId ? BigInt(this._sessionState.clientMqttSessionId) : null;
432
- const clientMqttSessionIdValue = persistedClientMqttSessionId ?? (BigInt(Date.now()) & BigInt(0xffffffff));
433
-
434
233
  this.connection = new mqttot_1.MQTToTConnection({
435
234
  clientIdentifier: deviceId.substring(0, 20),
436
235
  clientInfo: {
@@ -445,7 +244,7 @@ class RealtimeClient extends eventemitter3_1.EventEmitter {
445
244
  isInitiallyForeground: true,
446
245
  networkType: 1,
447
246
  networkSubtype: 0,
448
- clientMqttSessionId: clientMqttSessionIdValue,
247
+ clientMqttSessionId: BigInt(Date.now()) & BigInt(0xffffffff),
449
248
  subscribeTopics: [88, 135, 149, 150, 133, 146],
450
249
  clientType: 'cookie_auth',
451
250
  appId: BigInt(567067343352427),
@@ -467,123 +266,50 @@ class RealtimeClient extends eventemitter3_1.EventEmitter {
467
266
  'Accept-Language': this.ig.state.language.replace('_', '-'),
468
267
  },
469
268
  });
470
-
471
- // update persisted state with chosen session id
472
- try {
473
- this._sessionState = this._sessionState || {};
474
- this._sessionState.clientMqttSessionId = String(clientMqttSessionIdValue);
475
- this._saveSessionState();
476
- } catch (e) {
477
- // ignore
478
- }
479
269
  }
480
270
  async connect(options) {
481
271
  this.setInitOptions(options);
482
272
  this.constructConnection();
483
273
  const { MQTToTClient } = require("../mqttot");
484
274
  const { compressDeflate } = require("../shared");
485
-
486
- // ----------- Enhanced connect with exponential backoff & cooldown -----------
487
- const maxAttempts = (this.initOptions?.maxConnectAttempts) || 5;
488
- let attempt = 0;
489
- let lastError = null;
490
- while (attempt < maxAttempts) {
491
- attempt++;
492
- try {
493
- this.realtimeDebug(`Attempting MQTT connect (attempt ${attempt}/${maxAttempts})`);
494
- this._mqtt = new MQTToTClient({
495
- url: 'edge-mqtt.facebook.com',
496
- payloadProvider: async () => {
497
- return await compressDeflate(this.connection.toThrift());
498
- },
499
- autoReconnect: true,
500
- requirePayload: true,
501
- // pass through any socks/proxy options if provided
502
- socksOptions: this.initOptions?.socksOptions,
503
- additionalOptions: this.initOptions?.additionalOptions
504
- });
505
-
506
- await this._mqtt.connect();
507
-
508
- // mark metrics
509
- this.metrics.reconnectCount++;
510
- this.metrics.lastConnectTime = Date.now();
511
- this.metrics.backoffAttempts += (attempt - 1);
512
-
513
- // If we reach here, connected successfully
514
- break;
515
- } catch (err) {
516
- lastError = err;
517
- this.realtimeDebug(`MQTT connect attempt ${attempt} failed: ${err?.message || err}`);
518
-
519
- // exponential backoff with jitter
520
- const base = 1000; // 1s
521
- const delayMs = Math.min(30000, base * Math.pow(2, attempt - 1)) + Math.floor(Math.random() * 250);
522
- this.realtimeDebug(`Waiting ${delayMs}ms before next connect attempt...`);
523
- await (0, shared_1.delay)(delayMs);
524
-
525
- // cooldown behavior: if too many attempts, wait longer
526
- if (attempt >= maxAttempts) {
527
- this.realtimeDebug('Max connect attempts reached, giving up for now.');
528
- }
529
- }
530
- }
531
- if (!this._mqtt) {
532
- // fail with the last error
533
- throw lastError || new Error('Failed to create MQTT client');
534
- }
535
- // ----------------------------------------------------------------------------
536
-
275
+
276
+ this._mqtt = new MQTToTClient({
277
+ url: 'edge-mqtt.facebook.com',
278
+ payloadProvider: async () => {
279
+ return await compressDeflate(this.connection.toThrift());
280
+ },
281
+ autoReconnect: true,
282
+ requirePayload: true,
283
+ });
284
+
285
+ await this._mqtt.connect();
286
+
537
287
  this.commands = new commands_1.Commands(this._mqtt);
538
-
288
+
539
289
  this.emit('connected');
540
-
541
- // Enhanced message handling: try tolerant parse, log unknown fields, fallback raw
290
+
542
291
  this._mqtt.on('message', async (msg) => {
543
292
  const topicMap = this.mqtt?.topicMap;
544
293
  const topic = topicMap?.get(msg.topic);
545
-
294
+
546
295
  if (topic && topic.parser && !topic.noParse) {
547
296
  try {
548
297
  const unzipped = await (0, shared_1.tryUnzipAsync)(msg.payload);
549
- let parsedMessages;
550
- try {
551
- parsedMessages = topic.parser.parseMessage(topic, unzipped);
552
- } catch (parseErr) {
553
- // Tolerant parsing: log and attempt safe fallback
554
- this.metrics.parseErrors++;
555
- this.realtimeDebug('Parse error (topic parser) - attempting tolerant fallback:', parseErr?.message || parseErr);
556
-
557
- // Attempt to decode as JSON if possible (some thrift->json conversions may be present in tooling)
558
- try {
559
- const s = unzipped.toString('utf8');
560
- parsedMessages = JSON.parse(s);
561
- this.realtimeDebug('Fallback parsed as JSON.');
562
- } catch (jsonErr) {
563
- // Last resort: emit raw payload (preserve for debugging)
564
- this.realtimeDebug('Fallback JSON parse failed; emitting raw payload.');
565
- this.emit('receiveRaw', { topic: msg.topic, payload: unzipped, original: msg });
566
- return;
567
- }
568
- }
569
-
298
+ const parsedMessages = topic.parser.parseMessage(topic, unzipped);
570
299
  this.emit('receive', topic, Array.isArray(parsedMessages) ? parsedMessages : [parsedMessages]);
571
- } catch (e) {
572
- // Silent parse error but count & log minimal info
573
- this.metrics.parseErrors++;
574
- this.realtimeDebug('Silent parse/unzip error in message handler:', e?.message || e);
300
+ } catch(e) {
301
+ // Silent parse error
575
302
  }
576
303
  } else {
577
304
  try {
578
305
  await (0, shared_1.tryUnzipAsync)(msg.payload);
579
306
  this.emit('receiveRaw', msg);
580
- } catch (e) {
307
+ } catch(e) {
581
308
  // Silent decompress error
582
309
  }
583
310
  }
584
311
  });
585
312
  this._mqtt.on('error', this.emitError);
586
-
587
313
  await (0, shared_1.delay)(100);
588
314
  if (this.initOptions.graphQlSubs && this.initOptions.graphQlSubs.length > 0) {
589
315
  await this.graphQlSubscribe(this.initOptions.graphQlSubs);
@@ -614,23 +340,12 @@ class RealtimeClient extends eventemitter3_1.EventEmitter {
614
340
  url: '/api/v1/direct_v2/threads/get_most_recent_message/',
615
341
  method: 'POST',
616
342
  });
617
- } catch (e) {
343
+ } catch(e) {
618
344
  // Silent force fetch error
619
345
  }
620
346
  } catch (error) {
621
347
  // Silent inbox fetch error - MQTT still listening
622
348
  }
623
-
624
- // Persist last used clientMqttSessionId and optionally last seq_id if present in initOptions
625
- try {
626
- const clientMqttSessionId = this.connection?.toThrift()?.clientInfo?.clientMqttSessionId || this._sessionState?.clientMqttSessionId;
627
- if (clientMqttSessionId) {
628
- this._sessionState = this._sessionState || {};
629
- this._sessionState.clientMqttSessionId = String(clientMqttSessionId);
630
- this._saveSessionState();
631
- }
632
- } catch (e) { /* ignore */ }
633
-
634
349
  this._setupMessageHandlers();
635
350
  }
636
351
  /**
@@ -646,7 +361,7 @@ class RealtimeClient extends eventemitter3_1.EventEmitter {
646
361
  console.log('[RealtimeClient] Connecting from saved session...');
647
362
 
648
363
  const savedOptions = authStateHelper.getMqttConnectOptions?.();
649
-
364
+
650
365
  const connectOptions = {
651
366
  graphQlSubs: options.graphQlSubs || savedOptions?.graphQlSubs || ['ig_sub_direct', 'ig_sub_direct_v2_message_sync'],
652
367
  skywalkerSubs: options.skywalkerSubs || savedOptions?.skywalkerSubs || ['presence_subscribe', 'typing_subscribe'],
@@ -660,12 +375,8 @@ class RealtimeClient extends eventemitter3_1.EventEmitter {
660
375
  hasIrisData: !!connectOptions.irisData
661
376
  });
662
377
 
663
- // merge any persisted mqtt connect options into initOptions for continuity
664
- this.initOptions = { ...(this.initOptions || {}), ...(savedOptions || {}) };
665
-
666
378
  await this.connect(connectOptions);
667
379
 
668
- // Save session state into auth helper if supported
669
380
  if (authStateHelper.saveMqttSession) {
670
381
  try {
671
382
  await authStateHelper.saveMqttSession(this);
@@ -675,11 +386,6 @@ class RealtimeClient extends eventemitter3_1.EventEmitter {
675
386
  }
676
387
  }
677
388
 
678
- // Also persist locally
679
- try {
680
- if (this._sessionState) this._saveSessionState();
681
- } catch (e) { }
682
-
683
389
  return this;
684
390
  }
685
391
  /**
@@ -689,21 +395,13 @@ class RealtimeClient extends eventemitter3_1.EventEmitter {
689
395
  async saveSession(authStateHelper) {
690
396
  if (!authStateHelper || !authStateHelper.saveMqttSession) {
691
397
  console.warn('[RealtimeClient] No authStateHelper provided');
692
- // still save local state
693
- try { this._saveSessionState(); } catch (e) { }
694
398
  return false;
695
399
  }
696
400
  await authStateHelper.saveMqttSession(this);
697
- // also persist locally
698
- try { this._saveSessionState(); } catch (e) { }
699
401
  return true;
700
402
  }
701
403
  disconnect() {
702
404
  this.safeDisconnect = true;
703
- // clear metrics interval when shutting down gracefully
704
- try {
705
- if (this._metricsInterval) clearInterval(this._metricsInterval);
706
- } catch (e) { }
707
405
  return this.mqtt?.disconnect() ?? Promise.resolve();
708
406
  }
709
407
  graphQlSubscribe(sub) {
@@ -737,13 +435,6 @@ class RealtimeClient extends eventemitter3_1.EventEmitter {
737
435
  throw new mqtts_1.IllegalStateError('connect() must be called before irisSubscribe()');
738
436
  }
739
437
  this.realtimeDebug(`Iris Sub to: seqId: ${seq_id}, snapshot: ${snapshot_at_ms}`);
740
- // persist last seq_id for continuity
741
- try {
742
- this._sessionState = this._sessionState || {};
743
- if (seq_id) this._sessionState.last_seq_id = seq_id;
744
- if (snapshot_at_ms) this._sessionState.last_snapshot_at_ms = snapshot_at_ms;
745
- this._saveSessionState();
746
- } catch (e) { /* ignore */ }
747
438
  return this.commands.updateSubscriptions({
748
439
  topic: constants_1.Topics.IRIS_SUB,
749
440
  data: {
@@ -753,68 +444,6 @@ class RealtimeClient extends eventemitter3_1.EventEmitter {
753
444
  },
754
445
  });
755
446
  }
756
-
757
- // ---------------- Dynamic subscription helpers ----------------
758
- async addGraphQlSub(sub) {
759
- try {
760
- const current = this.initOptions.graphQlSubs || [];
761
- if (!current.includes(sub)) {
762
- current.push(sub);
763
- this.initOptions.graphQlSubs = current;
764
- if (this.commands) {
765
- await this.graphQlSubscribe(current);
766
- }
767
- }
768
- return true;
769
- } catch (e) {
770
- this.realtimeDebug('addGraphQlSub error:', e?.message || e);
771
- return false;
772
- }
773
- }
774
- async removeGraphQlSub(sub) {
775
- try {
776
- const current = (this.initOptions.graphQlSubs || []).filter(s => s !== sub);
777
- this.initOptions.graphQlSubs = current;
778
- if (this.commands) {
779
- await this.graphQlSubscribe(current);
780
- }
781
- return true;
782
- } catch (e) {
783
- this.realtimeDebug('removeGraphQlSub error:', e?.message || e);
784
- return false;
785
- }
786
- }
787
- async addSkywalkerSub(sub) {
788
- try {
789
- const current = this.initOptions.skywalkerSubs || [];
790
- if (!current.includes(sub)) {
791
- current.push(sub);
792
- this.initOptions.skywalkerSubs = current;
793
- if (this.commands) {
794
- await this.skywalkerSubscribe(current);
795
- }
796
- }
797
- return true;
798
- } catch (e) {
799
- this.realtimeDebug('addSkywalkerSub error:', e?.message || e);
800
- return false;
801
- }
802
- }
803
- async removeSkywalkerSub(sub) {
804
- try {
805
- const current = (this.initOptions.skywalkerSubs || []).filter(s => s !== sub);
806
- this.initOptions.skywalkerSubs = current;
807
- if (this.commands) {
808
- await this.skywalkerSubscribe(current);
809
- }
810
- return true;
811
- } catch (e) {
812
- this.realtimeDebug('removeSkywalkerSub error:', e?.message || e);
813
- return false;
814
- }
815
- }
816
- // ----------------------------------------------------------------
817
-
818
447
  }
819
448
  exports.RealtimeClient = RealtimeClient;
820
449
  //# sourceMappingURL=realtime.client.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nodejs-insta-private-api-mqtt",
3
- "version": "1.1.5",
3
+ "version": "1.1.7",
4
4
  "description": "Complete Instagram MQTT protocol with FULL iOS + Android support. 33 device presets (21 iOS + 12 Android). iPhone 16/15/14/13/12, iPad Pro, Samsung, Pixel, Huawei. Real-time DM messaging, view-once media extraction, sub-500ms latency.",
5
5
  "main": "dist/index.js",
6
6
  "scripts": {