nodejs-insta-private-api-mqtt 1.3.29 → 1.3.30

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.
@@ -1,24 +1,30 @@
1
- // realtime.client.js
2
1
  'use strict';
3
2
 
4
3
  /*
5
4
  RealtimeClient
6
5
  --------------
7
6
  This file implements the RealtimeClient which manages:
8
- - constructing the FBNS/MQTT connection payload
7
+ - constructing the FBNS/MQTT connection payload (Thrift)
9
8
  - starting the MQTT client (MQTToTClient)
10
9
  - attaching lifecycle handlers (connect/close/error)
11
10
  - keepalives, message-sync refresh and traffic watchdogs
12
11
  - wiring MQTT messages into higher-level events (message, iris, receive)
13
- Comments below are English explanations added next to the existing logic.
12
+
13
+ The implementation keeps original structure while applying compatibility tweaks:
14
+ - persistent clientMqttSessionId and client_context handling
15
+ - robust sessionid / mqttJwt / deviceSecret fallbacks
16
+ - slightly adjusted appSpecificInfo to match mobile client fields
17
+ - uses mqttot.MQTToTConnection to build a thrift connection payload
18
+ - MQTT URL remains 'edge-mqtt.facebook.com' (Instagram/Meta edge host)
19
+ Comments are in English next to logic so you can follow what's happening.
14
20
  */
15
21
 
16
- // (keep module imports headers the same as in your original code)
22
+ // Keep module imports similar to your original code
17
23
  const constants_1 = require("../constants");
18
24
  const commands_1 = require("./commands");
19
25
  const shared_1 = require("../shared");
20
26
  const mqttot_1 = require("../mqttot");
21
- // IMPORT MQTTS instead of the local shim (we're using mqtts package)
27
+ // Use mqtts for errors / IllegalStateError compatibility
22
28
  const mqtts_1 = require("mqtts");
23
29
  const errors_1 = require("../errors");
24
30
  const eventemitter3_1 = require("eventemitter3");
@@ -32,7 +38,7 @@ const gap_handler_1 = require("./features/gap-handler");
32
38
  const enhanced_direct_commands_1 = require("./commands/enhanced.direct.commands");
33
39
  const presence_typing_mixin_1 = require("./mixins/presence-typing.mixin");
34
40
 
35
- // Use INSTAGRAM_VERSION exported from mqttot so it is applied automatically
41
+ // Use INSTAGRAM_VERSION exported from mqttot so it is applied automatically where available
36
42
  const INSTAGRAM_VERSION = mqttot_1.INSTAGRAM_VERSION;
37
43
 
38
44
  /**
@@ -57,6 +63,9 @@ class RealtimeClient extends eventemitter3_1.EventEmitter {
57
63
  this.realtimeDebug = (0, shared_1.debugChannel)('realtime');
58
64
  this.messageDebug = this.realtimeDebug.extend('message');
59
65
 
66
+ // enhanced direct commands debug (exposed for compatibility with the enhanced commands)
67
+ this.enhancedDebug = this.realtimeDebug.extend('enhanced-commands');
68
+
60
69
  // safeDisconnect flag used when user intentionally disconnects
61
70
  this.safeDisconnect = false;
62
71
 
@@ -81,6 +90,10 @@ class RealtimeClient extends eventemitter3_1.EventEmitter {
81
90
  // apply mixins to this instance (keeps modular features separated)
82
91
  (0, mixins_1.applyMixins)(mixins, this, this.ig);
83
92
 
93
+ // Default subscriptions (force-enable if saved ones are empty)
94
+ this.defaultGraphQlSubs = ['ig_sub_direct', 'ig_sub_direct_v2_message_sync'];
95
+ this.defaultSkywalkerSubs = ['presence_subscribe', 'typing_subscribe'];
96
+
84
97
  // internal flags & timers
85
98
  this._attachedAuthState = null;
86
99
  this._messageSyncAttached = false;
@@ -314,6 +327,7 @@ class RealtimeClient extends eventemitter3_1.EventEmitter {
314
327
  * - Pulls deviceId, sessionid, device secrets and app-specific headers to populate the payload.
315
328
  */
316
329
  constructConnection() {
330
+ // Choose the best available user agent value from state fallbacks
317
331
  const userAgent =
318
332
  typeof this.ig.state.userAgent === 'string'
319
333
  ? this.ig.state.userAgent
@@ -323,8 +337,10 @@ class RealtimeClient extends eventemitter3_1.EventEmitter {
323
337
  ? this.ig.state.deviceString
324
338
  : 'Instagram 155.0.0.37.107 Android';
325
339
 
340
+ // deviceId / phoneId fallback handling (string coercion)
326
341
  const deviceId = String(this.ig.state.phoneId || this.ig.state.deviceId || 'device_unknown');
327
342
 
343
+ // attempt to extract sessionid from various locations: Authorization JWT, cookie helpers, state fields, or cookie jar
328
344
  let sessionid = null;
329
345
  try {
330
346
  sessionid = this.extractSessionIdFromJWT();
@@ -354,12 +370,14 @@ class RealtimeClient extends eventemitter3_1.EventEmitter {
354
370
  } catch (e) {}
355
371
  }
356
372
 
373
+ // fallback sessionid generation — only used if nothing else found
357
374
  if (!sessionid) {
358
375
  const userId = this.ig.state.cookieUserId || this.ig.state.userId || '0';
359
376
  sessionid = String(userId) + '_' + Date.now();
360
377
  this.realtimeDebug(`SessionID generated (fallback): ${sessionid}`);
361
378
  }
362
379
 
380
+ // device secret retrieval (from attached auth helper or state)
363
381
  let deviceSecret = null;
364
382
  try {
365
383
  if (this._attachedAuthState && typeof this._attachedAuthState.getData === 'function') {
@@ -388,6 +406,7 @@ class RealtimeClient extends eventemitter3_1.EventEmitter {
388
406
  if (!mqttJwt && this.ig.state.mqttJwt) mqttJwt = this.ig.state.mqttJwt;
389
407
  } catch (e) {}
390
408
 
409
+ // password is either "jwt=..." or "sessionid=..."
391
410
  let password;
392
411
  if (mqttJwt) {
393
412
  password = `jwt=${mqttJwt}`;
@@ -395,9 +414,10 @@ class RealtimeClient extends eventemitter3_1.EventEmitter {
395
414
  password = `sessionid=${sessionid}`;
396
415
  }
397
416
 
417
+ // client type indicates if device secret is available
398
418
  const clientType = deviceSecret ? 'secure_cookie_auth' : 'cookie_auth';
399
419
 
400
- // IMPORTANT: keep clientMqttSessionId persistent across reconnects
420
+ // IMPORTANT: keep clientMqttSessionId persistent across reconnects (already set in constructor)
401
421
  if (!this._clientMqttSessionId) {
402
422
  try {
403
423
  this._clientMqttSessionId = (BigInt(Date.now()) & BigInt(0xffffffff));
@@ -405,19 +425,21 @@ class RealtimeClient extends eventemitter3_1.EventEmitter {
405
425
  this._clientMqttSessionId = BigInt(0);
406
426
  }
407
427
  }
408
-
409
428
  const clientMqttSessionId = this._clientMqttSessionId;
410
429
 
430
+ // numeric topics Instagram uses for subscription mapping (kept consistent)
411
431
  const subscribeTopics = [88, 135, 149, 150, 133, 146];
412
432
 
413
433
  // Build the thrift connection object using mqttot.MQTToTConnection
434
+ // NOTE: use deviceId.substring(0,20) for clientIdentifier because the mobile app often truncates it.
435
+ // endpointCapabilities set to 128 for broader compatibility (some clients use 0, 128 provides better capability advertising).
414
436
  this.connection = new mqttot_1.MQTToTConnection({
415
437
  clientIdentifier: deviceId.substring(0, 20),
416
438
  clientInfo: {
417
439
  userId: BigInt(Number(this.ig.state.cookieUserId || this.ig.state.userId || 0)),
418
440
  userAgent,
419
441
  clientCapabilities: 183,
420
- endpointCapabilities: 0,
442
+ endpointCapabilities: 128,
421
443
  publishFormat: 1,
422
444
  noAutomaticForeground: false,
423
445
  makeUserAvailableInForeground: true,
@@ -444,8 +466,13 @@ class RealtimeClient extends eventemitter3_1.EventEmitter {
444
466
  video_call_participant_state_delivery: '17977239895057311',
445
467
  presence_subscribe: '17846944882223835',
446
468
  }),
469
+ // Extra app-specific fields observed in the mobile APK (helps the server identify the client capabilities)
447
470
  'User-Agent': userAgent,
448
471
  'Accept-Language': (this.ig.state.language || 'en_US').replace('_', '-'),
472
+ platform: 'android',
473
+ ig_mqtt_route: 'django',
474
+ pubsub_msg_type_blacklist: 'direct, typing_type',
475
+ auth_cache_enabled: '0',
449
476
  },
450
477
  });
451
478
  }
@@ -557,7 +584,7 @@ class RealtimeClient extends eventemitter3_1.EventEmitter {
557
584
  this._syncTimer = setInterval(async () => {
558
585
  try {
559
586
  if (!this.commands) return;
560
- const subs = (this.initOptions && this.initOptions.graphQlSubs && this.initOptions.graphQlSubs.length) ? this.initOptions.graphQlSubs : ['ig_sub_direct', 'ig_sub_direct_v2_message_sync'];
587
+ const subs = (this.initOptions && this.initOptions.graphQlSubs && this.initOptions.graphQlSubs.length) ? this.initOptions.graphQlSubs : this.defaultGraphQlSubs;
561
588
  await this.graphQlSubscribe(subs);
562
589
  this.realtimeDebug('[WATCHDOG] MESSAGE_SYNC refreshed.');
563
590
  } catch (e) {
@@ -620,6 +647,9 @@ class RealtimeClient extends eventemitter3_1.EventEmitter {
620
647
  await (0, shared_1.delay)(100);
621
648
  if (this.initOptions.graphQlSubs && this.initOptions.graphQlSubs.length > 0) {
622
649
  await this.graphQlSubscribe(this.initOptions.graphQlSubs);
650
+ } else {
651
+ // ensure defaults if none provided
652
+ await this.graphQlSubscribe(this.defaultGraphQlSubs);
623
653
  }
624
654
  if (this.initOptions.irisData) {
625
655
  await this.irisSubscribe(this.initOptions.irisData);
@@ -637,6 +667,9 @@ class RealtimeClient extends eventemitter3_1.EventEmitter {
637
667
  }
638
668
  if ((this.initOptions.skywalkerSubs ?? []).length > 0) {
639
669
  await this.skywalkerSubscribe(this.initOptions.skywalkerSubs);
670
+ } else {
671
+ // ensure default skywalker subs if none provided
672
+ await this.skywalkerSubscribe(this.defaultSkywalkerSubs);
640
673
  }
641
674
  await (0, shared_1.delay)(100);
642
675
  try {
@@ -790,8 +823,8 @@ class RealtimeClient extends eventemitter3_1.EventEmitter {
790
823
  }
791
824
 
792
825
  const connectOptions = {
793
- graphQlSubs: options.graphQlSubs || savedOptions.graphQlSubs || ['ig_sub_direct', 'ig_sub_direct_v2_message_sync'],
794
- skywalkerSubs: options.skywalkerSubs || savedOptions.skywalkerSubs || ['presence_subscribe', 'typing_subscribe'],
826
+ graphQlSubs: options.graphQlSubs || savedOptions.graphQlSubs || this.defaultGraphQlSubs,
827
+ skywalkerSubs: options.skywalkerSubs || savedOptions.skywalkerSubs || this.defaultSkywalkerSubs,
795
828
  irisData,
796
829
  ...options
797
830
  };
@@ -872,6 +905,8 @@ class RealtimeClient extends eventemitter3_1.EventEmitter {
872
905
  if (!this.commands) {
873
906
  throw new mqtts_1.IllegalStateError('connect() must be called before graphQlSubscribe()');
874
907
  }
908
+ // If the caller provided an empty array, ensure defaults are used
909
+ if (Array.isArray(sub) && sub.length === 0) sub = this.defaultGraphQlSubs;
875
910
  this.realtimeDebug(`Subscribing with GraphQL to ${sub.join(', ')}`);
876
911
  return this.commands.updateSubscriptions({
877
912
  topic: constants_1.Topics.REALTIME_SUB,
@@ -890,6 +925,8 @@ class RealtimeClient extends eventemitter3_1.EventEmitter {
890
925
  if (!this.commands) {
891
926
  throw new mqtts_1.IllegalStateError('connect() must be called before skywalkerSubscribe()');
892
927
  }
928
+ // If empty, use defaults
929
+ if (Array.isArray(sub) && sub.length === 0) sub = this.defaultSkywalkerSubs;
893
930
  this.realtimeDebug(`Subscribing with Skywalker to ${sub.join(', ')}`);
894
931
  return this.commands.updateSubscriptions({
895
932
  topic: constants_1.Topics.PUBSUB,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nodejs-insta-private-api-mqtt",
3
- "version": "1.3.29",
3
+ "version": "1.3.30",
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": {