De-Tracker (Anti-Tracking + Facebook Safe)

Removes tracking params, disables link ping, unwraps redirectors, and prevents Facebook tracking without blocking the site.

// ==UserScript==
// @name         De-Tracker (Anti-Tracking + Facebook Safe)
// @namespace    https://gf.qytechs.cn/en/users/1258004-jake-shue
// @version      2.2
// @description  Removes tracking params, disables link ping, unwraps redirectors, and prevents Facebook tracking without blocking the site.
// @author       you
// @match        *://*/*
// @run-at       document-start
// @grant        none
// @license     none
// ==/UserScript==

(function() {
  'use strict';

  const TRACK_PARAMS = new Set([
    'utm_source','utm_medium','utm_campaign','utm_term','utm_content','utm_id','utm_name','utm_reader','utm_referrer',
    'ga_source','ga_medium','ga_campaign','ga_content','ga_term',
    'gclid','dclid','gbraid','wbraid','fbclid','twclid','ttclid','yclid','msclkid',
    'mc_eid','mc_cid','_hsenc','_hsmi','vero_conv','vero_id','mkt_tok','oly_anon_id','oly_enc_id',
    'pk_campaign','pk_kwd','pk_source','pk_medium',
    'igshid','spm','si','ref','ref_src','ref_url','src','sc_channel','sc_campaign','sc_content','sc_medium',
    'ved','ei','usg','oq','uact','source','rlz','sxsrf',
    'campaign','campaignId','tracking','track','trk','ncid','ncid_src'
  ]);

  const REDIRECT_PARAM_CANDIDATES = [
    'url','u','dest','destination','redir','redirect','redirect_url','redirect_uri',
    'r','to','target','q','link','l','out','o','external','next','continue','goto','href','request','RU'
  ];

  const REDIRECT_PATTERNS = [
    {hostRe: /(^|\.)google\.[a-z.]+$/i, pathRe: /^\/(url|imgres|aclk|amp\/s|amp\/t)/i},
    {hostRe: /(^|\.)facebook\.com$/i, pathRe: /^\/l\.php$/i},
    {hostRe: /(^|\.)l\.facebook\.com$/i, pathRe: /^\//},
    {hostRe: /(^|\.)out\.reddit\.com$/i, pathRe: /^\//},
    {hostRe: /(^|\.)link\.medium\.com$/i, pathRe: /^\//},
    {hostRe: /(^|\.)steamcommunity\.com$/i, pathRe: /^\/linkfilter\/$/i},
    {hostRe: /(^|\.)youtube\.com$/i, pathRe: /^\/redirect$/i},
    {hostRe: /(^|\.)r\.t\.umblr\.com$/i, pathRe: /^\//},
    {hostRe: /(^|\.)news\.yahoo\.com$/i, pathRe: /^\//},
    {hostRe: /(^|\.)safelinks\.protection\.outlook\.com$/i, pathRe: /^\//},
    {hostRe: /(^|\.)lnkd\.in$/i, pathRe: /^\//}
  ];

  function safeDecode(str) {
    try { return decodeURIComponent(str); } catch { return str; }
  }

  function stripTrackParams(u) {
    const url = new URL(u, location.href);
    for (const key of [...url.searchParams.keys()]) {
      if (TRACK_PARAMS.has(key)) url.searchParams.delete(key);
    }
    if (url.hash && url.hash.includes('=')) {
      const hash = url.hash.replace(/^#/, '');
      const sp = new URLSearchParams(hash);
      let changed = false;
      for (const key of [...sp.keys()]) {
        if (TRACK_PARAMS.has(key)) {
          sp.delete(key);
          changed = true;
        }
      }
      if (changed) {
        const newHash = sp.toString();
        url.hash = newHash ? '#' + newHash : '';
      }
    }
    return url.toString();
  }

  function looksLikeRedirector(u) {
    const url = new URL(u, location.href);
    return REDIRECT_PATTERNS.some(p => p.hostRe.test(url.hostname) && p.pathRe.test(url.pathname));
  }

  function extractRedirectTarget(u) {
    const url = new URL(u, location.href);
    for (const key of REDIRECT_PARAM_CANDIDATES) {
      const v = url.searchParams.get(key);
      if (v && /^https?:\/\//i.test(v)) return safeDecode(v);
    }
    for (const [key, value] of url.searchParams.entries()) {
      if (!value || value.length < 8) continue;
      if (/^[A-Za-z0-9+/_-]+=*$/.test(value)) {
        try {
          const decoded = atob(value.replace(/-/g, '+').replace(/_/g, '/'));
          if (/^https?:\/\//i.test(decoded)) return decoded;
        } catch {}
      }
    }
    return null;
  }

  function cleanLinkHref(a) {
    if (!a || !a.href) return;
    if (!/^https?:/i.test(a.href)) return;
    if (looksLikeRedirector(a.href)) {
      const target = extractRedirectTarget(a.href);
      if (target) a.href = target;
    }
    a.href = stripTrackParams(a.href);
    if (a.hasAttribute('ping')) a.removeAttribute('ping');
    ['onmousedown','onclick','onmouseup'].forEach(k => {
      if (a.hasAttribute(k)) a.removeAttribute(k);
    });
    ['saferedirecturl','tracking','clicktrack','click_id'].forEach(k => {
      const dk = 'data-' + k;
      if (a.hasAttribute(dk)) a.removeAttribute(dk);
    });
  }

  function cleanAllLinks(root=document) {
    const links = root.querySelectorAll ? root.querySelectorAll('a[href]') : [];
    for (const a of links) cleanLinkHref(a);
  }

  function cleanLocationBar() {
    try {
      const cleaned = stripTrackParams(location.href);
      if (cleaned !== location.href) {
        history.replaceState(history.state, '', cleaned);
      }
    } catch {}
  }

  function onPreClick(e) {
    let el = e.target;
    while (el && el !== document && !(el instanceof HTMLAnchorElement)) el = el.parentElement;
    if (el && el instanceof HTMLAnchorElement) {
      cleanLinkHref(el);
    }
  }

  function onCopy(e) {
    const sel = document.getSelection();
    if (!sel) return;
    const node = sel.anchorNode && sel.anchorNode.parentElement;
    const a = node && node.closest && node.closest('a[href]');
    if (a && /^https?:/i.test(a.href)) {
      const cleaned = stripTrackParams(a.href);
      try {
        e.clipboardData.setData('text/plain', cleaned);
        e.preventDefault();
      } catch {}
    }
  }

  function patchHistory() {
    const origPush = history.pushState;
    const origReplace = history.replaceState;
    history.pushState = function(state, title, url) {
      if (typeof url === 'string') url = stripTrackParams(new URL(url, location.href).toString());
      const ret = origPush.apply(this, [state, title, url]);
      queueMicrotask(cleanLocationBar);
      return ret;
    };
    history.replaceState = function(state, title, url) {
      if (typeof url === 'string') url = stripTrackParams(new URL(url, location.href).toString());
      const ret = origReplace.apply(this, [state, title, url]);
      queueMicrotask(cleanLocationBar);
      return ret;
    };
    window.addEventListener('popstate', cleanLocationBar, true);
  }

  function startObserver() {
    const obs = new MutationObserver(muts => {
      for (const m of muts) {
        if (m.type === 'childList') {
          m.addedNodes.forEach(node => {
            if (node && node.nodeType === 1) {
              if (node.tagName === 'A') {
                cleanLinkHref(node);
              } else {
                cleanAllLinks(node);
              }
            }
          });
        } else if (m.type === 'attributes' && m.target.tagName === 'A' && m.attributeName === 'href') {
          cleanLinkHref(m.target);
        }
      }
    });
    obs.observe(document.documentElement || document, {
      childList: true,
      subtree: true,
      attributes: true,
      attributeFilter: ['href']
    });
  }

  function init() {
    patchHistory();
    cleanLocationBar();
    cleanAllLinks(document);
    startObserver();
    document.addEventListener('click', onPreClick, true);
    document.addEventListener('auxclick', onPreClick, true);
    document.addEventListener('copy', onCopy, true);
  }

  try { init(); } catch {}
  if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', () => { try { init(); } catch {} }, { once: true });
  }
})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址