No Description

Awesome 19b5882928 fixed market monitor 4 days ago
.gitignore 518f614d51 Added monitor for cost using requests instead of selenium for efficiency since it fires more frequently it's also gathering the pornstar_number, card-id, card level and amount of cards sold for possible later use 4 days ago
DiscordBot.md 0f98ee5191 Add stub for Discord Bot, and logging stubs. 5 months ago
README.md 0bb1022d63 JS Tweaks 1 week ago
config.py 9e4b571335 Create Objects Market and Player 5 months ago
db.py 19b5882928 fixed market monitor 4 days ago
discord-bot.py 19b5882928 fixed market monitor 4 days ago
gatherMarket.py 518f614d51 Added monitor for cost using requests instead of selenium for efficiency since it fires more frequently it's also gathering the pornstar_number, card-id, card level and amount of cards sold for possible later use 4 days ago
logger.py e33d90d23b added a notification function for the discord bot also modified the selenium init and the gathering schedule 5 days ago
market.py e33d90d23b added a notification function for the discord bot also modified the selenium init and the gathering schedule 5 days ago
marketMonitor.py 19b5882928 fixed market monitor 4 days ago
notifications.json e33d90d23b added a notification function for the discord bot also modified the selenium init and the gathering schedule 5 days ago
player.py e33d90d23b added a notification function for the discord bot also modified the selenium init and the gathering schedule 5 days ago

README.md

BoobyLegends Economic Helper

This is intended as a helper to find trends in the Booby Legends game.

I intend to built a Discord Bot Interface to let us share in our guild.

Running Executables

Gather Marketing Data

This will gather marketing data every 15 minutes, and build an average for each day in UTC timezone.

python3 gatherMarket.py

Classes

There are some main components written as python classes.

Market

Loads the current Market values for cards.

You can initialize the Market List like this:

from market import Market
m=Market()
m.fetchData()
m.show()

Show a filtered Market Page with cards you alreqady have Crystalized removed.

from market import Market
m=Market()
m.fetchData()
m.filterMarketCards(False)
#After doing stuff...
m.closePage()

Player

Loads the current Player values for cards. To get stock values, a "config.json" file must be in the folder with a "cookie" defined to have the player's login. I got my cookie by using the "EditThisCookie" plugin for Chrome. The config file should looks omething like this.

{
    "cookie":
        {
            "name":"wordpress_logged_in_f129834928365fbaer348384348y23423uy2323",
            "value":"keymasterofgozer%8ksojhelFM88efn98709cvaSneOVWEUVn9dvs9eVASEvjSEv8ksojhelFM88efn98709cvaSneOVWEUVn9dvs9eVASEvjSEv8ksojhelFM88efn98709cvaSneOVW"
        }
}

You can initialize your Player list like this:

from player import Player
p=Player()
p.fetchData()
p.show()

Get a List of non-crystal remaining Cards to collect

from player import Player
p=Player()
p.fetchData()
p.show(fields=['name','level','stock'],filter='stock != "CRYSTAL"')

Market Data

from db import MarketDB, hist, plot
hist(2)
plot(2)

Javascript clips for console use to do things on pages.

Player Page

