Torn Bazaar Filler

On "Fill" click autofills bazaar item price with lowest bazaar price currently minus $1, fills max items, checks item checkboxes.

当前为 2023-08-19 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Torn Bazaar Filler
  3. // @namespace https://github.com/SOLiNARY
  4. // @version 0.2
  5. // @description On "Fill" click autofills bazaar item price with lowest bazaar price currently minus $1, fills max items, checks item checkboxes.
  6. // @author Ramin Quluzade, Silmaril [2665762]
  7. // @license MIT License
  8. // @match https://www.torn.com/bazaar.php
  9. // @require https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js
  10. // @run-at document-idle
  11. // @grant GM_addStyle
  12. // ==/UserScript==
  13.  
  14. (function() {
  15. 'use strict';
  16.  
  17. const bazaarUrl = "https://api.torn.com/market/{itemId}?selections=bazaar&key={apiKey}";
  18. let apiKey = localStorage.getItem("silmaril-torn-bazaar-filler-apikey");
  19.  
  20. // TornPDA support for GM_addStyle
  21. let GM_addStyle = function (s) {
  22. let style = document.createElement("style");
  23. style.type = "text/css";
  24. style.innerHTML = s;
  25. document.head.appendChild(style);
  26. };
  27.  
  28. GM_addStyle(`
  29. .btn-wrap.torn-bazaar-fill-qty-price {
  30. float: right;
  31. margin-left: auto;
  32. }
  33.  
  34. div.title-wrap div.name-wrap {
  35. display: flex;
  36. justify-content: flex-end;
  37. }
  38. `);
  39.  
  40.  
  41. var observerTarget = $(".content-wrapper")[0];
  42. var observerConfig = { attributes: false, childList: true, characterData: false, subtree: true };
  43.  
  44. var observer = new MutationObserver(function(mutations) {
  45. $("ul.ui-tabs-nav").on("click", "li:not(.ui-state-active):not(.ui-state-disabled):not(.m-show)", function() {
  46. observer.observe(observerTarget, observerConfig);
  47. });
  48.  
  49. let containerItems = $("ul.items-cont li.clearfix");
  50. containerItems.find("div.title-wrap div.name-wrap").each(function(){
  51. let isParentRowDisabled = this.parentElement.parentElement.classList.contains("disabled");
  52. let alreadyHasFillBtn = this.querySelector(".btn-wrap.torn-bazaar-fill-qty-price") != null;
  53. if (!alreadyHasFillBtn && !isParentRowDisabled){
  54. insertFillBtn(this);
  55. }
  56. });
  57.  
  58. if($("div.amount").length) {
  59. observer.disconnect();
  60. }
  61. });
  62. observer.observe(observerTarget, observerConfig);
  63.  
  64. function insertFillBtn(element){
  65. const outerSpan = document.createElement('span');
  66. outerSpan.className = 'btn-wrap torn-bazaar-fill-qty-price';
  67.  
  68. const innerSpan = document.createElement('span');
  69. innerSpan.className = 'btn';
  70.  
  71. const inputElement = document.createElement('input');
  72. inputElement.type = 'submit';
  73. inputElement.value = 'Fill';
  74. inputElement.className = 'torn-btn';
  75.  
  76. innerSpan.appendChild(inputElement);
  77. outerSpan.appendChild(innerSpan);
  78.  
  79. element.appendChild(outerSpan);
  80. $(outerSpan).on("click", "input", function() {
  81. checkApiKey();
  82. fillQuantityAndPrice(this);
  83. event.stopPropagation();
  84. });
  85. }
  86.  
  87. function getQuantity(element){
  88. let rgx = /x(\d+)$/;
  89. let quantityText = $(element).parent().parent().prev().text();
  90. let match = rgx.exec(quantityText);
  91. let quantity = match === null ? 1 : match[1];
  92. return quantity;
  93. }
  94.  
  95. function fillQuantityAndPrice(element){
  96. let amountDiv = element.parentElement.parentElement.parentElement.parentElement.parentElement.querySelector("div.amount-main-wrap");
  97. let priceInputs = amountDiv.querySelectorAll("div.price div input");
  98. let keyupEvent = new Event("keyup", {bubbles: true});
  99. let inputEvent = new Event("input", {bubbles: true});
  100.  
  101. let image = element.parentElement.parentElement.parentElement.parentElement.querySelector("div.image-wrap img");
  102. let numberPattern = /\/(\d+)\//;
  103. let match = image.src.match(numberPattern);
  104. let extractedItemId = 0;
  105. if (match) {
  106. extractedItemId = parseInt(match[1], 10);
  107. } else {
  108. console.error("[TornBazaarFiller] ItemId not found!");
  109. }
  110.  
  111. let requestUrl = bazaarUrl
  112. .replace("{itemId}", extractedItemId)
  113. .replace("{apiKey}", apiKey);
  114.  
  115. fetch(requestUrl)
  116. .then(response => response.json())
  117. .then(data => {
  118. let lowBallPrice = data.bazaar[0].cost - 1;
  119. priceInputs[0].value = lowBallPrice;
  120. priceInputs[1].value = lowBallPrice;
  121. priceInputs[0].dispatchEvent(inputEvent);
  122. })
  123. .catch(error => {
  124. console.error("[TornBazaarFiller] Error fetching data:", error);
  125. });
  126.  
  127. let isQuantityCheckbox = amountDiv.querySelector("div.amount.choice-container") !== null;
  128. if (isQuantityCheckbox){
  129. amountDiv.querySelector("div.amount.choice-container input").click();
  130. } else {
  131. let quantityInput = amountDiv.querySelector("div.amount input");
  132. quantityInput.value = getQuantity(element);
  133. quantityInput.dispatchEvent(keyupEvent);
  134. }
  135. }
  136.  
  137. function checkApiKey(){
  138. if (apiKey == null){
  139. let userInput = prompt("Please enter a PUBLIC Api Key, it will be used to get current bazaar prices:");
  140. if (userInput !== null) {
  141. apiKey = userInput;
  142. localStorage.setItem("silmaril-torn-bazaar-filler-apikey", userInput);
  143. } else {
  144. console.error("[TornBazaarFiller] User cancelled the Api Key input.");
  145. }
  146. }
  147. }
  148. })();