Greasy Fork

Greasy Fork is available in English.

Twitter/X 屏蔽互fo/互粉/互关推文

自动折叠包含"互fo"、"互粉"、"互关"、"互赞"等关键词的用户推文

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         Twitter/X 屏蔽互fo/互粉/互关推文
// @namespace    https://github.com/Asakushen/Twitter_Mutual_Follow_Blocker
// @version      1.0
// @description  自动折叠包含"互fo"、"互粉"、"互关"、"互赞"等关键词的用户推文
// @author       Asakushen
// @match        https://twitter.com/*
// @match        https://x.com/*
// @icon           https://s2.loli.net/2025/11/27/NsGnT9k3HFJeUhv.png
// @grant        GM_addStyle
// @run-at       document-end
// @license      MIT
// @homepageURL  https://github.com/Asakushen/Twitter_Mutual_Follow_Blocker
// @supportURL   https://github.com/Asakushen/Twitter_Mutual_Follow_Blocker/issues
// ==/UserScript==

(function() {
    'use strict';

    //Configs: 你可以在这里添加更多你想屏蔽的关键词
    const BLOCK_KEYWORDS = [/互(fo|粉|关|赞|推|回)/i, /fo回/i, /诚信互/i];

    // CSS样式:定义折叠后的外观
    GM_addStyle(`
        .my-blocked-tweet {
            display: none !important;
        }
        .my-blocked-placeholder {
            padding: 12px 16px;
            background-color: rgba(29, 155, 240, 0.1); /* 推特蓝底色 */
            border-bottom: 1px solid rgb(56, 68, 77);
            color: #71767b;
            font-size: 13px;
            cursor: pointer;
            text-align: center;
            border-radius: 4px;
            margin: 5px 0;
            transition: all 0.2s;
        }
        .my-blocked-placeholder:hover {
            background-color: rgba(29, 155, 240, 0.2);
            color: #1d9bf0;
        }
        /* 适配亮色模式 */
        @media (prefers-color-scheme: light) {
             .my-blocked-placeholder {
                background-color: #f7f9f9;
                border-bottom: 1px solid #eff3f4;
             }
        }
    `);

    // 核心检测函数
    function checkAndBlockTweets() {
        // 选取所有的推文容器 (根据你提供的HTML,article元素带有 data-testid="tweet")
        const tweets = document.querySelectorAll('article[data-testid="tweet"]:not([data-mutual-checked])');

        tweets.forEach(tweet => {
            // 标记已检查,避免重复处理
            tweet.setAttribute('data-mutual-checked', 'true');

            // 1. 查找用户名区域
            const userNameNode = tweet.querySelector('[data-testid="User-Name"]');
            if (!userNameNode) return;

            // 2. 获取所有文本内容(包括昵称、@ID等)
            const textContent = userNameNode.innerText;

            // 3. 正则匹配
            const isMatch = BLOCK_KEYWORDS.some(regex => regex.test(textContent));

            if (isMatch) {
                // 找到匹配的关键词,执行折叠操作
                console.log(`[屏蔽互粉] 折叠了用户: ${textContent.replace(/\n/g, ' ')}`);
                foldTweet(tweet, textContent);
            }
        });
    }

    // 折叠推文的具体逻辑
    function foldTweet(tweetElement, userInfo) {
        // 获取第一行文本作为提示(通常是昵称)
        const displayName = userInfo.split('\n')[0] || "互关用户";

        // 1. 隐藏推文内容的直接子元素(不使用display:none隐藏article本身,因为可能会影响推特的虚拟滚动计算)
        const children = Array.from(tweetElement.children);
        children.forEach(child => child.classList.add('my-blocked-tweet'));

        // 2. 插入占位提示条
        const placeholder = document.createElement('div');
        placeholder.className = 'my-blocked-placeholder';
        placeholder.textContent = `已折叠一条来自 "${displayName}" 的推文 (包含互粉关键词) - 点击查看`;

        // 3. 点击事件:恢复显示
        placeholder.addEventListener('click', (e) => {
            e.stopPropagation(); // 防止触发推特原本的点击事件
            placeholder.remove();
            children.forEach(child => child.classList.remove('my-blocked-tweet'));
        });

        tweetElement.appendChild(placeholder);
    }

    // 使用 MutationObserver 监听页面变化(因为推特是无限滚动页面)
    const observer = new MutationObserver((mutations) => {
        // 简单的防抖优化,不需要每次微小变动都执行,但推特流很快,直接执行通常也无妨
        checkAndBlockTweets();
    });

    // 开始监听 body
    observer.observe(document.body, {
        childList: true,
        subtree: true
    });

    // 初始运行一次
    checkAndBlockTweets();

})();