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 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();
}
});