Rants, rambles, news and notes from another geek

World of Warcraft Addon Updater in PowerShell

UPDATE: I am now maintaining this script over on my Warcraft Wiki site on pbwiki. Please go there for documentation and the most recent updates.

I couldn’t resist. Sometimes I don’t even know why I do these things. Last night I decided to start playing with System.Net.WebClient from Windows PowerShell and four hours later I had something close. A few more hours today and now I have a PowerShell script that:

  • Updates Subversion working copies (can be disabled w/ a switch)
  • Can be configured to check for updates from wowinterface.com using a simple data file in your addons folder called addons.ps1 (see the comment header below for a sample of this file) and a nice XML endpoint provided by the guys at wowi (thanks Dolby!)
  • Will check to see if an addon is one of the WowAce family of addons and will try to update it from their using their RSS feed for information.

I know I can add more addon sites to this later, but now that Auctioneer is up on wowi (ID: 4812) I don’t know if I have any addons that I use that can’t be updated by this script. Perhaps one or two, but that isn’t bad given that I have 169 addons in my Addons directory.

This script has a few dependencies for extracting ZIP and RAR files. You will need to get unzip.exe from Info-ZIP and UnRar.exe from RARLabs&nbsp_place_holder;and put them both on your path. Both are free.

Anyway, here is the script. There is a download at the bottom of this post for those of you who just want to have it.

DISCLAIMER: This script is supplied without warranty or support. If you play WoW and don’t know anything about PowerShell, this might be a bit much for you. Take a look at WowAceUpdater or WUU instead.

  1 #########################################################################
  2 # Name: update-addons.ps1
  3 # Version: 1.0
  4 # Author: Peter Provost <peter@provost.org>
  5 #
  6 # Usage: update-addons [-skipSvn]
  7 #
  8 # Remarks: This is a simple powershell script for updating your
  9 #   World of Warcraft addons. It will autodetect SVN working copies
 10 #   and update them. It will look for a file called addons.ps1
 11 #   in your Addons folder to define special locations for downloading
 12 #   addons (see below for a sample addons.ps1). After that it will test
 13 #   if the addon can be updated from [http://files.wowace.com](http://files.wowace.com). Any addon
 14 #   still unmatched will be skipped.
 15 #
 16 #   This sample addons.ps1 hows how to configure updates from 
 17 #   wowinterface.com:
 18 #
 19 #   $wowiAddons = @{
 20 #     'FlightMap' = 3897;
 21 #     'Clique' = 5108;
 22 #   };
 23 #
 24 #   At this point only wowinterface.com is supported in this data file
 25 #   but more may be added later.
 26 #
 27 #########################################################################
 28 
 29 **param** (
 30     **[switch]** $skipSvn
 31 );
 32 
 33 # Configuration - change these as needed
 34 $wowAddonDir = "C:\World of Warcraft\Interface\Addons";
 35 $stateFile = "PSUpdateAddons.state";
 36 
 37 **function** update-addon($addonSource, $downloadUrl, $remoteVersion, $fileName)
 38 {
 39     **if** ( $remoteVersion -**ne** $localVer) {
 40       **write-host** "$_ : ($addonSource) Update required: Current ver=$localVer, Remote ver=$remoteVersion";
 41 
 42       $tempFilePath = **join-path** $tempDir $fileName;
 43       **downloadextract-addon** $downloadUrl $tempFilePath;
 44 
 45       **write-host** "`tUpdating state file..." -noNewLine;
 46       $remoteVersion > $stateFilePath;
 47       "done." | **write-host**;
 48     } **else** {
 49       **write-host** "$_ : ($addonSource) Addon up-to-date. Skipping.";
 50     }
 51 }
 52 
 53 # Helper function for updating a single addon]
 54 **function** downloadextract-addon (**[string]** $uri, **[string]** $tempFile)
 55 {
 56   **write-host** "`tDownloading $uri to $($tempFile)..." -noNewLine;
 57   $wc.DownloadFile( $uri, $tempFile );
 58   **write-host** "done.";
 59 
 60   $ext = **[System.IO.Path]**::GetExtension($tempFile);
 61   **switch** ($ext) {
 62     ".rar" {
 63       **write-host** "`tExtracting RAR Archive..." -noNewLine;
 64       & unrar x -o+ $tempFile $wowAddonDir;
 65     }
 66     ".zip" {
 67       **write-host** "`tExtracting ZIP Archive..." -noNewLine;
 68       & unzip -o $tempFile -d $wowAddonDir;
 69     }
 70     **default** { **write-host** "UNKNOWN EXTENSION TYPE!" }
 71   }
 72   **write-host** "done.";
 73 
 74   **write-host** "`tDeleting zip file..." -noNewLine;
 75   **remove-item** $tempFile;
 76   **write-host** "done.";
 77 }
 78 
 79 **function** test-wowaceaddon( **[string]** $addonName )
 80 {
 81   **return** ((**get-wowaceaddon** $addonName)-**ne** $null)
 82 }
 83 
 84 **function** get-wowaceaddon( **[string]** $addonName )
 85 {
 86   $xpath = ("//item[title='ADDON']" -**replace** 'ADDON', $_.Name);
 87   **return** $indexXmlDoc.SelectSingleNode($xpath);
 88 }
 89 
 90 # Setup a few things before we get started
 91 $wc = **new-object** System.Net.WebClient;
 92 $tempDir = **join-path** (**get-content** env:\temp) "PsWowUpdater";
 93 **if** (-**not** (**test-path** $tempDir)) { **new-item** -type directory -path $tempDir; }
 94 
 95 # Load in the WowAce Index file
 96 **write-host** "Downloading latest.xml from [http://files.wowace.com](http://files.wowace.com)";
 97 $uri = "[http://files.wowace.com/latest.xml](http://files.wowace.com/latest.xml)";
 98 $rssData = $wc.DownloadString($uri);
 99 $indexXmlDoc = **[xml]** $rssData;
