Greasy Fork

Greasy Fork is available in English.

微博热搜过滤

重定向热搜页至经典版本,过滤娱乐标签和关键词

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         微博热搜过滤
// @namespace    http://greasyfork.icu/zh-CN/users/1502715
// @version      3.0
// @description  重定向热搜页至经典版本,过滤娱乐标签和关键词
// @author       电视卫士
// @license      MIT
// @match        https://weibo.com/*
// @match        https://s.weibo.com/top/summary*
// @run-at       document-start
// @grant        GM_registerMenuCommand
// @grant        GM_setValue
// @grant        GM_getValue
// ==/UserScript==

(function() {
    "use strict";

    /*********************
     * 0. 配置 + 菜单开关
     *********************/
    const TAG_FILTER_KEY = "tag_filter_enabled";
    const KW_FILTER_KEY = "kw_filter_enabled";

    if (GM_getValue(TAG_FILTER_KEY) === undefined) GM_setValue(TAG_FILTER_KEY, true);
    if (GM_getValue(KW_FILTER_KEY) === undefined) GM_setValue(KW_FILTER_KEY, true);

    function refreshAndReload() {
        location.reload();
    }

    function updateMenu() {
        GM_registerMenuCommand(
            "标签过滤:" + (GM_getValue(TAG_FILTER_KEY) ? "已开启✅" : "已关闭❌"),
            () => { GM_setValue(TAG_FILTER_KEY, !GM_getValue(TAG_FILTER_KEY)); refreshAndReload(); }
        );
        GM_registerMenuCommand(
            "关键词过滤:" + (GM_getValue(KW_FILTER_KEY) ? "已开启✅" : "已关闭❌"),
            () => { GM_setValue(KW_FILTER_KEY, !GM_getValue(KW_FILTER_KEY)); refreshAndReload(); }
        );
    }
    updateMenu();



    /*********************
     * 1. 热搜跳转映射
     *********************/
    const redirectMap = {
        "https://weibo.com/hot/mine":          "https://s.weibo.com/top/summary?cate=recommend",
        "https://weibo.com/hot/search":        "https://s.weibo.com/top/summary?cate=realtimehot",
        "https://weibo.com/hot/entertainment": "https://s.weibo.com/top/summary?cate=entrank",
        "https://weibo.com/hot/life":          "https://s.weibo.com/top/summary?cate=life",
        "https://weibo.com/hot/social":        "https://s.weibo.com/top/summary?cate=entrank"
    };

    function tryRedirect(url) {
        for (const oldUrl in redirectMap) {
            if (url.startsWith(oldUrl)) {
                location.replace(redirectMap[oldUrl]);
                return true;
            }
        }
        return false;
    }

    if (tryRedirect(location.href)) return;

    // 抓取 pushState / replaceState
    const _pushState = history.pushState;
    history.pushState = function() {
        _pushState.apply(this, arguments);
        tryRedirect(location.href);
    };

    const _replaceState = history.replaceState;
    history.replaceState = function() {
        _replaceState.apply(this, arguments);
        tryRedirect(location.href);
    };

    window.addEventListener("popstate", () => tryRedirect(location.href));



    /*********************
     * 2. 提示气泡
     *********************/
    function showBubble(tagCount, kwCount) {
        const enableTag = GM_getValue(TAG_FILTER_KEY);
        const enableKW = GM_getValue(KW_FILTER_KEY);

        // 文案
        let text = "";
        if (enableTag) text += `✅ 标签过滤已开启,${tagCount} 条热搜被过滤\n`;
        else text += `❌ 标签过滤已关闭\n`;

        if (enableKW) text += `✅ 关键词过滤已开启,${kwCount} 条热搜被过滤`;
        else text += `❌ 关键词过滤已关闭`;

        // 移除旧提示
        const old = document.getElementById("weibo-filter-bubble");
        if (old) old.remove();

        // 外层容器
        const wrap = document.createElement("div");
        wrap.id = "weibo-filter-bubble";
        Object.assign(wrap.style, {
            position: "fixed",
            right: "22px",
            bottom: "22px",
            padding: "12px 14px",
            width: "260px",
            background: "rgba(30,32,35,0.85)",
            borderRadius: "8px",
            color: "rgba(255,255,255,0.92)",
            fontSize: "14px",
            lineHeight: "1.45",
            whiteSpace: "pre-line",
            zIndex: 99999,
            border: "3px solid transparent",
            transition: "opacity 0.3s",
            boxShadow: "0 2px 12px rgba(0,0,0,0.25)",
            pointerEvents: "auto",
        });

        wrap.textContent = text;
        document.body.appendChild(wrap);

        /*********************************
     * 柔和倒计时环动画(v3.2 优化)
     *********************************/
        const duration = 5000;
        let timeLeft = duration;
        let paused = false;

        let lastTime = performance.now();

        function setRing(progress) {
            const deg = progress * 360;
            wrap.style.borderImage = `conic-gradient(
            rgba(255,255,255,0.25) ${deg}deg,
            rgba(255,255,255,0.05) ${deg}deg
        ) 1`;
        }

        function animate(now) {
            if (!wrap.isConnected) return;

            if (!paused) {
                const delta = now - lastTime;
                timeLeft -= delta;
                const progress = Math.min(1, Math.max(0, 1 - timeLeft / duration));

                // 环动画更平滑:progress 直接映射角度
                setRing(progress);

                if (timeLeft <= 0) {
                    wrap.style.opacity = 0;
                    setTimeout(() => wrap.remove(), 300);
                    return;
                }
            }

            lastTime = now;
            requestAnimationFrame(animate);
        }

        requestAnimationFrame(animate);

        // 悬停暂停倒计时
        wrap.addEventListener("mouseenter", () => paused = true);
        wrap.addEventListener("mouseleave", () => {
            paused = false;
            lastTime = performance.now();
        });
    }

    /*********************
     * 3. 内容过滤逻辑
     *********************/
    const BLOCK_TAGS = ["综艺", "电影", "剧集", "演出"];
    const BLOCK_KEYWORDS = [
        "关键词1",
        "关键词2",
        "关键词n"
    ]; //自行修改/增加即可,但请记得删去最后一个关键词末的逗号(,)

    function removeBlockedRows() {
        if (!location.href.includes("s.weibo.com/top/summary")) return;

        const enableTagFilter = GM_getValue(TAG_FILTER_KEY);
        const enableKWFilter = GM_getValue(KW_FILTER_KEY);

        let tagCount = 0;
        let kwCount = 0;

        const rows = document.querySelectorAll("table tbody tr");
        rows.forEach(tr => {
            const td2 = tr.querySelector("td.td-02");
            if (!td2) return;

            const span = td2.querySelector("span");
            const link = td2.querySelector("a");

            const tagText = span?.textContent.trim() ?? "";
            const titleText = link?.textContent.trim() ?? "";

            // A. 标签过滤
            if (enableTagFilter && tagText && BLOCK_TAGS.some(tag => tagText.startsWith(tag))) {
                tr.remove();
                tagCount++;
                return;
            }

            // B. 关键词过滤
            if (enableKWFilter && titleText && BLOCK_KEYWORDS.some(kw => titleText.includes(kw))) {
                tr.remove();
                kwCount++;
                return;
            }
        });

        const enableTag = GM_getValue(TAG_FILTER_KEY);
        const enableKW = GM_getValue(KW_FILTER_KEY);

        if (tagCount > 0 || kwCount > 0 || (!enableTag && !enableKW)) {
            showBubble(tagCount, kwCount);
        }

    }

    // DOM 完成后再执行过滤逻辑
    window.addEventListener("load", () => {
        removeBlockedRows();
        const observer = new MutationObserver(() => removeBlockedRows());
        observer.observe(document.body, { childList: true, subtree: true });
    });

})();