虎牙重复弹幕过滤

虎牙重复弹幕过滤, 过滤视频弹幕, 过滤聊天弹幕, 过滤表情弹幕, 放过自己弹幕

目前為 2020-06-23 提交的版本,檢視 最新版本

// ==UserScript==
// @name         虎牙重复弹幕过滤
// @namespace    http://tampermonkey.net/
// @version      0.3
// @description  虎牙重复弹幕过滤, 过滤视频弹幕, 过滤聊天弹幕, 过滤表情弹幕, 放过自己弹幕
// @author       Mindfulness
// @match        https://www.huya.com/*
// @grant        GM_setValue
// @grant        GM_getValue
// @run-at       document-end
// ==/UserScript==

(function() {
    'use strict';

    const HuyaFilter = {
        selfUserName: '',
        filterCount: 0,
        filterTopList: [],
        _options: [],
        getOptions: function(optionKey, defaultValue)
        {
            return this._options[optionKey] == undefined ? GM_getValue('_huya_filter_' + optionKey, defaultValue) : this._options[optionKey];
        },
        setOptions: function(optionKey, optionValue)
        {
            GM_setValue('_huya_filter_' + optionKey, optionValue);
            this._options[optionKey] = optionValue;
        },
        createUI: function()
        {
            let icon = document.createElement('i');
            icon.id = 'J-room-chat-filter';
            icon.className = 'room-chat-tool';
            icon.style = 'display: inline-block;width: 35px;height: 22px;cursor:pointer;text-align:center;margin-top: 1px;border: 1px solid #ff8a00;border-radius: 5px;background-color: #ff8a00;color: #fff;font-weight: bold;';
            icon.innerText = '过滤';

            let toolsPannel = document.querySelector('#tipsOrchat .chat-room__ft .chat-room__ft__pannel .room-chat-tools');
            toolsPannel.appendChild(icon);

            let tipsOrchatRect = document.querySelector('#tipsOrchat').getBoundingClientRect();
            let mainCol = document.querySelector('#main_col');
            let mainColRect = mainCol.getBoundingClientRect();
            let popup = document.createElement('div');
            popup.id = 'J-room-chat-filter-pannel';
            let popTop = tipsOrchatRect.top - mainColRect.top - 300 - 2;
            let popLeft = tipsOrchatRect.left - mainColRect.left;
            popup.style='border:1px solid #333;width:336px;height:300px;background-color:#eee;cursor:default;position:absolute;top:' + popTop + 'px;left:' + popLeft + 'px;z-index:100;display:none;';

            let popupHeader = document.createElement('div');
            popupHeader.style='padding:10px;border-bottom:1px solid #888;';

            let filterCounter = document.createElement('span');
            filterCounter.id = 'J-room-chat-filter-counter';
            filterCounter.style = 'color:#008;';
            filterCounter.innerText = '已过滤: 0';
            popupHeader.appendChild(filterCounter);

            let lastFilterDanmu = document.createElement('span');
            lastFilterDanmu.id = 'J-room-chat-filter-last';
            lastFilterDanmu.style = 'margin-left:10px;padding:0 2px;display:inline-flex;overflow:hidden;max-width:200px;height:18px;color:#aaa;background-color:#ff8;';
            lastFilterDanmu.title = '最后过滤弹幕内容';
            popupHeader.appendChild(lastFilterDanmu);

            let popupClose = document.createElement('a');
            popupClose.innerText = '关闭';
            popupClose.style='cursor:pointer;color:#800;padding-right:10px;position:absolute;right:0;';
            popupClose.addEventListener('click', () => {popup.style.display = 'none'});
            popupHeader.appendChild(popupClose);
            popup.appendChild(popupHeader);

            let popupOptions = document.createElement('div');
            popupOptions.style='padding:10px;border-bottom:1px solid #888;';
            popup.appendChild(popupOptions);

            let popupFilterTopList = document.createElement('div');
            popupFilterTopList.style='padding:10px;';

            let topListElement = document.createElement('ul');
            topListElement.id = 'J-room-chat-filter-topList';
            topListElement.style = 'padding:0;margin:0;';
            popupFilterTopList.appendChild(topListElement);

            let resetElement = document.createElement('a');
            resetElement.innerText = '重置统计';
            resetElement.style = 'cursor:pointer;color:#800;padding-right:10px;position:absolute;right:0;bottom:10px;';
            resetElement.addEventListener('click', function()
            {
                this.filterCount = 0;
                this.filterTopList = [];
                this.showFilterCountInfo();
            }.bind(this));
            popupFilterTopList.appendChild(resetElement);

            popup.appendChild(popupFilterTopList);

            mainCol.appendChild(popup);

            icon.addEventListener('click', (e) => {popup.style.display = popup.style.display == 'block' ? 'none' : 'block'});

            this.showCheckboxOptions(popupOptions, 'filter_video_danmu', '过滤视频弹幕');
            this.showCheckboxOptions(popupOptions, 'filter_chat_danmu', '过滤聊天弹幕');
            this.showCheckboxOptions(popupOptions, 'filter_icon_danmu', '过滤表情弹幕');
            this.showCheckboxOptions(popupOptions, 'filter_skip_me', '放过自己发的弹幕');
        },

        showCheckboxOptions: function(contriner, optionKey, optionText)
        {
            let checkbox = document.createElement('input');
            checkbox.type = 'checkbox';
            checkbox.checked = this.getOptions(optionKey, 1) == 1;

            checkbox.addEventListener('change', function(e)
            {
                this.setOptions(optionKey, checkbox.checked ? 1 : 0);
                console.log(optionText + ': ' + (checkbox.checked ? '已开启' : '已关闭'));
            }.bind(this));

            let span = document.createElement('span');
            span.innerText = optionText;
            span.style='padding-left:10px;';

            let label = document.createElement('label');
            label.style = 'display: inline-block;width:45%;';
            label.appendChild(checkbox);
            label.appendChild(span);
            contriner.appendChild(label);
        },

        showFilterCountInfo: function()
        {
            document.querySelector('#J-room-chat-filter-counter').innerHTML = '已过滤: ' + this.filterCount;

            let topListElement = document.querySelector('#J-room-chat-filter-topList');
            topListElement.innerHTML = '';

            for(let i = 0; i < 10; i++)
            {
                let itemElement = document.createElement('li');
                let showIndex = i + 1;
                let itemHtml = '<span style="font-weight:bold;">' + showIndex + '.</span>';
                if(i < this.filterTopList.length) itemHtml += ' (' + this.filterTopList[i].count + ') ' + this.filterTopList[i].danmu;
                itemElement.innerHTML = itemHtml;
                itemElement.style = 'padding:0;margin:0;list-style:none;display:block;width:316px;overflow:hidden;height:18px;line-height:18px;';
                topListElement.appendChild(itemElement);
            }
        },

        start: function()
        {
            let recordDanmuList = {};

            let videoDanmuDiv = document.querySelector('#danmudiv');
            let chatDanmuDiv = document.querySelector('#chat-room__list');

            if(!videoDanmuDiv || !chatDanmuDiv) return setTimeout(this.start.bind(this), 1000);

            this.selfUserName = document.querySelector('#login-username').innerText;

            let videoDanmuFilterObserver = new MutationObserver((recordList, observer) => {
                if(this.getOptions('filter_video_danmu', 1) != 1) return;
                recordList.forEach((record) => {
                    if(record.type !== 'childList') return;
                    for(let i = 0; i < record.addedNodes.length; i++)
                    {
                        let danmuElement = record.addedNodes[i];
                        if(parseFloat(danmuElement.style.borderWidth) > 0 && this.getOptions('filter_skip_me', 1) == 1) continue;

                        let danmuText = danmuElement.innerText.trim();
                        if(danmuText.length == 0)
                        {
                            if(this.getOptions('filter_icon_danmu', 1) != 1) continue;
                            danmuText = '<表情>';
                        }
                        let filterDanmuText = danmuText;

                        let repeatMatches = danmuText.match(/^(.+?)\1+$/);
                        if(repeatMatches) filterDanmuText = repeatMatches[1] + ' ...';
                        if(!recordDanmuList[filterDanmuText]) recordDanmuList[filterDanmuText] = 0;
                        let repeatCount = ++recordDanmuList[filterDanmuText];
                        if(repeatCount == 1) return;

                        danmuElement.remove();

                        // 统计新增
                        this.filterCount++;

                        // 追加
                        let alreadyExist = false;
                        for(let i = 0; i < this.filterTopList.length; i++)
                        {
                            if(filterDanmuText === this.filterTopList[i].danmu)
                            {
                                this.filterTopList[i].count = repeatCount;
                                alreadyExist = true;
                                break;
                            }
                        }
                        if(!alreadyExist) this.filterTopList.push({danmu: filterDanmuText, count: repeatCount});
                        document.querySelector('#J-room-chat-filter-last').innerHTML = '(' + repeatCount + ') ' + danmuText;
                    }
                });

                // 从多到少排序
                this.filterTopList.sort((a, b) => b.count - a.count);

                // 删除多余
                while(this.filterTopList.length > 10) this.filterTopList.pop();

                this.showFilterCountInfo();

                // observer.disconnect();
            });

            videoDanmuFilterObserver.observe(videoDanmuDiv, {childList: true});

            let chatDanmuFilterObserver = new MutationObserver((recordList, observer) => {
                if(this.getOptions('filter_chat_danmu', 1) != 1) return;
                recordList.forEach((record) => {
                    if(record.type !== 'childList') return;
                    for(let i = 0; i < record.addedNodes.length; i++)
                    {
                        let danmuElement = record.addedNodes[i];
                        let danmuUserNameElement = danmuElement.querySelector('.name');
                        // 不是个人用户发言
                        if(!danmuUserNameElement) continue;

                        let danmuUserName = danmuUserNameElement.innerText;
                        if(danmuUserName == this.selfUserName && this.getOptions('filter_skip_me', 1) == 1) continue;

                        let danmuText = danmuElement.querySelector('.msg').innerText;
                        if(danmuText.length == 0)
                        {
                            if(this.getOptions('filter_icon_danmu', 1) != 1) continue;
                            danmuText = '<表情>';
                        }
                        let filterDanmuText = danmuText;

                        let repeatMatches = danmuText.match(/^(.+?)\1+$/);
                        if(repeatMatches) filterDanmuText = repeatMatches[1] + ' ...';

                        if(!recordDanmuList[filterDanmuText]) recordDanmuList[filterDanmuText] = 0;
                        let repeatCount = recordDanmuList[filterDanmuText];
                        if(repeatCount <= 1) return;

                        danmuElement.remove();
                    }
                });
            });

            chatDanmuFilterObserver.observe(chatDanmuDiv, {childList: true});
        }
    };

    HuyaFilter.start();
    HuyaFilter.createUI();
})();