// ==UserScript== // @name SteamTrade Matcher Userscript - Automatically add cards to trade offers // @description Allows quicker trade offers by automatically adding cards as matched by SteamTrade Matcher // @match *://steamcommunity.com/tradeoffer/new/*source=stm* // @match *://*.steamtradematcher.com/* // @connect steamtradematcher.com // @version 2.0.2 // @grant GM.getValue // @grant GM.setValue // @grant GM_getValue // @grant GM_setValue // @grant GM_deleteValue // @grant GM.deleteValue // @grant GM_xmlhttpRequest // @grant GM.xmlHttpRequest // @namespace https://www.steamtradematcher.com // @icon https://www.steamtradematcher.com/favicon.png // @require https://greasemonkey.github.io/gm4-polyfill/gm4-polyfill.js // @author Robou / Tithen-Firion // ==/UserScript== /*jslint browser:true*/ const WEBSITE_HOSTS = ["steamtradematcher.com", "www.steamtradematcher.com", "beta.steamtradematcher.com"]; function getRandomInt(min, max) { "use strict"; return Math.floor(Math.random() * (max - min)) + min; } function mySort(a, b) { "use strict"; return parseInt(b.id) - parseInt(a.id); } ///// Steam functions ///// function restoreCookie(oldCookie) { "use strict"; if (oldCookie) { var now = new Date(); var time = now.getTime(); time += 15 * 24 * 60 * 60 * 1000; now.setTime(time); document.cookie = 'strTradeLastInventoryContext=' + oldCookie + '; expires=' + now.toUTCString() + '; path=/tradeoffer/'; } } function addCards(g_s, g_v) { "use strict"; var tmpCards, inv, index, currentCards; var failLater = false; var cardTypes = [[], []]; g_v.Cards.forEach(function (requestedCards, i) { tmpCards = {}; inv = g_v.Users[i].rgContexts[753][6].inventory; inv.BuildInventoryDisplayElements(); inv = inv.rgInventory; Object.keys(inv).forEach(function (item) { // add all matching cards to temporary dict index = requestedCards.indexOf(inv[item].classid); if (index > -1) { if (tmpCards[inv[item].classid] === undefined) { tmpCards[inv[item].classid] = []; } tmpCards[inv[item].classid].push({type: inv[item].type, element: inv[item].element, id: inv[item].id}); } }); if (g_s.ORDER === 'SORT') { // sort cards descending by card id for each classid Object.keys(tmpCards).forEach(function (classid) { tmpCards[classid].sort(mySort); }); } // add cards to trade in order given by STM requestedCards.forEach(function (classid) { currentCards = tmpCards[classid] || []; // all cards from inventory with requested classid if (currentCards.length === 0) { failLater = true; } else { index = 0; if (g_s.ORDER === 'RANDOM') { // randomize index index = getRandomInt(0, currentCards.length); } unsafeWindow.MoveItemToTrade(currentCards[index].element); cardTypes[i].push(currentCards[index].type); currentCards.splice(index, 1); } }); }); if(failLater || document.querySelectorAll('#your_slots .has_item').length != document.querySelectorAll('#their_slots .has_item').length) { unsafeWindow.ShowAlertDialog('Items missing', 'Some items are missing and were not added to trade offer. Script aborting.'); throw ('Cards missing'); } // check if item types match cardTypes[1].forEach(function (type) { index = cardTypes[0].indexOf(type); if (index > -1) { cardTypes[0].splice(index, 1); } else { unsafeWindow.ShowAlertDialog('Not 1:1 trade', 'This is not a valid 1:1 trade. Script aborting.'); throw ('Not 1:1 trade'); } }); restoreCookie(g_v.oldCookie); // inject some JS to do something after trade offer is sent if (g_s.DO_AFTER_TRADE !== 'NOTHING') { var functionToInject = 'var DO_AFTER_TRADE = "' + g_s.DO_AFTER_TRADE + '";'; functionToInject += '$J(document).ajaxSuccess(function (event, xhr, settings) {'; functionToInject += 'if (settings.url === "https://steamcommunity.com/tradeoffer/new/send") {'; functionToInject += 'if (DO_AFTER_TRADE === "CLOSE_WINDOW") { window.close();'; functionToInject += '} else if (DO_AFTER_TRADE === "CLICK_OK") {'; functionToInject += 'document.querySelector("div.newmodal_buttons > div").click(); } } });'; var script = document.createElement('script'); script.appendChild(document.createTextNode(functionToInject)); document.body.appendChild(script); } // send trade offer if (g_s.AUTO_SEND) { unsafeWindow.ToggleReady(true); unsafeWindow.CTradeOfferStateManager.ConfirmTradeOffer(); } } function checkContexts(g_s, g_v) { "use strict"; var ready = 0; // check if Steam loaded everything needed g_v.Users.forEach(function (user) { if (user.rgContexts && user.rgContexts[753] && user.rgContexts[753][6]) { if (user.cLoadsInFlight === 0) { if (user.rgContexts[753][6].inventory) { ready += 1; } else { unsafeWindow.document.getElementById('trade_inventory_unavailable').show(); unsafeWindow.document.getElementById('trade_inventory_pending').show(); user.loadInventory(753, 6); } } } }); if (ready === 2) { // select your inventory unsafeWindow.TradePageSelectInventory(g_v.Users[0], 753, "6"); // set trade offer message document.getElementById('trade_offer_note').value = g_s.MESSAGE; try { addCards(g_s, g_v); } catch (e) { // no matter what happens, restore old cookie restoreCookie(g_v.oldCookie); console.log(e); } } else { window.setTimeout(checkContexts, 500, g_s, g_v); } } function getUrlVars() { "use strict"; var vars = []; var hashes = window.location.href.slice(window.location.href.indexOf('?') + 1).split('&'); hashes.forEach(function (hash) { hash = hash.split('='); vars.push(hash[0]); vars[hash[0]] = hash[1]; }); return vars; } ///// STM functions ///// async function restoreDefaultSettings() { if (window.confirm('Are you sure you want to restore default settings?')) { Promise.all([ GM.deleteValue('MESSAGE'), GM.deleteValue('DO_AFTER_TRADE'), GM.deleteValue('ORDER'), GM.deleteValue('AUTO_SEND') ]).then(()=>{ document.location.reload(); }); } } function saveSettings() { Promise.all([ GM.setValue('MESSAGE', document.getElementById('trade-message').value), GM.setValue('DO_AFTER_TRADE', document.getElementById('after-trade').value), GM.setValue('ORDER', document.getElementById('cards-order').value), GM.setValue('AUTO_SEND', document.getElementById('auto-send').checked) ]).then(()=>{ document.getElementById('alert').style.display = 'block'; window.scroll(0, 0); }); } function prepareSettings(g_s) { "use strict"; var template = '
Determines what happens when you complete a trade offer.
Determines which card is added to trade.
Custom text that will be included automatically with your trade offers created through SteamTrade Matcher while using this userscript. To remove this functionality, simply delete the text.
'); newHTML += template.replace('{T}', 'Auto-send trade offer').replace('{B}', 'Makes it possible for the script to automatically send trade offers without any action on your side. This is not recommended as you should always check your trade offers, but, well, this is a possible thing. Please note that incomplete trade offers (missing cards, ...) won\'t be sent automatically even when this parameter is enabled.