Back to blog overview

May 4, 2020

Creating a Twitch Chatbot

Erik Guzman

&

&

Senior Software Engineer
San Diego, CA

## STREAM RECAP

Have you ever wondered how to build a Twitch chatbot? Well, you're in luck! On the latest edition of the [Coding ZEAL Twitch stream](http://twitch.com/codingzeal) Erik Guzman, Senior Software Engineer at ZEAL, did just that. Go from zero to Twitch chatbot hero in this recap of [the stream](https://www.twitch.tv/videos/601528377).

## GETTING STARTED

To get started, let's create a new project directory for our chatbot project and initialize npm.

Use the following commands in the terminal:

-- CODE language-bash line-numbers --$ mkdir zeal-chat-bot && cd zeal-chat-bot
$ npm init -y

Next, we want to throw in all the bells and whistles of JavaScript, so let's add [Webpack to our project](https://webpack.js.org/guides/).

In the terminal:

-- CODE language-bash --$ npm install webpack webpack-cli

Now let's create our source code directory (**src**) and source file (**index.js**). In the terminal:

-- CODE language-bash line-numbers --$ mkdir src
$ touch src/index.js

To make sure everything works, let's add a simple hello via `console.log` in the **index.js** file.

Inside **src/index.js:**

-- CODE language-js --console.log("HELLO")

Now that we have some code, we need to build it using Webpack. Let's add a `build` command to our **package.json** file. This file was automatically generated when we ran `npm init -y`.

In package.json:

-- CODE language-js line-numbers --.
.
"scripts": {
 "build": "webpack"
},
.
.
.

## BUILDING AND RUNNING OUR CODE

Now, we can run our newly created `build` command. In the terminal:

-- CODE language-bash --npm run build

After running the build command, Webpack will create a **dist** folder and transpile our code into **main.js**. All that's left to do is execute the code.

In the terminal:

-- CODE language-bash --$ node dist/min.js
$ Hello

You should see `Hello` logged in the terminal. NICE, we made a super simple node application.

## CUSTOMIZING OUR WEBPACK BUILD

Up to this point, we have been using Webpack's default build configuration. It gets the job done, but we want to add some custom configuration through our own config file.

In the terminal create a **webpack.config.js** file:

-- CODE language-bash --$ touch webpack.config.js

