Greasy Fork

Greasy Fork is available in English.

X/Twitter 一键屏蔽用户

在推特推文右下方添加一键屏蔽按钮

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         X/Twitter 一键屏蔽用户
// @namespace    http://tampermonkey.net/
// @version      0.1
// @description  在推特推文右下方添加一键屏蔽按钮
// @author       @Maige
// @match        https://twitter.com/*
// @match        https://x.com/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=twitter.com
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    // 配置参数
    const CONFIG = {
        buttonColor: 'rgb(83, 100, 113)',
        hoverColor: 'rgb(244, 33, 46)',
        timeouts: {
            init: 1500,
            menuAppear: 300,
            confirmAppear: 300,
            closeMenu: 300
        },
        selectors: {
            tweet: 'article[data-testid="tweet"]',
            actionBar: '[role="group"]',
            userLink: 'a[role="link"][href*="/"]',
            moreButton: '[data-testid="caret"]',
            menuItem: '[role="menuitem"]',
            confirmButton: 'div[role="button"]'
        }
    };

    // 定义屏蔽按钮的SVG图标,保持Twitter风格
    const blockIconSVG = `
        <svg viewBox="0 0 24 24" aria-hidden="true" class="r-4qtqp9 r-yyyyoo r-1xvli5t r-dnmrzs r-bnwqim r-1plcrui r-lrvibr r-1hdv0qi">
            <g>
                <path d="M12 3.75C7.99 3.75 4.75 7 4.75 11s3.24 7.25 7.25 7.25S19.25 15 19.25 11 16.01 3.75 12 3.75zm0 12.5c-2.9 0-5.25-2.35-5.25-5.25S9.1 5.75 12 5.75 17.25 8.1 17.25 11s-2.35 5.25-5.25 5.25z"></path>
                <path d="M12 2.75c-4.55 0-8.25 3.7-8.25 8.25s3.7 8.25 8.25 8.25 8.25-3.7 8.25-8.25S16.55 2.75 12 2.75zm0 14.5c-3.45 0-6.25-2.8-6.25-6.25S8.55 4.75 12 4.75s6.25 2.8 6.25 6.25-2.8 6.25-6.25 6.25z"></path>
                <path d="M18.35 4.35l-14 14 1.4 1.4 14-14-1.4-1.4z"></path>
            </g>
        </svg>
    `;

    // 监听DOM变化,为新加载的推文添加屏蔽按钮
    const observer = new MutationObserver((mutations) => {
        for (const mutation of mutations) {
            if (mutation.addedNodes && mutation.addedNodes.length > 0) {
                for (const node of mutation.addedNodes) {
                    if (node.nodeType === 1) { // 元素节点
                        const tweets = node.querySelectorAll(CONFIG.selectors.tweet);
                        if (tweets.length) tweets.forEach(addBlockButton);
                    }
                }
            }
        }
    });

    // 为已存在的推文添加屏蔽按钮
    function initExistingTweets() {
        const tweets = document.querySelectorAll(CONFIG.selectors.tweet);
        if (tweets.length) tweets.forEach(addBlockButton);
    }

    // 添加屏蔽按钮到推文
    function addBlockButton(tweet) {
        // 检查是否已经添加了屏蔽按钮
        if (tweet.querySelector('.one-click-block-btn')) return;

        // 查找推文的操作区域(右下角的分享、喜欢等按钮区域)
        const actionBar = tweet.querySelector(CONFIG.selectors.actionBar);
        if (!actionBar) return;

        // 创建屏蔽按钮
        const blockButton = document.createElement('div');
        blockButton.className = 'one-click-block-btn';
        blockButton.innerHTML = blockIconSVG;
        blockButton.title = '屏蔽此用户';
        blockButton.style.cssText = `
            cursor: pointer;
            display: inline-flex;
            margin-left: 8px;
            color: ${CONFIG.buttonColor};
        `;

        // 添加悬停效果
        blockButton.addEventListener('mouseover', () => blockButton.style.color = CONFIG.hoverColor);
        blockButton.addEventListener('mouseout', () => blockButton.style.color = CONFIG.buttonColor);

        // 添加点击事件
        blockButton.addEventListener('click', (e) => {
            e.preventDefault();
            e.stopPropagation();
            
            // 获取用户信息
            const userLink = tweet.querySelector(CONFIG.selectors.userLink);
            if (!userLink) return;
            
            const username = userLink.href.split('/').filter(Boolean).pop();
            if (!username) return;
            
            // 屏蔽用户
            blockUser(username, tweet);
        });

        // 将按钮添加到操作栏
        actionBar.appendChild(blockButton);
    }

    // 屏蔽用户的函数
    function blockUser(username, tweetElement) {
        // 查找用户的更多操作按钮
        const moreButton = tweetElement.querySelector(CONFIG.selectors.moreButton);
        if (!moreButton) {
            console.error(`无法找到用户 @${username} 的更多操作按钮`);
            return;
        }
        
        // 点击更多按钮
        moreButton.click();
        
        // 等待菜单出现并点击屏蔽选项
        setTimeout(() => {
            const blockOption = Array.from(document.querySelectorAll(CONFIG.selectors.menuItem))
                .find(item => item.textContent.includes('屏蔽') || item.textContent.includes('Block'));
            
            if (!blockOption) {
                document.body.click(); // 关闭菜单
                return;
            }
            
            // 点击屏蔽选项
            blockOption.click();
            
            // 等待确认对话框出现并确认
            setTimeout(() => {
                const confirmButton = Array.from(document.querySelectorAll(CONFIG.selectors.confirmButton))
                    .find(btn => btn.textContent.includes('屏蔽') || btn.textContent.includes('Block'));
                
                if (!confirmButton) {
                    document.body.click(); // 关闭菜单
                    return;
                }
                
                // 点击确认按钮
                confirmButton.click();
                console.log(`已屏蔽用户: @${username}`);
                
                // 关闭菜单
                setTimeout(() => document.body.click(), CONFIG.timeouts.closeMenu);
            }, CONFIG.timeouts.confirmAppear);
        }, CONFIG.timeouts.menuAppear);
    }

    // 初始化
    function init() {
        // 开始观察DOM变化
        observer.observe(document.body, {
            childList: true,
            subtree: true
        });
        
        // 处理已存在的推文
        setTimeout(initExistingTweets, CONFIG.timeouts.init);
        
        // 处理页面内导航(Twitter是SPA)
        let lastUrl = location.href;
        new MutationObserver(() => {
            const url = location.href;
            if (url !== lastUrl) {
                lastUrl = url;
                setTimeout(initExistingTweets, CONFIG.timeouts.init);
            }
        }).observe(document, {subtree: true, childList: true});
    }

    // 页面加载完成后初始化
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', init);
    } else {
        init();
    }
})();