IRC on a Higher Level Concluded - Something Practical
(Page 4 of 4 )
Let's take a look at something a bit more practical now: a full-fledged IRC bot capable of accepting commands from users. If that doesn't sound interesting, then let's take things one step futher: a bot that responds to users based on external scripts that can be loaded and reloaded on-the-fly.
Our bot will scan a directory, “commands”, for Python files. It will load these files as modules and store them in a dictionary. If a user prefixes the bot's name with a dollar symbol ( for example, “$PyBot” ), then the bot will examine the incoming message for a second argument. If a second argument is present, it will be compared to the dictionary of modules. If a module's name matches the command, the index function of the module will be called, and the connection and event type will be passed. Also, if we receive a CTCP command whose name matches a special password, the bot will rescan. Let's translate this to Python:
import glob
import ircbot
import imp
# Bot scan password
password = 'd3vsh3d0652'
# Connection informatoin
network = 'irc.freenode.net'
port = 6667
channel = '#irclib'
nick = 'PyBot'
name = 'Python Bot'
# We'll store the commands here
commands = {}
# Scan the "commands" directory and load the modules
def scan():
commands.clear()
for moduleSource in glob.glob ( 'commands/*.py' ):
name = moduleSource.replace ( '.py', '' ).replace ( '\\',
'/' ).split ( '/' ) [ 1 ].upper()
handle = open ( moduleSource )
module = imp.load_module ( 'COMMAND', handle, ( 'commands/'
+ moduleSource ), ( '.py', 'r', imp.PY_SOURCE ) )
commands [ name ] = module
# Create our bot class
class ModularBot ( ircbot.SingleServerIRCBot ):
# Join a channel when welcomed
def on_welcome ( self, connection, event ):
connection.join ( channel )
# Listen to public messages
# If the user says our name, prefixed with "$", then we act
def on_pubmsg ( self, connection, event ):
if event.arguments() [ 0 ].split() [ 0 ].upper() == ( '$' +
nick.upper() ):
# See if the user specified a valid command
# If so, call the module
if len ( event.arguments() [ 0 ].split() ) == 1:
pass
elif commands.has_key ( event.arguments() [ 0 ].split()
[ 1 ].upper() ):
commands [ event.arguments() [ 0 ].split()
[ 1 ].upper() ].index ( connection, event )
# Listen to CTCP messages for the scan password
# If we get it, rescan
def on_ctcp ( self, connection, event ):
if event.arguments() [ 0 ] == password.upper():
scan()
# Scan for commands
scan()
# Create the bot and run it
bot = ModularBot ( [( network, port )], nick, name )
bot.start()
There are no suprises, the bot works exactly in the way I explained above. Now, all that's left is giving the bot a few commands to work with. To begin, let's start with an “about” command. To implement this, we simply create a file called “about.py” and place it in the “commands” directory, filling an index function with the appropriate information:
def index ( connection, event ):
connection.privmsg ( event.source().split ( '!' ) [ 0 ], 'I am
an IRC bot built in Python.' )
connection.privmsg ( event.source().split ( '!' ) [ 0 ], 'I am
modular and can load command modules on the fly.' )
The module will be automatically loaded when the bot is started up. Alternatively, we can have it reload the module by using this command in an IRC client:
/ctcp PyBot d3vsh3d0652
Pretty neat, huh? Of course, we can create commands that accept additional arguments as well. Consider a command that accepts a number and then attempts to square it:
def index ( connection, event ):
# Do we have an extra argument?
# If not, complain
if len ( event.arguments() [ 0 ].split() ) == 2:
connection.privmsg ( event.source().split ( '!' ) [ 0 ],
'Please include a number.' )
# Try to square the number and return it to the user
else:
try:
number = float ( event.arguments() [ 0 ].split() [ 2 ] )
number = number ** 2
connection.privmsg ( event.source().split ( '!' ) [ 0 ],
str ( number ) )
except:
pass
Conclusion
While working directly with the IRC protocol can get the job done, development can be messy and slow. The Python-IRCLib library exists between your applications and the IRC protocol, allowing you to take an object-oriented approach to the IRC protocol. It ensures that the result is quickly put together, compact and clean.
The library allows you to work with the protocol on several levels. In this article, we explored the top-most level of interaction: objects that act as clients, automating much of the dirty or otherwise repetitive work. You now have the skill necessary to develop a wide variety of IRC applications fit for just about any task you can think of –- whether it is recording statistics about users or accepting commands and interacting with users.
We also built a modular IRC bot. Feel free to use it and modify it as you see fit. If you wish to share any modifications or modules to the bot –- or information about any IRC project –- then please e-mail me because I would love to hear about it. One simple modification to the bot might be a help command, which lists the supported commands or calls a certain method of the modules.
There is one thing you should remember though: bots are nice, but the niceness can quickly wear off. Never build an overly-annoying application, and never seek to use a bot for malicious purposes.
| DISCLAIMER: The content provided in this article is not warranted or guaranteed by Developer Shed, Inc. The content provided is intended for entertainment and/or educational purposes in order to introduce to the reader key ideas, concepts, and/or product reviews. As such it is incumbent upon the reader to employ real-world tactics for security and implementation of best practices. We are not liable for any negative consequences that may result from implementing any information covered in our articles or tutorials. If this is a hardware review, it is not recommended to open and/or modify your hardware. |