phpircbot2/modules/irc.class.php

356 lines
11 KiB
PHP
Raw Normal View History

2010-11-21 23:13:54 +01:00
<?php
class ircbot{
public $debug = false; //Debugging
protected $nick;
protected $username;
protected $realname;
protected $host;
protected $port;
public $alive; //Bot alive?
protected $sleep; //How long to wait after sending 1 line
protected $channels = array(); //Channel-Array
private $protocol; //IRC-Protocol Handler
private $connection; //Fsock-class Handler (false if not connected)
private $recvbuf; //RECV-Buffer
private $sendbuf; //SEND-Buffer
private $reactbuf; //REACT-Buffer (to bypass linebreak errors)
private $commands; //Command-Handler for additional files
private $userdb; //User-Permission-DB
private $endl = "\r\n"; //ENDL-Marker
private $prefix; //Command Prefix
private $autoreconnect; //Shall the Bot try to reconnect again?
private $login = false; //connection successful, we can work if it's true
private $lastline = ""; //last sent line... (flood protection)
private $lastlinetime = 0; //timestamp of last sent line
/*
* Class general stuff
*/
function __construct($nick, $username, $realname){
$this -> alive = true;
$this -> nick = $nick;
$this -> username = $username;
$this -> realname = $realname;
$this -> connection = false;
$this -> protocol = new ircprotocol($nick, $username, $realname);
$this -> sendbuf = new blockstring($this -> endl);
$this -> recvbuf = new blockstring($this -> endl);
$this -> reactbuf = new blockstring($this -> endl);
$this -> commands = new command_handler();
$this -> userdb = new userdb();
}
public function setServer($host, $port){
$this -> host = $host;
$this -> port = $port;
}
public function setAutoReconnect($bool){
$this -> autoreconnect = $bool;
}
public function setPrefix($prefix){
$this -> prefix = $prefix;
}
public function addChannels($channels){
$this -> channels = array_merge($this -> channels, $channels);
if($this -> login){
foreach($channels as $channel){
$this -> call("join", $channel);
}
}
}
public function removeChannels($channels){
foreach($channels as $channel){
unset($this -> channels[array_search($channel, $this -> channels)]);
if($this -> login) $this -> call("part", $channel, "Removed ".$channel." from list.");
}
}
public function setSleep($sleep){
$this -> sleep = $sleep * 1000000;
}
public function clearBuffer(){
$this -> sendbuf -> clear();
$this -> recvbuf -> clear();
$this -> reactbuf -> clear();
}
function __destruct(){
$this -> clearBuffer();
if($this -> connection){
$this -> autoreconnect = false;
$this -> disconnect();
}
unset($this -> recvbuf);
unset($this -> sendbuf);
unset($this -> reactbuf);
unset($this -> commands);
unset($this -> userdb);
$this -> alive = false;
}
/*
* Connection handling stuff
*/
public function connect(){
$this -> connection = new fsock($this -> host, $this -> port, 16384, $this -> endl, 5, 0.5);
$this -> send($this -> protocol -> auth());
$this -> send($this -> protocol -> nick());
//Wait for being able to join channels
while(!$this -> login) $this -> update();
//Join given channels
foreach($this -> channels as $channel){
$this -> call("join", $channel);
}
//Update in order to send all join commands...
$this -> update();
}
public function send($msg){
if($msg != "") $this -> sendbuf -> addData($msg.$this -> endl);
$this -> update();
}
public function recv(){
$this -> update();
if(!$line = $this -> recvbuf -> getNextLine()) return false;
$recv = $this -> protocol -> parse($line);
//add permission level, process-flag and default value for "command"
if(isset($recv["nick"])) $recv["level"] = $this -> userdb -> getLevel($recv["nick"]);
$recv["processed"] = false;
$recv["command"] = false;
//If we've got a Query...
if($this -> protocol -> checkQuery($recv)){
$recv["processed"] = true;
$recv["query"] = true;
$recv["command"] = ($recv["msgarr"][0][0] == $this -> prefix) ? substr($recv["msgarr"][0], 1) : $recv["msgarr"][0];
if($code = $this -> commands -> getCode($recv["command"], "query", $recv["level"])){
if($this -> debug) echo "[EVAL] Command: ".$recv["command"]." (query)\n";
if(trim($code) != "" && !is_numeric($code)){
eval($code);
}
unset($code, $recv["command"]);
}
}//if query
//if command with prefix has been called...
if($this -> protocol -> checkCommand($this -> prefix, $recv)){
$recv["processed"] = true;
$recv["command"] = $this -> protocol -> getCommand($this -> prefix, $recv);
$code = $this -> commands -> getCode($recv["command"], "prefix", $recv["level"]);
if(trim($code) != "" && !is_numeric($code)){
if($this -> debug) echo "[EVAL] Command: ".$recv["command"]." (prefix)\n".$code."\n";
eval($code);
unset($code, $recv["command"]);
}
elseif($code == 2){
$this -> call("privmsg", $recv["target"], $recv["nick"].": permission denied! (your level: ".$recv["level"]." < ".$this -> commands -> getLevel($recv["command"]).")");
if(!$this -> userdb -> isActive($recv["nick"]) && $this -> userdb -> isUser($recv["nick"])) $this -> call("notice", $recv["nick"], "You have to identify yourself! (Type ".$this -> prefix."help for further information)");
}
elseif($code == 3){
$this -> call("privmsg", $recv["target"], $recv["nick"].": permission denied! (context: ".$this -> commands -> getContext($recv["command"]).")");
}
}//if prefix command
//If we've got a Channel-Command without trigger...that has NOT been processed before!
if(!$recv["processed"] && $this -> protocol -> checkChannel($recv)){
$recv["command"] = $recv["target"];
if($code = $this -> commands -> getCode($recv["command"], "channel", $recv["level"])){
if($this -> debug) echo "[EVAL] Command: ".$recv["command"]." (channel)\n";
eval($code);
unset($code, $recv["command"]);
$recv["processed"] = true;
}
}//if channel
$recv["command"] = (isset($recv["command"]) && !$recv["processed"]) ? $recv["command"] : "";
return $recv;
}
/*
* Updating recvbuf and flushing sendbuf while checking for incoming pings
*/
public function update(){
if($this -> connection -> isConnected()){
//Putting recieved stuff into the recv-buffer
while($recv = $this -> connection -> recv()){
$this -> reactbuf -> addData($recv);
unset($recv);
}//recv loop
//emergency flood protection
$lines = 0;
$lines = $this -> reactbuf -> countLines();
if(($lines > 215)){
if($this -> debug) echo "[FLOOD] Houston, we have got a problem! (".$lines." lines at once)\n";
$this -> disconnect();
}
//react to stuff
while($this -> reactbuf -> gotLine()){
if(!$this -> react($recv = $this -> reactbuf -> getNextLine())) $this -> recvbuf -> addData($recv);
}
//Sending everything we've got in our buffer
while($this -> sendbuf -> gotLine()){
$data = $this -> sendbuf -> getNextLine();
if(($data != $this -> lastline && trim($data) != "") || ($data == $this -> lastline && time() - 10 > $this -> lastlinetime)){//we will never repeat something within 10 seconds
if($this -> debug) echo "Sending: '".$data."'\n";
$this -> connection -> send($data);
$this -> lastline = $data;
$this -> lastlinetime = time();
unset($data);
usleep($this -> sleep);
}
}//send loop
//if there is nothing to process and nothing to recieve, we can allow the cpu to have a break.
if(!$this -> recvbuf -> gotLine() && !$this -> connection -> gotData()) usleep($this -> sleep);
}//if connected
else{
if($this -> debug) echo "Connection closed!\n";
$this -> disconnect();
}
}
public function disconnect(){
if($this -> debug) echo "Disconnecting...";
$this -> clearBuffer();
$this -> login = false;
$this -> lastline = "";
$this -> lastlinetime = 0;
if($this -> connection){
unset($this -> connection);
$this -> connection = false;
}
if($this -> debug) echo "done.\n";
if($this -> autoreconnect){
sleep(1);
if($this -> debug) echo "AutoReconnect enabled. Reconnecting now...\n";
$this -> connect();
if($this -> debug) echo "Reconnect done.\n";
}
}
/*
* Other stuff...
*/
//Nickchange within runtime
public function setNick($nick){
if($this -> nick != $nick){
$this -> nick = $nick;
$this -> send($this -> protocol -> setNick($this -> nick));
$this -> update();
}
}
//Call IRC-Protocol functions (join, part, privmsg,....) and send the result directly
public function call(){
$params = func_get_args();
$function = $params[0];
$array = array_slice($params,1);
foreach($array as $value) $args[] = addslashes($value);
$args = implode("', '",$args);
$call = "\$data = \$this -> protocol -> ".$function."('".$args."');";
if($this -> debug) echo "[EVAL] Calling: '".$call."'\n";
eval($call);
$this -> send(stripslashes($data));
unset($call, $data);
}
/*
* Reacting to some important stuff
*/
private function react($recv){
$p = $this -> protocol -> parse($recv);
if($this -> login == false && ($p["servermsg"] === true && $p["action"] == "001")) $this -> login = true; //we're logged in if 001 MSG arrives
elseif($this -> login == false && ($p["servermsg"] === true && $p["action"] == "433")) $this -> setNick($this -> nick."|".rand(0,9)); //our chosen nick is already in use
elseif($p["servermsg"] === true && $p["action"] == "474") $this -> removeChannels(array($p["param"])); //our chosen nick is already in use
//if we've got a PING
elseif(trim($data = $this -> protocol -> checkPing($recv)) != ""){
$this -> send($data);
unset($data);
}
//check for a serverkick (ERROR)
elseif(preg_match("/^ERROR :/", $recv)){
if($this -> debug) echo "Serverkick!\n";
$this -> disconnect();
}
elseif($p["action"] == "KICK" && $p["param"] == $this -> nick) $this -> call("join", $p["target"]); //check for being kicked..
elseif($p["action"] == "PART" && $p["nick"] == $this -> nick) $this -> call("join", $p["target"]); //check for being parted..
elseif($p["action"] == "QUIT" || $p["action"] == "PART") $this -> userdb -> logout($p["nick"]); //check for user logout
else{
//bot did not react to this line
return false;
}
//bot reacted to this in some way.
return true;
}
/*
* Other stuff....
*/
//evaluating php-code and return output (WILL CAUSE PROBLEMS WHEN DEBUG-OUTPUT IS ON!)
public function eval_php($code){
ob_start();
eval($code);
$output = ob_get_contents();
ob_clean();
ob_end_clean();
return $output;
}
public function eval_sys($cmd){
$p = popen($cmd, "r");
while($data = fgets($p, 8192)){
$result[] = $data;
}
pclose($p);
return $result;
}
}
?>