nodejs-insta-private-api-mqt 1.4.6 → 1.4.7

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.
@@ -13,7 +13,7 @@ const LOGIN_EXPERIMENTS = 'ig_android_fci_onboarding_friend_search,ig_android_de
13
13
  const FACEBOOK_ANALYTICS_APPLICATION_ID = '567067343352427';
14
14
  const FACEBOOK_OTA_FIELDS = 'update%7Bdownload_uri%2Cdownload_uri_delta_base%2Cversion_code_delta_base%2Cdownload_uri_delta%2Cfallback_to_full_update%2Cfile_size_delta%2Cversion_code%2Cpublished_date%2Cfile_size%2Cota_bundle_type%2Cresources_checksum%2Callowed_networks%2Crelease_id%7D';
15
15
  const FACEBOOK_ORCA_APPLICATION_ID = '124024574287414';
16
- const BLOKS_VERSION_ID = 'ce555e5500576acd8e84a66018f54a05720f2dce29f0bb5a1f97f0c10d6fac48';
16
+ const BLOKS_VERSION_ID = '5e47baf35c5a270b44c8906c8b99063564b30ef69779f3dee0b828bee2e4ef5b';
17
17
  const SUPPORTED_CAPABILITIES = [
18
18
  {
19
19
  value: '119.0,120.0,121.0,122.0,123.0,124.0,125.0,126.0,127.0,128.0,129.0,130.0,131.0,132.0,133.0,134.0,135.0,136.0,137.0,138.0,139.0,140.0,141.0,142.0',
@@ -345,6 +345,175 @@ class AccountRepository extends Repository {
345
345
  return fallback;
346
346
  }
347
347
 
348
+ /**
349
+ * Fetches the latest bloksVersionId from Instagram's launcher/sync endpoint.
350
+ * Saves result to bloks-version.json in the session folder (authinfo_instagram/<username>/).
351
+ * If fetch fails, keeps the existing bloksVersionId from state or falls back to hardcoded.
352
+ */
353
+ async _fetchAndSaveBloksVersion(username) {
354
+ const fs = require('fs');
355
+ const path = require('path');
356
+ const FALLBACK_BLOKS = '5e47baf35c5a270b44c8906c8b99063564b30ef69779f3dee0b828bee2e4ef5b';
357
+
358
+ // Session folder: authinfo_instagram/<username>/
359
+ const sessionDir = path.join(
360
+ process.cwd(),
361
+ 'authinfo_instagram',
362
+ username ? String(username) : 'default'
363
+ );
364
+ const bloksFile = path.join(sessionDir, 'bloks-version.json');
365
+
366
+ try { fs.mkdirSync(sessionDir, { recursive: true }); } catch (e) {}
367
+
368
+ const state = this.client.state || {};
369
+ const userAgent = this._resolveUserAgent();
370
+ const lang = state.language || 'ro_RO';
371
+ const nowSec = Math.floor(Date.now() / 1000);
372
+ const androidDeviceId =
373
+ state.androidDeviceId || state.deviceId ||
374
+ `android-${crypto.randomBytes(8).toString('hex')}`;
375
+ const deviceUUID = state.uuid || state.deviceId ||
376
+ (crypto.randomUUID ? crypto.randomUUID() : require('uuid').v4());
377
+ const networkProps = buildNetworkProperties();
378
+
379
+ // Current bloksVersionId as starting point
380
+ const currentBloks = state.bloksVersionId || FALLBACK_BLOKS;
381
+
382
+ // Helper: extract 64-char hex bloksVersionId from any object/string
383
+ const extractBloks = (body) => {
384
+ if (!body) return null;
385
+ const bodyStr = typeof body === 'string' ? body : JSON.stringify(body);
386
+ // Direct fields
387
+ if (body.bloks_version_id && /^[a-f0-9]{64}$/.test(body.bloks_version_id)) return body.bloks_version_id;
388
+ if (body.bloksVersionId && /^[a-f0-9]{64}$/.test(body.bloksVersionId)) return body.bloksVersionId;
389
+ // Search in JSON string
390
+ const m = bodyStr.match(/"bloks_version_id"\s*:\s*"([a-f0-9]{64})"/) ||
391
+ bodyStr.match(/"bloksVersionId"\s*:\s*"([a-f0-9]{64})"/) ||
392
+ bodyStr.match(/"x-bloks-version-id"\s*:\s*"([a-f0-9]{64})"/) ||
393
+ bodyStr.match(/([a-f0-9]{64})/);
394
+ if (m && m[1] && /^[a-f0-9]{64}$/.test(m[1])) return m[1];
395
+ return null;
396
+ };
397
+
398
+ // Helper: safe state setter for bloksVersionId
399
+ // state.bloksVersionId may be read-only (getter-only via Object.defineProperty)
400
+ // so we force-redefine it
401
+ const setBloksInState = (val) => {
402
+ try {
403
+ // Try direct set first
404
+ this.client.state.bloksVersionId = val;
405
+ return;
406
+ } catch (e) {}
407
+ try {
408
+ // Force redefine the property as writable
409
+ Object.defineProperty(this.client.state, 'bloksVersionId', {
410
+ value: val,
411
+ writable: true,
412
+ configurable: true,
413
+ enumerable: true,
414
+ });
415
+ return;
416
+ } catch (e) {}
417
+ try { this.client.state._bloksVersionId = val; } catch (e) {}
418
+ try {
419
+ // Last resort: patch constants object if that's where it comes from
420
+ if (this.client.state.constants) {
421
+ this.client.state.constants.BLOKS_VERSION_ID = val;
422
+ }
423
+ } catch (e) {}
424
+ };
425
+
426
+ const commonHeaders = {
427
+ 'accept-language': `${lang.replace('_', '-')}, en-US`,
428
+ 'content-type': 'application/x-www-form-urlencoded; charset=UTF-8',
429
+ 'x-bloks-version-id': currentBloks,
430
+ 'x-ig-android-id': androidDeviceId,
431
+ 'x-ig-app-id': String(state.fbAnalyticsApplicationId || '567067343352427'),
432
+ 'x-ig-app-locale': lang,
433
+ 'x-ig-device-locale': lang,
434
+ 'x-ig-mapped-locale': lang,
435
+ 'x-ig-capabilities': '3brTv10=',
436
+ 'x-ig-connection-type': 'MOBILE(UNKNOWN)',
437
+ 'x-fb-connection-type': 'MOBILE.UNKNOWN',
438
+ 'x-fb-client-ip': 'True',
439
+ 'x-fb-server-cluster': 'True',
440
+ 'x-fb-http-engine': 'MNS/TCP',
441
+ 'x-fb-network-properties': networkProps,
442
+ 'x-ig-timezone-offset': String(typeof state.timezoneOffset === 'number' ? state.timezoneOffset : 7200),
443
+ 'x-mid': state.mid || `aZ${crypto.randomBytes(8).toString('hex')}`,
444
+ 'x-pigeon-rawclienttime': `${nowSec}.${Math.floor(Math.random() * 1000).toString().padStart(3, '0')}`,
445
+ 'x-pigeon-session-id': state.pigeonSessionId || `UFS-${crypto.randomBytes(16).toString('hex')}-0`,
446
+ 'x-tigon-is-retry': 'False',
447
+ 'x-ig-www-claim': state.igWWWClaim || '0',
448
+ 'accept-encoding': 'gzip, deflate',
449
+ 'user-agent': userAgent,
450
+ 'x-fb-rmd': 'state=URL_ELIGIBLE',
451
+ };
452
+
453
+ let fetchedBloks = null;
454
+ let rawBody = null;
455
+ let endpointUsed = null;
456
+
457
+ try {
458
+ // Endpoint 1: launcher/sync — sometimes returns bloks in config
459
+ const r1 = await this.client.request.send({
460
+ method: 'POST',
461
+ url: '/api/v1/launcher/sync/',
462
+ form: this.client.request.sign({ id: deviceUUID, server_config_retrieval: '1' }),
463
+ headers: { ...commonHeaders, 'x-fb-friendly-name': 'IgApi: launcher/sync/' },
464
+ });
465
+ rawBody = r1.body;
466
+ fetchedBloks = extractBloks(r1.body);
467
+ if (fetchedBloks) endpointUsed = 'launcher/sync';
468
+ } catch (e) {}
469
+
470
+ if (!fetchedBloks) {
471
+ try {
472
+ // Endpoint 2: qe/sync — often contains bloks version in headers/body
473
+ const r2 = await this.client.request.send({
474
+ method: 'POST',
475
+ url: '/api/v1/qe/sync/',
476
+ form: this.client.request.sign({ id: deviceUUID, server_config_retrieval: '1' }),
477
+ headers: { ...commonHeaders, 'x-fb-friendly-name': 'IgApi: qe/sync/' },
478
+ });
479
+ // Check response headers too
480
+ const hdrs = r2.headers || {};
481
+ const bloksHeader = hdrs['x-bloks-version-id'] || hdrs['bloks-version-id'];
482
+ if (bloksHeader && /^[a-f0-9]{64}$/.test(bloksHeader)) {
483
+ fetchedBloks = bloksHeader;
484
+ endpointUsed = 'qe/sync (header)';
485
+ } else {
486
+ fetchedBloks = extractBloks(r2.body);
487
+ if (fetchedBloks) endpointUsed = 'qe/sync (body)';
488
+ }
489
+ if (!rawBody) rawBody = r2.body;
490
+ } catch (e) {}
491
+ }
492
+
493
+ const gotNew = !!(fetchedBloks && fetchedBloks !== currentBloks);
494
+ const bloksToUse = fetchedBloks || currentBloks;
495
+
496
+ // Update state safely
497
+ setBloksInState(bloksToUse);
498
+
499
+ // Save to bloks-version.json in session folder
500
+ const saveData = {
501
+ username: username || 'unknown',
502
+ bloksVersionId: bloksToUse,
503
+ previousBloksVersionId: currentBloks,
504
+ updatedAt: new Date().toISOString(),
505
+ gotNewVersion: gotNew,
506
+ fetchSuccess: !!fetchedBloks,
507
+ endpointUsed: endpointUsed || 'none — kept existing',
508
+ rawResponse: rawBody || null,
509
+ };
510
+ try {
511
+ fs.writeFileSync(bloksFile, JSON.stringify(saveData, null, 2), 'utf8');
512
+ } catch (writeErr) {}
513
+
514
+ return bloksToUse;
515
+ }
516
+
348
517
  async ensureCsrfToken() {
349
518
  const cookieToken = this.client.state.cookieCsrfToken;
350
519
  if (cookieToken && cookieToken !== 'missing' && cookieToken !== 'pending') {
@@ -883,6 +1052,9 @@ class AccountRepository extends Repository {
883
1052
 
884
1053
  if (!username || !password) throw new Error('Username and password are required');
885
1054
 
1055
+ // Fetch latest bloksVersionId from Instagram and save to session folder
1056
+ try { await this._fetchAndSaveBloksVersion(username); } catch (e) {}
1057
+
886
1058
  await this.ensureCsrfToken();
887
1059
 
888
1060
  try { await this._launcherMobileConfig(true); } catch (e) {}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nodejs-insta-private-api-mqt",
3
- "version": "1.4.6",
3
+ "version": "1.4.7",
4
4
  "description": "Complete Instagram MQTT protocol with full-featured REALTIME and REST API — all in one project.",
5
5
 
6
6
  "main": "dist/dist/index.js",