Kastang Ramblings of a Geek

19Mar/110

WoW Realm Status PHP Class

I am working on a PHP Class called RealmStatus that will pull WoW Realm information from the WoW Realm Status Page without XML feeds available.

The RealmStatus Class currently supports the following operations:
--getAllServers() - Returns an associative array of all Server Names, Statuses, Populations, Types, and Locale.
--getServerType($) - Returns the type of a given server.
--getServerStatus($) - Returns the status of a given server.
--getServerPopulation($) - Returns the population of a given server.
--getServerLocale($) - Returns the locale of a given server.

For a detailed explanation of the operations, please read the function headers in the RealmStatus.php and example.php files.

The files can be pulled from GitHub here.

Filed under: Coding, PHP, WoW No Comments
13Jan/118

WoW Roster PHP Class

I am working on a PHP Class called RosterAPI that will parse the WoW Armory without XML Files. Unlike my previous two posts (Part 1 and Part 2), the information for each character can be pulled through get methods. Also included in RosterAPI are guild specific functions not included in my previous posts.

The following methods are currently included:

Character Specific:
* getLevel()
* getGender()
* getClass()
* getRace()
* getAchievementPoints()
* getProfessions(): Returns an associative array containing the name and value of each profession.
* getTalents(): Returns an associative array containing the name and value of each talent tree.
* getStat($stat): Please view the comment header of the getStat() function for a list of valid $stats.

Guild Specific:
* getGuildMembers($rank): If $rank is true, an associative array containing guild names and ranks will be returned. If $rank is false, an array will be returned containing all names in the guild.
* getTopWeeklyContributers(): Returns an array of the Top 5 Weekly Contributers in the guild.
* getGuildPerks(): Returns an array of the perks a guild currently has.

UPDATE:

I have received several emails with suggestions over the past week. In addition to everything listed above, my RosterAPI will also:

*getItems: Returns an Associative Array that will return every item equipped on a character along with the Item Level, Enchants, and Gems.
*getStatistics: Given a specific Statistic (ex: Number of deaths), the value of the statistic will be returned.

The code can be obtained via GitHub. While it is still a work in progress, it should be stable enough for personal use. This class is being used to power the backend of the We Know Roster. Please read the README file along with the example.php file for a brief introduction on how to use the class. I recommend reading the comments above each function in RosterAPI to understand what exactly will be returned (especially with those functions that return associative arrays).

Update #2

The Roster API now returns Glyphs for each character.

EDIT #2:

By request in the comments, here is a Paypal Donate button:


7Jan/111

Parsing the WoW Armory without XML – Part 2

This post is a continuation of my original post about parsing the WoW Armory with no XML feeds available. In my original post, I showed how to pull basic character information, such as professions and talents, from each member in a specified guild. This post will expand on more specific character information such as HP, MP, and Stats.

The getCharacterInformation($charName) function in my previous post can be expanded to include additional information available on the WoW Armory. (Please view my original post for the sample code). For the most part, the code below can be directly copied into the existing method. As in my previous post, I assume you have the knowledge to make minor changes to the method (such as modifying the return array).

Exact Health and Mana:

$health = $xpath->query('//li[@class="health"]/span[@class="value"]');
$mana = $xpath->query('//li[@id="summary-power"]/span[@class="value"]');
echo $health->item(0)->nodeValue."<br />";
echo $mana->item(0)->nodeValue."<br />";

Exact Talents:
This will return the exact talent in the form of xx/xx/xx for both talents.

$exactBuilds = $xpath->query('//span[@class="name-build"]/span[@class="build"]');
echo $exactBuilds->item(0)->nodeValue."<br />";
echo $exactBuilds->item(1)->nodeValue;

Stats and Resistances:
A stat listed in the comment above the code needs to be added to the $stat variable.

/**
 * Valid $stat values:
 * strength, agility, stamina, intellect,
 * spirit, mastery, meleedamage, meleedps,
 * meleeattackpower, meleespeed, meleehaste,
 * meleehit, meleecrit, meleecrip, expertise,
 * rangeddamage, rangeddps, rangedattackpower,
 * rangedspeed, rangedhaste, rangedhit, rangedcrit,
 * spellpower, spellhaste, spellhit, spellcrit,
 * spellpenetration, manaregen, combatregen, armor,
 * dodge, parry, block, resilience, arcaneres, fireres,
 * frostres, natureres, shadowres,
 */
 
$stat = "ADD_STAT_FROM_ABOVE_HERE";
$statName = $xpath->query('//li[@data-id="'.$stat.'"]/span[@class="name"]');
$statValue = $xpath->query('//li[@data-id="'.$stat.'"]/span[@class="value"]');
echo $statName->item(0)->nodeValue."<br />";
echo $statValue->item(0)->nodeValue."<br />";

Battleground Rating and Kills

$rating = $xpath->query('//li[@class="rating"]/span[@class="value"]');
$kills = $xpath->query('//li[@class="kills"]/span[@class="value"]');
echo $rating->item(0)->nodeValue."<br />";
echo $kills->item(0)->nodeValue."<br />";

My next post will cover looking into more interesting character information such as Achievements, Reputation, or Statistics.

31Dec/100

Parsing the WoW Armory without XML

A month or so ago Blizzard moved the WoW Armory to Battle.net servers. Currently, the new WoW Armory does not offer XML feeds for the data.  I spent a few hours working with PHP and DOM to create a 'parser' for the new Armory.  The below script is a trimmed down version of what is currently being used for the We Know Roster. I am only providing the back end script that will do the parsing and store the information in a MySQL database. Front end displaying can easily be achieved by querying the database with the stored results.

The script will pull the following information for each member in a specified guild: Name, Level, Class, Rank, Achievement Points, Profession 1 Name+Level, Profession 2 Name+Level, Talent1, and Talent2.

The scripts below require modifications to work properly. I recommend having knowledge of PHP/CLI before working with this script. I will develop a more user friendly version of this script only if Blizzard does not supply useful XML or JSON feeds in a reasonable amount of time.

The Bash Script:

The bash script pulls the newest HTML Roster file from the new Armory. This could probably be pulled via the PHP script, but since the file is several thousand lines long, I found it more efficient to save the file first and read it locally.

Please pay special attention to the paths, they will need to be altered in order to work correctly.

#!/bin/bash
#Replace YOUR_GUILD_NAME_HERE with your guild name. If your Guild Name is two or more words, it should be in the format
#of Your%20Guild%20Name
wget --directory-prefix=/path/to/your/desired/directory/ http://us.battle.net/wow/en/guild/YOUR_SERVER_HERE/YOUR_GUILD_NAME_HERE/roster
mv /path/to/your/desired/directory/roster /path/to/your/desired/directory/roster.html
php /path/to/php/file/ParseRoster.php

The SQL Dump:

Import this into a MySQL database.

--
-- Table structure for table `roster`
--
 
