您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Highlight items in the item market/bazaars that are at or below Arson Warehouse Pricelist and Market Value
// ==UserScript== // @name Torn Item Market Highlighter // @namespace http://tampermonkey.net/ // @version 2.16 // @description Highlight items in the item market/bazaars that are at or below Arson Warehouse Pricelist and Market Value // @author You // @match https://www.torn.com/page.php?sid=ItemMarket* // @match https://www.torn.com/bazaar.php* // @icon https://www.google.com/s2/favicons?sz=64&domain=torn.com // @grant GM_getValue // @grant GM_setValue // @grant GM_addStyle // @grant GM.xmlHttpRequest // ==/UserScript== (function() { 'use strict'; GM_addStyle(` /* Existing styles */ .price-indicators-row { display: inline-flex; gap: 4px; margin-left: 4px; font-size: 10px; vertical-align: middle; } .price-indicator { padding: 1px 3px; border-radius: 3px; font-weight: bold; white-space: nowrap; display: inline-flex; align-items: center; justify-content: center; gap: 2px; min-width: 44px; max-width: fit-content; text-align: center; } .diff-90-100 { background: #004d00; color: white; } .diff-60-90 { background: #006700; color: white; } .diff-30-60 { background: #008100; color: white; } .diff-0-30 { background: #009b00; color: white; } .diff0-30 { background: #cc0000; color: white; width: fit-content; padding: 1px 4px; } .diff30-60 { background: #b30000; color: white; width: fit-content; padding: 1px 4px; } .diff60-90 { background: #990000; color: white; width: fit-content; padding: 1px 4px; } .diff90-plus { background: #800000; color: white; width: fit-content; padding: 1px 4px; } .diff-equal { background: #666666; color: white; width: fit-content; padding: 1px 4px; } .icon-exchange { display: inline-block; width: 12px; height: 12px; background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3E%3Cpath fill='white' d='M0 168v-16c0-13.255 10.745-24 24-24h360V80c0-21.367 25.899-32.042 40.971-16.971l80 80c9.372 9.373 9.372 24.569 0 33.941l-80 80C409.956 271.982 384 261.456 384 240v-48H24c-13.255 0-24-10.745-24-24zm488 152H128v-48c0-21.314-25.862-32.08-40.971-16.971l-80 80c-9.372 9.373-9.372 24.569 0 33.941l80 80C102.057 463.997 128 453.437 128 432v-48h360c13.255 0 24-10.745 24-24v-16c0-13.255-10.745-24-24-24z'/%3E%3C/svg%3E"); } .icon-store { display: inline-block; width: 12px; height: 12px; background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 616 512'%3E%3Cpath fill='white' d='M602 118.6L537.1 15C531.3 5.7 521 0 510 0H106C95 0 84.7 5.7 78.9 15L14 118.6c-33.5 53.5-3.8 127.9 58.8 136.4 4.5.6 9.1.9 13.7.9 29.6 0 55.8-13 73.8-33.1 18 20.1 44.3 33.1 73.8 33.1 29.6 0 55.8-13 73.8-33.1 18 20.1 44.3 33.1 73.8 33.1 29.6 0 55.8-13 73.8-33.1 18.1 20.1 44.3 33.1 73.8 33.1 4.7 0 9.2-.3 13.7-.9 62.8-8.4 92.6-82.8 59-136.4zM529.5 288c-10 0-19.9-1.5-29.5-3.8V384H116v-99.8c-9.6 2.2-19.5 3.8-29.5 3.8-6 0-12.1-.4-18-1.2-5.6-.8-11.1-2.1-16.4-3.6V480c0 17.7 14.3 32 32 32h448c17.7 0 32-14.3 32-32V283.2c-5.4 1.6-10.8 2.9-16.4 3.6-6.1.8-12.1 1.2-18.2 1.2z'/%3E%3C/svg%3E"); } .icon-exchange, .icon-store { display: inline-block; width: 10px; height: 10px; background-size: contain; background-repeat: no-repeat; background-position: center; vertical-align: middle; margin-right: 2px; } /* Desktop layout improvements */ @media (min-width: 785px) { .sellerRow___AI0m6 { padding: 4px 4px !important; display: flex !important; align-items: center !important; gap: 2px !important; width: 100% !important; } .thumbnail___M_h9v { flex-shrink: 0; width: 40px !important; margin-right: 4px !important; } .userInfoWrapper___B2a2P { flex-shrink: 0; min-width: 110px; margin-right: 4px !important; } /* Stack indicators only in seller rows */ .sellerRow___AI0m6 .price-indicators-row { display: inline-flex !important; flex-direction: column !important; gap: 2px !important; margin-left: 2px !important; margin-right: 0 !important; } .price___Uwiv2 { display: flex !important; align-items: center !important; flex-shrink: 0; min-width: 85px; margin-right: 0 !important; } .available___xegv_ { flex-shrink: 0; min-width: 55px; text-align: right; margin-right: 2px !important; } .buyControlsInRow___GVAKp { flex-shrink: 0; } .buyControls___MxiIN { display: flex !important; align-items: center !important; gap: 2px !important; } .amountInputWrapper___a4BMt { min-width: 55px !important; width: 55px !important; flex-shrink: 0; } .input-money { min-width: 45px !important; width: 100% !important; padding: 0 2px !important; } .buyButton___Flkhg { flex-shrink: 0; min-width: 65px; padding-left: 8px !important; padding-right: 8px !important; } .price-indicator { padding: 1px 4px !important; min-width: 0 !important; } .space___qCLQp { display: none !important; } } /* Mobile-specific styles */ @media (max-width: 784px) { .sellerRow___Ca2pK { display: grid !important; grid-template-columns: minmax(80px, 1fr) auto auto auto !important; align-items: center !important; gap: 8px !important; padding: 8px 12px !important; } .sellerRow___Ca2pK:first-child { font-weight: bold; background-color: rgba(0, 0, 0, 0.1); } .userInfoWrapper___B2a2P { min-width: 80px; max-width: 120px; } .price___v8rRx { position: relative; display: flex; flex-direction: column; align-items: center; gap: 2px; min-width: 85px; } .price-indicators-row { position: static !important; display: flex !important; flex-direction: column !important; gap: 2px !important; margin-top: 2px !important; font-size: 9px !important; align-items: center !important; } .price-indicator { padding: 1px 4px !important; white-space: nowrap !important; text-align: center !important; justify-content: center !important; width: fit-content !important; min-width: 0 !important; margin: 0 auto !important; display: inline-flex !important; align-items: center !important; } .available___jtANf { text-align: center; min-width: 30px; } .showBuyControlsButton___K8f72 { padding: 6px !important; display: flex !important; align-items: center !important; justify-content: center !important; } .userInfoHead___LXxjB, .priceHead___Yo8ku, .availableHead___BkcpB, .showBuyControlsHead___SczEn { text-align: center !important; } .icon-exchange, .icon-store { width: 8px !important; height: 8px !important; margin: 0 2px 0 0 !important; display: inline-flex !important; align-items: center !important; justify-content: center !important; } } /* Styles for the floating container */ #floating-container { position: fixed; top: 100px; left: -200px; /* Adjust this value to the negative width of the container */ z-index: 1000; background-color: rgba(0, 0, 0, 0.7); padding: 10px; border-top-right-radius: 10px; border-bottom-right-radius: 10px; color: white; transition: left 0.3s ease; } #floating-container.expanded { left: 0; } #floating-container button { display: block; width: 100%; margin-bottom: 5px; background-color: #333; color: white; border: none; padding: 8px; border-radius: 5px; cursor: pointer; } #floating-container button:hover { background-color: #555; } /* Styles for the toggle button */ #toggle-button { position: fixed; top: 100px; left: 0; background-color: rgba(0,0,0,0.7); border-top-right-radius: 5px; border-bottom-right-radius: 5px; width: 25px; height: 50px; cursor: pointer; display: flex; align-items: center; justify-content: center; color: white; font-size: 18px; z-index: 1001; } .toast { position: fixed; top: 10px; left: 50%; transform: translateX(-50%); background-color: rgba(0,0,0,0.7); color: white; padding: 10px; border-radius: 5px; z-index: 9999; /* Ensure it's above other elements */ opacity: 0; transition: opacity 0.5s ease; } .toast.show { opacity: 1; } `); let item_prices = {}; let torn_market_values = {}; try { item_prices = JSON.parse(GM_getValue("AWH_Prices", "{}")); torn_market_values = JSON.parse(GM_getValue("Torn_Market_Values", "{}")); } catch (e) {} function getTornIDFromPage() { const tornUserInput = document.getElementById('torn-user'); if (tornUserInput) { try { const userData = JSON.parse(tornUserInput.value); return userData.id; } catch (e) { console.error('Error parsing torn-user data:', e); return null; } } return null; } function createFloatingContainer() { const container = document.createElement('div'); container.id = 'floating-container'; // Start collapsed by default (left position is negative in CSS) const toggleButton = document.createElement('div'); toggleButton.id = 'toggle-button'; toggleButton.innerHTML = '☰'; // Hamburger icon toggleButton.addEventListener('click', () => { if (container.classList.contains('expanded')) { container.classList.remove('expanded'); } else { container.classList.add('expanded'); } }); const buttonsWrapper = document.createElement('div'); const addAWHButton = document.createElement('button'); addAWHButton.textContent = GM_getValue("AWH_Key", "") ? 'Edit AWH API key' : 'Add AWH API key'; addAWHButton.addEventListener('click', () => { let AWH_Key = GM_getValue("AWH_Key", ""); AWH_Key = prompt("Enter your AWH API key", AWH_Key); if (AWH_Key !== null) { // Only proceed if user didn't press Cancel if (AWH_Key.trim() === "") { // If field was cleared, remove the key and clear AWH prices GM_setValue("AWH_Key", ""); GM_setValue("AWH_Prices", "{}"); item_prices = {}; // Clear the current prices in memory showToast("AWH API key and prices removed successfully!", 'success'); addAWHButton.textContent = 'Add AWH API key'; } else { // If new key provided, save it GM_setValue("AWH_Key", AWH_Key); showToast("AWH API key saved successfully!", 'success'); addAWHButton.textContent = 'Edit AWH API key'; checkAndUpdatePrices(); } updateButtonsVisibility(); processElements(); // Refresh the display } }); const addTornButton = document.createElement('button'); addTornButton.textContent = GM_getValue("Torn_API_Key", "") ? 'Edit Torn API key' : 'Add Torn API key'; addTornButton.addEventListener('click', () => { let tornApiKey = GM_getValue("Torn_API_Key", ""); tornApiKey = prompt("Enter your Torn API key", tornApiKey); if (tornApiKey !== null) { // Only proceed if user didn't press Cancel if (tornApiKey.trim() === "") { // If field was cleared, remove the key and clear market values GM_setValue("Torn_API_Key", ""); GM_setValue("Torn_Market_Values", "{}"); torn_market_values = {}; // Clear the current values in memory showToast("Torn API key and market values removed successfully!", 'success'); addTornButton.textContent = 'Add Torn API key'; } else { // If new key provided, save it GM_setValue("Torn_API_Key", tornApiKey); showToast("Torn API key saved successfully!", 'success'); addTornButton.textContent = 'Edit Torn API key'; getTornMarketValues(); } updateButtonsVisibility(); processElements(); // Refresh the display } }); const getAWHPricesButton = document.createElement('button'); getAWHPricesButton.textContent = 'Get AWH Prices Now'; getAWHPricesButton.addEventListener('click', getAWHPrices); const getMarketValuesButton = document.createElement('button'); getMarketValuesButton.textContent = 'Get Market Values Now'; getMarketValuesButton.addEventListener('click', getTornMarketValues); buttonsWrapper.appendChild(addAWHButton); buttonsWrapper.appendChild(addTornButton); buttonsWrapper.appendChild(getAWHPricesButton); buttonsWrapper.appendChild(getMarketValuesButton); container.appendChild(buttonsWrapper); document.body.appendChild(container); document.body.appendChild(toggleButton); // Function to update button visibility based on API keys function updateButtonsVisibility() { const AWH_Key = GM_getValue("AWH_Key", ""); const tornApiKey = GM_getValue("Torn_API_Key", ""); addAWHButton.textContent = AWH_Key ? 'Edit AWH API key' : 'Add AWH API key'; addTornButton.textContent = tornApiKey ? 'Edit Torn API key' : 'Add Torn API key'; if (!AWH_Key) { getAWHPricesButton.style.display = 'none'; // Ensure AWH prices are cleared if key is removed if (Object.keys(item_prices).length > 0) { item_prices = {}; GM_setValue("AWH_Prices", "{}"); } } else { getAWHPricesButton.style.display = ''; } if (!tornApiKey) { getMarketValuesButton.style.display = 'none'; // Ensure market values are cleared if key is removed if (Object.keys(torn_market_values).length > 0) { torn_market_values = {}; GM_setValue("Torn_Market_Values", "{}"); } } else { getMarketValuesButton.style.display = ''; } } updateButtonsVisibility(); // Set initial visibility } function showToast(message, type = 'info') { const toast = document.createElement('div'); toast.className = 'toast'; toast.textContent = message; if (type === 'success') { toast.style.backgroundColor = 'green'; } else if (type === 'error') { toast.style.backgroundColor = 'red'; } else { toast.style.backgroundColor = 'rgba(0,0,0,0.7)'; } document.body.appendChild(toast); setTimeout(() => { toast.classList.add('show'); }, 100); setTimeout(() => { toast.classList.remove('show'); setTimeout(() => { toast.remove(); }, 500); }, 3000); // Show for 3 seconds } function checkAndUpdatePrices() { const stored_torn_id = GM_getValue("AWH_TornID", ""); const page_torn_id = getTornIDFromPage(); const AWH_Key = GM_getValue("AWH_Key", ""); // Update stored Torn ID if we found one on the page if (page_torn_id && page_torn_id !== stored_torn_id) { GM_setValue("AWH_TornID", page_torn_id); } // Use page Torn ID if available, fall back to stored ID const torn_id = page_torn_id || stored_torn_id; if (AWH_Key) { getAWHPrices(); } } function scheduleNextUpdate() { const now = new Date(); const target = new Date(now); target.setUTCHours(20, 15, 0, 0); // 8:15 PM UTC if (now > target) { target.setDate(target.getDate() + 1); } const msUntilUpdate = target - now; setTimeout(() => { getAWHPrices(); getTornMarketValues(); scheduleNextUpdate(); }, msUntilUpdate); } function getTornMarketValues() { const tornApiKey = GM_getValue("Torn_API_Key", ""); if (!tornApiKey) { // Torn API key not set, skipping return; } GM.xmlHttpRequest({ method: "GET", url: `https://api.torn.com/torn/?key=${tornApiKey}&selections=items`, onload: function(response) { try { const data = JSON.parse(response.responseText); if (data.items) { Object.entries(data.items).forEach(([itemId, item]) => { torn_market_values[itemId] = item.market_value || 0; }); GM_setValue("Torn_Market_Values", JSON.stringify(torn_market_values)); GM_setValue("lastMarketUpdate", Date.now()); showToast('Market values updated successfully!', 'success'); processElements(); } else { showToast('No market value data received. Please check your API key.', 'error'); } } catch (e) { showToast('Error updating market values. Please check your API key.', 'error'); } }, onerror: function() { showToast('Failed to connect to Torn API. Please try again later.', 'error'); } }); } function getAWHPrices() { const AWH_Key = GM_getValue("AWH_Key", ""); const torn_id = getTornIDFromPage() || GM_getValue("AWH_TornID", ""); if (!AWH_Key) { // AWH API key not set, skipping return; } item_prices = {}; GM.xmlHttpRequest({ method: "GET", url: `https://arsonwarehouse.com/api/v1/bids/${torn_id}`, headers: { "Authorization": "Basic " + btoa(AWH_Key + ':') }, onload: function(response) { try { const items = JSON.parse(response.responseText); if (items.bids?.length > 0) { items.bids.forEach(bid => { if (bid.item_id && bid.bids?.length > 0) { item_prices[bid.item_id] = bid.bids[0].price || 0; } }); GM_setValue("AWH_Prices", JSON.stringify(item_prices)); GM_setValue("lastUpdate", Date.now()); showToast('Prices updated successfully!', 'success'); processElements(); } else { showToast('No price data received. Please check your credentials.', 'error'); } } catch (e) { showToast('Error updating prices. Please check your credentials.', 'error'); } }, onerror: function() { showToast('Failed to connect to AWH. Please try again later.', 'error'); } }); } function addPriceIndicator(itemId, itemPrice, container) { // Remove any existing indicators row const existingRow = container.nextElementSibling; if (existingRow?.classList.contains('price-indicators-row')) { existingRow.remove(); } // Create new indicators row const indicatorsRow = document.createElement('div'); indicatorsRow.classList.add('price-indicators-row'); // Get quantity if we're in a seller row let quantity = 1; if (container.closest('.sellerRow___AI0m6')) { const quantityElement = container.closest('.sellerRow___AI0m6').querySelector('.available___xegv_'); if (quantityElement) { // Extract number from "X available" text const match = quantityElement.textContent.match(/(\d+)\s+available/); quantity = match ? parseInt(match[1]) : 1; } } // AWH Price comparison (using exchange icon) if (item_prices[itemId]) { const awhPrice = item_prices[itemId]; const awhPriceDiff = Math.round(((awhPrice - itemPrice) / awhPrice) * 100 * 100) / 100; const potentialProfit = (awhPrice - itemPrice) * quantity; const awhIndicator = document.createElement('span'); awhIndicator.classList.add('price-indicator'); awhIndicator.title = `Potential profit: $${potentialProfit.toLocaleString()}` + (quantity > 1 ? ` (${quantity}x)` : ''); const icon = document.createElement('span'); icon.classList.add('icon-exchange'); awhIndicator.appendChild(icon); awhIndicator.appendChild(document.createTextNode( ` ${awhPriceDiff > 0 ? '-' : '+'}${Math.abs(Math.round(awhPriceDiff))}%` )); if (Math.abs(awhPriceDiff) < 0.5) { awhIndicator.classList.add('diff-equal'); } else if (awhPriceDiff > 0) { if (awhPriceDiff >= 90) awhIndicator.classList.add('diff-90-100'); else if (awhPriceDiff >= 60) awhIndicator.classList.add('diff-60-90'); else if (awhPriceDiff >= 30) awhIndicator.classList.add('diff-30-60'); else awhIndicator.classList.add('diff-0-30'); } else { if (awhPriceDiff <= -90) awhIndicator.classList.add('diff90-plus'); else if (awhPriceDiff <= -60) awhIndicator.classList.add('diff60-90'); else if (awhPriceDiff <= -30) awhIndicator.classList.add('diff30-60'); else awhIndicator.classList.add('diff0-30'); } indicatorsRow.appendChild(awhIndicator); } // Market Value comparison (using store icon) if (torn_market_values[itemId]) { const marketValue = torn_market_values[itemId]; const marketPriceDiff = Math.round(((marketValue - itemPrice) / marketValue) * 100 * 100) / 100; const potentialProfit = (marketValue - itemPrice) * quantity; const marketIndicator = document.createElement('span'); marketIndicator.classList.add('price-indicator'); marketIndicator.title = `Potential profit: $${potentialProfit.toLocaleString()}` + (quantity > 1 ? ` (${quantity}x)` : ''); const icon = document.createElement('span'); icon.classList.add('icon-store'); marketIndicator.appendChild(icon); marketIndicator.appendChild(document.createTextNode( ` ${marketPriceDiff > 0 ? '-' : '+'}${Math.abs(Math.round(marketPriceDiff))}%` )); if (Math.abs(marketPriceDiff) < 0.5) { marketIndicator.classList.add('diff-equal'); } else if (marketPriceDiff > 0) { if (marketPriceDiff >= 90) marketIndicator.classList.add('diff-90-100'); else if (marketPriceDiff >= 60) marketIndicator.classList.add('diff-60-90'); else if (marketPriceDiff >= 30) marketIndicator.classList.add('diff-30-60'); else marketIndicator.classList.add('diff-0-30'); } else { if (marketPriceDiff <= -90) marketIndicator.classList.add('diff90-plus'); else if (marketPriceDiff <= -60) marketIndicator.classList.add('diff60-90'); else if (marketPriceDiff <= -30) marketIndicator.classList.add('diff30-60'); else marketIndicator.classList.add('diff0-30'); } indicatorsRow.appendChild(marketIndicator); } // Only add the row if we have at least one indicator if (indicatorsRow.children.length > 0) { container.after(indicatorsRow); } } function updateSingleElement(element) { let itemId, priceElement; // Check if we're in mobile view const isMobileView = window.innerWidth < 785; if (isMobileView) { // Find item ID from info button's aria-controls const infoButton = document.querySelector('button[aria-controls^="wai-itemInfo-"]'); if (infoButton) { const ariaControls = infoButton.getAttribute('aria-controls'); const match = ariaControls.match(/wai-itemInfo-(\d+)/); if (match) itemId = match[1]; } if (element.classList.contains('price___v8rRx')) { priceElement = element; } } else { let container = element; while (container && !itemId) { const img = container.querySelector('img[src*="/images/items/"]'); if (img) { const idMatch = img.src.match(/\/images\/items\/(\d+)\//); if (idMatch) itemId = idMatch[1]; } container = container.parentElement; } if (element.classList.contains('priceAndTotal___eEVS7') || element.classList.contains('price___Uwiv2') || element.className.includes('price_')) { priceElement = element; } } if (!itemId || !priceElement) return; const priceMatch = priceElement.textContent.match(/\$([0-9,]+)/); if (priceMatch) { const itemPrice = parseInt(priceMatch[1].replace(/,/g, '')); addPriceIndicator(itemId, itemPrice, priceElement); } } function processElements() { const isMobileView = window.innerWidth < 785; if (document.URL.includes('sid=ItemMarket')) { // Item tiles - keep original handling for both mobile and desktop document.querySelectorAll('.itemTile___cbw7w').forEach(tile => { const img = tile.querySelector('img.torn-item'); if (!img) return; const idMatch = img.src.match(/\/images\/items\/(\d+)\//); if (!idMatch) return; const itemId = idMatch[1]; const priceElement = tile.querySelector('.priceAndTotal___eEVS7'); if (priceElement) { const priceMatch = priceElement.textContent.match(/\$([0-9,]+)/); if (priceMatch) { const itemPrice = parseInt(priceMatch[1].replace(/,/g, '')); addPriceIndicator(itemId, itemPrice, priceElement); } } }); // Seller rows - handle differently for mobile vs desktop if (isMobileView) { const infoButton = document.querySelector('button[aria-controls^="wai-itemInfo-"]'); if (infoButton) { const ariaControls = infoButton.getAttribute('aria-controls'); const match = ariaControls.match(/wai-itemInfo-(\d+)/); if (match) { const itemId = match[1]; document.querySelectorAll('.sellerRow___Ca2pK').forEach(row => { const priceElement = row.querySelector('.price___v8rRx'); if (priceElement) { const priceMatch = priceElement.textContent.match(/\$([0-9,]+)/); if (priceMatch) { const itemPrice = parseInt(priceMatch[1].replace(/,/g, '')); addPriceIndicator(itemId, itemPrice, priceElement); // Restructure mobile layout if (!row.querySelector('.userInfoHead___LXxjB')) { // Skip header row const indicatorsRow = row.querySelector('.price-indicators-row'); if (indicatorsRow) { priceElement.appendChild(indicatorsRow); } } } } }); } } } else { document.querySelectorAll('.sellerRow___AI0m6').forEach(row => { const img = row.querySelector('.thumbnail___M_h9v img'); if (!img) return; const idMatch = img.src.match(/\/images\/items\/(\d+)\//); if (!idMatch) return; const itemId = idMatch[1]; const priceElement = row.querySelector('.price___Uwiv2'); if (priceElement) { const priceText = priceElement.textContent; const priceMatch = priceText.match(/\$([0-9,]+)/); if (priceMatch) { const itemPrice = parseInt(priceMatch[1].replace(/,/g, '')); addPriceIndicator(itemId, itemPrice, priceElement); } } }); } } else if (document.URL.includes('bazaar.php')) { document.querySelectorAll('img[src*="/images/items/"][src*="/large.png"]').forEach(img => { if (!img.parentElement?.parentElement?.parentElement) return; const idMatch = img.src.match(/\/images\/items\/(\d+)\//); if (!idMatch) return; const itemId = idMatch[1]; const container = img.parentElement.parentElement.parentElement; const priceElement = container.querySelector('[class*="price_"]'); if (priceElement) { const priceMatch = priceElement.textContent.match(/\$([0-9,]+)/); if (priceMatch) { const itemPrice = parseInt(priceMatch[1].replace(/,/g, '')); addPriceIndicator(itemId, itemPrice, priceElement); } } }); } } function initialize() { const lastUpdate = GM_getValue("lastUpdate", 0); const lastMarketUpdate = GM_getValue("lastMarketUpdate", 0); const now = Date.now(); if (now - lastUpdate > 24 * 60 * 60 * 1000) { getAWHPrices(); } if (now - lastMarketUpdate > 24 * 60 * 60 * 1000) { getTornMarketValues(); } try { torn_market_values = JSON.parse(GM_getValue("Torn_Market_Values", "{}")); } catch (e) {} scheduleNextUpdate(); setTimeout(() => { observer.observe(document.body, { childList: true, subtree: true, characterData: true, characterDataOldValue: true, attributes: true, attributeFilter: ['class'] }); processElements(); }, 1000); createFloatingContainer(); } const observer = new MutationObserver(mutations => { let affected = new Set(); for (const mutation of mutations) { if (mutation.type === 'characterData') { let parentElement = mutation.target.parentElement; while (parentElement) { if (parentElement.classList) { if (parentElement.classList.contains('priceAndTotal___eEVS7') || parentElement.classList.contains('price___Uwiv2') || [...parentElement.classList].some(c => c.includes('price_'))) { affected.add(parentElement); break; } } parentElement = parentElement.parentElement; } } else if (mutation.addedNodes.length) { for (const node of mutation.addedNodes) { if (node.nodeType === Node.ELEMENT_NODE) { if (node.classList?.contains('itemTile___cbw7w') || node.classList?.contains('sellerRow___AI0m6') || node.querySelector?.('.itemTile___cbw7w, .sellerRow___AI0m6, [class*="price_"]')) { processElements(); return; } } } } } affected.forEach(element => updateSingleElement(element)); }); // Start the script initialize(); })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址