// ==UserScript==
// @name View Bazaar Anytime
// @namespace heartflower.torn.com
// @version 1.1
// @description View anyone's bazaar from hospital, jail or travelling!
// @author Heartflower [2626587]
// @match https://www.torn.com/bazaar.php*
// @icon https://www.google.com/s2/favicons?sz=64&domain=torn.com
// ==/UserScript==
(function() {
'use strict';
// Set to 'true' if you want to show a link to remove your API key
let removeKeyLink = true;
// 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.
let maximumCalls = 40;
let apiCallCount = 0;
let itemIDs = [];
let itemUIDs = [];
let userName = '';
let userID = '';
let apiKey = '';
let storedAPIKey = localStorage.getItem('hf-public-apiKey');
if (storedAPIKey) {
apiKey = storedAPIKey;
}
function checkElement() {
let contentWrapper = document.querySelector('.content-wrapper');
if (contentWrapper) {
let messageContent = document.querySelector('.msg.right-round');
let originalText = messageContent.textContent;
if (messageContent && messageContent.textContent.includes('This area is unavailable')) {
if (userName == '') {
userName = 'Unknown';
}
let newText = `${userName}'s bazaar is fetched by the "View Bazaar Anytime" script" with the help of the API!`;
messageContent.textContent = newText;
if (apiKey !== '') {
fetchBazaarData();
if (removeKeyLink === true) {
let link = document.createElement('a');
link.href = '#'; // Set a placeholder href
link.textContent = ' Click here to remove your (public) API key!';
// Add click event listener to the link
link.addEventListener('click', function(event) {
event.preventDefault(); // Prevent the default link behavior
localStorage.removeItem('hf-public-apiKey');
alert('API key successfully removed!');
link.remove();
let enterLink = document.createElement('a');
enterLink.href = '#'; // Set a placeholder href
enterLink.textContent = ' Click here to enter your (public) API key!';
// Add click event listener to the link
enterLink.addEventListener('click', function(event) {
event.preventDefault(); // Prevent the default link behavior
promptAPIKey();
link.remove();
});
messageContent.appendChild(enterLink);
});
messageContent.appendChild(link);
}
} else {
let link = document.createElement('a');
link.href = '#'; // Set a placeholder href
link.textContent = ' Click here to enter your (public) API key!';
// Add click event listener to the link
link.addEventListener('click', function(event) {
event.preventDefault(); // Prevent the default link behavior
promptAPIKey();
});
messageContent.appendChild(link);
}
}
}
}
function promptAPIKey() {
let enterAPIKey = prompt('Enter a public API key here:');
if (enterAPIKey !== null && enterAPIKey.trim() !== '') {
localStorage.setItem('hf-public-apiKey', enterAPIKey);
alert('API key set succesfully');
fetchBazaarData();
} else {
alert('No valid API key entered!');
}
}
function checkUserID() {
let url = new URL(window.location.href);
userID = url.searchParams.get('userId');
let apiUrl = `https://api.torn.com/user/${userID}?key=${apiKey}&selections=basic&comment=ViewBazaarAnytime`;
fetch(apiUrl)
.then(response => response.json())
.then(data => {
userName = data.name;
console.log(userName);
})
.catch(error => console.error('Error fetching data: ' + error));
}
function createTable (data) {
let contentWrapper = document.querySelector('.content-wrapper');
let tableDiv = document.createElement('div');
tableDiv.style.paddingTop = '16px';
let table = document.createElement('table');
table.style.margin = '0 auto';
table.style.background = 'var(--default-bg-panel-color)';
table.style.width = '100%';
let thead = document.createElement('thead');
let tbody = document.createElement('tbody');
tbody.style.borderRadius = '5px';
// Create table headers
let headers = ['Image', 'Name', 'Bonus', 'Stock', 'Price each', 'Price total', 'Lowest price in other bazaars'];
let headerRow = document.createElement('tr');
headers.forEach(function(header) {
let th = document.createElement('th');
th.style.padding = '4px';
th.textContent = header;
th.style.background = 'var(--tabs-active-bg-gradient)';
th.style.color = 'var(--default-color)';
th.style.fontWeight = 'bold';
th.style.textAlign = 'center';
th.style.padding = '8px 4px';
th.style.borderBottom = '1px solid grey';
th.style.borderBottomColor = 'var(--default-panel-divider-outer-side-color)';
headerRow.appendChild(th);
});
thead.appendChild(headerRow);
table.appendChild(thead);
// Create table body
data.forEach(function(item, index) {
let row = document.createElement('tr');
row.style.borderBottom = '1px solid grey';
row.style.borderBottomColor = 'var(--default-panel-divider-outer-side-color)';
let itemID = item.ID;
let itemUID = item.UID;
let bonusText = '';
if (itemUID) {
itemUIDs.push({ uid: itemUID, index: index});
bonusText = 'Loading...';
}
// Store itemID with index
itemIDs.push({ id: itemID, index: index});
createCell('Image', row, itemID);
createCell(item.name, row);
createCell(bonusText, row);
createCell(item.quantity, row);
createCell(item.price.toLocaleString('en-US', {style: 'currency', currency: 'USD', maximumFractiondigits: 0, minimumFractionDigits: 0}), row);
createCell((item.quantity * item.price).toLocaleString('en-US', {style: 'currency', currency: 'USD', maximumFractiondigits: 0, minimumFractionDigits: 0}), row);
createCell('Loading...', row);
tbody.appendChild(row);
});
table.appendChild(tbody);
tableDiv.appendChild(table);
contentWrapper.appendChild(tableDiv);
fetchMarketDataForItems();
fetchItemDetailsForUIDs();
}
function createCell(text, row, itemID) {
let cell = document.createElement('td');
if (text == 'Image') {
let img = document.createElement('img');
img = document.createElement('img');
img.src = `/images/items/${itemID}/large.png`;
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`;
img.alt = 'Item Image';
img.style.height = '25px';
cell.appendChild(img);
} else {
cell.textContent = text;
}
cell.style.color = 'var(--default-color)';
cell.style.textAlign = 'center';
cell.style.verticalAlign = 'middle';
cell.style.padding = '4px';
row.appendChild(cell);
}
function fetchBazaarData() {
let apiUrl = `https://api.torn.com/user/${userID}?key=${apiKey}&selections=bazaar&comment=ViewBazaarAnytime`;
fetch(apiUrl)
.then(response => response.json())
.then(data => {
createTable(data.bazaar);
apiCallCount++;
})
.catch(error => console.error('Error fetching data: ' + error));
}
function fetchMarketDataForItems() {
let itemsToFetch = Math.min(maximumCalls, itemIDs.length);
for (let i = 0; i < itemsToFetch; i++) {
let { id, index } = itemIDs[i];
fetchMarketData(id, index);
}
if (itemIDs.length > maximumCalls) {
setTimeout(function () {
fetchMarketDataForItems();
}, 60000);
}
}
function fetchMarketData(itemID, index) {
let apiUrl = `https://api.torn.com/market/${itemID}?selections=bazaar&key=${apiKey}&comment=ViewBazaarAnytime`;
// Make the API call
fetch(apiUrl)
.then(response => response.json())
.then(data => {
if (data.bazaar && data.bazaar.length > 0) {
let lowestPrice = data.bazaar[0].cost;
// Update table with lowest bazaar price
let table = document.querySelector('table');
let cell = table.rows[index + 1].cells[6];
cell.textContent = lowestPrice.toLocaleString('en-US', {style: 'currency', currency: 'USD', maximumFractiondigits: 0, minimumFractionDigits: 0});
// Remove from itemIDs
itemIDs = itemIDs.filter(item => item.id !== itemID);
} else {
throw new Error('No items found in the bazaar');
}
})
.catch(error => console.error('Error fetching data: ' + error));
}
function fetchItemDetailsForUIDs() {
console.log('Fetching all item details');
let itemsToFetch = Math.min(maximumCalls, itemUIDs.length);
for (let i = 0; i < itemsToFetch; i++) {
let { uid, index } = itemUIDs[i];
fetchItemDetails(uid, index);
}
if (itemIDs.length > maximumCalls) {
setTimeout(function () {
fetchItemDetailsForUIDs();
}, 60000);
}
}
function fetchItemDetails(itemUID, index) {
console.log('Fetching item details');
let apiUrl = `https://api.torn.com/torn/${itemUID}?selections=itemdetails&key=${apiKey}&comment=ViewBazaarAnytime`;
console.log(apiUrl);
// Make the API call
fetch(apiUrl)
.then(response => response.json())
.then(data => {
if (data.itemdetails) {
let rarity = data.itemdetails.rarity;
if (rarity == 'None') {
rarity = '';
}
let quality = data.itemdetails.quality;
let bonuses = data.itemdetails.bonuses;
let bonusText = '';
if (bonuses) {
if (Object.keys(bonuses).length === 1) {
// If there is only one bonus
let bonus = bonuses[Object.keys(bonuses)[0]];
bonusText = `<p style="padding:4px">${bonus.value}% ${bonus.bonus}</p>`;
} else if (Object.keys(bonuses).length === 2) {
// If there are two bonuses
Object.keys(bonuses).forEach(key => {
let bonus = bonuses[key];
bonusText += `<p style="padding:4px">${bonus.value}% ${bonus.bonus}</p>`;
});
}
}
// Update table with bonus text
let table = document.querySelector('table');
let cell = table.rows[index + 1].cells[2];
cell.innerHTML = `<p style="padding:4px">${rarity}<p>${quality} Quality</p>${bonusText}`;
// Remove from itemUIDs
itemUIDs = itemUIDs.filter(item => item.uid !== itemUID);
} else {
throw new Error('No item details found');
}
})
.catch(error => console.error('Error fetching data: ' + error));
}
checkUserID();
setTimeout(checkElement, 300);
})();