A Simple Go IRC Bot

How to create a small IRC bot using the Go programming language.

If you don’t have a Go environment set up, take a look at the installation guide. I found it fairly straightforward, which seems to be a common occurrence when working with Go.

We’ll use the github.com/thoj/go-ircevent package as the basis of our IRC bot, as it provides a simple wrapper over the low level IRC protocol, handles connections and pings and easily allows us to attach callback functions to events.

To get this package, run go get github.com/thoj/go-ircevent. You’ll need a working Go environment with GOPATH set to the absolute location of your Go install.

Then, create a new Go application, for example ircbot.go, and import go-ircevent and the fmt package for debugging.

package main

import (
    "github.com/thoj/go-ircevent"
    "fmt"
)

Creating a new connection is super easy. Here I’m using irc.freenode.net as an example, but you can substitute any IRC server.

func main() {
    con := irc.IRC("my-bot-test", "my-bot-test")
    err := con.Connect("irc.freenode.net:6667")
    if err != nil {
        fmt.Println("Failed connecting")
        return
    }
}

Now we have a connected con, we can attach handler functions to various different events. We need our bot to join a room when the connection is ready, which is signalled by a "001" event. We’ll be using the room name a lot so we should store it in a variable.

var roomName = "#my-bot-test"

func main() {
    •••
    con.AddCallback("001", func (e *irc.Event) {
        con.Join(roomName)
    })
}

Here we’re creating a package-global variable roomName, and add a callback function, triggered by the "001" event which joins that room. The function is a closure as it references the con variable in it’s parent scope. It accepts a pointer (hence the * dereferencing syntax) to an object of the type irc.Event.

At this point you should be able to run the bot. In an IRC client, connect to the "my-bot-test" room on whichever server you chose, then run go run ircbot.go.

You should see output which looks a bit like this:

2013/08/25 13:22:36 Connected to irc.freenode.net:6667 (213.92.8.4:6667)

and then nothing. What gives? The problem is that your application has finished running before it ever got the chance to execute the callback you set. Add

      con.Loop()

to the very end of your main function and try again. It may take around 20 seconds the first time, but after that you should see your bot join the room!

The next step is to make the bot actually do something. Let’s make it echo whatever we say.

func main() {
    •••
    con.AddCallback("JOIN", func (e *irc.Event) {
        con.Privmsg(roomName, "Hello! I am a friendly IRC bot who will echo everything you say.")
    })
    con.AddCallback("PRIVMSG", func (e *irc.Event) {
        con.Privmsg(roomName, e.Message)
    })
    con.Loop()
}

The first callback is triggered when the bot has successfully joined a room, and introduces itself by sending a PRIVMSG to the room.

The second callback is triggered whenever a PRIVMSG event happens in a room the bot is in — whenever we say something. Here we access the Message property of the event struct, and echo it back out.

Quit the bot with ctrl + c and re-run the bot. Once it’s introduced itself it should echo everything you say.

To package the bot up into an executable file you can run without compiling beforehand, run go build ircbot.go, which should produce an ircbot file you can run with ./ircbot.


By now you’ve successfully picked up the foundations of simple IRC bots — connecting to a server, listening to events and sending commands. Adding extra functionality is left as an exercise to the reader. Ideas:

  • A bot which watches for mentions of URLs and checks whether those sites are up or down (try net/http)
  • A bot which watches for image URLs and adds mustaches to them (try mustachify.me)
  • A bot which only responds to messages from particular users