Greasy Fork

Greasy Fork is available in English.

【B站】评论区成分标签·关键词屏蔽

B站评论区可视化添加用户成分标签,用户评论关键词屏蔽

当前为 2022-10-06 提交的版本,查看 最新版本

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         【B站】评论区成分标签·关键词屏蔽
// @namespace    lycoris
// @version      1.9.3
// @description  B站评论区可视化添加用户成分标签,用户评论关键词屏蔽
// @author       Lyzoris
// @match        https://www.bilibili.com/video/*
// @match        https://t.bilibili.com/*
// @match        https://space.bilibili.com/*
// @icon         https://static.hdslb.com/images/favicon.ico
// @connect      bilibili.com
// @grant        unsafeWindow
// @grant        GM_xmlhttpRequest
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_setClipboard
// @license MIT
// @run-at document-end
// ==/UserScript==
(function () {
    "use surict";
    const ApiBlog = 'https://api.bilibili.com/x/polymer/web-dynamic/v1/feed/space?&host_mid=';
    const ApiMedal = 'https://api.live.bilibili.com/xlive/web-ucenter/user/MedalWall?target_id=';
    const is_new = document.getElementsByClassName('item goback').length != 0; // 检测B站版本
    const TAG_STYLE = document.createElement('style');
    const TAGSIZE_STYLE = document.createElement('style');
    const TAGBAR_STYLE = document.createElement('style');
    const TAG_HTML = document.createElement('div');
    TAGSIZE_STYLE.setAttribute('id', 'tag-size');
    TAG_STYLE.setAttribute('id', 'tagname-style');
    const tagSizeDict = {
        small: `.tag-text, .tag-name{height: 12px;line-height: 12px;}.tag-font{transform: scale(0.5) translate(-50%, -50%);}`,
        middle: `.tag-text, .tag-name{height: 16px;line-height: 16px;}.tag-font{transform: scale(0.6) translate(-40%, -30%);}`
    };
    const tagHideDict = {
        true: `.tag-name {display:none;}`,
        false: `.tag-name {display:block;}`
    };
    TAGSIZE_STYLE.innerHTML = tagSizeDict.middle;
    TAG_STYLE.innerHTML = tagHideDict.false;
    TAGBAR_STYLE.innerHTML = `
    .userTag{display:inline-block;position:relative;text-align:center;border-width:0px;vertical-align:text-top;margin-left:4px;cursor:default}.tag-text{border-color:rgba(169,195,233,0.1803921568627451);color:rgba(87,127,184,1);background-color:#9ebae833;float:left;text-align:center;border-width:0.5px;border-style:solid;border-bottom-left-radius:1px;border-top-left-radius:1px}.tag-name{position:relative;border-bottom-right-radius:1px;border-top-right-radius:1px;float:left;box-sizing:content-box;text-align:center;border-width:0.5px;border-color:#f25d8e;border-style:solid;border-bottom-left-radius:1px;border-top-left-radius:1px;color:#f25d8e}.tag-font{width:200%;height:200%;font-weight:400;transform-origin:center;font-size:20px;line-height:24px}
    .tagbar-hide{position:fixed;right:10px;bottom:10px;width:20px;height:20px;font-size:15px;line-height:20px;text-align:center;color:#009688;background-color:#e2e1e2b3;border-radius:5px;box-shadow:2px 0px 4px 0px #0000002b}.tagbar-hide svg{width:100%;height:100%}.tagbar-hide:hover{background-color:#76cb9dc9;box-shadow:2px 0px 4px 0px #76cb9d91}.tagbar-hide:hover svg path{fill:#ffffff}.tagbar-active{background-color:#ab85d1c9;box-shadow:2px 0px 4px 0px #c893e291}.tagbar-active svg path{fill:#ffffff}.setbar{display:none;position:absolute;left:32px;top:30px;padding:10px;width:200px;border-radius:5px;background-color:#fffefd;box-shadow:0px 0px 4px 2px #00000036;z-index:2}
    .tagbar{display:none;position:fixed;z-index:999;right:65px;bottom:20px;background-color:#ffffff;color:#929292;height:350px;width:260px;border-radius:5px;box-shadow:0px 0px 4px 2px #0000002b;padding:10px}.tag-bar{width:100%;height:69%;margin-bottom:4px}.comment-bar{width:100%;height:32%}svg.icon{width:10px;height:10px}.tagbar-action{position:absolute;top:2px;width:15px;height:15px;line-height:15px;margin:0;border-radius:50%;background-color:#fff0;text-align:center;font-size:small;color:#929292}#tagbar-setting{right:0;top:0}#tagbar-setting svg:hover path{fill:#76b1ef}.tagbar-btn{background-color:#ffffff;color:#929292;height:20px;margin:5px 0px 5px 0;padding:0 4px 0 4px;border:none;border-radius:4px;font-weight:bold;transform-origin:center;transform:scale(0.9) translate(-10%,-10%);font-size:13px;text-align:center;box-shadow:#b1b1b13d 0px 0px 3px 2px}
    .tagbar-btn:hover{color:#37a279ab}.tagbar-btn:active{color:#b4b6ee}.input-tag{margin-bottom:4px;width:74%;left:20%;border-radius:4px;border:solid 1px #b0b0b0}.input-tag:focus{outline:none}.tagbar-label{font-size:12px;display:inline-block;margin-right:10px}#input-tagcolor{width:52px;height:20px;border-radius:4px;border:solid 1px #b0b0b0}.tags{display:inline-block;width:40px;height:20px;font-size:12px;border-radius:5px;color:#49414b;text-align:center;transform-origin:center;transform:scale(0.9) translate(-10%,-10%);line-height:20px;background-color:#ededed;margin-left:3px;margin-top:5px;cursor:pointer;box-shadow:0px 1px 5px 0 #00000033}.tags:hover{background-color:#ced1d2c4}
    .delete-tag{position:relative;top:-3px;right:0;width:20px;height:20px;border-radius:50%;transform-origin:center;transform:scale(0.6) translate(-50%,-50%);font-size:20px;color:#837171;line-height:18px;background-color:#b2bfc67a;box-shadow:0px 0px 3px 1px #00000033}.delete-tag:hover{color:white;background-color:crimson}.delete-tag:active{color:white;background-color:#f0f}.tag-info{position:relative;margin-top:-20px;font-weight:bold}#refresh-time{width:80px;height:5px;outline:none;margin:0 10px 0 10px;appearance:none;background:#b6b2b8;border-radius:4px}#refresh-time::-webkit-slider-thumb{-webkit-appearance:none;-moz-appearance:none;appearance:none;-webkit-box-shadow:0 0 2px;width:10px;height:10px;border-radius:50%;background-size:cover;background-color:#fff}
    .tagbar-taglist{margin-top:2px;width:98%;height:43%;bottom:0px;border-radius:4px;background-color:rgb(243,238,233);overflow-y:scroll;padding:4px}.tagbar-commentlist{margin-top:2px;width:98%;height:60%;bottom:0px;border-radius:4px;background-color:rgb(243,238,233);overflow-y:scroll;padding:4px}.tagbar-taglist::-webkit-scrollbar,.tagbar-commentlist::-webkit-scrollbar,.medal-table::-webkit-scrollbar,#import-area::-webkit-scrollbar{width:4px;height:4px}.tagbar-taglist::-webkit-scrollbar-thumb,.tagbar-commentlist::-webkit-scrollbar-thumb,.medal-table::-webkit-scrollbar-thumb,#import-area::-webkit-scrollbar-thumb{border-radius:5px;box-shadow:inset 0 0 5px rgba(0,0,0,0.2);background:rgba(0,0,0,0.2)}
    .tagbar-taglist::-webkit-scrollbar-track,.tagbar-commentlist::-webkit-scrollbar-track,.medal-table::-webkit-scrollbar-track{box-shadow:inset 0 0 5px rgba(0,0,0,0.2);border-radius:0px;background:rgba(0,0,0,0.1)}#flash-time{margin-right:10px}.tagSize{margin:5px 0 5px 0}.tagSize-label{display:inline-block;text-align:center}.tagSize-radio{vertical-align:text-bottom}#set-exit{right:0;top:0}#set-exit:hover svg path{fill:#cf2222}.set-label{font-size:12px;display:inline-block;margin:5px 0 5px 0}.medalTag{margin-left:5px;cursor:pointer}.medal-table{display:none;position:absolute;padding:5px;max-width:230px;max-height:160px;overflow-y:auto;font-size:12px;background-color:#fffefd;border-radius:5px;cursor:default;z-index:2;box-shadow:0px 0px 4px 2px #0000002b}
    .fans-tag{float:left;margin:4px 4px 4px 4px;border:solid 1px #6a99de;border-radius:2px}.fans-tag .tag-name{border:none;width:18px}#import-area{font-size:10px;margin:10px 0 0 0;max-width:195px;min-width:195px;height:30px;min-height:20px;max-height:100px;background-color:#f3f3f3;border-radius:5px}#import-area:focus{outline:none}
    `;
    TAG_HTML.innerHTML = `
    <div class='tagbar-hide'>
        <svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" width="16" height="16">
            <path d="M593.949942 770.425747c2.908236-4.233418 4.614088-9.380648 4.614088-14.892175s-1.705851-10.658757-4.614088-14.922874L431.084621 577.00041c-10.32209-10.324136-10.32209-27.042913 0-37.381375l158.601204-159.117974c5.376451-4.843308 8.771781-11.818163 8.771781-19.61371 0-14.602579-11.83249-26.433022-26.434046-26.433022-5.953595 0-11.420097 1.979074-15.835663 5.298679l-5.300726 5.299703L375.020744 520.936533c-20.650319 20.647249-20.650319 54.114478 0 74.763774l177.556928 177.590698 1.811252 1.794879c4.690836 4.264117 10.903328 6.884804 17.740036 6.884804C581.18829 781.968641 589.183382 777.399579 593.949942 770.425747z" fill="#009688"></path>
        </svg>
    </div>
    <div class='tagbar'>
        <div class="setbar">
            <div id="set-exit" class="tagbar-action">
                <svg class="icon" viewBox="0 0 1024 1024"  xmlns="http://www.w3.org/2000/svg" width="10" height="10">
                    <path d="M850.538343 895.516744c-11.494799 0-22.988574-4.386914-31.763424-13.161764L141.103692 204.669426c-17.548678-17.534352-17.548678-45.992497 0-63.525825 17.548678-17.548678 45.977147-17.548678 63.525825 0l677.671227 677.685553c17.548678 17.534352 17.548678 45.992497 0 63.525825C873.526917 891.128807 862.032118 895.516744 850.538343 895.516744z" p-id="4294" fill="#8a8a8a"></path><path d="M172.867116 895.516744c-11.494799 0-22.988574-4.386914-31.763424-13.161764-17.548678-17.534352-17.548678-45.992497 0-63.525825l677.671227-677.685553c17.548678-17.548678 45.977147-17.548678 63.525825 0 17.548678 17.534352 17.548678 45.992497 0 63.525825L204.629517 882.354979C195.85569 891.128807 184.360891 895.516744 172.867116 895.516744z" fill="#8a8a8a"></path>
                </svg>
            </div>
            <input type="button" class="tagbar-btn" id="export-tag" value="导出配置" title="导出标签配置到剪切板" style="float:left; margin:0 30px 0 0;">
            <input type="button" class="tagbar-btn" id="import-tag" value="导入配置" title="导入配置到油猴存储&#10;请先将配置粘贴到下方并确认无误,再点击本按键&#10;导入配置后请刷新界面以应用配置" style="float:left; margin:0;">
            <textarea id="import-area" placeholder="请在此处粘贴配置后点击【导入配置】"></textarea>
            <div class="tagSize set-label">
                <label title="更改标签大小后刷新界面即可">标签大小:</label>
                <div class="tagSzie-option" style="display:inline-block; margin-right:10px;">
                    <label calss="tagSize-label">较小</label>
                    <input class="tagSize-radio" type="radio" name="tagSize" value="small">
                </div>
                <div class="tagSzie-option" style="display:inline-block; margin-right:10px;">
                    <label calss="tagSize-label">标准</label>
                    <input class="tagSize-radio" type="radio" name="tagSize" value="middle" checked="true">
                </div>
            </div>
            <label for="refresh-time" style="margin: 5px 0 5px 0;font-size: 12px;display: block;" title="页面滚动时脚本刷新间隔时间,5-10s 为宜">刷新间隔<input type="range" id="refresh-time" min="2" max="20" step="1" value="5"><span id="show-time">5s</span></label>
            <label class="set-label" for="tagname-hide" title="不显示标签分类,标签较多时可以开启">标签不显示分类<input type="checkbox" id="tagname-hide" style="margin-left:25px;height:11px"></label>
            <label class="set-label" for="medal-show" title="显示用户的粉丝勋章,更改后刷新界面&#10;开启此选项后将粉丝勋章墙纳入标签检测&#10;开启后用户标签末尾出现【勋章】标签,&#10;点击【勋章】展开勋章栏显示所有勋章&#10;鼠标放上显示对应Up主,点击可跳转&#10;点击【勋章】或勋章栏空白处收起">显示粉丝勋章<input type="checkbox" id="medal-show" style="margin-left:37px;height:11px"></label>
            <label class="set-label" for="link-delete" title="去除 评论区评论关键词蓝色点击跳转">去除关键词跳转<input type="checkbox" id="link-delete" style="margin-left:25px;height:11px"></label>
            <label class="set-label" for="close-comment" title="动态评论末尾添加【收起评论】按键">动态添加收起评论<input type="checkbox" id="close-comment" style="margin-left:14px;height:11px"></label>
        </div>
        <div id='tagbar-setting' class="tagbar-action">
            <svg class='icon' viewBox='0 0 1024 1024' xmlns='http://www.w3.org/2000/svg' width='10' height='10'>
                <path d='M1008.535135 710.670332l-70.446388 116.386904a112.099975 112.099975 0 0 1-140.572857 43.829042l-69.422645-17.083729c-5.502624 3.1992-11.133217 6.3984-16.763809 9.341665l-18.299425 62.896276C687.335435 981.514621 639.4754 1023.744064 582.657604 1023.744064H441.764828c-56.817796 0-104.677831-42.293427-110.308423-97.703574l-18.299425-62.896276a422.166458 422.166458 0 0 1-16.827793-9.341665l-69.614597
                17.083729a112.163959 112.163959 0 0 1-140.508873-43.893026L15.631361 710.542364a107.493127 107.493127 0 0 1 32.43989-144.731817L96.251206 518.910272a410.649338 410.649338 0 0 1 0-14.204448l-48.179955-46.836291a107.365159 107.365159 0 0 1-32.375906-144.731817l70.190452-116.450888a112.099975 112.099975 0 0 1 140.572857-43.893026l69.550612 17.083729c5.566608-3.1992 11.133217-6.3984 16.827794-9.341665l18.299425-62.896276C336.831061
                42.229443 384.691096 0 441.508892 0H582.529636c56.817796 0 104.677831 42.293427 110.372407 97.63959l18.235441 62.704324c5.75856 3.007248 11.325169 6.142464 16.827793 9.341665l69.678581-17.083729a112.163959 112.163959 0 0 1 140.508873 43.893026l70.446388 116.514872a107.493127 107.493127 0 0 1-32.375906 144.731817l-48.179955 46.772307a414.616346 414.616346 0 0 1 0 14.204448l48.243939 46.836291c47.092227 31.928018 62.192452 94.952262
                32.247938 145.115721z m-213.066733-142.23644l3.391152-31.032242a246.210447 246.210447 0 0 0 0-51.699076l-3.263184-30.904274 93.992502-91.305173-53.554612-88.553862-131.615096 32.311922-23.546113-16.059985a294.134466 294.134466 0 0 0-48.627844-26.809297l-27.513121-11.901025L568.453156 127.968008H455.649356L419.370426 252.736816l-27.705074 11.965009c-16.635841 7.166208-32.951762 16.187953-48.499875 26.745313l-23.610097 15.996001-131.487128-32.247938-53.36266 88.489878 94.056486 91.433141-3.263184 30.968258a247.874031 247.874031
                0 0 0 0 51.699076l3.1992 30.904274-93.928518 91.369157 53.554612 88.489878 131.551112-32.311922 23.610097 16.059985c15.292177 10.429393 31.544114 19.451137 48.563859 26.809297l27.577106 11.965009L455.905292 895.776056h112.611848l36.342914-124.768808 27.64109-11.965009c16.699825-7.166208 32.951762-16.187953 48.563859-26.745313l23.546113-15.996001 131.423144 32.247938 53.68258-88.745814-94.184454-91.369157zM512.147232 615.846038c57.329668 0
                103.846038-46.516371 103.974006-103.974006 0-57.329668-46.644339-103.974006-103.974006-103.974007S408.173225 454.542364 408.173225 511.872032 454.817564 615.846038 512.147232 615.846038z m0 127.968008c-127.968008 0-231.942014-103.910022-231.942015-231.942014 0-127.968008 103.910022-231.942014 231.942015-231.942015 127.968008 0 231.942014 103.910022 231.942014 232.133967A232.133967 232.133967 0 0 1 512.147232 743.814046z' fill='#929292'></path>
            </svg>
        </div>
        <div class="tag-bar">
            <label class="tagbar-label" for="input-tagname" title="标签的分类,比如游戏、Vtuber、主播、UP主等&#10;用来识别标签类型,尽量短一些">标签分类</label><input type="text" id="input-tagname" class="input-tag" autocomplete="off">
            <label class="tagbar-label" for="input-tagtext" title="标签具体显示的内容,以区别每个标签">标签内容</label><input type="text" id="input-tagtext" class="input-tag" autocomplete="off">
            <label class="tagbar-label" for="input-tagreg" title="匹配用户标签的关键词,使用 & 和 | 分隔,使用 ( ) 组合&#10;多关键词任一匹配:王者荣耀或王者 =&gt; 王者荣耀|王者 &#10;多关键词同时匹配:王者荣耀与吃鸡 =&gt; 王者荣耀&吃鸡 &#10;多关键词组合匹配:(王者|王者荣耀)&(吃鸡|和平精英)">标签规则</label><input type="text" id="input-tagreg" class="input-tag" autocomplete="off">
            <label class="tagbar-label" for="input-tagcolor" title="标签文字颜色,可在面板中预览">标签颜色</label><input type="color" name="" id="input-tagcolor">
            <label class="tagbar-label" style="margin-left: 6px;" for="tag-hide">屏蔽标签用户评论<input type="checkbox" id="tag-hide" style="margin-left:9px;height:11px"></label>
            <input type="button" class="tagbar-btn" id="add-tag" value="添加标签">
            <div class="tagbar-taglist"></div>
        </div>
        <div class="comment-bar">
            <label class="tagbar-label" for="input-comment-reg" title="评论关键词屏蔽规则,同 【标签规则】">评论规则</label><input type="text" id="input-comment-reg" class="input-tag" autocomplete="off" style="width:45%;">
            <input type="button" class="tagbar-btn" id="add-comment-reg" style="float:right; margin:2px 0 0 0;" value="添加规则">
            <div class="tagbar-commentlist"></div>
        </div>
    </div>`;
    const Head = document.head || document.querySelector('head');
    const Body = document.body || document.querySelector('body');
    Head.appendChild(TAG_STYLE);
    Head.appendChild(TAGBAR_STYLE);
    Head.appendChild(TAGSIZE_STYLE);
    Body.appendChild(TAG_HTML);
    const sideBar = document.querySelector('.tagbar-hide');
    const tagBar = document.querySelector('.tagbar');
    const setBar = document.querySelector('.setbar');
    let set_exit = document.querySelector('#set-exit');
    let setting_btn = document.querySelector('#tagbar-setting');
    let tagname_hide = document.querySelector('#tagname-hide');
    let medal_show = document.querySelector('#medal-show');
    let link_delete = document.querySelector('#link-delete');
    let close_comment = document.querySelector('#close-comment');
    let tagSize_radio = document.querySelectorAll('.tagSize-radio');
    let import_tag = document.querySelector('#import-tag');
    let import_area = document.querySelector('#import-area');
    let tag_name = document.querySelector('#input-tagname');
    let tag_text = document.querySelector('#input-tagtext');
    let tag_reg = document.querySelector('#input-tagreg');
    let tag_color = document.querySelector('#input-tagcolor');
    let add_tag_btn = document.querySelector('#add-tag');
    let export_tag = document.querySelector('#export-tag');
    let taglist = document.querySelector('.tagbar-taglist');
    let tag_hide = document.querySelector('#tag-hide');
    let refresh_time = document.querySelector("#refresh-time");
    let add_tag_reg = document.querySelector('#add-comment-reg');
    let comment_reg = document.querySelector('#input-comment-reg');
    let commentlist = document.querySelector('.tagbar-commentlist');
    sideBar.onclick = () => {
        if (tagBar.style.display === 'block') {
            tagBar.style.display = 'none';
            setBar.style.display = 'none';
            sideBar.classList.remove('tagbar-active');
        }
        else {
            tagBar.style.display = 'block';
            sideBar.classList.add('tagbar-active');
        }
    };
    const TagNameHide = () => {
        if (tagname_hide.checked) {
            TAG_STYLE.innerHTML = tagHideDict.true;
        }else {
            TAG_STYLE.innerHTML = tagHideDict.false;
        }
        GM_setValue('TagNameHide', tagname_hide.checked);
    };
    setting_btn.onclick = () => { setBar.style.display = 'block'; };
    set_exit.onclick = () => { setBar.style.display = 'none'; };
    tagname_hide.onclick = TagNameHide;
    medal_show.onclick = () => {
        tag_list.medalShow = medal_show.checked;
        GM_setValue('MedalShow', medal_show.checked);
    };
    link_delete.onclick = () => { GM_setValue('NoJump', link_delete.checked); };
    close_comment.onclick = () => { GM_setValue('CloseComment', close_comment.checked); };
    add_tag_btn.onclick = () => {
        if (tag_name.value && tag_text.value && tag_reg.value) {
            addTag({
                tag: tag_name.value,
                text: tag_text.value,
                reg: tag_reg.value,
                color: tag_color.value,
                hide: tag_hide.checked
            });
            tag_name.value = '';
            tag_text.value = '';
            tag_reg.value = '';
            tag_hide.checked = false;
        }else {
            alert('请将标签信息补充完整');
        }
    };
    export_tag.onclick = () => {
        GM_setClipboard(JSON.stringify({
            refreshTime: refreshTime,
            TagNameHide: tagname_hide.checked,
            MedalShow: medal_show.checked,
            NoJump: link_delete.checked,
            CloseComment: close_comment.checked,
            tagSize: tagSize,
            keyword: keyword,
            tag: tag
        }));
        alert('已导出标签数据到剪切板!');
    };
    import_tag.onclick = () => {
        if (import_area.value) {
            try {
                let importData = JSON.parse(import_area.value);
                let key = Object.keys(importData);
                key.map(i => GM_setValue(i, importData[i]));
            }
            catch (e) {
                console.log(e);
                alert('导入配置出错,请检查配置项');
                return;
            }
            import_area.value = '';
            ConfigInit();
        }
    };
    refresh_time.onchange = () => {
        refreshTime = Number(refresh_time.value) * 1000;
        GM_setValue('refreshTime', refreshTime);
        document.querySelector("#show-time").innerText = refresh_time.value + 's';
    };
    const getTagSize = () => {
        for (let radio of tagSize_radio) {
            if (radio.checked) {
                TAGSIZE_STYLE.innerHTML = tagSizeDict[radio.value];
                tagSize = radio.value;
                GM_setValue('tagSize', tagSize);
            }
        }
    };
    tagSize_radio.forEach(radio => { radio.onclick = getTagSize; });
    add_tag_reg.onclick = () => {
        if (comment_reg.value) {
            addKeyWord(comment_reg.value);
            comment_reg.value = '';
        }else {
            alert('请将关键词正则信息补充完整');
        };
    };
    const addKeyWord = (reg_text) => {
        if (!keyword.includes(reg_text)) {
            let new_tag = insertTag(commentlist, reg_text);
            let keyword_index = Object.keys(comment_keyword).length;
            comment_keyword[keyword_index] = reg_text;
            keyword.push(reg_text);
            GM_setValue('keyword', keyword);
            let deleteTag = () => {
                commentlist.removeChild(new_tag);
                delete comment_keyword[keyword_index];
                keyword = [];
                let keyword_index_new = Object.keys(comment_keyword);
                keyword_index_new.map(key => keyword.push(comment_keyword[key]));
                GM_setValue('keyword', keyword);
            };
            new_tag.children[0].onclick = deleteTag;
            new_tag.children[1].ondblclick = () => {
                comment_reg.value = reg_text;
                deleteTag();
            };
        }
    };
    const addTag = (tag_dic) => {
        let title = `${tag_dic.tag}&#10;规则:${tag_dic.reg}&#10;隐藏评论:${tag_dic.hide}`;
        let new_tag = insertTag(taglist, tag_dic.text, tag_dic.color, title);
        let tag_index = Object.keys(tag).length;
        tag[tag_index] = tag_dic;
        tag_list.push(tag, tag_index);
        let deleteTag = () => {
            taglist.removeChild(new_tag);
            tag_list.pop(tag, tag_index);
        };
        new_tag.children[0].onclick = deleteTag;
        new_tag.children[1].ondblclick = () => {
            tag_name.value = tag_dic.tag;
            tag_text.value = tag_dic.text;
            tag_reg.value = tag_dic.reg;
            tag_color.value = tag_dic.color;
            tag_hide.checked = tag_dic.hide;
            deleteTag();
        };
    };
    const insertTag = (parentNode, text, color = "#49414b", title = "") => {
        let new_tag = document.createElement('div');
        new_tag.innerHTML = `<div class="delete-tag">x</div><p class="tag-info" title="${title}">${text}</p>`;
        new_tag.classList.add('tags');
        new_tag.style.width = measureTextWidth("12px", text) + 8 + 'px';
        new_tag.style.color = color;
        parentNode.appendChild(new_tag);
        return new_tag;
    };
    const measureTextWidth = (fontSize, text) => {
        let fontFamily = "PingFang SC, HarmonyOS_Regular, Helvetica Neue, Microsoft YaHei, sans-serif";
        let canvas = document.createElement("canvas");
        let context = canvas.getContext("2d");
        context.font = fontSize + " " + fontFamily;
        let result = context.measureText(text);
        return Math.ceil(result.width);
    };
    class Tag {
        constructor(tag_dic) {
            this.tag = tag_dic.tag;
            this.tag_class = this.Str2Hex(this.tag);
            this.text = tag_dic.text;
            this.tagReg = new RegExp(tag_dic.text);
            this.reg = Tag.MultiReg(tag_dic.reg);
            this.hide = tag_dic.hide;
            this.color = tag_dic.color;
            this.tag_size = tagSize === 'middle' ? '15px' : '12px';
            this.tag_width = measureTextWidth(this.tag_size, this.tag);
            this.width = measureTextWidth(this.tag_size, this.text);
            this.list = new Set();
            this.nolist = new Set();
            this.tag_inner = `<div class='tag-text' style='color: ${this.color}; width:${this.width}px;'><div class='tag-font'>${this.text}</div></div>`;
            this.inner = `<div class='userTag ${this.tag_class}'><div class='tag-name' style='border-color:#8da8e8;color:#5e80c4; width:${this.tag_width}px'><div class='tag-font'>${this.tag}</div></div>${this.tag_inner}</div>`;
        };
        Str2Hex(str) {
            let hex = '';
            for (let i = 0; i < str.length; i++) {
                hex += str.charCodeAt(i).toString(16);
            }
            return 'tag-' + hex;
        }
        static MultiReg(query) {
            let regStr = query;
            if (query.indexOf('&') !== -1) {
                let str = query.split('&').map(q => `(?=.*${q})`).join('');
                regStr = `^${str}.*`;
            }
            return new RegExp(regStr);
        };
        check(pid, c) {
            if (this.list.has(pid)) {
                if (this.hide) {
                    getCommentTextNode(c).innerText = '评论已屏蔽';
                }
                if (!this.tagReg.test(c.textContent)) {
                    if (c.querySelector('.' + this.tag_class)) {
                        c.querySelector('.' + this.tag_class).innerHTML += this.tag_inner;
                    }else {
                        c.innerHTML += this.inner;
                    }
                }
            }
        };
        detect(pid, c, st) {
            if (this.reg.test(st)) {
                if (this.hide) {
                    getCommentTextNode(c).innerText = '评论已屏蔽';
                }
                if (!this.tagReg.test(c.textContent)) {
                    if (c.querySelector('.' + this.tag_class)) {
                        c.querySelector('.' + this.tag_class).innerHTML += this.tag_inner;
                    }else {
                        c.innerHTML += this.inner;
                    }
                    this.list.add(pid);
                }
            }else {
                this.nolist.add(pid);
            }
        };
    }
    class TagList {
        constructor() {
            this.isDetect = false;
            this.medalShow = medal_show.checked;
            this.list = [];
        }
        push(tag, index) {
            this.list.push(new Tag(tag[index]));
            GM_setValue('tag', tag);
        }
        pop(tag, index) {
            delete tag[index];
            this.list = [];
            let tag_key = Object.keys(tag);
            tag_key.map(key => this.list.push(new Tag(tag[key])));
            GM_setValue('tag', tag);
        }
        check(pid, c) {
            if (keyword.length > 0) {
                let comment = getCommentTextNode(c);
                for (let reg of keyword) {
                    let Reg = Tag.MultiReg(reg);
                    if (Reg.test(comment.innerText)) {
                        comment.innerText = '评论已屏蔽';
                        break;
                    }
                }
            }
            this.list.map(i => i.check(pid, c));
            let tag0 = this.list[0];
            this.isDetect = (tag0.list.has(pid) || tag0.nolist.has(pid)) ? true : false;
            if (this.isDetect && this.medalShow) {
                this.MedalWall(pid, c);
            }
        }
        detect(pid, c) {
            if (this.medalShow) {
                const p1 = new Promise((resolve, reject) => { Requests(ApiBlog + pid, (data) => { resolve(data); }); });
                const p2 = new Promise((resolve, reject) => { Requests(ApiMedal + pid, (data) => { resolve(data); }); });
                Promise.all([p1, p2]).then((result) => {
                    this.list.map(i => i.detect(pid, c, result.join('')));
                    this.medal(pid, c, result[1]);
                });
            }else {
                Requests(ApiBlog + pid, (data) => { this.list.map(i => i.detect(pid, c, data)); });
            }
        }
        medal(pid, c, data) {
            let medal_list = JSON.parse(data).list;
            if (medal_list.length != 0) {
                let medalInfo = [];
                medal_list.forEach(e => {
                    medalInfo.push({
                        targetName: e.target_name,
                        link: e.link,
                        medalName: e.medal_info.medal_name,
                        level: e.medal_info.level,
                        color: { start: '#' + e.medal_info.medal_color_start.toString(16).padStart(6, '0'),
                            end: '#' + e.medal_info.medal_color_end.toString(16).padStart(6, '0'),
                            border: '#' + e.medal_info.medal_color_border.toString(16).padStart(6, '0')
                        }
                    });
                });
                MedalDict[pid] = medalInfo;
                this.MedalWall(pid, c);
            }
        }
        MedalWall(pid, c) {
            if (c.querySelector('.medalTag')) {
                return;
            }
            RunOnce(() => {
                window.addEventListener('click', (event) => {
                    let e = event || window.event;
                    let targetClass = e.target ? e.target.classList : e.srcElement.classList;
                    if (!targetClass.contains('medal-font')) {
                        document.querySelectorAll('.medal-table').forEach(e => {e.style.display = 'none'});
                    }
                });
            });
            let medal = MedalDict[pid];
            if (medal) {
                let medal_list = '';
                medal.forEach(e => {
                    medal_list += `
                    <div class="fans-tag">
                    <div class="tag-text" title="${e.targetName}" style="width:${measureTextWidth('15px', e.medalName)}px;background-image: linear-gradient(90deg, ${e.color.start}, ${e.color.end});">
                    <div class="tag-font"><a href="${e.link}" style="color:#fff;">${e.medalName}</a></div></div>
                    <div class="tag-name" style="color:${e.color.end};"><div class="tag-font">${e.level}</div></div>
                    </div>`;
                });
                let medalInner = `
                <div class="medalTag">
                    <div class='tag-text' style="color: #fff;width:28px;background-image: linear-gradient(90deg,#ffcaec,#a187ff);">
                    <div class='tag-font medal-font'>勋章</div></div>
                    <div class="medal-table">${medal_list}</div>
                </div>`;
                c.innerHTML += medalInner;
                let medalBtn = c.querySelector('.medalTag');
                let table = c.querySelector('.medal-table');
                medalBtn.onclick = () => {
                    let disp = table.style.display;
                    table.style.left = medalBtn.offsetLeft + 50 + 'px';
                    table.style.display = disp === 'block' ? 'none' : 'block';
                };
            }
        }
    }
    let RunOnce = function (fn) {
        fn.apply(arguments);
        RunOnce = () => { };
    };
    const IngredientDetection = () => {
        let commentlist = getCommentList();
        if (commentlist.length != 0) {
            commentlist.forEach(c => {
                let pid = getPid(c);
                tag_list.check(pid, c);
                if (tag_list.isDetect) {
                    return;
                }
                tag_list.detect(pid, c);
            });
        }
        if (link_delete.checked) {
            noJump();
        }
    };
    const Requests = (requestUrl, func) => {
        GM_xmlhttpRequest({
            method: "get",
            url: requestUrl,
            data: '',
            headers: {
                'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36'
            },
            onload: function (res) {
                if (res.status === 200) {
                    let data = JSON.stringify(JSON.parse(res.response).data);
                    func(data);
                }else {
                    console.log('加载用户信息失败');
                    console.log(res);
                }
            },
        });
    };
    const getPid = (c) => {
        if (is_new) {
            return c.querySelector('.user-name,.sub-user-name').dataset.userId || c.querySelector('.name').dataset.usercardMid;
        }else {
            return c.querySelector('.name').getAttribute('data-usercard-mid') || c.children[0].href.replace(/[^\d]/g, "");
        }
    };
    const getCommentList = () => {
        if (is_new) {
            return document.querySelectorAll('.user-info,.sub-user-info') || document.querySelectorAll('.user');
        }else {
            return document.querySelectorAll('.user');
        }
    };
    const getCommentTextNode = (c) => {
        let comment;
        if (is_new) {
            if (c.classList.contains('user-info')) {
                comment = c.nextSibling.children[0];
            }else {
                comment = c.nextSibling;
            }
        }else {
            if (c.querySelector('.text-con')) {
                comment = c.querySelector('.text-con');
            }else {
                comment = c.nextSibling;
            }
        }
        return comment;
    };
    const noJump = () => {
        let jump_word = document.querySelectorAll('.jump-link,.search-word');
        for (let i of jump_word) {
            i.outerHTML = i.innerText;
        }
    };
    const closeComment = () => {
        if (!close_comment.checked) {
            return;
        }
        const commentBtn = document.querySelectorAll('.bili-dyn-action.comment:not(.active)');
        for (let Btn of commentBtn) {
            Btn.onclick = () => {
                let scroll = document.body.scrollTop || document.documentElement.scrollTop;
                setTimeout(() => {
                    const pls = document.querySelectorAll('.bb-comment');
                    for (let pl of pls) {
                        if (pl.querySelectorAll('.close-btn').length === 0) {
                            let close_btn = document.createElement('div');
                            close_btn.classList.add('close-btn');
                            close_btn.innerHTML = "<a class='close-text' style='color:#26902e'>收起评论</a>";
                            pl.appendChild(close_btn);
                            close_btn.style.cssText = "text-align: center; font-size: 14px; color: #99a2aa; border-top: 1px solid #e5e9ef; margin: 0; overflow: hidden; padding: 12px 0 10px; position: relative;";
                            close_btn.onclick = () => {
                                Btn.click();
                                window.scrollTo(0, scroll);
                            };
                        }
                    }
                }, 200);
            };
        }
    };
    const throttle = (func, wait) => {
        let timeout, startTime = new Date();
        return function () {
            let context = this, args = arguments, curTime = new Date();
            clearTimeout(timeout);
            if (Number(curTime) - Number(startTime) >= refreshTime) {
                func.apply(context, args);
                startTime = curTime;
            }else {
                timeout = setTimeout(func, wait);
            }
        };
    };
    const wheel = () => {
        let btns = document.querySelectorAll('.btn-more,.paging-box,.view-more-pagination,.view-more-btn,.bili-dyn-action.comment');
        for (let btn of btns) {
            btn.onclick = () => {
                setTimeout(() => {
                    IngredientDetection();
                }, 500);
            };
        }
        IngredientDetection();
        closeComment();
    };
    const ConfigInit = () => {
        refreshTime = GM_getValue('refreshTime', 5000);
        refresh_time.value = String(refreshTime / 1000);
        document.querySelector("#show-time").innerText = refresh_time.value + 's';
        tagname_hide.checked = GM_getValue('TagNameHide', false);
        TagNameHide();
        medal_show.checked = GM_getValue('MedalShow', false);
        link_delete.checked = GM_getValue('NoJump', false);
        close_comment.checked = GM_getValue('CloseComment', false);
        tagSize = GM_getValue('tagSize', 'middle');
        TAGSIZE_STYLE.innerHTML = tagSizeDict[tagSize];
        tagSize_radio.forEach(radio => {
            if (radio.value == tagSize) {
                radio.checked = true;
            }
        });
    };
    let tag = {};
    let comment_keyword = {};
    let keyword = [];
    let MedalDict = {};
    let refreshTime = 5000;
    let tagSize = 'middle';
    ConfigInit();
    const key_storage = GM_getValue('keyword', []);
    key_storage.map(key => addKeyWord(key));
    let tag_list = new TagList();
    const tag_storage = GM_getValue('tag', {});
    for (let key in tag_storage) {
        addTag(tag_storage[key]);
    }
    IngredientDetection();
    window.addEventListener("scroll", throttle(wheel, 2000));
    const webType = [/https:\/\/(t|space).bilibili.com/, /https:\/\/www.bilibili.com\/video/];
    let local_href = location.href;
    if (webType[0].test(local_href)) {
        medal_show.checked = false;
        medal_show.disabled = true;
        tag_list.medalShow = false;
        link_delete.checked = false;
        link_delete.disabled = true;
    }
    else if (webType[1].test(local_href)) {
        close_comment.checked = false;
        close_comment.disabled = true;
    }
})();