Skip to content

Instantly share code, notes, and snippets.

@Csqhi515
Last active October 18, 2024 13:33
Show Gist options
  • Save Csqhi515/a4af0ce48e9e199750c77575935ac72b to your computer and use it in GitHub Desktop.
Save Csqhi515/a4af0ce48e9e199750c77575935ac72b to your computer and use it in GitHub Desktop.
// ==UserScript==
// @name Trade1 - csgoexo.com
// @namespace Scripts
// @match https://csgoexo.com/
// @grant none
// @version 1.0
// @description
// ==/UserScript==
(() => {
//Get all items for data object
let getAllItems = () => {
let genericError = 'Error while getting items. Try again.';
//All bots items
fetch(config.botsInventoryURL).then(r => r.json()).then(r => {
if (!r.success || !r.items || !r.items) {
return console.log(genericError);
} else {
console.log('Got botsItems.'); data.botsItems = r.items; console.log(data);
if (data.userItems && data.botsItems) console.log('READY!');
}
}).catch(e => console.log(genericError));
//All user items
fetch(config.inventoryURL).then(r => r.json()).then(r => {
if (!r.success || !r.items || !r.items.length) {
return console.log(genericError);
} else {
console.log('Got items.'); data.userItems = r.items; console.log(data);
if (data.userItems && data.botsItems) console.log('READY!');
}
}).catch(e => console.log(genericError));
}
//On click if item
let itemboxClickHandler = (e) => {
if (!document.querySelector('.tradeButton.clone')) {
if (!prepareUI()) return console.log('Error preparing UI.');
}
if (e.target.classList.toString().includes('csgo-item-')) updateDataFromUI();
let itembox = e.target.closest('.item-box');
if (!itembox) return;
let userOffersElement = document.querySelector('.card.offer-user .csgo-items'),
botOffersElement = document.querySelector('.card.offer-bots .csgo-items'),
cardHeaderText = itembox.closest('.card')?.querySelector('.card-header')?.innerText;
let currentOfferLength = userOffersElement.querySelectorAll('.item-box').length,
currentBotsOfferLength = botOffersElement.querySelectorAll('.item-box').length;
let isUserOfferitem = itembox.closest('.card.offer-user') ? true : false,
isBotsOfferitem = itembox.closest('.card.offer-bots') ? true : false,
isUseritem = cardHeaderText && cardHeaderText.indexOf('Your inventory') > -1,
isBotsitem = cardHeaderText && cardHeaderText.indexOf('Bot') > -1,
isItemDisabled = itembox.getAttribute('disabled');
let itemInsp = itembox.querySelector('.csgo-item--insp > a')?.href;
if (!itemInsp) return;
let itemInspExists = (container, itemInsp) => {
let exist = false;
container.querySelectorAll('.csgo-item--insp > a').forEach(e => {
if (e.href == itemInsp) exist = true;
});
return exist;
}
if (isItemDisabled) {
//Add or remove anyway
if (isUserOfferitem)
itembox.remove();
else if (isBotsOfferitem)
itembox.remove();
else if (isUseritem) {
if (!itemInspExists(userOffersElement, itemInsp) && data.userOfferItems.length == currentOfferLength) { userOffersElement.append(itembox.cloneNode(true)); }
}
else if (isBotsitem) {
if (!itemInspExists(botOffersElement, itemInsp) && data.botsOfferItems.length == currentBotsOfferLength) { botOffersElement.append(itembox.cloneNode(true)); }
}
}
updateDataFromUI();
}
//For finding data of selected items to prepare request
let updateDataFromUI = () => {
var userOffersElement = document.querySelector('.card.offer-user .csgo-items'),
botOffersElement = document.querySelector('.card.offer-bots .csgo-items'),
userOfferAmount = document.querySelector('.card.offer-user .card-header .amount'),
botsOfferAmount = document.querySelector('.card.offer-bots .card-header .amount'),
userOfferItemboxes = userOffersElement.querySelectorAll('.item-box'),
botsOfferItemboxes = botOffersElement.querySelectorAll('.item-box');
var datauserItems = data.userItems,
dataBotsItems = data.botsItems;
var userPriceSum = 0,
botsPriceSum = 0;
data.userOfferItems = []; data.botsOfferItems = [];
//Find item data
for (var i = 0; i < userOfferItemboxes.length; i++) {
var element = userOfferItemboxes[i],
insp = element.querySelector('.csgo-item--insp > a')?.href;
for (var ii = 0; ii < datauserItems.length; ii++) {
var item = datauserItems[ii];
if (item.inspectLink && item.inspectLink == insp) {
data.userOfferItems.push(item);
}
}
//For amount UI
let itemprice = element.querySelector('.csgo-item--price')?.innerText;
itemprice = itemprice ? parseFloat(itemprice.split('$')[1]) : undefined;
if (itemprice && !isNaN(itemprice)) {
userPriceSum = (parseFloat(userPriceSum) + itemprice).toFixed(2);
}
}
//Find item data
for (var i = 0; i < botsOfferItemboxes.length; i++) {
var element = botsOfferItemboxes[i],
insp = element.querySelector('.csgo-item--insp > a')?.href;
for (var k in dataBotsItems) {
if (dataBotsItems.hasOwnProperty(k)) {
var items = dataBotsItems[k];
for (var ii = 0; ii < items.length; ii++) {
var item = items[ii];
if (item.inspectLink && item.inspectLink == insp) {
data.botsOfferItems.push(item);
}
}
}
}
//For amount UI
var itemprice = element.querySelector('.csgo-item--price')?.innerText;
itemprice = itemprice ? parseFloat(itemprice.split('$')[1]) : undefined;
if (itemprice && !isNaN(itemprice)) {
botsPriceSum = (parseFloat(botsPriceSum) + itemprice).toFixed(2);
}
}
//Update amount UI
if (userOfferAmount && botsOfferAmount) {
userOfferAmount.innerText = `${userOfferItemboxes.length} Item${userOfferItemboxes.length == 1 ? '' : 's'} - $${userPriceSum}`;
botsOfferAmount.innerText = `${botsOfferItemboxes.length} Item${botsOfferItemboxes.length == 1 ? '' : 's'} - $${botsPriceSum}`;
}
if (config.debug) console.log('Data.', data);
}
//Prepares offers container, go button, and modes
let prepareUI = () => {
var userOffers = document.querySelector('.offer.user')?.closest('.card'),
botOffers = document.querySelector('.offer.bot')?.closest('.card');
if (!userOffers?.querySelector('.csgo-items') || !botOffers?.querySelector('.csgo-items')) {
console.log('Error finding offer elements'); return false;
}
userOffers.classList.add('offer-user');
botOffers.classList.add('offer-bots');
var tradeButton = document.querySelector('.tradeButton');
if (!tradeButton) {
console.log('Error finding trade button');
return false;
}
//Copies and inserts a UI element
let cloneNode = (node) => {
let clone = node.cloneNode(true); node.hidden = true; node.insertAdjacentElement('beforebegin', clone); return clone;
}
//To avoid conflict with existing function, making new button
tradeButton = cloneNode(tradeButton);
tradeButton.className = 'btn tradeButton clone Green btn-success btn-lg btn-block';
tradeButton.disabled = false; tradeButton.innerText = '← GO →';
tradeButton.addEventListener('click', tradeClickHandler);
//Modes next to button
var modeHTML = '<div class="row"><style>.holder .filterCheckbox {margin-left: 5px;color: #fff;}</style>';
for (let mode of [{ label: 'Now™', id: 'mode_now' }, { label: `${padNumber(config.targetTime[0], 2)}:${padNumber(config.targetTime[1], 2)}™`, id: 'mode_timestamp' }]) {
modeHTML += `
<div class="col-sm-12 col-md-6">
<div class="filterCheckbox custom-control custom-radio">
<input type="radio" class="custom-control-input" id="${mode.id}" value="${mode.id}" name="mode" ${mode.id == "mode_timestamp" ? "checked" : ""}>
<label class="custom-control-label" for="${mode.id}">${mode.label}</label>
</div></div>`;
}
modeHTML += '</div>';
tradeButton.insertAdjacentHTML('beforebegin', modeHTML);
document.querySelector('.autoSelect.btn')?.addEventListener('click', updateDataFromUI);
return true;
}
var padNumber = (n, l) => `${n}`.padStart(l, '0');
//On clicking go button
let tradeClickHandler = (e) => {
data.audio?.play();
//Make required request data
prepareOffer();
config.onError = r => {
if (config.debug) {
console.log('Initiate offer did not work, check the request response.' +
(r.message || r.err ? `\n"${r.message || r.err}"` : ''));
}
data.tradeButton ? data.tradeButton.disabled = false : '';
}
config.onSuccess = r => {
acceptOffer(r.trade);
// declineOffer(r.trade);
console.log('Initiate offer worked!.' +
(r.message || r.err ? `\n"${r.message || r.err}"` : ''), "https://steamcommunity.com/tradeoffer/" + r.trade);
data.tradeButton ? data.tradeButton.disabled = false : ''; updateDataFromUI();
}
config.targetTZ = 'america/los_angeles';
let mode = document.querySelector('[name="mode"]:checked').value;
if (mode == 'mode_now') {
initiateOffer();
}
if (mode == 'mode_timestamp') {
var targetTime = config.targetTime;
var targetTZOffsetFromUTC = config.targetTZOffsetFromUTC * -60; //Minutes PDT: 420, PST: 480
var now = new Date();
//A date object referencing targetTime in targetTZ by reference;
var target = new Date(now.toLocaleString('en-us', { timeZone: config.targetTZ })); //current
target = new Date(target.getFullYear(), target.getMonth(), target.getDate() + config.dateIncrement, targetTime[0], targetTime[1], targetTime[2], targetTime[3]);//Target;
//Applying offset to make both same standard to compare
target.setTime(target.getTime() + (-1 * now.getTimezoneOffset() - (-1 * targetTZOffsetFromUTC)) * 60000);//Target;
//Get difference
var duration = target - now;
duration -= config.offsetMS;
//Wait to send at exact time
setTimeout(initiateOffer, duration);
//Remaining UI
var tradeButton = document.querySelector('.tradeButton.clone');
data.tradeButton = tradeButton; tradeButton.disabled = true;
var msToFormat = (milliseconds) => {
var h = Math.floor(milliseconds / 36e5),
m = Math.floor((milliseconds % 36e5) / 6e4),
s = Math.floor((milliseconds % 6e4) / 1000), ms = milliseconds % 1000;
return `${padNumber(h, 2)}:${padNumber(m, 2)}:${padNumber(s, 2)}:${padNumber(ms, 3)}`
}
var milliseconds = target.getTime() - Date.now();
console.log('Remaining: ', msToFormat(milliseconds));
var refreshRemainingUI = config.refreshRemainingUI;
if (refreshRemainingUI) {
var ms = 50; //ms, less for faster ui refresh
var intervalHandle = setInterval(() => {
var milliseconds = target.getTime() - Date.now();
if (milliseconds < 0) {
clearInterval(intervalHandle); tradeButton.innerText = '← GO →';
return tradeButton.disabled = false;
}
tradeButton.innerText = msToFormat(milliseconds);
}, ms);
}
}
}
//Make required request data
let prepareOffer = () => {
var body = { getItems: [], sendItems: [], };
for (var i = 0; i < data.botsOfferItems.length; i++) {
let item = data.botsOfferItems[i];
body['sendItems'].push(item['assetID']);
if (item['owner']) body['bot'] = item['owner'];
}
for (var i = 0; i < data.userOfferItems.length; i++) {
let item = data.userOfferItems[i];
body['getItems'].push(item['assetID']);
}
data.initiatedBot = body['bot'];
data.requestData = {
"headers": {
"accept": "application/json, text/plain, */*",
"content-type": "application/json;charset=UTF-8",
},
"method": "POST",
"body": JSON.stringify(body),
};
}
//Console logs
let logTimestamp = () => {
var now = new Date();
console.log('At: ' + (now.toLocaleString('en-us', { timeZone: config.targetTZ })), now.getMilliseconds() + 'ms', 'UTC' + config.targetTZOffsetFromUTC, now.getTime());
}
//Sends offer request
let initiateOffer = () => {
fetch(config.initiateOfferURL, data.requestData).then(r => r.json()).then(r => {
console.log('Request result.', r);
if (!r.success) config.onError(r);
else config.onSuccess(r);
}).catch(e => { console.log(e); });
console.log('Initiate offer request sent.', data.requestData.body); logTimestamp();
}
//Define in advance for performance
let steamRequestData = {
"headers": { "content-type": "application/x-www-form-urlencoded; charset=UTF-8" },
"body": "",
"method": "POST", "credentials": "include", "mode": "no-cors"
};
//Sends accept request
let acceptOffer = (tradeId) => {
steamRequestData["body"] = "sessionid=" + config.sessionId + "&serverid=1&tradeofferid=" + tradeId + "&partner=" + data.initiatedBot + "&captcha=";
fetch("https://steamcommunity.com/tradeoffer/" + tradeId + "/accept", steamRequestData).then(r => {
console.log('Please check if accept worked on steam.');
return r.json();
}).then(r => {
console.log('Accept Request result.', r);
}).catch(e => { });
console.log('Accept request sent.'); logTimestamp();
}
//Sends decline request
let declineOffer = (tradeId) => {
steamRequestData["body"] = "sessionid=" + config.sessionId;
fetch("https://steamcommunity.com/tradeoffer/" + tradeId + "/decline", steamRequestData).then(r => {
console.log('Please check if decline worked on steam.');
return r.json();
}).then(r => {
console.log('Decline Request result.', r);
}).catch(e => { });
console.log('Decline request sent.'); logTimestamp();
}
//On click
let onClick = (e) => { itemboxClickHandler(e); }
var data = {
userItems: "",
botsItems: "",
userOfferItems: [],
botsOfferItems: [],
requestData: {},
audio: new Audio('https://www.myinstants.com/media/sounds/bomb-has-been-planted-sound-effect-cs-go.mp3'),
};
var config = {
inventoryURL: 'https://csgoexo.com/api/v1/items/inventory/730',
botsInventoryURL: 'https://csgoexo.com/api/v1/items/botsInventory/730',
initiateOfferURL: 'https://csgoexo.com/api/v1/trades/initiateOffer',
targetTime: [0, 0, 0, 0], //Hour, Minutes, Seconds, Milliseconds
targetTZOffsetFromUTC: -7, //Hours PDT: -7, PST: -8
dateIncrement: 1,
offsetMS: 3, //Milliseconds reduced from final target, useful for offseting execution time and ping to server when needed.
refreshRemainingUI: true, //Shows remaining time in button
sessionId: '', //GetCookie('sessionid') from steam
debug: false,
};
//Main
(() => {
getAllItems();
document.addEventListener('click', onClick);
})();
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment