哔哩哔哩用户标记器

B站评论区点击查询, 自动标记 tag,依据是动态里是否有对应的 keyword

  1.  
  2. // ==UserScript==
  3. // @name 哔哩哔哩用户标记器
  4. // @namespace www.cber.ltd
  5. // @version 6.666666-bugfix
  6. // @description B站评论区点击查询, 自动标记 tag,依据是动态里是否有对应的 keyword
  7. // @author xulaupuz, xcl
  8. // @match https://www.bilibili.com/video/*
  9. // @match https://www.bilibili.com/bangumi/*
  10. // @match https://space.bilibili.com/*/dynamic
  11. // @match https://t.bilibili.com/*
  12. // @icon https://static.hdslb.com/images/favicon.ico
  13. // @connect bilibili.com
  14. // @grant GM_xmlhttpRequest
  15. // @license MIT
  16. // @run-at document-end
  17. // ==/UserScript==
  18.  
  19. // fork from: https://gf.qytechs.cn/zh-CN/scripts/450720 感谢原作者的贡献
  20. (function () {
  21. 'use strict';
  22. const unknown = new Set()
  23. const targetUserMap = new Map();
  24. const otherUserSet = new Set();
  25.  
  26. // 使用指南,只要匹配到 keywords 里的任意一项,即标记为对应的 tag
  27. const keywordsTagGroup = [
  28. {
  29. keywords: ['原神'],
  30. tag: '原神玩家',
  31. },
  32. {
  33. keywords: ['王者荣耀'],
  34. tag: '农药玩家',
  35. },
  36. {
  37. keywords: ['明日方舟', '舟游'],
  38. tag: '舟游玩家',
  39. }
  40. ];
  41. // 不满足关键字匹配的用户的 tag
  42. const otherUserTag = '路人';
  43. // 按钮文字
  44. const btnText = '查询';
  45. // 是否 hover 才显示查询按钮
  46. const hoverToDisplayButton = false;
  47.  
  48. const blog = 'https://api.bilibili.com/x/polymer/web-dynamic/v1/feed/space?&host_mid='
  49. const is_new = document.getElementsByClassName('item goback').length != 0 // 检测是不是新版
  50.  
  51. const get_pid = (c) => {
  52. if (is_new) {
  53. return c.dataset['userId']
  54. } else {
  55. return c.children[0]['href'].replace(/[^\d]/g, "")
  56. }
  57. }
  58.  
  59. const get_comment_list = () => {
  60. if (is_new) {
  61. let lst = new Set()
  62. for (let c of document.getElementsByClassName('user-name')) {
  63. lst.add(c)
  64. }
  65. for (let c of document.getElementsByClassName('sub-user-name')) {
  66. lst.add(c)
  67. }
  68. return lst
  69. } else {
  70. return document.getElementsByClassName('user')
  71. }
  72. }
  73.  
  74. console.log('正常加载', 'isNew', is_new)
  75.  
  76. const getTagSpan = (text) => {
  77. return `&nbsp;<span style="background-color: #6bc047; color: #fff; border-radius:7px; font-size: 10px; padding: 0 4px;">${text}</span>`
  78. }
  79.  
  80. const appendTags = (elem, tags = []) => {
  81. if (!elem) return;
  82. for (const tag of tags) {
  83. elem.innerHTML += getTagSpan(tag);
  84. }
  85. }
  86.  
  87. const fetchDataAndSetTag = (c, pid) => {
  88. let blogUrl = blog + pid;
  89. if (targetUserMap.has(pid)) {
  90. const tags = targetUserMap.get(pid).tags ?? [];
  91. console.log('Reading Memory Cache, Tags:', tags);
  92. appendTags(c, tags)
  93. return;
  94. }
  95. GM_xmlhttpRequest({
  96. method: "get",
  97. url: blogUrl,
  98. data: '',
  99. headers: {
  100. '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'
  101. },
  102. onload: function (res) {
  103. if (res.status === 200) {
  104. let st = JSON.stringify(JSON.parse(res.response).data)
  105. unknown.delete(pid)
  106. const tags = [];
  107. keywordsTagGroup.forEach(({ keywords, tag }) => {
  108. for (const keyword of keywords) {
  109. if (st.includes(keyword)) {
  110. tags.push(tag);
  111. break;
  112. }
  113. }
  114. });
  115.  
  116. if (tags.length) {
  117. appendTags(c, tags);
  118. if (!targetUserMap.has(pid)) {
  119. targetUserMap.set(pid, {
  120. tags,
  121. })
  122. }
  123. } else {
  124. appendTags(c, [otherUserTag])
  125. otherUserSet.add(pid);
  126. }
  127. } else {
  128. console.log('失败')
  129. console.log(res)
  130. }
  131. },
  132. });
  133. }
  134.  
  135. const appendButton = (elem, pid) => {
  136. if (!elem || !pid) {
  137. return;
  138. }
  139. const btn = document.createElement('span');
  140. btn.style.cssText = "outline: none; border: none; background-color: skyblue; color: #fff; border-radius:7px; font-size: 10px; padding: 0 4px; margin-left: 2px;";
  141. btn.onmouseover = () => {
  142. btn.style.backgroundColor = '#6bc047';
  143. }
  144. btn.onmouseleave = () => {
  145. btn.style.backgroundColor = 'skyblue';
  146. }
  147. btn.onclick = (e) => {
  148. e.stopPropagation();
  149. btn.style.display = "none";
  150. btn.remove();
  151. fetchDataAndSetTag(elem, pid);
  152. elem.onmouseover = () => { };
  153. elem.onmouseleave = () => { };
  154. }
  155. if (hoverToDisplayButton) {
  156. btn.style.display = "none";
  157. elem.onmouseover = () => {
  158. btn.style.display = "inline";
  159. }
  160. elem.onmouseleave = () => {
  161. btn.style.display = "none"
  162. }
  163. }
  164. btn.textContent = btnText;
  165. elem.appendChild(btn);
  166. }
  167.  
  168. const modifyContent = () => {
  169. let commentList = get_comment_list()
  170. if (commentList.length != 0) {
  171. commentList.forEach(c => {
  172. let pid = get_pid(c)
  173. if (c.innerHTML.includes('span')) {
  174. return;
  175. }
  176. appendButton(c, pid);
  177. });
  178. }
  179. }
  180.  
  181.  
  182. let monitor = setInterval(() => {
  183. modifyContent();
  184. }, 4000);
  185.  
  186. setTimeout(() => {
  187. modifyContent();
  188. }, 0);
  189. })();

QingJ © 2025

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