您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Adds a 'Quick Block' button to tweets on x.com for one-click blocking. Robust version.
- // ==UserScript==
- // @name X.com Quick Block Button
- // @namespace http://tampermonkey.net/
- // @version 1.3
- // @description Adds a 'Quick Block' button to tweets on x.com for one-click blocking. Robust version.
- // @author Your Name Here
- // @match https://x.com/*
- // @match https://twitter.com/*
- // @grant none
- // @run-at document-idle
- // ==/UserScript==
- (function() {
- 'use strict';
- // --- Configuration ---
- const CHECK_INTERVAL = 1000;
- const CLICK_DELAY = 300;
- const BUTTON_TEXT = 'Block';
- const MAX_RETRIES = 3;
- // --- Styling for the Button ---
- const buttonStyle = `
- color: lightgray;
- background-color: transparent;
- border: none;
- margin-left: 10px;
- font-family: monospace;
- font-weight: bold;
- cursor: pointer;
- overflow: hidden; /* Clip the extra part of the shadow when scaled */
- position: relative; /* Needed for the pseudo-element */
- `;
- // --- Helper Function: Wait for an element to appear (RETRYING) ---
- async function waitForElement(selector, timeout = 2000, interval = 200, maxRetries = MAX_RETRIES) {
- return new Promise((resolve, reject) => {
- let elapsedTime = 0;
- let retries = 0;
- const timer = setInterval(() => {
- const element = document.querySelector(selector);
- if (element) {
- clearInterval(timer);
- resolve(element);
- } else {
- elapsedTime += interval;
- if (elapsedTime >= timeout) {
- if (retries < maxRetries) {
- retries++;
- elapsedTime = 0;
- console.warn(`Quick Block: Element not found, retrying (${retries}/${maxRetries}): ${selector}`);
- } else {
- clearInterval(timer);
- reject(new Error(`Element not found after ${timeout}ms and ${maxRetries} retries: ${selector}`));
- }
- }
- }
- }, interval);
- });
- }
- // --- Helper Function: Simulate a click with delay ---
- async function clickElement(element) {
- if (element && typeof element.click === 'function') {
- try {
- element.click();
- await new Promise(resolve => setTimeout(resolve, CLICK_DELAY));
- return true;
- } catch (e) {
- console.error("Quick Block: Error clicking element:", e, element);
- return false;
- }
- } else {
- console.error("Quick Block: Invalid element or no click method:", element);
- return false;
- }
- }
- // --- Helper Function: Find Parent Tweet Article (Robust) ---
- function findParentTweetArticle(node) {
- let current = node;
- while (current && current !== document.body) {
- if (current.tagName === 'ARTICLE' && current.getAttribute('data-testid') === 'tweet') {
- return current;
- }
- current = current.parentNode;
- }
- return null;
- }
- // --- Helper Function: Find Action Bar (More Robust) ---
- function findActionBar(tweetArticle) {
- const selectors = [
- 'div[role="group"]',
- 'div:has(> button[data-testid="reply"])',
- 'div:has(> button[data-testid="like"])',
- 'div:has(> button[data-testid="retweet"])'
- ];
- for (const selector of selectors) {
- const found = tweetArticle.querySelector(selector);
- if (found) {
- return found;
- }
- }
- return null;
- }
- // --- Main Blocking Logic (Robust) ---
- async function quickBlockUser(event) {
- const button = event.target;
- button.disabled = true;
- button.textContent = 'Blocking...';
- button.style.backgroundImage = 'linear-gradient(to bottom right, #ff9800, #f57c00)'; // Orange gradient
- button.style.boxShadow = '0px 4px 10px rgba(0, 0, 0, 0.3)';
- const tweetArticle = findParentTweetArticle(button);
- if (!tweetArticle) {
- console.error('Quick Block: Could not find parent tweet article.', button, event.currentTarget);
- button.textContent = 'Error';
- button.style.backgroundColor = 'orange';
- return;
- }
- try {
- let moreButton = null;
- for (let i = 0; i < MAX_RETRIES; i++) {
- moreButton = tweetArticle.querySelector('button[data-testid="caret"]');
- if (moreButton) break;
- await new Promise(resolve => setTimeout(resolve, CLICK_DELAY * 2));
- }
- if (!moreButton) {
- throw new Error('Could not find More button (caret) after retries.');
- }
- if (!await clickElement(moreButton)) {
- throw new Error('Failed to click More button.');
- }
- let blockMenuItem = null;
- for (let i = 0; i < MAX_RETRIES; i++) {
- try{
- blockMenuItem = await waitForElement('div[data-testid="block"]', 2000, 200, 1);
- if(blockMenuItem){
- break;
- }
- }catch(e){
- await new Promise(resolve => setTimeout(resolve, CLICK_DELAY * 2));
- }
- }
- if (!blockMenuItem) {
- throw new Error('Could not find Block menu item after retries.');
- }
- if (!await clickElement(blockMenuItem)) {
- throw new Error('Failed to click Block menu item.');
- }
- let confirmButton = null;
- for (let i = 0; i < MAX_RETRIES; i++) {
- try{
- confirmButton = await waitForElement('button[data-testid="confirmationSheetConfirm"]', 2000, 200, 1);
- if(confirmButton){
- break;
- }
- }catch(e){
- await new Promise(resolve => setTimeout(resolve, CLICK_DELAY * 2));
- }
- }
- if (!confirmButton) {
- throw new Error('Could not find confirmation Block button after retries.');
- }
- if (!await clickElement(confirmButton)) {
- throw new Error('Failed to click confirmation Block button.');
- }
- console.log('Quick Block: User blocked successfully!');
- button.textContent = 'Blocked!';
- button.style.backgroundImage = 'linear-gradient(to bottom right, #4CAF50, #388E3C)'; // Green gradient
- button.style.boxShadow = '0px 4px 10px rgba(0, 0, 0, 0.3)';
- } catch (error) {
- console.error('Quick Block Error:', error.message, tweetArticle);
- button.textContent = 'Error';
- button.style.backgroundColor = 'orange';
- button.style.backgroundImage = 'none';
- setTimeout(() => {
- button.disabled = false;
- button.textContent = BUTTON_TEXT;
- button.style.backgroundImage = 'linear-gradient(to bottom right, #f44336, #d32f2f)';
- button.style.boxShadow = '0px 4px 10px rgba(0, 0, 0, 0.3)';
- }, 2000);
- }
- }
- // --- Function to Add Buttons to Tweets (Robust) ---
- function addBlockButtons() {
- const tweets = document.querySelectorAll('article[data-testid="tweet"]:not([data-quickblock-added])');
- tweets.forEach(tweet => {
- tweet.setAttribute('data-quickblock-added', 'true');
- const actionBar = findActionBar(tweet);
- if (actionBar) {
- const blockButton = document.createElement('button');
- blockButton.textContent = BUTTON_TEXT;
- blockButton.style.cssText = buttonStyle;
- blockButton.title = "Quickly block the user who posted this tweet";
- blockButton.addEventListener('mouseover', () => {
- if (!blockButton.disabled) {
- blockButton.style.cssText = buttonHoverStyle;
- }
- });
- blockButton.addEventListener('mouseout', () => {
- if (!blockButton.disabled && blockButton.textContent === BUTTON_TEXT) {
- blockButton.style.cssText = buttonStyle;
- }
- });
- blockButton.addEventListener('mousedown', () => {
- if (!blockButton.disabled) {
- blockButton.style.cssText = buttonActiveStyle;
- }
- });
- blockButton.addEventListener('mouseup', () => {
- if (!blockButton.disabled) {
- blockButton.style.cssText = buttonStyle;
- }
- });
- blockButton.addEventListener('click', quickBlockUser);
- if (!actionBar.querySelector('.quick-block-button')) {
- blockButton.classList.add('quick-block-button');
- actionBar.appendChild(blockButton);
- }
- } else {
- const tweetLinkElement = tweet.querySelector('a[href*="/status/"]');
- let tweetIdentifier = 'Tweet content unavailable or structure changed';
- if (tweetLinkElement && tweetLinkElement.href) {
- tweetIdentifier = tweetLinkElement.href;
- } else {
- const userHandleElement = tweet.querySelector('a[href^="/"][role="link"] > div[dir="ltr"] > span');
- if (userHandleElement && userHandleElement.textContent.startsWith('@')) {
- tweetIdentifier = `Tweet by ${userHandleElement.textContent}`;
- }
- }
- console.warn(`Quick Block: Could not find action bar for tweet: ${tweetIdentifier}`, tweet);
- }
- });
- }
- // --- Run Periodically and Use MutationObserver ---
- addBlockButtons();
- const observer = new MutationObserver((mutationsList) => {
- let foundTweets = false;
- for (const mutation of mutationsList) {
- if (mutation.type === 'childList' && mutation.addedNodes.length > 0) {
- mutation.addedNodes.forEach(node => {
- if (node.nodeType === Node.ELEMENT_NODE && (node.matches('article[data-testid="tweet"]') || node.querySelector('article[data-testid="tweet"]'))) {
- foundTweets = true;
- }
- });
- }
- if (foundTweets) break;
- }
- if (foundTweets) {
- requestAnimationFrame(addBlockButtons);
- }
- });
- const mainContentArea = document.querySelector('main');
- if (mainContentArea) {
- observer.observe(mainContentArea, { childList: true, subtree: true });
- } else {
- console.warn("Quick Block: Could not find <main> element, observing document.body. This might be less efficient.");
- observer.observe(document.body, { childList: true, subtree: true });
- }
- setInterval(addBlockButtons, CHECK_INTERVAL * 2);
- console.log('X.com Quick Block script loaded (v1.3 - Stylish).');
- })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址