Skip to content

PyBCPythonBots

Katy Huff edited this page Jan 24, 2012 · 4 revisions

TOC(PyBc, PyBc/Session01, PyBc/Session02, PyBc/Session03, PyBc/Session04, PyBc/Session05, PyBc/Session06, PyBc/Session07, PyBc/Session08, PyBc/Session09, PyBc/f2py, PyBc/swig, PyBc/Cpython, PyBc/Cython, PyBc/PyTables, PyBc/PyTaps, PyBc/PythonBots, PyBc/Django, PyBc/GIS, PyBc/AdvancedPython, PyBc/WxPython, PyBc/standardlib, depth=1)

= Creating IRC and Email Bots =

Thanks to a rich set of libraries, Python makes it extremely simple to perform otherwise complicated tasks. In this instance, we'll be looking at using Python to create email and IRC bots that automatically reply to messages. Specifically, we'll be translating any incoming messages to Pig Latin. Before we get started, make sure you have [http://pypi.python.org/pypi/python-irclib/0.4.8 python-irclib] (typing {{{sudo easy_install python-irclib}}} should be sufficient on Linux systems). Also be sure to download the Pig Latin [http://hackerwithin.org/cgi-bin/hackerwithin.fcgi/raw-attachment/wiki/PyBc/PythonBots/piglatin.py module] and place it in your working directory.

== Creating an Email Bot ==

First, let's create a Gmail account so that we have a place for messages to go. In our case, the account is {{{pybc.bot@gmail.com}}}. Once we have the account set up, we can start checking for messages. For simplicity, we'll be using POP. This first version of our email bot merely logs into Gmail, retrieves a list of the new messages, and then prints out their contents. For testing, you may want to comment out the last line, {{{pop.quit()}}}, since removing it will let you retrieve the same messages over and over again.

{{{ #!CodeExample #!python import getpass import poplib

passwd = getpass.getpass()

pop = poplib.POP3_SSL("pop.gmail.com") pop.user("pybc.bot@gmail.com") pop.pass_(passwd)

messages = pop.list()[1] for i in messages:

num = int(i.split()[0]) contents = "n".join(pop.retr(num)[1]) print contents print

pop.quit() }}}

If you run this, you'll end up with a dump of all the new messages in your Inbox. Unfortunately, it's not in a very useful format yet, and it would be a headache to try to parse the email headers manually. Luckily, Python's standard library includes the {{{email}}} module, which will parse the message for us.

{{{ #!div style="border: 1px solid #d7d7d7; margin: 1em 1.75em; padding: .25em; overflow: auto;" '''Hands-on Example''[[BR]]

Using the documentation for the {{{email}}} module, modify our above script so that it only prints the subjects of each message. }}}

=== Extracting Data ===

Email messages can have multiple parts (e.g. attachments, HTML versions of the message, etc). To keep this simple, we're only going to look at plain text, but we still have to be aware of multipart messages. To do this, we can "walk" through each of the email payloads until we find one with a content type of {{{"text/plain"}}}. Once we've found this, we can use it as the body of the message we're reading.

{{{ #!CodeExample #!python import email import getpass import poplib

passwd = getpass.getpass()

pop = poplib.POP3_SSL("pop.gmail.com") pop.user("pybc.bot@gmail.com") pop.pass_(passwd)

messages = pop.list()[1] for i in messages:

num = int(i.split()[0]) contents = "n".join(pop.retr(num)[1]) message = email.message_from_string(contents)

body = None for i in message.walk():

if i.get_content_type() == "text/plain":
body = i.get_payload()

print "Subject:", message["Subject"] print "Body:", body print

pop.quit() }}}

{{{ #!div style="border: 1px solid #d7d7d7; margin: 1em 1.75em; padding: .25em; overflow: auto;" '''Hands-on Example''[[BR]]

Print out the Pig Latin-ized version of the message subject and body. }}}

=== Sending Email ===

Sending email is just as easy as receiving email. Even though Gmail requires STARTTLS security and authentication, it's still only a few lines of code:

{{{ #!CodeExample #!python import getpass import smtplib

passwd = getpass.getpass()

smtp = smtplib.SMTP("smtp.gmail.com", 587) smtp.ehlo() smtp.starttls() smtp.login("pybc.bot@gmail.com", passwd) smtp.sendmail("pybc.bot@gmail.com", "your@address.here",

"testing")

smtp.quit() }}}

This creates an anemic message with no subject and a body of "testing". To make sure we have all the appropriate headers, we can use the {{{email}}} module again.

{{{ #!div style="border: 1px solid #d7d7d7; margin: 1em 1.75em; padding: .25em; overflow: auto;" '''Hands-on Example''[[BR]]

Using the {{{email}}} documentation, create an email message with the following headers: From, To, and Subject. Don't forget to add an interesting body! }}}

=== Gluing the Pieces Together ===

All that remains is for us to glue together the mail-checking and mail-sending scripts we've written, and then run them in an infinite loop to keep our brand new bot alive!

{{{ #!CodeExample #!python import email import getpass import piglatin import poplib import smtplib import time

myaddr = "pybc.bot@gmail.com" passwd = getpass.getpass()

while True:

pop = poplib.POP3_SSL("pop.gmail.com") pop.user(myaddr) pop.pass_(passwd)

smtp = smtplib.SMTP("smtp.gmail.com") smtp.ehlo() smtp.starttls() smtp.login(myaddr, passwd)

messages = pop.list()[1] for i in messages:

num = int(i.split()[0]) contents = "n".join(pop.retr(num)[1]) message = email.message_from_string(contents)

body = None for i in message.walk():

if i.get_content_type() == "text/plain":
body = i.get_payload()
if body == None:
print "HTML only message detected:", message["Subject"] continue

reply = email.message.Message() reply["From"] = myaddr reply["To"] = message["From"] reply["Subject"] = piglatin.translate(message["Subject"]) reply.set_payload(piglatin.translate(body)) smtp.sendmail(reply["From"], reply["To"], reply.as_string())

smtp.quit() pop.quit()

time.sleep(60)

}}}

== Creating an IRC Bot ==

Before we get started, let's go over a brief summary of how IRC works for those who don't know. IRC ("internet relay chat") is a protocol designed to let groups of users chat in individual rooms ("channels") on one or more networks. Most networks have several servers to minimize the load, and the servers stay in constant communication to allow users from one server on a network to talk to users on other servers on that network. There are hundreds of IRC networks available on the internet, each with thousands of channels. The actual protocol is pretty simple, and we could probably implement an IRC bot in Python just using network sockets, but why reinvent the wheel?

=== Getting Started ===

The first thing any IRC bot needs to do is connect to a server and (usually) join a channel. Using {{{python-irclib}}}, this is a simple matter.

{{{ #!CodeExample #!python import irclib

server = "irc.synirc.org" port = 6667 nick = "pybcbot" channel = "#botsploitation"

class pybc_bot(irclib.SimpleIRCClient):
def __init__(self, server, port, nick, channel):
irclib.SimpleIRCClient.__init__(self) self.connect(server, port, nick) self.channel = channel
def on_welcome(self, connection, event):
connection.join(self.channel)

irc = pybc_bot(server, port, nick, channel) irc.start() }}}

{{{ #!div style="border: 1px solid #d7d7d7; margin: 1em 1.75em; padding: .25em; overflow: auto;" '''Hands-on Example''[[BR]]

Modify our IRC bot so that it sends a message to the channel when it joins. Hint: in IRC, the command to send a message to a channel (or user) is {{{PRIVMSG}}}. }}}

== Replying to Messages ==

In order to make our IRC bot useful, we need to make it respond to messages from other users. We're going to make our bot respond to messages in channels that begin with "!pig". When our bot sees a message like that, it will look at the text after the "!pig" and translate it into Pig Latin and send it back to the user. To eliminate confusion, we'll also figure out the username of the person who sent the command and prefix our reply with that name.

{{{ #!CodeExample #!python import irclib import piglatin import re

server = "irc.synirc.org" port = 6667 nick = "pybcbot" channel = "#botsploitation"

class pybc_bot(irclib.SimpleIRCClient):
def __init__(self, server, port, nick, channel):
irclib.SimpleIRCClient.__init__(self) self.connect(server, port, nick) self.channel = channel
def on_welcome(self, connection, event):
connection.join(self.channel)
def on_pubmsg(self, connection, event):

msg = event.arguments()[0].split(' ', 1) user = event.source().split('!')[0] if msg[0] == "!pig":

if len(msg) == 1:
connection.action(self.channel, "glares at "+user)
else:
connection.privmsg(self.channel, user+": "+
piglatin.translate(msg[1]))

irc = pybc_bot(server, port, nick, channel) irc.start() }}}

Clone this wiki locally