<?php
/***************************************************************************************************
 * BeBot - An Anarchy Online & Age of Conan Chat Automaton
 * Copyright (C) 2004 Jonas Jax
 * Copyright (C) 2005-2010 Thomas Juberg, ShadowRealm Creations and the BeBot development team.
 *
 * Developed by:
 * - Alreadythere (RK2)
 * - Blondengy (RK1)
 * - Blueeagl3 (RK1)
 * - Glarawyn (RK1)
 * - Khalem (RK1)
 * - Naturalistic (RK1)
 * - Temar (RK1)
 *
 * See Credits file for all acknowledgements.
 ***************************************************************************************************
 * AAMon module for Bebot
 * This module lets users keep track of their AA training in a central database.
 * Users can later get hold of their data using external clients (WIP).
 *
 * Author:    Kentarii [Ragnarok] @ EN Fury PvP
 * E-mail:    Does not take a rocket scientist to figure out..
 * Website:    http://aoc.is-better-than.tv/aamon.php
 ***************************************************************************************************
 * Installation:
 *        Copy AAMon.phps to <bebot_dir>/custom/modules/AAMon.php and restart bot
 * Disable module:
 *        Rename AAMon.php to _AAMon.php and restart bot
 * Uninstallation:
 *        Delete <bebot_dir>/custom/modules/AAMon.php and restart bot
 ***************************************************************************************************
 * Changelog:
 *    2011-09-28    0.0.12    Add support for registering an e-mail to a character
 *    2011-06-15    0.0.11    Automatically update apiurl when initializing if changed
 *    2011-06-14    0.0.10    Update AAMon apiurl
 *    2011-03-05    0.0.9    Fix html entity decoding of skillnames.
 *    2010-12-26    0.0.8    Change some info text. Add agent/version string to requests.
 *                        Add fetch_api_data function for querying API.
 *    2010-11-27    0.0.7    Add "AuthKey" for easier copy/paste of authentication from chat to the
 *                        AAMon for Windows client.
 *    2010-11-26    0.0.6    Add new version check. Change some text. First public release.
 *    2010-11-26    0.0.5    Change text for skill which has completed for show.
 *    2010-11-25    0.0.4    Add more info to apikey call. Fix sorting for showall.
 *    2010-11-24    0.0.3    Add function to retrieve API key for character, fixed some colors and bugs.
 *    2010-11-23    0.0.2    First closed beta release.
 *    2010-11-22    0.0.1    Started coding on this module, replicated all functionality of timeraa.
 ***************************************************************************************************
 * Credits:
 *        Getrix for the original TimerAA module
 ***************************************************************************************************
 */

$AAMon = new AAMon($bot);

