Torn Inventory & Display Case Market Value

Combined script for Inventory and Display Case market value

  1. // ==UserScript==
  2. // @name Torn Inventory & Display Case Market Value
  3. // @namespace Violentmonkey Scripts
  4. // @match https://www.torn.com/*
  5. // @grant GM_xmlhttpRequest
  6. // @version 1.0
  7. // @license MIT
  8. // @author BillyBourbon/Bilbosaggings[2323763]
  9. // @description Combined script for Inventory and Display Case market value
  10. // ==/UserScript==
  11.  
  12. // ================================
  13. // Input your apikey in between the quote marks ""
  14. const apikey = "";
  15. // ================================
  16.  
  17. (async () => {
  18. // Function to format numbers to currency format (USD)
  19. function formatToCurrency(n) {
  20. n = Number(n);
  21. return new Intl.NumberFormat("en-US", {
  22. style: "currency",
  23. currency: "USD",
  24. minimumFractionDigits: 0,
  25. maximumFractionDigits: 0
  26. }).format(n);
  27. }
  28.  
  29. // Function to load Torn items with caching
  30. async function loadTornItems() {
  31. const cacheKey = "tornItemsCache";
  32. const cacheExpiryKey = "tornItemsCacheExpiry";
  33. const cacheDuration = 60 * 60 * 1000; // 1 hour in milliseconds
  34.  
  35. // Check for cached data
  36. const cachedData = localStorage.getItem(cacheKey);
  37. const cachedExpiry = localStorage.getItem(cacheExpiryKey);
  38.  
  39. if (cachedData && cachedExpiry && Date.now() < cachedExpiry) {
  40. console.log("Using cached data");
  41. return JSON.parse(cachedData);
  42. }
  43.  
  44. let attempt = 0;
  45. let jsonResponse = null;
  46.  
  47. // Retry logic for API request
  48. while (attempt < 3) {
  49. try {
  50. jsonResponse = await new Promise((resolve, reject) => {
  51. GM_xmlhttpRequest({
  52. method: "GET",
  53. url: `https://api.torn.com/v2/torn/items`,
  54. headers: {
  55. "Authorization": `ApiKey ${apikey}`,
  56. },
  57. onload: function (response) {
  58. if (response.status >= 200 && response.status < 300) {
  59. try {
  60. const responseData = JSON.parse(response.responseText);
  61. resolve(responseData);
  62. } catch (error) {
  63. reject(new Error("Failed to parse JSON"));
  64. }
  65. } else {
  66. reject(new Error(`API request failed with status: ${response.status}`));
  67. }
  68. },
  69. onerror: function (error) {
  70. reject(new Error(`API request failed with error: ${error}`));
  71. }
  72. });
  73. });
  74.  
  75. console.log(jsonResponse);
  76.  
  77. // Cache the API response
  78. localStorage.setItem(cacheKey, JSON.stringify(jsonResponse));
  79. localStorage.setItem(cacheExpiryKey, Date.now() + cacheDuration);
  80.  
  81. return jsonResponse;
  82. } catch (error) {
  83. attempt++;
  84. console.error(`Attempt ${attempt} failed: ${error.message}`);
  85.  
  86. if (attempt < 3) {
  87. await new Promise(resolve => setTimeout(resolve, 2000)); // Delay before retrying
  88. }
  89. }
  90. }
  91. }
  92.  
  93. // Function to find a Torn item by its ID
  94. function findTornItem(itemId, tornItems) {
  95. const item = tornItems.find(o => o.id.toString() === itemId.toString());
  96.  
  97. // Return null if item is not found
  98. return item || null;
  99. }
  100.  
  101. // ========================
  102. // Inventory Market Value
  103. // ========================
  104. async function insertMarketValues(itemList) {
  105. let counter = 0;
  106.  
  107. const {
  108. items: tornItems
  109. } = await loadTornItems();
  110.  
  111. for (let child of itemList.querySelectorAll("li")) {
  112. const itemId = child.getAttribute("data-item");
  113. if (itemId !== null && itemId > 0 && child.querySelector(".name-wrap .name") !== null) {
  114. const itemNameSpan = child.querySelector(".name-wrap .name");
  115. const itemQuantitySpan = child.querySelector(".name-wrap .qty");
  116. const itemQuantity = itemQuantitySpan.innerHTML.length === 0 ? 1 : Number(itemQuantitySpan.innerHTML.substring(1));
  117. const {
  118. value: {
  119. market_price
  120. }
  121. } = findTornItem(itemId, tornItems);
  122. itemNameSpan.innerHTML += ` (${formatToCurrency(market_price * itemQuantity)})`;
  123.  
  124. counter++;
  125. }
  126. }
  127.  
  128. return counter;
  129. }
  130.  
  131. const callback = async (mutationList, observer) => {
  132. console.log("mutation observed");
  133. for (const mutation of mutationList) {
  134. if (mutation.type === "childList") {
  135. if (mutation.addedNodes.length === 0) return;
  136. const editedElementCount = await insertMarketValues(mutation.target);
  137.  
  138. if (editedElementCount > 0) observer.disconnect();
  139. }
  140. }
  141. }
  142.  
  143. // ========================
  144. // Display Case Market Value
  145. // ========================
  146. async function updateDisplayCaseMarketValue() {
  147. // Wait for display case page to load
  148. while (document.querySelector(".display-cabinet") === null) {
  149. await new Promise(resolve => setTimeout(resolve, 500)); // Delay before retrying
  150. }
  151.  
  152. const displayCaseContainer = document.querySelector(".display-cabinet");
  153.  
  154. const { items: tornItems } = await loadTornItems();
  155.  
  156. displayCaseContainer.querySelectorAll("li").forEach(li => {
  157. const temp = li.querySelector(".item-hover");
  158.  
  159. if (temp === null || temp.getAttribute("data-userscript") === "true") return; // Skip if there's no item
  160.  
  161. const itemId = temp.getAttribute("itemId");
  162. const ammountField = li.querySelector(".b-item-amount");
  163. const ammount = ammountField.innerHTML.trim().substring(1);
  164.  
  165. const item = findTornItem(itemId, tornItems);
  166.  
  167. // Check if item was found
  168. if (item && item.value && item.value.market_price) {
  169. const marketPrice = item.value.market_price;
  170. const totalValue = ammount * marketPrice
  171. if(totalValue > 0) ammountField.innerHTML += `(${formatToCurrency(totalValue)})`;
  172. } else {
  173. console.error(`Item with ID ${itemId} not found or missing market price.`);
  174. }
  175.  
  176. temp.setAttribute("data-userscript", "true")
  177. });
  178. }
  179.  
  180. // ========================
  181. // Main Logic
  182. // ========================
  183. const currentUrl = window.location.href;
  184.  
  185. if (currentUrl.includes("item.php")) {
  186. console.log("Updating Inventory Market Value...");
  187. // Observe changes in the inventory page
  188. document.querySelectorAll(".items-cont").forEach(container => {
  189. const observer = new MutationObserver(callback);
  190. observer.observe(container, {
  191. attributes: true,
  192. childList: true,
  193. subtree: true
  194. });
  195. })
  196. }
  197. else if (currentUrl.includes("displaycase.php")) {
  198. console.log("Updating Display Case Market Value...");
  199. await updateDisplayCaseMarketValue();
  200. }
  201. })();