BG6UFA ブロック機能については、現在私はこのGreasy Squirrelスクリプトを使用してフィルタリングを行っています。

これらの場所で操作が可能です。



// ==ユーザースクリプト==
// @名前 HamCQ のユーザーをブロック
// @名前空間 https://flarum.blocker.HamCQ
// @バージョン 1.0
// @説明 HamCQ によるブロック + キーワードフィルタリング
// @マッチ https://forum.hamcq.cn/*
// @許可 GM.getValue
// @許可 GM.setValue
// @許可 GM.registerMenuCommand
// @実行タイミング document-end
// ==/ユーザースクリプト==
(() => {> {
'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));
words = new Set(JSON.parse(await GM.getValue(KEY_W, '[]')).map(x => x));
}
async function save() {
await GM.setValue(KEY_U, Array.from(users).join(','));
await GM.setValue(KEY_W, Array.from(words).join(','));
}
// 例:ユーザーと単語のロードと保存
await load();
console.log('Users:', users);
console.log('Words:', words);
// 例:ユーザーと単語の保存
// await save();
}> 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", クラス="Dropdown-item">
<i クラス「fas fa-ban」スタイル「マージン・ライト:8px;」></アイ>
ユーザーをブロックする
</ボタン>
`;
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 スタイル属性(フォントウェイト:600、マージンボトム:10px)>フィルタリング管理<<div>>
<div スタイル属性(フォントサイズ:12px、不透明度:0.7)>ユーザー ( ${users.size} )<<div>>
<入力 ID: "u" スタイル: 幅: 100%, パディング: 6px,>
<ボタン id="ua" スタイル="幅:100%;マージン上:6px;">ユーザーを追加する</ボタン>
<div id="ulist" style="margin-トップ:8px;"><<div>>
<hr>
<div スタイル属性(フォントサイズ:12px、不透明度:0.7)>キーワード (${words.size})<<div>>
<入力 ID: "w" スタイル: 幅: 100%, パディング: 6px,>
<ボタン id="wa" スタイル="幅:100%;マージン上:6px;">キーワードを追加する</ボタン>
<div id="wlist" style="margin-トップ:8px;"><<div>>
<ボタン id="close" スタイル="幅:100%;マージン上:10px;">停止</ボタン>
`;
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 => `
<li>
<div class="user-item">
<span class="username">${u.name}</span>
<span class="user-status">${u.status}</span>
</div>
</li>
`);
wlist.innerHTML = [...users].map(u => `
<li>
<div class="user-item">
<span class="username">${u.name}</span>
<span class="user-status">${u.status}</span>
</div>
</li>
`);
}
render();>
`<<div>スタイル属性:表示方法、コンテンツの配置、マージン</div>>
<span>${u}</span>
<ボタン データ-u="${u}">削除</ボタン>
<<div>>```markdown
).join('');
wlist.innerHTML = [...words].map(w => w);
```
```markdown
).join('');
wlist.innerHTML = [...words].map(w => w);
```>
`<<div>スタイル属性:表示方法、コンテンツの配置、マージン</div>>
<span>${w}</span>
<ボタン データ-w="${w}">削除</ボタン>
<<div>>```markdown
).join('');
ulist.querySelectorAll('button').forEach(b => {
```
```markdown
b.addEventListener('click', () => {
// ここにクリック時の処理を記述
});
});
```> {
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 クラス「fas fa-filter」></アイ>`;
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();
})();