Greasy Fork

Greasy Fork is available in English.

B站用户成分标签

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         B站用户成分标签
// @namespace    lycoris
// @version      1.3
// @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';

    // 脚本刷新时间(推荐在10s左右) 单位:ms
    const refreshTime = 10000;

    // 用户评论按照 关键词 屏蔽,数组形式,可自行添加字符串(和标签设置里的屏蔽不同),用逗号分隔
    const keyword = [];

    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;}
    .side-bar {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;}.side-bar:hover {color: #ffffff;background-color: #76cb9dc9;box-shadow: 2px 0px 4px 0px #76cb9d91;}
    .script-bar {display: none;position: fixed;z-index: 999;right: 65px;bottom: 20px;background-color: #ffffff;color: #929292;height: 235px;
    width: 180px;border-radius: 5px;box-shadow: 0px 0px 4px 2px #0000002b;padding: 16px;}.ActionBar {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;}#About {right: 20px;}#Exit {right: 0;}#About svg:hover path {fill: #76b1ef;}#Exit svg:hover path {fill: #f85c5cc9;}
    .btn {background-color: #ffffff;color: #929292;width: 40px;height: 20px;margin: 20px 5px 0 7px;border: none;border-radius: 4px;font-weight: bold;
    font-size: 13px;text-align: center;box-shadow: #b1b1b154 0px 2px 5px 2px;}.btn:hover,#download-btn:hover {color: #76cb9dc9;}.input-tag {margin-bottom: 4px;width: 60%;left: 20%;border-radius: 4px;border: solid 1px rgb(100, 97, 97);}
    .input-tag:focus {outline: none;}.tag-label {font-size: 12px;display: inline-block;margin-right: 10px;}#input-tagcolor {width: 40px;height: 20px;}
    .tag-list {margin-top: 4px;width: 95%;height: 50%;bottom: 0px;border-radius: 4px;background-color: rgb(243, 238, 233);overflow-y: scroll;padding: 4px;}.tags {display: inline-block;width: 40px;height: 20px;font-size: 10px;border-radius: 5px;color: #49414b;text-align: center;
    line-height: 20px;background-color: #bbc4cdd1;margin-left: 4px;margin-top: 6px;}.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;}`;
    TAG_HTML.innerHTML = `
    <div class='side-bar'>&lt;</div>
    <div class='script-bar'>
        <div id="About" class="ActionBar">
            <svg class='icon' viewBox='0 0 1024 1024' xmlns='http://www.w3.org/2000/svg' width='1em' height='1em'>
                <path d='M873.93304 149.954855A511.943948 511.943948 0 1 0 312.687215 983.626704 512.426118 512.426118 0 0 0 983.626704 711.200681a511.58232 511.58232 0 0 0-109.693664-561.245826z m66.057284 361.627465A428.046376 428.046376 0 1 1 511.943948 84.379742 428.528546 428.528546 0 0 1 940.231409 511.943948z' fill='#929292'></path>
                <path d='M511.943948 328.960451A41.948786 41.948786 0 0 0 470.115704 370.909236v423.104134a41.948786 41.948786 0 0 0 84.379742 0v-423.104134a41.948786 41.948786 0 0 0-42.551498-41.948785zM511.943948 164.540497A41.948786 41.948786 0 0 0 470.115704 206.489282v23.505786a41.948786 41.948786 0 0 0 84.379742 0v-23.505786a41.948786 41.948786 0 0 0-42.551498-41.948785z' fill='#929292'></path>
            </svg>
        </div>
        <div id="Exit" class="ActionBar">
            <svg class='icon' viewBox='0 0 1024 1024' xmlns='http://www.w3.org/2000/svg' width='1em' height='1em'>
                <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>
        <label class="tag-label" for="input-tagname">标签名称</label><input type="text" id="input-tagname" class="input-tag">
        <label class="tag-label" for="input-tagtext">标签内容</label><input type="text" id="input-tagtext" class="input-tag">
        <label class="tag-label" for="input-tagreg" title="多个关键词词用 | 分隔">标签正则</label><input type="text" id="input-tagreg" class="input-tag">
        <label class="tag-label" for="input-tagcolor">标签颜色</label><input type="color" name="" id="input-tagcolor">
        <label class="tag-label" style="margin-left: 4px; margin-right:5px" for="tag-hide">隐藏评论</label><input type="radio" id="tag-hide" style="marhin:0;height:10px">
        <input type="button" name="" id="add-tag" value="添加标签" style='height: 20px;'>
        <div class="tag-list">
        </div>
    </div>`
    document.querySelector('head').appendChild(TAG_STYLE);
    document.querySelector('body').appendChild(TAG_HTML);
    let hideSet = false;
    const sideBar = document.querySelector('.side-bar');
    const scriptBar = document.querySelector('.script-bar');
    const exitBtn = document.querySelector('#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('.tag-list');
    let tag_hide = document.querySelector('#tag-hide');
    sideBar.onclick = () => {
        scriptBar.style.display = 'block';
        sideBar.style.display = 'none';
    };
    exitBtn.onclick = () => {
        scriptBar.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('请将标签信息补充完整');
        }
    };
    const addTag = (tag_dic) => {
        let tag_index = Object.keys(tag).length;
        tag[tag_index] = {
            tag: tag_dic.tag,
            text: tag_dic.text,
            reg: tag_dic.reg,
            color: tag_dic.color,
            hide: tag_dic.hide
        };
        tag_list.push(new Tag(tag[tag_index]));
        GM_setValue('tag', tag);
        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';
        taglist.appendChild(new_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);
                        }
                    },
                });
            });
        }
    };
    let tag = {};
    const tag_store = GM_getValue('tag', {});
    let tag_list = new TagList();
    let tag_store_key = Object.keys(tag_store);
    tag_store_key.map(key => addTag(tag_store[key]));
    IngredientDetection();
    setInterval(() => {
        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();
    }, refreshTime);
})();