javascript to filter Player Page for salable cards
sellThreshold = {};
sellThreshold["2000"] = 10000;
sellThreshold["350"] = 195;
sellThreshold["250"] = 126;
sellThreshold["200"] =  87;
sellThreshold["150"] = 110;
sellThreshold["115"] =  80;
sellThreshold["75"]  =  32;
sellThreshold["50"]  =  25;
sellThreshold["40"]  =  32;
sellThreshold["20"]  =  22;
sellThreshold["15"]  =  12;
sellThreshold["10"]  =   8;
sellThreshold["4"]   =   9;
sellThreshold["3"]   =   4;
sellThreshold["2"]   =   4;
sellThreshold["1"]   =   4;
clist=[];
for (var i = 0; i < 153; i++) {clist.push(0);}
let market = document.createElement('div');
market.id = 'market';
market.innerHTML=await (await fetch('https://play.boobylegends.com/market/')).text();
marketCards=market.getElementsByClassName('wrapper-market-card');
for (var i = 0; i < marketCards.length; i++) {
    try{
    cardNum = parseInt(marketCards.item(i).getElementsByClassName('pornstar-number')[0].innerText);
    price = parseInt(marketCards.item(i).getElementsByClassName('cost')[0].innerText.trim());
    clist[cardNum]=price;
    }catch(err){console.log('error on '+cardNum.toString());}
};
cards=document.getElementsByClassName('wrapper-card');
for (var i = 0; i < cards.length; i++) {
    cardNum = parseInt(cards.item(i).getElementsByClassName('pornstar-number')[0].outerText);
    cardVal = parseInt(cards.item(i).getElementsByClassName('score-card')[0].outerText);
    price = clist[cardNum];
    var cardNB = cards.item(i).getElementsByClassName('card-nb')[0];
    if (cardNB){try{
        stock = parseInt(cards.item(i).getElementsByClassName('nb-label')[0].outerText.trim());
        var fuzed = cards.item(i).getElementsByClassName('nb-name')[0];
        if (fuzed){stock = stock + 1;}
        HTMLText = "Price: " + price;
        var priceDiv = cardNB.getElementsByClassName('Price')[0];
        if (priceDiv){priceDiv.innerText = HTMLText;}
        else{
            let qty = document.createElement('div');
            qty.classList.add('Price');
            qty.innerText = HTMLText;
            cardNB.appendChild(qty);
        }
    }catch(err){stock=0;}}else{
        stock=0;
    }
    if (stock<2){
        cards.item(i).style.setProperty('Display','None');
    }
    if (price<sellThreshold[cardVal.toString()]){
        cards.item(i).style.setProperty('Display','None');
    }
} //filter Player Page for salable cards
javascript to show number cards for sale
numForSale=[];
for (var i = 0; i < 153; i++) {numForSale.push(0);}
let marketSelling = document.createElement('div');
marketSelling.id = 'market';
marketSelling.innerHTML=await (await fetch('https://play.boobylegends.com/market/?tab=selling')).text();
marketCards=marketSelling.getElementsByClassName('wrapper-market-card');
for (var i = 0; i < marketCards.length; i++) {
    try{
    cardNum = parseInt(marketCards.item(i).getElementsByClassName('pornstar-number')[0].innerText);
    price = parseInt(marketCards.item(i).getElementsByClassName('cost')[0].innerText.trim());
    numForSale[cardNum]=numForSale[cardNum]+1;
    }catch(err){console.log('error on '+cardNum.toString());}
};
cards=document.getElementsByClassName('wrapper-card');
for (var i = 0; i < cards.length; i++) {
    cardNum = parseInt(cards.item(i).getElementsByClassName('pornstar-number')[0].outerText);
    cardVal = parseInt(cards.item(i).getElementsByClassName('score-card')[0].outerText);
    if (numForSale[cardNum] > 0){
        HTMLText = " +" + numForSale[cardNum];
    }else{HTMLText=""}
    var cardNB = cards.item(i).getElementsByClassName('card-nb')[0];
    if (cardNB){try{
        var priceDiv = cardNB.getElementsByClassName('Price')[0];
        if (priceDiv){priceDiv.innerText = HTMLText;}
        else{
            let qty = document.createElement('div');
            qty.classList.add('Price');
            qty.innerText = HTMLText;
            cardNB.appendChild(qty);
        }
    }catch(err){stock=0;}}else{
        stock=0;
    }
} //Player page show number cards for sale
javascript to show only cards for sale
numForSale=[];
for (var i = 0; i < 153; i++) {numForSale.push(0);}
let marketSelling = document.createElement('div');
marketSelling.id = 'market';
marketSelling.innerHTML=await (await fetch('https://play.boobylegends.com/market/?tab=selling')).text();
marketCards=marketSelling.getElementsByClassName('wrapper-market-card');
for (var i = 0; i < marketCards.length; i++) {
    try{
    cardNum = parseInt(marketCards.item(i).getElementsByClassName('pornstar-number')[0].innerText);
    price = parseInt(marketCards.item(i).getElementsByClassName('cost')[0].innerText.trim());
    numForSale[cardNum]=numForSale[cardNum]+1;
    }catch(err){console.log('error on '+cardNum.toString());}
};
cards=document.getElementsByClassName('wrapper-card');
for (var i = 0; i < cards.length; i++) {
    cardNum = parseInt(cards.item(i).getElementsByClassName('pornstar-number')[0].outerText);
    cardVal = parseInt(cards.item(i).getElementsByClassName('score-card')[0].outerText);
    if (numForSale[cardNum] > 0){
        HTMLText = " +" + numForSale[cardNum];
    }else{HTMLText=""; cards.item(i).style.setProperty('Display','None');}
    var cardNB = cards.item(i).getElementsByClassName('card-nb')[0];
    if (cardNB){try{
        var priceDiv = cardNB.getElementsByClassName('Price')[0];
        if (priceDiv){priceDiv.innerText = HTMLText;}
        else{
            let qty = document.createElement('div');
            qty.classList.add('Price');
            qty.innerText = HTMLText;
            cardNB.appendChild(qty);
        }
    }catch(err){stock=0;}}else{
        stock=0;
    }
} //Player page show only cards for sale

