Python and IRC
Python and IRC
Introduction
I’m sure you’ve all heard of it – the modern miracle known as Internet Relay Chat, or IRC. It allows geeks, such asmyself, to converse with other people from around the globe. While you can connectto it with a vanilla client, you can also connect to it with another miracle – Python.
Python can connect to a channel and act asanything you like – acalculator, a weatherman, a scribe or a silent occupant. In addition, it isfairly simple to make Python and IRC get along, contrary to what you might bethinking right now, and this article will explain exactly how to do it. By theend of this article, you should have a basic understanding of the IRC protocoland how to use it in your Python scripts.
To understand this article, you will need anunderstanding of the Python language and an understanding of sockets. Youshould also be familiar with IRC.
Hello, IRC world!
Our first task is to connect to an IRC network. Todo this, we must first create a socket. Then, we must connect to the network,and, finally, we must complete a few short steps to become eligible to interactwith other users.
1 import socket
2
3 network = 'irc.insert.a.network.here'
4 port = 6667
5 irc = socket.socket ( socket.AF_INET, socket.SOCK_STREAM )
6 irc.connect ( ( network, port ) )
7 irc.send ( 'NICK PyIRC\r\n' )
8 irc.send ( 'USER PyIRC PyIRCPyIRC :Python IRC\r\n' )
9 irc.send ( 'QUIT\r\n' )
10 irc.close()
Although the code demonstrates the absolutebasics, it doesn't do anything special. In fact, the server you connect tomight not even acknowledge the data you send it until after a few seconds – which we do not allow for inthe script. Don't worry though, we will soon take a look at a fully functionalscript after we tackle the very basics.
The first piece of data we send to it sets ournickname. Notice how we suffix each outgoing message with a carriage return andline feed ( "\r\n" ). Take note of this because it is very important.We then specify our username ( "PyIRC" ), host name ("PyIRC" ), server name ( "PyIRC" ) and real name ("Python IRC" ) in the next outgoing line. Finally, we issue the "QUIT"command and close the connection.
You should also take note of the colon before"Python IRC." Colons tell the server that the attribute will possiblybe made up of multiple words.
We will use and develop this skeleton throughoutthe remainder of the article, so it is important that you understand it.
Python and IRC - Can You Hear Me?
Now that we know how to connect, our next task isjoining a channel and sending a message to its occupants. This is suprisinglyeasy.
1 import socket
2
3 network = 'irc.insert.a.network.here'
4 port = 6667
5 irc = socket.socket ( socket.AF_INET, socket.SOCK_STREAM )
6 irc.connect ( ( network, port ) )
7 irc.send ( 'NICK PyIRC\r\n' )
8 irc.send ( 'USER PyIRC PyIRCPyIRC :Python IRC\r\n' )
9 irc.send ( 'JOIN #pyirc\r\n' )
10 irc.send ( 'PRIVMSG #pyirc:Can you hear me?\r\n' )
11 irc.send ( 'PART #pyirc\r\n' )
12 irc.send ( 'QUIT\r\n' )
13 irc.close()
Note that, again, the code will probably notperform the instructions we gave it, for the same reason as last time. However,the above code would ideally join the channel #pyirc and say "Can you hearme?" Let's break the code down to see how it works. The first few linesshould already look familiar, and the last few lines should look familiar aswell. We wrote them in the previous section. However, the "JOIN,""PRIVMGS," and "PART" lines are new. They simply join thechannel, send a message to the channel and leave the channel, respectively.
To join multiple channels, just issue the"JOIN" command again. When sending a message, be sure to specify thename of the channel in the "PRIVMSG" command. You can also messageanother user with the "PRIVMSG" command.
1 import socket
2
3 network = 'irc.insert.a.network.here'
4 port = 6667
5 irc = socket.socket ( socket.AF_INET, socket.SOCK_STREAM )
6 irc.connect ( ( network, port ) )
7 irc.send ( 'NICK PyIRC\r\n' )
8 irc.send ( 'USER PyIRC PyIRCPyIRC :Python IRC\r\n' )
9 irc.send ( 'PRIVMSG Jimbo:Can you hear me?\r\n' )
10 irc.send ( 'QUIT\r\n' )
11 irc.close()
Python and IRC - I Can Hear You!
So far, we can connect to an IRC network and senda message to a given channel. We can also send a message to another user. Nowwe will accept messages from both the server and from other users.
Every once in a while, the server will send us a"PING" command to check on us. To stay connected, we must send theserver a "PONG" command. Let's build a script that does just that.
1 import socket
2
3 network = 'irc.insert.a.network.here'
4 port = 6667
5 irc = socket.socket ( socket.AF_INET, socket.SOCK_STREAM )
6 irc.connect ( ( network, port ) )
7 print irc.recv ( 4096 )
8 irc.send ( 'NICK PyIRC\r\n' )
9 irc.send ( 'USER PyIRC PyIRCPyIRC :Python IRC\r\n' )
10 irc.send ( 'JOIN #pyirc\r\n' )
11 irc.send ( 'PRIVMSG #pyirc:Hello.\r\n' )
12 while True:
13 data = irc.recv ( 4096 )
14 if data.find ( 'PING' ) != -1:
15 irc.send ( 'PONG ' + data.split() [ 1 ] + '\r\n' )
16 print data
As you can see, we've changed our skeleton a bitin this script. We have replaced the bottom section with an infinite loop. Theloop receives data, and if a "PING" command is present, it replieswith a "PONG" command. Feel free to test the script out.
Let's modify our script to accept messages.Messages come in a form similar to this:
:Nick!user@host PRIVMSG destination :Message
Here's an example:
:Peyton!~peyton@python.org PRIVMSG #pyrc :Hey!
In our new script, we will break down the aboveform of data.
1 import socket
2
3 network = 'irc.insert.a.network.here'
4 port = 6667
5 irc = socket.socket ( socket.AF_INET, socket.SOCK_STREAM )
6 irc.connect ( ( network, port ) )
7 trash = irc.recv ( 4096 )
8 irc.send ( 'NICK PyIRC\r\n' )
9 irc.send ( 'USER PyIRC PyIRCPyIRC :Python IRC\r\n' )
10 irc.send ( 'JOIN #pyirc\r\n' )
11 irc.send ( 'PRIVMSG #pyirc:Hello.\r\n' )
12 while True:
13 data = irc.recv ( 4096 )
14 if data.find ( 'PING' ) != -1:
15 irc.send ( 'PONG ' + data.split() [ 1 ] + '\r\n' )
16 elif data.find ( 'PRIVMSG' ) != -1:
17 nick = data.split ( '!' ) [ 0 ].replace ( ':', '' )
18 message = ':'.join ( data.split ( ':' ) [ 2: ] )
19 print nick + ':', message
We've added a few lines to the script. If the"PRIVMSG" command is found inside the line of data, we pull the lineapart to get the nickname of the person who sent it and the message by usingthe split() function. Run the script, join #pyirc on the specified network andtest out the script.
Now we need our script to discriminate betweenmessages in different channels and private messages. This can be done easily byextracting the destination from the "PRIVMSG" command.
1 import socket
2
3 network = 'irc.insert.a.network.here'
4 port = 6667
5 irc = socket.socket ( socket.AF_INET, socket.SOCK_STREAM )
6 irc.connect ( ( network, port ) )
7 irc.recv ( 4096 )
8 irc.send ( 'NICK PyIRC\r\n' )
9 irc.send ( 'USER PyIRC PyIRCPyIRC :Python IRC\r\n' )
10 irc.send ( 'JOIN #pyirc\r\n' )
11 irc.send ( 'PRIVMSG #pyirc:Hello.\r\n' )
12 while True:
13 data = irc.recv ( 4096 )
14 if data.find ( 'PING' ) != -1:
15 irc.send ( 'PONG ' + data.split() [ 1 ] + '\r\n' )
16 elif data.find ( 'PRIVMSG' ) != -1:
17 nick = data.split ( '!' ) [ 0 ].replace ( ':', '' )
18 message = ':'.join ( data.split ( ':' ) [ 2: ] )
19 destination = ''.join ( data.split ( ':' ) [ :2 ] ).split ( ' ' ) [ -2 ]
20 if destination == 'PyIRC':
21 destination = 'PRIVATE'
22 print '(', destination, ')', nick + ':', message
Test out the script like before and see theresult. You should see something similar to this:
( #pyirc ) Peyton: Test
( PRIVATE ) Peyton: This is a private message.
IRC Mathematics
Let's apply our knowledge to something useful – a Python-powered IRC bot thatperforms basic mathematical functions. Let's make the bot perform arithmeticcalculations and a few trigonometric functions: sine, cosine and tangent.
We will first create a file to perform thecalculations for us. Create a file named ircMath.py and insert the followingcode.
1 import math
2
3 def arithmatic ( args ):
4
5 args [ 0 ] = args [ 0 ].replace ( '\r\n', '' )
6 for letter in 'abcdefghijklmnopqrstuvwxyz':
7 args [ 0 ] = args [ 0 ].replace ( letter, '' )
8 solution = str ( eval ( args [ 0 ], { '__builtins__' : {} } ) )
9 return solution
10
11 def sine ( args ):
12
13 solution = str ( math.sin ( float ( args [ 0 ] ) * ( 2 * math.pi ) / 360 ) )
14 return solution
15
16 def cosine ( args ):
17
18 solution = str ( math.cos ( float ( args [ 0 ] ) * ( 2 * math.pi ) / 360 ) )
19 return solution
20
21 def tangent ( args ):
22
23 solution = str ( math.tan ( float ( args [ 0 ] ) * ( 2 * math.pi ) / 360 ) )
24 return solution
The guts of ircMath.py aren't too important, andthe code should be pretty straightforward, so I won't get into detail.
We will now create the bot. Its code will be amodified version of the last section's script. We will search the incomingmessage for the string "%PyIRC" to discriminate between messages wedo and do not need. We will then split up the message into a function andarguments. Finally, we will call the appropriate function.
1 import ircMath
2 import socket
3
4 network = 'irc.insert.a.network.here'
5 port = 6667
6 irc = socket.socket ( socket.AF_INET, socket.SOCK_STREAM )
7 irc.connect ( ( network, port ) )
8 irc.recv ( 4096 )
9 irc.send ( 'NICK PyIRC\r\n' )
10 irc.send ( 'USER PyIRC PyIRCPyIRC :Python IRC\r\n' )
11 irc.send ( 'JOIN #pyirc\r\n' )
12 irc.send ( 'PRIVMSG #pyirc:Hello.\r\n' )
13 while True:
14 data = irc.recv ( 4096 )
15 if data.find ( 'PING' ) != -1:
16 irc.send ( 'PONG ' + data.split() [ 1 ] + '\r\n' )
17 elif data.find ( 'PRIVMSG' ) != -1:
18 message = ':'.join ( data.split ( ':' ) [ 2: ] )
19 if message.lower().find ( '%pyirc' ) == 0:
20 nick = data.split ( '!' ) [ 0 ].replace ( ':', '' )
21 destination = ''.join ( data.split ( ':' ) [ :2 ] ).split ( ' ' ) [ -2 ]
22 function = message.split ( ' ' ) [ 1 ]
23 if function == 'calc':
24 try:
25 args = message.split ( ' ' ) [ 2: ]
26 solution = ircMath.arithmatic ( args )
27 irc.send ( 'PRIVMSG ' + destination + ' :' + nick + ': ' + solution + '\r\n' )
28 except:
29 pass
30 if function == 'sin':
31 try:
32 args = message.split ( ' ' ) [ 2: ]
33 solution = ircMath.sine ( args )
34 irc.send ( 'PRIVMSG ' + destination + ' :' + nick + ': ' + solution + '\r\n' )
35 except:
36 pass
37 if function == 'cos':
38 try:
39 args = message.split ( ' ' ) [ 2: ]
40 solution = ircMath.cosine ( args )
41 irc.send ( 'PRIVMSG ' + destination + ' :' + nick + ': ' + solution + '\r\n' )
42 except:
43 pass
44 if function == 'tan':
45 try:
46 args = message.split ( ' ' ) [ 2: ]
47 solution = ircMath.tangent ( args )
48 irc.send ( 'PRIVMSG ' + destination + ' :' + nick + ': ' + solution + '\r\n' )
49 except:
50 pass
Start up the script and enter the specifiedchannel on the specified network. Try saying these lines:
%PyIRC calc 2+2
%PyIRC calc 8+10
%PyIRC sin 30
%PyIRC cos 45
%PyIRC tan 27
Our bot should give us the answer to each of theproblems. Pretty neat, huh?
Conclusion
You should now know the basics of Python and IRCconnectivity. Try to expand on the examples provided in this article to createyour own unique scripts. If you would like to learn more about the IRCprotocol, the protocol is documented here: