Automated Armory Access Tutorial
2009-01-07 17:52:15Automated Armory Access Tutorial
Introduction
Welcome to a little tutorial on how to grab data from the Armory and bend it to your will. [insert evil laughter]Armory Basics
The World of Warcraft Armory is a website that holds information about all characters above level 10 on all of Blizzard's WoW servers.The Armory was introduced quite a while ago, some time into TBC and first of caused an uproar by people who felt that this information about their character ought to be private and no one shall see it. It paints a decently complete picture of your characters, just some things are missing - but that's good. There's nothing about "amount of gold", "items in inventory and bank", "Recipes knows" and "mailbox system". But you'll find: Class, Race, Level, Guild, Rank, Titles, Equipped Items, Spec, Reputation, Skills, Professions and now after WotLK - also Achievements and Statistics. Some of these could be seen by inspecting before anyway.
What information is available in which file/section is fairly self-explaining. If in doubt, open the URL in a decent browser and inspect the source. It's nice and well-formed XML and they even put some thought on it (well, most of the time, with "Riding skill" you'll tear your hair out).
Armory Structure
This is a typical Armory URL:http://eu.wowarmory.com/character-statistics.xml?r=vek'lor&n=Armagon&c=130
With these important subparts:
region (eu, www (for us), kr, zh, ru, ...) realm urlencoded (" " = "+") name |
file character-sheet character-talents character-reputation character-achievements character-statistics guild-info [ character-calendar ] [ item-info ] |
section: achievements: 92 general 96 quests 97 exploration 95 pvp 168 dungeons & raids 169 professions 201 reputation 155 world events 81 feats of strength |
section statistics: 130 character 141 combat 128 kills 122 deaths 133 quests 132 skills 14807 dungeons & raids 131 social 134 travel 21 pvp |
Tools
If you're trying to read data from the Armory be sure to tell your means of communications (socket functions, cURL, etc.) some sensible defaults. With a wrong useragent and language setting you won't get the expected results. "English" and "'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.2) Gecko/20070219 Firefox/2.0.0.2" for example works.
My toolset is:
- PHP 5.2 (you need xml stuff, so don't build without libxml, if that's even possible nowadays)
- cURL / peclhttp (better than writing your own socket functions, or abusing fileget_contents())
- [optional] xmllint (part of libxml2-tools or libxml2-dev in debian/ubuntu, more on this later)
Some people published some of their armory-accessing code, you can look at those for reference or use and modify if the licence permits:
- AntiArc's Armory Tools: http://armory.mmo-champion.com/
- ArmoryProfileBot by DarkRyder on WoWWiki: http://www.wowwiki.com/User:ArmoryProfileBot#Source
Getting Started (Short Version)
Well, the basic workflow is this:
- figure out what information you need
- figure out in which part of the armory it's located
- construct the url to query
- query the url and grab the XML
- [ now it gets interesting ]
- [ are you still here? cool. ]
- apply some XPath voodoo to extract the data you need from the xml
- display (and be happy)
Getting Started (Long Version)
We'll be skipping the first 2 steps of the last paragraph and assume you know what you need.
I'll make an example of creating a text-only Armory display for typical use in an IRC Bot.
So it should look like this.
14:00 * Now talking in #c0ders
14:00 < niceguyeddie> 'sup guys, anyone playing WoW?
14:02 <@MrOrange> !char eu vek'lor armagon
14:02 <+Bender> Armagon <Rockthrone>, 25 Undead Warrior
14:05 < niceguyeddie> cool
Shouldn't be too hard. And no, it's not.
To abstract the irc thingy we'll assume the somewhat "dumb" bot just wants a CSV list that reads:
Armagon,Rockthrone,25,Undead,Warrior,that will be formatted, so he can do a simple
file_get_contents("http://example.org/armory.php?r=vek'lor&n=armagon");
to achieve this (if it's a PHP bot, of course ;))
So, step 1: what do we need?
Just basic data, it's all on character-sheet.xml - very good.
step 2: build the query url:
http://eu.wowarmory.com/character-statistics.xml?r=vek'lor&n=Armagon(possibly %27 instead of ')
I'm gonna use an example with pecl_http here as it's shorter than with curl:
$useragent = 'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.2) Gecko/20070219 Firefox/2.0.0.2';
$query = sprintf("http://%s.wowarmory.com/%s.xml?r=%s&n=%s", $region, $file, $realm, $name);
$response = http_get($query, array('redirect' => 10,'useragent' => $useragent), $info);
$xml = http_parse_message($response)->body;
works like a charm! Well, at least for me. You should get something like
<?xml version="1.0" encoding="UTF-8"?><?xml-stylesheet type="text/xsl" href="/layout/character-sheet.xsl"?><page globalSearch="1" lang="en_us" requestUrl="/character-sheet.xml">
<characterInfo>
<character battleGroup="Schattenbrand" charUrl="r=Vek%27lor&n=Armagon" class="Warrior"
[...]
in $xml, as well
So, now the fun part.
What do we need:
- character name
- guild name
- level
- race
- class
Now starts the XPath Voodoo, which can be annoying at times. There are also other methods, like SimpleXML, so I'm not gonna explain XPath now, it's just what I'm familiar with and it works.
But now comes xmllint into play. if you save all the XML you just got into some "test.xml" and got xmllint handy, you can do this:
$ xmllint --shell test.xml
/ > cd //baseStats/strength
strength > ls @base
ta- 2 53
strength > ls @effective
ta- 2 97
that's displaying this part deep in the tree:
<baseStats>
<strength attack="174" base="53" block="4" effective="97"/>
But back to our example, what we need is
//characterso we'll do:
$path = '//character';
try {
$dom = new DOMDocument();
$dom->loadxml($xml);
$xpath = new DOMXPath($dom);
} catch (Exception $e) {
}
$nodes = $xpath->query($path);
foreach ($nodes as $no) {
foreach(array('name', 'guildName', 'level', 'race', 'class') as $key) {
$result[$key] = $no->getAttribute($key);
}
}
Hey, nearly finished.
Now we just need to reformat it.
just change it to:
$result = array();
$output = '';
foreach ($nodes as $no) {
foreach(array('name', 'guildName', 'level', 'race', 'class') as $key) {
$result[$key] = $no->getAttribute($key);
$output .= sprintf('%s,', $no->getAttribute($key));
}
}
echo $output.PHP_EOL;
and indeed it will output:
Armagon,Rockthrone,25,Undead,Warrior,
You could also enclose every item in " ", but there should be no commas in this example items, so I let it out.
Congratulations, you've successfully read from the Armory.
Thanks
- AntiArc for his Armory Tools, I think I would've never brought myself to start playing around if I had to start from scratch. At the start I just hacked up on his source to get things done quick.
- DarkRyder for some more ideas in the ArmoryProfileBot source.
- pcj for having more clearsight than me in a typical ROAR SMASH CANT FIND BUG moment and the suggestion of an alternative title for this document: Web-based Armory Normalization, Keying, Extracting, and Reading
Appendix
Full source code of armory.php
<?php
$region = 'eu';
$file = 'character-sheet';
$realm = "vek'lor";
$name = 'Armagon';
$useragent = 'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.2) Gecko/20070219 Firefox/2.0.0.2';
$query = sprintf("http://%s.wowarmory.com/%s.xml?r=%s&n=%s", $region, $file, $realm, $name);
$response = http_get($query, array('redirect' => 10,'useragent' => $useragent), $info);
$xml = http_parse_message($response)->body;
$path = '//character';
try {
$dom = new DOMDocument();
$dom->loadxml($xml);
$xpath = new DOMXPath($dom);
} catch (Exception $e) {
}
$nodes = $xpath->query($path);
$result = array();
$output = '';
foreach ($nodes as $no) {
foreach(array('name', 'guildName', 'level', 'race', 'class') as $key) {
$result[$key] = $no->getAttribute($key);
$output .= sprintf('%s,', $no->getAttribute($key));
}
}
echo $output.PHP_EOL;
?>
About
Life's a bitch, life's a whore. Nothing less, nothing more.