nodejs-insta-private-api-mqtt 1.3.49 → 1.3.50
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/dist/fbns/fbns.client.js +94 -25
- package/dist/mqttot/mqttot.client.js +180 -36
- package/package.json +1 -1
package/dist/fbns/fbns.client.js
CHANGED
|
@@ -10,6 +10,18 @@ const mqtts_1 = require("../mqtt-shim");
|
|
|
10
10
|
const errors_1 = require("../errors");
|
|
11
11
|
const eventemitter3_1 = require("eventemitter3");
|
|
12
12
|
const fbns_utilities_1 = require("./fbns.utilities");
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* FbnsClient
|
|
16
|
+
*
|
|
17
|
+
* Lightweight wrapper around MQTToTClient to handle FBNS-specific connection
|
|
18
|
+
* payloads, registration, message handling and safe disconnect behavior.
|
|
19
|
+
*
|
|
20
|
+
* NOTE: This file contains a small but important robustness change:
|
|
21
|
+
* when receiving an empty CONNACK payload we no longer force a disconnect.
|
|
22
|
+
* Instagram sometimes returns a successful CONNACK with an empty payload;
|
|
23
|
+
* treating that as a fatal error causes unnecessary disconnect loops.
|
|
24
|
+
*/
|
|
13
25
|
class FbnsClient extends eventemitter3_1.EventEmitter {
|
|
14
26
|
get auth() {
|
|
15
27
|
return this._auth;
|
|
@@ -24,6 +36,7 @@ class FbnsClient extends eventemitter3_1.EventEmitter {
|
|
|
24
36
|
this.safeDisconnect = false;
|
|
25
37
|
this._auth = new fbns_device_auth_1.FbnsDeviceAuth(this.ig);
|
|
26
38
|
}
|
|
39
|
+
|
|
27
40
|
buildConnection() {
|
|
28
41
|
this.fbnsDebug('Constructing connection');
|
|
29
42
|
this.conn = new mqttot_1.MQTToTConnection({
|
|
@@ -51,9 +64,25 @@ class FbnsClient extends eventemitter3_1.EventEmitter {
|
|
|
51
64
|
password: this._auth.password,
|
|
52
65
|
});
|
|
53
66
|
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Connect to FBNS (Firebase/FBNS equivalent over Instagram MQTT)
|
|
70
|
+
*
|
|
71
|
+
* Options:
|
|
72
|
+
* - enableTrace
|
|
73
|
+
* - autoReconnect
|
|
74
|
+
* - socksOptions
|
|
75
|
+
* - additionalTlsOptions
|
|
76
|
+
*
|
|
77
|
+
* Important robustness changes:
|
|
78
|
+
* - Do not force disconnect on empty CONNACK payload; log and continue.
|
|
79
|
+
* - Use clean:false to prefer session resume on reconnect (less aggressive).
|
|
80
|
+
*/
|
|
54
81
|
async connect({ enableTrace, autoReconnect, socksOptions, additionalTlsOptions, } = {}) {
|
|
55
82
|
this.fbnsDebug('Connecting to FBNS...');
|
|
83
|
+
// Ensure auth info is up-to-date
|
|
56
84
|
this.auth.update();
|
|
85
|
+
|
|
57
86
|
this.client = new mqttot_1.MQTToTClient({
|
|
58
87
|
url: constants_1.FBNS.HOST_NAME_V6,
|
|
59
88
|
payloadProvider: () => {
|
|
@@ -69,54 +98,91 @@ class FbnsClient extends eventemitter3_1.EventEmitter {
|
|
|
69
98
|
socksOptions,
|
|
70
99
|
additionalOptions: additionalTlsOptions,
|
|
71
100
|
});
|
|
101
|
+
|
|
102
|
+
// Re-emit warnings/errors to outer listeners
|
|
72
103
|
this.client.on('warning', w => this.emit('warning', w));
|
|
73
104
|
this.client.on('error', e => this.emit('error', e));
|
|
105
|
+
|
|
106
|
+
// If disconnect happens and safeDisconnect is false, emit error to upper layer
|
|
74
107
|
this.client.on('disconnect', reason => this.safeDisconnect
|
|
75
108
|
? this.emit('disconnect', reason && JSON.stringify(reason))
|
|
76
109
|
: this.emit('error', new errors_1.ClientDisconnectedError(`MQTToTClient got disconnected. Reason: ${reason && JSON.stringify(reason)}`)));
|
|
110
|
+
|
|
111
|
+
// Listen for FBNS messages and transform them accordingly
|
|
77
112
|
this.client.listen(constants_1.FbnsTopics.FBNS_MESSAGE.id, msg => this.handleMessage(msg));
|
|
78
113
|
this.client.listen({
|
|
79
114
|
topic: constants_1.FbnsTopics.FBNS_EXP_LOGGING.id,
|
|
80
115
|
transformer: async (msg) => JSON.parse((await (0, shared_1.tryUnzipAsync)(msg.payload)).toString()),
|
|
81
116
|
}, msg => this.emit('logging', msg));
|
|
82
117
|
this.client.listen(constants_1.FbnsTopics.PP.id, msg => this.emit('pp', msg.payload.toString()));
|
|
118
|
+
|
|
119
|
+
// Handle connect event from the underlying mqtt client
|
|
83
120
|
this.client.on('connect', async (res) => {
|
|
84
121
|
if (!this.client) {
|
|
85
122
|
throw new mqtts_1.IllegalStateError('No client registered but an event was received');
|
|
86
123
|
}
|
|
124
|
+
|
|
87
125
|
this.fbnsDebug('Connected to MQTT');
|
|
126
|
+
|
|
127
|
+
// IMPORTANT: Instagram sometimes returns a valid CONNACK with an empty payload.
|
|
128
|
+
// Historically this library treated that as an error and disconnected.
|
|
129
|
+
// That behavior causes unnecessary reconnect loops. Instead, log and continue.
|
|
88
130
|
if (!res.payload?.length) {
|
|
89
|
-
this.fbnsDebug(`Received empty connect packet. Reason: ${res.errorName}
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
return
|
|
131
|
+
this.fbnsDebug(`Received empty connect packet. Reason: ${res.errorName} (continuing without disconnect)`);
|
|
132
|
+
// Emit a warning but do not disconnect.
|
|
133
|
+
this.emit('warning', new errors_1.EmptyPacketError('Received empty connect packet (continuing).'));
|
|
134
|
+
// NOTE: we do not return here because some flows expect the registration step below.
|
|
135
|
+
// If you prefer to skip registration on empty payload, uncomment the next line:
|
|
136
|
+
// return;
|
|
137
|
+
} else {
|
|
138
|
+
// If payload present, read auth as before
|
|
139
|
+
try {
|
|
140
|
+
const payload = res.payload.toString('utf8');
|
|
141
|
+
this.fbnsDebug(`Received auth: ${payload}`);
|
|
142
|
+
this._auth.read(payload);
|
|
143
|
+
this.emit('auth', this.auth);
|
|
144
|
+
} catch (e) {
|
|
145
|
+
this.fbnsDebug(`Failed to parse connect payload: ${e?.message || e}`);
|
|
146
|
+
this.emit('error', e);
|
|
147
|
+
// do not force disconnect here - let reconnect logic handle transient issues
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Continue with registration attempt if possible.
|
|
152
|
+
try {
|
|
153
|
+
await this.client.mqttotPublish({
|
|
154
|
+
topic: constants_1.FbnsTopics.FBNS_REG_REQ.id,
|
|
155
|
+
payload: Buffer.from(JSON.stringify({
|
|
156
|
+
pkg_name: constants_1.INSTAGRAM_PACKAGE_NAME,
|
|
157
|
+
appid: this.ig.state.fbAnalyticsApplicationId,
|
|
158
|
+
}), 'utf8'),
|
|
159
|
+
qosLevel: 1,
|
|
160
|
+
});
|
|
161
|
+
} catch (e) {
|
|
162
|
+
// Log registration publish error but don't necessarily abort connection entirely here
|
|
163
|
+
this.fbnsDebug(`FBNS_REG_REQ publish failed: ${e?.message || e}`);
|
|
164
|
+
this.emit('warning', e);
|
|
93
165
|
}
|
|
94
|
-
const payload = res.payload.toString('utf8');
|
|
95
|
-
this.fbnsDebug(`Received auth: ${payload}`);
|
|
96
|
-
this._auth.read(payload);
|
|
97
|
-
this.emit('auth', this.auth);
|
|
98
|
-
await this.client.mqttotPublish({
|
|
99
|
-
topic: constants_1.FbnsTopics.FBNS_REG_REQ.id,
|
|
100
|
-
payload: Buffer.from(JSON.stringify({
|
|
101
|
-
pkg_name: constants_1.INSTAGRAM_PACKAGE_NAME,
|
|
102
|
-
appid: this.ig.state.fbAnalyticsApplicationId,
|
|
103
|
-
}), 'utf8'),
|
|
104
|
-
qosLevel: 1,
|
|
105
|
-
});
|
|
106
|
-
// this.buildConnection(); ?
|
|
107
166
|
});
|
|
167
|
+
|
|
168
|
+
// Establish connection with conservative options (prefer session resume)
|
|
108
169
|
await this.client
|
|
109
170
|
.connect({
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
171
|
+
keepAlive: 60,
|
|
172
|
+
protocolLevel: 3,
|
|
173
|
+
// Use clean: false to allow session resume where the broker supports it.
|
|
174
|
+
// If you have issues with stale state, consider setting clean: true.
|
|
175
|
+
clean: false,
|
|
176
|
+
connectDelay: 60 * 1000,
|
|
177
|
+
})
|
|
115
178
|
.catch(e => {
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
179
|
+
this.fbnsDebug(`Connection failed: ${e}`);
|
|
180
|
+
throw e;
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
// Subscribe to FBNS message topic and wait for register response
|
|
119
184
|
await this.client.subscribe({ topic: constants_1.FbnsTopics.FBNS_MESSAGE.id });
|
|
185
|
+
|
|
120
186
|
const msg = await (0, shared_1.listenOnce)(this.client, constants_1.FbnsTopics.FBNS_REG_RESP.id);
|
|
121
187
|
const data = await (0, shared_1.tryUnzipAsync)(msg.payload);
|
|
122
188
|
const payload = data.toString('utf8');
|
|
@@ -136,6 +202,7 @@ class FbnsClient extends eventemitter3_1.EventEmitter {
|
|
|
136
202
|
throw e;
|
|
137
203
|
}
|
|
138
204
|
}
|
|
205
|
+
|
|
139
206
|
disconnect() {
|
|
140
207
|
this.safeDisconnect = true;
|
|
141
208
|
if (!this.client) {
|
|
@@ -143,6 +210,7 @@ class FbnsClient extends eventemitter3_1.EventEmitter {
|
|
|
143
210
|
}
|
|
144
211
|
return this.client.disconnect();
|
|
145
212
|
}
|
|
213
|
+
|
|
146
214
|
async handleMessage(msg) {
|
|
147
215
|
const payload = JSON.parse((await (0, shared_1.tryUnzipAsync)(msg.payload)).toString('utf8'));
|
|
148
216
|
if ((0, shared_1.notUndefined)(payload.fbpushnotif)) {
|
|
@@ -156,6 +224,7 @@ class FbnsClient extends eventemitter3_1.EventEmitter {
|
|
|
156
224
|
this.emit('message', payload);
|
|
157
225
|
}
|
|
158
226
|
}
|
|
227
|
+
|
|
159
228
|
async sendPushRegister(token) {
|
|
160
229
|
const { body } = await this.ig.request.send({
|
|
161
230
|
url: `/api/v1/push/register/`,
|
|
@@ -1,23 +1,35 @@
|
|
|
1
1
|
'use strict';
|
|
2
|
-
// Instagram version
|
|
2
|
+
// Instagram version marker (adjust as you like)
|
|
3
3
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
4
|
exports.mqttotConnectFlow = exports.MQTToTClient = void 0;
|
|
5
|
-
// Export the Instagram version so it can be used programmatically
|
|
6
5
|
exports.INSTAGRAM_VERSION = '420.0.0.42.95';
|
|
6
|
+
|
|
7
7
|
const shared_1 = require("../shared");
|
|
8
8
|
const mqttot_connect_request_packet_1 = require("./mqttot.connect.request.packet");
|
|
9
|
-
//
|
|
9
|
+
// Use external mqtts package (must be installed in your project)
|
|
10
10
|
const mqtts_1 = require("mqtts");
|
|
11
11
|
const errors_1 = require("../errors");
|
|
12
12
|
const mqttot_connect_response_packet_1 = require("./mqttot.connect.response.packet");
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
15
|
* MQTToTClient
|
|
16
|
-
* - Subclasses
|
|
17
|
-
* -
|
|
18
|
-
* -
|
|
16
|
+
* - Subclasses external mqtts.MqttClient
|
|
17
|
+
* - Adds Instagram-specific helpers (connect flow, compressed publish)
|
|
18
|
+
* - Adds keepalive (PING) and robust reconnect with exponential backoff
|
|
19
|
+
*
|
|
20
|
+
* Comments/notes are inline. This file is ready to paste into your project.
|
|
19
21
|
*/
|
|
20
22
|
class MQTToTClient extends mqtts_1.MqttClient {
|
|
23
|
+
/**
|
|
24
|
+
* @param {Object} options
|
|
25
|
+
* options:
|
|
26
|
+
* - url: broker host
|
|
27
|
+
* - socksOptions: optional proxy options
|
|
28
|
+
* - autoReconnect: boolean
|
|
29
|
+
* - payloadProvider: async function returning connection payload (Thrift blob)
|
|
30
|
+
* - requirePayload: boolean (legacy behavior)
|
|
31
|
+
* - additionalOptions: transport options passthrough
|
|
32
|
+
*/
|
|
21
33
|
constructor(options) {
|
|
22
34
|
super({
|
|
23
35
|
autoReconnect: options.autoReconnect,
|
|
@@ -42,13 +54,73 @@ class MQTToTClient extends mqtts_1.MqttClient {
|
|
|
42
54
|
additionalOptions: options.additionalOptions,
|
|
43
55
|
}),
|
|
44
56
|
});
|
|
45
|
-
|
|
46
|
-
|
|
57
|
+
|
|
58
|
+
// Save options for reconnect attempts
|
|
59
|
+
this._options = options || {};
|
|
60
|
+
// Debug helper prefixed with broker url
|
|
61
|
+
this.mqttotDebug = (msg, ...args) => (0, shared_1.debugChannel)('mqttot')(`${this._options.url}: ${msg}`, ...args);
|
|
47
62
|
this.connectPayloadProvider = options.payloadProvider;
|
|
48
63
|
this.mqttotDebug(`Creating client`);
|
|
64
|
+
// Register listeners (errors, disconnect, pingresps, etc.)
|
|
49
65
|
this.registerListeners();
|
|
50
66
|
this.requirePayload = options.requirePayload;
|
|
67
|
+
|
|
68
|
+
// Keepalive: periodically send PINGREQ to avoid idle timeouts on Instagram
|
|
69
|
+
// Default interval 10-15 minutes; adjust if you need more/less aggressive keepalive.
|
|
70
|
+
// Use a relatively long interval to avoid being rate-limited, but short enough to stay online.
|
|
71
|
+
this._keepaliveMs = (typeof options.keepaliveMs === 'number') ? options.keepaliveMs : (10 * 60 * 1000); // 10 min default
|
|
72
|
+
this._startKeepalive();
|
|
51
73
|
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Start the periodic keepalive ping. Wrapped so we can restart/clear safely.
|
|
77
|
+
*/
|
|
78
|
+
_startKeepalive() {
|
|
79
|
+
try {
|
|
80
|
+
if (this._keepaliveTimer) clearInterval(this._keepaliveTimer);
|
|
81
|
+
this._keepaliveTimer = setInterval(() => {
|
|
82
|
+
try {
|
|
83
|
+
// Defensive: only call ping if function exists
|
|
84
|
+
if (typeof this.ping === 'function') {
|
|
85
|
+
this.mqttotDebug('Sending PINGREQ (keepalive)');
|
|
86
|
+
// ping() may be sync or return a promise; wrap in try/catch
|
|
87
|
+
const res = this.ping();
|
|
88
|
+
if (res && typeof res.then === 'function') {
|
|
89
|
+
res.catch((e) => this.mqttotDebug(`Ping promise rejected: ${e?.message || e}`));
|
|
90
|
+
}
|
|
91
|
+
} else {
|
|
92
|
+
// As a fallback: write a zero-length control packet if library exposes low-level send
|
|
93
|
+
this.mqttotDebug('ping() not available on client - keepalive skipped');
|
|
94
|
+
}
|
|
95
|
+
} catch (e) {
|
|
96
|
+
this.mqttotDebug(`Ping error: ${e?.message || e}`);
|
|
97
|
+
}
|
|
98
|
+
}, this._keepaliveMs);
|
|
99
|
+
} catch (e) {
|
|
100
|
+
this.mqttotDebug(`Keepalive setup error: ${e?.message || e}`);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Stop keepalive timer (call on explicit close/disconnect)
|
|
106
|
+
*/
|
|
107
|
+
_stopKeepalive() {
|
|
108
|
+
try {
|
|
109
|
+
if (this._keepaliveTimer) {
|
|
110
|
+
clearInterval(this._keepaliveTimer);
|
|
111
|
+
this._keepaliveTimer = null;
|
|
112
|
+
}
|
|
113
|
+
} catch (e) {
|
|
114
|
+
// ignore
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Register event listeners on the underlying mqtt client to handle:
|
|
120
|
+
* - errors / warnings
|
|
121
|
+
* - disconnects -> attempt reconnection with exponential backoff
|
|
122
|
+
* - pingresp events for diagnostics
|
|
123
|
+
*/
|
|
52
124
|
registerListeners() {
|
|
53
125
|
const printErrorOrWarning = (type) => (e) => {
|
|
54
126
|
if (typeof e === 'string') {
|
|
@@ -58,47 +130,106 @@ class MQTToTClient extends mqtts_1.MqttClient {
|
|
|
58
130
|
this.mqttotDebug(`${type}: ${e.message}\n\tStack: ${e.stack}`);
|
|
59
131
|
}
|
|
60
132
|
};
|
|
61
|
-
|
|
133
|
+
|
|
134
|
+
// Attach diagnostics
|
|
62
135
|
this.on('error', printErrorOrWarning('Error'));
|
|
63
136
|
this.on('warning', printErrorOrWarning('Warning'));
|
|
64
|
-
|
|
137
|
+
|
|
138
|
+
// Listen to ping responses if the library emits them
|
|
139
|
+
this.on('pingresp', () => {
|
|
140
|
+
this.mqttotDebug('Received PINGRESP (keepalive ok)');
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
// On disconnect: try to reconnect with exponential backoff.
|
|
144
|
+
// This avoids immediate tight reconnect loops and gives the server time.
|
|
145
|
+
this.on('disconnect', async (reason) => {
|
|
146
|
+
try {
|
|
147
|
+
this.mqttotDebug(`Disconnected. Reason: ${reason}`);
|
|
148
|
+
// Stop keepalive while disconnected
|
|
149
|
+
this._stopKeepalive();
|
|
150
|
+
|
|
151
|
+
// If autoReconnect option is false, do not attempt manual reconnect
|
|
152
|
+
if (this._options && this._options.autoReconnect === false) {
|
|
153
|
+
this.mqttotDebug('autoReconnect disabled; will not attempt reconnect.');
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Exponential backoff parameters
|
|
158
|
+
let delay = 2000; // start with 2s
|
|
159
|
+
const maxAttempts = 8; // 2s,4s,8s,... up to ~512s
|
|
160
|
+
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
161
|
+
try {
|
|
162
|
+
this.mqttotDebug(`Reconnect attempt #${attempt + 1} (delay ${delay}ms)`);
|
|
163
|
+
// Attempt to reconnect; call this.connect which will fetch new payload as needed
|
|
164
|
+
// Use saved options; this.connect will call connectPayloadProvider internally
|
|
165
|
+
await this.connect(this._options);
|
|
166
|
+
this.mqttotDebug('Reconnected successfully');
|
|
167
|
+
// restart keepalive after reconnect
|
|
168
|
+
this._startKeepalive();
|
|
169
|
+
return;
|
|
170
|
+
} catch (err) {
|
|
171
|
+
this.mqttotDebug(`Reconnect attempt #${attempt + 1} failed: ${err?.message || err}`);
|
|
172
|
+
// wait before next attempt
|
|
173
|
+
await new Promise(r => setTimeout(r, delay));
|
|
174
|
+
// exponential backoff
|
|
175
|
+
delay = Math.min(delay * 2, 5 * 60 * 1000); // cap to 5 minutes
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
this.mqttotDebug('Exceeded reconnect attempts; giving up until next disconnect event triggers it again.');
|
|
179
|
+
} catch (e) {
|
|
180
|
+
this.mqttotDebug(`Error in disconnect handler: ${e?.message || e}`);
|
|
181
|
+
}
|
|
182
|
+
});
|
|
65
183
|
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* connect override
|
|
187
|
+
* - Waits for connect payload from provider before calling parent connect
|
|
188
|
+
*/
|
|
66
189
|
async connect(options) {
|
|
67
190
|
// Acquire the payload (Thrift serialized connection blob) before connecting.
|
|
68
|
-
|
|
191
|
+
if (typeof this.connectPayloadProvider === 'function') {
|
|
192
|
+
try {
|
|
193
|
+
this.connectPayload = await this.connectPayloadProvider();
|
|
194
|
+
} catch (e) {
|
|
195
|
+
this.mqttotDebug(`connectPayloadProvider failed: ${e?.message || e}`);
|
|
196
|
+
throw e;
|
|
197
|
+
}
|
|
198
|
+
} else {
|
|
199
|
+
this.mqttotDebug('No connectPayloadProvider provided; proceeding without payload');
|
|
200
|
+
this.connectPayload = null;
|
|
201
|
+
}
|
|
202
|
+
// Call super.connect to establish connection
|
|
69
203
|
return super.connect(options);
|
|
70
204
|
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Return an Instagram-flavored connect flow function for the mqtts client.
|
|
208
|
+
* If payload is missing but CONNACK indicates success, accept it (robustness).
|
|
209
|
+
*/
|
|
71
210
|
getConnectFlow() {
|
|
72
211
|
if (!this.connectPayload) {
|
|
73
212
|
throw new mqtts_1.IllegalStateError('Called getConnectFlow() before calling connect()');
|
|
74
213
|
}
|
|
75
214
|
return mqttotConnectFlow(this.connectPayload, this.requirePayload);
|
|
76
215
|
}
|
|
216
|
+
|
|
77
217
|
/**
|
|
78
|
-
* Compresses
|
|
79
|
-
*
|
|
80
|
-
* Rationale: Instagram/edge MQTT sometimes behaves inconsistently with QoS1 (missing PUBACKs,
|
|
81
|
-
* delayed acks, etc.). For stability we force QoS 0 here so the library avoids waiting on ACKs
|
|
82
|
-
* that may never arrive and causing reconnect loops.
|
|
83
|
-
*
|
|
84
|
-
* @param {MqttMessage} message
|
|
85
|
-
* @returns {Promise}
|
|
218
|
+
* Compresses payload using shared.compressDeflate and publishes with QoS 0.
|
|
219
|
+
* QoS 0 forced for Instagram edge stability (avoid PUBACK waits causing reconnect loops).
|
|
86
220
|
*/
|
|
87
221
|
async mqttotPublish(message) {
|
|
88
|
-
this.mqttotDebug(`Publishing ${message.payload.byteLength}bytes to topic ${message.topic}`);
|
|
222
|
+
this.mqttotDebug(`Publishing ${message.payload.byteLength || message.payload.length} bytes to topic ${message.topic}`);
|
|
223
|
+
const compressed = await (0, shared_1.compressDeflate)(message.payload);
|
|
89
224
|
return await this.publish({
|
|
90
225
|
topic: message.topic,
|
|
91
|
-
payload:
|
|
226
|
+
payload: compressed,
|
|
92
227
|
qosLevel: 0, // FORCED: QoS 0 for stability on Instagram edge
|
|
93
228
|
});
|
|
94
229
|
}
|
|
230
|
+
|
|
95
231
|
/**
|
|
96
|
-
*
|
|
97
|
-
* This helper attaches a transformer that converts raw message payloads into
|
|
98
|
-
* structured data before calling the provided handler.
|
|
99
|
-
*
|
|
100
|
-
* @param {Object} config - { topic, transformer }
|
|
101
|
-
* @param {Function} handler - Callback to handle transformed data
|
|
232
|
+
* Helper to listen for a specific topic and run transformer before calling handler.
|
|
102
233
|
*/
|
|
103
234
|
listen(config, handler) {
|
|
104
235
|
this.mqttotDebug(`[LISTEN] Setting up listener on topic ${config.topic} with transformer`);
|
|
@@ -108,23 +239,36 @@ class MQTToTClient extends mqtts_1.MqttClient {
|
|
|
108
239
|
const data = await config.transformer({ payload: msg.payload });
|
|
109
240
|
handler(data);
|
|
110
241
|
} catch (e) {
|
|
111
|
-
this.mqttotDebug(`Error in transformer for topic ${config.topic}: ${e
|
|
242
|
+
this.mqttotDebug(`Error in transformer for topic ${config.topic}: ${e?.message || e}`);
|
|
112
243
|
this.emit('error', e);
|
|
113
244
|
}
|
|
114
245
|
}
|
|
115
246
|
});
|
|
116
247
|
}
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Clean shutdown helper: stop keepalive & close
|
|
251
|
+
*/
|
|
252
|
+
async gracefulClose() {
|
|
253
|
+
try {
|
|
254
|
+
this._stopKeepalive();
|
|
255
|
+
if (typeof super.close === 'function') {
|
|
256
|
+
// some libs provide close() or end()
|
|
257
|
+
await super.close();
|
|
258
|
+
} else if (typeof super.end === 'function') {
|
|
259
|
+
await super.end();
|
|
260
|
+
}
|
|
261
|
+
} catch (e) {
|
|
262
|
+
this.mqttotDebug(`Error during gracefulClose: ${e?.message || e}`);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
117
265
|
}
|
|
118
266
|
exports.MQTToTClient = MQTToTClient;
|
|
119
267
|
|
|
120
268
|
/**
|
|
121
|
-
*
|
|
122
|
-
* -
|
|
123
|
-
*
|
|
124
|
-
* - To avoid noisy 'CONNACK: no payload (payloadExpected)' errors and unnecessary disconnect logs,
|
|
125
|
-
* we now treat any successful CONNACK (packet.isSuccess) as success regardless of payload.
|
|
126
|
-
*
|
|
127
|
-
* If you *do* need to enforce payload presence for some reason, revert this block to check requirePayload.
|
|
269
|
+
* mqttotConnectFlow
|
|
270
|
+
* - Returns a flow object that the mqtts client uses to perform CONNECT/CONNACK handshake.
|
|
271
|
+
* - Changed behavior: treat CONNACK success as success even if payload missing (robustness).
|
|
128
272
|
*/
|
|
129
273
|
function mqttotConnectFlow(payload, requirePayload) {
|
|
130
274
|
return (success, error) => ({
|
|
@@ -138,7 +282,7 @@ function mqttotConnectFlow(payload, requirePayload) {
|
|
|
138
282
|
accept: mqtts_1.isConnAck,
|
|
139
283
|
next: (packet) => {
|
|
140
284
|
if (packet.isSuccess) {
|
|
141
|
-
//
|
|
285
|
+
// Accept success even if payload is empty to avoid noisy errors
|
|
142
286
|
success(packet);
|
|
143
287
|
}
|
|
144
288
|
else {
|
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.50",
|
|
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": {
|