March 15, 2008

IRC, NoNameScript, and Python - Oh My!


I have been a Half-Op (HOP) for an IRC channel that belongs to the World of Warcraft server I have played on for a while now. Currently I am using NoNameScript, an extension to the ever-popular mIRC that is the workhorse of Windows IRC clients. Most of the time I'm satisfied with its features and what is easily done with it. However, I curse mIRC Script and bring a plague upon both its houses.

It's a complete nightmare to script in. Here's an example of a simple part of a script that is used in a "no swearing" script that completely boggles my mind:

; When you are an op in #Dorvan and anything is said in the channel
on @*:text:*:#Dorvan:{
var %swear = these,are,swear,words ; The swear words to look for
var %i = $numtok(%swear,44) ; The number of words separated by commas
var %u = 2 ; The number violations before kick/ban
while (%i) { ; While loop counting the number of words
... snip ...
dec %i ; %i--
}
... snip ...
}

Now let us start analyzing the significant eccentricities found in mIRC script.

on @*:TEXT:*:#Dorvan:{
}

This little brick is one part that I don't have too many complaints about. Given the nature of the program, this makes a fair amount of sense. It is saying "on receiving text in the channel #Dorvan, where you are a channel operator", which is pretty self-explanatory. Here's a breakdown for those a little more inexperienced in programming:

  • The @* section instructs that this block of code will only be executed if you are a Channel Operator (commonly denoted as @ in IRC clients) for the channel #Dorvan.
  • The TEXT section says that it will look for a TEXT event in the channel. This is actually an abstraction of the actual IRC message that denotes a user message. In RFC 1459, user messages are actually denoted as PRIVMSG <channel>; TEXT is an abstraction to make it easier to understand that you want any incoming text from another user.
  • The * says the code will execute regardless of what the text reads. A programmer can put a string there that will execute the code block if a user says that string. For this script, however, we have several substrings that we want to parse for.
  • The fourth parameter specifies what type of messages to look for. The actual parameter used in this demo will look for any message to the channel #Dorvan. Other choices would be # for any channel, ? for any query (message from peer-to-peer), or * for any received message.
  • Now we come to the first eccentricity that is throwing me through hoops. We have another parameter where one can specify a one line command or a | separated list of commands. Being primarily a Python and C++ programmer, I prefer to have individual commands on their own line; therefore I use the brace syntax. The funny thing about this is that you still have to include the fourth colon, so the syntax looks very odd. A small thing, I know, but little irksome things like that build up after a while.
var %swear = these,are,swear,words

The next line is pretty self-explanatory. It just declares a local variable named %swear. I know it should be a global, but the script was made to be distributed, and this is much easier to distribute as it's all in one block of code.

var %i = $numtok(%swear,44)

The next line takes a little bit of explaining, I think. It sets a variable %i to the number of comma-delimited tokens in %swear. Yes, the 44 is the ASCII code for a comma. Why they can't just use the string "," is beyond me, but that is the convention the mIRC developers chose to use. In my opinion, this adversely affects both readability and writeability, unless one is very familiar with ASCII codes.

var %u = 2

A very self-explanatory line, all this does is set the local variable %u to 2.

while (%i) {
}

A while loop would not be my first choice for this script; I wanted to use a for loop. However, mIRC script does not contain a for loop. Counter loops are just semantically cleaner for most iterative loops, and logical loops just aren't as semantically "happy" for me in cases like this. I suppose it works and cuts down on the parsing needed for the scripting engine, but I wish counter loops existed in mIRC script.

dec %i

The last line I included is the command to decrement %i. inc and dec are interesting choices to me for these instructions. What ever happened to the good old ++ and --? Or even Python's simpler x += 1 and x -= 1? Those choices would make more sense to me than this apparent "I'm going to try and be Lisp without the parenthesis" instruction.

There are many things about mIRC script that I just don't like. This drove me to find an IRC client that is either written in Python or uses Python as its scripting language. Either would work for me. I like Python because programs just plain work with it. My professor is a big proponent of Python because it is easy to teach beginners, but powerful enough to use for full-fledged programs.

While looking for a Python IRC client, I realized how easy it actually is to create an IRC client in Python. For example, here is some code that will connect to an IRC server, join the channel #testserver, say "Hello, world.", and then part and quit:

import socket
network = 'enter.a.network'
port = 6667
irc = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
irc.connect((network, port))
irc.send('NICK TestPy\r\n')
irc.send('USER TestPy TestPy TestPy :Python IRC\r\n')
irc.send('JOIN #testchannel\r\n')
irc.send('PRIVMSG #testchannel :Hello, world.\r\n')
irc.send('PART #testchannel\r\n')
irc.send('QUIT\r\n')
irc.close()

The ease of doing this got me thinking about writing my own IRC client. Handling the entirety of RFC 1459 is a semi-daunting task though, so I might start implementing and see how it goes.

I'm just wondering if there is a market for this outside of myself. On GNU/Linux, BSD and Mac OS X, a user can choose Konversation, KDE's graphical IRC client. It supports shell scripts, Ruby, Python, Perl, Java, C++, C#, and Javascript for scripting. That's quite a formidable list. Quite a formidable development team. Not something that a sane person would consider competing with. I've always wanted to start a project that people will use. Maybe one person other than me would use it. That will have to be good enough.