Building a WelcomeBot

Boston Python User Group, December 2014

Shauna Gordon-McKeon (@shauna_gm, shauna@openhatch.org, shauna on Freenode)

The background

The problem

The solution

So what is WelcomeBot?

So what is IRC?

So what is an application layer protocol?

Try it! @ bit.ly/ircbot-python

Socket Library

We start by creating a socket object, with some details about the connection we want to make.

ircsock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

We then use this object to connect to the server on port 6667.

server = "irc.freenode.net" 
ircsock.connect((server, 6667))

Socket Library (cont)

We can now communicate using send and recv:

ircsock.send("PRIVMSG "+ channel +" :Hello!\n")
ircsock.recv(2048)

These sent messages can include a variety of IRC syntax:

ircsock.send("PONG :pingis\n")  
ircsock.send("JOIN "+ chan +"\n")
ircsock.send("NICK "+ botnick +"\n") 

Basic Structure of WelcomeBot

(as seen at https://github.com/shaunagm/WelcomeBot/)

  • If newcomer joins, create a newcomer object with a datetime field.
  • If a non-newcomer speaks into the channel, silently record nickname and delete newcomer object.
  • Regularly check how long the newcomer has been around.
  • Once the newcomer has been around for longer than a set time (default is 60 seconds) greet them.

A mistake!

aka a learning experience

How do you listen and respond at the same time?

import queue, threading
def getIRC():
    q = Queue.LifoQueue()
    while True:
        ircmsg = ircsock.recv(2048) # receive data from the server
        ircmsg = ircmsg.strip('\n\r') # removing any unnecessary linebreaks.
        q.put(ircmsg)
t = Thread(target=getIRC)
t.daemon = True
t.start()
                    

Totally unnecessary

import select
ready_to_read, b, c = select.select([ircsock],[],[], 1)

Learning experience for everyone

12 contributors, including 6 who are making their first open source contribution!

  • Saves known nicknames
  • Recognizes patterns of nicknames, eg "shauna__" as "shauna"
  • Responds to greetings and requests for more info
  • Best of all...

Tests!

class TestBotClass(unittest.TestCase):

    def setUp(self):
        self.bot = botcode.Bot()
        
    def test_known_nicks_setup(self):
        bot = botcode.Bot('test_nicks.csv')
        self.assertEqual(bot.known_nicks, [['Alice'], ['Bob']])

    def test_add_nick_underscore_removal(self):
        self.bot.known_nicks = [['Fluffy'], ['Spot']]
        self.bot.add_known_nick('Roger__')
        self.assertEqual(self.bot.known_nicks,[['Fluffy'], ['Spot'], ['Roger']])

    def tearDown(self):
        with open('test_nicks.csv', 'w') as csv_file:
            csv_file.write('Alice\nBob\n')

Thanks!

Guides

Ned (seriously, watch his Ned's Pycon 2014 talk on unit testing), Asheesh Laroia, and the kind folks at project night.

Contributors

jbertino, artLopez, aaparella, clarissavazquez, dcarrot2, ppegusii, sunu, BananaObserved, lkuper, kwurst and Aaron1011