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.3
  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. $("div.topSection___U7sVi").on("click", "div.linksContainer___LiOTN a[aria-labelledby=add-items]", function(){
  49. observer.observe(observerTarget, observerConfig);
  50. });
  51. $("div.topSection___U7sVi").on("click", "div.listItem___Q3FFU a[aria-labelledby=add-items]", function(){
  52. observer.observe(observerTarget, observerConfig);
  53. });
  54.  
  55. let containerItems = $("ul.items-cont li.clearfix");
  56. containerItems.find("div.title-wrap div.name-wrap").each(function(){
  57. let isParentRowDisabled = this.parentElement.parentElement.classList.contains("disabled");
  58. let alreadyHasFillBtn = this.querySelector(".btn-wrap.torn-bazaar-fill-qty-price") != null;
  59. if (!alreadyHasFillBtn && !isParentRowDisabled){
  60. insertFillBtn(this);
  61. }
  62. });
  63.  
  64. if($("div.amount").length) {
  65. observer.disconnect();
  66. }
  67. });
  68. observer.observe(observerTarget, observerConfig);
  69.  
  70. function insertFillBtn(element){
  71. const outerSpan = document.createElement('span');
  72. outerSpan.className = 'btn-wrap torn-bazaar-fill-qty-price';
  73.  
  74. const innerSpan = document.createElement('span');
  75. innerSpan.className = 'btn';
  76.  
  77. const inputElement = document.createElement('input');
  78. inputElement.type = 'submit';
  79. inputElement.value = 'Fill';
  80. inputElement.className = 'torn-btn';
  81.  
  82. innerSpan.appendChild(inputElement);
  83. outerSpan.appendChild(innerSpan);
  84.  
  85. element.appendChild(outerSpan);
  86. $(outerSpan).on("click", "input", function() {
  87. checkApiKey();
  88. fillQuantityAndPrice(this);
  89. event.stopPropagation();
  90. });
  91. }
  92.  
  93. function getQuantity(element){
  94. let rgx = /x(\d+)$/;
  95. let quantityText = $(element).parent().parent().prev().text();
  96. let match = rgx.exec(quantityText);
  97. let quantity = match === null ? 1 : match[1];
  98. return quantity;
  99. }
  100.  
  101. function fillQuantityAndPrice(element){
  102. let amountDiv = element.parentElement.parentElement.parentElement.parentElement.parentElement.querySelector("div.amount-main-wrap");
  103. let priceInputs = amountDiv.querySelectorAll("div.price div input");
  104. let keyupEvent = new Event("keyup", {bubbles: true});
  105. let inputEvent = new Event("input", {bubbles: true});
  106.  
  107. let image = element.parentElement.parentElement.parentElement.parentElement.querySelector("div.image-wrap img");
  108. let numberPattern = /\/(\d+)\//;
  109. let match = image.src.match(numberPattern);
  110. let extractedItemId = 0;
  111. if (match) {
  112. extractedItemId = parseInt(match[1], 10);
  113. } else {
  114. console.error("[TornBazaarFiller] ItemId not found!");
  115. }
  116.  
  117. let requestUrl = bazaarUrl
  118. .replace("{itemId}", extractedItemId)
  119. .replace("{apiKey}", apiKey);
  120.  
  121. fetch(requestUrl)
  122. .then(response => response.json())
  123. .then(data => {
  124. let lowBallPrice = data.bazaar[0].cost - 1;
  125. priceInputs[0].value = lowBallPrice;
  126. priceInputs[1].value = lowBallPrice;
  127. priceInputs[0].dispatchEvent(inputEvent);
  128. })
  129. .catch(error => {
  130. console.error("[TornBazaarFiller] Error fetching data:", error);
  131. });
  132.  
  133. let isQuantityCheckbox = amountDiv.querySelector("div.amount.choice-container") !== null;
  134. if (isQuantityCheckbox){
  135. amountDiv.querySelector("div.amount.choice-container input").click();
  136. } else {
  137. let quantityInput = amountDiv.querySelector("div.amount input");
  138. quantityInput.value = getQuantity(element);
  139. quantityInput.dispatchEvent(keyupEvent);
  140. }
  141. }
  142.  
  143. function checkApiKey(){
  144. if (apiKey == null){
  145. let userInput = prompt("Please enter a PUBLIC Api Key, it will be used to get current bazaar prices:");
  146. if (userInput !== null) {
  147. apiKey = userInput;
  148. localStorage.setItem("silmaril-torn-bazaar-filler-apikey", userInput);
  149. } else {
  150. console.error("[TornBazaarFiller] User cancelled the Api Key input.");
  151. }
  152. }
  153. }
  154. })();