NGA Filter

troll must die

当前为 2023-05-11 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name NGA Filter
  3. // @namespace https://greasyfork.org/users/263018
  4. // @version 1.9.6
  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. // @grant GM_registerMenuCommand
  17.  
  18. // @noframes
  19. // ==/UserScript==
  20.  
  21. ((n, self) => {
  22. if (n === undefined) return;
  23.  
  24. // KEY
  25. const DATA_KEY = "NGAFilter";
  26. const USER_AGENT_KEY = "USER_AGENT_KEY";
  27.  
  28. // User Agent
  29. const USER_AGENT = (() => {
  30. const data = GM_getValue(USER_AGENT_KEY) || "Nga_Official";
  31.  
  32. GM_registerMenuCommand(`修改UA${data}`, () => {
  33. const value = prompt("修改UA", data);
  34.  
  35. if (value) {
  36. GM_setValue(USER_AGENT_KEY, value);
  37.  
  38. location.reload();
  39. }
  40. });
  41.  
  42. return data;
  43. })();
  44.  
  45. // 简单的统一请求
  46. const request = (url, config = {}) =>
  47. fetch(url, {
  48. headers: {
  49. "X-User-Agent": USER_AGENT,
  50. },
  51. ...config,
  52. });
  53.  
  54. // 过滤提示
  55. const FILTER_TIPS =
  56. "过滤顺序:用户 &gt; 标记 &gt; 关键字 &gt; 属地<br/>过滤级别:隐藏 &gt; 遮罩 &gt; 标记 &gt; 继承 &gt; 显示<br/>相同类型按最高级别过滤";
  57.  
  58. // 过滤方式
  59. const FILTER_MODE = ["继承", "标记", "遮罩", "隐藏", "显示"];
  60.  
  61. // 切换过滤方式
  62. const switchFilterMode = (value) => {
  63. const next = FILTER_MODE.indexOf(value) + 1;
  64.  
  65. if (next >= FILTER_MODE.length) {
  66. return FILTER_MODE[0];
  67. }
  68.  
  69. return FILTER_MODE[next];
  70. };
  71.  
  72. // 数据
  73. const data = (() => {
  74. const d = {
  75. tags: {},
  76. users: {},
  77. keywords: {},
  78. locations: {},
  79. options: {
  80. filterRegdateLimit: 0,
  81. filterPostnumLimit: 0,
  82. filterReputationLimit: NaN,
  83. filterMode: "隐藏",
  84. },
  85. };
  86.  
  87. const v = GM_getValue(DATA_KEY);
  88.  
  89. if (typeof v !== "object") {
  90. return d;
  91. }
  92.  
  93. return Object.assign(d, v);
  94. })();
  95.  
  96. // 保存数据
  97. const saveData = () => {
  98. GM_setValue(DATA_KEY, data);
  99. };
  100.  
  101. // 增加标记
  102. const addTag = (name) => {
  103. const tag = Object.values(data.tags).find((item) => item.name === name);
  104.  
  105. if (tag) return tag.id;
  106.  
  107. const id =
  108. Math.max(...Object.values(data.tags).map((item) => item.id), 0) + 1;
  109.  
  110. const hash = (() => {
  111. let h = 5381;
  112. for (var i = 0; i < name.length; i++) {
  113. h = ((h << 5) + h + name.charCodeAt(i)) & 0xffffffff;
  114. }
  115. return h;
  116. })();
  117.  
  118. const hex = Math.abs(hash).toString(16) + "000000";
  119.  
  120. const hsv = [
  121. `0x${hex.substring(2, 4)}` / 255,
  122. `0x${hex.substring(2, 4)}` / 255 / 2 + 0.25,
  123. `0x${hex.substring(4, 6)}` / 255 / 2 + 0.25,
  124. ];
  125.  
  126. const rgb = n.hsvToRgb(hsv[0], hsv[1], hsv[2]);
  127.  
  128. const color = ["#", ...rgb].reduce((a, b) => {
  129. return a + ("0" + b.toString(16)).slice(-2);
  130. });
  131.  
  132. data.tags[id] = {
  133. id,
  134. name,
  135. color,
  136. filterMode: FILTER_MODE[0],
  137. };
  138.  
  139. saveData();
  140.  
  141. return id;
  142. };
  143.  
  144. // 增加用户
  145. const addUser = (id, name = null, tags = [], filterMode = FILTER_MODE[0]) => {
  146. if (data.users[id]) return data.users[id];
  147.  
  148. data.users[id] = {
  149. id,
  150. name,
  151. tags,
  152. filterMode,
  153. };
  154.  
  155. saveData();
  156.  
  157. return data.users[id];
  158. };
  159.  
  160. // 增加关键字
  161. const addKeyword = (
  162. keyword,
  163. filterMode = FILTER_MODE[0],
  164. filterLevel = 0
  165. ) => {
  166. const id =
  167. Math.max(...Object.values(data.keywords).map((item) => item.id), 0) + 1;
  168.  
  169. data.keywords[id] = {
  170. id,
  171. keyword,
  172. filterMode,
  173. filterLevel,
  174. };
  175.  
  176. saveData();
  177.  
  178. return id;
  179. };
  180.  
  181. // 增加属地
  182. const addLocation = (keyword, filterMode = FILTER_MODE[0]) => {
  183. const id =
  184. Math.max(...Object.values(data.locations).map((item) => item.id), 0) + 1;
  185.  
  186. data.locations[id] = {
  187. id,
  188. keyword,
  189. filterMode,
  190. };
  191.  
  192. saveData();
  193.  
  194. return id;
  195. };
  196.  
  197. // 旧版本数据迁移
  198. {
  199. const dataKey = "troll_data";
  200. const modeKey = "troll_mode";
  201. const keywordKey = "troll_keyword";
  202.  
  203. if (localStorage.getItem(dataKey)) {
  204. let trollMap = (function () {
  205. try {
  206. return JSON.parse(localStorage.getItem(dataKey)) || {};
  207. } catch (e) {}
  208.  
  209. return {};
  210. })();
  211.  
  212. let filterMode = ~~localStorage.getItem(modeKey);
  213.  
  214. let filterKeyword = localStorage.getItem(keywordKey) || "";
  215.  
  216. // 整理标签
  217. [...new Set(Object.values(trollMap).flat())].forEach((item) =>
  218. addTag(item)
  219. );
  220.  
  221. // 整理用户
  222. Object.keys(trollMap).forEach((item) => {
  223. addUser(
  224. item,
  225. null,
  226. (typeof trollMap[item] === "object" ? trollMap[item] : []).map(
  227. (tag) => addTag(tag)
  228. )
  229. );
  230. });
  231.  
  232. data.options.filterMode = filterMode ? "隐藏" : "标记";
  233. data.options.keyword = filterKeyword;
  234.  
  235. localStorage.removeItem(dataKey);
  236. localStorage.removeItem(modeKey);
  237. localStorage.removeItem(keywordKey);
  238.  
  239. saveData();
  240. }
  241.  
  242. // v1.1.0 -> v1.1.1
  243. {
  244. Object.values(data.users).forEach(({ id, name, tags, enabled }) => {
  245. if (enabled !== undefined) {
  246. data.users[id] = {
  247. id,
  248. name,
  249. tags,
  250. filterMode: enabled ? "继承" : "显示",
  251. };
  252. }
  253. });
  254.  
  255. Object.values(data.tags).forEach(({ id, name, color, enabled }) => {
  256. if (enabled !== undefined) {
  257. data.tags[id] = {
  258. id,
  259. name,
  260. color,
  261. filterMode: enabled ? "继承" : "显示",
  262. };
  263. }
  264. });
  265.  
  266. if (data.options.filterMode === 0) {
  267. data.options.filterMode = "隐藏";
  268. } else if (data.options.filterMode === 1) {
  269. data.options.filterMode = "标记";
  270. }
  271.  
  272. saveData();
  273. }
  274.  
  275. // v1.2.x -> v1.3.0
  276. {
  277. if (data.options.keyword) {
  278. addKeyword(data.options.keyword);
  279.  
  280. delete data.options.keyword;
  281.  
  282. saveData();
  283. }
  284. }
  285. }
  286.  
  287. // 编辑用户标记
  288. const editUser = (() => {
  289. let window;
  290. return (uid, name, callback) => {
  291. if (window === undefined) {
  292. window = n.createCommmonWindow();
  293. }
  294.  
  295. const user = data.users[uid];
  296.  
  297. const content = document.createElement("div");
  298.  
  299. const size = Math.floor((screen.width * 0.8) / 200);
  300.  
  301. const items = Object.values(data.tags).map(
  302. (tag, index) => `
  303. <td class="c1">
  304. <label for="s-tag-${index}" style="display: block; cursor: pointer;">
  305. <b class="block_txt nobr" style="background:${
  306. tag.color
  307. }; color:#fff; margin: 0.1em 0.2em;">${tag.name}</b>
  308. </label>
  309. </td>
  310. <td class="c2" width="1">
  311. <input id="s-tag-${index}" type="checkbox" value="${tag.id}" ${
  312. user && user.tags.find((item) => item === tag.id) && "checked"
  313. }/>
  314. </td>
  315. `
  316. );
  317.  
  318. const rows = [...new Array(Math.ceil(items.length / size))].map(
  319. (item, index) =>
  320. `
  321. <tr class="row${(index % 2) + 1}">
  322. ${items.slice(size * index, size * (index + 1)).join("")}
  323. </tr>
  324. `
  325. );
  326.  
  327. content.className = "w100";
  328. content.innerHTML = `
  329. <div class="filter-table-wrapper" style="width: 80vw;">
  330. <table class="filter-table forumbox">
  331. <tbody>
  332. ${rows.join("")}
  333. </tbody>
  334. </table>
  335. </div>
  336. <div style="margin: 10px 0;">
  337. <input placeholder="一次性添加多个标记用&quot;|&quot;隔开,不会添加重名标记" style="width: -webkit-fill-available;" />
  338. </div>
  339. <div style="margin: 10px 0;">
  340. <span>过滤方式:</span>
  341. <button>${(user && user.filterMode) || FILTER_MODE[0]}</button>
  342. <div class="right_">
  343. <button>删除</button>
  344. <button>保存</button>
  345. </div>
  346. </div>
  347. <div class="silver" style="margin-top: 5px;">${FILTER_TIPS}</div>
  348. `;
  349.  
  350. const actions = content.getElementsByTagName("button");
  351.  
  352. actions[0].onclick = () => {
  353. actions[0].innerText = switchFilterMode(
  354. actions[0].innerText || FILTER_MODE[0]
  355. );
  356. };
  357.  
  358. actions[1].onclick = () => {
  359. if (confirm("是否确认?")) {
  360. delete data.users[uid];
  361.  
  362. saveData();
  363.  
  364. callback && callback();
  365.  
  366. window._.hide();
  367. }
  368. };
  369.  
  370. actions[2].onclick = () => {
  371. if (confirm("是否确认?")) {
  372. const values = [...content.getElementsByTagName("input")];
  373. const newTags = values[values.length - 1].value
  374. .split("|")
  375. .filter((item) => item.length)
  376. .map((item) => addTag(item));
  377. const tags = [
  378. ...new Set(
  379. values
  380. .filter((item) => item.type === "checkbox" && item.checked)
  381. .map((item) => ~~item.value)
  382. .concat(newTags)
  383. ),
  384. ].sort();
  385.  
  386. if (user) {
  387. user.tags = tags;
  388. user.filterMode = actions[0].innerText;
  389. } else {
  390. addUser(uid, name, tags, actions[0].innerText);
  391. }
  392.  
  393. saveData();
  394.  
  395. callback && callback();
  396.  
  397. window._.hide();
  398. }
  399. };
  400.  
  401. if (user === undefined) {
  402. actions[1].style = "display: none;";
  403. }
  404.  
  405. window._.addContent(null);
  406. window._.addTitle(`编辑标记 - ${name ? name : "#" + uid}`);
  407. window._.addContent(content);
  408. window._.show();
  409. };
  410. })();
  411.  
  412. // 猎巫
  413. const witchHunter = (() => {
  414. const key = "WITCH_HUNTER";
  415.  
  416. const data = GM_getValue(key) || {};
  417.  
  418. const add = async (fid, label) => {
  419. if (Object.values(data).find((item) => item.fid === fid)) {
  420. alert("已有相同版面ID");
  421. return;
  422. }
  423.  
  424. const info = await new Promise((resolve) => {
  425. request(`/thread.php?lite=js&fid=${fid}`)
  426. .then((res) => res.blob())
  427. .then((blob) => {
  428. const reader = new FileReader();
  429.  
  430. reader.onload = () => {
  431. const text = reader.result;
  432. const result = JSON.parse(
  433. text.replace("window.script_muti_get_var_store=", "")
  434. );
  435.  
  436. resolve(result.data);
  437. };
  438.  
  439. reader.readAsText(blob, "GBK");
  440. })
  441. .catch(() => {
  442. resolve({});
  443. });
  444. });
  445.  
  446. if (info.__F === undefined) {
  447. alert("版面ID有误");
  448. return;
  449. }
  450.  
  451. const name = info.__F.name;
  452.  
  453. const id = Math.max(...Object.values(data).map((item) => item.id), 0) + 1;
  454.  
  455. const hash = (() => {
  456. let h = 5381;
  457. for (var i = 0; i < label.length; i++) {
  458. h = ((h << 5) + h + label.charCodeAt(i)) & 0xffffffff;
  459. }
  460. return h;
  461. })();
  462.  
  463. const hex = Math.abs(hash).toString(16) + "000000";
  464.  
  465. const hsv = [
  466. `0x${hex.substring(2, 4)}` / 255,
  467. `0x${hex.substring(2, 4)}` / 255 / 2 + 0.25,
  468. `0x${hex.substring(4, 6)}` / 255 / 2 + 0.25,
  469. ];
  470.  
  471. const rgb = n.hsvToRgb(hsv[0], hsv[1], hsv[2]);
  472.  
  473. const color = ["#", ...rgb].reduce((a, b) => {
  474. return a + ("0" + b.toString(16)).slice(-2);
  475. });
  476.  
  477. data[id] = {
  478. id,
  479. fid,
  480. name,
  481. label,
  482. color,
  483. };
  484.  
  485. GM_setValue(key, data);
  486. };
  487.  
  488. const remove = (id) => {
  489. delete data[id];
  490.  
  491. GM_setValue(key, data);
  492. };
  493.  
  494. const run = (uid, element) => {
  495. if (uid < 0) {
  496. return;
  497. }
  498.  
  499. Promise.all(
  500. Object.values(data).map(async (item) => {
  501. const api = `/thread.php?lite=js&fid=${item.fid}&authorid=${uid}`;
  502.  
  503. const verify =
  504. (await new Promise((resolve) => {
  505. request(api)
  506. .then((res) => res.blob())
  507. .then((blob) => {
  508. const reader = new FileReader();
  509.  
  510. reader.onload = () => {
  511. const text = reader.result;
  512. const result = JSON.parse(
  513. text.replace("window.script_muti_get_var_store=", "")
  514. );
  515.  
  516. if (result.error) {
  517. resolve(false);
  518. return;
  519. }
  520.  
  521. resolve(true);
  522. };
  523.  
  524. reader.readAsText(blob, "GBK");
  525. })
  526. .catch(() => {
  527. resolve(false);
  528. });
  529. })) ||
  530. (await new Promise((resolve) => {
  531. request(`${api}&searchpost=1`)
  532. .then((res) => res.blob())
  533. .then((blob) => {
  534. const reader = new FileReader();
  535.  
  536. reader.onload = () => {
  537. const text = reader.result;
  538. const result = JSON.parse(
  539. text.replace("window.script_muti_get_var_store=", "")
  540. );
  541.  
  542. if (result.error) {
  543. resolve(false);
  544. return;
  545. }
  546.  
  547. resolve(true);
  548. };
  549.  
  550. reader.readAsText(blob, "GBK");
  551. })
  552. .catch(() => {
  553. resolve(false);
  554. });
  555. }));
  556.  
  557. if (verify) {
  558. return item;
  559. }
  560. })
  561. )
  562. .then((res) => res.filter((item) => item))
  563. .then((res) => {
  564. res
  565. .filter(
  566. (current, index) =>
  567. res.findIndex((item) => item.label === current.label) === index
  568. )
  569. .forEach((item) => {
  570. element.style.display = "block";
  571. element.innerHTML += `<b class="block_txt nobr" style="background:${item.color}; color:#fff; margin: 0.1em 0.2em;">${item.label}</b>`;
  572. });
  573. });
  574. };
  575.  
  576. return {
  577. add,
  578. remove,
  579. run,
  580. data,
  581. };
  582. })();
  583.  
  584. // 小号过滤和声望过滤
  585. const getFilterModeByUserInfo = async (userInfo, reputation) => {
  586. const filterRegdateLimit = data.options.filterRegdateLimit || 0;
  587.  
  588. const filterPostnumLimit = data.options.filterPostnumLimit || 0;
  589.  
  590. const filterReputationLimit = data.options.filterReputationLimit || NaN;
  591.  
  592. if (userInfo) {
  593. const { regdate, postnum } = userInfo;
  594.  
  595. if (
  596. filterRegdateLimit > 0 &&
  597. regdate * 1000 > new Date() - filterRegdateLimit
  598. ) {
  599. return true;
  600. }
  601.  
  602. if (filterPostnumLimit > 0 && postnum < filterPostnumLimit) {
  603. return true;
  604. }
  605. }
  606.  
  607. if (Number.isNaN(filterReputationLimit) === false) {
  608. if (reputation < filterReputationLimit) {
  609. return true;
  610. }
  611. }
  612.  
  613. return false;
  614. };
  615.  
  616. // 判断过滤方式
  617. const getFilterMode = async (uid, subject, content) => {
  618. let result = -1;
  619.  
  620. const user = data.users[uid];
  621.  
  622. const tags = user ? user.tags.map((tag) => data.tags[tag]) : [];
  623.  
  624. const keywords = Object.values(data.keywords);
  625.  
  626. const locations = Object.values(data.locations);
  627.  
  628. if (uid && uid > 0) {
  629. const userInfo = n.userInfo.users[uid];
  630.  
  631. const reputation = (() => {
  632. const reputations = n.userInfo.reputations;
  633.  
  634. if (reputations) {
  635. for (let fid in reputations) {
  636. return reputations[fid][uid] || 0;
  637. }
  638. }
  639.  
  640. return NaN;
  641. })();
  642.  
  643. if (await getFilterModeByUserInfo(userInfo, reputation)) {
  644. return FILTER_MODE.indexOf("隐藏");
  645. }
  646. }
  647.  
  648. if (user) {
  649. const filterMode = FILTER_MODE.indexOf(user.filterMode);
  650.  
  651. if (filterMode > 0) {
  652. return filterMode;
  653. }
  654.  
  655. result = filterMode;
  656. }
  657.  
  658. if (tags.length) {
  659. const filterMode = (() => {
  660. if (tags.some((tag) => tag.filterMode !== "显示")) {
  661. return tags
  662. .filter((tag) => tag.filterMode !== "显示")
  663. .map((tag) => FILTER_MODE.indexOf(tag.filterMode) || 0)
  664. .sort((a, b) => b - a)[0];
  665. }
  666.  
  667. return FILTER_MODE.indexOf("显示");
  668. })();
  669.  
  670. if (filterMode > 0) {
  671. return filterMode;
  672. }
  673.  
  674. result = filterMode;
  675. }
  676.  
  677. if (keywords.length) {
  678. const filterMode = (() => {
  679. const sR = (() => {
  680. if (subject) {
  681. const r = keywords
  682. .filter((item) => item.keyword && item.filterMode !== "显示")
  683. .filter((item) => (item.filterLevel || 0) >= 0)
  684. .sort(
  685. (a, b) =>
  686. FILTER_MODE.indexOf(b.filterMode) -
  687. FILTER_MODE.indexOf(a.filterMode)
  688. )
  689. .find((item) => subject.search(item.keyword) >= 0);
  690.  
  691. if (r) {
  692. return FILTER_MODE.indexOf(r.filterMode);
  693. }
  694. }
  695.  
  696. return -1;
  697. })();
  698.  
  699. const cR = (() => {
  700. if (content) {
  701. const r = keywords
  702. .filter((item) => item.keyword && item.filterMode !== "显示")
  703. .filter((item) => (item.filterLevel || 0) >= 1)
  704. .sort(
  705. (a, b) =>
  706. FILTER_MODE.indexOf(b.filterMode) -
  707. FILTER_MODE.indexOf(a.filterMode)
  708. )
  709. .find((item) => content.search(item.keyword) >= 0);
  710.  
  711. if (r) {
  712. return FILTER_MODE.indexOf(r.filterMode);
  713. }
  714. }
  715.  
  716. return -1;
  717. })();
  718.  
  719. return Math.max(sR, cR, result);
  720. })();
  721.  
  722. if (filterMode > 0) {
  723. return filterMode;
  724. }
  725.  
  726. result = filterMode;
  727. }
  728.  
  729. if (locations.length) {
  730. const { ipLoc } = await new Promise((resolve) => {
  731. request(`/nuke.php?lite=js&__lib=ucp&__act=get&uid=${uid}`)
  732. .then((res) => res.blob())
  733. .then((blob) => {
  734. const reader = new FileReader();
  735.  
  736. reader.onload = () => {
  737. const text = reader.result;
  738. const result = JSON.parse(
  739. text.replace("window.script_muti_get_var_store=", "")
  740. );
  741.  
  742. resolve(result.data[0]);
  743. };
  744.  
  745. reader.readAsText(blob, "GBK");
  746. })
  747. .catch(() => {
  748. resolve({});
  749. });
  750. });
  751.  
  752. if (ipLoc) {
  753. const filterMode = (() => {
  754. const r = locations
  755. .filter((item) => item.keyword && item.filterMode !== "显示")
  756. .sort(
  757. (a, b) =>
  758. FILTER_MODE.indexOf(b.filterMode) -
  759. FILTER_MODE.indexOf(a.filterMode)
  760. )
  761. .find((item) => ipLoc.search(item.keyword) >= 0);
  762.  
  763. if (r) {
  764. return FILTER_MODE.indexOf(r.filterMode);
  765. }
  766.  
  767. return Math.max(r, result);
  768. })();
  769.  
  770. if (filterMode > 0) {
  771. return filterMode;
  772. }
  773.  
  774. result = filterMode;
  775. }
  776. }
  777.  
  778. return result;
  779. };
  780.  
  781. // 根据 TID 获取过滤方式
  782. const getFilterModeByTopic = async (tid) => {
  783. return await new Promise((resolve, reject) => {
  784. const api = `/read.php?tid=${tid}`;
  785.  
  786. request(api)
  787. .then((res) => res.blob())
  788. .then((blob) => {
  789. const getLastIndex = (content, position) => {
  790. if (position >= 0) {
  791. let nextIndex = position + 1;
  792.  
  793. while (nextIndex < content.length) {
  794. if (content[nextIndex] === "}") {
  795. return nextIndex;
  796. }
  797.  
  798. if (content[nextIndex] === "{") {
  799. nextIndex = getLastIndex(content, nextIndex);
  800.  
  801. if (nextIndex < 0) {
  802. break;
  803. }
  804. }
  805.  
  806. nextIndex = nextIndex + 1;
  807. }
  808. }
  809.  
  810. return -1;
  811. };
  812.  
  813. const reader = new FileReader();
  814.  
  815. reader.onload = async () => {
  816. const parser = new DOMParser();
  817.  
  818. const doc = parser.parseFromString(reader.result, "text/html");
  819.  
  820. const html = doc.body.innerHTML;
  821.  
  822. // 验证帖子正常
  823. const verify = doc.querySelector("#m_posts");
  824.  
  825. if (verify) {
  826. // 取得顶楼 UID
  827. const uid = (() => {
  828. const ele = doc.querySelector("#postauthor0");
  829.  
  830. if (ele) {
  831. const res = ele.getAttribute("href").match(/uid=(\S+)/);
  832.  
  833. if (res) {
  834. return res[1];
  835. }
  836. }
  837.  
  838. return 0;
  839. })();
  840.  
  841. // 取得顶楼标题
  842. const subject = doc.querySelector("#postsubject0").innerHTML;
  843.  
  844. // 取得顶楼内容
  845. const content = doc.querySelector("#postcontent0").innerHTML;
  846.  
  847. if (uid && uid > 0) {
  848. // 取得用户信息
  849. const userInfo = (() => {
  850. // 起始JSON
  851. const str = `"${uid}":{`;
  852.  
  853. // 起始下标
  854. const index = html.indexOf(str) + str.length;
  855.  
  856. // 结尾下标
  857. const lastIndex = getLastIndex(html, index);
  858.  
  859. if (lastIndex >= 0) {
  860. try {
  861. return JSON.parse(
  862. `{${html.substring(index, lastIndex)}}`
  863. );
  864. } catch {}
  865. }
  866.  
  867. return null;
  868. })();
  869.  
  870. // 取得用户声望
  871. const reputation = (() => {
  872. const reputations = (() => {
  873. // 起始JSON
  874. const str = `"__REPUTATIONS":{`;
  875.  
  876. // 起始下标
  877. const index = html.indexOf(str) + str.length;
  878.  
  879. // 结尾下标
  880. const lastIndex = getLastIndex(html, index);
  881.  
  882. if (lastIndex >= 0) {
  883. return JSON.parse(
  884. `{${html.substring(index, lastIndex)}}`
  885. );
  886. }
  887.  
  888. return null;
  889. })();
  890.  
  891. if (reputations) {
  892. for (let fid in reputations) {
  893. return reputations[fid][uid] || 0;
  894. }
  895. }
  896.  
  897. return NaN;
  898. })();
  899.  
  900. if (await getFilterModeByUserInfo(userInfo, reputation)) {
  901. resolve(FILTER_MODE.indexOf("隐藏"));
  902. }
  903. }
  904.  
  905. resolve(getFilterMode(uid, subject, content));
  906. } else {
  907. reject();
  908. }
  909. };
  910.  
  911. reader.readAsText(blob, "GBK");
  912. })
  913. .catch(() => {
  914. reject();
  915. });
  916. }).catch(() => {
  917. return FILTER_MODE.indexOf("隐藏");
  918. });
  919. };
  920.  
  921. // 处理引用
  922. const handleQuote = async (content) => {
  923. const quotes = content.querySelectorAll(".quote");
  924.  
  925. await Promise.all(
  926. [...quotes].map(async (quote) => {
  927. const uid = (() => {
  928. const ele = quote.querySelector("a[href^='/nuke.php']");
  929.  
  930. if (ele) {
  931. const res = ele.getAttribute("href").match(/uid=(\S+)/);
  932.  
  933. if (res) {
  934. return res[1];
  935. }
  936. }
  937.  
  938. return 0;
  939. })();
  940.  
  941. const filterMode = await new Promise(async (resolve) => {
  942. const mode = await getFilterMode(uid, "", quote.innerText);
  943.  
  944. if (mode === 0) {
  945. resolve(data.options.filterMode);
  946. }
  947.  
  948. if (mode > 0) {
  949. resolve(FILTER_MODE[mode]);
  950. }
  951.  
  952. resolve("");
  953. });
  954.  
  955. if (filterMode === "标记") {
  956. quote.innerHTML = `
  957. <div class="lessernuke" style="background: #81C7D4; border-color: #66BAB7; ">
  958. <span class="crimson">Troll must die.</span>
  959. <a href="javascript:void(0)" onclick="[...document.getElementsByName('troll_${uid}')].forEach(item => item.style.display = '')">点击查看</a>
  960. <div style="display: none;" name="troll_${uid}">
  961. ${quote.innerHTML}
  962. </div>
  963. </div>`;
  964. } else if (filterMode === "遮罩") {
  965. const source = document.createElement("DIV");
  966.  
  967. source.innerHTML = quote.innerHTML;
  968. source.style.display = "none";
  969.  
  970. const caption = document.createElement("CAPTION");
  971.  
  972. caption.className = "filter-mask filter-mask-block";
  973.  
  974. caption.innerHTML = `<span class="crimson">Troll must die.</span>`;
  975. caption.onclick = () => {
  976. quote.removeChild(caption);
  977.  
  978. source.style.display = "";
  979. };
  980.  
  981. quote.innerHTML = "";
  982. quote.appendChild(source);
  983. quote.appendChild(caption);
  984. } else if (filterMode === "隐藏") {
  985. quote.innerHTML = "";
  986. }
  987. })
  988. );
  989. };
  990.  
  991. // 过滤
  992. const reFilter = (() => {
  993. let hasNext = false;
  994. let isRunning = false;
  995.  
  996. const func = async () => {
  997. const tPage = location.pathname === "/thread.php";
  998. const pPage = location.pathname === "/read.php";
  999.  
  1000. if (tPage) {
  1001. const params = new URLSearchParams(location.search);
  1002.  
  1003. if (params.has("favor")) {
  1004. return;
  1005. }
  1006.  
  1007. if (params.has("authorid")) {
  1008. return;
  1009. }
  1010. }
  1011.  
  1012. if (tPage) {
  1013. const tData = n.topicArg.data;
  1014.  
  1015. await Promise.all(
  1016. Object.values(tData).map(async (item) => {
  1017. if (item.containerC) return;
  1018.  
  1019. const tid = item[8];
  1020.  
  1021. const filterMode = await new Promise(async (resolve) => {
  1022. const mode = await getFilterModeByTopic(tid);
  1023.  
  1024. if (mode === 0) {
  1025. resolve(data.options.filterMode);
  1026. }
  1027.  
  1028. if (mode > 0) {
  1029. resolve(FILTER_MODE[mode]);
  1030. }
  1031.  
  1032. resolve("");
  1033. });
  1034.  
  1035. item.contentC = item[1];
  1036.  
  1037. item.contentB = item.contentB || item.contentC.innerHTML;
  1038.  
  1039. item.containerC =
  1040. item.containerC || item.contentC.parentNode.parentNode;
  1041.  
  1042. item.containerC.style = "";
  1043. item.contentC.style = "";
  1044. item[1].className = item[1].className.replace(" filter-mask", "");
  1045. item[2].className = item[2].className.replace(" filter-mask", "");
  1046.  
  1047. if (filterMode === "标记") {
  1048. item.contentC.style = "text-decoration: line-through;";
  1049. } else if (filterMode === "遮罩") {
  1050. item[1].className += " filter-mask";
  1051. item[2].className += " filter-mask";
  1052. } else if (filterMode === "隐藏") {
  1053. item.containerC.style = "display: none;";
  1054. }
  1055. })
  1056. );
  1057. } else if (pPage) {
  1058. const pData = n.postArg.data;
  1059.  
  1060. await Promise.all(
  1061. Object.values(pData).map(async (item) => {
  1062. if (~~item.pAid === self) return;
  1063. if (item.containerC) return;
  1064.  
  1065. if (typeof item.i === "number") {
  1066. item.actionC =
  1067. item.actionC ||
  1068. (() => {
  1069. const container =
  1070. item.uInfoC
  1071. .closest("tr")
  1072. .querySelector(".posterInfoLine") ||
  1073. item.uInfoC.querySelector("div");
  1074.  
  1075. const ele = container.querySelector('[name="uid"]');
  1076.  
  1077. if (ele) {
  1078. ele.innerHTML = `屏蔽`;
  1079. ele.onclick = null;
  1080.  
  1081. return ele;
  1082. }
  1083.  
  1084. const anchor = container.querySelector('.author ~ br');
  1085.  
  1086. if (anchor) {
  1087. const btn = document.createElement("A");
  1088.  
  1089. btn.name = "uid";
  1090. btn.href = "javascript:void(0)";
  1091. btn.className = "small_colored_text_btn stxt block_txt_c0 vertmod";
  1092. btn.innerHTML = `屏蔽`;
  1093.  
  1094. anchor.parentNode.insertBefore(btn, anchor);
  1095.  
  1096. return btn;
  1097. }
  1098. })();
  1099.  
  1100. item.tagC =
  1101. item.tagC ||
  1102. (() => {
  1103. const tc = document.createElement("div");
  1104.  
  1105. tc.className = "filter-tags";
  1106.  
  1107. item.uInfoC.appendChild(tc);
  1108.  
  1109. return tc;
  1110. })();
  1111. }
  1112.  
  1113. item.pName =
  1114. item.pName ||
  1115. item.uInfoC.getElementsByClassName("author")[0].innerText;
  1116.  
  1117. item.reFilter =
  1118. item.reFilter ||
  1119. (async () => {
  1120. const uid = item.pAid;
  1121.  
  1122. const filterMode = await new Promise(async (resolve) => {
  1123. const mode = await getFilterMode(
  1124. uid,
  1125. item.subjectC.innerText,
  1126. item.contentC.innerText
  1127. );
  1128.  
  1129. if (mode === 0) {
  1130. resolve(data.options.filterMode);
  1131. }
  1132.  
  1133. if (mode > 0) {
  1134. resolve(FILTER_MODE[mode]);
  1135. }
  1136.  
  1137. resolve("");
  1138. });
  1139.  
  1140. item.avatarC =
  1141. item.avatarC ||
  1142. (() => {
  1143. const tc = document.createElement("div");
  1144.  
  1145. const avatar = document.getElementById(
  1146. `posteravatar${item.i}`
  1147. );
  1148.  
  1149. if (avatar) {
  1150. avatar.parentNode.insertBefore(tc, avatar.nextSibling);
  1151.  
  1152. tc.appendChild(avatar);
  1153. }
  1154.  
  1155. return tc;
  1156. })();
  1157.  
  1158. item.contentB = item.contentB || item.contentC.innerHTML;
  1159.  
  1160. item.containerC =
  1161. item.containerC ||
  1162. (() => {
  1163. let temp = item.contentC;
  1164.  
  1165. if (item.i >= 0) {
  1166. while (temp.nodeName !== "TBODY") {
  1167. temp = temp.parentNode;
  1168. }
  1169. } else {
  1170. while (temp.nodeName !== "DIV") {
  1171. temp = temp.parentNode;
  1172. }
  1173. }
  1174.  
  1175. return temp;
  1176. })();
  1177.  
  1178. item.avatarC.style.display = "";
  1179. item.containerC.style.display = "";
  1180. item.contentC.innerHTML = item.contentB;
  1181.  
  1182. if (item.actionC) {
  1183. item.actionC.style = "background: #aaa;";
  1184. }
  1185.  
  1186. if (filterMode === "标记") {
  1187. item.avatarC.style.display = "none";
  1188. item.contentC.innerHTML = `
  1189. <div class="lessernuke" style="background: #81C7D4; border-color: #66BAB7; ">
  1190. <span class="crimson">Troll must die.</span>
  1191. <a href="javascript:void(0)" onclick="[...document.getElementsByName('troll_${uid}')].forEach(item => item.style.display = '')">点击查看</a>
  1192. <div style="display: none;" name="troll_${uid}">
  1193. ${item.contentB}
  1194. </div>
  1195. </div>`;
  1196.  
  1197. if (item.actionC && data.users[uid]) {
  1198. item.actionC.style = "background: #cb4042;";
  1199. }
  1200. } else if (filterMode === "遮罩") {
  1201. const caption = document.createElement("CAPTION");
  1202.  
  1203. if (item.i >= 0) {
  1204. caption.className = "filter-mask filter-mask-block";
  1205. } else {
  1206. caption.className = "filter-mask filter-mask-block left";
  1207. caption.style = "width: 47%;";
  1208. }
  1209.  
  1210. caption.innerHTML = `<span class="crimson">Troll must die.</span>`;
  1211. caption.onclick = () => {
  1212. item.containerC.parentNode.removeChild(caption);
  1213. item.containerC.style.display = "";
  1214. };
  1215.  
  1216. item.containerC.parentNode.insertBefore(
  1217. caption,
  1218. item.containerC
  1219. );
  1220. item.containerC.style.display = "none";
  1221.  
  1222. if (item.actionC && data.users[uid]) {
  1223. item.actionC.style = "background: #cb4042;";
  1224. }
  1225. } else if (filterMode === "隐藏") {
  1226. item.containerC.style.display = "none";
  1227. } else {
  1228. await handleQuote(item.contentC);
  1229. }
  1230.  
  1231. if (item.tagC) {
  1232. const tags = data.users[uid]
  1233. ? data.users[uid].tags.map((tag) => data.tags[tag]) || []
  1234. : [];
  1235.  
  1236. item.tagC.style.display = tags.length ? "" : "none";
  1237. item.tagC.innerHTML = tags
  1238. .map(
  1239. (tag) =>
  1240. `<b class="block_txt nobr" style="background:${tag.color}; color:#fff; margin: 0.1em 0.2em;">${tag.name}</b>`
  1241. )
  1242. .join("");
  1243.  
  1244. witchHunter.run(uid, item.tagC);
  1245. }
  1246. });
  1247.  
  1248. if (item.actionC) {
  1249. item.actionC.onclick =
  1250. item.actionC.onclick ||
  1251. ((e) => {
  1252. if (item.pAid < 0) return;
  1253.  
  1254. const user = data.users[item.pAid];
  1255.  
  1256. if (e.ctrlKey === false) {
  1257. editUser(item.pAid, item.pName, item.reFilter);
  1258. } else {
  1259. if (user) {
  1260. delete data.users[user.id];
  1261. } else {
  1262. addUser(item.pAid, item.pName);
  1263. }
  1264.  
  1265. saveData();
  1266. item.reFilter();
  1267. }
  1268. });
  1269. }
  1270.  
  1271. await item.reFilter();
  1272. })
  1273. );
  1274. }
  1275. };
  1276.  
  1277. const execute = () =>
  1278. func().finally(() => {
  1279. if (hasNext) {
  1280. hasNext = false;
  1281.  
  1282. execute();
  1283. } else {
  1284. isRunning = false;
  1285. }
  1286. });
  1287.  
  1288. return async () => {
  1289. if (isRunning) {
  1290. hasNext = true;
  1291. } else {
  1292. isRunning = true;
  1293.  
  1294. await execute();
  1295. }
  1296. };
  1297. })();
  1298.  
  1299. // STYLE
  1300. GM_addStyle(`
  1301. .filter-table-wrapper {
  1302. max-height: 80vh;
  1303. overflow-y: auto;
  1304. }
  1305. .filter-table {
  1306. margin: 0;
  1307. }
  1308. .filter-table th,
  1309. .filter-table td {
  1310. position: relative;
  1311. white-space: nowrap;
  1312. }
  1313. .filter-table th {
  1314. position: sticky;
  1315. top: 2px;
  1316. z-index: 1;
  1317. }
  1318. .filter-table input:not([type]), .filter-table input[type="text"] {
  1319. margin: 0;
  1320. box-sizing: border-box;
  1321. height: 100%;
  1322. width: 100%;
  1323. }
  1324. .filter-input-wrapper {
  1325. position: absolute;
  1326. top: 6px;
  1327. right: 6px;
  1328. bottom: 6px;
  1329. left: 6px;
  1330. }
  1331. .filter-text-ellipsis {
  1332. display: flex;
  1333. }
  1334. .filter-text-ellipsis > * {
  1335. flex: 1;
  1336. width: 1px;
  1337. overflow: hidden;
  1338. text-overflow: ellipsis;
  1339. }
  1340. .filter-button-group {
  1341. margin: -.1em -.2em;
  1342. }
  1343. .filter-tags {
  1344. margin: 2px -0.2em 0;
  1345. text-align: left;
  1346. }
  1347. .filter-mask {
  1348. margin: 1px;
  1349. color: #81C7D4;
  1350. background: #81C7D4;
  1351. }
  1352. .filter-mask-block {
  1353. display: block;
  1354. border: 1px solid #66BAB7;
  1355. text-align: center !important;
  1356. }
  1357. .filter-input-wrapper {
  1358. position: absolute;
  1359. top: 6px;
  1360. right: 6px;
  1361. bottom: 6px;
  1362. left: 6px;
  1363. }
  1364. `);
  1365.  
  1366. // UI
  1367. const u = (() => {
  1368. const modules = {};
  1369.  
  1370. const tabContainer = (() => {
  1371. const c = document.createElement("div");
  1372.  
  1373. c.className = "w100";
  1374. c.innerHTML = `
  1375. <div class="right_" style="margin-bottom: 5px;">
  1376. <table class="stdbtn" cellspacing="0">
  1377. <tbody>
  1378. <tr></tr>
  1379. </tbody>
  1380. </table>
  1381. </div>
  1382. <div class="clear"></div>
  1383. `;
  1384.  
  1385. return c;
  1386. })();
  1387.  
  1388. const tabPanelContainer = (() => {
  1389. const c = document.createElement("div");
  1390.  
  1391. c.style = "width: 80vw;";
  1392.  
  1393. return c;
  1394. })();
  1395.  
  1396. const content = (() => {
  1397. const c = document.createElement("div");
  1398.  
  1399. c.append(tabContainer);
  1400. c.append(tabPanelContainer);
  1401.  
  1402. return c;
  1403. })();
  1404.  
  1405. const addModule = (() => {
  1406. const tc = tabContainer.getElementsByTagName("tr")[0];
  1407. const cc = tabPanelContainer;
  1408.  
  1409. return (module) => {
  1410. const tabBox = document.createElement("td");
  1411.  
  1412. tabBox.innerHTML = `<a href="javascript:void(0)" class="nobr silver">${module.name}</a>`;
  1413.  
  1414. const tab = tabBox.childNodes[0];
  1415.  
  1416. const toggle = () => {
  1417. Object.values(modules).forEach((item) => {
  1418. if (item.tab === tab) {
  1419. item.tab.className = "nobr";
  1420. item.content.style = "display: block";
  1421. item.refresh();
  1422. } else {
  1423. item.tab.className = "nobr silver";
  1424. item.content.style = "display: none";
  1425. }
  1426. });
  1427. };
  1428.  
  1429. tc.append(tabBox);
  1430. cc.append(module.content);
  1431.  
  1432. tab.onclick = toggle;
  1433.  
  1434. modules[module.name] = {
  1435. ...module,
  1436. tab,
  1437. toggle,
  1438. };
  1439.  
  1440. return modules[module.name];
  1441. };
  1442. })();
  1443.  
  1444. return {
  1445. content,
  1446. modules,
  1447. addModule,
  1448. };
  1449. })();
  1450.  
  1451. // 用户
  1452. const userModule = (() => {
  1453. const content = (() => {
  1454. const c = document.createElement("div");
  1455.  
  1456. c.style = "display: none";
  1457. c.innerHTML = `
  1458. <div class="filter-table-wrapper">
  1459. <table class="filter-table forumbox">
  1460. <thead>
  1461. <tr class="block_txt_c0">
  1462. <th class="c1" width="1">昵称</th>
  1463. <th class="c2">标记</th>
  1464. <th class="c3" width="1">过滤方式</th>
  1465. <th class="c4" width="1">操作</th>
  1466. </tr>
  1467. </thead>
  1468. <tbody></tbody>
  1469. </table>
  1470. </div>
  1471. `;
  1472.  
  1473. return c;
  1474. })();
  1475.  
  1476. const refresh = (() => {
  1477. const container = content.getElementsByTagName("tbody")[0];
  1478.  
  1479. const func = () => {
  1480. container.innerHTML = "";
  1481.  
  1482. Object.values(data.users).forEach((item) => {
  1483. const tc = document.createElement("tr");
  1484.  
  1485. tc.className = `row${
  1486. (container.querySelectorAll("TR").length % 2) + 1
  1487. }`;
  1488.  
  1489. tc.refresh = () => {
  1490. if (data.users[item.id]) {
  1491. tc.innerHTML = `
  1492. <td class="c1">
  1493. <a href="/nuke.php?func=ucp&uid=${
  1494. item.id
  1495. }" class="b nobr">[${
  1496. item.name ? "@" + item.name : "#" + item.id
  1497. }]</a>
  1498. </td>
  1499. <td class="c2">
  1500. ${item.tags
  1501. .map((tag) => {
  1502. if (data.tags[tag]) {
  1503. return `<b class="block_txt nobr" style="background:${data.tags[tag].color}; color:#fff; margin: 0.1em 0.2em;">${data.tags[tag].name}</b>`;
  1504. }
  1505. })
  1506. .join("")}
  1507. </td>
  1508. <td class="c3">
  1509. <div class="filter-table-button-group">
  1510. <button>${item.filterMode || FILTER_MODE[0]}</button>
  1511. </div>
  1512. </td>
  1513. <td class="c4">
  1514. <div class="filter-table-button-group">
  1515. <button>编辑</button>
  1516. <button>删除</button>
  1517. </div>
  1518. </td>
  1519. `;
  1520.  
  1521. const actions = tc.getElementsByTagName("button");
  1522.  
  1523. actions[0].onclick = () => {
  1524. data.users[item.id].filterMode = switchFilterMode(
  1525. data.users[item.id].filterMode || FILTER_MODE[0]
  1526. );
  1527.  
  1528. actions[0].innerHTML = data.users[item.id].filterMode;
  1529.  
  1530. saveData();
  1531. reFilter();
  1532. };
  1533.  
  1534. actions[1].onclick = () => {
  1535. editUser(item.id, item.name, tc.refresh);
  1536. };
  1537.  
  1538. actions[2].onclick = () => {
  1539. if (confirm("是否确认?")) {
  1540. delete data.users[item.id];
  1541. container.removeChild(tc);
  1542.  
  1543. saveData();
  1544. reFilter();
  1545. }
  1546. };
  1547. } else {
  1548. tc.remove();
  1549. }
  1550. };
  1551.  
  1552. tc.refresh();
  1553.  
  1554. container.appendChild(tc);
  1555. });
  1556. };
  1557.  
  1558. return func;
  1559. })();
  1560.  
  1561. return {
  1562. name: "用户",
  1563. content,
  1564. refresh,
  1565. };
  1566. })();
  1567.  
  1568. // 标记
  1569. const tagModule = (() => {
  1570. const content = (() => {
  1571. const c = document.createElement("div");
  1572.  
  1573. c.style = "display: none";
  1574. c.innerHTML = `
  1575. <div class="filter-table-wrapper">
  1576. <table class="filter-table forumbox">
  1577. <thead>
  1578. <tr class="block_txt_c0">
  1579. <th class="c1" width="1">标记</th>
  1580. <th class="c2">列表</th>
  1581. <th class="c3" width="1">过滤方式</th>
  1582. <th class="c4" width="1">操作</th>
  1583. </tr>
  1584. </thead>
  1585. <tbody></tbody>
  1586. </table>
  1587. </div>
  1588. `;
  1589.  
  1590. return c;
  1591. })();
  1592.  
  1593. const refresh = (() => {
  1594. const container = content.getElementsByTagName("tbody")[0];
  1595.  
  1596. const func = () => {
  1597. container.innerHTML = "";
  1598.  
  1599. Object.values(data.tags).forEach((item) => {
  1600. const tc = document.createElement("tr");
  1601.  
  1602. tc.className = `row${
  1603. (container.querySelectorAll("TR").length % 2) + 1
  1604. }`;
  1605.  
  1606. tc.innerHTML = `
  1607. <td class="c1">
  1608. <b class="block_txt nobr" style="background:${
  1609. item.color
  1610. }; color:#fff; margin: 0.1em 0.2em;">${item.name}</b>
  1611. </td>
  1612. <td class="c2">
  1613. <button>${
  1614. Object.values(data.users).filter((user) =>
  1615. user.tags.find((tag) => tag === item.id)
  1616. ).length
  1617. }
  1618. </button>
  1619. <div style="white-space: normal; display: none;">
  1620. ${Object.values(data.users)
  1621. .filter((user) =>
  1622. user.tags.find((tag) => tag === item.id)
  1623. )
  1624. .map(
  1625. (user) =>
  1626. `<a href="/nuke.php?func=ucp&uid=${
  1627. user.id
  1628. }" class="b nobr">[${
  1629. user.name ? "@" + user.name : "#" + user.id
  1630. }]</a>`
  1631. )
  1632. .join("")}
  1633. </div>
  1634. </td>
  1635. <td class="c3">
  1636. <div class="filter-table-button-group">
  1637. <button>${item.filterMode || FILTER_MODE[0]}</button>
  1638. </div>
  1639. </td>
  1640. <td class="c4">
  1641. <div class="filter-table-button-group">
  1642. <button>删除</button>
  1643. </div>
  1644. </td>
  1645. `;
  1646.  
  1647. const actions = tc.getElementsByTagName("button");
  1648.  
  1649. actions[0].onclick = (() => {
  1650. let hide = true;
  1651. return () => {
  1652. hide = !hide;
  1653. actions[0].nextElementSibling.style.display = hide
  1654. ? "none"
  1655. : "block";
  1656. };
  1657. })();
  1658.  
  1659. actions[1].onclick = () => {
  1660. data.tags[item.id].filterMode = switchFilterMode(
  1661. data.tags[item.id].filterMode || FILTER_MODE[0]
  1662. );
  1663.  
  1664. actions[1].innerHTML = data.tags[item.id].filterMode;
  1665.  
  1666. saveData();
  1667. reFilter();
  1668. };
  1669.  
  1670. actions[2].onclick = () => {
  1671. if (confirm("是否确认?")) {
  1672. delete data.tags[item.id];
  1673.  
  1674. Object.values(data.users).forEach((user) => {
  1675. const index = user.tags.findIndex((tag) => tag === item.id);
  1676. if (index >= 0) {
  1677. user.tags.splice(index, 1);
  1678. }
  1679. });
  1680.  
  1681. container.removeChild(tc);
  1682.  
  1683. saveData();
  1684. reFilter();
  1685. }
  1686. };
  1687.  
  1688. container.appendChild(tc);
  1689. });
  1690. };
  1691.  
  1692. return func;
  1693. })();
  1694.  
  1695. return {
  1696. name: "标记",
  1697. content,
  1698. refresh,
  1699. };
  1700. })();
  1701.  
  1702. // 关键字
  1703. const keywordModule = (() => {
  1704. const content = (() => {
  1705. const c = document.createElement("div");
  1706.  
  1707. c.style = "display: none";
  1708. c.innerHTML = `
  1709. <div class="filter-table-wrapper">
  1710. <table class="filter-table forumbox">
  1711. <thead>
  1712. <tr class="block_txt_c0">
  1713. <th class="c1">列表</th>
  1714. <th class="c2" width="1">过滤方式</th>
  1715. <th class="c3" width="1">包括内容</th>
  1716. <th class="c4" width="1">操作</th>
  1717. </tr>
  1718. </thead>
  1719. <tbody></tbody>
  1720. </table>
  1721. </div>
  1722. <div class="silver" style="margin-top: 10px;">支持正则表达式。比如同类型的可以写在一条规则内用&quot;|&quot;隔开,&quot;ABC|DEF&quot;即为屏蔽带有ABC或者DEF的内容。</div>
  1723. `;
  1724.  
  1725. return c;
  1726. })();
  1727.  
  1728. const refresh = (() => {
  1729. const container = content.getElementsByTagName("tbody")[0];
  1730.  
  1731. const func = () => {
  1732. container.innerHTML = "";
  1733.  
  1734. Object.values(data.keywords).forEach((item) => {
  1735. const tc = document.createElement("tr");
  1736.  
  1737. tc.className = `row${
  1738. (container.querySelectorAll("TR").length % 2) + 1
  1739. }`;
  1740.  
  1741. tc.innerHTML = `
  1742. <td class="c1">
  1743. <div class="filter-input-wrapper">
  1744. <input value="${item.keyword || ""}" />
  1745. </div>
  1746. </td>
  1747. <td class="c2">
  1748. <div class="filter-table-button-group">
  1749. <button>${item.filterMode || FILTER_MODE[0]}</button>
  1750. </div>
  1751. </td>
  1752. <td class="c3">
  1753. <div style="text-align: center;">
  1754. <input type="checkbox" ${
  1755. item.filterLevel ? `checked="checked"` : ""
  1756. } />
  1757. </div>
  1758. </td>
  1759. <td class="c4">
  1760. <div class="filter-table-button-group">
  1761. <button>保存</button>
  1762. <button>删除</button>
  1763. </div>
  1764. </td>
  1765. `;
  1766.  
  1767. const inputElement = tc.querySelector("INPUT");
  1768. const levelElement = tc.querySelector(`INPUT[type="checkbox"]`);
  1769. const actions = tc.getElementsByTagName("button");
  1770.  
  1771. actions[0].onclick = () => {
  1772. actions[0].innerHTML = switchFilterMode(actions[0].innerHTML);
  1773. };
  1774.  
  1775. actions[1].onclick = () => {
  1776. if (inputElement.value) {
  1777. data.keywords[item.id] = {
  1778. id: item.id,
  1779. keyword: inputElement.value,
  1780. filterMode: actions[0].innerHTML,
  1781. filterLevel: levelElement.checked ? 1 : 0,
  1782. };
  1783.  
  1784. saveData();
  1785. refresh();
  1786. }
  1787. };
  1788.  
  1789. actions[2].onclick = () => {
  1790. if (confirm("是否确认?")) {
  1791. delete data.keywords[item.id];
  1792.  
  1793. saveData();
  1794. refresh();
  1795. }
  1796. };
  1797.  
  1798. container.appendChild(tc);
  1799. });
  1800.  
  1801. {
  1802. const tc = document.createElement("tr");
  1803.  
  1804. tc.className = `row${
  1805. (container.querySelectorAll("TR").length % 2) + 1
  1806. }`;
  1807.  
  1808. tc.innerHTML = `
  1809. <td class="c1">
  1810. <div class="filter-input-wrapper">
  1811. <input value="" />
  1812. </div>
  1813. </td>
  1814. <td class="c2">
  1815. <div class="filter-table-button-group">
  1816. <button>${FILTER_MODE[0]}</button>
  1817. </div>
  1818. </td>
  1819. <td class="c3">
  1820. <div style="text-align: center;">
  1821. <input type="checkbox" />
  1822. </div>
  1823. </td>
  1824. <td class="c4">
  1825. <div class="filter-table-button-group">
  1826. <button>添加</button>
  1827. </div>
  1828. </td>
  1829. `;
  1830.  
  1831. const inputElement = tc.querySelector("INPUT");
  1832. const levelElement = tc.querySelector(`INPUT[type="checkbox"]`);
  1833. const actions = tc.getElementsByTagName("button");
  1834.  
  1835. actions[0].onclick = () => {
  1836. actions[0].innerHTML = switchFilterMode(actions[0].innerHTML);
  1837. };
  1838.  
  1839. actions[1].onclick = () => {
  1840. if (inputElement.value) {
  1841. addKeyword(
  1842. inputElement.value,
  1843. actions[0].innerHTML,
  1844. levelElement.checked ? 1 : 0
  1845. );
  1846.  
  1847. saveData();
  1848. refresh();
  1849. }
  1850. };
  1851.  
  1852. container.appendChild(tc);
  1853. }
  1854. };
  1855.  
  1856. return func;
  1857. })();
  1858.  
  1859. return {
  1860. name: "关键字",
  1861. content,
  1862. refresh,
  1863. };
  1864. })();
  1865.  
  1866. // 属地
  1867. const locationModule = (() => {
  1868. const content = (() => {
  1869. const c = document.createElement("div");
  1870.  
  1871. c.style = "display: none";
  1872. c.innerHTML = `
  1873. <div class="filter-table-wrapper">
  1874. <table class="filter-table forumbox">
  1875. <thead>
  1876. <tr class="block_txt_c0">
  1877. <th class="c1">列表</th>
  1878. <th class="c2" width="1">过滤方式</th>
  1879. <th class="c3" width="1">操作</th>
  1880. </tr>
  1881. </thead>
  1882. <tbody></tbody>
  1883. </table>
  1884. </div>
  1885. <div class="silver" style="margin-top: 10px;">支持正则表达式。比如同类型的可以写在一条规则内用&quot;|&quot;隔开,&quot;ABC|DEF&quot;即为屏蔽带有ABC或者DEF的内容。<br/>属地过滤功能需要占用额外的资源,请谨慎开启</div>
  1886. `;
  1887.  
  1888. return c;
  1889. })();
  1890.  
  1891. const refresh = (() => {
  1892. const container = content.getElementsByTagName("tbody")[0];
  1893.  
  1894. const func = () => {
  1895. container.innerHTML = "";
  1896.  
  1897. Object.values(data.locations).forEach((item) => {
  1898. const tc = document.createElement("tr");
  1899.  
  1900. tc.className = `row${
  1901. (container.querySelectorAll("TR").length % 2) + 1
  1902. }`;
  1903.  
  1904. tc.innerHTML = `
  1905. <td class="c1">
  1906. <div class="filter-input-wrapper">
  1907. <input value="${item.keyword || ""}" />
  1908. </div>
  1909. </td>
  1910. <td class="c2">
  1911. <div class="filter-table-button-group">
  1912. <button>${item.filterMode || FILTER_MODE[0]}</button>
  1913. </div>
  1914. </td>
  1915. <td class="c3">
  1916. <div class="filter-table-button-group">
  1917. <button>保存</button>
  1918. <button>删除</button>
  1919. </div>
  1920. </td>
  1921. `;
  1922.  
  1923. const inputElement = tc.querySelector("INPUT");
  1924. const actions = tc.getElementsByTagName("button");
  1925.  
  1926. actions[0].onclick = () => {
  1927. actions[0].innerHTML = switchFilterMode(actions[0].innerHTML);
  1928. };
  1929.  
  1930. actions[1].onclick = () => {
  1931. if (inputElement.value) {
  1932. data.locations[item.id] = {
  1933. id: item.id,
  1934. keyword: inputElement.value,
  1935. filterMode: actions[0].innerHTML,
  1936. };
  1937.  
  1938. saveData();
  1939. refresh();
  1940. }
  1941. };
  1942.  
  1943. actions[2].onclick = () => {
  1944. if (confirm("是否确认?")) {
  1945. delete data.locations[item.id];
  1946.  
  1947. saveData();
  1948. refresh();
  1949. }
  1950. };
  1951.  
  1952. container.appendChild(tc);
  1953. });
  1954.  
  1955. {
  1956. const tc = document.createElement("tr");
  1957.  
  1958. tc.className = `row${
  1959. (container.querySelectorAll("TR").length % 2) + 1
  1960. }`;
  1961.  
  1962. tc.innerHTML = `
  1963. <td class="c1">
  1964. <div class="filter-input-wrapper">
  1965. <input value="" />
  1966. </div>
  1967. </td>
  1968. <td class="c2">
  1969. <div class="filter-table-button-group">
  1970. <button>${FILTER_MODE[0]}</button>
  1971. </div>
  1972. </td>
  1973. <td class="c3">
  1974. <div class="filter-table-button-group">
  1975. <button>添加</button>
  1976. </div>
  1977. </td>
  1978. `;
  1979.  
  1980. const inputElement = tc.querySelector("INPUT");
  1981. const actions = tc.getElementsByTagName("button");
  1982.  
  1983. actions[0].onclick = () => {
  1984. actions[0].innerHTML = switchFilterMode(actions[0].innerHTML);
  1985. };
  1986.  
  1987. actions[1].onclick = () => {
  1988. if (inputElement.value) {
  1989. addLocation(inputElement.value, actions[0].innerHTML);
  1990.  
  1991. saveData();
  1992. refresh();
  1993. }
  1994. };
  1995.  
  1996. container.appendChild(tc);
  1997. }
  1998. };
  1999.  
  2000. return func;
  2001. })();
  2002.  
  2003. return {
  2004. name: "属地",
  2005. content,
  2006. refresh,
  2007. };
  2008. })();
  2009.  
  2010. // 猎巫
  2011. const witchHuntModule = (() => {
  2012. const content = (() => {
  2013. const c = document.createElement("div");
  2014.  
  2015. c.style = "display: none";
  2016. c.innerHTML = `
  2017. <div class="filter-table-wrapper">
  2018. <table class="filter-table forumbox">
  2019. <thead>
  2020. <tr class="block_txt_c0">
  2021. <th class="c1">版面</th>
  2022. <th class="c2">标签</th>
  2023. <th class="c3" width="1">操作</th>
  2024. </tr>
  2025. </thead>
  2026. <tbody></tbody>
  2027. </table>
  2028. </div>
  2029. <div class="silver" style="margin-top: 10px;">猎巫模块需要占用额外的资源,请谨慎开启<br/>该功能为实验性功能,仅判断用户是否曾经在某个版面发言<br/>未来可能会加入发言的筛选或是屏蔽功能,也可能移除此功能</div>
  2030. `;
  2031.  
  2032. return c;
  2033. })();
  2034.  
  2035. const refresh = (() => {
  2036. const container = content.getElementsByTagName("tbody")[0];
  2037.  
  2038. const func = () => {
  2039. container.innerHTML = "";
  2040.  
  2041. Object.values(witchHunter.data).forEach((item, index) => {
  2042. const tc = document.createElement("tr");
  2043.  
  2044. tc.className = `row${
  2045. (container.querySelectorAll("TR").length % 2) + 1
  2046. }`;
  2047.  
  2048. tc.innerHTML = `
  2049. <td class="c1">
  2050. <div class="filter-input-wrapper">
  2051. <a href="/thread.php?fid=${item.fid}" class="b nobr">[${item.name}]</a>
  2052. </div>
  2053. </td>
  2054. <td class="c2">
  2055. <b class="block_txt nobr" style="background:${item.color}; color:#fff; margin: 0.1em 0.2em;">${item.label}</b>
  2056. </td>
  2057. <td class="c3">
  2058. <div class="filter-table-button-group">
  2059. <button>删除</button>
  2060. </div>
  2061. </td>
  2062. `;
  2063.  
  2064. const actions = tc.getElementsByTagName("button");
  2065.  
  2066. actions[0].onclick = () => {
  2067. if (confirm("是否确认?")) {
  2068. witchHunter.remove(item.id);
  2069.  
  2070. refresh();
  2071. }
  2072. };
  2073.  
  2074. container.appendChild(tc);
  2075. });
  2076.  
  2077. {
  2078. const tc = document.createElement("tr");
  2079.  
  2080. tc.className = `row${
  2081. (container.querySelectorAll("TR").length % 2) + 1
  2082. }`;
  2083.  
  2084. tc.innerHTML = `
  2085. <td class="c1">
  2086. <div class="filter-input-wrapper">
  2087. <input value="" placeholder="版面ID" />
  2088. </div>
  2089. </td>
  2090. <td class="c2">
  2091. <div class="filter-input-wrapper">
  2092. <input value="" />
  2093. </div>
  2094. </td>
  2095. <td class="c3">
  2096. <div class="filter-table-button-group">
  2097. <button>添加</button>
  2098. </div>
  2099. </td>
  2100. `;
  2101.  
  2102. const inputElement = tc.getElementsByTagName("INPUT");
  2103. const actions = tc.getElementsByTagName("button");
  2104.  
  2105. actions[0].onclick = async () => {
  2106. const fid = parseInt(inputElement[0].value, 10);
  2107. const tag = inputElement[1].value.trim();
  2108.  
  2109. if (isNaN(fid) || tag.length === 0) {
  2110. return;
  2111. }
  2112.  
  2113. await witchHunter.add(fid, tag);
  2114.  
  2115. refresh();
  2116. };
  2117.  
  2118. container.appendChild(tc);
  2119. }
  2120. };
  2121.  
  2122. return func;
  2123. })();
  2124.  
  2125. return {
  2126. name: "猎巫",
  2127. content,
  2128. refresh,
  2129. };
  2130. })();
  2131.  
  2132. // 通用设置
  2133. const commonModule = (() => {
  2134. const content = (() => {
  2135. const c = document.createElement("div");
  2136.  
  2137. c.style = "display: none";
  2138.  
  2139. return c;
  2140. })();
  2141.  
  2142. const refresh = (() => {
  2143. const container = content;
  2144.  
  2145. const func = () => {
  2146. container.innerHTML = "";
  2147.  
  2148. // 默认过滤方式
  2149. {
  2150. const tc = document.createElement("div");
  2151.  
  2152. tc.innerHTML += `
  2153. <div>默认过滤方式</div>
  2154. <div></div>
  2155. <div class="silver" style="margin-top: 10px;">${FILTER_TIPS}</div>
  2156. `;
  2157.  
  2158. ["标记", "遮罩", "隐藏"].forEach((item, index) => {
  2159. const ele = document.createElement("SPAN");
  2160.  
  2161. ele.innerHTML += `
  2162. <input id="s-fm-${index}" type="radio" name="filterType" ${
  2163. data.options.filterMode === item && "checked"
  2164. }>
  2165. <label for="s-fm-${index}" style="cursor: pointer;">${item}</label>
  2166. `;
  2167.  
  2168. const inp = ele.querySelector("input");
  2169.  
  2170. inp.onchange = () => {
  2171. if (inp.checked) {
  2172. data.options.filterMode = item;
  2173. saveData();
  2174. reFilter();
  2175. }
  2176. };
  2177.  
  2178. tc.querySelectorAll("div")[1].append(ele);
  2179. });
  2180.  
  2181. container.appendChild(tc);
  2182. }
  2183.  
  2184. // 小号过滤(时间)
  2185. {
  2186. const tc = document.createElement("div");
  2187.  
  2188. tc.innerHTML += `
  2189. <br/>
  2190. <div>
  2191. 隐藏注册时间小于<input value="${
  2192. (data.options.filterRegdateLimit || 0) / 86400000
  2193. }" maxLength="4" style="width: 48px;" />天的用户
  2194. <button>确认</button>
  2195. </div>
  2196. `;
  2197.  
  2198. const actions = tc.getElementsByTagName("button");
  2199.  
  2200. actions[0].onclick = () => {
  2201. const v = actions[0].previousElementSibling.value;
  2202.  
  2203. const n = Number(v) || 0;
  2204.  
  2205. data.options.filterRegdateLimit = n < 0 ? 0 : n * 86400000;
  2206.  
  2207. saveData();
  2208. reFilter();
  2209. };
  2210.  
  2211. container.appendChild(tc);
  2212. }
  2213.  
  2214. // 小号过滤(发帖数)
  2215. {
  2216. const tc = document.createElement("div");
  2217.  
  2218. tc.innerHTML += `
  2219. <br/>
  2220. <div>
  2221. 隐藏发帖数量小于<input value="${
  2222. data.options.filterPostnumLimit || 0
  2223. }" maxLength="5" style="width: 48px;" />贴的用户
  2224. <button>确认</button>
  2225. </div>
  2226. `;
  2227.  
  2228. const actions = tc.getElementsByTagName("button");
  2229.  
  2230. actions[0].onclick = () => {
  2231. const v = actions[0].previousElementSibling.value;
  2232.  
  2233. const n = Number(v) || 0;
  2234.  
  2235. data.options.filterPostnumLimit = n < 0 ? 0 : n;
  2236.  
  2237. saveData();
  2238. reFilter();
  2239. };
  2240.  
  2241. container.appendChild(tc);
  2242. }
  2243.  
  2244. // 声望过滤
  2245. {
  2246. const tc = document.createElement("div");
  2247.  
  2248. tc.innerHTML += `
  2249. <br/>
  2250. <div>
  2251. 隐藏版面声望低于<input value="${
  2252. data.options.filterReputationLimit || ""
  2253. }" maxLength="5" style="width: 48px;" />点的用户
  2254. <button>确认</button>
  2255. </div>
  2256. `;
  2257.  
  2258. const actions = tc.getElementsByTagName("button");
  2259.  
  2260. actions[0].onclick = () => {
  2261. const v = actions[0].previousElementSibling.value;
  2262.  
  2263. const n = Number(v);
  2264.  
  2265. data.options.filterReputationLimit = n;
  2266.  
  2267. saveData();
  2268. reFilter();
  2269. };
  2270.  
  2271. container.appendChild(tc);
  2272. }
  2273.  
  2274. // 删除没有标记的用户
  2275. {
  2276. const tc = document.createElement("div");
  2277.  
  2278. tc.innerHTML += `
  2279. <br/>
  2280. <div>
  2281. <button>删除没有标记的用户</button>
  2282. </div>
  2283. `;
  2284.  
  2285. const actions = tc.getElementsByTagName("button");
  2286.  
  2287. actions[0].onclick = () => {
  2288. if (confirm("是否确认?")) {
  2289. Object.values(data.users).forEach((item) => {
  2290. if (item.tags.length === 0) {
  2291. delete data.users[item.id];
  2292. }
  2293. });
  2294.  
  2295. saveData();
  2296. reFilter();
  2297. }
  2298. };
  2299.  
  2300. container.appendChild(tc);
  2301. }
  2302.  
  2303. // 删除没有用户的标记
  2304. {
  2305. const tc = document.createElement("div");
  2306.  
  2307. tc.innerHTML += `
  2308. <br/>
  2309. <div>
  2310. <button>删除没有用户的标记</button>
  2311. </div>
  2312. `;
  2313.  
  2314. const actions = tc.getElementsByTagName("button");
  2315.  
  2316. actions[0].onclick = () => {
  2317. if (confirm("是否确认?")) {
  2318. Object.values(data.tags).forEach((item) => {
  2319. if (
  2320. Object.values(data.users).filter((user) =>
  2321. user.tags.find((tag) => tag === item.id)
  2322. ).length === 0
  2323. ) {
  2324. delete data.tags[item.id];
  2325. }
  2326. });
  2327.  
  2328. saveData();
  2329. reFilter();
  2330. }
  2331. };
  2332.  
  2333. container.appendChild(tc);
  2334. }
  2335.  
  2336. // 删除非激活中的用户
  2337. {
  2338. const tc = document.createElement("div");
  2339.  
  2340. tc.innerHTML += `
  2341. <br/>
  2342. <div>
  2343. <button>删除非激活中的用户</button>
  2344. <div style="white-space: normal;"></div>
  2345. </div>
  2346. `;
  2347.  
  2348. const action = tc.querySelector("button");
  2349. const list = action.nextElementSibling;
  2350.  
  2351. action.onclick = () => {
  2352. if (confirm("是否确认?")) {
  2353. const waitingQueue = Object.values(data.users).map(
  2354. (item) => () =>
  2355. new Promise((resolve) => {
  2356. fetch(
  2357. `/nuke.php?lite=js&__lib=ucp&__act=get&uid=${item.id}`
  2358. )
  2359. .then((res) => res.blob())
  2360. .then((blob) => {
  2361. const reader = new FileReader();
  2362.  
  2363. reader.onload = () => {
  2364. const text = reader.result;
  2365. const result = JSON.parse(
  2366. text.replace(
  2367. "window.script_muti_get_var_store=",
  2368. ""
  2369. )
  2370. );
  2371.  
  2372. if (!result.error) {
  2373. const { bit } = result.data[0];
  2374.  
  2375. const activeInfo = n.activeInfo(0, 0, bit);
  2376.  
  2377. const activeType = activeInfo[1];
  2378.  
  2379. if (!["ACTIVED", "LINKED"].includes(activeType)) {
  2380. list.innerHTML += `<a href="/nuke.php?func=ucp&uid=${
  2381. item.id
  2382. }" class="b nobr">[${
  2383. item.name ? "@" + item.name : "#" + item.id
  2384. }]</a>`;
  2385.  
  2386. delete data.users[item.id];
  2387. }
  2388. }
  2389.  
  2390. resolve();
  2391. };
  2392.  
  2393. reader.readAsText(blob, "GBK");
  2394. })
  2395. .catch(() => {
  2396. resolve();
  2397. });
  2398. })
  2399. );
  2400.  
  2401. const queueLength = waitingQueue.length;
  2402.  
  2403. const execute = () => {
  2404. if (waitingQueue.length) {
  2405. const next = waitingQueue.shift();
  2406.  
  2407. action.innerHTML = `删除非激活中的用户 (${
  2408. queueLength - waitingQueue.length
  2409. }/${queueLength})`;
  2410. action.disabled = true;
  2411.  
  2412. next().finally(execute);
  2413. } else {
  2414. action.disabled = false;
  2415.  
  2416. saveData();
  2417. reFilter();
  2418. }
  2419. };
  2420.  
  2421. execute();
  2422. }
  2423. };
  2424.  
  2425. container.appendChild(tc);
  2426. }
  2427. };
  2428.  
  2429. return func;
  2430. })();
  2431.  
  2432. return {
  2433. name: "通用设置",
  2434. content,
  2435. refresh,
  2436. };
  2437. })();
  2438.  
  2439. u.addModule(userModule).toggle();
  2440. u.addModule(tagModule);
  2441. u.addModule(keywordModule);
  2442. u.addModule(locationModule);
  2443. u.addModule(witchHuntModule);
  2444. u.addModule(commonModule);
  2445.  
  2446. // 增加菜单项
  2447. (() => {
  2448. const title = "过滤设置";
  2449.  
  2450. let window;
  2451.  
  2452. const container = document.createElement("DIV");
  2453.  
  2454. container.className = `td`;
  2455. container.innerHTML = `<a class="mmdefault" href="javascript: void(0);" style="white-space: nowrap;">屏蔽</a>`;
  2456.  
  2457. const content = container.querySelector("A");
  2458.  
  2459. const anchor = document.querySelector("#mainmenu .td:last-child");
  2460.  
  2461. anchor.before(container);
  2462.  
  2463. content.onclick = () => {
  2464. if (window === undefined) {
  2465. window = n.createCommmonWindow();
  2466. }
  2467.  
  2468. window._.addContent(null);
  2469. window._.addTitle(title);
  2470. window._.addContent(u.content);
  2471. window._.show();
  2472. };
  2473. })();
  2474.  
  2475. // 执行过滤
  2476. (() => {
  2477. const hookFunction = (object, functionName, callback) => {
  2478. ((originalFunction) => {
  2479. object[functionName] = function () {
  2480. const returnValue = originalFunction.apply(this, arguments);
  2481.  
  2482. callback.apply(this, [returnValue, originalFunction, arguments]);
  2483.  
  2484. return returnValue;
  2485. };
  2486. })(object[functionName]);
  2487. };
  2488.  
  2489. const initialized = {
  2490. topicArg: false,
  2491. postArg: false,
  2492. };
  2493.  
  2494. hookFunction(n, "eval", () => {
  2495. if (Object.values(initialized).findIndex((item) => item === false) < 0) {
  2496. return;
  2497. }
  2498.  
  2499. if (n.topicArg && initialized.topicArg === false) {
  2500. hookFunction(n.topicArg, "add", reFilter);
  2501.  
  2502. initialized.topicArg = true;
  2503. }
  2504.  
  2505. if (n.postArg && initialized.postArg === false) {
  2506. hookFunction(n.postArg, "proc", reFilter);
  2507.  
  2508. initialized.postArg = true;
  2509. }
  2510. });
  2511.  
  2512. reFilter();
  2513. })();
  2514. })(commonui, __CURRENT_UID);