您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Automatically set the price of items relative to the current market with settings menu
// ==UserScript== // @name Item Market Auto Price // @namespace dev.kwack.torn.imarket-auto-price // @version 1.1.3 // @description Automatically set the price of items relative to the current market with settings menu // @author Kwack (original), Mr_Awaken (modified) // @match https://www.torn.com/page.php?sid=ItemMarket // @connect api.torn.com // @grant GM_addStyle // @require https://code.jquery.com/jquery-3.6.0.min.js // ==/UserScript== // @ts-check const inputSelector = ` div[class*=itemRowWrapper] div[class*=priceInputWrapper] > div.input-money-group > input.input-money:not([type=hidden]):not(.kw--price-set), div[class*=itemRowWrapper] div[class*=amountInputWrapper][class*=hideMaxButton] > div.input-money-group > input.input-money:not([type=hidden]):not(.kw--price-set) `; const diff = 5; let mainCalled = false; // Function to show API key modal function showApiKeyModal() { console.log("Showing API key modal"); const modalHTML = ` <div id="kw-modal" style="position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0, 0, 0, 0.8); z-index: 9999; display: flex; justify-content: center; align-items: center;"> <div style="background: #1c1c1c; padding: 30px; border-radius: 8px; width: 400px; max-width: 90%; color: #fff; text-align: center; font-family: Arial, sans-serif;"> <h2 style="margin-bottom: 10px;">🔐 Enter Your Torn API Key</h2> <p style="font-size: 14px; color: #ccc; margin-bottom: 20px;"> We'll store it securely in your browser and only use it for fetching item market prices. </p> <input id="kw-api-input" type="text" value="${localStorage.getItem("tornAutoPriceAPIKey") || ''}" placeholder="Your API Key..." style=" width: 100%; padding: 10px; border-radius: 4px; border: none; font-size: 16px; margin-bottom: 20px;" /> <button id="kw-save-api" style=" background: #00ccff; border: none; color: #000; padding: 10px 20px; font-weight: bold; border-radius: 5px; cursor: pointer;"> Save & Continue </button> </div> </div> `; document.body.insertAdjacentHTML("beforeend", modalHTML); document.getElementById("kw-save-api").addEventListener("click", () => { const input = /** @type {HTMLInputElement} */ (document.getElementById("kw-api-input")); const value = input.value.trim(); if (value.length >= 16) { localStorage.setItem("tornAutoPriceAPIKey", value); document.getElementById("kw-modal").remove(); console.log("API key saved:", value); main(); } else { input.style.border = "2px solid red"; } }); } // Function to get API key from localStorage or show modal function getApiKey() { const key = localStorage.getItem("tornAutoPriceAPIKey"); if (!key) { showApiKeyModal(); } else { console.log("API key retrieved:", key); main(); } } // Add settings menu item function addMarketAutoPriceSettingsMenuItem() { const menu = document.querySelector('.settings-menu'); if (!menu || document.querySelector('.market-auto-price-settings-button')) return; const li = document.createElement('li'); li.className = 'link market-auto-price-settings-button'; const a = document.createElement('a'); a.href = '#'; const iconDiv = document.createElement('div'); iconDiv.className = 'icon-wrapper'; const svgIcon = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); svgIcon.setAttribute('class', 'default'); svgIcon.setAttribute('fill', '#fff'); svgIcon.setAttribute('stroke', 'transparent'); svgIcon.setAttribute('stroke-width', '0'); svgIcon.setAttribute('width', '16'); svgIcon.setAttribute('height', '16'); svgIcon.setAttribute('viewBox', '0 0 512 512'); const path = document.createElementNS('http://www.w3.org/2000/svg', 'path'); path.setAttribute('d', 'M487.4 315.7l-42.6-24.6c4.3-23.2 4.3-47 0-70.2l42.6-24.6c4.9-2.8 7.1-8.6 5.5-14-11.1-35.6-30-67.8-54.7-94.6-3.8-4.1-10-5.1-14.8-2.3L380.8 110c-17.9-15.4-38.5-27.3-60.8-35.1V25.8c0-5.6-3.9-10.5-9.4-11.7-36.7-8.2-74.3-7.8-109.2 0-5.5 1.2-9.4 6.1-9.4 11.7V75c-22.2 7.9-42.8 19.8-60.8 35.1L88.7 85.5c-4.9-2.8-11-1.9-14.8 2.3-24.7 26.7-43.6 58.9-54.7 94.6-1.7 5.4.6 11.2 5.5 14L67.3 221c-4.3 23.2-4.3 47 0 70.2l-42.6 24.6c-4.9 2.8-7.1 8.6-5.5 14 11.1 35.6 30 67.8 54.7 94.6 3.8 4.1 10 5.1 14.8 2.3l42.6-24.6c17.9 15.4 38.5 27.3 60.8 35.1v49.2c0 5.6 3.9 10.5 9.4 11.7 36.7 8.2 74.3 7.8 109.2 0 5.5-1.2 9.4-6.1 9.4-11.7v-49.2c22.2-7.9 42.8-19.8 60.8-35.1l42.6 24.6c4.9 2.8 11 1.9 14.8-2.3 24.7-26.7 43.6-58.9 54.7-94.6 1.5-5.5-.7-11.3-5.6-14.1zM256 336c-44.1 0-80-35.9-80-80s35.9-80 80-80 80 35.9 80 80-35.9 80-80 80z'); svgIcon.appendChild(path); iconDiv.appendChild(svgIcon); const span = document.createElement('span'); span.textContent = 'Item Market Auto Price Settings'; a.appendChild(iconDiv); a.appendChild(span); li.appendChild(a); a.addEventListener('click', e => { e.preventDefault(); document.body.click(); showApiKeyModal(); }); const logoutButton = menu.querySelector('li.logout'); if (logoutButton) { menu.insertBefore(li, logoutButton); } else { menu.appendChild(li); } } const menuObserver = new MutationObserver(mutations => { mutations.forEach(mutation => { if (mutation.addedNodes.length > 0) { for (const node of mutation.addedNodes) { if (node.nodeType === Node.ELEMENT_NODE && node.classList.contains('settings-menu')) { addMarketAutoPriceSettingsMenuItem(); break; } } } }); }); menuObserver.observe(document.body, { childList: true, subtree: true }); addMarketAutoPriceSettingsMenuItem(); // Wait for the specific elements to be present function waitForElements(selector, callback) { const observer = new MutationObserver(() => { if (document.querySelector(selector)) { observer.disconnect(); callback(); } }); observer.observe(document.body, { childList: true, subtree: true }); } // Main function to set up event listeners function main() { if (mainCalled) return; mainCalled = true; console.log("Running main"); $(document).on("click", inputSelector, (e) => { const input = e.target; if (input.getAttribute("placeholder") === "Qty") { addQuantity(input).catch((e) => handleError(e, input)); } else { addPrice(input).catch((e) => handleError(e, input)); } }); } // Function to get the lowest price from the API function getLowestPrice(itemId, apiKey) { const baseURL = "https://api.torn.com/v2/market"; const searchParams = new URLSearchParams({ selections: "itemmarket", key: apiKey, id: itemId, offset: "0", }); const url = new URL(`?${searchParams.toString()}`, baseURL); return fetch(url) .then((res) => res.json()) .then((data) => { if ("error" in data) throw new Error(data.error.error); const price = data?.itemmarket?.listings?.[0]?.price; if (typeof price === "number" && price >= 1) return price; throw new Error(`Invalid price: ${price}`); }); } // Function to update the input field function updateInput(input, value) { input.value = `${value}`; input.dispatchEvent(new Event("input", { bubbles: true })); } // Function to set the price async function addPrice(input) { if (!(input instanceof HTMLInputElement)) throw new Error("Input is not an HTMLInputElement"); const apiKey = localStorage.getItem("tornAutoPriceAPIKey"); if (!apiKey) { throw new Error("API key not set. Please set it in the settings."); } const row = input.closest("div[class*=itemRowWrapper]"); const image = row?.querySelector("img"); if (!image) throw new Error("Could not find image element"); if (image.parentElement?.matches("[class*='glow-']")) throw new Warning("Skipping a glowing RW item"); const itemId = image.src?.match(/\/images\/items\/([\d]+)\//)?.[1]; if (!itemId) throw new Error("Could not find item ID"); const currentLowestPrice = await getLowestPrice(itemId, apiKey); const priceToSet = Math.max(1, currentLowestPrice - diff); updateInput(input, priceToSet); input.classList.add("kw--price-set"); } // Function to set the quantity to max async function addQuantity(input) { if (!(input instanceof HTMLInputElement)) throw new Error("Input is not an HTMLInputElement"); updateInput(input, "max"); input.classList.add("kw--price-set"); } // Error handling function handleError(e, input) { if (e instanceof Warning) { console.warn(e); input.style.outline = "2px solid yellow"; } else { console.error(e); input.style.outline = "2px solid red"; } } // Custom Warning class class Warning extends Error {} // Start the script by waiting for the elements waitForElements(inputSelector, getApiKey);
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址