[银河奶牛]社区BUFF显示

自动读取社区BUFF并显示剩余时间,精确到分秒,过期自动刷新

当前为 2025-10-27 提交的版本,查看 最新版本

// ==UserScript==
// @name         [银河奶牛]社区BUFF显示
// @version      1.0
// @description  自动读取社区BUFF并显示剩余时间,精确到分秒,过期自动刷新
// @match        https://www.milkywayidle.com/*
// @match        https://test.milkywayidle.com/*
// @icon         https://www.milkywayidle.com/favicon.svg
// @author       DOUBAO-DiamondMoo
// @license      MIT
// @namespace    https://gf.qytechs.cn/users/1530578
// ==/UserScript==

(function() {
    'use strict';

    // 防止重复处理的锁
    let isProcessing = false;

    /**
     * 计算剩余时间并格式化显示文本
     * @param {string} expireTimeStr - 过期时间字符串(ISO格式)
     * @returns {Object} 包含显示文本和是否小于1天的标识
     */
    function calculateRemainingTime(expireTimeStr) {
        const expireTime = new Date(expireTimeStr);
        const now = new Date();
        const diffMs = expireTime - now;

        // 过期状态处理
        if (diffMs <= 0) return { text: '已过期', isLessThanOneDay: false };

        // 计算各时间单位
        const diffYears = Math.floor(diffMs / (1000 * 60 * 60 * 24 * 365));
        const diffDays = Math.floor((diffMs % (1000 * 60 * 60 * 24 * 365)) / (1000 * 60 * 60 * 24));
        const diffHours = Math.floor((diffMs % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
        const diffMinutes = Math.floor((diffMs % (1000 * 60 * 60)) / (1000 * 60));
        const diffSeconds = Math.floor((diffMs % (1000 * 60)) / 1000);

        // 按优先级返回格式化文本
        if (diffYears > 0) return { text: `${diffYears}y`, isLessThanOneDay: false };
        if (diffDays > 0) return { text: `${diffDays}d`, isLessThanOneDay: false };
        if (diffHours > 0) return { text: `${diffHours}h`, isLessThanOneDay: true };
        if (diffMinutes > 0) return { text: `${diffMinutes}m`, isLessThanOneDay: true };
        return { text: `${diffSeconds}s`, isLessThanOneDay: true };
    }

    /**
     * 根据文本长度计算动态字体大小
     * @param {string} text - 显示文本
     * @returns {number} 字体大小(em)
     */
    function getDynamicFontSize(text) {
        const baseSize = 1.4; // 基础字体大小
        const minSize = 0.6; // 最小字体限制
        const textLength = text.length;

        if (textLength <= 3) return baseSize;

        const reducedSize = baseSize - (textLength - 3) * 0.2;
        return Math.max(reducedSize, minSize);
    }

    /**
     * 注入BUFF剩余时间到页面
     * 核心功能:读取BUFF数据 -> 计算剩余时间 -> 渲染到页面 -> 过期检测
     */
    function injectBuffTime() {
        if (isProcessing) return;
        isProcessing = true;

        try {
            // 获取游戏页面根元素及React状态
            const gamePageEl = document.querySelector('[class^="GamePage"]');
            if (!gamePageEl) return;

            const reactFiberKey = Object.keys(gamePageEl).find(k => k.startsWith('__reactFiber$'));
            const stateNode = gamePageEl[reactFiberKey]?.return?.stateNode;
            const communityBuffs = stateNode?.state?.communityBuffs || [];
            if (communityBuffs.length === 0) return;

            // 过期检测:若当前时间超过首个BUFF过期时间则刷新页面
            const targetExpireTime = new Date(communityBuffs[0].expireTime);
            if (new Date() > targetExpireTime) {
                window.location.reload();
                return;
            }

            // 匹配BUFF元素并注入时间显示
            const buffElements = document.querySelectorAll('.CommunityBuff_communityBuff__1BILG');
            if (buffElements.length === 0) return;

            buffElements.forEach((el, index) => {
                if (index >= communityBuffs.length) return;

                const expireTime = communityBuffs[index].expireTime;
                const { text, isLessThanOneDay } = calculateRemainingTime(expireTime);
                const fontSize = getDynamicFontSize(text);

                // 样式定义:区分短期和长期BUFF的显示样式
                const baseStyles = `
                    position: absolute;
                    bottom: 0px;
                    right: 0px;
                    font-weight: bold;
                    z-index: 10;
                    pointer-events: none;
                    font-size: ${fontSize}em;
                `;
                const styles = isLessThanOneDay
                    ? `${baseStyles} color: #000; text-shadow: -0.5px 0 white, 0 0.5px white, 0.5px 0 white, 0 -0.5px white;`
                    : `${baseStyles} color: #fff; text-shadow: -1px 0 var(--color-background-game), 0 1px var(--color-background-game), 1px 0 var(--color-background-game), 0 -1px var(--color-background-game);`;

                // 更新或创建时间显示元素
                const existingTimeEl = el.querySelector('.buff-remaining-time');
                if (existingTimeEl) {
                    if (existingTimeEl.textContent !== text) {
                        existingTimeEl.textContent = text;
                        existingTimeEl.style.cssText = styles;
                    }
                    return;
                }

                const timeEl = document.createElement('div');
                timeEl.className = 'buff-remaining-time';
                timeEl.textContent = text;
                timeEl.style.cssText = styles;
                el.style.position = 'relative'; // 确保绝对定位生效
                el.appendChild(timeEl);
            });
        } finally {
            isProcessing = false;
        }
    }

    /**
     * 初始化执行
     * 页面加载完成后立即执行一次BUFF时间注入
     */
    if (document.readyState === 'complete' || document.readyState === 'interactive') {
        injectBuffTime();
    } else {
        document.addEventListener('DOMContentLoaded', injectBuffTime);
    }

    /**
     * DOM变化监听
     * 当BUFF区域发生变化时重新注入时间显示
     */
    const targetNode = document.querySelector('.Header_communityBuffs__3x-B2') || document.body;
    const observer = new MutationObserver((mutations) => {
        if (mutations.some(m => m.addedNodes.length > 0 || m.removedNodes.length > 0)) {
            injectBuffTime();
        }
    });
    observer.observe(targetNode, { childList: true, subtree: true });

    /**
     * 定时刷新
     */
    setInterval(injectBuffTime, 60000);
})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址