posh-Brawl.psm1


Function Get-BrawlerIDIndexList {
   <#
.Synopsis
   Gets a Brawler ID Index list.
.DESCRIPTION
   Gets a list of brawler IDs. This is used for autocompletion and is not a public function.
.EXAMPLE
   Get-BrawlerIDIndexList
 
   #>


   [CmdletBinding()]
   Param (
   )
   Process {
      $Path = "$Script:moduleBase\assets\BrawlerIndex.csv"
      $BrawlerIndex = Import-Csv -Path $Path
      $BrawlerIndex.id
   }
}
Function Get-BrawlerIndexList {
   <#
.Synopsis
   Gets a Brawler list.
.DESCRIPTION
   Gets a list of brawler names. This is used for autocompletion and is not a public function.
.EXAMPLE
   Get-BrawlerIndexList
 
   #>


   [CmdletBinding()]
   Param (
   )
   Process {
      $BrawlerIndex = Import-Csv -Path "$Script:moduleBase\bin\BrawlerIndex.csv"
      $BrawlerIndex.name | Sort-Object
   }
}
Function Invoke-BrawlRequest {
   <#
.SYNOPSIS
   Private function that is invoked when a command is ran
.DESCRIPTION
   This essentially acts as a wrapper for an API call to the Brawl Stars API.
   It utilizes Invoke-Restmethod under the hood. This is a private function so it will not be
   available upon import.
.EXAMPLE
   Invoke-BrawlRequest -uri $uri
.PARAMETER Uri
   URI of the resource/endpoint for Brawl Stars API.
.NOTES
    https://developer.brawlstars.com/#/documentation
#>


   [CmdletBinding()]
   Param (
      [Parameter(Mandatory, ValueFromPipeline)]
      [Uri]$Uri
   )
   Begin {
      $ErrorActionPreference = "Stop"
   }
   Process {
      Write-Verbose -Message "Invoke-BrawlRequest: URI set to $URI"
      $Response = Invoke-RestMethod -Method Get -Uri $Uri -Headers $Script:headers -ContentType "application/json" -SkipHttpErrorCheck -StatusCodeVariable StatusCode
      if ($Response.Reason) {
         Write-Error -Message "$($Response.Reason),$($Response.Message),StatusCode: $StatusCode."
      }
      switch -wildcard ($URI) {
         "$Script:PlayersEndPoint/%23$PlayerTag/battlelog" { $Response.items }
         "$script:ClubsEndpoint/%23$ClubTag/members" { $Response.items }
         "$script:BrawlersEndpoint" { $Response.items }
         "$script:RankingsEndpoint*" {$Response.Items}
         Default { $Response }
      }

   }
}
Function Connect-BrawlStars {
   <#
.SYNOPSIS
   Establishes Connection to Brawl Stars API.
.DESCRIPTION
   Establishes Connection to Brawl Stars API.
.PARAMETER KeyFile
   Specifies the key file which contains the token needed for authentication against the endpoints.
      -Default KeyFile is set $Script:DefaultKeyFile, found in $Script:KeysDirectory
      -Default originally set in PSM1 File. if specified, this function overrides it
      -If you want to use a different token, specify entire path E.G.:
      C:\Users\username\Documents\MyLittleSecret.Secret
.EXAMPLE
   Connect-BrawlStars
 
   Connection Successful.
.NOTES
   https://developer.brawlstars.com/#/getting-started
#>


   [CmdletBinding()]
   Param (
      [String]$KeyFile = "$Script:DefaultKeyFile"
   )

   Process {
      If (Test-Path -Path $KeyFile) {
         Write-Verbose -Message "$Keyfile found..."
         Try {
            $Creds = Import-Clixml -Path $KeyFile
            $Token = $creds.Password | ConvertFrom-SecureString -AsPlainText -ErrorAction Stop
         } Catch [System.Security.Cryptography.CryptographicException] {
            Write-Error -Exception (System.Security.Cryptography.CryptographicException]::new("Unable to decrypt Credentials. Please generate new credentials file or specify a new KeyFile.")) -ErrorAction Stop
         }
      } Else {
         Write-Verbose -Message "`$KeyFile not specified. Trying to find another Key to use..."
         $Keys = Get-ChildItem -Path "$Script:KeysDirectory\*" -Include *.Secret
         If ($Keys) {
            Write-Verbose -Message "Found files, selecting most recently modified..."
            $KeyFile = $Keys | Sort-Object -Descending LastWriteTime | Select-Object -First 1
            Try {
               $Creds = Import-Clixml -Path $KeyFile -ErrorAction Stop
               $Token = $creds.Password | ConvertFrom-SecureString -AsPlainText
            } Catch [System.Security.Cryptography.CryptographicException] {
               Write-Error -Exception ([System.Security.Cryptography.CryptographicException]::new("Unable to decrypt Credentials. Please generate new credentials file or specify a new KeyFile.")) -ErrorAction Stop
            }
         } Else {
            throw "No valid key files found, please create one manually or generate one with New-BrawlKeyFile"
         }

      }
      $Script:headers = @{
         authorization = "Bearer $token"
      }
      $Response = Invoke-BrawlRequest -URI $script:BaseURI
      Write-Verbose -Message "$Reponse"

      If (-not ($Response.Reason)) {
         Write-Output "Connection Successful."
      }

   }
}
Function Get-BattleData {
   <#
.Synopsis
   Get Battle Summary Data of last 25 games of player
.DESCRIPTION
   Get Battle Summary Data of last 25 games of player
.PARAMETER PlayerTag
   PlayerTag of a user/player
.PARAMETER IgnoreDraws
   Ignore games that ended in a draw. This may mean the total count is less than 25.
.EXAMPLE
   Get-BattleData -PlayerTag 'YGU20CRG'
 
      Player : VitalShark
      PlayerTag : YGU20CRG
      Wins : 24
      RoyaleWins : 0
      StarPlayerTotal : 12
      StarPlayerPercentage : 50
      Loses : 1
      Draws : 0
      WinPercent : 96
      TotalRoyaleGames :
      TotalGames : 25
      Modes : {@{GamesPlayed=3; Name=bounty}, @{GamesPlayed=22; Name=brawlBall}}
.EXAMPLE
   Get-BattleData -PlayerTag '#YGU20CRG' -IgnoreDraws
 
.NOTES
   https://developer.brawlstars.com/#/documentation
#>

   [CmdletBinding()]
   Param (
      [Parameter(ValueFromPipeline)]
      [String]$PlayerTag,
      [Switch]$IgnoreDraws
   )
   Begin {
      #Need to be build a complete dataset for calculations
      $PlayerName = (Get-Player -PlayerTag $PlayerTag).name

      $response = Get-BattleLog -PlayerTag $PlayerTag

      [PSCustomObject]$ModesPlayed = $response.battle | Group-Object Mode | Select-Object @{ Name = 'GamesPlayed'; Expression = { $_.Count } }, Name
      $RoyaleGames = $Response.Battle | Where-Object { ($_.mode -like "*Showdown*") -or ($_.mode -like "*Lonestar*") -or ($_.mode -like "*Takedown*") }
      If ($RoyaleGames) {
         #Games where there can be more than 1 entity that wins. In this case, we calculate a win if there's a postive change in trophies
         $RoyaleCount = $RoyaleGames.Count
         $RoyaleWins = $Royalegames | Where-Object -Property trophychange -GT 0
         $RoyaleDraws = $Royalegames | Where-Object -Property trophychange -EQ 0
         $RoyaleLosses = $Royalegames | Where-Object -Property trophychange -LT 0
      }
      $TotalGames = $response.count
      $Results = $response.battle.result
      $NonRoyaleWinCount = ($Results | Where-Object { $_ -eq "victory" }).count
      $WinCount = $NonRoyaleWinCount + $RoyaleWins.count
      $LoseCount = ($Results | Where-Object { $_ -eq "Defeat" }).count + $RoyaleLosses.count
      $DrawCount = ($Results | Where-Object { $_ -eq "draw" }).count + $RoyaleDraws.count
      $StarPlayerCount = ($response.battle.starplayer.tag | Where-Object { $_ -match "$PlayerTag" }).count
   }
   Process {
      switch ($IgnoreDraws) {
         { $IgnoreDraws } {
            Write-Verbose -Message "Draws will be thrown out"
            If ($DrawCount) {
               $CalculatedGames = $TotalGames - $DrawCount
            }
            $Percentage = ($WinCount / $CalculatedGames) * 100
            # It's impossible to get Star Player when you lose, or if you are playing in a Royale so we focus on non royale wins
            $sTarPlayerPercentage = ($StarPlayerCount / $NonRoyaleWinCount) * 100
            $Final = [math]::Round($Percentage, 2)
            $StarPlayerFinal = [math]::Round($StarPlayerPercentage, 2)
            $Properties = [ordered]@{
               "Player"               = $PlayerName
               "PlayerTag"            = $PlayerTag
               "Wins"                 = $WinCount
               "RoyaleWins"           = $RoyaleWins.count
               "StarPlayerTotal"      = $StarPlayerCount
               "StarPlayerPercentage" = $StarPlayerFinal
               "Loses"                = $LoseCount
               "Draws"                = "Ignored"
               "WinPercent"           = $Final
               "TotalRoyaleGames"     = $RoyaleCount
               "TotalGames"           = $TotalGames
               "TotalGames(No Draws)" = $CalculatedGames
               "Modes"                = $ModesPlayed
            }
         }
         { (-not($IgnoreDraws)) } {
            If ($DrawCount) {
               Write-Verbose "Draws detected.."
               $Percentage = (($WinCount + (.5 * $DrawCount)) / $TotalGames) * 100
               $Final = [math]::Round($Percentage, 2)
            } Else {
               $Percentage = ($WinCount / $TotalGames) * 100
               $Final = [math]::Round($Percentage, 2)
            }
            $sTarPlayerPercentage = ($StarPlayerCount / ($NonRoyaleWinCount + $DrawCount)) * 100
            $StarPlayerFinal = [math]::Round($StarPlayerPercentage, 2)
            $Properties = [ordered]@{
               "Player"               = $PlayerName
               "PlayerTag"            = $PlayerTag
               "Wins"                 = $WinCount
               "RoyaleWins"           = $RoyaleWins.count
               "StarPlayerTotal"      = $StarPlayerCount
               "StarPlayerPercentage" = $StarPlayerFinal
               "Loses"                = $LoseCount
               "Draws"                = $DrawCount
               "WinPercent"           = $Final
               "TotalRoyaleGames"     = $RoyaleCount
               "TotalGames"           = $TotalGames
               "Modes"                = $ModesPlayed
            }
         }
      }
      New-Object -TypeName PSCustomObject -Property $Properties
   }
}
Function Get-BattleLog {
   <#
.Synopsis
   Get list of recent battle results for a player
.DESCRIPTION
   Get list of recent battle results for a player.
   If no recent matches have been played, may return a status 404
   NOTE: It may take up to 30 minutes for a new battle to appear in the battlelog
.PARAMETER PlayerTag
   Playertag
 
.PARAMETER URI
   uri of endpoint..
.EXAMPLE
   Get-BattleLog -PlayerTag 'YGU20CRG'
 
      battleTime event battle
      ---------- ----- ------
      20220602T062543.000Z @{id=0; map=} @{mode=bounty; type=friendly; result=victory; duration=120; starPlayer=; teams=System.Object[]}
      20220602T062245.000Z @{id=0; map=} @{mode=bounty; type=friendly; result=victory; duration=120; starPlayer=; teams=System.Object[]}
      20220602T061942.000Z @{id=0; map=} @{mode=bounty; type=friendly; result=defeat; duration=120; starPlayer=; teams=System.Object[]}
      20220602T061704.000Z @{id=15000531; mode=brawlBall; map=Extra Bouncy} @{mode=brawlBall; type=friendly; result=victory; duration=32; starPlayer=; teams=System.Object[]}
      20220530T052241.000Z @{id=15000132; mode=brawlBall; map=Center Stage} @{mode=brawlBall; type=ranked; result=victory; duration=118; trophyChange=3; starPlayer=; teams=System.Object[]}
      20220530T051448.000Z @{id=15000132; mode=brawlBall; map=Center Stage} @{mode=brawlBall; type=ranked; result=victory; duration=28; trophyChange=3; starPlayer=; teams=System.Object[]}
      20220530T050909.000Z @{id=15000132; mode=brawlBall; map=Center Stage} @{mode=brawlBall; type=ranked; result=victory; duration=65; trophyChange=3; starPlayer=; teams=System.Object[]}
      20220530T050218.000Z @{id=15000132; mode=brawlBall; map=Center Stage} @{mode=brawlBall; type=ranked; result=victory; duration=36; trophyChange=3; starPlayer=; teams=System.Object[]}
      20220530T045627.000Z @{id=15000132; mode=brawlBall; map=Center Stage} @{mode=brawlBall; type=ranked; result=victory; duration=90; trophyChange=3; starPlayer=; teams=System.Object[]}
      20220530T044741.000Z @{id=15000132; mode=brawlBall; map=Center Stage} @{mode=brawlBall; type=ranked; result=victory; duration=67; trophyChange=3; starPlayer=; teams=System.Object[]}
      20220530T044120.000Z @{id=15000132; mode=brawlBall; map=Center Stage} @{mode=brawlBall; type=ranked; result=victory; duration=45; trophyChange=3; starPlayer=; teams=System.Object[]}
      20220530T043503.000Z @{id=15000132; mode=brawlBall; map=Center Stage} @{mode=brawlBall; type=ranked; result=victory; duration=38; trophyChange=3; starPlayer=; teams=System.Object[]}
      20220530T042350.000Z @{id=15000132; mode=brawlBall; map=Center Stage} @{mode=brawlBall; type=ranked; result=victory; duration=120; trophyChange=3; starPlayer=; teams=System.Object[]}
      20220530T041903.000Z @{id=15000132; mode=brawlBall; map=Center Stage} @{mode=brawlBall; type=ranked; result=victory; duration=78; trophyChange=3; starPlayer=; teams=System.Object[]}
      20220530T041218.000Z @{id=15000132; mode=brawlBall; map=Center Stage} @{mode=brawlBall; type=ranked; result=victory; duration=67; trophyChange=3; starPlayer=; teams=System.Object[]}
      20220530T040459.000Z @{id=15000132; mode=brawlBall; map=Center Stage} @{mode=brawlBall; type=ranked; result=victory; duration=32; trophyChange=3; starPlayer=; teams=System.Object[]}
      20220530T035916.000Z @{id=15000132; mode=brawlBall; map=Center Stage} @{mode=brawlBall; type=ranked; result=victory; duration=150; trophyChange=3; starPlayer=; teams=System.Object[]}
      20220530T034611.000Z @{id=15000132; mode=brawlBall; map=Center Stage} @{mode=brawlBall; type=ranked; result=victory; duration=75; trophyChange=3; starPlayer=; teams=System.Object[]}
      20220530T034033.000Z @{id=15000132; mode=brawlBall; map=Center Stage} @{mode=brawlBall; type=ranked; result=victory; duration=106; trophyChange=3; starPlayer=; teams=System.Object[]}
      20220530T033405.000Z @{id=15000132; mode=brawlBall; map=Center Stage} @{mode=brawlBall; type=ranked; result=victory; duration=37; trophyChange=3; starPlayer=; teams=System.Object[]}
      20220530T032729.000Z @{id=15000132; mode=brawlBall; map=Center Stage} @{mode=brawlBall; type=ranked; result=victory; duration=88; trophyChange=3; starPlayer=; teams=System.Object[]}
      20220530T032226.000Z @{id=15000132; mode=brawlBall; map=Center Stage} @{mode=brawlBall; type=ranked; result=victory; duration=60; trophyChange=3; starPlayer=; teams=System.Object[]}
      20220530T031550.000Z @{id=15000132; mode=brawlBall; map=Center Stage} @{mode=brawlBall; type=ranked; result=victory; duration=63; trophyChange=3; starPlayer=; teams=System.Object[]}
      20220530T030021.000Z @{id=15000132; mode=brawlBall; map=Center Stage} @{mode=brawlBall; type=ranked; result=victory; duration=134; trophyChange=3; starPlayer=; teams=System.Object[]}
      20220530T025445.000Z @{id=15000132; mode=brawlBall; map=Center Stage} @{mode=brawlBall; type=ranked; result=victory; duration=85; trophyChange=3; starPlayer=; teams=System.Object[]}
.NOTES
   https://developer.brawlstars.com/#/documentation
 
 
#>


   [CmdletBinding()]
   Param (
      [Parameter(ValueFromPipeline)]
      [String]$PlayerTag,
      [uri]$Uri = "$Script:PlayersEndPoint/%23$PlayerTag/battlelog"
   )

   Process {
      #Tags are all upper case
      $PlayerTag = $PlayerTag.ToUpper()
      If ($PlayerTag -match "^#") {
         $PlayerTag = $PlayerTag -replace "^#", ""
         $Uri = "$Script:PlayersEndPoint/%23$PlayerTag/battlelog"
      }
      Write-Verbose "Player Tag is set to $PlayerTag"
      Invoke-BrawlRequest -uri $Uri
   }
}
Function Get-Brawler {
   <#
.Synopsis
   Get Brawler information
.DESCRIPTION
   Get-Brawler, simply put, returns brawler information.
.PARAMETER BrawlerID
   Part of the ParameterSet 'BrawlerID'.
.PARAMETER Name
   The Name of the Brawler.
   Part of the ParameterSet 'Name'.
   This will use a local index to determine the ID to pass to the endpoint.
.PARAMETER All
   Part of the default ParameterSet 'All'. If no parameters are passed and no pipeline data is pass, the all parameterset is used.
   This will return all brawlers from the /brawlers endpoint.
.EXAMPLE
   Get-brawler
 
      id name starPowers gadgets
      -- ---- ---------- -------
      16000000 SHELLY {@{id=23000076; name=SHELL SHOCK}, @{id=23000135; name=BAND-AID}} {@{id=23000255; name=FAST FORWARD}, @{id=23000288; name=CLAY PIGEONS}}
      16000001 COLT {@{id=23000077; name=SLICK BOOTS}, @{id=23000138; name=MAGNUM SPECIAL}} {@{id=23000273; name=SPEEDLOADER}, @{id=23000319; name=SILVER BULLET}}
      16000002 BULL {@{id=23000078; name=BERSERKER}, @{id=23000137; name=TOUGH GUY}} {@{id=23000272; name=T-BONE INJECTOR}, @{id=23000310; name=STOMPER}}
      16000003 BROCK {@{id=23000079; name=INCENDIARY}, @{id=23000150; name=ROCKET NO. 4}} {@{id=23000245; name=ROCKET LACES}, @{id=23000316; name=ROCKET FUEL}}
      16000004 RICO {@{id=23000080; name=SUPER BOUNCY}, @{id=23000156; name=ROBO RETREAT}} {@{id=23000246; name=MULTIBALL LAUNCHER}}
      16000005 SPIKE {@{id=23000081; name=FERTILIZE}, @{id=23000151; name=CURVEBALL}} {@{id=23000247; name=POPPING PINCUSHION}}
      16000006 BARLEY {@{id=23000082; name=MEDICAL USE}, @{id=23000158; name=EXTRA NOXIOUS}} {@{id=23000250; name=STICKY SYRUP MIXER}, @{id=23000293; name=HERBAL TONIC}}
      16000007 JESSIE {@{id=23000083; name=ENERGIZE}, @{id=23000149; name=SHOCKY}} {@{id=23000251; name=SPARK PLUG}, @{id=23000295; name=RECOIL SPRING}}
      16000008 NITA {@{id=23000084; name=BEAR WITH ME}, @{id=23000136; name=HYPER BEAR}} {@{id=23000249; name=BEAR PAWS}, @{id=23000314; name=FAUX FUR}}
      16000009 DYNAMIKE {@{id=23000085; name=DYNA-JUMP}, @{id=23000155; name=DEMOLITION}} {@{id=23000258; name=FIDGET SPINNER}, @{id=23000294; name=SATCHEL CHARGE}}
      16000010 EL PRIMO {@{id=23000086; name=EL FUEGO}, @{id=23000140; name=METEOR RUSH}} {@{id=23000264; name=SUPLEX SUPPLEMENT}, @{id=23000292; name=ASTEROID BELT}}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~Trimmed Response for Comment-based help~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.EXAMPLE
   Get-Brawler -BrawlerID "16000011"
            id name starPowers gadgets
            -- ---- ---------- -------
            16000011 MORTIS {@{id=23000087; name=CREEPY HARVEST}, @{id=23000154; name=COILED SNAKE}} {@{id=23000265; name=COMBO SPINNER}, @{id=23000290; name=SURVIVAL SHOVEL}}
.EXAMPLE
   Get-Brawler -Name Brock
            id name starPowers gadgets
            -- ---- ---------- -------
            16000003 BROCK {@{id=23000079; name=INCENDIARY}, @{id=23000150; name=ROCKET NO. 4}} {@{id=23000245; name=ROCKET LACES}, @{id=23000316; name=ROCKET FUEL}}
.EXAMPLE
   "El Primo" | Get-Brawler
            id name starPowers gadgets
            -- ---- ---------- -------
            16000010 EL PRIMO {@{id=23000086; name=EL FUEGO}, @{id=23000140; name=METEOR RUSH}} {@{id=23000264; name=SUPLEX SUPPLEMENT}, @{id=23000292; name=ASTEROID BELT}}
.NOTES
   https://developer.brawlstars.com/#/documentation
   GET /brawlers/{brawlerId}
   GET /brawlers
#>


   [CmdletBinding(DefaultParameterSetName = 'All')]
   Param (
      [Alias("ID", "Identifier")]
      [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'BrawlerID')]
      [String]$BrawlerID,

      [Alias("Brawler", "Hero")]
      [Parameter(ValueFromPipeline, ValueFromPipelineByPropertyName, ParameterSetName = 'Name', Position = 0)]
      [String]$Name,

      [Parameter(ParameterSetName = 'All')]
      [Switch]$All
   )


   Process {
      Write-Verbose -Message "ParameterSetName : $($PSCmdlet.ParameterSetName)"
      switch ($PSCmdlet.ParameterSetName) {

         BrawlerID {
            [Uri]$Uri = "$script:BrawlersEndpoint/$BrawlerID"
            Write-Verbose "Brawler ID is set to $BrawlerID"
            Invoke-BrawlRequest -URI $URI

         }

         Name {
            $Path = "$Script:moduleBase\bin\BrawlerIndex.csv"
            $BrawlerIndex = Import-Csv -Path $Path
            $BrawlerID = ($brawlerindex | Where-Object -Property Name -EQ $Name).id
            if ($BrawlerID) {
               [Uri]$Uri = "$script:BrawlersEndpoint/$BrawlerID"
               Invoke-BrawlRequest -URI $URI
            }
         }

         All {
            [Uri]$Uri = "$script:BrawlersEndpoint"
            Invoke-BrawlRequest -Uri $Uri
         }

      }
   }
}
Function Get-BrawlerRanking {
   <#
.Synopsis
   Get brawler rankings for a country or global ranking.
.DESCRIPTION
   Get brawler rankings for a country or global rankings. Brawler identifiers can be found by using the /v1/brawlers API endpoint.
.PARAMETER Limit
   Type: INT
   Default Value: 200
   Limit the number of items returned in the response. Valid value is between 1 and 200
.PARAMETER CountryCode
   Type: String
   Two letter country code
.PARAMETER Global
   Type: Switch
   This parameter forces checking the Global endpoint
 
.PARAMETER BrawlerID
   Type: INT
   Alias: ID
   Identifier of the brawler. Can be queried using Get-Brawler
 
.EXAMPLE
   Get-BrawlerRanking -BrawlerID 16000011 -Limit 1
 
      tag : #20288898G
      name : ☔︎LT|Rayy-Nex☔︎
      nameColor : 0xffffffff
      icon : @{id=28000014}
      trophies : 1270
      rank : 1
      club : @{name=ReVant}
 
.EXAMPLE
   Get-Brawler -Name Shelly | Get-BrawlerRanking
 
.EXAMPLE
   16000012 | Get-BrawlerRanking
 
.EXAMPLE
 
16000013 | Get-BrawlerRanking -Limit 1
 
.NOTES
   https://developer.brawlstars.com/#/documentation
   To do: This endpoint supports before and after query strings, need to add logic and parameter sets
   to support this scenario.
 
 
#>


   [CmdletBinding(DefaultParameterSetName = 'Global')]
   param (
      [Parameter(ParameterSetName = 'CountryCode')]
      [AllowNull()]
      [ValidatePattern('^[A-Za-z]{2}$')]
      [String]$CountryCode,

      [Parameter(ParameterSetName = 'Global')]
      [Parameter(ParameterSetName = 'CountryCode')]
      [ValidateRange(1, 200)]
      [int]$Limit = 200,

      [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName, ParameterSetName = 'Global')]
      [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName, ParameterSetName = 'CountryCode')]
      [Alias('ID')]
      [int]$BrawlerID,

      [Parameter(ParameterSetName = 'Global')]
      [Switch]$Global
   )

   process {
      if ($Global -or (-not ($CountryCode))) {
         Write-Verbose -Message "Running in global mode..."
         [Uri]$Uri = "$script:RankingsEndpoint/Global/brawlers/${brawlerid}?limit=$Limit"
      } else {
         Write-Verbose -Message "Running in country mode..."
         [Uri]$Uri = "$script:RankingsEndpoint/$CountryCode/${brawlerid}?limit=$limit"
      }

      Invoke-BrawlRequest -Uri $URI

   }
}
Function Get-Club {
   <#
.Synopsis
   Get Infomration about a single club
.DESCRIPTION
   Get information about a single club by tag. Club tags can be found in game.
 
.PARAMETER ClubTag
   Club tag
 
 
.EXAMPLE
   Get-Club -ClubTag "#2089L0PG2"
 
      tag : #2089L0PG2
      name : CODEMAGIC
      description : Clan OFFICIEL de CODEMAGIC.web : codemagic.fr 🧙‍♂️ twitter : @_CODEMAGIC_ 🧙‍♂️
                        discord.gg/codemagic 🧙‍♂️youtube.com/codemagic
      type : closed
      badgeId : 8000025
      requiredTrophies : 25000
      trophies : 0
      members : {}
 
.NOTES
   GET /clubs/{clubTag}
 
#>


   [CmdletBinding()]
   Param (
      [Parameter(Mandatory)][ValidatePattern('[#0289PYLQGRJCUV]')]
      [String]$ClubTag
   )
   Begin {
      [Uri]$Uri = "$script:ClubsEndpoint/%23$ClubTag"
   }
   Process {
      #Club tags start with hash character '#' and that needs to be URL-encoded properly to work in URL, so for example clan tag '#ABC' would become '%232ABC' in the URL.
      If ($ClubTag -match "^#") {
         $ClubTag = $ClubTag -replace "^#", ""
         $Uri = "$script:ClubsEndpoint/%23$ClubTag"
      }
      Write-Verbose "Player Tag is set to $ClubTag"
      Invoke-BrawlRequest -Uri $URI
   }
}
Function Get-ClubMember {
   <#
.Synopsis
   List Club Members
.DESCRIPTION
   This function gets a list of club members.
.EXAMPLE
   Get-ClubMember -ClubTag "208UU822P" | Select-Object -First 2
 
      tag : #90QUR2QY
      name : TQ | RaaFa
      nameColor : 0xfff9c908
      role : member
      trophies : 54485
      icon : @{id=28000038}
 
      tag : #VGQ0P2VU
      name : Uncle Carry
      nameColor : 0xff1ba5f5
      role : president
      trophies : 51279
      icon : @{id=28000011}
 
 
.PARAMETER ClubTag
   tag of club
.NOTES
   https://developer.brawlstars.com/#/documentation
 
 
#>


   [CmdletBinding()]
   Param (
      [Parameter(ValueFromPipeline, Mandatory)]
      [String]$ClubTag
   )
   Begin {
      [Uri]$Uri = "$script:ClubsEndpoint/%23$ClubTag/members"
   }
   Process {
      If ($ClubTag -match "^#") {
         $ClubTag = $ClubTag -replace "^#", ""
         $Uri = "$script:ClubsEndpoint/%23$ClubTag/members"
      }
      Write-Verbose "Player Tag is set to $ClubTag"
      Invoke-BrawlRequest -uri $Uri

   }
}
Function Get-ClubRanking {
   <#
.Synopsis
   Get information about a single player by player tag
.DESCRIPTION
   Get information about a single player by player tag
.PARAMETER CountryCode
   Type: String
   Two letter country code
 
.PARAMETER limit
   Type: INT
   Default Value: 200
   Limit the number of items returned in the response. Valid value is between 1 and 200
 
.PARAMETER global
   Global endpoint
.EXAMPLE
   Get-ClubRanking
      tag : #2GUU9908V
      name : BC*|Family*
      badgeId : 8000006
      trophies : 1504063
      rank : 1
      memberCount : 30
 
      tag : #P8PV2YU9
      name : Game.Tv ITA
      badgeId : 8000055
      trophies : 1474250
      rank : 2
      memberCount : 30
.NOTES
   https://developer.brawlstars.com/#/documentation
 
 
#>


   [CmdletBinding()]
   Param (
      [Parameter(ValueFromPipeline)][AllowNull()][ValidatePattern('[A-Za-z]')]
      [String]$CountryCode,
      [ValidateRange(1, 200)]
      [int]$Limit = 200,
      [Switch]$Global
   )
   Begin {
      [Uri]$Uri = "$script:RankingsEndpoint/Global/clubs?limit=$Limit"
   }
   Process {
      if ($Global -or (-not ($CountryCode))) {
         Write-Verbose -Message "Global Switch detected or country code is null, setting search to Global..."
         Invoke-BrawlRequest -Uri $URI
      } else {
         [Uri]$Uri = "$script:RankingsEndpoint/$CountryCode/clubs?limit=$limit"
         Invoke-BrawlRequest -Uri $URI
      }
   }
}
Function Get-Player {
   <#
.Synopsis
   Get information about a single player by player tag
.DESCRIPTION
   Get information about a single player by player tag
.PARAMETER PlayerTag
   Player tag
.Parameter BestBrawler
   Type: Switch
   outputs the brawler with the highest trophy this player has.
.EXAMPLE
   Get-Player -PlayerTag '#C08UC2C'
 
      tag : #C08UC2C
      name : Imagine
      nameColor : 0xffffce89
      icon : @{id=28000000}
      trophies : 27802
      highestTrophies : 38103
      highestPowerPlayPoints : 1123
      expLevel : 264
      expPoints : 357018
      isQualifiedFromChampionshipChallenge : False
      3vs3Victories : 20896
      soloVictories : 569
      duoVictories : 2744
      bestRoboRumbleTime : 5
      bestTimeAsBigBrawler : 0
      club :
      brawlers : {@{id=16000000; name=SHELLY; power=9; rank=26; trophies=574;
                                             highestTrophies=807; gears=System.Object[]; starPowers=System.Object[];
                                             gadgets=System.Object[]}, @{id=16000001; name=COLT; power=9; rank=27;
                                             trophies=524; highestTrophies=874; gears=System.Object[];
                                             starPowers=System.Object[]; gadgets=System.Object[]}, @{id=16000002; name=BULL;
                                             power=9; rank=27; trophies=549; highestTrophies=858; gears=System.Object[];
                                             starPowers=System.Object[]; gadgets=System.Object[]}, @{id=16000003;
                                             name=BROCK; power=9; rank=30; trophies=574; highestTrophies=1039;
                                             gears=System.Object[]; starPowers=System.Object[]; gadgets=System.Object[]}…}
.EXAMPLE
   Get-Player -PlayerTag C08UC2C -BestBrawler
 
      tag : #C08UC2C
      name : Imagine
      nameColor : 0xffffce89
      icon : @{id=28000000}
      trophies : 27802
      highestTrophies : 38103
      highestPowerPlayPoints : 1123
      expLevel : 264
      expPoints : 357018
      isQualifiedFromChampionshipChallenge : False
      3vs3Victories : 20896
      soloVictories : 569
      duoVictories : 2744
      bestRoboRumbleTime : 5
      bestTimeAsBigBrawler : 0
      club :
      brawlers : {@{id=16000000; name=SHELLY; power=9; rank=26; trophies=574;
                                             highestTrophies=807; gears=System.Object[]; starPowers=System.Object[];
                                             gadgets=System.Object[]}, @{id=16000001; name=COLT; power=9; rank=27;
                                             trophies=524; highestTrophies=874; gears=System.Object[];
                                             starPowers=System.Object[]; gadgets=System.Object[]}, @{id=16000002; name=BULL;
                                             power=9; rank=27; trophies=549; highestTrophies=858; gears=System.Object[];
                                             starPowers=System.Object[]; gadgets=System.Object[]}, @{id=16000003;
                                             name=BROCK; power=9; rank=30; trophies=574; highestTrophies=1039;
                                             gears=System.Object[]; starPowers=System.Object[]; gadgets=System.Object[]}…}
      bestbrawler : @{id=16000003; name=BROCK; power=9; rank=30; trophies=574;
                                             highestTrophies=1039; gears=System.Object[]; starPowers=System.Object[];
                                             gadgets=System.Object[]}
.NOTES
   https://developer.brawlstars.com/#/documentation
 
 
#>


   [CmdletBinding(DefaultParameterSetName = 'PlayerTag')]
   param (
      [Parameter(ValueFromPipeline, ParameterSetName = 'PlayerTag')][ValidatePattern('[#0289PYLQGRJCUV]')]
      [String]$PlayerTag,
      [Switch]$BestBrawler
   )
   begin {
      $URI = "$Script:PlayersEndPoint/%23$PlayerTag"
   }
   process {
      Write-Verbose -Message "Get-Player : ParameterSet is set to $($PSCmdlet.ParameterSetName)"
      switch ($PSCmdlet.ParameterSetName) {
         PlayerTag {

               #Tags are all upper case
               $PlayerTag = $PlayerTag.ToUpper()
               If ($PlayerTag -match "^#") {
                  $PlayerTag = $PlayerTag -replace "^#", ""
                  $URI = "$Script:PlayersEndPoint/%23$PlayerTag"
               }
               Write-Verbose -Message "Player Tag is set to $PlayerTag"
               $Response = Invoke-BrawlRequest -URI $URI
               If ($BestBrawler) {
                  $Maximum = ($Response.brawlers.highesttrophies | Measure-Object -Maximum).Maximum
                  $HighestBrawler = $Response.Brawlers | Where-Object { $_.HighestTrophies -eq $Maximum }
                  $Response | Add-Member -NotePropertyName bestbrawler -NotePropertyValue $HighestBrawler
               }
               $Response
         }
      }
   }
}
Function Get-PlayerRanking {
   <#
.Synopsis
   Get player rankings for a country or global rankings.
 
.DESCRIPTION
   Get player rankings for a country or global rankings.
 
.PARAMETER CountryCode
   Type: String
   Two letter country code
 
.PARAMETER limit
   Type: INT
   Default Value: 200
   Limit the number of items returned in the response. Valid value is between 1 and 200
 
.PARAMETER Global
   Switches context to global endpoint.
 
.EXAMPLE
   Get-PlayerRanking -Limit 1
 
      tag : #YGU20CRG
      name : VitalShark
      nameColor : 0xffcb5aff
      icon : @{id=28000029}
      trophies : 64534
      rank : 1
      club : @{name=<c1>MERCY</c>}
 
.NOTES
   https://developer.brawlstars.com/#/documentation
 
 
#>


   [CmdletBinding()]
   Param (
      [Parameter()]
      [AllowNull()]
      [ValidatePattern('^[A-Za-z]{2}$')]
      [String]$CountryCode,

      [ValidateRange(1, 200)]
      [int]$Limit = 200,

      [Switch]$Global
   )
   Begin {
      [Uri]$Uri = "$script:RankingsEndpoint/Global/players?limit=$Limit"
   }
   Process {
      if ($Global -or (-not ($CountryCode))) {
         Write-Verbose -Message "Global Switch detected or country code is null, setting search to Global..."
         Invoke-BrawlRequest -Uri $URI
      } else {
         [Uri]$Uri = "$script:BaseUri/$script:RankingsEndpoint/$CountryCode/players?limit=$limit"
         Invoke-BrawlRequest -Uri $URI
      }
   }
}
Function Get-PowerPlayRanking {
   <#
.Synopsis
   Coming Soon
.DESCRIPTION
   Coming Soon
 
.PARAMETER CountryCode
   Type: String
   Two letter country code
 
.PARAMETER limit
   Type: INT
   Default Value: 200
   Limit the number of items returned in the response. Valid value is between 1 and 200
 
.PARAMETER SeasonID
   ID of Season, found by running Get-PowerPlaySeasonID
 
.PARAMETER URI
   Probably not needed but URI of endpoint
 
.EXAMPLE
   Get-PowerPlayRanking
 
   Coming Soon
.NOTES
   https://developer.brawlstars.com/#/documentation
   GET /rankings/{countryCode}/powerplay/seasons/{seasonId}
 
#>


   [CmdletBinding()]
   Param (
      [Parameter(ValueFromPipeline)][AllowNull()][ValidatePattern('^[A-Za-z]{2}$|^Global$')]
      [String]$CountryCode,
      [ValidateRange(1, 200)]
      [int]$Limit = 200,
      [Uri]$Uri = "$script:BaseUri/$script:RankingsEndpoint/Global/powerplay/seasons?limit=$Limit",
      [String]$SeasonID = "latest"
   )
   Process {
      Write-Host "Coming Soon, this function isn't implemented yet!"
   }
}
Function Get-PowerPlaySeasonID {
   <#
.Synopsis
   Get information about a single player by player tag
.DESCRIPTION
   Get information about a single player by player tag
 
.PARAMETER CountryCode
   Type: String
   Two letter country code or 'GLOBAL' to query global endpoint
 
 
.PARAMETER LIMIT
   Type: INT
   Default Value: 200
   Limit the number of items returned in the response. Valid value is between 1 and 200
.PARAMETER Date
   Date to query in MM-dd-yyyy OR M-d-yyyy format.
 
.EXAMPLE
   Get-PowerPlaySeasonID -Limit 5
 
      id startDate endDate
      -- --------- -------
      56 10-28-2019 11-11-2019
      57 11-11-2019 11-25-2019
      58 11-25-2019 12-09-2019
      59 12-09-2019 12-23-2019
      60 12-23-2019 01-06-2020
 
.EXAMPLE
   Get-PowerPlaySeasonID -CountryCode US -Date 6-2-2022
 
      id startDate endDate
      -- --------- -------
      123 05-23-2022 06-06-2022
.NOTES
   https://developer.brawlstars.com/#/documentation
#>

   [CmdletBinding()]
   Param (
      [Parameter(ValueFromPipeline)][AllowNull()][ValidatePattern('^[A-Za-z]{2}$|^Global$')]
      [String]$CountryCode,
      [ValidateRange(1, 200)]
      [int]$Limit = 200,
      [String]$Date
   )
   Begin {
      [Uri]$Uri = "$script:RankingsEndpoint/Global/powerplay/seasons?limit=$Limit"

      $pattern = '^(?<Year>\d{4})(?<Month>\d{2})(?<Day>\d{2}T\d{2})(?<Hour>\d{2})(?<Minute>\d{2})(?<Seconds>\.\d{3}Z)$'
      If ($Date) {
         If ($Date -notmatch '\d{1,2}-\d{1,2}-\d{4}') {
            Throw "`$Date value is not in the correct format. Please use the following formats: MM-dd-yyyy OR M-d-yyyy."
         }
         if (-not(Test-PowerPlayDate $Date)) {
            Write-Warning -Message "Date provided in `$Date variable is outside range of available dates. All SeasonIds within the given limit will be returned..."
            Remove-Variable -Name Date -Force
         }
      }
      $replacement = '${Year}-${Month}-${Day}:${Hour}:${Minute}${Seconds}'
   }
   Process {
      if ($Global -or (-not($CountryCode))) {
         Write-Verbose -Message "Global Switch detected or country code is null, setting search to Global..."
         Try {
            $Response = Invoke-BrawlRequest -Uri $URI
            ForEach ($Record in $Response) {
               $Record.startTime = Get-Date ($Record.startTime -replace $pattern, $replacement) -Format "MM-dd-yyyy"
               $Record.endTime = Get-Date ($Record.endTime -replace $pattern, $replacement) -Format "MM-dd-yyyy"
            }
         } catch {
         }
      } else {
         Try {
            [Uri]$Uri = "$script:RankingsEndpoint/$CountryCode/powerplay/seasons?limit=$limit"
            $Response = Invoke-BrawlRequest -Uri $URI
            ForEach ($Record in $Response) {
               $Record.startTime = Get-Date ($Record.startTime -replace $pattern, $replacement) -Format "MM-dd-yyyy"
               $Record.endTime = Get-Date ($Record.endTime -replace $pattern, $replacement) -Format "MM-dd-yyyy"
            }
         } catch {
         }
      }
      $object = $Response | Select-Object id, @{Name = "startDate"; Expression = { $_.Starttime } }, @{Name = "endDate"; Expression = { $_.Endtime } }
      if ($Date) {
         Write-Verbose "Date set to : [DateTime]$Date"
         $object | Where-Object { ([DateTime]$Date -ge [DateTime]$_.StartDate) -and ([DateTime]$Date -le [DateTime]$_.endDate) }
      } Else {
         $Object
      }
   }
}
Function New-BrawlKeyFile {
    <#
        .SYNOPSIS
            Creates a key file
        .DESCRIPTION
            Creates a key file. This is used to authenticate to the brawl stars API
            Under the hood it uses Export-CLIXML to utilize windows dpapi encryption.
 
        .PARAMETER Secretname
            Name of file
        .PARAMETER Directory
            directory to store file in
        .EXAMPLE
        New-BrawlKeyFile
 
            File
            ----
            Test.Secret
        .NOTES
            Should make this a little cleaner.
 
    #>


    [CmdletBinding()]
    Param (
        $Directory = "$Script:KeysDirectory",
        [String]$SecretName = "Default.Secret"
    )

    Process {
        $Credential = Get-Credential -Message "Enter API Key" -UserName "API"


        If (-not (Test-Path -Path "$Directory\$SecretName")) {
            $Credential | Export-Clixml -Path "$Directory\$SecretName" | Out-Null
        } Else {
            Write-Warning -Message "File $SecretName already exists in $Directory... setting unique name"
            $SecretName = "Key-$(Get-Date -Format FileDateTime).Secret"
            Write-Host "File name Set to $SecretName"
            $Credential | Export-Clixml -Path "$Script:KeysDirectory\$SecretName" | Out-Null
        }
        $CredentialInfo = [PSCustomObject][ordered]@{
            "File" = $SecretName
        }
        $CredentialInfo
    }
}
Function Test-PowerPlayDate {
   <#
   .Synopsis
      Tests to ensure the dates being checked is between the proper dates for powerplay
   .DESCRIPTION
      Tests to ensure the dates being checked is between the proper dates for powerplay
   .PARAMETER Date
      Date to query
   .EXAMPLE
      Test-PowerPlayDate -Date "$((Get-Date).date)"
 
         True
   .EXAMPLE
      Test-PowerPlayDate -Date 10/30/2019
 
      True
   .EXAMPLE
      Test-PowerPlayDate -Date 10/01/2018
 
      False
   #>


   [CmdletBinding()]
   Param (
      [Parameter(ValueFromPipeline)]
      $Date
   )
   Begin {
      $ScriptDate = (Get-Date $Date).Date
      $BeginDate = (Get-Date "10-28-2019").Date
      $EndDate = (Get-Date -Date ((Get-PowerPlaySeasonID)[-1].endDate)).Date
   }
   Process {
      #Write-Host "ScriptDate:$ScriptDate`nBeginDate:$BeginDate`nEndDate:$EndDate"
      if (($ScriptDate -ge $BeginDate) -and ($ScriptDate -le $EndDate)) {
         $true
      } else {
         $false
      }
   }
}
#Module base and region files
$Script:moduleBase = (Resolve-Path -Path "$PSScriptRoot\..").Path
$Script:Regions = "$Script:moduleBase\bin\regions.csv"

