From 59a945728d8640554184fb3cf7d11e819914aa81 Mon Sep 17 00:00:00 2001 From: Sam Hadow Date: Sun, 20 Apr 2025 14:07:54 +0200 Subject: [PATCH] X adblock script --- README.md | 5 +++ scripts/X-adblock.user.js | 95 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 100 insertions(+) create mode 100644 scripts/X-adblock.user.js diff --git a/README.md b/README.md index e69de29..a768650 100644 --- a/README.md +++ b/README.md @@ -0,0 +1,5 @@ +Scripts are in the script folder. Please use a userscript manager like [greasemonkey](https://addons.mozilla.org/en-US/firefox/addon/greasemonkey/) to use them. + +## X-adblock + +Script to automatically block accounts posting ads on x.com. [Ublock Origin](https://addons.mozilla.org/en-US/firefox/addon/ublock-origin/) is a much better option. But this script was written thinking "If I block every single account posting ads would I stop seeing ads on twitter? How much time would it take?", still unanswered for now. diff --git a/scripts/X-adblock.user.js b/scripts/X-adblock.user.js new file mode 100644 index 0000000..a02aae7 --- /dev/null +++ b/scripts/X-adblock.user.js @@ -0,0 +1,95 @@ +// ==UserScript== +// @name X Ad Auto-Blocker +// @version 0.1 +// @description Automatically block accounts posting ads on x.com +// @match https://x.com/* +// @grant none +// @run-at document-end +// ==/UserScript== + +(function() { + 'use strict'; + + const processed = new WeakSet(); + + function waitForSelector(selector, timeout = 3000) { + return new Promise((resolve, reject) => { + const element = document.querySelector(selector); + if (element) return resolve(element); + + const observer = new MutationObserver((_, obs) => { + const el = document.querySelector(selector); + if (el) { + obs.disconnect(); + resolve(el); + } + }); + observer.observe(document.body, { childList: true, subtree: true }); + + setTimeout(() => { + observer.disconnect(); + reject(new Error(`Timeout waiting for selector: ${selector}`)); + }, timeout); + }); + } + + async function processAdSpan(span) { + try { + processed.add(span); + const container = span.parentElement?.parentElement; + if (!container) return; + + const moreBtn = container.querySelector('button[data-testid="caret"]'); + if (!moreBtn) return; + moreBtn.click(); + + const blockItem = await waitForSelector('div[data-testid="block"]'); + if (/^\s*Block\s+@.+/i.test(blockItem.innerText)) { + blockItem.click(); + } else { + const closeBtn = await waitForSelector('button[aria-label="Close"]'); + closeBtn.click(); + return; + } + + const confirmBtn = await waitForSelector('button[data-testid="confirmationSheetConfirm"]'); + confirmBtn.click(); + + // close "buy X premium" popup + const sheetClose = await waitForSelector('button[data-testid="app-bar-close"]'); + sheetClose.click(); + + console.log('Blocked account posting ads'); + } catch (err) { + console.error('Error processing ad span:', err); + } + } + + const observer = new MutationObserver((mutations) => { + for (const m of mutations) { + m.addedNodes.forEach(node => { + if (node.nodeType !== 1) return; + + // observe these spans to spot ads + const spans = node.matches && node.matches('span.css-1jxf684.r-bcqeeo.r-1ttztb7.r-qvutc0.r-poiln3') + ? [node] + : Array.from(node.querySelectorAll('span.css-1jxf684.r-bcqeeo.r-1ttztb7.r-qvutc0.r-poiln3')); + + spans.forEach(span => { + if (span.textContent.trim() === 'Ad' && !processed.has(span)) { + processAdSpan(span); + } + }); + }); + } + }); + + observer.observe(document.body, { childList: true, subtree: true }); + + // Initial scan + document.querySelectorAll('span.css-1jxf684.r-bcqeeo.r-1ttztb7.r-qvutc0.r-poiln3').forEach(span => { + if (span.textContent.trim() === 'Ad' && !processed.has(span)) { + processAdSpan(span); + } + }); +})();