Make Twitter Great Again

为Twitter增加两个按钮,快速让Twitter算法知道你“不感兴趣的推文“ From Chrome Extension "Make Twitter Great Again" not interesting this post

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         Make Twitter Great Again
// @namespace    https://github.com/androidcn/userscripts/
// @version      2024-12-25
// @description  为Twitter增加两个按钮,快速让Twitter算法知道你“不感兴趣的推文“ From Chrome Extension "Make Twitter Great Again" not interesting this post
// @author       theopenprojects.io
// @match        https://twitter.com/home
// @match        https://x.com/home
// @icon         https://www.google.com/s2/favicons?sz=64&domain=twitter.com
// @grant        GM_setValue
// @grant        GM_getValue
// @license      MIT
// ==/UserScript==

var hideButton = document.createElement('button');
 function createButton() {
        // Create a button element
        hideButton.textContent = '隐藏';
        hideButton.style.position = 'fixed';
        hideButton.style.top = '60px';
        hideButton.style.right = '20px';
        hideButton.style.zIndex = '9999';
        // Append the button to the body
        document.body.appendChild(hideButton);
        // Add click event listener to the button
        hideButton.addEventListener('click', performHidedAction);
    }
function hideIt(){
    document.querySelector("header[role='banner']").style="display:none;";
    document.querySelector('div[aria-label="Home timeline"] div:first-child').style="display:none;";
    //console.log('已隐藏');
    hideButton.textContent = '显示';
    //GM_setValue("leftSideBar_hide",true);
}
function showIt(){
    document.querySelector("header[role='banner']").style="";
    document.querySelector('div[aria-label="Home timeline"] div:first-child').style="";
    //console.log('已显示');
    hideButton.textContent = '隐藏';
    //GM_setValue("leftSideBar_hide",false);
}
    function performHidedAction() {
        var HideText = hideButton.textContent ;
        if (HideText == "隐藏"){
            hideIt();
        }
        else{
             showIt();
        }
    }

    // Wait for the page to load
    window.addEventListener('load', createButton);
    //Scroll to hide lefe bar and top bar
    //window.addEventListener('scroll',hideIt);

const silencePath = 'path[d="M18 6.59V1.2L8.71 7H5.5C4.12 7 3 8.12 3 9.5v5C3 15.88 4.12 17 5.5 17h2.09l-2.3 2.29 1.42 1.42 15.5-15.5-1.42-1.42L18 6.59zm-8 8V8.55l6-3.75v3.79l-6 6zM5 9.5c0-.28.22-.5.5-.5H8v6H5.5c-.28 0-.5-.22-.5-.5v-5zm6.5 9.24l1.45-1.45L16 19.2V14l2 .02v8.78l-6.5-4.06z"]'
const shitPath = 'path[d="M9.5 7c.828 0 1.5 1.119 1.5 2.5S10.328 12 9.5 12 8 10.881 8 9.5 8.672 7 9.5 7zm5 0c.828 0 1.5 1.119 1.5 2.5s-.672 2.5-1.5 2.5S13 10.881 13 9.5 13.672 7 14.5 7zM12 22.25C6.348 22.25 1.75 17.652 1.75 12S6.348 1.75 12 1.75 22.25 6.348 22.25 12 17.652 22.25 12 22.25zm0-18.5c-4.549 0-8.25 3.701-8.25 8.25s3.701 8.25 8.25 8.25 8.25-3.701 8.25-8.25S16.549 3.75 12 3.75zM8.947 17.322l-1.896-.638C7.101 16.534 8.322 13 12 13s4.898 3.533 4.949 3.684l-1.897.633c-.031-.09-.828-2.316-3.051-2.316s-3.021 2.227-3.053 2.322z"]'
const moreProfilePath = 'path[d="M3 12c0-1.1.9-2 2-2s2 .9 2 2-.9 2-2 2-2-.9-2-2zm9 2c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm7 0c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2z"]'

const sleep = ms => new Promise(r => setTimeout(r, ms));

const waitForElm = (selector) => {
    return new Promise((resolve, reject) => {
        const observer = new MutationObserver((mutations) => {
            mutations.forEach((mutation) => {
                const elm = mutation.target.querySelector(selector);
                if (elm) {
                    observer.disconnect();
                    resolve(elm);
                }
            });
        });
        observer.observe(document.body, {
            childList: true,
            subtree: true,
        });
    });
};

const setTabStatusToBody = () => {
    const body = document.querySelector('body')
    if (!body || !document.querySelectorAll('[role="tablist"] [role="tab"]')) return
    //if (document.URL !== 'https://twitter.com/home') return;

    if (!Array.from(document.querySelectorAll('[role="tablist"] [role="tab"][data-index]')).length > 0) Array.from(document.querySelectorAll('[role="tablist"] [role="tab"]')).map((el, idx) => el.closest('div').setAttribute('data-index', idx + 1))

    const selectedIndexElm = document.querySelector('[role="tablist"] [role="tab"][aria-selected="true"]')
    if (selectedIndexElm) {
        const parent = selectedIndexElm.closest('div')
        if (parent) {
            body.setAttribute('data-make-twitter-great-again', parent.getAttribute('data-index'))
        }
    }
}

const createShityBtn = (tweet) => {
    if (!tweet || tweet.querySelector('.shitBtn')) return;
    //if (document.URL !== 'https://twitter.com/home') return;

    const button = document.createElement('button');
    tweet.setAttribute('data-shit', true);
    button.classList.add('shitBtn');
    button.innerHTML = '没兴趣';

    const navAction = tweet.querySelector('div[role="group"][id*="id__"]');
    if (navAction) navAction.appendChild(button);
}

const createSilenceBtn = tweet => {
    if (!tweet || tweet.querySelector('.silenceBtn')) return;
   // if (document.URL !== 'https://twitter.com/home') return;

    const button = document.createElement('button');
    tweet.setAttribute('data-silence', true);
    button.classList.add('silenceBtn');
    button.innerHTML = '封禁';

    const navAction = tweet.querySelector('div[role="group"][id*="id__"]');
    if (navAction) navAction.appendChild(button);
}

const handleBtnClick = async (e, selector) => {
    const tweet = e.target.closest('article');
    document.querySelector('body').setAttribute('data-pop-open', true)

    const btnDropdown = tweet.querySelector('[aria-haspopup="menu"][role="button"][data-testid="caret"]');
    if (!btnDropdown) return

    btnDropdown.click();

    await sleep(5);
    const dropdown = document.querySelector('[data-testid="Dropdown"],[data-testid="sheetDialog"]');
    if (!dropdown) return;
    
    const item = dropdown.querySelector(selector)
    if (item) item.closest('[role]').click()

    btnDropdown.click();

    if (document.querySelector('body').getAttribute('data-make-twitter-great-again') !== "1" || document.querySelector('body').getAttribute('data-make-twitter-great-again') !== "2") tweet.remove()
}

const handleProfileBtnClick = async (e, selector) => {
    const userActions = document.querySelector('[role="main"] [data-testid="userActions"]')
    if (!userActions) return

    document.querySelector('body').setAttribute('data-pop-open', true)

    const path = userActions.querySelector(moreProfilePath);
    if (!path) return
    const btnDropdown = path.closest('div[dir]')
    if (!btnDropdown) return

    btnDropdown.click();

    await sleep(5);
    const dropdown = document.querySelector('[data-testid="Dropdown"]');
    if (!dropdown) return;

    const item = dropdown.querySelector(selector)
    if (item) item.closest('[role]').click()

    dropdown.remove()
}

const addBtnToTweets = () => {
    const tweets = document.querySelectorAll('[role="region"] article:not([data-shit]):not([data-silence])');
    if (tweets && tweets.length > 0) tweets.forEach(tweet => {
        createShityBtn(tweet)
        createSilenceBtn(tweet)
    });
}

const addProfileSilenceBtn = () => {
    const userActions = document.querySelector('[role="main"] [data-testid="userActions"]')
    if (!userActions) return

    const contentActions = userActions.parentElement
    if (!contentActions || contentActions.getAttribute('data-silence') === 'true') return

    const button = document.createElement('button');
    contentActions.setAttribute('data-silence', true);
    button.classList.add('profileSilence');
    button.innerHTML = '🤫';

    contentActions.insertBefore(button, contentActions.firstChild);
}

const isProfile = () => {
    if (document.querySelector('head meta[content*="twitter://user?screen_name="]')) {
        document.querySelector('body').setAttribute('data-profile', true)

        addProfileSilenceBtn()
    }
    else document.querySelector('body').removeAttribute('data-profile')
}

const observeTweets = () => {
    const observer = new MutationObserver((mutations) => {
        setTabStatusToBody();
        isProfile()
        if (!document.querySelector('[data-make-twitter-great-again] [role="group"] > div > [role="menu"]')) document.querySelector('body').removeAttribute('data-pop-open')
        mutations.forEach(() => addBtnToTweets());
    });
    observer.observe(document.body, {
        childList: true,
        subtree: true,
    });
    return observer;
}

(async () => {
    await waitForElm('[role="region"] article');

    setTabStatusToBody();
    isProfile();
    addBtnToTweets();

    document.addEventListener('click', (e) => {
        if (e.target.classList.contains('shitBtn')) handleBtnClick(e, shitPath);
        if (e.target.classList.contains('silenceBtn')) handleBtnClick(e, silencePath);
        if (e.target.classList.contains('profileSilence')) handleProfileBtnClick(e, silencePath);
    })

    const tweetsObserver = observeTweets();
    window.addEventListener('beforeunload', () => {
        tweetsObserver.disconnect();
    });
})();