NGA Filter

troll must die

当前为 2021-10-25 提交的版本,查看 最新版本

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

QingJ © 2025

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