Greasy Fork

Kmoe屏蔽器

屏蔽含關鍵詞的非五星/未評價評論,支援添加與管理關鍵詞

当前为 2025-04-18 提交的版本,查看 最新版本

// ==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
// ==/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 });

})();