// ==UserScript== // @name 【B站】评论区成分标签·关键词屏蔽 // @namespace lycoris // @version 1.6.3 // @description B站评论区可视化添加用户成分标签,用户评论关键词屏蔽 // @author Lyzoris // @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 // @downloadURL none // ==/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 {background-color: #76cb9dc9;box-shadow: 2px 0px 4px 0px #76cb9d91;} .active{background-color: #ab85d1c9;box-shadow: 2px 0px 4px 0px #c893e291;} .active svg path{fill: #ffffff;} .tagbar-hide svg{width: 100%; height: 100%;} .tagbar-hide:hover svg path{fill: #ffffff;} .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-setting {right: 0;top: 0;} #tagbar-setting svg:hover path {fill: #76b1ef;} .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 #b0b0b0;} .input-tag:focus {outline: none;} #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: #bbc4cdd1;margin-left: 3px;margin-top: 5px;cursor: pointer;} .tags:hover{background-color: #8dbbccc4;} .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 = `
`; document.querySelector('head').appendChild(TAG_STYLE); document.querySelector('body').appendChild(TAG_HTML); const sideBar = document.querySelector('.tagbar-hide'); const tagBar = document.querySelector('.tagbar'); 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 clean_input = document.querySelector('#clean-input'); 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 === 'none') { tagBar.style.display = 'block'; sideBar.classList.add('active'); } else { tagBar.style.display = 'none'; sideBar.classList.remove('active'); } }; clean_input.onclick = () => { tag_name.value = ''; tag_text.value = ''; tag_reg.value = ''; tag_hide.checked = false; }; 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 }); } 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); } else { alert('请将关键词正则信息补充完整'); }; }; const addKeyWord = (comment_reg) => { if (!keyword.includes(comment_reg)) { let new_tag = insertTag(commentlist, comment_reg); 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 = insertTag(taglist, tag_dic.text, tag_dic.color); 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); } }; const insertTag = (parentNode, text, color = "#49414b") => { let new_tag = document.createElement('div'); new_tag.innerHTML = `
x

${text}

`; 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.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.tag_width = measureTextWidth("12px", this.tag) + 2; this.width = measureTextWidth("12px", this.text) + 2; this.list = new Set(); this.nolist = new Set(); this.inner = `
${this.tag}
${this.text}
`; }; check(pid, c) { if (this.list.has(pid)) { if (this.hide) { getCommentTextNode(c).innerText = '评论已屏蔽'; } if (!this.tagReg.test(c.textContent)) { c.innerHTML += this.inner; } CHECK = true; } else if (this.nolist.has(pid)) { CHECK = true; } }; detect(st, c, pid) { if (!this.list.has(pid) && !this.nolist.has(pid)) { if (this.reg.test(st)) { if (this.hide) { getCommentTextNode(c).innerText = '评论已屏蔽'; } if (!this.tagReg.test(c.textContent)) { 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) { CHECK = false; if (keyword.length > 0) { let comment = getCommentTextNode(c); for (let reg of keyword) { reg = new RegExp(reg); if (reg.test(comment.innerText)) { comment.innerText = '评论已屏蔽'; break; } } } this.list.map(i => i.check(pid, c)); } 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 getCommentTextNode = (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; }; const IngredientDetection = () => { let commentlist = getCommentList(); if (commentlist.length != 0) { commentlist.forEach(c => { let pid = getPid(c); tag_list.check(pid, c); if (CHECK) { 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 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 CHECK = false; 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)); })();