nodejs-insta-private-api-mqtt 1.1.3 → 1.1.5
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 +5 -14
- package/dist/constants/constants.js +2 -4
- package/dist/core/request.js +1 -11
- package/dist/core/state.js +4 -8
- package/dist/mqttot/mqttot.client.js +3 -17
- package/dist/mqttot/mqttot.connection.js +0 -2
- package/dist/realtime/realtime.client.js +419 -48
- package/dist/shared/shared.js +3 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,24 +2,15 @@ Dear users,
|
|
|
2
2
|
First of all, when handling view-once images or videos, please make sure to save the received media in a folder such as /storage/emulated/0/Pictures/, or depending on your device's path. The photos and videos are saved correctly on your phone, but the library currently has some issues with uploading them back to Instagram. This will be resolved as soon as possible.
|
|
3
3
|
I post many versions of the project because Instagram changes the protocol almost daily, if you like this project leave a star on github https://github.com/Kunboruto20/nodejs-insta-private-api.git
|
|
4
4
|
|
|
5
|
-
⚠️ IMPORTANT NOTICE
|
|
6
|
-
This is an important announcement regarding the nodejs-insta-private-api library.
|
|
7
|
-
The old repository and NPM package were lost and are now associated with a different NPM account.
|
|
8
|
-
Starting from today, this NPM account will be the official and active source where this library will be published and maintained.
|
|
9
|
-
Please make sure you are using this package from this account only to receive future updates, fixes, and support.
|
|
10
5
|
|
|
11
|
-
You can subscribe to any Instagram topic in this library visit the bookstore code, it would help me a lot if you like this project and if it helps you!
|
|
12
6
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
# nodejs-insta-private-api-mqtt
|
|
7
|
+
# nodejs-insta-private-api
|
|
17
8
|
|
|
18
9
|
This project implements a complete and production-ready MQTT protocol client for Instagram's real-time messaging infrastructure. Instagram uses MQTT natively for direct messages, notifications, and real-time presence updates. This library replicates that exact implementation, allowing developers to build high-performance bots and automation tools that communicate with Instagram's backend using the same protocol the official app uses.
|
|
19
10
|
|
|
20
11
|
By leveraging MQTT instead of Instagram's REST API, this library achieves sub-500ms message latency, bidirectional real-time communication, and native support for notifications, presence tracking, and thread management. The implementation is reverse-engineered from Instagram's mobile app protocol and tested extensively for reliability and compatibility.
|
|
21
12
|
|
|
22
|
-
## Features ( - iOS + Android Full Support)
|
|
13
|
+
## Features (v5.61.11 - iOS + Android Full Support)
|
|
23
14
|
|
|
24
15
|
- **NEW: FULL iOS SUPPORT** - iPhone 16/15/14/13/12 + iPad Pro/Air device emulation
|
|
25
16
|
- **NEW: FULL ANDROID SUPPORT** - Samsung, Huawei, Google Pixel, OnePlus, Xiaomi, OPPO
|
|
@@ -75,7 +66,7 @@ Requires **Node.js 18 or higher**.
|
|
|
75
66
|
|
|
76
67
|
---
|
|
77
68
|
|
|
78
|
-
## NEW: Custom Device Emulation ()
|
|
69
|
+
## NEW: Custom Device Emulation (v5.60.7)
|
|
79
70
|
|
|
80
71
|
**Default Device:** Samsung Galaxy S25 Ultra (Android 15) - used automatically if you don't set a custom device.
|
|
81
72
|
|
|
@@ -93,7 +84,7 @@ This feature allows you to **choose which phone model Instagram sees** when your
|
|
|
93
84
|
The easiest way to set a custom device is using the built-in presets:
|
|
94
85
|
|
|
95
86
|
```javascript
|
|
96
|
-
const { IgApiClient } = require('nodejs-insta-private-api
|
|
87
|
+
const { IgApiClient } = require('nodejs-insta-private-api');
|
|
97
88
|
|
|
98
89
|
const ig = new IgApiClient();
|
|
99
90
|
|
|
@@ -1004,7 +995,7 @@ realtime.on('message', async (data) => {
|
|
|
1004
995
|
### Extract Media URLs Without Downloading
|
|
1005
996
|
|
|
1006
997
|
```javascript
|
|
1007
|
-
const { extractMediaUrls } = require('nodejs-insta-private-api
|
|
998
|
+
const { extractMediaUrls } = require('nodejs-insta-private-api');
|
|
1008
999
|
|
|
1009
1000
|
realtime.on('message', async (data) => {
|
|
1010
1001
|
const msg = data.message;
|
|
@@ -84,16 +84,14 @@ exports.FBNS = {
|
|
|
84
84
|
PACKAGE: 'com.instagram.android',
|
|
85
85
|
APP_ID: '567310203415052',
|
|
86
86
|
HOST_NAME_V6: 'mqtt-mini.facebook.com',
|
|
87
|
-
CLIENT_CAPABILITIES:
|
|
88
|
-
ENDPOINT_CAPABILITIES:
|
|
87
|
+
CLIENT_CAPABILITIES: 439,
|
|
88
|
+
ENDPOINT_CAPABILITIES: 128,
|
|
89
89
|
CLIENT_STACK: 3,
|
|
90
90
|
PUBLISH_FORMAT: 1,
|
|
91
91
|
};
|
|
92
92
|
exports.REALTIME = {
|
|
93
93
|
HOST_NAME_V6: 'edge-mqtt.facebook.com',
|
|
94
94
|
};
|
|
95
|
-
exports.APP_VERSION = '380.0.0.40.94';
|
|
96
|
-
exports.APP_VERSION_CODE = '525123456';
|
|
97
95
|
// TODO: exclude in release
|
|
98
96
|
/* eslint @typescript-eslint/no-unused-vars: "off" */
|
|
99
97
|
exports.PossibleTopics = [
|
package/dist/core/request.js
CHANGED
|
@@ -14,14 +14,7 @@ class Request {
|
|
|
14
14
|
timeout: 30000,
|
|
15
15
|
headers: {
|
|
16
16
|
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
|
|
17
|
-
}
|
|
18
|
-
// Modern TLS settings to bypass basic fingerprinting (Strict Instagram Fizz mimicry)
|
|
19
|
-
httpsAgent: new (require('https').Agent)({
|
|
20
|
-
ciphers: 'TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305',
|
|
21
|
-
honorCipherOrder: true,
|
|
22
|
-
minVersion: 'TLSv1.3', // Instagram 2026 enforces TLS 1.3
|
|
23
|
-
maxVersion: 'TLSv1.3'
|
|
24
|
-
})
|
|
17
|
+
}
|
|
25
18
|
});
|
|
26
19
|
}
|
|
27
20
|
|
|
@@ -208,9 +201,6 @@ class Request {
|
|
|
208
201
|
'X-IG-Android-ID': this.client.state.deviceId,
|
|
209
202
|
'Accept-Language': this.client.state.language.replace('_', '-'),
|
|
210
203
|
'X-FB-HTTP-Engine': 'Liger',
|
|
211
|
-
'X-FB-Client-IP': 'True',
|
|
212
|
-
'X-FB-Server-Cluster': 'True',
|
|
213
|
-
'X-IG-Nav-Chain': 'MainFeed:FeedTimelineFragment:1',
|
|
214
204
|
'Authorization': this.client.state.authorization,
|
|
215
205
|
'Host': 'i.instagram.com',
|
|
216
206
|
'Accept-Encoding': 'gzip, deflate',
|
package/dist/core/state.js
CHANGED
|
@@ -21,10 +21,8 @@ class State {
|
|
|
21
21
|
this.language = 'en_US';
|
|
22
22
|
this.timezoneOffset = String(new Date().getTimezoneOffset() * -60);
|
|
23
23
|
this.radioType = 'wifi-none';
|
|
24
|
-
this.capabilitiesHeader = '
|
|
24
|
+
this.capabilitiesHeader = '3brTv10=';
|
|
25
25
|
this.connectionTypeHeader = 'WIFI';
|
|
26
|
-
this.clientCapabilities = 6143;
|
|
27
|
-
this.endpointCapabilities = 255;
|
|
28
26
|
this.isLayoutRTL = false;
|
|
29
27
|
this.adsOptOut = false;
|
|
30
28
|
this.thumbnailCacheBustingValue = 1000;
|
|
@@ -37,12 +35,10 @@ class State {
|
|
|
37
35
|
|
|
38
36
|
// ===== PLATFORM SUPPORT (iOS + Android) =====
|
|
39
37
|
this.platform = 'android'; // 'android' or 'ios'
|
|
40
|
-
this.androidVersion = '15';
|
|
41
|
-
this.androidApiLevel = '35';
|
|
42
38
|
this.iosVersion = '18.1';
|
|
43
|
-
this.iosAppVersion = '
|
|
44
|
-
this.iosAppVersionCode = '
|
|
45
|
-
this.iosDeviceModel = '
|
|
39
|
+
this.iosAppVersion = '347.0.0.36.89';
|
|
40
|
+
this.iosAppVersionCode = '618023787';
|
|
41
|
+
this.iosDeviceModel = 'iPhone16,2'; // iPhone 15 Pro Max
|
|
46
42
|
this.iosDeviceName = 'iPhone';
|
|
47
43
|
this.iosBundleId = 'com.burbn.instagram';
|
|
48
44
|
|
|
@@ -23,26 +23,12 @@ class MQTToTClient extends mqtts_1.MqttClient {
|
|
|
23
23
|
host: options.url,
|
|
24
24
|
port: 443,
|
|
25
25
|
proxyOptions: options.socksOptions,
|
|
26
|
-
additionalOptions:
|
|
27
|
-
...options.additionalOptions,
|
|
28
|
-
ciphers: 'TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256',
|
|
29
|
-
minVersion: 'TLSv1.3',
|
|
30
|
-
maxVersion: 'TLSv1.3',
|
|
31
|
-
honorCipherOrder: true,
|
|
32
|
-
ALPNProtocols: ['h2', 'mqtt']
|
|
33
|
-
},
|
|
26
|
+
additionalOptions: options.additionalOptions,
|
|
34
27
|
})
|
|
35
28
|
: new mqtts_1.TlsTransport({
|
|
36
29
|
host: options.url,
|
|
37
30
|
port: 443,
|
|
38
|
-
additionalOptions:
|
|
39
|
-
...options.additionalOptions,
|
|
40
|
-
ciphers: 'TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256',
|
|
41
|
-
minVersion: 'TLSv1.3',
|
|
42
|
-
maxVersion: 'TLSv1.3',
|
|
43
|
-
honorCipherOrder: true,
|
|
44
|
-
ALPNProtocols: ['h2', 'mqtt']
|
|
45
|
-
},
|
|
31
|
+
additionalOptions: options.additionalOptions,
|
|
46
32
|
}),
|
|
47
33
|
});
|
|
48
34
|
this.mqttotDebug = (msg, ...args) => (0, shared_1.debugChannel)('mqttot')(`${options.url}: ${msg}`, ...args);
|
|
@@ -114,7 +100,7 @@ function mqttotConnectFlow(payload, requirePayload) {
|
|
|
114
100
|
type: mqtts_1.PacketType.Connect,
|
|
115
101
|
options: {
|
|
116
102
|
payload,
|
|
117
|
-
keepAlive:
|
|
103
|
+
keepAlive: 60,
|
|
118
104
|
},
|
|
119
105
|
}),
|
|
120
106
|
accept: mqtts_1.isConnAck,
|
|
@@ -7,8 +7,6 @@ class MQTToTConnection {
|
|
|
7
7
|
this.fbnsConnectionData = connectionData;
|
|
8
8
|
}
|
|
9
9
|
toThrift() {
|
|
10
|
-
this.fbnsConnectionData.clientCapabilities = this.fbnsConnectionData.clientCapabilities || 6143;
|
|
11
|
-
this.fbnsConnectionData.endpointCapabilities = this.fbnsConnectionData.endpointCapabilities || 255;
|
|
12
10
|
return (0, thrift_1.thriftWriteFromObject)(this.fbnsConnectionData, MQTToTConnection.thriftConfig);
|
|
13
11
|
}
|
|
14
12
|
toString() {
|
|
@@ -17,6 +17,9 @@ 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
|
+
|
|
20
23
|
class RealtimeClient extends eventemitter3_1.EventEmitter {
|
|
21
24
|
get mqtt() {
|
|
22
25
|
return this._mqtt;
|
|
@@ -35,7 +38,20 @@ class RealtimeClient extends eventemitter3_1.EventEmitter {
|
|
|
35
38
|
this.emitWarning = (e) => this.emit('warning', e);
|
|
36
39
|
this.ig = ig;
|
|
37
40
|
this.threads = new Map();
|
|
38
|
-
|
|
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
|
+
|
|
39
55
|
this.irisHandshake = new iris_handshake_1.IrisHandshake(this);
|
|
40
56
|
this.skywalkerProtocol = new skywalker_protocol_1.SkywalkerProtocol(this);
|
|
41
57
|
this.presenceManager = new presence_manager_1.PresenceManager(this);
|
|
@@ -43,21 +59,52 @@ class RealtimeClient extends eventemitter3_1.EventEmitter {
|
|
|
43
59
|
this.errorHandler = new error_handler_1.ErrorHandler(this);
|
|
44
60
|
this.gapHandler = new gap_handler_1.GapHandler(this);
|
|
45
61
|
this.directCommands = new enhanced_direct_commands_1.EnhancedDirectCommands(this);
|
|
46
|
-
|
|
62
|
+
|
|
47
63
|
this.realtimeDebug(`Applying mixins: ${mixins.map(m => m.name).join(', ')}`);
|
|
48
64
|
(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
|
+
}
|
|
49
94
|
}
|
|
50
95
|
|
|
96
|
+
// ------------------------------------------------------------------
|
|
97
|
+
|
|
51
98
|
/**
|
|
52
99
|
* Start Real-Time Listener with Auto-Inbox Fetch + MQTT
|
|
53
100
|
*/
|
|
54
101
|
async startRealTimeListener(options = {}) {
|
|
55
102
|
try {
|
|
56
103
|
console.log('[REALTIME] Starting Real-Time Listener...');
|
|
57
|
-
|
|
104
|
+
|
|
58
105
|
console.log('[REALTIME] Fetching inbox (IRIS data)...');
|
|
59
106
|
const inboxData = await this.ig.direct.getInbox();
|
|
60
|
-
|
|
107
|
+
|
|
61
108
|
console.log('[REALTIME] Connecting to MQTT with IRIS subscription...');
|
|
62
109
|
await this.connect({
|
|
63
110
|
graphQlSubs: [
|
|
@@ -70,15 +117,15 @@ class RealtimeClient extends eventemitter3_1.EventEmitter {
|
|
|
70
117
|
],
|
|
71
118
|
irisData: inboxData
|
|
72
119
|
});
|
|
73
|
-
|
|
120
|
+
|
|
74
121
|
console.log('[REALTIME] MQTT Connected with IRIS');
|
|
75
122
|
console.log('----------------------------------------');
|
|
76
123
|
console.log('[REALTIME] Real-Time Listener ACTIVE');
|
|
77
124
|
console.log('[REALTIME] Waiting for messages...');
|
|
78
125
|
console.log('----------------------------------------');
|
|
79
|
-
|
|
126
|
+
|
|
80
127
|
this._setupMessageHandlers();
|
|
81
|
-
|
|
128
|
+
|
|
82
129
|
return { success: true };
|
|
83
130
|
} catch (error) {
|
|
84
131
|
console.error('[REALTIME] Failed:', error.message);
|
|
@@ -90,46 +137,92 @@ class RealtimeClient extends eventemitter3_1.EventEmitter {
|
|
|
90
137
|
* Setup automatic message handlers
|
|
91
138
|
*/
|
|
92
139
|
_setupMessageHandlers() {
|
|
140
|
+
// handle raw mqtt 'message' events (parsed by this._parseMessage)
|
|
93
141
|
this.on('message', (data) => {
|
|
94
|
-
const
|
|
95
|
-
|
|
96
|
-
|
|
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
|
|
97
155
|
}
|
|
98
156
|
});
|
|
99
157
|
|
|
158
|
+
// handle iris events (graph/iris)
|
|
100
159
|
this.on('iris', (data) => {
|
|
101
|
-
const
|
|
102
|
-
if (
|
|
103
|
-
this.emit('
|
|
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
|
|
104
170
|
}
|
|
105
171
|
});
|
|
106
172
|
}
|
|
107
173
|
|
|
108
174
|
/**
|
|
109
175
|
* Parse direct message
|
|
176
|
+
* Returns an object that always contains the original raw as _rawMessage when available
|
|
110
177
|
*/
|
|
111
178
|
_parseMessage(data) {
|
|
112
179
|
try {
|
|
113
180
|
const msg = data.message;
|
|
114
181
|
if (!msg) return null;
|
|
115
|
-
|
|
182
|
+
|
|
116
183
|
if (data.parsed) {
|
|
117
|
-
|
|
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;
|
|
118
195
|
}
|
|
119
|
-
|
|
196
|
+
|
|
120
197
|
const threadInfo = this.threads.get(msg.thread_id);
|
|
121
|
-
|
|
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 = {
|
|
122
210
|
id: msg.item_id || msg.id,
|
|
123
211
|
userId: msg.user_id || msg.from_user_id,
|
|
124
|
-
username
|
|
125
|
-
text
|
|
212
|
+
username,
|
|
213
|
+
text,
|
|
126
214
|
itemType: msg.item_type || 'text',
|
|
127
215
|
thread: threadInfo?.title || `Thread ${msg.thread_id}`,
|
|
128
216
|
thread_id: msg.thread_id,
|
|
129
217
|
timestamp: msg.timestamp,
|
|
130
218
|
isGroup: threadInfo?.isGroup,
|
|
131
|
-
status: 'good'
|
|
219
|
+
status: 'received', // replaced 'good'
|
|
220
|
+
mqttString,
|
|
221
|
+
mqtt,
|
|
222
|
+
_rawMessage: msg
|
|
132
223
|
};
|
|
224
|
+
|
|
225
|
+
return out;
|
|
133
226
|
} catch (e) {
|
|
134
227
|
return null;
|
|
135
228
|
}
|
|
@@ -141,22 +234,125 @@ class RealtimeClient extends eventemitter3_1.EventEmitter {
|
|
|
141
234
|
_parseIrisMessage(data) {
|
|
142
235
|
try {
|
|
143
236
|
if (data.event !== 'message_create' && !data.type?.includes('message')) {
|
|
144
|
-
return
|
|
237
|
+
// still return raw for potential debug, but normalized filter will drop it
|
|
238
|
+
return { _rawIris: data };
|
|
145
239
|
}
|
|
146
|
-
|
|
147
|
-
|
|
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 = {
|
|
148
252
|
id: data.item_id || data.id,
|
|
149
253
|
userId: data.user_id || data.from_user_id,
|
|
150
|
-
username
|
|
151
|
-
text
|
|
254
|
+
username,
|
|
255
|
+
text,
|
|
152
256
|
itemType: data.item_type || 'text',
|
|
153
257
|
thread_id: data.thread_id,
|
|
154
258
|
timestamp: data.timestamp,
|
|
155
|
-
status: 'good'
|
|
259
|
+
status: 'received', // replaced 'good'
|
|
260
|
+
mqttString,
|
|
261
|
+
mqtt,
|
|
262
|
+
_rawIris: data
|
|
156
263
|
};
|
|
264
|
+
|
|
265
|
+
return out;
|
|
157
266
|
} 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
|
|
158
308
|
return null;
|
|
159
309
|
}
|
|
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;
|
|
160
356
|
}
|
|
161
357
|
|
|
162
358
|
setInitOptions(initOptions) {
|
|
@@ -230,6 +426,11 @@ class RealtimeClient extends eventemitter3_1.EventEmitter {
|
|
|
230
426
|
this.realtimeDebug(`SessionID generated (fallback): ${sessionid}`);
|
|
231
427
|
}
|
|
232
428
|
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
|
+
|
|
233
434
|
this.connection = new mqttot_1.MQTToTConnection({
|
|
234
435
|
clientIdentifier: deviceId.substring(0, 20),
|
|
235
436
|
clientInfo: {
|
|
@@ -244,7 +445,7 @@ class RealtimeClient extends eventemitter3_1.EventEmitter {
|
|
|
244
445
|
isInitiallyForeground: true,
|
|
245
446
|
networkType: 1,
|
|
246
447
|
networkSubtype: 0,
|
|
247
|
-
clientMqttSessionId:
|
|
448
|
+
clientMqttSessionId: clientMqttSessionIdValue,
|
|
248
449
|
subscribeTopics: [88, 135, 149, 150, 133, 146],
|
|
249
450
|
clientType: 'cookie_auth',
|
|
250
451
|
appId: BigInt(567067343352427),
|
|
@@ -266,50 +467,123 @@ class RealtimeClient extends eventemitter3_1.EventEmitter {
|
|
|
266
467
|
'Accept-Language': this.ig.state.language.replace('_', '-'),
|
|
267
468
|
},
|
|
268
469
|
});
|
|
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
|
+
}
|
|
269
479
|
}
|
|
270
480
|
async connect(options) {
|
|
271
481
|
this.setInitOptions(options);
|
|
272
482
|
this.constructConnection();
|
|
273
483
|
const { MQTToTClient } = require("../mqttot");
|
|
274
484
|
const { compressDeflate } = require("../shared");
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
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
|
+
|
|
287
537
|
this.commands = new commands_1.Commands(this._mqtt);
|
|
288
|
-
|
|
538
|
+
|
|
289
539
|
this.emit('connected');
|
|
290
|
-
|
|
540
|
+
|
|
541
|
+
// Enhanced message handling: try tolerant parse, log unknown fields, fallback raw
|
|
291
542
|
this._mqtt.on('message', async (msg) => {
|
|
292
543
|
const topicMap = this.mqtt?.topicMap;
|
|
293
544
|
const topic = topicMap?.get(msg.topic);
|
|
294
|
-
|
|
545
|
+
|
|
295
546
|
if (topic && topic.parser && !topic.noParse) {
|
|
296
547
|
try {
|
|
297
548
|
const unzipped = await (0, shared_1.tryUnzipAsync)(msg.payload);
|
|
298
|
-
|
|
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
|
+
|
|
299
570
|
this.emit('receive', topic, Array.isArray(parsedMessages) ? parsedMessages : [parsedMessages]);
|
|
300
|
-
} catch(e) {
|
|
301
|
-
// Silent parse error
|
|
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);
|
|
302
575
|
}
|
|
303
576
|
} else {
|
|
304
577
|
try {
|
|
305
578
|
await (0, shared_1.tryUnzipAsync)(msg.payload);
|
|
306
579
|
this.emit('receiveRaw', msg);
|
|
307
|
-
} catch(e) {
|
|
580
|
+
} catch (e) {
|
|
308
581
|
// Silent decompress error
|
|
309
582
|
}
|
|
310
583
|
}
|
|
311
584
|
});
|
|
312
585
|
this._mqtt.on('error', this.emitError);
|
|
586
|
+
|
|
313
587
|
await (0, shared_1.delay)(100);
|
|
314
588
|
if (this.initOptions.graphQlSubs && this.initOptions.graphQlSubs.length > 0) {
|
|
315
589
|
await this.graphQlSubscribe(this.initOptions.graphQlSubs);
|
|
@@ -340,12 +614,23 @@ class RealtimeClient extends eventemitter3_1.EventEmitter {
|
|
|
340
614
|
url: '/api/v1/direct_v2/threads/get_most_recent_message/',
|
|
341
615
|
method: 'POST',
|
|
342
616
|
});
|
|
343
|
-
} catch(e) {
|
|
617
|
+
} catch (e) {
|
|
344
618
|
// Silent force fetch error
|
|
345
619
|
}
|
|
346
620
|
} catch (error) {
|
|
347
621
|
// Silent inbox fetch error - MQTT still listening
|
|
348
622
|
}
|
|
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
|
+
|
|
349
634
|
this._setupMessageHandlers();
|
|
350
635
|
}
|
|
351
636
|
/**
|
|
@@ -361,7 +646,7 @@ class RealtimeClient extends eventemitter3_1.EventEmitter {
|
|
|
361
646
|
console.log('[RealtimeClient] Connecting from saved session...');
|
|
362
647
|
|
|
363
648
|
const savedOptions = authStateHelper.getMqttConnectOptions?.();
|
|
364
|
-
|
|
649
|
+
|
|
365
650
|
const connectOptions = {
|
|
366
651
|
graphQlSubs: options.graphQlSubs || savedOptions?.graphQlSubs || ['ig_sub_direct', 'ig_sub_direct_v2_message_sync'],
|
|
367
652
|
skywalkerSubs: options.skywalkerSubs || savedOptions?.skywalkerSubs || ['presence_subscribe', 'typing_subscribe'],
|
|
@@ -375,8 +660,12 @@ class RealtimeClient extends eventemitter3_1.EventEmitter {
|
|
|
375
660
|
hasIrisData: !!connectOptions.irisData
|
|
376
661
|
});
|
|
377
662
|
|
|
663
|
+
// merge any persisted mqtt connect options into initOptions for continuity
|
|
664
|
+
this.initOptions = { ...(this.initOptions || {}), ...(savedOptions || {}) };
|
|
665
|
+
|
|
378
666
|
await this.connect(connectOptions);
|
|
379
667
|
|
|
668
|
+
// Save session state into auth helper if supported
|
|
380
669
|
if (authStateHelper.saveMqttSession) {
|
|
381
670
|
try {
|
|
382
671
|
await authStateHelper.saveMqttSession(this);
|
|
@@ -386,6 +675,11 @@ class RealtimeClient extends eventemitter3_1.EventEmitter {
|
|
|
386
675
|
}
|
|
387
676
|
}
|
|
388
677
|
|
|
678
|
+
// Also persist locally
|
|
679
|
+
try {
|
|
680
|
+
if (this._sessionState) this._saveSessionState();
|
|
681
|
+
} catch (e) { }
|
|
682
|
+
|
|
389
683
|
return this;
|
|
390
684
|
}
|
|
391
685
|
/**
|
|
@@ -395,13 +689,21 @@ class RealtimeClient extends eventemitter3_1.EventEmitter {
|
|
|
395
689
|
async saveSession(authStateHelper) {
|
|
396
690
|
if (!authStateHelper || !authStateHelper.saveMqttSession) {
|
|
397
691
|
console.warn('[RealtimeClient] No authStateHelper provided');
|
|
692
|
+
// still save local state
|
|
693
|
+
try { this._saveSessionState(); } catch (e) { }
|
|
398
694
|
return false;
|
|
399
695
|
}
|
|
400
696
|
await authStateHelper.saveMqttSession(this);
|
|
697
|
+
// also persist locally
|
|
698
|
+
try { this._saveSessionState(); } catch (e) { }
|
|
401
699
|
return true;
|
|
402
700
|
}
|
|
403
701
|
disconnect() {
|
|
404
702
|
this.safeDisconnect = true;
|
|
703
|
+
// clear metrics interval when shutting down gracefully
|
|
704
|
+
try {
|
|
705
|
+
if (this._metricsInterval) clearInterval(this._metricsInterval);
|
|
706
|
+
} catch (e) { }
|
|
405
707
|
return this.mqtt?.disconnect() ?? Promise.resolve();
|
|
406
708
|
}
|
|
407
709
|
graphQlSubscribe(sub) {
|
|
@@ -435,6 +737,13 @@ class RealtimeClient extends eventemitter3_1.EventEmitter {
|
|
|
435
737
|
throw new mqtts_1.IllegalStateError('connect() must be called before irisSubscribe()');
|
|
436
738
|
}
|
|
437
739
|
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 */ }
|
|
438
747
|
return this.commands.updateSubscriptions({
|
|
439
748
|
topic: constants_1.Topics.IRIS_SUB,
|
|
440
749
|
data: {
|
|
@@ -444,6 +753,68 @@ class RealtimeClient extends eventemitter3_1.EventEmitter {
|
|
|
444
753
|
},
|
|
445
754
|
});
|
|
446
755
|
}
|
|
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
|
+
|
|
447
818
|
}
|
|
448
819
|
exports.RealtimeClient = RealtimeClient;
|
|
449
820
|
//# sourceMappingURL=realtime.client.js.map
|
package/dist/shared/shared.js
CHANGED
|
@@ -21,10 +21,10 @@ function createFbnsUserAgent(ig) {
|
|
|
21
21
|
FBBD: 'Android',
|
|
22
22
|
FBPN: 'com.instagram.android',
|
|
23
23
|
FBDV: deviceName.trim(),
|
|
24
|
-
FBSV:
|
|
24
|
+
FBSV: androidVersion.split('/')[1],
|
|
25
25
|
FBLR: '0',
|
|
26
26
|
FBBK: '1',
|
|
27
|
-
FBCA: '
|
|
27
|
+
FBCA: 'x86:armeabi-v7a',
|
|
28
28
|
};
|
|
29
29
|
return `[${Object.entries(params)
|
|
30
30
|
.map(p => p.join('/'))
|
|
@@ -32,7 +32,7 @@ function createFbnsUserAgent(ig) {
|
|
|
32
32
|
}
|
|
33
33
|
exports.createFbnsUserAgent = createFbnsUserAgent;
|
|
34
34
|
function compressDeflate(data) {
|
|
35
|
-
return deflatePromise(data, { level:
|
|
35
|
+
return deflatePromise(data, { level: 9 });
|
|
36
36
|
}
|
|
37
37
|
exports.compressDeflate = compressDeflate;
|
|
38
38
|
function unzipAsync(data) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nodejs-insta-private-api-mqtt",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.5",
|
|
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": {
|