NGA Filter

troll must die

当前为 2022-06-04 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name NGA Filter
  3. // @namespace https://gf.qytechs.cn/users/263018
  4. // @version 1.6.2
  5. // @author snyssss
  6. // @description troll must die
  7. // @license MIT
  8.  
  9. // @match *://bbs.nga.cn/*
  10. // @match *://ngabbs.com/*
  11. // @match *://nga.178.com/*
  12.  
  13. // @grant GM_addStyle
  14. // @grant GM_setValue
  15. // @grant GM_getValue
  16.  
  17. // @noframes
  18. // ==/UserScript==
  19.  
  20. ((n, self) => {
  21. if (n === undefined) return;
  22.  
  23. const key = "NGAFilter";
  24.  
  25. // 过滤提示
  26. const FILTER_TIPS =
  27. "过滤顺序:用户 &gt; 标记 &gt; 关键字<br/>过滤级别:隐藏 &gt; 遮罩 &gt; 标记 &gt; 继承 &gt; 显示<br/>多个标记或者关键字按最高级别过滤";
  28.  
  29. // 过滤方式
  30. const FILTER_MODE = ["继承", "标记", "遮罩", "隐藏", "显示"];
  31.  
  32. // 切换过滤方式
  33. const switchFilterMode = (value) => {
  34. const next = FILTER_MODE.indexOf(value) + 1;
  35.  
  36. if (next >= FILTER_MODE.length) {
  37. return FILTER_MODE[0];
  38. }
  39.  
  40. return FILTER_MODE[next];
  41. };
  42.  
  43. // 数据
  44. const data = (() => {
  45. const d = {
  46. tags: {},
  47. users: {},
  48. keywords: {},
  49. options: {
  50. filterRegdateLimit: 0,
  51. filterPostnumLimit: 0,
  52. filterMode: "隐藏",
  53. },
  54. };
  55.  
  56. const v = GM_getValue(key);
  57.  
  58. if (typeof v !== "object") {
  59. return d;
  60. }
  61.  
  62. return Object.assign(d, v);
  63. })();
  64.  
  65. // 保存数据
  66. const saveData = () => {
  67. GM_setValue(key, data);
  68. };
  69.  
  70. // 增加标记
  71. const addTag = (name) => {
  72. const tag = Object.values(data.tags).find((item) => item.name === name);
  73.  
  74. if (tag) return tag.id;
  75.  
  76. const id =
  77. Math.max(...Object.values(data.tags).map((item) => item.id), 0) + 1;
  78.  
  79. const hash = (() => {
  80. let h = 5381;
  81. for (var i = 0; i < name.length; i++) {
  82. h = ((h << 5) + h + name.charCodeAt(i)) & 0xffffffff;
  83. }
  84. return h;
  85. })();
  86.  
  87. const hex = Math.abs(hash).toString(16) + "000000";
  88.  
  89. const hsv = [
  90. `0x${hex.substring(2, 4)}` / 255,
  91. `0x${hex.substring(2, 4)}` / 255 / 2 + 0.25,
  92. `0x${hex.substring(4, 6)}` / 255 / 2 + 0.25,
  93. ];
  94.  
  95. const rgb = n.hsvToRgb(hsv[0], hsv[1], hsv[2]);
  96.  
  97. const color = ["#", ...rgb].reduce((a, b) => {
  98. return a + ("0" + b.toString(16)).slice(-2);
  99. });
  100.  
  101. data.tags[id] = {
  102. id,
  103. name,
  104. color,
  105. filterMode: FILTER_MODE[0],
  106. };
  107.  
  108. saveData();
  109.  
  110. return id;
  111. };
  112.  
  113. // 增加用户
  114. const addUser = (id, name = null, tags = [], filterMode = FILTER_MODE[0]) => {
  115. if (data.users[id]) return data.users[id];
  116.  
  117. data.users[id] = {
  118. id,
  119. name,
  120. tags,
  121. filterMode,
  122. };
  123.  
  124. saveData();
  125.  
  126. return data.users[id];
  127. };
  128.  
  129. // 增加关键字
  130. const addKeyword = (
  131. keyword,
  132. filterMode = FILTER_MODE[0],
  133. filterLevel = 0
  134. ) => {
  135. const id =
  136. Math.max(...Object.values(data.keywords).map((item) => item.id), 0) + 1;
  137.  
  138. data.keywords[id] = {
  139. id,
  140. keyword,
  141. filterMode,
  142. filterLevel,
  143. };
  144.  
  145. saveData();
  146.  
  147. return id;
  148. };
  149.  
  150. // 旧版本数据迁移
  151. {
  152. const dataKey = "troll_data";
  153. const modeKey = "troll_mode";
  154. const keywordKey = "troll_keyword";
  155.  
  156. if (localStorage.getItem(dataKey)) {
  157. let trollMap = (function () {
  158. try {
  159. return JSON.parse(localStorage.getItem(dataKey)) || {};
  160. } catch (e) {}
  161.  
  162. return {};
  163. })();
  164.  
  165. let filterMode = ~~localStorage.getItem(modeKey);
  166.  
  167. let filterKeyword = localStorage.getItem(keywordKey) || "";
  168.  
  169. // 整理标签
  170. [...new Set(Object.values(trollMap).flat())].forEach((item) =>
  171. addTag(item)
  172. );
  173.  
  174. // 整理用户
  175. Object.keys(trollMap).forEach((item) => {
  176. addUser(
  177. item,
  178. null,
  179. (typeof trollMap[item] === "object" ? trollMap[item] : []).map(
  180. (tag) => addTag(tag)
  181. )
  182. );
  183. });
  184.  
  185. data.options.filterMode = filterMode ? "隐藏" : "标记";
  186. data.options.keyword = filterKeyword;
  187.  
  188. localStorage.removeItem(dataKey);
  189. localStorage.removeItem(modeKey);
  190. localStorage.removeItem(keywordKey);
  191.  
  192. saveData();
  193. }
  194.  
  195. // v1.1.0 -> v1.1.1
  196. {
  197. Object.values(data.users).forEach(({ id, name, tags, enabled }) => {
  198. if (enabled !== undefined) {
  199. data.users[id] = {
  200. id,
  201. name,
  202. tags,
  203. filterMode: enabled ? "继承" : "显示",
  204. };
  205. }
  206. });
  207.  
  208. Object.values(data.tags).forEach(({ id, name, color, enabled }) => {
  209. if (enabled !== undefined) {
  210. data.tags[id] = {
  211. id,
  212. name,
  213. color,
  214. filterMode: enabled ? "继承" : "显示",
  215. };
  216. }
  217. });
  218.  
  219. if (data.options.filterMode === 0) {
  220. data.options.filterMode = "隐藏";
  221. } else if (data.options.filterMode === 1) {
  222. data.options.filterMode = "标记";
  223. }
  224.  
  225. saveData();
  226. }
  227.  
  228. // v1.2.x -> v1.3.0
  229. {
  230. if (data.options.keyword) {
  231. addKeyword(data.options.keyword);
  232.  
  233. delete data.options.keyword;
  234.  
  235. saveData();
  236. }
  237. }
  238. }
  239.  
  240. // 编辑用户标记
  241. const editUser = (() => {
  242. let window;
  243. return (uid, name, callback) => {
  244. if (window === undefined) {
  245. window = n.createCommmonWindow();
  246. }
  247.  
  248. const user = data.users[uid];
  249.  
  250. const content = document.createElement("div");
  251.  
  252. const size = Math.floor((screen.width * 0.8) / 200);
  253.  
  254. const items = Object.values(data.tags).map(
  255. (tag, index) => `
  256. <td class="c1">
  257. <label for="s-tag-${index}" style="display: block; cursor: pointer;">
  258. <b class="block_txt nobr" style="background:${
  259. tag.color
  260. }; color:#fff; margin: 0.1em 0.2em;">${tag.name}</b>
  261. </label>
  262. </td>
  263. <td class="c2" width="1">
  264. <input id="s-tag-${index}" type="checkbox" value="${tag.id}" ${
  265. user && user.tags.find((item) => item === tag.id) && "checked"
  266. }/>
  267. </td>
  268. `
  269. );
  270.  
  271. const rows = [...new Array(Math.ceil(items.length / size))].map(
  272. (item, index) =>
  273. `
  274. <tr class="row${(index % 2) + 1}">
  275. ${items.slice(size * index, size * (index + 1)).join("")}
  276. </tr>
  277. `
  278. );
  279.  
  280. content.className = "w100";
  281. content.innerHTML = `
  282. <div class="filter-table-wrapper" style="width: 80vw;">
  283. <table class="filter-table forumbox">
  284. <tbody>
  285. ${rows.join("")}
  286. </tbody>
  287. </table>
  288. </div>
  289. <div style="margin: 10px 0;">
  290. <input placeholder="一次性添加多个标记用&quot;|&quot;隔开,不会添加重名标记" style="width: -webkit-fill-available;" />
  291. </div>
  292. <div style="margin: 10px 0;">
  293. <span>过滤方式:</span>
  294. <button>${(user && user.filterMode) || FILTER_MODE[0]}</button>
  295. <div class="right_">
  296. <button>删除</button>
  297. <button>保存</button>
  298. </div>
  299. </div>
  300. <div class="silver" style="margin-top: 5px;">${FILTER_TIPS}</div>
  301. `;
  302.  
  303. const actions = content.getElementsByTagName("button");
  304.  
  305. actions[0].onclick = () => {
  306. actions[0].innerText = switchFilterMode(
  307. actions[0].innerText || FILTER_MODE[0]
  308. );
  309. };
  310.  
  311. actions[1].onclick = () => {
  312. if (confirm("是否确认?")) {
  313. delete data.users[uid];
  314.  
  315. saveData();
  316.  
  317. callback && callback();
  318.  
  319. window._.hide();
  320. }
  321. };
  322.  
  323. actions[2].onclick = () => {
  324. if (confirm("是否确认?")) {
  325. const values = [...content.getElementsByTagName("input")];
  326. const newTags = values[values.length - 1].value
  327. .split("|")
  328. .filter((item) => item.length)
  329. .map((item) => addTag(item));
  330. const tags = [
  331. ...new Set(
  332. values
  333. .filter((item) => item.type === "checkbox" && item.checked)
  334. .map((item) => ~~item.value)
  335. .concat(newTags)
  336. ),
  337. ].sort();
  338.  
  339. if (user) {
  340. user.tags = tags;
  341. user.filterMode = actions[0].innerText;
  342. } else {
  343. addUser(uid, name, tags, actions[0].innerText);
  344. }
  345.  
  346. saveData();
  347.  
  348. callback && callback();
  349.  
  350. window._.hide();
  351. }
  352. };
  353.  
  354. if (user === undefined) {
  355. actions[1].style = "display: none;";
  356. }
  357.  
  358. window._.addContent(null);
  359. window._.addTitle(`编辑标记 - ${name ? name : "#" + uid}`);
  360. window._.addContent(content);
  361. window._.show();
  362. };
  363. })();
  364.  
  365. // 判断过滤方式
  366. const getFilterMode = async (uid, subject, content) => {
  367. let result = -1;
  368.  
  369. const filterRegdateLimit = data.options.filterRegdateLimit || 0;
  370.  
  371. const filterPostnumLimit = data.options.filterPostnumLimit || 0;
  372.  
  373. const user = data.users[uid];
  374.  
  375. const tags = user ? user.tags.map((tag) => data.tags[tag]) : [];
  376.  
  377. const keywords = Object.values(data.keywords);
  378.  
  379. if (filterRegdateLimit > 0 || filterPostnumLimit > 0) {
  380. if (uid && uid > 0) {
  381. const userInfo = n.userInfo.users[uid];
  382.  
  383. if (userInfo) {
  384. if (
  385. userInfo.regdate * 1000 > new Date() - filterRegdateLimit ||
  386. (userInfo.postnum && userInfo.postnum < filterPostnumLimit) ||
  387. (userInfo.posts && userInfo.posts < filterPostnumLimit)
  388. ) {
  389. return FILTER_MODE.indexOf("隐藏");
  390. }
  391. }
  392. }
  393. }
  394.  
  395. if (user) {
  396. const filterMode = FILTER_MODE.indexOf(user.filterMode);
  397.  
  398. if (filterMode > 0) {
  399. return filterMode;
  400. }
  401.  
  402. result = filterMode;
  403. }
  404.  
  405. if (tags.length) {
  406. const filterMode = (() => {
  407. if (tags.some((tag) => tag.filterMode !== "显示")) {
  408. return tags
  409. .filter((tag) => tag.filterMode !== "显示")
  410. .map((tag) => FILTER_MODE.indexOf(tag.filterMode) || 0)
  411. .sort((a, b) => b - a)[0];
  412. }
  413.  
  414. return FILTER_MODE.indexOf("显示");
  415. })();
  416.  
  417. if (filterMode > 0) {
  418. return filterMode;
  419. }
  420.  
  421. result = filterMode;
  422. }
  423.  
  424. if (keywords.length) {
  425. const filterMode = (() => {
  426. const sR = (() => {
  427. if (subject) {
  428. const r = keywords
  429. .filter((item) => item.keyword && item.filterMode !== "显示")
  430. .filter((item) => (item.filterLevel || 0) >= 0)
  431. .sort(
  432. (a, b) =>
  433. FILTER_MODE.indexOf(b.filterMode) -
  434. FILTER_MODE.indexOf(a.filterMode)
  435. )
  436. .find((item) => subject.search(item.keyword) >= 0);
  437.  
  438. if (r) {
  439. return FILTER_MODE.indexOf(r.filterMode);
  440. }
  441. }
  442.  
  443. return -1;
  444. })();
  445.  
  446. const cR = (() => {
  447. if (content) {
  448. const r = keywords
  449. .filter((item) => item.keyword && item.filterMode !== "显示")
  450. .filter((item) => (item.filterLevel || 0) >= 1)
  451. .sort(
  452. (a, b) =>
  453. FILTER_MODE.indexOf(b.filterMode) -
  454. FILTER_MODE.indexOf(a.filterMode)
  455. )
  456. .find((item) => content.search(item.keyword) >= 0);
  457.  
  458. if (r) {
  459. return FILTER_MODE.indexOf(r.filterMode);
  460. }
  461. }
  462.  
  463. return -1;
  464. })();
  465.  
  466. return Math.max(sR, cR, result);
  467. })();
  468.  
  469. if (filterMode > 0) {
  470. return filterMode;
  471. }
  472.  
  473. result = filterMode;
  474. }
  475.  
  476. return result;
  477. };
  478.  
  479. // 根据 TID 获取过滤方式
  480. const getFilterModeByTopic = async (tid) => {
  481. return await new Promise((resolve, reject) => {
  482. const api = `/read.php?tid=${tid}`;
  483.  
  484. fetch(api)
  485. .then((res) => res.blob())
  486. .then((blob) => {
  487. const getLastIndex = (content, position) => {
  488. if (position >= 0) {
  489. let nextIndex = position + 1;
  490.  
  491. while (nextIndex < content.length) {
  492. if (content[nextIndex] === "}") {
  493. return nextIndex;
  494. }
  495.  
  496. if (content[nextIndex] === "{") {
  497. nextIndex = getLastIndex(content, nextIndex);
  498.  
  499. if (nextIndex < 0) {
  500. break;
  501. }
  502. }
  503.  
  504. nextIndex = nextIndex + 1;
  505. }
  506. }
  507.  
  508. return -1;
  509. };
  510.  
  511. const reader = new FileReader();
  512.  
  513. reader.onload = () => {
  514. const parser = new DOMParser();
  515.  
  516. const doc = parser.parseFromString(reader.result, "text/html");
  517.  
  518. const html = doc.body.innerHTML;
  519.  
  520. // 验证帖子正常
  521. const verify = doc.querySelector("#m_posts");
  522.  
  523. if (verify) {
  524. // 取得顶楼 UID
  525. const uid = (() => {
  526. const ele = doc.querySelector("#postauthor0");
  527.  
  528. if (ele) {
  529. const res = ele.getAttribute("href").match(/uid=(\S+)/);
  530.  
  531. if (res) {
  532. return res[1];
  533. }
  534. }
  535.  
  536. return 0;
  537. })();
  538.  
  539. // 取得顶楼标题
  540. const subject = doc.querySelector("#postsubject0").innerHTML;
  541.  
  542. // 取得顶楼内容
  543. const content = doc.querySelector("#postcontent0").innerHTML;
  544.  
  545. // 根据 UID 把用户信息写入缓存
  546. if (uid > 0) {
  547. // 起始JSON
  548. const str = `"${uid}":{`;
  549.  
  550. // 起始下标
  551. const index = html.indexOf(str) + str.length;
  552.  
  553. // 结尾下标
  554. const lastIndex = getLastIndex(html, index);
  555.  
  556. if (lastIndex >= 0) {
  557. if (n.userInfo.users[uid] === undefined) {
  558. n.userInfo.users[uid] = JSON.parse(
  559. `{${html.substring(index, lastIndex)}}`
  560. );
  561. }
  562. }
  563. }
  564.  
  565. resolve(getFilterMode(uid, subject, content));
  566. } else {
  567. reject();
  568. }
  569. };
  570.  
  571. reader.readAsText(blob, "GBK");
  572. })
  573. .catch(() => {
  574. reject();
  575. });
  576. }).catch(() => {
  577. return FILTER_MODE.indexOf("隐藏");
  578. });
  579. };
  580.  
  581. // 处理引用
  582. const handleQuote = async (content) => {
  583. const quotes = content.querySelectorAll(".quote");
  584.  
  585. await Promise.all(
  586. [...quotes].map(async (quote) => {
  587. const uid = (() => {
  588. const ele = quote.querySelector("a[href^='/nuke.php']");
  589.  
  590. if (ele) {
  591. const res = ele.getAttribute("href").match(/uid=(\S+)/);
  592.  
  593. if (res) {
  594. return res[1];
  595. }
  596. }
  597.  
  598. return 0;
  599. })();
  600.  
  601. const filterMode = await new Promise(async (resolve) => {
  602. const mode = await getFilterMode(uid, "", quote.innerText);
  603.  
  604. if (mode === 0) {
  605. resolve(data.options.filterMode);
  606. }
  607.  
  608. if (mode > 0) {
  609. resolve(FILTER_MODE[mode]);
  610. }
  611.  
  612. resolve("");
  613. });
  614.  
  615. if (filterMode === "标记") {
  616. quote.innerHTML = `
  617. <div class="lessernuke" style="background: #81C7D4; border-color: #66BAB7; ">
  618. <span class="crimson">Troll must die.</span>
  619. <a href="javascript:void(0)" onclick="[...document.getElementsByName('troll_${uid}')].forEach(item => item.style.display = '')">点击查看</a>
  620. <div style="display: none;" name="troll_${uid}">
  621. ${quote.innerHTML}
  622. </div>
  623. </div>`;
  624. } else if (filterMode === "遮罩") {
  625. const source = document.createElement("DIV");
  626.  
  627. source.innerHTML = quote.innerHTML;
  628. source.style.display = "none";
  629.  
  630. const caption = document.createElement("CAPTION");
  631.  
  632. caption.className = "filter-mask filter-mask-block";
  633.  
  634. caption.innerHTML = `<span class="crimson">Troll must die.</span>`;
  635. caption.onclick = () => {
  636. quote.removeChild(caption);
  637.  
  638. source.style.display = "";
  639. };
  640.  
  641. quote.innerHTML = "";
  642. quote.appendChild(source);
  643. quote.appendChild(caption);
  644. } else if (filterMode === "隐藏") {
  645. quote.innerHTML = "";
  646. }
  647. })
  648. );
  649. };
  650.  
  651. // 过滤
  652. const reFilter = (() => {
  653. let hasNext = false;
  654. let isRunning = false;
  655.  
  656. const func = async () => {
  657. const tPage = location.pathname === "/thread.php";
  658. const pPage = location.pathname === "/read.php";
  659.  
  660. if (tPage && new RegExp(`authorid=${self}`).test(location.search)) {
  661. return;
  662. }
  663.  
  664. if (tPage) {
  665. const tData = n.topicArg.data;
  666.  
  667. await Promise.all(
  668. Object.values(tData).map(async (item) => {
  669. if (item.containerC) return;
  670.  
  671. const tid = item[8];
  672.  
  673. const filterMode = await new Promise(async (resolve) => {
  674. const mode = await getFilterModeByTopic(tid);
  675.  
  676. if (mode === 0) {
  677. resolve(data.options.filterMode);
  678. }
  679.  
  680. if (mode > 0) {
  681. resolve(FILTER_MODE[mode]);
  682. }
  683.  
  684. resolve("");
  685. });
  686.  
  687. item.contentC = item[1];
  688.  
  689. item.contentB = item.contentB || item.contentC.innerHTML;
  690.  
  691. item.containerC =
  692. item.containerC || item.contentC.parentNode.parentNode;
  693.  
  694. item.containerC.style = "";
  695. item.contentC.style = "";
  696. item[1].className = item[1].className.replace(" filter-mask", "");
  697. item[2].className = item[2].className.replace(" filter-mask", "");
  698.  
  699. if (filterMode === "标记") {
  700. item.contentC.style = "text-decoration: line-through;";
  701. } else if (filterMode === "遮罩") {
  702. item[1].className += " filter-mask";
  703. item[2].className += " filter-mask";
  704. } else if (filterMode === "隐藏") {
  705. item.containerC.style = "display: none;";
  706. }
  707. })
  708. );
  709. } else if (pPage) {
  710. const pData = n.postArg.data;
  711.  
  712. await Promise.all(
  713. Object.values(pData).map(async (item) => {
  714. if (~~item.pAid === self) return;
  715. if (item.containerC) return;
  716.  
  717. if (typeof item.i === "number") {
  718. item.actionC =
  719. item.actionC ||
  720. (() => {
  721. const ele = item.uInfoC.querySelector('[name="uid"]');
  722.  
  723. ele.onclick = null;
  724.  
  725. return ele;
  726. })();
  727.  
  728. item.tagC =
  729. item.tagC ||
  730. (() => {
  731. const tc = document.createElement("div");
  732.  
  733. tc.className = "filter-tags";
  734.  
  735. item.uInfoC.appendChild(tc);
  736.  
  737. return tc;
  738. })();
  739. }
  740.  
  741. item.pName =
  742. item.pName ||
  743. item.uInfoC.getElementsByClassName("author")[0].innerText;
  744.  
  745. item.reFilter =
  746. item.reFilter ||
  747. (async () => {
  748. const uid = item.pAid;
  749.  
  750. const filterMode = await new Promise(async (resolve) => {
  751. const mode = await getFilterMode(
  752. uid,
  753. item.subjectC.innerText,
  754. item.contentC.innerText
  755. );
  756.  
  757. if (mode === 0) {
  758. resolve(data.options.filterMode);
  759. }
  760.  
  761. if (mode > 0) {
  762. resolve(FILTER_MODE[mode]);
  763. }
  764.  
  765. resolve("");
  766. });
  767.  
  768. item.avatarC =
  769. item.avatarC ||
  770. (() => {
  771. const tc = document.createElement("div");
  772.  
  773. const avatar = document.getElementById(
  774. `posteravatar${item.i}`
  775. );
  776.  
  777. if (avatar) {
  778. avatar.parentNode.insertBefore(tc, avatar.nextSibling);
  779.  
  780. tc.appendChild(avatar);
  781. }
  782.  
  783. return tc;
  784. })();
  785.  
  786. item.contentB = item.contentB || item.contentC.innerHTML;
  787.  
  788. item.containerC =
  789. item.containerC ||
  790. (() => {
  791. let temp = item.contentC;
  792.  
  793. if (item.i >= 0) {
  794. while (temp.nodeName !== "TBODY") {
  795. temp = temp.parentNode;
  796. }
  797. } else {
  798. while (temp.nodeName !== "DIV") {
  799. temp = temp.parentNode;
  800. }
  801. }
  802.  
  803. return temp;
  804. })();
  805.  
  806. item.avatarC.style.display = "";
  807. item.containerC.style.display = "";
  808. item.contentC.innerHTML = item.contentB;
  809.  
  810. if (item.actionC) {
  811. item.actionC.style = "background: #aaa;";
  812. }
  813.  
  814. if (filterMode === "标记") {
  815. item.avatarC.style.display = "none";
  816. item.contentC.innerHTML = `
  817. <div class="lessernuke" style="background: #81C7D4; border-color: #66BAB7; ">
  818. <span class="crimson">Troll must die.</span>
  819. <a href="javascript:void(0)" onclick="[...document.getElementsByName('troll_${uid}')].forEach(item => item.style.display = '')">点击查看</a>
  820. <div style="display: none;" name="troll_${uid}">
  821. ${item.contentB}
  822. </div>
  823. </div>`;
  824.  
  825. if (item.actionC && data.users[uid]) {
  826. item.actionC.style = "background: #cb4042;";
  827. }
  828. } else if (filterMode === "遮罩") {
  829. const caption = document.createElement("CAPTION");
  830.  
  831. if (item.i >= 0) {
  832. caption.className = "filter-mask filter-mask-block";
  833. } else {
  834. caption.className = "filter-mask filter-mask-block left";
  835. caption.style = "width: 47%;";
  836. }
  837.  
  838. caption.innerHTML = `<span class="crimson">Troll must die.</span>`;
  839. caption.onclick = () => {
  840. item.containerC.parentNode.removeChild(caption);
  841. item.containerC.style.display = "";
  842. };
  843.  
  844. item.containerC.parentNode.insertBefore(
  845. caption,
  846. item.containerC
  847. );
  848. item.containerC.style.display = "none";
  849.  
  850. if (item.actionC && data.users[uid]) {
  851. item.actionC.style = "background: #cb4042;";
  852. }
  853. } else if (filterMode === "隐藏") {
  854. item.containerC.style.display = "none";
  855. } else {
  856. await handleQuote(item.contentC);
  857. }
  858.  
  859. if (item.tagC) {
  860. const tags = data.users[uid]
  861. ? data.users[uid].tags.map((tag) => data.tags[tag]) || []
  862. : [];
  863.  
  864. item.tagC.style.display = tags.length ? "" : "none";
  865. item.tagC.innerHTML = tags
  866. .map(
  867. (tag) =>
  868. `<b class="block_txt nobr" style="background:${tag.color}; color:#fff; margin: 0.1em 0.2em;">${tag.name}</b>`
  869. )
  870. .join("");
  871. }
  872. });
  873.  
  874. if (item.actionC) {
  875. item.actionC.onclick =
  876. item.actionC.onclick ||
  877. ((e) => {
  878. if (item.pAid < 0) return;
  879.  
  880. const user = data.users[item.pAid];
  881.  
  882. if (e.ctrlKey === false) {
  883. editUser(item.pAid, item.pName, item.reFilter);
  884. } else {
  885. if (user) {
  886. delete data.users[user.id];
  887. } else {
  888. addUser(item.pAid, item.pName);
  889. }
  890.  
  891. saveData();
  892. item.reFilter();
  893. }
  894. });
  895. }
  896.  
  897. await item.reFilter();
  898. })
  899. );
  900. }
  901. };
  902.  
  903. const execute = () =>
  904. func().finally(() => {
  905. if (hasNext) {
  906. hasNext = false;
  907.  
  908. execute();
  909. } else {
  910. isRunning = false;
  911. }
  912. });
  913.  
  914. return async () => {
  915. if (isRunning) {
  916. hasNext = true;
  917. } else {
  918. isRunning = true;
  919.  
  920. await execute();
  921. }
  922. };
  923. })();
  924.  
  925. // STYLE
  926. GM_addStyle(`
  927. .filter-table-wrapper {
  928. max-height: 80vh;
  929. overflow-y: auto;
  930. }
  931. .filter-table {
  932. margin: 0;
  933. }
  934. .filter-table th,
  935. .filter-table td {
  936. position: relative;
  937. white-space: nowrap;
  938. }
  939. .filter-table th {
  940. position: sticky;
  941. top: 2px;
  942. z-index: 1;
  943. }
  944. .filter-table input:not([type]), .filter-table input[type="text"] {
  945. margin: 0;
  946. box-sizing: border-box;
  947. height: 100%;
  948. width: 100%;
  949. }
  950. .filter-input-wrapper {
  951. position: absolute;
  952. top: 6px;
  953. right: 6px;
  954. bottom: 6px;
  955. left: 6px;
  956. }
  957. .filter-text-ellipsis {
  958. display: flex;
  959. }
  960. .filter-text-ellipsis > * {
  961. flex: 1;
  962. width: 1px;
  963. overflow: hidden;
  964. text-overflow: ellipsis;
  965. }
  966. .filter-button-group {
  967. margin: -.1em -.2em;
  968. }
  969. .filter-tags {
  970. margin: 2px -0.2em 0;
  971. text-align: left;
  972. }
  973. .filter-mask {
  974. margin: 1px;
  975. color: #81C7D4;
  976. background: #81C7D4;
  977. }
  978. .filter-mask-block {
  979. display: block;
  980. border: 1px solid #66BAB7;
  981. text-align: center !important;
  982. }
  983. .filter-input-wrapper {
  984. position: absolute;
  985. top: 6px;
  986. right: 6px;
  987. bottom: 6px;
  988. left: 6px;
  989. }
  990. `);
  991.  
  992. // UI
  993. const u = (() => {
  994. const modules = {};
  995.  
  996. const tabContainer = (() => {
  997. const c = document.createElement("div");
  998.  
  999. c.className = "w100";
  1000. c.innerHTML = `
  1001. <div class="right_" style="margin-bottom: 5px;">
  1002. <table class="stdbtn" cellspacing="0">
  1003. <tbody>
  1004. <tr></tr>
  1005. </tbody>
  1006. </table>
  1007. </div>
  1008. <div class="clear"></div>
  1009. `;
  1010.  
  1011. return c;
  1012. })();
  1013.  
  1014. const tabPanelContainer = (() => {
  1015. const c = document.createElement("div");
  1016.  
  1017. c.style = "width: 80vw;";
  1018.  
  1019. return c;
  1020. })();
  1021.  
  1022. const content = (() => {
  1023. const c = document.createElement("div");
  1024.  
  1025. c.append(tabContainer);
  1026. c.append(tabPanelContainer);
  1027.  
  1028. return c;
  1029. })();
  1030.  
  1031. const addModule = (() => {
  1032. const tc = tabContainer.getElementsByTagName("tr")[0];
  1033. const cc = tabPanelContainer;
  1034.  
  1035. return (module) => {
  1036. const tabBox = document.createElement("td");
  1037.  
  1038. tabBox.innerHTML = `<a href="javascript:void(0)" class="nobr silver">${module.name}</a>`;
  1039.  
  1040. const tab = tabBox.childNodes[0];
  1041.  
  1042. const toggle = () => {
  1043. Object.values(modules).forEach((item) => {
  1044. if (item.tab === tab) {
  1045. item.tab.className = "nobr";
  1046. item.content.style = "display: block";
  1047. item.refresh();
  1048. } else {
  1049. item.tab.className = "nobr silver";
  1050. item.content.style = "display: none";
  1051. }
  1052. });
  1053. };
  1054.  
  1055. tc.append(tabBox);
  1056. cc.append(module.content);
  1057.  
  1058. tab.onclick = toggle;
  1059.  
  1060. modules[module.name] = {
  1061. ...module,
  1062. tab,
  1063. toggle,
  1064. };
  1065.  
  1066. return modules[module.name];
  1067. };
  1068. })();
  1069.  
  1070. return {
  1071. content,
  1072. modules,
  1073. addModule,
  1074. };
  1075. })();
  1076.  
  1077. // 用户
  1078. const userModule = (() => {
  1079. const content = (() => {
  1080. const c = document.createElement("div");
  1081.  
  1082. c.style = "display: none";
  1083. c.innerHTML = `
  1084. <div class="filter-table-wrapper">
  1085. <table class="filter-table forumbox">
  1086. <thead>
  1087. <tr class="block_txt_c0">
  1088. <th class="c1" width="1">昵称</th>
  1089. <th class="c2">标记</th>
  1090. <th class="c3" width="1">过滤方式</th>
  1091. <th class="c4" width="1">操作</th>
  1092. </tr>
  1093. </thead>
  1094. <tbody></tbody>
  1095. </table>
  1096. </div>
  1097. `;
  1098.  
  1099. return c;
  1100. })();
  1101.  
  1102. const refresh = (() => {
  1103. const container = content.getElementsByTagName("tbody")[0];
  1104.  
  1105. const func = () => {
  1106. container.innerHTML = "";
  1107.  
  1108. Object.values(data.users).forEach((item) => {
  1109. const tc = document.createElement("tr");
  1110.  
  1111. tc.className = `row${
  1112. (container.querySelectorAll("TR").length % 2) + 1
  1113. }`;
  1114.  
  1115. tc.refresh = () => {
  1116. if (data.users[item.id]) {
  1117. tc.innerHTML = `
  1118. <td class="c1">
  1119. <a href="/nuke.php?func=ucp&uid=${
  1120. item.id
  1121. }" class="b nobr">[${
  1122. item.name ? "@" + item.name : "#" + item.id
  1123. }]</a>
  1124. </td>
  1125. <td class="c2">
  1126. ${item.tags
  1127. .map((tag) => {
  1128. if (data.tags[tag]) {
  1129. return `<b class="block_txt nobr" style="background:${data.tags[tag].color}; color:#fff; margin: 0.1em 0.2em;">${data.tags[tag].name}</b>`;
  1130. }
  1131. })
  1132. .join("")}
  1133. </td>
  1134. <td class="c3">
  1135. <div class="filter-table-button-group">
  1136. <button>${item.filterMode || FILTER_MODE[0]}</button>
  1137. </div>
  1138. </td>
  1139. <td class="c4">
  1140. <div class="filter-table-button-group">
  1141. <button>编辑</button>
  1142. <button>删除</button>
  1143. </div>
  1144. </td>
  1145. `;
  1146.  
  1147. const actions = tc.getElementsByTagName("button");
  1148.  
  1149. actions[0].onclick = () => {
  1150. data.users[item.id].filterMode = switchFilterMode(
  1151. data.users[item.id].filterMode || FILTER_MODE[0]
  1152. );
  1153.  
  1154. actions[0].innerHTML = data.users[item.id].filterMode;
  1155.  
  1156. saveData();
  1157. reFilter();
  1158. };
  1159.  
  1160. actions[1].onclick = () => {
  1161. editUser(item.id, item.name, tc.refresh);
  1162. };
  1163.  
  1164. actions[2].onclick = () => {
  1165. if (confirm("是否确认?")) {
  1166. delete data.users[item.id];
  1167. container.removeChild(tc);
  1168.  
  1169. saveData();
  1170. reFilter();
  1171. }
  1172. };
  1173. } else {
  1174. tc.remove();
  1175. }
  1176. };
  1177.  
  1178. tc.refresh();
  1179.  
  1180. container.appendChild(tc);
  1181. });
  1182. };
  1183.  
  1184. return func;
  1185. })();
  1186.  
  1187. return {
  1188. name: "用户",
  1189. content,
  1190. refresh,
  1191. };
  1192. })();
  1193.  
  1194. // 标记
  1195. const tagModule = (() => {
  1196. const content = (() => {
  1197. const c = document.createElement("div");
  1198.  
  1199. c.style = "display: none";
  1200. c.innerHTML = `
  1201. <div class="filter-table-wrapper">
  1202. <table class="filter-table forumbox">
  1203. <thead>
  1204. <tr class="block_txt_c0">
  1205. <th class="c1" width="1">标记</th>
  1206. <th class="c2">列表</th>
  1207. <th class="c3" width="1">过滤方式</th>
  1208. <th class="c4" width="1">操作</th>
  1209. </tr>
  1210. </thead>
  1211. <tbody></tbody>
  1212. </table>
  1213. </div>
  1214. `;
  1215.  
  1216. return c;
  1217. })();
  1218.  
  1219. const refresh = (() => {
  1220. const container = content.getElementsByTagName("tbody")[0];
  1221.  
  1222. const func = () => {
  1223. container.innerHTML = "";
  1224.  
  1225. Object.values(data.tags).forEach((item) => {
  1226. const tc = document.createElement("tr");
  1227.  
  1228. tc.className = `row${
  1229. (container.querySelectorAll("TR").length % 2) + 1
  1230. }`;
  1231.  
  1232. tc.innerHTML = `
  1233. <td class="c1">
  1234. <b class="block_txt nobr" style="background:${
  1235. item.color
  1236. }; color:#fff; margin: 0.1em 0.2em;">${item.name}</b>
  1237. </td>
  1238. <td class="c2">
  1239. <button>${
  1240. Object.values(data.users).filter((user) =>
  1241. user.tags.find((tag) => tag === item.id)
  1242. ).length
  1243. }
  1244. </button>
  1245. <div style="white-space: normal; display: none;">
  1246. ${Object.values(data.users)
  1247. .filter((user) =>
  1248. user.tags.find((tag) => tag === item.id)
  1249. )
  1250. .map(
  1251. (user) =>
  1252. `<a href="/nuke.php?func=ucp&uid=${
  1253. user.id
  1254. }" class="b nobr">[${
  1255. user.name ? "@" + user.name : "#" + user.id
  1256. }]</a>`
  1257. )
  1258. .join("")}
  1259. </div>
  1260. </td>
  1261. <td class="c3">
  1262. <div class="filter-table-button-group">
  1263. <button>${item.filterMode || FILTER_MODE[0]}</button>
  1264. </div>
  1265. </td>
  1266. <td class="c4">
  1267. <div class="filter-table-button-group">
  1268. <button>删除</button>
  1269. </div>
  1270. </td>
  1271. `;
  1272.  
  1273. const actions = tc.getElementsByTagName("button");
  1274.  
  1275. actions[0].onclick = (() => {
  1276. let hide = true;
  1277. return () => {
  1278. hide = !hide;
  1279. actions[0].nextElementSibling.style.display = hide
  1280. ? "none"
  1281. : "block";
  1282. };
  1283. })();
  1284.  
  1285. actions[1].onclick = () => {
  1286. data.tags[item.id].filterMode = switchFilterMode(
  1287. data.tags[item.id].filterMode || FILTER_MODE[0]
  1288. );
  1289.  
  1290. actions[1].innerHTML = data.tags[item.id].filterMode;
  1291.  
  1292. saveData();
  1293. reFilter();
  1294. };
  1295.  
  1296. actions[2].onclick = () => {
  1297. if (confirm("是否确认?")) {
  1298. delete data.tags[item.id];
  1299.  
  1300. Object.values(data.users).forEach((user) => {
  1301. const index = user.tags.findIndex((tag) => tag === item.id);
  1302. if (index >= 0) {
  1303. user.tags.splice(index, 1);
  1304. }
  1305. });
  1306.  
  1307. container.removeChild(tc);
  1308.  
  1309. saveData();
  1310. reFilter();
  1311. }
  1312. };
  1313.  
  1314. container.appendChild(tc);
  1315. });
  1316. };
  1317.  
  1318. return func;
  1319. })();
  1320.  
  1321. return {
  1322. name: "标记",
  1323. content,
  1324. refresh,
  1325. };
  1326. })();
  1327.  
  1328. // 关键字
  1329. const keywordModule = (() => {
  1330. const content = (() => {
  1331. const c = document.createElement("div");
  1332.  
  1333. c.style = "display: none";
  1334. c.innerHTML = `
  1335. <div class="filter-table-wrapper">
  1336. <table class="filter-table forumbox">
  1337. <thead>
  1338. <tr class="block_txt_c0">
  1339. <th class="c1">列表</th>
  1340. <th class="c2" width="1">过滤方式</th>
  1341. <th class="c3" width="1">包括内容</th>
  1342. <th class="c4" width="1">操作</th>
  1343. </tr>
  1344. </thead>
  1345. <tbody></tbody>
  1346. </table>
  1347. </div>
  1348. <div class="silver" style="margin-top: 10px;">支持正则表达式。比如同类型的可以写在一条规则内用&quot;|&quot;隔开,&quot;ABC|DEF&quot;即为屏蔽带有ABC或者DEF的内容。</div>
  1349. `;
  1350.  
  1351. return c;
  1352. })();
  1353.  
  1354. const refresh = (() => {
  1355. const container = content.getElementsByTagName("tbody")[0];
  1356.  
  1357. const func = () => {
  1358. container.innerHTML = "";
  1359.  
  1360. Object.values(data.keywords).forEach((item) => {
  1361. const tc = document.createElement("tr");
  1362.  
  1363. tc.className = `row${
  1364. (container.querySelectorAll("TR").length % 2) + 1
  1365. }`;
  1366.  
  1367. tc.innerHTML = `
  1368. <td class="c1">
  1369. <div class="filter-input-wrapper">
  1370. <input value="${item.keyword || ""}" />
  1371. </div>
  1372. </td>
  1373. <td class="c2">
  1374. <div class="filter-table-button-group">
  1375. <button>${item.filterMode || FILTER_MODE[0]}</button>
  1376. </div>
  1377. </td>
  1378. <td class="c3">
  1379. <div style="text-align: center;">
  1380. <input type="checkbox" ${
  1381. item.filterLevel ? `checked="checked"` : ""
  1382. } />
  1383. </div>
  1384. </td>
  1385. <td class="c4">
  1386. <div class="filter-table-button-group">
  1387. <button>保存</button>
  1388. <button>删除</button>
  1389. </div>
  1390. </td>
  1391. `;
  1392.  
  1393. const inputElement = tc.querySelector("INPUT");
  1394. const levelElement = tc.querySelector(`INPUT[type="checkbox"]`);
  1395. const actions = tc.getElementsByTagName("button");
  1396.  
  1397. actions[0].onclick = () => {
  1398. actions[0].innerHTML = switchFilterMode(actions[0].innerHTML);
  1399. };
  1400.  
  1401. actions[1].onclick = () => {
  1402. if (inputElement.value) {
  1403. data.keywords[item.id] = {
  1404. id: item.id,
  1405. keyword: inputElement.value,
  1406. filterMode: actions[0].innerHTML,
  1407. filterLevel: levelElement.checked ? 1 : 0,
  1408. };
  1409.  
  1410. saveData();
  1411. refresh();
  1412. }
  1413. };
  1414.  
  1415. actions[2].onclick = () => {
  1416. if (confirm("是否确认?")) {
  1417. delete data.keywords[item.id];
  1418.  
  1419. saveData();
  1420. refresh();
  1421. }
  1422. };
  1423.  
  1424. container.appendChild(tc);
  1425. });
  1426.  
  1427. {
  1428. const tc = document.createElement("tr");
  1429.  
  1430. tc.className = `row${
  1431. (container.querySelectorAll("TR").length % 2) + 1
  1432. }`;
  1433.  
  1434. tc.innerHTML = `
  1435. <td class="c1">
  1436. <div class="filter-input-wrapper">
  1437. <input value="" />
  1438. </div>
  1439. </td>
  1440. <td class="c2">
  1441. <div class="filter-table-button-group">
  1442. <button>${FILTER_MODE[0]}</button>
  1443. </div>
  1444. </td>
  1445. <td class="c3">
  1446. <div style="text-align: center;">
  1447. <input type="checkbox" />
  1448. </div>
  1449. </td>
  1450. <td class="c4">
  1451. <div class="filter-table-button-group">
  1452. <button>添加</button>
  1453. </div>
  1454. </td>
  1455. `;
  1456.  
  1457. const inputElement = tc.querySelector("INPUT");
  1458. const levelElement = tc.querySelector(`INPUT[type="checkbox"]`);
  1459. const actions = tc.getElementsByTagName("button");
  1460.  
  1461. actions[0].onclick = () => {
  1462. actions[0].innerHTML = switchFilterMode(actions[0].innerHTML);
  1463. };
  1464.  
  1465. actions[1].onclick = () => {
  1466. if (inputElement.value) {
  1467. addKeyword(
  1468. inputElement.value,
  1469. actions[0].innerHTML,
  1470. levelElement.checked ? 1 : 0
  1471. );
  1472.  
  1473. saveData();
  1474. refresh();
  1475. }
  1476. };
  1477.  
  1478. container.appendChild(tc);
  1479. }
  1480. };
  1481.  
  1482. return func;
  1483. })();
  1484.  
  1485. return {
  1486. name: "关键字",
  1487. content,
  1488. refresh,
  1489. };
  1490. })();
  1491.  
  1492. // 通用设置
  1493. const commonModule = (() => {
  1494. const content = (() => {
  1495. const c = document.createElement("div");
  1496.  
  1497. c.style = "display: none";
  1498.  
  1499. return c;
  1500. })();
  1501.  
  1502. const refresh = (() => {
  1503. const container = content;
  1504.  
  1505. const func = () => {
  1506. container.innerHTML = "";
  1507.  
  1508. // 默认过滤方式
  1509. {
  1510. const tc = document.createElement("div");
  1511.  
  1512. tc.innerHTML += `
  1513. <div>默认过滤方式</div>
  1514. <div></div>
  1515. <div class="silver" style="margin-top: 10px;">${FILTER_TIPS}</div>
  1516. `;
  1517.  
  1518. ["标记", "遮罩", "隐藏"].forEach((item, index) => {
  1519. const ele = document.createElement("SPAN");
  1520.  
  1521. ele.innerHTML += `
  1522. <input id="s-fm-${index}" type="radio" name="filterType" ${
  1523. data.options.filterMode === item && "checked"
  1524. }>
  1525. <label for="s-fm-${index}" style="cursor: pointer;">${item}</label>
  1526. `;
  1527.  
  1528. const inp = ele.querySelector("input");
  1529.  
  1530. inp.onchange = () => {
  1531. if (inp.checked) {
  1532. data.options.filterMode = item;
  1533. saveData();
  1534. reFilter();
  1535. }
  1536. };
  1537.  
  1538. tc.querySelectorAll("div")[1].append(ele);
  1539. });
  1540.  
  1541. container.appendChild(tc);
  1542. }
  1543.  
  1544. // 小号过滤(时间)
  1545. {
  1546. const tc = document.createElement("div");
  1547.  
  1548. tc.innerHTML += `
  1549. <br/>
  1550. <div>
  1551. 隐藏注册(不可用)时间小于<input value="${
  1552. (data.options.filterRegdateLimit || 0) / 86400000
  1553. }" maxLength="4" style="width: 48px;" />天的用户
  1554. <button>确认</button>
  1555. </div>
  1556. `;
  1557.  
  1558. const actions = tc.getElementsByTagName("button");
  1559.  
  1560. actions[0].onclick = () => {
  1561. const v = actions[0].previousElementSibling.value;
  1562.  
  1563. const n = Number(v) || 0;
  1564.  
  1565. data.options.filterRegdateLimit = n < 0 ? 0 : n * 86400000;
  1566.  
  1567. saveData();
  1568. reFilter();
  1569. };
  1570.  
  1571. container.appendChild(tc);
  1572. }
  1573.  
  1574. // 小号过滤(发帖数)
  1575. {
  1576. const tc = document.createElement("div");
  1577.  
  1578. tc.innerHTML += `
  1579. <br/>
  1580. <div>
  1581. 隐藏发帖数量小于<input value="${
  1582. data.options.filterPostnumLimit || 0
  1583. }" maxLength="5" style="width: 48px;" />贴的用户
  1584. <button>确认</button>
  1585. </div>
  1586. `;
  1587.  
  1588. const actions = tc.getElementsByTagName("button");
  1589.  
  1590. actions[0].onclick = () => {
  1591. const v = actions[0].previousElementSibling.value;
  1592.  
  1593. const n = Number(v) || 0;
  1594.  
  1595. data.options.filterPostnumLimit = n < 0 ? 0 : n;
  1596.  
  1597. saveData();
  1598. reFilter();
  1599. };
  1600.  
  1601. container.appendChild(tc);
  1602. }
  1603.  
  1604. // 删除没有标记的用户
  1605. {
  1606. const tc = document.createElement("div");
  1607.  
  1608. tc.innerHTML += `
  1609. <br/>
  1610. <div>
  1611. <button>删除没有标记的用户</button>
  1612. </div>
  1613. `;
  1614.  
  1615. const actions = tc.getElementsByTagName("button");
  1616.  
  1617. actions[0].onclick = () => {
  1618. if (confirm("是否确认?")) {
  1619. Object.values(data.users).forEach((item) => {
  1620. if (item.tags.length === 0) {
  1621. delete data.users[item.id];
  1622. }
  1623. });
  1624.  
  1625. saveData();
  1626. reFilter();
  1627. }
  1628. };
  1629.  
  1630. container.appendChild(tc);
  1631. }
  1632.  
  1633. // 删除没有用户的标记
  1634. {
  1635. const tc = document.createElement("div");
  1636.  
  1637. tc.innerHTML += `
  1638. <br/>
  1639. <div>
  1640. <button>删除没有用户的标记</button>
  1641. </div>
  1642. `;
  1643.  
  1644. const actions = tc.getElementsByTagName("button");
  1645.  
  1646. actions[0].onclick = () => {
  1647. if (confirm("是否确认?")) {
  1648. Object.values(data.tags).forEach((item) => {
  1649. if (
  1650. Object.values(data.users).filter((user) =>
  1651. user.tags.find((tag) => tag === item.id)
  1652. ).length === 0
  1653. ) {
  1654. delete data.tags[item.id];
  1655. }
  1656. });
  1657.  
  1658. saveData();
  1659. reFilter();
  1660. }
  1661. };
  1662.  
  1663. container.appendChild(tc);
  1664. }
  1665. };
  1666.  
  1667. return func;
  1668. })();
  1669.  
  1670. return {
  1671. name: "通用设置",
  1672. content,
  1673. refresh,
  1674. };
  1675. })();
  1676.  
  1677. u.addModule(userModule).toggle();
  1678. u.addModule(tagModule);
  1679. u.addModule(keywordModule);
  1680. u.addModule(commonModule);
  1681.  
  1682. // 增加菜单项
  1683. (() => {
  1684. const title = "过滤设置";
  1685.  
  1686. let window;
  1687. const container = document.createElement("DIV");
  1688.  
  1689. container.className = `td`;
  1690. container.innerHTML = `<a class="mmdefault" href="javascript: void(0);" style="white-space: nowrap;">屏蔽</a>`;
  1691.  
  1692. const content = container.querySelector("A");
  1693.  
  1694. const anchor = document.querySelector("#mainmenu .td:last-child");
  1695.  
  1696. anchor.before(container);
  1697.  
  1698. content.onclick = () => {
  1699. if (window === undefined) {
  1700. window = n.createCommmonWindow();
  1701. }
  1702.  
  1703. window._.addContent(null);
  1704. window._.addTitle(title);
  1705. window._.addContent(u.content);
  1706. window._.show();
  1707. };
  1708. })();
  1709.  
  1710. // 执行过滤
  1711. (() => {
  1712. const hookFunction = (object, functionName, callback) => {
  1713. ((originalFunction) => {
  1714. object[functionName] = function () {
  1715. const returnValue = originalFunction.apply(this, arguments);
  1716.  
  1717. callback.apply(this, [returnValue, originalFunction, arguments]);
  1718.  
  1719. return returnValue;
  1720. };
  1721. })(object[functionName]);
  1722. };
  1723.  
  1724. const initialized = {
  1725. topicArg: false,
  1726. postArg: false,
  1727. };
  1728.  
  1729. hookFunction(n, "eval", () => {
  1730. if (Object.values(initialized).findIndex((item) => item === false) < 0) {
  1731. return;
  1732. }
  1733.  
  1734. if (n.topicArg && initialized.topicArg === false) {
  1735. hookFunction(n.topicArg, "add", reFilter);
  1736.  
  1737. initialized.topicArg = true;
  1738. }
  1739.  
  1740. if (n.postArg && initialized.postArg === false) {
  1741. hookFunction(n.postArg, "proc", reFilter);
  1742.  
  1743. initialized.postArg = true;
  1744. }
  1745. });
  1746.  
  1747. reFilter();
  1748. })();
  1749. })(commonui, __CURRENT_UID);

QingJ © 2025

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