Market Page

javascript to filter Market for needed cards
playerStock=[];
for (var i = 0; i < 153; i++) {x={};x["extra"]=0;x["status"]="NORMAL";playerStock.push(x);}
let player = document.createElement('div');
player.id = 'player';
player.innerHTML=await (await fetch('https://play.boobylegends.com/player/')).text();
playerCards=player.getElementsByClassName('wrapper-card');
for (var i = 0; i < playerCards.length; i++) {
    stock = {};
    stock["extra"]=0;
    stock["status"]="NORMAL";
    try{
    cardNum = parseInt(playerCards.item(i).getElementsByClassName('pornstar-number')[0].innerText);
    var cardNB = playerCards.item(i).getElementsByClassName('card-nb')[0];
    if (cardNB){
        try{
        cnt = parseInt(cardNB.getElementsByClassName('nb-label')[0].outerText.trim());
        }catch(err){cnt=0;}
        stock["extra"]=cnt-1;
        try{
        var fuzed = cardNB.getElementsByClassName('nb-name')[0].outerText.trim();
        if (fuzed){
            stock["extra"] = stock["extra"] + 1;
            stock["status"]= fuzed;
            }
        }catch(err){console.log('No Status, or Normal Card : '+cardNum.toString());}
        playerStock[cardNum]=stock;
    }
    }catch(err){console.log('Error on Card : '+cardNum.toString());}
};
cards=document.getElementsByClassName('wrapper-market-card');
for (var i = 0; i < cards.length; i++) {
    cardNum = cards.item(i).getElementsByClassName('pornstar-number')[0].outerText;
    if (playerStock[cardNum].status != "NORMAL"){
        HTMLText=playerStock[cardNum].status + " +"
    }else{HTMLText=""}
    HTMLText = "Stock: " + HTMLText + playerStock[cardNum].extra;
    innerCard = cards.item(i).getElementsByClassName('inner-market-card')[0];
    var stock = innerCard.getElementsByClassName('Stock')[0];
    if (stock){stock.innerText = HTMLText}
    else {
        let qty = document.createElement('div');
        qty.classList.add('Stock');
        qty.innerText = HTMLText;
        innerCard.appendChild(qty);
        }
    if (playerStock[cardNum].status == 'CRYSTAL'){
        cards.item(i).style.setProperty('Display','None');
    }
} //filter Market for needed cards
javascript to filter Market for good buys for needed cards
buyThreshold = {};
buyThreshold["2000"] = 1000;
buyThreshold["350"] = 155;
buyThreshold["250"] = 100;
buyThreshold["200"] =  65;
buyThreshold["150"] =  85;
buyThreshold["115"] =  60;
buyThreshold["75"]  =  25;
buyThreshold["50"]  =  18;
buyThreshold["40"]  =  25;
buyThreshold["20"]  =  15;
buyThreshold["15"]  =   8;
buyThreshold["10"]  =   5;
buyThreshold["4"]   =   6;
buyThreshold["3"]   =   1;
buyThreshold["2"]   =   1;
buyThreshold["1"]   =   1;
playerStock=[];
for (var i = 0; i < 153; i++) {x={};x["extra"]=0;x["status"]="NORMAL";playerStock.push(x);}
let player = document.createElement('div');
player.id = 'player';
player.innerHTML=await (await fetch('https://play.boobylegends.com/player/')).text();
playerCards=player.getElementsByClassName('wrapper-card');
for (var i = 0; i < playerCards.length; i++) {
    stock = {};
    stock["extra"]=0;
    stock["status"]="NORMAL";
    try{
    cardNum = parseInt(playerCards.item(i).getElementsByClassName('pornstar-number')[0].innerText);
    var cardNB = playerCards.item(i).getElementsByClassName('card-nb')[0];
    if (cardNB){
        try{
        cnt = parseInt(cardNB.getElementsByClassName('nb-label')[0].outerText.trim());
        }catch(err){cnt=0;}
        stock["extra"]=cnt-1;
        try{
        var fuzed = cardNB.getElementsByClassName('nb-name')[0].outerText.trim();
        if (fuzed){
            stock["extra"] = stock["extra"] + 1;
            stock["status"]= fuzed;
            }
        }catch(err){console.log('No Status, or Normal Card : '+cardNum.toString());}
        playerStock[cardNum]=stock;
    }
    }catch(err){console.log('Error on Card : '+cardNum.toString());}
};
cards=document.getElementsByClassName('wrapper-market-card');
for (var i = 0; i < cards.length; i++) {
    cardNum = cards.item(i).getElementsByClassName('pornstar-number')[0].outerText;
    cardVal = parseInt(cards.item(i).getElementsByClassName('score-card')[0].outerText);
    cardCost = parseInt(cards.item(i).getElementsByClassName('cost')[0].outerText);
    cardPPG = cardVal/cardCost;
    if (playerStock[cardNum].status != "NORMAL"){
        HTMLText=playerStock[cardNum].status + " +"
    }else{HTMLText=""}
    HTMLText = "Stock: " + HTMLText + playerStock[cardNum].extra;
    HTMLText = HTMLText+"<br/>"+
            "Points/<img src='https://play.boobylegends.com/wp-content/themes/child-roulette/img/gem.png' style='height:22px'>:<br/>" + cardPPG.toFixed(2);
    innerCard = cards.item(i).getElementsByClassName('inner-market-card')[0];
    var extraInfo = innerCard.getElementsByClassName('extraInfo')[0];
    if (extraInfo){extraInfo.innerHTML = HTMLText}
    else {
        let extraInfoDiv = document.createElement('div');
        extraInfoDiv.classList.add('extraInfo');
        extraInfoDiv.innerHTML = HTMLText;
        extraInfoDiv.setAttribute("align", "center");
        innerCard.appendChild(extraInfoDiv);
        }
    if (playerStock[cardNum].status == 'CRYSTAL'){
        cards.item(i).style.setProperty('Display','None');
    }
    /*if (cardPPG < 2){
        cards.item(i).style.setProperty('Display','None');
    }*/
    if (cardCost > buyThreshold[cardVal.valueOf()]){
        cards.item(i).style.setProperty('Display','None');
    }
} //filter Market for good buys for needed cards
javascript to filter Market for uncrystalized cards
clist=[];
for (var i = 0; i < 153; i++) {clist.push(0);}
let player = document.createElement('div');
player.id = 'player';
player.innerHTML=await (await fetch('https://play.boobylegends.com/player/')).text();
playerCards=player.getElementsByClassName('wrapper-card');
for (var i = 0; i < playerCards.length; i++) {
    try{
    cardNum = parseInt(playerCards.item(i).getElementsByClassName('pornstar-number')[0].innerText);
    stock = playerCards.item(i).getElementsByClassName('card-nb')[0].innerText.trim();
    clist[cardNum]=stock;
    }catch(err){console.log('hi');}
};
cards=document.getElementsByClassName('wrapper-market-card');
for (var i = 0; i < cards.length; i++) {
    cardNum = cards.item(i).getElementsByClassName('pornstar-number')[0].outerText;
    HTMLText = "Stock: " + clist[cardNum];
    innerCard = cards.item(i).getElementsByClassName('inner-market-card')[0];
    var stock = innerCard.getElementsByClassName('Stock')[0];
    if (stock){stock.innerText = HTMLText}
    else {
        let qty = document.createElement('div');
        qty.classList.add('Stock');
        qty.innerText = HTMLText;
        innerCard.appendChild(qty);
        }
    if (clist[cardNum].toString().includes('CRYSTAL')){
        cards.item(i).style.setProperty('Display','None');
    }
} //filter Market for uncrystalized cards
javascript to filter Market for good value cards
playerStock=[];
for (var i = 0; i < 153; i++) {playerStock.push(0);}
let player = document.createElement('div');
player.id = 'player';
player.innerHTML=await (await fetch('https://play.boobylegends.com/player/')).text();
playerCards=player.getElementsByClassName('wrapper-card');
for (var i = 0; i < playerCards.length; i++) {
    try{
    cardNum = parseInt(playerCards.item(i).getElementsByClassName('pornstar-number')[0].innerText);
    stock = playerCards.item(i).getElementsByClassName('card-nb')[0].innerText.trim();
    playerStock[cardNum]=stock;
    }catch(err){console.log('hi');}
};
cards=document.getElementsByClassName('wrapper-market-card');
for (var i = 0; i < cards.length; i++) {
    cardNum = cards.item(i).getElementsByClassName('pornstar-number')[0].outerText;
    cardVal = parseInt(cards.item(i).getElementsByClassName('score-card')[0].outerText);
    cardCost = parseInt(cards.item(i).getElementsByClassName('cost')[0].outerText);
    cardPPG = cardVal/cardCost;
    HTMLText = "Stock: " + playerStock[cardNum]+"<br/>"+
            "Points/<img src='https://play.boobylegends.com/wp-content/themes/child-roulette/img/gem.png' style='height:22px'>:<br/>" + cardPPG.toFixed(2);
    innerCard = cards.item(i).getElementsByClassName('inner-market-card')[0];
    var PPG = innerCard.getElementsByClassName('PPG')[0];
    if (PPG){PPG.innerHTML = HTMLText}
    else {
        let extraInfoDiv = document.createElement('div');
        extraInfoDiv.classList.add('PPG');
        extraInfoDiv.innerHTML = HTMLText;
        extraInfoDiv.setAttribute("align", "center");
        innerCard.appendChild(extraInfoDiv);
        }
    if (cardPPG < 2.5){
        cards.item(i).style.setProperty('Display','None');
    }
} //filter Market for good value cards

