New tutorials View all
New forum replies
New frontpage replies
New articles
New news
Creating an IRC bot in PHP (917 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:
Nvm got the >msg part...
I need the system uptime, server and w.e else 
uh and also
is there a thing for when a user joins (onjoin) the bot msgs something or other
[EDIT] I got that onjoin part XD, how would I set userlevels
SORRY for the mad questions hehe I just wanna learn this stuff
Thanks
Hehe, about the server stats, use: print_r($_SERVER); it will show you many variables ($_SERVER['VAR_EXAMPLE'] < how to use). It should also contain OS, PHPversion etc. You can set userlevels with "MODE". CHeck out the rfc for the exact syntax:
http://www.irchelp.org/irchelp/text/rfc1459.txt
Hahahaha thanks bro, another question, I run my bot on PHP and after one hour it disconnects. Any idea why and how to prevent it from disconnecting. Message I get is Quit (Read error: EOF from client)
Thanks
Hmm are you sure you don't have a max execution time? So have you set the set_time_limit() function to zero? Or maybe if you run in your browser, the brower might stop loading.
Hi there,
was wondering if you wouldnt mind giving me a little pointer as my head hurts trying to figure out what i'm doing wrong.
for the life of me i cant figure out how to get this thing connected to irc.quakenet.org. Well i can get it connected but it will not join a channel
using the USER and NICK lines i am trying work ok with rizon but not with quakenet. Think it might have something to do with the host and server name..but what i should set that to i am at a loss
perhaps a little more info would help...i get an irc 451 error...i have put in sleeps all over the place to slow it down...still not working though
Many servers don't allow you to join a server immediatly. Try to make a command (eg: !join #channel), and send it to the bot when it's connected (via pm).
If it works then, you know it's that problem. If it's still not working please post the code at our forum.
Hi...
I've created irc bots in java, vb and delphi, and if people want to play about with this i'd definately advise a couple of things - 1, get a light irc server. Personally I haven't found anything as nice and simple as the one i have. I'll post it when I get back home later tonight. This way you can connect to your pc, without having to muck about with ircd or something.
Another thing I did was picked up little bit of source code from pscode.com to create an extremely simple socks server. I then made my bots connect to localhost on the socks server which redirected the connection to the irc server on my computer on port 6667. On this simple socks server I also had a huge text box
that's all it consisted off, which showed all the data's sent across it, and seein as the only datas sent was for irc i just did stuff in my mirc client, and read what it said (so i quickly new the command for doing stuff). I guess this is also some good practise.
P.S. You can do that with dodgy servers that require a longish time wait; for example, you can make this socks tunnel to connect to an outside irc server rather than a local server so you can see what the irc client is doing differently to your bot.
Hi there
I am kinda new to this stuff, I created the bot it works fine added a few commands, but got stuck at a point where I want to use send a /notice msg to a user who used a specified command:
if ($command == ":!help"
{
fputs($socket, "NOTICE ".$ex[2]." :Here is list of commands and their syntax:n"
;
But like this it will send the notice, but not to the user who typed it, but to whole channel, kinda like global notice.
Any suggestions? :P
Hi RealShadow 
$ex[0], so the first word of the whole string contains the username.
Use the following code:
preg_match('/^
.*?)!(.*?)$/i', $ex[0], $matches);
$hostname = "!".$matches[2];
$username = $matches[1];
Place this code right after extracting the data. And you'll have the usernamd and hostname for the rest of the script. 
You can also post a topic on the forums if it's not working.
Is there a way to make the bot be connected to multiple servers at a time? Like the /server -m command. I tried it, but it says unknown command...
Oh sorry, forgot to ask, how would I make the bot reply to VERSION requests? A few servers I go on require replies from VERSION requests.
About the VERSION, you will need to do that with CTCP commands, you i have posted a link to the explenation of the protocol somewhere in these ~40 posts.
About the multiple servers, -m is ment for mIRC itself, not the IRC protocol.
It's posible to connect to multiple servers at a time. One is to load the file multiple times with different connection data. One is to make it dynamic and work with multiple sockets.
I would recommend to start with the first and learn more about sockets before starting that.
(In theory it should be fine, just repleat the code of extracting information of all servers some times)
Thanks for the quick reply, I will look into that (also got the VERSION thing to work, thanks for the link!). Great tutorial 
Oh, I have one last question, I am opening a file on my webserver, that holds data split by commas, I can get the data out with the list() & explode(), but I am only able to show one of the variables of data when sending it to the irc channel. How can I get around this? This is what I have right now,
fputs($sock, "$a $b :Name: $name Access: $access Rank: $rankn"
;
and it doesn't work for some reason, but if I took out all of the variables but one, it works fine...
Hmm, it can be a very known problem. The problem with data extraction is that there is extra text behind the string you recieve.
You dont recieve:
bla*bla!bla PRIVMSG #test
ohoo
No, you recieve:
bla*bla!bla PRIVMSG #test
ohoorn
So when you extract the data sometimes you extraxt it like:
$ex[3] = "
oohoorn";
So when you use that in fputs:
fputs($sock, "bla PRIVMSG :Wooho? $ex[3] Access: $access Rank: $rankn"
;
You send this to the server:
Wooho?
oohoorn Access: bla Rank: blan
The line splits it in two, because it end at the n newline.
It don't know for sure this is the remedy to you problem. If this doesnt fix it, please reply on the main IRC bot tutorial topic on the forum boards. Also include the output of:
echo "$a $b :Name $name .. etc
Hope it will fix it!
im slightly confused on some areas,
how would i create a command the uses the /me function at the start of the setence?
and how would i create a command that included a username, eg: "!poke username" resulting in the bot saying "/me pokes username"
thanks in advance to whoever can help me with this.
http://www.irchelp.org/irchelp/rfc/ctcpspec.html
^ The ACTION part
thanks for the link, i got the /me stuff working, but i cant get the !poke username to do anything? so how would i make that say /me pokes username or: 01ACTION pokes username 01n
thanks in advance
Hmmm, try this:
"PRIVMSG #channel :" . chr(001) . "ACTION pokes username" . chr(001) . "n";
http://www.asciitable.com/
Look at this website, you might need to use 1 in stead of 001. I will ask someone else to look at your problem since i havent used the ACTION code in a while.
how do I cleanly kill the process?
i am starting via a web page, and quitting via irc.
eg capturing text ~/quit
then die();
but find that the httpd process does not quit.
sorry I am a bit clueless as to what and why these httpd processes do start, or stop.
cheers
~:"
Thats very weird, the script HAS to stop when die() is called, are you 100% sure the function is called? Can you give me the code? (maybe at the forum in the general IRC BOT topic)
Can you help me figure out what is wrwong with my script?
elseif
}
if ($command == ":^say"
&& (numtok($ex[3]," "
> 1)) {
fputs($socket,"PRIVMSG ".$ex[2]." :".gettok($ex[3],2," "
."n"
;
hi.. is there any code like this:
when i type !ban <username> <reason> in irc , the bot will ban the username in the forum website.
That's posible. You should check out the protocol guide, the guide is somewhere listed in these comments.
You are not logged in. Please login or register an account, it just takes 30 seconds.

this is what I have
{
;
$command = str_replace(array(chr(10), chr(13)), '', $ex[3]);
if ($command == ":>msg"
fputs($socket,"PRIVMSG ".$ex[2]." $ex[4]n"
Also is there a command to show the server bot is on system uptime and like phpversion :O
Thanks