您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
View Torn's point market from hospital, jail or travelling!
// ==UserScript== // @name View Points Market Anytime // @namespace heartflower.torn.com // @version 1.2 // @description View Torn's point market from hospital, jail or travelling! // @author Heartflower [2626587] // @match https://www.torn.com/pmarket.php* // @icon https://www.google.com/s2/favicons?sz=64&domain=torn.com // @grant GM_addStyle // ==/UserScript== (function() { 'use strict'; // Set to 'true' if you want to highlight your own point listings // If set to 'true', you need a full access key, otherwise you need a public key let highlightSelf = true; // Set to 'true' if you want to show a link to remove your API key let removeKeyLink = true; // Fetch API key from storage let apiKey = ''; let storedAPIKey; if (highlightSelf === true) { storedAPIKey = localStorage.getItem('hf-full-access-apiKey'); } else if (highlightSelf === false) { storedAPIKey = localStorage.getItem('hf-public-apiKey'); } if (storedAPIKey) { apiKey = storedAPIKey; } // Set some variables to use later let personalListings = []; let timestamp = 0; let hospitalTimestamp = 0; // Find the info message on Torn function findMessage() { let contentWrapper = document.body.querySelector('.content-wrapper'); // If you can't find the content wrapper, try again if (!contentWrapper) { console.log(`Can't find content wrapper`); setTimeout(findMessage, 100); } let messageContainer = document.body.querySelector('.info-msg'); let message = messageContainer.querySelector('.msg'); // If you can't find the message, try again if (!message) { console.log(`Can't find message`); setTimeout(findMessage, 100); } let messageContent = message.textContent; // If the points market is available, don't show the table if (!messageContent.includes('This area is unavailable')) { console.log('Is available'); return; } let originalTitle = document.querySelector('h4#skip-to-content'); if (originalTitle) { originalTitle.parentNode.remove(); } // Create the points market title as it's not usually there let titleContainer = document.createElement('div'); let title = document.createElement('p'); title.id = 'hf-pmv-title'; title.textContent = 'Points Market'; // Add a line break after the title let hrElement = document.createElement('hr'); hrElement.className = 'page-head-delimiter m-top10 m-bottom10'; titleContainer.appendChild(title); titleContainer.appendChild(hrElement); contentWrapper.insertBefore(titleContainer, contentWrapper.firstChild); // Change the message message.innerHTML = `The points market is fetched by the <p style='font-style:italic; display:inline'>View Points Market Anytime</p> script with the help of the API!`; messageContainer.style.background = 'rgb(85,137,33)'; if (apiKey !== '') { if (highlightSelf === true) { fetchLogs(); fetchHospitalTime(); } else if (highlightSelf === false) { fetchPointsMarket(); fetchHospitalTime(); } if (removeKeyLink === true) { let link = document.createElement('a'); link.href = '#'; // Set a placeholder href if (highlightSelf === true) { link.textContent = ' Click here to remove your (full access) API key!'; } else if (highlightSelf === false) { console.log('Remove public'); 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 if (highlightSelf === true) { localStorage.removeItem('hf-full-access-apiKey'); alert('API key successfully removed!'); link.remove(); } else if (highlightSelf === false) { localStorage.removeItem('hf-public-apiKey'); alert('API key successfully removed!'); link.remove(); } let enterLink = document.createElement('a'); enterLink.href = '#'; // Set a placeholder href if (highlightSelf === true) { enterLink.textContent = ' Click here to enter your (full access) API key!'; } else if (highlightSelf === false) { 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(); }); message.appendChild(enterLink); }); message.appendChild(link); } } else { // If no API key is found, create a link to set one console.error('API key not found!'); let link = document.createElement('a'); link.href = '#'; // Set a placeholder href if (highlightSelf === true) { link.textContent = ' Click here to enter your (full access) API key!'; } else if (highlightSelf === false) { 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(); link.remove(); }); message.appendChild(link); } // Add another line break after the message let anotherHrElement = document.createElement('hr'); anotherHrElement.className = 'page-head-delimiter m-top10 m-bottom10'; contentWrapper.appendChild(anotherHrElement); } // Prompt the user to enter their API key function promptAPIKey() { let enterAPIKey = null; if (highlightSelf === true) { enterAPIKey = prompt('Enter a full access API key here:'); } else if (highlightSelf === false) { enterAPIKey = prompt('Enter a public API key here:'); } if (enterAPIKey !== null && enterAPIKey.trim() !== '') { if (highlightSelf === true) { localStorage.setItem('hf-full-access-apiKey', enterAPIKey); } else if (highlightSelf === false) { localStorage.setItem('hf-public-apiKey', enterAPIKey); } alert('API key set succesfully'); if (highlightSelf === true) { fetchLogs(); fetchHospitalTime(); } else { fetchPointsMarket(); fetchHospitalTime(); } } else { alert('No valid API key entered!'); } } // Fetch the logs to fetch own listing IDs function fetchLogs() { let apiUrl = `https://api.torn.com/user/?key=${apiKey}&selections=log&log=5000&comment=ViewPointsMarketAnytime`; fetch(apiUrl) .then(response => response.json()) .then(data => { let logData = data.log; for (let logID in logData) { let log = logData[logID]; let listingID = log.data.listing_id; personalListings.push(listingID); } fetchPointsMarket(); }) .catch(error => console.error('Error fetching data: ' + error)); } // Fetch the points market to fetch currently available listings function fetchPointsMarket() { let apiUrl = `https://api.torn.com/market/?selections=pointsmarket,timestamp&key=${apiKey}&comment=ViewPointsMarketAnytime`; fetch(apiUrl) .then(response => response.json()) .then(data => { let pointsMarket = data.pointsmarket; timestamp = data.timestamp; addTimer(); createTable(pointsMarket); }) .catch(error => console.error('Error fetching data: ' + error)); } // Fetch and display the time remaining in the hospital function fetchHospitalTime() { console.log('Fetching hospital time'); let messageContainer = document.body.querySelector('.info-msg'); let message = messageContainer.querySelector('.msg'); if (!message) { console.error(`Can't find message!`) setTimeout(fetchHospitalTime, 100); return; } let apiUrl = `https://api.torn.com/user/?selections=basic&key=${apiKey}&comment=ViewPointsMarketAnytime`; fetch(apiUrl) .then(response => response.json()) .then(data => { let status = data.status; hospitalTimestamp = status.until; console.log(messageContainer); let timeRemaining = 0; let timerElement = document.createElement('div'); timerElement.id = 'hf-pmv-hospital-timer'; timerElement.style.display = 'inline'; timerElement.style.paddingLeft = '4px'; if (removeKeyLink === true) { message.insertBefore(timerElement, message.lastChild); } else if (removeKeyLink === false) { message.appendChild(timerElement); } updateTimer(); setInterval(updateTimer, 100); }) .catch(error => console.error('Error fetching data: ' + error)); } // Function to calculate remaining time function calculateTimeRemaining() { let currentTime = Math.floor(Date.now() / 1000); // Current time in seconds let timeDifference = hospitalTimestamp - currentTime; if (timeDifference <= 0) { return "You're out of the hospital!"; } let days = Math.floor(timeDifference / (24 * 60 * 60)); let hours = Math.floor((timeDifference % (24 * 60 * 60)) / (60 * 60)); let minutes = Math.floor((timeDifference % (60 * 60)) / 60); let seconds = timeDifference % 60; let remainingTime = "You will be out of the hospital in "; // Days if (days > 0) { remainingTime += `${days} ${days === 1 ? 'day' : 'days'}, `; } // Hours if (hours > 0) { remainingTime += `${hours} ${hours === 1 ? 'hour' : 'hours'}, `; } // Minutes if (minutes > 0) { remainingTime += `${minutes} ${minutes === 1 ? 'minute' : 'minutes'} and `; } // Seconds remainingTime += `${seconds} ${seconds === 1 ? 'second' : 'seconds'}`; remainingTime += '.'; return remainingTime; } // Function to update timer display function updateTimer(hospitalTimestamp) { let timerElement = document.getElementById('hf-pmv-hospital-timer'); if (timerElement) { timerElement.textContent = calculateTimeRemaining(hospitalTimestamp); } } // Create the table function createTable(data) { let contentWrapper = document.body.querySelector('.content-wrapper'); // If the page hasn't fully loaded in yet, try again if (!contentWrapper) { console.log('No content wrapper'); setTimeout(function () { createTable(data); }, 100); } let existingContainer = document.getElementById('hf-pmv-table'); // If the table already exists, don't add it again if (existingContainer) { console.log('Table already there'); return; } let tableRows = []; // Sort listing IDs by cost for (let listingID in data) { let listing = data[listingID]; let cost = listing.cost; let quantity = listing.quantity; let totalCost = listing.total_cost; let row = { listingID: listingID, cost: cost, quantity: quantity, totalCost: totalCost }; tableRows.push(row); } tableRows.sort((a, b) => a.cost - b.cost); // Create the table container let table = document.createElement('ul'); table.id = 'hf-pmv-table'; // Create the header row let headerRow = document.createElement('li'); headerRow.className = 'hf-pmv-table-header'; createTableCell(headerRow, 'hf-pmv-cost', 'Cost'); createTableCell(headerRow, 'hf-pmv-qty', 'Quantity'); createTableCell(headerRow, 'hf-pmv-total', 'Total'); table.appendChild(headerRow); // Loop through every listingID to fetch the necessary info for (let i = 0; i < tableRows.length; i++) { let row = tableRows[i]; let numericListingID = parseInt(row.listingID); // Check if the listing ID is part of the personal listings fetched from the logs if (personalListings.includes(numericListingID)) { createTableRow(table, 'self', row.listingID, row.cost, row.quantity, row.totalCost); } else { createTableRow(table, 'other', row.listingID, row.cost, row.quantity, row.totalCost); } } contentWrapper.appendChild(table); } // Create a table row with the necessary data function createTableRow(table, info, listingID, cost, quantity, totalCost) { let tableRow = document.createElement('li'); tableRow.id = 'hf-pmv-' + listingID; tableRow.className = 'hf-pmv-table-row'; let costText = cost.toLocaleString('en-US', {style: 'currency', currency: 'USD', maximumFractionDigits: 0, minimumFractionDigits: 0}); createTableCell(tableRow, 'hf-pmv-cost', costText) let quantityText = quantity.toLocaleString('en-US'); createTableCell(tableRow, 'hf-pmv-qty', quantityText) let totalCostText = totalCost.toLocaleString('en-US', {style: 'currency', currency: 'USD', maximumFractionDigits: 0, minimumFractionDigits: 0}); createTableCell(tableRow, 'hf-pmv-total', totalCostText) table.appendChild(tableRow); if (info === 'self') { tableRow.style.background = 'rgba(85,137,33,.5)'; } } // Create a table cell with the necessary data function createTableCell(tableRow, className, textContent) { let span = document.createElement('span'); span.className = className; span.textContent = textContent; tableRow.appendChild(span); } // Add a refresh button function addRefreshButton() { let existingButton = document.getElementById('hf-refresh'); if (existingButton) { return; } let timer = document.getElementById('hf-refresh-timer'); if (timer) { timer.remove(); } let title = document.getElementById('hf-pmv-title'); let refreshButton = document.createElement('button'); refreshButton.id = 'hf-refresh'; refreshButton.style.float = 'right'; refreshButton.innerHTML = `<svg class="hf-refresh-svg" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 14.47"><defs><style>.cls-1{opacity:0.35;}.cls-2{fill:#fff;}.cls-3{fill:#777;}</style></defs><g id="Слой_2" data-name="Слой 2"><g id="icons"><g class="cls-1"><path class="cls-2" d="M1.68,7.74A5.05,5.05,0,0,1,11.5,6.05H8.42l3.84,4L16,6.05H13.26A6.74,6.74,0,1,0,11,13l-1.06-1.3A5.06,5.06,0,0,1,1.68,7.74"></path></g><path class="cls-3" d="M1.68,6.74A5.05,5.05,0,0,1,11.5,5.05H8.42l3.84,4L16,5.05H13.26A6.74,6.74,0,1,0,11,12l-1.06-1.3A5.06,5.06,0,0,1,1.68,6.74"></path></g></g></svg>`; title.appendChild(refreshButton); // Add event listener to the button refreshButton.addEventListener('click', function() { location.reload(); }); let cls2 = refreshButton.querySelector('.cls-2'); let cls3 = refreshButton.querySelector('.cls-3'); // Add event listeners for hover refreshButton.addEventListener('mouseenter', function() { refreshButton.style.color = 'var(--default-blue-color)'; cls2.style.fill = 'var(--default-blue-color)'; cls3.style.fill = 'var(--default-blue-color)'; }); refreshButton.addEventListener('mouseleave', function() { refreshButton.style.color = 'var(--default-color)'; cls2.style.fill = 'var(--default-color)'; cls3.style.fill = 'var(--default-color)'; }); } // Add a timer to count down to refresh time function addTimer() { let refreshTimestamp = timestamp + 30; let intervalID = setInterval(() => { let currentTimestamp = Math.floor(Date.now() / 1000); let remainingTime = refreshTimestamp - currentTimestamp; if (remainingTime <= 0) { clearInterval(intervalID); addRefreshButton(); return; } let title = document.getElementById('hf-pmv-title'); let timer = document.getElementById('hf-refresh-timer'); if (!timer) { timer = document.createElement('span'); timer.id = 'hf-refresh-timer'; } timer.textContent = remainingTime; title.appendChild(timer); }, 1000); } // Add CSS to the document GM_addStyle(` #hf-pmv-title { padding: 4px; color: var(--content-title-color); font-weight: 700; font-size: 22px; margin: 5px 0 -5px -4px } #hf-pmv-table { text-align: right; width: fit-content; margin: auto; color: var(--default-color); } .hf-pmv-table-header { border-bottom: var(--default-panel-divider-outer-side-color) 1px solid; background: var(--title-black-gradient); border-radius: 5px 5px 0 0; width: fit-content; font-weight: bold; color: var(--tutorial-title-color); } .hf-pmv-table-row { border-bottom: var(--default-panel-divider-outer-side-color) 1px solid; border-top: var(--default-panel-divider-inner-side-color) 1px solid; background: var(--default-bg-panel-color); width: fit-content; } .hf-pmv-qty { border-left = 'var(--default-panel-divider-outer-side-color) 2px solid'; border-right = 'var(--default-panel-divider-outer-side-color) 2px solid'; } .hf-pmv-cost, .hf-pmv-qty { width: 100px; padding: 8px; display: inline-block; } .hf-pmv-total { width: 150px; padding: 8px; display: inline-block; } #hf-refresh { z-index: 9999; top: 188px; right: 475px; color: var(--default-color); cursor: pointer; width: 30px; } #hf-refresh-timer { float: right; font-size: 14px; margin-top: 6px; width: 30px; text-align: center; } .hf-refresh-svg { width: 14px } .hf-refresh-svg { margin-top: 4px; } @media only screen and (max-width: 785px) { .hf-pmv-total { width: 138px; } #hf-pmv-title { font-size: 14px; } .hf-refresh-svg, #hf-refresh-timer { margin-top: 0px; } #hf-refresh { margin-top: -2px; } } @media only screen and (max-width: 386px) { .hf-pmv-total { width: 100px; } .hf-pmv-cost, .hf-pmv-qty { width: 86px; } } `); // Call the script upon page loading findMessage(); })();