// ==UserScript== // @name Kmoe屏蔽器 // @author yaulei // @version 1.4 // @description 屏蔽含關鍵詞的非五星/未評價評論,支援添加與管理關鍵詞 // @match *://mox.moe/* // @match *://kox.moe/* // @match *://koz.moe/* // @exclude *://mox.moe/c/10001.htm // @exclude *://kox.moe/c/10001.htm // @exclude *://koz.moe/c/10001.htm // @grant none // @license MIT // @namespace https://greasyfork.org/users/1379965 // @downloadURL none // ==/UserScript== (function () { 'use strict'; // --- 儲存與讀取 --- const loadKeywords = () => JSON.parse(localStorage.getItem("customKeywords") || "[]"); const saveKeywords = (list) => localStorage.setItem("customKeywords", JSON.stringify(list)); // --- 創建 UI --- const createUI = () => { const container = document.createElement("div"); container.style.position = "fixed"; container.style.left = "10px"; container.style.bottom = "10px"; container.style.background = "#eee"; container.style.padding = "5px 10px"; container.style.borderRadius = "8px"; container.style.fontSize = "14px"; container.style.zIndex = "9999"; container.style.boxShadow = "0 2px 6px rgba(0,0,0,0.2)"; const toggleBtn = document.createElement("div"); toggleBtn.textContent = "屏蔽器設置"; toggleBtn.style.cursor = "pointer"; toggleBtn.style.fontWeight = "bold"; container.appendChild(toggleBtn); const panel = document.createElement("div"); panel.style.display = "none"; panel.style.marginTop = "5px"; // --- 添加區域 --- const input = document.createElement("input"); input.type = "text"; input.style.width = "240px"; input.placeholder = "輸入關鍵詞,多個詞請用空格分開"; panel.appendChild(input); const addBtn = document.createElement("button"); addBtn.textContent = "添加"; addBtn.style.marginLeft = "5px"; panel.appendChild(addBtn); // --- 屏蔽詞管理 --- const manageToggle = document.createElement("div"); manageToggle.textContent = "▸ 屏蔽詞管理"; manageToggle.style.cursor = "pointer"; manageToggle.style.marginTop = "8px"; panel.appendChild(manageToggle); const keywordList = document.createElement("ul"); keywordList.style.marginTop = "5px"; keywordList.style.listStyle = "none"; keywordList.style.padding = "0"; keywordList.style.display = "none"; // 初始隐藏 keywordList.style.flexWrap = "wrap"; keywordList.style.flexDirection = "row"; keywordList.style.flex = "1"; keywordList.style.maxWidth = "300px"; keywordList.style.paddingLeft = "0"; keywordList.style.margin = "0"; keywordList.style.gap = "6px"; keywordList.style.flexWrap = "wrap"; keywordList.style.alignItems = "flex-start"; keywordList.style.justifyContent = "flex-start"; keywordList.style.flexGrow = "1"; keywordList.style.flexShrink = "1"; keywordList.style.flexBasis = "auto"; keywordList.style.overflowWrap = "break-word"; keywordList.style.wordBreak = "break-word"; panel.appendChild(keywordList); const renderKeywords = () => { keywordList.innerHTML = ""; const keywords = loadKeywords(); keywords.forEach((kw, i) => { const li = document.createElement("li"); li.style.background = "#ddd"; li.style.borderRadius = "6px"; li.style.padding = "2px 6px"; li.style.display = "flex"; li.style.alignItems = "center"; li.style.marginRight = "6px"; li.style.marginBottom = "6px"; li.textContent = kw + " "; const del = document.createElement("button"); del.textContent = "x"; del.style.marginLeft = "4px"; del.style.border = "none"; del.style.background = "transparent"; del.style.cursor = "pointer"; del.style.color = "#900"; del.style.fontWeight = "bold"; del.onclick = () => { keywords.splice(i, 1); saveKeywords(keywords); renderKeywords(); }; li.appendChild(del); keywordList.appendChild(li); }); }; // 控制展開與收起 let keywordListVisible = false; manageToggle.addEventListener("click", () => { keywordListVisible = !keywordListVisible; keywordList.style.display = keywordListVisible ? "flex" : "none"; manageToggle.textContent = keywordListVisible ? "▾ 屏蔽詞管理" : "▸ 屏蔽詞管理"; if (keywordListVisible) { renderKeywords(); // 只有在展开时更新 } }); // 事件綁定 toggleBtn.onclick = () => { panel.style.display = panel.style.display === "none" ? "block" : "none"; }; // 点击页面任意非 UI 区域自动折叠 UI document.addEventListener('click', (e) => { if (!container.contains(e.target)) { panel.style.display = "none"; // 隐藏面板 manageToggle.textContent = "▸ 屏蔽詞管理"; // 恢复原状 } }); addBtn.onclick = () => { const raw = input.value.trim(); if (!raw) return; const words = raw.split(/\s+/); // 用空白符切割(空格、tab、換行) const keywords = loadKeywords(); let added = false; words.forEach(word => { if (word && !keywords.includes(word)) { keywords.push(word); added = true; } }); if (added) { saveKeywords(keywords); renderKeywords(); } input.value = ""; }; // 监听回车事件 input.addEventListener("keydown", (e) => { if (e.key === "Enter") { addBtn.click(); } }); renderKeywords(); container.appendChild(panel); document.body.appendChild(container); }; // --- 屏蔽邏輯 --- const filterComments = () => { const keywords = loadKeywords(); const allComments = document.querySelectorAll("td[id^='comm_cont_']"); allComments.forEach(td => { const text = td.textContent; if (!text) return; const hasKeyword = keywords.some(k => text.includes(k)); if (!hasKeyword) return; const ratingMatch = text.match(/對本書評價\s*:\s*(\d)\s*星/); const rating = ratingMatch ? parseInt(ratingMatch[1]) : null; if (!rating || rating < 5) { td.style.display = "none"; } }); }; // --- 初始化 --- createUI(); setTimeout(filterComments, 1000); // 等待頁面內容載入 // 使用 MutationObserver 监控 DOM 变化,并在有新评论加载时重新运行检查 const observer = new MutationObserver(filterComments); observer.observe(document.body, { childList: true, subtree: true }); })();