nodejs-insta-private-api-mqtt 1.3.23 → 1.3.25
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.
|
@@ -3,11 +3,17 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.mqttotConnectFlow = exports.MQTToTClient = void 0;
|
|
4
4
|
const shared_1 = require("../shared");
|
|
5
5
|
const mqttot_connect_request_packet_1 = require("./mqttot.connect.request.packet");
|
|
6
|
-
// ***
|
|
6
|
+
// *** Change: import the external mqtts package instead of a local mqtt-shim ***
|
|
7
7
|
const mqtts_1 = require("mqtts");
|
|
8
8
|
const errors_1 = require("../errors");
|
|
9
9
|
const mqttot_connect_response_packet_1 = require("./mqttot.connect.response.packet");
|
|
10
10
|
|
|
11
|
+
/**
|
|
12
|
+
* MQTToTClient
|
|
13
|
+
* - Subclasses the external mqtts.MqttClient implementation.
|
|
14
|
+
* - Provides Instagram-specific connect/publish helpers (Thrift payload, debug).
|
|
15
|
+
* - Keeps compatibility with options used by the original library.
|
|
16
|
+
*/
|
|
11
17
|
class MQTToTClient extends mqtts_1.MqttClient {
|
|
12
18
|
constructor(options) {
|
|
13
19
|
super({
|
|
@@ -33,6 +39,7 @@ class MQTToTClient extends mqtts_1.MqttClient {
|
|
|
33
39
|
additionalOptions: options.additionalOptions,
|
|
34
40
|
}),
|
|
35
41
|
});
|
|
42
|
+
// small debug helper that prefixes messages with the broker url
|
|
36
43
|
this.mqttotDebug = (msg, ...args) => (0, shared_1.debugChannel)('mqttot')(`${options.url}: ${msg}`, ...args);
|
|
37
44
|
this.connectPayloadProvider = options.payloadProvider;
|
|
38
45
|
this.mqttotDebug(`Creating client`);
|
|
@@ -48,11 +55,13 @@ class MQTToTClient extends mqtts_1.MqttClient {
|
|
|
48
55
|
this.mqttotDebug(`${type}: ${e.message}\n\tStack: ${e.stack}`);
|
|
49
56
|
}
|
|
50
57
|
};
|
|
58
|
+
// Attach basic diagnostics so errors/warnings are visible via debugChannel
|
|
51
59
|
this.on('error', printErrorOrWarning('Error'));
|
|
52
60
|
this.on('warning', printErrorOrWarning('Warning'));
|
|
53
61
|
this.on('disconnect', e => this.mqttotDebug(`Disconnected. ${e}`));
|
|
54
62
|
}
|
|
55
63
|
async connect(options) {
|
|
64
|
+
// Acquire the payload (Thrift serialized connection blob) before connecting.
|
|
56
65
|
this.connectPayload = await this.connectPayloadProvider();
|
|
57
66
|
return super.connect(options);
|
|
58
67
|
}
|
|
@@ -63,20 +72,28 @@ class MQTToTClient extends mqtts_1.MqttClient {
|
|
|
63
72
|
return mqttotConnectFlow(this.connectPayload, this.requirePayload);
|
|
64
73
|
}
|
|
65
74
|
/**
|
|
66
|
-
* Compresses the payload
|
|
75
|
+
* Compresses the payload and publishes it.
|
|
76
|
+
* NOTE: Always publishes with qosLevel: 0 to avoid puback/retry instability.
|
|
77
|
+
* Rationale: Instagram/edge MQTT sometimes behaves inconsistently with QoS1 (missing PUBACKs,
|
|
78
|
+
* delayed acks, etc.). For stability we force QoS 0 here so the library avoids waiting on ACKs
|
|
79
|
+
* that may never arrive and causing reconnect loops.
|
|
80
|
+
*
|
|
67
81
|
* @param {MqttMessage} message
|
|
68
|
-
* @returns {Promise
|
|
82
|
+
* @returns {Promise}
|
|
69
83
|
*/
|
|
70
84
|
async mqttotPublish(message) {
|
|
71
85
|
this.mqttotDebug(`Publishing ${message.payload.byteLength}bytes to topic ${message.topic}`);
|
|
72
86
|
return await this.publish({
|
|
73
87
|
topic: message.topic,
|
|
74
88
|
payload: await (0, shared_1.compressDeflate)(message.payload),
|
|
75
|
-
qosLevel:
|
|
89
|
+
qosLevel: 0, // FORCED: QoS 0 for stability on Instagram edge
|
|
76
90
|
});
|
|
77
91
|
}
|
|
78
92
|
/**
|
|
79
93
|
* Special listener for specific topics with transformers
|
|
94
|
+
* This helper attaches a transformer that converts raw message payloads into
|
|
95
|
+
* structured data before calling the provided handler.
|
|
96
|
+
*
|
|
80
97
|
* @param {Object} config - { topic, transformer }
|
|
81
98
|
* @param {Function} handler - Callback to handle transformed data
|
|
82
99
|
*/
|
|
@@ -99,7 +116,7 @@ exports.MQTToTClient = MQTToTClient;
|
|
|
99
116
|
|
|
100
117
|
/**
|
|
101
118
|
* NOTE: changed acceptance logic for CONNACK payload:
|
|
102
|
-
* - Historically
|
|
119
|
+
* - Historically the connect flow expected a payload in CONNACK (requirePayload = true),
|
|
103
120
|
* but Instagram/edge sometimes replies with an empty payload even when connection is valid.
|
|
104
121
|
* - To avoid noisy 'CONNACK: no payload (payloadExpected)' errors and unnecessary disconnect logs,
|
|
105
122
|
* we now treat any successful CONNACK (packet.isSuccess) as success regardless of payload.
|
|
@@ -128,3 +145,4 @@ function mqttotConnectFlow(payload, requirePayload) {
|
|
|
128
145
|
});
|
|
129
146
|
}
|
|
130
147
|
exports.mqttotConnectFlow = mqttotConnectFlow;
|
|
148
|
+
|
|
@@ -10,22 +10,23 @@ class Commands {
|
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
12
|
* Publish a compressed payload to a topic.
|
|
13
|
-
* -
|
|
14
|
-
* doesn't reply PUBACK for these internal topics and QoS1 causes timeouts
|
|
15
|
-
*
|
|
13
|
+
* - Forces QoS 0 (no PUBACK wait) because Instagram internal MQTT often
|
|
14
|
+
* doesn't reply PUBACK for these internal topics and QoS1 causes timeouts
|
|
15
|
+
* and reconnect instability.
|
|
16
|
+
* - No-op if client is not connected to avoid "Socket not writable" errors.
|
|
17
|
+
* - Defensive checks: if the client exposes .connected or .isConnected(), respect them.
|
|
16
18
|
*/
|
|
17
19
|
async publishToTopic(topic, compressedData, qos = 0) {
|
|
18
20
|
try {
|
|
19
|
-
//
|
|
20
|
-
// .connected boolean; this is defensive — if not present, still try but safe-guard write errors.
|
|
21
|
+
// Defensive: ensure we have a client
|
|
21
22
|
if (!this.client) return;
|
|
22
23
|
|
|
23
|
-
//
|
|
24
|
+
// If client exposes a boolean 'connected' flag, respect it
|
|
24
25
|
if (typeof this.client.connected !== 'undefined' && !this.client.connected) {
|
|
25
|
-
// do not attempt publish when disconnected
|
|
26
26
|
return;
|
|
27
27
|
}
|
|
28
|
-
|
|
28
|
+
|
|
29
|
+
// If client exposes an isConnected() method, respect it
|
|
29
30
|
if (typeof this.client.isConnected === 'function' && !this.client.isConnected()) {
|
|
30
31
|
return;
|
|
31
32
|
}
|
|
@@ -33,38 +34,38 @@ class Commands {
|
|
|
33
34
|
// Build payload buffer
|
|
34
35
|
const payloadBuf = compressedData instanceof Buffer ? compressedData : Buffer.from(compressedData);
|
|
35
36
|
|
|
36
|
-
// Always
|
|
37
|
+
// Always publish with QoS 0 for stability on IG edge MQTT
|
|
37
38
|
return this.client.publish({
|
|
38
39
|
topic,
|
|
39
40
|
payload: payloadBuf,
|
|
40
41
|
qosLevel: 0,
|
|
41
42
|
});
|
|
42
43
|
} catch (err) {
|
|
43
|
-
// Swallow non-fatal publish errors
|
|
44
|
-
// Avoid throwing so callers (watchdog timers) won't crash the loop.
|
|
44
|
+
// Swallow non-fatal publish errors. Upper layers/watchdog should handle reconnections.
|
|
45
45
|
return;
|
|
46
46
|
}
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
/**
|
|
50
|
-
* updateSubscriptions used by RealtimeClient for
|
|
51
|
-
*
|
|
52
|
-
*
|
|
50
|
+
* updateSubscriptions used by RealtimeClient for keepalive / subscription refreshes.
|
|
51
|
+
* - Compresses the subscription payload and publishes with QoS 0.
|
|
52
|
+
* - Skips publishing if the MQTT client doesn't appear ready.
|
|
53
53
|
*/
|
|
54
54
|
async updateSubscriptions(options) {
|
|
55
55
|
try {
|
|
56
56
|
if (!this.client) return;
|
|
57
57
|
|
|
58
|
-
//
|
|
58
|
+
// Defensive connection checks
|
|
59
59
|
if (typeof this.client.connected !== 'undefined' && !this.client.connected) return;
|
|
60
60
|
if (typeof this.client.isConnected === 'function' && !this.client.isConnected()) return;
|
|
61
61
|
|
|
62
62
|
const payload = await (0, shared_1.compressDeflate)(JSON.stringify(options.data || {}));
|
|
63
63
|
return this.publishToTopic(options.topic.id, payload, 0);
|
|
64
64
|
} catch (e) {
|
|
65
|
-
// Non-fatal
|
|
65
|
+
// Non-fatal — avoid bubbling errors from keepalive timers
|
|
66
66
|
return;
|
|
67
67
|
}
|
|
68
68
|
}
|
|
69
69
|
}
|
|
70
70
|
exports.Commands = Commands;
|
|
71
|
+
|
|
@@ -58,11 +58,11 @@ class EnhancedDirectCommands {
|
|
|
58
58
|
const json = JSON.stringify(command);
|
|
59
59
|
const payload = await shared_1.compressDeflate(json);
|
|
60
60
|
|
|
61
|
-
// Send to MQTT (QoS
|
|
61
|
+
// Send to MQTT (QoS 0 for stability)
|
|
62
62
|
this.enhancedDebug(`Publishing to MQTT topic ${constants_1.Topics.SEND_MESSAGE.id}`);
|
|
63
63
|
const result = await mqtt.publish({
|
|
64
64
|
topic: constants_1.Topics.SEND_MESSAGE.id,
|
|
65
|
-
qosLevel:
|
|
65
|
+
qosLevel: 0,
|
|
66
66
|
payload: payload,
|
|
67
67
|
});
|
|
68
68
|
|
|
@@ -101,7 +101,7 @@ class EnhancedDirectCommands {
|
|
|
101
101
|
this.enhancedDebug(`Publishing delete command to MQTT topic ${constants_1.Topics.SEND_MESSAGE.id}`);
|
|
102
102
|
const result = await mqtt.publish({
|
|
103
103
|
topic: constants_1.Topics.SEND_MESSAGE.id,
|
|
104
|
-
qosLevel:
|
|
104
|
+
qosLevel: 0,
|
|
105
105
|
payload: payload,
|
|
106
106
|
});
|
|
107
107
|
|
|
@@ -141,7 +141,7 @@ class EnhancedDirectCommands {
|
|
|
141
141
|
this.enhancedDebug(`Publishing edit command to MQTT topic ${constants_1.Topics.SEND_MESSAGE.id}`);
|
|
142
142
|
const result = await mqtt.publish({
|
|
143
143
|
topic: constants_1.Topics.SEND_MESSAGE.id,
|
|
144
|
-
qosLevel:
|
|
144
|
+
qosLevel: 0,
|
|
145
145
|
payload: payload,
|
|
146
146
|
});
|
|
147
147
|
|
|
@@ -182,7 +182,7 @@ class EnhancedDirectCommands {
|
|
|
182
182
|
this.enhancedDebug(`Publishing reply command to MQTT topic ${constants_1.Topics.SEND_MESSAGE.id}`);
|
|
183
183
|
const result = await mqtt.publish({
|
|
184
184
|
topic: constants_1.Topics.SEND_MESSAGE.id,
|
|
185
|
-
qosLevel:
|
|
185
|
+
qosLevel: 0,
|
|
186
186
|
payload: payload,
|
|
187
187
|
});
|
|
188
188
|
|
|
@@ -220,7 +220,7 @@ class EnhancedDirectCommands {
|
|
|
220
220
|
this.enhancedDebug(`Publishing follow subscription to MQTT`);
|
|
221
221
|
const result = await mqtt.publish({
|
|
222
222
|
topic: constants_1.Topics.SEND_MESSAGE.id,
|
|
223
|
-
qosLevel:
|
|
223
|
+
qosLevel: 0,
|
|
224
224
|
payload: payload,
|
|
225
225
|
});
|
|
226
226
|
|
|
@@ -258,7 +258,7 @@ class EnhancedDirectCommands {
|
|
|
258
258
|
this.enhancedDebug(`Publishing mention subscription to MQTT`);
|
|
259
259
|
const result = await mqtt.publish({
|
|
260
260
|
topic: constants_1.Topics.SEND_MESSAGE.id,
|
|
261
|
-
qosLevel:
|
|
261
|
+
qosLevel: 0,
|
|
262
262
|
payload: payload,
|
|
263
263
|
});
|
|
264
264
|
|
|
@@ -296,7 +296,7 @@ class EnhancedDirectCommands {
|
|
|
296
296
|
this.enhancedDebug(`Publishing call subscription to MQTT`);
|
|
297
297
|
const result = await mqtt.publish({
|
|
298
298
|
topic: constants_1.Topics.SEND_MESSAGE.id,
|
|
299
|
-
qosLevel:
|
|
299
|
+
qosLevel: 0,
|
|
300
300
|
payload: payload,
|
|
301
301
|
});
|
|
302
302
|
|
|
@@ -335,7 +335,7 @@ class EnhancedDirectCommands {
|
|
|
335
335
|
this.enhancedDebug(`Publishing add member command to MQTT`);
|
|
336
336
|
const result = await mqtt.publish({
|
|
337
337
|
topic: constants_1.Topics.SEND_MESSAGE.id,
|
|
338
|
-
qosLevel:
|
|
338
|
+
qosLevel: 0,
|
|
339
339
|
payload: payload,
|
|
340
340
|
});
|
|
341
341
|
|
|
@@ -374,7 +374,7 @@ class EnhancedDirectCommands {
|
|
|
374
374
|
this.enhancedDebug(`Publishing remove member command to MQTT`);
|
|
375
375
|
const result = await mqtt.publish({
|
|
376
376
|
topic: constants_1.Topics.SEND_MESSAGE.id,
|
|
377
|
-
qosLevel:
|
|
377
|
+
qosLevel: 0,
|
|
378
378
|
payload: payload,
|
|
379
379
|
});
|
|
380
380
|
|
|
@@ -416,7 +416,7 @@ class EnhancedDirectCommands {
|
|
|
416
416
|
this.enhancedDebug(`Publishing reaction to MQTT`);
|
|
417
417
|
const result = await mqtt.publish({
|
|
418
418
|
topic: constants_1.Topics.SEND_MESSAGE.id,
|
|
419
|
-
qosLevel:
|
|
419
|
+
qosLevel: 0,
|
|
420
420
|
payload: payload,
|
|
421
421
|
});
|
|
422
422
|
|
|
@@ -455,7 +455,7 @@ class EnhancedDirectCommands {
|
|
|
455
455
|
this.enhancedDebug(`Publishing mark as seen to MQTT`);
|
|
456
456
|
const result = await mqtt.publish({
|
|
457
457
|
topic: constants_1.Topics.SEND_MESSAGE.id,
|
|
458
|
-
qosLevel:
|
|
458
|
+
qosLevel: 0,
|
|
459
459
|
payload: payload,
|
|
460
460
|
});
|
|
461
461
|
|
|
@@ -494,7 +494,7 @@ class EnhancedDirectCommands {
|
|
|
494
494
|
this.enhancedDebug(`Publishing activity indicator to MQTT`);
|
|
495
495
|
const result = await mqtt.publish({
|
|
496
496
|
topic: constants_1.Topics.SEND_MESSAGE.id,
|
|
497
|
-
qosLevel:
|
|
497
|
+
qosLevel: 0,
|
|
498
498
|
payload: payload,
|
|
499
499
|
});
|
|
500
500
|
|
|
@@ -535,7 +535,7 @@ class EnhancedDirectCommands {
|
|
|
535
535
|
this.enhancedDebug(`Publishing media to MQTT`);
|
|
536
536
|
const result = await mqtt.publish({
|
|
537
537
|
topic: constants_1.Topics.SEND_MESSAGE.id,
|
|
538
|
-
qosLevel:
|
|
538
|
+
qosLevel: 0,
|
|
539
539
|
payload: payload,
|
|
540
540
|
});
|
|
541
541
|
|
|
@@ -576,7 +576,7 @@ class EnhancedDirectCommands {
|
|
|
576
576
|
this.enhancedDebug(`Publishing location to MQTT`);
|
|
577
577
|
const result = await mqtt.publish({
|
|
578
578
|
topic: constants_1.Topics.SEND_MESSAGE.id,
|
|
579
|
-
qosLevel:
|
|
579
|
+
qosLevel: 0,
|
|
580
580
|
payload: payload,
|
|
581
581
|
});
|
|
582
582
|
|
|
@@ -617,7 +617,7 @@ class EnhancedDirectCommands {
|
|
|
617
617
|
this.enhancedDebug(`Publishing profile to MQTT`);
|
|
618
618
|
const result = await mqtt.publish({
|
|
619
619
|
topic: constants_1.Topics.SEND_MESSAGE.id,
|
|
620
|
-
qosLevel:
|
|
620
|
+
qosLevel: 0,
|
|
621
621
|
payload: payload,
|
|
622
622
|
});
|
|
623
623
|
|
|
@@ -658,7 +658,7 @@ class EnhancedDirectCommands {
|
|
|
658
658
|
this.enhancedDebug(`Publishing hashtag to MQTT`);
|
|
659
659
|
const result = await mqtt.publish({
|
|
660
660
|
topic: constants_1.Topics.SEND_MESSAGE.id,
|
|
661
|
-
qosLevel:
|
|
661
|
+
qosLevel: 0,
|
|
662
662
|
payload: payload,
|
|
663
663
|
});
|
|
664
664
|
|
|
@@ -697,7 +697,7 @@ class EnhancedDirectCommands {
|
|
|
697
697
|
this.enhancedDebug(`Publishing like to MQTT`);
|
|
698
698
|
const result = await mqtt.publish({
|
|
699
699
|
topic: constants_1.Topics.SEND_MESSAGE.id,
|
|
700
|
-
qosLevel:
|
|
700
|
+
qosLevel: 0,
|
|
701
701
|
payload: payload,
|
|
702
702
|
});
|
|
703
703
|
|
|
@@ -738,7 +738,7 @@ class EnhancedDirectCommands {
|
|
|
738
738
|
this.enhancedDebug(`Publishing story to MQTT`);
|
|
739
739
|
const result = await mqtt.publish({
|
|
740
740
|
topic: constants_1.Topics.SEND_MESSAGE.id,
|
|
741
|
-
qosLevel:
|
|
741
|
+
qosLevel: 0,
|
|
742
742
|
payload: payload,
|
|
743
743
|
});
|
|
744
744
|
|
|
@@ -1,12 +1,24 @@
|
|
|
1
1
|
// realtime.client.js
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
/*
|
|
5
|
+
RealtimeClient
|
|
6
|
+
--------------
|
|
7
|
+
This file implements the RealtimeClient which manages:
|
|
8
|
+
- constructing the FBNS/MQTT connection payload
|
|
9
|
+
- starting the MQTT client (MQTToTClient)
|
|
10
|
+
- attaching lifecycle handlers (connect/close/error)
|
|
11
|
+
- keepalives, message-sync refresh and traffic watchdogs
|
|
12
|
+
- wiring MQTT messages into higher-level events (message, iris, receive)
|
|
13
|
+
Comments below are English explanations added next to the existing logic.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
// (keep module imports headers the same as in your original code)
|
|
5
17
|
const constants_1 = require("../constants");
|
|
6
18
|
const commands_1 = require("./commands");
|
|
7
19
|
const shared_1 = require("../shared");
|
|
8
20
|
const mqttot_1 = require("../mqttot");
|
|
9
|
-
// IMPORT MQTTS
|
|
21
|
+
// IMPORT MQTTS instead of the local shim (we're using mqtts package)
|
|
10
22
|
const mqtts_1 = require("mqtts");
|
|
11
23
|
const errors_1 = require("../errors");
|
|
12
24
|
const eventemitter3_1 = require("eventemitter3");
|
|
@@ -20,20 +32,40 @@ const gap_handler_1 = require("./features/gap-handler");
|
|
|
20
32
|
const enhanced_direct_commands_1 = require("./commands/enhanced.direct.commands");
|
|
21
33
|
const presence_typing_mixin_1 = require("./mixins/presence-typing.mixin");
|
|
22
34
|
|
|
35
|
+
/**
|
|
36
|
+
* RealtimeClient
|
|
37
|
+
* - Extends EventEmitter to emit high-level events for consumers.
|
|
38
|
+
* - Responsible for constructing the thrift payload, creating the MQTToTClient and wiring its events.
|
|
39
|
+
*/
|
|
23
40
|
class RealtimeClient extends eventemitter3_1.EventEmitter {
|
|
41
|
+
// getter to expose the underlying mqtt client instance
|
|
24
42
|
get mqtt() {
|
|
25
43
|
return this._mqtt;
|
|
26
44
|
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* constructor(ig, mixins)
|
|
48
|
+
* - ig: instance of instagram client (used for building payloads & fetching inbox)
|
|
49
|
+
* - mixins: collection of mixins applied to this realtime client (message-sync, realtime-sub, presence/typing)
|
|
50
|
+
*/
|
|
27
51
|
constructor(ig, mixins = [new mixins_1.MessageSyncMixin(), new mixins_1.RealtimeSubMixin(), new presence_typing_mixin_1.PresenceTypingMixin()]) {
|
|
28
52
|
super();
|
|
53
|
+
// debug helpers
|
|
29
54
|
this.realtimeDebug = (0, shared_1.debugChannel)('realtime');
|
|
30
55
|
this.messageDebug = this.realtimeDebug.extend('message');
|
|
56
|
+
|
|
57
|
+
// safeDisconnect flag used when user intentionally disconnects
|
|
31
58
|
this.safeDisconnect = false;
|
|
59
|
+
|
|
60
|
+
// convenience wrappers to emit events
|
|
32
61
|
this.emitError = (e) => this.emit('error', e);
|
|
33
62
|
this.emitWarning = (e) => this.emit('warning', e);
|
|
63
|
+
|
|
64
|
+
// persistent references
|
|
34
65
|
this.ig = ig;
|
|
35
66
|
this.threads = new Map();
|
|
36
|
-
|
|
67
|
+
|
|
68
|
+
// instantiate features / protocols / mixins
|
|
37
69
|
this.irisHandshake = new iris_handshake_1.IrisHandshake(this);
|
|
38
70
|
this.skywalkerProtocol = new skywalker_protocol_1.SkywalkerProtocol(this);
|
|
39
71
|
this.presenceManager = new presence_manager_1.PresenceManager(this);
|
|
@@ -41,8 +73,9 @@ class RealtimeClient extends eventemitter3_1.EventEmitter {
|
|
|
41
73
|
this.errorHandler = new error_handler_1.ErrorHandler(this);
|
|
42
74
|
this.gapHandler = new gap_handler_1.GapHandler(this);
|
|
43
75
|
this.directCommands = new enhanced_direct_commands_1.EnhancedDirectCommands(this);
|
|
44
|
-
|
|
76
|
+
|
|
45
77
|
this.realtimeDebug(`Applying mixins: ${mixins.map(m => m.name).join(', ')}`);
|
|
78
|
+
// apply mixins to this instance (keeps modular features separated)
|
|
46
79
|
(0, mixins_1.applyMixins)(mixins, this, this.ig);
|
|
47
80
|
|
|
48
81
|
// internal flags & timers
|
|
@@ -82,6 +115,11 @@ class RealtimeClient extends eventemitter3_1.EventEmitter {
|
|
|
82
115
|
const fs = require('fs');
|
|
83
116
|
const path = require('path');
|
|
84
117
|
|
|
118
|
+
/**
|
|
119
|
+
* waitForMqttCredentials(auth, timeoutMs, pollMs)
|
|
120
|
+
* - Poll the auth state for device / mqtt credentials before proceeding with auto-connect.
|
|
121
|
+
* - This ensures the saved auth state contains the fields required to build the MQTT payload.
|
|
122
|
+
*/
|
|
85
123
|
const waitForMqttCredentials = async (auth, timeoutMs = 15000, pollMs = 250) => {
|
|
86
124
|
const start = Date.now();
|
|
87
125
|
const hasCreds = () => {
|
|
@@ -103,6 +141,7 @@ class RealtimeClient extends eventemitter3_1.EventEmitter {
|
|
|
103
141
|
return false;
|
|
104
142
|
};
|
|
105
143
|
|
|
144
|
+
// Auto-start if saved creds exist on disk (non-blocking)
|
|
106
145
|
if (fs.existsSync(path.join(folder, 'creds.json'))) {
|
|
107
146
|
setTimeout(async () => {
|
|
108
147
|
try {
|
|
@@ -134,6 +173,10 @@ class RealtimeClient extends eventemitter3_1.EventEmitter {
|
|
|
134
173
|
}
|
|
135
174
|
}
|
|
136
175
|
|
|
176
|
+
/**
|
|
177
|
+
* startRealTimeListener(options)
|
|
178
|
+
* - Convenience method to fetch initial inbox (IRIS) and connect with those subscriptions.
|
|
179
|
+
*/
|
|
137
180
|
async startRealTimeListener(options = {}) {
|
|
138
181
|
try {
|
|
139
182
|
console.log('[REALTIME] Starting Real-Time Listener...');
|
|
@@ -164,6 +207,7 @@ class RealtimeClient extends eventemitter3_1.EventEmitter {
|
|
|
164
207
|
}
|
|
165
208
|
}
|
|
166
209
|
|
|
210
|
+
// Attach convenience handlers that convert raw message events into higher-level events
|
|
167
211
|
_setupMessageHandlers() {
|
|
168
212
|
this.on('message', (data) => {
|
|
169
213
|
const msg = this._parseMessage(data);
|
|
@@ -175,6 +219,7 @@ class RealtimeClient extends eventemitter3_1.EventEmitter {
|
|
|
175
219
|
});
|
|
176
220
|
}
|
|
177
221
|
|
|
222
|
+
// Parse a standard realtime message packet into a simplified message object
|
|
178
223
|
_parseMessage(data) {
|
|
179
224
|
try {
|
|
180
225
|
const msg = data.message;
|
|
@@ -198,6 +243,7 @@ class RealtimeClient extends eventemitter3_1.EventEmitter {
|
|
|
198
243
|
}
|
|
199
244
|
}
|
|
200
245
|
|
|
246
|
+
// Parse IRIS message data into a simplified message object
|
|
201
247
|
_parseIrisMessage(data) {
|
|
202
248
|
try {
|
|
203
249
|
if (data.event !== 'message_create' && !data.type?.includes('message')) return null;
|
|
@@ -216,6 +262,10 @@ class RealtimeClient extends eventemitter3_1.EventEmitter {
|
|
|
216
262
|
}
|
|
217
263
|
}
|
|
218
264
|
|
|
265
|
+
/**
|
|
266
|
+
* setInitOptions(initOptions)
|
|
267
|
+
* - Normalizes init options for connect calls (accepts array or object)
|
|
268
|
+
*/
|
|
219
269
|
setInitOptions(initOptions) {
|
|
220
270
|
if (Array.isArray(initOptions))
|
|
221
271
|
initOptions = { graphQlSubs: initOptions };
|
|
@@ -227,6 +277,7 @@ class RealtimeClient extends eventemitter3_1.EventEmitter {
|
|
|
227
277
|
};
|
|
228
278
|
}
|
|
229
279
|
|
|
280
|
+
// Extract session ID from a JWT-like authorization header if present
|
|
230
281
|
extractSessionIdFromJWT() {
|
|
231
282
|
try {
|
|
232
283
|
const authHeader = this.ig.state.authorization;
|
|
@@ -254,6 +305,11 @@ class RealtimeClient extends eventemitter3_1.EventEmitter {
|
|
|
254
305
|
}
|
|
255
306
|
}
|
|
256
307
|
|
|
308
|
+
/**
|
|
309
|
+
* constructConnection()
|
|
310
|
+
* - Build the MQTToTConnection payload (thrift object) used to connect.
|
|
311
|
+
* - Pulls deviceId, sessionid, device secrets and app-specific headers to populate the payload.
|
|
312
|
+
*/
|
|
257
313
|
constructConnection() {
|
|
258
314
|
const userAgent =
|
|
259
315
|
typeof this.ig.state.userAgent === 'string'
|
|
@@ -319,6 +375,7 @@ class RealtimeClient extends eventemitter3_1.EventEmitter {
|
|
|
319
375
|
}
|
|
320
376
|
} catch (e) {}
|
|
321
377
|
|
|
378
|
+
// mqtt JWT token if available (preferred)
|
|
322
379
|
let mqttJwt = null;
|
|
323
380
|
try {
|
|
324
381
|
if (this._attachedAuthState && typeof this._attachedAuthState.getData === 'function') {
|
|
@@ -350,6 +407,7 @@ class RealtimeClient extends eventemitter3_1.EventEmitter {
|
|
|
350
407
|
|
|
351
408
|
const subscribeTopics = [88, 135, 149, 150, 133, 146];
|
|
352
409
|
|
|
410
|
+
// Build the thrift connection object using mqttot.MQTToTConnection
|
|
353
411
|
this.connection = new mqttot_1.MQTToTConnection({
|
|
354
412
|
clientIdentifier: deviceId.substring(0, 20),
|
|
355
413
|
clientInfo: {
|
|
@@ -388,12 +446,20 @@ class RealtimeClient extends eventemitter3_1.EventEmitter {
|
|
|
388
446
|
});
|
|
389
447
|
}
|
|
390
448
|
|
|
449
|
+
/**
|
|
450
|
+
* connect(options)
|
|
451
|
+
* - Creates the MQTToTClient using mqttot.MQTToTClient and the payloadProvider.
|
|
452
|
+
* - Attaches idempotent lifecycle handlers for connect/close/error.
|
|
453
|
+
* - Starts keepalive/watchdog timers and message listeners after successful connect.
|
|
454
|
+
*/
|
|
391
455
|
async connect(options) {
|
|
392
456
|
this.setInitOptions(options);
|
|
393
457
|
this.constructConnection();
|
|
394
458
|
const { MQTToTClient } = require("../mqttot");
|
|
395
459
|
const { compressDeflate } = require("../shared");
|
|
396
|
-
|
|
460
|
+
|
|
461
|
+
// Create the MQTT client used by the runtime.
|
|
462
|
+
// requirePayload set to false to avoid treating empty CONNACK payload as fatal.
|
|
397
463
|
this._mqtt = new MQTToTClient({
|
|
398
464
|
url: 'edge-mqtt.facebook.com',
|
|
399
465
|
payloadProvider: async () => {
|
|
@@ -402,8 +468,8 @@ class RealtimeClient extends eventemitter3_1.EventEmitter {
|
|
|
402
468
|
autoReconnect: true,
|
|
403
469
|
requirePayload: false,
|
|
404
470
|
});
|
|
405
|
-
|
|
406
|
-
// attach lifecycle handlers idempotent
|
|
471
|
+
|
|
472
|
+
// attach lifecycle handlers idempotent (create once)
|
|
407
473
|
try {
|
|
408
474
|
if (typeof this._attachMqttLifecycle !== 'function') {
|
|
409
475
|
this._attachMqttLifecycle = async () => {
|
|
@@ -415,6 +481,7 @@ class RealtimeClient extends eventemitter3_1.EventEmitter {
|
|
|
415
481
|
});
|
|
416
482
|
} catch (e) {}
|
|
417
483
|
try {
|
|
484
|
+
// handle close -> schedule reconnect attempt with debounce
|
|
418
485
|
this._mqtt.on('close', async () => {
|
|
419
486
|
this.realtimeDebug('[MQTT] client close event');
|
|
420
487
|
this._lastMessageAt = Date.now();
|
|
@@ -425,6 +492,7 @@ class RealtimeClient extends eventemitter3_1.EventEmitter {
|
|
|
425
492
|
});
|
|
426
493
|
} catch (e) {}
|
|
427
494
|
try {
|
|
495
|
+
// handle error -> schedule reconnect attempt with debounce
|
|
428
496
|
this._mqtt.on('error', (err) => {
|
|
429
497
|
this.realtimeDebug('[MQTT] client error:', err?.message || err);
|
|
430
498
|
if (this._reconnectTimeoutId) clearTimeout(this._reconnectTimeoutId);
|
|
@@ -438,11 +506,13 @@ class RealtimeClient extends eventemitter3_1.EventEmitter {
|
|
|
438
506
|
try { await this._attachMqttLifecycle(); } catch (e) {}
|
|
439
507
|
} catch (e) {}
|
|
440
508
|
|
|
509
|
+
// actually connect the mqtt client (this will emit connect when done)
|
|
441
510
|
await this._mqtt.connect();
|
|
442
|
-
|
|
443
|
-
// Commands uses mqtt client;
|
|
511
|
+
|
|
512
|
+
// Commands uses mqtt client; Commands.updateSubscriptions has been set to use qos 0.
|
|
444
513
|
this.commands = new commands_1.Commands(this._mqtt);
|
|
445
|
-
|
|
514
|
+
|
|
515
|
+
// Notify higher-level code that we are connected
|
|
446
516
|
this.emit('connected');
|
|
447
517
|
|
|
448
518
|
// WATCHDOG / KEEPALIVE / TRAFFIC MONITOR
|
|
@@ -459,6 +529,7 @@ class RealtimeClient extends eventemitter3_1.EventEmitter {
|
|
|
459
529
|
const TRAFFIC_INACTIVITY_MS = (this.initOptions && this.initOptions.trafficInactivityMs) ? this.initOptions.trafficInactivityMs : 90000;
|
|
460
530
|
|
|
461
531
|
try {
|
|
532
|
+
// Foreground keepalive - periodically send a small "foreground true" subscription update
|
|
462
533
|
if (this._foregroundTimer) clearInterval(this._foregroundTimer);
|
|
463
534
|
this._foregroundTimer = setInterval(async () => {
|
|
464
535
|
try {
|
|
@@ -477,6 +548,7 @@ class RealtimeClient extends eventemitter3_1.EventEmitter {
|
|
|
477
548
|
}
|
|
478
549
|
|
|
479
550
|
try {
|
|
551
|
+
// MESSAGE_SYNC refresh - re-subscribe GraphQL subscriptions periodically to keep message-sync fresh
|
|
480
552
|
if (this._syncTimer) clearInterval(this._syncTimer);
|
|
481
553
|
this._syncTimer = setInterval(async () => {
|
|
482
554
|
try {
|
|
@@ -493,6 +565,7 @@ class RealtimeClient extends eventemitter3_1.EventEmitter {
|
|
|
493
565
|
}
|
|
494
566
|
|
|
495
567
|
try {
|
|
568
|
+
// Traffic watchdog - if no activity for TRAFFIC_INACTIVITY_MS, attempt reconnect
|
|
496
569
|
if (this._trafficWatchdog) clearInterval(this._trafficWatchdog);
|
|
497
570
|
this._trafficWatchdog = setInterval(async () => {
|
|
498
571
|
try {
|
|
@@ -513,6 +586,11 @@ class RealtimeClient extends eventemitter3_1.EventEmitter {
|
|
|
513
586
|
this.realtimeDebug('[WATCHDOG] initialization error:', e?.message || e);
|
|
514
587
|
}
|
|
515
588
|
|
|
589
|
+
/**
|
|
590
|
+
* Set up on-message handler:
|
|
591
|
+
* - Messages arriving from low-level MQTT are looked up in mqtt.topicMap,
|
|
592
|
+
* then parsed by the topic's parser if available. Otherwise payload is emitted as raw.
|
|
593
|
+
*/
|
|
516
594
|
this._mqtt.on('message', async (msg) => {
|
|
517
595
|
const topicMap = this.mqtt?.topicMap;
|
|
518
596
|
const topic = topicMap?.get(msg.topic);
|
|
@@ -529,10 +607,12 @@ class RealtimeClient extends eventemitter3_1.EventEmitter {
|
|
|
529
607
|
} catch(e) {}
|
|
530
608
|
}
|
|
531
609
|
});
|
|
610
|
+
// propagate mqtt errors to RealtimeClient error handler
|
|
532
611
|
this._mqtt.on('error', this.emitError);
|
|
533
612
|
|
|
534
613
|
try { await this._afterConnectHandlers(); } catch (e) { this.realtimeDebug('[MQTT] afterConnectHandlers failed', e?.message || e); }
|
|
535
614
|
|
|
615
|
+
// Initial subscriptions / iris
|
|
536
616
|
await (0, shared_1.delay)(100);
|
|
537
617
|
if (this.initOptions.graphQlSubs && this.initOptions.graphQlSubs.length > 0) {
|
|
538
618
|
await this.graphQlSubscribe(this.initOptions.graphQlSubs);
|
|
@@ -567,6 +647,11 @@ class RealtimeClient extends eventemitter3_1.EventEmitter {
|
|
|
567
647
|
this._setupMessageHandlers();
|
|
568
648
|
}
|
|
569
649
|
|
|
650
|
+
/**
|
|
651
|
+
* _attemptReconnectSafely()
|
|
652
|
+
* - Ensure only a single reconnect flow runs at once.
|
|
653
|
+
* - Disconnects existing mqtt client, waits briefly, and calls connect() again.
|
|
654
|
+
*/
|
|
570
655
|
async _attemptReconnectSafely() {
|
|
571
656
|
if (this._reconnectInProgress) return;
|
|
572
657
|
this._reconnectInProgress = true;
|
|
@@ -586,6 +671,11 @@ class RealtimeClient extends eventemitter3_1.EventEmitter {
|
|
|
586
671
|
}
|
|
587
672
|
}
|
|
588
673
|
|
|
674
|
+
/**
|
|
675
|
+
* _ensureIrisSnapshotAndSubscribe()
|
|
676
|
+
* - Ensure IRIS snapshot is present and subscribe to it.
|
|
677
|
+
* - Tries a fresh fetch after connect to get the latest snapshot.
|
|
678
|
+
*/
|
|
589
679
|
async _ensureIrisSnapshotAndSubscribe() {
|
|
590
680
|
try {
|
|
591
681
|
let iris = this.initOptions?.irisData || null;
|
|
@@ -616,6 +706,10 @@ class RealtimeClient extends eventemitter3_1.EventEmitter {
|
|
|
616
706
|
}
|
|
617
707
|
}
|
|
618
708
|
|
|
709
|
+
/**
|
|
710
|
+
* Attach message_sync listener to mqtt if available
|
|
711
|
+
* - Some mqtt clients provide a listen() helper; attach to both numeric id and string names for compatibility.
|
|
712
|
+
*/
|
|
619
713
|
async _ensureMessageSyncListener() {
|
|
620
714
|
try {
|
|
621
715
|
if (this._messageSyncAttached) return;
|
|
@@ -647,6 +741,11 @@ class RealtimeClient extends eventemitter3_1.EventEmitter {
|
|
|
647
741
|
}
|
|
648
742
|
}
|
|
649
743
|
|
|
744
|
+
/**
|
|
745
|
+
* connectFromSavedSession(authStateHelper, options)
|
|
746
|
+
* - Reconstructs connect options from saved authState and then calls connect()
|
|
747
|
+
* - Attempts to fetch a fresh IRIS snapshot (up to a few attempts) for safety.
|
|
748
|
+
*/
|
|
650
749
|
async connectFromSavedSession(authStateHelper, options = {}) {
|
|
651
750
|
if (!authStateHelper) {
|
|
652
751
|
throw new Error('authStateHelper is required - use useMultiFileAuthState()');
|
|
@@ -723,6 +822,11 @@ class RealtimeClient extends eventemitter3_1.EventEmitter {
|
|
|
723
822
|
|
|
724
823
|
return this;
|
|
725
824
|
}
|
|
825
|
+
|
|
826
|
+
/**
|
|
827
|
+
* saveSession(authStateHelper)
|
|
828
|
+
* - Helper to persist current MQTT session to auth helper if available.
|
|
829
|
+
*/
|
|
726
830
|
async saveSession(authStateHelper) {
|
|
727
831
|
if (!authStateHelper || !authStateHelper.saveMqttSession) {
|
|
728
832
|
console.warn('[RealtimeClient] No authStateHelper provided');
|
|
@@ -731,6 +835,11 @@ class RealtimeClient extends eventemitter3_1.EventEmitter {
|
|
|
731
835
|
await authStateHelper.saveMqttSession(this);
|
|
732
836
|
return true;
|
|
733
837
|
}
|
|
838
|
+
|
|
839
|
+
/**
|
|
840
|
+
* disconnect()
|
|
841
|
+
* - Perform a graceful shutdown: clear timers and disconnect mqtt client.
|
|
842
|
+
*/
|
|
734
843
|
disconnect() {
|
|
735
844
|
this.safeDisconnect = true;
|
|
736
845
|
try {
|
|
@@ -749,6 +858,11 @@ class RealtimeClient extends eventemitter3_1.EventEmitter {
|
|
|
749
858
|
} catch (e) {}
|
|
750
859
|
return this.mqtt?.disconnect() ?? Promise.resolve();
|
|
751
860
|
}
|
|
861
|
+
|
|
862
|
+
/**
|
|
863
|
+
* graphQlSubscribe(sub)
|
|
864
|
+
* - Helper that delegates to Commands.updateSubscriptions (which uses qos 0).
|
|
865
|
+
*/
|
|
752
866
|
graphQlSubscribe(sub) {
|
|
753
867
|
sub = typeof sub === 'string' ? [sub] : sub;
|
|
754
868
|
if (!this.commands) {
|
|
@@ -762,6 +876,11 @@ class RealtimeClient extends eventemitter3_1.EventEmitter {
|
|
|
762
876
|
},
|
|
763
877
|
});
|
|
764
878
|
}
|
|
879
|
+
|
|
880
|
+
/**
|
|
881
|
+
* skywalkerSubscribe(sub)
|
|
882
|
+
* - Delegate to Commands.updateSubscriptions (pubsub topic).
|
|
883
|
+
*/
|
|
765
884
|
skywalkerSubscribe(sub) {
|
|
766
885
|
sub = typeof sub === 'string' ? [sub] : sub;
|
|
767
886
|
if (!this.commands) {
|
|
@@ -775,6 +894,11 @@ class RealtimeClient extends eventemitter3_1.EventEmitter {
|
|
|
775
894
|
},
|
|
776
895
|
});
|
|
777
896
|
}
|
|
897
|
+
|
|
898
|
+
/**
|
|
899
|
+
* irisSubscribe({ seq_id, snapshot_at_ms })
|
|
900
|
+
* - Subscribe to IRIS using the provided snapshot properties.
|
|
901
|
+
*/
|
|
778
902
|
irisSubscribe({ seq_id, snapshot_at_ms, }) {
|
|
779
903
|
if (!this.commands) {
|
|
780
904
|
throw new mqtts_1.IllegalStateError('connect() must be called before irisSubscribe()');
|
|
@@ -791,3 +915,4 @@ class RealtimeClient extends eventemitter3_1.EventEmitter {
|
|
|
791
915
|
}
|
|
792
916
|
}
|
|
793
917
|
exports.RealtimeClient = RealtimeClient;
|
|
918
|
+
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nodejs-insta-private-api-mqtt",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.25",
|
|
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": {
|