您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
On click, auto-fills bazaar item quantities and prices based on your preferences
当前为
- // ==UserScript==
- // @name Customizable Bazaar Filler
- // @namespace http://tampermonkey.net/
- // @version 1.0
- // @description On click, auto-fills bazaar item quantities and prices based on your preferences
- // @match https://www.torn.com/bazaar.php*
- // @require https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js
- // @grant GM_getValue
- // @grant GM_setValue
- // @grant GM_registerMenuCommand
- // ==/UserScript==
- (function() {
- 'use strict';
- const styleBlock = `
- .item-toggle {
- position: absolute;
- width: 16px;
- height: 16px;
- top: 50%;
- right: 10px;
- transform: translateY(-50%);
- cursor: pointer;
- border-radius: 3px;
- -webkit-appearance: none;
- -moz-appearance: none;
- appearance: none;
- outline: none;
- }
- .item-toggle::after {
- content: '\\2713';
- position: absolute;
- font-size: 12px;
- top: 50%;
- left: 50%;
- transform: translate(-50%, -50%);
- display: none;
- }
- .item-toggle:checked::after {
- display: block;
- }
- /* Light mode */
- body:not(.dark-mode) .item-toggle {
- border: 1px solid #ccc;
- background: #fff;
- }
- body:not(.dark-mode) .item-toggle:checked {
- background: #007bff;
- }
- body:not(.dark-mode) .item-toggle:checked::after {
- color: #fff;
- }
- /* Dark mode */
- body.dark-mode .item-toggle {
- border: 1px solid #4e535a;
- background: #2f3237;
- }
- body.dark-mode .item-toggle:checked {
- background: #4e535a;
- }
- body.dark-mode .item-toggle:checked::after {
- color: #fff;
- }
- /* Modal overlay */
- .settings-modal-overlay {
- position: fixed;
- top: 0; left: 0;
- width: 100%; height: 100%;
- background: rgba(0,0,0,0.5);
- z-index: 9999;
- display: flex;
- align-items: center;
- justify-content: center;
- }
- /* Modal container */
- .settings-modal {
- background: #fff;
- padding: 20px;
- border-radius: 8px;
- min-width: 300px;
- box-shadow: 0 2px 10px rgba(0,0,0,0.3);
- color: #000;
- }
- .settings-modal h2 {
- margin-top: 0;
- }
- .settings-modal label {
- display: block;
- margin: 10px 0 5px;
- }
- .settings-modal input, .settings-modal select {
- width: 100%;
- padding: 5px;
- box-sizing: border-box;
- }
- .settings-modal button {
- margin-top: 15px;
- padding: 5px 10px;
- }
- /* Button group alignment */
- .settings-modal div[style*="text-align:right"] {
- text-align: right;
- }
- /* Dark mode modal overrides */
- body.dark-mode .settings-modal {
- background: #2f3237;
- color: #fff;
- box-shadow: 0 2px 10px rgba(0,0,0,0.7);
- }
- body.dark-mode .settings-modal input,
- body.dark-mode .settings-modal select {
- background: #3c3f41;
- color: #fff;
- border: 1px solid #555;
- }
- body.dark-mode .settings-modal button {
- background: #555;
- color: #fff;
- border: none;
- }
- `;
- $('<style>').prop('type', 'text/css').html(styleBlock).appendTo('head');
- let apiKey = GM_getValue("tornApiKey", "");
- let pricingSource = GM_getValue("pricingSource", "Market Value");
- let itemMarketOffset = GM_getValue("itemMarketOffset", -1);
- let itemMarketMarginType = GM_getValue("itemMarketMarginType", "absolute");
- let itemMarketListing = GM_getValue("itemMarketListing", 1);
- let itemMarketClamp = GM_getValue("itemMarketClamp", false);
- let marketMarginOffset = GM_getValue("marketMarginOffset", 0);
- let marketMarginType = GM_getValue("marketMarginType", "absolute");
- const validPages = ["#/add", "#/manage"];
- let currentPage = window.location.hash;
- let itemMarketCache = {};
- const inputEvent = new Event("input", { bubbles: true });
- const keyupEvent = new Event("keyup", { bubbles: true });
- function getItemIdByName(itemName) {
- const storedItems = JSON.parse(localStorage.getItem("tornItems") || "{}");
- for (let [id, info] of Object.entries(storedItems)) {
- if (info.name === itemName) return id;
- }
- return null;
- }
- function getPriceColor(listedPrice, marketValue) {
- if (marketValue <= 0) {
- return "#FFFFFF";
- }
- const ratio = listedPrice / marketValue;
- if (ratio < 0) return "#FF0000";
- if (ratio > 2) return "#008000";
- if (ratio < 1) {
- let t = Math.max(0, ratio);
- let r1 = 255, g1 = 0, b1 = 0;
- let r2 = 255, g2 = 255, b2 = 255;
- let r = Math.round(r1 + (r2 - r1) * t);
- let g = Math.round(g1 + (g2 - g1) * t);
- let b = Math.round(b1 + (b2 - b1) * t);
- return `rgb(${r},${g},${b})`;
- } else {
- let t = ratio - 1; // 0..1
- let r1 = 255, g1 = 255, b1 = 255;
- let r2 = 0, g2 = 128, b2 = 0;
- let r = Math.round(r1 + (r2 - r1) * t);
- let g = Math.round(g1 + (g2 - g1) * t);
- let b = Math.round(b1 + (b2 - b1) * t);
- return `rgb(${r},${g},${b})`;
- }
- }
- async function fetchItemMarketData(itemId) {
- if (!apiKey) {
- console.error("No API key set for Item Market calls.");
- alert("No API key set. Please enter one in Settings first.");
- return null;
- }
- const now = Date.now();
- if (itemMarketCache[itemId] && (now - itemMarketCache[itemId].time < 30000)) {
- return itemMarketCache[itemId].data;
- }
- const url = `https://api.torn.com/v2/market/${itemId}/itemmarket`;
- try {
- const res = await fetch(url, {
- headers: { 'Authorization': 'ApiKey ' + apiKey }
- });
- const data = await res.json();
- if (data.error) {
- console.error("Item Market API error:", data.error);
- alert("Item Market API error: " + data.error.error);
- return null;
- }
- itemMarketCache[itemId] = { time: now, data };
- return data;
- } catch (err) {
- console.error("Failed fetching Item Market data:", err);
- alert("Failed to fetch Item Market data. Check your API key or try again later.");
- return null;
- }
- }
- async function updateAddRow($row, isChecked) {
- const $qtyInput = $row.find(".amount input").first();
- const $priceInput = $row.find(".price input").first();
- if (!isChecked) {
- // Reset fields
- if ($qtyInput.data("orig") !== undefined) {
- $qtyInput.val($qtyInput.data("orig"));
- $qtyInput.removeData("orig");
- } else {
- $qtyInput.val("");
- }
- $qtyInput[0].dispatchEvent(new Event("keyup", { bubbles: true }));
- if ($priceInput.data("orig") !== undefined) {
- $priceInput.val($priceInput.data("orig"));
- $priceInput.removeData("orig");
- $priceInput.css("color", "");
- } else {
- $priceInput.val("");
- }
- $priceInput[0].dispatchEvent(new Event("input", { bubbles: true }));
- return;
- }
- if (!$qtyInput.data("orig")) $qtyInput.data("orig", $qtyInput.val());
- if (!$priceInput.data("orig")) $priceInput.data("orig", $priceInput.val());
- const itemName = $row.find(".name-wrap span.t-overflow").text().trim();
- const itemId = getItemIdByName(itemName);
- const storedItems = JSON.parse(localStorage.getItem("tornItems") || "{}");
- const matchedItem = Object.values(storedItems).find(i => i.name === itemName);
- if (pricingSource === "Market Value" && matchedItem) {
- let qty = $row.find(".item-amount.qty").text().trim();
- $qtyInput.val(qty).trigger("keyup");
- let mv = Number(matchedItem.market_value);
- let finalPrice = mv;
- if (marketMarginType === "absolute") {
- finalPrice += marketMarginOffset;
- } else if (marketMarginType === "percentage") {
- finalPrice = Math.round(mv * (1 + marketMarginOffset / 100));
- }
- $priceInput.val(finalPrice.toLocaleString()).trigger("input");
- $priceInput.css("color", getPriceColor(finalPrice, mv));
- }
- else if (pricingSource === "Item Market" && itemId) {
- const data = await fetchItemMarketData(itemId);
- if (!data || !data.itemmarket?.listings?.length) return;
- let listings = data.itemmarket.listings;
- const $checkbox = $row.find(".item-toggle").first();
- const listingsText = listings.slice(0, 5)
- .map((x, i) => `${i + 1}) $${x.price.toLocaleString()} x${x.amount}`)
- .join('\n');
- $checkbox.attr("title", listingsText);
- setTimeout(() => {
- $checkbox.removeAttr("title");
- }, 30000);
- let baseIndex = Math.min(itemMarketListing - 1, listings.length - 1);
- let listingPrice = listings[baseIndex].price;
- let finalPrice;
- if (itemMarketMarginType === "absolute") {
- finalPrice = listingPrice + Number(itemMarketOffset);
- } else if (itemMarketMarginType === "percentage") {
- finalPrice = Math.round(listingPrice * (1 + Number(itemMarketOffset) / 100));
- }
- if (itemMarketClamp && matchedItem && matchedItem.market_value) {
- finalPrice = Math.max(finalPrice, Number(matchedItem.market_value));
- }
- $qtyInput.val($row.find(".item-amount.qty").text().trim()).trigger("keyup");
- $priceInput.val(finalPrice.toLocaleString()).trigger("input");
- if (matchedItem && matchedItem.market_value) {
- let marketVal = Number(matchedItem.market_value);
- $priceInput.css("color", getPriceColor(finalPrice, marketVal));
- }
- }
- else if (pricingSource === "Bazaars/TornPal") {
- alert("Bazaars/TornPal is not available. Please select another source.");
- }
- }
- async function updateManageRow($row, isChecked) {
- const $priceInput = $row.find(".price___DoKP7 .input-money-group.success input.input-money").first();
- if (!isChecked) {
- // Reset fields
- if ($priceInput.data("orig") !== undefined) {
- $priceInput.val($priceInput.data("orig"));
- $priceInput.removeData("orig");
- $priceInput.css("color", "");
- } else {
- $priceInput.val("");
- }
- $priceInput[0].dispatchEvent(new Event("input", { bubbles: true }));
- return;
- }
- if (!$priceInput.data("orig")) $priceInput.data("orig", $priceInput.val());
- const itemName = $row.find(".desc___VJSNQ b").text().trim();
- const itemId = getItemIdByName(itemName);
- const storedItems = JSON.parse(localStorage.getItem("tornItems") || "{}");
- const matchedItem = Object.values(storedItems).find(i => i.name === itemName);
- if (pricingSource === "Market Value" && matchedItem) {
- let mv = Number(matchedItem.market_value);
- let finalPrice = mv;
- if (marketMarginType === "absolute") {
- finalPrice += marketMarginOffset;
- } else if (marketMarginType === "percentage") {
- finalPrice = Math.round(mv * (1 + marketMarginOffset / 100));
- }
- $priceInput.val(finalPrice.toLocaleString()).trigger("input");
- $priceInput.css("color", getPriceColor(finalPrice, mv));
- }
- else if (pricingSource === "Item Market" && itemId) {
- const data = await fetchItemMarketData(itemId);
- if (!data || !data.itemmarket?.listings?.length) return;
- let listings = data.itemmarket.listings;
- const $checkbox = $row.find(".item-toggle").first();
- const listingsText = listings.slice(0, 5)
- .map((x, i) => `${i + 1}) $${x.price.toLocaleString()} x${x.amount}`)
- .join('\n');
- $checkbox.attr("title", listingsText);
- setTimeout(() => {
- $checkbox.removeAttr("title");
- }, 30000);
- let baseIndex = Math.min(itemMarketListing - 1, listings.length - 1);
- let listingPrice = listings[baseIndex].price;
- let finalPrice;
- if (itemMarketMarginType === "absolute") {
- finalPrice = listingPrice + Number(itemMarketOffset);
- } else if (itemMarketMarginType === "percentage") {
- finalPrice = Math.round(listingPrice * (1 + Number(itemMarketOffset) / 100));
- }
- if (itemMarketClamp && matchedItem && matchedItem.market_value) {
- finalPrice = Math.max(finalPrice, Number(matchedItem.market_value));
- }
- $priceInput.val(finalPrice.toLocaleString()).trigger("input");
- if (matchedItem && matchedItem.market_value) {
- let marketVal = Number(matchedItem.market_value);
- $priceInput.css("color", getPriceColor(finalPrice, marketVal));
- }
- }
- else if (pricingSource === "Bazaars/TornPal") {
- alert("Bazaars/TornPal is not available. Please select another source.");
- }
- }
- function openSettingsModal() {
- $('.settings-modal-overlay').remove();
- const $overlay = $('<div class="settings-modal-overlay"></div>');
- const $modal = $(`
- <div class="settings-modal" style="width:400px; max-width:90%; font-family:Arial, sans-serif;">
- <h2 style="margin-bottom:6px;">Bazaar Filler Settings</h2>
- <hr style="border-top:1px solid #ccc; margin:8px 0;">
- <div style="margin-bottom:15px;">
- <label for="api-key-input" style="font-weight:bold; display:block;">Torn API Key</label>
- <input id="api-key-input" type="text" placeholder="Enter API key" style="width:100%; padding:6px; box-sizing:border-box;" value="${apiKey || ''}">
- </div>
- <hr style="border-top:1px solid #ccc; margin:8px 0;">
- <div style="margin-bottom:15px;">
- <label for="pricing-source-select" style="font-weight:bold; display:block;">Pricing Source</label>
- <select id="pricing-source-select" style="width:100%; padding:6px; box-sizing:border-box;">
- <option value="Market Value">Market Value</option>
- <option value="Bazaars/TornPal">Bazaars/TornPal</option>
- <option value="Item Market">Item Market</option>
- </select>
- </div>
- <div id="market-value-options" style="display:none; margin-bottom:15px;">
- <hr style="border-top:1px solid #ccc; margin:8px 0;">
- <h3 style="margin:0 0 10px 0; font-size:1em; font-weight:bold;">Market Value Options</h3>
- <div style="margin-bottom:10px;">
- <label for="market-margin-offset" style="display:block;">Margin (ie: -1 is either $1 less or 1% less depending on margin type)</label>
- <input id="market-margin-offset" type="number" style="width:100%; padding:6px; box-sizing:border-box;" value="${marketMarginOffset}">
- </div>
- <div style="margin-bottom:10px;">
- <label for="market-margin-type" style="display:block;">Margin Type</label>
- <select id="market-margin-type" style="width:100%; padding:6px; box-sizing:border-box;">
- <option value="absolute">Absolute ($)</option>
- <option value="percentage">Percentage (%)</option>
- </select>
- </div>
- </div>
- <div id="item-market-options" style="display:none; margin-bottom:15px;">
- <hr style="border-top:1px solid #ccc; margin:8px 0;">
- <h3 style="margin:0 0 10px 0; font-size:1em; font-weight:bold;">Item Market Options</h3>
- <div style="margin-bottom:10px;">
- <label for="item-market-listing" style="display:block;">Listing Index (1 = lowest, 2 = 2nd lowest, etc)</label>
- <input id="item-market-listing" type="number" style="width:100%; padding:6px; box-sizing:border-box;" value="${itemMarketListing}">
- </div>
- <div style="margin-bottom:10px;">
- <label for="item-market-offset" style="display:block;">Margin (ie: -1 is either $1 less or 1% less depending on margin type)</label>
- <input id="item-market-offset" type="number" style="width:100%; padding:6px; box-sizing:border-box;" value="${itemMarketOffset}">
- </div>
- <div style="margin-bottom:10px;">
- <label for="item-market-margin-type" style="display:block;">Margin Type</label>
- <select id="item-market-margin-type" style="width:100%; padding:6px; box-sizing:border-box;">
- <option value="absolute">Absolute ($)</option>
- <option value="percentage">Percentage (%)</option>
- </select>
- </div>
- <div style="display:inline-flex; align-items:center; margin-bottom:5px;">
- <input id="item-market-clamp" type="checkbox" style="margin-right:5px;" ${itemMarketClamp ? "checked" : ""}>
- <label for="item-market-clamp" style="margin:0; cursor:pointer;">Clamp minimum price to Market Value</label>
- </div>
- </div>
- <hr style="border-top:1px solid #ccc; margin:8px 0;">
- <div style="text-align:right;">
- <button id="settings-save" style="margin-right:8px; padding:6px 10px; cursor:pointer;">Save</button>
- <button id="settings-cancel" style="padding:6px 10px; cursor:pointer;">Cancel</button>
- </div>
- </div>
- `);
- $overlay.append($modal);
- $('body').append($overlay);
- // Set initial selections
- $('#pricing-source-select').val(pricingSource);
- $('#item-market-margin-type').val(itemMarketMarginType);
- $('#market-margin-type').val(marketMarginType);
- function toggleFields() {
- let src = $('#pricing-source-select').val();
- $('#item-market-options').toggle(src === 'Item Market');
- $('#market-value-options').toggle(src === 'Market Value');
- }
- $('#pricing-source-select').change(toggleFields);
- toggleFields();
- $('#settings-save').click(function() {
- apiKey = $('#api-key-input').val().trim();
- pricingSource = $('#pricing-source-select').val();
- if (pricingSource === "Bazaars/TornPal") {
- alert("Bazaars/TornPal is not available. Please select another source.");
- return;
- }
- if (pricingSource === "Market Value") {
- marketMarginOffset = Number($('#market-margin-offset').val() || 0);
- marketMarginType = $('#market-margin-type').val();
- GM_setValue("marketMarginOffset", marketMarginOffset);
- GM_setValue("marketMarginType", marketMarginType);
- }
- if (pricingSource === "Item Market") {
- itemMarketListing = Number($('#item-market-listing').val() || 1);
- itemMarketOffset = Number($('#item-market-offset').val() || -1);
- itemMarketMarginType = $('#item-market-margin-type').val();
- itemMarketClamp = $('#item-market-clamp').is(':checked');
- GM_setValue("itemMarketListing", itemMarketListing);
- GM_setValue("itemMarketOffset", itemMarketOffset);
- GM_setValue("itemMarketMarginType", itemMarketMarginType);
- GM_setValue("itemMarketClamp", itemMarketClamp);
- }
- GM_setValue("tornApiKey", apiKey);
- GM_setValue("pricingSource", pricingSource);
- $overlay.remove();
- });
- $('#settings-cancel').click(() => $overlay.remove());
- }
- function addPricingSourceLink() {
- if (document.getElementById('pricing-source-button')) return;
- let linksContainer = document.querySelector('.linksContainer___LiOTN');
- if (!linksContainer) return;
- let link = document.createElement('a');
- link.id = 'pricing-source-button';
- link.href = '#';
- link.className = 'linkContainer___X16y4 inRow___VfDnd greyLineV___up8VP iconActive___oAum9';
- link.target = '_self';
- link.rel = 'noreferrer';
- const iconSpan = document.createElement('span');
- iconSpan.className = 'iconWrapper___x3ZLe iconWrapper___COKJD svgIcon___IwbJV';
- iconSpan.innerHTML = `
- <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16">
- <path d="M8 4.754a3.246 3.246 0 1 1 0 6.492 3.246 3.246 0 0 1 0-6.492zM5.754 8a2.246 2.246 0 1 0 4.492 0 2.246 2.246 0 0 0-4.492 0z"/>
- <path d="M9.796 1.343c-.527-1.79-3.065-1.79-3.592 0l-.094.319a.873.873 0 0 1-1.255.52l-.292-.16c-1.64-.892-3.433.902-2.54 2.541l.159.292a.873.873 0 0 1-.52 1.255l-.319.094c-1.79.527-1.79 3.065 0 3.592l.319.094a.873.873 0 0 1 .52 1.255l-.16.292c-.892 1.64.901 3.433 2.54 2.54l.292-.16a.873.873 0 0 1 1.255.52l.094.319c.527 1.79 3.065 1.79 3.592 0l.094-.319a.873.873 0 0 1 1.255-.52l.292.16c1.64.893 3.433-.902 2.54-2.541l-.16-.292a.873.873 0 0 1 .52-1.255l.319-.094c1.79-.527 1.79-3.065 0-3.592l-.319-.094a.873.873 0 0 1-.52-1.255l.16-.292c.893-1.64-.902-3.433-2.54-2.54l-.292.16a.873.873 0 0 1-1.255-.52l-.094-.319zm-2.633.283c.246-.835 1.428-.835 1.674 0l.094.319a1.873 1.873 0 0 0 2.693 1.115l.291-.16c.764-.416 1.6.42 1.184 1.185l-.16.292a1.873 1.873 0 0 0 1.116 2.692l.318.094c.835.246.835 1.428 0 1.674l-.318.094a1.873 1.873 0 0 0-1.116 2.692l.16.292c.416.764-.42 1.6-1.185 1.184l-.291-.16a1.873 1.873 0 0 0-2.693 1.116l-.094.318c-.246.835-1.428.835-1.674 0l-.094-.318a1.873 1.873 0 0 0-2.692-1.116l-.292.16c-.764.416-1.6-.42-1.184-1.185l.16-.292a1.873 1.873 0 0 0-1.116-2.692l-.318-.094c-.835-.246-.835-1.428 0-1.674l.318-.094a1.873 1.873 0 0 0 1.116-2.692l-.16-.292c-.416-.764.42-1.6 1.185-1.184l.292.16a1.873 1.873 0 0 0 2.693-1.116l.094-.318z"/>
- </svg>
- `;
- link.appendChild(iconSpan);
- const textSpan = document.createElement('span');
- textSpan.className = 'linkTitle____NPyM';
- textSpan.textContent = 'Bazaar Filler Settings';
- link.appendChild(textSpan);
- link.addEventListener('click', function(e) {
- e.preventDefault();
- openSettingsModal();
- });
- linksContainer.insertBefore(link, linksContainer.firstChild);
- }
- function addFillButtonAddPage() {
- if (pricingSource !== "Bazaars/TornPal") return;
- if ($("#fill-checked-items").length) return;
- let clearAction = $(".items-footer .clear-action");
- if (!clearAction.length) return;
- let fillBtn = $('<span id="fill-checked-items" class="clear-action t-blue h c-pointer">Fill Checked Items (Disabled)</span>');
- clearAction.after(fillBtn);
- fillBtn.click(() => alert("Bazaars/TornPal is not available. Please select another source."));
- clearAction.off("click").on("click", () => $(".item-toggle").prop("checked", false));
- }
- function addUpdateButtonManagePage() {
- if (pricingSource !== "Bazaars/TornPal") return;
- if ($("#update-checked-items").length) return;
- let undoBtn = $(".confirmation___eWdQi .undo___FTgvP");
- if (!undoBtn.length) return;
- let updateBtn = $('<button id="update-checked-items" type="button" style="margin-left:10px;">Update Checked (Disabled)</button>');
- undoBtn.after(updateBtn);
- updateBtn.click(() => alert("Bazaars/TornPal is not available. Please select another source."));
- }
- function addAddPageCheckboxes() {
- $(".items-cont .title-wrap").each(function() {
- if ($(this).find(".item-toggle").length) return;
- $(this).css("position", "relative");
- const checkbox = $('<input>', {
- type: "checkbox",
- class: "item-toggle",
- click: async function(e) {
- e.stopPropagation();
- await updateAddRow($(this).closest("li.clearfix"), this.checked);
- }
- });
- $(this).append(checkbox);
- });
- }
- function addManagePageCheckboxes() {
- $(".item___jLJcf").each(function() {
- const $desc = $(this).find(".desc___VJSNQ");
- if (!$desc.length || $desc.find(".item-toggle").length) return;
- $desc.css("position", "relative");
- const checkbox = $('<input>', {
- type: "checkbox",
- class: "item-toggle",
- click: async function(e) {
- e.stopPropagation();
- await updateManageRow($(this).closest(".item___jLJcf"), this.checked);
- }
- });
- $desc.append(checkbox);
- });
- }
- if (!validPages.includes(currentPage)) return;
- const storedItems = localStorage.getItem("tornItems");
- const lastUpdated = GM_getValue("lastUpdated", "");
- const todayUTC = new Date().toISOString().split('T')[0];
- if (apiKey && (!storedItems || lastUpdated !== todayUTC || new Date().getUTCHours() === 0)) {
- fetch(`https://api.torn.com/torn/?key=${apiKey}&selections=items`)
- .then(r => r.json())
- .then(data => {
- if (!data.items) {
- console.error("Failed to fetch Torn items or no items found. Possibly invalid API key or rate limit.");
- return;
- }
- let filtered = {};
- for (let [id, item] of Object.entries(data.items)) {
- if (item.tradeable) {
- filtered[id] = {
- name: item.name,
- market_value: item.market_value
- };
- }
- }
- localStorage.setItem("tornItems", JSON.stringify(filtered));
- GM_setValue("lastUpdated", todayUTC);
- })
- .catch(err => {
- console.error("Error fetching Torn items:", err);
- });
- }
- const domObserver = new MutationObserver(() => {
- if (window.location.hash === "#/add") {
- addAddPageCheckboxes();
- addFillButtonAddPage();
- } else if (window.location.hash === "#/manage") {
- addManagePageCheckboxes();
- addUpdateButtonManagePage();
- }
- addPricingSourceLink();
- });
- domObserver.observe(document.body, { childList: true, subtree: true });
- window.addEventListener('hashchange', () => {
- currentPage = window.location.hash;
- if (currentPage === "#/add") {
- addAddPageCheckboxes();
- addFillButtonAddPage();
- } else if (currentPage === "#/manage") {
- addManagePageCheckboxes();
- addUpdateButtonManagePage();
- }
- addPricingSourceLink();
- });
- if (currentPage === "#/add") {
- addAddPageCheckboxes();
- addFillButtonAddPage();
- } else if (currentPage === "#/manage") {
- addManagePageCheckboxes();
- addUpdateButtonManagePage();
- }
- addPricingSourceLink();
- })();