gawdsed Posted May 27, 2013 Share Posted May 27, 2013 I've posted below the entire bot's source code, with edited values for passwords and such. My bot for IRC works great, doesn't disconnect for the most part... but after 1-3 days, the bot hangs without any data being received or sent. If for example, I reset/disable my NIC card locally, the bot detects it and keeps trying to re-connect(detects fine...). I went as far as adding all raw data received to a TXT file, but even then... nothing seems abnormal. I suspect that the server is closing the connection, for whatever reason after a certain amount of time, and sends no message to my bot, so my bot stays connected and is waiting for data on the following line (line 337) $flag = @socket_recv($this->socket, $buffer, 4096, 0); I check for things like the buffer being empty or the flag being empty, I've been struggling with this error for a while... any help would be most grateful. I also tried using MSG_DONTWAIT as follows: $flag = @socket_recv($this->socket, $buffer, 4096, MSG_DONTWAIT); But with this method, I was unable to obtain any data correctly... just prints nothing for flags or the buffer... maybe i just don't know what this does or understand how it works? If I could get MSG_DONTWAIT working correctly, I could check the time between the last data and reconnect the bot this way... Thanks in advance /* * JTV Chat bot by Gawdsed * * @author Mathieu "Gawdsed" Graham (mathieugraham@gmail.com) and unofficially Ferdinand E. Silva (six519@phpugph.com) * @version Version 0.25 Alpha * * Special thanks to Ferdinand E. Silva for his starter code to connect to the IRC server, his code was great help as a starting point. * I've edited this starter code heavily though, including parsing text with correct triggers and editing the login process. * More is in stock and I will keep this program updated. */ <?php class PHPIRC { /************************************* /////////////EDIT BELOW/////////////// *************************************/ //server and channel connection info private $IrcServer = "avilo.jtvirc.com"; private $IrcPort = 6667; private $IrcNick = "avilomodbot"; private $IrcPass = "fake123"; private $IrcRoom = "avilo"; //database connection if you have one, set usedatabase to false if you dont private $useDatabase = false; private $DB_HOST = "123.123.123.123"; private $DB_USER = "asdasdasda"; private $DB_NAME = "asd"; private $DB_PASS = "fake123"; //timezone since PHP can't grab the local time for whatever reason... Just change it to your timezone number... central -6 for example private $timezone = "-7"; //by minutes... interval between announcement messages... recommended(20-30) private $announcementInterval = 30; //Decide if the bot should announce itself when logging in. private $announceSelf = false; //Show ping/pong? private $showPingPong = false; /************************************* ////////////STOP EDITING////////////// *************************************/ //standard variables and objects private $socket; private $isConnected = false; private $isAuthenticated = false; private $nextmessage = ""; //stores ops in array from information sent from server private $ops = []; //used for triggers for messages sent private $fileCanned = []; private $fileReply = []; private $fileNews = []; //constructor public function __construct() { $this->main(); } //main loop private function main() { date_default_timezone_set('Etc/GMT' . $this->timezone); //infinit loop while(1==1) { if(!$this->getDbInfo()) { echo $this->timestamp() . "Database not loaded, using local files\n"; } $this->loadFiles(); $this->connect(); //connect to irc server sleep(5); echo $this->timestamp() . "Attempting to reconnect...\n"; } } //send out message if time from now and set is greater private function checkminutes() { $date1 = new DateTime($this->timestampTostring()); if($this->nextmessage == "") { $this->nextmessage = new DateTime($this->timestampTostring()); $this->nextmessage->add(new DateInterval("PT" . $this->announcementInterval . "M")); $date2 = $this->nextmessage; } else { $date2 = $this->nextmessage; } $interval = $date1->diff($date2, false); //echo $this->timestampTostring(); $minutes = $interval->i; $hours = $interval->h; $days = $interval->d; $months = $interval->m; $minutescount = $minutes + ($hours*60) + ($days*24*60) + ($months*24*60*31); if (($minutescount <= 0 && count($this->fileNews) != 0) || (($date1 > $date2) && count($this->fileNews))) { for($i = 0;$i<=count($this->fileNews)-2;$i++) { $this->sendMessage("PRIVMSG #" . $this->IrcRoom . " :" . $this->fileNews[$i] . "\r\n"); sleep(1); //adding the amount requested by user till next broadcast $this->nextmessage = new DateTime($this->timestampTostring()); $this->nextmessage->add(new DateInterval("PT" . $this->announcementInterval . "M")); //echo to console echo $this->timestamp() . $this->IrcNick . ": " . $this->fileNews[$i] . "\n"; } //get mods as well, why not? $this->sendMessage("PRIVMSG " . $this->IrcRoom . " :/mods\r\n"); } } //returns the time since there is no tostring for dates... private function timestampTostring() { return date('Y-m-d H:i:s'); } //return string of a timestamp private function timestamp() { $date = date_create(); return "(" . $date->format( 'h:i:s' ) . ")"; } //loads files to arrays for easier access private function loadFiles() { $this->fileCanned = explode("\r\n", $this->readFile("1")); $this->fileReply = explode("\r\n", $this->readFile("2")); $this->fileNews = explode("\r\n", $this->readFile("3")); } //connects to db, overwrites files if needed private function getDbInfo() { if(!$this->useDatabase) { $this->createFile("1"); $this->createFile("2"); $this->createFile("3"); return false; } $db = new mysqli($this->DB_HOST, $this->DB_USER, $this->DB_PASS, $this->DB_NAME); if (mysqli_connect_errno()) { echo $this->timestamp() . "Connect failed: ", mysqli_connect_error() . "\n"; return false; } //get canned trigger and reply from table (name) and put into files $query = "SELECT * FROM ". $this->IrcRoom . " WHERE isnews = 0"; $result = $db->query($query); if($result === true) { $rowCount = $result->num_rows; } else { $rowCount = 0; } if($rowCount != 0) { $this->recreateFile("1"); $this->recreateFile("2"); while($row = mysqli_fetch_row($result)) { $this->appendFile($row[1],"1"); $this->appendFile($row[2],"2"); } } else { $this->createFile("1"); $this->createFile("2"); } //get news and put into file (name)news $query = "SELECT * FROM ". $this->IrcRoom . " WHERE isnews = 1"; $result = $db->query($query); if($result === true) { $rowCount = $result->num_rows; } else { $rowCount = 0; } if($rowCount != 0) { $this->recreateFile("3"); while($row = mysqli_fetch_row($result)) { $this->appendFile($row[2],"3"); } } else { $this->createFile("3"); } return true; } //reads a file private function readFile($number) { $data = ""; $fileName = $this->IrcRoom . $number; if(file_exists($fileName) && (filesize($fileName) != 0)) { $handle = fopen($fileName, 'r'); $data = fread($handle,filesize($fileName)); fclose($handle); } return $data; } //adds to a file private function appendFile($data,$number) { $fileName = $this->IrcRoom . $number; $fh = fopen($fileName, "a"); fwrite($fh, "" . $data . "\r\n"); fclose($fh); } //recreates files if they exist private function recreateFile($number) { $fileName = $this->IrcRoom . $number; if(file_exists($fileName)) { unlink($fileName); } $handle = fopen($fileName, 'w') or die('Cannot open file: ' . $fileName); fclose($handle); } //creates a file private function createFile($number) { $fileName = $this->IrcRoom . $number; if(file_exists($fileName)) { } else { $handle = fopen($fileName, 'w') or die('Cannot open file: ' . $fileName); fclose($handle); } } //gets user input private function getUserInput($msg) { $endInput = false; while(!$endInput) { echo "\n" . $msg . ": "; $handle = fopen ("php://stdin","r"); $line = fgets($handle); if(trim($line) != "") { $endInput = true; return trim($line); } fclose($handle); } } //connects to IRC server private function connect() { $this->socket = socket_create(AF_INET,SOCK_STREAM,SOL_TCP); if(@socket_connect($this->socket, $this->IrcServer,$this->IrcPort)) { $this->isConnected = true; $this->receiveMessages(); }else { echo $this->timestamp() . "Cannot Connect To Server. \n"; echo $this->timestamp() . socket_strerror(socket_last_error($this->socket)) . "\n"; } } //main parsing loop for all incoming server messages private function receiveMessages() { if($this->isConnected) { $flag = 1; $buffer = ""; $this->sendMessage("PASS " . $this->IrcPass . "\r\n" . "USER " . $this->IrcNick . " 0 * :" . $this->IrcNick . "\r\n" . "NICK " . $this->IrcNick . "\r\n"); echo $this->timestamp() . "Connecting to " . $this->IrcServer . " on port " . $this->IrcPort . " as user " . $this->IrcNick . "\n"; $this->sendMessage("JOIN #" . $this->IrcRoom . "\r\n"); echo $this->timestamp() . "Joining channel " . $this->IrcRoom . "\n"; $this->isAuthenticated=true; $this->sendMessage("JTVCLIENT \r\n"); $this->sendMessage("PRIVMSG " . $this->IrcRoom . " :/mods\r\n"); if($this->announceSelf) { $this->sendMessage("PRIVMSG #" . $this->IrcRoom . " :Bot initializing... please wait 10 seconds before sending commands. \r\n"); echo $this->timestamp() . $this->IrcNick . ": " . "Bot initializing... please wait up to 10 seconds before sending commands.\n"; } echo $this->timestamp() . "Connected successfully to server, waiting for text...\n"; while($this->isConnected) { $this->checkminutes(); $flag = @socket_recv($this->socket, $buffer, 4096, 0); if($buffer == "" || $flag === 0) { $this->isConnected = false; $this->isAuthenticated = false; echo $this->timestamp() . "\n********Client Disconnected********\n"; } //$this->appendFile("error: " . socket_strerror(socket_last_error($this->socket)),"debug"); $pieces = explode("\r\n", $buffer); //$this->appendFile($this->timestamp() . "\n****new***** " . $buffer . " *****end*****","debug"); for($i = 0; $i < count($pieces);$i++) { //message handle //echo "\n***BUFFER***:\n" . $pieces[$i] . "\n***ENDBUFFER***\n"; if(preg_match("/Checking Ident/",$pieces[$i]) && !$this->isAuthenticated) { $this->sendMessage("PASS " . $this->IrcPass . "\r\n" . "USER " . $this->IrcNick . " 0 * :" . $this->IrcNick . "\r\n" . "NICK " . $this->IrcNick . "\r\n"); } if(preg_match("/Nickname is already in use/",$pieces[$i]) && !$this->isAuthenticated) { $this->IrcNick=$this->asdgetUserInput("Please Enter New Irc Nick"); $this->sendMessage("PASS " . $this->IrcPass . "\r\n" . "USER " . $this->IrcNick . " 0 * :" . $this->IrcNick . "\r\n" . "NICK " . $this->IrcNick . "\r\n"); } if(preg_match("/Erroneous Nickname/",$pieces[$i]) && !$this->isAuthenticated) { $this->IrcNick=$this->getUserInput("Please Enter New Irc Nick"); $this->sendMessage("PASS " . $this->IrcPass . "\r\n" . "USER " . $this->IrcNick . " 0 * :" . $this->IrcNick . "\r\n" . "NICK " . $this->IrcNick . "\r\n"); } if(preg_match("/This nickname is registered/",$pieces[$i]) && !$this->isAuthenticated) { $this->IrcNick=$this->getUserInput("Please Enter New Irc Nick"); $this->sendMessage("PASS " . $this->IrcPass . "\r\n" . "USER " . $this->IrcNick . " 0 * :" . $this->IrcNick . "\r\n" . "NICK " . $this->IrcNick . "\r\n"); } if(preg_match("/End of \/MOTD command/",$pieces[$i]) && !$this->isAuthenticated) { $this->isAuthenticated=true; $this->sendMessage("JOIN #" . $this->IrcRoom . "\r\n"); } if(preg_match("/PING/",$pieces[$i])) { $this->sendMessage("PONG tmi.twitch.tv :TIMEOUTCHECK\r\n"); //$this->appendFile( $this->timestamp() . "PONG tmi.twitch.tv :TIMEOUTCHECK\r\n","debug"); if($this->showPingPong) { echo $this->timestamp() . "ping pong\n"; } } if(preg_match("/PART/", $pieces[$i])) { $tmpStr = preg_split("/:/", $pieces[$i]); $tmpStr = preg_split("/!/", $tmpStr[1]); $name = $tmpStr[0]; //nick of the sender $message = explode(" ", $pieces[$i]); echo $this->timestamp() . $message[1] . ": " . $name . "\n"; } if(preg_match("/JOIN/", $pieces[$i])) { $tmpStr = preg_split("/:/", $pieces[$i]); if(isset($tmpStr[1])) { $tmpStr = preg_split("/!/", $tmpStr[1]); $name = $tmpStr[0]; //nick of the sender $message = explode(" ", $pieces[$i]); echo $this->timestamp() . $message[1] . ": " . $name . "\n"; } else { echo $this->timestamp() . "Exception caught: " . $pieces[$i] . "\n"; } } if(preg_match("/PRIVMSG/", $pieces[$i])) { //echo $pieces[$i] . "(prvimsg)\n"; //parse and display channel messages $messagetogether = ""; $k = 2; $tmpStr = preg_split("/:/", $pieces[$i]); $tmpStr = preg_split("/!/", $tmpStr[1]); $name = $tmpStr[0]; //nick of the sender if(strpos($name," ") !== false) { $nametemp = explode(" ", $name); $name = $nametemp[0]; } $message = explode(":", $pieces[$i]); //message received //sometimes formatting changes a bit... lets make sure we only get the first item, aka the name. if($name == "jtv") { if(strpos($pieces[$i],":The moderators of this room are:") !== false) { $this->ops = []; $message[3] = str_replace(' ', '', $message[3]); $opsinfo = explode(",", $message[3]); for($t = 0;$t<count($opsinfo);$t++) { array_push($this->ops, $opsinfo[$t]); } echo $this->timestamp() . "Obtained " . count($opsinfo) . " mods from JTV" ."\n"; } }else if(isset($message[2])) { while(isset($message[$k])) { if($k == 2) { $messagetogether .= $message[$k]; $k+=1; } else { $messagetogether .= ":" . $message[$k]; $k+=1; } } //check to see if user is a mod or not $ismod = false; for($x = 0;$x<count($this->ops)-1;$x++) { if($name == $this->ops[$x]) { $ismod = true; } } if($ismod) { echo $this->timestamp() . "@" . $name . ": " . $messagetogether . "\n"; } else { echo $this->timestamp() . $name . ": " . $messagetogether . "\n"; } $this->trigger($name,$messagetogether); } else { echo $this->timestamp() . "Received message but parsed wrong... \n"; echo $pieces[$i] . "\n"; } } if(preg_match("/MODE/",$pieces[$i])) { //add mods to an array list, using /mods instead /* $tmpStr = preg_split("/ /", $pieces[$i]); if($tmpStr[3] == "+o") { array_push($this->ops, $tmpStr[4]); $this->ops = array_unique($this->ops); //echo "adding " . $tmpStr[4] . " as ops\n"; } elseif($tmpStr[3] == "-o") { $index = array_search($tmpStr[4],$this->ops); if($index !== false){ unset($this->ops[$index]); $this->ops = array_values($this->ops); } }*/ } } } echo $this->timestamp() . "Not connected anymore\n"; } } //triggers for messages that people send, check for mod, then ! and the keyword, reply if found, exit once found. private function trigger($nick, $msg) { for($i = 0;$i<count($this->ops)-1;$i++) { if($nick == $this->ops[$i]) { if(isset($this->fileCanned[0])) { for($e=0;$e<=count($this->fileCanned)-1;$e++) { $temp = explode(" ", $this->fileCanned[$e]); for($k=0;$k<=count($temp)-1;$k++) { if($msg == "!" . $temp[$k]) { echo $this->timestamp() . $this->IrcNick . ": " . $this->fileReply[$e] . "\n"; $this->sendMessage("PRIVMSG #" . $this->IrcRoom . " :" . $this->fileReply[$e] . "\r\n"); usleep(1500000); return; } } } } if($msg == "!?") { $messagestring = "Possible commands:"; for($e=0;$e<=count($this->fileCanned)-1;$e++) { $temp = explode(" ", $this->fileCanned[$e]); for($k=0;$k<=count($temp)-1;$k++) { $messagestring .= " " . $temp[$k]; } } $this->sendMessage("PRIVMSG #" . $this->IrcRoom . " :" . $messagestring . " sync " . "reload" . "\r\n"); echo $this->timestamp() . $this->IrcNick . ": " . $messagestring . "sync" . " reload" . "\n"; return; } if($msg == "!sync" || $msg == "!reload") { if($this->useDatabase) { $this->sendMessage("PRIVMSG #" . $this->IrcRoom . " :" . "sync comand received, syncing from database!" . "\r\n"); echo $this->timestamp() . $this->IrcNick . ": " . "sync comand received, syncing from database!" . "\n"; } else { $this->sendMessage("PRIVMSG #" . $this->IrcRoom . " :" . "Reload comand received, rebooting and re-loading files! (database sync not enabled)" . "\r\n"); echo $this->timestamp() . $this->IrcNick . ": " . "Reload comand received, rebooting and re-loading files! (database sync not enabled)" . "\n"; } $this->isConnected = false; return; } } } } //sends a message to the server private function sendMessage($msg) { socket_write($this->socket,$msg,strlen($msg)); } } //run PHPIRC $run = new PHPIRC(); ?> Quote Link to comment https://forums.phpfreaks.com/topic/278423-irc-bot-does-not-detect-disconnect-from-server-but-does-for-local/ Share on other sites More sharing options...
kicken Posted May 27, 2013 Share Posted May 27, 2013 Read up on non-blocking sockets and socket_select. Try and implement that solution and if you still have issues then post back with some updated code. You could also try enabling the SO_KEEPALIVE option using socket_set_option. Quote Link to comment https://forums.phpfreaks.com/topic/278423-irc-bot-does-not-detect-disconnect-from-server-but-does-for-local/#findComment-1432480 Share on other sites More sharing options...
gawdsed Posted May 27, 2013 Author Share Posted May 27, 2013 (edited) I will attempt to implement this and reply if I have any more questions. I knew I had to use a non-blocking socket(which is why i tried using MSG_DONTWAIT ), but I didn't really know how or if there was a better method... first time socket coder. Thanks Edited May 27, 2013 by gawdsed Quote Link to comment https://forums.phpfreaks.com/topic/278423-irc-bot-does-not-detect-disconnect-from-server-but-does-for-local/#findComment-1432482 Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.