Last active
October 18, 2024 13:33
-
-
Save Csqhi515/a4af0ce48e9e199750c77575935ac72b to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ==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