100 
101 # Load in the WOWI config database
102 . (**join-path** $wowAddonDir "addons.ps1")
103 
104 (**get-childitem** $wowAddonDir | ? { $_.PSIsContainer }) | % {
105 ## SVN UPDATE
106   **if** (**join-path** $_.Fullname ".svn" | **test-path** ) {
107     **if** ($skipSvn.isPresent) {
108       **write-host** "$_ : Skipping SVN working copy";
109     } **else** {
110       **write-host** "$_ : Updating SVN working copy";
111       svn up -q $_.Fullname;
112     }
113   }
114 
115 ## WOWINTERFACE.COM
116   **elseif** ($wowiAddons.Contains($_.Name)) {
117     $stateFilePath = **join-path** $_.Fullname "PSUpdateAddons.state";
118     $localVer = "";
119     **if** (**test-path** $stateFilePath) { $localVer = (**get-content** $stateFilePath); }
120 
121     $uri = ("[http://www.wowinterface.com/patcherXXXX.xml](http://www.wowinterface.com/patcherXXXX.xml)" -**replace** "XXXX", $wowiAddons[$_.Name]);
122     $wowiXml = **[xml]** $wc.DownloadString($uri);
123 
124     $downloadUrl = $wowiXml.UpdateUI.Current.UIFileURL;
125     $remoteVersion = $wowiXml.UpdateUI.Current.UIVersion;
126     $fileName = $wowiXml.UpdateUI.Current.UIFile;
127     $addonSource = "WowInterface.com";
128 
129     **update-addon** $addonSource $downloadUrl $remoteVersion $fileName
130   }
131 
132 ## WOWACE FILES
133   **elseif** (**test-wowaceaddon** $_.Name) {
134     $stateFilePath = **join-path** $_.Fullname "PSUpdateAddons.state";
135     $localVer = "";
136     **if** (**test-path** $stateFilePath) { $localVer = (**get-content** $stateFilePath); }
137 
138     $elt = **get-wowaceaddon** $_.Name;
139 
140     $downloadUrl = $elt.enclosure.url;
141     $remoteVersion = $elt.version;
142     $fileName = $downloadUrl.Substring($downloadUrl.LastIndexOf("/")+1);
143     $addonSource = "WowAce.com";
144 
145     **update-addon** $addonSource $downloadUrl $remoteVersion $fileName
146   }
147 
148 ## Unknown addon source
149   **else** {
150     **write-host** "$_ : Can't figure this one out. Skipping.";
151   }
152 }

Download update-addons.zip (2KB). And here is my addons.ps1 data file to update my wowi addons. Create a file like that in your Addons folder and the script will find it.