您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Displays a player's net worth on their Torn profile page using API v2, with a user-configurable API key.
// ==UserScript== // @name TORN: Easy Player Net Worth Display // @namespace http://tampermonkey.net/ // @version 3.1 // @description Displays a player's net worth on their Torn profile page using API v2, with a user-configurable API key. // @author JohnBattlefield // @match https://www.torn.com/profiles.php* // @grant GM_xmlhttpRequest // @grant GM_setValue // @grant GM_getValue // @grant GM_deleteValue // @license MIT License // ==/UserScript== (function() { 'use strict'; // Tampermonkey storage key for the API key const API_KEY_STORAGE_KEY = 'tornNetWorthApiKey'; // Function to get a URL parameter by name function getUrlParameter(name) { name = name.replace(/[\[]/, '\\[').replace(/[\]]/, '\\]'); const regex = new RegExp('[\\?&]' + name + '=([^&#]*)'); const results = regex.exec(location.search); return results === null ? '' : decodeURIComponent(results[1].replace(/\+/g, ' ')); } // Function to simplify a number and add a suffix (k, M, B, T) function formatNumberToSimplified(num) { if (num >= 1000000000000) { return `$${(num / 1000000000000).toFixed(2)}T`; } else if (num >= 1000000000) { return `$${(num / 1000000000).toFixed(2)}B`; } else if (num >= 1000000) { return `$${(num / 1000000).toFixed(0)}M`; } else if (num >= 1000) { return `$${(num / 1000).toFixed(0)}k`; } // For numbers less than a thousand, return the formatted number return new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD', minimumFractionDigits: 0, maximumFractionDigits: 0 }).format(num); } // Function to get a color based on the net worth value function getNetWorthBadgeColor(num) { if (num >= 1000000000000) { return '#6a1b9a'; // Trillions (Darker Purple) } else if (num >= 100000000000) { return '#993333'; // 100B - 1T (Maroon) } else if (num >= 10000000000) { return '#c06015'; // 10B - 100B (Darker Orange) } else if (num >= 1000000000) { return '#008080'; // 1B - 10B (Teal) } else if (num >= 1000000) { return '#388e3c'; // Millions (Darker Green) } else { return '#3a556b'; // Thousands or less } } // Function to create and insert the net worth element function displayNetWorth(netWorth) { const playerNameElement = document.querySelector('h4#skip-to-content'); if (!playerNameElement) { console.warn("Torn Net Worth Script: Could not find player name element to insert net worth."); return; } if (document.getElementById('torn-net-worth')) { console.log("Torn Net Worth Script: Net worth element already exists, skipping insertion."); return; } const netWorthBadgeElement = document.createElement('span'); netWorthBadgeElement.id = 'torn-net-worth'; netWorthBadgeElement.style.marginLeft = '20px'; // Increased space netWorthBadgeElement.style.fontSize = '14px'; netWorthBadgeElement.style.cursor = 'pointer'; netWorthBadgeElement.style.fontWeight = 'bold'; // Add a click event listener to show the API key input form netWorthBadgeElement.addEventListener('click', () => { showApiKeyInput(); }); // Create the combined text for the badge const simplifiedNetWorthText = formatNumberToSimplified(netWorth); netWorthBadgeElement.textContent = `Net Worth: ${simplifiedNetWorthText}`; // Apply badge styling netWorthBadgeElement.style.backgroundColor = getNetWorthBadgeColor(netWorth); netWorthBadgeElement.style.color = 'white'; netWorthBadgeElement.style.padding = '4px 8px'; netWorthBadgeElement.style.borderRadius = '4px'; // Less round edges netWorthBadgeElement.style.fontWeight = 'bold'; netWorthBadgeElement.style.display = 'inline-block'; playerNameElement.appendChild(netWorthBadgeElement); console.log("Torn Net Worth Script: Net worth element successfully inserted."); } // Helper function to display messages in the UI function showMessage(message, isError = false) { let messageBox = document.getElementById('api-key-message'); if (!messageBox) { const formContainer = document.getElementById('api-key-form'); if (formContainer) { messageBox = document.createElement('p'); messageBox.id = 'api-key-message'; messageBox.style.marginTop = '10px'; formContainer.appendChild(messageBox); } else { return; } } messageBox.textContent = message; messageBox.style.color = isError ? 'red' : '#38a169'; } // Function to show the API key input form function showApiKeyInput() { const playerNameElement = document.querySelector('h4#skip-to-content'); if (!playerNameElement) { console.warn("Torn Net Worth Script: Could not find player name element to insert API key form."); return; } if (document.getElementById('api-key-form')) { return; } const existingNetWorthElement = document.getElementById('torn-net-worth'); if (existingNetWorthElement) { existingNetWorthElement.remove(); } const formContainer = document.createElement('div'); formContainer.id = 'api-key-form'; formContainer.style.marginTop = '10px'; formContainer.style.fontSize = '12px'; formContainer.style.backgroundColor = 'var(--c-background-secondary, #333)'; formContainer.style.color = 'var(--c-text-primary, #fff)'; formContainer.style.border = '1px solid var(--c-border-primary, #555)'; formContainer.style.padding = '10px'; formContainer.style.borderRadius = '5px'; formContainer.style.display = 'flex'; formContainer.style.flexDirection = 'column'; formContainer.style.gap = '15px'; // Increased gap for better mobile spacing formContainer.innerHTML = ` <div> <p style="font-weight: bold; margin-bottom: 10px;">API Key Required</p> <p style="margin: 0 0 10px 0;"> Please enter your Torn API key. </p> <p style="margin: 0;"> <a href="https://www.torn.com/preferences.php#tab=api" target="_blank" style="color: #38a169; text-decoration: underline;">Get your API key here.</a> </p> </div> <div style="display: flex; gap: 5px; flex-wrap: wrap;"> <input type="text" id="api-key-input" placeholder="Enter API Key" style="flex-grow: 1; padding: 5px; background-color: var(--c-background-primary, #444); color: var(--c-text-primary, #fff); border: 1px solid var(--c-border-primary, #666); border-radius: 3px; min-width: 150px;"> <button id="save-key-btn" style="padding: 5px 10px; background-color: #38a169; color: white; border: none; border-radius: 3px; cursor: pointer;">Save</button> <button id="clear-key-btn" style="padding: 5px 10px; background-color: #d9534f; color: white; border: none; border-radius: 3px; cursor: pointer;">Clear</button> </div> `; playerNameElement.parentNode.insertBefore(formContainer, playerNameElement.nextSibling); document.getElementById('save-key-btn').addEventListener('click', () => { const key = document.getElementById('api-key-input').value; if (key) { GM_setValue(API_KEY_STORAGE_KEY, key); showMessage('API key saved! Reloading page to apply changes.'); setTimeout(() => location.reload(), 1000); } else { showMessage('Please enter a valid API key.', true); } }); document.getElementById('clear-key-btn').addEventListener('click', () => { GM_deleteValue(API_KEY_STORAGE_KEY); showMessage('API key cleared! Reloading page.'); setTimeout(() => location.reload(), 1000); }); } // Main function to fetch the data and display it async function fetchAndDisplayNetWorth() { const oldNetWorthElement = document.getElementById('torn-net-worth'); if (oldNetWorthElement) { oldNetWorthElement.remove(); } const playerId = getUrlParameter('XID'); const apiKey = await GM_getValue(API_KEY_STORAGE_KEY); if (!apiKey) { console.log("Torn Net Worth Script: No API key found. Showing input form."); showApiKeyInput(); return; } if (playerId && apiKey) { console.log(`Torn Net Worth Script: Initiating API request for Player ID: ${playerId} using V2 API`); const apiUrl = `https://api.torn.com/v2/user/${playerId}/personalstats?cat=networth&key=${apiKey}`; GM_xmlhttpRequest({ method: "GET", url: apiUrl, onload: function(response) { console.log("Torn Net Worth Script: API response received. Status:", response.status); try { const data = JSON.parse(response.responseText); // Check for API errors first if (data && data.error) { console.error("Torn Net Worth Script: API returned an error:", data.error); // If the API key is invalid, delete it and prompt for a new one. if (data.error.code === 2 || data.error.code === 3 || data.error.code === 10) { GM_deleteValue(API_KEY_STORAGE_KEY); showApiKeyInput(); } else { // For other errors, just display 0 net worth to avoid the loop displayNetWorth(0); } return; } if (data && data.personalstats && data.personalstats.networth) { // Check if 'total' property exists. It might be 0 or negative, which is a valid value. const totalNetWorth = data.personalstats.networth.total !== undefined ? data.personalstats.networth.total : 0; console.log(`Torn Net Worth Script: Net worth data found. Total: ${totalNetWorth}`); displayNetWorth(totalNetWorth); } else { // If data structure is unexpected, display 0 net worth to avoid the API key loop. console.error("Torn Net Worth Script: Could not retrieve net worth data. Full response:", data); displayNetWorth(0); } } catch (e) { console.error("Torn Net Worth Script: Failed to parse JSON response. Response text:", response.responseText, "Error:", e); GM_deleteValue(API_KEY_STORAGE_KEY); showApiKeyInput(); } }, onerror: function(response) { console.error("Torn Net Worth Script: GM_xmlhttpRequest failed. Status:", response.status, "Status Text:", response.statusText, "Response Text:", response.responseText); GM_deleteValue(API_KEY_STORAGE_KEY); showApiKeyInput(); } }); } } const observer = new MutationObserver(function(mutations) { mutations.forEach(function(mutation) { if (mutation.type === 'childList' && mutation.addedNodes.length > 0) { for (const node of mutation.addedNodes) { if (node.querySelector && node.querySelector('h4#skip-to-content')) { console.log("Torn Net Worth Script: Profile header element detected, running script."); fetchAndDisplayNetWorth(); return; } } } }); }); observer.observe(document.body, { childList: true, subtree: true }); console.log("Torn Net Worth Script: Initial script run initiated."); fetchAndDisplayNetWorth(); })();