水源低质量发帖过滤器

从水源主页直接屏蔽常见类型的低质量发帖,如垃圾标题、戾气帖子等。误伤概率高,宁杀一千原则,请谨慎使用。

目前為 2023-12-02 提交的版本,檢視 最新版本

// ==UserScript==
// @name         水源低质量发帖过滤器
// @license      MIT
// @namespace    https://gf.qytechs.cn/scripts/480756
// @version      2.1
// @description  从水源主页直接屏蔽常见类型的低质量发帖,如垃圾标题、戾气帖子等。误伤概率高,宁杀一千原则,请谨慎使用。
// @match        https://shuiyuan.sjtu.edu.cn/*
// @grant        none
// ==/UserScript==

(function() {
    'use strict';
    var removeMode = "remove";
    var device;
    var counter = 0;//屏蔽计数器
    var blacklistKeywords = ["😅", "插头", "图书馆键盘", "占座", "交小苗", "拔电", "电动车充", "充电桩", "拔充", "拔插", "车充", "拔线", "恶心", "分流", "被拔", "乱拔", "偷充", "别拔", "拔人"];
    var whitelistWords = ["投喂", "公告", "搭子", "分享", "建一"];//最高优先级:有白名单总会不屏蔽
    var clickbaitList = ["!", ":", ",", "禁", "官", "竟"];
    // 检测 customBlacklistWord 是否存在自定义值
    if (localStorage.getItem('customBlacklistWord')) {
        var customBlacklistWord = localStorage.getItem('customBlacklistWord');
        blacklistKeywords = customBlacklistWord.split(',');
    }

    // 检测 customWhitelistWord 是否存在于 localStorage
    if (localStorage.getItem('customWhitelistWord')) {
        var customWhitelistWord = localStorage.getItem('customWhitelistWord');
        whitelistWords = customWhitelistWord.split(',');
    }

    // 检测 customClickbaitWord 是否存在于 localStorage
    if (localStorage.getItem('customClickbaitWord')) {
        var customClickbaitWord = localStorage.getItem('customClickbaitWord');
        clickbaitList = customClickbaitWord.split(',');
    }

    // 主页已屏蔽数量文字提示
    var blockTextInfo = "😍: 0";
    var blocktext = document.createElement('div');
    blocktext.innerText = blockTextInfo;
    blocktext.style.height = '100%';
    blocktext.style.display = 'flex';
    blocktext.style.alignItems = 'center';
    blocktext.style.justifyContent = 'center';
    blocktext.style.padding = '0 10px';
    blocktext.classList.add('block-number-text');
    blocktext.addEventListener('click', function() {
        var blocktextClickConfirmed = confirm("水源低质量发帖过滤器运行中,前往“偏好设置-界面”以自定义过滤器功能。");
    });


    //屏蔽模式自动识别
    var htmlElement = document.documentElement;
    var isDeviceMobile = htmlElement.classList.contains('mobile-view');//移动端设备采用无感屏蔽存在无法无限加载的问题。移动端不启用移除模式,而是使用隐藏模式,保留控件占位
    if(isDeviceMobile){
        device = "mobile";
    }
    else{
        device = "others";
    }
    if (localStorage.getItem('removeMode') === null) {
        if (device == "mobile"){
            removeMode = "hide";
        }
        localStorage.setItem('removeMode', removeMode);
    }
    else{
        removeMode = localStorage.getItem('removeMode');
    }
    //顺便读取过短标题阈值长度
    var shortTitleThresh = localStorage.getItem('shortTitleThresh');
    if (shortTitleThresh === null){
        shortTitleThresh = 6;//默认
    }

    function getCurrentDate() {
        var today = new Date();
        var year = today.getFullYear();
        var month = String(today.getMonth() + 1).padStart(2, '0');
        var day = String(today.getDate()).padStart(2, '0');
        var currentDate = year + '-' + month + '-' + day;
        console.log('currentDate:'+currentDate);
        return currentDate;
    }

    //屏蔽日志
    function blockLogWrite(blockExplanation){
        //blockExplanation
        var blocklogtext = "";
        var blockLogDate = localStorage.getItem("blockLogDateV1")
        if(blockLogDate==getCurrentDate()){
            blocklogtext = localStorage.getItem("blockLogV1")
        }
        if(removeMode=="hide"){
            //hide模式会反复写入同一个标题
            if(!blocklogtext.includes(blockExplanation)){
                localStorage.setItem('blockLogV1', blockExplanation+"\n"+blocklogtext);
                localStorage.setItem('blockLogDateV1', getCurrentDate());
            }
        }
        else{
            localStorage.setItem('blockLogV1', blockExplanation+"\n"+blocklogtext);
            localStorage.setItem('blockLogDateV1', getCurrentDate());
        }
    }


    function blockLogRead() {
        event.preventDefault();
        var blockLogDate = localStorage.getItem("blockLogDateV1");
        var blockLogText = localStorage.getItem("blockLogV1");
        console.log("blockLogRead_blockLogDateV1:" + blockLogDate);

        // 创建对话框的内容
        var blockLogDialogContent = `
        blockLogDate: ${blockLogDate}
        blockLogText: ${blockLogText}


    `;

        // 弹出对话框
        window.alert(blockLogDialogContent);
    }

    //屏蔽函数
    function block(subject){
        if(removeMode=="remove"){
            subject.remove();
        }
        else if(removeMode=="hide"){
            subject.style.visibility = 'hidden';
        }
        else{
            console.log("错误:不存在的removeMode");
        }
    }

    // 查找目标位置并添加按钮
    function addTextToContainer(num) {
        if(num == 0){
            blockTextInfo = "😍: 0";
        }
        else{
            blockTextInfo = "🚫: "+num;
        }
        var container = document.getElementById('navigation-bar');
        if(container){
            var existingBlockText = container.querySelector('.block-number-text');
            if(existingBlockText){
                existingBlockText.innerText = blockTextInfo;
            }
            else{
                var firstChild = container.firstElementChild;
                container.insertBefore(blocktext, firstChild);
            }
        }
    }


    setInterval(function() {

        // 获取所有需要检测的元素
        var elements = document.querySelectorAll('a[role="heading"]');
        console.log('元素数量:', elements.length);
        var isTrashContentFlag;
        var blockExplanation;


        // 屏蔽检测与删除
        for (var i = 0; i < elements.length; i++) {
            isTrashContentFlag = false;

            //---白名单检测---
            var element = elements[i];
            var textContent = element.innerText.trim();
            blockExplanation = '['+textContent+'] '
            var containsWhitelistWord = false;
            for (var j = 0; j < whitelistWords.length; j++) {
                if (textContent.includes(whitelistWords[j])) {
                    containsWhitelistWord = true;
                    console.log('白名单通过:', textContent, ' ', whitelistWords[j]);
                    break;
                }
            }
            //---白名单检测:结束---

            var parentElement = element.parentElement;
            // 1. 如果文本内容少于等于六个字,判定为过短标题
            if (textContent.length <= shortTitleThresh) {
                console.log('标记:', textContent);
                if (parentElement && parentElement.parentElement && parentElement.parentElement.parentElement) {
                    isTrashContentFlag = true;
                    blockExplanation = blockExplanation+"规则1:过短标题(文本内容少于等于一定长度:"+shortTitleThresh+");";
                    //parentElement.parentElement.parentElement.style.display = 'none';
                    //parentElement.parentElement.parentElement.style.height = '0';
                    //parentElement.parentElement.parentElement.style.visibility = 'hidden';
                    //parentElement.parentElement.parentElement.style.opacity = '0';
                    //parentElement.parentElement.parentElement.remove();
                    //block(parentElement.parentElement.parentElement);
                    //counter++;
                }
            }


            // 2. 检查文本是否包含关键词
            var containsKeyword = false;
            for (j = 0; j < blacklistKeywords.length; j++) {
                if (textContent.includes(blacklistKeywords[j])) {
                    containsKeyword = true;
                    console.log('关键词检测:', textContent, ' ', blacklistKeywords[j]);
                    break;
                }
            }
            if (containsKeyword) {
                console.log('标记:', textContent);
                if (parentElement && parentElement.parentElement && parentElement.parentElement.parentElement) {
                    isTrashContentFlag = true;
                    blockExplanation = blockExplanation+"规则2:黑名单屏蔽(标题命中黑名单关键词);";
                }
            }

            // 3. 标题党专项屏蔽:逻辑为,12个字符以内,含有2个敏感字符(!:,禁官竟)
            var clickbaitScore = 0;
            for (j = 0; j < clickbaitList.length; j++) {
                if (textContent.includes(clickbaitList[j])) {
                    clickbaitScore++;
                }
            }
            if (clickbaitScore>=2 && textContent.length <= 12) {
                console.log('标记:', textContent);
                parentElement = element.parentElement;
                if (parentElement && parentElement.parentElement && parentElement.parentElement.parentElement) {
                    isTrashContentFlag = true;
                    blockExplanation = blockExplanation+"规则3:疑似标题党(标题党指数过高,且文本过短。clickbaitScore="+clickbaitScore+");";
                }
            }

            //---开始屏蔽----
            if(isTrashContentFlag && !containsWhitelistWord){
                block(parentElement.parentElement.parentElement);
                blockLogWrite(blockExplanation);
                counter++;
            }
            //---开始屏蔽:完成----
        }



        //显示屏蔽数量
        addTextToContainer(counter);
        if(removeMode=="hide"){
            counter = 0;
        }

    }, 500);


    function addSettingArea() {
        //<div class="control-group theme" data-setting-name="user-theme">
        var targetElement = document.querySelector('div[data-setting-name="user-theme"]');
        var createdElement = document.querySelector('.lowQualityPostsBlocker');

        // 检测指定代码是否存在
        if (targetElement && !createdElement) {
            console.log("已找到control-group theme标签");

            // 创建外层容器
            var containerElement = document.createElement('div');
            containerElement.style.backgroundColor = '#f4f4f4';
            containerElement.style.padding = '10px';
            containerElement.style.marginBottom = '10px';
            containerElement.classList.add("lowQualityPostsBlocker");

            // 创建标题元素
            var titleElement = document.createElement('h2');
            titleElement.style.fontWeight = 'bold';
            titleElement.style.color = 'black';
            titleElement.style.marginBottom = '10px';
            titleElement.textContent = '低质量发帖过滤器';
            containerElement.appendChild(titleElement);

            //分割线
            var separator0 = document.createElement('hr');
            containerElement.appendChild(separator0);
            var titleElement0 = document.createElement('h3');
            titleElement0.style.fontWeight = 'bold';
            titleElement0.style.color = 'black';
            titleElement0.style.marginBottom = '10px';
            titleElement0.textContent = '关于与调试';
            containerElement.appendChild(titleElement0);

            // 创建按钮容器
            var buttonsContainer = document.createElement('div');
            buttonsContainer.style.display = 'flex';
            buttonsContainer.style.gap = '10px';
            buttonsContainer.style.marginTop = '10px';
            containerElement.appendChild(buttonsContainer);

            // 创建脚本主页按钮
            var homepageButton = document.createElement('button');
            homepageButton.textContent = '脚本主页';
            homepageButton.style.padding = '6px 12px';
            homepageButton.style.backgroundColor = '#ccc';
            homepageButton.style.border = 'none';
            homepageButton.style.color = '#000';
            homepageButton.addEventListener('click', function() {
                event.preventDefault();
                window.open('https://gf.qytechs.cn/scripts/480756', '_blank');
            });
            buttonsContainer.appendChild(homepageButton);

            // 创建查看日志按钮
            var logButton = document.createElement('button');
            logButton.textContent = '查看日志';
            logButton.style.padding = '6px 12px';
            logButton.style.backgroundColor = '#ccc';
            logButton.style.border = 'none';
            logButton.style.color = '#000';
            logButton.addEventListener('click', blockLogRead);
            buttonsContainer.appendChild(logButton);

            //分割线
            var separator1 = document.createElement('hr');
            containerElement.appendChild(separator1);
            var titleElement1 = document.createElement('h3');
            titleElement1.style.fontWeight = 'bold';
            titleElement1.style.color = 'black';
            titleElement1.style.marginBottom = '10px';
            titleElement1.textContent = '屏蔽自定义';
            containerElement.appendChild(titleElement1);

            // 创建文本框
            var textBox = document.createElement('textarea');
            textBox.rows = 3;
            textBox.placeholder = '输入关键词,用英文逗号分割。例如:拔插头,震惊,占座';
            textBox.style.marginTop = '10px';
            containerElement.appendChild(textBox);

            // 创建单选框容器
            var radioContainer = document.createElement('div');
            radioContainer.style.display = 'flex';
            radioContainer.style.gap = '10px';
            radioContainer.style.marginTop = '10px';
            containerElement.appendChild(radioContainer);

            // 创建黑名单词单选框
            var blacklistRadio = document.createElement('input');
            blacklistRadio.type = 'radio';
            blacklistRadio.name = 'filterType';
            blacklistRadio.checked = true; // 设置为默认选中
            radioContainer.appendChild(blacklistRadio);

            // 创建黑名单词标签
            var blacklistLabel = document.createElement('label');
            blacklistLabel.textContent = '黑名单词';
            radioContainer.appendChild(blacklistLabel);

            // 创建白名单词单选框
            var whitelistRadio = document.createElement('input');
            whitelistRadio.type = 'radio';
            whitelistRadio.name = 'filterType';
            radioContainer.appendChild(whitelistRadio);

            // 创建白名单词标签
            var whitelistLabel = document.createElement('label');
            whitelistLabel.textContent = '白名单词';
            radioContainer.appendChild(whitelistLabel);

            // 创建标题党词单选框
            var clickbaitRadio = document.createElement('input');
            clickbaitRadio.type = 'radio';
            clickbaitRadio.name = 'filterType';
            radioContainer.appendChild(clickbaitRadio);

            // 创建标题党词标签
            var clickbaitLabel = document.createElement('label');
            clickbaitLabel.textContent = '标题党词';
            radioContainer.appendChild(clickbaitLabel);

            // 创建按钮容器
            var actionButtonsContainer = document.createElement('div');
            actionButtonsContainer.style.display = 'flex';
            actionButtonsContainer.style.gap = '10px';
            actionButtonsContainer.style.marginTop = '10px';
            containerElement.appendChild(actionButtonsContainer);

            // 创建读取按钮
            var readButton = document.createElement('button');
            readButton.textContent = '读取';
            readButton.style.padding = '6px 12px';
            readButton.style.backgroundColor = '#ccc';
            readButton.style.border = 'none';
            readButton.style.color = '#000';
            actionButtonsContainer.appendChild(readButton);

            // 创建覆写按钮
            var overrideButton = document.createElement('button');
            overrideButton.textContent = '覆写';
            overrideButton.style.padding = '6px 12px';
            overrideButton.style.backgroundColor = '#ccc';
            overrideButton.style.border = 'none';
            overrideButton.style.color = '#000';
            actionButtonsContainer.appendChild(overrideButton);

            // 创建重写按钮
            var rewriteButton = document.createElement('button');
            rewriteButton.textContent = '默认';
            rewriteButton.style.padding = '6px 12px';
            rewriteButton.style.backgroundColor = '#ccc';
            rewriteButton.style.border = 'none';
            rewriteButton.style.color = '#000';
            actionButtonsContainer.appendChild(rewriteButton);

            // 创建说明文字
            var descriptionText = document.createElement('p');
            descriptionText.style.fontSize = '12px';
            descriptionText.textContent = '标题命中黑名单词的帖子会被直接屏蔽;命中标题党词数量越多,屏蔽指数越大,超过一定阈值会被屏蔽;命中白名单词的帖子永远不会被屏蔽。\n本脚本自带词库,可通过本功能自定义。自定义词库不会随脚本更新而更新。\n\n请注意:白名单规则具有最高优先级,如若一个符合其它规则的帖子没有被屏蔽,请优先检查其是否被白名单规则保护。';
            containerElement.appendChild(descriptionText);

            overrideButton.addEventListener('click', function(event) {handleOverrideButtonClick(textBox, blacklistRadio, whitelistRadio, clickbaitRadio)});
            readButton.addEventListener('click', function(event) {handleReadButtonClick(textBox, blacklistRadio, whitelistRadio, clickbaitRadio)});
            rewriteButton.addEventListener('click', function(event) {handleRewriteButtonClick(textBox, blacklistRadio, whitelistRadio, clickbaitRadio)});

            //分割线
            var separator2 = document.createElement('hr');
            containerElement.appendChild(separator2);
            var titleElement2 = document.createElement('h3');
            titleElement2.style.fontWeight = 'bold';
            titleElement2.style.color = 'black';
            titleElement2.style.marginBottom = '10px';
            titleElement2.textContent = '屏蔽模式';
            containerElement.appendChild(titleElement2);

            // 创建单选框容器
            var radioContainer2 = document.createElement('div');
            radioContainer2.style.display = 'flex';
            radioContainer2.style.gap = '10px';
            radioContainer2.style.marginTop = '10px';
            containerElement.appendChild(radioContainer2);

            // 创建兼容模式单选框
            var compatibilityRadio = document.createElement('input');
            compatibilityRadio.type = 'radio';
            compatibilityRadio.name = 'removeMode';
            compatibilityRadio.value = 'hide';
            radioContainer2.appendChild(compatibilityRadio);

            // 创建兼容模式标签
            var compatibilityLabel = document.createElement('label');
            compatibilityLabel.textContent = '兼容模式';
            radioContainer2.appendChild(compatibilityLabel);

            // 创建无感屏蔽单选框
            var removeRadio = document.createElement('input');
            removeRadio.type = 'radio';
            removeRadio.name = 'removeMode';
            removeRadio.value = 'remove';
            radioContainer2.appendChild(removeRadio);

            // 创建无感屏蔽标签
            var removeLabel = document.createElement('label');
            removeLabel.textContent = '无感屏蔽';
            radioContainer2.appendChild(removeLabel);

            // 创建按钮容器
            var actionButtonsContainer2 = document.createElement('div');
            actionButtonsContainer2.style.display = 'flex';
            actionButtonsContainer2.style.gap = '10px';
            actionButtonsContainer2.style.marginTop = '10px';
            containerElement.appendChild(actionButtonsContainer2);

            // 创建保存按钮
            var saveButton4RemoveType = document.createElement('button');
            saveButton4RemoveType.textContent = '保存';
            saveButton4RemoveType.style.padding = '6px 12px';
            saveButton4RemoveType.style.backgroundColor = '#ccc';
            saveButton4RemoveType.style.border = 'none';
            saveButton4RemoveType.style.color = '#000';
            actionButtonsContainer2.appendChild(saveButton4RemoveType);

            // 创建说明文本
            var descriptionTextRemoveMode = document.createElement('p');
            descriptionTextRemoveMode.style.fontSize = '12px';
            descriptionTextRemoveMode.textContent = '兼容模式适用于不同设备,屏蔽帖子时会保留其占位,也即显示一块空白区域。无感屏蔽则会将帖子直接移除,这在移动设备上可能引发加载问题。';
            containerElement.appendChild(descriptionTextRemoveMode);

            // 从 localStorage 获取保存的单选框状态
            var savedMode = localStorage.getItem('removeMode');
            if (savedMode === 'hide') {
                compatibilityRadio.checked = true;
            } else if (savedMode === 'remove') {
                removeRadio.checked = true;
            }

            // 保存按钮点击事件处理函数
            saveButton4RemoveType.addEventListener('click', function() {
                var selectedMode = '';
                if (compatibilityRadio.checked) {
                    selectedMode = 'hide';
                } else if (removeRadio.checked) {
                    selectedMode = 'remove';
                }
                localStorage.setItem('removeMode', selectedMode);
                alert('已保存选择的屏蔽模式:' + selectedMode +",刷新网页生效。");
            });

            // 分割线
            var separator3 = document.createElement('hr');
            containerElement.appendChild(separator3);

            // 创建标题元素
            var titleElement3 = document.createElement('h3');
            titleElement3.style.fontWeight = 'bold';
            titleElement3.style.color = 'black';
            titleElement3.style.marginBottom = '10px';
            titleElement3.textContent = '过短标题屏蔽';
            containerElement.appendChild(titleElement3);

            // 创建数字调整框
            var numberInput4ShortTitle = document.createElement('input');
            numberInput4ShortTitle.type = 'number';
            numberInput4ShortTitle.value = localStorage.getItem('shortTitleThresh') || 6; // 默认值为6
            containerElement.appendChild(numberInput4ShortTitle);

            // 创建保存按钮
            var saveButton4ShortTitle = document.createElement('button');
            saveButton4ShortTitle.textContent = '保存';
            saveButton4ShortTitle.style.padding = '6px 12px';
            saveButton4ShortTitle.style.backgroundColor = '#ccc';
            saveButton4ShortTitle.style.border = 'none';
            saveButton4ShortTitle.style.marginLeft = '10px';
            saveButton4ShortTitle.style.color = '#000';
            containerElement.appendChild(saveButton4ShortTitle);

            // 创建说明文本
            var descriptionText4ShortTitle = document.createElement('p');
            descriptionText4ShortTitle.style.fontSize = '12px';
            descriptionText4ShortTitle.textContent = '当标题过短时,经验判断该帖为低质量重复伸手党提问或牢骚帖的概率较大。你可以设置当标题长度小于等于某个值时默认屏蔽该帖。请注意,此选项不为0时,总会存在误伤,但过小的阈值可能导致漏网之鱼过多,且难以被其它规则屏蔽。';
            containerElement.appendChild(descriptionText4ShortTitle);

            // 点击保存按钮时的事件处理函数
            saveButton4ShortTitle.addEventListener('click', function() {
                var value = numberInput4ShortTitle.value;
                if (value>20||value<0){alert("阈值过大可能影响网页正常加载,请设置为0-20之间的值,默认为6。");}
                else{
                    localStorage.setItem('shortTitleThresh', value);
                    alert('已保存选择的阈值:' + value+",刷新网页生效。");
                }
            });

            // 将外层容器插入到目标元素之前
            targetElement.parentNode.insertBefore(containerElement, targetElement);
        }
    }

    function getSelectedFilterType(blacklistRadio, whitelistRadio, clickbaitRadio) {
        var selectedRadio = document.querySelector('input[name="filterType"]:checked');
        if (selectedRadio === blacklistRadio) {
            return 'customBlacklistWord';
        } else if (selectedRadio === whitelistRadio) {
            return 'customWhitelistWord';
        } else if (selectedRadio === clickbaitRadio) {
            return 'customClickbaitWord';
        }
    }
    // 点击"覆写"按钮时的处理函数
    function handleOverrideButtonClick(textBox, blacklistRadio, whitelistRadio, clickbaitRadio) {
        event.preventDefault();
        var text = textBox.value.trim(); // 获取文本框内容并去除首尾空格

        if (text !== '') {
            var localStorageKey = getSelectedFilterType(blacklistRadio, whitelistRadio, clickbaitRadio); // 获取选中的过滤类型
            localStorage.setItem(localStorageKey, text); // 将文本框内容存储到对应的 localStorage
            window.alert("已保存,请刷新网页以生效");
        } else {
            textBox.placeholder = '不能为空'; // 修改文本框的提示词为"不能为空"
        }
    }

    // 点击"读取"按钮时的处理函数
    function handleReadButtonClick(textBox, blacklistRadio, whitelistRadio, clickbaitRadio) {
        event.preventDefault();
        var localStorageKey = getSelectedFilterType(blacklistRadio, whitelistRadio, clickbaitRadio); // 获取选中的过滤类型
        var text = localStorage.getItem(localStorageKey); // 从对应的 localStorage 中读取内容

        if (text !== null) {
            textBox.value = text; // 将内容显示在文本框中
        } else {
            var nullKeywordsDefaultValue;
            if(localStorageKey == "customBlacklistWord") nullKeywordsDefaultValue = blacklistKeywords;
            else if(localStorageKey == "customWhitelistWord") nullKeywordsDefaultValue = whitelistWords;
            else if(localStorageKey == "customClickbaitWord") nullKeywordsDefaultValue = clickbaitList;
            else nullKeywordsDefaultValue = [];
            textBox.value = nullKeywordsDefaultValue.join(',');
        }
    }

    // 点击"默认"按钮时的处理函数
    function handleRewriteButtonClick(textBox, blacklistRadio, whitelistRadio, clickbaitRadio) {
        event.preventDefault();
        var localStorageKey = getSelectedFilterType(blacklistRadio, whitelistRadio, clickbaitRadio); // 获取选中的过滤类型
        localStorage.removeItem(localStorageKey); // 删除对应的 localStorage 中的内容
        textBox.value = '';
        textBox.placeholder = '已恢复默认设置,请刷新网页以生效';
    }

    // 执行函数并每秒检测
    function loadSetting() {
        //console.log("寻找control-group theme标签");
        setInterval(addSettingArea, 1000);
    }
    loadSetting();

})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址