3 * This file is part of IrBot, irc robot.
4 * Copyright (C) 2007 Bellière Ludovic
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 * $Id: main-class.inc.php 28 2008-01-19 17:17:01Z xrogaan $
24 // Timeout avant une reconnexion
25 public $socketTimeout = 180;
28 static public $instance = FALSE;
32 static $botVersion = '1.0';
39 static $connection_password;
47 public $connected = false;
49 private $core_commands;
54 public static function GetInstance() {
55 if (!self
::$instance) {
56 self
::$instance = new bot();
57 self
::$instance->plugins
= new plugins();
58 self
::$instance->irc
= new irc
;
59 self
::$instance->tick
= tick
::GetInstance();
61 return self
::$instance;
64 private function __construct() {
65 // load config and other things echo "oups main\n";die;
66 $this->formater
= text_format
::GetInstance();
67 $this->connected
= false;
70 private function load_core_plugin() {
71 // core plugin is an exeption in do_command
72 $this->plugins
->add_command('core','shownick',0,'Show the current nick used (debug)','mixed');
73 $this->plugins
->add_command('core','nick',1,'Change the current nick','mixed');
74 $this->plugins
->add_command('core','quit',0,'Disconnect and stop the process','mixed');
75 $this->plugins
->add_command('core','restart',0,'Disconnect and restart the process','mixed');
76 $this->plugins
->add_command('core','help',0,'Hmm, je dois recoder cette fonction','mixed');
79 public function launch() {
80 self
::load_core_plugin();
81 self
::$plugins->Init();
83 //$this->C = @fsockopen(self::$server, self::$port, $errno, $errstr, 10);
84 $this->lastData
= mktime();
85 $this->C
= socket_create(AF_INET
, SOCK_STREAM
, SOL_TCP
);
86 if ($this->C
=== false) {
87 throw new Exception('Impossible de créer le socket IRC !',0);
90 if (self
::$ip != "") { socket_bind($this->C
, self
::$ip); }
92 $x = socket_connect($this->C
, self
::$server, self
::$port);
94 throw new Exception('Impossible de se connecter au server IRC ! ('.socket_last_error().')' ,0);
97 $this->connected
= true;
99 if (self
::$connection_password !== false) {
100 $this->put('PASS '.self
::$connection_password);
102 // TODO : be sure for the validity of the connection password (what chain server return if fail ?)
104 $this->put('USER '.self
::$myBotName.' '.self
::$myBotName.'@'.self
::$ip.' '.self
::$server.' :'.self
::$myBotName);
105 $this->put('NICK '.self
::$myBotName);
107 /*$this->tick->setTick('all5sec',5);
108 $this->tick->addJob('all5sec','hello chan','privmsg',array(self::$channel,"I'm a tick. I show this msg all 5sec."),0,$this);
111 $this->msg
= $this->get();
112 if (is_array($this->msg
)) {
113 foreach ($this->msg
as $msg) {
115 $this->irc
->parse_get($msg);
116 $this->work_on_listen();
120 $this->irc
->parse_get($this->msg
);
121 $this->work_on_listen();
124 } catch (myRuntimeException
$e) {
125 $x = $e->getMessage();
127 $x.= backtrace($e->getTrace());
128 file_put_contents('errorlog',$x);
129 //if ($e->_level < 2 || ($e->_level >= 256 && $e->_level < 1024)) {
130 echo 'Error level : '.$e->_level
."\n\n";
131 throw new Exception('Error occured',0);
133 echo 'Error level : '.$e->_level."\n\n\n\n";
134 throw new Exception("Error occured. Please see errorlog for details.",1);
136 } catch (Exception
$e) {
141 private function work_on_listen () {
142 $this->msg_info
= $msg_info = $this->irc
->get_msg_info();
144 switch ($msg_info['type']) {
149 if ($msg_info['message'][0] == chr(001)) {
151 // we don't need this character
152 $msg_info['message'] = str_replace(chr(001), "", $msg_info['message']);
153 if (strstr($msg_info['message'],' ')===true) {
154 list($cmd,$query) = explode(" ",$msg_info['message']);
156 $cmd = $msg_info['message'];
159 // we don't need this character
160 //$cmd = str_replace(chr(001), "", $msg_info['message']);
165 self
::notice($msg_info['from'],$this->formater
->ctcp('PING VERSION TIME USERINFO CLIENTINFO'));
169 self
::notice($msg_info['from'],$this->formater
->ctcp('RPGBot version '.bot
::$botVersion.' - PHP '.phpversion().' -- on http://irstat.org'));
173 self
::notice($msg_info['from'],$this->formater
->ctcp('RPGBot'));
177 self
::notice($msg_info['from'],$this->formater
->ctcp(date('Y-m-d H:i:s')));
181 self
::notice($msg_info['from'],$this->formater
->ctcp("PING ".$query));
184 self
::notice($msg_info['from'],$this->formater
->ctcp("UNKNOWN CTCP REQUEST : '$cmd'"));
188 $msg_info['message'] = trim($msg_info['message']);
189 if ($msg_info['to'] == bot
::$myBotName) {
191 if (preg_match('`^connect ([^ ]+) ([^ ]+)`',$msg_info['message'],$m)) {
192 if ($m[1] == 'admin' && $m[2] == 'mypass') {
194 self
::notice($msg_info['from'],'Vous êtes bien authentifié comme administrateur.');
196 self
::notice($msg_info['from'],'Erreur dans votre login et / ou mot de passe.');
197 echo debug() ?
'l: '.$m[1].' p: '.$m[2]."\n":'';
203 if ($msg_info['message'][0] == '!') {
204 $message = substr($msg_info['message'],1);
205 $query = explode(' ',$message);
206 $query_count = count($query);
208 if (isset($this->plugins->commands[$query[0]])) {
209 if ($query_count > 1) {
210 if (isset($this->plugins->commands[$query[0]][$query[1]])) {
211 call_user_func_array(array($this->plugins,'do_command'),array_merge(array('msg_info'=>$msg_info),$query));
213 $this->privmsg($msg_info['from'],"This command do not exist. Try !{$query[0]} for the list of commands avaiable with this plugin.");
216 // no method : need a list of commands
219 $commands = $this->plugins->list_command($plugin);
220 foreach ($commands as $command_info) {
221 if ($command_info['type'] == 'public') {
222 $get_plugin_method = '!'.$plugin;
224 $get_plugin_method = '/msg ' . bot::$myBotName . ' !' . $plugin;
227 $msg = array_merge(array($this->formater->bold('Command :')." $get_plugin_method {$command_info['method']}".(($command_info['accepted_args']>0)?' [some args...]':'')),text_format::paragraphe($command_info['help']));
228 $this->mprivmsg($msg_info['from'],$msg);
232 //var_dump($this->plugins->commands);
241 self
::$plugins->set_event($msg_info);
244 public function joinChannel($channel) {
245 $this->put('JOIN '.$channel);
246 echo "Join channel $channel ...\n";
249 public function newNick($new=false) {
251 case self
::$myBotName:
252 echo "New nick : [ERR] no changes :". self
::$myBotName . ' == ' . $new ."\n";
255 self
::$myBotName .= '_';
259 self
::$myBotName = $new;
266 * Envoie une notice a un salon / utilisateur
269 * @param string $message
271 public function notice ($to,$message) {
272 self
::put('NOTICE '.$to.' :'.$message."\n");
276 * Envoie un message (PRIVMSG) a un salon / utilisateur
279 * @param string $message
281 public function privmsg ($to,$message) {
282 $search = array('#name');
283 $replace = array(self
::$myBotName);
284 $message = str_replace($search,$replace,$message);
285 self
::put('PRIVMSG '.$to.' :'.$message."\n");
288 public function mprivmsg($to,$messages,$interval=650000) {
289 if (is_array($messages)) {
290 foreach ($messages as $msg) {
291 $this->privmsg($to,$msg);
297 public function put($data) {
298 if (!is_resource($this->C
)) {
299 throw new Exception('Connection lost...',1);
303 echo debug() ?
'bot::put() -> ' . $data . "\n" : '';
305 $ok = socket_write($this->C
, $data ."\n");
312 // fputs($this->C, $command . "\n");
316 public function get() {
319 socket_set_nonblock($this->C
);
322 //echo "TOC - ",time(),"\n";
323 $this->tick
->doAllTicks();
324 $buf = @socket_read
($this->C
, 4096);
327 $this->CheckTimeout();
332 $this->lastData
= mktime();
334 if (!strpos($buf, "\n")) { //Si ne contient aucun retour, on bufferise
335 $buffer = $buffer.$buf;
336 $data = ""; //rien è envoyer
338 //Si contient au moins un retour,
339 //on vèrifie que le dernier caractère en est un
340 if (substr($buf, -1, 1) == "\n") {
341 //alors on additionne ces donnèes au buffer
342 $data = $buffer.$buf;
343 $buffer = ""; //on vide le buffer
345 //si le dernier caractère n'est pas un retour è la
346 //ligne, alors on envoit tout jusqu'au dernier retour
347 //puis on bufferise le reste
348 $buffer = $buffer.substr($buf, strrchr($buf, "\n"));
349 $data = substr($buf, 0, strrchr($buf, "\n"));
350 $data = $buffer.$data;
351 $buffer = ""; //on vide le buffer
356 $data = split("\n", $data);
365 public function shownick() {
366 $this->privmsg(bot
::$channel,bot
::$myBotName."\n");
369 public function nick($newNick) {
370 $this->newNick($newNick);
373 public function help() {
374 $this->privmsg($this->msg_info
['from'],"List of plugins :");
375 foreach($this->plugins
->plugin_list
as $pluginName => $object) {
376 $this->privmsg($this->msg_info
['from'],"!$pluginName");
381 public function restart() {
383 echo 'Restart...'."\n";
386 throw new Exception('Restart...',1);
388 $this->privmsg($this->msg_info
['from'],"Vous n'etes pas authentifie.");
392 public function quit() {
395 throw new Exception('Quit from',3);
397 $this->privmsg($this->msg_info
['from'],"Vous n'etes pas authentifie.");
401 public function disconnect($msg='EOL ;') {
402 $this->plugins
->unload_plugin('_all');
403 echo "Closing Link...";
404 if (is_resource($this->C
)) {
405 if ($this->put('QUIT :'.$msg)) {
406 $this->connected
= false;
407 socket_close($this->C
);
409 echo "\t\t\tdone.\n";
411 echo "\t\t\tError.\nConnection already breack down. ".'bot::disconnect'."\n";
416 private function nick_change() {
417 echo "New nick : ".self
::$myBotName."\n";
418 if ($this->put('NICK :'.self
::$myBotName)) {
424 private function CheckTimeout() {
426 if ($this->lastData+
$this->socketTimeout
< mktime()) {
427 throw new Exception('Connection lost (Timeout).',1);
429 $lag = $now-$this->lastData
;
430 if ( $lag > 20 && ($lag %
10) == 0) {
431 echo "Lag: ".$lag." seconds\n";
435 private function load_user($file='users.db') {
436 echo "Loading users ...";
437 $ulist = explode("\n",file_get_contents($file));
438 foreach ($ulist as $user) {
439 list($uname,$upass,$uright) = explode(':',$user);
440 $this->users_list
[$uname] = array($upass,$uright);
442 echo "\t\t\tdone.\n";
445 function __destruct() {
446 if (is_resource($this->C
)) {
447 if ($this->put('QUIT :Error occured')) {
448 socket_close($this->C
);