[GC] - Underwater Fishing Data Logger

Fishing rewards sorting, logging and Discord Webhooks.

// ==UserScript==
// @name        [GC] - Underwater Fishing Data Logger
// @namespace   Grundo's Cafe
// @match       https://www.grundos.cafe/water/fishing/
// @grant       GM.getValue
// @grant     	GM.setValue
// @grant       GM.addStyle
// @grant       GM.notification
// @grant       GM.xmlHttpRequest
// @version     1.3.6
// @license     MIT
// @author      Cupkait
// @icon        https://i.imgur.com/4Hm2e6z.png
// @description Fishing rewards sorting, logging and Discord Webhooks.
// @require     https://update.greasyfork.org/scripts/489454/1467893/%5BGC%5D%20-%20Underwater%20Fishing%20Prizes%20Library.js
// ==/UserScript==


const userName = /user=(.*?)"/g.exec(document.body.innerHTML)[1];
const buttonFishAll = document.querySelector('input[value="Fish with Everyone!"]');
const buttonFishOne = document.querySelector('input[value="Reel in Your Line"]');
var webHooks = GM.getValue("webHooks", []);

if (window.location.href.endsWith('settings')) {
    document.querySelector('main').remove();
}


async function getCurrentPetLevels() {
    try {
        const response = await fetch("/quickref/");
        if (!response.ok) {
            throw new Error('Network response was not ok');
        }
        const html = await response.text();
        const parser = new DOMParser();
        const doc = parser.parseFromString(html, "text/html");
        const petList = doc.querySelector("#quickref_petlist").children;
        const petLevels = {};

        Array.from(petList).forEach(pet => {
            const petrefName = pet.querySelector("a").getAttribute("href").match(/_name=(.*?)$/)[1];
            const petFishingLevel = pet.querySelectorAll("span")[12].textContent.match(/Fishing : (.*?)$/)[1];
            petLevels[petrefName] = petFishingLevel;
        });

        sessionStorage.setItem('petLevels', JSON.stringify(petLevels));

    } catch (error) {
        console.error("Error fetching data:", error);
        return null;
    }
}

async function collectFishingResults() {
	let displayResults = document.createElement("div");
			displayResults.innerHTML = "Loading results... please wait.";
			displayResults.id = "displayResults";
			displayResults.style.cssText =
  		"color: green; text-align: center; link-color:green; font-size:14px; font-weight:bold;";
			document.querySelector("div#page_content > main").insertAdjacentElement("beforebegin", displayResults);



  const resultsList = document.querySelectorAll('.center-items');
  const resultsDiv = document.querySelector('.flex-column')
		let results = [];
    let highlights = false;
  const goodPrizes = document.createElement('div');


if (resultsList.length > 0) {

	      displayResults.innerHTML = await `Your fishing results have been <a href="https://lookerstudio.google.com/reporting/ec18c798-ee62-4a7d-8315-5569c8de5ef6" target="_blank">submitted</a>.`;
    resultsList.forEach(result => {
        var name = result.querySelector('strong').textContent;
        var item = result.querySelector('p').textContent.match(/ a (.*?)!/)[1];
        var cooldown = result.querySelectorAll('strong')[1].textContent;
        var image = result.querySelectorAll('img')[1].src;
        var newlevel = result.querySelectorAll("p")[1].textContent.includes("fishing level") ? result.querySelectorAll("p")[1].textContent.match(/\d+/)[0] : null;
        var storedLevels = JSON.parse(sessionStorage.getItem('petLevels'));
        var oldlevel = storedLevels && storedLevels[name];

        results.push({ name, item, cooldown, image, oldlevel, newlevel });
								sendResultToDatabase(name, oldlevel, newlevel, item, cooldown);
					const prize = prizes.find(prize => prize.item === item);
    					if (prize) {
                highlights = true
                goodPrizes.append(result);
        						generatePrizeWebhooks(name, oldlevel, item, image);
   							 } else {
									 	console.log("Prize sucked, no webhook sent.")
								 };
		});

  if (highlights === true) {

const fishingstyle = document.createElement('style');
fishingstyle.innerHTML = `
    .center-items img {
        transform: translate3d(0, 0, 0);
        mix-blend-mode: multiply;
    }
`;

//document.head.appendChild(fishingstyle);
goodPrizes.classList.add("center-items");
goodPrizes.style.backgroundColor = "#efef404f";
goodPrizes.style.border = "2px solid black"
resultsDiv.insertAdjacentElement('beforebegin', goodPrizes);
}


} else {
    const resultSingle = document.querySelector('#page_content .center');

    if (resultSingle) {
        var name = document.querySelectorAll('#userinfo a')[2].textContent;
        var item = resultSingle.querySelector("img").alt;
        var cooldown = resultSingle.querySelector('strong').textContent;
        var image = resultSingle.querySelector('img').src;
        var newlevel = resultSingle.querySelectorAll("p")[1].textContent.includes("fishing level") ? resultSingle.querySelectorAll("p")[1].textContent.match(/\d+/)[0] : null;
        var storedLevels = JSON.parse(sessionStorage.getItem('petLevels'));
        var oldlevel = storedLevels && storedLevels[name];

        results.push({ name, item, cooldown, image, oldlevel, newlevel });
								sendResultToDatabase(name, oldlevel, newlevel, item, cooldown);
					const prize = prizes.find(prize => prize.item === item);
    					if (prize) {
									console.log("Valid prize identified.")

        						generatePrizeWebhooks(name, oldlevel, item, image);
   							 } else {
									 	console.log("Prize sucked, no webhook sent.")
								 };

}}

}

