2016-12-27 programming

Building an IRC Chatbot

For Christmas 2016, my brother got me a Sphero BB8 droid that I lovingly hacked for a decent 1.5 days before I wanted to create an interface to let my club to play around as well. The perfect platform: Slack. Or, more specifically, IRC.

Our club's Slack channel is very active. I'm pretty sure the entire CS department at Notre Dame is in the chat now, but the small group that comes to meetings also keeps the rest up to date with their latest hackings, so neat projects and snippets of code are thrown around often in #lug. (It's a great learning environment and we love each other, shameless plug but check us out...)
So, creating a Slack interface for my BB8 droid was the proper next step. However, builing an IRC bot that works with Slack seemed like a better move, since it didn't take up one of the allotted apps/integrations Slack allows (chatbots, Github and Twitter integrations, etc.). Additionally, IRC doesn't require users to be human - or authenticate like a human - so it was a much easier conquest.

Our club advisor and professor, Dr. Peter Bui is also in our chat, along with his sassy IRC bot, Bobbit. Bobbit was the first IRC bot I've ever interacted with, so he kind of became what I expect bots to act like. I've also interacted with my boyfriend's Facebook/GroupMe chatbot, Wolfrat Bot, who is pretty similar to Bobbit in terms of functionality, from what I've seen. I didn't want to make a redundant bot that has the same functionality as Bobbit and Wolfrat, so I wanted to keep it simple and stick to a few keyword responses and a few commands to my Sphero BB8.

There were two big steps in making this chatbot: 1) making a regular, simple chatbot and 2) making the chatbot interact with the actual droid, hardware and all. This blog post will only go over #1. If you want to read about #2, hop on over to this post.

The basic BB8 bot is made simply from networks and the IRC protocol, obviously written in Python 2. It's very simple and doesn't use the entirety of the IRC protocol, but that's all that I need at this time. Maybe in the future I'll come back to this project and beef it up, but there is a big problem right now that makes continuing this project hard. I'll get back to that later.

Anyways, the only IRC commands I worked with are USER, PASS, NICK, JOIN, and PRIVMSG. But before that even matters, you have to make a socket so you can even connect with the server.

Connecting

Since IRC works over TCP/IP and we want to use stream sockets and IPv4, the python2 code for this is irc = socket.socket(socket.AF_INET, socket.SOCK_STREAM).

Once the socket is created, you need to connect to some host at some port. For me, the command looked like irc.connect(("nd-cse.irc.slack.com", 6667)). Slack has a native IRC gateway (you can connect to Slack from your favorite IRC client, like WeeChat), and if you go to https://your-team-name.slack.com/account/gateways, you can find the correct HOST, USER, and PASS to use for connecting via IRC.

After connecting, you need to authenticate. IRC requires a few steps here, order mattered for me: sending the PASS, the NICK you want to use, the USER info, and then JOINing the channel you want. IRC also requires a certain format:

PASS super-secret-psk
NICK your-nick
USER your-nick your-host your-user : your-nick
JOIN your-channel

I always chose the same nick as my username. The python2 for this, with some of my details, looks like this:

irc.send("PASS " + password + "\r\n") 
irc.send("NICK " + nick + "\r\n")
irc.send("USER " + nick + " " + server + " bb8 : " + nick + "\r\n") 
irc.send("JOIN " + channel + "\r\n")

The returns and newlines matter.
That's all you need to connect, but you need more in order to stay connected.

Sending and receiving

Sending and receiving are pretty straightforward, but you do have to follow IRC formatting:

irc.send("PRIVMSG " + your-channel + " :" + your-msg + "\r\n") 
buff = irc.recv(2040) # 2040 is good enough, and buff will be the received text.  

Now you're able to have a little fun, but now you also know enough to stay connected. Per the IRC protocol, the server will send a "PING" message every once in a while to make sure that clients are still active. You have to respond with "PONG" if you want to keep your connection open:

if buff.find('PING') != -1: #assuming buff is the received text
    irc.send('PONG ' + buff.split()[1] + '\r\n') 

Quitting

Quitting is the last necessary thing, and it's very straightforward:

irc.send("QUIT " + " : sad goodbye message \r\n")

That's it for the regular, simple chatbot. If you want to see all of this code put together, checkout my BB8 Droid repo. If you want to read about how I made this code work with the actual Sphero, check out my next blog post Chatbots and Droids.