Bilibili【哔哩哔哩】 用户成分标签

根据 Bilibili 用户近期动态、粉丝勋章内容检测成分添加自定义标签,按规则屏蔽评论

  1. // ==UserScript==
  2. // @name Bilibili【哔哩哔哩】 用户成分标签
  3. // @namespace lycoris
  4. // @version 2.6.2
  5. // @description 根据 Bilibili 用户近期动态、粉丝勋章内容检测成分添加自定义标签,按规则屏蔽评论
  6. // @author Lyzoris
  7. // @supportURL https://github.com/lyzoris/Bilibili-UserComponentTag
  8. // @compatible chrome 80 or later
  9. // @compatible edge 80 or later
  10. // @match https://www.bilibili.com
  11. // @match https://www.bilibili.com/video/*
  12. // @match https://www.bilibili.com/opus/*
  13. // @match https://t.bilibili.com/*
  14. // @match https://space.bilibili.com/*
  15. // @match https://www.bilibili.com/bangumi/play/*
  16. // @match https://www.bilibili.com/read/*
  17. // @icon https://static.hdslb.com/images/favicon.ico
  18. // @connect bilibili.com
  19. // @grant GM_setValue
  20. // @grant GM_getValue
  21. // @grant GM_deleteValue
  22. // @grant GM_listValues
  23. // @grant GM_setClipboard
  24. // @require https://gf.qytechs.cn/scripts/448895-elementgetter%E5%BA%93/code/ElementGetter%E5%BA%93.js?version=1106656
  25. // @license MIT
  26. // @run-at document-end
  27. // ==/UserScript==
  28. (function () {
  29. "use surict";
  30. const elmGetter = new ElementGetter();
  31. const Cookie = document.cookie;
  32. const goOldVideo = Cookie.match(/(?<=go_old_video=)[-\d]{1,2}/);
  33. let isNew = true;
  34. if (Cookie && goOldVideo) {
  35. isNew = goOldVideo[0] === '-1';
  36. }
  37. let Page = '';
  38. const webType = { '^https:\/\/www.bilibili.com[\/]$': 'Main', 'https:\/\/(t|space).bilibili.com': 'Dynamic', 'https:\/\/www.bilibili.com\/video': 'Video' };
  39. Object.keys(webType).map(urlReg => { if (RegExp(urlReg).test(location.href)) {
  40. Page = webType[urlReg];
  41. } });
  42. const ApiUrl = {
  43. blog: 'https://api.bilibili.com/x/polymer/web-dynamic/v1/feed/space?offset=&host_mid=',
  44. concerns: 'https://api.bilibili.com/x/relation/followings?vmid=',
  45. medal: 'https://api.live.bilibili.com/xlive/web-ucenter/user/MedalWall?target_id='
  46. };
  47. const childTagReg = new RegExp(/^(\[.*?\])(\[.*?\])*(\[.*?\])$/);
  48. const tagClassHide = {
  49. true: '.tag-class {display:none;}',
  50. false: '.tag-class {display:flex;}'
  51. };
  52. const Style_tagClassHide = elmGetter.create(`<style>${tagClassHide.false}</style>`);
  53. const ScriptStyle_STR = `
  54. <style>
  55. .adblock-tips{display:none!important}.userTag{display:inline-block;position:relative;text-align:center;border-width:0px;vertical-align:sub;margin-left:4px;margin-right:4px;cursor:default}.tag-name{border-color:rgba(169,195,233,0.1803921568627451);color:rgba(87,127,184,1);background-color:#9ebae833;float:left;display:flex;flex-wrap:wrap;align-content:center;justify-content:center;border-width:0.5px;border-style:solid;border-bottom-left-radius:1px;border-top-left-radius:1px;height:16px}.tag-class{position:relative;border-bottom-right-radius:1px;border-top-right-radius:1px;float:left;display:flex;flex-wrap:wrap;align-content:center;justify-content:center;box-sizing:content-box;border-width:0.5px;border-color:#f25d8e;border-style:solid;border-bottom-left-radius:1px;border-top-left-radius:1px;color:#f25d8e;height:16px}.tag-font{font-weight:400;font-size:10px}
  56. .script-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}.script-hide svg{width:100%;height:100%;vertical-align:unset}.script-hide:hover{background-color:#76cb9dc9;box-shadow:2px 0px 4px 0px #76cb9d91}.script-hide:hover svg path{fill:#ffffff}.script-like{display:none;position:fixed;right:10px;bottom:40px;width:20px;height:20px;font-size:15px;line-height:26px;text-align:center;color:#009688;background-color:#e2e1e2b3;border-radius:5px;box-shadow:2px 0px 4px 0px #0000002b}.script-like:hover{background-color:#76cb9dc9;box-shadow:2px 0px 4px 0px #76cb9d91}.script-like svg{width:80%;height:80%;margin-left:1px;vertical-align:unset}.script-like:hover svg path{fill:#ffffff}
  57. .tagbar-active{background-color:#ab85d1c9;box-shadow:2px 0px 4px 0px #c893e291}.tagbar-active svg path{fill:#ffffff}.script-main{display:none;position:fixed;z-index:999;right:65px;bottom:20px;background-color:#ffffff;color:#929292;height:320px;width:270px;border-radius:4px;box-sizing:border-box;box-shadow:0px 0px 4px 2px #0000002b;padding:10px}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}.script-main input[type="checkbox"],input[type="radio"]{appearance:auto}.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}
  58. .tagbar-btn:hover{color:#37a279ab}.tagbar-btn:active{color:#b4b6ee}.input-tag,#input-comment-reg{margin-bottom:4px;width:74%;left:20%;border-radius:4px;border:solid 1px #b0b0b0}.input-tag:focus,#input-comment-reg:focus{outline:none;background-color:#d5d5d559}.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-top:5px;margin-left: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:#ffffff00;line-height:18px;background-color:#ffffff00}
  59. .delete-tag:hover{color:white!important;background-color:crimson!important}.tags:hover > .delete-tag{color:#434343fa;background-color:#bbb5b54d}.tag-info{position:relative;margin-top:-20px;line-height:unset;font-weight:bold}.scriptBar{display:flex;flex-direction:column;margin-top:25px;height:92%}.tagbar-taglist,.tagbar-commentlist{margin-top:5px;width:100%;flex:auto;bottom:0px;border-radius:4px;background-color:rgb(243,238,233);overflow-y:auto;padding:4px;box-sizing:border-box}.tagbar-taglist::-webkit-scrollbar,.tagbar-commentlist::-webkit-scrollbar,.medal-table::-webkit-scrollbar,#import-area::-webkit-scrollbar{width:4px;height:4px}
  60. .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)}.tagSize{margin:5px 0 5px 0}.tagSize-label{display:inline-block;text-align:center}.tagSize-radio{vertical-align:text-bottom}.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}
  61. .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:244px;min-width:244px;height:40px;min-height:20px;max-height:40px;background-color:#f3f3f3;border-radius:5px}#import-area:focus{outline:none}.topnav{top:8px;left:5px;height:20px;line-height:20px;position:absolute;color:#8e8e8e;background-color:#ddddd98c;border-radius:4px;cursor:default}.topnav-active{color:#dd7a7a;background-color:#ffffff;border-radius:4px;height:16px;line-height:16px;box-shadow:0 0 3px 1px #3f3d3d80}.topnav-option{display:inline-block;font-size:12px;font-weight:bold;padding:0 5px 0 5px}.topnav-option:nth-child(2){margin:0 38px 0 38px}#dynamic-like svg{width:25px;height:25px;margin:0 0 0 25px}#dynamic-like:hover svg path{fill:#19aada}
  62. .search-btn{display:inline-block;font-size:15px;background-color:#eef1f3;color:#377f79b3;width:44px;border-radius:10px;text-align:center;margin:0 5px 0 5px!important;transform:scale(0.8) translate(-15%,0%);box-shadow:none;cursor:default}.search-btn:hover{color:#9c27b0;box-shadow:0px 0px 3px 1px #33323229}.block-btn{margin:0 20px 0 20px;cursor:pointer}.block-btn:hover{color:#00aeec}.search-btn:active{color:#dd4438}.close-btn{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 a{color:#99a2aa!important}.close-btn a:hover{color:#e132c2!important}#detectionMode,#blockUser{display:inline-block;font-size:14px;font-weight:bold;text-align:center;width:250px;margin:4px 0 8px 0;cursor:default}#detectionMode-input,#blockUser-input{display:none}
  63. #detectionMode-label{display:inline-block;width:50px;height:16px;border-radius:16px;background:#4fb153;border:1px solid #eee;cursor:pointer;position:relative;overflow:hidden;margin:0 8px 0 8px;vertical-align:text-bottom}#detectionMode-label::before,#blockUser-label::before{display:block;content:'';width:16px;height:16px;border-radius:50%;background:white;position:absolute;left:0px;top:50%;transform:translateY(-50%);transition:all .3s}#detectionMode-label::after{display:block;content:'';width:0;height:100%;background:#2695dc;transition:all .3s;border-radius:10px}#detectionMode-manual{color:#4fb153}#detectionMode-input:checked ~ #detectionMode-label::before{left:34px}#detectionMode-input:checked ~ #detectionMode-label::after{width:100%}#detectionMode-input:checked ~ #detectionMode-auto{color:#2695dc}#detectionMode-input:checked ~ #detectionMode-manual{color:#929292}
  64. #blockUser-label{display:inline-block;width:50px;height:16px;border-radius:16px;background:#929292;border:1px solid #eee;cursor:pointer;position:relative;overflow:hidden;margin:0 8px 0 8px;vertical-align:text-bottom}#blockUser-label::after{display:block;content:'';width:0;height:100%;background:#4fb153;transition:all .3s;border-radius:10px}#blockUser-input:checked ~ #blockUser-label::before{left:34px}#blockUser-input:checked ~ #blockUser-label::after{width:100%}#blockUser-input:checked ~ #blockUser-manual{color:#4fb153}#reg-ruler:hover{color:#81afc5}#reg-ruler:active{color:#cc6d89}#detect-page{height:5px;width:100px}#offset-page{padding-left:20px}
  65. </style>
  66. `;
  67. const ScriptBody_STR = `
  68. <div id='Script-Body'>
  69. <div class='script-like'>
  70. <svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" width="16" height="16">
  71. <path d="M857.28 344.992h-264.832c12.576-44.256 18.944-83.584 18.944-118.208 0-78.56-71.808-153.792-140.544-143.808-60.608 8.8-89.536 59.904-89.536 125.536v59.296c0 76.064-58.208 140.928-132.224 148.064l-117.728-0.192A67.36 67.36 0 0 0 64 483.04V872c0 37.216 30.144 67.36 67.36 67.36h652.192a102.72 102.72 0 0 0 100.928-83.584l73.728-388.96a102.72 102.72 0 0 0-100.928-121.824zM128 872V483.04c0-1.856 1.504-3.36 3.36-3.36H208v395.68H131.36A3.36 3.36 0 0 1 128 872z m767.328-417.088l-73.728 388.96a38.72 38.72 0 0 1-38.048 31.488H272V476.864a213.312 213.312 0 0 0 173.312-209.088V208.512c0-37.568 12.064-58.912 34.72-62.176 27.04-3.936 67.36 38.336 67.36 80.48 0 37.312-9.504 84-28.864 139.712a32 32 0 0 0 30.24 42.496h308.512a38.72 38.72 0 0 1 38.048 45.888z" fill="#459cdd"></path>
  72. </svg>
  73. </div>
  74. <div class='script-hide'>
  75. <svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" width="16" height="16">
  76. <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>
  77. </svg>
  78. </div>
  79. <div class='script-main'>
  80. <div class="topnav">
  81. <div class="topnav-option topnav-active" index=1 id="tag-btn">成分标签</div>
  82. <div class="topnav-option" id="comment-btn" index=2>评论屏蔽</div>
  83. <div class="topnav-option" id="setting-btn" index=0>脚本设置</div>
  84. </div>
  85. <div class="scriptBar setting-bar" style="display:none;">
  86. <div class="inputBar">
  87. <input type="button" class="tagbar-btn" id="export-tag" value="导出配置" title="导出标签配置到剪切板" style="float:left; margin:0;">
  88. <input type="button" class="tagbar-btn" id="import-tag" value="导入配置" title="导入配置到油猴存储&#10;请先将配置粘贴到下方并确认无误,再点击本按键&#10;导入配置后请刷新界面以应用配置" style="float:right; margin:0;">
  89. </div>
  90. <textarea id="import-area" placeholder="请在此处粘贴配置后点击【导入配置】"></textarea>
  91. <label class="set-label" title="用户标签检测内容(可多选)">检测选项
  92. <label for="detect-repost" title="检测用户转发的动态内容(建议开启)" style="margin-left:20px">转发动态
  93. <input type="checkbox" id="detect-repost" style="margin-left:5px;height:11px">
  94. </label>
  95. <label for="detect-concerns" title="检测用户关注列表" style="margin-left:20px">关注列表
  96. <input type="checkbox" id="detect-concerns" style="margin-left:5px;height:11px">
  97. </label>
  98. <label for="detect-medal" title="检测用户的粉丝勋章&#10;开启后用户标签末尾出现【勋章】标签,&#10;点击【勋章】展开勋章栏显示所有勋章&#10;鼠标放上显示对应Up主,点击可跳转&#10;点击【勋章】或空白处收起" style="margin-left:72px">
  99. 粉丝勋章<input type="checkbox" id="detect-medal" style="margin-left:8px;height:11px">
  100. </label>
  101. </label>
  102. <label class="set-label" for="detect-page" title="检测用户动态和关注列表页数&#10;越大加载时间越长">检测页数<span id="offset-page">1</span><input type="range" id="detect-page" value=1 max=10 min=1 step=1 style="margin-left:50px;height:11px"></label>
  103. <label class="set-label" for="tagClass-hide" title="不显示标签分类,建议开启&#10;出现标签样式错误时请开启此项">标签不显示分类<input type="checkbox" id="tagClass-hide" style="margin-left:50px;height:11px"></label>
  104. <label class="set-label" for="tag-merge" title="相同类型标签合并,只显示一个标签分类&#10;可能影响标签样式,建议同时打开【标签不显示分类】">同类型标签合并<input type="checkbox" id="tag-merge" style="margin-left:50px;height:11px"></label>
  105. <label class="set-label" for="link-delete" title="去除 评论区评论关键词蓝色点击跳转">去除关键词跳转<input type="checkbox" id="link-delete" style="margin-left:50px;height:11px"></label>
  106. <label class="set-label" for="close-comment" title="动态评论末尾添加【收起评论】按键">动态添加收起评论<input type="checkbox" id="close-comment" style="margin-left:38px;height:11px"></label>
  107. <label class="set-label" for="dynamic-btn" title="动态页面添加【批量点赞】按键">动态页批量点赞<input type="checkbox" id="dynamic-btn" style="margin-left:50px;height:11px"></label>
  108. </div>
  109. <div class="scriptBar tag-bar">
  110. <div class="inputBar">
  111. <div id="detectionMode">
  112. <input type="checkbox" id="detectionMode-input">
  113. <span id="detectionMode-manual" title="手动点击‘检测’以检测用户标签&#10;请求量较少,比较安全">手动检测</span>
  114. <label for="detectionMode-input" id="detectionMode-label"></label>
  115. <span id="detectionMode-auto" title="自动检测所有评论用户标签&#10;请求量较大,存在封号风险">自动检测</span>
  116. </div>
  117. <label class="tagbar-label" for="input-tagname" title="标签的分类,比如游戏、Vtuber、主播、UP主等&#10;用来识别标签类型,尽量短一些">标签分类</label><input type="text" id="input-tagname" index=0 class="input-tag" autocomplete="off">
  118. <label class="tagbar-label" for="input-tagtext" title="标签具体显示的内容,以区别每个标签">标签内容</label><input type="text" id="input-tagtext" index=1 class="input-tag" autocomplete="off">
  119. <label class="tagbar-label" for="input-tagreg" title="匹配用户标签的关键词,使用 & 和 | 分隔,使用 ( ) 组合&#10;多关键词任一匹配:王者荣耀或王者 -&gt; 王者荣耀|王者 &#10;多关键词同时匹配:王者荣耀与吃鸡 -&gt; 王者荣耀&吃鸡 &#10;多关键词组合匹配:(王者|王者荣耀)&(吃鸡|和平精英)&#10;&#10;合并标签时填写 [子标签内容][子标签内容] 如[王者][原神]&#10;注意:面板中合并标签需在所有子标签之后">标签规则</label><input type="text" id="input-tagreg" index=2 class="input-tag" autocomplete="off">
  120. <label class="tagbar-label" for="input-tagcolor" title="标签文字颜色,可在面板中预览">标签颜色</label><input type="color" name="" id="input-tagcolor">
  121. <label class="tagbar-label" style="margin: 0 18px 0 10px ;" for="tag-hide" title="屏蔽该标签用户评论">屏蔽评论<input type="checkbox" id="tag-hide" style="margin-left:9px;height:11px"></label>
  122. <input type="button" class="tagbar-btn" id="add-tag" value="+" style="width:30px">
  123. </div>
  124. <div class="tagbar-taglist"></div>
  125. </div>
  126. <div class="scriptBar comment-bar" style="display:none;">
  127. <div class="inputBar">
  128. <label class="set-label" title="点击开启指定屏蔽功能(可多选)&#10;在对应页面生效">屏蔽选项
  129. <label for="block-video" title="【主页】按规则屏蔽主页推荐视频" style="margin-left:20px">主页推荐
  130. <input type="checkbox" id="block-video" style="margin-left:5px;height:11px">
  131. </label>
  132. <label for="block-dynamic" title="【动态、空间页】按规则屏蔽用户动态内容" style="margin-left:20px">用户动态
  133. <input type="checkbox" id="block-dynamic" style="margin-left:5px;height:11px">
  134. </label>
  135. <label for="block-comment" title="【视频页】按规则屏蔽用户评论" style="margin-left:72px">
  136. 评论内容<input type="checkbox" id="block-comment" style="margin-left:8px;height:11px">
  137. </label>
  138. <label for="block-user" title="【动态、视频页】需先打开 “评论内容” 选项&#10;点击 “屏蔽” 按键以手动屏蔽用户&#10;同时添加用户id到黑名单" style="margin-left:20px">
  139. 用户屏蔽<input type="checkbox" id="block-user" style="margin-left:8px;height:11px">
  140. </label>
  141. </label>
  142. <label class="tagbar-label" for="input-comment-reg" id="reg-ruler" title="正则表达式屏蔽规则&#10;可屏蔽首页视频推荐,用户动态,用户评论&#10;不了解正则表达式?按Alt点击跳转学习">屏蔽规则</label><input type="text" id="input-comment-reg" autocomplete="off" style="width:64%;">
  143. <input type="button" class="tagbar-btn" id="add-comment-reg" style="float:right; margin:2px 0 5px 0; width:18px;" value="+">
  144. </div>
  145. <div class="tagbar-commentlist"></div>
  146. </div>
  147. </div>
  148. </div>`;
  149. const ScriptBody = elmGetter.create(ScriptBody_STR);
  150. const ScriptStyle = elmGetter.create(ScriptStyle_STR);
  151. const Head = document.head || document.querySelector('head');
  152. const Body = document.body || document.querySelector('body');
  153. Head.appendChild(ScriptStyle);
  154. Head.appendChild(Style_tagClassHide);
  155. Body.appendChild(ScriptBody);
  156. const scriptHide = ScriptBody.querySelector('.script-hide');
  157. const scriptMain = ScriptBody.querySelector('.script-main');
  158. const topNav = ScriptBody.querySelectorAll('.topnav-option');
  159. const scriptBar = ScriptBody.querySelectorAll('.scriptBar');
  160. const inputFields = ScriptBody.querySelectorAll('.input-tag');
  161. let tagClass_hide = ScriptBody.querySelector('#tagClass-hide');
  162. let tag_merge = ScriptBody.querySelector('#tag-merge');
  163. let detect_repost = ScriptBody.querySelector('#detect-repost');
  164. let detect_concerns = ScriptBody.querySelector('#detect-concerns');
  165. let detect_medal = ScriptBody.querySelector('#detect-medal');
  166. let detection_mode = ScriptBody.querySelector('#detectionMode-input');
  167. let detect_page = ScriptBody.querySelector('#detect-page');
  168. let offset_page = ScriptBody.querySelector('#offset-page');
  169. let link_delete = ScriptBody.querySelector('#link-delete');
  170. let close_comment = ScriptBody.querySelector('#close-comment');
  171. let import_area = ScriptBody.querySelector('#import-area');
  172. let tag_name = ScriptBody.querySelector('#input-tagname');
  173. let tag_text = ScriptBody.querySelector('#input-tagtext');
  174. let tag_reg = ScriptBody.querySelector('#input-tagreg');
  175. let tag_color = ScriptBody.querySelector('#input-tagcolor');
  176. let add_tag = ScriptBody.querySelector('#add-tag');
  177. let import_tag = ScriptBody.querySelector('#import-tag');
  178. let export_tag = ScriptBody.querySelector('#export-tag');
  179. let taglist = ScriptBody.querySelector('.tagbar-taglist');
  180. let tag_hide = ScriptBody.querySelector('#tag-hide');
  181. let add_comment = ScriptBody.querySelector('#add-comment-reg');
  182. let comment_reg = ScriptBody.querySelector('#input-comment-reg');
  183. let commentlist = ScriptBody.querySelector('.tagbar-commentlist');
  184. let dynamic_btn = ScriptBody.querySelector('#dynamic-btn');
  185. let script_like = ScriptBody.querySelector('.script-like');
  186. let reg_ruler = ScriptBody.querySelector('#reg-ruler');
  187. let block_user = ScriptBody.querySelector('#block-user');
  188. let block_comment = ScriptBody.querySelector('#block-comment');
  189. let block_dynamic = ScriptBody.querySelector('#block-dynamic');
  190. let block_video = ScriptBody.querySelector('#block-video');
  191. inputFields.forEach(e => {
  192. e.onkeyup = (event) => {
  193. let index = Number(event.target?.getAttribute('index'));
  194. if (['Enter', 'ArrowDown'].includes(event.key)) {
  195. index = index == 2 ? 0 : index + 1;
  196. } else if (event.key == 'ArrowUp') {
  197. index = index == 0 ? 2 : index - 1;
  198. }
  199. inputFields[index].focus();
  200. };
  201. });
  202. scriptHide.onclick = () => {
  203. if (scriptMain.style.display === 'block') {
  204. scriptMain.style.display = 'none';
  205. scriptHide.classList.remove('tagbar-active');
  206. } else {
  207. scriptMain.style.display = 'block';
  208. scriptHide.classList.add('tagbar-active');
  209. }
  210. };
  211. topNav.forEach(btn => {
  212. btn.onclick = () => {
  213. topNav.forEach(i => { i.classList.remove('topnav-active'); });
  214. btn.classList.add('topnav-active');
  215. scriptBar.forEach(i => { i.style.display = 'none'; });
  216. scriptBar[Number(btn.getAttribute('index'))].style.display = 'flex';
  217. };
  218. });
  219. reg_ruler.onclick = (event) => {
  220. event.altKey && window.open('https://gitee.com/thinkyoung/learn_regex');
  221. };
  222. block_user.onclick = () => {
  223. BlockOption.User = block_user.checked;
  224. GM_setValue('BlockOption', BlockOption);
  225. };
  226. block_video.onclick = () => {
  227. BlockOption.Video = block_video.checked;
  228. GM_setValue('BlockOption', BlockOption);
  229. if (block_video.checked && Page === 'Main') {
  230. mainPageBlock();
  231. }
  232. };
  233. block_dynamic.onclick = () => {
  234. BlockOption.Dynamic = block_dynamic.checked;
  235. GM_setValue('BlockOption', BlockOption);
  236. if (block_dynamic.checked && Page === 'Dynamic') {
  237. dynamicPageBlock();
  238. }
  239. };
  240. block_comment.onclick = () => {
  241. BlockOption.Comment = block_comment.checked;
  242. GM_setValue('BlockOption', BlockOption);
  243. };
  244. detection_mode.onclick = () => {
  245. AutoDetect = detection_mode.checked;
  246. GM_setValue('AutoDetect', AutoDetect);
  247. };
  248. dynamic_btn.onclick = () => {
  249. script_like.style.display = dynamic_btn.checked ? 'block' : 'none';
  250. GM_setValue('DynamicLike', dynamic_btn.checked);
  251. };
  252. script_like.onclick = dynamicBatchLike;
  253. tagClass_hide.onclick = () => {
  254. Style_tagClassHide.innerHTML = tagClassHide[String(tagClass_hide.checked)];
  255. GM_setValue('TagClassHide', tagClass_hide.checked);
  256. };
  257. detect_repost.onclick = () => {
  258. DetectOption.Repost = detect_repost.checked;
  259. GM_setValue('DetectOption', DetectOption);
  260. };
  261. detect_concerns.onclick = () => {
  262. DetectOption.Concerns = detect_concerns.checked;
  263. GM_setValue('DetectOption', DetectOption);
  264. };
  265. detect_medal.onclick = () => {
  266. DetectOption.Medal = detect_medal.checked;
  267. GM_setValue('DetectOption', DetectOption);
  268. };
  269. detect_page.onchange = () => {
  270. offset_page.innerText = detect_page.value;
  271. maxOffset = Number(detect_page.value);
  272. GM_setValue('MaxDetectPage', maxOffset);
  273. };
  274. tag_merge.onclick = () => {
  275. tagMerge = tag_merge.checked;
  276. GM_setValue('TagMerge', tagMerge);
  277. };
  278. link_delete.onclick = () => { GM_setValue('NoJump', link_delete.checked); };
  279. close_comment.onclick = () => {
  280. closeComment();
  281. GM_setValue('CloseComment', close_comment.checked);
  282. };
  283. add_tag.onclick = () => {
  284. if (tag_name.value && tag_text.value && tag_reg.value) {
  285. if (!/[\n\r"'{}、]/.test(tag_reg.value)) {
  286. addTag({
  287. tag: tag_name.value,
  288. text: tag_text.value,
  289. reg: tag_reg.value,
  290. color: tag_color.value,
  291. hide: tag_hide.checked
  292. });
  293. tag_name.value = '';
  294. tag_text.value = '';
  295. tag_reg.value = '';
  296. tag_hide.checked = false;
  297. } else {
  298. alert(`标签规则不应该包含‘ " ' { } \\n \\r等字符`);
  299. }
  300. } else {
  301. alert('请将标签信息补充完整');
  302. }
  303. };
  304. add_comment.onclick = () => {
  305. if (comment_reg.value) {
  306. addKeyWord(comment_reg.value);
  307. comment_reg.value = '';
  308. } else {
  309. alert('请将关键词正则信息补充完整');
  310. }
  311. };
  312. function exportDataToClipboard() {
  313. let exportData = {};
  314. let key_storage = GM_listValues();
  315. key_storage.forEach(i => {
  316. exportData[i] = GM_getValue(i, null);
  317. });
  318. GM_setClipboard(JSON.stringify(exportData));
  319. alert('已导出标签数据到剪切板!');
  320. }
  321. function importDataToTemperMonkey() {
  322. if (import_area.value) {
  323. try {
  324. let importData = JSON.parse(import_area.value);
  325. Object.keys(importData).forEach(i => { GM_setValue(i, importData[i]); });
  326. }
  327. catch (e) {
  328. alert('导入配置出错,请检查配置项');
  329. return;
  330. }
  331. import_area.value = '';
  332. configInit();
  333. } else {
  334. alert('配置为空!请先在输入框粘贴配置并检查无误后点击【导入配置】');
  335. }
  336. }
  337. export_tag.onclick = exportDataToClipboard;
  338. import_tag.onclick = importDataToTemperMonkey;
  339. function addKeyWord(reg_text) {
  340. if (!keyword.includes(reg_text)) {
  341. let new_tag = insertTag(commentlist, reg_text);
  342. let keyword_index = Object.keys(temp_keyword).length;
  343. temp_keyword[keyword_index] = reg_text;
  344. keyword.push(reg_text);
  345. GM_setValue('Keyword', keyword);
  346. function deleteTag() {
  347. commentlist.removeChild(new_tag);
  348. delete temp_keyword[keyword_index];
  349. keyword = [];
  350. Object.keys(temp_keyword).map((key) => keyword.push(temp_keyword[key]));
  351. GM_setValue('Keyword', keyword);
  352. }
  353. ;
  354. new_tag.children[0].onclick = deleteTag;
  355. new_tag.children[1].ondblclick = () => {
  356. comment_reg.value = reg_text;
  357. deleteTag();
  358. };
  359. }
  360. }
  361. function addTag(tag_dic) {
  362. let new_tag = insertTag(taglist, tag_dic);
  363. let tag_index = Object.keys(tag).length;
  364. tag[tag_index] = tag_dic;
  365. tagList.push(tag, tag_index);
  366. function deleteTag() {
  367. taglist.removeChild(new_tag);
  368. delete tag[tag_index];
  369. let tag_temp = { ...tag };
  370. tag = {};
  371. Object.values(tag_temp).forEach((value, i) => { tag[i] = value; });
  372. tagList.pop(tag);
  373. }
  374. ;
  375. new_tag.children[0].onclick = deleteTag;
  376. new_tag.children[1].ondblclick = () => {
  377. tag_name.value = tag_dic.tag;
  378. tag_text.value = tag_dic.text;
  379. tag_reg.value = tag_dic.reg;
  380. tag_color.value = tag_dic.color;
  381. tag_hide.checked = tag_dic.hide;
  382. deleteTag();
  383. };
  384. }
  385. function insertTag(parentNode, tagInfo) {
  386. let title = '';
  387. let text = '';
  388. let color = '#1d1d1dbd';
  389. let border = 'none';
  390. if (typeof (tagInfo) === 'object') {
  391. if (childTagReg.test(tagInfo.reg)) {
  392. border = 'solid 1px #4fc3f7';
  393. title = `分类:${tagInfo.tag} (合并标签)&#10;子标签:${tagInfo.reg}&#10;隐藏评论:${tagInfo.hide}&#10;请放在待合并标签后`;
  394. } else {
  395. title = `分类:${tagInfo.tag}&#10;规则:${tagInfo.reg}&#10;隐藏评论:${tagInfo.hide}`;
  396. }
  397. text = tagInfo.text;
  398. color = tagInfo.color;
  399. } else {
  400. text = tagInfo;
  401. }
  402. let new_tag = elmGetter.create(`<div class="tags" style="color:${color}; width:${measureTextWidth("12px", text) + 8}px; border:${border}">
  403. <div class="delete-tag">x</div><p class="tag-info" title="${title}">${text}</p></div>`);
  404. parentNode.appendChild(new_tag);
  405. return new_tag;
  406. }
  407. function measureTextWidth(fontSize, text) {
  408. let fontFamily = 'PingFang SC, HarmonyOS_Regular, Helvetica Neue, Microsoft YaHei, sans-serif';
  409. let canvas = document.createElement('canvas');
  410. let context = canvas.getContext('2d');
  411. context.font = `${fontSize} ${fontFamily}`;
  412. let result = context.measureText(text);
  413. return result.width > 18 ? Math.ceil(result.width) : 18;
  414. }
  415. class Tag {
  416. constructor(tag_dic) {
  417. this.child_tag = [];
  418. this.tag_class = this.str2tagID(tag_dic.tag, 'class');
  419. this.text = tag_dic.text;
  420. this.tag_id = this.str2tagID(this.text, 'id');
  421. this.reg = Tag.multiReg(tag_dic.reg);
  422. this.hide = tag_dic.hide;
  423. this.tag_width = measureTextWidth('15px', tag_dic.tag);
  424. this.width = measureTextWidth('15px', this.text);
  425. this.list = new Set();
  426. this.nolist = new Set();
  427. this.childTag(tag_dic);
  428. this.tag_childNode = this.createElement(`<div class='tag-name ${this.tag_id}' style='color: ${tag_dic.color}; width:${this.width}px;'><div class='tag-font'>${this.text}</div></div>`);
  429. this.tagNode = this.createElement(`<div class='userTag ${this.tag_class}' title="${this.child_tag.join(' ')}"><div class='tag-class' style='border-color:#8da8e8;color:#5e80c4; width:${this.tag_width}px'><div class='tag-font'>${tag_dic.tag}</div></div><div class='tag-name ${this.tag_id}' style='color: ${tag_dic.color}; width:${this.width}px;'><div class='tag-font'>${this.text}</div></div></div>`);
  430. }
  431. createElement(dom_str) {
  432. return dom_str;
  433. }
  434. childTag(tag_dic) {
  435. if (childTagReg.test(tag_dic.reg)) {
  436. this.child_tag = tag_dic.reg.match(/(?<=\[)(.*?)(?=\])/g);
  437. let regs = [];
  438. tagList.list.filter(i => { if (this.child_tag.includes(i.text)) {
  439. return i;
  440. } }).forEach(e => {
  441. let new_reg = String(e.reg).match(/\((.*?)\){1,2}/g);
  442. if (new_reg) {
  443. regs.splice(0, 0, ...new_reg);
  444. } else {
  445. regs.push(`(?=.*(${String(e.reg).replace(/\//g, '')}))`);
  446. }
  447. });
  448. this.reg = new RegExp(`^${regs.join('')}.*`);
  449. }
  450. }
  451. str2tagID(str, state) {
  452. let hex = '';
  453. for (let i = 0; i < str.length; i++) {
  454. hex += str.charCodeAt(i).toString(32);
  455. }
  456. return 'tag-' + state + '-' + hex;
  457. }
  458. static multiReg(query) {
  459. let regStr = query.indexOf('&') !== -1 ? `^${query.split('&').map(q => `(?=.*${q})`).join('')}.*` : query;
  460. return new RegExp(regStr);
  461. }
  462. checkUserTag(pid, userNode, replyNode) {
  463. if (this.list.has(pid)) {
  464. this.hide && removeUserComment(replyNode);
  465. if (!userNode.querySelector('.' + this.tag_id)) {
  466. this.addUserTag(userNode);
  467. }
  468. }
  469. }
  470. detectNewUserTag(pid, userNode, replyNode, userInfo) {
  471. if (this.reg.test(userInfo)) {
  472. this.hide && removeUserComment(replyNode);
  473. if (!userNode.querySelector('.' + this.tag_id) && !this.list.has(pid) && !this.nolist.has(pid)) {
  474. this.addUserTag(userNode);
  475. }
  476. this.combineChildTag(pid, userNode);
  477. this.list.add(pid);
  478. } else {
  479. this.nolist.add(pid);
  480. }
  481. }
  482. addUserTag(userNode) {
  483. if (userNode.querySelector('.' + this.tag_class) && tagMerge) {
  484. userNode.querySelector('.' + this.tag_class).innerHTML += this.tag_childNode;
  485. } else {
  486. userNode.innerHTML += this.tagNode;
  487. }
  488. }
  489. combineChildTag(pid, userNode) {
  490. if (this.child_tag.length != 0) {
  491. tagList.list.forEach(e => {
  492. if (this.child_tag.includes(e.text)) {
  493. let child_tag = userNode.querySelector('.' + e.tag_id)?.parentNode;
  494. child_tag && userNode.removeChild(child_tag);
  495. e.list.delete(pid);
  496. e.nolist.add(pid);
  497. }
  498. });
  499. }
  500. }
  501. }
  502. class TagList {
  503. constructor() {
  504. this.list = [];
  505. }
  506. push(tag, index) {
  507. this.list.push(new Tag(tag[index]));
  508. GM_setValue('Tag', tag);
  509. }
  510. pop(tag) {
  511. this.list = [];
  512. Object.values(tag).map(i => this.list.push(new Tag(i)));
  513. GM_setValue('Tag', tag);
  514. }
  515. checkUserInfo(pid, userNode, commentNode) {
  516. this.list.map(i => i.checkUserTag(pid, userNode, commentNode));
  517. if (DetectOption.Medal && this.isChecked(pid)) {
  518. this.addMedalWall(pid, userNode);
  519. }
  520. }
  521. isChecked(pid) {
  522. let tag0 = this.list[0];
  523. return (tag0.list.has(pid) || tag0.nolist.has(pid)) ? true : false;
  524. }
  525. detectNewUserInfo(pid, userNode, commentNode) {
  526. let p = [];
  527. const p1 = (resolve) => { Requests(ApiUrl.blog + pid, (data) => { resolve(data); }, 'blog', pid); };
  528. const p2 = (resolve) => { Requests(ApiUrl.medal + pid, (data) => { resolve(data); }, 'medal'); };
  529. const p3 = (resolve) => { Requests(`${ApiUrl.concerns}${pid}&pn=1&ps=50`, (data) => { resolve(data); }, 'concerns'); };
  530. if (DetectOption.Concerns && DetectOption.Medal) {
  531. p = [p1, p2, p3];
  532. } else if (DetectOption.Concerns && !DetectOption.Medal) {
  533. p = [p1, p3];
  534. } else if (!DetectOption.Concerns && DetectOption.Medal) {
  535. p = [p1, p2];
  536. } else {
  537. p = [p1];
  538. }
  539. Promise.all(p.map(i => new Promise(i))).then((result) => {
  540. this.list.map(i => i.detectNewUserTag(pid, userNode, commentNode, result.join('')));
  541. if (DetectOption.Medal && MedalShow) {
  542. this.getMedalData(pid, userNode, result[1]);
  543. }
  544. }).catch(error => { console.log(error); });
  545. }
  546. getMedalData(pid, userNode, data) {
  547. let medal_list = JSON.parse(data).list;
  548. if (medal_list.length != 0) {
  549. let medalInfo = [];
  550. medal_list.forEach((e) => {
  551. medalInfo.push({
  552. targetName: e.target_name,
  553. link: e.link,
  554. medalName: e.medal_info.medal_name,
  555. level: e.medal_info.level,
  556. color: { start: '#' + e.medal_info.medal_color_start.toString(16).padStart(6, '0'),
  557. end: '#' + e.medal_info.medal_color_end.toString(16).padStart(6, '0'),
  558. border: '#' + e.medal_info.medal_color_border.toString(16).padStart(6, '0')
  559. }
  560. });
  561. });
  562. MedalDict[pid] = medalInfo;
  563. this.addMedalWall(pid, userNode);
  564. }
  565. }
  566. addMedalWall(pid, userNode) {
  567. if (userNode.querySelector('.medalTag')) {
  568. return;
  569. }
  570. RunOnce(() => {
  571. window.addEventListener('click', (event) => {
  572. let e = event || window.event;
  573. let targetClass = e.target?.classList;
  574. if (!targetClass.contains('medal-font')) {
  575. document.querySelectorAll('.medal-table').forEach(e => {
  576. e.style.display = 'none';
  577. });
  578. }
  579. });
  580. });
  581. let medal = MedalDict[pid];
  582. if (medal) {
  583. let medal_list = '';
  584. medal.forEach((e) => {
  585. medal_list += `
  586. <div class="fans-tag">
  587. <div class="tag-name" title="${e.targetName}" style="width:${measureTextWidth('15px', e.medalName)}px;background-image: linear-gradient(90deg, ${e.color.start}, ${e.color.end});">
  588. <div class="tag-font"><a href="${e.link}" style="color:#fff;">${e.medalName}</a></div></div>
  589. <div class="tag-class" style="color:${e.color.end};"><div class="tag-font">${e.level}</div></div>
  590. </div>`;
  591. });
  592. let medalInner = `
  593. <div class="medalTag">
  594. <div class='tag-name' style="color: #fff;width:28px;background-image: linear-gradient(90deg,#ffcaec,#a187ff);">
  595. <div class='tag-font medal-font'>勋章</div></div>
  596. <div class="medal-table">${medal_list}</div>
  597. </div>`;
  598. userNode.innerHTML += medalInner;
  599. let medalBtn = userNode.querySelector('.medalTag');
  600. let table = userNode.querySelector('.medal-table');
  601. medalBtn.onclick = () => {
  602. let disp = table.style.display;
  603. table.style.left = medalBtn.offsetLeft + 50 + 'px';
  604. table.style.display = disp === 'block' ? 'none' : 'block';
  605. };
  606. }
  607. }
  608. }
  609. let RunOnce = function FunctionRunOnlyOnce(func) {
  610. func.apply(arguments);
  611. RunOnce = () => { };
  612. };
  613. function Requests(requestUrl, func, state, pid) {
  614. fetch(requestUrl, {
  615. method: 'GET',
  616. credentials: 'include',
  617. headers: {
  618. 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, Concerns Gecko) Chrome/104.0.0.0 Safari/537.36'
  619. }
  620. })
  621. .then(response => response.json())
  622. .then(async (data) => {
  623. let data_list = [];
  624. let data_json = [];
  625. let offset = '';
  626. let offsetNum = 1;
  627. let totalPage = 1;
  628. switch (state) {
  629. case 'blog':
  630. data_json = data?.data?.items || [];
  631. if (data_json.length === 0) {
  632. return;
  633. }
  634. offset = data?.data.offset || '';
  635. do {
  636. if (offsetNum > 1) {
  637. let nextPage = await fetch(`https://api.bilibili.com/x/polymer/web-dynamic/v1/feed/space?offset=${offset}&host_mid=${pid}`, {
  638. credentials: 'include'
  639. });
  640. let nextPageData = await nextPage.json();
  641. offset = nextPageData?.data.offset || '';
  642. data_json = nextPageData?.data?.items || [];
  643. }
  644. data_json.map((blog) => {
  645. let zhuanf_text = blog?.modules?.module_dynamic?.desc?.text || '';
  646. let origin_text = blog?.orig?.modules?.module_dynamic?.desc?.text || '';
  647. if (zhuanf_text) {
  648. if (!DetectOption.Repost) {
  649. data_list.push({ 'text': zhuanf_text });
  650. } else {
  651. data_list.push({ 'text': zhuanf_text, 'orig_text': origin_text });
  652. }
  653. }
  654. });
  655. offsetNum += 1;
  656. } while (offset && offsetNum <= maxOffset);
  657. data = JSON.stringify(data_list).replace(/\[.{1,12}\]/g, '');
  658. break;
  659. case 'concerns':
  660. if (data.code !== 0) {
  661. break;
  662. }
  663. totalPage = data?.data.total / 50;
  664. do {
  665. if (offsetNum > 1) {
  666. let nextPage = await fetch(`https://api.bilibili.com/x/relation/followings?vmid=${pid}&pn=${offsetNum}&ps=50&order=desc`);
  667. data = await nextPage.json();
  668. }
  669. data_json = data?.data?.list || [];
  670. data_json.length > 0 && data_json.map((i) => { data_list.push(i?.uname); });
  671. offsetNum += 1;
  672. } while (offsetNum <= totalPage && offsetNum <= maxOffset);
  673. data = data_list.join(' ');
  674. break;
  675. default:
  676. data = JSON.stringify(data?.data || []);
  677. }
  678. func(data);
  679. })
  680. .catch(error => console.log(error));
  681. }
  682. function getUserID(userNode) {
  683. if (isNew) {
  684. return userNode?.querySelector('.user-name,.sub-user-name')?.dataset?.userId || userNode?.querySelector('.name')?.dataset?.usercardMid;
  685. } else {
  686. return userNode?.querySelector('.name')?.dataset?.usercardMid || userNode.children[0].href.replace(/[^\d]/g, "");
  687. }
  688. }
  689. function deleteJumpLink(comment) {
  690. if (link_delete.checked) {
  691. let jump_word = comment?.querySelectorAll('.jump-link.search-word');
  692. if (jump_word) {
  693. for (let link of jump_word) {
  694. link.outerHTML = link.innerText;
  695. }
  696. }
  697. }
  698. }
  699. function closeComment() {
  700. if (close_comment.checked) {
  701. elmGetter.each('div.bottom-page.paging-box-big.center', document, (reply) => {
  702. let close_btn = elmGetter.create('<div class="close-btn"><a>收起评论</a></div>');
  703. reply.parentNode.insertBefore(close_btn, reply);
  704. close_btn.onclick = () => {
  705. let comment_btn = reply?.parentNode?.parentNode?.parentNode?.parentNode?.querySelector('.bili-dyn-action.comment.active');
  706. comment_btn.click();
  707. comment_btn.scrollIntoView({ behavior: 'instant', block: 'center' });
  708. };
  709. if (!close_comment.checked) {
  710. return false;
  711. }
  712. });
  713. }
  714. }
  715. function dynamicBatchLike() {
  716. let likebtns = document.querySelectorAll('div.like:not(.active)');
  717. let likebtnsiter = likebtns.entries();
  718. let clicktimer = setInterval(() => {
  719. let nextele = likebtnsiter.next();
  720. if (nextele.done) {
  721. clearInterval(clicktimer);
  722. return;
  723. }
  724. nextele.value[1].click();
  725. }, 600);
  726. }
  727. function mainPageBlock() {
  728. if (keyword.length === 0) {
  729. return;
  730. }
  731. elmGetter.each('.bili-video-card.is-rcmd', document, (reply) => {
  732. let author = reply?.querySelector('.bili-video-card__info--owner');
  733. let authorName = author.innerText;
  734. let authorHref = author?.href?.match(/(?<=https:\/\/space.bilibili.com\/)\d+$/);
  735. let authorID = authorHref ? authorHref[0] : '';
  736. let title = (reply?.querySelector('.bili-video-card__info--tit')).title;
  737. for (let reg of keyword) {
  738. if (RegExp(reg).test(title)) {
  739. reply?.parentNode?.removeChild(reply);
  740. console.log(`已屏蔽视频 %c${title} %c关键词:%c${reg}`, 'color: #67d0ff;', 'color: #30aa35;', 'color: #f44336; font-weight: bolder');
  741. return;
  742. }
  743. }
  744. if (blackList.has(authorID) || blackList.has(authorName) || keyword.includes(authorName)) {
  745. reply?.parentNode?.removeChild(reply);
  746. console.log(`已屏蔽视频 %c${title} %c作者ID: %c${authorID}`, 'color: #67d0ff;', 'color: #30aa35;', 'color: #f44336; font-weight: bolder');
  747. }
  748. if (!BlockOption.Video) {
  749. return false;
  750. }
  751. });
  752. }
  753. function dynamicPageBlock() {
  754. elmGetter.each('.bili-dyn-list__item', document, (reply) => {
  755. let content = reply?.querySelector('.bili-rich-text__content')?.innerText || '';
  756. let title = reply?.querySelector('.bili-dyn-card-video__title.bili-ellipsis')?.innerText || '';
  757. let desc = reply?.querySelector('.bili-dyn-card-video__desc.bili-ellipsis')?.innerText || '';
  758. let all_content = content + title + desc;
  759. for (let reg of keyword) {
  760. if (RegExp(reg).test(all_content)) {
  761. reply?.parentNode?.removeChild(reply);
  762. console.log(`已屏蔽动态 %c${content} %c关键词:%c${reg}`, 'color: #67d0ff;', 'color: #30aa35;', 'color: #f44336; font-weight: bolder');
  763. return;
  764. }
  765. }
  766. if (!BlockOption.Dynamic) {
  767. return false;
  768. }
  769. });
  770. }
  771. function userCommentBlock(userID, reply, comment) {
  772. if (!BlockOption.Comment) {
  773. return;
  774. }
  775. let commentText = comment?.innerText;
  776. if (blackList.has(userID)) {
  777. removeUserComment(reply);
  778. console.log(`已屏蔽评论 %c${commentText} %c用户ID: %c${userID}`, 'color: #67d0ff;', 'color: #30aa35;', 'color: #f44336; font-weight: bolder');
  779. return true;
  780. }
  781. if (keyword.length > 0 && commentText) {
  782. for (let reg of keyword) {
  783. if (RegExp(reg).test(commentText)) {
  784. removeUserComment(reply);
  785. console.log(`已屏蔽评论 %c${commentText} %c关键词:%c${reg}`, 'color: #67d0ff;', 'color: #30aa35;', 'color: #f44336; font-weight: bolder');
  786. return true;
  787. }
  788. }
  789. }
  790. if (BlockOption.User) {
  791. let blockBtn = elmGetter.create('<span class="block-btn btn-hover">屏蔽</span>');
  792. let replyInfo = reply?.querySelector('.reply-info,.sub-reply-info,.info');
  793. blockBtn.onclick = () => {
  794. blackList.add(userID);
  795. GM_setValue('BlackList', Array.from(blackList));
  796. console.log(`已屏蔽用户: %cID %c${userID}`, 'color: #f44336; font-weight: bolder', 'color: #f44336; font-weight: bolder');
  797. replyInfo.removeChild(blockBtn);
  798. removeUserComment(reply);
  799. };
  800. replyInfo.insertBefore(blockBtn, replyInfo.childNodes[replyInfo.childNodes.length - 1]);
  801. }
  802. return false;
  803. }
  804. function removeUserComment(reply) {
  805. if (reply.getAttribute('class') === 'content-warp') {
  806. reply.parentNode?.parentNode?.parentNode?.removeChild(reply.parentNode?.parentNode);
  807. } else {
  808. reply.parentNode?.removeChild(reply);
  809. }
  810. }
  811. function detectUserTag(userID, userInfo, reply) {
  812. function detect(userID, userInfo, reply) {
  813. if (tagList.list.length == 0) {
  814. return;
  815. }
  816. tagList.checkUserInfo(userID, userInfo, reply);
  817. if (tagList.isChecked(userID)) {
  818. return;
  819. }
  820. tagList.detectNewUserInfo(userID, userInfo, reply);
  821. }
  822. if (!AutoDetect && !tagList.isChecked(userID)) {
  823. let searchBtn = elmGetter.create('<p class="search-btn">检测</p>');
  824. searchBtn.onclick = () => {
  825. detect(userID, userInfo, reply);
  826. userInfo.removeChild(searchBtn);
  827. };
  828. userInfo.insertBefore(searchBtn, userInfo.childNodes[2]);
  829. return;
  830. }
  831. detect(userID, userInfo, reply);
  832. }
  833. function tagInsertObserver() {
  834. elmGetter.each('.sub-reply-item,.content-warp,.list-item.reply-wrap,.reply-item.reply-wrap', document, (reply) => {
  835. let userInfo = reply?.querySelector('div.user-info:not(.section),.sub-user-info,div.user');
  836. let comment = reply?.querySelector('span.reply-content,p.text,span.text-con');
  837. let userID = getUserID(userInfo);
  838. if (userID) {
  839. if (userCommentBlock(userID, reply, comment)) {
  840. return;
  841. }
  842. deleteJumpLink(comment);
  843. detectUserTag(userID, userInfo, reply);
  844. }
  845. });
  846. }
  847. function hideOption(option) {
  848. if (option.Medal) {
  849. MedalShow = false;
  850. }
  851. if (option.Comment) {
  852. close_comment.checked = false;
  853. close_comment.parentNode.style.display = 'none';
  854. }
  855. if (option.Like) {
  856. dynamic_btn.checked = false;
  857. dynamic_btn.parentNode.style.display = 'none';
  858. script_like.style.display = 'none';
  859. }
  860. if (option.Link) {
  861. link_delete.checked = false;
  862. link_delete.parentNode.style.display = 'none';
  863. }
  864. }
  865. function configInit() {
  866. tagClass_hide.checked = GM_getValue('TagClassHide', false);
  867. Style_tagClassHide.innerHTML = tagClassHide[String(tagClass_hide.checked)];
  868. AutoDetect = GM_getValue('AutoDetect', false);
  869. detection_mode.checked = AutoDetect;
  870. DetectOption = GM_getValue('DetectOption', { Repost: false, Concerns: false, Medal: false });
  871. ({ Repost: detect_repost.checked, Concerns: detect_concerns.checked, Medal: detect_medal.checked } = DetectOption);
  872. maxOffset = GM_getValue('MaxDetectPage', 1);
  873. detect_page.value = maxOffset.toString();
  874. offset_page.innerText = maxOffset.toString();
  875. BlockOption = GM_getValue('BlockOption', { User: false, Comment: false, Dynamic: false, Video: false });
  876. ({ User: block_user.checked, Comment: block_comment.checked, Dynamic: block_dynamic.checked, Video: block_video.checked } = BlockOption);
  877. blackList = new Set(GM_getValue('BlackList', []));
  878. tagMerge = GM_getValue('TagMerge', false);
  879. tag_merge.checked = tagMerge;
  880. link_delete.checked = GM_getValue('NoJump', false);
  881. close_comment.checked = GM_getValue('CloseComment', false);
  882. dynamic_btn.checked = GM_getValue('DynamicLike', false);
  883. script_like.style.display = dynamic_btn.checked ? 'block' : 'none';
  884. GM_getValue('Keyword', []).map((key) => addKeyWord(key));
  885. Object.values(GM_getValue('Tag', {})).map((i) => { addTag(i); });
  886. }
  887. const tagList = new TagList();
  888. const temp_keyword = {};
  889. const MedalDict = {};
  890. let tag = {};
  891. let keyword = [];
  892. let tagMerge = false;
  893. let MedalShow = false;
  894. let AutoDetect = false;
  895. let DetectOption = { Repost: false, Concerns: false, Medal: false };
  896. let BlockOption = { User: false, Comment: false, Dynamic: false, Video: false };
  897. let blackList = new Set();
  898. let maxOffset = 1;
  899. configInit();
  900. switch (Page) {
  901. case 'Main':
  902. mainPageBlock();
  903. hideOption({ Medal: true, Comment: true, Like: true, Link: true });
  904. break;
  905. case 'Dynamic':
  906. dynamicPageBlock();
  907. tagInsertObserver();
  908. closeComment();
  909. hideOption({ Medal: true, Comment: false, Like: false, Link: true });
  910. break;
  911. case 'Video':
  912. tagInsertObserver();
  913. hideOption({ Medal: false, Comment: true, Like: true, Link: false });
  914. break;
  915. default:
  916. tagInsertObserver();
  917. hideOption({ Medal: true, Comment: true, Like: true, Link: false });
  918. }
  919. })();
  920.  

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址