[银河奶牛]食用工具

开箱记录、箱子期望、离线统计、公会钉钉

目前为 2024-07-21 提交的版本。查看 最新版本

// ==UserScript==
// @name         [银河奶牛]食用工具
// @namespace    http://tampermonkey.net/
// @version      0.38
// @description  开箱记录、箱子期望、离线统计、公会钉钉
// @author       Truth_Light
// @license      Truth_Light
// @match        https://www.milkywayidle.com/*
// @match        https://test.milkywayidle.com/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=milkywayidle.com
// @grant        GM.xmlHttpRequest
// ==/UserScript==

(function() {
    'use strict';

    const itemSelector = '.ItemDictionary_drop__24I5f';
    const iconSelector = '.Icon_icon__2LtL_ use';
    const chestNameSelector = '#root > div > div > div.Modal_modalContainer__3B80m > div.Modal_modal__1Jiep > div.ItemDictionary_modalContent__WvEBY > div.ItemDictionary_itemAndDescription__28_he > div.Item_itemContainer__x7kH1 > div > div > div > div > svg > use';
    const resultDisplaySelector = '.ItemDictionary_openToLoot__1krnv';
    const marketDataStr = localStorage.getItem('MWITools_marketAPI_json');
    let marketData = JSON.parse(marketDataStr);
    const MARKET_API_URL = "https://raw.githubusercontent.com/holychikenz/MWIApi/main/medianmarket.json";
    let timer = null;
    let chestList = {};

    if (!marketData) {
        GM.xmlHttpRequest({
            method: 'GET',
            url: MARKET_API_URL,
            responseType: 'json',
            timeout: 5000,
            onload: function(response) {
                if (response.status === 200) {
                    marketData = JSON.parse(response.responseText);
                    console.log('从API获取到的数据:', marketData);
                } else {
                    console.error('获取数据失败。状态码:', response.status);
                }
            },
            ontimeout: function() {
                console.error('请求超时:超过5秒未能获取到数据');
            },
            onerror: function(error) {
                console.error('获取数据时发生错误:', error);
            }
        });
    }

    function getSpecialItemPrice(itemName, priceType) {
        if (marketData) {
            if (marketData.market && marketData.market[itemName]) {
                const itemPrice = marketData.market[itemName][priceType];
                if (itemPrice !== undefined && itemPrice !== -1) {
                    return itemPrice;
                }
            }
        }
        console.error(`未找到物品 ${itemName} 的 ${priceType} 价格信息`);
        return null; // 或者返回默认值,视情况而定
    }

    let specialItemPrices = {
        'Coin': { ask: 1, bid: 1 }, // 默认的特殊物品价值,包括 ask 和 bid 价值
        'Cowbell': {
            ask: getSpecialItemPrice('Bag Of 10 Cowbells', 'ask') / 10 || 30000,
            bid: getSpecialItemPrice('Bag Of 10 Cowbells', 'bid') / 10 || 25000
        },
        'Chimerical Token': {
            ask: getSpecialItemPrice('Chimerical Essence', 'ask') || 1300,
            bid: getSpecialItemPrice('Chimerical Essence', 'bid') || 1100
        },
        'Sinister Token': {
            ask: getSpecialItemPrice('Sinister Essence', 'ask') || 1400,
            bid: getSpecialItemPrice('Sinister Essence', 'bid') || 1200
        },
        'Enchanted Token': {
            ask: getSpecialItemPrice('Enchanted Essence', 'ask') || 3900,
            bid: getSpecialItemPrice('Enchanted Essence', 'bid') || 3500
        },
    };


    function saveChestList() {
        localStorage.setItem('chestList', JSON.stringify(chestList));
    }

    function loadChestList() {
        const savedChestList = localStorage.getItem('chestList');
        chestList = savedChestList ? JSON.parse(savedChestList) : {};
    }

    function getItemNameFromElement(element) {
        const itemNameRaw = element.getAttribute('href').split('#').pop();
        return formatItemName(itemNameRaw);
    }

    function handleNaNValues(itemName,values) {
        if (isNaN(values)) {
            console.error(`物品 ${itemName} 的值 为 NaN`);
            return -1;
        }
        return values;
    }

    function getItemPrice(itemName) {
        let itemAskValue = 0;
        let itemBidValue = 0;
        let priceColor = '#E7E7E7';

        if (chestList[itemName] && chestList[itemName].totalAskValue) {
            // 如果是箱子,直接使用chestList中的价格信息
            itemAskValue = chestList[itemName].totalAskValue;
            itemBidValue = chestList[itemName].totalBidValue;
        } else {
            if (specialItemPrices[itemName]) {
                itemAskValue = specialItemPrices[itemName].ask;
                itemBidValue = specialItemPrices[itemName].bid;
            } else {
                if (marketData) {
                    try {
                        if (marketData && marketData.market && marketData.market[itemName]) {
                            itemAskValue = marketData.market[itemName].ask;
                            itemBidValue = marketData.market[itemName].bid;

                            if (itemAskValue === -1 && itemBidValue === -1) {
                                priceColor = 'yellow';
                            } else if (itemAskValue === -1) {
                                priceColor = '#D95961';
                            } else if (itemBidValue === -1) {
                                priceColor = '#2FC4A7';
                            }

                            if (itemAskValue === -1 && itemBidValue !== -1) {
                                itemAskValue = itemBidValue;
                            }
                        } else {
                            console.error(`未找到物品 ${itemName} 的价格信息`);
                            priceColor = 'yellow';
                        }

                    } catch (error) {
                        console.error(`解析 MWITools_marketAPI_json 数据时出错:`, error);
                    }
                } else {
                    console.error('未找到 MWITools_marketAPI_json 的本地存储数据');
                }
            }
        }

        return { ask: itemAskValue, bid: itemBidValue, priceColor };
    }

    function formatItemName(itemNameRaw) {
        let formattedName = itemNameRaw.replace('#', '').replace(/_/g, ' ').replace(/\b\w/g, char => char.toUpperCase());

        if (formattedName.includes(' ')) {
            const words = formattedName.split(' ');
            let firstWord = words[0];
            const restOfName = words.slice(1).join(' ');
            if (firstWord.endsWith('s') && !firstWord.endsWith("'s")) {
                firstWord = `${firstWord.slice(0, -1)}'${firstWord.slice(-1)}`;
            }
            formattedName = `${firstWord}${restOfName ? " " + restOfName : ""}`;
        }

        return formattedName;
    }

    function parseQuantityRange(rangeText) {
        const parts = rangeText.split('-').map(str => parseInt(str.trim().replace(',', ''), 10));
        if (parts.length === 1) {
            return { min: parts[0], max: parts[0] };
        } else {
            return { min: parts[0], max: parts[1] };
        }
    }

    function parseProbability(probabilityText) {
        const probPercentage = parseFloat(probabilityText.replace('%', ''));
        return probPercentage / 100;
    }

    function formatPrice(value) {
        const isNegative = value < 0;
        value = Math.abs(value);

        if (value >= 1000000) {
            return (isNegative ? '-' : '') + (value / 1000000).toFixed(1) + 'M';
        } else if (value >= 1000) {
            return (isNegative ? '-' : '') + (value / 1000).toFixed(1) + 'K';
        } else {
            return (isNegative ? '-' : '') + value.toString();
        }
    }


    function parseQuantityString(quantityStr) {
        const suffix = quantityStr.slice(-1);
        const base = parseFloat(quantityStr.slice(0, -1));
        if (suffix === 'K') {
            return base * 1000;
        } else if (suffix === 'M') {
            return base * 1000000;
        } else if (suffix === 'B') {
            return base * 1000000000;
        } else {
            return parseFloat(quantityStr);
        }
    }

    function displayResult(container, totalExpectedOutputASK, totalExpectedOutputBID) {
        const formattedASK = formatPrice(totalExpectedOutputASK);
        const formattedBID = formatPrice(totalExpectedOutputBID);

        const dropListContainer = container.querySelector(resultDisplaySelector);

        // 继续执行其他操作
        const previousResults = dropListContainer.querySelectorAll('.resultDiv');
        previousResults.forEach(result => result.remove());

        // 创建期望产出(最低买入价计算)元素
        const minPriceOutput = document.createElement('div');
        minPriceOutput.className = 'resultDiv';
        minPriceOutput.textContent = `期望产出 (最低买入价计算): ${formattedASK}`;
        minPriceOutput.style.color = 'gold';
        minPriceOutput.style.fontSize = '14px';
        minPriceOutput.style.fontWeight = '400';
        minPriceOutput.style.paddingTop = '10px';

        // 创建期望产出(最高收购价计算)元素
        const maxPriceOutput = document.createElement('div');
        maxPriceOutput.className = 'resultDiv';
        maxPriceOutput.textContent = `期望产出 (最高收购价计算): ${formattedBID}`;
        maxPriceOutput.style.color = 'gold';
        maxPriceOutput.style.fontSize = '14px';
        maxPriceOutput.style.fontWeight = '400';
        maxPriceOutput.style.paddingTop = '10px';

        // 插入新创建的元素到掉落物表的最后一个物品后面
        dropListContainer.appendChild(minPriceOutput);
        dropListContainer.appendChild(maxPriceOutput);
    }

    function displayChestStatistics(chestName,chestCount) {
        const elementA = document.querySelector('.Inventory_modalContent__3ObSx');

        if (elementA) {
            // 获取总计开箱次数和开箱价值(ask/bid)
            const chestData = chestList[chestName];
            const totalOpened = chestData ? chestData.totalOpened : 0;
            const lastOpenAskValue = chestData ? chestData.lastOpenAskValue : 0;
            const lastOpenBidValue = chestData ? chestData.lastOpenBidValue : 0;
            const openTotalAskValue = chestData ? chestData.OpenTotalAskValue : 0;
            const openTotalBidValue = chestData ? chestData.OpenTotalBidValue : 0;

            // 创建显示内容
            const displayElement = document.createElement('div');
            displayElement.classList.add('ChestStatistics'); // 自定义类名,用于样式控制
            displayElement.style.position = 'absolute';
            displayElement.style.left = `${elementA.offsetLeft}px`;
            displayElement.style.top = `${elementA.offsetTop}px`;
            displayElement.style.fontSize = '12px';
            displayElement.innerHTML = `
            总计开箱次数: ${totalOpened}<br>
            本次开箱价值:<br>
            ${formatPrice(lastOpenAskValue)}/${formatPrice(lastOpenBidValue)}<br>
            总计开箱价值:<br>
            ${formatPrice(openTotalAskValue)}/${formatPrice(openTotalBidValue)}<br>
        `;

            elementA.appendChild(displayElement);

            // 判断是否显示期望产出元素
            if (chestData && chestData.totalAskValue && chestData.totalBidValue) {
                const totalAskValue = parseFloat(chestData.totalAskValue);
                const totalBidValue = parseFloat(chestData.totalBidValue);
                const expectedAskOutput = totalAskValue * chestCount;
                const expectedBidOutput = totalBidValue * chestCount;

                // 创建新的显示元素
                const expectedOutputElement = document.createElement('div');
                expectedOutputElement.classList.add('ExpectedOutput');
                expectedOutputElement.style.position = 'absolute';
                expectedOutputElement.style.left = `${elementA.offsetLeft}px`;
                expectedOutputElement.style.fontSize = '12px';
                expectedOutputElement.innerHTML = `
                期望产出:<br>
                ${formatPrice(expectedAskOutput)}/${formatPrice(expectedBidOutput)}<br>
            `;
                //调整位置
                document.body.appendChild(expectedOutputElement);
                expectedOutputElement.style.top = `${elementA.offsetTop + elementA.offsetHeight - expectedOutputElement.offsetHeight}px`;

                elementA.appendChild(expectedOutputElement);
            }

            return displayElement;
        } else {
            console.error('未找到窗体元素');
            return null;
        }
    }


    function processItems() {
        const modalContainer = document.querySelector(".Modal_modalContainer__3B80m");
        if (!modalContainer) return; // 如果不存在 Modal_modalContainer__3B80m 元素,则直接返回

        const chestNameElem = document.querySelector(chestNameSelector);
        if (!chestNameElem) return;

        const chestName = getItemNameFromElement(chestNameElem);

        const items = document.querySelectorAll(itemSelector);

        const itemDataList = [];
        let totalAskValue = 0;
        let totalBidValue = 0;

        items.forEach(item => {
            const quantityRangeElem = item.querySelector('div:first-child');
            const quantityRangeText = quantityRangeElem.textContent.trim();
            const quantityRange = parseQuantityRange(quantityRangeText);

            const itemName = getItemNameFromElement(item.querySelector(iconSelector));

            let probabilityElem = item.querySelector('div:nth-child(3)');//提取物品的概率
            let probabilityText = probabilityElem ? probabilityElem.textContent.trim() : '';
            probabilityText = probabilityText.replace('~', '');

            let probability;
            if (probabilityText === '') {
                probability = 1.0; // 如果概率文本为空,则假定掉落率为100%
            } else {
                probability = parseProbability(probabilityText);
            }

            let expectedOutput = 0;
            if (quantityRange.min === quantityRange.max) {
                expectedOutput = quantityRange.min * probability;
            } else {
                const average = (quantityRange.min + quantityRange.max) / 2;
                expectedOutput = average * probability;
            }

            let { ask: itemAskValue, bid: itemBidValue, priceColor } = getItemPrice(itemName);

            const itemTotalAskValue = expectedOutput * itemAskValue;
            const itemTotalBidValue = expectedOutput * itemBidValue;

            totalAskValue += itemTotalAskValue;
            totalBidValue += itemTotalBidValue;

            const itemData = {
                itemName,
                quantityRange: `${quantityRange.min}-${quantityRange.max}`,
                probability: probability * 100,
                expectedOutput: expectedOutput.toFixed(2),
                itemAskValue,
                itemBidValue,
                itemTotalAskValue: itemTotalAskValue.toFixed(2),
                itemTotalBidValue: itemTotalBidValue.toFixed(2),
                priceColor
            };

            itemDataList.push(itemData);

            const itemNameElem = item.querySelector('.Item_name__2C42x');
            if (itemNameElem) {
                if (priceColor) {
                    itemNameElem.style.color = priceColor;
                }
            }

        });

        if (itemDataList.length > 0) {
            chestList[chestName] = {
                items: itemDataList,
                totalAskValue: totalAskValue.toFixed(2),
                totalBidValue: totalBidValue.toFixed(2)
            };
            saveChestList();
            displayResult(document.body, totalAskValue, totalBidValue);
        }
    }

    function recordChestOpening(modalElement) {

        if (document.querySelector('.ChestStatistics')) {
            return;
        }

        const chestNameElement = modalElement.querySelector("div.Modal_modal__1Jiep > div.Inventory_modalContent__3ObSx > div.Item_itemContainer__x7kH1 > div > div > div.Item_iconContainer__5z7j4 > div > svg > use");
        const chestCountElement = modalElement.querySelector("div.Modal_modal__1Jiep > div.Inventory_modalContent__3ObSx > div.Item_itemContainer__x7kH1 > div > div > div.Item_count__1HVvv");

        if (chestNameElement && chestCountElement) {
            const chestName = getItemNameFromElement(chestNameElement);
            const chestCount = parseQuantityString(chestCountElement.textContent.trim());

            const itemsContainer = modalElement.querySelector('.Inventory_gainedItems___e9t9');
            const itemElements = itemsContainer.querySelectorAll('.Item_itemContainer__x7kH1');

            let totalAskValue = 0;
            let totalBidValue = 0;
            const items = [];

            itemElements.forEach(itemElement => {
                const itemNameElement = itemElement.querySelector('.Item_iconContainer__5z7j4 use');
                const itemQuantityElement = itemElement.querySelector('.Item_count__1HVvv');

                if (itemNameElement && itemQuantityElement) {
                    const itemName = getItemNameFromElement(itemNameElement);
                    const itemQuantity = parseQuantityString(itemQuantityElement.textContent.trim());
                    const { ask: itemAskValue, bid: itemBidValue, priceColor } = getItemPrice(itemName);

                    const itemOpenTotalAskValue = itemAskValue * itemQuantity;
                    const itemOpenTotalBidValue = itemBidValue * itemQuantity;

                    items.push({
                        itemName,
                        itemAskValue,
                        itemBidValue,
                        itemOpenTotalAskValue,
                        itemOpenTotalBidValue,
                        quantity: itemQuantity,
                        priceColor
                    });

                    totalAskValue += itemOpenTotalAskValue;
                    totalBidValue += itemOpenTotalBidValue;
                }
            });

            if (!chestList[chestName]) {
                chestList[chestName] = {
                    items: [],
                    OpenTotalAskValue: 0,
                    OpenTotalBidValue: 0,
                    totalOpened: 0,
                    lastOpenAskValue: 0,
                    lastOpenBidValue: 0
                };
            } else {
                const chestData = chestList[chestName];

                chestData.OpenTotalAskValue = chestData.OpenTotalAskValue || 0;
                chestData.OpenTotalBidValue = chestData.OpenTotalBidValue || 0;
                chestData.totalOpened = chestData.totalOpened || 0;
                chestData.lastOpenAskValue = chestData.lastOpenAskValue || 0;
                chestData.lastOpenBidValue = chestData.lastOpenBidValue || 0;
            }

            const chestData = chestList[chestName];

            chestData.lastOpenAskValue = totalAskValue;
            chestData.lastOpenBidValue = totalBidValue;
            chestData.OpenTotalAskValue += totalAskValue;
            chestData.OpenTotalBidValue += totalBidValue;
            chestData.totalOpened += chestCount;

            items.forEach(item => {
                const existingItem = chestData.items.find(i => i.itemName === item.itemName);
                if (existingItem) {
                    existingItem.quantity += item.quantity;
                    existingItem.itemOpenTotalAskValue += item.itemOpenTotalAskValue;
                    existingItem.itemOpenTotalBidValue += item.itemOpenTotalBidValue;
                } else {
                    chestData.items.push(item);
                }
            });

            saveChestList();
            displayChestStatistics(chestName,chestCount);

        }
    }


    function calculateTotalValues(itemElements) {
        let totalAskValue = 0;
        let totalBidValue = 0;
        itemElements.forEach(itemElement => {
            const itemNameElement = itemElement.querySelector('.Item_iconContainer__5z7j4 use');
            const itemQuantityElement = itemElement.querySelector('.Item_count__1HVvv');
            if (itemNameElement && itemQuantityElement) {
                const itemName = getItemNameFromElement(itemNameElement);
                const itemQuantity = parseQuantityString(itemQuantityElement.textContent.trim());
                const { ask: itemAskValue, bid: itemBidValue, priceColor } = getItemPrice(itemName);

                const itemTotalAskValue = itemAskValue * itemQuantity;
                const itemTotalBidValue = itemBidValue * itemQuantity;
                totalAskValue += itemTotalAskValue;
                totalBidValue += itemTotalBidValue;
            }
        });
        console.log(totalAskValue)
        return { totalAskValue, totalBidValue };
    }


    function OfflineStatistics(modalElement) {
        const itemsContainer = modalElement.querySelectorAll(".OfflineProgressModal_itemList__26h-Y");

        let timeContainer = null;
        let getItemContainer = null;
        let spendItemContainer = null;


        itemsContainer.forEach(container => {
            const labelElement = container.querySelector('.OfflineProgressModal_label__2HwFG');
            if (labelElement) {
                const textContent = labelElement.textContent.trim();
                if (textContent.startsWith("You were offline for") || textContent.startsWith("你离线了")) {
                    timeContainer = container;
                } else if (textContent.startsWith("Items gained:") || textContent.startsWith("获得物品:")) {
                    getItemContainer = container;
                } else if (textContent.startsWith("You consumed:") || textContent.startsWith("你消耗了:")) {
                    spendItemContainer = container;
                }
            }
        });

        let TotalSec = null;
        if (timeContainer) {
            const textContent = timeContainer.textContent;
            const match = textContent.match(/(?:(\d+)d\s*)?(?:(\d+)h\s*)?(?:(\d+)m\s*)?(?:(\d+)s)/);
            if (match) {
                let days = parseInt(match[1], 10) || 0;
                let hours = parseInt(match[2], 10) || 0;
                let minutes = parseInt(match[3], 10) || 0;
                let seconds = parseInt(match[4], 10) || 0;
                TotalSec = days * 86400 + hours * 3600 + minutes * 60 + seconds;
            }
        }

        let getitemtotalAskValue = 0;
        let getitemtotalBidValue = 0;
        if (getItemContainer) {
            const getitemElements = getItemContainer.querySelectorAll('.Item_itemContainer__x7kH1');
            const { totalAskValue, totalBidValue } = calculateTotalValues(getitemElements);
            getitemtotalAskValue = totalAskValue;
            getitemtotalBidValue = totalBidValue;
        }


        let spenditemtotalAskValue = 0;
        let spenditemtotalBidValue = 0;
        if (spendItemContainer) {
            const spenditemElements = spendItemContainer.querySelectorAll('.Item_itemContainer__x7kH1');
            const { totalAskValue, totalBidValue } = calculateTotalValues(spenditemElements);
            spenditemtotalAskValue = totalAskValue;
            spenditemtotalBidValue = totalBidValue;
        }

        if (timeContainer) {
            const newElement = document.createElement('span');
            newElement.textContent = `利润:${formatPrice(getitemtotalBidValue - spenditemtotalAskValue)}[${formatPrice((getitemtotalBidValue - spenditemtotalAskValue) / (TotalSec / 3600) * 24)}/天]`;
            newElement.style.float = 'right';
            newElement.style.color = 'gold';
            timeContainer.querySelector(':first-child').appendChild(newElement);
        }
        if (getItemContainer) {
            const newElement = document.createElement('span');
            newElement.textContent = `产出:[${formatPrice(getitemtotalAskValue)}/${formatPrice(getitemtotalBidValue)}]`;
            newElement.style.float = 'right';
            newElement.style.color = 'gold';
            getItemContainer.querySelector(':first-child').appendChild(newElement);
        }
        if (spendItemContainer) {
            const newElement = document.createElement('span');
            newElement.textContent = `成本:[${formatPrice(spenditemtotalAskValue)}/${formatPrice(spenditemtotalBidValue)}]`;
            newElement.style.float = 'right';
            newElement.style.color = 'gold';
            spendItemContainer.querySelector(':first-child').appendChild(newElement);
        }
    }


    // 初始化时加载已保存的箱子列表
    loadChestList();
    console.log(chestList);
    // 初始化


    function initObserver() {
        // 选择要观察的目标节点
        const targetNode = document.body;

        // 观察器的配置(需要观察子节点的变化)
        const config = { childList: true, subtree: true };

        // 创建一个观察器实例并传入回调函数
        const observer = new MutationObserver(mutationsList => {
            for (let mutation of mutationsList) {
                if (mutation.type === 'childList') {
                    // 监听到子节点变化
                    mutation.addedNodes.forEach(addedNode => {
                        // 检查是否是我们关注的 Modal_modalContainer__3B80m 元素被添加
                        if (addedNode.classList && addedNode.classList.contains('Modal_modalContainer__3B80m')) {
                            // Modal_modalContainer__3B80m 元素被添加,执行处理函数
                            processItems();
                            recordChestOpening(addedNode);

                            // 开始监听箱子图标的变化
                            startIconObserver();
                        }
                        if (addedNode.classList && addedNode.classList.contains('OfflineProgressModal_modalContainer__knnk7')) {
                            OfflineStatistics(addedNode);
                            console.log("离线报告已创建!")
                        }
                    });

                    mutation.removedNodes.forEach(removedNode => {
                        // 检查是否是 Modal_modalContainer__3B80m 元素被移除
                        if (removedNode.classList && removedNode.classList.contains('Modal_modalContainer__3B80m')) {
                            // Modal_modalContainer__3B80m 元素被移除,停止监听箱子图标的变化
                            stopIconObserver();
                        }
                    });
                }
            }
        });

        // 以上述配置开始观察目标节点
        observer.observe(targetNode, config);

        // 定义箱子图标变化的观察器
        let iconObserver = null;

        // 开始监听箱子图标的变化
        function startIconObserver() {
            const chestNameElem = document.querySelector(chestNameSelector);
            if (!chestNameElem) return;

            // 创建一个观察器实例来监听图标的变化
            iconObserver = new MutationObserver(() => {
                // 当箱子图标变化时,执行处理函数
                processItems();
            });

            // 配置观察器的选项
            const iconConfig = { attributes: true, attributeFilter: ['href'] };

            // 以上述配置开始观察箱子图标节点
            iconObserver.observe(chestNameElem, iconConfig);
        }

        // 停止监听箱子图标的变化
        function stopIconObserver() {
            if (iconObserver) {
                iconObserver.disconnect();
                iconObserver = null;
            }
        }
    }




    initObserver();

    const userLanguage = navigator.language || navigator.userLanguage;
    const isZH = userLanguage.startsWith("zh");
    const updataDealy = 24*60*60*1000; //数据更新时限
    let rateXPDayMap = {};

    function hookWS() {
        const dataProperty = Object.getOwnPropertyDescriptor(MessageEvent.prototype, "data");
        const oriGet = dataProperty.get;

        dataProperty.get = hookedGet;
        Object.defineProperty(MessageEvent.prototype, "data", dataProperty);

        function hookedGet() {
            const socket = this.currentTarget;
            if (!(socket instanceof WebSocket)) {
                return oriGet.call(this);
            }
            if (socket.url.indexOf("api.milkywayidle.com/ws") <= -1 && socket.url.indexOf("api-test.milkywayidle.com/ws") <= -1) {
                return oriGet.call(this);
            }

            const message = oriGet.call(this);
            Object.defineProperty(this, "data", { value: message }); // Anti-loop

            return handleMessage(message);
        }
    }



    //奶牛钉钉
    function handleMessage(message) {
        try {
            let obj = JSON.parse(message);
            if (obj && obj.type === "guild_updated") {
                const Guild_ID = obj.guild.id;
                let storedData = JSON.parse(localStorage.getItem("Guild_Data")) || {};

                // 判断是否已经存在旧数据
                if (storedData[Guild_ID] && storedData[Guild_ID].guild_updated && storedData[Guild_ID].guild_updated.old.updatedAt) {
                    const oldUpdatedAt = new Date(storedData[Guild_ID].guild_updated.new.updatedAt);
                    const newUpdatedAt = new Date(obj.guild.updatedAt);

                    // 计算时间差(单位:毫秒)
                    const timeDifference = newUpdatedAt - oldUpdatedAt;

                    if (timeDifference >= updataDealy) {
                        // 更新老数据为新数据
                        storedData[Guild_ID].guild_updated.old = storedData[Guild_ID].guild_updated.new;
                        // 更新新数据为当前数据
                        storedData[Guild_ID].guild_updated.new = {
                            experience: obj.guild.experience,
                            level: obj.guild.level,
                            updatedAt: obj.guild.updatedAt
                        };
                    } else {
                        // 仅更新新数据
                        storedData[Guild_ID].guild_updated.new = {
                            experience: obj.guild.experience,
                            level: obj.guild.level,
                            updatedAt: obj.guild.updatedAt
                        };
                    }
                    //计算Δ
                    const Delta = {
                        Delta_Xp: storedData[Guild_ID].guild_updated.new.experience - storedData[Guild_ID].guild_updated.old.experience,
                        Delta_Level: storedData[Guild_ID].guild_updated.new.level - storedData[Guild_ID].guild_updated.old.level,
                        Delta_Time: (newUpdatedAt - new Date(storedData[Guild_ID].guild_updated.old.updatedAt)) / 1000, // 转换为秒
                        Rate_XP_Hours: (3600*(obj.guild.experience - storedData[Guild_ID].guild_updated.old.experience)/((newUpdatedAt - new Date(storedData[Guild_ID].guild_updated.old.updatedAt)) / 1000)).toFixed(2)
                    };
                    storedData[Guild_ID].guild_updated.Delta = Delta;

                    const Guild_TotalXp_div = document.querySelectorAll(".GuildPanel_value__Hm2I9")[1];
                    if (Guild_TotalXp_div) {
                        const xpText = isZH ? "经验值 / 小时" : "XP / Hour";

                        Guild_TotalXp_div.insertAdjacentHTML(
                            "afterend",
                            `<div>${formatPrice(Delta.Rate_XP_Hours)} ${xpText}</div>`
                        );
                        const Guild_NeedXp_div = document.querySelectorAll(".GuildPanel_value__Hm2I9")[2];
                        if (Guild_NeedXp_div) {
                            const Guild_NeedXp = document.querySelectorAll(".GuildPanel_value__Hm2I9")[2].textContent.replace(/,/g, '');
                            const Time = TimeReset(Guild_NeedXp/Delta.Rate_XP_Hours);
                            Guild_NeedXp_div.insertAdjacentHTML(
                                "afterend", // 使用 "afterend" 在元素的后面插入内容
                                `<div>${Time}</div>`
                        );
                        }
                    }
                } else {
                    // 如果没有旧数据,则直接添加新数据
                    storedData[Guild_ID] = {
                        guild_name: obj.guild.name,
                        guild_updated: {
                            old: {
                                experience: obj.guild.experience,
                                level: obj.guild.level,
                                updatedAt: obj.guild.updatedAt
                            },
                            new: {},
                        }
                    };
                }

                // 存储更新后的数据到 localStorage
                localStorage.setItem("Guild_Data", JSON.stringify(storedData));
            } else if (obj && obj.type === "guild_characters_updated") {
                let storedData = JSON.parse(localStorage.getItem("Guild_Data")) || {};
                for (const key in obj.guildSharableCharacterMap) {
                    if (obj.guildSharableCharacterMap.hasOwnProperty(key)) {
                        const Guild_ID = obj.guildCharacterMap[key].guildID;
                        const name = obj.guildSharableCharacterMap[key].name;
                        const newUpdatedAt = new Date();
                        storedData[Guild_ID].guild_player = storedData[Guild_ID].guild_player || {};
                        if (storedData[Guild_ID] && storedData[Guild_ID].guild_player && storedData[Guild_ID].guild_player[name] && storedData[Guild_ID].guild_player[name].old && storedData[Guild_ID].guild_player[name].old.updatedAt) {
                            const oldUpdatedAt = storedData[Guild_ID].guild_player[name].old.updatedAt
                            const timeDifference = newUpdatedAt - oldUpdatedAt
                            if (timeDifference >= updataDealy) {
                                // 更新老数据为新数据
                                storedData[Guild_ID].guild_player[name].old = storedData[Guild_ID].guild_player[name].new;
                                // 更新新数据为当前数据
                                storedData[Guild_ID].guild_player[name].new = {
                                    id: key,
                                    gameMode: obj.guildSharableCharacterMap[key].gameMode,
                                    guildExperience: obj.guildCharacterMap[key].guildExperience,
                                    updatedAt: newUpdatedAt,
                                };
                            } else {
                                // 仅更新新数据
                                storedData[Guild_ID].guild_player[name].new = {
                                    id: key,
                                    gameMode: obj.guildSharableCharacterMap[key].gameMode,
                                    guildExperience: obj.guildCharacterMap[key].guildExperience,
                                    updatedAt: newUpdatedAt,
                                };
                            }
                            //计算Δ
                            const Delta = {
                                Delta_Time:(newUpdatedAt - new Date(storedData[Guild_ID].guild_player[name].old.updatedAt)) / 1000,
                                Delta_Xp: storedData[Guild_ID].guild_player[name].new.guildExperience - storedData[Guild_ID].guild_player[name].old.guildExperience,
                                Rate_XP_Day: (24*3600*(obj.guildCharacterMap[key].guildExperience - storedData[Guild_ID].guild_player[name].old.guildExperience)/((newUpdatedAt - new Date(storedData[Guild_ID].guild_player[name].old.updatedAt)) / 1000)).toFixed(2)
                            };
                            storedData[Guild_ID].guild_player[name].Delta = Delta;
                            rateXPDayMap[name] = Delta.Rate_XP_Day;
                        }else {
                            storedData[Guild_ID].guild_player[name] = {
                                old: {
                                    id: key,
                                    gameMode: obj.guildSharableCharacterMap[key].gameMode,
                                    guildExperience: obj.guildCharacterMap[key].guildExperience,
                                    updatedAt: newUpdatedAt,
                                },
                                new:{}
                            };
                        }
                    }

                }
                //console.log("测试数据",storedData);
                //console.log("guild_characters_updated", obj);
                updateExperienceDisplay(rateXPDayMap);
                localStorage.setItem("Guild_Data", JSON.stringify(storedData));
            }




        } catch (error) {
            console.error("Error processing message:", error);
        }
        return message;
    }


    function TimeReset(hours) {
        const totalMinutes = hours * 60;
        const days = Math.floor(totalMinutes / (24 * 60));
        const yudays = totalMinutes % (24 * 60);
        const hrs = Math.floor(yudays / 60);
        const minutes = Math.floor(yudays % 60);
        const dtext = isZH ? "天" : "d";
        const htext = isZH ? "时" : "h";
        const mtext = isZH ? "分" : "m";
        return `${days}${dtext} ${hrs}${htext} ${minutes}${mtext}`;
    }

    function updateExperienceDisplay(rateXPDayMap) {
        const trElements = document.querySelectorAll(".GuildPanel_membersTable__1NwIX tbody tr");
        const idleuser_list = [];
        const dtext = isZH ? "天" : "d";

        // 将 rateXPDayMap 转换为数组并排序
        const sortedMembers = Object.entries(rateXPDayMap)
        .map(([name, XPdata]) => ({ name, XPdata }))
        .sort((a, b) => b.XPdata - a.XPdata);

        sortedMembers.forEach(({ name, XPdata }) => {
            trElements.forEach(tr => {
                const nameElement = tr.querySelector(".CharacterName_name__1amXp");
                const experienceElement = tr.querySelector("td:nth-child(3) > div");
                const activityElement = tr.querySelector('.GuildPanel_activity__9vshh');

                if (nameElement && nameElement.textContent.trim() === name) {
                    if (activityElement.childElementCount === 0) {
                        idleuser_list.push(nameElement.textContent.trim());
                    }

                    if (experienceElement) {
                        const newDiv = document.createElement('div');
                        newDiv.textContent = `${formatPrice(XPdata)}/${dtext}`;

                        // 计算颜色
                        const rank = sortedMembers.findIndex(member => member.name === name);
                        const hue = 120 - (rank * (120 / (sortedMembers.length - 1)));
                        newDiv.style.color = `hsl(${hue}, 100%, 50%)`;

                        experienceElement.insertAdjacentElement('afterend', newDiv);
                    }
                    return;
                }
            });
        });

        update_idleuser_tb(idleuser_list);
    }

    function update_idleuser_tb(idleuser_list) {
        const targetElement = document.querySelector('.GuildPanel_noticeMessage__3Txji');
        if (!targetElement) {
            console.error('公会标语元素未找到!');
            return;
        }
        const clonedElement = targetElement.cloneNode(true);

        const namesText = idleuser_list.join(', ');
        clonedElement.innerHTML = '';
        clonedElement.textContent = isZH ? `闲置的成员:${namesText}` : `Idle User : ${namesText}`;
        clonedElement.style.color = '#ffcc00';

        // 设置复制元素的高度为原元素的25%
        const originalStyle = window.getComputedStyle(targetElement);
        const originalHeight = originalStyle.height;
        const originalMinHeight = originalStyle.minHeight;
        clonedElement.style.height = `25%`;
        clonedElement.style.minHeight = `25%`; // 也设置最小高度
        targetElement.parentElement.appendChild(clonedElement);
    }


    hookWS();

})();