#API Endpoints
$script:baseUri = "https://api.brawlstars.com/v1"
$script:PlayersEndPoint = "$script:baseUri/players"
$script:ClubsEndpoint = "$script:baseUri/clubs"
$script:RankingsEndpoint = "$script:baseUri/rankings"
$script:BrawlersEndpoint = "$script:baseUri/brawlers"

# Config
$Script:ConfigBase = "$env:USERPROFILE\documents\.posh-brawl"
$Script:KeysDirectory = "$Script:ConfigBase\Keys"
$Script:DefaultKeyFile = "$Script:KeysDirectory\Default.secret"
$Script:ConfigDirectory = "$Script:ConfigBase\Configs"
$Script:DefaultConfig = "$Script:ConfigBase\Configs\Default.json"


# Module Variables

Resolve-Path "$PSScriptRoot\Variables\*.ps1" -ErrorAction Ignore | ForEach-Object { . $_.ProviderPath }


# Module Functions

Resolve-Path "$PSScriptRoot\Functions\Public\*.ps1" -ErrorAction Ignore | ForEach-Object{ . $_.ProviderPath }
Resolve-Path "$PSScriptRoot\Functions\Private\*.ps1" -ErrorAction Ignore | ForEach-Object{ . $_.ProviderPath }

#Auto Completers

Resolve-Path "$PSScriptRoot\ArgumentCompleters\*.ps1" -ErrorAction Ignore | ForEach-Object{ . $_.ProviderPath }




$WarningMessage = @"
No credential files found in Config directory : $Script:ConfigBase\{0}. Please run New-BrawlConfig
to generate credentials, or manually do so using Get-Credential. The Variable must be saved
by using Export-CLIXML to the Keys directory with a .SECRET extension. You can also manually specify a credential when using
Connect-BrawlStars
"@
 -f "Keys"


if (-not (Test-Path $Script:KeysDirectory)) {
    Write-Warning -Message "Creating Keys directory..."
    New-Item -Name Keys -ItemType Directory -Path $Script:ConfigBase -Force
}
if (-not (Test-Path "$Script:ConfigDirectory")) {
    Write-Warning -Message "Creating Configs directory..."
    New-Item -Name Configs -ItemType Directory -Path $Script:ConfigBase -Force
}

If (-not (Get-ChildItem -Path $Script:KeysDirectory\* -Include *.Secret -ErrorAction SilentlyContinue)) {
    Write-Warning -Message $WarningMessage
}