屏蔽 B 站赛事内容

屏蔽 B 站 PC 端个人主页、赛事主页等页面部分比赛内容,比如屏蔽 LOL 所有赛事,也可自定义关键词,让眼睛不再受到污染

// ==UserScript==
// @name         屏蔽 B 站赛事内容
// @namespace    http://tampermonkey.net/
// @version      2024-07-08
// @description  屏蔽 B 站 PC 端个人主页、赛事主页等页面部分比赛内容,比如屏蔽 LOL 所有赛事,也可自定义关键词,让眼睛不再受到污染
// @author       GurifYuanin
// @match        https://t.bilibili.com/
// @icon         https://www.google.com/s2/favicons?sz=64&domain=bilibili.com
// @grant        none
// @run-at       document-start
// @license MIT
// ==/UserScript==


(function() {
    'use strict';
    // 关键字
    const lolKeywords = [
        'Doinb', 'Rookie', 'TheShy', 'Faker', 'Chovy', 'Ruler',
        'Bin', 'Knight', 'Xun', 'Elk', 'JackyLove', 'Tian', 'Meiko', '369',
        'T1', 'GenG', 'DK', 'KT',
        'BLG', 'TES', 'JDG', 'LNG', 'WBG', 'FPX', 'IG', 'EDG', 'RNG',
        'LOL', 'LPL', 'LCK', '英雄联盟'
    ].map(keyword => new RegExp(keyword, 'i'));

    function set(obj, path, value) {
        const paths = path.split('.');
        let src = obj;
        for (let i = 0; i < paths.length; i++) {
            const key = paths[i];
            if (i === paths.length - 1) {
                src[key] = value;
            } else {
                src = src[key];
            }
        }
    }

    function get(obj, path) {
        const paths = path.split('.');
        let src = obj;
        for (let i = 0; i < paths.length; i++) {
            const key = paths[i];
            if (i === paths.length - 1) {
                return src[key];
            } else {
                src = src[key];
            }
        }
    }

    function updateResponse({
        json,
        path,
        key
    }) {
        set(
            json,
            path,
            get(json, path)?.filter(item => !lolKeywords.some(keyword => keyword.test(get(item, key))))
        );
    }
    async function updateResponseByFetch({
        reqUrl,
        matchUrl,
        resp,
        path,
        key
    }) {
        if (reqUrl.includes(matchUrl)) {
            const json = await resp.json();
            updateResponse({ json, path, key });
            resp.json = () => Promise.resolve(json);
        }
    }

    const FETCH_CONFIGS = [
        // 个人主页右侧话题接口
        {
            matchUrl: '/x/topic/web/dynamic/rcmd',
            path: 'data.topic_items',
            key: 'name'
        },
        // 搜索栏热搜
        {
            matchUrl: '/x/web-interface/wbi/search/square',
            path: 'data.trending.list',
            key: 'show_name'
        },
    ];
    const XML_CONFIGS = [
        // 赛事页面赛程日历
        {
            matchUrl: '/x/esports/component/contests/link',
            path: ['data.history', 'data.future'],
            key: ['season.title', 'season.title']
        },
        // 赛事页面推荐栏
        {
            matchUrl: '/x/esports/season/recommend',
            path: 'data',
            key: 'game.title'
        },
        // 赛事页面头部封面
        {
            matchUrl: '/x/web-show/res/locs',
            path: 'data.4941',
            key: 'name'
        }
    ];

    // 拦截 fetch
    const _fetch = fetch;
    window.fetch = async (url, ...args) => {
        const resp = await _fetch(url, ...args);
        for (const config of FETCH_CONFIGS) {
            await updateResponseByFetch({
                resp,
                reqUrl: url,
                ...config,
            });
        }
        return resp;
    }

    // 拦截 XMLHttpRequest
    const originalOpen = XMLHttpRequest.prototype.open;
    XMLHttpRequest.prototype.open = function(method, url, async) {
        this.addEventListener("readystatechange", function() {
            if (this.readyState === 4) {
                for (const config of XML_CONFIGS) {
                    if (url.includes(config.matchUrl)) {
                        const json = JSON.parse(this.responseText);
                        Object.defineProperty(this, 'responseText', {
                            get() {
                                if (Array.isArray(config.path)) {
                                    for (let i = 0; i < config.path.length; i++) {
                                        updateResponse({ json, path: config.path[i], key: config.key[i] });
                                    }
                                } else {
                                    updateResponse({ json, path: config.path, key: config.key });
                                }
                                return json;
                            }
                        });
                    }
                }
            }
        }, false);
        originalOpen.apply(this, arguments);
    }
})();