Market Selling Page

javascript to show overbid cards for sale
sellThreshold = {};
sellThreshold["2000"] = 10000;
sellThreshold["350"] = 195;
sellThreshold["250"] = 126;
sellThreshold["200"] =  87;
sellThreshold["150"] = 110;
sellThreshold["115"] =  80;
sellThreshold["75"]  =  32;
sellThreshold["50"]  =  25;
sellThreshold["40"]  =  32;
sellThreshold["20"]  =  22;
sellThreshold["15"]  =  12;
sellThreshold["10"]  =   8;
sellThreshold["4"]   =   9;
sellThreshold["3"]   =   4;
sellThreshold["2"]   =   4;
sellThreshold["1"]   =   4;
clist=[];
for (var i = 0; i < 153; i++) {clist.push(0);}
let market = document.createElement('div');
market.id = 'market';
market.innerHTML=await (await fetch('https://play.boobylegends.com/market/')).text();
marketCards=market.getElementsByClassName('wrapper-market-card');
for (var i = 0; i < marketCards.length; i++) {
    try{
    cardNum = parseInt(marketCards.item(i).getElementsByClassName('pornstar-number')[0].innerText);
    price = parseInt(marketCards.item(i).getElementsByClassName('cost')[0].innerText.trim());
    clist[cardNum]=price;
    }catch(err){console.log('error on '+cardNum.toString());}
};
cards=document.getElementsByClassName('wrapper-market-card');
for (var i = 0; i < cards.length; i++) {
    cardNum = cards.item(i).getElementsByClassName('pornstar-number')[0].outerText;
    cardVal = parseInt(cards.item(i).getElementsByClassName('score-card')[0].outerText);
    cardCost = parseInt(cards.item(i).getElementsByClassName('cost')[0].outerText);
    cardPPG = cardVal/cardCost;
    HTMLText = "Current Bid: "+clist[cardNum].toString()+"<br/>Threshold: "+sellThreshold[cardVal.toString()];
    innerCard = cards.item(i).getElementsByClassName('inner-market-card')[0];
    var PPG = innerCard.getElementsByClassName('PPG')[0];
    if (PPG){PPG.innerHTML = HTMLText}
    else {
        let qty = document.createElement('div');
        qty.classList.add('PPG');
        qty.innerHTML = HTMLText;
        qty.setAttribute("align", "center");
        innerCard.appendChild(qty);
        }
    if (cardCost <= clist[cardNum]){
        cards.item(i).style.setProperty('Display','None');
    }
    if (clist[cardNum] < sellThreshold[cardVal.toString()]){
        //cards.item(i).style.setProperty('Display','None');
    }
} //Market page show overbid cards for sale