BigNaturals / Invisible Story Viewer for IG

// ==UserScript==
// @name               Invisible Story Viewer for IG
// @name:pt-BR         Visualizador de Stories Invisível para IG
// @name:es            Visor de Stories Invisível para IG
// @name:hi            IG के लिए अदृश्य स्टोरी व्यूअर
// @name:id            Penonton Stories Anonim untuk IG
// @name:ja            IG の見えないストーリービューアー
// @name:tr            IG için Görünmez Hikaye İzleyici
// @name:de            Unsichtbarer Story-Viewer für IG
// @name:it            Visualizzatore di storie invisibile per IG
// @name:fr            Visionneuse de stories invisible pour IG
// @name:fil           Invisible Story Viewer para sa IG
// @name:ko            IG용 인비저블 스토리 뷰어
// @name:ru            Невидимый просмотрщик историй для IG
// @name:zh-TW         IG 隱形限時動態檢視器
// @name:zh-CN         IG 隐形限时动态检视器
// @name:ar            مشاهد ستوري انستقرام غير مرئي
// @name:pl            Niewidzialny przeglądarka Stories na IG
// @name:sv            Osynlig Story-visare för IG
// @name:uk            Невидимий переглядач сторіз для IG
// @namespace          Violentmonkey Scripts
// @version            1.0
// @description        Watch stories anonymously. Optimized, silent and based on Mobile46's original script.
// @description:pt-BR  Assista stories anonimamente. Versão otimizada, silenciosa e baseada no script original de Mobile46.
// @description:es     Mira stories anónimamente. Versión optimizada, silenciosa y basada en el script original de Mobile46.
// @description:hi     कहानियों को गुमनाम रूप से देखें। अनुकूलित, शांत और Mobile46 के मूल स्क्रिप्ट पर आधारित।
// @description:id     Tonton stories secara anonim. Dioptimalkan, senyap, dan berdasarkan skrip asli Mobile46.
// @description:ja     ストーリーズを匿名で閲覧します。Mobile46の元スクリプトに基づいた、最適化されたサイレント版です。
// @description:tr     Hikayeleri anonim olarak izleyin. Optimize edilmiş, sessiz ve Mobile46'nın orijinal scriptine dayanmaktadır.
// @description:de     Stories anonym ansehen. Optimiert, geräuschlos und basierend auf dem Originalskript von Mobile46.
// @description:it     Guarda le storie in modo anonimo. Ottimizzato, silenzioso e basato sullo script originale di Mobile46.
// @description:fr     Regardez des stories de manière anonyme. Optimisé, silencieux et basé sur le script original de Mobile46.
// @description:fil     Manood ng mga story nang hindi nagpapakilala. Na-optimize, tahimik, at batay sa orihinal na script ng Mobile46.
// @description:ko     스토리를 익명으로 시청하세요. Mobile46의 원본 스크립트를 기반으로 최적화된 저소음 버전입니다.
// @description:ru     Просматривайте истории анонимно. Оптимизированная, бесшумная версия на основе оригинального скрипта Mobile46.
// @description:zh-TW  匿名觀看限時動態。經過優化、無聲,且基於 Mobile46 的原始腳本。
// @description:zh-CN  匿名观看限时动态。经过优化、无声,且基于 Mobile46 的原始脚本。
// @description:ar     مشاهدة الستوري بشكل مجهول. نسخة محسنة وصامتة تعتمد على سكربت Mobile46 الأصلي.
// @description:pl     Oglądaj Stories anonimowo. Zoptymalizowana, cicha wersja oparta na oryginalnym skrypcie Mobile46.
// @description:sv     Titta på stories anonymt. Optimerad, tyst och baserad na Mobile46s ursprungliga skript.
// @description:uk     Переглядайте сторіз анонімно. Оптимізована, безшумна версія на основі оригінального скрипта Mobile46.
// @author             Big Naturals
// @match              *://*.instagram.com/*
// @run-at             document-start
// @grant              none
// @license            MIT
// @contributionURL    https://www.blockchain.com/explorer/addresses/btc/16JXciLoAs6R8iQjmpKqWLSNreC1epfz6R
// @compatible chrome
// @compatible firefox
// @compatible opera
// @compatible safari
// @compatible edge
// @icon               https://www.google.com/s2/favicons?domain=www.instagram.com&sz=32
// ==/UserScript==

(function () {
  "use strict";

  const config = {
    urlPatterns: /media\/seen|api\/v1\/stories\/seen|stories_record_view|StoriesViewEvent/i,
    bodyPatterns: /view_seen_at|viewSeenAt|mark_seen|stories_seen|StoriesViewEventRequestMutation|story_reel_view/i,
  };

  const isStoryPage = () => window.location.pathname.startsWith('/stories/');

  function analyzeRequest(url, body) {
    if (!isStoryPage()) return false;

    if (config.urlPatterns.test(url)) return true;

    if (body) {
      let bodyStr = "";
      try {
        bodyStr = typeof body === "string" ? body : JSON.stringify(body);
      } catch (e) {
        bodyStr = String(body);
      }

      if (config.bodyPatterns.test(bodyStr)) return true;
    }

    return false;
  }

  const originalOpen = XMLHttpRequest.prototype.open;
  const originalSend = XMLHttpRequest.prototype.send;

  XMLHttpRequest.prototype.open = function (method, url) {
    this._url = url;
    return originalOpen.apply(this, arguments);
  };

  XMLHttpRequest.prototype.send = function (body) {
    if (analyzeRequest(this._url, body)) {
      this.abort();
      return;
    }
    return originalSend.apply(this, arguments);
  };

  const originalFetch = window.fetch;
  window.fetch = async function (resource, options) {
    const url = typeof resource === "string" ? resource : resource.url;
    const body = options ? options.body : null;

    if (analyzeRequest(url, body)) {
      return new Response(null, { status: 204, statusText: "No Content" });
    }
    return originalFetch.apply(this, arguments);
  };

  if (navigator.sendBeacon) {
    const originalBeacon = navigator.sendBeacon;
    navigator.sendBeacon = function (url, data) {
      if (analyzeRequest(url, data)) return true;
      return originalBeacon.apply(this, arguments);
    };
  }

  try {
    Object.defineProperty(XMLHttpRequest.prototype, "send", { writable: false, configurable: false });
    Object.defineProperty(window, "fetch", { writable: false, configurable: false });
  } catch (e) {}

})();