MH - Fast RT Tool

Fast RT: Send treasure chests' tradeables in a click!

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         MH - Fast RT Tool
// @description  Fast RT: Send treasure chests' tradeables in a click!
// @author       Snowbits
// @version      1.0.4
// @match        https://www.mousehuntgame.com/*
// @match        https://apps.facebook.com/mousehunt/*
// @resource     https://code.jquery.com/ui/1.11.4/themes/smoothness/jquery-ui.css
// @require      https://code.jquery.com/ui/1.12.1/jquery-ui.js
// @require      https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js
// @icon         https://www.google.com/s2/favicons?domain=mousehuntgame.com
// @namespace https://greasyfork.org/users/1140737
// @license MIT
// ==/UserScript==

// --- CONSTANT DATA ---
const CHESTS = {
  rare_ny: {
    name: "Rare New Year's Party Treasure Chest",
    items: {
      glazed_pecan_pecorino_cheese: 20,
      jingle_bell_stat_item: 10,
      festive_spirit_stat_item: 10,
      golem_magical_hat_stat_item: 1,
    },
  },
  rare_naughty: {
    name: "Rare Naughty Treasure Chest",
    items: {
      glazed_pecan_pecorino_cheese: 15,
      jingle_bell_stat_item: 5,
      festive_spirit_stat_item: 5,
    },
  },
};

const ITEMNAMES = {
    glazed_pecan_pecorino_cheese: "Glazed Pecan Pecorino Cheese",
    jingle_bell_stat_item: "Festive Jingle Bell",
    festive_spirit_stat_item: "Festive Spirit",
    golem_magical_hat_stat_item: "Magical Holiday Hat",
}

$(document).ready(function () {
  addTouchPoint();
});

function addTouchPoint() {
  if ($(".snipeHix").length == 0) {
    const invPages = $(".friends .tournament_scoreboards ");
    const snipeHix = document.createElement("li");
    snipeHix.classList.add("fast_rt_tool");
    const snipeHixBtn = document.createElement("a");
    snipeHixBtn.innerText = "Fast RT Tool";
    snipeHixBtn.onclick = function () {
      render();
    };

    const icon = document.createElement("div");
    icon.className = "icon";
    snipeHixBtn.appendChild(icon);
    snipeHix.appendChild(snipeHixBtn);
    $(snipeHix).insertAfter(invPages);
  }
}

function getFriends() {
  return new Promise((resolve, reject) => {
    postReq(
      "https://www.mousehuntgame.com/managers/ajax/users/userData.php",
      `sn=Hitgrab&hg_is_ajax=1&uh=${user.unique_hash}&get_friends=true`
    ).then((res) => {
      try {
        var data = JSON.parse(res.responseText);
        var friendNameList = [];
        if (data.user_data) {
          var friendSnuidList = Object.keys(data.user_data);
          for (var i = 0; i < friendSnuidList.length; i++) {
            friendNameList.push(data.user_data[friendSnuidList[i]].name);
          }
          resolve([friendSnuidList, friendNameList]);
        }
      } catch (error) {
        alert(`Error getting friends! I know it's hard to get friends nowadays`);
        console.log(error);
      }
    });
  });
}

async function render() {
  document.querySelectorAll("#map-tool-box").forEach((el) => el.remove());

  const div = document.createElement("div");
  div.id = "map-tool-box";
  div.style.backgroundColor = "#F5F5F5";
  div.style.position = "fixed";
  div.style.zIndex = "9999";
  div.style.left = "35vw";
  div.style.top = "28vh";
  div.style.border = "solid 3px #696969";
  div.style.borderRadius = "20px";
  div.style.padding = "10px";
  div.style.textAlign = "center";
  div.style.fontSize = "12px";

  const closeButton = document.createElement("button", {
    id: "close-button",
  });
  closeButton.textContent = "x";
  closeButton.style.marginLeft = "5px";
  closeButton.style.cursor = "pointer";
  closeButton.onclick = function () {
    document.body.removeChild(div);
  };

  const minButton = document.createElement("button", {
    id: "minimise-button",
  });
  minButton.textContent = "-";
  minButton.style.marginLeft = "57px";
  minButton.style.cursor = "pointer";
  minButton.onclick = function () {
    if (minButton.textContent == "-") {
      $("#maptain-tool-content")[0].style.display = "none";
      $(".maptain-tool-info")[0].style.marginLeft = "0px";
      minButton.textContent = "+";
      minButton.style.marginLeft = "5px";
    } else if (minButton.textContent == "+") {
      $("#maptain-tool-content")[0].style.display = "";
      $(".maptain-tool-info")[0].style.marginLeft = "17px";
      minButton.textContent = "-";
      minButton.style.marginLeft = "57px";
    }
  };

  const toolInfo = document.createElement("div");
  toolInfo.className = "maptain-tool-info";
  toolInfo.textContent = "Fast RT Tool";
  toolInfo.style.height = "21px";
  toolInfo.style.textAlign = "Left";
  toolInfo.style.marginLeft = "17px";
  toolInfo.style.fontWeight = "bold";
  toolInfo.style.cursor = "context-menu";

  const contentDiv = document.createElement("div");
  contentDiv.id = "maptain-tool-content";

  const table = document.createElement("table");
  table.id = "maptain-tool-table";
  table.style.textAlign = "left";
  table.style.borderSpacing = "1em 0";

  //hid-------------------------------------------
  const hid_row = document.createElement("tr");
  const hid_td1 = document.createElement("td");
  const hid_td2 = document.createElement("td");

  const hid_radio = document.createElement("input");
  hid_radio.type = "radio";
  hid_radio.name = "chro-hunter-friend";
  hid_radio.id = "chro-hunter-radio";
  hid_radio.style.verticalAlign = "middle";
  hid_radio.style.marginTop = "-2px";
  hid_radio.checked = true;
  hid_radio.onchange = function () {
    processRadio();
  };
  hid_td1.appendChild(hid_radio);

  const hid_label = document.createElement("label");
  hid_label.innerText = "Hunter ID: ";
  hid_label.htmlFor = "chro-radio-hid";
  hid_td1.appendChild(hid_label);

  const hid_input = document.createElement("input");
  hid_input.type = "text";
  hid_input.id = "hunter-input-id";
  hid_input.size = "10";
  hid_input.style.placeHolder = "1";
  hid_td2.appendChild(hid_input);

  //friends---------------------------------------
  const friend_row = document.createElement("tr");
  const friend_td1 = document.createElement("td");
  friend_td1.style.textAlign = "right";
  const friend_td2 = document.createElement("td");

  const friend_radio = document.createElement("input");
  friend_radio.type = "radio";
  friend_radio.name = "chro-hunter-friend";
  friend_radio.id = "chro-friend-radio";
  friend_radio.style.verticalAlign = "middle";
  friend_radio.style.marginTop = "-2px";
  friend_radio.style.position = "relative";
  friend_radio.style.right = "17.5px";
  friend_radio.onchange = function () {
    processRadio();
  };
  friend_td1.appendChild(friend_radio);

  const friend_label = document.createElement("label");
  friend_label.innerHTML = "Friend:";
  friend_label.htmlFor = "chro-radio-friend";
  friend_td1.appendChild(friend_label);

  const friend_input = document.createElement("input");
  friend_input.type = "text";
  friend_input.id = "hunter-input-id";
  friend_input.disabled = true;
  friend_input.size = "10";

  friend_td2.appendChild(friend_input);

  //Either friend or hunter-------------------------------------
  var friendList;
  var friendSnuid;
  var snuid;
  var name;
  async function processRadio() {
    if (hid_radio.checked) {
      hid_input.disabled = false;
      friend_input.disabled = true;
      friend_input.value = "";
    } else if (friend_radio.checked) {
      hid_input.disabled = true;
      friend_input.disabled = false;
      hid_input.value = "";
      var list = await getFriends();
      friendSnuid = list[0];
      friendList = list[1];
      $("#hunter-input-id").autocomplete({
        source: list[1],
        open: function () {
          setTimeout(function () {
            $(".ui-autocomplete").css("z-index", 99999999999999);
          }, 0);
        },
      });
    }
  }

  //map--------------------------------------
  const map_row = document.createElement("tr");
  const map_td1 = document.createElement("td");
  map_td1.style.textAlign = "right";
  const map_td2 = document.createElement("td");

  const map_label = document.createElement("label");
  map_label.innerText = "Map: ";
  map_td1.appendChild(map_label);

  const map_select = document.createElement("select", {
    id: "map_select",
  });
  map_select.style.width = "103px";
  const entries = Object.entries(CHESTS);
  if (entries.length === 0) {
    const option = document.createElement("option");
    option.textContent = "None";
    option.disabled = true;
    option.selected = true;
    map_select.appendChild(option);
  } else {
    for (const [id, chest] of entries) {
      const option = document.createElement("option");
      option.value = id; // IMPORTANT
      option.textContent = chest.name;
      map_select.appendChild(option);
    }
  }

  map_td2.appendChild(map_select);

  //append-------------------------------------------
  hid_row.appendChild(hid_td1);
  hid_row.appendChild(hid_td2);
  friend_row.appendChild(friend_td1);
  friend_row.appendChild(friend_td2);
  map_row.appendChild(map_td1);
  map_row.appendChild(map_td2);
  table.appendChild(map_row);
  table.appendChild(friend_row);
  table.appendChild(hid_row);

  //Buttons---------------------------------------------
  const actionDiv = document.createElement("div");
  actionDiv.className = "action-div";

  const sendBtn = document.createElement("button", {
    id: "send-button",
  });
  sendBtn.textContent = "Send Tradeables";
  sendBtn.style.cursor = "pointer";
  sendBtn.onclick = async function () {
    var snuidAndName = await getSnuidAndName(
        hid_input.value,
        friend_input.value,
        friendList,
        friendSnuid
    );
    const snuid = snuidAndName[0];
    const name = snuidAndName[1];
    const selectedChest = CHESTS[map_select.value];
    await getConfirmation("sendItems", snuid, name, selectedChest.name);
      const sentItems = [];
    for (const [itemType, quantity] of Object.entries(selectedChest.items)) {
        const success = await sendItems(snuid, itemType, quantity);
        if (success) {
           sentItems.push(`${quantity} ${ITEMNAMES[itemType] ?? itemType}`);
        }
    }
    if (sentItems.length > 0) {
    alert("Successfully sent:\n" + sentItems.join("\n"));
    }
};
  actionDiv.appendChild(sendBtn);

  toolInfo.appendChild(minButton);
  toolInfo.appendChild(closeButton);
  div.appendChild(toolInfo);
  contentDiv.appendChild(document.createElement("br"));
  contentDiv.appendChild(table);
  contentDiv.appendChild(document.createElement("br"));
  contentDiv.appendChild(actionDiv);
  div.appendChild(contentDiv);
  document.body.appendChild(div);
  dragElement(div);
}

