New tutorials View all
New forum replies
New frontpage replies
New articles
New news
Creating an IRC bot in PHP (916 views)
Combined Minds presents you the biggest and best PHP -> IRC bot tutorial on the web! This tutorial learns you how to create a very good working IRC bot from scratch!
You may think, "what does an IRC bot has to do with webdevelopment?". Well, nothing actually. I will just use a interesting subject for talking about the sockets. On this way the tutorial will be much more fun to write, and read.
When making a PHP powered bot to connect to some external source, you need to know 2 major things. How to connect and talk to the server, and what you need to talk.
The first one is the PHP part, while the second part is the API or Protocol of the sort of server you are connecting to.
We will connect to an IRC server in this tutorial, i will explain the basics of the IRC protocol. But i will mainly talk about how to connect and read/write to the server in PHP. For that is the one thing you will be using on every protocol.
How to connect
First select the server you want to connect to. The server i will use is irc.rizon.net, port 6667. We will use fsockopen(); for opening the socket You could also use the socket_create() function, but this does not work well often for the windows powered servers.
This function works by first specifying the server address (can be a URL, can be an IP address). The second is the port. You may use a third and fourth one for error handling, and a fifth one for a time-out number. For more information, please see PHP.net.
The following example is how to use it. I did not use the third, fourth and fifth parameters. Because i want to keep it simple for this tutorial.
| php | |
|
1 2 3 4 5 6 |
<?php // Opening the socket to the Rizon network $socket = fsockopen("irc.rizon.net", 6667); ?> |
As you can see, save the socket data in a variable/object.
After connecting to the server, you must give it some details:
USER A-trivial-nickname a-domain-like-combined-minds.net some-trivial-stuff :your-real-name
NICK CM-bot
The first line looks rather weird. But just fill in something the same as i got there. If you do not know what to edit exactly, just copy the line from me.
The second line is simple, just the NICK command, followed by the nickname desired.
To send text to the server, you need to use the fputs(); function. This functions works by first giving it the socket variable, where the data needs to go. The second is the text needs to be send. When you send text to the server, always close it with a new line! (\n) Or else the server will think the text you will send is all one line, and wont process it correctly.
This is what i send, using the fputs(); function.
| php | |
|
1 2 3 4 5 6 7 8 9 10 |
|
This will open the connection, and after that it will tell the server what/who it is.
Two important things, tell the server which channel it should join. This is done by the JOIN command. Followed by the channel you want to join. For this tutorial i registered the channel #cm-bot-test on the rizon network.
The second very important thing, is to change the nickname of the bot for testing!. If you will test it at the Rizon network that is. (i suggest you do it at the rizon network, for i will be there sometimes to help you guys)
| php | |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
Next thing to do is read what the server has to say back. But there is one little problem you might think. Most people who think of "How can you keep an idle connection in PHP? It will stop after the script has run right?". Well of course the script stops when its ended. Thats why we'll make sure the script wont end
.We will need 2 things. An endless while, and something to stop PHP from canceling the script after the normal 30sec time limit. The last one is easy to solve, PHP has the function set_time_limit(). You use this function the following way:
| php | |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
<?php // Prevent PHP from stopping the script after 30 sec set_time_limit(0); // Opening the socket to the Rizon network $socket = fsockopen("irc.rizon.net", 6667); // Send auth info fputs($socket,"USER CMbot combined-minds.net CM :CM bot\n"); fputs($socket,"NICK CM-bot\n"); // Join channel fputs($socket,"JOIN #cm-bot-test\n"); ?> |
Simple give PHP the number of second you want the script to run maximally. When it's set to zero it will cancel the whole max run time thing. And you are able to run the script as long as you want.
Next is actually preventing from PHP to stop exiting the script while the bot is running. We will use while() for this job. We will make a while loop, that wont stop until we say it can. This is called an endless while, a while loop without an end. For this to work we need to keep the so called "expression" to be TRUE. (the expression is the check between the ( and the )) When the expression is TRUE, it will continue with the job thats done between the { and the } (accolades). We can just use "1" as the expression. For one is always TRUE!
| php | |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
<?php // Prevent PHP from stopping the script after 30 sec set_time_limit(0); // Opening the socket to the Rizon network $socket = fsockopen("irc.rizon.net", 6667); // Send auth info fputs($socket,"USER CMbot combined-minds.net CM :CM bot\n"); fputs($socket,"NICK CM-bot\n"); // Join channel fputs($socket,"JOIN #cm-bot-test\n"); // Force an endless while while(1) { // Continue the rest of the script here } ?> |
Now the script won't stop.
Next is to read what the server is sending us. For this we will use the fgets() function. The function will return the text the server is sending us. So we will store the text what the function returns into a variable. The two things the function needs are the socket variable and the number of bytes it may read each time when its called.
| php | |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
<?php // Prevent PHP from stopping the script after 30 sec set_time_limit(0); // Opening the socket to the Rizon network $socket = fsockopen("irc.rizon.net", 6667); // Send auth info fputs($socket,"USER CMbot combined-minds.net CM :CM bot\n"); fputs($socket,"NICK CM-bot\n"); // Join channel fputs($socket,"JOIN #cm-bot-test\n"); // Force an endless while while(1) { // Continue the rest of the script here while($data = fgets($socket, 128)) { echo $data; } } ?> |
Why i use 128 bytes to send is trivial information. I Think this is pretty clear, so save your file. And run it in your browser! If it all went right, you should be able to find your bot in the user list of channel #cm-bot-test!
When you are running the script in your browser. You may want to use the nl2br(), and flush() functions. nl2br() to make sure your $data output is all one a new line, that is quite handy for reading. ;-) You want to use the flush() function for flushing the data to the browser. Normally PHP send it's output to the browser after the outcome is calculated, so on the very and. But when flush() is called, all that's calculated so far will be already send to the browser.
| php | |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
<?php // Prevent PHP from stopping the script after 30 sec set_time_limit(0); // Opening the socket to the Rizon network $socket = fsockopen("irc.rizon.net", 6667); // Send auth info fputs($socket,"USER CMbot combined-minds.net CM :CM bot\n"); fputs($socket,"NICK CM-bot\n"); // Join channel fputs($socket,"JOIN #cm-bot-test\n"); // Force an endless while while(1) { // Continue the rest of the script here while($data = fgets($socket, 128)) { echo nl2br($data); flush(); } } ?> |
How to use IRC
Now a few other things are important. First, to stay connected. And second, how to make functions for the bot.
When I am talking about the first subject, I am talking about PING and PONG. This is an technique from the IRC protocol to check of you are still there. This is needed, because sometimes you cannot send the QUIT command when you are stopping your bot. This can happen by just stopping your bot in the browser, or your ISP can crash down. ;-)
Before we use the PING / PONG technique, we need to read what the server is saying right? Well, for this i often use the explode() function. This function can separate a string, or integer (whatever you want) and will store all separated things into an array. We will separate the string we receive with a simple " " (space). And store the data into an array called "$ex".
| php | |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
<?php // Prevent PHP from stopping the script after 30 sec set_time_limit(0); // Opening the socket to the Rizon network $socket = fsockopen("irc.rizon.net", 6667); // Send auth info fputs($socket,"USER CMbot combined-minds.net CM :CM bot\n"); fputs($socket,"NICK CM-bot\n"); // Join channel fputs($socket,"JOIN #cm-bot-test\n"); // Force an endless while while(1) { // Continue the rest of the script here while($data = fgets($socket, 128)) { echo nl2br($data); flush(); // Separate all data $ex = explode(' ', $data); } } ?> |
Now, we got a an array with all words in it. This is easy for checking the commands. The fourth word (in channel) is most of the time the first word said in the channel itself. It is the fourth word because you still have 3 big and important words before the text. So knowing you can just check $ex[3] is rather nice, isn't it? (notice $ex[3] is the fourth, because the array starts at zero, not one)
Lets check for the PING command shall we? The server will send you something like this:
PING :Unique-Code
The server expects you will send:
PONG :Unique-Code
Back to the server. But be warned, every server has another unique code. So we will extract the unique code from the server, and then send it back in a variable!
| php | |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
<?php // Prevent PHP from stopping the script after 30 sec set_time_limit(0); // Opening the socket to the Rizon network $socket = fsockopen("irc.rizon.net", 6667); // Send auth info fputs($socket,"USER CMbot combined-minds.net CM :CM bot\n"); fputs($socket,"NICK CM-bot\n"); // Join channel fputs($socket,"JOIN #cm-bot-test\n"); // Force an endless while while(1) { // Continue the rest of the script here while($data = fgets($socket, 128)) { echo nl2br($data); flush(); // Separate all data $ex = explode(' ', $data); // Send PONG back to the server if($ex[0] == "PING"){ fputs($socket, "PONG ".$ex[1]."\n"); } } } ?> |
This should not be difficult right? Well then, lets continue to making your own commands! Lets make a command to let the bot simply say something in the same room the command is given in.
There are some problems with this though. But i will help you along the way, for i have done this a trillion times a few years ago. The problem is simple, when a command is given by the server it adds a newline, and carriage return with it. (not always the carriage return) So when you are checking a command, and it is the last word from this line, it has the newline after it.
This gives the following problem. When you want to check the word for a command, and you will simply use the if() function it won't work. This is because !command is not the same as !command\n for example. So we first have to strip the word we want to check from the newline and carriage return.
This is very simple though:
| php | |
|
1 2 3 4 5 |
<?php $command = str_replace(array(chr(10), chr(13)), '', $ex[3]); ?> |
What this does, is replacing an array of both newline, and carriage return with nothing. An saves that word in the $command variable. The chr() function returns an ASCII value by giving it's number. Check this website for all ASCII numbers.
Now you can easily check the $command for if it contains the command you want! Well then, lets continue with the command. First we need to check if the command variable contains the correct command. And then, we need an action for that command. To keep it simple, we will let the bot say something in the channel the command is given OK?
First we need to know more about the IRC protocol. When someone says something, the protocol will send something like this:
:Nickname!Host@name PRIVMSG #cm-bot-test :The stuff thats said
This little line is very resourceful. It contains the nickname of the person who said it. It contains the hostname (good for a admin module). It contains the channel, and it contains the string the person said in the channel.
So lets take a close look at that line, if we want to know the channel it's said in. We need the third word. So $ex[2] contains the channel!
Now lets use our freshly learned knowledge, and use it on the bot!
| php | |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
<?php // Prevent PHP from stopping the script after 30 sec set_time_limit(0); // Opening the socket to the Rizon network $socket = fsockopen("irc.rizon.net", 6667); // Send auth info fputs($socket,"USER CMbot combined-minds.net CM :CM bot\n"); fputs($socket,"NICK CM-bot\n"); // Join channel fputs($socket,"JOIN #cm-bot-test\n"); // Force an endless while while(1) { // Continue the rest of the script here while($data = fgets($socket, 128)) { echo nl2br($data); flush(); // Separate all data $ex = explode(' ', $data); // Send PONG back to the server if($ex[0] == "PING"){ fputs($socket, "PONG ".$ex[1]."\n"); } // Say something in the channel $command = str_replace(array(chr(10), chr(13)), '', $ex[3]); if ($command == ":!sayit") { fputs($socket, "PRIVMSG ".$ex[2]." :Combined-Minds.net irc bot tutorial!\n"); } } } ?> |
Notice the ":" in front of the !sayit command. This is because the word contains the : when extracting the words. This is for the IRC protocol.
Well then, I tested it here, and it works fine! Please also show your bot in the IRC channel, let me see what you guys made of it. :-)
Replies on Creating an IRC bot in PHP:
Hmm. So if I wanted to echo a hostname of someone, like, say I do ".akill nickname reason" how would I set it up to check the hostname of the nick?
Yes Fahad, a CLI script would be better. But when i wrote my bot (four years ago), just creating the bot was enough for me. So i thought in the tutorial, it doesn't matter how it works... As long as it works.
But still your right, would be handier in CLI
Very helpful tutorial; thanks!
I'm running into a problem though, that I'm hoping you might know a good solution to, Jim. It seems like each incoming $data line can only be a certain length long before cutting off into an entirely new line. I think this is inherently a problem with CTCP in general, but maybe not?
It is basically posing a problem for me because I want to be able to grab a line that someone might say in a channel and then repeat it back to them, but I don't think I can do that when their message spans several $data lines. For example:
:Eric!n=IRC@eric.tld PRIVMSG #channel :hello my name is eric. how are you today? i like cheese. i like long walks
Array
(
[0] => :Eric!n=IRC@eric.tld
[1] => PRIVMSG
[2] => #channel
[3] => :hello
[4] => my
[5] => name
[6] => is
[7] => eric.
[8] => how
[9] => are
[10] => you
[11] => today?
[12] => i
[13] => like
[14] => cheese.
[15] => i
[16] => like
[17] => long
[18] => walks
)
on the beach too.
Array
(
[0] =>
[1] => on
[2] => the
[3] => beach
[4] => too.
)
The same would hold true if I wanted to grab the output from a NAMES command, for example, for a channel with more than a few users in it.
Do you know of some way to get around this? Thanks.
Hmm, i think that should be posible by highering the number in the fgets() function.
Thanks for good tutorial!
Im just wondering about the same thing few others here, how to get this work:
1. Someone says on channel: !pick $word1 $word2
How to get the script to pick it up?
I tried both ways:
if($command == ":!pick ".$ex[4]." ".$ex[5]){
$words = array($ex[4], $ex[5]);
and
if($command == ":!pick"
{
$words = array($ex[4], $ex[5]);
The main purpose is to choose randomly between 2 words, the script itself goes:
$rand_keys = array_rand($words, 2);
$key = $words[$rand_keys[0]];
fputs($socket,"PRIVMSG ".$ex[2]." :".$key."n"
;
What am i doing wrong and whats the "command" to get those words after: ":!pick" ?
Hello, thanks for the great tutorial.
When I started making my bot, I split it into parts to make configureing it easier, but now it is failing to work, im not sure if there is an easy way to make it work as the three files I already have, but all it is saying is "NOTICE AUTH :*** Looking up your hostname..." over and over.I am not sure what is wrong with the code, but it just not working. Is there anyway that you could help?
<?php
set_time_limit(0);
$socket = fsockopen("irc.server.com",6667) or die("Server offline"
;
fputs($socket,"USER P-Bot orella.co.uk P-Bot :Totally Pn"
;
fputs($socket,"NICK P-Botn"
;
$trigger=array( "!die",
"!echo",
"!join",
"!part"
;
while(1){
usleep(500000);
while($data = fgets($socket)){
$get=explode(' ',$data);
if(substr_count($get[2],"#"
){
$nick=explode(':',$get[0]);
$nick=explode('!',$nick[1]);
$nick=$nick[0];
$chan=$get[2];
$num=3;
while($get[$num]){
if($num===3){
$split=explode(':',$get[3],2);
$text=$split[1];
if(in_array(rtrim($text),$trigger)){
switch (rtrim($text)){
case "!die":
fputs($socket,"PRIVMSG ".$chan." Client leaving.n"
;
fputs($socket,"QUIT L33T BOT!n"
;
die("Forced disconnect by client<br>"
;
break;
case "!echo":
fputs($socket,"PRIVMSG ".$chan." Todo echo command.n"
;
break;
case "!join":
fputs($socket,"PRIVMSG ".$chan." Join command....n"
;
fputs($socket,"JOIN ".$get[4]."n"
;
break;
case "!part":
fputs($socket,"PART ".$get[4]."n"
;
break;
}
}
}
else $text=$text." ".$get[$num];
$num++;
}
echo $nick." in ".$chan." says: ".$text."<br>";
}
elseif(substr_count($get[1],'PRIVMSG')){
fputs($socket,"PRIVMSG ".$nick." Sorry, not accepting PMs at this time.n"
;
}
elseif($get[1]=="MODE"
{ //Consider the client connected
fputs($socket,"PRIVMSG nickserv identify totallypbot n"
;
fputs($socket,"JOIN #P-Bot n"
;
}
else echo nl2br($data);
if($get[0] == "PING"
fputs($socket,"PONG ".$get[1]."n"
;
flush();
}
}
?>
Pretty messy, was doing it for fun.. plan to turn it into oop code later 
Pretty easy to understand once you get the hang of it.
why should i uploap this php-file?
i use free server u.u
i dont udersatnd please respond me
Edit:
where should i uploap this php-file?
i use free server u.u
i dont udersatnd please respond me
Edit:
where should i uploap this php-file?
i use free server u.u
i dont udersatnd please respond me
The textfield you saw at last is the full file, copy that to a .pho file and it will work. And please don't just post 3 replies with nothing extra!
You are not logged in. Please login or register an account, it just takes 30 seconds.

Can I just point out that it may be easier to write this as a PHP-CLI script?
This removes the maximum execution time and browser closed problems, owing to the script not needing a webserver to run.