PyBc/PythonBots

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 python-irclib (typing sudo easy_install python-irclib should be sufficient on Linux systems). Also be sure to download the Pig Latin 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.

EXAMPLE: SELECT ALL
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.

Hands-on Example

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.

EXAMPLE: SELECT ALL
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()

Hands-on Example

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:

EXAMPLE: SELECT ALL
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.

Hands-on Example

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!

EXAMPLE: SELECT ALL
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.

EXAMPLE: SELECT ALL
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()

Hands-on Example

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.

EXAMPLE: SELECT ALL
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()

Attachments