async function getSnuidAndName(
  hid_input,
  friend_input,
  friendList,
  friendSnuid
) {
  return new Promise(async (resolve, reject) => {
    if (hid_input) {
      var snuidAndName = await getSnuidId(hid_input);
      var snuid = snuidAndName[0];
      var name = snuidAndName[1];
    } else if (friend_input) {
      var friend_index = friendList.indexOf(friend_input);
      snuid = friendSnuid[friend_index];
      name = friend_input;
    }
    resolve([snuid, name]);
  });
}

function getSnuidId(hid) {
  return new Promise((resolve, reject) => {
    if (hid != user.user_id && hid.length > 0) {
      postReq(
        "https://www.mousehuntgame.com/managers/ajax/pages/friends.php",
        `sn=Hitgrab&hg_is_ajax=1&action=community_search_by_id&user_id=${hid}&uh=${user.unique_hash}`
      ).then((res) => {
        try {
          var data = JSON.parse(res.responseText);
          if (data) {
            var hunter_name = data.friend.name;
            var hunter_snuid = data.friend.sn_user_id;
            resolve([hunter_snuid, hunter_name]);
          }
        } catch (error) {
          console.log(error);
        }
      });
    }
  });
}

function getConfirmation(action, snuid, name, input_value) {
  return new Promise((resolve, reject) => {
    document
      .querySelectorAll("#confirm-action-box")
      .forEach((el) => el.remove());

    const actdiv = document.createElement("div");
    actdiv.id = "confirm-action-box";
    actdiv.style.backgroundColor = "#F5F5F5";
    actdiv.style.position = "fixed";
    actdiv.style.zIndex = "9999";
    actdiv.style.left = "28vw";
    actdiv.style.top = "28vh";
    actdiv.style.border = "solid 3px #696969";
    actdiv.style.borderRadius = "20px";
    actdiv.style.padding = "10px";
    actdiv.style.textAlign = "center";

    const cfmdiv = document.createElement("div");
    cfmdiv.id = "map-confirm-header";
    cfmdiv.style.fontSize = "12px";

    if (action == "sendItems") {
      cfmdiv.innerText = "Send ".concat(
        input_value,
        "'s tradeables to ",
        name,
        "?"
      );
    }
    //Buttons -----------
    const doBtnDiv = document.createElement("div");

    const cfmBtn = document.createElement("button", {
      id: "cfm-button",
    });
    cfmBtn.style.cursor = "pointer";
    cfmBtn.innerText = "Confirm";
    cfmBtn.onclick = function () {
      document.body.removeChild(actdiv);
      resolve();
    };

    const noBtn = document.createElement("button", {
      id: "no-button",
    });
    noBtn.innerText = "Cancel";
    noBtn.style.marginLeft = "5px";
    noBtn.style.cursor = "pointer";
    noBtn.onclick = function () {
      document.body.removeChild(actdiv);
      reject(this);
    };
    doBtnDiv.appendChild(cfmBtn);
    doBtnDiv.appendChild(noBtn);
    actdiv.appendChild(cfmdiv);
    actdiv.appendChild(document.createElement("br"));
    actdiv.appendChild(doBtnDiv);
    document.body.appendChild(actdiv);
    dragElement(actdiv);
  });
}

