Using Node to create a Twitch Chatbot

Last week I wrote a post here: https://www.richwerks.com/index.php/2019/twitch-bot-tutorial/. This post showed how to create a twitch chat bot using python. This week I wanted to take a stab at creating one using Node to create a Twitch chatbot. I’m using the same Ubuntu f1-micro setup on Google Cloud Platform. For this tutorial, I am using tmi.js (twitch messaging interface) to create the connection to the Twitch chatrooms. Again we will have our configuration for the connection located in a separate file from our main application code.

I also created a separate file to store the chat commands. I did this for a couple reasons. One was I wanted a configuration that could be iterated through, in the event that I wanted to add more commands without having to change my application logic. Then I started to think, what can I do with this file? Do I just store keywords and chat responses? That is useful enough if people wanted to get information about your social media or merchandising, but what about our previous tutorial that included a “!roll” command?

Well as it turns out using this external file is perfect. I just needed a standardized format. I chose to use an array of objects. These objects are commands the bot should react to. IN the object, I store a command name, “name”, a command type, and depending on the command type, I store either a chat message, “message” or a function. Currently the command type is limited to “message” or “function”. Because of this, I can have a rudimentary command/reply system, but also a system that executes some function.

Contents of the command.js file for our Node Twitch chatbot

Here is the contents of the commands.js file so that you can see what the object array looks like.

const commands = [
  {
    name : "merch",
    type : "message",
    message : "I don't currently have any merch at this time."
  },
  {
    name : "roll",
    type : "function",
    func : function (channel, userstate, message, client){
      let randNum = Math.floor(Math.random() * (100 - 0 + 1)) + 0;
      client.say(channel, `${userstate.username} rolled a ${randNum}!`);
    }
  },
  {
    name : "social",
    type : "message",
    message : "You can see me at http://www.twitch.tv/XerouzTV or you can go to http://www.youtube.com/cRichOffical"
  }
  ];
module.exports = commands

If you wanted to, you can just add additional objects to the array in the commands.js file. In addition to is, you could also add them directly from your main application code by creating a new object and appending it to the commands array like so. Iv

const newCommmand = {
    name: "newCommand".
    type: "message",
    message: "This is a new command message. It will send to the channel when !newCommand is used."
};

commands.push(newCommand)

Here is another example of adding a command that uses a function. The function commands take several arguments: channel, userState, message and client. These are objects that are part of the tmi client, so they should be available with every message. Just make sure your commands follow the same format.

const newFuncCommand = {
    name: "runFunction",
    type: "function",
    func: function(channel, userState, message, client){
        //Do some database stuff or maybe read from a text file here.
        //Really you can do whatever you want here. At the end, you can send
        //data back through client.say() to send a message to the chat channel
        client.say(channel, "Here I will send back some message after processing my function.");
    }
};

commands.push(newFuncCommand);

The configuration for the Twitch connection in config.js for our Node Twitch Chatbot

Great, we’ve defined some commands in an external commands.js file. Now we need to store connection information for the Twitch chat connection. This is a configuration object for tmi.js. It will store your OAuth token, so it is wise to keep it separate from your main application code, just in case you are storing your code in some sort of public repository. The options takes an options object that specifies debugging. debug: true will output your bot’s chat responses to the console. Connection.reconnect will tell the bot to attempt a reconnect if it gets disconnected.

The identity section takes the username of your bot and it’s OAuth token to authenticate it. If you do not have a username or OAuth token for your chatbot, you can follow the links in my article about creating a python chatbot. https://www.richwerks.com/index.php/2019/twitch-bot-tutorial/. The channels section is an array of channel names that your bot will join. Here is an example of a config.js file here.

const options = {
  options: {
    debug: true
  },
  connection: {
    reconnect: true
  },
  identity: {
    username: "OmegaDroid",
    password: "oauth:YOUR_OATH_TOKEN_HERE"
  },
  channels: ["XerouzTV"]
};

module.exports = options;

Chatbot.js. The meat and potatoes of our bot

Our next step in using Node to create a Twitch chatbot is to create the meat and potatoes of the whole thing, our chatbot.js file. This application will utilize tmi.js. tmi.js can be installed either by installing from the npm repository

npm install --save tmi.js

or it can be installed by downloading the files from the git repository at https://github.com/tmijs.

Now, first thing we need to do in our chatbot.js file is to import tmi.js, our config file and our commands file. We will also define our command prefix that we want to use to determine if a message is a command.

const tmi = require("tmi.js");
const options = require("./config.js");
const commands = require("./commands.js");
const CMD_PREFIX = "!";

Next, we need to create our tmi client object and connect it to the twitch IRC service.

const client = new tmi.client(options);
client.connect();

After we’ve established our connection, we need to create the functionality that tells the client what to do when we get a message. This is done by listening for a chat event on the client. We will output the chat message to the console, and call a command called handleCommands.

client.on("chat", function(channel, userstate, message, self){
  if(self) return;
  console.log(userstate, message);
  handleCommmand(channel, userstate, message);
});

Once we’ve got the client listening for a chat even, we need to define our handleCommand function. This function takes in the arguments from our chat event. The handleCommand function simply looks for the CMD_PREFIX and determines if it is the first character of our message. If it is, we have a chat command. We will then call the processCommand function, which will take the same arguments as the handleCommand function.

function handleCommmand(channel, userstate, message){
  if(message.charAt(0) == CMD_PREFIX){
    processCommand(channel, userstate, message)
  }
}

Now the processCommand function will iterate through our commands object and will perform an action based on the commands type (message or function). If it is a function, we can the “func()” function of the command object to process the function that is defined.

function processCommand(channel, userstate, message){
  console.log("processing commands...");
  message = message.replace(CMD_PREFIX, '').toLowerCase();
  command = message.split(' ');
  for(let i = 0; i < commands.length; i++){
    if(command[0] == commands[i].name){
      if(commands[i].type == "function"){
        commands[i].func(channel, userstate, message, client);
      }
      else if(commands[i].type == "message"){
        client.say(channel, commands[i].message);
      }
    }
  }
}

As you can see, we first strip the from the message. We then split the message into a command array. Then the commands array (confusing, maybe I should change the names) is iterated through and the name of our defined command is checked against the first item in the command array (our command minus the prefix). Once this is matched, the script then checks for the command type. If it is a “message” command type, we send the defined message to the chat channel, otherwise, if it is a “function” command type, we call command[i].func() with the arguments channel, userstate, message and client.

Our Twitch bot using Node is now ready to run

Great, we have all the pieces in place. Now we can run our bot by running:

node chatbot.js

Our chatbot will now connect to the channel defined and start listening to commands. As our chatbot is currently configured, it will listen for the “!merch” command and state we have no merchandise, “!roll” command and output a random number between 0 and 100 or the “!social” command and send links to our social media accounts. It is important to note that by default Twitch will mask URLs in chat channels. So if you want your bot to be able to properly display URLs, make your bot a moderator in your channel.

Making your bot a moderator will be a good idea as well, as you can use tmi.js’s client.ban(channel, user, reason) to ban users for, say, using words that you want banned from your channel or for spamming.

Here we go. Shown below will be the whole config.js, commands.js and chatbot.js files

config.js

const options = {
  options: {
    debug: true
  },
  connection: {
    reconnect: true
  },
  identity: {
    username: "OmegaDroid",
    password: "oauth:"
  },
  channels: ["XerouzTV"]
};

module.exports = options;

commands.js

const commands = [
  {
    name : "merch",
    type : "message",
    message : "I don't currently have any merch at this time."
  },
  {
    name : "roll",
    type : "function",
    func : function (channel, userstate, message, client){
      let randNum = Math.floor(Math.random() * (100 - 0 + 1)) + 0;
      client.say(channel, `${userstate.username} rolled a ${randNum}!`);
    }
  },
  {
    name : "social",
    type : "message",
    message : "You can see me at http://www.twitch.tv/XerouzTV or you can go to http://www.youtube.com/cRichOffical"
  }
  ];
module.exports = commands

chatbot.js

const tmi = require("tmi.js");
const options = require("./config.js");
const commands = require("./commands.js");
const CMD_PREFIX = "!";

const client = new tmi.client(options);
client.connect();



client.on("chat", function(channel, userstate, message, self){
  if(self) return;
  console.log(userstate, message);
  handleCommmand(channel, userstate, message);
});

function handleCommmand(channel, userstate, message){
  if(message.charAt(0) == CMD_PREFIX){
    processCommand(channel, userstate, message)
  }
}

function processCommand(channel, userstate, message){
  console.log("processing commands...");
  message = message.replace(CMD_PREFIX, '').toLowerCase();
  command = message.split(' ');
  for(let i = 0; i < commands.length; i++){
    if(command[0] == commands[i].name){
      if(commands[i].type == "function"){
        commands[i].func(channel, userstate, message, client);
      }
      else if(commands[i].type == "message"){
        client.say(channel, commands[i].message);
      }
    }
  }
}

36 thoughts on “Using Node to create a Twitch Chatbot”

  1. I’m not great in Python; what does the (100 - 0 + 1) part do in the expression Math.floor(Math.random() * (100 - 0 + 1))? I assume it has to do with casting the float from Math.random to an integer, but I’m confused why the +0 is needed?

    1. Jonathan, the 0 isn’t needed at all, it’s just there out of habit. You do Math.floor(Math.random(max -min)) to be exclusive of the max number, or Math.floor(Math.random(max – min + 1)) to be inclusive of tha max number.

  2. I’ve been surfing on-line more than three hours nowadays,
    yet I never discovered any fascinating article like yours.
    It is beautiful price enough for me. In my opinion,
    if all webmasters and bloggers made excellent content material as you
    probably did, the net shall be much more useful than ever before.

  3. Excellent goods from you, man. I’ve understand your stuff previous to and you’re just extremely fantastic.
    I actually like what you’ve acquired here, certainly
    like what you’re stating and the way in which you say
    it. You make it enjoyable and you still care for to keep it wise.
    I can not wait to read far more from you. This is actually a great site.

  4. Hey very cool site!! Guy .. Excellent .. Superb ..
    I will bookmark your blog and take the feeds additionally?
    I’m glad to find a lot of useful info right here in the submit, we
    want develop more strategies in this regard, thanks for
    sharing. . . . . .

  5. Hi there would you mind sharing which blog platform
    you’re using? I’m looking to start my own blog soon but I’m having a difficult time choosing between BlogEngine/Wordpress/B2evolution and Drupal.
    The reason I ask is because your layout seems different then most blogs and I’m looking for something completely unique.
    P.S Sorry for being off-topic but I had to ask!

  6. Reduce costs with our low cost composing program | Get ve ry high excellent quality papers of any problems stage but for your reasonable expense!

  7. Great weblog here! Additionally your web site loads up fast! What host are you the use of? Can I am getting your affiliate hyperlink to your host? I wish my web site loaded up as quickly as yours lol| а

  8. First off I want to say superb blog! I had a quick question in which I’d like to ask if you do not mind. I was interested to find out how you center yourself and clear your mind prior to writing. I have had a hard time clearing my thoughts in getting my thoughts out. I truly do enjoy writing however it just seems like the first 10 to 15 minutes tend to be lost simply just trying to figure out how to begin. Any ideas or tips? Thanks!| а

Leave a Reply

Your email address will not be published. Required fields are marked *