MouseHunt - Event Reward Tracker

Tool that tracks bespoke event rewards (e.g. GWH & Birthday) for analysis

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==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)}`);
            }
          }
        }
      }
    }
  }
})();