您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Quality of Life changes for Diamond Hunt Online
当前为
// ==UserScript== // @name DHQoL // @namespace http://tampermonkey.net/ // @version 1.3 // @description Quality of Life changes for Diamond Hunt Online // @author John / WhoIsYou // @match *.diamondhunt.co/game.php // @run-at document-idle // @grant unsafeWindow // ==/UserScript== /* jshint -W097 */ 'use strict'; /** FEATURES: - Clickable links in chat! Very simple regex pattern that won't catch ALL links, but it will catch the majority / common formats - Market exclusion list, to filter out users you don't like or don't want to see (such as justanoob) - Market clutter remover, only shows the 5 first/cheapest listings for any single, item not counting your own items - Market inclusion list, doesn't count any offers by these users in the 5 offer limit per item (always shows these people's items) - Rocket timer that's fairly accurate (though RNG based) based on average distances traveled per second found via logging - Furnace timer (accurate) in addition to the percentage. Bar type text removed and iron furnace image replaced with current item being smelted - Improved timers for, fishing boat, rocket, robot, furnace, exploring, potions, farming (only if you have the donor perk), spells, and ancient crystal in the HH:MM:SS format - Farming, and Exploring timers factor in the donor perks if you have them, giving much more accurate timers than the default (though farming is still slightly RNG based) - Prevents the selling of brewing ingredients in the brewing tab - Added indicators that let you know when you have Explorer's Potions or Artifact Potions active - Improved functionality for converting your coins to plat and plat to coins (left click them) - Removed binding of equal or lower tier furnaces and ovens if you have promethium or higher bound - Added the ability to craft multiple rocket fuel barrels, activate with 200m+ oil if you have an oil refinery, or 100m+ without, otherwise it crafts 1 per click like normal **/ /** CHANGELOG: v1.3: - Improved coin swapping functionality, left click coins to convert to plat, left click plat to convert to coins - Dialog asking how many rocket fuel barrels you would like to craft if you're above a certain oil threshold, otherwise it crafts one like normal - Removed the ability to bind equal or lower tier ovens and furnaces if you have promethium or higher bound - Added users "faggots" and "nogresh" to the market exclusions list (they don't seem to play anymore) - Added formatting for magic spell timers - Added formatting for ancient crystal timer - Restructured some code v1.2a: (Jan 13 2016) - Updated code structure a little (not done yet) - Added timer formatting for the new fishing boat - Made items in the brewing tab unsellable on left click (req: https://redd.it/40uw3m) v1.0: (Jan 8 2016) - Release v1.1a: (Jan 8 2016) - Fixed furnace timer, no longer performs additional calculations for donator perk (this was default behaviour!) **/ var config = { ENABLE_LOGGING : true }; // Array of potions that use timers var TIMED_POTION_LIST = ["starDustPotion", "seedPotion", "smeltingPotion", "oilPotion", "miningPotion", "superStarDustPotion", "coinPotion", "compost", "fishingPotion", "essencePotion", "fastFurnacePotion", "superOilPotion", "megaStarDustPotion", "whaleFishingPotion", "megaOilPotion"]; // Array of spells that use timers var TIMED_SPELL_LIST = ["superDrills", "superGemFinder", "smallSips", "superPirate", "superCrushers", "sparklingCompostPotion", "superGiantDrills", "fastVendor", "superRoadHeaders", "animatedAxe", ]; // Users to be excluded from the market listing var MARKET_EXCLUSION_LIST = ["justanoob", "faggots", "nogresh"]; // Users to always be included in the market listing, useful if you have a habit of trading with a specific person var MARKET_INCLUSION_LIST = []; // Immediately-invoked function expression (IIFE), runs once on page load (function() { cLog("Launching DH QoL! Welcome, " + unsafeWindow.username + "."); // Create new notification boxes for artifact and explorer's potions createNotificationBoxes(); // Disable the selling of brewing items disableBrewingItemSelling(); // Clicking the vendor notification opens vendor tab //makeVendorNotifClickable(); // Improve the coin swapping functionality when clicking on coin icons improveCoinSwap() // Adds the ability to craft multiple rocket fuel barrels setCraftMultipleRocketFuel(); // Remove the ability to bind furnaces and ovens if you have higher tier ones setTimeout(disableBindFurnacesAndOvens, 3000); // 3 second delay after page loads so our variables (such as bound items) can load from ajax requests })(); /* --- Market --- */ /* Make changes to #loadOffers to filter out unwanted market items from the market table Filters out any users in our MARKET_EXCLUSION_LIST and filters out all but the cheapest 5 offers for any item (your own items do not count towards this limit) Items in the MARKET_INCLUSION_LIST will always be shown even if they are not among the cheapest 5 */ unsafeWindow.loadOffers = function(unparsedData) { var lastItem = ""; var lastItemCount = ""; var dataArray = unparsedData.split("~"); var table = document.getElementById("market-buy-table"); resetGEBox(1); resetGEBox(2); resetGEBox(3); table.innerHTML = "<tr class='table-th'><th>Player</th><th>Item</th><th>Icon</th><th>Amount</th><th>Price each</th></tr>" for(var i = 0; i < dataArray.length; i++) { var playerId = dataArray[i]; i++; var playerUsername = dataArray[i]; i++; var item = dataArray[i];i++; var total = dataArray[i];i++; var pricePer = dataArray[i];i++; var collect = dataArray[i];i++; var slot = dataArray[i];i++; var isPlatinum = dataArray[i];i++; var category = dataArray[i]; // Loads the items we're selling into our collection boxes if(playerUsername == username) { loadGEBoxSelling(item + "~" + total + "~" + pricePer + "~" + collect, slot); } // Adds offers to the market table as long as they're not in the excluded users list // Keeps tracks of how many offers exist for each individual type of item and only displays the first (cheapest) five not counting our own items if(total > 0 && MARKET_EXCLUSION_LIST.indexOf(playerUsername) < 0) { if (unsafeWindow.username != playerUsername && MARKET_INCLUSION_LIST.indexOf(playerUsername) < 0) { if (item == lastItem) { if (lastItemCount >= 5) { continue; } else { lastItemCount += 1; } } else { lastItem = item; lastItemCount = 1; } } // Adds any items that made it through the filter to the market table applyToBuyingTable(playerId, playerUsername, item, total, pricePer, slot, isPlatinum, category); } } } /* --- Chat --- */ /* Make links in chat clickable, not 100% perfect regex pattern but it gets the job done */ var oldRefreshChat = unsafeWindow.refreshChat; unsafeWindow.refreshChat = function(data) { var pattern = /(\b(https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/ig; data = data.replace(pattern, "<a href='$1' target='_blank'>$1</a>"); oldRefreshChat(data); } /* --- Misc --- */ /* Removes the left click to sell item functionality from all brewing-tab items since you'd almost never want to sell these */ function disableBrewingItemSelling() { var brewingTabDoc = document.getElementById("brewing-tab"); if (brewingTabDoc) { var sellableItems = brewingTabDoc.getElementsByClassName("item-box-spot")[0].querySelectorAll("[onclick=\"sellDailog(this.id)\"]"); for (var i = 0; i < sellableItems.length; i++) { sellableItems[i].removeAttribute("onclick"); } } } /* Left clicking the new vendor notification will open the vendor tab */ function makeVendorNotifClickable() { var node = document.getElementById("vendorChanged-notif"); if (node) { node.addEventListener("click", function() { openTab("vendor"); }); node.style.cursor = "pointer"; } } /* Removes the default right and left click functionality when clicking on your money Left clicking your coins icon will convert up to 2B coins to plat Left clicking your plat icon will convert plat to coins until you're carrying up to 2B */ function improveCoinSwap() { var moneyDocList = document.querySelectorAll("[oncontextmenu=\"send('COINS_TO_PLATINUM='+parseInt(coins/1000000));return false;\"]"); if (moneyDocList.length > 0) { var moneyDoc = moneyDocList[0]; moneyDoc.removeAttribute("oncontextmenu"); moneyDoc.removeAttribute("onclick"); var coinsElement = moneyDoc.childNodes[0]; var platElement = document.getElementById("platinum-coins-status-bar-span").childNodes[0]; coinsElement.onclick = function() { var convert = Math.min(Math.floor(parseInt(unsafeWindow.coins / 1000000)), 2000); if (convert > 0 ) { unsafeWindow.send("COINS_TO_PLATINUM=" + convert); } }; platElement.onclick = function() { var convert = Math.min(parseInt(unsafeWindow.platinumCoins), Math.max(2000 - Math.floor(parseInt(unsafeWindow.coins / 1000000)), 0)); if (convert > 0 ) { unsafeWindow.send("PLATINUM_TO_COINS=" + convert); } } } } /* Allows you to craft more than one rocket fuel at a time if you're above a certain amount of oil, otherwise it crafts just one like normal */ function setCraftMultipleRocketFuel() { var node = document.getElementById("craft-rocketFuel"); var dialogNode = document.getElementById("dialog-craft-multi"); if (node && dialogNode) { var dialogConfirmButton = dialogNode.querySelectorAll("[type='button']")[0]; if (dialogConfirmButton) { // Add the craft multiple dialog to the rocket fuel crafting node.removeAttribute("onclick"); node.addEventListener("click", function() { var requiredOil = (bindedOilRefinery == 0 ? 100000000 : 200000000); if (oil >= requiredOil) { craftMultipleItem("rocketFuel"); } else { craftItem("rocketFuel"); } }); // Craft in a loop since #craftMultiple doesn't allow rocket fuel crafting for some reason dialogConfirmButton.removeAttribute("onclick"); dialogConfirmButton.addEventListener("click", function(){ if (document.getElementById('craft-multiple-item-hidden').value == "rocketFuel") { var amount = document.getElementById('craftMulti-amount-txt').value; var delay = 0; $(this).closest('.ui-dialog-content').dialog('close'); for (var i = 0; i < amount; i++) { setTimeout(function() { craftItem("rocketFuel"); }, delay); delay += 50; } } else { craftMultiple(document.getElementById('craft-multiple-item-hidden').value, document.getElementById('craftMulti-amount-txt').value);$(this).closest('.ui-dialog-content').dialog('close') } }); } } } /* Removes the ability to left click / bind equal or lower tier furnaces and ovens if you have promethium or higher */ function disableBindFurnacesAndOvens() { var furnaceIds = ["item-stoneFurnace-box", "item-bronzeFurnace-box", "item-ironFurnace-box", "item-silverFurnace-box", "item-goldFurnace-box", "item-ancientFurnace-box", "item-promethiumFurnace-box"]; var ovenIds = ["item-bronzeOven-box", "item-ironOven-box", "item-silverOven-box", "item-goldOven-box", "item-promethiumOven-box", "item-ancientOven-box", "item-runiteOven-box"]; if (unsafeWindow.bindedFurnaceLevel >= 7) { // Remove the option to left click / bind prom or lower furnaces if we have a prom or higher furnace bound for (var i = 0; i < furnaceIds.length; i++) { var node = document.getElementById(furnaceIds[i]); if (node) { node.removeAttribute("onclick"); } } } if (unsafeWindow.bindedPromOven == 1 || unsafeWindow.bindedAncientOven == 1 || unsafeWindow.bindedRuniteOven == 1) { var boundOvenLevel = (unsafeWindow.bindedRuniteOven == 1 ? ovenIds.length : (unsafeWindow.bindedAncientOven == 1 ? ovenIds.length - 1 : ovenIds.length - 2)); for (var i = 0; i < boundOvenLevel; i++) { var node = document.getElementById(ovenIds[i]); if (node) { node.removeAttribute("onclick"); } } } } /* --- Timers --- */ /* Format seconds into HH:MM:SS format */ function formatTime(seconds) { var hours = Math.floor(seconds / 3600); var mins = Math.floor((seconds % 3600) / 60); var secs = Math.round(seconds % 60); var hourString = ("00" + hours.toString()).slice(-2) + ":"; var minString = ("00" + mins.toString()).slice(-2) + ":"; var secString = ("00" + secs.toString()).slice(-2); return hourString + minString + secString } /* Roughly calculate the time remaining until the rocket journey completes. Uses averages gathered so it may vary slightly. Rocket info (found from logging) that we need to know Rocket lifts off at a speed of 0-10km/s (averaging 5km/s) until there is about 383401km left in the trip The majority of the trip (~383100-~383125) is traveled at 25-75km/s (averaging 50km/s) At ~275-300km remaining the rocket slows down to 0-10km/s (averagig 5km/s) At ~30km remaining the rocket slows down to a constant 1km/s */ function getRocketTimer() { var totalDistanceRemaining = unsafeWindow.rocketTimer; var liftoffDistanceRemaining = Math.max((totalDistanceRemaining-383401), 0); var mainTravelDistanceRemaining = Math.max((totalDistanceRemaining-liftoffDistanceRemaining-300), 0); var endSlowdownDistanceRemaining = (totalDistanceRemaining > 300) ? 300-30 : Math.max((totalDistanceRemaining-30), 0); var landingDistanceRemaining = (totalDistanceRemaining > 30) ? 30 : totalDistanceRemaining; var averageSecondsRemaining = Math.ceil(liftoffDistanceRemaining/5) + Math.ceil(mainTravelDistanceRemaining/50) + Math.ceil(endSlowdownDistanceRemaining/5) + landingDistanceRemaining; return averageSecondsRemaining; } // Make changes to the document to replace our rocket timer, giving us an estimate time until completion based on average times to travel each kilometer (depending on the distance remaining) function replaceRocketTimer() { document.getElementById("notification-timer-span-rocket").innerHTML = formatTime(getRocketTimer()); } // Calculates the time left until your furnace is done smelting the current load and formats the time into a string function getfurnaceTimer() { var timeRemaining = unsafeWindow.furnaceTotalTimer - unsafeWindow.furnaceCurrentTimer; return timeRemaining; } // Returns an image path based on the "furnaceBarId" variable (item you're currently smelting) function getProductImageByFurnaceId(id) { switch (unsafeWindow.furnaceBarId) { case "1": return "images/minerals/bronzeBar.png" case "2": return "images/minerals/ironBar.png" case "3": return "images/minerals/silverBar.png" case "4": return "images/minerals/goldBar.png" case "5": return "images/minerals/glass.png" case "6": return "images/minerals/promethiumBar.png" default: return "images/crafting/ironfurnace.gif" } } // Make changes to the document to replace our furnace timer with a new image and a time rather than just a percentage function replaceFurnaceTimer() { var furnaceNode = document.getElementById("furnace-timer"); var imageNode = document.getElementById("furnace-timer").childNodes[1]; var newImagePath = getProductImageByFurnaceId(unsafeWindow.furnaceBarId); if (imageNode.getAttribute("src") != newImagePath) { imageNode.setAttribute("src", newImagePath); } document.getElementById("notification-timer-span-furnace").innerHTML = formatTime(getfurnaceTimer()) + " (" + unsafeWindow.furnacePerc + "%)"; } // Loops through all potions which can have a timer (TIMED_POTION_LIST) and returns an array with timer variable name of those which are currently active function getActivePotionTimers() { var activePotions = []; for (var i = 0; i < TIMED_POTION_LIST.length; i++) { var potionTimer = TIMED_POTION_LIST[i] + "Timer"; if (unsafeWindow[potionTimer] > 0) { activePotions.push(TIMED_POTION_LIST[i]); } } return activePotions; } // Replaces the timers for all active potions with new timers formated as HH:MM:SS function replaceActivePotionTimers(activePotions) { for (var i = 0; i < activePotions.length; i++) { var activePotion = activePotions[i]; document.getElementById("notification-timer-span-" + activePotion).innerHTML = formatTime(unsafeWindow[activePotion + "Timer"]); } } // Loops through all spells which can have a timer (TIMED_SPELL_LIST) and returns an array with timer variable name of those which are currently active function getActiveSpellTimers() { var activeSpells = []; for (var i = 0; i < TIMED_SPELL_LIST.length; i++) { var spellTimer = TIMED_SPELL_LIST[i] + "Timer"; if (unsafeWindow[spellTimer] > 0) { activeSpells.push(TIMED_SPELL_LIST[i]); } } return activeSpells; } // Replaces the timers for all active spells with new timers formated as HH:MM:SS function replaceActiveSpellTimers(activeSpells) { for (var i = 0; i < activeSpells.length; i++) { var activeSpell = activeSpells[i]; document.getElementById("notification-timer-span-" + activeSpell).innerHTML = formatTime(unsafeWindow[activeSpell + "Timer"]); } } // Creates two new notification boxes, one for explorer's potions, and one for artifact potions. These are initially hidden until you have one active. function createNotificationBoxes() { // A little bit of voodoo to get the correct element to edit since it has no ID (smitty pls add ID). // This is prone to breaking fairly easily if any center tags are added or removed in the wrong order, but it's not a difficult fix. var mainGameElement = document.getElementById("main-game"); if (mainGameElement) { var centerElements = mainGameElement.getElementsByTagName("center"); if (centerElements && centerElements.length > 1) { var notificationElement = centerElements[1].getElementsByTagName("div"); if (notificationElement && notificationElement.length > 0) { var doc = notificationElement[0]; if (doc.innerHTML.indexOf("<!-- timer box -->")) { // Backslashes allow you to break lines in JS, neat thing to keep in mind doc.innerHTML += '\ <!-- (DH QOL) Explorer\'s Potion Notification -->\ <span class="notification-timer-box-potion" id="explorersPotion-notification-box" style="display: none;">\ <img width="30px" height="40px" style="vertical-align: middle;padding:5px 0px 5px 0px;" src="images/brewing/explorersPotion.png" title="Explorer\'s Potion">\ <span id="notification-span-explorersPotion"></span>\ </span>\ <!-- (DH QOL) End Notification -->\ \ <!-- (DH QOL) Artifact Potion Notification -->\ <span class="notification-timer-box-potion" id="artifactPotion-notification-box" style="display: none;">\ <img width="30px" height="40px" style="vertical-align: middle;padding:5px 0px 5px 0px;" src="images/brewing/artifactPotion.png" title="Artifact Potion">\ <span id="notification-span-artifactPotion"></span>\ </span>\ <!-- (DH QOL) End Notification -->\ '; } } } } } // Show or hide our custom potion notifications depending on whether or not those potions are active function updateCustomPotionNotification() { var explorerNotificationElement = document.getElementById("explorersPotion-notification-box"); var artifactNotificationElement = document.getElementById("artifactPotion-notification-box"); if (explorerNotificationElement) { if (unsafeWindow.explorersPotionOn > 0) { document.getElementById("notification-span-explorersPotion").innerHTML = unsafeWindow.explorersPotionOn + " Active"; explorerNotificationElement.style.display = "inline-block"; } else { explorerNotificationElement.style.display = "none"; } } if (artifactNotificationElement) { if (unsafeWindow.artifactPotionIsActivated > 0) { document.getElementById("notification-span-artifactPotion").innerHTML = unsafeWindow.artifactPotionIsActivated + " Active"; artifactNotificationElement.style.display = "inline-block"; } else { artifactNotificationElement.style.display = "none"; } } } // Get & format our robot timer function getRobotTimer() { var robotTime = unsafeWindow.robotTimer; var mins = Math.floor(robotTime / 60); var secs = Math.round(robotTime % 60); var minString = ("00" + mins.toString()).slice(-2) + ":"; var secString = ("00" + secs.toString()).slice(-2); return minString + secString } // Make changes to the document to replace our robot timer to be formatted in minutes and seconds rather than just seconds function replaceRobotTimer() { document.getElementById("notification-timer-span-robot").innerHTML = "Returns in " + getRobotTimer(); } // Get & format our fishing boat timer function getFishingBoatTimer() { return formatTime(unsafeWindow.fishingBoatTimer); } // Make changes to the document to replace our fishing boat timer to be formatted in HH:MM:SS rather than the default function replaceFishingBoatTimer() { document.getElementById("notification-timer-span-fishingBoat").innerHTML = getFishingBoatTimer(); } // Get & format our exploring timer, takes donor perk into account function getExploringTimer() { var exploringTime = (unsafeWindow.explorerTimer > 0) ? Math.round(unsafeWindow.exploringTimer * 0.8) : unsafeWindow.exploringTimer; return formatTime(exploringTime) } // Make changes to the document to replace our exploring timer to be formatted in HH:MM:SS rather than the default function replaceExploringTimer() { document.getElementById("notification-timer-span-exploring").innerHTML = getExploringTimer(); } // Get & format our magic cooldown timer function getMagicCooldown() { var mcd = unsafeWindow.magicCoolDown; return formatTime(mcd) } // Make changes to the document to replace our magic cooldown timer to be formatted in HH:MM:SS rather than the default function replaceMagicCooldownTimer() { document.getElementById("notification-timer-span-magicCoolDown").innerHTML = getMagicCooldown(); } // Format the ancient crystal timer in HH:MM:SS function replaceAncientCrystalTimer() { if (unsafeWindow.bindedAncientCrystal > 0 && unsafeWindow.ancientCrystalCooldown > 0) { var node = document.getElementById("ancientCrystalChargesSpan"); if (node) { node.innerHTML = formatTime(unsafeWindow.ancientCrystalCooldown); } } } // Replaces diamondhunt's "loadMiscVariabled()" function with a new one that updates our many timers to be more fancy var oldLoadMiscVariables = unsafeWindow.loadMiscVariables; unsafeWindow.loadMiscVariables = function() { oldLoadMiscVariables(); replaceRocketTimer(); replaceFurnaceTimer(); replaceRobotTimer(); replaceFishingBoatTimer(); replaceExploringTimer(); replaceMagicCooldownTimer(); replaceAncientCrystalTimer(); } // Replaces diamondhunt's "loadPotionTimers()" function with a new one that updates our potion timers to be more fancy // Also adds a notification for explorer's potions and artifact potions (#updateCustomePotionNotifications()) var oldLoadPotionTimers = unsafeWindow.loadPotionTimers; unsafeWindow.loadPotionTimers = function() { oldLoadPotionTimers(); replaceActivePotionTimers(getActivePotionTimers()); updateCustomPotionNotification() } /* Replaces Diamond Hunt's #loadNotifSpells function with a new one that formats our magic timers */ var oldLoadNotifSpells = unsafeWindow.loadNotifSpells; unsafeWindow.loadNotifSpells = function() { oldLoadNotifSpells(); replaceActiveSpellTimers(getActiveSpellTimers()); } // Replace the farming timers with new timers formated in HH:MM:SS, also more accurately estimates the time if you have the Gardener donor perk // Farming has slight RNG to it though, so it may not tick correctly every second function replaceFarmingTimers() { if (unsafeWindow.farmingTimer == 1) { var patchIDs = ["1", "2", "3", "4"]; (unsafeWindow.hasFarmingPatch5) ? patchIDs.push("5") : patchIDs; (unsafeWindow.hasFarmingPatch6) ? patchIDs.push("6") : patchIDs; for (var i = 0; i < patchIDs.length; i++) { var matches = document.getElementById("farming-patch-status-"+patchIDs[i]).innerHTML.match(/>(.* min remaining)/); if (matches != null) { var match = matches.pop(); var timeRemaining = unsafeWindow.gardenerTimer == "0" ? unsafeWindow["farmingPatchTimer"+patchIDs[i]] : Math.round(unsafeWindow["farmingPatchTimer"+patchIDs[i]] * 0.8); document.getElementById("farming-patch-status-"+patchIDs[i]).innerHTML = document.getElementById("farming-patch-status-"+patchIDs[i]).innerHTML.replace(match, formatTime(timeRemaining)); } } } } // Replaces diamondhunt's "refreshFarmingPatch()" function with a new one that updates our farming timers to be more fancy var oldRefreshFarmingPatch = unsafeWindow.refreshFarmingPatch; unsafeWindow.refreshFarmingPatch = function(patchId, seedId, seedName, deadCropImg) { oldRefreshFarmingPatch(patchId, seedId, seedName, deadCropImg); replaceFarmingTimers(); } /* --- Other --- */ /* Set and forget, logging that can be enabled or disabled through config */ function cLog(l) { if (config.ENABLE_LOGGING === true) { var date = new Date(); console.log(("00" + date.getHours()).slice(-2) + ":" + ("00" + date.getMinutes()).slice(-2) + ":" + ("00" + date.getSeconds()).slice(-2) + ": " + l); } }
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址