X/Twitter SOL CA Spam Killer (v2.3, stronger heuristics)

过滤 X/Twitter 搜索/时间线里的 Solana 合约广告:地址+CA邻近/重复地址/TICKER与版式词多条件命中;域名直拦;白名单;菜单开关

当前为 2025-08-13 提交的版本,查看 最新版本

// ==UserScript==
// @name         X/Twitter SOL CA Spam Killer (v2.3, stronger heuristics)
// @namespace    https://x.com/
// @version      2.3.0
// @description  过滤 X/Twitter 搜索/时间线里的 Solana 合约广告:地址+CA邻近/重复地址/TICKER与版式词多条件命中;域名直拦;白名单;菜单开关
// @match        https://x.com/*
// @match        https://twitter.com/*
// @run-at       document-start
// @grant        GM_registerMenuCommand
// ==/UserScript==

(function () {
  'use strict';

  /*** 配置 ***/
  const STORAGE_KEY = 'sol_ca_filter_enabled_v23';
  let ENABLED = JSON.parse(localStorage.getItem(STORAGE_KEY) || 'true');

  // 域名直拦(出现即视为推广)
  const BLOCKED_DOMAINS = [
    'okai.hk', 'okai.hk/alpha',
    'alpha.mevx.io',
    'gmgn.ai', 'gmgn.ai/sol/token', 'gmgn.cc', 'gmgn.org',
    'photon-sol.com', 'dexscreener.com', 'birdeye.so', 'rugcheck.xyz',
    'pump.fun', 'pumpswap', 't.me'
  ];

  // 拉盘/版式词(命中越多越可疑)
  const KEYWORDS = [
    'token alert', 'token stats', 'links', 'security',
    'mc $', 'market cap', 'vol', 'lp', 'ath',
    'watch your entry', 'called at', 'quick buy', 'signal',
    'up up up', 'gmgn', 'bonkbot', 'trojan',
    'chain: solana', 'dev: holds token', 'mint authority: no', 'freeze authority: no',
    // 常见 CA 行配套
    '📍ca', ' ca:', ' ca:', ' ca,', ' ca,', ' ca;', ' ca;', ' ca>', ' ca>>', 'ca>',
  ];

  // 白名单(避免误杀)
  const WHITELIST = [
    '$smiley', '#smiley'
  ];

  /*** 识别规则 ***/
  // Sol 地址:Base58,32~44,不含 0OIl
  const SOL_ADDR_RE = /\b(?=.{32,44}\b)(?!.*[OIl0])[1-9A-HJ-NP-Za-km-z]{32,44}\b/g;

  // “CA 近邻地址”——覆盖 : , ; > >> 以及 >、中英文符号、换行/空格
  const CA_NEAR_ADDR_RE =
    /\b(?:ca|contract|合约)\b[\s::,,;;>>»》›]+[\s\r\n]{0,40}[1-9A-HJ-NP-Za-km-z]{32,44}\b/i;

  // TICKER:$ + 2~8字母
  const TICKER_RE = /\$[A-Z]{2,8}\b/;

  // 归一化:小写,去零宽,统一冒号,压缩空白,转义 > 为 >
  const normalize = (s) =>
    (s || '')
      .replace(/>/gi, '>')
      .toLowerCase()
      .replace(/[\u200B-\u200D\uFEFF]/g, '')
      .replace(/[:﹕꞉⦂︰]/g, ':')
      .replace(/\s+/g, ' ')
      .trim();

  const normKeywords = KEYWORDS.map(normalize);
  const normWhitelist = WHITELIST.map(normalize);

  const hasBlockedDomain = (article) => {
    const links = article.querySelectorAll('a[href], a[role="link"]');
    for (const a of links) {
      const href = (a.getAttribute('href') || a.textContent || '').toLowerCase();
      for (const d of BLOCKED_DOMAINS) if (href.includes(d)) return true;
    }
    const t = (article.innerText || '').toLowerCase();
    return BLOCKED_DOMAINS.some((d) => t.includes(d));
  };

  const keywordScore = (t) => {
    let n = 0;
    for (const k of normKeywords) if (k && t.includes(k)) n++;
    return n;
  };

  const hitWhitelist = (t) => normWhitelist.some((w) => w && t.includes(w));

  function isSpamArticle(article) {
    const raw = article.innerText || article.textContent || '';
    if (!raw) return false;

    // 域名直拦
    if (hasBlockedDomain(article)) return true;

    const text = normalize(raw);
    if (hitWhitelist(text)) return false;

    // 找出地址与重复次数
    const addrs = (raw.match(SOL_ADDR_RE) || []);
    const addrCountMap = {};
    for (const a of addrs) addrCountMap[a] = (addrCountMap[a] || 0) + 1;
    const repeatedAddr = Object.values(addrCountMap).some(c => c >= 2);

    // 规则 A:CA 邻近地址(CA: / CA, / CA; / CA> / CA>> / Ca> 等)
    if (CA_NEAR_ADDR_RE.test(raw)) return true;

    if (addrs.length) {
      // 规则 B:同一地址在同条里重复出现(截图里的“贴两遍/三遍”)
      if (repeatedAddr) return true;

      // 规则 C:地址 + TICKER 或 版式词(得分阈值)
      const score = keywordScore(text);
      const hasTicker = TICKER_RE.test(raw);
      if (hasTicker && score >= 1) return true;          // 地址 + $TICKER + ≥1 版式词
      if (!hasTicker && score >= 2) return true;         // 地址 + ≥2 版式词
    }

    // 规则 D:即使没有地址,拉盘词极度密集(≥4)也拦
    if (!addrs.length && keywordScore(text) >= 4) return true;

    return false;
  }

  /*** DOM 处理 ***/
  function hideTweet(article) {
    if (!article || article.dataset.__solCaHidden === '1') return;
    article.style.display = 'none';
    article.dataset.__solCaHidden = '1';
  }

  function restoreAll() {
    document.querySelectorAll('article[data-__solCaHidden="1"]').forEach(a => (a.style.display = ''));
  }

  function checkArticle(article) {
    if (!ENABLED || !article || article.dataset.__solCaChecked === '1') return;
    article.dataset.__solCaChecked = '1';
    if (isSpamArticle(article)) hideTweet(article);
  }

  function scanAll() {
    document.querySelectorAll('article[data-testid="tweet"], article[role="article"]').forEach(checkArticle);
  }

  const obs = new MutationObserver(muts => {
    if (!ENABLED) return;
    for (const m of muts) {
      for (const node of m.addedNodes) {
        if (!(node instanceof HTMLElement)) continue;
        if (node.matches?.('article[data-testid="tweet"], article[role="article"]')) {
          checkArticle(node);
        } else {
          node.querySelectorAll?.('article[data-testid="tweet"], article[role="article"]').forEach(checkArticle);
        }
      }
    }
  });

  function startObserver() {
    obs.observe(document.documentElement, { childList: true, subtree: true });
  }

  function registerMenu() {
    if (typeof GM_registerMenuCommand !== 'function') return;
    GM_registerMenuCommand(`SOL 合约广告过滤:${ENABLED ? '✅ 开启' : '⛔ 关闭'}`, () => {});
    GM_registerMenuCommand(ENABLED ? '🔕 关闭过滤' : '🔔 开启过滤', () => {
      ENABLED = !ENABLED;
      localStorage.setItem(STORAGE_KEY, JSON.stringify(ENABLED));
      ENABLED ? scanAll() : restoreAll();
      alert(`SOL 合约广告过滤已${ENABLED ? '开启' : '关闭'}`);
    });
  }

  function ready(fn) {
    if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', fn, { once: true });
    else fn();
  }

  ready(() => {
    registerMenu();
    startObserver();
    scanAll();
    setTimeout(scanAll, 600);
    setTimeout(scanAll, 2000);
    setInterval(() => ENABLED && scanAll(), 6000);
  });
})();

QingJ © 2025

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