// ==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="导入配置到油猴存储 请先将配置粘贴到下方并确认无误,再点击本按键 导入配置后请刷新界面以应用配置" 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="显示用户的粉丝勋章,更改后刷新界面 开启此选项后将粉丝勋章墙纳入标签检测 开启后用户标签末尾出现【勋章】标签, 点击【勋章】展开勋章栏显示所有勋章 鼠标放上显示对应Up主,点击可跳转 点击【勋章】或勋章栏空白处收起">显示粉丝勋章<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主等 用来识别标签类型,尽量短一些">标签分类</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: 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} 规则:${tag_dic.reg} 隐藏评论:${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;
}
})();