CREATE TABLE IF NOT EXISTS `roster` (
  `id` INT(11) NOT NULL AUTO_INCREMENT,
  `name` VARCHAR(255) NOT NULL,
  `race` VARCHAR(255) NOT NULL,
  `class` VARCHAR(255) NOT NULL,
  `level` VARCHAR(255) NOT NULL,
  `rank` VARCHAR(255) NOT NULL,
  `ap` VARCHAR(255) NOT NULL,
  `prof1name` VARCHAR(255) DEFAULT NULL,
  `prof1value` VARCHAR(255) DEFAULT NULL,
  `prof2name` VARCHAR(255) DEFAULT NULL,
  `prof2value` VARCHAR(255) DEFAULT NULL,
  `talent1` VARCHAR(255) DEFAULT NULL,
  `talent2` VARCHAR(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM  DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;

The PHP Backend:

The PHP file should be fairly straight forward.

A few notes:

  • I have a config file that holds information for my database, if you have a similar file you should include it, otherwise add in the proper mysql_connect() information.
  • Make sure the path to the Roster.html file is correct.
 
< ?php
/**
 * This script will parse the new WoW Armory without an XML file.
 * This script will currently pull the Name, Level, Class, Race,
 * Achievement Points, Professions, and Talents of every member
 * In a specified guild. The script works for me but may not work
 * as expected on every system. Use at your own risk.
 *
 * @author Josh Grochowski (josh[dot]kastang[at]gmail[dot]com)
 *
 */
 
set_time_limit(8000);
 
include("/path/to/config/file.php");
 
getRosterInformation();
 
function getRosterInformation() {
 
    $roster = file_get_contents("/path/to/roster/file/roster.html");
 
    $dom = new domDocument;
    $dom->loadHTML($roster);
    $dom->preserveWhiteSpace = false;
 
    //The first tbody tag marks the start of the actual
    //'roster' part of the html.
    $roster = $dom->getElementsByTagName('tbody');
 
    //Each Character has its own tr block.
    $char = $roster->item(0)->getElementsByTagName('tr');
 
    foreach ($char as $c) {
 
        //Character information is split into individual
        //td blocks.
        $charInfo = $c->getElementsByTagName('td');
        $charImages = $c->getElementsByTagName('img');
 
        //I only care about active characters. Inactive characters
        //will display 0 Achievement points.
        if((int)$charInfo->item(5)->nodeValue > 0) {
 
                $name = $charInfo->item(0)->nodeValue;
                $race = $charImages->item(0)->getAttribute('src');
                $class = $charImages->item(1)->getAttribute('src');
                $level = $charInfo->item(3)->nodeValue;
                $rank = trim($charInfo->item(4)->nodeValue);
                $ap = trim($charInfo->item(5)->nodeValue);
 
                //Returns an array containing the professions name/level and
                //talents of each individual character.
                $charArray = getCharacterInformation($name);
                $query = "INSERT INTO roster(name,race,class,level,rank,ap,prof1name,prof1value,prof2name,prof2value,talent1,talent2)
                                VALUES('$name','$race','$class','$level','$rank','$ap','$charArray[profName1]','$charArray[profValue1]',
                                '$charArray[profName2]','$charArray[profValue2]','$charArray[talent1]','$charArray[talent2]')";
 
                mysql_query($query) or die(mysql_error());
 
                //Wait 5 seconds inbetween queries to keep from getting banned from WoW Armory servers.
                //This can probably be adjusted to three or four seconds, but if you do get banned, it can
                //last las long as 48 hours.
                sleep(5);
        }
    }
}
 
function getCharacterInformation($charName) {
 
    //link to characters page on WoW Armory
    $charInfo = file_get_contents("http://us.battle.net/wow/en/character/eitrigg/".$charName."/simple");
 
    $dom = new domDocument;
    $dom->loadHTML($charInfo);
    $dom->preserveWhiteSpace = false;
 
    //Profession Names
    $xpath = new DOMXPath($dom);
    $profName = $xpath->query('//span[@class="profession-details"]/span[@class="name"]');
 
    //Profession Values
    $profValue = $xpath->query('//span[@class="profession-details"]/span[@class="value"]');
 
    //Talents
    $talents = $xpath->query('//span[@class="name-build"]/span[@class="name"]');
 
    $charArray = array("profName1" => $profName->item(0)->nodeValue,
                        "profValue1" => $profValue->item(0)->nodeValue,
                        "profName2" => $profName->item(1)->nodeValue,
                        "profValue2" => $profValue->item(1)->nodeValue,
                        "talent1" => $talents->item(0)->nodeValue,
                        "talent2" => $talents->item(1)->nodeValue);
 
    return $charArray;
}
 
?>
11Aug/100

Push Twitter Replies to your iPhone using Prowl

Yesterday I bought Prowl for the iPhone. Prowl is similar to Growl on OSX systems. Prowl pushes notifications to your iPhone or iTouch. One of the first uses I thought to use Prowl for was pushing my @replies and mentions from Twitter to my iPhone. I wanted to run the script from my Ubuntu server rather then keeping my desktop on 24/7. I did a quick search online and didn't find any command line options for achieving such a task. I decided to code a quick and dirty PHP script to accomplish what I wanted to do.

This is a quick hack. There are probably more efficient ways of accomplishing this task. I used ProwlPHP to link to the Prowl API.

I assume you know PHP and know how to navigate in Terminal. This is a command line app, it will run on any computer/server running PHP. I have extensively commented the script. Hopefully it is easy to follow along.

Quick Instructions:

  1. Download the newest version of ProwlPHP and copy it a directory.
  2. Create a file lastreply.txt and stick in in the same directory as the below code will be copied into. This file needs to have read and write permissions.
  3. Copy the below code into another file. Alter the ProwlPHP includes directory on line 2, and add your Prowl API key, Twitter Username, and Twitter Password in the constructor.
//CHANGE THIS PATH TO WHERE ProwlPHP IS LOCATED ON
//YOUR SERVER
include('../API/ProwlPHP.php');
$t = new Twitter();
 
/**
 * @class Twitter
 * This class integrates Twitter and Prowl notifications with the
 * iPhone. When this script runs it checks to see if any new
 * Twitter mentions have occured on your account since last check.
 * If any exist, the Tweet is sent to Prowl and will be notified
 * via push on your iPhone.
 *
 * This class uses ProwlPHP located at:
 * 			http://github.com/Fenric/ProwlPHP
 *
 * @author Kastang (josh dot kastang at gmail dot com)
 */
class Twitter {
 
    var $xml;
    var $lastID;
    var $tUser;
    var $tPass;
    var $prowl;
 
    /**
     * Constructor for Twitter Class. Three lines need to be edited
     * below before running the file: prowl, tUser, and tPass.
     */
    function __construct() {
 
        //EDIT THE 3 LINES BELOW
        $this->prowl = new Prowl('YOUR PROWL API KEY');
        $this->tUser = "TWITTER USERNAME";
        $this->tPass = "TWITTER PASSWORD";
 
        //XML info loaded from Twitter API.
        $this->xml = simplexml_load_string($this->getReplies());
 
        //Opens the lastreply file which contants the
        //id of the last mentioned tweet.
        $this->lastID = file_get_contents("lastreply.txt");
 
        //If the file is empty (probably the first time
        //you are using the script). It will pull the
        //newest mention id from your twitter feed and
        //store it in the file.
        if ($this->lastID == null) {
            $this->lastID = $this->xml->status[0]->id;
            $full = "@" . $this->xml->status[0]->user->screen_name .
                    ": " . $this->xml->status[0]->text;
            $this->prowl($full);
            $this->updateNewest($this->xml->status[0]->id);
        }
 
        //Checks for new Twitter Mentions.
        $this->checkForUpdates();
    }
 
    /**
     * Checks for updates.
     */
    function checkForUpdates() {
        //first run boolean.
        $first = true;
 
        //For each mention in the XML array, check to see if
        //the current ID is greater then the last recorded ID.
        //If it is, push the current Tweet to Prowl, if it isn't,
        //check to see if it is the first run, if it is, break out
        //of the for loop, if it isn't the first run, update the
        //lastreply.txt file and break from the forloop.
        for ($i = 0; $i &lt; 10; $i++) {             $curr = $this->xml->status[$i]->id;
            if ($curr > $this->lastID) {
                $first = false;
                $full = "@" . $this->xml->status[$i]->user->screen_name .
                        ": " . $this->xml->status[$i]->text;
                $this->prowl($full);
            } else {
                if ($first) {
                    break;
                } else {
                    $this->updateNewest($this->xml->status[($i - 1)]->id);
                    break;
                }
            }
        }
    }
 
    /**
     * Writes the newest @reply id to a file.
     */
    function updateNewest($id) {
        $file = fopen("lastreply.txt", "w");
        fwrite($file, $id);
        fclose($file);
    }
 
    /**
     * Push the Tweet to Prowl. This code is modified from
     * example.php in the ProwlPHP API Wrapper.
     */
    function prowl($tweet) {
 
        $this->prowl->push(array(
            'application' => 'Twitter',
            'event' => 'Reply',
            'description' => $tweet,
            'priority' => 0,
                ), true);
    }
 
    /**
     * Gets replies from Twitter. In order to grab replies, you
     * must be authenticated.
     */
    function getReplies() {
        $twitterHost = "http://twitter.com/statuses/mentions.xml";
        $curl;
        $curl = curl_init();
        curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, 2);
        curl_setopt($curl, CURLOPT_HEADER, false);
        curl_setopt($curl, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($curl, CURLOPT_USERPWD, "$this->tUser:$this->tPass");
        curl_setopt($curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
        curl_setopt($curl, CURLOPT_URL, $twitterHost);
        $result = curl_exec($curl);
        curl_close($curl);
        header('Content-Type: application/xml; charset=ISO-8859-1');
        return $result;
    }
}

I chose to add an entry in my crontab to run this script every 10 minutes. The time can be adjusted to suit your needs. Personally I do not see a need to ping Twitter more then once every ten minutes.

*/10 * * * * curl /path/to/replies.php

If all goes well, you should see something like this when someone mentions you in a tweet:

Prowl pushing a notification to the iPhone.

Prowl pushing a notification to the iPhone.

It is possible to setup a redirection within Prowl to automatically launch your Twitter client of choice when you get a Twitter based notification. You can also open Prowl and view all notifications:

Prowl Main Screen

Prowl Main Screen

The script should be fairly easy to modify. I will probably add Direct Messaging next to my Prowl Push Notifications.