Greasy Fork

Greasy Fork is available in English.

B站用户成分标签

B站评论区根据用户动态成分可视化添加标签(根据 新·三相之力指示器,原神指示器 修改制作)

当前为 2022-09-16 提交的版本,查看 最新版本

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         B站用户成分标签
// @namespace    lycoris
// @version      1.5
// @description  B站评论区根据用户动态成分可视化添加标签(根据 新·三相之力指示器,原神指示器 修改制作)
// @author       Lemonades
// @match        https://www.bilibili.com/video/*
// @icon         https://static.hdslb.com/images/favicon.ico
// @connect      bilibili.com
// @grant        GM_xmlhttpRequest
// @grant        GM_setValue
// @grant        GM_getValue
// @license MIT
// @run-at document-end
// ==/UserScript==
(function() {
    'use strict';

    const blog = 'https://api.bilibili.com/x/polymer/web-dynamic/v1/feed/space?&host_mid=';
    const is_new = document.getElementsByClassName('item goback').length != 0; // 检测是不是新版
    const TAG_STYLE = document.createElement('style');
    const TAG_HTML = document.createElement('div');
    TAG_STYLE.innerHTML = `
    .userTag {display: inline-block;position: relative;text-align: center;border-width: 0px;vertical-align: middle;margin-left: 8px;}
    .tag-text {border-color: rgba(169, 195, 233, 0.1803921568627451);color: rgba(87, 127, 184, 1);background-image: linear-gradient(90deg, rgba(158, 186, 232, 0.2), rgba(158, 186, 232, 0.2));
     float: left;text-align: center;height: 12px;line-height: 12px;border-width: 0.5px;border-style: solid;border-bottom-left-radius: 1px;border-top-left-radius: 1px;}
    .tag-font {width: 200%;height: 200%;font-weight: 400;transform-origin: center;transform: scale(0.5) translate(-50%, -50%);font-size: 20px;line-height: 24px;}
    .tag-name {position: relative;border-bottom-right-radius: 1px;border-top-right-radius: 1px;float: left;box-sizing: content-box;text-align: center;height: 12px;line-height: 12px;border-width: 0.5px;
    border-color: #f25d8e;border-style: solid;border-bottom-left-radius: 1px;border-top-left-radius: 1px;color: #f25d8e;}
    .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:hover {color: #ffffff;background-color: #76cb9dc9;box-shadow: 2px 0px 4px 0px #76cb9d91;}
    .tagbar {display: none;position: fixed;z-index: 999;right: 65px;bottom: 20px;background-color: #ffffff;color: #929292;height: 400px;width: 300px;border-radius: 5px;box-shadow: 0px 0px 4px 2px #0000002b;padding: 10px;}
    .tag-bar{width:100%;height:64%} .comment-bar{width:100%;height:36%;}
    .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-exit {right: 0;top: 0;} #tagbar-exit svg:hover path {fill: #f85c5cc9;}
    .tagbar-btn {background-color: #ffffff;color: #929292;width: 60px;height: 20px;margin: 5px 0px 5px 0;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: #76cb9dc9;} .tagbar-label {font-size: 12px;display: inline-block;margin-right: 10px;}
    .input-tag {margin-bottom: 4px;width: 79%;left: 20%;border-radius: 4px;border: solid 1px rgb(100, 97, 97);} .input-tag:focus {outline: none;}
    #input-tagcolor {width: 52px;height: 20px;border-radius: 4px;border: solid 1px rgb(100, 97, 97);}
    .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: #bbc4cdd1;margin-left: 3px;margin-top: 5px;}
    .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;}
    .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 3px 0 3px;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: 4px;width: 98%;height: 46%;bottom: 0px;border-radius: 4px;background-color: rgb(243, 238, 233);overflow-y: scroll;padding: 4px;}
    .tagbar-commentlist {margin-top: 4px;width: 98%;height: 70%;bottom: 0px;border-radius: 4px;background-color: rgb(243, 238, 233);overflow-y: scroll;padding: 4px;}
    .tagbar-taglist::-webkit-scrollbar,.tagbar-commentlist::-webkit-scrollbar {width: 4px;height: 4px;}
    .tagbar-taglist::-webkit-scrollbar-thumb,.tagbar-commentlist::-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 { box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.2);border-radius: 0px;background: rgba(0, 0, 0, 0.1);}
    #flash-time {float: right;margin-right: 10px;}`;
    TAG_HTML.innerHTML = `
    <div class='tagbar-hide'>&lt;</div>
    <div class='tagbar'>
        <div id="tagbar-exit" class="tagbar-action">
            <svg class='tagbar-icon' viewBox='0 0 1024 1024' xmlns='http://www.w3.org/2000/svg' width='10px' height='10px'>
                <path d='M783.8 892.7L512 620.9 240.2 892.7c-30 30-78.7 30-108.7 0s-30-78.7 0-108.7l271.8-271.8-271.9-271.9c-30-30-30-78.7 0-108.7s78.7-30 108.7 0L512 403.4l271.8-271.8c30-30 78.7-30 108.7 0s30 78.7 0 108.7L620.7 512.1 892.6 784c30 30 30 78.7 0 108.7s-78.7 30-108.8 0z' fill='#929292'></path>
            </svg>
        </div>
        <div class="tag-bar">
            <label class="tagbar-label" for="input-tagname" title="标签的分类,比如游戏、Vtuber、主播等等">标签分类</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="匹配用户标签的正则关键词,多个关键词词用 | 分隔">标签正则</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: 34px;" for="tag-hide">屏蔽该标签用户评论<input type="radio" id="tag-hide" style="margin-left:20px;height:11px"></label>
            <input type="button" class="tagbar-btn" id="add-tag" value="添加标签">
            <label for="refresh-time" style="float:right;margin-top: 5px;margin-right:6px;font-size: 12px;display: inline-block;">刷新间隔<input type="range" id="refresh-time" min="2" max="20" step="1" value="5"><span id="show-time">5s</span></label>
            <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="margin-top: 5px;margin-left: 15px;" value="添加规则">
            <div class="tagbar-commentlist"></div>
        </div>
    </div>`;
    document.querySelector('head').appendChild(TAG_STYLE);
    document.querySelector('body').appendChild(TAG_HTML);
    let hideSet = false;
    const sideBar = document.querySelector('.tagbar-hide');
    const tagBar = document.querySelector('.tagbar');
    const exitBtn = document.querySelector('#tagbar-exit');
    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 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 = () => {
        tagBar.style.display = 'block';
        sideBar.style.display = 'none';
    };
    exitBtn.onclick = () => {
        tagBar.style.display = 'none';
        sideBar.style.display = 'block';
    };
    tag_hide.onclick = () => {
        hideSet = !hideSet;
        tag_hide.checked = hideSet;
    };
    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: hideSet
            });
        } else {
            alert('请将标签信息补充完整');
        }
    };
    refresh_time.onchange = () => {
        refreshTime = refresh_time.value * 1000;
        document.querySelector("#show-time").innerText = refresh_time.value + 's';
    };
    add_tag_reg.onclick = () => {
        if (comment_reg.value) {
            addKeyWord(comment_reg.value);
        };
    };
    const addKeyWord = (comment_reg) => {
        let new_tag = document.createElement('div');
        new_tag.innerHTML = `<div class="delete-tag">x</div><p class="tag-info">${comment_reg}</p>`;
        new_tag.classList.add('tags');
        new_tag.style.width = comment_reg.length * 12 + 10 + 'px';
        commentlist.appendChild(new_tag);
        let keyword_index = Object.keys(comment_keyword).length;
        comment_keyword[keyword_index] = comment_reg;
        keyword.push(comment_reg);
        GM_setValue('keyword', keyword);
        new_tag.children[0].onclick = () => {
            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);
        }
    };
    const addTag = (tag_dic) => {
        let new_tag = document.createElement('div');
        new_tag.innerHTML = `<div class="delete-tag">x</div><p class="tag-info">${tag_dic.text}</p>`;
        new_tag.classList.add('tags');
        new_tag.style.width = tag_dic.text.length * 12 + 10 + 'px';
        new_tag.style.color = tag_dic.color;
        taglist.appendChild(new_tag);
        let tag_index = Object.keys(tag).length;
        tag[tag_index] = tag_dic;
        tag_list.push(new Tag(tag[tag_index]));
        GM_setValue('tag', tag);
        new_tag.children[0].onclick = () => {
            taglist.removeChild(new_tag);
            delete tag[tag_index];
            tag_list.pop(tag);
            GM_setValue('tag', tag);
        }
    };
    class Tag {
        constructor(tag_dic) {
            this.tag = tag_dic.tag;
            this.text = tag_dic.text;
            this.tagReg = new RegExp(tag_dic.text);
            this.reg = new RegExp(tag_dic.reg);
            this.hide = tag_dic.hide;
            this.color = tag_dic.color;
            this.width = tag_dic.text.length * 10 + 5;
            this.tag_width = RegExp('[A-Za-z0-9]').test(this.tag) ? this.tag.length * 5 + 5 : this.tag.length * 10 + 5;
            this.list = new Set();
            this.nolist = new Set();
            this.inner = `<div class='userTag'><div class='tag-name' style='border-color:#8da8e8;color:#5e80c4; width:${this.tag_width}px'><div class='tag-font'>${this.tag}</div></div>
            <div class='tag-text' style='color: ${this.color}; width:${this.width}px;'><div class='tag-font'>${this.text}</div></div></div>`;
        };
        static hideComment(c) {
            let comment = '';
            if (is_new) {
                if (c.classList.contains('user-name')) {
                    comment = c.parentElement.nextSibling.children[0];
                } else {
                    comment = c.parentElement.nextSibling;
                }
            } else {
                if (c.querySelector('.text-con')) {
                    comment = c.querySelector('.text-con');
                } else {
                    comment = c.nextSibling;
                }
            }
            return comment;
        };
        check(pid, c) {
            if (this.list.has(pid)) {
                if (this.hide) {
                    Tag.hideComment(c).innerText = '评论已屏蔽';
                }
                if (!this.tagReg.test(c.textContent)) {
                    c.innerHTML += this.inner;
                }
                return true;
            } else if (this.nolist.has(pid)) {
                return true;
            }
        };
        detect(st, c, pid) {
            if (this.reg.test(st)) {
                if (this.hide) {
                    Tag.hideComment(c).innerText = '评论已屏蔽';
                }
                c.innerHTML += this.inner;
                this.list.add(pid);
            } else {
                this.nolist.add(pid);
            }
        };
    }
    class TagList {
        constructor() {
            this.list = [];
        }
        push(tag) {
            this.list.push(tag);
        }
        pop(tag) {
            this.list = [];
            let tag_key = Object.keys(tag);
            tag_key.map(key => this.list.push(new Tag(tag[key])));
        }
        check(pid, c) {
            for (let i of this.list) {
                let a = i.check(pid, c);
                if (a) {
                    return true;
                }
            }
        }
        detect(st, c, pid) {
            this.list.map(i => i.detect(st, c, pid));
        }
    }
    const getPid = (c) => {
        if (is_new) {
            return c.dataset.userId;
        } 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-name,.sub-user-name');
        } else {
            return document.querySelectorAll('.user');
        }
    };
    const IngredientDetection = () => {
        let commentlist = getCommentList();
        if (commentlist.length != 0) {
            commentlist.forEach(c => {
                if (keyword.length > 0) {
                    let comment = Tag.hideComment(c);
                    for (let reg of keyword) {
                        reg = new RegExp(reg);
                        if (reg.test(comment.innerText)) {
                            comment.innerText = '评论已屏蔽';
                            break;
                        }
                    }
                }
                let pid = getPid(c);
                let a = tag_list.check(pid, c);
                if (a) {return;}
                let blogurl = blog + pid;
                GM_xmlhttpRequest({
                    method: "get",
                    url: blogurl,
                    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 st = JSON.stringify(JSON.parse(res.response).data);
                            tag_list.detect(st, c, pid)
                        } else {
                            console.log('加载用户信息失败');
                            console.log(res);
                        }
                    },
                });
            });
        }
    };
    function throttle(func, wait) {
        let timeout,
            startTime = new Date();
        return function() {
            let context = this,
                args = arguments,
                curTime = new Date();
            clearTimeout(timeout);
            if (curTime - startTime >= refreshTime) {
                func.apply(context, args);
                startTime = curTime;
            } else {
                timeout = setTimeout(func, wait);
            }
        };
    };
    const wheel = () => {
        let time1 = new Date();
        let btns = document.querySelectorAll('.btn-more,.paging-box,.view-more-pagination,.view-more-btn');
        for (let btn of btns) {
            btn.onclick = () => {
                setTimeout(() => {
                    IngredientDetection();
                }, 500);
            }
        }
        IngredientDetection();
    };
    let tag = {};
    let comment_keyword = {};
    let keyword = [];
    let refreshTime = 5000;
    const key_storage = GM_getValue('keyword', []);
    key_storage.map(key => addKeyWord(key));
    let tag_list = new TagList();
    const tag_storage = GM_getValue('tag', {});
    let tag_storage_key = Object.keys(tag_storage);
    tag_storage_key.map(tag => addTag(tag_storage[tag]));
    IngredientDetection();
    window.addEventListener("scroll", throttle(wheel, 2000));
})();