CodeDemons PHP IRC bot

An example of a PHP-based IRC bot by codedemons from a tutorial which is unfortunately not online anymore and can only be fetched from archive.org.

README.txt
You can use this code however you please. Just remember to put in a good word for CodeDemons, and our tutorials ;) By: Jordan Smith For: http://www.codedemons.net/

Both PHP files in this archive are working examples of a bot.

skeleton_bot.php --- This bot only connects to the channel. Adding features is up to you. This is the bot that is created in the tutorial.

complete_bot.php --- This bot does everything skeleton_bot.php does, but also includes examples of functions, such as .time (to display the current time), .nick (to change the bot's nick), and some more. Perfect if you want to see the bot in action, and add a few more features. There is also a BASIC logging function.

This bot also includes some more functions such as prep_text, parse_buffer, process_commands.

skeleton_bot.php
 <?php /* demonbot - a demonstation for codedemons.net

/* Variables that determine server, channel, etc */ $CONFIG = array; $CONFIG['server'] = 'irc.gamesurge.net'; // server (i.e. irc.gamesnet.net) $CONFIG['nick'] = 'demonbot'; // nick (i.e. demonbot $CONFIG['port'] = 6667; // port (standard: 6667) $CONFIG['channel'] = '#php'; // channel (i.e. #php) $CONFIG['name'] = 'demonbot'; // bot name (i.e. demonbot) $CONFIG['admin_pass'] = 'pass'; // admin pass (to change settings remotely)

/* Let it run forever (no timeouts) */ set_time_limit(0);

/* The connection */ $con = array;

/* start the bot... */ init;

function init {	global $con, $CONFIG; /* We need this to see if we need to JOIN (the channel) during the first iteration of the main loop */ $firstTime = true; /* Connect to the irc server */ $con['socket'] = fsockopen($CONFIG['server'], $CONFIG['port']); /* Check that we have connected */ if (!$con['socket']) { print ("Could not connect to: ". $CONFIG['server'] ." on port ". $CONFIG['port']); } else { /* Send the username and nick */ cmd_send("USER ". $CONFIG['nick'] ." codedemons.net codedemons.net :". $CONFIG['name']); cmd_send("NICK ". $CONFIG['nick'] ." codedemons.net"); /* Here is the loop. Read the incoming data (from the socket connection) */ while (!feof($con['socket'])) {			/* Think of $con['buffer']['all'] as a line of chat messages. We are getting a 'line' and getting rid of whitespace around it. */			$con['buffer']['all'] = trim(fgets($con['socket'], 4096)); /* Pring the line/buffer to the console I used <- to identify incoming data, -> for outgoing. This is so that you can identify messages that appear in the console. */			print date("[d/m @ H:i]")."<- ".$con['buffer']['all'] ."\n"; /* If the server is PINGing, then PONG. This is to tell the server that we are still here, and have not lost the connection */ if(substr($con['buffer']['all'], 0, 6) == 'PING :') { /* PONG : is followed by the line that the server sent us when PINGing */ cmd_send('PONG :'.substr($con['buffer']['all'], 6)); /* If this is the first time we have reached this point, then JOIN the channel */ if ($firstTime == true){ cmd_send("JOIN ". $CONFIG['channel']); /* The next time we get here, it will NOT be the firstTime */ $firstTime = false; }				/* Make sure that we have a NEW line of chats to analyse. If we don't,				there is no need to parse the data again */ } elseif ($old_buffer != $con['buffer']['all']) { /* Determine the patterns to be passed to parse_buffer. buffer is in the form: :username!~identd@hostname JOIN :#php :username!~identd@hostname PRIVMSG #PHP :action text :username!~identd@hostname command channel :text */ /*				Right now this bot does nothing. But this is where you would add some conditions, or see what is being said in the chat, and then respond. Before you try doing that you should become familiar with how commands are send over IRC. Just read the console when you run this script, and then you will see the patterns in chats, i.e. where the username occurs, where the hostmask is, etc. All you need is functions such as				preg_replace_callback, or perhaps your own function that checks for patterns in the text. Good Luck. */			}			$old_buffer = $con['buffer']['all']; }	} }

/* Accepts the command as an argument, sends the command to the server, and then displays the command in the console for debugging */ function cmd_send($command) {	global $con, $time, $CONFIG; /* Send the command. Think of it as writing to a file. */	fputs($con['socket'], $command."\n\r"); /* Display the command locally, for the sole purpose of checking output. (line is not actually not needed) */ print (date("[d/m @ H:i]") ."-> ". $command. "\n\r"); } ?> 

complete_bot.php
 <?php /* demonbot - a demonstation for codedemons.net

/* Variables that determine server, channel, etc */ $CONFIG = array; $CONFIG['server'] = 'irc.gamesurge.net'; // server (i.e. irc.gamesnet.net) $CONFIG['nick'] = 'demonbot'; // nick (i.e. demonbot $CONFIG['port'] = 6667; // port (standard: 6667) $CONFIG['channel'] = '#bot_test'; // channel (i.e. #php) $CONFIG['name'] = 'demonbot'; // bot name (i.e. demonbot) $CONFIG['admin_pass'] = 'asus';

/* Let it run forever (no timeouts) */ set_time_limit(0);

/* The connection */ $con = array;

/* start the bot... */ init;

function init {	global $con, $CONFIG; /* We need this to see if we need to JOIN (the channel) during the first iteration of the main loop */ $firstTime = true; /* Connect to the irc server */ $con['socket'] = fsockopen($CONFIG['server'], $CONFIG['port']); /* Check that we have connected */ if (!$con['socket']) { print ("Could not connect to: ". $CONFIG['server'] ." on port ". $CONFIG['port']); } else { /* Send the username and nick */ cmd_send("USER ". $CONFIG['nick'] ." codedemons.net codedemons.net :". $CONFIG['name']); cmd_send("NICK ". $CONFIG['nick'] ." codedemons.net"); /* Here is the loop. Read the incoming data (from the socket connection) */ while (!feof($con['socket'])) {			/* Think of $con['buffer']['all'] as a line of chat messages. We are getting a 'line' and getting rid of whitespace around it. */			$con['buffer']['all'] = trim(fgets($con['socket'], 4096)); /* Pring the line/buffer to the console I used <- to identify incoming data, -> for outgoing. This is so that you can identify messages that appear in the console. */			print date("[d/m @ H:i]")."<- ".$con['buffer']['all'] ."\n"; /* If the server is PINGing, then PONG. This is to tell the server that we are still here, and have not lost the connection */ if(substr($con['buffer']['all'], 0, 6) == 'PING :') { /* PONG : is followed by the line that the server sent us when PINGing */ cmd_send('PONG :'.substr($con['buffer']['all'], 6)); /* If this is the first time we have reached this point, then JOIN the channel */ if ($firstTime == true){ cmd_send("JOIN ". $CONFIG['channel']); /* The next time we get here, it will NOT be the firstTime */ $firstTime = false; }				/* Make sure that we have a NEW line of chats to analyse. If we don't,				there is no need to parse the data again */ } elseif ($old_buffer != $con['buffer']['all']) { /* Determine the patterns to be passed to parse_buffer. buffer is in the form: :username!~identd@hostname JOIN :#php :username!~identd@hostname PRIVMSG #PHP :action text :username!~identd@hostname command channel :text */ /*				Right now this bot does nothing. But this is where you would add some conditions, or see what is being said in the chat, and then respond. Before you try doing that you should become familiar with how commands are send over IRC. Just read the console when you run this script, and then you will see the patterns in chats, i.e. where the username occurs, where the hostmask is, etc. All you need is functions such as				preg_replace_callback, or perhaps your own function that checks for patterns in the text. Good Luck. */				// log the buffer to "log.txt" (file must have				// already been created). // log_to_file($con['buffer']['all']); // make sense of the buffer parse_buffer; // now process any commands issued to the bot process_commands; }			$old_buffer = $con['buffer']['all']; }	} }

/* Accepts the command as an argument, sends the command to the server, and then displays the command in the console for debugging */ function cmd_send($command) {	global $con, $time, $CONFIG; /* Send the command. Think of it as writing to a file. */	fputs($con['socket'], $command."\n\r"); /* Display the command locally, for the sole purpose of checking output. (line is not actually not needed) */ print (date("[d/m @ H:i]") ."-> ". $command. "\n\r"); }

function log_to_file ($data) {	$filename = "log.txt"; $data .= "\n"; // open the log file if ($fp = fopen($filename, "ab")) {       // now write to the file if ((fwrite($fp, $data) === FALSE)) {           echo "Could not write to file. "; }   }    else {       echo "File could not be opened. "; } }

function process_commands {	global $con, $CONFIG; /* TIME */ if(strtoupper($con['buffer']['text']) == '.TIME') { cmd_send(prep_text("Time", date("F j, Y, g:i a", time))); }	/* NICK */ if (substr(strtoupper($con['buffer']['text']), 0, 5) == ".NICK"){ $args = explode(" ", $con['buffer']['text']); if (count($args) < 3) cmd_send(prep_text("Nick", "Syntax: .nick admin_pass new_nick")); else {			if ($args[1] == $CONFIG['admin_pass']) cmd_send("NICK ". $args[2]); else cmd_send(prep_text("Nick", "Invalid password")); }	}	/* Noob */ if(strtoupper(substr($con['buffer']['text'], 0, 5)) == '.NOOB') { $args = explode(" ", $con['buffer']['text'], 2); $name = (!empty($args[1]))?$args[1]:"beginner"; cmd_send(prep_text("Beginner Help", "Welcome, ".$name.", to PHP! Some tutorials: www.codedemons.net, www.zend.com, www.phpbuilder.com, www.php.net")); }	/* No PMs */ if(strtoupper(substr($con['buffer']['text'], 0, 5)) == '.PM') { cmd_send(prep_text("please"," Please do not send PMs to ops/peons unless you have asked first.")); } }

function parse_buffer {	/*	:username!~identd@hostname JOIN :#php :username!~identd@hostname PRIVMSG #PHP :action text :username!~identd@hostname command channel :text */	global $con, $CONFIG; $buffer = $con['buffer']['all']; $buffer = explode(" ", $buffer, 4); /* Get username */ $buffer['username'] = substr($buffer[0], 1, strpos($buffer['0'], "!")-1); /* Get identd */ $posExcl = strpos($buffer[0], "!"); $posAt = strpos($buffer[0], "@"); $buffer['identd'] = substr($buffer[0], $posExcl+1, $posAt-$posExcl-1); $buffer['hostname'] = substr($buffer[0], strpos($buffer[0], "@")+1); /* The user and the host, the whole shabang */ $buffer['user_host'] = substr($buffer[0],1); /* Isolate the command the user is sending from the "general" text that is sent to the channel This is privmsg to the channel we are talking about. We also format $buffer['text'] so that it can be logged nicely. */	switch (strtoupper($buffer[1])) {		case "JOIN": $buffer['text'] = "*JOINS: ". $buffer['username']." ( ".$buffer['user_host']." )"; $buffer['command'] = "JOIN"; $buffer['channel'] = $CONFIG['channel']; break; case "QUIT": $buffer['text'] = "*QUITS: ". $buffer['username']." ( ".$buffer['user_host']." )"; $buffer['command'] = "QUIT"; $buffer['channel'] = $CONFIG['channel']; break; case "NOTICE": $buffer['text'] = "*NOTICE: ". $buffer['username']; $buffer['command'] = "NOTICE"; $buffer['channel'] = substr($buffer[2], 1); break; case "PART": $buffer['text'] = "*PARTS: ". $buffer['username']." ( ".$buffer['user_host']." )"; $buffer['command'] = "PART"; $buffer['channel'] = $CONFIG['channel']; break; case "MODE": $buffer['text'] = $buffer['username']." sets mode: ".$buffer[3]; $buffer['command'] = "MODE"; $buffer['channel'] = $buffer[2]; break; case "NICK": $buffer['text'] = "*NICK: ".$buffer['username']." => ".substr($buffer[2], 1)." ( ".$buffer['user_host']." )"; $buffer['command'] = "NICK"; $buffer['channel'] = $CONFIG['channel']; break; default: // it is probably a PRIVMSG $buffer['command'] = $buffer[1]; $buffer['channel'] = $buffer[2]; $buffer['text'] = substr($buffer[3], 1); break; }	$con['buffer'] = $buffer; }

function prep_text($type, $message) {	global $con; return ('PRIVMSG '. $con['buffer']['channel'] .' :['.$type.']'.$message); } ?> 