B站直播间小玩具

一些小功能

// ==UserScript==
// @name         B站直播间小玩具
// @namespace    https://greasyfork.org/zh-CN/scripts/
// @version      1.4
// @description  一些小功能
// @author       Eric Lam
// @license      MIT
// @iconURL      https://www.bilibili.com/favicon.ico
// @icon64URL    https://www.bilibili.com/favicon.ico
// @match        *://live.bilibili.com/1*
// @match        *://live.bilibili.com/2*
// @match        *://live.bilibili.com/3*
// @match        *://live.bilibili.com/4*
// @match        *://live.bilibili.com/5*
// @match        *://live.bilibili.com/6*
// @match        *://live.bilibili.com/7*
// @match        *://live.bilibili.com/8*
// @match        *://live.bilibili.com/9*
// @match        *://live.bilibili.com/blanc/1*
// @match        *://live.bilibili.com/blanc/2*
// @match        *://live.bilibili.com/blanc/3*
// @match        *://live.bilibili.com/blanc/4*
// @match        *://live.bilibili.com/blanc/5*
// @match        *://live.bilibili.com/blanc/6*
// @match        *://live.bilibili.com/blanc/7*
// @match        *://live.bilibili.com/blanc/8*
// @match        *://live.bilibili.com/blanc/9*
// @match        *://live.bilibili.com/blackboard/era/*
// @connect      bilibili.com
// @require      https://unpkg.com/[email protected]/dist/jquery.js
// @require      https://unpkg.com/[email protected]/dist/ajaxhook.min.js
// @grant        unsafeWindow
// @run-at       document-idle
// ==/UserScript==


//修改自https://greasyfork.org/zh-CN/scripts/435942  https://greasyfork.org/zh-CN/scripts/477746

