X/Twitter 1 Click report spam and block

Report spam and block profiles with 1 click

// ==UserScript==
// @name        X/Twitter 1 Click report spam and block
// @description Report spam and block profiles with 1 click
// @version     1.4
// @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(6)';
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();
    }
});