Published on January 11, 2010 by Andrew Sledge in Python
Chatbots have always been around, but they have mainly been used by annoying IM-marketers or spammers. Now you, too, can have that annoying chatbot at your disposal.
The Twisted framework is a Python-based networking framework that supports several networking protocols like HTTP, SMTP, SSH, IRC, and FTP as well as several lower level networking interfaces at the TCP, UDP, and Unix sockets levels. It has a large community support base and plenty of documentation. I will be using the Twisted framework in this tutorial.
The Twisted framework comes equipped with AOL instant messaging protocols. Let’s start by importing the proper modules.
#!/usr/bin/python from twisted.words.protocols import oscar from twisted.internet import protocol, reactor import getpass, pprint, feedparser, random, re, struct, string
In order for your bot to be able to talk to other clients, you first have to be able to authenticate against the AIM servers.
USERNAME = raw_input('Username: ')
PASSWORD = getpass.getpass('Password: ')
hostport = ('login.oscar.aol.com', 5190)
If you know anything about Twisted, you know that its primary listening handler is called reactor. The reactor listens for connections and handles the connection appropriately. We will use the reactor to create a client to talk to the AIM servers. This is accomplished by extending the OscarAuthenticator class (that comes with Twisted) and providing that class with a handler to perform the actual connections and responses.
class ChatBotOA(oscar.OscarAuthenticator): BOSClass = ChatBotB protocol.ClientCreator(reactor, ChatBotOA, USERNAME, PASSWORK, 0).connectTCP(*hostport) reactor.run()
Now we’ve come to the real worker class, ChatBotBOS. This will handle the messages that are received. ChatBotBOS extends the Twisted oscar.BOSConnection class. Before we do anything interesting, we need to set up some basic methods that are expected.
capabilities = [oscar.CAP_CHAT] def initDone(self): self.requestSelfInfo().addCallback(self.gotSelfInfo) self.requestSSI().addCallback(self.gotBuddyList) def gotSelfInfo(self, user): print user.dict self.name = user.name def gotBuddyList(self, l): print l self.activateSSI() self.setProfile(”““ChatBot”“”) self.setIdleTime(0) self.clientReady() def gotAway(self, away, user): if away: print 'User ', user,’: ', away
These methods are not really that interesting but are needed by Twisted to connect to the AIM servers. The next method handles the line and parses it. We will provide an interface so that the other clients can request a fortune or a random image from Flickr. For instance, if the client sends 'image’ to the chatbot, the chatbot will return a random image from a Photobucket account.
def receiveMessage(self, user, multiparts, flags): print user.name, multiparts, flags self.getAway(user.name).addCallback(self.gotAway, user.name) if multiparts0[0].find('away’)!=-1: self.setAway('I am away from my computer right now.’) elif multiparts0[0].find('back’)!=-1: self.setAway(None) if self.awayMessage: self.sendMessage(user.name,’<html><font color=”#0000ff”>’+self.awayMessage,autoResponse=1) else: command = self.stripTags(multiparts0[0]) self.lastUser = user.name if command == “image”: image = self.getImage() self.sendMessage(user.name,image,autoResponse=1) elif command == “fortune”: fortune = self.getFortune() self.sendMessage(user.name,fortune,autoResponse=1) elif command == “commands”: commands = '<html><font color=”#0000ff”><br />Allowed commands:<br />image – get a random image<br />fortune – get your daily fortune</font></html>’ self.sendMessage(user.name,commands,autoResponse=1) else: commands = '<html><font color=”#0000ff”><br />Allowed commands:<br />image – get a random image<br />fortune – get your daily fortune</font></html>’ self.sendMessage(user.name,commands,autoResponse=1)
As you can see, the method receives a command and returns the appropriate data from the methods getImage() or getFortune(). There is also another method, stripTags(), which is necessary because many IM clients (Trillian, et. Al.) can send HTML messages. The stripTags() method uses Pythons regular expression class to filter any tags. The getImage() method uses the feedparser class to parse the RSS feed. The getFortune() class reads newline delimited fortunes from the file fort.txt.
def getImage(self):
“”“Returns an image”“”
d = feedparser.parse(“http://feed100.photobucket.com/albums/m100/randomaccount/account.rss”)
topindex = len(d['entries’])
index = random.randint(0, topindex – 1)
return str(d.entries[index].guid)
def getFortune(self):
“”“Returns a fortune”“”
f = open('fort.txt’, 'r’)
l = 0
it = '’
while 1:
line = f.readline()
l = l + 1
if line != “”:
if random.uniform(0, l) < 1:
it = line
else:
break
f.close()
return it
def stripTags(self,value):
return re.sub(r’<[^>]*?>’, '’, value)
Once you have your script ready to go, you launch it with
$ twisted -y chatbot_tutorial.py
in the Twisted reactor – daemon mode.