仙家军成分查询Helper

用于标记仙家军和动态转发仙以及使用仙话术的b站用户。可能存在误伤,请注意辨别。标记仅供参考,不建议直接作为“依据”使用。

目前为 2024-01-23 提交的版本。查看 最新版本

  1. // ==UserScript==
  2. // @name 仙家军成分查询Helper
  3. // @namespace www.bilibili.com
  4. // @version 1.6.5.6
  5. // @description 用于标记仙家军和动态转发仙以及使用仙话术的b站用户。可能存在误伤,请注意辨别。标记仅供参考,不建议直接作为“依据”使用。
  6. // @author Darknights
  7. // @match https://*.bilibili.com/*
  8. // @match https://*.biligame.com/detail/?id=*
  9. // @exclude https://message.bilibili.com/*
  10. // @exclude https://manga.bilibili.com/*
  11. // @exclude https://www.bilibili.com/correspond/*
  12. // @exclude https://www.bilibili.com/page-proxy/*
  13. // @exclude https://live.bilibili.com/*
  14. // @exclude https://search.bilibili.com/*
  15. // @icon https://static.hdslb.com/images/favicon.ico
  16. // @connect bilibili.com
  17. // @connect biligame.com
  18. // @connect fastly.jsdelivr.net
  19. // @connect raw.githubusercontent.com
  20. // @grant GM_xmlhttpRequest
  21. // @grant GM_info
  22. // @grant GM_registerMenuCommand
  23. // @grant GM_unregisterMenuCommand
  24. // @grant GM_setValue
  25. // @grant GM_getValue
  26. // @license MIT
  27. // @run-at document-end
  28. // ==/UserScript==
  29.  
  30. 'use strict';
  31.  
  32. // 以下为网络名单
  33. let xianList = [];
  34. let xianFavList = [];
  35. let wordLists = [];
  36. let xianLeakList = [];
  37. let ignoreList = [];
  38. let aidList = [];
  39.  
  40. //以下为本地名单,请在脚本编辑器-存储区自行添加,关键词列表均为字符串形式,注意要转义反斜杠
  41. // 大部分为仙,少数可能有误判
  42. const localXianList = GM_getValue("localXianList", []);
  43.  
  44. // 转发者常见仙的,包含且不限于一些up主/被仙缠上的人等等
  45. const localXianFavList = GM_getValue("localXianFavList", []);
  46.  
  47. // 无视官号和无关号的动态,防止匹配到关键词浪费标签
  48. const localIgnoreList = GM_getValue("localIgnoreList", []);
  49.  
  50. // 仙可能会用的词汇
  51. const localXianWordList = GM_getValue("localXianWordList", []);
  52.  
  53. // 被开盒者隐私信息,需要特殊处理故与关键词列表区分
  54. const localXianLeakList = GM_getValue("localXianLeakList", []);
  55.  
  56. // 辅助,因为有些正则匹配返回值为空
  57. const localAidList = GM_getValue("localAidList", []);
  58.  
  59. const recordMap = new Map();
  60. const uidSet = new Set();
  61.  
  62. const xianTag = ["仙", "#11DD77"];
  63. const localXianTag = ["仙(本地)", "#11DD77"];
  64.  
  65. const xianRepostTag = ["转发仙:", "#1E971E"];
  66. const localXianRepostTag = ["转发仙(本地):", "#1E971E"];
  67.  
  68. const favRepostTag = ["转发:", "#2C9EFF"];
  69. const localFavRepostTag = ["转发(本地):", "#2C9EFF"];
  70.  
  71. const wordTags = [["命中:", "#04AEAB"], ["命中Ⅰ:", "#04AEAB"], ["命中Ⅱ:", "#04AEAB"], ["命中Ⅲ:", "#04AEAB"]];
  72. const localXianWordTag = ["命中(本地):", "#04AEAB"];
  73.  
  74. const captchaTag = ["出错,点此消除", "#FF3434"];
  75.  
  76. const refreshTag = ["然后点此🔄", "#FF7B00"];
  77.  
  78. const newVerTag = ["*已有新版本*", "#990CD0"];
  79.  
  80. const BLOG_URL = "https://api.bilibili.com/x/polymer/web-dynamic/v1/feed/space?&host_mid=";
  81.  
  82. const BILI_URL = "https://t.bilibili.com/";
  83.  
  84. const SCRIPT_URL = "https://gf.qytechs.cn/zh-CN/scripts/467197";
  85.  
  86. const PRIVATE_TIPS = "*已隐藏,注意可能是无关话题被匹配*";
  87.  
  88. const XIAN_MATCH_TIPS = "*仙或其拥护者,可能存在误判,请注意辨别*";
  89.  
  90. const NEW_VERSION_TIPS = "*点击跳转安装页,更多功能尽在新版本*";
  91.  
  92. const BIG_V_CLASS = ".bili-avatar-icon-business,.bili-avatar-icon-personal,.bili-avatar-icon--business,.bili-avatar-icon--personal,.local-3,.local-4";
  93.  
  94. const CheckType = {
  95. Profile: "PROFILE",
  96. Comment: "COMMENT",
  97. At: "AT",
  98. Reference: "REFER",
  99. Repo: "REPO",
  100. Follow: "FOLLOW",
  101. GameComment: "GAME",
  102. Like: "LIKE"
  103. }
  104.  
  105. const urlSourceDic = {
  106. githubusercontent: "https://raw.githubusercontent.com/Darknights1750/XianLists/main/xianLists.json",
  107. jsdelivr: "https://fastly.jsdelivr.net/gh/Darknights1750/XianLists@main/xianLists.json"
  108. }
  109.  
  110. const statusDic = {
  111. 0: "脚本已暂停⚠️",
  112. 1: "脚本运行中✅"
  113. }
  114.  
  115. let updateTime;
  116. let onlineVersion;
  117. let isLatestVersion = false;
  118. let clearAllMenuId;
  119. let urlSourceMenuId;
  120. let statusMenuId;
  121.  
  122.  
  123. const commandClearTags = function (element) {
  124. Array.prototype.slice.call(element.getElementsByClassName('xian')).forEach(item => item.remove());
  125. Array.prototype.slice.call(element.getElementsByClassName('xian-fail')).forEach(item => item.remove());
  126. }
  127.  
  128. const commandUrlSource = function () {
  129. GM_unregisterMenuCommand(urlSourceMenuId);
  130. if ("jsdelivr" === GM_getValue("urlSource", "jsdelivr")) {
  131. GM_setValue("urlSource", "githubusercontent");
  132. } else {
  133. GM_setValue("urlSource", "jsdelivr");
  134. }
  135. urlSourceMenuId = GM_registerMenuCommand("切换数据源(刷新生效)|当前" + GM_getValue("urlSource", "jsdelivr"), commandUrlSource);
  136. }
  137.  
  138. const commandStatus = function () {
  139. GM_unregisterMenuCommand(statusMenuId);
  140. if (1 === GM_getValue("status", 1)) {
  141. GM_setValue("status", 0);
  142. } else {
  143. GM_setValue("status", 1);
  144. }
  145. statusMenuId = GM_registerMenuCommand("暂停/启动脚本|当前" + statusDic[GM_getValue("status", 1)], commandStatus);
  146. }
  147.  
  148. // 初始化存储区、菜单选项
  149. const initSettings = function () {
  150. // 0:不开启,1:开启
  151. if (null === GM_getValue("timeInterval", null)) GM_setValue("timeInterval", 2500); // 标签处理间隔时间 单位:ms
  152. if (null === GM_getValue("testLog", null)) GM_setValue("testLog", 0); // 是否开启调试日志
  153. if (null === GM_getValue("previewLength", null)) GM_setValue("previewLength", 60); // 文本预览长度
  154. if (null === GM_getValue("usingCheckProfile", null)) GM_setValue("usingCheckProfile", 1); // 是否监测个人主页UID
  155. if (null === GM_getValue("usingCheckComments", null)) GM_setValue("usingCheckComments", 1); // 是否监测评论区
  156. if (null === GM_getValue("usingCheckRepos", null)) GM_setValue("usingCheckRepos", 1); // 是否监测转发区
  157. if (null === GM_getValue("usingCheckReferences", null)) GM_setValue("usingCheckReferences", 1); // 是否监测动态被转发者
  158. if (null === GM_getValue("usingCheckAts", null)) GM_setValue("usingCheckAts", 1); // 是否监测@他人
  159. if (null === GM_getValue("usingCheckFollows", null)) GM_setValue("usingCheckFollows", 1); // 是否监测关注/粉丝列表
  160. if (null === GM_getValue("usingCheckGameComments", null)) GM_setValue("usingCheckGameComments", 1); // 是否监测游戏评价区
  161. if (null === GM_getValue("usingCheckLikes", null)) GM_setValue("usingCheckLikes", 1); // 是否监测动态转赞区
  162. if (null === GM_getValue("localXianList", null)) GM_setValue("localXianList", []);
  163. if (null === GM_getValue("localXianFavList", null)) GM_setValue("localXianFavList", []);
  164. if (null === GM_getValue("localIgnoreList", null)) GM_setValue("localIgnoreList", []);
  165. if (null === GM_getValue("localXianWordList", null)) GM_setValue("localXianWordList", []);
  166. if (null === GM_getValue("localXianLeakList", null)) GM_setValue("localXianLeakList", []);
  167. if (null === GM_getValue("localAidList", null)) GM_setValue("localAidList", []);
  168. if (null === GM_getValue("captchaUrl", null)) GM_setValue("captchaUrl", "https://space.bilibili.com/208259/dynamic"); // 输验证码跳转的个人主页,默认叔叔
  169. if (null === GM_getValue("fanLimit", null)) GM_setValue("fanLimit", 250000); // up主粉丝阈值,超过将不再进行匹配(对<仙>无效)
  170. clearAllMenuId = GM_registerMenuCommand("清空本页所有标签", commandClearTags(document));
  171. urlSourceMenuId = GM_registerMenuCommand("切换数据源(刷新生效)|当前" + GM_getValue("urlSource", "jsdelivr"), commandUrlSource);
  172. statusMenuId = GM_registerMenuCommand("暂停/启动脚本|当前" + statusDic[GM_getValue("status", 1)], commandStatus);
  173. }
  174.  
  175.  
  176. const log = function (message) {
  177. return GM_getValue("testLog", 0) ? console.log(message) : null;
  178. };
  179.  
  180. const spawnHtml = function (data, text) {
  181. return `<a class="xian" style='color: ${data[1]} !important' title='${text}' target='_blank' onclick="event.stopPropagation()">&lt;${data[0]}&gt;</a>`;
  182. }
  183.  
  184. const spawnCaptchaHtml = function (data) {
  185. return `<a class="xian-fail" style='color: ${data[1]} !important' href=${GM_getValue("captchaUrl", "https://space.bilibili.com/208259/dynamic")} target='_blank' onclick="event.stopPropagation()">&lt;${data[0]}&gt;</a>`;
  186. }
  187.  
  188. const spawnRefreshHtml = function (data) {
  189. return `<a class="xian-fail" style='color: ${data[1]} !important' target='_blank' onclick="event.stopPropagation();refreshTags();">&lt;${data[0]}&gt;</a>`;
  190. }
  191.  
  192. const spawnHtmlWithRef = function (data, word, link, text) {
  193. return `<a class="xian" style='color: ${data[1]} !important' href='${link}' title='${text}' target='_blank' onclick="event.stopPropagation()">&lt;${data[0]}${word}&gt;</a>`;
  194. }
  195.  
  196. // 检测是不是新版
  197. const isNew = function () {
  198. if (location.host === 'space.bilibili.com') {
  199. return true;
  200. }
  201. if (document.getElementsByClassName('item goback').length > 0) {
  202. return true;
  203. }
  204. if (document.getElementsByClassName('app-v1').length > 0) {
  205. return true;
  206. }
  207. if (document.getElementsByClassName('opus-detail').length > 0) {
  208. return true;
  209. }
  210. if (document.getElementsByClassName('bgc').length > 0) {
  211. return true;
  212. }
  213. return false;
  214. };
  215.  
  216. const sleep = (delay) => new Promise((resolve) => setTimeout(resolve, delay));
  217.  
  218. const getXianListOnline = function () {
  219. return new Promise(resolve => {
  220. GM_xmlhttpRequest({
  221. method: "GET",
  222. url: urlSourceDic[GM_getValue("urlSource", "jsdelivr")],
  223. data: '',
  224. headers: {
  225. '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'
  226. },
  227. onload: res => {
  228. if (res.status === 200) {
  229. resolve(JSON.parse(res.responseText));
  230. } else {
  231. resolve(JSON.parse('{"xianList":[],"xianFavList":[],"xianWordList":[]}'));
  232. }
  233. }
  234. });
  235. });
  236. }
  237.  
  238. const createScriptFun = function () {
  239. let refreshTagsScript = document.createElement('script');
  240. refreshTagsScript.type = "text/javascript";
  241. refreshTagsScript.innerText = `const refreshTags=function(){Array.prototype.slice.call(document.getElementsByClassName('xian-fail')).forEach(item=>item.remove());}`;
  242. document.head.appendChild(refreshTagsScript);
  243. }
  244.  
  245. const compareVersions = function (curVer, netVer) {
  246. var curArr = curVer.split('.');
  247. var netArr = netVer.split('.');
  248. for (var i = 0; i < Math.max(curArr.length, netArr.length); i++) {
  249. var curNum = parseInt(curArr[i] || 0);
  250. var netNum = parseInt(netArr[i] || 0);
  251. if (curNum < netNum) {
  252. return 1;
  253. }
  254. if (curNum > netNum + 10 || !/^\d+$/.test(curNum) || !/^\d+$/.test(netNum)) {
  255. return 2;
  256. }
  257. if (curNum > netNum) {
  258. return -1;
  259. }
  260. }
  261. return 0;
  262. }
  263.  
  264. const fillLists = async function () {
  265. let json = await getXianListOnline();
  266. xianList = json.xianList;
  267. xianFavList = json.xianFavList;
  268. ignoreList = [...localIgnoreList, ...json.ignoreList];
  269. wordLists = [
  270. json.wordLv1List.map((item) => new RegExp(item)),
  271. json.wordLv2List.map((item) => new RegExp(item)),
  272. json.wordLv3List.map((item) => new RegExp(item))
  273. ];
  274. xianLeakList = json.xianLeakList.map((item) => new RegExp(item));
  275. aidList = json.aidList.map((item) => new RegExp(item));
  276. aidList = [...aidList, ...localAidList];
  277. updateTime = json.updateTime;
  278. onlineVersion = json.version;
  279. }
  280.  
  281. const runHelper = function () {
  282.  
  283. /* Functions */
  284. const isBlank = function (str) {
  285. if (!str || /^\s*$/.test(str)) return true;
  286. return false;
  287. }
  288.  
  289. //忽略认证用户和大up主,但不忽略名单里的。html方法只能找到主页、评论和关注的
  290. const isBigUpByHtml = function (checkType, uid) {
  291. if (xianList.indexOf(uid) > -1) return false;
  292. if (checkType === CheckType.Profile) {
  293. const dynAvater = document.querySelector(`.bili-dyn-item__avatar`);
  294. if (dynAvater) {
  295. const bigV = dynAvater.querySelector(BIG_V_CLASS);
  296. if (bigV) return true;
  297. } else {
  298. const bigV = document.querySelector(`.h-user`).querySelector(BIG_V_CLASS);
  299. if (bigV) return true;
  300. }
  301. const fanCount = document.getElementById("n-fs");
  302. if (fanCount) {
  303. const count = Number(fanCount.computedName?.replace(/\,/g, ""));
  304. return (count ?? 0) >= GM_getValue("fanLimit", 250000);
  305. }
  306. }
  307. if (checkType === CheckType.Comment) {
  308. const bigVNew = document.querySelector(`[data-user-id="${uid}"]`)?.querySelector(BIG_V_CLASS);
  309. const bigVOld = document.querySelector(`[data-usercard-mid="${uid}"]`)?.querySelector(BIG_V_CLASS);
  310. if (bigVNew || bigVOld) return true;
  311. }
  312. if (checkType === CheckType.Follow) {
  313. const bigV = document.querySelector(`[href="//space.bilibili.com/${uid}"]`)?.querySelector(BIG_V_CLASS);
  314. if (bigV) return true;
  315. }
  316. return false;
  317. }
  318.  
  319. //忽略认证用户和大up主,但不忽略名单里的。
  320. const isBigUpByJson = function (moduleAuthor) {
  321. if (xianList.indexOf(String(moduleAuthor.mid)) > -1) return false;
  322. const verify = moduleAuthor?.official_verify?.type;
  323. return verify != null && verify > -1;
  324. }
  325.  
  326. const getUid = function (htmlEntity, checkType) {
  327. if (checkType === CheckType.Profile) {
  328. return window.location.href.match(/(?<=space\.bilibili\.com\/)\d+/)[0];
  329. }
  330. if (checkType === CheckType.Comment) {
  331. return htmlEntity.dataset.userId ?? htmlEntity.children[0].href.replace(/[^\d]/g, "");
  332. }
  333. if (checkType === CheckType.Repo) {
  334. return htmlEntity._profile.uid;
  335. }
  336. if (checkType === CheckType.At) {
  337. if (htmlEntity.dataset.oid) return htmlEntity.dataset.oid;
  338. if (htmlEntity.dataset.userId) return htmlEntity.dataset.userId;
  339. if (htmlEntity.dataset.usercardMid) return htmlEntity.dataset.usercardMid;
  340. }
  341. if (checkType === CheckType.Reference) {
  342. return htmlEntity._profile.uid;
  343. }
  344. if (checkType === CheckType.Follow) {
  345. return htmlEntity.parentElement.href.replace(/[^\d]/g, "");
  346. }
  347. if (checkType === CheckType.GameComment) {
  348. return htmlEntity.href.replace(/[^\d]/g, "");
  349. }
  350. if (checkType === CheckType.Like) {
  351. return htmlEntity.parentElement.previousElementSibling._profile.uid;
  352. }
  353. return null;
  354. }
  355.  
  356. const getName = function (htmlEntity, checkType) {
  357. if (checkType === CheckType.Profile) {
  358. return htmlEntity.textContent;
  359. }
  360. if (checkType === CheckType.Comment) {
  361. return htmlEntity.children[0].textContent;
  362. }
  363. if (checkType === CheckType.Repo) {
  364. return htmlEntity.textContent;
  365. }
  366. if (checkType === CheckType.At) {
  367. return htmlEntity.textContent.trim().replace(/@/g, "");
  368. }
  369. if (checkType === CheckType.Reference) {
  370. return htmlEntity.textContent;
  371. }
  372. if (checkType === CheckType.Follow) {
  373. return htmlEntity.textContent;
  374. }
  375. if (checkType === CheckType.GameComment) {
  376. return htmlEntity.textContent;
  377. }
  378. if (checkType === CheckType.Like) {
  379. return htmlEntity.textContent.slice(0, -2);
  380. }
  381. return null;
  382. }
  383.  
  384. const getCommentList = function () {
  385. return document.querySelectorAll(".user-name,.sub-user-name,.user");
  386. }
  387.  
  388. const getRepoList = function () {
  389. return document.getElementsByClassName('bili-dyn-forward-item__uname');
  390. }
  391.  
  392. const getReferenceList = function () {
  393. return document.getElementsByClassName('dyn-orig-author__name');
  394. }
  395.  
  396. const getLikeList = function () {
  397. return document.getElementsByClassName('reaction-item__name');
  398. }
  399.  
  400. const getFollowList = function () {
  401. return Array.from(document.getElementsByClassName('fans-name'));
  402. }
  403.  
  404. const getGameCommentList = function () {
  405. return Array.from(document.querySelectorAll("a.user-name"));
  406. }
  407.  
  408. const getAtList = function () {
  409. const lst = new Set();
  410. for (let c of document.getElementsByClassName('jump-link user')) {
  411. lst.add(c);
  412. }
  413. for (let c of document.getElementsByClassName('bili-rich-text-module at')) {
  414. lst.add(c);
  415. }
  416. for (let c of document.querySelectorAll('.text-con > a,.text > a')) {
  417. if (c.dataset.usercardMid) lst.add(c);
  418. }
  419. return Array.from(lst);
  420. }
  421.  
  422. const spliceText = function (moduleDynamic) {
  423. let fullTextArr = [];
  424. if (moduleDynamic.topic && !isBlank(moduleDynamic.topic.name)) {
  425. fullTextArr.push(moduleDynamic.topic.name);
  426. }
  427. if (moduleDynamic.desc && !isBlank(moduleDynamic.desc.text)) {
  428. fullTextArr.push(moduleDynamic.desc.text);
  429. }
  430. if (moduleDynamic.major) {
  431. if (moduleDynamic.major.archive) {
  432. if (!isBlank(moduleDynamic.major.archive.title)) {
  433. fullTextArr.push(moduleDynamic.major.archive.title);
  434. }
  435. if (!isBlank(moduleDynamic.major.archive.desc)) {
  436. fullTextArr.push(moduleDynamic.major.archive.desc);
  437. }
  438. }
  439. if (moduleDynamic.major.article) {
  440. if (!isBlank(moduleDynamic.major.article.title)) {
  441. fullTextArr.push(moduleDynamic.major.article.title);
  442. }
  443. if (!isBlank(moduleDynamic.major.article.desc)) {
  444. fullTextArr.push(moduleDynamic.major.article.desc);
  445. }
  446. }
  447. if (moduleDynamic.major.live && !isBlank(moduleDynamic.major.live.title)) {
  448. fullTextArr.push(moduleDynamic.major.live.title);
  449. }
  450. }
  451. if (moduleDynamic.additional && moduleDynamic.additional.ugc && !isBlank(moduleDynamic.additional.ugc.title)) {
  452. fullTextArr.push(moduleDynamic.additional.ugc.title);
  453. }
  454. return fullTextArr.join('//');
  455. }
  456.  
  457. const previewText = function (text, index, len) {
  458. if (!text) return '';
  459. const left = Math.max(0, index - len);
  460. const right = Math.min(text.length, index + len);
  461. let textPart = '';
  462. if (left > 0) {
  463. textPart += '...';
  464. }
  465. textPart += text.substring(left, right).replace(/\n|\r/g, '').trim();
  466. if (right < text.length) {
  467. textPart += '...';
  468. }
  469. return textPart;
  470. }
  471.  
  472. const findRepost = function (items, ownId, matchedDynamicList, isFav, isLocal) {
  473. const usingList = isLocal ? (isFav ? localXianFavList : localXianList) : (isFav ? xianFavList : xianList);
  474. for (let i = 0; i < items.length; i++) {
  475. const item = items[i];
  476. if (isBigUpByJson(item.modules.module_author)) {
  477. matchedDynamicList.push(String(item.id_str));
  478. continue;
  479. }
  480. if (item.orig) {
  481. if (!isFav && isBigUpByJson(item.orig.modules.module_author)) {
  482. matchedDynamicList.push(String(item.orig.id_str));
  483. continue;
  484. }
  485. const origId = String(item.orig.modules.module_author.mid);
  486. if (origId === ownId) continue;
  487. if (usingList.indexOf(origId) > -1) {
  488. const origName = String(item.orig.modules.module_author.name);
  489. const ownFullText = spliceText(item.modules.module_dynamic);
  490. const origFullText = spliceText(item.orig.modules.module_dynamic);
  491. const ownTextPart = previewText(ownFullText, 0, GM_getValue("previewLength", 60));
  492. const origTextPart = previewText(origFullText, 0, GM_getValue("previewLength", 60));
  493. const bothTextPart = `${ownTextPart}//@${origName}:${origTextPart}`;
  494. matchedDynamicList.push(String(item.id_str));
  495. matchedDynamicList.push(String(item.orig.id_str));
  496. return [origId, origName, String(item.id_str), bothTextPart];
  497. }
  498. }
  499. }
  500. return null;
  501. }
  502.  
  503. const hearOne = function (text, usingWordList, level) {
  504. for (const word of usingWordList) {
  505. const matchRes = text.match(word);
  506. if (matchRes) {
  507. let matchStr = matchRes[0];
  508. let matchIndex = matchRes.index;
  509. if (matchStr === '') {
  510. for (const aidWord of aidList) {
  511. const matchAid = text.match(aidWord);
  512. if (matchAid) {
  513. matchStr = matchAid[0];
  514. matchIndex = matchAid.index;
  515. break;
  516. }
  517. }
  518. }
  519. matchStr = matchStr.replace(/\n|\r/g, ' ').trim();
  520. return [matchStr, matchIndex + matchStr.length / 2, level];
  521. }
  522. }
  523. return null;
  524. }
  525.  
  526. const hear = function (text, name, isLocal) {
  527. if (isBlank(text) || ignoreList.indexOf(name) > -1) return null;
  528. const usingLeakList = isLocal ? localXianLeakList : xianLeakList;
  529. if (!isLocal) {
  530. for (let level = 2; level >= 0; level--) {
  531. const wordList = wordLists[level];
  532. const matchRes = hearOne(text, wordList, level);
  533. if (matchRes) return matchRes;
  534. }
  535. } else {
  536. const matchRes = hearOne(text, localXianWordList, -1);
  537. if (matchRes) return matchRes;
  538. }
  539. for (const word of usingLeakList) {
  540. const matchRes = text.match(word);
  541. if (matchRes) return ['可能是盒隐私', -1, -1];
  542. }
  543. return null;
  544. }
  545.  
  546. /**
  547. * 查找关键词
  548. * @param {} items 动态列表
  549. * @returns [关键词,动态id,动态片段]
  550. */
  551. const findWord = function (items, ownName, matchedDynamicList, isLocal) {
  552. for (let i = 0; i < items.length; i++) {
  553. const item = items[i];
  554. //忽略认证用户 但不忽略名单里的
  555. if (isBigUpByJson(item.modules.module_author)) {
  556. matchedDynamicList.push(String(item.id_str));
  557. continue;
  558. }
  559. let level = -1;
  560. let origName;
  561. let ownFullText = spliceText(item.modules.module_dynamic);
  562. let origFullText;
  563. let ownTextPart;
  564. let origTextPart;
  565. let returnWord; // 关键词
  566. let returnPart; // 预览文本
  567. if (matchedDynamicList.indexOf(String(item.id_str)) > -1) {
  568. ownFullText = null;
  569. }
  570. if (item.orig) {
  571. origName = String(item.orig.modules.module_author.name);
  572. origFullText = spliceText(item.orig.modules.module_dynamic);
  573. if (isBigUpByJson(item.orig.modules.module_author)) {
  574. matchedDynamicList.push(String(item.orig.id_str));
  575. origFullText = null;
  576. }
  577. if (matchedDynamicList.indexOf(String(item.orig.id_str)) > -1) {
  578. origFullText = null;
  579. }
  580. }
  581. const ownMatch = hear(ownFullText, ownName, isLocal);
  582. const origMatch = hear(origFullText, origName, isLocal);
  583. const ownLevel = ownMatch ? ownMatch[2] : -2;
  584. const origLevel = origMatch ? origMatch[2] : -2;
  585. if (ownLevel === -2 && origLevel === -2) {
  586. continue;
  587. }
  588. if (ownLevel < origLevel) {
  589. level = origLevel;
  590. returnWord = '🔁' + origMatch[0];
  591. } else {
  592. level = ownLevel;
  593. returnWord = ownMatch[0];
  594. }
  595. const ownIndex = ownMatch ? ownMatch[1] : 0;
  596. const origIndex = origMatch ? origMatch[1] : 0;
  597. ownTextPart = ownIndex < 0 ? PRIVATE_TIPS : previewText(ownFullText, ownIndex, GM_getValue("previewLength", 60) / 2);
  598. returnPart = ownTextPart;
  599. if (!isBlank(origName)) {
  600. origTextPart = origIndex < 0 ? PRIVATE_TIPS : previewText(origFullText, origIndex, GM_getValue("previewLength", 60) / 2);
  601. returnPart = ownTextPart + `//@${origName}:${origTextPart}`;
  602. }
  603. return [returnWord, String(item.id_str), returnPart, level + 1];
  604. }
  605. return null;
  606. }
  607.  
  608. //检查记录
  609. const findRecord = async function (uid, name) {
  610. let oldTag;
  611. if (recordMap.has(uid)) {
  612. oldTag = recordMap.get(uid);
  613. uidSet.delete(uid);
  614. if (oldTag) {
  615. log(`[xian-helper]>>Record:${name}@UID-${uid}>>find>>${oldTag.replaceAll(/<\/?a.*?>/g, "").replaceAll(/&gt;&lt;/g, "、").replaceAll(/&.t;/g, "")}`);
  616. }
  617. } else if (uidSet.has(uid)) {
  618. await sleep(500);
  619. oldTag = findRecord(uid, name);
  620. } else {
  621. uidSet.add(uid);
  622. }
  623. return oldTag;
  624. }
  625.  
  626. const checkEntity = async function (htmlEntity, checkType) {
  627. if (htmlEntity.innerHTML.indexOf(`<span class="xian`) === -1) {
  628. htmlEntity.innerHTML = htmlEntity.innerHTML.trim();
  629. let xianSpan = document.createElement('span');
  630. xianSpan.className = 'xian';
  631. htmlEntity.appendChild(xianSpan);
  632. if (compareVersions(GM_info.script.version, onlineVersion) > 0) {
  633. xianSpan.innerHTML += spawnHtmlWithRef(newVerTag, '', SCRIPT_URL, NEW_VERSION_TIPS);
  634. }
  635. const uid = String(getUid(htmlEntity, checkType));
  636. if (isBlank(uid)) return;
  637. if (isBigUpByHtml(checkType, uid)) return;
  638. const name = getName(htmlEntity, checkType).trim();
  639. if (ignoreList.indexOf(name) > -1) return;
  640. let oldTag = await findRecord(uid, name);
  641.  
  642. if (typeof oldTag === 'string') {
  643. xianSpan.innerHTML += oldTag;
  644. } else {
  645. let newTag = '';
  646. if (xianList.indexOf(uid) > -1) {
  647. log(`[xian-helper]>>Find Target:${name}@UID-${uid}>>${checkType}`);
  648. newTag += spawnHtml(xianTag, XIAN_MATCH_TIPS);
  649. } else if (localXianList.indexOf(uid) > -1) {
  650. log(`[xian-helper]>>Find Local Target:${name}@UID-${uid}>>${checkType}`);
  651. newTag += spawnHtml(localXianTag, XIAN_MATCH_TIPS);
  652. }
  653. GM_xmlhttpRequest({
  654. method: "get",
  655. url: BLOG_URL + uid,
  656. data: '',
  657. headers: {
  658. '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'
  659. },
  660. onload: res => {
  661. if (res.status === 200) {
  662. const dynamicJson = JSON.parse(res.response).data;
  663. if (dynamicJson) {
  664. if (dynamicJson.items) {
  665. let matchedDynamicList = [];
  666. const repostMatch = findRepost(dynamicJson.items, uid, matchedDynamicList, false, false);
  667. if (repostMatch) {
  668. log(`[xian-helper]>>Find Repost:${name}@UID-${uid}>>repost>>${repostMatch[1]}@UID-${repostMatch[0]}>>${checkType}`);
  669. const fixedText = repostMatch[1].length > 12 ? repostMatch[1].slice(0, 9) + '...' : repostMatch[1];
  670. newTag += spawnHtmlWithRef(xianRepostTag, fixedText, BILI_URL + repostMatch[2], repostMatch[3]);
  671. } else {
  672. const localRepostMatch = findRepost(dynamicJson.items, uid, matchedDynamicList, false, true);
  673. if (localRepostMatch) {
  674. log(`[xian-helper]>>Find Local Repost:${name}@UID-${uid}>>repost>>${repostMatch[1]}@UID-${repostMatch[0]}>>${checkType}`);
  675. const fixedText = repostMatch[1].length > 12 ? repostMatch[1].slice(0, 9) + '...' : repostMatch[1];
  676. newTag += spawnHtmlWithRef(localXianRepostTag, fixedText, BILI_URL + repostMatch[2], repostMatch[3]);
  677. }
  678. }
  679. const favRepostMatch = findRepost(dynamicJson.items, uid, matchedDynamicList, true, false);
  680. if (favRepostMatch) {
  681. log(`[xian-helper]>>Find Fav:${name}@UID-${uid}>>repost>>${favRepostMatch[1]}@UID-${favRepostMatch[0]}>>${checkType}`);
  682. const fixedText = favRepostMatch[1].length > 12 ? favRepostMatch[1].slice(0, 9) + '...' : favRepostMatch[1];
  683. newTag += spawnHtmlWithRef(favRepostTag, fixedText, BILI_URL + favRepostMatch[2], favRepostMatch[3]);
  684. } else {
  685. const localFavRepostMatch = findRepost(dynamicJson.items, uid, matchedDynamicList, true, true);
  686. if (localFavRepostMatch) {
  687. log(`[xian-helper]>>Find Local Fav:${name}@UID-${uid}>>repost>>${favRepostMatch[1]}@UID-${favRepostMatch[0]}>>${checkType}`);
  688. const fixedText = favRepostMatch[1].length > 12 ? favRepostMatch[1].slice(0, 9) + '...' : favRepostMatch[1];
  689. newTag += spawnHtmlWithRef(localFavRepostTag, fixedText, BILI_URL + favRepostMatch[2], favRepostMatch[3]);
  690. }
  691. }
  692. const wordMatch = findWord(dynamicJson.items, name, matchedDynamicList, false);
  693. if (wordMatch) {
  694. log(`[xian-helper]>>Find Word:${name}@UID-${uid}>>say>>${wordMatch[0]}>>${checkType}`);
  695. const fixedText = wordMatch[0].length > 12 ? wordMatch[0].slice(0, 9) + '...' : wordMatch[0];
  696. newTag += spawnHtmlWithRef(wordTags[wordMatch[3]], fixedText, BILI_URL + wordMatch[1], wordMatch[2]);
  697. } else {
  698. const localWordMatch = findWord(dynamicJson.items, name, matchedDynamicList, true);
  699. if (localWordMatch) {
  700. log(`[xian-helper]>>Find Local Word:${name}@UID-${uid}>>say>>${wordMatch[0]}>>${checkType}`);
  701. const fixedText = wordMatch[0].length > 12 ? wordMatch[0].slice(0, 9) + '...' : wordMatch[0];
  702. newTag += spawnHtmlWithRef(localXianWordTag, fixedText, BILI_URL + wordMatch[1], wordMatch[2]);
  703. }
  704. }
  705. }
  706. xianSpan.innerHTML += newTag;
  707. recordMap.set(uid, newTag);
  708. } else {
  709. xianSpan.className = 'xian-fail';
  710. newTag = newTag.replace(/"xian"/g, '"xian-fail"');
  711. xianSpan.innerHTML += newTag;
  712. xianSpan.innerHTML += spawnCaptchaHtml(captchaTag);
  713. xianSpan.innerHTML += spawnRefreshHtml(refreshTag);
  714. uidSet.delete(uid);
  715. log('[xian-helper]仙家军成分查询Helper get dynamic fail...');
  716. log(htmlEntity);
  717. log(res);
  718. }
  719. } else {
  720. xianSpan.className = 'xian-fail';
  721. xianSpan.innerHTML += newTag;
  722. log('[xian-helper]仙家军成分查询Helper request fail...');
  723. log(htmlEntity);
  724. log(res);
  725. }
  726. },
  727. });
  728. }
  729. }
  730. }
  731.  
  732. const checkComments = function () {
  733. const commentlist = getCommentList();
  734. if (commentlist && commentlist.length > 0) {
  735. commentlist.forEach(htmlEntity => {
  736. checkEntity(htmlEntity, CheckType.Comment);
  737. });
  738. }
  739. }
  740.  
  741. const checkRepos = function () {
  742. const repolist = getRepoList();
  743. if (repolist && repolist.length > 0) {
  744. repolist.forEach(htmlEntity => {
  745. checkEntity(htmlEntity, CheckType.Repo);
  746. });
  747. }
  748. }
  749.  
  750. const checkAts = function () {
  751. const atList = getAtList();
  752. if (atList && atList.length > 0) {
  753. atList.forEach(htmlEntity => checkEntity(htmlEntity, CheckType.At));
  754. }
  755. }
  756.  
  757. const checkReferences = function () {
  758. const referenceList = getReferenceList();
  759. if (referenceList && referenceList.length > 0) {
  760. referenceList.forEach(htmlEntity => checkEntity(htmlEntity, CheckType.Reference));
  761. }
  762. }
  763.  
  764. const checkFollows = function () {
  765. const followList = getFollowList();
  766. if (followList && followList.length > 0) {
  767. followList.forEach(htmlEntity => checkEntity(htmlEntity, CheckType.Follow));
  768. }
  769. }
  770.  
  771. const checkProfile = async function () {
  772. let htmlEntity = document.getElementById('h-name');
  773. if (htmlEntity) checkEntity(htmlEntity, CheckType.Profile);
  774. }
  775.  
  776. const checkGameComments = async function () {
  777. const gameCommentList = getGameCommentList();
  778. if (gameCommentList && gameCommentList.length > 0) {
  779. gameCommentList.forEach(htmlEntity => checkEntity(htmlEntity, CheckType.GameComment));
  780. }
  781. }
  782.  
  783. const checkLikes = async function () {
  784. const LikeList = getLikeList();
  785. if (LikeList && LikeList.length > 0) {
  786. LikeList.forEach(htmlEntity => checkEntity(htmlEntity, CheckType.Like));
  787. }
  788. }
  789.  
  790. log(`[xian-helper]仙家军成分查询Helper,启动!
  791. [xian-helper]>>isNew: ${isNew()}
  792. [xian-helper]>>Loading: ${window.location.href}
  793. [xian-helper]>>urlSource: ${GM_getValue("urlSource", "jsdelivr")}
  794. [xian-helper]>>List update time: ${updateTime}
  795. `)
  796.  
  797. setInterval(() => {
  798. if (GM_getValue("status", 1)) {
  799. if (location.host === "www.biligame.com") {
  800. if (GM_getValue("usingCheckGameComments", 1)) {
  801. checkGameComments();
  802. }
  803. } else {
  804. if (GM_getValue("usingCheckComments", 1)) {
  805. checkComments();
  806. }
  807. if (GM_getValue("usingCheckRepos", 1)) {
  808. checkRepos();
  809. }
  810. if (GM_getValue("usingCheckReferences", 1)) {
  811. checkReferences();
  812. }
  813. if (GM_getValue("usingCheckAts", 1)) {
  814. checkAts();
  815. }
  816. if (GM_getValue("usingCheckLikes", 1)) {
  817. checkLikes();
  818. }
  819. if (location.host === "space.bilibili.com") {
  820. if (GM_getValue("usingCheckProfile", 1)) {
  821. checkProfile();
  822. }
  823. if (GM_getValue("usingCheckFollows", 1)) {
  824. checkFollows();
  825. }
  826. }
  827. }
  828. }
  829. const zhuangbans = document.querySelectorAll('.sailing');
  830. for (const zhuangban of zhuangbans) {
  831. if (zhuangban.style.pointerEvents !== "none") {
  832. zhuangban.style.pointerEvents = "none";
  833. }
  834. }
  835. }, GM_getValue("timeInterval", 2500));
  836.  
  837. }
  838.  
  839. const fitBiliHelper = function () {
  840. if (!document.getElementById("helper-card")) {
  841. const args = Array.from(arguments).slice(0, arguments.length);
  842. setTimeout(fitBiliHelper, GM_getValue("timeInterval", 2500));
  843. return;
  844. }
  845. const target = document.querySelector('.helper-card-face.face');
  846. const observer = new MutationObserver(function (mutations) {
  847. mutations.map(function (mutation) {
  848. commandClearTags(document.getElementById("helper-card"));
  849. });
  850. });
  851.  
  852. observer.observe(target, { attributeFilter: ["src"] });
  853. }
  854.  
  855. const start = async function () {
  856. initSettings();
  857. createScriptFun();
  858. await fillLists();
  859. runHelper();
  860. fitBiliHelper();
  861. }
  862.  
  863. start();

QingJ © 2025

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