// Only collect results if you've actually attempted to fish.
if (document.referrer.endsWith("/water/fishing/") && document.querySelector('main').textContent.includes("You reel in your line and get")) {
collectFishingResults();
		}
//else {createFishHookSettings();}

// Only fetch current fishing levels when you click the button to fish.
if (buttonFishOne || buttonFishAll) {
    document.addEventListener("click", async function(event) {
        if (event.target === buttonFishOne || event.target === buttonFishAll) {
            event.preventDefault();
            await getCurrentPetLevels();
            event.target.form.submit();
        }
    });
}


function getTimestamp() {
    const currentDate = new Date();
    const pstOffset = -8 * 60; // PST offset is UTC-8
    const timestampPST = new Date(currentDate.getTime() + pstOffset * 60 * 1000);
    return timestampPST.toISOString().replace("T", " ").replace("Z", "");
}



async function sendResultToDatabase(name, oldlevel, newlevel, item, cooldown) {
    let opts = {
        mode: "no-cors",
        referrer: "no-referrer",
        headers: {
            "Content-Type": "application/x-www-form-urlencoded",
        },
    };
    const logResponse = `https://docs.google.com/forms/d/e/1FAIpQLSdBhJS1NSxHCmy32BqH0DEdQRJci1IVPOWUFcaothsiZjXu-w/formResponse?usp=pp_url&entry.886049257=${userName}&entry.343654154=${name}&entry.1922136733=${oldlevel}&entry.693447328=${newlevel}&entry.881968876=${item}&entry.303510013=${cooldown}&entry.1110645895=`;
    try {
        await fetch(logResponse, opts);
    } catch (error) {
        // Do nothing
    }
}


async function generatePrizeWebhooks(name, oldlevel, item, image) {
console.log("Webhook generated.")
  const webHooks = await GM.getValue("webHooks", []);

  const hook = {
    content: null,
    embeds: [
      {
        description:
          `${item}?\nWhat a great prize!`,
        color: 9356588,
        author: {
          name: `${userName} took ${name} fishing...`,
        },
        thumbnail: {
          url: `${image}`,
        },
      },
    ],
    username: "Underwater Fishing Prizes",
    avatar_url: "https://i.imgur.com/4Hm2e6z.png",
    attachments: [],
  };

  for (const webhook of webHooks) {
    try {
      await sendMessage(hook, webhook);
      console.log("Message sent successfully.");
    } catch (error) {
      console.error("Error sending message:", error);
    }
  }
}


async function sendMessage(hook, webhook) {
  console.log("Webhook triggered.");

  return new Promise((resolve, reject) => {
    GM.xmlHttpRequest({
      method: "POST",
      url: webhook,
      headers: {
        "Content-Type": "application/json"
      },
      data: JSON.stringify(hook),
      onload: function(response) {
        if (response.status >= 200 && response.status < 300) {
          resolve(response.responseText);
        } else {
          reject(new Error(`Request failed with status ${response.status}`));
        }
      },
      onerror: function(error) {
        reject(error);
      }
    });
  });
}