Open the file and paste some boilerplate code directly from the [Webpack Getting Started guide](https://webpack.js.org/guides/getting-started/):

-- CODE language-js --const path = require('path');
module.exports = {
 entry: './src/index.js',
 output: {
   filename: 'main.js',
   path: path.resolve(\_\_dirname, 'dist'),
 },
};

This code is the exact same configuration Webpack uses by default. You'll notice Webpack uses the source code (`entry`) **src/index.js** to create a new file (`filename`) named **main.js**, inside the **dist** folder (`path`). I like the entry and output file to match. Let's rename **main.js** to **index.js**.

-- CODE language-js --const path = require('path');
module.exports = {
 entry: './src/index.js',
 output: {
   filename: 'index.js', // CHANGED
   path: path.resolve(\_\_dirname, 'dist'),
 },
};

Now, let's delete the **dist** folder and rerun the build command:

-- CODE language-bash --$ rm -fr dist && npm run build

Now, inside the **dist** folder, you should see an **index.js** file instead of **main.js** file.

Let's make sure everything is still working:

-- CODE language-bash --$ node dist/index.js
$ Hello

This is cool. There's only one problem: each time we make changes to the Webpack build, we have to delete the **dist** folder to avoid future conflicts. Fortunately, there's a simple solution, thanks to Webpack. We can add a new Webpack plugin and modify the config.

In the terminal:

-- CODE language-bash --$ npm install -D clean-webpack-plugin

Open **webpack.config.js** and edit it to look like:

-- CODE language-js --const path = require('path');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
module.exports = {
 entry: './src/index.js',
 mode: 'none',
 target: 'node',
 plugins: [new CleanWebpackPlugin()],
 output: {
   filename: 'index.js',
   path: path.resolve(\_\_dirname, 'dist'),
 },
};

With this new plugin, each time you run `npm run build`, Webpack will delete the **dist** folder before transpiling. Now, our build process is a little more smooth.

## IMPLEMENTING THE CHATBOT WITH COMFY.JS

Let's start building our chatbot. We'll install an easy-to-use library called Comfy.js.

In the terminal:

-- CODE language-bash --$ npm install comfy.js

Now let's start implementing our chatbot logic, open **src/index.js** and replace the `console.log` with:

-- CODE language-js --import ComfyJS from "comfy.js";
 ComfyJS.onCommand = ( user, command, message, flags, extra ) => {
   if( command === "test" ) {
   console.log( "!test was typed in chat" );
 }
}
ComfyJS.Init( "codingzeal" );

The **Comfy.js** interface is straight forward and easy to understand. To connect to a Twitch chat channel, put the name of the channel you want to connect to inside `init`. In our case, I want to connect to the CodingZeal Twitch channel.

Next, `onCommand` is a special callback function that listens to the chat for anyone sending a message in this format `!<command> [message]`. If someone in the CodingZeal chat sends the message `!test` it will trigger the console message.

Rebuild and run to test out our code:

-- CODE language-bash --$ npm run build
$ node dist/index.js

You can test that this is working and connecting to your channel by doing a test command in the Twitch chat channel.

-- CODE language-text --!test
You should see the node app console log.
!test was typed in chat
WOO HOOO!

You may have noticed that anytime we want to run our application, we have to include the path: **dist/index.js** We can make this easier by including this information inside the package.json file. Open **package.json** and change `main` to use the built index.js file.

-- CODE language-js --...
"main": "dist/index.js",
...

Now, in the terminal type:

-- CODE language-bash --$ node .

It will run the file specified by main.

## LISTENING TO CHAT MESSAGES

Right now, the chatbot is listening to all incoming chat **commands**, but we can expand that, to include all chat **messages** really easily.

Open up **src/index.js** and add the code block below.

-- CODE language-js --import ComfyJS from "comfy.js";
ComfyJS.onCommand = ( user, command, message, flags, extra ) => {
 if( command === "test" ) {
   console.log( "!test was typed in chat" );
 }
}
// Code below listens to all chat messages and logs them out
ComfyJS.onChat = ( user, message, flags, self, extra ) => {
 console.log( user, message );
}
ComfyJS.Init( "codingzeal" );

With the `onChat` callback, it's great that we can listen to all the messages being sent in, but we can take this a step further. Let's add the ability for the bot to respond to chat commands or reply to a user.

## BOT SENDING MESSAGES TO CHAT

To send a message to the chat, the bot will need a token to authenticate. Twitch needs to know who you are and ensure you're not a rogue bot who spams people's chats. Since we're dealing with sensitive information, let's use some best practices for handling credentials. We'll store our credentials inside an **.env** file.

Install `dotenv` so we can read our future **.env** file.

-- CODE language-bash --$ npm install dotenv

We'll also need to install two more dependencies:

-- CODE language-bash --$ npm install -O bufferutil utf-8-validate

Next, create a file named **.env** that looks like this:

-- CODE language-bash --TWITCHUSER=[YOUR-USERNAME-HERE] # e.g. codingzeal
OAUTH=[YOUR-OAUTH-PASS HERE] # e.g. OAUTH=oauth:abcdefghijklm12345678

[Get a Twitch Chat OAuth Password Token](http://twitchapps.com/tmi/). Edit your newly created **.env** file with your new token and  Twitch username. Never share this token with anyone. Basically, it's your password for your chatbot. If someone gets a hold of it, they can do some serious damage.

To avoid any possibility of accidentally committing this file to Git source control, add it to your **.gitignore** file. If you don't have one,  create one in your root project directory.

In the terminal:

-- CODE language-bash --$ echo .env >> .gitignore

Open up **src/index.js** and update **ComfyJS.init** with the Username and OAUTH password. Let's also create a new hello command.

-- CODE language-js --const ComfyJS = require("comfy.js");
require('dotenv').config(); // Loads the env variables found in .env
ComfyJS.onCommand = ( user, command, message, flags, extra ) => {
 // New command that will greet someone with they type !hello in the chat
 if( command === "hello" ) {
   ComfyJS.Say(`Hello, ${user}`)
 }
}
ComfyJS.onChat = ( user, message, flags, self, extra ) => {
 // Logs all messages being sent to Twitch chat
 console.log( user, message );
}
// Uses our env variables to login as your Twitch user into the chat
ComfyJS.Init( process.env.TWITCHUSER, process.env.OAUTH );

Let's build and run our changes:

-- CODE language-bash --$ npm run build > node .

In Twitch chat try the new `!hello` command.

-- CODE language-text --!hello

You should see our bot reply to you.

## IMPLEMENT !PODCAST COMMAND TO FETCH INFORMATION

Now, we can:

- listen to chat commands
- log all messages coming into chat
- the bot can send messages to chat

Let's take the foundation that we laid and take it to the next level.

Let's create a new `!podcast` command that will fetch the latest ZEAL podcast episode with a description and link.

We can use the Creating ZEAL RSS feed to fetch the latest podcast, so let's try using that!

[https://feeds.buzzsprout.com/114820.rss](https://feeds.buzzsprout.com/114820.rss)

First things first, we'll need an RSS feed parser library. Here is a simple one I found:

-- CODE language-bash --$ npm install rss-parser

Next let's add the code to load the RSS feed and parse it to the top of **src/index.js**

-- CODE language-js --const ComfyJS = require("comfy.js");
require('dotenv').config();
import Parser from 'rss-parser'; // New Import
const parser = new Parser(); // Create parser
// Parse the Creating ZEAL podcast RSS feed
function fetchLatestPodcastEpisode() {
 let feed = await parser.parseURL('https://feeds.buzzsprout.com/114820.rss');
 console.log(feed.title);
 feed.items.forEach(item => {
   console.log(item.title + ':' + item.link)
 });
}

Now that we have the code to fetch the RSS information, let's add a brand new chat command that will call this function. We can see the log messages from the RSS.

-- CODE language-js --ComfyJS.onCommand = ( user, command, message, flags, extra ) => {
 if( command === "hello" ) {
   ComfyJS.Say(`Hello, ${user}`)
 } else if( command === 'podcast' ) { // New command
   fetchLatestPodcastEpisode();
 }
}

Let's build and run our new changes.

-- CODE language-bash --$ npm run build > node .

If you type `!podcast` in the chat, you should see a log message in the console looping through all the information. Awesome!

-- CODE language-text --Creating Zeal Podcast
What Makes a Good Manager With Tramale Turner:undefined
Exploring Social Anxiety with Liz Krane:undefined
Software Freedom with Karen Sandler:undefined
Learning to Teach Code with Ali Spittel:undefined
Coming Into Code with Anna Rankin:undefined
...

We don't need to look through all the items in the RSS feed, we only need the first one in the array since that's the latest episode. Let's get that done.

-- CODE language-js --async function fetchLatestPodcastEpisode() {
 let feed = await parser.parseURL('https://feeds.buzzsprout.com/114820.rss');
 const latest = feed.items[0];
 // Return back a simple object with all the details we need
 return {
   title: latest.title,
   season: latest.itunes.season,
   episode: latest.itunes.episode,
   summary: latest.itunes.summary,
   url: latest.enclosure.url
 }
}

Let's update our podcast command to respond back with the latest podcast information straight to chat using ComfyJS. Say:

-- CODE language-js --ComfyJS.onCommand = async ( user, command, message, flags, extra ) => {
 if( command === "hello" ) {
   ComfyJS.Say(`Hello, ${user}`)
 } else if( command === 'podcast' ) {
   const episode = await fetchLatestPodcastEpisode();
   ComfyJS.Say(`Episode ${episode.episode} season ${episode.season} from Creating Zeal Podcast. ${episode.title}: ${episode.summary} ${episode.url}`)
 }
}

Rebuild and run the code:

-- CODE language-bash --$ npm run build > node .

Test our newly updated command in Twitch chat.

-- CODE language-text --!podcast

It should return the latest episode from Creating ZEAL and send that in a message to the chat.

-- CODE language-text --Episode 8 season 4 from Creating Zeal Podcast. What Makes a Good Manager With Tramale Turner: What makes a good engineering manager? Join us for the latest Creating Zeal podcast episode where Host Adam Cuppy discusses becoming comfortable with discomfort and the noble pursuit of being a leader with Tramale Turner, Engineering Manager at Stripe... https://www.buzzsprout.com/114820/3306733-what-makes-a-good-manager-with-tramale-turner.mp3?blob_id=11899774

## FINAL THOUGHTS

We went from no code to a `!podcast` command, returning the latest episode of the Creating ZEAL podcast super quickly. Hopefully, this shows how easy it can be to create your very own chatbot for Twitch or for other platforms, like Slack.

Let's Chat

Are you ready to build something brilliant? We're ready to help.

Thank you! Your submission has been received!
Oops! Something went wrong while submitting the form.
RedwoodJS Logo
RedwoodJS
Conference

conference
for builders

Grants Pass, Oregon • September 26 - 29, 2023
View All