Racistator

DOM INJ

当前为 2025-09-30 提交的版本,查看 最新版本

// ==UserScript==
// @name         Racistator
// @namespace    http://tampermonkey.net/
// @version      1.2
// @description  DOM INJ
// @author       Golshi Best Waifu
// @match        https://x.com/*
// @match        https://www.x.com/*
// @grant        none
// ==/UserScript==

(function () {
    'use strict';

    const REPLACEMENT_TEXT = 'Esto pruebaa que soy Racista?';
    const MOD_FLAG = 'data-sm-text-replaced';

    function sleep(ms) { return new Promise(res => setTimeout(res, ms)); }
    function findPrimaryTextElement(tweetNode) {
        if (!tweetNode) return null;
        const langDiv = tweetNode.querySelector('div[lang]');
        if (langDiv) return langDiv;
        const candidates = Array.from(tweetNode.querySelectorAll('span,div')).filter(el => {
            const txt = (el.textContent || '').trim();
            if (txt.length < 3) return false;
            if (el.closest('div[role="button"], a[role="button"], [data-testid="caret"]')) return false;
            if (el.matches('[data-testid="like"], [data-testid="retweet"], [data-testid="reply"]')) return false;
            return true;
        });

        return candidates.length ? candidates[0] : null;
    }
    function isPromoted(tweetNode) {
        if (!tweetNode) return false;
        const txt = (tweetNode.textContent || '');
        return /Promoted|Sponsored|Anuncio|Patrocinado/i.test(txt);
    }

    function replaceTweetTextOnly(tweetNode) {
        if (!tweetNode) return;
        if (tweetNode.getAttribute(MOD_FLAG) === 'true') return; // MODIFICA
        if (isPromoted(tweetNode)) return;

        const textEl = findPrimaryTextElement(tweetNode);
        if (!textEl) {
            // marca
            tweetNode.setAttribute(MOD_FLAG, 'true');
            return;
        }

        try {
            textEl.textContent = REPLACEMENT_TEXT;

            tweetNode.setAttribute(MOD_FLAG, 'true');
        } catch (e) {
            // fallback soft
            try {
                const txtNode = document.createTextNode(REPLACEMENT_TEXT);
                while (textEl.firstChild) textEl.removeChild(textEl.firstChild);
                textEl.appendChild(txtNode);
                tweetNode.setAttribute(MOD_FLAG, 'true');
            } catch (err) {
                tweetNode.setAttribute(MOD_FLAG, 'true');
            }
        }
    }


    function collectTweetNodes() {
        const set = new Set();

        const selectors = [
            'article[role="article"]',
            'div[data-testid="tweet"]',
            'div[data-testid="cellInnerDiv"]',
            'div[role="article"]'
        ];
        selectors.forEach(sel => {
            try {
                document.querySelectorAll(sel).forEach(n => set.add(n));
            } catch (e) { /* ignore */ }
        });

        document.querySelectorAll('a[href*="/status/"]').forEach(a => {
            const anc = a.closest('article') || a.closest('div[role="article"]') || a.closest('div');
            if (anc) set.add(anc);
        });

        return Array.from(set);
    }

    function scanAndReplaceText() {
        const nodes = collectTweetNodes();
        nodes.forEach(node => replaceTweetTextOnly(node));
    }

    function startObservers() {
        const bodyObs = new MutationObserver(muts => {
            let added = false;
            for (const m of muts) {
                if (m.addedNodes && m.addedNodes.length) { added = true; break; }
            }
            if (added) setTimeout(scanAndReplaceText, 120);
        });
        bodyObs.observe(document.body, { childList: true, subtree: true });

        const timelineSelectors = ['[aria-label^="Timeline"]', 'main', 'div[role="main"]'];
        for (const sel of timelineSelectors) {
            const el = document.querySelector(sel);
            if (el) {
                const obs = new MutationObserver(() => setTimeout(scanAndReplaceText, 150));
                obs.observe(el, { childList: true, subtree: true });
            }
        }

        window.addEventListener('popstate', () => setTimeout(scanAndReplaceText, 250));
        window.addEventListener('hashchange', () => setTimeout(scanAndReplaceText, 250));

    }

    (async function init() {
        await sleep(300);
        scanAndReplaceText();
        setTimeout(scanAndReplaceText, 800);
        startObservers();

        // FRECUENCY6k
        setInterval(scanAndReplaceText, 60000);
    })();

})();