JeetDelete X.com

Hides posts on X with Hindi, other Indian languages, Arabic, Japanese, or Thai scripts, with enhanced spam detection

// ==UserScript==
// @name         JeetDelete X.com
// @namespace    http://tampermonkey.net/
// @version      1.4.6
// @description  Hides posts on X with Hindi, other Indian languages, Arabic, Japanese, or Thai scripts, with enhanced spam detection
// @author       xechostormx, hearing_echoes
// @match        https://x.com/*
// @match        https://www.x.com/*
// @grant        none
// @run-at       document-idle
// ==/UserScript==

(function() {
    'use strict';

    // Detect target language scripts
    function isTargetLanguage(text) {
        if (!text || typeof text !== 'string') return false;
        const ranges = [
            /[\u0900-\u097F]/, // Devanagari (Hindi)
            /[\u0B80-\u0BFF]/, // Tamil
            /[\u0C00-\u0C7F]/, // Telugu
            /[\u0980-\u09FF]/, // Bengali
            /[\u0A80-\u0AFF]/, // Gujarati
            /[\u0B00-\u0B7F]/, // Odia
            /[\u0A00-\u0A7F]/, // Gurmukhi
            /[\u0C80-\u0CFF]/, // Kannada
            /[\u0D00-\u0D7F]/, // Malayalam
            /[\u0600-\u06FF\u0750-\u077F\uFB50-\uFDFF\uFE70-\uFEFF]/, // Arabic
            /[\u3040-\u309F\u30A0-\u30FF\u4E00-\u9FFF]/, // Japanese
            /[\u0E00-\u0E7F]/  // Thai
        ];
        const totalChars = text.length;
        const targetChars = ranges.reduce((count, rx) => count + (text.match(rx) || []).length, 0);

        // Count unique scripts
        const scripts = new Set();
        ranges.forEach((rx, i) => {
            if (text.match(rx)) scripts.add(i);
        });

        // Debug log
        console.log(`Text: "${text.slice(0, 20)}...", Total: ${totalChars}, Target: ${targetChars}, Scripts: ${scripts.size}`);

        // Flag if any target chars OR all text is hashtags with multiple scripts
        const isAllHashtags = text.replace(/#[^\s#]+/g, '').trim().length === 0;
        return targetChars > 0 || (isAllHashtags && scripts.size > 1);
    }

    // Hide matching posts
    function hideTargetLanguagePosts() {
        const selector = 'div[data-testid="tweetText"]';
        const tweetTexts = document.querySelectorAll(selector);
        if (!tweetTexts.length) {
            console.warn('No tweet text elements found with selector:', selector);
            return;
        }
        console.log(`Scanning ${tweetTexts.length} tweet texts`);
        tweetTexts.forEach((textEl) => {
            const tweet = textEl.closest('article[data-testid="tweet"]') || textEl.closest('div[data-testid="cellInnerDiv"]');
            if (!tweet) return;
            const txt = textEl.textContent || '';
            if (isTargetLanguage(txt)) {
                tweet.style.display = 'none';
                console.log('Hid tweet:', txt.trim().slice(0, 50));
            }
        });
    }

    console.log('X-HideLang script initialized @', new Date().toLocaleTimeString());

    // Wait for timeline to load
    function waitForTimeline() {
        const timeline = document.querySelector('div[aria-label*="Timeline"]');
        if (timeline) {
            hideTargetLanguagePosts();
            observeTimeline(timeline);
        } else {
            requestAnimationFrame(waitForTimeline);
        }
    }

    // Observe timeline for new tweets
    function observeTimeline(timeline) {
        let debounceTimer = null; // Initialized to null to avoid const error
        const observer = new MutationObserver(() => {
            clearTimeout(debounceTimer);
            debounceTimer = setTimeout(hideTargetLanguagePosts, 200);
        });
        observer.observe(timeline, { childList: true, subtree: true });
        console.log('Observer attached to timeline');

        // Cleanup on page unload
        window.addEventListener('unload', () => {
            clearTimeout(debounceTimer);
            observer.disconnect();
        });
    }

    // Start when ready
    requestAnimationFrame(waitForTimeline);
})();