GC - Quest Reward Stat Collector

Collects reward and the items requested from quests to put into a shared spreadsheet for stats. https://docs.google.com/spreadsheets/d/17lADRhz5POPYPxKRa6ZVYlJuKuYxxev0_bxnFPbK0Bk/edit?gid=1499161972#gid=1499161972

  1. // ==UserScript==
  2. // @name GC - Quest Reward Stat Collector
  3. // @namespace https://greasyfork.org/en/users/1202961-twiggies
  4. // @version 1.92
  5. // @description Collects reward and the items requested from quests to put into a shared spreadsheet for stats. https://docs.google.com/spreadsheets/d/17lADRhz5POPYPxKRa6ZVYlJuKuYxxev0_bxnFPbK0Bk/edit?gid=1499161972#gid=1499161972
  6. // @author Twiggies
  7. // @match https://www.grundos.cafe/winter/snowfaerie/complete/
  8. // @match https://www.grundos.cafe/halloween/witchtower/complete/
  9. // @match https://www.grundos.cafe/halloween/esophagor/complete/
  10. // @match https://www.grundos.cafe/island/kitchen/complete/
  11. // @match https://www.grundos.cafe/halloween/braintree/complete/
  12. // @icon https://www.google.com/s2/favicons?sz=64&domain=grundos.cafe
  13. // @grant GM_registerMenuCommand
  14. // @grant GM_setValue
  15. // @grant GM_getValue
  16. // @license MIT
  17. // ==/UserScript==
  18.  
  19. let giveUsername = GM_getValue("sendUsername");
  20.  
  21. //This function asks if you want your username to be sent or not.
  22. function usernamePrompt() {
  23. if (window.confirm("Would you like to send your username with your quest data? Press OK for Yes, or cancel for No.\nThis can be prompted again in the script extension's menu.")) {
  24. GM_setValue("sendUsername",true);
  25. giveUsername = true;
  26. }
  27. else {
  28. GM_setValue("sendUsername",false);
  29. giveUsername = false;
  30. }
  31. }
  32.  
  33. //Adds the prompt for asking about username sending to the menu.
  34. const usernameMenuCommand = GM_registerMenuCommand("Set Send Username", function() {
  35. usernamePrompt();
  36. }, "u");
  37.  
  38.  
  39. if (giveUsername == undefined) {
  40. usernamePrompt();
  41. }
  42.  
  43. let formLink = '';
  44. let returnButton = null;
  45. let submitDiv = document.createElement("div");
  46. submitDiv.innerHTML = "Submitting Quest Data...";
  47. submitDiv.id = "submitDiv";
  48.  
  49. function taeliaRewards() {
  50. let rewardSpan = document.querySelector('div.center > div.flex-column.big-gap > span');
  51. if (rewardSpan == undefined) {
  52. return '';
  53. }
  54. let rewardText = rewardSpan.innerText;
  55. console.log(`Text: ${rewardText}`)
  56. //If text does not start with 'Taelia gives you a' then LEAVE.
  57. if (rewardText.startsWith('Taelia gives you a ') == false) {
  58. return '';
  59. }
  60. returnButton = document.querySelector('input.form-control[type="button"][value="Approach Taelia again..."]');
  61. returnButton.disabled = true;
  62. rewardSpan.insertAdjacentElement('afterend', submitDiv)
  63. //Taelia rewards should always be in the following format:
  64. //"Taelia gives you a Crazy Crisp Taco, Poison Snowball, and 17,983 Neopoints!!"
  65. //so "Taelia gives you a [Item], [SnowballItem], and [NP] Neopoints!!"
  66. //We need to figure out a way to split these out. Unfortunately, items can sometimes have commas in them (none of the items Taelia can reward atm does, but I want to future proof it), so we can't just split by comma.
  67.  
  68. //I think I can work backwards instead. I know how to get the Neopoints...
  69. let neopoints = Number(rewardText.match(/(?! )\d*,?\d+(?= Neopoints!!)/)[0].replaceAll(',',''))
  70. console.log(`Neopoints: ${neopoints}`)
  71. //Now we get rid of the neopoints section.
  72. rewardText = rewardText.replace(/, and \d*,?\d+ Neopoints!!/g,'');
  73. console.log(`Text 2: ${rewardText}`)
  74.  
  75. let snowBallItem = rewardText.match(/((?=, ).+ Snowball)|(, Snow \w*ball)|(, Frozen Pile of Dung)/gi)[0].trim();
  76. //Remove the , from the start if it's there.
  77. if (snowBallItem.startsWith(', ')) {
  78. snowBallItem = snowBallItem.slice(2).trim();
  79. }
  80. snowBallItem = encodeURIComponent(snowBallItem);
  81. console.log(`Snowball: ${snowBallItem}`)
  82. //Now get rid of the snowball section... Hoping it doesn't add a snowball that has a ', ' in it. Or a snowball that doesn't end in snowball.
  83. rewardText = rewardText.replace(/((?=, ).+ Snowball)|(, Snow \w*ball)|(, Frozen Pile of Dung)/gi,'')
  84. console.log(`Text 3: ${rewardText}`)
  85.  
  86. //Now get the ice cream machine coupon item
  87. let couponItem = rewardText.match(/((?=, ).+ Ice Cream Machine Coupon)/gi);
  88. if (couponItem != null) {
  89. couponItem = couponItem[0].trim()
  90. //Remove the , from the start if it's there.
  91. if (couponItem.startsWith(', ')) {
  92. couponItem = couponItem.slice(2).trim();
  93. }
  94. couponItem = encodeURIComponent(couponItem);
  95. console.log(`Coupon: ${couponItem}`)
  96. //Now get rid of the coupon section...
  97. rewardText = rewardText.replace(/((?=, ).+ Ice Cream Machine Coupon)/gi,'')
  98. console.log(`Text 4: ${rewardText}`)
  99. }
  100.  
  101.  
  102. //Now for the last item. We just need to remove the beginning part... and we're good!
  103. let item1 = encodeURIComponent(rewardText.replace('Taelia gives you a ','').trim());
  104. console.log(`Item: ${item1}`)
  105. return `https://docs.google.com/forms/d/e/1FAIpQLSd426f2LiLlDYPnvGnIt-IUOB5tfvnNjNfIujx0V_sQROGtYw/formResponse?usp=pp_url&entry.1167335245=Taelia&entry.1736384931=${neopoints}&entry.1829837696=${item1}&entry.558379744=${snowBallItem}` + (couponItem != null ? `&entry.1082355197=${couponItem}` : '')
  106. }
  107.  
  108. function ednaRewards() {
  109. const npRewardSpan = document.querySelector('div#quest_grid ~span');
  110. const itemRewardSpan = document.querySelector('div#quest_grid ~span ~span')
  111. if (npRewardSpan == undefined || itemRewardSpan == undefined) {
  112. return '';
  113. }
  114. let npRewardText = npRewardSpan.innerText;
  115. console.log(`npRewardText: ${npRewardText}`)
  116. let itemRewardText = itemRewardSpan.innerText;
  117. if (npRewardText.startsWith('Yes! I have all the ingredients!') == false || itemRewardText.startsWith('The old witch gives you ') == false) {
  118. return '';
  119. }
  120. returnButton = document.querySelector('input.form-control[type="button"][value="Approach the witch again..."]');
  121. returnButton.disabled = true;
  122. itemRewardSpan.insertAdjacentElement('afterend', submitDiv)
  123. //Extract the neopoints out of the text.
  124. let neopoints = Number(npRewardText.match(/\d+,+\d*(?!NP!)/gi)[0].replaceAll(',',''));
  125. console.log(`Neopoints: ${neopoints}`)
  126. //Extract the items... Edna reward text can come in two forms:
  127. // "The old witch gives you Spooky Lime Pudding !!"
  128. // And if she also rewards a second item:
  129. //"The old witch gives you Spooky Lime Pudding and Bottled Earth Faerie!!"
  130. // So if she gives a second item, it'll always be a bottled faerie, I'm pretty sure. So this will be based on that since there's probably lots of items with 'and' in the name.
  131.  
  132. //So first, let's remove the starting text that'll always be there.
  133. itemRewardText = itemRewardText.replace("The old witch gives you ",'');
  134. //And then remove the two !! at the end. Just twim two chars off the end.
  135. itemRewardText = itemRewardText.slice(0, -2).trim()
  136. console.log(`itemRewardText: ${itemRewardText}`)
  137.  
  138. //If there is ' and Bottled [x] Faerie', we want to split that out. If there's no extra faerie item it'll just be null.
  139. let faerieText = itemRewardText.match(/(?! and )Bottled .+ Faerie/g);
  140. console.log(`faerieText: ${faerieText}`);
  141. //We'll now remove the faerie text ,if it's there.
  142. itemRewardText = encodeURIComponent(itemRewardText.replace(/ and Bottled .+ Faerie/g,'').trim())
  143. console.log(`itemRewardText2: ${itemRewardText}`)
  144. let url = `https://docs.google.com/forms/d/e/1FAIpQLSd426f2LiLlDYPnvGnIt-IUOB5tfvnNjNfIujx0V_sQROGtYw/formResponse?usp=pp_url&entry.1167335245=Edna&entry.1736384931=${neopoints}&entry.1829837696=${itemRewardText}`
  145. if (faerieText != null) {
  146. url = url + `&entry.558379744=${encodeURIComponent(faerieText[0])}`
  147. }
  148. return url;
  149. }
  150.  
  151. function esoRewards() {
  152. let itemRewardSpan = document.querySelector('div.center > div.flex-column.big-gap > span');
  153. if (itemRewardSpan == undefined) {
  154. return '';
  155. }
  156. let itemRewardText = itemRewardSpan.innerText;
  157. console.log(`Text: ${itemRewardText}`)
  158. //If text does not start with 'The Esophagor gives you ' then LEAVE.
  159. if (itemRewardText.startsWith('The Esophagor gives you ') == false) {
  160. return '';
  161. }
  162. let neopointRewardSpan = itemRewardSpan.nextElementSibling;
  163. let neopointRewardText = neopointRewardSpan.innerText;
  164. returnButton = document.querySelector('input.form-control[type="button"][value="Approach the Esophagor again..."]');
  165. returnButton.disabled = true;
  166. neopointRewardSpan.insertAdjacentElement('afterend', submitDiv)
  167. //Eso rewards will be in the following format:
  168. //Item: "The Esophagor gives you Roast Tentacle!!"
  169. //NP: "He also drops 9,360 NP; you pocket it quickly!"
  170. let neopoints = Number(neopointRewardText.match(/(?! )\d*,?\d+(?= NP;)/g)[0].replaceAll(',',''))
  171. console.log(`Neopoints: ${neopoints}`)
  172. //Get rid of the static text and clip of the !! at the end.
  173. itemRewardText = encodeURIComponent(itemRewardText.replace('The Esophagor gives you ','').slice(0, -2).trim());
  174. console.log(`Text 2: ${itemRewardText}`)
  175.  
  176. return `https://docs.google.com/forms/d/e/1FAIpQLSd426f2LiLlDYPnvGnIt-IUOB5tfvnNjNfIujx0V_sQROGtYw/formResponse?usp=pp_url&entry.1167335245=Esophagor&entry.1736384931=${neopoints}&entry.1829837696=${itemRewardText}`
  177. }
  178.  
  179. function kitchenRewards() {
  180. const rewardConfirmationSpan = document.querySelector('div#quest_grid ~span ~span')
  181. if (rewardConfirmationSpan == undefined) {
  182. return '';
  183. }
  184. //If text does not start with 'The Esophagor gives you ' then LEAVE.
  185. if (rewardConfirmationSpan.innerText.startsWith('The Chef waves his hands, and you may collect your prize...') == false) {
  186. return '';
  187. }
  188. returnButton = document.querySelector('input.form-control[type="button"][value="Approach the Chef Again"]');
  189. returnButton.disabled = true;
  190. //Look for the active pet, this is so we can check if it got a stat boost.
  191. const activePet = document.querySelector('#userinfo a[href*="/quickref/"]').innerText.trim();
  192.  
  193. //Because the kitchen quest can have a few possible different formats of rewards, we'll loop through each element after the reward confirmation and do stuff based on that.
  194. let statBoost = '';
  195. let itemReward = '';
  196. let neopoints = '';
  197.  
  198. let url = 'https://docs.google.com/forms/d/e/1FAIpQLSd426f2LiLlDYPnvGnIt-IUOB5tfvnNjNfIujx0V_sQROGtYw/formResponse?usp=pp_url&entry.1167335245=Kitchen'
  199. //Get the next element.
  200. let nextElement = rewardConfirmationSpan.nextElementSibling;
  201. while (nextElement != null && !(nextElement.tagName == "DIV" && nextElement.childElementCount == 2 && nextElement.classList.contains('half-width'))){
  202. //If it is a stat boost...
  203. if (nextElement.innerText.startsWith(`${activePet} gained `)) {
  204. statBoost = nextElement.innerText.match(/ of .+/g)[0].replace(' of ','').trim();
  205. url += '&entry.2021892051=' + statBoost
  206. }
  207. //If it is an item...
  208. else if (nextElement.innerText.startsWith('You get ')) {
  209. itemReward = encodeURIComponent(nextElement.innerText.replace('You get ','').slice(0,-2).trim());
  210. url += '&entry.1829837696=' + itemReward;
  211. }
  212. //If it's codestones...
  213. else if (nextElement.innerText.includes('a sack of codestones')) {
  214. url += '&entry.558379744=Codestones'
  215. }
  216. //If it's neopoints...
  217. else if (nextElement.innerText.startsWith('You also get ')) {
  218. neopoints = Number(nextElement.innerText.match(/(?! )\d*,?\d+(?= Neopoints!)/)[0].replaceAll(',',''))
  219. url += '&entry.1736384931=' + neopoints
  220. }
  221. nextElement = nextElement.nextElementSibling;
  222. }
  223. nextElement.insertAdjacentElement('beforebegin', submitDiv)
  224. return url;
  225. }
  226.  
  227. function brainTreeRewards() {
  228. const wholeElement = document.querySelector('div.flex-column.big-gap')
  229. if (wholeElement == undefined || wholeElement.innerText.startsWith('Thank you, you are correct!') == false) {
  230. return '';
  231. }
  232. returnButton = document.querySelector('input.form-control[type="button"][value="Return to Haunted Woods"]');
  233. returnButton.disabled = true;
  234. let itemElement = wholeElement.children[3];
  235.  
  236. itemElement.insertAdjacentElement('afterend', submitDiv)
  237. let itemReward = itemElement.innerText.slice(0,-2);
  238. return `https://docs.google.com/forms/d/e/1FAIpQLSd426f2LiLlDYPnvGnIt-IUOB5tfvnNjNfIujx0V_sQROGtYw/formResponse?usp=pp_url&entry.1167335245=Brain+Tree&entry.1829837696=${itemReward}`
  239. }
  240.  
  241. //Only run if no quest items are NO. So exit if there's a single NO.
  242. let noNo = false;
  243. const itemList = document.querySelectorAll('.quest-text strong:nth-child(2)')
  244. //ALso get the items that were requested.
  245. let itemsRequested = {item0:"",item1:"",item2:"",item3:""}
  246.  
  247. //Loop through each quest item to check if already got.
  248. for (let i = 0; i < itemList.length; i++) {
  249. if (itemList[i].innerText === "NO") {
  250. noNo = true;
  251. break;
  252. }
  253. else {
  254. itemsRequested['item'+i] = itemList[i].previousElementSibling.innerText;
  255. }
  256. }
  257.  
  258.  
  259. //If all the items have been gotten then the quest is done, so continue.
  260. if (noNo == false) {
  261. if (window.location.href == 'https://www.grundos.cafe/winter/snowfaerie/complete/') {
  262. formLink = taeliaRewards();
  263. }
  264. else if (window.location.href == 'https://www.grundos.cafe/halloween/witchtower/complete/') {
  265. formLink = ednaRewards();
  266. }
  267. else if (window.location.href == 'https://www.grundos.cafe/halloween/esophagor/complete/') {
  268. formLink = esoRewards();
  269. }
  270. else if (window.location.href == 'https://www.grundos.cafe/island/kitchen/complete/') {
  271. formLink = kitchenRewards();
  272. }
  273. else if (window.location.href == 'https://www.grundos.cafe/halloween/braintree/complete/') {
  274. formLink = brainTreeRewards();
  275. }
  276.  
  277. if (formLink != '') {
  278. if (giveUsername) {
  279. formLink += '&entry.1732136035=' + document.querySelector('#userinfo a[href*="userlookup/?user="]').innerText.trim();
  280. }
  281. //Add the requested quest items.
  282. if (itemsRequested.item0 != "") {
  283. formLink += '&entry.669064702=' + itemsRequested.item0;
  284. }
  285. if (itemsRequested.item1 != "") {
  286. formLink += '&entry.1797043340=' + itemsRequested.item1;
  287. }
  288. if (itemsRequested.item2 != "") {
  289. formLink += '&entry.1426587972=' + itemsRequested.item2;
  290. }
  291. if (itemsRequested.item3 != "") {
  292. formLink += '&entry.1031263455=' + itemsRequested.item3;
  293. }
  294.  
  295. let opts = {
  296. mode: "no-cors",
  297. //redirect: "follow",
  298. referrer: "no-referrer",
  299. headers: {
  300. 'Content-Type': 'application/x-www-form-urlencoded',
  301. }
  302. }
  303.  
  304. let response = fetch(formLink, opts)
  305. .then(response => {
  306. // handle the response
  307. console.log("Quest Data Submitted");
  308. submitDiv.innerHTML = '<a href="https://docs.google.com/spreadsheets/d/17lADRhz5POPYPxKRa6ZVYlJuKuYxxev0_bxnFPbK0Bk/edit?gid=1499161972#gid=1499161972" style="color:green; text-decoration: underline">Quest Data Submitted!</a>';
  309. returnButton.disabled = false;
  310. })
  311. .catch(error => {
  312. // handle the error
  313. console.log("Error: " + error);
  314. console.log(response)
  315. returnButton.disabled = false;
  316. submitDiv.innerHTML = `Failed to submit quest data: ${error}`;
  317. submitDiv.style.color = "red"
  318. });
  319.  
  320. }
  321. }