PSSplinterlands.psm1

function Get-slBalanceHistory {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory,ValueFromPipeline,ValueFromPipelineByPropertyName)]
        [Alias("PlayerName")]
        [string]$UserName,

        [Parameter()]
        [ValidateSet("DEC","SPS","Credits","Voucher","Merits")]
        [string]$TokenType = "DEC",

        [Parameter()]
        [int]$Limit = 5000,

        [Parameter()]
        [int]$Offset = 0
    )

    DynamicParam
    {
        $attributeCollection = [System.Collections.ObjectModel.Collection[System.Attribute]]::new()
        $attributes = [System.Management.Automation.ParameterAttribute]::new()
        $attributeCollection.Add($attributes)

        switch ($TokenType){
            "DEC" {
                $ValidValues = [System.Management.Automation.ValidateSetAttribute]::new(
                    "dec_reward",
                    "season_rewards",
                    "guild_contribution",
                    "market_purchase",
                    "market_rental",
                    "rental_payment",
                    "rental_refund",
                    "rental_payment_fees",
                    "market_fees",
                    "quest_rewards",
                    "burn_cards",
                    "purchase_cl_presale_pack",
                    "mystery_prize",
                    "pack_purchase",
                    "orb_purchase",
                    "purchase_potion",
                    "tournament_creation",
                    "enter_tournament",
                    "leave_tournament",
                    "prize_tournament",
                    "cancel_tournament",
                    "token_transfer",
                    "leaderboard_prizes",
                    "token_award",
                    "unpaid_prizes"
                )
                break;
            }

            "SPS" {
                $ValidValues = [System.Management.Automation.ValidateSetAttribute]::new(
                    "token_award",
                    "stake_tokens",
                    "claim_staking_rewards",
                    "token_transfer",
                    "purchase_cl_presale_pack",
                    "tournament_payment",
                    "enter_tournament",
                    "leave_tournament",
                    "prize_tournament",
                    "cancel_tournament",
                    "unpaid_prizes"
                )
                break;
            }

            "Credits" {
                $ValidValues = [System.Management.Automation.ValidateSetAttribute]::new(
                    "purchase_cl_presale_pack",
                    "credits_purchase",
                    "market_purchase",
                    "market_rental",
                    "purchase_booster_pack"
                )
                break;
            }

            "Voucher" {
                $ValidValues = [System.Management.Automation.ValidateSetAttribute]::new(
                    "purchase_cl_presale_pack_voucher",
                    "voucher_drop",
                    "token_transfer"
                )
                break;
            }

            "Merits" {
                $ValidValues = [System.Management.Automation.ValidateSetAttribute]::new(
                    "brawl_prize",
                    "guild_gladius_purchase",
                    "guild_bldstone_purchase",
                    "guild_pwrstone_purchase"
                )
                break;
            }
        }
        $attributeCollection.Add($ValidValues)
        $dynParam1 = [System.Management.Automation.RuntimeDefinedParameter]::new(
            'TransactionType', [string], $attributeCollection
        )

        $paramDictionary = [System.Management.Automation.RuntimeDefinedParameterDictionary]::new()
        $paramDictionary.Add('TransactionType', $dynParam1)
        return $paramDictionary
    }

    Begin {
        $Uri = "https://api2.splinterlands.com/players/balance_history"  
    }

    Process{
        try{
            $Body = @{
                username    = $UserName
                token_type  = $TokenType.ToUpper()
                offset      = $Offset
                limit       = $Limit
            }

            if ($PSBoundParameters.ContainsKey("TransactionType")){
                $Body["types"] = $PSBoundParameters["TransactionType"]
            }

            $transactions = Invoke-RestMethod -Uri $Uri -Body $Body | ForEach-Object {$_}
            foreach ($transaction in $transactions){
                try {
                    $transaction.created_date = [datetime]::Parse($transaction.created_date)
                }
                catch{
                    Write-Information "Unable to parse date for created_date property"
                } #try/catch

                $decimalProperties = "amount","balance_end","balance_start"
                foreach ($property in $decimalProperties){
                    $transaction.$property = [double]$transaction.$property
                }
                
                $transaction.psobject.typenames.insert(0,"splinterlands.balancehistory")
                $transaction
            } #foreach
        }
        catch{
            $PSCmdlet.WriteError($_)
        } #try/catch
    } #Process
}
function Get-slBattleHistory {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory,ValueFromPipeline,ValueFromPipelineByPropertyName)]
        [Alias("UserName")]
        [string]$PlayerName
    )

    try{
        $Battles = Invoke-SplinterlandsAPI -Uri "https://game-api.splinterlands.io/battle/history?player=$PlayerName"
        foreach ($battle in $Battles.battles){
            $battle.details = $battle.details | ConvertFrom-Json
            $Teams = @($battle.details.team1,$battle.details.team2)
            if ($PlayerName -eq $battle.player_1){
                $Enemy = $battle.player_2
            }
            else {
                $Enemy = $battle.player_1
            }
            $battle | Add-Member -NotePropertyMembers @{
                Player = $PlayerName
                Enemty = $Enemy
                PlayerTeam = $Teams | Where-Object player -eq $PlayerName
                EnemyTeam = $Teams | Where-Object player -eq $Enemy
            }
            $battle
        }
    }
    catch{
        $PSCmdlet.WriteError($_)
    }
}
function Get-slCard {
    [CmdletBinding()]
    param(
        [int[]]$Id = 0.. [int]::MaxValue
    )

    try{
        $cards = Invoke-SplinterlandsAPI -Uri "https://api.splinterlands.io/cards/get_details"
        if ($cards){
            foreach ($card in $cards){
                $card | Add-Member -TypeName "splinterlands.card"
                Add-Member -InputObject $card -NotePropertyMembers @{
                    RarityName = Resolve-slRarity $card.Rarity
                    EditionName = ($card.editions -split "," | Resolve-slEdition)
                }
                $card | Where-Object id -in $Id
            }
        }
    }
    catch{
        $PSCmdlet.WriteError($_)
    }
}
function Get-slCardCollection {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory,ValueFromPipeline,ValueFromPipelineByPropertyName)]
        [Alias("UserName")]
        [string]$PlayerName
    )

    try{
        $Uri = "https://api.splinterlands.io/cards/collection/$PlayerName"
        $Collection = Invoke-SplinterlandsAPI -Uri $Uri
        $Collection.cards | ForEach-Object {
            Add-Member -InputObject $_ -TypeName "splinterlands.playercard"
            try{
                $_.last_used_date = [datetime]::Parse($_.last_used_date)
            }
            catch{
                Write-Information "Unable to parse date" 
            }
        }
        $Collection.cards
    }
    catch{
        $PSCmdlet.WriteError($_)
    }
}
function Get-slDailyDECBattleRewards {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory,ValueFromPipelineByPropertyName,ValueFromPipeline)]
        [Alias("PlayerName")]
        [string]$UserName,
        [int]$Limit = 5000
    )

    Process{
        $balancehistoryParam = @{
            UserName    = $UserName
            TokenType   = "DEC"
            Limit       = $Limit
            TransactionType = "dec_reward"
            ErrorAction = "Stop"
        }
        try{
            $decRewards = Get-slBalanceHistory @balancehistoryParam
        }
        catch{
            Write-Error "Unable to get reward infor: $($_.Exception.Message)" -ErrorAction Stop
        }
        $groupbyDay = {$_.created_date.ToShortDateString()}
        $decRewardGroupByDay = $decRewards | Group-Object $groupbyDay
        foreach ($day in $decRewardGroupByDay){
            [PSCustomObject]@{
                Date = $day.Name
                BattleCount = $day.Count
                DecEarned = ($day.group | Measure-Object -property amount -Sum).sum
            }
        }
    } #Process
}
function Get-slDECRewardsSummary {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory,ValueFromPipeline,ValueFromPipelineByPropertyName)]
        [Alias("PlayerName")]
        $UserName
    )

    Process {
        $allRewardList = [System.Collections.Generic.List[object]]::new()
        $rewardTypes = @("dec_reward","quest_rewards","season_rewards")
        foreach ($type in $rewardTypes){
            try{
                $firstandLastDate = $null
                $dateRange = $null
                $averageGamesperDay = $null
                $averageDailyEarned = $null

                $balancehistoryParam = @{
                    UserName    = $UserName
                    TokenType   = "DEC"
                    Limit       = 5000
                    TransactionType = $type
                    ErrorAction = "Stop"
                }
                $decRewards = Get-slBalanceHistory @balancehistoryParam
                $Stats = $decRewards | Measure-Object -Property amount -Sum -Average -Maximum -Minimum
                $groupbyDay = $decRewards | Group-Object -Property {
                    $_.created_date.ToShortDateString()
                }
                $firstandLastDate = $decRewards | Sort-Object created_date | Select-Object -First 1 -Last 1
                if (($firstandLastDate | Measure-Object).Count -eq 2){
                    $dateRange = New-TimeSpan -Start $firstandLastDate[0].created_date -End $firstandLastDate[1].created_date -ErrorAction SilentlyContinue
                    $averageGamesperDay = [math]::round(($groupbyDay | Measure-Object -Property Count -Sum).Sum / $dateRange.Days)
                    $averageDailyEarned = [math]::round(($groupbyDay.Group | Measure-Object -Property amount -Sum).Sum / $dateRange.Days)
                }
    
                $rewardTypeName = switch ($type){
                    "dec_reward" {"Battle"}
                    "quest_rewards" {"Quest"}
                    "season_rewards" {"Season"}
                }
    
                $rewardSumary = [PSCustomObject]@{
                    PSTypeName  = "splinterlands.decrewardsummary"
                    RewardType  = $rewardTypeName
                    RewardCount = ($decRewards | Measure-Object).Count
                    AvgReward   = [math]::Round($Stats.Average,2)
                    TotalDays   = $dateRange.Days
                    AvgRewardsPerDay = $averageGamesperDay
                    AvgDailyDEC = $averageDailyEarned
                    Total       = $Stats.Sum
                    Stats       = $Stats
                    DateRange   = $dateRange
                    StartDate   = ($firstandLastDate | Select-Object -First 1).created_date
                    EndDate     = ($firstandLastDate | Select-Object -Last 1).created_date
                    RewardTransactions = $decRewards
                }

                $allRewardList.Add($rewardSumary)
                $rewardSumary
            }
            catch{
                Write-Warning "Unable to calculate [$type] reward summary"
            } #try/catch
        } #foreach

        #summary
        $decRewards = $allRewardList.RewardTransactions
        $Stats = $decRewards | Measure-Object -Property amount -Sum -Average -Maximum -Minimum
        $groupbyDay = $decRewards | Group-Object -Property {
            $_.created_date.ToShortDateString()
        }
        $firstandLastDate = $decRewards | Sort-Object created_date | Select-Object -First 1 -Last 1
        if (($firstandLastDate | Measure-Object).Count -eq 2){
            $dateRange = New-TimeSpan -Start $firstandLastDate[0].created_date -End $firstandLastDate[1].created_date -ErrorAction SilentlyContinue
            $averageGamesperDay = [math]::round(($groupbyDay | Measure-Object -Property Count -Sum).Sum / $dateRange.Days)
            $averageDailyEarned = [math]::round(($groupbyDay.Group | Measure-Object -Property amount -Sum).Sum / $dateRange.Days)
        }

        [PSCustomObject]@{
            PSTypeName  = "splinterlands.decrewardsummary"
            RewardType  = "All"
            RewardCount = ($decRewards | Measure-Object).Count
            AvgReward   = [math]::Round($Stats.Average,2)
            TotalDays   = $dateRange.Days
            AvgRewardsPerDay = $averageGamesperDay
            AvgDailyDEC = $averageDailyEarned
            Total       = $Stats.Sum
            Stats       = $Stats
            DateRange   = $dateRange
            StartDate   = ($firstandLastDate | Select-Object -First 1).created_date
            EndDate     = ($firstandLastDate | Select-Object -Last 1).created_date
            RewardTransactions = $decRewards
        }
    }
}
function Get-slPlayerActiveRental {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory,ValueFromPipeline,ValueFromPipelineByPropertyName)]
        [string[]]$PlayerName,
        [Parameter()]
        [ValidateSet("renter","owner")]
        [string]$PlayerType = "owner",

        [int]$Limit = 50
    )

    Process{
        foreach ($player in $PlayerName){
            $Rentals = Invoke-SplinterlandsAPI -Uri "https://api2.splinterlands.com/market/active_rentals?$PlayerType=$player&limit=$Limit"
            foreach ($rental in $Rentals){
                try{
                    $rental.rental_date = [datetime]::Parse($rental.rental_date)
                    $rental.next_rental_payment = [datetime]::Parse($rental.next_rental_payment)
                    $rental.cancel_date = [datetime]::Parse($rental.cancel_date)
                }
                catch{
                    Write-Information "Unable to convert date properties to datetime type"
                }
                $rental | Add-Member -TypeName "splinterlands.playeractiverental"
                $rental | Add-Member -MemberType AliasProperty -Name DailyCost -Value buy_price
                $rental | Add-Member -MemberType NoteProperty -Name Cancelled -Value ([bool]$rental.cancel_tx)
                $rental
            }
        } #Foreach
    } #Process
}
function Get-slPlayerBalance {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory,ValueFromPipeline,ValueFromPipelineByPropertyName)]
        [Alias("UserName")]
        [string[]]$PlayerName
    )

    Process{
        foreach ($player in $PlayerName){
            $playerBalance = Invoke-SplinterlandsAPI -Uri "https://api2.splinterlands.com/players/balances?username=$player"
            $playerBalance | Foreach-Object {$_.psobject.typenames.insert(0,"splinterlands.playerbalance")}
            $playerBalance | Where-Object token -eq "ECR" | ForEach-Object {
                $_.balance = $_.balance / 100
                $_.last_reward_time = [datetime]$_.last_reward_time
            }
            $playerBalance
        }
    }
}
function Get-slPlayerBattleSummary {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory,ValueFromPipeline,ValueFromPipelineByPropertyName)]
        [Alias("opponent_player")]
        [string]$PlayerName,
        [Parameter(ValueFromPipelineByPropertyName)]
        [Alias("inactive")]
        [string]$IgnoreColors
    )

    Process{
        $IgnoreColorsList = $IgnoreColors -split ","
        $allCards = Get-slCard
        $recentBattles = Get-slBattleHistory -PlayerName $PlayerName
        $battleCount = ($recentBattles | Measure-Object).Count
        $playerQuest = Get-slPlayersQuest -PlayerName $PlayerName

        $top3Summoners = $recentBattles.PlayerTeam | Where-Object color -notin $IgnoreColorsList | Select-Object -ExpandProperty summoner |
            Group-Object card_detail_id | Sort-Object Count -Descending | Select-Object -First 3
        $summonerUsage = foreach ($summoner in $top3Summoners){
            $summonerDetails = $allCards | Where-Object Id -eq $summoner.Name
            $sPercentage = [math]::round(($summoner.count / $battleCount * 100))
            $summonerLevel = $summoner.group | Select-Object -First 1 -ExpandProperty level

            $summonerBattles = $recentBattles | Where-Object {$_.PlayerTeam.Summoner.card_detail_id -eq $summoner.name}
            $summonerBattleCount = ($summonerBattles | Measure-Object).Count
            $top6SummonerMonsters = $summonerBattles.PlayerTeam.monsters | Group-Object card_detail_id |
            Sort-Object Count -Descending | Select-Object -First 6
            $TopSummonerMonsters = foreach ($monster in $top6SummonerMonsters){
                $monsterDetails = $allCards | Where-Object Id -eq $monster.Name
                $monsterLevel = $monster.group | Select-Object -First 1 -ExpandProperty level
                $mPercentage = [math]::round(($monster.count / $summonerBattleCount * 100))
                [PSCustomObject]@{
                    Monster = $monsterDetails
                    Abilities = $monsterDetails.stats.abilities[0.. ($monsterLevel - 1)]
                    MonsterLevel = $monsterLevel
                    UsagePercentage = $mPercentage
                }
            }

            [PSCustomObject]@{
                Summoner = $summonerDetails
                UsagePercentage = $sPercentage
                SummonerLevel = $summonerLevel
                TopSummonerMonsters = $TopSummonerMonsters
            }
        }

        $top6Monsters = $recentBattles.PlayerTeam.monsters | Group-Object card_detail_id |
            Sort-Object Count -Descending | Select-Object -First 6
        $monsterUsage = foreach ($monster in $top6Monsters){
            $monsterLevel = $monster.group | Select-Object -First 1 -ExpandProperty level
            $monsterDetails = $allCards | Where-Object Id -eq $Monster.Name
            $mPercentage = [math]::round(($monster.count / $battleCount * 100))
            [PSCustomObject]@{
                Monster = $monsterDetails
                MonsterLevel = $monsterLevel
                Abilities = $monsterDetails.stats.abilities[0.. ($monsterLevel - 1)]
                UsagePercentage = $mPercentage
            }
        }

        [PSCustomObject]@{
            PSTypeName = "splinterlands.playerbattlesummary"
            PlayerName = $PlayerName
            CurrentQuest = $playerQuest
            TopSummoners = $summonerUsage
            TopMonsters = $monsterUsage
        }
    } #process
}
function Get-slPlayerOutstandingBattle {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory,ValueFromPipeline,ValueFromPipelineByPropertyName)]
        [Alias("UserName")]
        [string[]]$PlayerName
    )

    Process{
        foreach ($player in $PlayerName){
            $Battle = Invoke-SplinterlandsAPI -uri " https://api2.splinterlands.com/players/outstanding_match?username=$PlayerName"
            #api returns a string 'null' if no battle if found
            if ($Battle -ne "null"){
                $Battle | Add-Member -TypeName "splinterlands.outstandingbattle"

                $rulesetList = $Battle.ruleset -split "\|" | Where-Object {-not[string]::IsNullOrWhiteSpace($_)}
                $Battle | Add-Member -NotePropertyMembers @{
                    "RulesetList" = $rulesetList
                    "RulesetListFriendly" = ($rulesetList | Resolve-slRuleset)
                }

                $dateProperties = "created_date","expiration_date","match_date","submit_expiration_date"
                foreach ($dateProperty in $dateProperties){
                    try{
                        $Battle.$dateProperty = [datetime]::Parse($battle.$dateProperty)
                    }
                    catch{
                        Write-Information "Unable to parse date property [$dateProperty]"
                    }
                } #foreach date property
                $Battle
            } #if not null
        } #foreach player
    } #process
}
function Get-slPlayersQuest {
    [CmdletBinding()]
    param(
        [Parameter(ValueFromPipeline,ValueFromPipelineByPropertyName)]
        [Alias("UserName")]
        [string[]]$PlayerName
    )

    Process {
        foreach ($player in $PlayerName){
            #foreach at the end is needed for some weird reason
            try{
                $questInfo = Invoke-SplinterlandsAPI -Uri "https://api2.splinterlands.com/players/quests?username=$player" -ErrorAction Stop | ForEach-Object {$_}
                try{
                    $questInfo.created_date = [datetime]::Parse($questInfo.created_date)
                    $questInfo.claim_date = [datetime]::Parse($questInfo.claim_date)
                }
                catch{
                    Write-Information "Unable to parse date" 
                }
                $questInfo | Add-Member -TypeName "splinterlands.quest"
                $questInfo | Add-Member -MemberType NoteProperty -Name "QuestType" -Value (Resolve-slQuest $questInfo.Name)
                $questInfo | Add-Member -MemberType NoteProperty -Name "Completed" -Value ($questInfo.total_items -eq $questInfo.completed_items)
                $questInfo
            }
            catch{
                $PSCmdlet.WriteError($_)
            }
        }
    }
}
function Invoke-SplinterlandsAPI {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        $Uri
    )

    try {
        Invoke-RestMethod -Uri $Uri
    }
    catch{
        $PSCmdlet.WriteError($_)
    }
}
function Resolve-slEdition {
    [CmdletBinding()]
    param(
        [Parameter(ValueFromPipeline,ValueFromPipelineByPropertyName)]
        [int]$Edition
    )

    Process {
        switch ($Edition){
            0 {"Alpha"}
            1 {"Beta"}
            2 {"Promo"}
            3 {"Reward"}
            4 {"Untamed"}
            5 {"Dice"}
            6 {"Gladius"}
            default {"Unknown"}
        }
    }
}
function Resolve-slQuest {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory,ValueFromPipeline,ValueFromPipelineByPropertyName)]
        [string]$QuestName
    )

    Process{
        switch ($QuestName) {
            "Stir the Volcano" {"Fire"}
            "Lyanna's Call" {"Earth"}
            "Defend the Borders" {"Life"}
            "Rising Dead" {"Death"}
            "Pirate Attacks" {"Water"}
            "Stubborn Mercenaries" {"No Neutrals"}
            "High Priority Targets" {"Dragon"}
            "Stealth Mission" {"Sneak"}
            default {"Unknown"}
        }
    }
}
function Resolve-slRarity {
    [CmdletBinding()]
    param(
        [Parameter(ValueFromPipeline,ValueFromPipelineByPropertyName)]
        [int]$Rarity
    )

    Process {
        switch ($Rarity){
            1 {"Common"}
            2 {"Rare"}
            3 {"Epic"}
            4 {"Summoner"}
            default {"Unknown"}
        }
    }
}
function Resolve-slRuleset {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory,ValueFromPipeline,ValueFromPipelineByPropertyName)]
        [string[]]$RulesetName
    )

    Process{
        foreach ($ruleset in $RulesetName){
            switch ($ruleset){
                "Up Close & Personal"   {"Melee Only"}
                "Spreading Fury"        {"Fury Ability"}
                "Odd Ones Out"          {"Odds Only"}
                default {$ruleset}
            }
        }
    }
}
Export-ModuleMember -function Get-slBalanceHistory, Get-slBattleHistory, Get-slCard, Get-slCardCollection, Get-slDailyDECBattleRewards, Get-slDECRewardsSummary, Get-slPlayerActiveRental, Get-slPlayerBalance, Get-slPlayerBattleSummary, Get-slPlayerOutstandingBattle, Get-slPlayersQuest