Greasy Fork

Greasy Fork is available in English.

牛牛战斗助手

牛牛

目前为 2025-05-17 提交的版本,查看 最新版本

// ==UserScript==
// @name         牛牛战斗助手
// @namespace    http://tampermonkey.net/
// @version      0.0.4
// @description  牛牛
// @icon         https://www.milkywayidle.com/favicon.svg
// @author       xiaoshui
// @match        https://www.milkywayidle.com/*
// @grant        GM_xmlhttpRequest
// @connect      niuniu.0xa2c2a.shop
// @license MIT
// ==/UserScript==

(function() {
    if (document.URL.includes("test.milkywayidle.com"))return;

    const host = "https://niuniu.0xa2c2a.shop"
    let config={
        sikll_upload: host + "/api/player",
        party_upload:host + "/api/party",
        api_version:"0.0.1"
    }
        // 拦截WS
    function hookWebSocket() {
        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 });
            return handleMessage(message);
        }
    }
    hookWebSocket();

        // WS拦截后处理,主进程
    function handleMessage(message,debug=false) {
        let obj = JSON.parse(message);
        if (obj && obj.type === "init_character_data") {
            // 组队情况
            addPartyButton();
            addPlayerButton();
            // 上传初始化登录数据
            uploadInitCharacterData(obj);
        }else if (obj && obj.type === "profile_shared"){
            // 上传玩家面板数据
            uploadProfileShared(obj);
        }
        //other
        return message;
    }
        // 上传玩家面板数据
    function uploadProfileShared(obj){
        let data={};
        let profile = obj.profile;
        data.name=profile.sharableCharacter.name
        if(profile.sharableCharacter.gameMode !== "standard")return;
        data.weapon = profile.wearableItemMap === null ?null:getWeapon(profile?.wearableItemMap);
        let ranged,attack,power,magic = "";
        for( const item of profile.characterSkills){
            if(item.skillHrid === "/skills/ranged"){
                ranged = item.experience.toFixed();
                data.id=item.characterID;
            }else if(item.skillHrid === "/skills/attack"){
                attack = item.experience.toFixed();
            } else if(item.skillHrid === "/skills/power"){
                power = item.experience.toFixed();
            }else if(item.skillHrid === "/skills/magic"){
                magic = item.experience.toFixed();
            }
        }
        data.skill=ranged+","+attack+","+power+","+magic;
        return new Promise((resolve, reject) => {
             GM_xmlhttpRequest({
                 method: 'POST',
                 url: config.sikll_upload,
                 headers: {
                     "Content-Type": "application/json",
                     "reporter":data.id,
                     "apiVersion":config.api_version
                 },
                 data:JSON.stringify(data),
                 onload: function (response) {
                     resolve();
                 },
                 onerror: function (error) {
                     reject(error);
                 }
             });
         });
    }
    function getWeapon(wearableItemMap){
        if(!wearableItemMap) return null;
        let hand = wearableItemMap['/item_locations/main_hand']?.itemHrid
        if(!hand){
            hand = wearableItemMap['/item_locations/two_hand']?.itemHrid
        }
        return hand?.substr(7)

    }
        // 上传初始化登录数据
    function uploadInitCharacterData(obj){
        const fake_profile_shared={}
        fake_profile_shared.type="profile_shared";
        fake_profile_shared.profile={};
        fake_profile_shared.profile.characterSkills=obj.characterSkills;
        fake_profile_shared.profile.combatLevel=obj.combatUnit.combatDetails.combatLevel;
        if(obj.guild){
            fake_profile_shared.profile.guildName=obj.guild.name;
            fake_profile_shared.profile.guildRole=obj.guildCharacterMap[obj.character.id].role;
        }else{
            fake_profile_shared.profile.guildName="";
            fake_profile_shared.profile.guildRole="";
        }
        fake_profile_shared.profile.sharableCharacter=obj.character;
        fake_profile_shared.profile.wearableItemMap={}
        obj.characterItems.forEach(item=>{
            if(item.itemLocationHrid!='/item_locations/inventory'){
                fake_profile_shared.profile.wearableItemMap[item.itemLocationHrid]=item;
            }
        })
        uploadProfileShared(fake_profile_shared);
        uploadPartyInfo(obj);
    }

        // 上传组队数据
    function uploadPartyInfo(obj){
        let data={};
        const partyInfo = obj?.partyInfo
        if((!partyInfo) || (partyInfo?.party?.status !== "battling")) return;

        data.id=partyInfo.party.id;
        data.map=partyInfo.party.actionHrid.substr(16);

        data.players="";
        for(let k in partyInfo.sharableCharacterMap){
            if(data.players) data.players += ","
            data.players += partyInfo.sharableCharacterMap[k].name
        }

        let reporter = obj.user.id;
        return new Promise((resolve, reject) => {
             GM_xmlhttpRequest({
                 method: 'POST',
                 url: config.party_upload,
                 headers: {
                     "Content-Type": "application/json",
                     "reporter":reporter,
                     "apiVersion":config.api_version
                 },
                 data:JSON.stringify(data),
                 onload: function (response) {
                     resolve();
                 },
                 onerror: function (error) {
                     reject(error);
                 }
             });
         });
    }

        //组队情况
    function addPartyButton() {
        const waitForNavi = () => {
            const targetNode = document.querySelector("div.NavigationBar_minorNavigationLinks__dbxh7"); // 确认这个选择器是否适合你的环境
            const navigationLinks = document.querySelectorAll('div.NavigationBar_minorNavigationLink__31K7Y');
            let toolLink;
            for (let link of navigationLinks) {
                if (link.textContent.includes('插件设置')||link.textContent.includes('Script settings')) {
                    toolLink = link;
                    break;
                }
            }
            if (targetNode&&toolLink) {
                let statsButton = document.createElement("div");
                statsButton.setAttribute("class", "NavigationBar_minorNavigationLink__31K7Y");
                statsButton.style.color = toolLink.style.color;
                statsButton.innerHTML = "国人组队情况";
                statsButton.addEventListener("click", () => {
                    window.open("https://niuniu.0xa2c2a.shop/party.html", "_blank");
                });
                // 将按钮添加到目标节点
                targetNode.insertBefore(statsButton, toolLink.nextSibling);
            } else {
                setTimeout(addPartyButton, 200);
            }
        };
        waitForNavi(); // 开始等待目标节点出现
    }
    //组队情况
    function addPlayerButton() {
        const waitForNavi = () => {
            const targetNode = document.querySelector("div.NavigationBar_minorNavigationLinks__dbxh7"); // 确认这个选择器是否适合你的环境
            const navigationLinks = document.querySelectorAll('div.NavigationBar_minorNavigationLink__31K7Y');
            let toolLink;
            for (let link of navigationLinks) {
                if (link.textContent.includes('插件设置')||link.textContent.includes('Script settings')) {
                    toolLink = link;
                    break;
                }
            }
            if (targetNode&&toolLink) {
                let statsButton = document.createElement("div");
                statsButton.setAttribute("class", "NavigationBar_minorNavigationLink__31K7Y");
                statsButton.style.color = toolLink.style.color;
                statsButton.innerHTML = "战斗排行榜";
                statsButton.addEventListener("click", () => {
                    window.open("https://niuniu.0xa2c2a.shop/player.html", "_blank");
                });
                // 将按钮添加到目标节点
                targetNode.insertBefore(statsButton, toolLink.nextSibling);
            } else {
                setTimeout(addPlayerButton, 200);
            }
        };
        waitForNavi(); // 开始等待目标节点出现
    }

})();