View Bazaar Anytime

View anyone's bazaar from hospital, jail or travelling!

  1. // ==UserScript==
  2. // @name View Bazaar Anytime
  3. // @namespace heartflower.torn.com
  4. // @version 1.1
  5. // @description View anyone's bazaar from hospital, jail or travelling!
  6. // @author Heartflower [2626587]
  7. // @match https://www.torn.com/bazaar.php*
  8. // @icon https://www.google.com/s2/favicons?sz=64&domain=torn.com
  9. // ==/UserScript==
  10.  
  11. (function() {
  12. 'use strict';
  13.  
  14. // Set to 'true' if you want to show a link to remove your API key
  15. let removeKeyLink = true;
  16.  
  17. // Change maximum calls you want to do here. If it says 40, but those 40 items have an UID, it'll actually be 80 calls.
  18. let maximumCalls = 40;
  19. let apiCallCount = 0;
  20.  
  21. let itemIDs = [];
  22. let itemUIDs = [];
  23.  
  24. let userName = '';
  25. let userID = '';
  26.  
  27. let apiKey = '';
  28. let storedAPIKey = localStorage.getItem('hf-public-apiKey');
  29.  
  30. if (storedAPIKey) {
  31. apiKey = storedAPIKey;
  32. }
  33.  
  34. function checkElement() {
  35. let contentWrapper = document.querySelector('.content-wrapper');
  36.  
  37. if (contentWrapper) {
  38. let messageContent = document.querySelector('.msg.right-round');
  39. let originalText = messageContent.textContent;
  40.  
  41. if (messageContent && messageContent.textContent.includes('This area is unavailable')) {
  42. if (userName == '') {
  43. userName = 'Unknown';
  44. }
  45.  
  46. let newText = `${userName}'s bazaar is fetched by the "View Bazaar Anytime" script" with the help of the API!`;
  47. messageContent.textContent = newText;
  48.  
  49. if (apiKey !== '') {
  50. fetchBazaarData();
  51.  
  52. if (removeKeyLink === true) {
  53. let link = document.createElement('a');
  54. link.href = '#'; // Set a placeholder href
  55.  
  56. link.textContent = ' Click here to remove your (public) API key!';
  57.  
  58. // Add click event listener to the link
  59. link.addEventListener('click', function(event) {
  60. event.preventDefault(); // Prevent the default link behavior
  61.  
  62. localStorage.removeItem('hf-public-apiKey');
  63. alert('API key successfully removed!');
  64. link.remove();
  65.  
  66. let enterLink = document.createElement('a');
  67. enterLink.href = '#'; // Set a placeholder href
  68.  
  69. enterLink.textContent = ' Click here to enter your (public) API key!';
  70.  
  71.  
  72. // Add click event listener to the link
  73. enterLink.addEventListener('click', function(event) {
  74. event.preventDefault(); // Prevent the default link behavior
  75. promptAPIKey();
  76. link.remove();
  77. });
  78.  
  79. messageContent.appendChild(enterLink);
  80. });
  81.  
  82. messageContent.appendChild(link);
  83. }
  84.  
  85. } else {
  86. let link = document.createElement('a');
  87. link.href = '#'; // Set a placeholder href
  88. link.textContent = ' Click here to enter your (public) API key!';
  89.  
  90. // Add click event listener to the link
  91. link.addEventListener('click', function(event) {
  92. event.preventDefault(); // Prevent the default link behavior
  93. promptAPIKey();
  94. });
  95.  
  96. messageContent.appendChild(link);
  97. }
  98. }
  99. }
  100. }
  101.  
  102. function promptAPIKey() {
  103. let enterAPIKey = prompt('Enter a public API key here:');
  104.  
  105. if (enterAPIKey !== null && enterAPIKey.trim() !== '') {
  106. localStorage.setItem('hf-public-apiKey', enterAPIKey);
  107. alert('API key set succesfully');
  108.  
  109. fetchBazaarData();
  110. } else {
  111. alert('No valid API key entered!');
  112. }
  113. }
  114.  
  115. function checkUserID() {
  116. let url = new URL(window.location.href);
  117. userID = url.searchParams.get('userId');
  118.  
  119. let apiUrl = `https://api.torn.com/user/${userID}?key=${apiKey}&selections=basic&comment=ViewBazaarAnytime`;
  120.  
  121. fetch(apiUrl)
  122. .then(response => response.json())
  123. .then(data => {
  124. userName = data.name;
  125. console.log(userName);
  126. })
  127. .catch(error => console.error('Error fetching data: ' + error));
  128.  
  129. }
  130.  
  131. function createTable (data) {
  132. let contentWrapper = document.querySelector('.content-wrapper');
  133.  
  134. let tableDiv = document.createElement('div');
  135. tableDiv.style.paddingTop = '16px';
  136.  
  137. let table = document.createElement('table');
  138. table.style.margin = '0 auto';
  139. table.style.background = 'var(--default-bg-panel-color)';
  140. table.style.width = '100%';
  141.  
  142. let thead = document.createElement('thead');
  143.  
  144. let tbody = document.createElement('tbody');
  145. tbody.style.borderRadius = '5px';
  146.  
  147.  
  148. // Create table headers
  149. let headers = ['Image', 'Name', 'Bonus', 'Stock', 'Price each', 'Price total', 'Lowest price in other bazaars'];
  150. let headerRow = document.createElement('tr');
  151. headers.forEach(function(header) {
  152. let th = document.createElement('th');
  153. th.style.padding = '4px';
  154. th.textContent = header;
  155. th.style.background = 'var(--tabs-active-bg-gradient)';
  156. th.style.color = 'var(--default-color)';
  157. th.style.fontWeight = 'bold';
  158. th.style.textAlign = 'center';
  159. th.style.padding = '8px 4px';
  160. th.style.borderBottom = '1px solid grey';
  161. th.style.borderBottomColor = 'var(--default-panel-divider-outer-side-color)';
  162.  
  163. headerRow.appendChild(th);
  164. });
  165. thead.appendChild(headerRow);
  166. table.appendChild(thead);
  167.  
  168. // Create table body
  169. data.forEach(function(item, index) {
  170. let row = document.createElement('tr');
  171. row.style.borderBottom = '1px solid grey';
  172. row.style.borderBottomColor = 'var(--default-panel-divider-outer-side-color)';
  173.  
  174. let itemID = item.ID;
  175.  
  176. let itemUID = item.UID;
  177. let bonusText = '';
  178. if (itemUID) {
  179. itemUIDs.push({ uid: itemUID, index: index});
  180. bonusText = 'Loading...';
  181. }
  182.  
  183. // Store itemID with index
  184. itemIDs.push({ id: itemID, index: index});
  185.  
  186. createCell('Image', row, itemID);
  187. createCell(item.name, row);
  188. createCell(bonusText, row);
  189. createCell(item.quantity, row);
  190. createCell(item.price.toLocaleString('en-US', {style: 'currency', currency: 'USD', maximumFractiondigits: 0, minimumFractionDigits: 0}), row);
  191. createCell((item.quantity * item.price).toLocaleString('en-US', {style: 'currency', currency: 'USD', maximumFractiondigits: 0, minimumFractionDigits: 0}), row);
  192. createCell('Loading...', row);
  193.  
  194. tbody.appendChild(row);
  195. });
  196.  
  197. table.appendChild(tbody);
  198. tableDiv.appendChild(table);
  199. contentWrapper.appendChild(tableDiv);
  200.  
  201. fetchMarketDataForItems();
  202. fetchItemDetailsForUIDs();
  203. }
  204.  
  205. function createCell(text, row, itemID) {
  206. let cell = document.createElement('td');
  207.  
  208. if (text == 'Image') {
  209. let img = document.createElement('img');
  210. img = document.createElement('img');
  211. img.src = `/images/items/${itemID}/large.png`;
  212. img.srcset = `/images/items/${itemID}/large.png 1x, /images/items/${itemID}/large@2x.png 2x, /images/items/${itemID}/large@3x.png 3x, /images/items/${itemID}/large@4x.png 4x`;
  213. img.alt = 'Item Image';
  214. img.style.height = '25px';
  215. cell.appendChild(img);
  216. } else {
  217. cell.textContent = text;
  218. }
  219.  
  220. cell.style.color = 'var(--default-color)';
  221. cell.style.textAlign = 'center';
  222. cell.style.verticalAlign = 'middle';
  223. cell.style.padding = '4px';
  224.  
  225. row.appendChild(cell);
  226. }
  227.  
  228. function fetchBazaarData() {
  229. let apiUrl = `https://api.torn.com/user/${userID}?key=${apiKey}&selections=bazaar&comment=ViewBazaarAnytime`;
  230.  
  231. fetch(apiUrl)
  232. .then(response => response.json())
  233. .then(data => {
  234. createTable(data.bazaar);
  235. apiCallCount++;
  236. })
  237. .catch(error => console.error('Error fetching data: ' + error));
  238. }
  239.  
  240. function fetchMarketDataForItems() {
  241. let itemsToFetch = Math.min(maximumCalls, itemIDs.length);
  242.  
  243. for (let i = 0; i < itemsToFetch; i++) {
  244. let { id, index } = itemIDs[i];
  245. fetchMarketData(id, index);
  246. }
  247.  
  248. if (itemIDs.length > maximumCalls) {
  249. setTimeout(function () {
  250. fetchMarketDataForItems();
  251. }, 60000);
  252. }
  253. }
  254.  
  255. function fetchMarketData(itemID, index) {
  256. let apiUrl = `https://api.torn.com/market/${itemID}?selections=bazaar&key=${apiKey}&comment=ViewBazaarAnytime`;
  257.  
  258. // Make the API call
  259. fetch(apiUrl)
  260. .then(response => response.json())
  261. .then(data => {
  262. if (data.bazaar && data.bazaar.length > 0) {
  263. let lowestPrice = data.bazaar[0].cost;
  264.  
  265. // Update table with lowest bazaar price
  266. let table = document.querySelector('table');
  267. let cell = table.rows[index + 1].cells[6];
  268. cell.textContent = lowestPrice.toLocaleString('en-US', {style: 'currency', currency: 'USD', maximumFractiondigits: 0, minimumFractionDigits: 0});
  269.  
  270. // Remove from itemIDs
  271. itemIDs = itemIDs.filter(item => item.id !== itemID);
  272. } else {
  273. throw new Error('No items found in the bazaar');
  274. }
  275. })
  276. .catch(error => console.error('Error fetching data: ' + error));
  277. }
  278.  
  279. function fetchItemDetailsForUIDs() {
  280. console.log('Fetching all item details');
  281. let itemsToFetch = Math.min(maximumCalls, itemUIDs.length);
  282.  
  283. for (let i = 0; i < itemsToFetch; i++) {
  284. let { uid, index } = itemUIDs[i];
  285. fetchItemDetails(uid, index);
  286. }
  287.  
  288. if (itemIDs.length > maximumCalls) {
  289. setTimeout(function () {
  290. fetchItemDetailsForUIDs();
  291. }, 60000);
  292. }
  293. }
  294.  
  295. function fetchItemDetails(itemUID, index) {
  296. console.log('Fetching item details');
  297.  
  298. let apiUrl = `https://api.torn.com/torn/${itemUID}?selections=itemdetails&key=${apiKey}&comment=ViewBazaarAnytime`;
  299.  
  300. console.log(apiUrl);
  301.  
  302. // Make the API call
  303. fetch(apiUrl)
  304. .then(response => response.json())
  305. .then(data => {
  306. if (data.itemdetails) {
  307. let rarity = data.itemdetails.rarity;
  308.  
  309. if (rarity == 'None') {
  310. rarity = '';
  311. }
  312.  
  313. let quality = data.itemdetails.quality;
  314. let bonuses = data.itemdetails.bonuses;
  315.  
  316. let bonusText = '';
  317.  
  318. if (bonuses) {
  319. if (Object.keys(bonuses).length === 1) {
  320. // If there is only one bonus
  321. let bonus = bonuses[Object.keys(bonuses)[0]];
  322. bonusText = `<p style="padding:4px">${bonus.value}% ${bonus.bonus}</p>`;
  323. } else if (Object.keys(bonuses).length === 2) {
  324. // If there are two bonuses
  325. Object.keys(bonuses).forEach(key => {
  326. let bonus = bonuses[key];
  327. bonusText += `<p style="padding:4px">${bonus.value}% ${bonus.bonus}</p>`;
  328. });
  329. }
  330. }
  331.  
  332. // Update table with bonus text
  333. let table = document.querySelector('table');
  334. let cell = table.rows[index + 1].cells[2];
  335. cell.innerHTML = `<p style="padding:4px">${rarity}<p>${quality} Quality</p>${bonusText}`;
  336.  
  337. // Remove from itemUIDs
  338. itemUIDs = itemUIDs.filter(item => item.uid !== itemUID);
  339. } else {
  340. throw new Error('No item details found');
  341. }
  342. })
  343. .catch(error => console.error('Error fetching data: ' + error));
  344. }
  345.  
  346. checkUserID();
  347. setTimeout(checkElement, 300);
  348.  
  349. })();