(async function () {
    var roomId
    async function fetcher(url) {
        const res = await fetch(url)
        if (!res.ok) {
            throw new Error(res.statusText)
        }

        const data = await res.json()
        console.debug(data)
        if (data.code != 0) {
            throw new Error(`B站API请求错误: ${data.message}`)
        }
        return data
    }
    const newWindow = {
        init: () => {
            return newWindow.Toast.init();
        },
        Toast: {
            init: () => {
                try {
                    const list = [];
                    window.toast = (msg, type = 'success', timeout = 5e3) => {
                        switch (type){
                            case 'success':
                            case 'info':
                            case 'error':
                                break;
                            default:
                                type = 'info';
                        }
                        const a = $(`<div class="link-toast ${type} fixed" style="z-index:2001;text-align: left;"><span class="toast-text">${msg}</span></div>`)[0];
                        document.body.appendChild(a);
                        a.style.top = (document.body.scrollTop + list.length * 40 + 10) + 'px';
                        a.style.left = (document.body.offsetWidth + document.body.scrollLeft - a.offsetWidth - 5) + 'px';
                        list.push(a);
                        setTimeout(() => {
                            a.className += ' out';
                            setTimeout(() => {
                                list.shift();
                                list.forEach((v) => {
                                    v.style.top = (parseInt(v.style.top, 10) - 40) + 'px';
                                });
                                $(a).remove();
                            }, 200);
                        }, timeout);
                    };
                    return $.Deferred().resolve();
                } catch (err){
                    return $.Deferred().reject();
                }
            }
        }
    }
    newWindow.init()

    const seconds = 5
    const roomReg = /^\/(blanc\/)?(?<id>\d+)/
    roomId = parseInt(roomReg.exec(location.pathname)?.groups?.id)
    const res = await fetcher('https://api.live.bilibili.com/room/v1/Room/room_init?id=' + roomId)
    roomId = res.data.room_id
    const uid = res.data.uid
    var oldURL = window.top.location.href;
    var title = document.getElementsByTagName("title");
    if(oldURL.includes('lottery'))return
    if(oldURL.includes('treasurebox'))return
    if(!oldURL.includes('blanc') && !title[0].innerHTML.includes('哔哩哔哩直播')){
        let newURL = 'https://live.bilibili.com/blanc/' + roomId
        window.top.location = newURL
    }
    if(oldURL.includes('blackboard/era')){
        let newURL = 'https://live.bilibili.com/blanc/' + roomId
        window.top.location = newURL
    }
    setTimeout(() => {
        $('.web-player-icon-roomStatus').remove()
        $('.z-shop-popover-vm').remove()
        $('.web-player-module-area-mask').remove()
        try{
            window.toast('点赞拦截代码加载成功');
            window.toast('点赞后几秒内自动生效...');
            const originalFetch = unsafeWindow.fetch;
            unsafeWindow.fetch = async function(...args) {
                const [resource, config] = args;
                // 检查是否是点赞请求
                if (typeof resource === 'string' && resource.includes('/xlive/app-ucenter/v1/like_info_v3/like/likeReportV3')) {
                    const newResource = resource.replace(/click_time=[0-9]+/,'click_time=1000');
                    window.toast('点赞次数修改为1000次')
                    args[0] = newResource
                }
                return originalFetch.apply(this, args);
            };
        } catch (err) {
            console.error(err);
        }
        // 拦截API请求
    const originalOpen = XMLHttpRequest.prototype.open;
    XMLHttpRequest.prototype.open = function(method, url) {
        if (url.includes('GetWebList')) {
            this.addEventListener('load', function() {
                if (this.responseText) {
                    try {
                        const response = JSON.parse(this.responseText);
                        if (response.code === 0 && response.data && response.data.rooms) {
                            processRoomData(response.data.rooms);
                        }
                    } catch (e) {
                        console.error('Error processing response:', e);
                    }
                }
            });
        }
        originalOpen.apply(this, arguments);
    };

    // 处理房间数据并添加悬浮标题
    function processRoomData(rooms) {
        // 等待DOM加载完成
        const checkDomReady = setInterval(() => {
            const anchorElements = document.querySelectorAll('a[href^="/"][target="_blank"].one-anchor');
            if (anchorElements.length > 0) {
                clearInterval(checkDomReady);

                rooms.forEach(room => {
                    const roomId = room.room_id;
                    const title = room.title;

                    // 找到对应的a元素
                    const anchor = Array.from(anchorElements).find(el => {
                        const href = el.getAttribute('href');
                        return href && href.startsWith(`/${roomId}`);
                    });

                    if (anchor) {
                        // 创建悬浮标题容器
                        const tooltip = document.createElement('div');
                        tooltip.className = 'live-title-tooltip';
                        tooltip.textContent = title;

                        // 添加到DOM中
                        document.body.appendChild(tooltip);

                        // 获取头像元素
                        const avatar = anchor.querySelector('.avatar img.real-avatar');

                        // 鼠标移入事件
                        anchor.addEventListener('mouseenter', (e) => {
                            const rect = avatar.getBoundingClientRect();
                            tooltip.style.display = 'block';
                            tooltip.style.left = `${rect.left + window.scrollX}px`;
                            tooltip.style.top = `${rect.top + window.scrollY - tooltip.offsetHeight - 10}px`;
                        });

                        // 鼠标移出事件
                        anchor.addEventListener('mouseleave', () => {
                            tooltip.style.display = 'none';
                        });
                    }
                });
            }
        }, 100);
    }

    // 添加CSS样式
    const style = document.createElement('style');
    style.textContent = `
        .live-title-tooltip {
            position: absolute;
            display: none;
            background-color: rgba(0, 0, 0, 0.8);
            color: #fff;
            padding: 8px 12px;
            border-radius: 4px;
            font-size: 14px;
            max-width: 200px;
            text-align: center;
            z-index: 9999;
            pointer-events: none;
            box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
            animation: fadeIn 0.2s ease-out;
            line-height: 1.4;
            word-break: break-word;
            white-space: normal;
        }

        .live-title-tooltip::after {
            content: '';
            position: absolute;
            bottom: -5px;
            left: 50%;
            transform: translateX(-50%);
            border-width: 5px 5px 0;
            border-style: solid;
            border-color: rgba(0, 0, 0, 0.8) transparent transparent;
        }

        @keyframes fadeIn {
            from { opacity: 0; transform: translateY(5px); }
            to { opacity: 1; transform: translateY(0); }
        }
    `;
    document.head.appendChild(style);
    },2000)

    if (res.data.live_status != 1) {
        console.warn(`不在直播,已略过`)
        return
    }

    let rankGold = undefined
    let guardTab = undefined

    while ($('.tab-list.dp-flex').children().length == 0) {
        console.warn(`找不到Tab元素,等待3秒。`)
        await new Promise((res,) => setTimeout(res, 3000)) // wait 3 seconds
    }

    const keywords = ['高能榜', '房间观众', '应援日榜']
    let keyword;
    for (const element of $('.tab-list.dp-flex').children()) {
        console.log(element.innerText)
        if(element.innerText.indexOf("大航海") > -1)guardTab = element
        const kw = keywords.find(s => element.innerText.startsWith(s))
        console.log(kw)
        if (kw) {
            rankGold = element
            keyword = kw
        }
    }

    if (!rankGold || !keyword) {
        console.warn(`找不到高能榜元素。`)
        return
    }

    setInterval(async () => {
        let online = 0
        let online2 = 0
        let guard = 0
        try {
            const data = await fetcher(`https://api.live.bilibili.com/xlive/general-interface/v1/rank/getOnlineGoldRank?ruid=${uid}&roomId=${roomId}&page=1&pageSize=1`)
            online = data.data.onlineNum
        } catch (err) {
            console.warn(`查询高能榜时出现错误: ${err}`)
            console.warn(err)
        }
        try {
            const data2 = await fetcher(`https://api.live.bilibili.com/xlive/general-interface/v1/rank/queryContributionRank?ruid=${uid}&room_id=${roomId}&page=1&page_size=1&type=online_rank&switch=contribution_rank`)
            online2 = data2.data.count
        } catch (err) {
            console.warn(`查询贡献榜时出现错误: ${err}`)
            console.warn(err)
        }
        rankGold.innerHTML = `<span title="贡献用戶(左): ${online}\n在线用户(右): ${online2}">${keyword}(${online}/${online2})</span>`
        try {
            const data3 = await fetcher(`https://api.live.bilibili.com/xlive/app-room/v2/guardTab/topListNew?roomid=${roomId}&page=1&ruid=${uid}&page_size=20&typ=5&platform=web`)
            guard = data3.data.info.num
        } catch (err) {
            console.warn(`查询大航海时出现错误: ${err}`)
            console.warn(err)
        }
        guardTab.innerHTML = `<span >大航海(${guard})</span>`
    }, seconds * 1000)



})().catch(console.warn);