// ==UserScript== // @name Steam Trade Offer Enhancer // @description Browser script to enhance Steam trade offers. // @version 2.2.3 // @author Julia // @namespace http://steamcommunity.com/profiles/76561198080179568/ // @updateURL https://github.com/juliarose/steam-trade-offer-enhancer/raw/master/steam.trade.offer.enhancer.meta.js // @downloadURL https://github.com/juliarose/steam-trade-offer-enhancer/raw/master/steam.trade.offer.enhancer.user.js // @grant GM_addStyle // @grant GM_setValue // @grant GM_getValue // @grant unsafeWindow // @run-at document-end // @include /^https?:\/\/(.*\.)?backpack\.tf(:\\d+)?\/(stats|classifieds).*/ // @include /^https?:\/\/(.*\.)?backpack\.tf(:\d+)?\/(?:id|profiles)\/.*/ // @include /^https?:\/\/steamcommunity\.com\/market\/listings\/440\/.*/ // @include /^https?:\/\/steamcommunity\.com\/(?:id|profiles)\/.*\/inventory(\/$|\?|$)/ // @include /^https?:\/\/steamcommunity\.com\/(?:id|profiles)\/[^\/]+(\/$|\?|$)/ // @include /^https?:\/\/steamcommunity\.com\/(?:id|profiles)\/.*\/tradeoffers/ // @include /^https?:\/\/steamcommunity\.com\/tradeoffer.*/ // ==/UserScript== const scripts = [ { includes: [ /^https?:\/\/(.*\.)?backpack\.tf(:\\d+)?\/(stats|classifieds).*/ ], fn: function main({ Utils }) { const dom = { listingsElList: document.getElementsByClassName('listing') }; Array.from(dom.listingsElList).forEach((listingEl) => { const itemEl = listingEl.getElementsByClassName('item')[0]; const offerButtonEl = listingEl.getElementsByClassName('listing-buttons')[0].lastElementChild; const href = offerButtonEl.getAttribute('href'); const { listing_intent, listing_price } = itemEl.dataset; const currencies = Utils.stringToCurrencies(listing_price); // no currencies if (currencies === null) { // continue return; } // array of query string parameters // e.g. ['listing_intent=1', 'listing_currencies_keys=2'] const query = (function getQuery() { const params = { listing_intent: listing_intent === 'buy' ? 0 : 1 }; for (let k in currencies) { params['listing_currencies_' + k] = currencies[k]; } return Object.entries(params).map(([k, v]) => { return k + '=' + v; }); }()); // url with query added const url = [ href, ...query ].join('&'); offerButtonEl.setAttribute('href', url); }); } }, { includes: [ /^https?:\/\/(.*\.)?backpack\.tf(:\d+)?\/(?:id|profiles)\/.*/ ], fn: function main({ $, Utils, getStored, setStored }) { const urlParams = Utils.getURLParams(); const stored = { key_price: 'getInventory.key_price' }; const page = { $document: $(document), $backpack: $('#backpack'), $refined: $('.refined-value'), $inventorySortMenu: $('#inventory-sort-menu ul.dropdown-menu'), get: { $selected: () => page.$backpack.find('li.item:visible:not(.unselected)'), $listedItems: () => page.$backpack.find('li.item:visible:not(.unselected)[data-listing_price]'), $firstSelectPage: () => page.$backpack.find('span.select-page:first'), $backpackPage: () => page.$backpack.find('div.backpack-page'), $itemPricedInKeys: () => page.$backpack.find('li.item[data-p_bptf*="keys"]:first'), $crateKey: () => page.$backpack.find('.item[data-name="Mann Co. Supply Crate Key"]:first'), $inventoryCmpFrom: () => $('#inventory-cmp-from'), $inventoryCmpTo: () => $('#inventory-cmp-to') } }; // the key value is used for displaying totals in keys // get key value from cache, if available let keyValue = getStored(stored.key_price); // called when backpack is loaded function onBackpackLoad() { // get the value of keys in metal // this should be very approximate, but close enough function getKeyValue() { // gets pricing details from item element function parseItem(itemEl) { // parse price string e.g. "1-1.2 keys" function parseString(string) { const match = string.match(/^([\d\.]*)[\-\u2013]?([\d\.]*)? (\w*)/); const currencyNames = { 'metal': 'metal', 'ref': 'metal', 'keys': 'keys', 'key': 'keys' }; if (match) { details.value = parseFloat(match[1]); details.average = details.value; details.currency = currencyNames[match[3]]; // if there are 3 match groups, there is a range if (match[2]) { details.value_high = parseFloat(match[2]); details.average = (details.value + details.value_high) / 2; } } } function getRefinedValue(allStr) { const match = allStr.replace(/\,/g, '').match(/(\d+\.?\d*) ref/); const value = match && parseFloat(match[1]); const rawValue = details.raw; const canUseRawValue = Boolean( value && rawValue && value.toFixed(2) === rawValue.toFixed(2) ); // the raw value has extra precision but includes the value of paint/strange parts. // if it is close to the value of the price items, // we can use the raw value instead which is more precise if (canUseRawValue) { return rawValue; } else { return value || rawValue; } } const data = itemEl.dataset; const details = {}; if (data.price) { details.raw = parseFloat(data.price); } if (data.p_bptf) { parseString(data.p_bptf); } details.refined = getRefinedValue(data.p_bptf_all || ''); return details; } // find item priced in keys const item = page.get.$itemPricedInKeys()[0]; const price = item && parseItem(item); const useItemPricedInKeys = Boolean( price && price.currency === 'keys' && price.average && price.refined ); // use an item priced in keys to extract the key value if (useItemPricedInKeys) { // to get the value of keys in refined metal... // take the price in metal divided by the price in keys return price.refined / price.average; } else { // set value using the value of a key, if no items in inventory are priced in keys const key = page.get.$crateKey()[0]; const price = ( key && parseItem(key) ); return ( price && price.refined ); } } function filterItems($filtered) { // no items to filter if ($filtered.length === 0) { return; } const $backpack = page.$backpack; const $items = $backpack.find('li.item:not(.spacer)'); const $unfiltered = $items.not($filtered); const $spacers = $backpack.find('li.spacer'); // all hidden items are moved to a temp page const $tempPage = $('