ASF STM

ASF bot list trade matcher

当前为 2020-06-15 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name        ASF STM
// @language    English
// @namespace   https://greasyfork.org/users/2205
// @description ASF bot list trade matcher
// @license     Apache-2.0
// @author      Ryzhehvost
// @include     http*://steamcommunity.com/id/*/badges
// @include     http*://steamcommunity.com/id/*/badges/
// @include     http*://steamcommunity.com/profiles/*/badges
// @include     http*://steamcommunity.com/profiles/*/badges/
// @version     1.1
// @connect     asf.justarchi.net
// @grant       GM.xmlhttpRequest
// @grant       GM_xmlhttpRequest
// ==/UserScript==

/* global g_steamID */

(function()
 {
    const limiter = 0;
    const debug = false;
    const maxErrors = 5;
    let errors=0;
    let bots;
    let assets=[];
    let descriptions=[];
    let mybadges=[];
    let userbadges=[];
    let maxpages;
    let stop = false;

    function debugProfile(name){
        if (debug) {
            console.time(name);
        }
    }

    function debugProfileEnd(name){
        if (debug) {
            console.timeEnd(name);
        }
    }
    function debugPrint(msg){
    	if (debug) {
    	  console.log(msg);
    	}
    }

    function getPartner(str) {
        return (BigInt(str)%BigInt(4294967296)).toString(); // eslint-disable-line
    }

    function EnableButton(){
        let buttondiv=document.getElementById("asf_stm_button_div");
        buttondiv.setAttribute("class","profile_small_header_additional");
        let button=document.getElementById("asf_stm_button");
        button.addEventListener("click",ButtonPressed, false);
    }

    function DisableButton(){
        let buttondiv=document.getElementById("asf_stm_button_div");
        buttondiv.setAttribute("class","profile_small_header_additional btn_disabled");
        let button=document.getElementById("asf_stm_button");
        button.removeEventListener("click",ButtonPressed, 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){
        let bar=document.getElementById("asf_stm_progress");
        let progress = 100*((index+1)/bots.length);
        bar.setAttribute("style","width: "+progress+"%;");
    }
    function populateCards(item) {
        let classlist = "";
        let htmlCards = "";
        for (let j=0;j<item.cards.length; j++) {
            let item_icon = item.cards[j].icon_url;
            let item_name = 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+=item.cards[j].class;
                let cardTemplate =`
                          <div class="showcase_slot">
                            <img class="image-container" src="https://steamcommunity-a.akamaihd.net/economy/image/${item_icon}/98x115">
                            <div class="commentthread_subscribe_hint" style="width: 98px;">${item_name}</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+=item.cards[j].class;
            }
        }
        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 = "";
        let filterWidget = document.getElementById("asf_stm_filters");
        for (let i = 0; i < bots[index].itemsToSend.length; i++){
            let appid=bots[index].itemsToSend[i].appid;
            let checkbox=filterWidget.getElementsBySelector("#astm_"+appid)[0];
            if (checkbox.checked) {
                if (you!=""){
                    you+=";";
                }
                you=you+getClasses(bots[index].itemsToSend[i]);
                if (them!=""){
                    them+=";";
                }
                them=them+getClasses(bots[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.visible()){
                visible = true;
                break;
            }
        }
        if (visible) {
            row.show();
            UpdateTrade(row);
        } else {
            row.hide();
        }
    }

    function AddMatchRow(index,botname){
        debugPrint("AddMatchRow "+index);
        let itemsToSend=bots[index].itemsToSend;
        let itemsToReceive=bots[index].itemsToReceive;
        let tradeurl="https://steamcommunity.com/tradeoffer/new/?partner="+getPartner(bots[index].steam_id)+"&token="+bots[index].trade_token+"&source=stm";
        let globalyou="";
        let globalthem="";
        let matches = "";
        let any = "";
        if (bots[index].match_everything==1) {
            any=`&nbsp;<sup><span class="avatar_block_status_in-game" style="font-size: 8px; cursor:help" title="This bots trades for any cards within same set">&nbsp;ANY&nbsp;</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";

            //remove placeholder
            let filterWidget = document.getElementById("asf_stm_filters");
            let placeholder = filterWidget.getElementsBySelector("#asf_stm_placeholder");
            if (placeholder.length!=0) {
                placeholder[0].remove();
            }
            //add filter
            let checkboxes=filterWidget.getElementsBySelector("#astm_"+appid);
            if (checkboxes.length==0) {
                let newFilter=`<span style="margin-right: 15px; white-space: nowrap; display: inline-block;"><input type="checkbox" id="astm_${appid}" checked="">${gameName}</span>`
                let spanTemplate = document.createElement('template');
                spanTemplate.innerHTML = newFilter.trim();
                filterWidget.appendChild(spanTemplate.content.firstChild);
            } else {
                if (checkboxes[0].checked == false){
                    display = "none"
                }
            }

            let sendResult = populateCards(itemsToSend[i]);
            let receiveResult = populateCards(itemToReceive);

            let tradeurlappid=tradeurl+"&them="+receiveResult.classlist+"&you="+sendResult.classlist;

            let matchTemplate = `
                  <div class="asf_stm_appid_${appid}" style="display:${display}">
                    <div class="badge_row is_link goo_untradable_note showcase_slot">
                      <div class="notLoggedInText">
			            <img alt="${gameName}" src="https://steamcdn-a.akamaihd.net/steam/apps/${appid}/capsule_184x69.jpg">
                        <div>
                          <div title="View badge progress for this game">
                            <a target="_blank" href="https://steamcommunity.com/my/gamecards/${appid}/">${gameName}</a>
                          </div>
                        </div>
                        <div class="btn_darkblue_white_innerfade btn_medium">
                          <span>
                            <a href="${tradeurlappid}" target="_blank" rel="noopener">Offer a trade</a>
                          </span>
                        </div>
                      </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>&#10145;</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;">
                            ${botname}
                          </div>
                        ${receiveResult.htmlCards}
                      </div>
                    </div>
                  </div>
            `;
            if (checkboxes.length==0 || checkboxes[0].checked) {
                matches+=matchTemplate;
                if (globalyou!=""){
                    globalyou+=";";
                };
                globalyou+=sendResult.classlist;
                if (globalthem!=""){
                    globalthem+=";";
                };
                globalthem+=receiveResult.classlist
            }
        }
        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">
                    <div class="btn_darkblue_white_innerfade btn_medium">
                      <span>
                        <a class="full_trade_url" href="${tradeurlfull}" target="_blank" rel="noopener" >Offer a trade for all</a>
                      </span>
                    </div>
                  </div>
                  <div class="badge_title">
                    ${botname}${any}
                  </div>
                </div>
                <div class="badge_title_rule"></div>
                ${matches}
              </div>
            </div>
        `;
        let template = document.createElement('template');
        template.innerHTML = rowTemplate.trim();
        let maincontent = document.getElementsByClassName("maincontent")[0];
        let newchild = template.content.firstChild;
        maincontent.appendChild(newchild);
        CheckRow(newchild);
    }

    function DeepClone(object) {
        return JSON.parse(JSON.stringify(object));
    }

    function fetchinventory(steamid,startasset,callback) {
        let url="https://steamcommunity.com/inventory/"+steamid+"/753/6?l=english&count=5000&l=english";
        if (startasset>0) {
            url=url+"&start_assetid="+startasset.toString();
        } else {
            assets.clear();
            descriptions.clear();
        }
        debugPrint(url);
        let xhr = new XMLHttpRequest();
        xhr.open('GET', url, true);
        xhr.responseType = 'json';
        xhr.onload = function() {
            debugPrint("...");
            if (stop){
                UpdateMessage("Interrupted by user");
                HideThrobber();
                EnableButton();
                let stopButton = document.getElementById("asf_stm_stop");
                stopButton.remove();
                return;
            }
            let status = xhr.status;
            let lastasset=startasset;
            if (status === 200) {
                errors = 0;
                if (typeof(xhr.response) !== 'undefined') {
                    if (typeof(xhr.response.descriptions) !== 'undefined') {
                        assets=assets.concat(xhr.response.assets);
                        descriptions=descriptions.concat(xhr.response.descriptions);
                        if (typeof(xhr.response.last_assetid) == 'undefined') { //end of inventory
                            debugPrint("total_inventory_count = "+xhr.response.total_inventory_count);
                            callback();
                            return;
                        } else {
                            lastasset=xhr.response.last_assetid;
                        }
                    }
                }
            } else {
                errors++;
                debugPrint("HTTP Error="+status);
            }
            if (status==403) {
                assets.clear(); //switch to next bot
                callback();
            } else if ((status<400 || status>=500 || status==408)&&(errors <= maxErrors)) {
                setTimeout((function(steamid,startasset,callback) { return function(){ fetchinventory(steamid,startasset,callback); };})(steamid,lastasset,callback), limiter);
            } else {
                UpdateMessage("Error getting inventory, ERROR "+status);
                HideThrobber();
                EnableButton();
            }
        }
        xhr.onerror = function() {
            if (stop){
                UpdateMessage("Interrupted by user");
                HideThrobber();
                EnableButton();
                let stopButton = document.getElementById("asf_stm_stop");
                stopButton.remove();
                return;
            }
            errors++;
            if (errors <= maxErrors) {
                setTimeout((function(steamid,startasset,callback) { return function(){ fetchinventory(steamid,startasset,callback); };})(steamid,startasset,callback), limiter);
            } else {
                debugPrint("error getting inventory");
                UpdateMessage("Error getting inventory");
                HideThrobber();
                EnableButton();
            }
        }
        xhr.send();
    }

    function CalcState(badge) { //state 0 - less than max sets; state 1 - we have max sets, even out the rest, state 2 - all even
        let state = 0;
        if (badge.cards[0].count == badge.lastset) {
            return 2; //nothing to do
        } else if (badge.cards[badge.maxcards-1].count == badge.maxsets) {
            return 1; //max sets are here, but we can distribute cards further
        }
        return 0; //less than max sets
    }

    function CompareCards(index,callback) {
        let itemsToSend=[];
        let itemsToReceive=[];
        userbadges.clear();
        userbadges = DeepClone(mybadges);
        for (let i = 0; i < userbadges.length; i++) {
            userbadges[i].cards.clear();
        }
        PopulateExistingCards(userbadges,false);
        debugPrint("bot's cards");
        debugPrint(DeepClone(userbadges));
        debugPrint("our cards");
        debugPrint(DeepClone(mybadges));

        for(let i = 0; i < userbadges.length; i++) {
            let mybadge = DeepClone(mybadges[i]);
            let theirbadge = DeepClone(userbadges[i]);
            let mystate=CalcState(mybadge);
            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
                        //if (bots[index].match_everything) {}
                        let myInd = mybadge.cards.findIndex(a => a.item == theirbadge.cards[j].item); //index of slot where we receive card
                        if (myInd==-1) {
                            debugPrint("we don't have it");
                            let empty = mybadge.cards.find(card => card.item==null);
                            if (empty != undefined) {
                                debugPrint("found a place!");
                                empty.item=theirbadge.cards[j].item;
                                empty.icon_url=theirbadge.cards[j].icon_url;
                                myInd=mybadge.cards.indexOf(empty);
                            } else {
                                debugPrint("Error! We found more cards than expected");
                                debugPrint(DeepClone(mybadge.cards));
                                debugPrint(DeepClone(theirbadge.cards));
                            }
                        }
                        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 (theirInd == -1) { //they don't even know this card
                                        theirInd = theirbadge.cards.findIndex(a => a.item == null); //index of empty space
                                        //it's safe to assign item name to this card, they don't have it
                                        theirbadge.cards[theirInd].item = mybadge.cards[k].item;
                                    }
                                    if (bots[index].match_everything==0) { //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":mybadge.cards[k].class, "icon_url":mybadge.cards[k].icon_url};
                                    let itemToReceive = {"item":theirbadge.cards[j].item,"count":1, "class":theirbadge.cards[j].class, "icon_url":theirbadge.cards[j].icon_url};
                                    //fill items to send
                                    let sendmatch = itemsToSend.find(item => item.appid == mybadges[i].appid);
                                    if (sendmatch == undefined) {
                                        let newmatch = {"appid":mybadges[i].appid, "title":mybadge.title ,"cards":[ itemToSend ]};
                                        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 == mybadges[i].appid);
                                    if (receivematch == undefined) {
                                        let newmatch = {"appid":mybadges[i].appid,"title":mybadge.title,"cards":[ itemToReceive ]};
                                        itemsToReceive.push(newmatch);
                                    } else {
                                        let existingCard = sendmatch.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) {
                    break; //found no matches - move to next badge
                }
                mybadge.cards.sort((a,b)=>b.count-a.count);
                theirbadge.cards.sort((a,b)=>b.count-a.count);
                mystate=CalcState(mybadge);
            }
        }
        debugPrint("items to send");
        debugPrint(DeepClone(itemsToSend));
        debugPrint("items to receive");
        debugPrint(DeepClone(itemsToReceive));
        bots[index].itemsToSend = itemsToSend;
        bots[index].itemsToReceive = itemsToReceive;
        if (itemsToSend.length>0){
            getUsername(index,callback);
        } else {
            debugPrint("no matches");
            callback();
        }
    }

    function getUsername(index,callback){
                let url="https://steamcommunity.com/profiles/"+bots[index].steam_id+"?xml=1";
                let xhr = new XMLHttpRequest();
                xhr.open('GET', url, true);
                xhr.responseType = 'text';
                //xhr.setRequestHeader("Range","bytes=0-200"); //fuck it, get the whole page
                xhr.onload = function() {
                    let status = xhr.status;
                    let username=bots[index].steam_id;
                    debugPrint("getting username");
                    if (status === 200) {
                        errors = 0;
                        let re = /<steamID><!\[CDATA\[(.+)]]><\/steamID>/g;
                        username = re.exec(xhr.response)[1];
                        debugPrint(username);
                        AddMatchRow(index,username);
                        callback();
                    } else {
                        if (stop){
                            UpdateMessage("Interrupted by user");
                            HideThrobber();
                            EnableButton();
                            let stopButton = document.getElementById("asf_stm_stop");
                            stopButton.remove();
                            return;
                        }
                        errors++;
                        if (errors <= maxErrors) {
                            setTimeout((function(index,callback) {return function(){ getUsername(index,callback); } })(index,callback), limiter);
                        } else {
                            debugPrint("error HTTP Status="+status);
                            UpdateMessage("Error getting username data, ERROR="+status);
                            HideThrobber();
                            EnableButton();
                        }
                    }
                };
                xhr.onerror = function() {
                    if (stop){
                        UpdateMessage("Interrupted by user");
                        HideThrobber();
                        EnableButton();
                        let stopButton = document.getElementById("asf_stm_stop");
                        stopButton.remove();
                        return;
                    }
                    errors++;
                    if (errors <= maxErrors) {
                        setTimeout((function(index,callback) {return function(){ getUsername(index,callback); } })(index,callback), limiter);
                    } else {
                        debugPrint("error");
                        UpdateMessage("Error getting username data");
                        HideThrobber();
                        EnableButton();
                    }
                };
            xhr.send();

    }

    function checkuser(index){
        debugPrint(index);
        UpdateMessage("Fetching bot "+(index+1).toString()+" of "+bots.length.toString());
        UpdateProgress(index);
        fetchinventory(bots[index].steam_id,0,function() {
            debugPrint(bots[index].steam_id);
            debugPrint(assets.length);
            CompareCards(index, function() {
            if (index<bots.length-1) {
                    setTimeout((function(index) { return function(){ checkuser(index); };})(index+1), limiter);
            } else {
                debugPrint("finished");
                debugPrint(new Date(Date.now()));
                console.log(new Date(Date.now()));
                HideThrobber();
                HideMessage();
                UpdateProgress(bots.length-1);
                EnableButton();
            }
            });
        });
    }

    function PopulateExistingCards(badges, filter){
        debugProfile("PopulateExistingCards1");
        debugPrint(DeepClone(assets));
        debugPrint(DeepClone(descriptions));
        descriptions=descriptions.filter(desc=>badges.find(item=>item.appid==desc.market_hash_name.split("-")[0])!=undefined);
        assets=assets.filter(asset=>descriptions.find(item=>item.classid==asset.classid)!=undefined);
        for (let i = 0; i < assets.length; i++){
            debugPrint(".");
            let descr = descriptions.find(desc=>desc.classid==assets[i].classid); // eslint-disable-line
            if (descr != undefined) {
                let appid=descr.market_hash_name.split("-")[0];
                let title=appid;
                let game_tag=descr.tags.find(tag=>tag.category=="Game");
                if (game_tag != undefined) {
                    title=game_tag.localized_tag_name;
                }
                let item_class_tag=descr.tags.find(tag=>tag.category=="item_class");
                if (item_class_tag != undefined) {
                    if (item_class_tag.internal_name == "item_class_2") {
                        let cardborder_tag=descr.tags.find(tag=>tag.category=="cardborder");
                        if (cardborder_tag != undefined) {
                            if (cardborder_tag.internal_name == "cardborder_0") {
                                let badge = badges.find(badge=>badge.appid==appid);
                                if (badge != undefined) {
                                    let card=badge.cards.find(card=>card.item==descr.market_hash_name);
                                    if (card == undefined) {
                                        let newcard = {"item":descr.market_hash_name, "count":1,"class": assets[i].classid ,"icon_url":descr.icon_url};
                                        badge.cards.push(newcard);
                                        badge.title = title;
                                    } else {
                                        card.count+=1;
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
        debugProfileEnd("PopulateExistingCards1");
        debugProfile("PopulateExistingCards2");
        for (let i = badges.length-1; i >=0; i--) {
            for (let j = badges[i].cards.length; j < badges[i].maxcards; j++){
                badges[i].cards.push({"item":null, "count":0,"class":0, "icon_url":null}); //fill missing cards with dummy element
            }
            badges[i].cards.sort((a,b)=>b.count-a.count);
            if (filter) {
                if (badges[i].cards[0].count-badges[i].cards[badges[i].cards.length-1].count<2) {
                    //nothing to match, remove from list.
                    badges.splice(i,1);
                    continue;
                }
            }
            let totalcards = 0;
            for (let j = 0; j<badges[i].maxcards; j++) {
                totalcards+=badges[i].cards[j].count;
            }
            badges[i].maxsets = Math.floor(totalcards/badges[i].maxcards);
            badges[i].lastset = Math.ceil(totalcards/badges[i].maxcards);
        }
        debugProfileEnd("PopulateExistingCards2");
    }

    function PopulateMaxcards(index){
        while (index < mybadges.length) {
            if (mybadges[index].maxcards === 0) {
                let url="https://steamcommunity.com/my/gamecards/"+mybadges[index].appid+"?l=english";
                let xhr = new XMLHttpRequest();
                xhr.open('GET', url, true);
                xhr.responseType = 'document';
                xhr.onload = function() { // eslint-disable-line
                    if (stop){
                        UpdateMessage("Interrupted by user");
                        HideThrobber();
                        EnableButton();
                        let stopButton = document.getElementById("asf_stm_stop");
                        stopButton.remove();
                        return;
                    }
                    let status = xhr.status;
                    let lastasset=0;
                    if (status === 200) {
                        errors = 0;
                        debugPrint("processing badge "+mybadges[index].appid);
                        UpdateMessage("Getting badge data for "+mybadges[index].appid);
                        let maxcards = xhr.response.documentElement.getElementsByClassName("gamecard").length;
                        mybadges[index].maxcards=maxcards;
                        index++;
                    } else {
                        errors++;
                    }
                    if ((status<400 || status>=500)&&(errors <= maxErrors)) {
                        setTimeout((function(index) { return function(){ PopulateMaxcards(index); };})(index), limiter);
                    } else {
                        UpdateMessage("Error getting badge data, ERROR "+status);
                        HideThrobber();
                        EnableButton();
                    }

                };
                xhr.onerror = function() { // eslint-disable-line
                    if (stop){
                        UpdateMessage("Interrupted by user");
                        HideThrobber();
                        EnableButton();
                        let stopButton = document.getElementById("asf_stm_stop");
                        stopButton.remove();
                        return;
                    }
                    errors++;
                    if (errors <= maxErrors) {
                        setTimeout((function(index) { return function(){ PopulateMaxcards(index); };})(index), limiter);
                        return;
                    } else {
                        debugPrint("error");
                        UpdateMessage("Error getting badge data");
                        HideThrobber();
                        EnableButton();
                    }
                };
                xhr.send();
                return; //do this synchronously to avoid rate limit
            } else {
                index++;
            }
        }
        debugPrint("populated");
        UpdateMessage("Fetching own inventory");
        //g_steamID is a global steam variable
        fetchinventory(g_steamID,0,function(){
            debugPrint("fetched");
            debugPrint(DeepClone(assets));
            debugPrint(DeepClone(descriptions));
            debugPrint("our cards");
            debugPrint(DeepClone(mybadges));
            PopulateExistingCards(mybadges,true);
            checkuser(0);
        });
    }

    function getbadges(page){
        let url="https://steamcommunity.com/my/badges?p="+page+"&l=english";
        let xhr = new 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.remove();
                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());
                    }
                }
                let badges=xhr.response.documentElement.getElementsByClassName("badge_row_inner");
                for (let i=0;i<badges.length;i++){
                    if (badges[i].getElementsByClassName("owned").length>0){ //we only need badges where we have at least one card, and no special badges
                        let appidNodes = badges[i].getElementsByClassName("card_drop_info_dialog");
                        if (appidNodes.length>0) {
                            let appidText=appidNodes[0].getAttribute("id");
                            let appidSplitted = appidText.split("_");
                            if (appidSplitted.length >= 5) {
                                let appid = Number(appidSplitted[4]);
                                let maxcards=0;
                                if (badges[i].getElementsByClassName("badge_craft_button").length === 0 ){
                                    let maxcardsText=badges[i].getElementsByClassName("badge_progress_info")[0].innerText.trim()
                                    let maxcardsSplitted=maxcardsText.split(" ");
                                    maxcards = Number(maxcardsSplitted[2]);
                                }
                                let badge_stub = {"appid":appid,"title":null, "maxcards":maxcards, "maxsets":0, "lastset":0, "cards":[]};
                                mybadges.push(badge_stub);
                            }
                        }
                    }
                }
                page++;
            } else {
                errors++;
            }
            if ((status<400 || status>=500)&&(errors <= maxErrors)) {
                if (page<=maxpages) {
                    setTimeout((function(page) { return function(){ getbadges(page); };})(page), limiter);
                } else {
                    debugPrint("all badge pages processed");
                    if (mybadges.length===0){
                        HideThrobber();
                        UpdateMessage("No cards to match");
                        return;
                    } else {
                        PopulateMaxcards(0);
                    }
                }
            } else {
                UpdateMessage("Error getting badge data, ERROR "+status);
                HideThrobber();
                EnableButton();
            }
        };
        xhr.onerror = function() {
            if (stop){
                UpdateMessage("Interrupted by user");
                HideThrobber();
                EnableButton();
                let stopButton = document.getElementById("asf_stm_stop");
                stopButton.remove();
                return;
            }
            errors++;
            if (errors <= maxErrors) {
                    setTimeout((function(page) { return function(){ getbadges(page); };})(page), limiter);
            } else {
                debugPrint("error getting badge page");
                UpdateMessage("Error getting badge page");
                HideThrobber();
                EnableButton();
            }
        };
        xhr.send();
    }

    function FilterEventHandler(event){
        let appid = event.target.id.split("_")[1];
        let matches = document.getElementsByClassName("asf_stm_appid_"+appid);
        for (let i = 0; i<matches.length; i++) {
            matches[i].style.display = event.target.checked ? "inline-block" : "none";
            CheckRow(matches[i].parentElement.parentElement);
        }
    }

    function FiltersButton(){
        let filterWidget = document.getElementById("asf_stm_filters");
        if (filterWidget.style.marginRight == "-50%") {
            filterWidget.style.marginRight ="unset";
        } else {
            filterWidget.style.marginRight ="-50%";
        }
    }

    function StopButton(){
        let stopButton = document.getElementById("asf_stm_stop");
        stopButton.removeEventListener("click", StopButton, false);
        stopButton.title = "Stopping...";
        stopButton.classList.add("btn_disabled");
        UpdateMessage("Stopping...");
        stop = true;
    }

    function ButtonPressed(){
        DisableButton();
        debugPrint(new Date(Date.now()));
        console.log(new Date(Date.now()));
        let maincontent = document.getElementsByClassName("maincontent")[0];
        maincontent.textContent="";
        maincontent.style.width="90%";
        maincontent.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%;">
                    </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: rgba( 103, 193, 245, 0.2 );color: #67c1f5;">
              <span id="asf_stm_placeholder" style="margin-right: 15px;">No matches to filter</span>
          </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", StopButton, false);
        document.getElementById("asf_stm_filters").addEventListener("input",FilterEventHandler);
        document.getElementById("asf_stm_filters_button").addEventListener("click", FiltersButton, false);
        maxpages=1;
        stop=false;
        mybadges.clear();
        getbadges(1);
    }

    if (document.getElementsByClassName("badge_details_set_favorite").length != 0) {
        let requestURL = 'https://asf.justarchi.net/Api/Bots';
        GM_xmlhttpRequest({
            method: "GET",
            url: requestURL,
            onload: function(response) {
                let re = /("steam_id":)(\d+)/g;
                let fixedjson = response.response.replace(re, '$1\"$2\"'); //because fuck js
                bots = JSON.parse(fixedjson);
                //bots.filter(bot=>bot.matchable_cards===1||bot.matchable_foil_cards===1);  //I don't think this is really needed
                bots.sort(function(a,b) { //sort received array as I like it.
                    let result = b.match_everything - a.match_everything; //bots with match_everything go first
                    if (result===0) {
                        result = b.items_count - a.items_count; //then by items_counts descending
                    }
                    if (result===0) {
                        result = b.games_count - a.games_count; //then by games_count descending
                    }
                    return result;
                });
                debugPrint ("found total "+bots.length+" bots");
                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");
                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();
            }
        });
    }
})();