new script: X autoblock by keywords
This commit is contained in:
@@ -0,0 +1,139 @@
|
||||
// ==UserScript==
|
||||
// @name X Auto-Block by Keyword
|
||||
// @version 0.3
|
||||
// @description Automatically block accounts whose tweets contain banned words
|
||||
// @match https://x.com/*
|
||||
// @grant none
|
||||
// @run-at document-end
|
||||
// ==/UserScript==
|
||||
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
// 1) Banned words/phrases (case-insensitive)
|
||||
const bannedKeywords = [
|
||||
'like to see my',
|
||||
'wanna see my'
|
||||
];
|
||||
|
||||
// 2) Banned patterns (RegEx)
|
||||
const bannedRegexes = [
|
||||
/(?:^|\s)https?:\/\/t\.me\/[^\s]+/i,
|
||||
/(?:^|\s)t\.me\/[^\s]+/i
|
||||
];
|
||||
|
||||
function escapeRegex(str) {
|
||||
return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
||||
}
|
||||
|
||||
// Build keyword regex
|
||||
const keywordRegex = new RegExp(
|
||||
`\\b(?:${bannedKeywords.map(escapeRegex).join('|')})\\b`,
|
||||
'i'
|
||||
);
|
||||
|
||||
function containsBannedRegex(text) {
|
||||
return bannedRegexes.some(regex => regex.test(text));
|
||||
}
|
||||
|
||||
// Avoid reprocessing the same tweet/container
|
||||
const processed = new WeakSet();
|
||||
|
||||
function waitForSelector(selector, timeout = 3000) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const el = document.querySelector(selector);
|
||||
if (el) return resolve(el);
|
||||
|
||||
const obs = new MutationObserver((_, ob) => {
|
||||
const found = document.querySelector(selector);
|
||||
if (found) {
|
||||
ob.disconnect();
|
||||
resolve(found);
|
||||
}
|
||||
});
|
||||
obs.observe(document.body, { childList: true, subtree: true });
|
||||
|
||||
setTimeout(() => {
|
||||
obs.disconnect();
|
||||
reject(new Error(`Timeout waiting for selector: ${selector}`));
|
||||
}, timeout);
|
||||
});
|
||||
}
|
||||
|
||||
async function inspectAndBlock(textEl) {
|
||||
if (processed.has(textEl)) return;
|
||||
processed.add(textEl);
|
||||
|
||||
const text = textEl.innerText || textEl.textContent;
|
||||
if (!keywordRegex.test(text) && !containsBannedRegex(text)) return;
|
||||
|
||||
let container = textEl;
|
||||
while (container && container.getAttribute('data-testid') !== 'tweet') {
|
||||
container = container.parentElement;
|
||||
}
|
||||
if (!container) return;
|
||||
|
||||
try {
|
||||
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();
|
||||
|
||||
let matchedBanTerm = null;
|
||||
|
||||
const wordMatch = text.match(keywordRegex);
|
||||
if (wordMatch) {
|
||||
matchedBanTerm = wordMatch[0];
|
||||
} else {
|
||||
matchedBanTerm = bannedRegexes
|
||||
.map(rx => {
|
||||
const m = text.match(rx);
|
||||
return m ? m[0].trim() : null;
|
||||
})
|
||||
.find(Boolean);
|
||||
}
|
||||
|
||||
console.log(`Blocked account for tweet containing banned term: "${matchedBanTerm}"`);
|
||||
|
||||
} catch (err) {
|
||||
console.error('Error during block flow:', err);
|
||||
}
|
||||
}
|
||||
|
||||
const observer = new MutationObserver(mutations => {
|
||||
for (const mut of mutations) {
|
||||
mut.addedNodes.forEach(node => {
|
||||
if (node.nodeType !== 1) return;
|
||||
const texts = node.matches && (
|
||||
node.matches('[data-testid="tweetText"]') ||
|
||||
node.matches('[data-testid="card.layoutSmall.detail"]') ||
|
||||
node.matches('[data-testid="User-Name"]')
|
||||
)
|
||||
? [node]
|
||||
: Array.from(node.querySelectorAll(
|
||||
'[data-testid="tweetText"], [data-testid="card.layoutSmall.detail"], [data-testid="User-Name"]'
|
||||
));
|
||||
|
||||
texts.forEach(el => inspectAndBlock(el));
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
observer.observe(document.body, { childList: true, subtree: true });
|
||||
|
||||
document.querySelectorAll(
|
||||
'[data-testid="tweetText"], [data-testid="card.layoutSmall.detail"], [data-testid="User-Name"]'
|
||||
).forEach(el => inspectAndBlock(el));
|
||||
|
||||
})();
|
||||
Reference in New Issue
Block a user