add-type -Path "<THE_PATH_TO_THE_HTML_AGILITYPACK>\HtmlAgilityPack.1.4.6\Net45\HtmlAgilityPack.dll"
#### If you use the powershell default agent header the oxidemod.org will block any login attempt ####
$UserAgentHeader = "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.81 Safari/537.36"
$MessageList = @()
$OxideLogin = "<YOUR_OXIDE_LOGIN>"
$OxidePassword = "<YOUR_OXIDE_PASSWORD>"
$OxidePluginFolder = "<YOUR_OXIDE_PLUGIN_FOLDER>"
$RustDedicatedServerFolder = "<RUST_DEDICATED_SERVER_INSTALL_FOLDER>"
$LogFilePath = [System.IO.Path]::Combine($RustDedicatedServerFolder,"OxideServerUpdate.log")
$OxideExtensionFolder = [System.IO.Path]::Combine($RustDedicatedServerFolder,"RustDedicated_Data","Managed")
$OxideUri = "http://oxidemod.org/"
$OxidePlugins = @(668,675,819,1356,695,1067,842,1832,828,865,2320)
$OxideExtensions = @(768)
Function Out-Log
{
param(
[Parameter(Mandatory=$True,ValueFromPipeline=$true)]
[string]$Message,
[Parameter(Mandatory=$True)]
[ValidateSet("ERROR","INFO","WARNING")]
[String]$Type,
[string]$Context = [string]::Empty,
[string]$Component = [string]::Empty,
[int]$Thread = 1,
[ValidateScript({$_.Value.GetType().BaseType.Name -eq "Array"})]
[ref]$MessageList
)
process
{
$BackgroundColor
switch($type){
"ERROR" {
$LogType = 3
$BackgroundColor = "Red"
$ForegroundColor = "White"
}
"INFO" {
$LogType = 1
$BackgroundColor = "Blue"
$ForegroundColor = "White"
}
"WARNING" {
$LogType = 2
$BackgroundColor = "Yellow"
$ForegroundColor = "Black"
}
}
$CurrentDateTime = $(Get-Date)
$FileTime = $CurrentDateTime.ToFileTime()
$CurrentTime = $CurrentDateTime.ToString("HH:mm:ss.ffffff")
$CurrentDate = $CurrentDateTime.ToString("MM-dd-yyyy")
$File = [System.IO.Path]::GetFileName($LogFilePath)
$LogEntry = "<![LOG[$($Message)]LOG]!><time=`"$($CurrentTime)`" date=`"$($CurrentDate)`" component=`"$($Component)`" context=`"$($Context)`" type=`"$($LogType)`" thread=`"$($Thread)`" file=`"$($File)`">"
Write-Host "[$Type] $CurrentDate $CurrentTime | $Message" -BackgroundColor $BackgroundColor -ForegroundColor $ForegroundColor
Out-File -InputObject $LogEntry -Append -Encoding utf8 –FilePath $LogFilePath
if($MessageList)
{
$MessageList.Value += [pscustomobject]@{
Message = $Message
Type = $Type
Context = $Context
Thread = $Thread
Component = $Component
File = $File
Date = $FileTime
}
}
}
}
Function Get-OxideWebSession
{
param(
[parameter(Mandatory=$true)]
[string]$Login,
[parameter(Mandatory=$true)]
[string]$Password
)
process
{
$UrlEncodedLogin = [System.Net.WebUtility]::UrlEncode($Login)
$UrlEncodedPassword = [System.Net.WebUtility]::UrlEncode($Password)
$Uri = [string]::Format("{0}login/login?login={1}&password={2}", $OxideUri, $UrlEncodedLogin, $UrlEncodedPassword)
Out-Log -Message "Login URI : $Uri" -Type INFO
$ErrorActionPreference = [System.Management.Automation.ActionPreference]::Stop
try
{
$WebRequest = Invoke-WebRequest -Uri $Uri -UserAgent $UserAgentHeader -SessionVariable WebSession -Method Post
return $WebSession
}
catch [Exception]
{
Out-Log -Message "Error MESSAGE : $($($_.InvocationInfo.Line).Trim())" -Type ERROR
Out-Log -Message "Error MESSAGE (Line=$($_.InvocationInfo.ScriptLineNumber)): $($_.Exception.Message)" -Type ERROR
}
$ErrorActionPreference = [System.Management.Automation.ActionPreference]::Continue
return $null
}
}
Function Get-OxidePluginList
{
process
{
$PluginCollection = @()
$ErrorActionPreference = [System.Management.Automation.ActionPreference]::Stop
try
{
$Uri = "http://oxidemod.org/plugins/categories/rust.24/?order=title"
$WebRequest = Invoke-WebRequest -Uri $Uri -UserAgent $UserAgentHeader
$LastPage=1
foreach($Link in $($WebRequest.Links))
{
$RegexMatch = [System.Text.RegularExpressions.Regex]::Match($Link.href,'page[=]([0-9]+)')
$MatchedValue = $RegexMatch.Groups[1].Value
if($MatchedValue)
{
if($([int]$MatchedValue) -gt $LastPage)
{
$LastPage = [int]$MatchedValue
}
}
}
[HtmlAgilityPack.HtmlWeb]$Web = New-Object HtmlAgilityPack.HtmlWeb
for($i=1;$i -le ($LastPage+1);$i++)
{
Out-Log -Message "Working on page $i/$LastPage" -Type INFO
[HtmlAgilityPack.HtmlDocument]$Doc = $Web.Load("http://oxidemod.org/plugins/categories/rust.24/?page=$i")
[HtmlAgilityPack.HtmlNodeCollection]$tags = $Doc.DocumentNode.SelectNodes("//*[@class='itemHeader clearfix']");
foreach($tag in $tags)
{
$TempLink = $($tag.SelectNodes(".//*[@class='itemTitle']/a/@href")).Attributes[0].Value
$RegexMatch = [System.Text.RegularExpressions.Regex]::Match($TempLink,'[.]([0-9]+)')
$TempMatchedValue = $RegexMatch.Groups[1].Value
$TempId = $TempMatchedValue
$Icon = $Doc.DocumentNode.SelectNodes("//*[@id='resource-$TempId']//img/@src").Attributes[0].Value
$Description = [System.Net.WebUtility]::HtmlDecode("$($Doc.DocumentNode.SelectNodes("//*[@id='resource-$TempId']//p/text()").Text)".Trim())
$Title = $($tag.SelectNodes(".//*[@class='itemTitle']/a/text()")).Text
$Version = $($($tag.SelectNodes(".//*[@class='version']/text()")).Text).Replace("Version: ", "")
Out-Log -Message " -+ Working on : $TempId" -Type INFO
$PluginCollection += [PSCustomObject]@{
Title = $Title
Link = $TempLink
Version = $Version
Id = $TempId
Icon = $Icon
Description = $Description
}
Out-Log -Message " +--> Title = $Title" -Type INFO
Out-Log -Message " +--> Link = $TempLink" -Type INFO
Out-Log -Message " +--> Version = $Version" -Type INFO
Out-Log -Message " +--> Id = $TempId" -Type INFO
Out-Log -Message " +--> Icon = $Icon" -Type INFO
Out-Log -Message " +--> Description = $Description" -Type INFO
}
Start-Sleep 1
}
}
catch [Exception]
{
Out-Log -Message "Error MESSAGE : $($($_.InvocationInfo.Line).Trim())" -Type ERROR
Out-Log -Message "Error MESSAGE (Line=$($_.InvocationInfo.ScriptLineNumber)): $($_.Exception.Message)" -Type ERROR
}
$ErrorActionPreference = [System.Management.Automation.ActionPreference]::Continue
return $PluginCollection
}
}
Function Get-OxideFile
{
param
(
[parameter(Mandatory=$true)]
[Microsoft.PowerShell.Commands.WebRequestSession]$WebSession,
[parameter(Mandatory=$true)]
[int]$Id,
[parameter(Mandatory=$true)]
[string]$Destination,
[parameter(Mandatory=$true)]
[ValidateSet("plugins","extensions")]
[string]$Type
)
process
{
$Status = $false
$Uri = [string]::Format("{0}{1}/{2}",$OxideUri, $Type, $Id)
Out-Log -Message "$([System.Globalization.CultureInfo]::CurrentCulture.TextInfo.ToTitleCase($Type.ToLower())) URI : $Uri" -Type INFO
$ErrorActionPreference = [System.Management.Automation.ActionPreference]::Stop
try
{
$WebRequest = Invoke-WebRequest -Uri $Uri -WebSession $WebSession -UserAgent $UserAgentHeader
$Link = [string]::Format("{0}{1}", $OxideUri, $($($WebRequest.Links | Where-Object -FilterScript {$_.href -match "download[?]version" }).href))
Out-Log -Message "Download URI : $Link" -Type INFO
$WebRequest = Invoke-WebRequest -Uri $Link -WebSession $WebSession -UserAgent $UserAgentHeader
$RegexMatch = [System.Text.RegularExpressions.Regex]::Match($WebRequest.Headers["Content-Disposition"],'["](.+)["]')
if($RegexMatch.Success -and $($RegexMatch.Groups.Count -eq 2))
{
$PluginFileName = $RegexMatch.Groups[1].Value
if([bool]$PluginFileName)
{
Out-Log -Message "Plugin FILENAME : $PluginFileName" -Type INFO
if([System.IO.Directory]::Exists($Destination))
{
$PluginFileFullPath = [System.IO.Path]::Combine($Destination, $PluginFileName)
Out-Log -Message "Saving FILE : $PluginFileFullPath" -Type INFO
[System.IO.File]::WriteAllBytes($PluginFileFullPath, $WebRequest.Content)
if([System.IO.File]::Exists($PluginFileFullPath))
{
$Status = $true
}
else
{
Out-Log -Message "Error FILE : $PluginFileFullPath Doesn't exists !" -Type ERROR
}
}
else
{
Out-Log -Message "Error PLUGIN FOLDER : $Destination Doesn't exists !" -Type ERROR
}
}
else
{
Out-Log -Message "Error FILENAME : Not found !" -Type ERROR
}
}
else
{
Out-Log -Message "Error FILENAME : Not found !" -Type ERROR
}
}
catch [Exception]
{
Out-Log -Message "Error MESSAGE : $($($_.InvocationInfo.Line).Trim())" -Type ERROR
Out-Log -Message "Error MESSAGE (Line=$($_.InvocationInfo.ScriptLineNumber)): $($_.Exception.Message)" -Type ERROR
}
$ErrorActionPreference = [System.Management.Automation.ActionPreference]::Continue
return $Status
}
}
Function Get-OxideMod
{
param
(
[parameter(Mandatory=$true)]
[string]$Destination
)
process
{
$Status = $false
Add-Type -AssemblyName System.IO.Compression.FileSystem
$OxideRustZipFileName = "Oxide-Rust.zip"
$OxideRustTaggedZipFileName = "Oxide-Rust-$([DateTime]::Now.ToString('yyyy-mm-dd-hhmmss')).zip"
$ErrorActionPreference = [System.Management.Automation.ActionPreference]::Stop
try
{
$SearchURI = "http://oxidemod.org/downloads/"
$WebRequest = Invoke-WebRequest -Uri $SearchURI -UserAgent $UserAgentHeader
$Uri = $($WebRequest.Links | Where-Object -FilterScript {$_.href -match "Oxide-Rust.zip" }).href
#### $Uri = "https://dl.bintray.com/oxidemod/builds/$OxideRustZipFileName"
$TempFilePath = [System.IO.Path]::Combine($TempPath,$OxideRustTaggedZipFileName)
$WebRequest = Invoke-WebRequest -Uri $Uri -UserAgent $UserAgentHeader -OutFile $TempFilePath
[System.IO.Compression.ZipArchive]$ZipArchive = [System.IO.Compression.ZipFile]::OpenRead($TempFilePath)
foreach($Entry in $ZipArchive.Entries)
{
$EntryFilePath = [System.IO.Path]::Combine($Destination, $Entry.FullName)
$EntryDirectoryName = [System.IO.Path]::GetDirectoryName($EntryFilePath)
if(![System.IO.Directory]::Exists($EntryDirectoryName))
{
[System.IO.Directory]::CreateDirectory($EntryDirectoryName) | Out-Null
}
if(!$($EntryFilePath.EndsWith("/")))
{
Out-Log -Message "Extracting $($Entry.Name) to $EntryFilePath" -Type INFO
[System.IO.Compression.ZipFileExtensions]::ExtractToFile($Entry, $EntryFilePath, $true);
}
}
$ZipArchive.Dispose()
$Status = $true
Out-Log -Message "Trying to delete : $OxideRustTaggedZipFileName" -Type INFO
[System.IO.File]::Delete($OxideRustTaggedZipFileName)
if(![System.IO.File]::Exists($OxideRustTaggedZipFileName))
{
Out-Log -Message "Deleted : $OxideRustTaggedZipFileName" -Type INFO
}
else
{
Out-Log -Message "Cannot delete : $OxideRustTaggedZipFileName" -Type WARNING
}
}
catch [Exception]
{
Out-Log -Message "Error MESSAGE : $($($_.InvocationInfo.Line).Trim())" -Type ERROR
Out-Log -Message "Error MESSAGE (Line=$($_.InvocationInfo.ScriptLineNumber)): $($_.Exception.Message)" -Type ERROR
}
$ErrorActionPreference = [System.Management.Automation.ActionPreference]::Continue
return $Status
}
}
Out-Log -Message "#### Started OXIDE Update Script ####" -Type INFO
#### TODO : RustDedicated Stop Script ####
#### TODO : Steamcmd Update Script ####
#### First we have to create a valid web session on Oxidemod.org ####
$OxideWebSession = $(Get-OxideWebSession -Login $OxideLogin -Password $OxidePassword)[1]
if($OxideWebSession.Cookies.Count -gt 0)
{
Out-Log -Message "Web SESSION : Valid !" -Type INFO
#### Now we have to update the Oxide files
if($(Get-OxideMod -Destination $RustDedicatedServerFolder))
{
Out-Log -Message "OXIDE Mod update successfull ! (Do not care for any previous warning !)" -Type INFO
#### Now we should update the extentions
Out-Log -Message "Update EXTENTIONS ($($OxideExtensions.Count))" -Type INFO
#### For each item in the list ####
foreach($OxideExtension in $OxideExtensions)
{
if(Get-OxideFile -WebSession $OxideWebSession -Id $OxideExtension -Destination $OxideExtensionFolder -Type "extensions")
{
Out-Log -Message ">>>> Extension UPDATE OPERATION : Success !" -Type INFO
}
else
{
Out-Log -Message ">>>> Error Extension UPDATE OPERATION : Failed !" -Type ERROR
}
}
#### Now we should update the plugins
Out-Log "Update PLUGINS ($($OxidePlugins.Count))" -Type INFO
#### For each item in the list ####
foreach($OxidePlugin in $OxidePlugins)
{
#### Retrieve download link
if(Get-OxideFile -WebSession $OxideWebSession -Id $OxidePlugin -Destination $OxidePluginFolder -Type "plugins")
{
Out-Log -Message ">>>> Plugin UPDATE OPERATION : Success !" -Type INFO
}
else
{
Out-Log -Message ">>>> Error Plugin UPDATE OPERATION : Failed !" -Type ERROR
}
}
}
}
else
{
Out-Log -Message "Error SESSION : Creation failed !" -Type ERROR
}
#### TODO : RustDedicated Start Script ####
Out-Log -Message "#### Ended OXIDE Update Script ####" -Type INFO