链滴论坛 block 用户脚本

场景:首页列表、评论、发帖页面。

用法,打开目标用户发的帖子,左上角添加了红色按钮用于 block 用户。

作用:减少看见目标用户的可能。

支持移除 block 用户。

image.png

// ==UserScript==
// @name         Ld246.com User Blocker (with Comment Filter)
// @namespace    http://tampermonkey.net/
// @version      5.1
// @icon         https://ld246.com/images/favicon.png
// @description  Blocks posts and comments from specified users on ld246.com. Robust handling for dynamic content.
// @author       Gemini & Your Name
// @match        https://ld246.com/*
// @grant        GM_addStyle
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_registerMenuCommand
// ==/UserScript==

(function() {
    'use strict';

    // --- CONFIGURATION & STATE MANAGEMENT ---
    let config = {
        isEnabled: GM_getValue('isEnabled', true),
        blockedUsers: GM_getValue('blockedUsers', [])
    };

    function saveConfig() {
        GM_setValue('isEnabled', config.isEnabled);
        GM_setValue('blockedUsers', config.blockedUsers);
    }

    // --- MENU COMMANDS ---
    function toggleManagementPanel() {
        const menu = document.getElementById('blocker-management-panel');
        if (menu) {
            menu.style.display = menu.style.display === 'none' ? 'block' : 'none';
        }
    }
    GM_registerMenuCommand('Manage Blocked Users', toggleManagementPanel);
    GM_registerMenuCommand((config.isEnabled ? 'Disable' : 'Enable') + ' User Blocker', () => {
        config.isEnabled = !config.isEnabled;
        saveConfig();
        alert(`User blocker is now ${config.isEnabled ? 'enabled' : 'disabled'}. Page will reload.`);
        location.reload();
    });

    // --- CORE BLOCKING FUNCTIONS ---

    /**
     * A unified function to find and hide posts and comments from blocked users.
     * It uses a data attribute `data-block-checked` to ensure elements are processed only once ("cache" mechanism).
     */
    function processPageContent() {
        if (!config.isEnabled) return;

        // 1. Filter posts on any list page
        document.querySelectorAll('.article-list__item:not([data-block-checked])').forEach(post => {
            post.dataset.blockChecked = 'true'; // Mark as processed
            const userLink = post.querySelector('.article-list__user a:first-child');
            if (userLink) {
                const username = userLink.href.split('/member/')[1];
                if (username && config.blockedUsers.includes(username)) {
                    post.style.display = 'none';
                }
            }
        });

        // 2. Filter comments on article pages
        if (window.location.pathname.startsWith('/article/')) {
            document.querySelectorAll('.commentList > li:not([data-block-checked])').forEach(comment => {
                comment.dataset.blockChecked = 'true'; // Mark as processed
                const userLink = comment.querySelector('a[href*="/member/"]');
                if (userLink) {
                    const username = userLink.href.split('/member/')[1];
                    if (username && config.blockedUsers.includes(username)) {
                        comment.style.display = 'none';
                    }
                }
            });
        }
    }


    /**
     * If the article's author is blocked, show a modal overlay.
     */
    function handleBlockedAuthor() {
        const authorLink = document.querySelector('a[rel="author"]');
        if (!authorLink) return;

        const username = authorLink.href.split('/member/')[1];
        const title = document.title.substring(0, document.title.length - 5);
        if (username && config.blockedUsers.includes(username)) {
            const modal = document.createElement('div');
            modal.id = 'author-blocked-modal';
            modal.innerHTML = `
                <div class="modal-content">
                    <h2>关于《${title}》</h2>
                    <p>该贴主(<b>${username}</b>)已被加入屏蔽列表  </p>
                    <button id="continue-reading-btn">继续阅读</button>
                    <button id="back-home-btn">返回上一页</button>
                    <a href="https://ld246.com/">返回主页</a>
                </div>
            `;
            document.body.appendChild(modal);
            document.getElementById('continue-reading-btn').addEventListener('click', () => {
                modal.remove();
            });

            document.getElementById('back-home-btn').addEventListener('click', () => {
                history.back();
            });
        }
    }

    /**
     * Adds a "Block User" button next to the author's name on article pages.
     */
    function addBlockButtonToArticlePage() {
        setTimeout(() => {
            const sideUserDiv = document.querySelector('.article__sideuser');
            const authorLink = document.querySelector('a[rel="author"]');
            if (!sideUserDiv || !authorLink || sideUserDiv.querySelector('.user-block-btn')) return;

            const username = authorLink.href.split('/member/')[1];
            if (!username) return;

            const blockButton = document.createElement('button');
            blockButton.className = 'btn btn--small user-block-btn';
            blockButton.style.marginLeft = '10px';
            blockButton.style.color = 'white';
            blockButton.style.border = 'none';

            const updateButtonState = () => {
                blockButton.textContent = config.blockedUsers.includes(username) ? 'Unblock User' : 'Block User';
                blockButton.style.backgroundColor = config.blockedUsers.includes(username) ? '#5cb85c' : '#d9534f';
            };

            blockButton.addEventListener('click', () => {
                const isCurrentlyBlocked = config.blockedUsers.includes(username);
                if (isCurrentlyBlocked) {
                    config.blockedUsers = config.blockedUsers.filter(u => u !== username);
                } else {
                    config.blockedUsers.push(username);
                }
                saveConfig();
                alert(`User ${username} has been ${isCurrentlyBlocked ? 'unblocked' : 'blocked'}.`);
                updateButtonState();
                if (!isCurrentlyBlocked) location.reload();
            });

            updateButtonState();
            sideUserDiv.appendChild(blockButton);
        }, 500);
    }

    // --- UI PANEL (Accessed via Menu Command) ---
    function createManagementPanel() {
        const panel = document.createElement('div');
        panel.id = 'blocker-management-panel';
        panel.style.display = 'none';
        document.body.appendChild(panel);

        function renderPanel() {
            panel.innerHTML = `
                <div class="panel-header">
                    <h4>Manage Blocked Users</h4>
                    <span class="close-btn" title="Close">×</span>
                </div>
                <div class="panel-content">
                    <h5>Blocked Users List</h5>
                    <div id="blocked-users-list">
                        ${config.blockedUsers.map(user => `
                            <div class="blocked-user-item">
                                <span>${user}</span>
                                <button data-user="${user}" class="remove-btn">Remove</button>
                            </div>`).join('') || '<p>No users blocked.</p>'}
                    </div>
                </div>`;

            panel.querySelector('.close-btn').addEventListener('click', toggleManagementPanel);
            panel.querySelectorAll('.remove-btn').forEach(button => {
                button.addEventListener('click', (e) => {
                    const userToRemove = e.target.getAttribute('data-user');
                    config.blockedUsers = config.blockedUsers.filter(u => u !== userToRemove);
                    saveConfig();
                    renderPanel();
                    location.reload();
                });
            });
        }
        renderPanel();
    }

    // --- STYLES ---
    function addGlobalStyles() {
        GM_addStyle(`
            /* Management Panel Styles */
            #blocker-management-panel { position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 350px; background-color: #fff; border-radius: 8px; box-shadow: 0 5px 15px rgba(0,0,0,0.3); z-index: 2000; border: 1px solid #ddd; font-family: sans-serif; }
            #blocker-management-panel .panel-header { display: flex; justify-content: space-between; align-items: center; padding: 12px 15px; background-color: #f7f7f7; border-bottom: 1px solid #ddd; }
            #blocker-management-panel h4, #blocker-management-panel h5 { margin: 0; padding: 0; }
            #blocker-management-panel h5 { margin-bottom: 10px; }
            #blocker-management-panel .close-btn { font-size: 24px; cursor: pointer; color: #888; }
            #blocker-management-panel .panel-content { padding: 15px; max-height: 400px; overflow-y: auto; }
            #blocker-management-panel .blocked-user-item { display: flex; justify-content: space-between; align-items: center; padding: 8px 0; border-bottom: 1px solid #f0f0f0; }
            #blocker-management-panel .remove-btn { background-color: #007bff; color: white; border: none; padding: 4px 8px; border-radius: 4px; cursor: pointer; }
            #blocker-management-panel p { font-size: 0.9em; color: #666; }
            /* Blocked Author Modal Styles */
            #author-blocked-modal { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(0, 0, 0, 0.75); z-index: 9999; display: flex; align-items: center; justify-content: center; backdrop-filter: blur(5px); }
            #author-blocked-modal .modal-content { background-color: #fff; padding: 30px 40px; border-radius: 8px; text-align: center; box-shadow: 0 5px 20px rgba(0,0,0,0.4); }
            #author-blocked-modal h2 { margin-top: 0; }
            #author-blocked-modal button { background-color: #28a745; color: white; border: none; padding: 10px 20px; font-size: 1em; border-radius: 5px; cursor: pointer; margin-top: 15px; margin-right: 10px; }
        `);
    }

    // --- INITIALIZATION ---
    function init() {
        addGlobalStyles();
        createManagementPanel();

        if (!config.isEnabled) {
            console.log('Ld246 User Blocker is disabled.');
            return;
        }

        // --- Robust Content Filtering Strategy ---
        // 1. Initial run for content already on the page.
        processPageContent();

        // 2. MutationObserver for efficiently handling dynamically added content.
        const observer = new MutationObserver(processPageContent);
        observer.observe(document.body, { childList: true, subtree: true });

        // 3. Polling as a fallback for any content loading method that might bypass the observer.
        // This is the "兜底操作" (fallback operation).
        setInterval(processPageContent, 750); // Check roughly every 0.75 seconds.

        // --- Page-Specific Logic ---
        if (window.location.pathname.startsWith('/article/')) {
            handleBlockedAuthor();
            addBlockButtonToArticlePage();
        }
    }

    // Run the script after the DOM is ready.
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', init);
    } else {
        init();
    }

})();
  • 链滴

    链滴是一个记录生活的地方。

    记录生活,连接点滴

    203 引用 • 4024 回帖
1 操作
xqh042 在 2025-11-08 12:04:02 更新了该帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...

推荐标签 标签

  • Access
    1 引用 • 3 回帖 • 13 关注
  • jsoup

    jsoup 是一款 Java 的 HTML 解析器,可直接解析某个 URL 地址、HTML 文本内容。它提供了一套非常省力的 API,可通过 DOM,CSS 以及类似于 jQuery 的操作方法来取出和操作数据。

    6 引用 • 1 回帖 • 517 关注
  • Lute

    Lute 是一款结构化的 Markdown 引擎,支持 Go 和 JavaScript。

    29 引用 • 202 回帖 • 54 关注
  • uTools

    uTools 是一个极简、插件化、跨平台的现代桌面软件。通过自由选配丰富的插件,打造你得心应手的工具集合。

    9 引用 • 75 回帖 • 1 关注
  • OpenStack

    OpenStack 是一个云操作系统,通过数据中心可控制大型的计算、存储、网络等资源池。所有的管理通过前端界面管理员就可以完成,同样也可以通过 Web 接口让最终用户部署资源。

    10 引用 • 9 关注
  • Elasticsearch

    Elasticsearch 是一个基于 Lucene 的搜索服务器。它提供了一个分布式多用户能力的全文搜索引擎,基于 RESTful 接口。Elasticsearch 是用 Java 开发的,并作为 Apache 许可条款下的开放源码发布,是当前流行的企业级搜索引擎。设计用于云计算中,能够达到实时搜索,稳定,可靠,快速,安装使用方便。

    117 引用 • 99 回帖 • 191 关注
  • 单点登录

    单点登录(Single Sign On)是目前比较流行的企业业务整合的解决方案之一。SSO 的定义是在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统。

    9 引用 • 25 回帖 • 9 关注
  • Sphinx

    Sphinx 是一个基于 SQL 的全文检索引擎,可以结合 MySQL、PostgreSQL 做全文搜索,它可以提供比数据库本身更专业的搜索功能,使得应用程序更容易实现专业化的全文检索。

    1 引用 • 259 关注
  • Excel
    32 引用 • 29 回帖 • 1 关注
  • NGINX

    NGINX 是一个高性能的 HTTP 和反向代理服务器,也是一个 IMAP/POP3/SMTP 代理服务器。 NGINX 是由 Igor Sysoev 为俄罗斯访问量第二的 Rambler.ru 站点开发的,第一个公开版本 0.1.0 发布于 2004 年 10 月 4 日。

    316 引用 • 547 回帖 • 4 关注
  • Electron

    Electron 基于 Chromium 和 Node.js,让你可以使用 HTML、CSS 和 JavaScript 构建应用。它是一个由 GitHub 及众多贡献者组成的活跃社区共同维护的开源项目,兼容 Mac、Windows 和 Linux,它构建的应用可在这三个操作系统上面运行。

    16 引用 • 143 回帖 • 6 关注
  • 域名

    域名(Domain Name),简称域名、网域,是由一串用点分隔的名字组成的 Internet 上某一台计算机或计算机组的名称,用于在数据传输时标识计算机的电子方位(有时也指地理位置)。

    43 引用 • 208 回帖
  • 禅道

    禅道是一款国产的开源项目管理软件,她的核心管理思想基于敏捷方法 scrum,内置了产品管理和项目管理,同时又根据国内研发现状补充了测试管理、计划管理、发布管理、文档管理、事务管理等功能,在一个软件中就可以将软件研发中的需求、任务、bug、用例、计划、发布等要素有序的跟踪管理起来,完整地覆盖了项目管理的核心流程。

    11 引用 • 15 回帖 • 1 关注
  • 知乎

    知乎是网络问答社区,连接各行各业的用户。用户分享着彼此的知识、经验和见解,为中文互联网源源不断地提供多种多样的信息。

    10 引用 • 66 回帖
  • Vue.js

    Vue.js(读音 /vju ː/,类似于 view)是一个构建数据驱动的 Web 界面库。Vue.js 的目标是通过尽可能简单的 API 实现响应的数据绑定和组合的视图组件。

    269 引用 • 666 回帖 • 1 关注
  • Solo

    Solo 是一款小而美的开源博客系统,专为程序员设计。Solo 有着非常活跃的社区,可将文章作为帖子推送到社区,来自社区的回帖将作为博客评论进行联动(具体细节请浏览 B3log 构思 - 分布式社区网络)。

    这是一种全新的网络社区体验,让热爱记录和分享的你不再感到孤单!

    1449 引用 • 10092 回帖 • 489 关注
  • 宕机

    宕机,多指一些网站、游戏、网络应用等服务器一种区别于正常运行的状态,也叫“Down 机”、“当机”或“死机”。宕机状态不仅仅是指服务器“挂掉了”、“死机了”状态,也包括服务器假死、停用、关闭等一些原因而导致出现的不能够正常运行的状态。

    13 引用 • 82 回帖 • 75 关注
  • LeetCode

    LeetCode(力扣)是一个全球极客挚爱的高质量技术成长平台,想要学习和提升专业能力从这里开始,充足技术干货等你来啃,轻松拿下 Dream Offer!

    209 引用 • 72 回帖 • 1 关注
  • Kotlin

    Kotlin 是一种在 Java 虚拟机上运行的静态类型编程语言,由 JetBrains 设计开发并开源。Kotlin 可以编译成 Java 字节码,也可以编译成 JavaScript,方便在没有 JVM 的设备上运行。在 Google I/O 2017 中,Google 宣布 Kotlin 成为 Android 官方开发语言。

    19 引用 • 33 回帖 • 87 关注
  • Telegram

    Telegram 是一个非盈利性、基于云端的即时消息服务。它提供了支持各大操作系统平台的开源的客户端,也提供了很多强大的 APIs 给开发者创建自己的客户端和机器人。

    5 引用 • 35 回帖
  • 千千插件

    千千块(自定义块 css 和 js)
    可以用 ai 提示词来无限创作思源笔记

    32 引用 • 69 回帖 • 1 关注
  • 京东

    京东是中国最大的自营式电商企业,2015 年第一季度在中国自营式 B2C 电商市场的占有率为 56.3%。2014 年 5 月,京东在美国纳斯达克证券交易所正式挂牌上市(股票代码:JD),是中国第一个成功赴美上市的大型综合型电商平台,与腾讯、百度等中国互联网巨头共同跻身全球前十大互联网公司排行榜。

    14 引用 • 102 回帖 • 261 关注
  • Notion

    Notion - The all-in-one workspace for your notes, tasks, wikis, and databases.

    10 引用 • 80 回帖 • 1 关注
  • 星云链

    星云链是一个开源公链,业内简单的将其称为区块链上的谷歌。其实它不仅仅是区块链搜索引擎,一个公链的所有功能,它基本都有,比如你可以用它来开发部署你的去中心化的 APP,你可以在上面编写智能合约,发送交易等等。3 分钟快速接入星云链 (NAS) 测试网

    3 引用 • 16 回帖
  • OpenShift

    红帽提供的 PaaS 云,支持多种编程语言,为开发人员提供了更为灵活的框架、存储选择。

    14 引用 • 20 回帖 • 687 关注
  • OkHttp

    OkHttp 是一款 HTTP & HTTP/2 客户端库,专为 Android 和 Java 应用打造。

    16 引用 • 6 回帖 • 99 关注
  • flomo

    flomo 是新一代 「卡片笔记」 ,专注在碎片化时代,促进你的记录,帮你积累更多知识资产。

    6 引用 • 144 回帖