Writing an IRC Bot with JavaScript/Node.js and coffea

Daniel Bugl
Aug 5, 2015 · 5 min read

I am one of the main developers of coffea and, as you probably figured, I don’t write much (non-code). This is my first post on medium! Personally, I love getting started with a new library by following a tutorial about building something cool with that library, so here’s my article/tutorial on writing an IRC bot with JavaScript, Node.js and coffea.

You won’t need to know about Node.js or coffea, knowing JavaScript before doing this tutorial is strongly encouraged though.

Let’s keep it as simple as possible and write a bot that can:

  • Respond to !ping
  • Join channels: !join channel

If you’ve worked with Node.js before, you can probably skip the next section and start at First steps with coffea.

Setting up the project

Next, we are going to create a directory for our project (mkdir bot) and change into it (cd bot). Run npm init and fill out the information (or simply keep pressing return/enter) to setup your project. This creates a package.json file in the directory, which will store information about the project (like the name, version and dependencies).

First steps with coffea

Now let’s create a new file (touch index.js) and open it with your favorite editor (e.g. vim index.js). Type the following and save the file:

var client = require('coffea')('chat.freenode.net');

This will create a coffea IRC client and connect to chat.freenode.net. You can configure much more here than just setting the host as a string, simply replace the host with a JavaScript object, like this:

var client = require('coffea')({
host: 'chat.freenode.net',
port: 6667, // default value: 6667
ssl: false, // set to true if you want to use ssl
ssl_allow_invalid: false, // set to true if the server has a custom ssl certificate
prefix: '!', // used to parse commands and emit on('command') events, default: !
channels: ['#foo', '#bar'], // autojoin channels, default: []
nick: 'test', // default value: 'coffea' with random number
username: 'test', // default value: username = nick
realname: 'test', // default value: realname = nick
pass: 'sup3rS3cur3P4ssw0rd', // by default no password will be sent
nickserv: {
username: 'test',
password: 'l33tp455w0rD'
},
throttling: 250 // default value: 250ms, 1 message every 250ms, disable by setting to false
});

To connect to multiple networks, just use a JavaScript array:

var client = require('coffea')(['chat.freenode.net', 'irc.oftc.net']);

Of course this also works with objects instead of strings.

As an example, we are going to connect to freenode with a custom nick and make the bot join a channel (multiple channels can be specified in the JavaScript array):

var client = require('coffea')({
host: 'chat.freenode.net',
nick: 'coffea-testbot',
channels: ['#caffeinery']
});

Now let’s run the program with Node.js: node index.js

[16:20:45] coffea-testbot (~coffea-te@***) joined the channel

Success! That wasn’t that hard was it?

Responding to commands

client.on('message', function (event) {
console.log(event.channel.name, event.user.nick, event.message);
});

Add this to the end of index.js and restart the bot, then type a few messages in a channel the bot is in, to see what happens:

#caffeinery omnidan test

Let’s filter out commands by listening to on command events instead:

client.on('command', function (event) {
console.log(event.channel.name, event.user.nick, event.message);
});

Try this out by typing test, then !test and compare the results!

#caffeinery omnidan !test

Time to write our first command, it will be !ping to which the bot will respond with pong. The coffea library defines client.send, which allows us to send messages to a certain user or channel, but since we want to reply to whoever sent us the command, whether it be a private message or a channel, we are going to make use of the event.reply helper:

client.on('command', function (event) {
switch (event.cmd) {
case 'ping':
event.reply('pong');
break;
}
console.log(event.channel.name, event.user.nick, event.message);
});

Simple, right? Let’s try it out:

[10:42:57] <+omnidan> !ping

[10:42:58] <coffea-testbot> pong

Joining channels

case 'join':
event.reply('joining channel');
// TODO: join channel here
break;

But how do we actually join the channel? Let’s take a look at the coffea documentation index and look for join() (coffea function): http://coffea.caffeinery.org/en/latest/channel.html#coffeafunction-join(channels,keys,network,fn)

join(channels, keys, network, fn)

All coffea functions are prefixed with client. (at least as long as you import coffea via var client) Let’s try out the function — we need to pass a channel or a list of channels first, then the key(s) for the channel, then the network the channel is on and last but not least, we can specify a callback, which is a function that will be triggered after the join completes. As our channel won’t be secured by a password, we won’t need to specify any keys.

case 'join':
event.reply('joining channel');
client.join(args[0], event.network, function () {
event.reply('joined channel');
});
break;

[11:04:39] <+omnidan> !join #omnidan

[11:04:40] <coffea-testbot> joining channel

[11:04:40] coffea-testbot (~coffea-te@***) joined the channel

[11:04:41] <coffea-testbot> joined channel

But what if we don’t specify an argument to our !join command? The bot crashes — that’s not good! Let’s check the arguments first:

case 'join':
if (args.length < 1) {
event.reply('not enough arguments: please specify a channel');
break;
}
event.reply('joining channel');
client.join(args[0], event.network, function () {
event.reply('joined channel');
});
break;

Now it works fine.

[11:09:04] <+omnidan> !join

[11:09:04] <coffea-testbot> not enough arguments: please specify a channel

[11:09:07] <+omnidan> !join #omnidan

[11:09:08] <coffea-testbot> joining channel

[11:09:08] <coffea-testbot> joined channel

[11:09:23] <+omnidan> !ping

[11:09:24] <coffea-testbot> pong

That’s it! I know this guide could’ve been much shorter, but I wanted to describe the whole process from zero to a working IRC bot. Furthermore, I tried making it more interactive, so that readers can get a feel for working with coffea — even without actually trying it out. However, I do highly encourage doing so and tweaking the bot, adding more features, etc. to get more familiar with the library and event-based development.

Oh and, here is the source of the bot as a gist.

Going deeper

If you’re feeling experimental, you can try out coffea 0.5, which supports multiple protocols (like telegram), which means your IRC bot will automatically work on telegram too (as long as you load the plugin): https://github.com/caffeinery/coffea-telegram#setup This is still really new and experimental, so don’t get frustrated if something doesn’t work correctly. (Keep calm and report the issue)


Liked this post? Leave me a comment below or recommend it!

Also make sure to check out my other projects on my website or github — whichever you prefer :)

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch

Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore

Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store