Login

Basic IRC Tasks

Following up on an earlier DevShed article covering the basics of Python and Internet Relay Chat, this article takes some common IRC tasks, such as listing the users in a given channel or manipulating a channel’s modes, and shows how to turn them into Pyton code.

Introduction

In “Python and IRC,” I explained the basics of connecting to an IRC server and sending a few messages. Obviously, however, you know that there is much more to IRC than I explained. Forgive the cliché, but what the last article covers is truly only the tip of the iceberg that is the Internet Relay Chat Protocol.

This article will cover some more basics. We’ll take some common tasks, such as listing the users in a given channel or manipulating a channel’s modes, and turn them into Python code. If you haven’t read “Python and IRC,” this article won’t do you much good. Even if you have, it might be helpful to speed through it and refresh your memory. With that said, let’s move on.

Creating an IRC Module

If I loaded each and every example in this article with basic socket garbage, not only would it be incredibly pointless, but it would be annoying and difficult to sort through to actually get the important stuff. The solution is to create a module that will do all the socket work for us. Actually, scratch that, why not turn this article a bit and build a module that will do socket work and basic IRC tasks? That sounds a lot better, and, as a bonus, all the code we produce can be recycled in future applications.

Let’s call our module irchat. In ths module, let’s create a class, IRC. Our code will be put into IRC‘s methods. When an instance of IRC is created, Python will automatically connect to the network specified in the arguments, using the data specified in the arguments. To avoid repetition of “rn”, we’ll create a send method that automatically appends “rn” to the given text and then sends it over. To connect to the server, we’re going to have to specify a nickname using IRC’s “NICK” command. Since you may want your Python client to use the “NICK” command again, we’ll make a method for it. A quit method would also be helpful. Enough lecturing, though, let’s get to some code:

The above module will handle the very basics. When an instance of IRC is created and the required data is passed to it, a socket is created and connected. The nickname is then set using the nick method, which takes advantage of the send method. The “USER” command is then sent. No suprises there.

From here on, we will be adding methods to the IRC class. Keep that in mind. Don’t go off and create lone functions and later wonder why nothing happens.

Let’s catch up with “Python and IRC” now. One of the most important things that “Python and IRC” teaches us is how to receive messages from the server and take them apart, making them easier to process. To do this, we must first create a method, recv, which receives data from the network and then splits it up. The data should be split at “rn” (which effectively splits it into commands) and added to a variable, queue. Remember that data will get cut off, too, so we have to store partial data in a variable, partial, and let it get picked up by queue on the next call to recv. This may sound complicated, but it’s pretty simple:

queue = []

partial = ”

def recv ( self, size = 2048 ):

commands = self.socket.recv ( size ).split ( ‘rn’ )

if len ( self.partial ):

commands [ 0 ] = self.partial + commands [ 0 ]

self.partial = ”

if len ( commands [ -1 ] ):

self.partial = commands [ -1 ]

self.queue.extend ( commands [ :-1 ] )

else:

self.queue.extend ( commands )

We must now create a method, retrieve, that pops the first command off of queue and returns it. If the queue is empty, the method should return False:

def retrieve ( self ):

if len ( self.queue ):

command = self.queue [ 0 ]

self.queue.pop ( 0 )

return command

else:

return False

Finally, we have to create a method that dismantles individual commands into something more workable. Recall what a message from the server looks like:

:source some parameters

:source some parameters :another parameter

Pulling that apart isn’t too complicated, although it may seem like it. If we split the string in the correct places, we can manage:

Here’s one way to use our new module. The following script connects to an IRC network and spawns a thread to constantly call the recv module, which, as you know, puts commands into the queue list. It then dismantles the data and prints it out in a neater form:

All right. It’s time to get into some actual tasks now that the basics of our module are completed. Let’s start with communication, since it’s pretty basic. “PRIVMSG” is the logical place to start. Recall the command from the last article:

PRIVMSG destination :message

This is easy to work into our class. We’ll just create a method, privmsg, that accepts two arguments. The first argument is the destination, and the second argument is the message to be sent:

def privmsg ( self, destination, message ):

self.send ( ‘PRIVMSG ‘ + destination + ‘ :’ + message )

The “PRIVMSG” command works with nicknames and channels both.

