X.COM TWITTER Hide Tweets

Faster filtering of tweets with foreign characters, keywords, or spam symbols

// ==UserScript==
// @name         X.COM  TWITTER Hide Tweets
// @namespace    http://tampermonkey.net/
// @version      1.2
// @description  Faster filtering of tweets with foreign characters, keywords, or spam symbols
// @match        https://x.com/*
// @grant        none
// @license      GPLv3
// ==/UserScript==

(function() {
    'use strict';

    // Block lists & regexes
    const blockedKeywords = [
        "advice of this blogger",
        "miss the next move",
        "I made an easy",
        "ready for a run",
        "house of traders",
        "discord.gg"
    ];
    const keywordRegex = new RegExp(blockedKeywords.join("|"), "i");

   // Unified CJK regex (Japanese, Chinese, Korean)
    const cjkCharRegex = /[\u3040-\u30FF\u4E00-\u9FFF\u1100-\u11FF\u3130-\u318F\uAC00-\uD7AF]/;

    const symbolSpamRegex = /(?:\$(?:[^\$]*\$){5,})|(?:#(?:[^#]*#){5,})/i;


    // Hide tweet if it matches filters
    function shouldHide(text) {
        return (
            cjkCharRegex.test(text) ||
            keywordRegex.test(text) ||
            symbolSpamRegex.test(text)
        );
    }

    function processTweet(tweet) {
        // Prevent double-processing
        if (tweet.dataset.filtered) return;
        tweet.dataset.filtered = "true";

        const text = tweet.textContent; // faster than innerText
        if (shouldHide(text)) {
            tweet.style.display = "none";
        }
    }

    // Process existing tweets
    document.querySelectorAll("article").forEach(processTweet);

    // Observe only new tweets
    const observer = new MutationObserver(mutations => {
        for (const mutation of mutations) {
            for (const node of mutation.addedNodes) {
                if (node.nodeType === 1) {
                    if (node.tagName === "ARTICLE") {
                        processTweet(node);
                    } else {
                        node.querySelectorAll?.("article").forEach(processTweet);
                    }
                }
            }
        }
    });

    observer.observe(document.body, { childList: true, subtree: true });
})();