Twitter/X Bot and Flag Post Hider

Hides posts from suspected bot accounts or users with specific flags/emojis (e.g., 🇮🇱, 🎗️, 🇺🇦) in their name by checking text and image URLs.

// ==UserScript==
// @name         Twitter/X Bot and Flag Post Hider
// @namespace    http://tampermonkey.net/
// @version      1.7
// @description  Hides posts from suspected bot accounts or users with specific flags/emojis (e.g., 🇮🇱, 🎗️, 🇺🇦) in their name by checking text and image URLs.
// @author       CL
// @match        *://x.com/*
// @match        *://twitter.com/*
// @grant        none
// @run-at       document-idle
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';

    // --- Configuration ---
    // This regex pattern identifies usernames that have letters followed by two or more numbers at the end.
    const BOT_USERNAME_PATTERN = /[a-zA-Z].*\d{2,}$/;

    // Hides if user's display name INCLUDES these characters (e.g., for standard text emojis).
    const HIDE_IF_NAME_INCLUDES = ['🇮🇱', '🎗️', '🇺🇦'];

    // Hides if user's name contains an IMAGE with a SRC attribute containing these strings. This is more reliable for flags.
    const HIDE_IF_IMG_SRC_INCLUDES = [
        '1f1ee-1f1f1', // Israel Flag 🇮🇱
        '1f1fa-1f1e6', // Ukraine Flag 🇺🇦
        '1f397'        // Reminder Ribbon 🎗️
    ];

    // How often the script checks for new posts on the page (in milliseconds).
    const CHECK_INTERVAL = 1000;
    // --- End Configuration ---

    let hiddenPostCount = 0;
    const processedPosts = new Set();

    function createCounterElement() {
        const counterDiv = document.createElement('div');
        counterDiv.id = 'bot-hider-counter';
        Object.assign(counterDiv.style, {
            position: 'fixed',
            top: '15px',
            left: '15px',
            backgroundColor: 'rgba(29, 155, 240, 0.9)',
            color: 'white',
            padding: '5px 12px',
            borderRadius: '15px',
            zIndex: '10000',
            fontSize: '14px',
            fontFamily: 'system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Ubuntu, "Helvetica Neue", sans-serif',
            boxShadow: '0 2px 8px rgba(0,0,0,0.2)',
            userSelect: 'none',
            transition: 'opacity 0.3s ease-in-out'
        });
        document.body.appendChild(counterDiv);
        return counterDiv;
    }

    const counterElement = createCounterElement();

    // Check based on simple text content (includes alt text of images)
    function nameContainsHiddenString(displayName) {
        return HIDE_IF_NAME_INCLUDES.some(str => displayName.includes(str));
    }

    // New, more reliable check based on the image's SRC attribute
    function nameContainsHiddenImage(displayNameElement) {
        if (!displayNameElement) return false;
        const images = displayNameElement.querySelectorAll('img');
        for (const img of images) {
            const src = img.getAttribute('src');
            if (src && HIDE_IF_IMG_SRC_INCLUDES.some(str => src.includes(str))) {
                return true; // Found a matching image src
            }
        }
        return false;
    }

    function hidePosts() {
        const articles = document.querySelectorAll('article[data-testid="tweet"]');

        articles.forEach(article => {
            const articleId = article.getAttribute('aria-labelledby');
            if (!articleId || processedPosts.has(articleId)) {
                return;
            }
            processedPosts.add(articleId);

            const userLink = article.querySelector('a[href^="/"]:not([href*="/status/"])');
            const userDisplayNameElement = article.querySelector('[data-testid="User-Name"]');

            if (userLink && userDisplayNameElement) {
                const href = userLink.getAttribute('href');
                const username = href.substring(1);
                const displayName = userDisplayNameElement.textContent || '';

                // The hiding condition now includes the new, more reliable image check
                const shouldHide = BOT_USERNAME_PATTERN.test(username) ||
                                   nameContainsHiddenString(displayName) ||
                                   nameContainsHiddenImage(userDisplayNameElement) || // <-- Added new check
                                   href.toLowerCase().includes('jew');

                if (shouldHide) {
                    const postContainer = article.closest('[data-testid="cellInnerDiv"]');
                    if (postContainer) {
                        postContainer.style.display = 'none';
                        hiddenPostCount++;
                    }
                }
            }
        });

        counterElement.textContent = `Hiding ${hiddenPostCount} posts`;
    }

    console.log('Twitter/X Bot & Flag Hider script is now active.');
    setInterval(hidePosts, CHECK_INTERVAL);
})();