The “NOTICE” command can also be used to send messages:

def notice ( self, destination, message ):

self.send ( ‘NOTICE ‘ + destination + ‘ :’ + message )

As you can see, “NOTICE” works very similarly to “PRIVMSG.” The only difference is how the commands are intended to be handled. “NOTICE” must never be automatically replied to.

{mospagebreak title=Channel Related Tasks}

The two most basic channel related tasks are joining and parting a channel. The commands are “JOIN” and “PART” and look like this:

JOIN channel

PART channel

No surprises there. Let’s put it into Python code:

def join ( self, channel ):

self.send ( ‘JOIN ‘ + channel )

def part ( self, channel ):

self.send ( ‘PART ‘ + channel )

Getting and setting the topic is also pretty simple. The same command, “TOPIC”, is used for both operations. Here is how to get and set a topic, in the respective order:

TOPIC channel

TOPIC channel topic

Our topic should not actually retrieve the data, though. Instead, the recv and retrieve methods should catch it, so creating a method to “get” and set the topic should be pretty simple.

def topic ( self, channel, topic = ” ):

self.send ( ‘TOPIC ‘ + channel + ‘ ‘ + topic )

To get a list of names of everyone in a given channel, the “NAMES” command is used:

NAMES #channel

The Python version is short and sweet:

def names ( self, channel ):

self.send ( ‘NAMES ‘ + channel )

IRC allows people to invite their friends to a certain channel using the “INVITE” command:

INVITE person channel

Let’s create an invite method:

def invite ( self, nick, channel ):

self.send ( ‘INVITE ‘ + nick + ‘ ‘ + channel )

Setting a channel’s mode is done with the “MODE” command:

MODE channel mode

Sometimes, a user must be specified:

MODE channel user

To make that a bit more clear, here’s an example:

MODE #python +o Peyton

The above command would give “Peyton” operator status in a channel. Let’s create a mode method:

def mode ( self, channel, mode, nick = ” ):

self.send ( ‘MODE ‘ + channel + ‘ ‘ + mode + ‘ ‘ + nick )

If you have operator status in a channel, you may kick a user from the channel:

KICK channel user

A reason can also be specified:

KICK channel user reason

Here’s the Python way:

def kick ( self, channel, nick, reason = ” ):

self.send ( ‘KICK ‘ + channel + ‘ ‘ + nick + ‘ ‘ + reason )

{mospagebreak title=User Related Tasks}

The “WHO” command matches a pattern against users on a network:

WHO pattern

For example, let’s say you wanted to search for everyone whose username began with “K”:

WHO K*

Let’s create a method that queries the server with the “WHO” command:

def who ( self, pattern ):

self.send ( ‘WHO ‘ + pattern )

We can also request information on an indivudal using the “WHOIS” command:

WHOIS user

Creating a Python method that uses this command is, of course, easy:

def whois ( self, nick ):

self.send ( ‘WHOIS ‘ + nick )

Lastly, we can look up information on a user that has changed his or her nickname or has left the network using “WHOWAS”:

WHOWAS user

And here’s the Python method:

def whowas ( self, nick ):

self.send ( ‘WHOWAS ‘ + nick )

{mospagebreak title=Processing Information}

Now that I finished boring you with IRC commands and the ever-so-obvious Python methods that use them, it’s time to get information back from the server and process it. Before we get to that though, make sure your irchat module looks something like this:

Now, as you can see, the recv method gets data from the socket and adds it to the queue variable. The dismantle method is responsible for pulling it all apart. It returns a tuple containing the source and a list of parameters (which is a bit misleading, since the first thing in the list is the command itself). It may seem like quite a task to go from here, but it isn’t. All we need to do is match the first item in the list of parameters with a command and then go from there:

if parameterList [ 0 ] == ‘DUMMY’:

pass

We are, however, required to come up with a method to retrieve and process data efficiently. This can be done with a bit of threading. One thread can call the recv method, and the main script can process the data:

I’ve explained basic IRC commands beyond those explained in “Python and IRC” and have presented a method to send and receive those commands. In the process, we have created a module to assist us in the development of IRC applications. What you do from here on is totally up to you, but if you ever need a reference, see RFC 1459, which explains the Internet Relay Chat Protocol: