BG6UFA 关于 block user ,网页我现在是用这个油猴脚本过滤,眼不见为净

这些地方都可以操作



// ==UserScript==
// @name HamCQ 用户拉黑
// @namespace https://flarum.blocker.HamCQ
// @version 1.0
// @description HamCQ 拉黑 + 关键词过滤
// @match https://forum.hamcq.cn/*
// @grant GM.getValue
// @grant GM.setValue
// @grant GM.registerMenuCommand
// @run-at document-end
// ==/UserScript==
(() => {
'use strict';
const KEY_U = 'fb_users_v4';
const KEY_W = 'fb_words_v4';
let users = new Set();
let words = new Set();
async function load() {
users = new Set(JSON.parse(await GM.getValue(KEY_U, '[]')).map(x => x.toLowerCase()));
words = new Set(JSON.parse(await GM.getValue(KEY_W, '[]')).map(x => x.toLowerCase()));
}
async function save() {
await GM.setValue(KEY_U, JSON.stringify([...users]));
await GM.setValue(KEY_W, JSON.stringify([...words]));
}
function getUser(el) {
if (!el) return '';
return (el.getAttribute?.('data-username') || el.textContent || '').trim().toLowerCase();
}
function hasWord(text) {
if (!text) return false;
text = text.toLowerCase();
return [...words].some(w => w && text.includes(w));
}
function filterAll() {
const nodes = document.querySelectorAll(`
.DiscussionListItem,
.Post,
.PostStream-item,
.UserCard,
.PostUser,
article
`);
nodes.forEach(el => {
let userEl =
el.querySelector('[data-username]') ||
el.querySelector('.username a') ||
el.querySelector('.username') ||
el.querySelector('a[href*="/u/"], a[href*="/user/"]');
const name = getUser(userEl);
const text = (el.innerText || '').toLowerCase();
if (users.has(name) || hasWord(text)) {
el.style.display = 'none';
el.dataset.fbHidden = '1';
}
});
}
function injectDropdown() {
document.querySelectorAll('.Dropdown-menu').forEach(menu => {
if (menu.dataset.fbInjected) return;
const card =
menu.closest('.UserCard') ||
menu.closest('.PostUser') ||
menu.closest('.Post');
if (!card) return;
const userEl =
card.querySelector('[data-username]') ||
card.querySelector('.username a') ||
card.querySelector('.username');
const name = getUser(userEl);
if (!name) return;
const li = document.createElement('li');
li.className = 'Dropdown-menuItem item-block-user';
li.innerHTML = `
<button type="button" class="Dropdown-item">
<i class="icon fas fa-ban" style="margin-right:8px;"></i>
拉黑用户
</button>
`;
const btn = li.querySelector('button');
btn.onclick = async (e) => {
e.preventDefault();
e.stopPropagation();
users.add(name);
await save();
filterAll();
toast('已拉黑 ' + name);
};
menu.appendChild(li);
menu.dataset.fbInjected = '1';
});
}
function toast(msg) {
const el = document.createElement('div');
el.textContent = msg;
el.style.cssText = `
position:fixed;
top:20px;
right:20px;
background:rgba(0,0,0,.85);
color:#fff;
padding:8px 12px;
border-radius:6px;
z-index:999999;
font-size:12px;
`;
document.body.appendChild(el);
setTimeout(() => el.remove(), 1500);
}
function openPanel() {
const old = document.getElementById('fb-panel');
if (old) old.remove();
const overlay = document.createElement('div');
overlay.id = 'fb-panel';
overlay.style.cssText = `
position:fixed;
inset:0;
background:rgba(0,0,0,.4);
z-index:999999;
`;
const box = document.createElement('div');
box.style.cssText = `
position:absolute;
top:60px;
right:12px;
width:320px;
background:var(--body-bg);
color:var(--text-color);
border-radius:10px;
padding:12px;
box-shadow:0 10px 30px rgba(0,0,0,.2);
`;
box.innerHTML = `
<div style="font-weight:600;margin-bottom:10px;">过滤管理</div>
<div style="font-size:12px;opacity:.7;">用户 (${users.size})</div>
<input id="u" style="width:100%;padding:6px;">
<button id="ua" style="width:100%;margin-top:6px;">添加用户</button>
<div id="ulist" style="margin-top:8px;"></div>
<hr>
<div style="font-size:12px;opacity:.7;">关键词 (${words.size})</div>
<input id="w" style="width:100%;padding:6px;">
<button id="wa" style="width:100%;margin-top:6px;">添加关键词</button>
<div id="wlist" style="margin-top:8px;"></div>
<button id="close" style="width:100%;margin-top:10px;">关闭</button>
`;
overlay.appendChild(box);
document.body.appendChild(overlay);
overlay.onclick = (e) => {
if (e.target === overlay) overlay.remove();
};
box.onclick = (e) => e.stopPropagation();
const ulist = box.querySelector('#ulist');
const wlist = box.querySelector('#wlist');
function render() {
ulist.innerHTML = [...users].map(u =>
`<div style="display:flex;justify-content:space-between;margin:4px 0;">
<span>${u}</span>
<button data-u="${u}">删除</button>
</div>`
).join('');
wlist.innerHTML = [...words].map(w =>
`<div style="display:flex;justify-content:space-between;margin:4px 0;">
<span>${w}</span>
<button data-w="${w}">删除</button>
</div>`
).join('');
ulist.querySelectorAll('button').forEach(b => {
b.onclick = async () => {
users.delete(b.dataset.u);
await save();
render();
filterAll();
};
});
wlist.querySelectorAll('button').forEach(b => {
b.onclick = async () => {
words.delete(b.dataset.w);
await save();
render();
filterAll();
};
});
}
box.querySelector('#ua').onclick = async () => {
const v = box.querySelector('#u').value.trim().toLowerCase();
if (!v) return;
users.add(v);
await save();
render();
filterAll();
};
box.querySelector('#wa').onclick = async () => {
const v = box.querySelector('#w').value.trim().toLowerCase();
if (!v) return;
words.add(v);
await save();
render();
filterAll();
};
box.querySelector('#close').onclick = () => overlay.remove();
render();
}
function createFloat() {
if (document.getElementById('fb-float')) return;
const btn = document.createElement('div');
btn.id = 'fb-float';
btn.innerHTML = `<i class="icon fas fa-filter"></i>`;
btn.style.cssText = `
position:fixed;
top:12px;
right:12px;
width:36px;
height:36px;
background:var(--control-bg,#f5f5f5);
border:1px solid rgba(0,0,0,.1);
border-radius:6px;
display:flex;
align-items:center;
justify-content:center;
cursor:pointer;
z-index:999999;
`;
btn.onclick = openPanel;
document.body.appendChild(btn);
}
async function init() {
await load();
createFloat();
filterAll();
injectDropdown();
const obs = new MutationObserver(() => {
requestAnimationFrame(() => {
filterAll();
injectDropdown();
});
});
obs.observe(document.body, { childList: true, subtree: true });
GM.registerMenuCommand('过滤面板', openPanel);
}
init();
})();