Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
365 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
<?xml version="1.0" encoding="UTF-16"?> | ||
<Task version="1.4" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task"> | ||
<RegistrationInfo> | ||
<Date>2018-08-20T10:39:18.2757495</Date> | ||
<Description>Runs watcher script to detect new games and upload data</Description> | ||
<URI>\HeroesShareWatcher</URI> | ||
</RegistrationInfo> | ||
<Triggers> | ||
<CalendarTrigger> | ||
<Repetition> | ||
<Interval>PT5M</Interval> | ||
<StopAtDurationEnd>false</StopAtDurationEnd> | ||
</Repetition> | ||
<StartBoundary>2018-08-08T10:00:00</StartBoundary> | ||
<Enabled>true</Enabled> | ||
<ScheduleByDay> | ||
<DaysInterval>1</DaysInterval> | ||
</ScheduleByDay> | ||
</CalendarTrigger> | ||
</Triggers> | ||
<Principals> | ||
<Principal id="Author"> | ||
<LogonType>Password</LogonType> | ||
<RunLevel>LeastPrivilege</RunLevel> | ||
</Principal> | ||
</Principals> | ||
<Settings> | ||
<MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy> | ||
<DisallowStartIfOnBatteries>false</DisallowStartIfOnBatteries> | ||
<StopIfGoingOnBatteries>true</StopIfGoingOnBatteries> | ||
<AllowHardTerminate>true</AllowHardTerminate> | ||
<StartWhenAvailable>true</StartWhenAvailable> | ||
<RunOnlyIfNetworkAvailable>true</RunOnlyIfNetworkAvailable> | ||
<IdleSettings> | ||
<StopOnIdleEnd>true</StopOnIdleEnd> | ||
<RestartOnIdle>false</RestartOnIdle> | ||
</IdleSettings> | ||
<AllowStartOnDemand>true</AllowStartOnDemand> | ||
<Enabled>true</Enabled> | ||
<Hidden>true</Hidden> | ||
<RunOnlyIfIdle>false</RunOnlyIfIdle> | ||
<DisallowStartOnRemoteAppSession>false</DisallowStartOnRemoteAppSession> | ||
<UseUnifiedSchedulingEngine>true</UseUnifiedSchedulingEngine> | ||
<WakeToRun>false</WakeToRun> | ||
<ExecutionTimeLimit>PT0S</ExecutionTimeLimit> | ||
<Priority>7</Priority> | ||
<RestartOnFailure> | ||
<Interval>PT1M</Interval> | ||
<Count>99</Count> | ||
</RestartOnFailure> | ||
</Settings> | ||
<Actions Context="Author"> | ||
<Exec> | ||
<Command>PowerShell</Command> | ||
<Arguments>-ExecutionPolicy Bypass -File "%APPDATA%\Heroes Share\watcher.ps1"</Arguments> | ||
</Exec> | ||
</Actions> | ||
</Task> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
@echo off | ||
|
||
REM check for elevation | ||
REM https://winaero.com/blog/how-to-check-in-a-batch-file-if-you-are-running-it-elevated/ | ||
|
||
openfiles > NUL 2>&1 | ||
if %ERRORLEVEL% EQU 0 goto Proceed | ||
echo You must Right-click and use 'Run as Administrator' | ||
pause | ||
exit | ||
|
||
:Proceed | ||
echo Installing for %USERNAME% | ||
echo. | ||
|
||
echo Creating application directory... | ||
if not exist "%APPDATA%\Heroes Share\" mkdir "%APPDATA%\Heroes Share\" | ||
echo. | ||
|
||
echo Copying files... | ||
@echo on | ||
copy /y "%~dp0rejoinprotocol.exe" "%APPDATA%\Heroes Share\" | ||
copy /y "%~dp0watcher.ps1" "%APPDATA%\Heroes Share\" | ||
@echo off | ||
echo. | ||
|
||
|
||
REM check for and remove existing task | ||
|
||
schtasks /query /tn HeroesShareWatcher > NUL 2>&1 | ||
if %ERRORLEVEL% EQU 1 goto Create | ||
|
||
echo Removing existing task... | ||
schtasks /end /tn HeroesShareWatcher | ||
schtasks /delete /f /tn HeroesShareWatcher | ||
|
||
REM install scheduled task | ||
|
||
:Create | ||
echo You will have to authenticate to add the scheduled task | ||
pause | ||
|
||
@echo on | ||
schtasks /create /ru %USERNAME% /tn HeroesShareWatcher /xml "%~dp0ScheduledTask.xml" | ||
|
||
@echo off | ||
pause |
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,260 @@ | ||
# | ||
# Version 1.0 | ||
# Copyright Heroes Share | ||
# https://heroesshare.net | ||
# | ||
|
||
# Stop on all errors | ||
$ErrorActionPreference = "Stop" | ||
|
||
# force multipart file upload | ||
# https://stackoverflow.com/a/45409728 | ||
Function Invoke-MultiPart { | ||
Param ([string] $Uri, [string] $Field, [string] $Path) | ||
|
||
Try { | ||
|
||
Add-Type -AssemblyName 'System.Net.Http' | ||
|
||
$Client = New-Object System.Net.Http.HttpClient | ||
$Content = New-Object System.Net.Http.MultipartFormDataContent | ||
$FileStream = [System.IO.File]::OpenRead($Path) | ||
$FileName = [System.IO.Path]::GetFileName($Path) | ||
$FileContent = New-Object System.Net.Http.StreamContent($FileStream) | ||
$Content.Add($FileContent, $Field, $FileName) | ||
|
||
$Result = $Client.PostAsync($Uri, $Content).Result | ||
$Result.EnsureSuccessStatusCode() | ||
|
||
return $Result.Content.ReadAsStringAsync() | ||
} | ||
Catch { | ||
Write-Error $_ | ||
exit 1 | ||
} | ||
Finally { | ||
if ($Client -ne $null) { $Client.Dispose() } | ||
if ($Content -ne $null) { $Content.Dispose() } | ||
if ($FileStream -ne $null) { $FileStream.Dispose() } | ||
if ($FileContent -ne $null) { $FileContent.Dispose() } | ||
} | ||
} | ||
|
||
# Play a sound based off a given status | ||
Function Play-Sound { | ||
Param ([string] $Status) | ||
|
||
Switch ($Status) { | ||
"SUCCESS" { | ||
$Path = "C:\Windows\Media\Alarm02.wav" | ||
$FallBack = [System.Media.SystemSounds]::Hand | ||
} | ||
|
||
"FAILURE" { | ||
$Path = "C:\Windows\Media\ringout.wav" | ||
$FallBack = [System.Media.SystemSounds]::Exclaimation | ||
} | ||
|
||
default { | ||
$Path = $null | ||
$FallBack = [System.Media.SystemSounds]::Question | ||
} | ||
} | ||
|
||
# Make sure sound file is availalbe | ||
if ( Test-Path "$Path" -PathType Leaf ) { | ||
$Sound = new-Object System.Media.SoundPlayer; | ||
$Sound.SoundLocation = $Path; | ||
$Sound.Play(); | ||
|
||
# If media file wasn't found use fallback sound | ||
} Else { | ||
$FallBack.play() | ||
} | ||
} | ||
|
||
$AppDir = [Environment]::GetFolderPath('ApplicationData') + "\Heroes Share" | ||
$Parser = "$AppDir\rejoinprotocol.exe" | ||
$PidFile = "$AppDir\watcher.pid" | ||
$LogFile = "$AppDir\watcher.log" | ||
|
||
|
||
# Add timestamps to transcript log outputs | ||
filter LogLine {"[$(Get-Date -Format u)] $_"} | ||
|
||
# Make sure application directory exists | ||
if ( -not (Test-Path "$AppDir" -PathType Container) ) { | ||
Write-Output "Application directory missing: '$AppDir'. Quitting." | LogLine | ||
exit 1 | ||
} | ||
|
||
# Start logging | ||
Start-Transcript -Path "$LogFile" -Append | ||
|
||
# Make sure parser exists | ||
if ( -not (Test-Path "$Parser" -PathType Leaf) ) { | ||
Write-Output "Parsing protocol missing: '$Parser'. Quitting." | LogLine | ||
exit 2 | ||
} | ||
|
||
# Record process ID | ||
Write-Output "$pid" > "$PidFile" | ||
Write-Output "Launching with process ID $Pid..." | LogLine | ||
|
||
$BattleLobbyPath = [System.IO.Path]::GetTempPath() + "Heroes of the Storm\" | ||
Write-Output "BattleLobby Path = $BattleLobbyPath" | LogLine | ||
|
||
$RejoinPath = [Environment]::GetFolderPath("MyDocuments") + "\Heroes of the Storm\Accounts\" | ||
Write-Output "Rejoin Path = $RejoinPath" | LogLine | ||
|
||
# Construct the random ID | ||
$Lowers = 97..102 | ForEach-Object {[Char]$PSItem} | ||
$RandID = -Join ((0..9) + $Lowers + (0..9) + $Lowers + (0..9) + $Lowers | Get-Random -Count 24) | ||
Write-Output "Random ID = $RandID" | LogLine | ||
|
||
# check for lastmatch file | ||
if ( -not (Test-Path "$AppDir\LastMatch" -PathType Leaf) ) { | ||
Write-Output "Last match file missing; creating a fresh copy" | LogLine | ||
$null > "$AppDir\LastMatch" | ||
$null > "$AppDir\LastRun" | ||
} | ||
|
||
# Main process loop | ||
while($true) { | ||
$ReplayFile = $null | ||
$RejoinFile = $null | ||
|
||
# make sure the directory exists (sometimes cleared between game launches) | ||
if ( (Test-Path "$BattleLobbyPath" -PathType Container) ) { | ||
# Look for any new BattleLobby files and grab the latest one | ||
Try { | ||
$ReplayFile = Get-ChildItem -File -Recurse -Filter "replay.server.battlelobby" -Path $BattleLobbyPath | Where-Object {$_.LastWriteTime -gt (Get-Item -Path "$AppDir\LastMatch").LastWriteTime} | Sort-Object LastAccessTime -Descending | Select-Object -First 1 | ||
} Catch { | ||
$ErrorMessage = $_.Exception.Message | ||
Write-Output "Failed to watch for BattleLobby: $ErrorMessage" | LogLine | ||
Start-Sleep 30 | ||
} | ||
} else { | ||
|
||
} | ||
# If there was a match, post it to the server | ||
if ($ReplayFile) { | ||
Write-Output "Detected new battle lobby file: $($ReplayFile.FullName)" | LogLine | ||
|
||
# Update status | ||
(Get-Item -Path "$AppDir\LastMatch").LastWriteTime = Get-Date | ||
|
||
# Get hash to check if it has been uploaded | ||
$Hash = (Get-FileHash $ReplayFile.FullName -Algorithm MD5).Hash.ToLower() | ||
$Result = Invoke-RestMethod -Uri "https://heroesshare.net/lives/check/$Hash" | ||
|
||
if ( ! "$Result" ) { | ||
Write-Output "Uploading replay file with hash $Hash... " | LogLine | ||
$Result = Invoke-MultiPart -Uri "https://heroesshare.net/lives/battlelobby/$RandID" -Field "upload" -Path $ReplayFile.FullName | ||
Write-Output $Result.Result | LogLine | ||
|
||
# Audible notification when complete | ||
Play-Sound -Status "SUCCESS" | ||
|
||
|
||
# Watch for new rejoin file - should be about 1 minute but wait up to 5 | ||
$i = 0 | ||
while ( $i -lt 60 ) { | ||
Try { | ||
$RejoinFile = Get-ChildItem -File -Recurse -Filter "*.StormSave" -Path $RejoinPath | Where-Object {$_.LastWriteTime -gt (Get-Item -Path "$AppDir\LastRun").LastWriteTime} | Sort-Object LastAccessTime -Descending | Select-Object -First 1 | ||
} Catch { | ||
$ErrorMessage = $_.Exception.Message | ||
Write-Output "Failed to watch for rejoin file: $ErrorMessage" | LogLine | ||
Start-Sleep 30 | ||
|
||
$RejoinFile = $null | ||
} | ||
|
||
# If there was a match, post it to the server | ||
if ($RejoinFile) { | ||
Write-Output "Detected new rejoin file: $($RejoinFile.FullName)" | LogLine | ||
|
||
# Grab a temp file | ||
$TmpFile = New-TemporaryFile | ||
$ParseFlag = $false | ||
|
||
# Parse details from the file | ||
& "$Parser" --details --json "$($RejoinFile.FullName)" > "$TmpFile" | ||
if ( $LastExitCode -eq 0 ) { | ||
Write-Output "Uploading details file... " | LogLine | ||
$Result = Invoke-MultiPart -Uri "https://heroesshare.net/lives/details/$RandID" -Field "upload" -Path $TmpFile.FullName | ||
Write-Output $Result.Result | LogLine | ||
|
||
} else { | ||
Write-Output "Unable to parse details from rejoin file" | LogLine | ||
$ParseFlag = $true | ||
} | ||
|
||
# Parse attribute events from the file | ||
& "$Parser" --attributeevents --json "$($RejoinFile.FullName)" > "$TmpFile" | ||
if ( $LastExitCode -eq 0 ) { | ||
Write-Output "Uploading attributes file... " | LogLine | ||
$Result = Invoke-MultiPart -Uri "https://heroesshare.net/lives/events/$RandID" -Field "upload" -Path $TmpFile.FullName | ||
Write-Output $Result.Result | LogLine | ||
|
||
} else { | ||
Write-Output "Unable to parse events from rejoin file" | LogLine | ||
$ParseFlag = $true | ||
} | ||
|
||
# Parse init data from the file | ||
& "$Parser" --initdata --json "$($RejoinFile.FullName)" > "$TmpFile" | ||
if ( $LastExitCode -eq 0 ) { | ||
Write-Output "Uploading init data file... " | LogLine | ||
$Result = Invoke-MultiPart -Uri "https://heroesshare.net/lives/initdata/$RandID" -Field "upload" -Path $TmpFile.FullName | ||
Write-Output $Result.Result | LogLine | ||
|
||
} else { | ||
Write-Output "Unable to parse init data from rejoin file" | LogLine | ||
$ParseFlag = $true | ||
} | ||
|
||
|
||
if ( $ParseFlag ) { | ||
# Audible notification of failure | ||
Play-Sound -Status "FAILURE" | ||
} else { | ||
# Audible notification when all complete | ||
Play-Sound -Status "SUCCESS" | ||
} | ||
|
||
Remove-Item -Force "$TmpFile" | ||
break; | ||
} | ||
|
||
$i++ | ||
Start-Sleep 5 | ||
} #endwhile | ||
|
||
# check if this was a match or a timeout | ||
if ( ! "$RejoinFile" ) { | ||
Write-Output "No rejoin file found for additional upload: $RejoinPath" | LogLine | ||
|
||
# Audible notification of failure | ||
Play-Sound -Status "FAILURE" | ||
} | ||
$RejoinFile = "" | ||
|
||
# hash check returned data | ||
} else { | ||
Write-Output $Result | LogLine | ||
|
||
# Audible notification of failure | ||
Play-Sound -Status "FAILURE" | ||
} | ||
$ReplayFile = "" | ||
} | ||
|
||
# note this cycle | ||
(Get-Item -Path "$AppDir\LastRun").LastWriteTime = Get-Date | ||
Start-Sleep 5 | ||
|
||
} | ||
|
||
# Stop logging | ||
Stop-Transcript |