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/>.
22 // Timeout avant une reconnexion
23 public $socketTimeout = 280;
26 static public $instance = FALSE;
30 static $botVersion = '1.0';
37 static $connection_password;
45 public $connected = false;
47 private $core_commands;
51 public static function GetInstance() {
52 if (!self
::$instance) {
53 self
::$instance = new bot();
54 self
::$instance->plugins
= new plugins();
55 self
::$instance->irc
= new irc
;
56 self
::$instance->tick
= tick
::GetInstance();
58 return self
::$instance;
61 private function __construct() {
62 // load config and other things echo "oups main\n";die;
63 $this->formater
= text_format
::GetInstance();
64 $this->connected
= false;
67 private function load_core_plugin() {
68 // core plugin is an exeption in do_command
69 $this->plugins
->add_command('core','shownick',0,'Show the current nick used (debug)','mixed');
70 $this->plugins
->add_command('core','nick',1,'Change the current nick','mixed');
71 $this->plugins
->add_command('core','quit',0,'Disconnect and stop the process','mixed');
72 $this->plugins
->add_command('core','restart',0,'Disconnect and restart the process','mixed');
73 $this->plugins
->add_command('core','help',0,'Hmm, je dois recoder cette fonction','mixed');
76 public function launch() {
77 self
::load_core_plugin();
79 //$this->C = @fsockopen(self::$server, self::$port, $errno, $errstr, 10);
80 $this->C
= socket_create(AF_INET
, SOCK_STREAM
, SOL_TCP
);
81 if ($this->C
=== false) {
82 throw new Exception('Impossible de créer le socket IRC !',0);
85 if (self
::$ip != "") { socket_bind($this->C
, self
::$ip); }
87 $x = socket_connect($this->C
, self
::$server, self
::$port);
89 throw new Exception('Impossible de se connecter au server IRC ! ('.socket_last_error().')' ,0);
92 $this->connected
= true;
94 if (self
::$connection_password !== false) {
95 $this->put('PASS '.self
::$connection_password);
97 // TODO : be sure for the validity of the connection password (what chain server return if fail ?)
99 $this->put('USER '.self
::$myBotName.' '.self
::$myBotName.'@'.self
::$ip.' '.self
::$server.' :'.self
::$myBotName);
100 $this->put('NICK '.self
::$myBotName);
102 /*$this->tick->setTick('all5sec',5);
103 $this->tick->addJob('all5sec','hello chan','privmsg',array(self::$channel,"I'm a tick. I show this msg all 5sec."),0,$this);
106 $this->msg
= $this->get();
107 if (is_array($this->msg
)) {
108 foreach ($this->msg
as $msg) {
110 $this->irc
->parse_get($msg);
111 $this->work_on_listen();
115 $this->irc
->parse_get($this->msg
);
116 $this->work_on_listen();
119 } catch (myRuntimeException
$e) {
120 $x = $e->getMessage();
122 $x.= backtrace($e->getTrace());
123 file_put_contents('errorlog',$x);
124 //if ($e->_level < 2 || ($e->_level >= 256 && $e->_level < 1024)) {
125 echo 'Error level : '.$e->_level
."\n\n";
126 throw new Exception('Error occured',0);
128 echo 'Error level : '.$e->_level."\n\n\n\n";
129 throw new Exception("Error occured. Please see errorlog for details.",1);
131 } catch (Exception
$e) {
136 private function work_on_listen () {
137 $this->msg_info
= $msg_info = $this->irc
->get_msg_info();
139 switch ($msg_info['type']) {
144 if ($msg_info['message'][0] == chr(001)) {
146 // we don't need this character
147 $msg_info['message'] = str_replace(chr(001), "", $msg_info['message']);
148 if (strstr($msg_info['message'],' ')===true) {
149 list($cmd,$query) = explode(" ",$msg_info['message']);
151 $cmd = $msg_info['message'];
154 // we don't need this character
155 //$cmd = str_replace(chr(001), "", $msg_info['message']);
160 self
::notice($msg_info['from'],$this->formater
->ctcp('PING VERSION TIME USERINFO CLIENTINFO'));
164 self
::notice($msg_info['from'],$this->formater
->ctcp('RPGBot version '.bot
::$botVersion.' - PHP '.phpversion().' -- par Tornald et Bloodshed'));
168 self
::notice($msg_info['from'],$this->formater
->ctcp('RPGBot'));
172 self
::notice($msg_info['from'],$this->formater
->ctcp(date('Y-m-d H:i:s')));
176 self
::notice($msg_info['from'],$this->formater
->ctcp("PING ".$query));
179 self
::notice($msg_info['from'],$this->formater
->ctcp("UNKNOWN CTCP REQUEST : '$cmd'"));
183 $msg_info['message'] = trim($msg_info['message']);
184 if ($msg_info['to'] == bot
::$myBotName) {
186 if (preg_match('`^connect ([^ ]+) ([^ ]+)`',$msg_info['message'],$m)) {
187 if ($m[1] == 'admin' && $m[2] == 'mypass') {
189 self
::notice($msg_info['from'],'Vous êtes bien authentifié comme administrateur.');
191 self
::notice($msg_info['from'],'Erreur dans votre login et / ou mot de passe.');
192 echo debug() ?
'l: '.$m[1].' p: '.$m[2]."\n":'';
198 if ($msg_info['message'][0] == '!') {
199 $message = substr($msg_info['message'],1);
200 $query = explode(' ',$message);
201 $query_count = count($query);
203 if (isset($this->plugins
->commands
[$query[0]])) {
204 if ($query_count > 1) {
206 if ($this->plugins
->commands
[$query[0]][$query[1]]['type'] == 'mixed' ||
207 ($msg_info['to'] == bot
::$myBotName && $this->plugins
->commands
[$query[0]][$query[1]]['type'] == 'private') ||
208 ($msg_info['to'] == bot
::$channel && $this->plugins
->commands
[$query[0]][$query[1]]['type'] == 'public')) {
210 * $query = array( plugins, method[, arg[, ...]] )
211 * !plugin method[ arg[ ...]]
213 call_user_func_array(array($this->plugins
,'do_command'),array_merge(array('msg_info'=>$msg_info),$query));
216 echo $this->plugins
->commands
[$query[0]][$query[1]]['type']."\n";
217 $this->privmsg($msg_info['from'],"Error with the request.");
220 // no method : need a list of commands
223 $commands = $this->plugins
->list_command($plugin);
224 foreach ($commands as $command_info) {
225 if ($command_info['type'] == 'public') {
226 $get_plugin_method = '!'.$plugin;
228 $get_plugin_method = '/msg ' . bot
::$myBotName . ' !' . $plugin;
231 $this->privmsg($msg_info['from'],$this->formater
->bold('Command :')." $get_plugin_method {$command_info['method']}".(($command_info['accepted_args']>0)?
' [some args...]':''));
233 $help = explode("\n",$command_info['help']);
234 foreach($help as $help_msg) {
235 $this->privmsg($msg_info['from'],$help_msg);
240 //var_dump($this->plugins->commands);
250 public function joinChannel($channel) {
251 $this->put('JOIN '.$channel);
252 echo "Join channel $channel ...\n";
255 public function newNick($new=false) {
257 case self
::$myBotName:
258 echo "New nick : [ERR] no changes :". self
::$myBotName . ' == ' . $new ."\n";
261 self
::$myBotName .= '_';
265 self
::$myBotName = $new;
272 * Envoie une notice a un salon / utilisateur
275 * @param string $message
277 public function notice ($to,$message) {
278 self
::put('NOTICE '.$to.' :'.$message."\n");
282 * Envoie un message (PRIVMSG) a un salon / utilisateur
285 * @param string $message
287 public function privmsg ($to,$message) {
288 self
::put('PRIVMSG '.$to.' :'.$message."\n");
291 public function put($data) {
292 if (!is_resource($this->C
)) {
293 throw new Exception('Connection lost...',1);
296 echo debug() ?
'[->' . $data . "\n" : '';
298 $ok = socket_write($this->C
, $data ."\n");
305 // fputs($this->C, $command . "\n");
309 public function get() {
312 socket_set_nonblock($this->C
);
315 //echo "TOC - ",time(),"\n";
316 $this->tick
->doAllTicks();
318 $buf = @socket_read
($this->C
, 4096);
321 $this->CheckTimeout($time1);
326 if (!strpos($buf, "\n")) { //Si ne contient aucun retour, on bufferise
327 $buffer = $buffer.$buf;
328 $data = ""; //rien à envoyer
330 //Si contient au moins un retour,
331 //on vérifie que le dernier caractère en est un
332 if (substr($buf, -1, 1) == "\n") {
333 //alors on additionne ces données au buffer
334 $data = $buffer.$buf;
335 $buffer = ""; //on vide le buffer
337 //si le dernier caractère n'est pas un retour à la
338 //ligne, alors on envoit tout jusqu'au dernier retour
339 //puis on bufferise le reste
340 $buffer = $buffer.substr($buf, strrchr($buf, "\n"));
341 $data = substr($buf, 0, strrchr($buf, "\n"));
342 $data = $buffer.$data;
343 $buffer = ""; //on vide le buffer
348 $data = split("\n", $data);
355 echo "TOC - ",time(),"\n";
356 $this->tick->doAllTicks();
358 stream_set_timeout($this->C, $this->socketTimeout);
360 $content = fgets($this->C, 1024);
362 echo debug() ? "<-]$content" : '';
364 if (empty($content)) {
365 $this->CheckTimeout($tmp1);
376 public function shownick() {
377 $this->privmsg(bot
::$channel,bot
::$myBotName."\n");
380 public function nick($newNick) {
381 $this->newNick($newNick);
384 public function help() {
385 $this->privmsg($this->msg_info
['from'],"List of plugins :");
386 foreach($this->plugins
->plugin_list
as $pluginName => $object) {
387 $this->privmsg($this->msg_info
['from'],"!$pluginName");
392 public function restart() {
394 echo 'Restart...'."\n";
397 throw new Exception('Restart...',1);
399 $this->privmsg($this->msg_info
['from'],"Vous n'etes pas authentifie.");
403 public function quit() {
406 throw new Exception('Quit from',3);
408 $this->privmsg($this->msg_info
['from'],"Vous n'etes pas authentifie.");
412 public function disconnect($msg='EOL ;') {
413 $this->plugins
->unload_plugin('_all');
414 echo "Closing Link...";
415 if (is_resource($this->C
)) {
416 if ($this->put('QUIT :'.$msg)) {
417 $this->connected
= false;
418 socket_close($this->C
);
420 echo "\t\t\tdone.\n";
422 echo "\t\t\tError.\nConnection already breack down. ".'bot::disconnect'."\n";
427 private function nick_change() {
428 echo "New nick : ".self
::$myBotName."\n";
429 if ($this->put('NICK :'.self
::$myBotName)) {
435 private function CheckTimeout($time1) {
436 if (time()-$time1 >= $this->socketTimeout
) {
437 throw new Exception('TIMEOUT',0);
441 private function load_user($file='users.db') {
442 echo "Loading users ...";
443 $ulist = explode("\n",file_get_contents($file));
444 foreach ($ulist as $user) {
445 list($uname,$upass,$uright) = explode(':',$user);
446 $this->users_list
[$uname] = array($upass,$uright);
448 echo "\t\t\tdone.\n";
451 function __destruct() {
452 if (is_resource($this->C
)) {
453 if ($this->put('QUIT :Error occured')) {
454 socket_close($this->C
);