X/Twitter 1 Click report spam and block

Report spam and block profiles with 1 click

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

You will need to install an extension such as Tampermonkey to install this script.

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name        X/Twitter 1 Click report spam and block
// @description Report spam and block profiles with 1 click
// @version     2.0
// @grant       none
// @match       https://twitter.com/*
// @match       https://x.com/*
// @author      incognico
// @icon        https://www.google.com/s2/favicons?sz=64&domain=x.com
// @require     https://cdn.jsdelivr.net/npm/@violentmonkey/dom@2
// @license     GPL v3
// @author      incognico
// @namespace   https://greasyfork.org/users/931787
//
// ==/UserScript==


const accountMenuIconSelector = '[data-testid="primaryColumn"] svg:has([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 ourIconId = 'oneclickreportbtn';
const reportIconSelector = '[data-testid="Dropdown"] svg:has([d="M3 2h18.61l-3.5 7 3.5 7H5v6H3V2zm2 12h13.38l-2.5-5 2.5-5H5v10z"])';
const spamItemSelector = '[data-viewportview="true"] [role="radiogroup"] label:nth-child(7)';
const nextButtonSelector = '[data-testid="ChoiceSelectionNextButton"]';
const modalButtonsSelector = '[data-viewportview="true"] button';

const wait = (ms) => {
    return new Promise(resolve => setTimeout(resolve, ms));
};

const doReport = async (event) => {
    if (!event.shiftKey) {
        alert('Press shift to confirm reporting this account as spam and blocking');
        return;
    }
    // open account overflow menu
    document.querySelector(accountMenuIconSelector).parentElement.click();
    await wait(100);
    // press report
    document.querySelector(reportIconSelector).parentElement.parentElement.click();
    await wait(850);
    // select spam and press next
    document.querySelector(spamItemSelector).click();
    document.querySelector(nextButtonSelector).click();
    await wait(500);
    // second button is report
    document.querySelectorAll(modalButtonsSelector)[1].click();
};


const destroyIcon = () => {
    const icon = document.getElementById(ourIconId);
    if (icon) {
        icon.remove();
    }
};


const createIconIfNotExists = () => {
    const iconExists = document.getElementById(ourIconId);
    const accountMenuIcon = document.querySelector(accountMenuIconSelector);
    if (!iconExists && accountMenuIcon) {
        const icon = document.createElement('button');
        icon.id = ourIconId;
        icon.innerHTML = `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M8 5h-2a2 2 0 0 0 -2 2v12a2 2 0 0 0 2 2h5.697" /><path d="M18 14v4h4" /><path d="M18 11v-4a2 2 0 0 0 -2 -2h-2" /><path d="M8 3m0 2a2 2 0 0 1 2 -2h2a2 2 0 0 1 2 2v0a2 2 0 0 1 -2 2h-2a2 2 0 0 1 -2 -2z" /><path d="M18 18m-4 0a4 4 0 1 0 8 0a4 4 0 1 0 -8 0" /><path d="M8 11h4" /><path d="M8 15h3" /></svg>`;
        icon.title = '1-Click spam report & block';
        icon.onclick = doReport;

        // assign style to the button
        const style = {
            color: 'black',
            textAlign: 'center',
            fontWeight: 'bold',
            alignSelf: 'baseline',
            borderRadius: '50%',
            borderColor: 'rgba(0, 0, 0, 0)',
            marginLeft: '5px',
            backgroundColor: 'rgb(239, 243, 244)',
            fontFamily: 'TwitterChirp, sans-serif',
            cursor: 'pointer',
        };
        Object.assign(icon.style, style);

        // append icon to the profile icon list
        const header = accountMenuIcon.parentElement?.parentElement?.parentElement;
        if (header) {
            header.appendChild(icon);
        }
    }
};

const disconnect = VM.observe(document.body, () => {
    // append our icon on profile pages
    const profilePage = document.querySelector('[data-testid="UserName"]');
    if (profilePage) {
        createIconIfNotExists();
    } else {
        destroyIcon();
    }
});