async function createFishHookSettings() {
const wrapperContainer = document.createElement("div");
wrapperContainer.id = "wrapContainer";
wrapperContainer.style.position = "relative";
wrapperContainer.style.borderBottom = "3px solid";
wrapperContainer.style.padding = "5px 10px 5px 0px";
wrapperContainer.style.height = "30px";
wrapperContainer.style.width = "100%";
wrapperContainer.style.top = "0px";
wrapperContainer.style.left = "0px";
wrapperContainer.style.backgroundColor = "#d2d0cc";
wrapperContainer.style.boxShadow = "5px 0 5px rgba(0, 0, 0, 0.5)";

const wrapperSettings = document.createElement("button");
wrapperSettings.textContent = "Fish Hook Settings";
wrapperSettings.id = "wrapSettings";
wrapperSettings.style.position = "relative";
wrapperSettings.style.fontSize = "12px";
wrapperSettings.style.fontWeight = "bold";
wrapperSettings.style.fontFamily = "courier";
wrapperSettings.style.padding = "0px";
wrapperSettings.style.width = "100%";
wrapperSettings.style.height = "auto";
wrapperSettings.style.border = "1px solid rgb(204, 204, 204)";
wrapperSettings.style.cursor = "pointer";
wrapperSettings.style.backgroundColor = "transparent";

const wrapperMenu = document.createElement("div");
wrapperMenu.id = "wrapMenu";
wrapperMenu.style.display = "none";
wrapperMenu.style.borderRadius = "15px 15px 15px 0px";
wrapperMenu.style.borderBottom = "3px solid";
wrapperMenu.style.position = "absolute";
wrapperMenu.style.width = "200px";
wrapperMenu.style.height = "250px";
wrapperMenu.style.bottom = "0%";
wrapperMenu.style.left = "100%";
wrapperMenu.style.margin = "-3px";
wrapperMenu.style.padding = "10px";
wrapperMenu.style.backgroundColor = "#d2d0cc";
wrapperMenu.style.boxShadow = "5px 0 5px rgba(0, 0, 0, 0.5)";

const firstButton = document.createElement("button");
firstButton.textContent = "Add a Webhook";
firstButton.id = "menubutton";
firstButton.style.height = "35px";
firstButton.style.width = "98%";
firstButton.style.margin = "5px 3px";
firstButton.style.backgroundColor = "transparent";
firstButton.style.borderRadius = "5px";
firstButton.style.boxShadow = "5px 0 5px rgba(0, 0, 0, 0.5)";
firstButton.addEventListener("click", addWebhook);

wrapperMenu.appendChild(firstButton);
wrapperContainer.appendChild(wrapperSettings);
wrapperContainer.appendChild(wrapperMenu);
document.getElementById("sb_edge").appendChild(wrapperContainer);

wrapperSettings.addEventListener("click", () => {
    wrapperMenu.style.display = wrapperMenu.style.display === "none" ? "block" : "none";
});

async function addWebhook() {
  var newWebhook = prompt(
    "Paste one webhook URL to add to your script.\n\nIf you want to add multiple, do one at a time."
  );
  if (newWebhook) {
    let webHooks = await GM.getValue("webHooks", []);
    if (!Array.isArray(webHooks)) {
      webHooks = [];
    }
        webHooks = [...new Set(webHooks)];
        if (!webHooks.includes(newWebhook)) {
      webHooks.push(newWebhook);
      await GM.setValue("webHooks", webHooks);
      alert("Webhook added successfully!");
    } else {
              webHooks = [...new Set(webHooks)];
      await GM.setValue("webHooks", webHooks);

      alert("This webhook URL is already added.");
    }
  }
}


GM.addStyle(`
    #wrapContainer, #wrapSettings, #wrapMenu {
        background-color: #d2d0cc;
    }
    #wrapSettings {
        cursor: pointer;
    }
    #wrapMenu {
        display: none;
        border-radius: 15px 15px 15px 0px;
        border-bottom: 3px solid;
        position: absolute;
        width: 200px;
        height: 250px;
        bottom: 0%;
        left: 100%;
        margin: -3px;
        padding: 10px;
        box-shadow: 5px 0 5px rgba(0, 0, 0, 0.5);
    }
`);

}
createFishHookSettings();




GM.addStyle(`

#hookButton {
				width: 480px;
				height:20px;
				display: flex;
				justify-content: flex-end;
}

`);