Show
Ignore:
Timestamp:
02/14/2008 12:20:37 AM (11 months ago)
Author:
Shadowhand
Message:

Updating Kirc.... I think I need to sleep.

Files:
1 modified

Legend:

Unmodified
Added
Removed
  • trunk/modules/kirc/libraries/Kirc.php

    r2042 r2043  
    11<?php defined('SYSPATH') or die('No direct script access.'); 
    2  
     2/** 
     3 * Kohana IRC Bot. Yah, we do that too. 
     4 * 
     5 * $Id$ 
     6 * 
     7 * @package    Kirc 
     8 * @author     Woody Gilk 
     9 * @copyright  (c) 2007-2008 Kohana Team 
     10 * @license    http://kohanaphp.com/license.html 
     11 */ 
    312class Kirc_Core { 
    413 
    5         // Database instance 
    6         protected $db; 
    7  
    8         // ShIRC configuration 
    9         protected $config; 
    10  
    11         // IRC socket 
     14        // The characters that represent a newline 
     15        public static $newline = "\r\n"; 
     16 
     17        // Log level: 1 = errors, 2 = debug 
     18        public $log_level = 1; 
     19 
     20        // IRC socket and stats 
    1221        protected $socket; 
    13  
    14         // A newline 
    15         protected $newline = "\r\n"; 
    16  
    17         public function __construct() 
    18         { 
    19                 // Load the database 
    20                 $this->db = new Database('shirc'); 
    21  
    22                 // Load configuration 
    23                 $this->config = Config::item('shirc'); 
    24  
    25                 // Open the connection 
    26                 $this->connect(); 
    27                 $this->run(); 
    28         } 
    29  
    30         protected function connect() 
    31         { 
     22        protected $stats = array 
     23        ( 
     24                'start'              => 0, 
     25                'last_ping'          => 0, 
     26                'last_sent'          => 0, 
     27                'last_received'      => 0, 
     28                'commands_sent'      => 0, 
     29                'commands_received'  => 0, 
     30        ); 
     31 
     32        // Connected channels 
     33        protected $channels = array(); 
     34 
     35        public function __construct($server, $port = NULL, $timeout = NULL) 
     36        { 
     37                if (PHP_SAPI !== 'cli') 
     38                        throw new Kirc_Exception('kirc.command_line_only'); 
     39 
    3240                // Close all output buffers 
    3341                while (ob_get_level()) ob_end_clean(); 
    3442 
    35                 // Handle PHP errors inline 
    36                 set_error_handler(array($this, 'error')); 
    37  
    38                 // Make the script never terminate 
     43                // Keep-alive: TRUE 
    3944                set_time_limit(0); 
    4045 
    41                 // Open the socket 
    42                 $this->socket = fsockopen($this->config['server'], $this->config['port']); 
    43  
    44                 // Set non-blocking streams 
    45                 stream_set_blocking($this->socket, 0); 
    46  
    47                 // Connect 
    48                 $this->send('USER '.$this->config['username'].' * * '.$this->config['realname']); 
    49                 $this->send('NICK '.$this->config['username']); 
    50                 $this->send('JOIN '.$this->config['channel']); 
    51         } 
    52  
    53         public function error($error, $message, $file, $line) 
    54         { 
    55                 echo '[ERROR] '.$message.' >>> '.$file.': '.$line."\n"; 
    56         } 
    57  
    58         protected function send($cmd) 
    59         { 
    60                 $cmd .= $this->newline; 
    61                 fwrite($this->socket, $cmd); 
    62                 echo '[SEND] '.$cmd; 
    63                 flush(); 
     46                // Use internal an internal exception handler, to write logs 
     47                set_error_handler(array($this, 'exception_handler')); 
     48                set_exception_handler(array($this, 'exception_handler')); 
     49 
     50                // Set the port 
     51                empty($port) and $port = 6667; 
     52 
     53                // Set the timeout 
     54                empty($timeout) and $timeout = 10; 
     55 
     56                // Disable error reporting 
     57                $ER = error_reporting(0); 
     58 
     59                if ($this->socket = fsockopen($server, $port, $errno, $errstr, $timeout)) 
     60                { 
     61                        // Enable error reporting 
     62                        error_reporting($ER); 
     63 
     64                        // Set the start time 
     65                        $this->stats['start'] = microtime(TRUE); 
     66 
     67                        // Keep the response time as short as possible, for greater interactivity 
     68                        stream_set_blocking($this->socket, 0); 
     69 
     70                        // Connection is complete 
     71                        $this->log(1, 'Connected to '.$server.':'.$port); 
     72                } 
     73                else 
     74                { 
     75                        // Nothing left to do if the connection fails 
     76                        $this->log(1, 'Could not to connect to '.$server.':'.$port.' in less than '.$timeout.' seconds: '.$errstr); 
     77                        exit; 
     78                } 
     79        } 
     80 
     81        public function exception_handler($exception, $message = NULL, $file = NULL, $line = NULL) 
     82        { 
     83                if (func_num_args() === 5) 
     84                { 
     85                        if ((error_reporting() & $exception) !== 0) 
     86                        { 
     87                                // PHP Error 
     88                                $this->log(1, $message.' in '.$file.' on line '.$line); 
     89                        } 
     90                } 
     91                else 
     92                { 
     93                        // Exception 
     94                        $this->log(1, $exception->getMessage()); 
     95                } 
     96        } 
     97 
     98        public function log($level, $message) 
     99        { 
     100                if ($level >= $this->log_level) 
     101                { 
     102                        echo date('Y-m-d g:i:s').' --- '.$message."\n"; flush(); 
     103                } 
     104        } 
     105 
     106        public function login($username, $password = NULL, $realname = 'Kohana PHP Bot') 
     107        { 
     108                // Send the login commands 
     109                $this->send('USER '.$username.' * * :'.$realname); 
     110                $this->send('NICK '.$username); 
     111 
     112                // Update the last ping 
     113                $this->stats['last_ping'] = microtime(TRUE); 
     114 
     115                // Read the MOTD before continuing 
     116                $this->read(375, 376, FALSE); 
     117                $this->log(2, 'Received MOTD (suppressed)'); 
     118        } 
     119 
     120        public function join($channel) 
     121        { 
     122                if (empty($this->channels[$channel])) 
     123                { 
     124                        // Join the channel 
     125                        $this->send('JOIN '.$channel); 
     126 
     127                        // Read the USERS command 
     128                        $this->channels[$channel] = explode(':', $this->read(353)); 
     129                        $this->channels[$channel] = explode(' ', $this->channels[$channel][1]); 
     130 
     131                        // The end of the USERS command 
     132                        $this->read(366, FALSE); 
     133                        $this->log(2, 'Received USERS (suppressed)'); 
     134                } 
     135        } 
     136 
     137        public function part($channel) 
     138        { 
     139                if ( ! empty($this->channels[$channel])) 
     140                { 
     141                        // Leave the channel 
     142                        $this->send('PART '.$channel); 
     143 
     144                        // Remove the channel 
     145                        unset($this->channels[$channel]); 
     146                } 
     147        } 
     148 
     149        public function quit($message = '</Kirc> by Kohana Team') 
     150        { 
     151                // Quit, wait, and exit 
     152                $this->send('QUIT '.$message); 
     153                sleep(2); 
     154                exit; 
     155        } 
     156 
     157        public function send($command) 
     158        { 
     159                if (feof($this->socket)) 
     160                { 
     161                        // The socket has been terminated unexpectedly. Abort, now! 
     162                        $this->log(1, 'Disconnected unexpectedly, shutting down.'); 
     163                        exit; 
     164                } 
     165 
     166                if (fwrite($this->socket, $command.self::$newline)) 
     167                { 
     168                        // Log the sent command 
     169                        $this->log(2, '>>> '.$command); 
     170 
     171                        // Update the stats 
     172                        $this->stats['last_sent'] = microtime(TRUE); 
     173                } 
     174                else 
     175                { 
     176                        // Log error 
     177                        $this->log(1, 'Error sending command >>> '.$command); 
     178                } 
     179        } 
     180 
     181        public function read($start = NULL, $end = NULL, $return = TRUE) 
     182        { 
     183                if (is_bool($end)) 
     184                { 
     185                        // Return value and end are shifted one place 
     186                        $return = $end; 
     187                        $end = NULL; 
     188                } 
     189 
     190                // Make sure the start and end are strings 
     191                ($start === NULL) or $start = (string) $start; 
     192                ($end   === NULL) or $end   = (string) $end; 
     193 
     194                $buffer = ''; 
     195                while ( ! feof($this->socket)) 
     196                { 
     197                        while ($raw = fgets($this->socket, 128)) 
     198                        { 
     199                                if ( ! empty($start)) 
     200                                { 
     201                                        // Get the host and command from the stream 
     202                                        list ($host, $cmd, $msg) = explode(' ', $raw, 3); 
     203 
     204                                        if ($cmd === $start) 
     205                                        { 
     206                                                // Trim the message 
     207                                                $msg = trim($msg); 
     208 
     209                                                if (empty($end)) 
     210                                                { 
     211                                                        // Return the message 
     212                                                        return ($return === TRUE) ? $msg : NULL; 
     213                                                } 
     214 
     215                                                $buffer .= $msg; 
     216                                        } 
     217 
     218                                        if ( ! empty($buffer)) 
     219                                        { 
     220                                                if ($return === TRUE) 
     221                                                { 
     222                                                        // Only add to the buffer for returns 
     223                                                        $buffer .= $msg; 
     224                                                } 
     225 
     226                                                if ($cmd === $end) 
     227                                                { 
     228                                                        // Return the complete buffer 
     229                                                        return ($return === TRUE) ? $buffer : NULL; 
     230                                                } 
     231                                        } 
     232                                } 
     233                                else 
     234                                { 
     235                                        $this->log(2, '<<< '.trim($raw)); 
     236                                } 
     237                        } 
     238                        // One half-second is high enough interactivity 
     239                        usleep(500000); 
     240                } 
    64241        } 
    65242