Last active
July 8, 2024 11:36
-
-
Save Ne3tCode/bd5512b8d6b677255bf3038cd23f01e7 to your computer and use it in GitHub Desktop.
ASF bot list trade matcher
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 ASF STM (Nephrite Mod) | |
// @description ASF bot list trade matcher | |
// @license Apache-2.0 | |
// @author Rudokhvist, Nephrite | |
// @match https://steamcommunity.com/id/*/badges* | |
// @match https://steamcommunity.com/profiles/*/badges* | |
// @version 2.14-custom | |
// @connect asf.justarchi.net | |
// @grant GM.xmlHttpRequest | |
// @grant GM_addStyle | |
// @grant GM_xmlhttpRequest | |
// ==/UserScript== | |
/* global BigInt */ | |
(function () { | |
"use strict"; | |
//configuration | |
const weblimiter = 50; | |
const errorLimiter = 30000; | |
const debug = false; | |
const maxErrors = 3; | |
const botCacheTime = 5 * 60000; | |
const maxBotsToScan = 100; | |
const bRandomizeBots = false; | |
const maxPagesLimit = 0; | |
const filterBackgroundColor = "rgba(23, 26, 33, 0.8)"; | |
// Note: disables `maxPagesLimit` if not empty. ToDo: limit number of IDs | |
const appIdsToScanOverride = []; | |
//styles | |
const css = ` | |
#asf_stm_filters_body { | |
max-height: calc(100vh - 95px); | |
overflow-y: auto; | |
display: grid; | |
grid-template-columns: auto; | |
} | |
.asf_stm_trashbusket_button { | |
font-size: 30px; | |
width: 30px; | |
text-align: center; | |
line-height: 30px; | |
vertical-align: bottom; | |
} | |
`; | |
const EType = { | |
TradingCardFoil: 3, | |
TradingCard: 5 | |
}; | |
//do not change | |
let myProfileLink = ""; | |
let errors = 0; | |
let bots = null; | |
let myBadges = []; | |
let botBadges = []; | |
let maxPages = 1; | |
let stop = false; | |
let classIdsDB = JSON.parse(localStorage.getItem("Ryzhehvost.ASF.STM.ClassIDs")); | |
if (classIdsDB === null) { | |
classIdsDB = new Object(); | |
} | |
function debugTime(name) { | |
if (debug) { | |
console.time(name); | |
} | |
} | |
function debugTimeEnd(name) { | |
if (debug) { | |
console.timeEnd(name); | |
} | |
} | |
function debugPrint(msg) { | |
if (debug) { | |
console.log(new Date().toLocaleTimeString("en-GB", { hour12: false, hour: "2-digit", minute: "2-digit", second: "2-digit", fractionalSecondDigits: 3 }) + " : " + msg); | |
} | |
} | |
function deepClone(object) { | |
return JSON.parse(JSON.stringify(object)); | |
} | |
function getPartner(str) { | |
if (typeof BigInt !== "undefined") { | |
return (BigInt(str) % BigInt(4294967296)).toString(); | |
} else { | |
let result = 0; | |
for (let i = 0; i < str.length; i++) { | |
result = (result * 10 + Number(str[i])) % 4294967296; | |
} | |
return result; | |
} | |
} | |
function enableButton() { | |
let buttonDiv = document.getElementById("asf_stm_button_div"); | |
buttonDiv.setAttribute("class", "profile_small_header_additional"); | |
buttonDiv.setAttribute("title", "Scan ASF STM"); | |
let button = document.getElementById("asf_stm_button"); | |
button.addEventListener("click", buttonPressedEvent, false); | |
} | |
function disableButton() { | |
let buttonDiv = document.getElementById("asf_stm_button_div"); | |
buttonDiv.setAttribute("class", "profile_small_header_additional btn_disabled"); | |
buttonDiv.setAttribute("title", "Scan is in process"); | |
let button = document.getElementById("asf_stm_button"); | |
button.removeEventListener("click", buttonPressedEvent, false); | |
} | |
function updateMessage(text) { | |
let message = document.getElementById("asf_stm_message"); | |
message.textContent = text; | |
} | |
function hideMessage() { | |
let messageBox = document.getElementById("asf_stm_messagebox"); | |
messageBox.setAttribute("style", "display: none;"); | |
} | |
function hideThrobber() { | |
let throbber = document.getElementById("throbber"); | |
throbber.setAttribute("style", "display: none;"); | |
} | |
function updateProgress(index, total) { | |
const bar = document.getElementById("asf_stm_progress"); | |
let progress = 0; | |
if (total > 0) { | |
progress = 100 * ((index + 1) / total); | |
} | |
bar.style.width = `${progress}%`; | |
bar.style.transitionDuration = "0.5s"; | |
if (progress == 100) { | |
bar.style.transitionDuration = "0s"; | |
} | |
} | |
function getClassIDs(index) { | |
updateMessage("Updating cards database for badge " + (index + 1) + " of " + myBadges.length); | |
debugPrint("getClassIDs for " + myBadges[index].appId); | |
for (let i = 0; i < myBadges[index].maxCards; i++) { | |
if (classIdsDB.hasOwnProperty(myBadges[index].appId) && classIdsDB[myBadges[index].appId].hasOwnProperty(myBadges[index].cards[i].item)) { | |
if (i == myBadges[index].maxCards - 1) { | |
//it's last card, so it means we have them all | |
index++; | |
if (index < myBadges.length) { | |
setTimeout(getClassIDs, 0, index); // because recursion | |
//getClassIDs(index); | |
} else { | |
//debugPrint(JSON.stringify(classIdsDB)); | |
localStorage.setItem("Ryzhehvost.ASF.STM.ClassIDs", JSON.stringify(classIdsDB)); | |
setTimeout(GetCards, weblimiter, 0, 0); | |
} | |
return; | |
} | |
} else { | |
break; //missing something, update needed | |
} | |
} | |
let searchUrl = "https://steamcommunity.com/market/search/render/?start=0&count=30&search_descriptions=0&appid=753&category_753_Game[]=tag_app_" + myBadges[index].appId + "&category_753_cardborder[]=tag_cardborder_" + myBadges[index].border + "&norender=1"; | |
debugPrint(searchUrl); | |
let xhr = new XMLHttpRequest(); // ToDo: GM_xmlhttpRequest | |
xhr.open("GET", searchUrl, true); | |
xhr.responseType = "json"; | |
xhr.onload = function () { | |
if (stop) { | |
updateMessage("Interrupted by user"); | |
hideThrobber(); | |
enableButton(); | |
let stopButton = document.getElementById("asf_stm_stop"); | |
stopButton.parentNode.removeChild(stopButton); | |
return; | |
} | |
let status = xhr.status; | |
if (status === 200) { | |
let searchResponse = xhr.response; | |
//debugPrint(JSON.stringify(searchResponse)); | |
if (searchResponse.success == true && searchResponse.total_count >= myBadges[index].maxCards) { | |
let results = searchResponse.results; | |
errors = 0; | |
for (let cardnumber = 0; cardnumber < myBadges[index].maxCards; cardnumber++) { | |
debugPrint("looking for card " + myBadges[index].cards[cardnumber].item); | |
//debugPrint(myBadges[index].cards[cardnumber].item); | |
for (let i = 0; i < results.length; i++) { | |
//debugPrint(results[i].hash_name); | |
if (results[i].hash_name === myBadges[index].cards[cardnumber].item) { | |
debugPrint("found!"); | |
let classid = results[i].asset_description.classid; | |
if (classIdsDB[myBadges[index].appId] === undefined) { | |
classIdsDB[myBadges[index].appId] = new Object(); | |
} | |
let cardsClasses = classIdsDB[myBadges[index].appId]; | |
cardsClasses[myBadges[index].cards[cardnumber].item] = classid; | |
break; | |
} | |
} | |
if (!(classIdsDB.hasOwnProperty(myBadges[index].appId) && classIdsDB[myBadges[index].appId].hasOwnProperty(myBadges[index].cards[cardnumber].item))) { | |
//still not found... | |
updateMessage('Error getting classid for card "' + myBadges[index].cards[cardnumber].item + '" from ' + myBadges[index].appId + ", please report this!"); | |
// exclude this badge from matching | |
myBadges[index].maxSets = myBadges[index].lastSet = 0; | |
myBadges[index].cards.forEach((card) => { | |
card.count = 0; | |
}); | |
errors++; | |
break; | |
} | |
} | |
index++; | |
} else { | |
updateMessage("Error getting card data for " + myBadges[index].appId + ", please report this!"); | |
debugPrint("Error getting card data for " + myBadges[index].appId + ", please report this!"); | |
localStorage.setItem("Ryzhehvost.ASF.STM.ClassIDs", JSON.stringify(classIdsDB)); | |
errors++; | |
} | |
if (index < myBadges.length) { | |
setTimeout(getClassIDs, weblimiter + errorLimiter * errors, index); | |
} else { | |
//debugPrint(JSON.stringify(classIdsDB)); | |
localStorage.setItem("Ryzhehvost.ASF.STM.ClassIDs", JSON.stringify(classIdsDB)); | |
setTimeout(GetCards, weblimiter, 0, 0); | |
} | |
return; | |
} else { | |
errors++; | |
} | |
if ((status < 400 || status >= 500) && errors <= maxErrors) { | |
setTimeout(getClassIDs, weblimiter + errorLimiter * errors, index); | |
} else { | |
if (status != 200) { | |
updateMessage("Error getting classid, ERROR " + status); | |
} else { | |
//ToDo: should never happen | |
updateMessage("Error getting classid, malformed json"); | |
} | |
hideThrobber(); | |
enableButton(); | |
let stopButton = document.getElementById("asf_stm_stop"); | |
stopButton.parentNode.removeChild(stopButton); | |
return; | |
} | |
}; | |
xhr.onerror = function () { | |
if (stop) { | |
updateMessage("Interrupted by user"); | |
hideThrobber(); | |
enableButton(); | |
let stopButton = document.getElementById("asf_stm_stop"); | |
stopButton.parentNode.removeChild(stopButton); | |
return; | |
} | |
errors++; | |
if (errors <= maxErrors) { | |
setTimeout(getClassIDs, weblimiter + errorLimiter * errors, index); | |
return; | |
} else { | |
debugPrint("error"); | |
updateMessage("Error getting classid"); | |
hideThrobber(); | |
enableButton(); | |
let stopButton = document.getElementById("asf_stm_stop"); | |
stopButton.parentNode.removeChild(stopButton); | |
return; | |
} | |
}; | |
xhr.send(); | |
} | |
function populateCards(item) { | |
let classList = ""; | |
let htmlCards = ""; | |
for (let j = 0; j < item.cards.length; j++) { | |
let itemIcon = item.cards[j].iconUrl; | |
let itemName = item.cards[j].item.substring(item.cards[j].item.indexOf("-") + 1); | |
for (let k = 0; k < item.cards[j].count; k++) { | |
if (classList != "") { | |
classList += ";"; | |
} | |
classList += classIdsDB[item.appId][item.cards[j].item]; | |
let cardTemplate = ` | |
<div class="showcase_slot"> | |
<img class="image-container" src="${itemIcon}/98x115"> | |
<div class="commentthread_subscribe_hint" style="width: 98px;"> | |
<a target="_blank" rel="noopener noreferrer" href="https://steamcommunity.com/market/listings/753/${encodeURIComponent(item.cards[j].item)}">${itemName}</a> | |
</div> | |
</div> | |
`; | |
htmlCards += cardTemplate; | |
} | |
} | |
return { | |
htmlCards: htmlCards, | |
classList: classList, | |
}; | |
} | |
function getClasses(item) { | |
let classes = ""; | |
for (let j = 0; j < item.cards.length; j++) { | |
for (let k = 0; k < item.cards[j].count; k++) { | |
if (classes != "") { | |
classes += ";"; | |
} | |
classes += classIdsDB[item.appId][item.cards[j].item]; | |
} | |
} | |
return classes; | |
} | |
function updateTrade(row) { | |
let index = row.id.split("_")[1]; | |
let tradeLink = row.getElementsByClassName("full_trade_url")[0]; | |
let splitUrl = tradeLink.href.split("&"); | |
let them = ""; | |
let you = ""; | |
for (let i = 0; i < bots.Result[index].itemsToSend.length; i++) { | |
let appId = bots.Result[index].itemsToSend[i].appId; | |
let border = bots.Result[index].itemsToSend[i].border; | |
let checkBox = document.getElementById(`astm_${appId}_${border}`); | |
if (checkBox.checked) { | |
if (you != "") { | |
you += ";"; | |
} | |
you = you + getClasses(bots.Result[index].itemsToSend[i]); | |
if (them != "") { | |
them += ";"; | |
} | |
them = them + getClasses(bots.Result[index].itemsToReceive[i]); | |
} | |
} | |
splitUrl[3] = "them=" + them; | |
splitUrl[4] = "you=" + you; | |
tradeLink.href = splitUrl.join("&"); | |
} | |
function checkRow(row) { | |
debugPrint("checkRow"); | |
let matches = row.getElementsByClassName("badge_row"); | |
let visible = false; | |
for (let i = 0; i < matches.length; i++) { | |
if (matches[i].parentElement.style.display != "none") { | |
visible = true; | |
break; | |
} | |
} | |
if (visible) { | |
row.style.display = "block"; | |
updateTrade(row); | |
} else { | |
row.style.display = "none"; | |
} | |
} | |
function addMatchRow(index) { | |
debugPrint("addMatchRow " + index); | |
let itemsToSend = bots.Result[index].itemsToSend; | |
let itemsToReceive = bots.Result[index].itemsToReceive; | |
// sort by game name | |
function compareNames(a, b) { | |
const nameA = a.title; | |
const nameB = b.title; | |
return nameA.localeCompare(nameB); | |
} | |
itemsToSend.sort(compareNames); | |
itemsToReceive.sort(compareNames); | |
let tradeUrl = "https://steamcommunity.com/tradeoffer/new/?partner=" + getPartner(bots.Result[index].SteamIDText) + "&token=" + bots.Result[index].TradeToken + "&source=stm"; | |
let globalYou = ""; | |
let globalThem = ""; | |
let matches = ""; | |
let any = ""; | |
if (bots.Result[index].MatchEverything) { | |
any = ` <sup><span class="avatar_block_status_in-game" style="font-size: 8px; cursor:help" title="This bots trades for any cards within same set"> ANY </span></sup>`; | |
} | |
for (let i = 0; i < itemsToSend.length; i++) { | |
let appId = itemsToSend[i].appId; | |
let itemToReceive = itemsToReceive.find((a) => a.appId == appId); | |
let gameName = itemsToSend[i].title; | |
let display = "inline-block"; | |
let badgeBorder = itemsToSend[i].border ? "?border=1" : ""; | |
//remove placeholder | |
let filterWidget = document.getElementById("asf_stm_filters_body"); | |
let placeholder = document.getElementById("asf_stm_placeholder"); | |
if (placeholder != null) { | |
placeholder.parentNode.removeChild(placeholder); | |
} | |
//add filter | |
let checkBox = document.getElementById(`astm_${appId}_${itemsToSend[i].border}`); | |
if (checkBox == null) { | |
let newFilter = `<span style="margin-right: 20px; white-space: nowrap; display: inline-block;"><input type="checkbox" id="astm_${appId}_${itemsToSend[i].border}" checked="" /><label for="astm_${appId}_${itemsToSend[i].border}">${gameName}</label></span>`; | |
let spanTemplate = document.createElement("template"); | |
spanTemplate.innerHTML = newFilter.trim(); | |
filterWidget.appendChild(spanTemplate.content.firstChild); | |
} else { | |
if (!checkBox.checked) { | |
display = "none"; | |
} | |
} | |
let sendResult = populateCards(itemsToSend[i]); | |
let receiveResult = populateCards(itemToReceive); | |
let tradeUrlApp = tradeUrl + "&them=" + receiveResult.classList + "&you=" + sendResult.classList; | |
let matchTemplate = ` | |
<div class="asf_stm_appid_${appId}_${itemsToSend[i].border}" style="display:${display}"> | |
<div class="badge_row is_link goo_untradable_note showcase_slot"> | |
<div class="notLoggedInText"> | |
<img style="background-color: var(--gpStoreDarkerGrey);" height=69 alt="${gameName}" src="https://steamcdn-a.akamaihd.net/steam/apps/${appId}/capsule_184x69.jpg" | |
onerror="this.onerror=null;this.src='https://store.akamai.steamstatic.com/public/images/gift/steam_logo_digitalgiftcard.png'"> | |
<div> | |
<div title="View badge progress for this game"> | |
<a target="_blank" rel="noopener noreferrer" href="https://steamcommunity.com/${myProfileLink}/gamecards/${appId}/${badgeBorder}">${gameName}</a> | |
</div> | |
</div> | |
<a href="${tradeUrlApp}" target="_blank" rel="noopener noreferrer"> | |
<div class="btn_darkblue_white_innerfade btn_medium"> | |
<span>Offer a trade</span> | |
</div> | |
</a> | |
<a class="btn_darkred_white_innerfade asf_stm_trashbusket_button" data-appid="${appId}" data-border="${itemsToSend[i].border}" title="Remove match">🗑</a> | |
</div> | |
<div class="showcase_slot"> | |
<div class="showcase_slot profile_header"> | |
<div class="badge_info_unlocked profile_xp_block_mid avatar_block_status_in-game badge_info_title badge_row_overlay" style="height: 15px;">You</div> | |
${sendResult.htmlCards} | |
</div> | |
<span class="showcase_slot badge_info_title booster_creator_actions"> | |
<h1>➡</h1> | |
</span> | |
</div> | |
<div class="showcase_slot profile_header"> | |
<div class="badge_info_unlocked profile_xp_block_mid avatar_block_status_online badge_info_title badge_row_overlay ellipsis" style="height: 15px;"> | |
${bots.Result[index].Nickname} | |
</div> | |
${receiveResult.htmlCards} | |
</div> | |
</div> | |
</div> | |
`; | |
if (checkBox == null || checkBox.checked) { | |
matches += matchTemplate; | |
if (globalYou != "") { | |
globalYou += ";"; | |
} | |
globalYou += sendResult.classList; | |
if (globalThem != "") { | |
globalThem += ";"; | |
} | |
globalThem += receiveResult.classList; | |
} | |
} | |
// Sort filters | |
let filtersContainer = document.querySelector("#asf_stm_filters_body"); | |
let items = Array.from(filtersContainer.querySelectorAll("span")); | |
items.sort((nodeA, nodeB) => nodeA.textContent.localeCompare(nodeB.textContent)); | |
filtersContainer.append(...items); | |
let tradeUrlFull = tradeUrl + "&them=" + globalThem + "&you=" + globalYou; | |
let rowTemplate = ` | |
<div id="asfstmbot_${index}" class="badge_row"> | |
<div class="badge_row_inner"> | |
<div class="badge_title_row guide_showcase_contributors"> | |
<div class="badge_title_stats"> | |
<a class="full_trade_url" href="${tradeUrlFull}" target="_blank" rel="noopener noreferrer" > | |
<div class="btn_darkblue_white_innerfade btn_medium"> | |
<span>Offer a trade for all</span> | |
</div> | |
</a> | |
<a class="btn_darkred_white_innerfade asf_stm_trashbusket_button" data-asfstmbot="1" title="Remove match">🗑</a> | |
</div> | |
<div style="float: left;" class=""> | |
<div class="user_avatar playerAvatar online"> | |
<a target="_blank" rel="noopener noreferrer" href="https://steamcommunity.com/profiles/${bots.Result[index].SteamIDText}"> | |
<img src="https://avatars.cloudflare.steamstatic.com/${bots.Result[index].AvatarHash || "fef49e7fa7e1997310d705b2a6158ff8dc1cdfeb"}.jpg" /> | |
</a> | |
</div> | |
</div> | |
<div class="badge_title"> | |
<a target="_blank" rel="noopener noreferrer" href="https://steamcommunity.com/profiles/${bots.Result[index].SteamIDText}">${bots.Result[index].Nickname}</a>${any} | |
 <span style="color: #8F98A0;">(${bots.Result[index].TotalInventoryCount} items)</span> | |
</div> | |
</div> | |
<div class="badge_title_rule"></div> | |
${matches} | |
</div> | |
</div> | |
`; | |
let template = document.createElement("template"); | |
template.innerHTML = rowTemplate.trim(); | |
let mainContentDiv = document.getElementsByClassName("maincontent")[0]; | |
let newChild = template.content.firstChild; | |
mainContentDiv.appendChild(newChild); | |
checkRow(newChild); | |
newChild.querySelectorAll(".asf_stm_trashbusket_button").forEach((trashButton) => { | |
trashButton.addEventListener("click", function() { | |
if (this.dataset.asfstmbot) { | |
newChild.parentNode.removeChild(newChild); | |
} else if (this.dataset.appid) { | |
let idx = itemsToSend.findIndex((game) => game.appId == this.dataset.appid && game.border == this.dataset.border); | |
if (idx >= 0) { | |
let gameMatchElement = newChild.querySelector(`.asf_stm_appid_${this.dataset.appid}_${this.dataset.border}`); | |
gameMatchElement.parentNode.removeChild(gameMatchElement); | |
itemsToSend.splice(idx, 1); | |
itemsToReceive.splice(idx, 1); | |
checkRow(newChild); | |
} | |
} | |
}); | |
}); | |
} | |
function calcState(badge) { | |
//state 0 - less than max sets; state 1 - we have max sets, even out the rest, state 2 - all even | |
if (badge.cards[badge.maxCards - 1].count == badge.maxSets) { | |
if (badge.cards[0].count == badge.lastSet) { | |
return 2; //nothing to do | |
} else { | |
return 1; //max sets are here, but we can distribute cards further | |
} | |
} else { | |
return 0; //less than max sets | |
} | |
} | |
function compareCards(index, callback) { | |
let itemsToSend = []; | |
let itemsToReceive = []; | |
//debugPrint("bot's cards"); | |
//debugPrint(JSON.stringify(botBadges)); | |
//debugPrint("our cards"); | |
//debugPrint(JSON.stringify(myBadges)); | |
for (let i = 0; i < botBadges.length; i++) { | |
let myBadge = deepClone(myBadges[i]); | |
let theirBadge = deepClone(botBadges[i]); | |
let myState = calcState(myBadge); | |
debugPrint("state=" + myState); | |
debugPrint("myapp=" + myBadge.appId + " botapp=" + theirBadge.appId); | |
while (myState < 2) { | |
let foundMatch = false; | |
for (let j = 0; j < theirBadge.maxCards; j++) { | |
//index of card they give | |
if (theirBadge.cards[j].count > 0) { | |
//try to match | |
let myInd = myBadge.cards.findIndex((a) => a.item == theirBadge.cards[j].item); //index of slot where we receive card | |
if ((myState == 0 && myBadge.cards[myInd].count < myBadge.maxSets) || (myState == 1 && myBadge.cards[myInd].count < myBadge.lastSet)) { | |
//we need this ^Kfor the Emperor | |
debugPrint("we need this: " + theirBadge.cards[j].item + " (" + theirBadge.cards[j].count + ")"); | |
//find a card to match. | |
for (let k = 0; k < myInd; k++) { | |
//index of card we give | |
debugPrint("i=" + i + " j=" + j + " k=" + k + " myState=" + myState); | |
debugPrint("we have this: " + myBadge.cards[k].item + " (" + myBadge.cards[k].count + ")"); | |
if ((myState == 0 && myBadge.cards[k].count > myBadge.maxSets) || (myState == 1 && myBadge.cards[k].count > myBadge.lastSet)) { | |
//that's fine for us | |
debugPrint("it's a good trade for us"); | |
let theirInd = theirBadge.cards.findIndex((a) => a.item == myBadge.cards[k].item); //index of slot where they will receive card | |
if (!bots.Result[index].MatchEverything) { | |
//make sure it's neutral+ for them | |
if (theirBadge.cards[theirInd].count >= theirBadge.cards[j].count) { | |
debugPrint("Not fair for them"); | |
debugPrint("they have this: " + theirBadge.cards[theirInd].item + " (" + theirBadge.cards[theirInd].count + ")"); | |
continue; //it's not neutral+, check other options | |
} | |
} | |
debugPrint("it's a match!"); | |
let itemToSend = { | |
item: myBadge.cards[k].item, | |
count: 1, | |
class: classIdsDB[myBadge.appId][myBadge.cards[k].item], | |
iconUrl: myBadge.cards[k].iconUrl, | |
}; | |
let itemToReceive = { | |
item: theirBadge.cards[j].item, | |
count: 1, | |
class: classIdsDB[theirBadge.appId][theirBadge.cards[j].item], | |
iconUrl: theirBadge.cards[j].iconUrl, | |
}; | |
//fill items to send | |
let sendMatch = itemsToSend.find((item) => item.appId == myBadge.appId); | |
if (sendMatch == undefined) { | |
let newMatch = { | |
appId: myBadge.appId, | |
title: myBadge.title, | |
cards: [itemToSend], | |
border: myBadge.border, | |
}; | |
itemsToSend.push(newMatch); | |
} else { | |
let existingCard = sendMatch.cards.find((a) => a.item == itemToSend.item); | |
if (existingCard == undefined) { | |
sendMatch.cards.push(itemToSend); | |
} else { | |
existingCard.count += 1; | |
} | |
} | |
//add this item to their inventory | |
theirBadge.cards[theirInd].count += 1; | |
//remove this item from our inventory | |
myBadge.cards[k].count -= 1; | |
//fill items to receive | |
let receiveMatch = itemsToReceive.find((item) => item.appId == myBadge.appId); | |
if (receiveMatch == undefined) { | |
let newMatch = { | |
appId: myBadge.appId, | |
title: myBadge.title, | |
cards: [itemToReceive], | |
border: myBadge.border, | |
}; | |
itemsToReceive.push(newMatch); | |
} else { | |
let existingCard = receiveMatch.cards.find((a) => a.item == itemToReceive.item); | |
if (existingCard == undefined) { | |
receiveMatch.cards.push(itemToReceive); | |
} else { | |
existingCard.count += 1; | |
} | |
} | |
//add this item to our inventory | |
myBadge.cards[myInd].count += 1; | |
//remove this item from their inventory | |
theirBadge.cards[j].count -= 1; | |
foundMatch = true; | |
break; //found a match! | |
} | |
} | |
if (foundMatch) { | |
//if we found something - we need to sort cards again and start over. | |
myBadge.cards.sort((a, b) => b.count - a.count); | |
myState = calcState(myBadge); | |
debugPrint("new myState=" + myState); | |
} | |
} | |
} | |
} | |
if (!foundMatch) { | |
break; //found no matches - move to next badge | |
} | |
theirBadge.cards.sort((a, b) => b.count - a.count); | |
} | |
} | |
//debugPrint("items to send"); | |
//debugPrint(JSON.stringify(itemsToSend)); | |
//debugPrint("items to receive"); | |
//debugPrint(JSON.stringify(itemsToReceive)); | |
bots.Result[index].itemsToSend = itemsToSend; | |
bots.Result[index].itemsToReceive = itemsToReceive; | |
if (itemsToSend.length > 0) { | |
addMatchRow(index); | |
} else { | |
debugPrint("no matches"); | |
} | |
callback(); | |
} | |
function GetCards(badgeIndex, userindex) { | |
debugPrint("GetCards " + badgeIndex + " : " + userindex); | |
if (userindex >= bots.Result.length) { | |
debugPrint("finished"); | |
debugPrint(new Date(Date.now())); | |
hideThrobber(); | |
hideMessage(); | |
updateProgress(1, 1); | |
enableButton(); | |
let stopButton = document.getElementById("asf_stm_stop"); | |
stopButton.parentNode.removeChild(stopButton); | |
return; | |
} | |
if (badgeIndex == 0) { | |
//botBadges.length = 0; | |
botBadges = deepClone(myBadges); | |
for (let i = 0; i < botBadges.length; i++) { | |
botBadges[i].cards.forEach((card) => { | |
card.count = 0; | |
}); | |
} | |
} | |
if (badgeIndex < botBadges.length) { | |
let xhr = new XMLHttpRequest(); // ToDo: GM_xmlhttpRequest | |
if (userindex == -1) { | |
updateMessage("Getting our data for badge " + (badgeIndex + 1) + " of " + botBadges.length); | |
updateProgress(badgeIndex, botBadges.length); | |
let url = "https://steamcommunity.com/" + myProfileLink + "/ajaxgetbadgeinfo/" + botBadges[badgeIndex].appId + "?l=english" + (botBadges[badgeIndex].border ? "&border=1" : ""); | |
xhr.open("GET", url, true); | |
xhr.responseType = "json"; | |
xhr.onload = function () { | |
if (stop) { | |
updateMessage("Interrupted by user"); | |
hideThrobber(); | |
enableButton(); | |
let stopButton = document.getElementById("asf_stm_stop"); | |
stopButton.parentNode.removeChild(stopButton); | |
return; | |
} | |
let status = xhr.status; | |
if (status === 200 && xhr.response.eresult === 1) { | |
debugPrint("processing badge " + botBadges[badgeIndex].appId); | |
let badgeCards = xhr.response.badgedata; | |
if (!!badgeCards && !!badgeCards.rgCards && badgeCards.rgCards.length >= 5) { | |
errors = 0; | |
botBadges[badgeIndex].maxCards = badgeCards.rgCards.length; | |
badgeCards.rgCards.forEach(function (card, pos) { | |
let newcard = { | |
position: pos, | |
item: card.markethash, // card.name | |
count: card.owned, | |
iconUrl: card.imgurl, | |
}; | |
//debugPrint(JSON.stringify(newcard)); | |
botBadges[badgeIndex].cards.push(newcard); | |
}); | |
} | |
badgeIndex++; | |
setTimeout(GetCards, weblimiter, badgeIndex, userindex); | |
return; | |
} else { | |
errors++; | |
} | |
if ((status < 400 || status >= 500) && errors <= maxErrors) { | |
setTimeout(GetCards, weblimiter + errorLimiter * errors, badgeIndex, userindex); | |
} else { | |
if (status != 200) { | |
updateMessage("Error getting badge data, ERROR " + status); | |
} else { | |
//ToDo: should never happen | |
updateMessage("Error getting badge data, malformed JSON"); | |
} | |
hideThrobber(); | |
enableButton(); | |
let stopButton = document.getElementById("asf_stm_stop"); | |
stopButton.parentNode.removeChild(stopButton); | |
return; | |
} | |
}; | |
} else { | |
updateMessage("Fetching bot " + (userindex + 1).toString() + " of " + bots.Result.length.toString() + " (badge " + (badgeIndex + 1) + " of " + botBadges.length + ")"); | |
updateProgress(userindex, bots.Result.length); | |
if (botBadges[badgeIndex].border == 1 && !bots.Result[userindex].MatchableTypes.includes(EType.TradingCardFoil) || | |
botBadges[badgeIndex].border == 0 && !bots.Result[userindex].MatchableTypes.includes(EType.TradingCard) | |
) { | |
setTimeout(GetCards, weblimiter, badgeIndex + 1, userindex); | |
return; | |
} | |
let url = "https://steamcommunity.com/profiles/" + bots.Result[userindex].SteamIDText + "/gamecards/" + botBadges[badgeIndex].appId + "?l=english" + (botBadges[badgeIndex].border ? "&border=1" : ""); | |
xhr.open("GET", url, true); | |
xhr.responseType = "document"; | |
xhr.onload = function () { | |
if (stop) { | |
compareCards(userindex, function () {/* noop */}); | |
updateMessage("Interrupted by user"); | |
hideThrobber(); | |
enableButton(); | |
let stopButton = document.getElementById("asf_stm_stop"); | |
stopButton.parentNode.removeChild(stopButton); | |
return; | |
} | |
let status = xhr.status; | |
if (status === 200) { | |
debugPrint("processing badge " + botBadges[badgeIndex].appId); | |
let badgeCards = xhr.response.documentElement.querySelectorAll(".badge_card_set_card"); | |
if (badgeCards.length >= 5) { | |
errors = 0; | |
badgeCards.forEach(function (cardElem, pos) { | |
let quantityElement = cardElem.querySelector(".badge_card_set_text_qty"); | |
let quantity = quantityElement == null ? 0 : Number(quantityElement.innerText.trim().slice(1, -1)); | |
botBadges[badgeIndex].cards.find((card) => card.position == pos).count = quantity; | |
}); | |
setTimeout(GetCards, weblimiter, badgeIndex + 1, userindex); | |
return; | |
} else { | |
//if can't find any cards on badge page - retry, that's must be a bug. | |
//debugPrint(xhr.response.documentElement.outerHTML); | |
errors++; | |
} | |
} else { | |
errors++; | |
} | |
if ((status < 400 || status >= 500) && errors <= maxErrors) { | |
setTimeout(GetCards, weblimiter + errorLimiter * errors, badgeIndex, userindex); | |
} else { | |
if (status != 200) { | |
updateMessage("Error getting badge data, ERROR " + status); | |
} else { | |
//ToDo: should never happen | |
updateMessage("Error getting badge data, malformed HTML"); | |
} | |
hideThrobber(); | |
enableButton(); | |
let stopButton = document.getElementById("asf_stm_stop"); | |
stopButton.parentNode.removeChild(stopButton); | |
return; | |
} | |
}; | |
} | |
xhr.onerror = function () { | |
if (stop) { | |
compareCards(userindex, function () {/* noop */}); | |
updateMessage("Interrupted by user"); | |
hideThrobber(); | |
enableButton(); | |
let stopButton = document.getElementById("asf_stm_stop"); | |
stopButton.parentNode.removeChild(stopButton); | |
return; | |
} | |
errors++; | |
if (errors <= maxErrors) { | |
setTimeout(GetCards, weblimiter + errorLimiter * errors, badgeIndex, userindex); | |
return; | |
} else { | |
debugPrint("error"); | |
updateMessage("Error getting badge data"); | |
hideThrobber(); | |
enableButton(); | |
let stopButton = document.getElementById("asf_stm_stop"); | |
stopButton.parentNode.removeChild(stopButton); | |
return; | |
} | |
}; | |
xhr.send(); | |
return; //do this synchronously to avoid rate limit | |
} | |
debugPrint("populated"); | |
debugTime("Filter and sort"); | |
// ToDo: Array.filter | |
for (let i = botBadges.length - 1; i >= 0; i--) { | |
debugPrint("badge " + i + " " + JSON.stringify(botBadges[i])); | |
botBadges[i].cards.sort((a, b) => b.count - a.count); | |
if (userindex < 0) { | |
if (botBadges[i].cards[0].count - botBadges[i].cards[botBadges[i].cards.length - 1].count < 2) { | |
//nothing to match, remove from list. | |
botBadges.splice(i, 1); | |
continue; | |
} | |
} | |
let totalCards = 0; | |
for (let j = 0; j < botBadges[i].maxCards; j++) { | |
totalCards += botBadges[i].cards[j].count; | |
} | |
botBadges[i].maxSets = Math.floor(totalCards / botBadges[i].maxCards); | |
botBadges[i].lastSet = Math.ceil(totalCards / botBadges[i].maxCards); | |
debugPrint("totalCards=" + totalCards + " maxSets=" + botBadges[i].maxSets + " lastSet=" + botBadges[i].lastSet); | |
} | |
debugTimeEnd("Filter and sort"); | |
if (userindex < 0) { | |
if (botBadges.length == 0) { | |
hideThrobber(); | |
updateMessage("No cards to match"); | |
enableButton(); | |
let stopButton = document.getElementById("asf_stm_stop"); | |
stopButton.parentNode.removeChild(stopButton); | |
return; | |
} else { | |
myBadges = deepClone(botBadges); | |
getClassIDs(0); | |
return; | |
} | |
} else { | |
debugPrint(bots.Result[userindex].SteamIDText); | |
compareCards(userindex, function () { | |
setTimeout(GetCards, weblimiter, 0, userindex + 1); | |
}); | |
} | |
} | |
function getBadges(page) { | |
let url = "https://steamcommunity.com/" + myProfileLink + "/badges?p=" + page + "&l=english"; | |
let xhr = new XMLHttpRequest(); // ToDo: GM_xmlhttpRequest | |
xhr.open("GET", url, true); | |
xhr.responseType = "document"; | |
xhr.onload = function () { | |
if (stop) { | |
updateMessage("Interrupted by user"); | |
hideThrobber(); | |
enableButton(); | |
let stopButton = document.getElementById("asf_stm_stop"); | |
stopButton.parentNode.removeChild(stopButton); | |
return; | |
} | |
let status = xhr.status; | |
if (status === 200) { | |
errors = 0; | |
debugPrint("processing page " + page); | |
updateMessage("Processing badges page " + page); | |
if (page === 1) { | |
let pageLinks = xhr.response.documentElement.getElementsByClassName("pagelink"); | |
if (pageLinks.length > 0) { | |
maxPages = Number(pageLinks[pageLinks.length - 1].textContent.trim()); | |
} | |
if (maxPagesLimit && !appIdsToScanOverride.length) { | |
maxPages = Math.min(maxPages, maxPagesLimit); | |
} | |
} | |
updateProgress(page - 1, maxPages); // substract 1 from page number as it starts from 1 | |
let badges = xhr.response.documentElement.getElementsByClassName("badge_row_inner"); | |
for (let i = 0; i < badges.length; i++) { | |
//if (badges[i].getElementsByClassName("owned").length > 0) { | |
// not max level with cards OR max level AND has cards | |
if (!!badges[i].querySelector(".badge_progress_tasks .owned") || !badges[i].querySelector(".badge_progress_tasks .unowned") && !!badges[i].querySelector(".badge_progress_info")) { // .progress_info_bold | |
let overlayElement = badges[i].parentElement.querySelector(".badge_row_overlay"); | |
let appId = parseInt(overlayElement.href.match(/\/gamecards\/(\d+)/)[1], 10); | |
if (!!appIdsToScanOverride.length && !appIdsToScanOverride.includes(appId)) { | |
continue; // skip appid - not what we looking for | |
} | |
let badgeBorder = overlayElement.href.endsWith("border=1") ? 1 : 0; | |
let title = badges[i].querySelector(".badge_title").childNodes[0].textContent.trim().replaceAll(/\s+/g, ' '); | |
let badgeStub = { | |
appId: appId, | |
title: title, | |
border: badgeBorder, | |
maxCards: 0, | |
maxSets: 0, | |
lastSet: 0, | |
cards: [], | |
}; | |
myBadges.push(badgeStub); | |
} | |
} | |
page++; | |
} else { | |
errors++; | |
} | |
if ((status < 400 || status >= 500) && errors <= maxErrors) { | |
if (page <= maxPages) { | |
setTimeout(getBadges, weblimiter + errorLimiter * errors, page); | |
} else { | |
debugPrint("all badge pages processed"); | |
debugPrint(weblimiter + errorLimiter * errors); | |
if (myBadges.length === 0) { | |
hideThrobber(); | |
updateMessage("No cards to match"); | |
enableButton(); | |
let stopButton = document.getElementById("asf_stm_stop"); | |
stopButton.parentNode.removeChild(stopButton); | |
return; | |
} else { | |
setTimeout(GetCards, weblimiter + errorLimiter * errors, 0, -1); | |
} | |
} | |
} else { | |
if (status != 200) { | |
updateMessage("Error getting badge page, ERROR " + status); | |
} else { | |
updateMessage("Error getting badge page, malformed HTML"); | |
} | |
hideThrobber(); | |
enableButton(); | |
let stopButton = document.getElementById("asf_stm_stop"); | |
stopButton.parentNode.removeChild(stopButton); | |
return; | |
} | |
}; | |
xhr.onerror = function () { | |
if (stop) { | |
updateMessage("Interrupted by user"); | |
hideThrobber(); | |
enableButton(); | |
let stopButton = document.getElementById("asf_stm_stop"); | |
stopButton.parentNode.removeChild(stopButton); | |
return; | |
} | |
errors++; | |
if (errors <= maxErrors) { | |
setTimeout(getBadges, weblimiter + errorLimiter * errors, page); | |
} else { | |
debugPrint("error getting badge page"); | |
updateMessage("Error getting badge page"); | |
hideThrobber(); | |
enableButton(); | |
let stopButton = document.getElementById("asf_stm_stop"); | |
stopButton.parentNode.removeChild(stopButton); | |
return; | |
} | |
}; | |
xhr.send(); | |
} | |
function filterEventHandler(event) { | |
let [, appId, border] = event.target.id.split("_"); | |
let matches = document.getElementsByClassName(`asf_stm_appid_${appId}_${border}`); | |
for (let i = 0; i < matches.length; i++) { | |
matches[i].style.display = event.target.checked ? "inline-block" : "none"; | |
checkRow(matches[i].parentElement.parentElement); | |
} | |
} | |
function filterSwitchesHandler(event) { | |
let action = event.target.id.split("_")[3]; | |
let filterWidget = document.getElementById("asf_stm_filters_body"); | |
let checkboxes = filterWidget.getElementsByTagName("input"); | |
for (let i = 0; i < checkboxes.length; i++) { | |
if (action === "all") { | |
if (!checkboxes[i].checked) { | |
checkboxes[i].checked = true; | |
filterEventHandler({ target: checkboxes[i] }); | |
} | |
} else if (action === "none") { | |
if (checkboxes[i].checked) { | |
checkboxes[i].checked = false; | |
filterEventHandler({ target: checkboxes[i] }); | |
} | |
} else if (action === "invert") { | |
checkboxes[i].checked = !checkboxes[i].checked; | |
filterEventHandler({ target: checkboxes[i] }); | |
} | |
} | |
} | |
function filtersButtonEvent() { | |
let filterWidget = document.getElementById("asf_stm_filters"); | |
if (filterWidget.style.marginRight == "-50%") { | |
filterWidget.style.marginRight = "unset"; | |
} else { | |
filterWidget.style.marginRight = "-50%"; | |
} | |
} | |
function stopButtonEvent() { | |
let stopButton = document.getElementById("asf_stm_stop"); | |
stopButton.removeEventListener("click", stopButtonEvent, false); | |
stopButton.title = "Stopping..."; | |
stopButton.classList.add("btn_disabled"); | |
updateMessage("Stopping..."); | |
stop = true; | |
} | |
function buttonPressedEvent() { | |
if (bots === null || bots.Result === undefined || bots.Result.length == 0 || bots.Success != true || bots.cacheTime + botCacheTime < Date.now()) { | |
debugPrint("Bot cache invalidated"); | |
fetchBots(); | |
return; | |
} | |
disableButton(); | |
debugPrint(new Date(Date.now())); | |
let mainContentDiv = document.getElementsByClassName("maincontent")[0]; | |
mainContentDiv.textContent = ""; | |
mainContentDiv.style.width = "90%"; | |
mainContentDiv.innerHTML = ` | |
<div class="profile_badges_header"> | |
<div id="throbber"> | |
<div class="LoadingWrapper"> | |
<div class="LoadingThrobber"> | |
<div class="Bar Bar1"></div> | |
<div class="Bar Bar2"></div> | |
<div class="Bar Bar3"></div> | |
</div> | |
</div> | |
</div> | |
<div> | |
<div id="asf_stm_messagebox" class="profile_badges_header"> | |
<div id="asf_stm_message" class="profile_badges_header_title" style="text-align: center;">Initialization</div> | |
</div> | |
</div> | |
<div style="width: 100%;"> | |
<div id="asf_stm_stop" class="btn_darkred_white_innerfade btn_medium_thin" style="float: right;margin-top: -12px;margin-left: 10px;" title="Stop scan"> | |
<span>🛑</span> | |
</div> | |
<div style="width: auto;overflow: hidden;" class="profile_xp_block_remaining_bar"> | |
<div id="asf_stm_progress" class="profile_xp_block_remaining_bar_progress" style="width: 100%;transition: width 0.5s ease-in-out 0s"> | |
</div> | |
</div> | |
</div> | |
</div> | |
<div id="asf_stm_filters" style="position: fixed; z-index: 1000; right: 5px; bottom: 45px; transition-duration: 500ms; | |
transition-timing-function: ease; margin-right: -50%; padding: 5px; max-width: 40%; display: inline-block; border-radius: 2px; | |
background:${filterBackgroundColor}; color: #67c1f5;"> | |
<div style="white-space: nowrap;">Select: | |
<a id="asf_stm_filter_all" class="commentthread_pagelinks"> | |
all | |
</a> | |
<a id="asf_stm_filter_none" class="commentthread_pagelinks"> | |
none | |
</a> | |
<a id="asf_stm_filter_invert" class="commentthread_pagelinks"> | |
invert | |
</a> | |
</div> | |
<hr /> | |
<div id="asf_stm_filters_body"> | |
<span id="asf_stm_placeholder" style="margin-right: 15px;">No matches to filter</span> | |
</div> | |
</div> | |
<div style="position: fixed;z-index: 1000;right: 5px;bottom: 5px;" id="asf_stm_filters_button_div"> | |
<a id="asf_stm_filters_button" class="btnv6_blue_hoverfade btn_medium"> | |
<span>Filters</span> | |
</a> | |
</div> | |
`; | |
document.getElementById("asf_stm_stop").addEventListener("click", stopButtonEvent, false); | |
document.getElementById("asf_stm_filters_body").addEventListener("change", filterEventHandler); | |
document.getElementById("asf_stm_filter_all").addEventListener("click", filterSwitchesHandler); | |
document.getElementById("asf_stm_filter_none").addEventListener("click", filterSwitchesHandler); | |
document.getElementById("asf_stm_filter_invert").addEventListener("click", filterSwitchesHandler); | |
document.getElementById("asf_stm_filters_button").addEventListener("click", filtersButtonEvent, false); | |
stop = false; | |
myBadges.length = 0; | |
getBadges(1); | |
} | |
function fetchBots() { | |
let requestUrl = "https://asf.justarchi.net/Api/Listing/Bots"; | |
let requestFunc; | |
if (typeof GM_xmlhttpRequest !== "function") { | |
requestFunc = GM.xmlHttpRequest.bind(GM); | |
} else { | |
requestFunc = GM_xmlhttpRequest; | |
} | |
requestFunc({ // ToDo: .responseType = "json"; | |
method: "GET", | |
url: requestUrl, | |
onload: function (response) { | |
if (response.status != 200) { | |
disableButton(); | |
document.getElementById("asf_stm_button_div").setAttribute("title", "Can't fetch list of bots"); | |
debugPrint("can't fetch list of bots, ERROR=" + response.status); | |
//debugPrint(JSON.stringify(response)); | |
return; | |
} | |
try { | |
bots = JSON.parse(response.response); | |
bots.cacheTime = Date.now(); | |
if (bots.Success) { | |
console.log("total bots:", bots.Result.length); | |
bots.Result = bots.Result.filter(function (bot) { | |
return bot.MatchableTypes.includes(EType.TradingCardFoil) || | |
bot.MatchableTypes.includes(EType.TradingCard); | |
}); | |
console.log("filtered bots accept cards:", bots.Result.length); | |
if (bRandomizeBots) { | |
//Fisher–Yates shuffle | |
let currentIndex = bots.Result.length, randomIndex; | |
// While there remain elements to shuffle… | |
while (currentIndex) { | |
// Pick a remaining element… | |
randomIndex = Math.floor(Math.random() * currentIndex--); | |
// And swap it with the current element. | |
[bots.Result[currentIndex], bots.Result[randomIndex]] = [bots.Result[randomIndex], bots.Result[currentIndex]]; | |
} | |
} | |
if (maxBotsToScan > 0) { | |
bots.Result = bots.Result.slice(0, maxBotsToScan); | |
} | |
if (!bRandomizeBots) { | |
bots.Result.sort(function (a, b) { | |
//sort received array as I like it. TODO: sort according to settings | |
let result = b.MatchEverything - a.MatchEverything; //bots with MatchEverything go first | |
if (result === 0) { | |
result = b.TotalGamesCount - a.TotalGamesCount; //then by TotalGamesCount descending | |
} | |
if (result === 0) { | |
result = b.TotalItemsCount - a.TotalItemsCount; //then by TotalItemsCounts descending | |
} | |
if (result === 0) { | |
result = a.TotalInventoryCount - b.TotalInventoryCount; //then by TotalInventoryCount ascending | |
} | |
return result; | |
}); | |
} | |
debugPrint("found total " + bots.Result.length + " bots"); | |
localStorage.setItem("Ryzhehvost.ASF.STM.BotCache", JSON.stringify(bots)); | |
buttonPressedEvent(); | |
} else { | |
//ASF backend does not indicate success | |
disableButton(); | |
document.getElementById("asf_stm_button_div").setAttribute("title", "Can't fetch list of bots, try later"); | |
debugPrint("can't fetch list of bots"); | |
debugPrint(bots.Message); | |
//debugPrint(JSON.stringify(response)); | |
return; | |
} | |
return; | |
} catch (e) { | |
disableButton(); | |
document.getElementById("asf_stm_button_div").setAttribute("title", "Can't fetch list of bots, try later"); | |
debugPrint("can't fetch list of bots"); | |
debugPrint(e); | |
//debugPrint(JSON.stringify(response)); | |
return; | |
} | |
}, | |
onerror: function (response) { | |
disableButton(); | |
document.getElementById("asf_stm_button_div").setAttribute("title", "Can't fetch list of bots"); | |
debugPrint("can't fetch list of bots"); | |
//debugPrint(JSON.stringify(response)); | |
}, | |
onabort: function (response) { | |
disableButton(); | |
document.getElementById("asf_stm_button_div").setAttribute("title", "Can't fetch list of bots"); | |
debugPrint("can't fetch list of bots - aborted"); | |
//debugPrint(JSON.stringify(response)); | |
}, | |
ontimeout: function (response) { | |
disableButton(); | |
document.getElementById("asf_stm_button_div").setAttribute("title", "Can't fetch list of bots"); | |
debugPrint("can't fetch list of bots - timeout"); | |
//debugPrint(JSON.stringify(response)); | |
}, | |
}); | |
} | |
if (document.getElementsByClassName("badge_details_set_favorite").length != 0) { | |
let profileRegex = /http[s]?:\/\/steamcommunity.com\/(.*)\/badges.*/g; | |
let result = profileRegex.exec(document.location); | |
if (result) { | |
myProfileLink = result[1]; | |
} else { | |
//should never happen, but whatever. | |
myProfileLink = "my"; | |
} | |
//debugPrint(profileRegex); | |
let botCache = JSON.parse(localStorage.getItem("Ryzhehvost.ASF.STM.BotCache")); | |
if (botCache === null || botCache.cacheTime === undefined || botCache.cacheTime === null || botCache.cacheTime + botCacheTime < Date.now()) { | |
botCache = null; | |
debugPrint("Bot cache invalidated"); | |
} else { | |
bots = botCache; | |
} | |
let buttonDiv = document.createElement("div"); | |
buttonDiv.setAttribute("class", "profile_small_header_additional"); | |
buttonDiv.setAttribute("style", "margin-top: 40px;"); | |
buttonDiv.setAttribute("id", "asf_stm_button_div"); | |
buttonDiv.setAttribute("title", "Scan ASF STM"); | |
let button = document.createElement("a"); | |
button.setAttribute("class", "btnv6_blue_hoverfade btn_medium"); | |
button.setAttribute("id", "asf_stm_button"); | |
button.appendChild(document.createElement("span")); | |
button.firstChild.appendChild(document.createTextNode("Scan ASF STM")); | |
buttonDiv.appendChild(button); | |
let anchor = document.getElementsByClassName("profile_small_header_texture")[0]; | |
anchor.appendChild(buttonDiv); | |
enableButton(); | |
// add our styles to the document's style sheet | |
if (typeof GM_addStyle != "undefined") { | |
GM_addStyle(css); | |
} else { | |
const node = document.createElement("style"); | |
node.appendChild(document.createTextNode(css)); | |
if (document.head) { | |
document.head.appendChild(node); | |
} else { | |
// no head yet, stick it whereever | |
document.documentElement.appendChild(node); | |
} | |
} | |
} | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment