This tutorial walks you through how to make an IRC bot with Twisted. You will be introduced to testing, logging, an overview of how the internet works, as well as event-driven programming, different internet protocols, and making a portable application.
The project’s code is based off of Jessamyn Smith’s IRC bot – the talkbackbot, where if anyone says “That’s what she said” in an IRC channel, the bot replies with a notable quote from a woman (that’s what she really said!).
IRC stands for Internet Relay Chat, and is a protocol for live messaging over the internet. Specifically, the IRC protocol is within the application layer (just a simple abstraction of types of protocols). Other examples of internet protocols within the application layer are HTTP, IMAP, SMTP, POP, SSH, among many others.
If you have heard of IRC before, you may think it’s an antiquated means of communication. Created in 1988, it remains one of the most popular protocols for instant messaging within many aspects of the tech community.
IRCHelp is a great resource for learning IRC. PyLadies also has a great introduction on how to setup and use IRC. Of the Python community, some frequent channels visited are #python, #django, #python-dev, #twisted, and of course, #pyladies, all on Freenode.
For more information, Jessica McKellar gave a great talk at PyCon 2013 about How the Internet Works, with her slides here. She gives an overview of what happens when you click through your browser, what a protocol is, DNS, and generally how to communicate over the internet.
According to Twisted’s website, Twisted is an “event-driven networking engine written in Python”. OK…what does that mean?
Event-driven programming simply means that the flow of the program is determined by events. Events may be a click of the mouse, a message from another program, a response from a server, etc. The primary activity is a reaction to receiving a certain event(s).
Glyph, the author of Twisted, gave a great talk on event-driven architecture at PyCon 2013. He describes how to approach thinking about events and callbacks the right way. Essentially, an event is just a function call that you asked for. Glyph’s slides encapsulates it well:
Some paradigms include object-oriented, imperative, and functional, all of which can use event-driven programming. If you are curious about how to employ different programming paradigms for different problems, check out How to Design Programs.
Logging is quite important for applications. Transferring money, the black box on an airplane, cell phone bills - they all log actions to be referenced and checked later. As such, logging is quite important for developing, debugging, maintaining, and running systems and applications.
There is the
logging module in Python’s standard library, and a great how-to write up for the
But Twisted has its own logging module,
log. The initial reason that Twisted doesn’t use the
logging module from the Python standard library is that the Twisted logging module predates the stdlib one. There are many reasons that Twisted hasn’t moved to the stdlib logging module which you can read here.
You may start out simply adding
FAIL; it’s an all or nothing with
Identifying which level of importance to log a message at can get some getting use to. Use the
debug level for granular information, such as printing out variables that change within a for-loop:
1 2 3 4
def some_awesome_function(items): for i, item in enumerate(items): # do some complex computation/iteration logger.debug('%s iteration, item=%s', i, item)
info for routines and such, like starting or connecting to a server:
1 2 3 4
def start_IMAP_service(): logger.info('Starting IMAP service at port %s ...', port) service.start() logger.info('IMAP Service is started')
warn for important events happen, but no error has occurred, like a password that was incorrectly inputted. Finally, use
error for when an exception is thrown, user isn’t found in the database, connectivity issue, etc. As the admin of an application with a logging mechanism, you would setup your desired level of logs in some configuration for instance, if you only want to see errors, you would ideally set a configuration value to something like
These above examples use Python’s
logging module. For our tutorial, we’ll use Twisted’s
log module, which has slightly different syntax when passing in log levels.
Very similar to logging, testing is also quite important for your code base. Writing tests in parallel to writing code is considered a good habit. Submitting features and patches to projects without tests can be a big faux-pas.
There are different types of testing, and with this tutorial we will focus on unit testing.
A unit test just focuses on one tiny bit of functionality to prove correct behavior based on inputs, and should be independent of other unit tests.
An example of a unit test using Pythons
unittest module, taken from python-guide.org:
1 2 3 4 5 6 7 8
import unittest def fun(x): return x + 1 class MyTest(unittest.TestCase): def test(self): self.assertEqual(fun(3), 4)
The main part here is
assertEqual(fun(3), 4), where we feed
fun the number
3. The test will pass if the return value of
fun(3) == 4, else it will fail.
We will be using Twisted’s own unit testing framework that is built upon Python’s
unittest module with the added ability to test event-driven code.
We will first knock out the simple items by addressing the global variables/settings for our IRC bot. We’ll then build a quick function to select quotes (a list of quotes can be found in the GitHub repo).
Then we’ll approach the making of the bot module by first creating a class to create bots (a factory class), and then another class to define the behavior of the bot. We will also write a quick plugin to easily create and start up our bot from the command line.
Lastly, we will write tests for the expected behavior of our talkback bot application.
All set? Let’s begin!