Changeset 2067 for trunk/modules/kobot/libraries/Kobot.php
- Timestamp:
- 02/16/2008 06:06:40 PM (11 months ago)
- Files:
-
- 1 modified
-
trunk/modules/kobot/libraries/Kobot.php (modified) (23 diffs)
Legend:
- Unmodified
- Added
- Removed
-
trunk/modules/kobot/libraries/Kobot.php
r2057 r2067 18 18 public $log_level = 1; 19 19 20 // Command responses and timers20 // Command responses, timers, and triggers 21 21 protected $responses = array(); 22 22 protected $timers = array(); 23 protected $triggers = array(); 23 24 24 25 // Responses to drop by default … … 31 32 '004', 32 33 '005', 34 '250', 33 35 '251', 34 36 '252', … … 37 39 '265', 38 40 '266', 39 '250',40 41 '366', 41 42 '477', … … 43 44 44 45 // IRC socket, username, MOTD, and stats 45 protected $s ocket;46 protected $server; 46 47 protected $username; 47 48 protected $motd; … … 57 58 protected $channels = array(); 58 59 59 public function __construct($ server, $port = NULL, $timeout = NULL)60 public function __construct($host, $port = NULL, $timeout = NULL) 60 61 { 61 62 if (PHP_SAPI !== 'cli') … … 72 73 set_exception_handler(array($this, 'exception_handler')); 73 74 75 // Add the identify event 76 Event::add('kobot.motd_read', array($this, 'login_identify')); 77 74 78 // Set the port 75 79 empty($port) and $port = 6667; … … 78 82 empty($timeout) and $timeout = 10; 79 83 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(); 93 94 94 95 // 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); 122 97 } 123 98 else 124 99 { 125 100 // 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); 127 102 exit; 128 103 } 129 104 } 130 105 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 131 152 public function log($level, $message) 132 153 { 133 if ($ level >= $this->log_level)154 if ($this->log_level >= $level) 134 155 { 135 156 // Display the message with a timestamp, flush the output 136 157 echo date('Y-m-d g:i:s').' --- '.$message."\n"; flush(); 137 158 } 159 160 return TRUE; 138 161 } 139 162 … … 191 214 { 192 215 // Store the trigger and it's callback 193 $this-> msg_triggers[$pattern] = $callback;216 $this->triggers[$pattern] = $callback; 194 217 195 218 return $this; … … 199 222 { 200 223 // Remove the trigger 201 unset($this-> msg_triggers[$pattern]);224 unset($this->triggers[$pattern]); 202 225 203 226 return $this; … … 210 233 public function send($command) 211 234 { 212 if (feof($this->s ocket))235 if (feof($this->server->socket)) 213 236 { 214 237 // The socket has been terminated unexpectedly. Abort, now! … … 217 240 } 218 241 219 if (fwrite($this->s ocket, $command.self::$newline))242 if (fwrite($this->server->socket, $command.self::$newline)) 220 243 { 221 244 // Log the sent command 222 $this->log( 2, '>>> '.$command);245 $this->log(3, '>>> '.$command); 223 246 224 247 // Update the stats … … 234 257 public function read() 235 258 { 236 while ( ! feof($this->s ocket))259 while ( ! feof($this->server->socket)) 237 260 { 238 261 // Start a new read loop … … 240 263 241 264 // Read the raw server stream, up to 1024 characters 242 while ($raw = fgets($this->s ocket, 1024))265 while ($raw = fgets($this->server->socket, 1024)) 243 266 { 244 267 // Update the last received time … … 253 276 call_user_func($this->responses[$data['command']], $data); 254 277 } 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)); 260 281 } 261 282 … … 422 443 public function login($username, $password = NULL, $realname = 'Kohana PHP Bot') 423 444 { 424 // Cache the current username 445 // Cache the current username and password 425 446 $this->username = $username; 447 $this->password = $password; 426 448 427 449 // Send the login commands, use 8 for the mask (invisible) … … 433 455 } 434 456 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 = '') 436 464 { 437 465 if (empty($this->channels[$channel])) 438 466 { 439 // Set the channel as joined440 $this->channels[$channel] = array();467 // Create a new channel 468 $this->channels[$channel] = new Kobot_Channel($channel, $password); 441 469 442 470 // Join the channel 443 $this->send('JOIN '. $channel);471 $this->send('JOIN '.trim($channel.' '.$password)); 444 472 } 445 473 } … … 475 503 476 504 // PING 477 public function response_ping( $data)505 public function response_ping(array $data) 478 506 { 479 507 // Update the stats … … 485 513 486 514 // 375, 372+, 376 487 public function response_motd( $data)515 public function response_motd(array $data) 488 516 { 489 517 switch ($data['command']) … … 503 531 // Make the MOTD into a string 504 532 $this->motd = implode("\n", $this->motd); 533 534 // Run the motd_read event 535 Event::run('kobot.motd_read'); 505 536 break; 506 537 } 507 538 } 508 539 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 509 559 // 353, 366 510 public function response_userlist( $data)560 public function response_userlist(array $data) 511 561 { 512 562 if (strpos($data['target'], ' @ ') !== FALSE) … … 516 566 517 567 // Set the current users 518 $this->channels[$channel] = explode(' ', $data['message']);568 $this->channels[$channel]->users = ($users = explode(' ', $data['message'])); 519 569 520 570 // 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); 522 575 } 523 576 } 524 577 525 578 // JOIN 526 public function response_join( $data)579 public function response_join(array $data) 527 580 { 528 581 // Make sure the bot is joined to the target channel 529 582 if (isset($this->channels[$data['target']])) 530 583 { 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'].')'); 543 589 } 544 590 } 545 591 546 592 // PART 547 public function response_part( $data)593 public function response_part(array $data) 548 594 { 549 595 // Make sure the bot is joined to the target channel 550 596 if (isset($this->channels[$data['target']])) 551 597 { 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) 565 608 { 566 609 if ($data['message'] === chr(1).'VERSION'.chr(1)) … … 569 612 $this->response_version($data); 570 613 } 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 )) 573 627 { 574 628 // Process triggers 575 foreach ($this-> msg_triggers as $pattern => $func)629 foreach ($this->triggers as $pattern => $func) 576 630 { 577 631 if (preg_match('/'.$pattern.'/', $trigger, $matches)) … … 585 639 } 586 640 587 public function response_version( $data)641 public function response_version(array $data) 588 642 { 589 643 // Send a CTCP VERSION response … … 591 645 } 592 646 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 593 667 } // End Kobot
