您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Adds helpful timers and links to the GC sidebar.
// ==UserScript== // @name GC Helper // @description Adds helpful timers and links to the GC sidebar. // @version 0.3 // @author ben (mushroom), dani // @match https://grundos.cafe/* // @match https://www.grundos.cafe/* // @icon https://www.google.com/s2/favicons?domain=grundos.cafe // @namespace https://greasyfork.org/users/727556 // ==/UserScript== var storage; var fishingStorage; localStorage.getItem("trainingtrackerGC==") != null ? storage = JSON.parse(localStorage.getItem("trainingtrackerGC==")) : storage = {display: true, students: {}}; localStorage.getItem("fishingtrackerGC==") != null ? fishingStorage = JSON.parse(localStorage.getItem("fishingtrackerGC==")) : fishingStorage = {display: true, pets: {}}; var currentPage = window.location.href; var pageHTML = document.body.innerHTML; var documentText = document.body.innerText; var content = document.getElementsByClassName("content")[0]; var currentDate = new Date(); var usingDefaultTheme = document.body.innerHTML.indexOf('04/top_bar_anim') < 0 ? true : false; function trainingList() { // Loop through students in storage var newDate = new Date(); for (var student in storage.students) { // get name, parse date, update content of let studentName = student; var endDate = new Date(storage.students[student].timeToCompletion); var timeString; if (!isNaN(Date.parse(endDate))) // check if valid date { var hoursLeft = Math.floor((endDate - newDate)/(1000*60*60)); var minutesLeft = Math.floor(((endDate - newDate) % (1000*60*60)) / (1000*60)); var secondsLeft = Math.floor(((endDate - newDate) % (1000*60)) / (1000)); if (secondsLeft >= 0) { timeString = hoursLeft + "h " + minutesLeft + "m"; } else { timeString = "Course Finished!"; } } else { timeString = storage.students[student].timeToCompletion; } document.getElementById(studentName + "_ttc").innerText = timeString; } for (var fishingPet in fishingStorage.pets) { var endDateFish = new Date(fishingStorage.pets[fishingPet][0]) var timeStringFish; if (!isNaN(Date.parse(endDateFish))) // check if valid date { var hoursLeftFish = Math.floor((endDateFish - newDate)/(1000*60*60)); var minutesLeftFish = Math.floor(((endDateFish - newDate) % (1000*60*60)) / (1000*60)); var secondsLeftFish = Math.floor(((endDateFish - newDate) % (1000*60)) / (1000)); if (secondsLeftFish >= 0) { timeStringFish = hoursLeftFish + "h " + minutesLeftFish + "m"; } else { timeStringFish = "🐟"; } } document.getElementById(fishingPet + "_ttf").innerText = timeStringFish; } } (function() { 'use strict'; // When on training status page, parse HTML & store in local storage if (currentPage == "https://www.grundos.cafe/island/training/?type=status"){ var petTables = content.getElementsByTagName("table")[0].getElementsByTagName("tbody")[0].getElementsByTagName("tr"); var numOfPets = petTables.length / 2; var petNames = []; // Loop through pets for (var i = 0; i < numOfPets; i++) { var courseInfo = petTables[2*i].getElementsByTagName("td")[0].getElementsByTagName("b")[0].innerText.split(" "); petNames.push(courseInfo[0]); // if last element of courseInfo == course, remove petname from students if (courseInfo[courseInfo.length - 1] == "course") { delete storage.students[courseInfo[0]]; } // else, add student record (pet name, course, time to completion) else { var timeToCompletion = petTables[2*i + 1].getElementsByTagName("td")[1].getElementsByTagName("b")[0].innerText; if (timeToCompletion.indexOf("Codestone") > 0) { storage.students[courseInfo[0]] = {currentCourse: courseInfo[courseInfo.length - 1], timeToCompletion: timeToCompletion + " required"}; } else if (timeToCompletion == "Course Finished!") { storage.students[courseInfo[0]] = {currentCourse: courseInfo[courseInfo.length - 1], timeToCompletion: timeToCompletion}; } else { var re = /\d+/g; var timeParts = [...timeToCompletion.matchAll(re)]; var endTime = new Date(currentDate.getTime() + timeParts[0]*60*60*1000 + timeParts[1]*60*1000 + timeParts[2]*1000) storage.students[courseInfo[0]] = {currentCourse: courseInfo[courseInfo.length - 1], timeToCompletion: endTime}; } } } for (var s in storage.students) { var matchExists = 0; for (var j = 0; j < petNames.length; j++) { if (petNames[j] == s) { matchExists = 1; } } if (matchExists == 0) { delete storage.students[s]; } } localStorage.setItem("trainingtrackerGC==", JSON.stringify(storage)); } // When on fishing success page, parse HTML & store in local storage if (currentPage == "https://www.grundos.cafe/water/fishing/" && document.body.innerText.lastIndexOf('might be able to') > 0) { var activePet = usingDefaultTheme ? documentText.substring(documentText.lastIndexOf(' | Pet : ') + 9, documentText.lastIndexOf(' | NP : ')) : documentText.substring(documentText.lastIndexOf('\npet : ') + 7, documentText.lastIndexOf('\nNP : ')) var hoursUntilNext = documentText.substring(documentText.lastIndexOf('might be able to cast again in about ') + 37, documentText.lastIndexOf('might be able to cast again in about ') + 38) var currentLevel = activePet in fishingStorage.pets ? fishingStorage.pets[activePet][1] : "TBD" if (documentText.lastIndexOf("fishing level increases to") > 0){ const levRegex = /fishing level increases to (.*?)!/g; var levResult = levRegex.exec(documentText); currentLevel = levResult[1]; } fishingStorage.pets[activePet] = [ new Date(currentDate.getTime() + 60*60*1000*parseInt(hoursUntilNext)) , currentLevel ] localStorage.setItem("fishingtrackerGC==", JSON.stringify(fishingStorage)); } // When on any page with Neopets sidebar, add training module if (document.getElementsByName("a").length > 0) { var trainingModule = `<div class="tt" style="position:absolute; left:950px; top:240px; width: 140px;"><a href="https://www.grundos.cafe/island/training/?type=status" style='color:#FF8CA1'>Training:</a><br>` var studentCount = 0; // Loop through students in storage for (var student in storage.students) { let studentName = student; let currentCourse = storage.students[student].currentCourse; var endDate = new Date(storage.students[student].timeToCompletion); var timeString; if (!isNaN(Date.parse(endDate))) // check if valid date { if (Date.parse(endDate) > currentDate) { var hoursLeft = Math.floor((endDate - currentDate)/(1000*60*60)); var minutesLeft = Math.floor(((endDate - currentDate) % (1000*60*60)) / (1000*60)); var secondsLeft = Math.floor(((endDate - currentDate) % (1000*60)) / (1000)); timeString = hoursLeft + "h " + minutesLeft + "m"; } else { timeString = "Course Finished!"; } } else { timeString = storage.students[student].timeToCompletion; } var trainingModulePart = `<li>${student} (<i>${currentCourse}</i>): <span id="${student}_ttc">${timeString}</span></li>` trainingModule = trainingModule + trainingModulePart; studentCount++; } if (studentCount == 0) { trainingModule = trainingModule + `<br>No pets in courses - go get started!`; } // Adding fishing var fishingDisplay = fishingStorage.display ? "inline" : "none"; var fishingDisplayArrow = fishingStorage.display ? "↑" : "↓"; trainingModule = trainingModule + `<br><a href="/water/fishing/" style='color:#FF8CA1'>Fishing:</a> <span style="font-size: 16px; cursor: pointer;" id="TBD">` + fishingDisplayArrow + `</span><div style="display:` + fishingDisplay + `;" id="fishingModulePets">` var fishingPetsArray = []; for (var pet in fishingStorage.pets) { if (!Array.isArray(fishingStorage.pets[pet])){ var ph = fishingStorage.pets[pet] fishingStorage.pets[pet] = [ ph, "TBD" ] } fishingPetsArray.push([ pet, fishingStorage.pets[pet][0] ]); } fishingPetsArray.sort((a, b) => new Date(b[1]) - new Date(a[1])); for (var p = 0; p < fishingPetsArray.length; p++) { let petName = fishingPetsArray[p][0]; let petLevel = fishingStorage.pets[petName][1]; let nextFishingTime = new Date(fishingStorage.pets[petName][0]); var timeStringF; if (!isNaN(Date.parse(nextFishingTime))) // check if valid date { if (Date.parse(nextFishingTime) > currentDate) { var hoursLeftF = Math.floor((nextFishingTime - currentDate)/(1000*60*60)); var minutesLeftF = Math.floor(((nextFishingTime - currentDate) % (1000*60*60)) / (1000*60)); var secondsLeftF = Math.floor(((nextFishingTime - currentDate) % (1000*60)) / (1000)); timeStringF = hoursLeftF + "h " + minutesLeftF + "m"; } else { timeStringF = "🐟"; } } var fishingModulePart = `<li><a style="font-size: 10px; color:#FF8CA1" href="/setactivepet/?pet_name=${petName}">${petName}</a> (${petLevel}): <span id="${petName}_ttf">${timeStringF}</span></li>` trainingModule = trainingModule + fishingModulePart; } trainingModule = trainingModule + `</div>`; document.getElementsByTagName("body")[0].insertAdjacentHTML("afterbegin", trainingModule) // Add on-click event document.getElementById("TBD").onclick = function() { fishingStorage.display = !fishingStorage.display; if (fishingStorage.display){ console.log("hi") document.getElementById("TBD").innerText = "↑"; document.getElementById("fishingModulePets").style.display = "inline"; } else{ console.log("bye") document.getElementById("TBD").innerText = "↓"; document.getElementById("fishingModulePets").style.display = "none"; } localStorage.setItem("fishingtrackerGC==", JSON.stringify(fishingStorage)); }; setInterval(trainingList, 1000); } // When on quickref, remove abandoned fishers const getPetNames = () => Array.from(document.querySelectorAll(`.quickref_content a[href*="/setactivepet/?pet_name="]`)) .map(e => e.nextElementSibling.textContent) function pruneFishers() { const key = "fishingtrackerGC==" const actualPets = getPetNames() // Load saved fishing data let data = localStorage.getItem(key) if (!data) return // never visited the fishing module data = JSON.parse(data) // Remove pets that are no longer on the account const currentEntries = Object.entries(data.pets) const prunedEntries = currentEntries.filter(([fisher, _]) => actualPets.includes(fisher)) // Quit if nothing changed const changed = prunedEntries.length < currentEntries.length if (!changed) return // Update and save data.pets = Object.fromEntries(prunedEntries) localStorage.setItem(key, JSON.stringify(data)) } if (currentPage == "https://www.grundos.cafe/quickref/") { pruneFishers() } })();