您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
troll must die
当前为
- // ==UserScript==
- // @name NGA Filter
- // @namespace https://greasyfork.org/users/263018
- // @version 1.4.2
- // @author snyssss
- // @description troll must die
- // @match *://bbs.nga.cn/*
- // @match *://ngabbs.com/*
- // @match *://nga.178.com/*
- // @grant GM_addStyle
- // @grant GM_setValue
- // @grant GM_getValue
- // @noframes
- // ==/UserScript==
- ((n, self) => {
- if (n === undefined) return;
- const key = "NGAFilter";
- // 过滤提示
- const FILTER_TIPS =
- "过滤顺序:用户 > 标记 > 关键字<br/>过滤级别:隐藏 > 遮罩 > 标记 > 继承 > 显示<br/>多个标记或者关键字按最高级别过滤";
- // 过滤方式
- const FILTER_MODE = ["继承", "标记", "遮罩", "隐藏", "显示"];
- // 切换过滤方式
- const switchFilterMode = (value) => {
- const next = FILTER_MODE.indexOf(value) + 1;
- if (next >= FILTER_MODE.length) {
- return FILTER_MODE[0];
- }
- return FILTER_MODE[next];
- };
- // 数据
- const data = (() => {
- const d = {
- tags: {},
- users: {},
- keywords: {},
- options: {
- filterRegdateLimit: 0,
- filterPostnumLimit: 0,
- filterMode: "隐藏",
- },
- };
- const v = GM_getValue(key);
- if (typeof v !== "object") {
- return d;
- }
- return Object.assign(d, v);
- })();
- // 保存数据
- const saveData = () => {
- GM_setValue(key, data);
- };
- // 增加标记
- const addTag = (name) => {
- const tag = Object.values(data.tags).find((item) => item.name === name);
- if (tag) return tag.id;
- const id =
- Math.max(...Object.values(data.tags).map((item) => item.id), 0) + 1;
- const hash = (() => {
- let h = 5381;
- for (var i = 0; i < name.length; i++) {
- h = ((h << 5) + h + name.charCodeAt(i)) & 0xffffffff;
- }
- return h;
- })();
- const hex = Math.abs(hash).toString(16) + "000000";
- const hsv = [
- `0x${hex.substring(2, 4)}` / 255,
- `0x${hex.substring(2, 4)}` / 255 / 2 + 0.25,
- `0x${hex.substring(4, 6)}` / 255 / 2 + 0.25,
- ];
- const rgb = n.hsvToRgb(hsv[0], hsv[1], hsv[2]);
- const color = ["#", ...rgb].reduce((a, b) => {
- return a + ("0" + b.toString(16)).slice(-2);
- });
- data.tags[id] = {
- id,
- name,
- color,
- filterMode: FILTER_MODE[0],
- };
- saveData();
- return id;
- };
- // 增加用户
- const addUser = (id, name = null, tags = [], filterMode = FILTER_MODE[0]) => {
- if (data.users[id]) return data.users[id];
- data.users[id] = {
- id,
- name,
- tags,
- filterMode,
- };
- saveData();
- return data.users[id];
- };
- // 增加关键字
- const addKeyword = (
- keyword,
- filterMode = FILTER_MODE[0],
- filterLevel = 0
- ) => {
- const id =
- Math.max(...Object.values(data.keywords).map((item) => item.id), 0) + 1;
- data.keywords[id] = {
- id,
- keyword,
- filterMode,
- filterLevel,
- };
- saveData();
- return id;
- };
- // 旧版本数据迁移
- {
- const dataKey = "troll_data";
- const modeKey = "troll_mode";
- const keywordKey = "troll_keyword";
- if (localStorage.getItem(dataKey)) {
- let trollMap = (function () {
- try {
- return JSON.parse(localStorage.getItem(dataKey)) || {};
- } catch (e) {}
- return {};
- })();
- let filterMode = ~~localStorage.getItem(modeKey);
- let filterKeyword = localStorage.getItem(keywordKey) || "";
- // 整理标签
- [...new Set(Object.values(trollMap).flat())].forEach((item) =>
- addTag(item)
- );
- // 整理用户
- Object.keys(trollMap).forEach((item) => {
- addUser(
- item,
- null,
- (typeof trollMap[item] === "object" ? trollMap[item] : []).map(
- (tag) => addTag(tag)
- )
- );
- });
- data.options.filterMode = filterMode ? "隐藏" : "标记";
- data.options.keyword = filterKeyword;
- localStorage.removeItem(dataKey);
- localStorage.removeItem(modeKey);
- localStorage.removeItem(keywordKey);
- saveData();
- }
- // v1.1.0 -> v1.1.1
- {
- Object.values(data.users).forEach(({ id, name, tags, enabled }) => {
- if (enabled !== undefined) {
- data.users[id] = {
- id,
- name,
- tags,
- filterMode: enabled ? "继承" : "显示",
- };
- }
- });
- Object.values(data.tags).forEach(({ id, name, color, enabled }) => {
- if (enabled !== undefined) {
- data.tags[id] = {
- id,
- name,
- color,
- filterMode: enabled ? "继承" : "显示",
- };
- }
- });
- if (data.options.filterMode === 0) {
- data.options.filterMode = "隐藏";
- } else if (data.options.filterMode === 1) {
- data.options.filterMode = "标记";
- }
- saveData();
- }
- // v1.2.x -> v1.3.0
- {
- if (data.options.keyword) {
- addKeyword(data.options.keyword);
- delete data.options.keyword;
- saveData();
- }
- }
- }
- // 编辑用户标记
- const editUser = (() => {
- let window;
- return (uid, name, callback) => {
- if (window === undefined) {
- window = n.createCommmonWindow();
- }
- const user = data.users[uid];
- const content = document.createElement("div");
- const size = Math.floor((screen.width * 0.8) / 200);
- const items = Object.values(data.tags).map(
- (tag, index) => `
- <td class="c1">
- <label for="s-tag-${index}" style="display: block; cursor: pointer;">
- <b class="block_txt nobr" style="background:${
- tag.color
- }; color:#fff; margin: 0.1em 0.2em;">${tag.name}</b>
- </label>
- </td>
- <td class="c2" width="1">
- <input id="s-tag-${index}" type="checkbox" value="${tag.id}" ${
- user && user.tags.find((item) => item === tag.id) && "checked"
- }/>
- </td>
- `
- );
- const rows = [...new Array(Math.ceil(items.length / size))].map(
- (item, index) =>
- `
- <tr class="row${(index % 2) + 1}">
- ${items.slice(size * index, size * (index + 1)).join("")}
- </tr>
- `
- );
- content.className = "w100";
- content.innerHTML = `
- <div class="filter-table-wrapper" style="width: 80vw;">
- <table class="filter-table forumbox">
- <tbody>
- ${rows.join("")}
- </tbody>
- </table>
- </div>
- <div style="margin: 10px 0;">
- <input placeholder="一次性添加多个标记用"|"隔开,不会添加重名标记" style="width: -webkit-fill-available;" />
- </div>
- <div style="margin: 10px 0;">
- <span>过滤方式:</span>
- <button>${(user && user.filterMode) || FILTER_MODE[0]}</button>
- <div class="right_">
- <button>删除</button>
- <button>保存</button>
- </div>
- </div>
- <div class="silver" style="margin-top: 5px;">${FILTER_TIPS}</div>
- `;
- const actions = content.getElementsByTagName("button");
- actions[0].onclick = () => {
- actions[0].innerText = switchFilterMode(
- actions[0].innerText || FILTER_MODE[0]
- );
- };
- actions[1].onclick = () => {
- if (confirm("是否确认?")) {
- delete data.users[uid];
- saveData();
- callback && callback();
- window._.hide();
- }
- };
- actions[2].onclick = () => {
- if (confirm("是否确认?")) {
- const values = [...content.getElementsByTagName("input")];
- const newTags = values[values.length - 1].value
- .split("|")
- .filter((item) => item.length)
- .map((item) => addTag(item));
- const tags = [
- ...new Set(
- values
- .filter((item) => item.type === "checkbox" && item.checked)
- .map((item) => ~~item.value)
- .concat(newTags)
- ),
- ].sort();
- if (user) {
- user.tags = tags;
- user.filterMode = actions[0].innerText;
- } else {
- addUser(uid, name, tags, actions[0].innerText);
- }
- saveData();
- callback && callback();
- window._.hide();
- }
- };
- if (user === undefined) {
- actions[1].style = "display: none;";
- }
- window._.addContent(null);
- window._.addTitle(`编辑标记 - ${name ? name : "#" + uid}`);
- window._.addContent(content);
- window._.show();
- };
- })();
- // 判断过滤方式
- const getFilterMode = async (uid, content, level) => {
- let result = -1;
- const filterRegdateLimit = data.options.filterRegdateLimit || 0;
- const filterPostnumLimit = data.options.filterPostnumLimit || 0;
- const user = data.users[uid];
- const tags = user ? user.tags.map((tag) => data.tags[tag]) : [];
- const keywords = Object.values(data.keywords);
- if (filterRegdateLimit > 0 || filterPostnumLimit > 0) {
- if (uid > 0) {
- if (n.userInfo.users[uid] === undefined) {
- n.userInfo.users[uid] = await new Promise((resolve) => {
- fetch(`/nuke.php?lite=js&__lib=ucp&__act=get&uid=${uid}`)
- .then((res) => res.blob())
- .then((blob) => {
- const reader = new FileReader();
- reader.onload = () => {
- const text = reader.result;
- const result = JSON.parse(
- text.replace("window.script_muti_get_var_store=", "")
- );
- resolve(result.data[0]);
- };
- reader.readAsText(blob, "GBK");
- })
- .catch(() => {
- resolve();
- });
- });
- }
- const userInfo = n.userInfo.users[uid];
- if (
- userInfo.regdate * 1000 > new Date() - filterRegdateLimit ||
- userInfo.postnum < filterPostnumLimit
- ) {
- return FILTER_MODE.indexOf("隐藏");
- }
- }
- }
- if (user) {
- const filterMode = FILTER_MODE.indexOf(user.filterMode);
- if (filterMode > 0) {
- return filterMode;
- }
- result = filterMode;
- }
- if (tags.length) {
- const filterMode = (() => {
- if (tags.some((tag) => tag.filterMode !== "显示")) {
- return tags
- .filter((tag) => tag.filterMode !== "显示")
- .map((tag) => FILTER_MODE.indexOf(tag.filterMode) || 0)
- .sort((a, b) => b - a)[0];
- }
- return FILTER_MODE.indexOf("显示");
- })();
- if (filterMode > 0) {
- return filterMode;
- }
- result = filterMode;
- }
- if (keywords.length) {
- const filterMode = (() => {
- const r = keywords
- .filter((item) => item.keyword && item.filterMode !== "显示")
- .filter((item) => (item.filterLevel || 0) >= level)
- .sort(
- (a, b) =>
- FILTER_MODE.indexOf(b.filterMode) -
- FILTER_MODE.indexOf(a.filterMode)
- )
- .find((item) => content.search(item.keyword) >= 0);
- if (r) {
- return FILTER_MODE.indexOf(r.filterMode);
- }
- return result;
- })();
- if (filterMode > 0) {
- return filterMode;
- }
- result = filterMode;
- }
- return result;
- };
- // 处理引用
- const handleQuote = async (content) => {
- const quotes = content.querySelectorAll(".quote");
- await Promise.all(
- [...quotes].map(async (quote) => {
- const uid = (() => {
- const ele = quote.querySelector("a[href^='/nuke.php']");
- if (ele) {
- const res = ele.getAttribute("href").match(/uid=(\S+)/);
- if (res) {
- return res[1];
- }
- }
- return 0;
- })();
- if (uid) {
- const filterMode = await new Promise(async (resolve) => {
- const mode = await getFilterMode(uid, quote.innerText, 1);
- if (mode === 0) {
- resolve(data.options.filterMode);
- }
- if (mode > 0) {
- resolve(FILTER_MODE[mode]);
- }
- resolve("");
- });
- if (filterMode === "标记") {
- quote.innerHTML = `
- <div class="lessernuke" style="background: #81C7D4; border-color: #66BAB7; ">
- <span class="crimson">Troll must die.</span>
- <a href="javascript:void(0)" onclick="[...document.getElementsByName('troll_${uid}')].forEach(item => item.style.display = '')">点击查看</a>
- <div style="display: none;" name="troll_${uid}">
- ${quote.innerHTML}
- </div>
- </div>`;
- } else if (filterMode === "遮罩") {
- const source = document.createElement("DIV");
- source.innerHTML = quote.innerHTML;
- source.style.display = "none";
- const caption = document.createElement("CAPTION");
- caption.className = "filter-mask filter-mask-block";
- caption.innerHTML = `<span class="crimson">Troll must die.</span>`;
- caption.onclick = () => {
- quote.removeChild(caption);
- source.style.display = "";
- };
- quote.innerHTML = "";
- quote.appendChild(source);
- quote.appendChild(caption);
- } else if (filterMode === "隐藏") {
- quote.innerHTML = "";
- }
- }
- })
- );
- };
- // 过滤
- const reFilter = (() => {
- let hasNext = false;
- let isRunning = false;
- const func = async () => {
- const tPage = location.pathname === "/thread.php";
- const pPage = location.pathname === "/read.php";
- const uPage = location.pathname === "/nuke.php";
- if (tPage && new RegExp(`authorid=${self}`).test(location.search)) {
- return;
- }
- if (tPage) {
- const tData = n.topicArg.data;
- await Promise.all(
- Object.values(tData).map(async (item) => {
- if (item.containerC) return;
- const uid =
- item[2].search.match(/uid=(\S+)/) &&
- item[2].search.match(/uid=(\S+)/)[1];
- const filterMode = await new Promise(async (resolve) => {
- const mode = await getFilterMode(uid, item[1].innerText, 0);
- if (mode === 0) {
- resolve(data.options.filterMode);
- }
- if (mode > 0) {
- resolve(FILTER_MODE[mode]);
- }
- resolve("");
- });
- item.contentC = item[1];
- item.contentB = item.contentB || item.contentC.innerHTML;
- item.containerC =
- item.containerC || item.contentC.parentNode.parentNode;
- item.containerC.style = "";
- item.contentC.style = "";
- item[1].className = item[1].className.replace(" filter-mask", "");
- item[2].className = item[2].className.replace(" filter-mask", "");
- if (filterMode === "标记") {
- item.contentC.style = "text-decoration: line-through;";
- } else if (filterMode === "遮罩") {
- item[1].className += " filter-mask";
- item[2].className += " filter-mask";
- } else if (filterMode === "隐藏") {
- item.containerC.style = "display: none;";
- }
- })
- );
- } else if (pPage) {
- const pData = n.postArg.data;
- await Promise.all(
- Object.values(pData).map(async (item) => {
- if (~~item.pAid === self) return;
- if (item.containerC) return;
- if (typeof item.i === "number") {
- item.actionC =
- item.actionC ||
- (() => {
- const ele = item.uInfoC.querySelector('[name="uid"]');
- ele.onclick = null;
- return ele;
- })();
- item.tagC =
- item.tagC ||
- (() => {
- const tc = document.createElement("div");
- tc.className = "filter-tags";
- item.uInfoC.appendChild(tc);
- return tc;
- })();
- }
- item.pName =
- item.pName ||
- item.uInfoC.getElementsByClassName("author")[0].innerText;
- item.reFilter =
- item.reFilter ||
- (async () => {
- const uid = item.pAid;
- const filterMode = await new Promise(async (resolve) => {
- const mode = await getFilterMode(
- uid,
- item.contentC.innerText,
- 1
- );
- if (mode === 0) {
- resolve(data.options.filterMode);
- }
- if (mode > 0) {
- resolve(FILTER_MODE[mode]);
- }
- resolve("");
- });
- item.avatarC =
- item.avatarC ||
- (() => {
- const tc = document.createElement("div");
- const avatar = document.getElementById(
- `posteravatar${item.i}`
- );
- if (avatar) {
- avatar.parentNode.insertBefore(tc, avatar.nextSibling);
- tc.appendChild(avatar);
- }
- return tc;
- })();
- item.contentB = item.contentB || item.contentC.innerHTML;
- item.containerC =
- item.containerC ||
- (() => {
- let temp = item.contentC;
- if (item.i >= 0) {
- while (temp.nodeName !== "TBODY") {
- temp = temp.parentNode;
- }
- } else {
- while (temp.nodeName !== "DIV") {
- temp = temp.parentNode;
- }
- }
- return temp;
- })();
- item.avatarC.style.display = "";
- item.containerC.style.display = "";
- item.contentC.innerHTML = item.contentB;
- if (item.actionC) {
- item.actionC.style = "background: #aaa;";
- }
- if (filterMode === "标记") {
- item.avatarC.style.display = "none";
- item.contentC.innerHTML = `
- <div class="lessernuke" style="background: #81C7D4; border-color: #66BAB7; ">
- <span class="crimson">Troll must die.</span>
- <a href="javascript:void(0)" onclick="[...document.getElementsByName('troll_${uid}')].forEach(item => item.style.display = '')">点击查看</a>
- <div style="display: none;" name="troll_${uid}">
- ${item.contentB}
- </div>
- </div>`;
- if (item.actionC && data.users[uid]) {
- item.actionC.style = "background: #cb4042;";
- }
- } else if (filterMode === "遮罩") {
- const caption = document.createElement("CAPTION");
- if (item.i >= 0) {
- caption.className = "filter-mask filter-mask-block";
- } else {
- caption.className = "filter-mask filter-mask-block left";
- caption.style = "width: 47%;";
- }
- caption.innerHTML = `<span class="crimson">Troll must die.</span>`;
- caption.onclick = () => {
- item.containerC.parentNode.removeChild(caption);
- item.containerC.style.display = "";
- };
- item.containerC.parentNode.insertBefore(
- caption,
- item.containerC
- );
- item.containerC.style.display = "none";
- if (item.actionC && data.users[uid]) {
- item.actionC.style = "background: #cb4042;";
- }
- } else if (filterMode === "隐藏") {
- item.containerC.style.display = "none";
- } else {
- await handleQuote(item.contentC);
- }
- if (item.tagC) {
- const tags = data.users[uid]
- ? data.users[uid].tags.map((tag) => data.tags[tag]) || []
- : [];
- item.tagC.style.display = tags.length ? "" : "none";
- item.tagC.innerHTML = tags
- .map(
- (tag) =>
- `<b class="block_txt nobr" style="background:${tag.color}; color:#fff; margin: 0.1em 0.2em;">${tag.name}</b>`
- )
- .join("");
- }
- });
- if (item.actionC) {
- item.actionC.onclick =
- item.actionC.onclick ||
- ((e) => {
- if (item.pAid < 0) return;
- const user = data.users[item.pAid];
- if (e.ctrlKey === false) {
- editUser(item.pAid, item.pName, item.reFilter);
- } else {
- if (user) {
- delete data.users[user.id];
- } else {
- addUser(item.pAid, item.pName);
- }
- saveData();
- item.reFilter();
- }
- });
- }
- await item.reFilter();
- })
- );
- } else if (uPage) {
- const container = document.getElementById("ucp_block");
- if (container.firstChild) {
- const uid = container.innerText.match(/用户ID\s*:\s*(\S+)/)[1];
- const name = container.innerText.match(/用户名\s*:\s*(\S+)/)[1];
- container.tagC =
- container.tagC ||
- (() => {
- const c = document.createElement("span");
- c.innerHTML = `
- <h2 class="catetitle">:: ${name} 的标记 ::</h2>
- <div class="cateblock" style="text-align: left; line-height: 1.8em;">
- <div class="contentBlock" style="padding: 5px 10px;">
- <span>
- <ul class="actions" style="padding: 0px; margin: 0px;">
- <li style="padding-right: 5px;">
- <span>
- <a href="javascript: void(0);">[编辑 ${name} 的标记]</a>
- </span>
- </li>
- <div class="clear"></div>
- </ul>
- </span>
- <div class="filter-tags"></div>
- <div class="clear"></div>
- </div>
- </div>
- `;
- c.getElementsByTagName("a")[0].onclick = () => {
- editUser(uid, name, container.refresh);
- };
- container.firstChild.insertBefore(
- c,
- container.firstChild.childNodes[1]
- );
- return c.getElementsByClassName("filter-tags")[0];
- })();
- container.refresh = () => {
- container.tagC.innerHTML = data.users[uid].tags
- .map(
- (tag) =>
- `<b class="block_txt nobr" style="background:${data.tags[tag].color}; color:#fff; margin: 0.1em 0.2em;">${data.tags[tag].name}</b>`
- )
- .join("");
- };
- container.refresh();
- }
- }
- };
- const execute = () =>
- func().finally(() => {
- if (hasNext) {
- hasNext = false;
- execute();
- } else {
- isRunning = false;
- }
- });
- return async () => {
- if (isRunning) {
- hasNext = true;
- } else {
- isRunning = true;
- await execute();
- }
- };
- })();
- // STYLE
- GM_addStyle(`
- .filter-table-wrapper {
- max-height: 80vh;
- overflow-y: auto;
- }
- .filter-table {
- margin: 0;
- }
- .filter-table th,
- .filter-table td {
- position: relative;
- white-space: nowrap;
- }
- .filter-table th {
- position: sticky;
- top: 2px;
- z-index: 1;
- }
- .filter-table input:not([type]), .filter-table input[type="text"] {
- margin: 0;
- box-sizing: border-box;
- height: 100%;
- width: 100%;
- }
- .filter-input-wrapper {
- position: absolute;
- top: 6px;
- right: 6px;
- bottom: 6px;
- left: 6px;
- }
- .filter-text-ellipsis {
- display: flex;
- }
- .filter-text-ellipsis > * {
- flex: 1;
- width: 1px;
- overflow: hidden;
- text-overflow: ellipsis;
- }
- .filter-button-group {
- margin: -.1em -.2em;
- }
- .filter-tags {
- margin: 2px -0.2em 0;
- text-align: left;
- }
- .filter-mask {
- margin: 1px;
- color: #81C7D4;
- background: #81C7D4;
- }
- .filter-mask-block {
- display: block;
- border: 1px solid #66BAB7;
- text-align: center !important;
- }
- .filter-input-wrapper {
- position: absolute;
- top: 6px;
- right: 6px;
- bottom: 6px;
- left: 6px;
- }
- `);
- // UI
- const u = (() => {
- const modules = {};
- const tabContainer = (() => {
- const c = document.createElement("div");
- c.className = "w100";
- c.innerHTML = `
- <div class="right_" style="margin-bottom: 5px;">
- <table class="stdbtn" cellspacing="0">
- <tbody>
- <tr></tr>
- </tbody>
- </table>
- </div>
- <div class="clear"></div>
- `;
- return c;
- })();
- const tabPanelContainer = (() => {
- const c = document.createElement("div");
- c.style = "width: 80vw;";
- return c;
- })();
- const content = (() => {
- const c = document.createElement("div");
- c.append(tabContainer);
- c.append(tabPanelContainer);
- return c;
- })();
- const addModule = (() => {
- const tc = tabContainer.getElementsByTagName("tr")[0];
- const cc = tabPanelContainer;
- return (module) => {
- const tabBox = document.createElement("td");
- tabBox.innerHTML = `<a href="javascript:void(0)" class="nobr silver">${module.name}</a>`;
- const tab = tabBox.childNodes[0];
- const toggle = () => {
- Object.values(modules).forEach((item) => {
- if (item.tab === tab) {
- item.tab.className = "nobr";
- item.content.style = "display: block";
- item.refresh();
- } else {
- item.tab.className = "nobr silver";
- item.content.style = "display: none";
- }
- });
- };
- tc.append(tabBox);
- cc.append(module.content);
- tab.onclick = toggle;
- modules[module.name] = {
- ...module,
- tab,
- toggle,
- };
- return modules[module.name];
- };
- })();
- return {
- content,
- modules,
- addModule,
- };
- })();
- // 用户
- const userModule = (() => {
- const content = (() => {
- const c = document.createElement("div");
- c.style = "display: none";
- c.innerHTML = `
- <div class="filter-table-wrapper">
- <table class="filter-table forumbox">
- <thead>
- <tr class="block_txt_c0">
- <th class="c1" width="1">昵称</th>
- <th class="c2">标记</th>
- <th class="c3" width="1">过滤方式</th>
- <th class="c4" width="1">操作</th>
- </tr>
- </thead>
- <tbody></tbody>
- </table>
- </div>
- `;
- return c;
- })();
- const refresh = (() => {
- const container = content.getElementsByTagName("tbody")[0];
- const func = () => {
- container.innerHTML = "";
- Object.values(data.users).forEach((item) => {
- const tc = document.createElement("tr");
- tc.className = `row${
- (container.querySelectorAll("TR").length % 2) + 1
- }`;
- tc.refresh = () => {
- if (data.users[item.id]) {
- tc.innerHTML = `
- <td class="c1">
- <a href="/nuke.php?func=ucp&uid=${
- item.id
- }" class="b nobr">[${
- item.name ? "@" + item.name : "#" + item.id
- }]</a>
- </td>
- <td class="c2">
- ${item.tags
- .map((tag) => {
- if (data.tags[tag]) {
- return `<b class="block_txt nobr" style="background:${data.tags[tag].color}; color:#fff; margin: 0.1em 0.2em;">${data.tags[tag].name}</b>`;
- }
- })
- .join("")}
- </td>
- <td class="c3">
- <div class="filter-table-button-group">
- <button>${item.filterMode || FILTER_MODE[0]}</button>
- </div>
- </td>
- <td class="c4">
- <div class="filter-table-button-group">
- <button>编辑</button>
- <button>删除</button>
- </div>
- </td>
- `;
- const actions = tc.getElementsByTagName("button");
- actions[0].onclick = () => {
- data.users[item.id].filterMode = switchFilterMode(
- data.users[item.id].filterMode || FILTER_MODE[0]
- );
- actions[0].innerHTML = data.users[item.id].filterMode;
- saveData();
- reFilter();
- };
- actions[1].onclick = () => {
- editUser(item.id, item.name, tc.refresh);
- };
- actions[2].onclick = () => {
- if (confirm("是否确认?")) {
- delete data.users[item.id];
- container.removeChild(tc);
- saveData();
- reFilter();
- }
- };
- } else {
- tc.remove();
- }
- };
- tc.refresh();
- container.appendChild(tc);
- });
- };
- return func;
- })();
- return {
- name: "用户",
- content,
- refresh,
- };
- })();
- // 标记
- const tagModule = (() => {
- const content = (() => {
- const c = document.createElement("div");
- c.style = "display: none";
- c.innerHTML = `
- <div class="filter-table-wrapper">
- <table class="filter-table forumbox">
- <thead>
- <tr class="block_txt_c0">
- <th class="c1" width="1">标记</th>
- <th class="c2">列表</th>
- <th class="c3" width="1">过滤方式</th>
- <th class="c4" width="1">操作</th>
- </tr>
- </thead>
- <tbody></tbody>
- </table>
- </div>
- `;
- return c;
- })();
- const refresh = (() => {
- const container = content.getElementsByTagName("tbody")[0];
- const func = () => {
- container.innerHTML = "";
- Object.values(data.tags).forEach((item) => {
- const tc = document.createElement("tr");
- tc.className = `row${
- (container.querySelectorAll("TR").length % 2) + 1
- }`;
- tc.innerHTML = `
- <td class="c1">
- <b class="block_txt nobr" style="background:${
- item.color
- }; color:#fff; margin: 0.1em 0.2em;">${item.name}</b>
- </td>
- <td class="c2">
- <button>${
- Object.values(data.users).filter((user) =>
- user.tags.find((tag) => tag === item.id)
- ).length
- }
- </button>
- <div style="white-space: normal; display: none;">
- ${Object.values(data.users)
- .filter((user) =>
- user.tags.find((tag) => tag === item.id)
- )
- .map(
- (user) =>
- `<a href="/nuke.php?func=ucp&uid=${
- user.id
- }" class="b nobr">[${
- user.name ? "@" + user.name : "#" + user.id
- }]</a>`
- )
- .join("")}
- </div>
- </td>
- <td class="c3">
- <div class="filter-table-button-group">
- <button>${item.filterMode || FILTER_MODE[0]}</button>
- </div>
- </td>
- <td class="c4">
- <div class="filter-table-button-group">
- <button>删除</button>
- </div>
- </td>
- `;
- const actions = tc.getElementsByTagName("button");
- actions[0].onclick = (() => {
- let hide = true;
- return () => {
- hide = !hide;
- actions[0].nextElementSibling.style.display = hide
- ? "none"
- : "block";
- };
- })();
- actions[1].onclick = () => {
- data.tags[item.id].filterMode = switchFilterMode(
- data.tags[item.id].filterMode || FILTER_MODE[0]
- );
- actions[1].innerHTML = data.tags[item.id].filterMode;
- saveData();
- reFilter();
- };
- actions[2].onclick = () => {
- if (confirm("是否确认?")) {
- delete data.tags[item.id];
- Object.values(data.users).forEach((user) => {
- const index = user.tags.findIndex((tag) => tag === item.id);
- if (index >= 0) {
- user.tags.splice(index, 1);
- }
- });
- container.removeChild(tc);
- saveData();
- reFilter();
- }
- };
- container.appendChild(tc);
- });
- };
- return func;
- })();
- return {
- name: "标记",
- content,
- refresh,
- };
- })();
- // 关键字
- const keywordModule = (() => {
- const content = (() => {
- const c = document.createElement("div");
- c.style = "display: none";
- c.innerHTML = `
- <div class="filter-table-wrapper">
- <table class="filter-table forumbox">
- <thead>
- <tr class="block_txt_c0">
- <th class="c1">列表</th>
- <th class="c2" width="1">过滤方式</th>
- <th class="c3" width="1">包括内容</th>
- <th class="c4" width="1">操作</th>
- </tr>
- </thead>
- <tbody></tbody>
- </table>
- </div>
- <div class="silver" style="margin-top: 10px;">支持正则表达式。比如同类型的可以写在一条规则内用"|"隔开,"ABC|DEF"即为屏蔽带有ABC或者DEF的内容。</div>
- `;
- return c;
- })();
- const refresh = (() => {
- const container = content.getElementsByTagName("tbody")[0];
- const func = () => {
- container.innerHTML = "";
- Object.values(data.keywords).forEach((item) => {
- const tc = document.createElement("tr");
- tc.className = `row${
- (container.querySelectorAll("TR").length % 2) + 1
- }`;
- tc.innerHTML = `
- <td class="c1">
- <div class="filter-input-wrapper">
- <input value="${item.keyword || ""}" />
- </div>
- </td>
- <td class="c2">
- <div class="filter-table-button-group">
- <button>${item.filterMode || FILTER_MODE[0]}</button>
- </div>
- </td>
- <td class="c3">
- <div style="text-align: center;">
- <input type="checkbox" ${
- item.filterLevel ? `checked="checked"` : ""
- } />
- </div>
- </td>
- <td class="c4">
- <div class="filter-table-button-group">
- <button>保存</button>
- <button>删除</button>
- </div>
- </td>
- `;
- const inputElement = tc.querySelector("INPUT");
- const levelElement = tc.querySelector(`INPUT[type="checkbox"]`);
- const actions = tc.getElementsByTagName("button");
- actions[0].onclick = () => {
- actions[0].innerHTML = switchFilterMode(actions[0].innerHTML);
- };
- actions[1].onclick = () => {
- if (inputElement.value) {
- data.keywords[item.id] = {
- id: item.id,
- keyword: inputElement.value,
- filterMode: actions[0].innerHTML,
- filterLevel: levelElement.checked ? 1 : 0,
- };
- saveData();
- refresh();
- }
- };
- actions[2].onclick = () => {
- if (confirm("是否确认?")) {
- delete data.keywords[item.id];
- saveData();
- refresh();
- }
- };
- container.appendChild(tc);
- });
- {
- const tc = document.createElement("tr");
- tc.className = `row${
- (container.querySelectorAll("TR").length % 2) + 1
- }`;
- tc.innerHTML = `
- <td class="c1">
- <div class="filter-input-wrapper">
- <input value="" />
- </div>
- </td>
- <td class="c2">
- <div class="filter-table-button-group">
- <button>${FILTER_MODE[0]}</button>
- </div>
- </td>
- <td class="c3">
- <div style="text-align: center;">
- <input type="checkbox" />
- </div>
- </td>
- <td class="c4">
- <div class="filter-table-button-group">
- <button>添加</button>
- </div>
- </td>
- `;
- const inputElement = tc.querySelector("INPUT");
- const levelElement = tc.querySelector(`INPUT[type="checkbox"]`);
- const actions = tc.getElementsByTagName("button");
- actions[0].onclick = () => {
- actions[0].innerHTML = switchFilterMode(actions[0].innerHTML);
- };
- actions[1].onclick = () => {
- if (inputElement.value) {
- addKeyword(
- inputElement.value,
- actions[0].innerHTML,
- levelElement.checked ? 1 : 0
- );
- saveData();
- refresh();
- }
- };
- container.appendChild(tc);
- }
- };
- return func;
- })();
- return {
- name: "关键字",
- content,
- refresh,
- };
- })();
- // 通用设置
- const commonModule = (() => {
- const content = (() => {
- const c = document.createElement("div");
- c.style = "display: none";
- return c;
- })();
- const refresh = (() => {
- const container = content;
- const func = () => {
- container.innerHTML = "";
- // 默认过滤方式
- {
- const tc = document.createElement("div");
- tc.innerHTML += `
- <div>默认过滤方式</div>
- <div></div>
- <div class="silver" style="margin-top: 10px;">${FILTER_TIPS}</div>
- `;
- ["标记", "遮罩", "隐藏"].forEach((item, index) => {
- const ele = document.createElement("SPAN");
- ele.innerHTML += `
- <input id="s-fm-${index}" type="radio" name="filterType" ${
- data.options.filterMode === item && "checked"
- }>
- <label for="s-fm-${index}" style="cursor: pointer;">${item}</label>
- `;
- const inp = ele.querySelector("input");
- inp.onchange = () => {
- if (inp.checked) {
- data.options.filterMode = item;
- saveData();
- reFilter();
- }
- };
- tc.querySelectorAll("div")[1].append(ele);
- });
- container.appendChild(tc);
- }
- // 小号过滤(时间)
- {
- const tc = document.createElement("div");
- tc.innerHTML += `
- <br/>
- <div>
- 隐藏注册时间小于<input value="${
- (data.options.filterRegdateLimit || 0) / 86400000
- }" maxLength="4" style="width: 48px;" />天的用户
- <button>确认</button>
- </div>
- `;
- const actions = tc.getElementsByTagName("button");
- actions[0].onclick = () => {
- const v = actions[0].previousElementSibling.value;
- const n = Number(v) || 0;
- data.options.filterRegdateLimit = n < 0 ? 0 : n * 86400000;
- saveData();
- reFilter();
- };
- container.appendChild(tc);
- }
- // 小号过滤(发帖数)
- {
- const tc = document.createElement("div");
- tc.innerHTML += `
- <br/>
- <div>
- 隐藏发帖数量小于<input value="${
- data.options.filterPostnumLimit || 0
- }" maxLength="5" style="width: 48px;" />贴的用户
- <button>确认</button>
- </div>
- `;
- const actions = tc.getElementsByTagName("button");
- actions[0].onclick = () => {
- const v = actions[0].previousElementSibling.value;
- const n = Number(v) || 0;
- data.options.filterPostnumLimit = n < 0 ? 0 : n;
- saveData();
- reFilter();
- };
- container.appendChild(tc);
- }
- // 删除没有标记的用户
- {
- const tc = document.createElement("div");
- tc.innerHTML += `
- <br/>
- <div>
- <button>删除没有标记的用户</button>
- </div>
- `;
- const actions = tc.getElementsByTagName("button");
- actions[0].onclick = () => {
- if (confirm("是否确认?")) {
- Object.values(data.users).forEach((item) => {
- if (item.tags.length === 0) {
- delete data.users[item.id];
- }
- });
- saveData();
- reFilter();
- }
- };
- container.appendChild(tc);
- }
- // 删除没有用户的标记
- {
- const tc = document.createElement("div");
- tc.innerHTML += `
- <br/>
- <div>
- <button>删除没有用户的标记</button>
- </div>
- `;
- const actions = tc.getElementsByTagName("button");
- actions[0].onclick = () => {
- if (confirm("是否确认?")) {
- Object.values(data.tags).forEach((item) => {
- if (
- Object.values(data.users).filter((user) =>
- user.tags.find((tag) => tag === item.id)
- ).length === 0
- ) {
- delete data.tags[item.id];
- }
- });
- saveData();
- reFilter();
- }
- };
- container.appendChild(tc);
- }
- };
- return func;
- })();
- return {
- name: "通用设置",
- content,
- refresh,
- };
- })();
- u.addModule(userModule).toggle();
- u.addModule(tagModule);
- u.addModule(keywordModule);
- u.addModule(commonModule);
- // 增加菜单项
- (() => {
- const title = "过滤设置";
- let window;
- n.mainMenu.addItemOnTheFly(title, null, () => {
- if (window === undefined) {
- window = n.createCommmonWindow();
- }
- window._.addContent(null);
- window._.addTitle(title);
- window._.addContent(u.content);
- window._.show();
- });
- })();
- // 执行过滤
- (() => {
- const hookFunction = (object, functionName, callback) => {
- ((originalFunction) => {
- object[functionName] = function () {
- const returnValue = originalFunction.apply(this, arguments);
- callback.apply(this, [returnValue, originalFunction, arguments]);
- return returnValue;
- };
- })(object[functionName]);
- };
- const initialized = {
- topicArg: false,
- postArg: false,
- };
- hookFunction(n, "eval", () => {
- if (Object.values(initialized).findIndex((item) => item === false) < 0) {
- return;
- }
- if (n.topicArg && initialized.topicArg === false) {
- hookFunction(n.topicArg, "add", reFilter);
- initialized.topicArg = true;
- }
- if (n.postArg && initialized.postArg === false) {
- hookFunction(n.postArg, "proc", reFilter);
- initialized.postArg = true;
- }
- });
- if (n.ucp) {
- hookFunction(n.ucp, "_echo", reFilter);
- }
- reFilter();
- })();
- })(commonui, __CURRENT_UID);