新浪微博热搜屏蔽

在微博热搜右边栏屏蔽不想看的热搜,屏蔽娱乐圈,支持关键词和分类屏蔽

// ==UserScript==
// @name         新浪微博热搜屏蔽
// @namespace    https://greasyfork.org/zh-CN/users/756550
// @version      0.13
// @description  在微博热搜右边栏屏蔽不想看的热搜,屏蔽娱乐圈,支持关键词和分类屏蔽
// @author       festoney8
// @license      MIT
// @match        http*://weibo.com/*
// @match        http*://www.weibo.com/*
// @match        http*://s.weibo.com/*
// @grant        GM_xmlhttpRequest
// @grant        GM_addStyle
// @run-at       document-end
// ==/UserScript==

(function () {
    'use strict'

    // 自定义设置

    // 屏蔽指定的关键词,用竖线分割,如:"关键词A|关键词B"
    let keywordBlacklist = "肖战|王一博"

    // 是否保留爆字label的话题
    let keepSuperHotLabel = true;

    // 是否移除包含任何明星的话题
    let removeStar = true;

    // 屏蔽指定的分类,用竖线分割,默认屏蔽娱乐圈
    let categoryBlacklist = "美妆|影视|艺人|综艺|作品衍生"

    // —————————————————————————————————————————————————————————————————————————————————
    // 全局css修改,固定热搜栏,热搜标题全长度
    if (window.location.pathname === "/hot/search") {
        log("热搜榜页面,不执行css更改")
    } else if (window.location.hostname === "weibo.com" || window.location.hostname === "www.weibo.com") {
        GM_addStyle(`
        /* 热搜栏sticky */
        [class^="Side_posSticky"] {
            position: sticky;
            top: 60px !important;
        }
        [class^="Side_sideBox"] {
            top: 60px !important;
        }
        /* 移除热度icon */
        .wbpro-side-card7 .icon,
        .wbpro-icon-search- {
            display: none !important;
        }
        /* 全长度热搜 */
        .wbpro-side-card7 .con {
            padding-right: 0 !important;
        }
        .wbpro-side-panel {
            padding-left: 10px !important;
            padding-right: 10px !important;
        }
        /* 高度微调 */
        #app [class^="cardHotSearch_tit"] {
            padding-bottom: unset !important;
        }
        .wbpro-side-bottom {
            padding-top: 0 !important;
        }
    `)
        log("css修改完成")
    } else if (window.location.hostname === "s.weibo.com") {
        GM_addStyle(`
        /* 热度icon移除 */
        .card-hotrank .card-content i.icon-txt {
            display: none !important;
        }
        /* 全长度热搜 */
        .card-hotrank .card-content ul>li {
            padding-right: 0 !important;
        }
        .card-hotrank .card-content span.val {
            padding-left: 15px !important;
        }
        /* 右栏移除其他板块 */
        #pl_right_msg,
        #pl_right_side .card-user {
            display: none !important;
        }
        #pl_right_side {
            z-index: unset !important;
            top: 70px !important;
        }
        `)
        log("css修改完成")
    }

    function containKeyword(obj) {
        if (keywordBlacklist.length === 0) {
            return false
        }
        let lst = keywordBlacklist.split('|')
        let regex = new RegExp(`(${lst.join('|')})`, 'i')
        return regex.test(obj.word)
    }

    function containCategory(obj) {
        if (categoryBlacklist.length === 0) {
            return false
        }
        let lst = categoryBlacklist.split('|')
        let regex = new RegExp(`(${lst.join('|')})`, 'i')
        return regex.test(obj.category)
    }

    function containStarName(obj) {
        // 包含明星的话题
        return JSON.stringify(obj.star_name) !== '{}'
    }

    function containAd(obj) {
        // 屏蔽广告
        return obj.is_ad === 1 || obj.ad_type === "商业投放" || obj.ad_type === "资源投放";
    }

    function containSuperHot(obj) {
        // 爆 label的热搜
        return obj.label_name === "爆";
    }


    function log(...args) {
        const nowTime = new Date().toLocaleTimeString();
        const newArgs = [`[WeiboGreasyfork] ${nowTime}:`, ...args];
        console.log.apply(console, newArgs);
    }

    // 移除置顶热搜,添加热搜位,移除无排名热搜(dot)
    function adjustHotSearchPlace(num, removeTop = true) {
        if (window.location.pathname === "/hot/search") {
            return
        }
        if (window.location.hostname === "weibo.com" || window.location.hostname === "www.weibo.com") {
            // 移除置顶热搜,移除点开头的非排名热搜
            let topicElems = document.querySelectorAll('.wbpro-side-card7 .wbpro-side-panel')
            if (removeTop) {
                topicElems[0].remove()
                log(`移除了置顶热搜`)
            }
            for (let i = 0; i < topicElems.length; i++) {
                let e = topicElems[i];
                let span = e.querySelector(".wbpro-icon-doticon")
                if (span) {
                    e.remove()
                    log(`移除了Dot热搜`)
                }
            }
            // 添加热搜位
            let topic = document.querySelector('.wbpro-side-card7')
            let lastChild = topic.children[topic.children.length - 1]
            for (let i = 0; i < num; i++) {
                let newChild = lastChild.cloneNode(true)
                topic.appendChild(newChild)
            }
            log(`添加了 ${num} 个热搜位`)
        } else if (window.location.hostname === "s.weibo.com") {
            let topicElems = document.querySelectorAll('.card-hotrank .card-content li')
            if (topicElems.length > 0) {
                topicElems[0].remove()
                log(`移除了置顶热搜`)
            }
        }
    }

    // 覆盖热搜列表
    function overwriteHotSearch(filteredData) {
        function convertIntToStr(intNum) {
            let strNum;
            if (intNum >= 100000000) {
                strNum = (intNum / 100000000).toFixed(1) + '亿';
            } else if (intNum >= 10000) {
                // strNum = (intNum / 10000).toFixed(1) + '万';
                strNum = (intNum / 10000).toFixed(1);
            } else {
                strNum = intNum.toString();
            }
            return strNum;
        }

        if (window.location.pathname === "/hot/search") {
            return
        }
        if (window.location.hostname === "weibo.com" || window.location.hostname === "www.weibo.com") {
            let topicElems = document.querySelectorAll('.wbpro-side-card7 .wbpro-side-panel')
            if (topicElems.length > 0) {
                // 用筛选后热搜列表覆盖热搜内容
                for (let i = 0; i < topicElems.length; i++) {
                    let oldTopic = topicElems[i]
                    let newData = filteredData[i]
                    try {
                        oldTopic.querySelector("a").href = "//s.weibo.com/weibo?q=" + encodeURIComponent(newData.word)
                        oldTopic.querySelector(".rank").textContent = " " + (i + 1) + " "
                        oldTopic.querySelector(".wbpro-textcut").textContent = newData.word
                        oldTopic.querySelector(".wbpro-textcut").title = newData.word
                        // 将icon颜色显示在热度上,热度加粗显示
                        let hotstatus = oldTopic.querySelector(".clb")
                        // 有时会出现无热度指数span的情况(原因未知),手动创建
                        if (!hotstatus) {
                            let hotStatusSpan = document.createElement("span")
                            hotStatusSpan.setAttribute("class", "f13 clb")
                            hotStatusSpan.textContent = "0"
                            let parentNode = oldTopic.querySelector(".woo-box-alignCenter")
                            let targetNode = oldTopic.querySelector(".wbpro-textcut")
                            parentNode.insertBefore(hotStatusSpan, targetNode.nextSibling)
                        }
                        hotstatus = oldTopic.querySelector(".clb")
                        hotstatus.textContent = convertIntToStr(newData.raw_hot)
                        let e = oldTopic.querySelector(".icon")
                        if (e) {
                            e.remove()
                        }
                        let dict = {
                            "": "font-weight: bold;",
                            "热": "font-weight: bold; color: #ff7100 !important",
                            "沸": "font-weight: bold; color: #f86400 !important",
                            "暖": "font-weight: bold; color: #FFAB5A !important",
                            "商": "font-weight: bold; color: #00b7ee !important",
                            "新": "font-weight: bold; color: #22dd9f !important",
                            "爆": "font-weight: bold; color: red !important; text-shadow: 0px 0px 10px yellow;",
                        }
                        hotstatus.style = dict[newData.label_name]
                    } catch (e) {
                        console.error("Error, 跳过本条热搜:", e);
                        log(oldTopic)
                        log(newData)
                    }
                }
                log("完成热搜覆盖")
            }
        }
        if (window.location.hostname === "s.weibo.com") {
            let topicElems = document.querySelectorAll('.card-hotrank .card-content li')
            if (topicElems.length > 0) {
                for (let i = 0; i < topicElems.length; i++) {
                    let oldTopic = topicElems[i]
                    let newData = filteredData[i]
                    try {
                        oldTopic.querySelector("a").href = "//s.weibo.com/weibo?q=" + encodeURIComponent(newData.word)
                        oldTopic.querySelector("a").textContent = newData.word
                        // 移除icon,将icon颜色显示在热度上,热度加粗显示
                        let hotstatus = oldTopic.querySelector("span.val")
                        hotstatus.textContent = convertIntToStr(newData.raw_hot)
                        let e = oldTopic.querySelector("i.icon-txt")
                        if (e) {
                            e.remove()
                        }
                        let dict = {
                            "": "font-weight: bold;",
                            "热": "font-weight: bold; color: #ff7100 !important",
                            "暖": "font-weight: bold; color: #FFAB5A !important",
                            "新": "font-weight: bold; color: #22dd9f !important",
                            "爆": "font-weight: bold; color: red !important; text-shadow: 0px 0px 10px yellow;",
                        }
                        hotstatus.style = dict[newData.label_name]
                    } catch (e) {
                        console.error("Error, 跳过本条热搜:", e);
                        log(oldTopic)
                        log(newData)
                    }
                }
                log("完成热搜覆盖")
            }
        }
    }

    // fetch新热搜, 筛选
    function newHotSearch() {
        // api获取top50个热搜并筛选
        GM_xmlhttpRequest({
            method: "GET", url: "https://weibo.com/ajax/side/hotSearch", onload: function (response) {
                let jsonapi = JSON.parse(response.responseText)
                let hotSearch = jsonapi.data.realtime
                let filteredData = hotSearch.filter(function (obj) {
                    // 黑名单关键字最高优先级
                    if (containKeyword(obj)) {
                        return false
                    }
                    // 放过爆字label的话题
                    if (keepSuperHotLabel && containSuperHot(obj)) {
                        return true
                    }
                    // 是否屏蔽全部明星
                    if (containStarName(obj) && removeStar) {
                        return false
                    }
                    return !(containAd(obj) || containCategory(obj))
                })
                if (!filteredData) {
                    return
                }
                log("筛选后热搜数量:", filteredData.length)
                overwriteHotSearch(filteredData)
            }, onerror: function (response) {
                console.error(response.responseText)
            }
        })
    }

    // 监听 URL 变化,并刷新热搜
    var lastExecTime = null;
    var lastURL = null;
    window.addEventListener('load', function () {
        lastURL = window.location.href;
        setInterval(() => {
            let newURL = window.location.href;
            if (newURL !== lastURL) {
                log("URL change detected")
                log("current url", window.location.href)
                checkUrlChange()
                lastURL = newURL;
            }
        }, 100);
    });

    function checkUrlChange() {
        if (window.location.pathname === "/hot/search") {
            return
        }
        if (!lastExecTime || Date.now() - lastExecTime > 60 * 1000) {
            log("检测到 URL 变化,刷新热搜");
            adjustHotSearchPlace(0, false)
            newHotSearch();
            lastExecTime = Date.now();
        } else {
            log("1 分钟内已经刷新过,忽略");
        }
    }

    function runAfterDOMContentLoaded(retries) {
        function work() {
            try {
                if (window.location.pathname === "/hot/search") {
                    return
                }
                // 添加热搜位
                adjustHotSearchPlace(2)
                // 覆盖热搜
                newHotSearch()
                // 刷新热搜按钮 重新绑定事件
                if (window.location.hostname === "weibo.com" || window.location.hostname === "www.weibo.com") {
                    let oldButton = document.querySelector(".wbpro-side-tit .woo-box-flex.woo-box-alignCenter")
                    if (!oldButton) {
                        throw Error("No button")
                    }
                    let newButton = oldButton.cloneNode(true)
                    newButton.addEventListener("click", newHotSearch)
                    oldButton.parentNode.replaceChild(newButton, oldButton)
                }
            } catch (error) {
                if (retries > 0) {
                    retries--;
                    setTimeout(work, 500);
                }
            }
        }

        work();
    }

    runAfterDOMContentLoaded(3)
})()