async function sendItems(snuid, itemType, quantity) {
    try {
        const res = await postReq(
            "https://www.mousehuntgame.com/managers/ajax/users/supplytransfer.php",
            `sn=Hitgrab&hg_is_ajax=1&receiver=${snuid}&uh=${user.unique_hash}&item=${itemType}&item_quantity=${quantity}`
        );
        const data = JSON.parse(res.responseText);
        if (data.success == "1") {
            return true;
        } else {
            alert(`Failed to send ${quantity} ${ITEMNAMES[itemType] ?? itemType}`);
            return false;
        }
    } catch (error) {
        alert("Ajax Request Failed!");
        console.log(error);
        return false;
    }
}

function dragElement(elmnt) {
  var pos1 = 0,
    pos2 = 0,
    pos3 = 0,
    pos4 = 0;
  if (elmnt.firstElementChild) {
    // if present, the header is where you move the DIV from:
    elmnt.firstElementChild.onmousedown = dragMouseDown;
  } else {
    // otherwise, move the DIV from anywhere inside the DIV:
    elmnt.onmousedown = dragMouseDown;
  }

  function dragMouseDown(e) {
    e = e || window.event;
    e.preventDefault();
    // get the mouse cursor position at startup:
    pos3 = e.clientX;
    pos4 = e.clientY;
    document.onmouseup = closeDragElement;
    // call a function whenever the cursor moves:
    document.onmousemove = elementDrag;
  }

  function elementDrag(e) {
    e = e || window.event;
    e.preventDefault();
    // calculate the new cursor position:
    pos1 = pos3 - e.clientX;
    pos2 = pos4 - e.clientY;
    pos3 = e.clientX;
    pos4 = e.clientY;
    // set the element's new position:
    elmnt.style.top = elmnt.offsetTop - pos2 + "px";
    elmnt.style.left = elmnt.offsetLeft - pos1 + "px";
  }

  function closeDragElement() {
    // stop moving when mouse button is released:
    document.onmouseup = null;
    document.onmousemove = null;
  }
}

function postReq(url, form) {
  return new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest();
    xhr.open("POST", url, true);
    xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
    xhr.onreadystatechange = function () {
      if (this.readyState === XMLHttpRequest.DONE && this.status === 200) {
        resolve(this);
      }
    };
    xhr.onerror = function () {
      reject(this);
    };
    xhr.send(form);
  });
}