Greasy Fork is available in English.

Instagram Messages Only (Hard Redirect & Blocker)

Only allow Instagram Direct (/direct/*). Everything else is blocked and redirected to the inbox.

// ==UserScript==
// @name         Instagram Messages Only (Hard Redirect & Blocker)
// @namespace    ig-messages-only
// @version      1.0
// @description  Only allow Instagram Direct (/direct/*). Everything else is blocked and redirected to the inbox.
// @match        https://www.instagram.com/*
// @run-at       document-start
// @grant        none
// @license      MIT
// ==/UserScript==

(() => {
  'use strict';

  /* ───────── Config ───────── */
  const DM_INBOX_URL = 'https://www.instagram.com/direct/inbox/';
  // Set to false if you literally want *only* /direct/* and to block even login/challenge pages.
  const ALLOW_LOGIN_FLOWS = true;

  /* ───────── Helpers ───────── */
  const log = (...a) => console.info('[IG-DM-ONLY]', ...a);
  const sameOrigin = (u) => {
    try { return new URL(u, location.origin).origin === location.origin; }
    catch { return false; }
  };
  const pathOf = (u) => {
    try { return new URL(u, location.origin).pathname; }
    catch { return ''; }
  };

  const isDMPath = (p = location.pathname) => p.startsWith('/direct');
  const isLoginFlowPath = (p = location.pathname) =>
    /^\/(accounts|challenge|oauth)(\/|$)/.test(p);

  const isAllowedPath = (p = location.pathname) =>
    isDMPath(p) || (ALLOW_LOGIN_FLOWS && isLoginFlowPath(p));

  /* ───────── Page block styling (prevents flashes) ───────── */
  const style = document.createElement('style');
  style.textContent = `
    /* Hide everything while we enforce DM-only. Keeps SPA from showing feed/reels briefly. */
    .tm-dm-block body { opacity: 0 !important; pointer-events: none !important; }
  `;
  document.documentElement.appendChild(style);

  const blockContent = () => document.documentElement.classList.add('tm-dm-block');
  const allowContent = () => document.documentElement.classList.remove('tm-dm-block');

  const gotoDM = (reason = '') => {
    try { sessionStorage.setItem('tm_dm_only_reason', reason); } catch {}
    if (location.href !== DM_INBOX_URL) location.replace(DM_INBOX_URL);
  };

  const applyPolicyForLocation = () => {
    if (isAllowedPath()) {
      allowContent();
      return;
    }
    blockContent();
    gotoDM(`policy:${location.pathname}`);
  };

  /* ───────── Early enforcement on initial load ───────── */
  if (!isAllowedPath()) {
    blockContent();
    gotoDM('initial');
  }

  /* ───────── Intercept all in-app link clicks ───────── */
  function wireAnchors(root = document) {
    root.querySelectorAll('a[href]:not([data-tm-wired])').forEach(a => {
      a.dataset.tmWired = '1';
      a.addEventListener('click', (ev) => {
        const href = a.getAttribute('href') || '';
        if (!href || !sameOrigin(href)) return; // external links allowed
        const p = pathOf(href);
        if (!isAllowedPath(p)) {
          ev.preventDefault();
          ev.stopImmediatePropagation();
          blockContent();
          gotoDM(`click:${p}`);
        }
      }, { capture: true });
    });
  }

  wireAnchors();
  new MutationObserver(m => {
    for (const rec of m) {
      for (const n of rec.addedNodes || []) {
        if (n.nodeType === 1) wireAnchors(n);
      }
    }
  }).observe(document.documentElement, { childList: true, subtree: true });

  /* ───────── Hook SPA navigation (pushState/replaceState + back/forward) ───────── */
  (function hookHistory() {
    const wrap = (fn) => function (...args) {
      const rv = fn.apply(this, args);
      queueMicrotask(applyPolicyForLocation);
      return rv;
    };
    history.pushState    = wrap(history.pushState.bind(history));
    history.replaceState = wrap(history.replaceState.bind(history));
  })();

  window.addEventListener('popstate', applyPolicyForLocation);
  window.addEventListener('DOMContentLoaded', applyPolicyForLocation);

  // Safety: run once now, in case we landed on /direct/*
  applyPolicyForLocation();

  log('Messages-only mode active.');
})();