您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Tool that tracks bespoke event rewards (e.g. GWH & Birthday) for analysis
// ==UserScript== // @name MouseHunt - Event Reward Tracker // @author Tran Situ (tsitu) // @namespace https://greasyfork.org/en/users/232363-tsitu // @version 2.0 // @description Tool that tracks bespoke event rewards (e.g. GWH & Birthday) for analysis // @match http://www.mousehuntgame.com/* // @match https://www.mousehuntgame.com/* // ==/UserScript== (function() { // Generate toast popup UI const toast = document.createElement("fieldset"); toast.style.visibility = "hidden"; toast.style.position = "fixed"; toast.style.left = "40px"; toast.style.top = "40px"; toast.style.width = "250px"; toast.style.height = "auto"; toast.style.textAlign = "center"; toast.style.fontSize = "16px"; toast.style.border = "2px dotted black"; const toastLegend = document.createElement("legend"); toastLegend.style.fontSize = "10px"; toastLegend.innerText = "MH Event Reward Tracker v2.0"; const toastText = document.createElement("span"); toast.appendChild(toastText); toast.appendChild(toastLegend); document.body.appendChild(toast); /** * Shows custom toast message * @param {string} message Message to be displayed inside <span> * @param {string} color Background color of the toast <fieldset> element */ function generateToast(message, color) { toastText.innerText = message; toast.style.backgroundColor = color; toast.style.visibility = "visible"; setTimeout(() => { toast.style.visibility = "hidden"; }, 3000); } const originalOpen = XMLHttpRequest.prototype.open; XMLHttpRequest.prototype.open = function() { this.addEventListener("load", function() { if ( this.responseURL.indexOf( "mousehuntgame.com/managers/ajax/events/birthday_factory.php" ) >= 0 && Date.now() < 1585699200000 // (GMT): Wednesday, April 1, 2020 12:00:00 AM [Birthday 2020 definitive end] ) { try { const data = JSON.parse(this.responseText); if (data) bday2020Parse(data); } catch (error) { console.log("Failed to parse server response for Birthday 2020"); console.error(error.stack); } } }); this.addEventListener("load", function() { if ( this.responseURL.indexOf( "mousehuntgame.com/managers/ajax/events/winter_hunt.php" ) >= 0 && Date.now() < 1579219200000 // (GMT): Friday, January 17, 2020 12:00:00 AM [GWH 2019 definitive end] ) { try { const data = JSON.parse(this.responseText); if (data) gwh2019Parse(data); } catch (error) { console.log("Failed to parse server response for GWH 2019"); console.error(error.stack); } } }); originalOpen.apply(this, arguments); }; /** * Parses Birthday 2020 server response and fires POST to Aard's endpoint * @param {object} data JSON-parsed server response */ function bday2020Parse(data) { const msgs = data.messageData.message_model.messages; if (msgs && msgs.length > 0) { for (let msg of msgs) { const content = msg.messageData.content; if (content) { const title = content.title; if (title.indexOf("claimed my cheese crate") >= 0) { // Initialize data object const userID = data.user.user_id; const obj = {}; obj[userID] = []; // Initialize DOM parsing of HTML string const body = content.body; const dom = new DOMParser(); const doc = dom.parseFromString(body, "text/html"); doc .querySelectorAll(".birthday2020ClaimReward-item-details") .forEach(el => { const item = el.textContent; if ( item.indexOf("to unlock") >= 0 || item.indexOf("brie+") >= 0 ) { // Skip locked boxes as well as [Emp] SB+ } else { obj[userID].push(item); } }); if (obj[userID].length > 0) { // Send payload to Google Forms const xhr = new XMLHttpRequest(); xhr.open( "POST", "https://script.google.com/macros/s/AKfycbwdxCoJwShmV5CcUBWpq_8Y6joww29cdIZrH2XO/exec" ); xhr.setRequestHeader( "content-type", "application/x-www-form-urlencoded" ); xhr.onload = function() { const response = xhr.responseText; if ( response.indexOf("success") >= 0 && response.indexOf("Thanks") >= 0 ) { generateToast( `${Date( Date.now() ).toLocaleString()}\n\nBirthday 2020 crate data submitted successfully!`, "#caf4ae" ); } }; xhr.onerror = function() { console.error(xhr.statusText); generateToast( `${Date( Date.now() ).toLocaleString()}\n\nBirthday 2020 crate data submission failed`, "#ffc0cb" ); }; xhr.send(`crateCheese=${JSON.stringify(obj)}`); } } } } } } /** * Parses GWH 2019 server response and fires POST to Aard's endpoint * @param {object} data JSON-parsed server response */ function gwh2019Parse(data) { const msgs = data.messageData.message_model.messages; if (msgs && msgs.length > 0) { for (let msg of msgs) { const content = msg.messageData.content; if (content) { const title = content.title; if (title.indexOf("Snow Golem reward") >= 0) { // Parse location name let locationName = "N/A"; if (title.indexOf(" from the ") >= 0) { locationName = title.split("from the ")[1].split("!")[0]; } else { locationName = title.split("from ")[1].split("!")[0]; } // Miscellaneous tidbits // const level = title // .split("claimed a Level ")[1] // .split(" Snow Golem")[0]; // parseInt(level); // const journalID = // msg.messageData.stream_publish_data.params.journal_id; // msg.messageDate; // Initialize data object const userID = msg.messageData.stream_publish_data.params.user_id; const obj = {}; obj[userID] = {}; obj[userID][locationName] = {}; // Initialize DOM parsing of HTML string const body = content.body; const dom = new DOMParser(); const doc = dom.parseFromString(body, "text/html"); const itemDiv = doc.querySelector( ".winterHunt2019-claimRewardPopup-content" ); itemDiv .querySelectorAll(".winterHunt2019-claimRewardPopup-item") .forEach(el => { const rarityEl = el.querySelector( ".winterHunt2019-claimRewardPopup-item-rarity" ); const qtyEl = el.querySelector(".quantity"); const itemEl = el.querySelector( ".winterHunt2019-claimRewardPopup-item-name" ); if (rarityEl && qtyEl && itemEl) { let rarity = rarityEl.textContent; rarity = rarity == "Magical Hat" ? "Hat" : rarity; rarity = rarity.charAt(0).toUpperCase() + rarity.slice(1); const quantity = parseInt( qtyEl.textContent.replace(/,/g, "") // Trim commas (e.g. for gold qty) ); let item = itemEl.textContent; // Item name edge cases if (item.indexOf("SUPER|brie") >= 0) item = "SUPER|brie+"; // Fixed qty rolls -> Avg/Raw = % Chance // e.g. total qty of 20 in 40 rolls -> 20/40 = 0.5 avg -> 0.5 / 5 per roll = 10% chance if (obj[userID][locationName][rarity] === undefined) { obj[userID][locationName][rarity] = {}; obj[userID][locationName][rarity].count = 1; obj[userID][locationName][rarity][item] = quantity; } else { if (obj[userID][locationName][rarity][item] === undefined) { obj[userID][locationName][rarity][item] = quantity; } else { obj[userID][locationName][rarity][item] += quantity; } obj[userID][locationName][rarity].count += 1; } } }); if (Object.keys(obj).length > 0) { // Send payload to Google Forms const xhr = new XMLHttpRequest(); xhr.open( "POST", // "https://cors-anywhere.herokuapp.com/", "https://script.google.com/macros/s/AKfycbz1JH5rngtwVGoMx_5550TALj4WFdoTxqYbCQjFS-pIGToIA6Q/exec" ); xhr.setRequestHeader( "content-type", "application/x-www-form-urlencoded" ); xhr.onload = function() { const response = xhr.responseText; if ( response.indexOf("success") >= 0 && response.indexOf("Thanks") >= 0 ) { generateToast( `${Date( Date.now() ).toLocaleString()}\n\n[${locationName}]\n\nGolem data submitted successfully!`, "#caf4ae" ); } }; xhr.onerror = function() { console.error(xhr.statusText); generateToast( `${Date( Date.now() ).toLocaleString()}\n\nGolem data submission failed`, "#ffc0cb" ); }; xhr.send(`golemString=${JSON.stringify(obj)}`); } } } } } } })();