// ==UserScript== // @name Neopets Shop Stock Pricer // @version 1.1.0 // @author manacake.co // @namespace manacake.co // @description For use on the user's shop stock page: queries the latest price of an item and displays it so the user can adjust their prices accordingly. // @license CC-BY-NC-4.0 // @website https://manacake.co // @updateURL https://raw.githubusercontent.com/manacake/userscripts/main/neopetsShopStockPricer.user.js // @downloadURL https://raw.githubusercontent.com/manacake/userscripts/main/neopetsShopStockPricer.user.js // @match *://*.neopets.com/market.phtml* // @icon https://manacake.co/favicon.ico // @grant GM_xmlhttpRequest // @connect itemdb.com.br // @noframes // ==/UserScript== (async function() { 'use strict'; const possibleShopStockPage = document.querySelector('td.content p'); if (!possibleShopStockPage) return; // Helper to fetch item price from itemdb's API using GM_xmlhttpRequest to bypass CORS const fetchItemPriceHistory = (names) => { return new Promise((resolve, reject) => { GM_xmlhttpRequest({ method: "POST", url: "https://itemdb.com.br/api/v1/items/many", headers: { "Content-Type": "application/json" }, data: JSON.stringify({ name: names }), onload: function(response) { if (response.status >= 200 && response.status < 300) { try { resolve(JSON.parse(response.responseText)); } catch (e) { reject("Error parsing JSON response"); } } else { reject(`[itemdb error] ${response.status} ${response.statusText}`); } }, onerror: function(error) { console.error('Cannot fetch item prices', error); reject(error); } }); }); }; // Sanity checking if we're on a user's shop stock page if (possibleShopStockPage.textContent.includes('When you sell an item, the Neopoints will go into your')) { const table = document.querySelector('form table'); const rows = table.querySelectorAll('tr'); // Some users might have their pin security enabled which will require logic adjustments const isPinEnabled = !!table.querySelector('input[name="pin"]'); const listLengthAdjustment = isPinEnabled ? 3 : 1; const names = []; // Add a new column: Price History to the header row const headerCell = document.createElement('td'); headerCell.setAttribute('align', 'center'); headerCell.setAttribute('bgcolor', '#dddd77'); headerCell.innerHTML = 'Price History'; rows[0].cells[3].insertAdjacentElement('afterend', headerCell); // Grab item names to fetch price check for (let i = 1; i < rows.length - listLengthAdjustment; i++) { const row = rows[i]; const itemName = row.cells[0].textContent.trim(); if (!names.includes(itemName)) names.push(itemName); } try { const data = await fetchItemPriceHistory(names); // Update the item rows to include historical pricing for (let i = 1; i < rows.length - listLengthAdjustment; i++) { const row = rows[i]; const itemName = row.cells[0].textContent.trim(); const newCell = document.createElement('td'); const itemData = data[itemName]; const isInflated = itemData?.price?.inflated; const itemPrice = itemData?.price?.value; const styleAttr = isInflated ? 'style="color: red;"' : ''; newCell.setAttribute('align', 'center'); newCell.setAttribute('bgcolor', '#ffffcc'); newCell.innerHTML = `${itemPrice ?? '??'}`; row.cells[3].insertAdjacentElement('afterend', newCell); } // Fix colspans for the footer rows if (isPinEnabled) rows[rows.length - 3].querySelector('td').setAttribute('colspan', '8'); rows[rows.length - 1].querySelector('td').setAttribute('colspan', '8'); } catch (error) { console.error("Failed to update shop prices:", error); } } })();