Skip to content

Instantly share code, notes, and snippets.

@stecman
Last active August 12, 2024 02:31
Show Gist options
  • Save stecman/83a731dc36eae905356d to your computer and use it in GitHub Desktop.
Save stecman/83a731dc36eae905356d to your computer and use it in GitHub Desktop.
Steam auto trader

Maximum Hax

Automates the UI actions (clicks, typing) to sell Steam trading cards from the Steam web interface. To use this:

  1. Log into your Steam account in Chrome
  2. Go to [Username] -> Inventory
  3. Open the Javascript console (View -> Developer -> Javascript Console)
  4. Paste in the entire script below and press enter
  5. To start selling all trading cards in your inventory, type doHax() in the console and press enter
  6. ...
  7. Profit (kind of.. yay! cents!)

The quickest way way to stop the process once it has started is to refresh or close the current tab.

/**
* Schedule a function to run on the next tick
* This is useful to wait for the DOM to render changes that have just been made
*
* @param {function} func
*/
function setImmediate(func) {
setTimeout(func, 0);
}
/**
* Timeout as a promise
* @param {int} time - time in milliseconds to wait
* @return {Promise}
*/
function timeout(time) {
return new Promise(function(resolve, reject) {
setTimeout(resolve, time)
});
}
/**
* Return a promise that resolves once a function returns true
*
* @param {function() : bool} conditionFunc
* @return {Promise}
*/
function wait(conditionFunc) {
return new Promise(function(resolve, reject) {
var interval;
interval = setInterval(function() {
if (conditionFunc()) {
clearInterval(interval);
resolve();
}
}, 100);
});
}
/**
* Round a number to a specified number of decmial places
*
* Javascript's Math.round function doesn't support specifying decimal places
*
* @param {number} number
* @param {int} decimalPlaces
* @return {number}
*/
function round(number, decimalPlaces) {
var offset = Math.pow(10, decimalPlaces);
return Math.round(number * offset) / offset;
}
/**
* Calculate the average of the recent price history for the selected trading card
*
* @return {int} dollars
*/
function calcAveragePrice() {
// Use the last 100 data points displayed in the visible price history graph
// This is equal to about five days most of the time
var data = SellItemDialog.m_plotPriceHistory.data;
var recent = data[0].slice(-100);
var recentTotal = recent.reduce((sum, item) => {
// item is an array in the form: [dateString, priceInDollars, number]
return sum + item[1];
}, 0);
return recentTotal / recent.length;
}
/**
* Calculate the price to sell the current selection for
*
* @return {number} dollars
*/
function calcSalePrice() {
var avg = calcAveragePrice();
var price = round(avg * 0.9, 2);
// Print the amount details in the console
var name = document.getElementById('market_sell_dialog_item_name').innerText;
console.log('Will list %s for %f (average is %f)', name, price, avg);
return price;
}
/**
* Perform all UI actions to sell the selected card
* @return {Promise} - promise to be resolved once the sale operation is complete
*/
function sellSelection() {
return new Promise(function(resolve, reject) {
// Trigger Steam's trade dialog to display for the selected card
SellCurrentSelection();
wait(function() {
// Wait for the price history graph to be rendered
// At this point we know the trading dialog is completely ready to use
return jQuery('#pricehistory:visible').length > 0;
}).then(function() {
// Calculate sale price using the graph data
var price = calcSalePrice();
// Update the "buyer pays" field in the UI (converted to dollars, truncated to 2 dp)
document.getElementById('market_sell_buyercurrency_input').value = price.toFixed(2);
return timeout(50);
}).then(function() {
// Call their event handler to register the amount we're selling for in their code
SellItemDialog.OnBuyerPriceInputKeyUp();
return timeout(50);
}).then(function() {
// Accept terms of sale
document.getElementById('market_sell_dialog_accept_ssa').checked = true;
SellItemDialog.OnAccept(new Event('click'));
// Wait for the UI to update
return timeout(50);
}).then(function() {
// Click the sell button
document.getElementById('market_sell_dialog_accept').click();
// Wait for the final OK button to be visible
return wait(function() {
return jQuery('#market_sell_dialog_ok:visible').length > 0;
});
}).then(function() {
// Click the sell confirmation button
document.getElementById('market_sell_dialog_ok').click();
resolve();
});
});
}
function process(index, cards) {
// Click the "Sell" button in the inventory to bring up the sell dialog for this item
var node = cards[index];
node.click();
timeout(100)
.then(function() {
return sellSelection()
})
.then(function() {
// Once selection is sold, wait for the UI to return
return wait(function() {
return SellItemDialog.m_bWaitingOnServer === false
&& jQuery('#inventories:visible').length > 0;
})
})
.then(function() {
// Process the next item
if (cards[index + 1]) {
process(index + 1, cards);
} else {
console.log('Done!');
}
});
}
function doHax() {
// Get all of the links for trading cards rendered on the current page
// (This may include cards that aren't visible in on the current 25-per-page view)
cards = document.querySelectorAll('.inventory_item_link[href^="#753_6"]');
if (cards.length) {
console.log('Starting to process %d cards', cards.length);
process(0, cards);
} else {
console.log('No cards to process');
}
}
@devarthax
Copy link

devarthax commented Aug 11, 2024

Hello. I can use this once and then an exception is raised. Can you fix that? Thanks sweetie.

economy_v2.js?v=egRSKXwDfiOf&l=english:3830 Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'description')
    at Object.Show (economy_v2.js?v=egRSKXwDfiOf&l=english:3830:26)
    at SellCurrentSelection (economy_v2.js?v=egRSKXwDfiOf&l=english:3698:18)
    at <anonymous>:96:9
    at new Promise (<anonymous>)
    at sellSelection (<anonymous>:94:12)
    at <anonymous>:144:20

@stecman
Copy link
Author

stecman commented Aug 12, 2024

Hey @devarthax, this is from 2016 and I haven't used it since then - things have almost certainly changed on Steam's end in that time.

It looks like a fork of the gist has been worked on more recently, so give that a try.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment