您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
troll must die
当前为
// ==UserScript== // @name NGA Filter // @namespace https://gf.qytechs.cn/users/263018 // @version 1.0.1 // @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+)/).length && 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); // commonui.postArg // commonui.topicArg
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址