class 
AAMon extends BaseActiveModule {
    var 
$bot;
    var 
$agent;
    var 
$version;
    var 
$aoctv_version;

    function 
__construct (&$bot) {
        
parent::__construct(&$botget_class($this));

        
$this -> agent 'AAMon module for Bebot';
        
$this -> version '0.0.12';
        
$this -> bot -> core('settings') -> create('AAMon''apiurl''http://aoc.is-better-than.tv/api.php'"URL to AoC>TV API.");
        
$this -> bot -> core('settings') -> create('AAMon''botkey'''"AoC>TV API Key.");

        if (
$this -> init()) {
            
$this -> register_command('all''aamon''MEMBER');

            
$this -> help['description'] = "This module lets users keep track of their AA training in a central database.";
            
$this -> help['command']['aamon set <duration> [skillname] [skillrank]'] = "Set the duration for your current AA skill training. Duration should be hours left. Skillname is the name of the skill (optional). Skillrank should be a number from 1 to 5 (optional).";
            
$this -> help['command']['aamon show [nickname]'] = "Show how long time left on AA skill training for [nickname].";
            
$this -> help['command']['aamon showall [nickname]'] = "Show how long time left on AA skill training for all chars of [nickname].";
            
$this -> help['command']['aamon apikey'] = "Retrieve AuthKey/ApiKey for your character so that you can access the data outside of the game.";
            
$this -> help['command']['aamon email'] = "Show current registered e-mail and status for your character.";
            
$this -> help['command']['aamon register <email>'] = "Register an e-mail address to enable notificiation e-mails to be sent when a skill finishes.";
            
$this -> help['notes'] = sprintf("AAMon v##lightbeige##%s##end##, created by Kentarii [Ragnarok] @ EN Fury PvP 2008-%d\nDownload new versions and AAMon for Windows @ http://aoc.is-better-than.tv/aamon.php\n"$this -> versiondate('Y'));
        }
        else {
            
$this -> bot -> log('AAMon''ERROR''Bot failed authentication, make sure you set the right botkey in settings. If you have lost your botkey, contact Kentarii @ AoC>TV Forums.');
        }
    }

    
/**
     * This function registers with the central database if connected for the first time.
     * If already registered, it validates the apikey.
     * @return bool
     */
    
function init() {
        
$botkey $this -> bot -> core('settings') -> get('AAMon''botkey');
        
$data $this -> fetch_api_data('aamon/bot/init');
        if (!
$data['error'] && $data['content']) {
            try {
                
$xml simplexml_load_string($data['content']);
                if (
intval($xml -> status) == 1) {
                    
$this -> aoctv_version = (string) $xml -> data -> version;
                    
// save new botkey to settings
                    
if ($botkey != (string)$xml -> data -> apikey) {
                        
$this -> bot -> core('settings') -> save('AAMon''botkey', (string)$xml -> data -> apikey);
                    }
                    
// update apiurl if needed
                    
if ($this -> bot -> core('settings') -> get('AAMon''apiurl') != (string)$xml -> data -> apiurl) {
                        
$this -> bot -> core('settings') -> save('AAMon''apiurl', (string)$xml -> data -> apiurl);
                    }
                    return 
true;
                }
            }
            catch (
Exception $ex) {
                return 
false;
            }
        }
        return 
false;
    }

    
/**
     * Command handler for module
     * @param string $name
     * @param string $msg
     * @param string $origin
     * @return string
     */
    
function command_handler($name$msg$origin) {
        if (
preg_match("/^aamon set ([\d]+) ([\w\' ]+) ([\d]{1})$/"$msg$m)) {
            
$duration intval($m[1]);
            
$skillname trim($m[2]);
            
$skillrank intval($m[3]);
            
$rv $this -> aa_set($name$duration$skillname$skillrank) ."##end##";
        }
        else if (
preg_match("/^aamon set ([\d]+) ([\w\' ]+)$/i"$msg$m)) {
            
$duration intval($m[1]);
            
$skillname trim($m[2]);
            
$rv $this -> aa_set($name$duration$skillname) ."##end##";
        }
        else if (
preg_match("/^aamon set ([\d]+)$/i"$msg$m)) {
            
$duration intval($m[1]);
            
$rv $this -> aa_set($name$duration) ."##end##";
        }
        else if (
preg_match("/^aamon show ([\w]+)$/i"$msg$m)) {
            
$char_name ucfirst(strtolower(trim($m[1])));
            
$rv $this -> aa_show($char_name) ."##end##";
        }
        else if (
preg_match("/^aamon show$/i"$msg)) {
            
$rv $this -> aa_show($name) ."##end##";
        }
        else if (
preg_match("/^aamon showall ([\w]+)$/i"$msg$m)) {
            
$char_name ucfirst(strtolower(trim($m[1])));
            
$rv $this -> aa_showall($char_name) ."##end##";
        }
        else if (
preg_match("/^aamon showall$/i"$msg)) {
            
$rv $this -> aa_showall($name) ."##end##";
        }
        else if (
preg_match("/^aamon apikey$/i"$msg)) {
            
$this -> bot -> send_tell($name$this -> apikey_get($name));
            return 
false;
        }
        else if (
preg_match("/^aamon email$/i"$msg)) {
            
$this -> bot -> send_tell($name$this -> email_get($name));
            return 
false;
        }
        else if (
preg_match("/^aamon register (.*)$/i"$msg$m)) {
            
$email strtolower(trim($m[1]));
            
$this -> bot -> send_tell($name$this -> email_set($name$email));
            return 
false;
        }
        else { 
// show help
            
$this -> bot -> send_help($name'aamon');
            return 
false;
        }
        return 
"##white####clan##[-AAMon-]##end## :: "$rv;
    }

    
/**
     * Fetch data from the AoC>TV API
     * @param string $op
     * @param array $params
     * @return array
     */
    
function fetch_api_data($op$params = array()) {
        
$url sprintf("%s?op=%s&dimension=%s&botname=%s&botkey=%s&agent=%s&version=%s",
            
$this -> bot -> core('settings') -> get('AAMon''apiurl'),
            
$op,
            
urlencode($this -> bot -> dimension),
            
urlencode($this -> bot -> botname),
            
urlencode($this -> bot -> core('settings') -> get('AAMon''botkey')),
            
urlencode($this -> agent),
            
urlencode($this -> version)
        );
        if (
count($params) > 0) {
            foreach (
$params as $k => $v) {
                
$url .= '&'$k .'='urlencode($v);
            }
        }
        return 
$this -> bot -> core('tools') -> get_site($url1);
    }

    
/**
     * Set skill training for selected character
     * @param string $char_name
     * @param string $duration
     * @param string $skillname
     * @param string $skillrank
     * @return string
     */
    
function aa_set($char_name$duration$skillname 'Unknown Skill'$skillrank 1) {
        if (
$char_id $this -> bot -> core('chat') -> get_uid($char_name)) {
            
$data $this -> fetch_api_data('aamon/aa/set', array(
                
'char_id' => $char_id,
                
'nickname' => $char_name,
                
'duration' => $duration,
                
'skillname' => $skillname,
                
'skillrank' => $skillrank,
            ));
            if (!
$data['error'] && $data['content']) {
                try {
                    
$xml simplexml_load_string($data['content']);
                    if (
intval($xml -> status) == 1) {
                        
$this -> aoctv_version = (string) $xml -> data -> version;
                        return 
sprintf("Timer added for ##seablue##%s##end##. Training ##forestgreen##%s##end## rank ##forestgreen##%d##end## on a ##forestgreen##%dh##end## timer. ETA: ##yellow##%s##end## UTC, time left: ##yellow##%s##end##."$char_name$skillname$skillrank$duration$xml -> data -> end_dt$xml -> data -> countdown);
                    }
                    else {
                        return 
sprintf("##red##An error occured when trying to add skill training for ##seablue##%s##end##: %s##end##"$char_name$xml -> message);
                    }
                }
                catch (
Exception $ex) {
                    return 
sprintf("##red##An error occured when parsing xml: %s##end##"$ex->getMessage());
                }
            }
            return 
"##red##An error occured when fetching info from database, please try again later.##end##";
        }
        return 
sprintf("##red##Character ##seablue##%s##end## is not a member of this bot.##end##"$char_name);
    }

    
/**
     * Show skill training for selected character
     * @param string $char_name
     * @return string
     */
    
function aa_show($char_name) {
        if (
$char_id $this -> bot -> core('chat') -> get_uid($char_name)) {
            
$data $this -> fetch_api_data('aamon/aa/get', array('char_id' => $char_id));
            if (!
$data['error'] && $data['content']) {
                try {
                    
$xml simplexml_load_string($data['content']);
                    if (
intval($xml -> status) == 1) {
                        if (
$xml -> data -> character -> current_skill_id == 0) {
                            return 
sprintf("%s has no skill in training at the moment."$this -> aa_history($char_name$xml));
                        }
                        else if (
$xml -> data -> character -> current -> seconds == 0) {
                            return 
sprintf("%s has completed skill training of ##forestgreen##%s##end## rank ##forestgreen##%d##end## @ ##yellow##%s##end## UTC.",
                                
$this -> aa_history($char_name$xml),
                                
html_entity_decode($xml -> data -> character -> current -> skillname),
                                
$xml -> data -> character -> current -> skillrank,
                                
$xml -> data -> character -> current -> end_dt
                            
);
                        }
                        else {
                            return 
sprintf("%s is currently training ##forestgreen##%s##end## rank ##forestgreen##%d##end## on a ##forestgreen##%dh##end## timer. ETA: ##yellow##%s##end## UTC, time left: ##yellow##%s##end##.",
                                
$this -> aa_history($char_name$xml),
                                
html_entity_decode($xml -> data -> character -> current -> skillname),
                                
$xml -> data -> character -> current -> skillrank,
                                
$xml -> data -> character -> current -> duration,
                                
$xml -> data -> character -> current -> end_dt,
                                
$xml -> data -> character -> current -> countdown
                            
);
                        }
                    }
                    else {
                        return 
sprintf("##red##An error occured when trying to get skill training for ##seablue##%s##end##: %s##end##"$char_name$xml -> message);
                    }
                }
                catch (
Exception $ex) {
                    return 
sprintf("##red##An error occured when parsing xml: %s"$ex->getMessage());
                }
            }
            return 
"##red##An error occured when fetching info from database, please try again later.##end##";
        }
        return 
sprintf("##red##Character ##seablue##%s##end## is not a member of this bot.##end##"$char_name);
    }

    
/**
     * Create a string/blob with AA history for a character
     * @param string $char_name
     * @param SimpleXMLElement $xml
     * @return string
     */
    
function aa_history($char_name$xml) {
        if (
count($xml -> data -> character -> history -> skill) > 0) {
            
$content sprintf("<center><font face='hyborianlarge' color='#ff9933'>AA History for %s</font></center><br>"$char_name);
            foreach (
$xml -> data -> character -> history -> skill as $skill) {
                if (
intval($xml -> data -> character -> current_skill_id) == intval($skill -> id)) {
                    
$content .= sprintf("##forestgreen##%s##end## rank ##forestgreen##%d##end## :: ##forestgreen##%dh##end## :: ##yellow##%s##end## -> ##yellow##%s##end## :: ##yellow##%s##end##<br>",
                        
html_entity_decode($skill -> skillname),
                        
$skill -> skillrank,
                        
$skill -> duration,
                        
substr($skill -> start_dt010),
                        
substr($skill -> end_dt010),
                        
$skill -> countdown
                    
);
                }
                else {
                    
$content .= sprintf("##forestgreen##%s##end## rank ##forestgreen##%d##end## :: ##forestgreen##%dh##end## :: ##yellow##%s##end## -> ##yellow##%s##end##<br>",
                        
html_entity_decode($skill -> skillname),
                        
$skill -> skillrank,
                        
$skill -> duration,
                        
substr($skill -> start_dt010),
                        
substr($skill -> end_dt010)
                    );
                }
            }
            
$this -> version_notify($content);
            return 
$this -> bot -> core('tools') -> make_blob($char_name'##white##'$content .'##end##');
        }
        else {
            return 
sprintf("##seablue##%s##end##"$char_name);
        }
    }

    
/**
     * Show skill training for all alts of selected character
     * @param string $char_name
     * @return string
     */
    
function aa_showall($char_name) {
        if (
$char_id $this -> bot -> core('chat') -> get_uid($char_name)) {
            
$chars = array();
            
$rows $this -> bot -> db -> select(sprintf("SELECT main FROM alts WHERE alt = '%s'"mysql_real_escape_string($char_name)), MYSQL_ASSOC);
            if (
is_array($rows) && count($rows) == 1) {
                
$main_char_name $rows[0]['main']; //
                
$main_char_id $this -> bot -> core('chat') -> get_uid($main_char_name);
                if (
$main_char_id 0) {
                    
$chars[$main_char_id] = array('char_id' => $main_char_id'nickname' => $main_char_name);
                }
            }
            else {
                
$main_char_name $char_name;
                
$main_char_id $char_id;
                
$chars[$main_char_id] = array('char_id' => $main_char_id'nickname' => $main_char_name);
            }
            
$rows $this -> bot -> db -> select(sprintf("SELECT alt FROM alts WHERE main = '%s'"mysql_real_escape_string($main_char_name)), MYSQL_ASSOC);
            if (
is_array($rows) && count($rows) > 0) {
                foreach (
$rows as $row) {
                    
$cid $this -> bot -> core('chat') -> get_uid($row['alt']);
                    if (
$cid 0) {
                        
$chars[$cid] = array('char_id' => $cid'nickname' => $row['alt']);
                    }
                }
            }
            foreach (
$chars as $cid => $char) {
                
$data $this -> fetch_api_data('aamon/aa/get', array('char_id' => $cid));
                if (!
$data['error'] && $data['content']) {
                    try {
                        
$xml simplexml_load_string($data['content']);
                        if (
intval($xml -> status) == 1) {
                            if (
$xml -> data -> character -> current_skill_id == 0) {
                                
$char['result'] = sprintf("##seablue##%s##end## has no skill in training at the moment."$char['nickname']);
                                
$char['seconds'] = -1;
                            }
                            else if (
$xml -> data -> character -> current -> seconds == 0) {
                                
$char['result'] = sprintf("##seablue##%s##end## has completed skill training of ##forestgreen##%s##end## rank ##forestgreen##%d##end## @ ##yellow##%s##end## UTC.",
                                    
$char['nickname'],
                                    
html_entity_decode($xml -> data -> character -> current -> skillname),
                                    
$xml -> data -> character -> current -> skillrank,
                                    
$xml -> data -> character -> current -> end_dt
                                
);
                                
$char['seconds'] = 0;
                            }
                            else {
                                
$char['result'] = sprintf("##seablue##%s##end## is currently training ##forestgreen##%s##end## rank ##forestgreen##%d##end## on a ##forestgreen##%dh##end## timer.<br>ETA: ##yellow##%s##end## UTC, time left: ##yellow##%s##end##.",
                                    
$char['nickname'],
                                    
html_entity_decode($xml -> data -> character -> current -> skillname),
                                    
$xml -> data -> character -> current -> skillrank,
                                    
$xml -> data -> character -> current -> duration,
                                    
$xml -> data -> character -> current -> end_dt,
                                    
$xml -> data -> character -> current -> countdown
                                
);
                                
$char['seconds'] = $xml -> data -> character -> current -> seconds;
                            }
                        }
                        else {
                            
$char['result'] = sprintf("##seablue##%s##end## has not been registered."$char['nickname']);
                            
$char['seconds'] = 10000000// sort this at the bottom
                        
}
                    }
                    catch (
Exception $ex) {
                        
$char['result'] = sprintf("##red##An error occured when parsing xml for ##seablue##%s##end##: %s"$char['nickname'], $ex->getMessage());
                        
$char['seconds'] = 20000000// sort this at the bottom
                    
}
                }
                else {
                    
$char['result'] = sprintf("##red##An error occured when fetching info from database for ##seablue##%s##end##, please try again later.##end##"$char['nickname']);
                    
$char['seconds'] = 30000000// sort this at the bottom
                
}
                
$chars[$cid] = $char;
            }
            
$content sprintf("<center><font face='hyborianlarge' color='#ff9933'>AA Skill Training for %s and alts</font></center><br>"$main_char_name);
            
usort($chars, array($this'char_cmp'));
            foreach (
$chars as $c) {
                
$content .= $c['result'] .'<br><b></b><br>';
            }
            
$this -> version_notify($content);
            return 
sprintf("AA Skill Training for %s and alts"$this -> bot -> core('tools') -> make_blob($main_char_name'##white##'$content .'##end##'));
        }
        return 
sprintf("##red##Character ##seablue##%s##end## is not a member of this bot.##end##"$char_name);
    }

    
/**
     * Sort by seconds left or nickname if equal
     * @param array $a
     * @param array $b
     * @return int
     */
    
function char_cmp($a$b) {
        if (
intval($a['seconds']) == intval($b['seconds'])) {
            if (
$a['nickname'] == $b['nickname']) return 0;
            return (
$a['nickname'] < $b['nickname']) ? -1;
        }
        return (
intval($a['seconds']) < intval($b['seconds'])) ? -1;
    }

    
/**
     * Retrieve and send API key to character through tell
     */
    
function apikey_get($char_name) {
        if (
$char_id $this -> bot -> core('chat') -> get_uid($char_name)) {
            
$data $this -> fetch_api_data('aamon/char/apikey', array('char_id' => $char_id));
            if (!
$data['error'] && $data['content']) {
                try {
                    
$xml simplexml_load_string($data['content']);
                    if (
intval($xml -> status) == 1) {
                        return 
sprintf("Authentication details for ##seablue##%s##end## :: [AuthKey: ##forestgreen##%s;%s;%s##end##] :: [Dimension: ##forestgreen##%s##end##][CharID: ##forestgreen##%s##end##][ApiKey: ##forestgreen##%s##end##]",
                            
$char_name,
                            
$xml -> data -> dimension,
                            
$xml -> data -> char_id,
                            
$xml -> data -> apikey,
                            
$xml -> data -> dimension,
                            
$xml -> data -> char_id,
                            
$xml -> data -> apikey
                        
);
                    }
                    else {
                        return 
sprintf("##red##An error occured when trying to get API key for ##seablue##%s##end##: %s##end##"$char_name$xml -> message);
                    }
                }
                catch (
Exception $ex) {
                    return 
sprintf("##red##An error occured when parsing xml: %s"$ex->getMessage());
                }
            }
            return 
"##red##An error occured when fetching info from database, please try again later.##end##";
        }
        return 
sprintf("##red##Character ##seablue##%s##end## is not a member of this bot.##end##"$char_name);
    }

    
/**
     * Retrieve and send e-mail info to character through tell
     */
    
function email_get($char_name) {
        if (
$char_id $this -> bot -> core('chat') -> get_uid($char_name)) {
            
$data $this -> fetch_api_data('aamon/char/email/get', array('char_id' => $char_id));
            if (!
$data['error'] && $data['content']) {
                try {
                    
$xml simplexml_load_string($data['content']);
                    if (
intval($xml -> status) == 1) {
                        
$email = (string) $xml -> data -> email;
                        return 
sprintf("E-mail details for ##seablue##%s##end## :: [E-mail address: ##forestgreen##%s##end##] :: [Verified: ##forestgreen##%s##end##]",
                            
$char_name,
                            (
$email) ? $email 'not registered',
                            (
intval($xml -> data -> verified) == 1) ? 'yes' 'no'
                        
);
                    }
                    else {
                        return 
sprintf("##red##An error occured when trying to get e-mail details for ##seablue##%s##end##: %s##end##"$char_name$xml -> message);
                    }
                }
                catch (
Exception $ex) {
                    return 
sprintf("##red##An error occured when parsing xml: %s"$ex->getMessage());
                }
            }
            return 
"##red##An error occured when fetching info from database, please try again later.##end##";
        }
        return 
sprintf("##red##Character ##seablue##%s##end## is not a member of this bot.##end##"$char_name);
    }

    
/**
     * Register e-mail address for a character
     */
    
function email_set($char_name$email) {
        if (
$char_id $this -> bot -> core('chat') -> get_uid($char_name)) {
            if (
$this -> is_email($email)) {
                
$data $this -> fetch_api_data('aamon/char/email/set', array('char_id' => $char_id'email' => $email));
                if (!
$data['error'] && $data['content']) {
                    try {
                        
$xml simplexml_load_string($data['content']);
                        if (
intval($xml -> status) == 1) {
                            return 
sprintf("E-mail address ##seablue##%s##end## has been registered for ##seablue##%s##end##, please check your e-mail and click on the confirmation link (remember to check your spam folder).",
                                
$email,
                                
$char_name
                            
);
                        }
                        else {
                            return 
sprintf("##red##An error occured when trying to register ##seablue##%s##end## as e-mail address for ##seablue##%s##end##: %s##end##"$email$char_name$xml -> message);
                        }
                    }
                    catch (
Exception $ex) {
                        return 
sprintf("##red##An error occured when parsing xml: %s"$ex->getMessage());
                    }
                }
                return 
"##red##An error occured when fetching info from database, please try again later.##end##";
            }
            return 
sprintf("##red####seablue##%s##end## is not a valid e-mail address.##end##"$email);
        }
        return 
sprintf("##red##Character ##seablue##%s##end## is not a member of this bot.##end##"$char_name);
    }

    function 
is_email($addr) {
        return 
preg_match('/[^ @<>]+?@([a-zA-Z0-9-]+\.)+[a-zA-Z0-9]+$/'$addr);
    }

    function 
version_notify(&$text) {
        if (
$this -> version != $this -> aoctv_version) {
            
$text .= sprintf("<b></b><br>AAMon module for Bebot v%s is now available (your Bebot is currently running v%s).<br>"$this -> aoctv_version$this -> version);
        }
    }
}
?>