Begin of quizz plugin.
[irbot.git] / sources / main-class.inc.php
blobc6226fea87cc4ac4efab8bacc51abd079b32ee94
1 <?php
2 /**
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 $
22 final class bot {
24 // Timeout avant une reconnexion
25 public $socketTimeout = 180;
27 // Variables privèes
28 static public $instance = FALSE;
30 protected $C;
32 static $botVersion = '1.0';
33 static $server;
34 static $port;
35 static $channel;
36 static $myBotName;
37 static $ip;
38 static $domain;
39 static $connection_password;
41 public $irc;
42 public $tick;
43 public $plugins;
44 public $formater;
45 public $base;
46 public $msg;
47 public $connected = false;
49 private $core_commands;
50 private $users_list;
51 private $msg_info;
52 private $lastData;
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();
82 try {
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);
93 if ($x===false) {
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);
110 while (1) {
111 $this->msg = $this->get();
112 if (is_array($this->msg)) {
113 foreach ($this->msg as $msg) {
114 if (!empty($msg)) {
115 $this->irc->parse_get($msg);
116 $this->work_on_listen();
119 } else {
120 $this->irc->parse_get($this->msg);
121 $this->work_on_listen();
124 } catch (myRuntimeException $e) {
125 $x = $e->getMessage();
126 echo $x;
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);
132 /*} else {
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) {
137 throw $e;
141 private function work_on_listen () {
142 $this->msg_info = $msg_info = $this->irc->get_msg_info();
144 switch ($msg_info['type']) {
146 case 'PRIVMSG':
148 // ctcp
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']);
155 } else {
156 $cmd = $msg_info['message'];
159 // we don't need this character
160 //$cmd = str_replace(chr(001), "", $msg_info['message']);
161 $cmd = trim($cmd);
163 switch ($cmd) {
164 case 'CLIENTINFO':
165 self::notice($msg_info['from'],$this->formater->ctcp('PING VERSION TIME USERINFO CLIENTINFO'));
166 break;
168 case 'VERSION':
169 self::notice($msg_info['from'],$this->formater->ctcp('RPGBot version '.bot::$botVersion.' - PHP '.phpversion().' -- on http://irstat.org'));
170 break;
172 case 'USERINFO':
173 self::notice($msg_info['from'],$this->formater->ctcp('RPGBot'));
174 break;
176 case 'TIME':
177 self::notice($msg_info['from'],$this->formater->ctcp(date('Y-m-d H:i:s')));
178 break;
180 case 'PING':
181 self::notice($msg_info['from'],$this->formater->ctcp("PING ".$query));
182 break;
183 default:
184 self::notice($msg_info['from'],$this->formater->ctcp("UNKNOWN CTCP REQUEST : '$cmd'"));
185 break;
187 } else {
188 $msg_info['message'] = trim($msg_info['message']);
189 if ($msg_info['to'] == bot::$myBotName) {
190 // auth proccess
191 if (preg_match('`^connect ([^ ]+) ([^ ]+)`',$msg_info['message'],$m)) {
192 if ($m[1] == 'admin' && $m[2] == 'mypass') {
193 $this->auth = true;
194 self::notice($msg_info['from'],'Vous êtes bien authentifié comme administrateur.');
195 } else {
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":'';
199 continue;
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));
212 } else {
213 $this->privmsg($msg_info['from'],"This command do not exist. Try !{$query[0]} for the list of commands avaiable with this plugin.");
215 } else {
216 // no method : need a list of commands
217 $plugin = $message;
219 $commands = $this->plugins->list_command($plugin);
220 foreach ($commands as $command_info) {
221 if ($command_info['type'] == 'public') {
222 $get_plugin_method = '!'.$plugin;
223 } else {
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);
231 } else {
232 //var_dump($this->plugins->commands);
237 break;
238 case 'NOTICE':
239 break;
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) {
250 switch ($new) {
251 case self::$myBotName:
252 echo "New nick : [ERR] no changes :". self::$myBotName . ' == ' . $new ."\n";
253 break;
254 case false:
255 self::$myBotName .= '_';
256 self::nick_change();
257 break;
258 default:
259 self::$myBotName = $new;
260 self::nick_change();
261 break;
266 * Envoie une notice a un salon / utilisateur
268 * @param string $to
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
278 * @param string $to
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);
292 usleep($interval);
297 public function put($data) {
298 if (!is_resource($this->C)) {
299 throw new Exception('Connection lost...',1);
300 return;
303 echo debug() ? 'bot::put() -> ' . $data . "\n" : '';
305 $ok = socket_write($this->C, $data ."\n");
306 if ($ok) {
307 return true;
308 } else {
309 return false;
312 // fputs($this->C, $command . "\n");
313 return true;
316 public function get() {
318 $buffer = '';
319 socket_set_nonblock($this->C);
321 while (1) {
322 //echo "TOC - ",time(),"\n";
323 $this->tick->doAllTicks();
324 $buf = @socket_read($this->C, 4096);
326 if (empty($buf)) {
327 $this->CheckTimeout();
328 sleep(1);
329 continue;
332 $this->lastData = mktime();
334 if (!strpos($buf, "\n")) { //Si ne contient aucun retour, on bufferise
335 $buffer = $buffer.$buf;
336 $data = ""; //rien è envoyer
337 } else {
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
344 } else {
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
355 if ($data != '') {
356 $data = split("\n", $data);
357 return $data;
360 continue;
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");
377 usleep(500000);
381 public function restart() {
382 if ($this->auth) {
383 echo 'Restart...'."\n";
384 $this->disconnect();
385 sleep(5);
386 throw new Exception('Restart...',1);
387 } else {
388 $this->privmsg($this->msg_info['from'],"Vous n'etes pas authentifie.");
392 public function quit() {
393 if ($this->auth) {
394 $this->disconnect();
395 throw new Exception('Quit from',3);
396 } else {
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";
410 } else {
411 echo "\t\t\tError.\nConnection already breack down. ".'bot::disconnect'."\n";
413 return true;
416 private function nick_change() {
417 echo "New nick : ".self::$myBotName."\n";
418 if ($this->put('NICK :'.self::$myBotName)) {
419 return true;
421 return false;
424 private function CheckTimeout() {
425 $now = mktime();
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);