From 1883d5a594b3d6887c22c1e61d30cf45c33e96bf Mon Sep 17 00:00:00 2001 From: Matt Holland Date: Tue, 7 Jul 2026 01:42:48 -0400 Subject: [PATCH] Added basic element for travel item appraisal. --- travel-time/travel-helper.js | 161 ++++++++++++++++++++++++++--------- 1 file changed, 121 insertions(+), 40 deletions(-) diff --git a/travel-time/travel-helper.js b/travel-time/travel-helper.js index 6564721..f5b7b2c 100644 --- a/travel-time/travel-helper.js +++ b/travel-time/travel-helper.js @@ -1,11 +1,12 @@ // ==UserScript== // @name Torn - Arrival Time Predictor // @namespace http://tampermonkey.net/ -// @version 1.02 +// @version 1.03 // @description Calculates and displays the exact local time you will land while traveling // @author You // @match https://www.torn.com/page.php?sid=travel // @grant GM_addStyle +// @grant GM_xmlhttpRequest // ==/UserScript== (function() { @@ -25,6 +26,13 @@ } `); + // Setup Formatter + const currencyFormatter = new Intl.NumberFormat('en-US', { + style: 'currency', + currency: 'USD', + maximumFractionDigits: 0, + }); + //const flight_image = document.querySelector("#travel-root > div.viewport___cdEK9 > div.airspaceScene___euCKY.outboundFlight___rIvIA"); //flight_image.remove(); @@ -40,48 +48,121 @@ }, 500); function initClockExtension(timerElement) { - // Create a custom UI box to display our text safely without breaking Torn's CSS - const travelRoot = document.querySelector("#travel-root"); - const parentContainer = travelRoot.querySelector('[class^="progressText"]'); - // Remove travel fact - travelRoot.querySelector('[class^="factWrapper"]').remove(); - // Remove flight scene - travelRoot.querySelector('[class^="airspaceScene"]').remove(); - const ogTimer = document.querySelector("#travel-root > div.viewport___cdEK9 > div.flightProgressSection___OEJoR > div.progressText___BEQ0k > div > span"); - var timerContainer = null; - const timeDisplay = document.createElement('div'); - timeDisplay.style.cssText = 'padding: 10px; margin-top: 10px; color: #fff; text-align: center; border-radius: 5px; font-weight: bold; border: 1px solid #444; transition: color 0.5s ease; font-weight: bold; font-size: 1.5rem;'; - //timerElement.parentNode.insertBefore(timeDisplay, timerElement.nextSibling); - parentContainer.insertAdjacentElement('beforebegin', timeDisplay); - timeDisplay.innerText = 'Calculating landing time...'; - timerContainer = timeDisplay; - - // Update the landing time prediction every single second - setInterval(() => { - const timeString = timerElement.innerText; // Grabs 'HH:MM:SS' from the page - if (!timeString) return; + // Create a custom UI box to display our text safely without breaking Torn's CSS + const travelRoot = document.querySelector("#travel-root"); + const parentContainer = travelRoot.querySelector( + '[class^="progressText"]', + ); + // Remove travel fact + travelRoot.querySelector('[class^="factWrapper"]').remove(); + // Remove flight scene + travelRoot.querySelector('[class^="airspaceScene"]').remove(); + const ogTimer = document.querySelector( + "#travel-root > div.viewport___cdEK9 > div.flightProgressSection___OEJoR > div.progressText___BEQ0k > div > span", + ); + var timerContainer = null; + const timeDisplay = document.createElement("div"); + timeDisplay.style.cssText = + "padding: 10px; margin-top: 10px; color: #fff; text-align: center; font-weight: bold; transition: color 0.5s ease; font-weight: bold; font-size: 1.5rem;"; + //timerElement.parentNode.insertBefore(timeDisplay, timerElement.nextSibling); + parentContainer.insertAdjacentElement("beforebegin", timeDisplay); + timeDisplay.innerText = "Calculating landing time..."; + timerContainer = timeDisplay; - const timeParts = timeString.split(':'); - if (timeParts.length === 3) { - const hours = parseInt(timeParts[0], 10); - const minutes = parseInt(timeParts[1], 10); - const seconds = parseInt(timeParts[2], 10); - - // Convert countdown duration entirely to total milliseconds - const msRemaining = ((hours * 60 * 60) + (minutes * 60) + seconds) * 1000; - // Blink the text if time left is < 2 minutes. - if (msRemaining <= 120000) { - // Add the blinking class to the text. - timerContainer.classList.add('pulse-text'); + // Enumerate Inventory items + var travelInventory = travelRoot.querySelectorAll('[class^="itemButton"]'); + var iteminfo = []; + console.log(`${travelInventory.length} items found.`); // TODO: DEBUG: Remove. + if (travelInventory.length > 0) { + travelInventory.forEach((item) => { + let itemImg = item.querySelector("img"); // Use the image URL to get the item ID + if (itemImg) { + let splitUrl = itemImg.src.split("/"); // Split the URL by the forward slashes + const id = splitUrl[5]; // The 6th element is the Item ID + var itemIndex = iteminfo.findIndex(item => item.id === id); + if (itemIndex === -1) { + itemIndex = iteminfo.push( { id: id, qty: 0, item_name: "", market: 0, bazaar_min: 0, bazaar_average: 0}) - 1; } - - // Add the flight duration to the current system date/time - const landingTime = new Date(Date.now() + msRemaining); - - // Format the output neatly (e.g., "16:24:05") - timerContainer.innerText = `Estimated Landing: ${landingTime.toLocaleTimeString()}`; + iteminfo[itemIndex].qty++; } - }, 1000); + }); + iteminfo.forEach(item => { + getPriceInfo(item.id); + }); + } + // Function to update the page with price info + function updatePriceInfo() { + var totalValue = 0; + var values = { + market: 0, + bazaar_average: 0, + bazaar_min: 0 + }; + + const invPanel = travelRoot.querySelector('[class^="inventoryPanel"]'); + const appraisePanel = document.createElement("div"); + appraisePanel.style.cssText = "color: #0c0; font-weight: bold; font-size: 1.2rem;" + invPanel.parentNode.insertBefore(appraisePanel, invPanel.nextSibling); + appraisePanel.innerText = "Appraising items..."; + iteminfo.forEach(item => { + console.log(`Market Value of ${item.qty} ${item.item_name}: ${item.market * item.qty}. Bazaar Avg: ${item.bazaar_average * item.qty}. Bazaar Min: ${item.bazaar_min * item.qty}`); + values.bazaar_min += currencyFormatter.format(item.bazaar_min * item.qty); + values.bazaar_average += currencyFormatter.format(item.bazaar_average * item.qty); + values.market += currencyFormatter.format(item.market * item.qty); + }); + // TODO: Update UI Elements + appraisePanel.innerText = `Quick Sell: ${values.bazaar_min} | Market: ${values.market} | Bazaar Avg: ${values.bazaar_average}`; + } + // Function to get price information + function getPriceInfo(itemId) { + const w3bPriceUrl = "https://weav3r.dev/api/marketplace/" + itemId; // API Endpoint for W3B Market Information + var data = null; + GM_xmlhttpRequest({ + method: "GET", + url: w3bPriceUrl, + headers: { "Content-Type": "application/json" }, + onload: function(response) { + if (response.status === 200) { + data = JSON.parse(response.responseText); + // Start updating the item's information. + let index = iteminfo.findIndex(item => item.id === itemId); + iteminfo[index].item_name = data.item_name; + iteminfo[index].market = data.market_price; + iteminfo[index].bazaar_min = data.listings[0].price; + iteminfo[index].bazaar_average = data.bazaar_average; + + updatePriceInfo(); // Update the UI + } + } + }); + } + // Update the landing time prediction every single second + setInterval(() => { + const timeString = timerElement.innerText; // Grabs 'HH:MM:SS' from the page + if (!timeString) return; + + const timeParts = timeString.split(":"); + if (timeParts.length === 3) { + const hours = parseInt(timeParts[0], 10); + const minutes = parseInt(timeParts[1], 10); + const seconds = parseInt(timeParts[2], 10); + + // Convert countdown duration entirely to total milliseconds + const msRemaining = (hours * 60 * 60 + minutes * 60 + seconds) * 1000; + // Blink the text if time left is < 2 minutes. + if (msRemaining <= 120000) { + // Add the blinking class to the text. + timerContainer.classList.add("pulse-text"); + } + + // Add the flight duration to the current system date/time + const landingTime = new Date(Date.now() + msRemaining); + + // Format the output neatly (e.g., "16:24:05") + timerContainer.innerText = `Estimated Landing: ${landingTime.toLocaleTimeString()}`; + } + }, 1000); + } })(); \ No newline at end of file