nodejs-insta-private-api-mqtt 1.1.7 → 1.1.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@ I post many versions of the project because Instagram changes the protocol almos
|
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
|
|
7
|
-
# nodejs-insta-private-api
|
|
7
|
+
# nodejs-insta-private-api-mqtt
|
|
8
8
|
|
|
9
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.
|
|
10
10
|
|
package/dist/core/client.js
CHANGED
|
@@ -72,10 +72,61 @@ class IgApiClient extends EventEmitter {
|
|
|
72
72
|
* NOTE: removed automatic realtime connection attempt.
|
|
73
73
|
*/
|
|
74
74
|
async login(credentials) {
|
|
75
|
+
// ANTI-BOT: Automatically simulate app launch if not already done recently
|
|
76
|
+
if (this.state.deviceId) {
|
|
77
|
+
try {
|
|
78
|
+
await this.simulateAppStart();
|
|
79
|
+
// Real behavior: fetch contact permissions, explore, etc.
|
|
80
|
+
await this.simulateAndroidBehavior();
|
|
81
|
+
} catch (e) {}
|
|
82
|
+
await new Promise(resolve => setTimeout(resolve, 3000));
|
|
83
|
+
}
|
|
75
84
|
const result = await this.account.login(credentials);
|
|
76
85
|
return result;
|
|
77
86
|
}
|
|
78
87
|
|
|
88
|
+
/**
|
|
89
|
+
* Additional Android-specific behavior simulation
|
|
90
|
+
*/
|
|
91
|
+
async simulateAndroidBehavior() {
|
|
92
|
+
if (this.state.verbose) console.log('[Anti-Bot] Simulating Android-specific background behaviors...');
|
|
93
|
+
|
|
94
|
+
// 0. Pre-login notification suppression / Trust signal
|
|
95
|
+
try {
|
|
96
|
+
await this.request.send({
|
|
97
|
+
url: '/api/v1/accounts/get_presence_disabled/',
|
|
98
|
+
method: 'GET',
|
|
99
|
+
});
|
|
100
|
+
} catch (e) {}
|
|
101
|
+
|
|
102
|
+
// 1. Fetch Contact Permission (often requested on start/login)
|
|
103
|
+
try {
|
|
104
|
+
await this.request.send({
|
|
105
|
+
url: '/api/v1/accounts/contact_point_pref/',
|
|
106
|
+
method: 'GET',
|
|
107
|
+
});
|
|
108
|
+
} catch (e) {}
|
|
109
|
+
|
|
110
|
+
// 2. Fetch Zero Rating (Mobile data settings)
|
|
111
|
+
try {
|
|
112
|
+
await this.request.send({
|
|
113
|
+
url: '/api/v1/zero/get_headers/',
|
|
114
|
+
method: 'GET',
|
|
115
|
+
});
|
|
116
|
+
} catch (e) {}
|
|
117
|
+
|
|
118
|
+
// 3. Fetch Banyan (Explore/Search context)
|
|
119
|
+
try {
|
|
120
|
+
await this.request.send({
|
|
121
|
+
url: '/api/v1/banyan/banyan/',
|
|
122
|
+
method: 'GET',
|
|
123
|
+
qs: {
|
|
124
|
+
views: '["direct_user_search","direct_search_context","direct_share_sheet"]',
|
|
125
|
+
},
|
|
126
|
+
});
|
|
127
|
+
} catch (e) {}
|
|
128
|
+
}
|
|
129
|
+
|
|
79
130
|
async logout() {
|
|
80
131
|
return await this.account.logout();
|
|
81
132
|
}
|
|
@@ -116,6 +167,88 @@ class IgApiClient extends EventEmitter {
|
|
|
116
167
|
// === UTILITY HELPER METHODS
|
|
117
168
|
// -------------------------------
|
|
118
169
|
|
|
170
|
+
/**
|
|
171
|
+
* Simulates the exact sequence of requests an Android device makes when opening the Instagram app.
|
|
172
|
+
* This should be called BEFORE login to establish the device's presence as a legitimate app instance.
|
|
173
|
+
*/
|
|
174
|
+
async simulateAppStart() {
|
|
175
|
+
if (this.state.verbose) console.log('[Anti-Bot] Starting Android App Launch Simulation...');
|
|
176
|
+
|
|
177
|
+
// 1. Emulate Launcher Sync (Fetching app configuration)
|
|
178
|
+
// This tells Instagram "I am opening the app and checking for feature flags"
|
|
179
|
+
try {
|
|
180
|
+
await this.request.send({
|
|
181
|
+
url: '/api/v1/launcher/sync/',
|
|
182
|
+
method: 'POST',
|
|
183
|
+
form: {
|
|
184
|
+
configs: 'ig_android_launcher_sync_config',
|
|
185
|
+
id: this.state.uuid,
|
|
186
|
+
},
|
|
187
|
+
});
|
|
188
|
+
if (this.state.verbose) console.log('[Anti-Bot] Launcher Sync: OK');
|
|
189
|
+
} catch (e) {
|
|
190
|
+
if (this.state.verbose) console.warn('[Anti-Bot] Launcher Sync Failed (Non-fatal):', e.message);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// 2. Emulate QE Sync (Quick Experiment Sync)
|
|
194
|
+
// Instagram apps check for A/B testing experiments on launch
|
|
195
|
+
try {
|
|
196
|
+
await this.request.send({
|
|
197
|
+
url: '/api/v1/qe/sync/',
|
|
198
|
+
method: 'POST',
|
|
199
|
+
form: {
|
|
200
|
+
experiments: 'ig_android_qe_sync_config',
|
|
201
|
+
id: this.state.uuid,
|
|
202
|
+
},
|
|
203
|
+
});
|
|
204
|
+
if (this.state.verbose) console.log('[Anti-Bot] QE Sync: OK');
|
|
205
|
+
} catch (e) {
|
|
206
|
+
if (this.state.verbose) console.warn('[Anti-Bot] QE Sync Failed (Non-fatal):', e.message);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// 3. Log Attribution (App Launch Event)
|
|
210
|
+
// This is crucial. Real apps report "I have launched".
|
|
211
|
+
try {
|
|
212
|
+
await this.request.send({
|
|
213
|
+
url: '/api/v1/attribution/log_attribution/',
|
|
214
|
+
method: 'POST',
|
|
215
|
+
form: {
|
|
216
|
+
adb_ifa: this.state.adsId, // Advertising ID
|
|
217
|
+
did: this.state.deviceId,
|
|
218
|
+
event_name: 'app_launch',
|
|
219
|
+
extra: JSON.stringify({
|
|
220
|
+
clock_skew: 0,
|
|
221
|
+
}),
|
|
222
|
+
},
|
|
223
|
+
});
|
|
224
|
+
if (this.state.verbose) console.log('[Anti-Bot] Attribution Logged: OK');
|
|
225
|
+
} catch (e) {
|
|
226
|
+
if (this.state.verbose) console.warn('[Anti-Bot] Attribution Log Failed (Non-fatal):', e.message);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// 4. Check App Updates (Optional but realistic)
|
|
230
|
+
// Simulates checking if a new version is available (often happens on launch)
|
|
231
|
+
if (this.state.verbose) console.log('[Anti-Bot] App Simulation Complete. Ready to Login.');
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Login -> uses account.login. Returns whatever account.login returns.
|
|
236
|
+
* NOTE: removed automatic realtime connection attempt.
|
|
237
|
+
*/
|
|
238
|
+
async login(credentials) {
|
|
239
|
+
// ANTI-BOT: Automatically simulate app launch if not already done recently
|
|
240
|
+
// We check if we have a device ID, if so, we run simulation.
|
|
241
|
+
// Ideally we should track if simulated, but for now we run it on every login call to be safe.
|
|
242
|
+
if (this.state.deviceId) {
|
|
243
|
+
await this.simulateAppStart();
|
|
244
|
+
// Small delay to mimic human speed/app loading time
|
|
245
|
+
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
const result = await this.account.login(credentials);
|
|
249
|
+
return result;
|
|
250
|
+
}
|
|
251
|
+
|
|
119
252
|
/**
|
|
120
253
|
* Generic retry helper for async functions.
|
|
121
254
|
*
|
|
@@ -227,6 +360,122 @@ class IgApiClient extends EventEmitter {
|
|
|
227
360
|
return this.state.verbose;
|
|
228
361
|
}
|
|
229
362
|
|
|
363
|
+
/**
|
|
364
|
+
* Simulates the exact sequence of requests an Android device makes when opening the Instagram app.
|
|
365
|
+
*/
|
|
366
|
+
async simulateAppStart() {
|
|
367
|
+
if (this.state.verbose) console.log('[Anti-Bot] Starting Android App Launch Simulation...');
|
|
368
|
+
|
|
369
|
+
// 1. Emulate Launcher Sync
|
|
370
|
+
try {
|
|
371
|
+
await this.request.send({
|
|
372
|
+
url: '/api/v1/launcher/sync/',
|
|
373
|
+
method: 'POST',
|
|
374
|
+
form: {
|
|
375
|
+
configs: 'ig_android_launcher_sync_config',
|
|
376
|
+
id: this.state.uuid,
|
|
377
|
+
},
|
|
378
|
+
});
|
|
379
|
+
if (this.state.verbose) console.log('[Anti-Bot] Launcher Sync: OK');
|
|
380
|
+
} catch (e) {
|
|
381
|
+
if (this.state.verbose) console.warn('[Anti-Bot] Launcher Sync Failed (Non-fatal):', e.message);
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
// 2. Emulate QE Sync
|
|
385
|
+
try {
|
|
386
|
+
await this.request.send({
|
|
387
|
+
url: '/api/v1/qe/sync/',
|
|
388
|
+
method: 'POST',
|
|
389
|
+
form: {
|
|
390
|
+
experiments: 'ig_android_qe_sync_config',
|
|
391
|
+
id: this.state.uuid,
|
|
392
|
+
},
|
|
393
|
+
});
|
|
394
|
+
if (this.state.verbose) console.log('[Anti-Bot] QE Sync: OK');
|
|
395
|
+
} catch (e) {
|
|
396
|
+
if (this.state.verbose) console.warn('[Anti-Bot] QE Sync Failed (Non-fatal):', e.message);
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
// 3. Log Attribution
|
|
400
|
+
try {
|
|
401
|
+
await this.request.send({
|
|
402
|
+
url: '/api/v1/attribution/log_attribution/',
|
|
403
|
+
method: 'POST',
|
|
404
|
+
form: {
|
|
405
|
+
adb_ifa: this.state.adsId,
|
|
406
|
+
did: this.state.deviceId,
|
|
407
|
+
event_name: 'app_launch',
|
|
408
|
+
extra: JSON.stringify({
|
|
409
|
+
clock_skew: 0,
|
|
410
|
+
}),
|
|
411
|
+
},
|
|
412
|
+
});
|
|
413
|
+
if (this.state.verbose) console.log('[Anti-Bot] Attribution Logged: OK');
|
|
414
|
+
} catch (e) {
|
|
415
|
+
if (this.state.verbose) console.warn('[Anti-Bot] Attribution Log Failed (Non-fatal):', e.message);
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
if (this.state.verbose) console.log('[Anti-Bot] App Simulation Complete. Ready to Login.');
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
/**
|
|
422
|
+
* Simulates the exact sequence of requests an Android device makes when opening the Instagram app.
|
|
423
|
+
*/
|
|
424
|
+
async simulateAppStart() {
|
|
425
|
+
if (this.state.verbose) console.log('[Anti-Bot] Starting Android App Launch Simulation...');
|
|
426
|
+
|
|
427
|
+
// 1. Emulate Launcher Sync
|
|
428
|
+
try {
|
|
429
|
+
await this.request.send({
|
|
430
|
+
url: '/api/v1/launcher/sync/',
|
|
431
|
+
method: 'POST',
|
|
432
|
+
form: {
|
|
433
|
+
configs: 'ig_android_launcher_sync_config',
|
|
434
|
+
id: this.state.uuid,
|
|
435
|
+
},
|
|
436
|
+
});
|
|
437
|
+
if (this.state.verbose) console.log('[Anti-Bot] Launcher Sync: OK');
|
|
438
|
+
} catch (e) {
|
|
439
|
+
if (this.state.verbose) console.warn('[Anti-Bot] Launcher Sync Failed (Non-fatal):', e.message);
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
// 2. Emulate QE Sync
|
|
443
|
+
try {
|
|
444
|
+
await this.request.send({
|
|
445
|
+
url: '/api/v1/qe/sync/',
|
|
446
|
+
method: 'POST',
|
|
447
|
+
form: {
|
|
448
|
+
experiments: 'ig_android_qe_sync_config',
|
|
449
|
+
id: this.state.uuid,
|
|
450
|
+
},
|
|
451
|
+
});
|
|
452
|
+
if (this.state.verbose) console.log('[Anti-Bot] QE Sync: OK');
|
|
453
|
+
} catch (e) {
|
|
454
|
+
if (this.state.verbose) console.warn('[Anti-Bot] QE Sync Failed (Non-fatal):', e.message);
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
// 3. Log Attribution
|
|
458
|
+
try {
|
|
459
|
+
await this.request.send({
|
|
460
|
+
url: '/api/v1/attribution/log_attribution/',
|
|
461
|
+
method: 'POST',
|
|
462
|
+
form: {
|
|
463
|
+
adb_ifa: this.state.adsId,
|
|
464
|
+
did: this.state.deviceId,
|
|
465
|
+
event_name: 'app_launch',
|
|
466
|
+
extra: JSON.stringify({
|
|
467
|
+
clock_skew: 0,
|
|
468
|
+
}),
|
|
469
|
+
},
|
|
470
|
+
});
|
|
471
|
+
if (this.state.verbose) console.log('[Anti-Bot] Attribution Logged: OK');
|
|
472
|
+
} catch (e) {
|
|
473
|
+
if (this.state.verbose) console.warn('[Anti-Bot] Attribution Log Failed (Non-fatal):', e.message);
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
if (this.state.verbose) console.log('[Anti-Bot] App Simulation Complete. Ready to Login.');
|
|
477
|
+
}
|
|
478
|
+
|
|
230
479
|
/**
|
|
231
480
|
* safeDestroy: a slightly more robust destroy which attempts to stop requests, etc.
|
|
232
481
|
*/
|
|
@@ -2,18 +2,43 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.MQTToTConnection = void 0;
|
|
4
4
|
const thrift_1 = require("../thrift");
|
|
5
|
+
|
|
5
6
|
class MQTToTConnection {
|
|
6
7
|
constructor(connectionData) {
|
|
7
8
|
this.fbnsConnectionData = connectionData;
|
|
8
9
|
}
|
|
10
|
+
|
|
9
11
|
toThrift() {
|
|
10
|
-
|
|
12
|
+
// Clone safely – DO NOT mutate original objects used by MQTT
|
|
13
|
+
const connCopy = Object.assign({}, this.fbnsConnectionData);
|
|
14
|
+
const originalClientInfo = this.fbnsConnectionData && this.fbnsConnectionData.clientInfo;
|
|
15
|
+
const clientInfoCopy = originalClientInfo ? Object.assign({}, originalClientInfo) : undefined;
|
|
16
|
+
|
|
17
|
+
if (clientInfoCopy) {
|
|
18
|
+
// ✅ MQTT REQUIRES STRING
|
|
19
|
+
if (typeof clientInfoCopy.userAgent !== 'string' || !clientInfoCopy.userAgent) {
|
|
20
|
+
clientInfoCopy.userAgent = 'Instagram 289.0.0.77.109 Android';
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// ✅ MQTT REQUIRES STRING
|
|
24
|
+
if (typeof clientInfoCopy.clientType !== 'string' || !clientInfoCopy.clientType) {
|
|
25
|
+
clientInfoCopy.clientType = 'com.instagram.android';
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
connCopy.clientInfo = clientInfoCopy;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// ✅ Thrift will serialize binary fields correctly
|
|
32
|
+
return (0, thrift_1.thriftWriteFromObject)(connCopy, MQTToTConnection.thriftConfig);
|
|
11
33
|
}
|
|
34
|
+
|
|
12
35
|
toString() {
|
|
13
36
|
return this.toThrift().toString();
|
|
14
37
|
}
|
|
15
38
|
}
|
|
39
|
+
|
|
16
40
|
exports.MQTToTConnection = MQTToTConnection;
|
|
41
|
+
|
|
17
42
|
MQTToTConnection.thriftConfig = [
|
|
18
43
|
thrift_1.ThriftDescriptors.binary('clientIdentifier', 1),
|
|
19
44
|
thrift_1.ThriftDescriptors.binary('willTopic', 2),
|
|
@@ -47,10 +72,8 @@ MQTToTConnection.thriftConfig = [
|
|
|
47
72
|
thrift_1.ThriftDescriptors.int64('anotherUnknown', 26),
|
|
48
73
|
]),
|
|
49
74
|
thrift_1.ThriftDescriptors.binary('password', 5),
|
|
50
|
-
// polyfill
|
|
51
75
|
thrift_1.ThriftDescriptors.int16('unknown', 5),
|
|
52
76
|
thrift_1.ThriftDescriptors.listOfBinary('getDiffsRequests', 6),
|
|
53
77
|
thrift_1.ThriftDescriptors.binary('zeroRatingTokenHash', 9),
|
|
54
78
|
thrift_1.ThriftDescriptors.mapBinaryBinary('appSpecificInfo', 10),
|
|
55
79
|
];
|
|
56
|
-
//# sourceMappingURL=mqttot.connection.js.map
|
|
@@ -189,8 +189,18 @@ class RealtimeClient extends eventemitter3_1.EventEmitter {
|
|
|
189
189
|
}
|
|
190
190
|
}
|
|
191
191
|
constructConnection() {
|
|
192
|
-
|
|
193
|
-
const
|
|
192
|
+
// --- SAFE userAgent resolution (fix for "Value of userAgent is not a string") ---
|
|
193
|
+
const userAgent =
|
|
194
|
+
typeof this.ig.state.userAgent === 'string'
|
|
195
|
+
? this.ig.state.userAgent
|
|
196
|
+
: typeof this.ig.state.appUserAgent === 'string'
|
|
197
|
+
? this.ig.state.appUserAgent
|
|
198
|
+
: typeof this.ig.state.deviceString === 'string'
|
|
199
|
+
? this.ig.state.deviceString
|
|
200
|
+
: 'Instagram 155.0.0.37.107 Android';
|
|
201
|
+
|
|
202
|
+
// Ensure deviceId is a string to avoid substring errors
|
|
203
|
+
const deviceId = String(this.ig.state.phoneId || this.ig.state.deviceId || 'device_unknown');
|
|
194
204
|
let sessionid;
|
|
195
205
|
// First try: Extract from JWT authorization header (PRIMARY METHOD)
|
|
196
206
|
sessionid = this.extractSessionIdFromJWT();
|
|
@@ -225,15 +235,15 @@ class RealtimeClient extends eventemitter3_1.EventEmitter {
|
|
|
225
235
|
}
|
|
226
236
|
// Last resort: Generate from userId + timestamp
|
|
227
237
|
if (!sessionid) {
|
|
228
|
-
const userId = this.ig.state.cookieUserId;
|
|
229
|
-
sessionid = userId + '_' + Date.now();
|
|
238
|
+
const userId = this.ig.state.cookieUserId || this.ig.state.userId || '0';
|
|
239
|
+
sessionid = String(userId) + '_' + Date.now();
|
|
230
240
|
this.realtimeDebug(`SessionID generated (fallback): ${sessionid}`);
|
|
231
241
|
}
|
|
232
242
|
const password = `sessionid=${sessionid}`;
|
|
233
243
|
this.connection = new mqttot_1.MQTToTConnection({
|
|
234
244
|
clientIdentifier: deviceId.substring(0, 20),
|
|
235
245
|
clientInfo: {
|
|
236
|
-
userId: BigInt(Number(this.ig.state.cookieUserId)),
|
|
246
|
+
userId: BigInt(Number(this.ig.state.cookieUserId || this.ig.state.userId || 0)),
|
|
237
247
|
userAgent,
|
|
238
248
|
clientCapabilities: 183,
|
|
239
249
|
endpointCapabilities: 0,
|
|
@@ -263,7 +273,7 @@ class RealtimeClient extends eventemitter3_1.EventEmitter {
|
|
|
263
273
|
presence_subscribe: '17846944882223835',
|
|
264
274
|
}),
|
|
265
275
|
'User-Agent': userAgent,
|
|
266
|
-
'Accept-Language': this.ig.state.language.replace('_', '-'),
|
|
276
|
+
'Accept-Language': (this.ig.state.language || 'en_US').replace('_', '-'),
|
|
267
277
|
},
|
|
268
278
|
});
|
|
269
279
|
}
|
|
@@ -88,6 +88,25 @@ class AccountRepository extends Repository {
|
|
|
88
88
|
|
|
89
89
|
// Handle common login errors
|
|
90
90
|
const body = response.body;
|
|
91
|
+
|
|
92
|
+
// ANTI-BOT: Handle choice/checkpoint automatically if requested
|
|
93
|
+
if (body.two_factor_required || body.step_name === 'select_verify_method') {
|
|
94
|
+
// Some versions of the API return checkpoint info here
|
|
95
|
+
if (this.client.state.verbose) console.log('[Anti-Bot] Checkpoint/2FA detected, attempting to bypass notification...');
|
|
96
|
+
try {
|
|
97
|
+
await this.client.request.send({
|
|
98
|
+
url: '/api/v1/accounts/login_confirm/',
|
|
99
|
+
method: 'POST',
|
|
100
|
+
form: this.client.request.sign({
|
|
101
|
+
_csrftoken: this.client.state.cookieCsrfToken,
|
|
102
|
+
guid: this.client.state.uuid,
|
|
103
|
+
device_id: this.client.state.deviceId,
|
|
104
|
+
is_relogin: true,
|
|
105
|
+
}),
|
|
106
|
+
});
|
|
107
|
+
} catch (e) {}
|
|
108
|
+
}
|
|
109
|
+
|
|
91
110
|
if (body.two_factor_required) {
|
|
92
111
|
const err = new Error('Two factor authentication required');
|
|
93
112
|
err.name = 'IgLoginTwoFactorRequiredError';
|
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.9",
|
|
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": {
|