您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
On "Fill" click autofills market item price with lowest market price currently minus $1 (can be customised), fills max quantity for items, marks checkboxes for guns.
当前为
// ==UserScript== // @name Torn Market Filler // @namespace https://github.com/SOLiNARY // @version 0.1.4 // @description On "Fill" click autofills market item price with lowest market price currently minus $1 (can be customised), fills max quantity for items, marks checkboxes for guns. // @author Ramin Quluzade, Silmaril [2665762] // @license MIT License // @match https://www.torn.com/page.php?sid=ItemMarket* // @match https://*.torn.com/page.php?sid=ItemMarket* // @icon https://www.google.com/s2/favicons?sz=64&domain=torn.com // @run-at document-idle // @grant GM_addStyle // @grant GM_registerMenuCommand // ==/UserScript== (async function() { 'use strict'; try { GM_registerMenuCommand('Set Price Delta', setPriceDelta); GM_registerMenuCommand('Set Api Key', function() { checkApiKey(false); }); } catch (error) { console.log('[TornMarketFiller] Tampermonkey not detected!'); } const itemUrl = "https://api.torn.com/torn/{itemId}?selections=items&key={apiKey}"; const marketUrl = "https://api.torn.com/market/{itemId}?selections=itemmarket&key={apiKey}"; let priceDeltaRaw = localStorage.getItem("silmaril-torn-bazaar-filler-price-delta") ?? '-1[market]'; let apiKey = localStorage.getItem("silmaril-torn-bazaar-filler-apikey") ?? '###PDA-APIKEY###'; // TornPDA support for GM_addStyle let GM_addStyle = function (s) { let style = document.createElement("style"); style.type = "text/css"; style.innerHTML = s; document.head.appendChild(style); }; GM_addStyle(` #item-market-root [class^=addListingWrapper___] [class^=panels___] [class^=priceInputWrapper___] > .input-money-group > .input-money { border-bottom-left-radius: 0 !important; border-top-left-radius: 0 !important; } #item-market-root [class^=viewListingWrapper___] [class^=priceInputWrapper___] > .input-money-group > .input-money { border-bottom-left-radius: 0 !important; border-top-left-radius: 0 !important; } `); const pages = { "AddItems": 10, "ViewItems": 20}; let currentPage = pages.AddItems; const addItemsLabels = ["Fill", "Clear"]; const updateItemsLabels = ["Update", "Clear"]; let holdTimer; const isMobileView = window.innerWidth <= 784; const observerTarget = document.querySelector("#item-market-root"); const observerConfig = { attributes: false, childList: true, characterData: false, subtree: true }; const observer = new MutationObserver(function(mutations) { mutations.forEach(mutationRaw => { let mutation = mutationRaw.target; if (window.location.href.indexOf('#/addListing') > -1){ currentPage = pages.AddItems; if (mutation.id.startsWith('headlessui-tabs-panel-')) { mutation.querySelectorAll('[class*=itemRowWrapper___]:not(.silmaril-market-filler-processed) > [class*=itemRow___]:not([class*=grayedOut___]) [class^=priceInputWrapper___]').forEach(x => AddFillButton(x)); } if (mutation.className.indexOf('priceInputWrapper___') > -1){ AddFillButton(mutation); } } else if (window.location.href.indexOf('#/viewListing') > -1){ currentPage = pages.ViewItems; if (mutation.className.startsWith('viewListingWrapper___')) { mutation.querySelectorAll('[class*=itemRowWrapper___]:not(.silmaril-market-filler-processed) > [class*=itemRow___]:not([class*=grayedOut___]) [class^=priceInputWrapper___]').forEach(x => AddFillButton(x)); } } }); }); observer.observe(observerTarget, observerConfig); function AddFillButton(itemPriceElement){ if (itemPriceElement.querySelector('.silmaril-market-filler-button') != null){ return; } const wrapperParent = findParentByCondition(itemPriceElement, (el) => el.className.indexOf('itemRowWrapper___') > -1); wrapperParent.classList.add('silmaril-market-filler-processed'); let itemIdString = wrapperParent.querySelector('[class^=itemRow___] [type=button][class^=viewInfoButton___]').getAttribute('aria-controls'); let itemImage = wrapperParent.querySelector('[class*=viewInfoButton] img'); let itemId = currentPage == pages.AddItems ? getItemIdFromString(itemIdString) : getItemIdFromImage(itemImage); const span = document.createElement('span'); span.title = 'Click to autofill the price & quantity if not set already'; span.className = 'silmaril-market-filler-button input-money-symbol'; span.setAttribute('data-action-flag', 'fill'); span.addEventListener('click', async () => {await handleFillClick(itemId)}); span.addEventListener('mousedown', () => { holdTimer = setTimeout(() => { setPriceDelta(); checkApiKey(false); }, 2000); }); span.addEventListener('mouseup', () => { clearTimeout(holdTimer); }); span.addEventListener('mouseleave', () => { clearTimeout(holdTimer); }); const input = document.createElement('input'); input.title = 'Click to autofill the price & quantity if not set already'; input.type = 'button'; input.className = 'wai-btn'; span.appendChild(input); itemPriceElement.querySelector('.input-money-group').prepend(span); } async function GetPrice(itemId){ let requestUrl = priceDeltaRaw.indexOf('[market]') != -1 ? itemUrl : marketUrl; requestUrl = requestUrl .replace("{itemId}", itemId) .replace("{apiKey}", apiKey); return fetch(requestUrl) .then(response => response.json()) .then(data => { if (data.error != null){ switch (data.error.code){ case 2: apiKey = null; localStorage.setItem("silmaril-torn-bazaar-filler-apikey", null); console.error("[TornMarketFiller] Incorrect Api Key:", data); return 'Wrong API key!'; case 9: console.warning("[TornMarketFiller] The API is temporarily disabled, please, try again later"); return 'API is OFF!'; default: console.error("[TornMarketFiller] Error:", data.error.error); return data.error.error; } } if (priceDeltaRaw.indexOf('[market]') != -1) { let priceDelta = priceDeltaRaw.indexOf('[') == -1 ? priceDeltaRaw : priceDeltaRaw.substring(0, priceDeltaRaw.indexOf('[')); let price = data.items[itemId].market_value; return Math.round(performOperation(price, priceDelta)); } else { let marketSlotOffset = priceDeltaRaw.indexOf('[') == -1 ? 0 : parseInt(priceDeltaRaw.substring(priceDeltaRaw.indexOf('[') + 1, priceDeltaRaw.indexOf(']'))); let priceDeltaWithoutMarketOffset = priceDeltaRaw.indexOf('[') == -1 ? priceDeltaRaw : priceDeltaRaw.substring(0, priceDeltaRaw.indexOf('[')); return Math.round(performOperation(data.bazaar[Math.min(marketSlotOffset, data.bazaar.length - 1)].cost, priceDeltaWithoutMarketOffset)); } }) .catch(error => { console.error("[TornMarketFiller] Error fetching data:", error); return 'Failed!'; }); } async function handleFillClick(itemId){ let inputEvent = new Event("input", {bubbles: true}); let target = event.target; let action = target.getAttribute('data-action-flag'); let price = action == 'fill' ? await GetPrice(itemId) : ''; switchActionFlag(target); let parentRow = findParentByCondition(target, (el) => el.className.indexOf('info___') > -1); let quantityInputs = parentRow.querySelectorAll('[class^=amountInputWrapper___] .input-money-group > .input-money'); if (quantityInputs.length > 0){ if (quantityInputs[0].value.length == 0 || parseInt(quantityInputs[0].value) < 1){ quantityInputs[0].value = action == 'fill' ? Number.MAX_SAFE_INTEGER : 0; quantityInputs[1].value = action == 'fill' ? Number.MAX_SAFE_INTEGER : 0; } else { quantityInputs[0].value = action == 'clear' ? '' : quantityInputs[0].value; quantityInputs[1].value = action == 'clear' ? '' : quantityInputs[1].value; } quantityInputs[0].dispatchEvent(inputEvent); } else { let checkbox = parentRow.querySelector('[class^=checkboxWrapper___] > [class^=checkboxContainer___] [type=checkbox]'); if (action == 'fill' && !checkbox.checked || action == 'clear' && checkbox.checked){ checkbox.click(); } } let priceInputs = target.parentNode.querySelectorAll('input.input-money'); priceInputs.forEach(x => {x.value = price}); priceInputs[0].dispatchEvent(inputEvent); } function getItemIdFromString(string){ const match = string.match(/-(\d+)-/); if (match) { const number = match[1]; return number; } else { console.error("[TornMarketFiller] ItemId not found!"); return -1; } } function getItemIdFromImage(image){ let numberPattern = /\/(\d+)\//; let match = image.src.match(numberPattern); if (match) { return parseInt(match[1], 10); } else { console.error("[TornMarketFiller] ItemId not found!"); return -1; } } function switchActionFlag(target){ switch (target.getAttribute('data-action-flag')){ case 'fill': target.setAttribute('data-action-flag', 'clear'); break; case 'clear': default: target.setAttribute('data-action-flag', 'fill'); break; } } function findParentByCondition(element, conditionFn){ let currentElement = element; while (currentElement !== null) { if (conditionFn(currentElement)) { return currentElement; } currentElement = currentElement.parentElement; } return null; } function setPriceDelta() { let userInput = prompt('Enter price delta formula (default: -1[market]):', priceDeltaRaw); if (userInput !== null) { priceDeltaRaw = userInput; localStorage.setItem("silmaril-torn-bazaar-filler-price-delta", userInput); } else { console.error("[TornMarketFiller] User cancelled the Price Delta input."); } } function performOperation(number, operation) { // Parse the operation string to extract the operator and value const match = operation.match(/^([-+]?)(\d+(?:\.\d+)?)(%)?$/); if (!match) { throw new Error('Invalid operation string'); } const [, operator, operand, isPercentage] = match; const operandValue = parseFloat(operand); // Check for percentage and convert if necessary const adjustedOperand = isPercentage ? (number * operandValue) / 100 : operandValue; // Perform the operation based on the operator switch (operator) { case '': case '+': return number + adjustedOperand; case '-': return number - adjustedOperand; default: throw new Error('Invalid operator'); } } function checkApiKey(checkExisting = true) { if (!checkExisting || apiKey === null || apiKey.indexOf('PDA-APIKEY') > -1 || apiKey.length != 16){ let userInput = prompt("Please enter a PUBLIC Api Key, it will be used to get current bazaar prices:", apiKey ?? ''); if (userInput !== null && userInput.length == 16) { apiKey = userInput; localStorage.setItem("silmaril-torn-bazaar-filler-apikey", userInput); } else { console.error("[TornMarketFiller] User cancelled the Api Key input."); } } } })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址