Skip to content

Instantly share code, notes, and snippets.

@p0w3rsh3ll
Created May 11, 2013 16:18
Show Gist options
  • Save p0w3rsh3ll/5560438 to your computer and use it in GitHub Desktop.
Save p0w3rsh3ll/5560438 to your computer and use it in GitHub Desktop.
#Requires -Version 3
Function Get-PSH2013Learder {
<#
.SYNOPSIS
Get the Scripting Games 2013 leaderboard of a given category
.DESCRIPTION
Just for fun.
.PARAMETER Category
The full name of the category you're looking for.
.PARAMETER EventNumber
Integer that represents the event number.
.PARAMETER Repeat
Switch to go in an infinite loop.
.PARAMETER EveryMinutes
When executed in loop mode, it allows you to set how often the display is "refreshed".
By default, it refreshes every 10 minutes.
.PARAMETER UserName
Your full user name. It will be added to the Top users list and show your rank and how much you scored.
.PARAMETER TopUsers
Integer that allows you how many top users are included in the output.
.EXAMPLE
Get-PSH2013Learder -Category Beginner -EventNumber 1 -UserName "ScriptingWife"
Retrieve the top 10 users in the Beginner category for Event 1 and include ScriptingWife regardless her current position
.EXAMPLE
Get-PSH2013Learder -Category Advanced -EventNumber 1 -TopUsers 5| Select Rank,Name,Score | ft -AutoSize
Retrieve the top 5 users in the Advanced category
.EXAMPLE
Get-PSH2013Learder -Category Advanced -EventNumber 1 -UserName "_Emin_" -EveryMinutes 60 -Repeat
Retrieve the Top 10 users in the Advanced category, include my username and refresh the list every hour
#>
[CmdletBinding(DefaultParameterSetName='All')]
Param(
[Parameter(Mandatory)]
[Alias('Track')]
[ValidateSet('Advanced','Beginner')]
[string]$Category,
[Parameter(Mandatory)]
[ValidateRange(1,6)]
[int]$EventNumber,
[Parameter(ParameterSetName='Loop',Mandatory)]
[switch]$Repeat,
[Parameter(ParameterSetName='Loop')]
[ValidateRange(2,1140)]
[int]$EveryMinutes=10,
[Parameter()]
[string]$UserName,
[Parameter()]
[ValidateRange(1,10)]
[Int]$TopUsers = 10
)
Begin{
}
Process {
if ($PSCmdlet.ParameterSetName -eq 'Loop') {
Write-Verbose -Message ('Execution time: {0}' -f (Get-Date).ToLongTimeString()) -Verbose
Write-Verbose -Message ('Event {0}: {1}' -f $EventNumber,$(($SG2013Calendar | Where EventID -eq $EventNumber).Title)) -Verbose
Write-Verbose -Message ('Event Closed?: {0}' -f (Test-isEventClosed -ID $EventNumber)) -Verbose
# Call the function once, i.e., without the EveryMinutes parameters
$PSBoundParameters.Remove('EveryMinutes') | Out-Null
$PSBoundParameters.Remove('Repeat') | Out-Null
Get-PSH2013Learder @PSBoundParameters
# Wait
Write-Verbose -Message ('Waiting {0} minutes, next refresh at {1}' -f $EveryMinutes,(Get-Date).AddMinutes($EveryMinutes).ToLongTimeString()) -Verbose
Start-Sleep -Seconds ($EveryMinutes*60)
Clear-Host
# Call the function recursively
Get-PSH2013Learder @PSBoundParameters -Repeat:$true -EveryMinutes $EveryMinutes
} else {
if (Test-isEventClosed -ID $EventNumber) {
Write-Verbose -Message "Event $($EventNumber): $(($SG2013Calendar|Where EventID -eq $EventNumber).Title) closed on $(($SG2013Calendar|Where EventID -eq $EventNumber).VotingEnd)"
if ($UserName) {
$LeaderBoard = Get-SlowLeader -EventNumber $EventNumber -Category $Category
$LeaderBoard | Sort-Object -Descending:$false -Property Rank | Select -First $TopUsers
$LeaderBoard | Where-Object { $_.Name -eq "$UserName"}
} else {
Get-QuickLeader -EventNumber $EventNumber -Category $Category -TopUsers $TopUsers
}
} else {
Get-QuickLeader -EventNumber $EventNumber -Category $Category -TopUsers $TopUsers
if ($UserName) {
Get-QuickLeader -EventNumber $EventNumber -Category $Category | Where Name -eq $UserName
}
}
}
}
End {}
}
# Courtesy of Tobias Weltner
# http://powershell.com/cs/blogs/tobias/archive/2011/10/27/regular-expressions-are-your-friend-part-1.aspx
Function Get-Matches {
param(
[Parameter(Mandatory=$true)]
$Pattern,
[Parameter(ValueFromPipeline=$true)]
$InputObject
)
begin {
try {
$regex = New-Object Regex($pattern)
}
catch {
Throw "Get-Matches: Pattern not correct. '$Pattern' is no valid regular expression."
}
$groups = @($regex.GetGroupNames() |
Where-Object { ($_ -as [Int32]) -eq $null } |
ForEach-Object { $_.toString() })
}
process {
foreach ($line in $InputObject) {
foreach ($match in ($regex.Matches($line))) {
if ($groups.Count -eq 0) {
([Object[]]$match.Groups)[-1].Value
} else {
$rv = 1 | Select-Object -Property $groups
$groups | ForEach-Object {
$rv.$_ = $match.Groups[$_].Value
}
$rv
}
}
}
}
}
# Function used to read closed entries
Function Get-SlowLeader {
[CmdletBinding()]
Param(
$EventNumber,
$Category
)
Begin {
$ok = $true
# Build a local path
$LocalPath = Join-Path -Path $env:temp -ChildPath "SG2013\$($Category)\$($EventNumber)"
if (-not(Test-Path -Path $LocalPath -PathType Container)) {
# Create the folder
try {
New-Item -Path $LocalPath -ItemType Directory -Force -ErrorAction Stop | Out-Null
Write-Verbose -Message "Creating folder $LocalPath"
} Catch {
Write-Warning -Message "Failed to create directory $LocalPath because $($_.Exception.Message)"
$ok = $false
}
}
# Check if download is required
$root= "http://scriptinggames.org/entries/2013/Summer"
$mainpage = Invoke-WebRequest -URI "$root/$Category/$EventNumber/"
if ($ok) {
if ((Get-ChildItem -Path $LocalPath -Filter "*.txt").Count -gt 0) {
Write-Verbose -Message "Items for event $($Category)\$($EventNumber) already downloaded"
} else {
$Count = 0
$mainpage.Links.href | Where-Object { $_ -match "\.txt" } |
ForEach-Object -Process {
Write-Progress -Activity "Reading files for event $($EventNumber): $(($SG2013Calendar |
Where EventID -eq $EventNumber).Title)" -Status $Count -PercentComplete ($Count/($mainpage.Links.href).Count*100)
$Count++
try {
$entry = (Invoke-WebRequest -URI "$root/$Category/$EventNumber/$_" -ErrorAction Stop)
$Content = $entry.Content
# Save the content locally
Set-Content -Path (Join-Path -Path $LocalPath -ChildPath $_) -Value $($entry.Content) -ErrorAction Stop
} catch {
Write-Warning -Message "Failed to download entry $count because $($_.Exception.Message)"
$ok = $false
}
}
}
}
# Initialize variables for process block
$allusers = @()
}
Process {
if ($ok) {
Get-ChildItem -Path $LocalPath -Filter "*.txt" | ForEach-Object {
$Content = Get-Content $_.FullName -ReadCount 0
$extractedValues = @($Content -split ([char]10)) |
Get-Matches "^TOTAL\sSCORE\sWAS\s(?<Points>\d{1,4})\sFROM\s(?<Votes>\d{1,4})\sTOTAL\sVOTES"
$allusers += New-Object -TypeName psobject -Property @{
Name = ($_ -split "\.")[0]
Score = '{0:N4}' -f ($extractedValues.Points/$extractedValues.Votes)
Votes = $extractedValues.Votes
}
}
# Add a rank property
$i=0
$allusers | Sort-Object -Descending -Property Score | ForEach-Object {
$i++
$_ | Add-Member -MemberType NoteProperty -Name Rank -Value $i
}
# Output
$allusers # | Sort-Object -Descending:$false -Property Rank | Select -First 11
}
}
End {}
}
# This is the calendar of the Scripting Games 2013
$SG2013Calendar = @(
@{
EventID = 1 ; Title = "An Archival Atrocity" ;
Start = ([datetime]'2013-04-23 00:00:00').ToLocalTime() ;
End = ([datetime]'2013-04-30 00:00:00').ToLocalTime() ;
VotingEnd = ([datetime]'2013-05-07 00:00:00').ToLocalTime()
},
@{
EventID = 2 ; Title = "An Inventory Intervention" ;
Start = ([datetime]'2013-05-02 00:00:00').ToLocalTime() ;
End = ([datetime]'2013-05-07 00:00:00').ToLocalTime() ;
VotingEnd = ([datetime]'2013-05-14 00:00:00').ToLocalTime()
},
@{
EventID = 3 ; Title = "A Disk Decision" ;
Start = ([datetime]'2013-05-09 00:00:00').ToLocalTime() ;
End = ([datetime]'2013-05-14 00:00:00').ToLocalTime() ;
VotingEnd = ([datetime]'2013-05-21 00:00:00').ToLocalTime()
},
@{
EventID = 4 ; Title = "An Auditing Adventure" ;
Start = ([datetime]'2013-05-16 00:00:00').ToLocalTime() ;
End = ([datetime]'2013-05-21 00:00:00').ToLocalTime() ;
VotingEnd = ([datetime]'2013-05-28 00:00:00').ToLocalTime()
},
@{
EventID = 5 ; Title = "The Logfile Labyrinth" ;
Start = ([datetime]'2013-05-23 00:00:00').ToLocalTime() ;
End = ([datetime]'2013-05-28 00:00:00').ToLocalTime() ;
VotingEnd = ([datetime]'2013-05-04 00:00:00').ToLocalTime()
},
@{
EventID = 6 ; Title = "The Core Configurator" ;
Start = ([datetime]'2013-05-30 00:00:00').ToLocalTime() ;
End = ([datetime]'2013-06-04 00:00:00').ToLocalTime() ;
VotingEnd = ([datetime]'2013-06-11 00:00:00').ToLocalTime() ;
}
)
# Uses the above calendar
Function Test-isEventClosed {
Param($ID)
Process {
if ((Get-Date) -gt ($SG2013Calendar | Where EventID -eq $ID).VotingEnd){
$true
} else {
$false
}
}
}
# Function to read the top 10 online leaderboard's page
Function Get-QuickLeader {
[CmdletBinding()]
Param(
$EventNumber,
$Category,
[int]$TopUsers=10
)
Begin {}
Process {
# Get the top 10 users from page
try {
$URI = 'http://scriptinggames.org/serverside-leaderboard.php'
$page = (Invoke-WebRequest -Uri $URI -ErrorAction Stop).AllElements[0].OuterText -split "`n"
} catch {
Write-Warning "Failed to read the web page"
break
}
# Calculate the line number where the "caterory eventnumber" keyword is located
# Using select-string as there's only 1 match
$StartingLine = ($page | Select-String -Pattern "^$($Category)\s[$EventNumber]").LineNumber
Write-Verbose "Found category $Category at line $StartingLine"
# Was using a Select-String previously that could have 0, 1 or mores matches
# NEW: using a while loop to look for participant
# Could also have used a Select-String with -Context parameter on previous command $StartingLine = ...
$arusers = @()
$i = 0
while ($true) {
if (@($page)[$StartingLine+$i] -notmatch "^$([char]8226)\s") {
# If the line isn't a participant, we exit the loop
break
} else {
# Store a participant object in the array
$arusers += @($page)[$StartingLine+$i]| ForEach-Object -Process {
New-Object -TypeName PSObject -Property @{
Rank = $i+1
Name = (($_ -split ",")[0] -replace ([char]8226),"").Trim()
Score = ($_ -split ", scoring")[1].Trim()
}
}
# Increase counter
$i++
}
}
$arusers | Select -First $TopUsers
}
End {}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment