Changeset 2067

Show
Ignore:
Timestamp:
02/16/2008 06:06:40 PM (11 months ago)
Author:
Shadowhand
Message:

Updated Kobot:

  • Added login capabilities
  • Proper log level handling
  • Created Kobot_Server and Kobot_Channel, to be more extensible
  • Added "info" and "register" demo commands
  • Added private message capabilities (user-to-user chat)
Location:
trunk/modules/kobot
Files:
2 added
2 modified

Legend:

Unmodified
Added
Removed
  • trunk/modules/kobot/controllers/kobot.php

    r2055 r2067  
    99 
    1010                // Enable debugging 
    11                 $bog->log_level = 4; 
     11                $bog->log_level = 1; 
    1212 
    1313                // Add triggers 
    1414                $bot->set_trigger('^goodnight, bot$', array($this, 'trigger_quit')) 
     15                    ->set_trigger('^register(.+)?$', array($this, 'register')) 
    1516                    ->set_trigger('^tell (.+?) about (.+)$', array($this, 'trigger_say')) 
    1617                    ->set_trigger('^([r|#])(\d+)$', array($this, 'trigger_trac')) 
    1718                    ->set_trigger('^[a-z_]+$', array($this, 'trigger_default')); 
    1819 
    19                 // Add timers 
    20                 $bot->set_timer(5, array($this, 'say_hi')); 
    21  
    2220                // Login and join the default channel 
    23                 $bot->login('koboto'); 
     21                $bot->login('koboto', 'PhoenixRisingKO'); 
    2422                $bot->join('#koboto'); 
    2523                $bot->read(); 
    2624        } 
     25 
     26        public function register(Kobot $bot, array $data, array $params) 
     27        { 
     28                if ($data['target'] === $bot->username) 
     29                { 
     30                        // Send the message back to the sender 
     31                        $data['target'] = $data['sender']; 
     32 
     33                        if (isset($params[1])) 
     34                        { 
     35                                if (preg_match('/[^a-z\d]+/i', $params[1])) 
     36                                { 
     37                                        // Send the confirmation message 
     38                                        $message = 'You have been registered as '.$data['sender']; 
     39                                } 
     40                                else 
     41                                { 
     42                                        // Send the registration error 
     43                                        $message = 'Passwords may only contain letters and numbers'; 
     44                                } 
     45                        } 
     46                        else 
     47                        { 
     48                                // Send the register usage, no password was supplied 
     49                                $message = 'Usage: register <password>'; 
     50                        } 
     51                } 
     52                else 
     53                { 
     54                        // Only allow registration in private messages 
     55                        $message = $data['sender'].': Send me a private message: /msg '.$bot->username.' register <password>'; 
     56                } 
     57 
     58                // Send the response message 
     59                $bot->send('PRIVMSG '.$data['target'].' :'.$message); 
     60        } 
     61 
    2762 
    2863        public function say_hi(Kobot $bot) 
     
    5085        public function trigger_say(Kobot $bot, array $data, array $params) 
    5186        { 
    52                 switch ($params[1]) 
     87                switch ($params[2]) 
    5388                { 
    5489                        case 'yourself': 
    55                                 $bot->send('PRIVMSG '.$data['target'].' :Who wants to know? '.$params[0].'? HA!'); 
     90                                $bot->send('PRIVMSG '.$data['target'].' :Who wants to know? '.$params[1].'? HA!'); 
    5691                        break; 
    5792                } 
  • trunk/modules/kobot/libraries/Kobot.php

    r2057 r2067  
    1818        public $log_level = 1; 
    1919 
    20         // Command responses and timers 
     20        // Command responses, timers, and triggers 
    2121        protected $responses = array(); 
    2222        protected $timers = array(); 
     23        protected $triggers = array(); 
    2324 
    2425        // Responses to drop by default 
     
    3132                '004', 
    3233                '005', 
     34                '250', 
    3335                '251', 
    3436                '252', 
     
    3739                '265', 
    3840                '266', 
    39                 '250', 
    4041                '366', 
    4142                '477', 
     
    4344 
    4445        // IRC socket, username, MOTD, and stats 
    45         protected $socket; 
     46        protected $server; 
    4647        protected $username; 
    4748        protected $motd; 
     
    5758        protected $channels = array(); 
    5859 
    59         public function __construct($server, $port = NULL, $timeout = NULL) 
     60        public function __construct($host, $port = NULL, $timeout = NULL) 
    6061        { 
    6162                if (PHP_SAPI !== 'cli') 
     
    7273                set_exception_handler(array($this, 'exception_handler')); 
    7374 
     75                // Add the identify event 
     76                Event::add('kobot.motd_read', array($this, 'login_identify')); 
     77 
    7478                // Set the port 
    7579                empty($port) and $port = 6667; 
     
    7882                empty($timeout) and $timeout = 10; 
    7983 
    80                 // Disable error reporting 
    81                 $ER = error_reporting(0); 
    82  
    83                 if ($this->socket = fsockopen($server, $port, $errno, $errstr, $timeout)) 
    84                 { 
    85                         // Enable error reporting 
    86                         error_reporting($ER); 
    87  
    88                         // Set the start time 
    89                         $this->stats['start'] = microtime(TRUE); 
    90  
    91                         // Keep the response time as short as possible, for greater interactivity 
    92                         stream_set_blocking($this->socket, 0); 
     84                // Set the start time 
     85                $this->stats['start'] = microtime(TRUE); 
     86 
     87                // Load the server object 
     88                $this->server = new Kobot_Server($host, $port, $timeout); 
     89 
     90                if ($this->server->connect()) 
     91                { 
     92                        // Set the default responses 
     93                        $this->default_responses(); 
    9394 
    9495                        // Connection is complete 
    95                         $this->log(1, 'Connected to '.$server.':'.$port); 
    96  
    97                         // Read the PING command 
    98                         $this->set_response('PING', array($this, 'response_ping')); 
    99  
    100                         // Read the MOTD command 
    101                         $this->set_response('375', array($this, 'response_motd')); 
    102                         $this->set_response('372', array($this, 'response_motd')); 
    103                         $this->set_response('376', array($this, 'response_motd')); 
    104  
    105                         // Read the USERS command 
    106                         $this->set_response('353', array($this, 'response_userlist')); 
    107  
    108                         // Read the JOIN command 
    109                         $this->set_response('JOIN', array($this, 'response_join')); 
    110  
    111                         // Read the PART command 
    112                         $this->set_response('PART', array($this, 'response_part')); 
    113  
    114                         // Read the PRIVMSG command 
    115                         $this->set_response('PRIVMSG', array($this, 'response_privmsg')); 
    116  
    117                         foreach ($this->dropped as $cmd) 
    118                         { 
    119                                 // Drop all requested commands 
    120                                 $this->set_response($cmd, array($this, 'response_drop')); 
    121                         } 
     96                        $this->log(1, 'Connected to '.$this->server->host.':'.$this->server->port); 
    12297                } 
    12398                else 
    12499                { 
    125100                        // Nothing left to do if the connection fails 
    126                         $this->log(1, 'Could not to connect to '.$server.':'.$port.' in less than '.$timeout.' seconds: '.$errstr); 
     101                        $this->log(1, 'Could not to connect to '.$this->server->host.':'.$this->server->port.' in less than '.$this->server->timeout.' seconds: '.$this->server->error); 
    127102                        exit; 
    128103                } 
    129104        } 
    130105 
     106        public function default_responses() 
     107        { 
     108                // Read the PING command 
     109                $this->set_response('PING', array($this, 'response_ping')); 
     110 
     111                // Read the MOTD command 
     112                $this->set_response('375', array($this, 'response_motd')); 
     113                $this->set_response('372', array($this, 'response_motd')); 
     114                $this->set_response('376', array($this, 'response_motd')); 
     115 
     116                // Read the JOINTOPIC command 
     117                $this->set_response('332', array($this, 'response_topic')); 
     118 
     119                // Read the USERS command 
     120                $this->set_response('353', array($this, 'response_userlist')); 
     121 
     122                // Read the TOPIC command 
     123                $this->set_response('TOPIC', array($this, 'response_topic')); 
     124 
     125                // Read the JOIN command 
     126                $this->set_response('JOIN', array($this, 'response_join')); 
     127 
     128                // Read the PART command 
     129                $this->set_response('PART', array($this, 'response_part')); 
     130 
     131                // Read the PRIVMSG command 
     132                $this->set_response('PRIVMSG', array($this, 'response_privmsg')); 
     133 
     134                // Read the "info" trigger 
     135                $this->set_trigger('^info ([^\s]+)$', array($this, 'trigger_info')); 
     136 
     137                foreach ($this->dropped as $cmd) 
     138                { 
     139                        // Drop all requested commands 
     140                        $this->set_response($cmd, array($this, 'response_drop')); 
     141                } 
     142        } 
     143 
     144        public function __get($key) 
     145        { 
     146                if (isset($this->$key)) 
     147                { 
     148                        return $this->$key; 
     149                } 
     150        } 
     151 
    131152        public function log($level, $message) 
    132153        { 
    133                 if ($level >= $this->log_level) 
     154                if ($this->log_level >= $level) 
    134155                { 
    135156                        // Display the message with a timestamp, flush the output 
    136157                        echo date('Y-m-d g:i:s').' --- '.$message."\n"; flush(); 
    137158                } 
     159 
     160                return TRUE; 
    138161        } 
    139162 
     
    191214        { 
    192215                // Store the trigger and it's callback 
    193                 $this->msg_triggers[$pattern] = $callback; 
     216                $this->triggers[$pattern] = $callback; 
    194217 
    195218                return $this; 
     
    199222        { 
    200223                // Remove the trigger 
    201                 unset($this->msg_triggers[$pattern]); 
     224                unset($this->triggers[$pattern]); 
    202225 
    203226                return $this; 
     
    210233        public function send($command) 
    211234        { 
    212                 if (feof($this->socket)) 
     235                if (feof($this->server->socket)) 
    213236                { 
    214237                        // The socket has been terminated unexpectedly. Abort, now! 
     
    217240                } 
    218241 
    219                 if (fwrite($this->socket, $command.self::$newline)) 
     242                if (fwrite($this->server->socket, $command.self::$newline)) 
    220243                { 
    221244                        // Log the sent command 
    222                         $this->log(2, '>>> '.$command); 
     245                        $this->log(3, '>>> '.$command); 
    223246 
    224247                        // Update the stats 
     
    234257        public function read() 
    235258        { 
    236                 while ( ! feof($this->socket)) 
     259                while ( ! feof($this->server->socket)) 
    237260                { 
    238261                        // Start a new read loop 
     
    240263 
    241264                        // Read the raw server stream, up to 1024 characters 
    242                         while ($raw = fgets($this->socket, 1024)) 
     265                        while ($raw = fgets($this->server->socket, 1024)) 
    243266                        { 
    244267                                // Update the last received time 
     
    253276                                        call_user_func($this->responses[$data['command']], $data); 
    254277                                } 
    255                                 else 
    256                                 { 
    257                                         // Debug the response 
    258                                         $this->log(3, '<<< '.$data['command'].' <<< '.trim($raw)); 
    259                                 } 
     278 
     279                                // Debug the response 
     280                                $this->log(3, '<<< '.$data['command'].' <<< '.trim($raw)); 
    260281                        } 
    261282 
     
    422443        public function login($username, $password = NULL, $realname = 'Kohana PHP Bot') 
    423444        { 
    424                 // Cache the current username 
     445                // Cache the current username and password 
    425446                $this->username = $username; 
     447                $this->password = $password; 
    426448 
    427449                // Send the login commands, use 8 for the mask (invisible) 
     
    433455        } 
    434456 
    435         public function join($channel) 
     457        public function login_identify() 
     458        { 
     459                // Send the IDENTIFY command 
     460                $this->send('PRIVMSG NickServ :IDENTIFY '.$this->password); 
     461        } 
     462 
     463        public function join($channel, $password = '') 
    436464        { 
    437465                if (empty($this->channels[$channel])) 
    438466                { 
    439                         // Set the channel as joined 
    440                         $this->channels[$channel] = array(); 
     467                        // Create a new channel 
     468                        $this->channels[$channel] = new Kobot_Channel($channel, $password); 
    441469 
    442470                        // Join the channel 
    443                         $this->send('JOIN '.$channel); 
     471                        $this->send('JOIN '.trim($channel.' '.$password)); 
    444472                } 
    445473        } 
     
    475503 
    476504        // PING 
    477         public function response_ping($data) 
     505        public function response_ping(array $data) 
    478506        { 
    479507                // Update the stats 
     
    485513 
    486514        // 375, 372+, 376 
    487         public function response_motd($data) 
     515        public function response_motd(array $data) 
    488516        { 
    489517                switch ($data['command']) 
     
    503531                                // Make the MOTD into a string 
    504532                                $this->motd = implode("\n", $this->motd); 
     533 
     534                                // Run the motd_read event 
     535                                Event::run('kobot.motd_read'); 
    505536                        break; 
    506537                } 
    507538        } 
    508539 
     540        // TOPIC 
     541        public function response_topic(array $data) 
     542        { 
     543                if ($data['command'] === '332') 
     544                { 
     545                        // Remove the user from the target 
     546                        list ($user, $data['target']) = explode(' ', $data['target']); 
     547                } 
     548 
     549                if (isset($this->channels[$data['target']])) 
     550                { 
     551                        // Set the channel topic 
     552                        $this->channels[$data['target']]->topic = $data['message']; 
     553 
     554                        // Log the topic change 
     555                        $this->log(2, 'Topic of '.$data['target'].' changed: '.$data['message']); 
     556                } 
     557        } 
     558 
    509559        // 353, 366 
    510         public function response_userlist($data) 
     560        public function response_userlist(array $data) 
    511561        { 
    512562                if (strpos($data['target'], ' @ ') !== FALSE) 
     
    516566 
    517567                        // Set the current users 
    518                         $this->channels[$channel] = explode(' ', $data['message']); 
     568                        $this->channels[$channel]->users = ($users = explode(' ', $data['message'])); 
    519569 
    520570                        // Log the user count 
    521                         $this->log(1, 'Found '.count($this->channels[$channel]).' users in channel'); 
     571                        $this->log(2, 'Found '.count($users).' users in channel'); 
     572 
     573                        // Log the channel join 
     574                        $this->log(1, 'Joined '.$channel); 
    522575                } 
    523576        } 
    524577 
    525578        // JOIN 
    526         public function response_join($data) 
     579        public function response_join(array $data) 
    527580        { 
    528581                // Make sure the bot is joined to the target channel 
    529582                if (isset($this->channels[$data['target']])) 
    530583                { 
    531                         // Only add the user if they are not already in the list 
    532                         if ( ! in_array($data['sender'], $this->channels[$data['target']])) 
    533                         { 
    534                                 // Add the sender to the channel userlist 
    535                                 $this->channels[$data['target']][] = $data['sender']; 
    536  
    537                                 // This prevents the userlist key from growing too large, causing a buffer overflow 
    538                                 $this->channels[$data['target']] = array_values($this->channels[$data['target']]); 
    539  
    540                                 // Debug the join 
    541                                 $this->log(2, '> '.$data['sender'].' ('.$data['target'].')'); 
    542                         } 
     584                        // Add the user to the channel 
     585                        $this->channels[$data['target']]->user_join($data['sender']); 
     586 
     587                        // Debug the join 
     588                        $this->log(2, '> '.$data['sender'].' ('.$data['target'].')'); 
    543589                } 
    544590        } 
    545591 
    546592        // PART 
    547         public function response_part($data) 
     593        public function response_part(array $data) 
    548594        { 
    549595                // Make sure the bot is joined to the target channel 
    550596                if (isset($this->channels[$data['target']])) 
    551597                { 
    552                         // Only remove the user if they are in the list 
    553                         if (($key = array_search($data['sender'], $this->channels[$data['target']])) !== FALSE) 
    554                         { 
    555                                 // Remove the sender from the channel userlist 
    556                                 unset($this->channels[$data['target']][$key]); 
    557  
    558                                 // Debug the join 
    559                                 $this->log(2, '< '.$data['sender'].' ('.$data['target'].')'); 
    560                         } 
    561                 } 
    562         } 
    563  
    564         public function response_privmsg($data) 
     598                        // Remove the user from the channel 
     599                        $this->channels[$data['target']]->user_part($data['sender']); 
     600 
     601                        // Debug the join 
     602                        $this->log(2, '< '.$data['sender'].' ('.$data['target'].')'); 
     603                } 
     604        } 
     605 
     606        // PRIVMSG 
     607        public function response_privmsg(array $data) 
    565608        { 
    566609                if ($data['message'] === chr(1).'VERSION'.chr(1)) 
     
    569612                        $this->response_version($data); 
    570613                } 
    571                 elseif (substr($data['message'], 0, strlen($this->username)) === $this->username 
    572                     AND $trigger = trim(substr($data['message'], strlen($this->username)), ' :')) 
     614                elseif 
     615                (( 
     616                        // Private messages, reply to the sender and log 
     617                        $data['target'] === $this->username 
     618                        AND $trigger = trim($data['message']) 
     619                        AND $this->log(2, 'Private message from '.$data['sender'].' received') 
     620                ) 
     621                OR 
     622                ( 
     623                        // Channel messages 
     624                        substr($data['message'], 0, strlen($this->username)) === $this->username 
     625                        AND $trigger = trim(substr($data['message'], strlen($this->username)), ' :') 
     626                )) 
    573627                { 
    574628                        // Process triggers 
    575                         foreach ($this->msg_triggers as $pattern => $func) 
     629                        foreach ($this->triggers as $pattern => $func) 
    576630                        { 
    577631                                if (preg_match('/'.$pattern.'/', $trigger, $matches)) 
     
    585639        } 
    586640 
    587         public function response_version($data) 
     641        public function response_version(array $data) 
    588642        { 
    589643                // Send a CTCP VERSION response 
     
    591645        } 
    592646 
     647        /** 
     648         * Default triggers. 
     649         */ 
     650 
     651        protected function trigger_info(Kobot $bot, array $data, array $params) 
     652        { 
     653                if (isset($this->channels[$data['target']])) 
     654                { 
     655                        switch ($params[1]) 
     656                        { 
     657                                case 'topic': 
     658                                        $this->send('PRIVMSG '.$data['target'].' :'.$data['sender'].': '.$this->channels[$data['target']]->topic); 
     659                                break; 
     660                                default: 
     661                                        $this->log(1, var_export($data, TRUE)); 
     662                                break; 
     663                        } 
     664                } 
     665        } 
     666 
    593667} // End Kobot