356 lines
11 KiB
PHP
356 lines
11 KiB
PHP
<?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;
|
|
}
|
|
|
|
}
|
|
|
|
?>
|