您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
troll must die
当前为
- // ==UserScript==
- // @name NGA Filter
- // @namespace https://greasyfork.org/users/263018
- // @version 1.0.2
- // @author snyssss
- // @description troll must die
- // @match *bbs.nga.cn/thread.php?fid=*
- // @match *bbs.nga.cn/read.php?tid=*
- // @match *bbs.nga.cn/nuke.php?*
- // @match *ngabbs.com/thread.php?fid=*
- // @match *ngabbs.com/read.php?tid=*
- // @match *ngabbs.com/nuke.php?*
- // @grant GM_addStyle
- // @grant GM_setValue
- // @grant GM_getValue
- // @noframes
- // ==/UserScript==
- (n => {
- "use strict";
- if (n === undefined) return;
- const key = "NGAFilter";
- // 数据
- const data = (() => {
- const d = {
- tags: {},
- users: {},
- options: {
- filterMode: 0,
- keyword: ""
- }
- };
- 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.substr(2, 2)}` / 255,
- `0x${hex.substr(2, 2)}` / 255 / 2 + 0.25,
- `0x${hex.substr(4, 2)}` / 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,
- enabled: true
- };
- saveData();
- return id;
- };
- // 增加用户
- const addUser = (id, name = null, tags = [], isEnabled = true) => {
- if (data.users[id]) return data.users[id];
- data.users[id] = {
- id,
- name,
- tags,
- enabled: isEnabled
- };
- saveData();
- return data.users[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,
- trollMap[item].map(tag => addTag(tag))
- );
- });
- data.options.filterMode = filterMode ? 0 : 1;
- data.options.keyword = filterKeyword;
- localStorage.removeItem(dataKey);
- localStorage.removeItem(modeKey);
- localStorage.removeItem(keywordKey);
- 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");
- content.className = "w100";
- content.innerHTML = `
- <table class="filter-table" style="min-width: 400px;">
- <tbody>
- ${Object.values(data.tags)
- .map(
- tag => `
- <tr>
- <td>
- <b class="block_txt nobr" style="background:${
- tag.color
- }; color:#fff; margin: 0.1em 0.2em;">${
- tag.name
- }</b>
- </td>
- <td>
- <input type="checkbox" value="${
- tag.id
- }" ${user &&
- user.tags.find(item => item === tag.id) &&
- "checked"}/>
- </td>
- </tr>
- `
- )
- .join("")}
- </tbody>
- <tfoot>
- <tr>
- <td colspan="2">
- <input placeholder="一次性添加多个标记用"|"隔开,不会添加重名标记" style="width: -webkit-fill-available;" />
- </td>
- </tr>
- </tfoot>
- </table>
- <div style="margin: 10px 0;">
- <button>${user && user.enabled === false ? "启用" : "禁用"}</button>
- <div class="right_">
- <button>删除</button>
- <button>保存</button>
- </div>
- </div>
- `;
- const actions = content.getElementsByTagName("button");
- actions[0].onclick = () => {
- actions[0].innerText =
- actions[0].innerText === "禁用" ? "启用" : "禁用";
- };
- 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.enabled = 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 reFilter = (() => {
- const tPage = location.pathname === "/thread.php";
- const pPage = location.pathname === "/read.php";
- const uPage = location.pathname === "/nuke.php";
- const func = (() => {
- if (tPage) {
- return () => {
- const tData = n.topicArg.data;
- Object.values(tData).forEach(item => {
- const uid =
- item[2].search.match(/uid=(\S+)/) &&
- item[2].search.match(/uid=(\S+)/)[1];
- const user = data.users[uid];
- const tags = user ? user.tags.map(tag => data.tags[tag]) : [];
- const isBlock =
- user &&
- user.enabled &&
- (tags.length === 0 || tags.filter(tag => tag.enabled).length);
- item.contentC = item[1];
- item.contentB = item.contentB || item.contentC.innerHTML;
- item.containerC =
- item.containerC || item.contentC.parentNode.parentNode;
- item.containerC.style =
- isBlock && data.options.filterMode === 0 ? "display: none;" : "";
- item.contentC.style =
- isBlock && data.options.filterMode === 1
- ? "text-decoration: line-through;"
- : "";
- });
- };
- } else if (pPage) {
- return () => {
- const pData = n.postArg.data;
- Object.values(pData).forEach(item => {
- if (typeof item.i === "number") {
- item.actionC =
- item.actionC ||
- (() => {
- const ele = item.uInfoC.firstElementChild.lastElementChild;
- 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 ||
- (() => {
- const user = data.users[item.pAid];
- const tags = user ? user.tags.map(tag => data.tags[tag]) : [];
- const isBlock =
- user &&
- user.enabled &&
- (tags.length === 0 || tags.filter(tag => tag.enabled).length);
- 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;
- while (
- temp.className !== "forumbox postbox" &&
- temp.className !== "comment_c left"
- ) {
- temp = temp.parentNode;
- }
- return temp;
- })();
- item.containerC.style.display =
- isBlock && data.options.filterMode === 0 ? "none" : "";
- item.contentC.innerHTML =
- isBlock && data.options.filterMode === 1
- ? `
- <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_${user.id}')].forEach(item => item.style.display = '')">点击查看</a>
- <div style="display: none;" name="troll_${user.id}">
- ${item.contentB}
- </div>
- </div>`
- : item.contentB;
- item.avatarC.style.display = isBlock ? "none" : "";
- if (item.actionC) {
- item.actionC.style =
- user && user.enabled
- ? "background: #cb4042;"
- : "background: #aaa;";
- }
- if (item.tagC) {
- 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) {
- editUser(item.pAid, item.pName, item.reFilter);
- } else {
- if (user) {
- if (user.tags.length) {
- user.enabled = !user.enabled;
- user.name = item.pName;
- } else {
- delete data.users[user.id];
- }
- } else {
- addUser(item.pAid, item.pName);
- }
- saveData();
- item.reFilter();
- }
- });
- }
- item.reFilter();
- });
- };
- } else if (uPage) {
- return () => {
- 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();
- }
- };
- }
- return () => {};
- })();
- const observer = new MutationObserver(mutations => {
- if (mutations.find(mutation => mutation.addedNodes.length)) {
- func();
- }
- });
- if (tPage) {
- observer.observe(document.getElementById("topicrows"), {
- childList: true
- });
- } else if (pPage) {
- observer.observe(document.getElementById("m_posts_c"), {
- childList: true
- });
- } else if (uPage) {
- observer.observe(document.getElementById("ucp_block"), {
- childList: true
- });
- }
- func();
- return func;
- })();
- // STYLE
- GM_addStyle(`
- .filter-tags {
- margin: 2px -0.2em 0;
- text-align: left;
- }
- .filter-table {
- border: 1px solid #ead5bc;
- border-left: none;
- border-bottom: none;
- width: 99.95%;
- }
- .filter-table thead {
- background-color: #591804;
- color: #fff8e7;
- }
- .filter-table tbody tr {
- background-color: #fff0cd;
- }
- .filter-table tbody tr:nth-of-type(odd) {
- background-color: #fff8e7;
- }
- .filter-table td {
- border: 1px solid #ead5bc;
- border-top: none;
- border-right: none;
- padding: 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 =
- "min-width: 20vw; max-width: 80vw; max-height: 80vh; overflow: auto;";
- c.innerHTML = `
- `;
- 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 blockModule = (() => {
- const content = (() => {
- const c = document.createElement("div");
- c.style = "display: none";
- c.innerHTML = `
- <table class="filter-table">
- <thead>
- <tr>
- <td>
- <b style="margin: 0.1em 0.2em;">昵称</b>
- </td>
- <td>
- <b style="margin: 0.1em 0.2em;">标记</b>
- </td>
- <td>
- <b style="margin: 0.1em 0.2em;">操作</b>
- </td>
- </tr>
- </thead>
- <tbody></tbody>
- </table>
- `;
- 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.refresh = () => {
- if (data.users[item.id]) {
- tc.innerHTML = `
- <tr>
- <td>
- <a href="/nuke.php?func=ucp&uid=${
- item.id
- }" class="b nobr">[${
- item.name ? "@" + item.name : "#" + item.id
- }]</a>
- </td>
- <td>
- ${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="nobr">
- <button>编辑</button>
- <button>${item.enabled ? "禁用" : "启用"}</button>
- <button>删除</button>
- </td>
- </tr>
- `;
- const actions = tc.getElementsByTagName("button");
- actions[0].onclick = () => {
- editUser(item.id, item.name, tc.refresh);
- };
- actions[1].onclick = () => {
- data.users[item.id].enabled = !data.users[item.id].enabled;
- actions[1].innerHTML = data.users[item.id].enabled
- ? "禁用"
- : "启用";
- saveData();
- reFilter();
- };
- 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 = `
- <table class="filter-table">
- <thead>
- <tr>
- <td>
- <b style="margin: 0.1em 0.2em;">标记</b>
- </td>
- <td>
- <b style="margin: 0.1em 0.2em;">列表</b>
- </td>
- <td>
- <b style="margin: 0.1em 0.2em;">操作</b>
- </td>
- </tr>
- </thead>
- <tbody></tbody>
- </table>
- `;
- 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.innerHTML = `
- <tr>
- <td>
- <b class="block_txt nobr" style="background:${
- item.color
- }; color:#fff; margin: 0.1em 0.2em;">${item.name}</b>
- </td>
- <td>
- <button>${
- Object.values(data.users).filter(user =>
- user.tags.find(tag => tag === item.id)
- ).length
- }
- </button>
- <div style="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="nobr">
- <button>${item.enabled ? "禁用" : "启用"}</button>
- <button>删除</button>
- </td>
- </tr>
- `;
- 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].enabled = !data.tags[item.id].enabled;
- actions[1].innerHTML = data.tags[item.id].enabled ? "禁用" : "启用";
- 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 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>
- <input value="${data.options.keyword}"/>
- <button>确认</button>
- </div>
- `;
- const actions = tc.getElementsByTagName("button");
- actions[0].onclick = () => {
- const v = actions[0].previousElementSibling.value;
- data.options.keyword = v;
- saveData();
- reFilter();
- };
- container.appendChild(tc);
- }
- // 过滤方式
- {
- const tc = document.createElement("div");
- tc.innerHTML += `
- <br/>
- <div>过滤方式</div>
- <div>
- <input type="radio" name="filterType" ${data.options
- .filterMode === 0 && "checked"}>
- <span>隐藏</span>
- <input type="radio" name="filterType" ${data.options
- .filterMode === 1 && "checked"}>
- <span>标记</span>
- <button>确认</button>
- </div>
- `;
- const actions = tc.getElementsByTagName("button");
- actions[0].onclick = () => {
- const values = document.getElementsByName("filterType");
- for (let i = 0, length = values.length; i < length; i++) {
- if (values[i].checked) {
- data.options.filterMode = i;
- break;
- }
- }
- 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(blockModule).toggle();
- u.addModule(tagModule);
- 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();
- });
- })();
- })(commonui);