Menu

Settings

Theme
Animations
Scrolling

Build a Twitch Chatbot for Sharing Your Content Using Algolia Search

Over the past year of streaming Some Antics and over the past several years of blogging here, I've amassed a minor backlog of content that I sometimes want to link people to. I've found that searching for that previous stream or blogpost while on air can seriously disrupt the flow of a stream, though, especially when I'm supposed to be chatting with my guest.

While watching Bryan Robinson's stream the other day, however, I had a moment of inspiration: what if I could build a Twitch chatbot that could let me input some search command, providing a few keywords, and it would return a link to the most relevant stream or article? And thus, SomeAnticsBot was born. Anyone can go to the Some Antics chat and send a !stream command or a !blog command to get a relevant stream or blogpost. I recommend trying it with these queries:

  • !stream vite
  • !stream generative
  • !blog data cascade
  • !blog dl
  • !blog chatbot (Look familiar? 😉)

I think a bot like this is incredibly handy for any streamer with a backlog of handy content to link to, so I'm sharing how I built SomeAnticsBot with Algolia, Node.js, tmi.js, and Heroku.

Step 1: Set Up Your Algolia Index

Algolia is a service that lets us upload our content and then search through it quickly. It's predominantly designed to support website searchbar experiences, but it's what we'll use to ensure our bot is capable of taking any number of keywords and returning the most relevant piece of content.

Before we can use Algolia to search through our content, we'll need to tell Algolia what content exists. This entails uploading an index, a structured representation of our content (likely in a JSON format) with properties representing key attributes of our content such as the title, description, publish date, and body. If you're indexing pages on your site, you'll probably want a setup that's integrated with your workflow for deploying new versions of your site, so that anytime you publish a new page, it automatically gets added to your Algolia index so your search results remain up to date.

Unfortunately, because the specifics of different kinds of content and their methods of deployment vary a lot, I can't really give you The One True Way™ to set up your Algolia index and workflow. I apologize for this section being very "draw the rest of the owl," but I'll share some resources I ended up using to set up my index.

If you're using Netlify to deploy your site, I highly recommend following Algolia's quickstart for setting up a Netlify site with Algolia. This quickstart walks you through installing Algolia's official plugin for Netlify, which will add Algolia's site crawler to your build process and handle updating your index for you.

If you instead end up needing to manually create and deploy an index, I recommend reading through Raymond Camden's guide to creating an Algolia index in Eleventy, which uses the algolia-indexing library to publish incremental updates to your index.

You'll know everything's working when your Algolia index says it has records!

Step 2: Create a Twitch Account and Get Bot Credentials

You don't have to create a new Twitch account specifically for your bot — you could use your own account that you broadcast from as the chatbot account. However, I recommend using a new account for your chatbot for two reasons.

  1. It lets your audience more clearly delineate which messages are from you and which ones are from the bot. This is a less vital reason, but I tend to find clear delineations between humans and bots a better user experience for all involved.

  2. It ensures that if anything happens to the bot, your channel is safe. I personally get a little uneasy about the possibility that platforms such as Twitch could crack down on even benign bots such as this, and I want to remove any possibility that my actual account could be a casualty of crackdowns on botlike behavior. Additionally, we will be using authorization tokens for this bot, and if those tokens were to leak, I'd vastly prefer a burner account's tokens to leak than my own streamer account.

That in mind, go to Twitch and create a brand new account. I'd recommend picking a name that's very explicit that this is a chatbot for your channel. For instance, my channel is "SomeAnticsDev," and my bot is "SomeAnticsBot."

Once you're signed in as this new account, you'll want to get credentials to use this account as a bot. Here's how I did that (kudos to Colby Fayock for documenting these steps!):

  1. While logged into Twitch as your chatbot account, go to Twitch Token Generator.
  2. Select Bot Chat Token.
  3. Authorize Twitch Token Generator with your chatbot's Twitch account in the OAuth flow, and complete the captcha.
  4. Copy the provided access token and save it for later.

Step 3: Initialize a Node.js Project for Our Twitch Bot

Let's spin up the beginnings of a repository for our chatbot project.

Step 3a: Initialize Project

In your terminal, run the following commands:

mkdir twitch-chatbot
cd twitch-chatbot
git init
npm init -y

This creates a new Git repository, and initializes a Node project with a default configuration.

Step 3b: Install Dependencies

Next up, we'll install our dependencies:

npm install algoliasearch dotenv tmi.js

In order, these dependencies will do the following for us:

  • algoliasearch: Lets us query Algolia to get the most relevant results from our content
  • dotenv: Lets us leverage environment variables locally while we develop to keep our secrets separate from our code
  • tmi.js: Enables us to listen and respond to our Twitch chat

Installing like this will give you the latest versions of these dependencies. If you run into any issues with the following steps, you may be on a different version of a dependency than I am. The versions I used to get this working are:

  • algoliasearch: 4.12.1
  • dotenv: 14.3.2
  • tmi.js: 1.8.5

Step 3c: Add Scripts

We'll need a script to run to kick off our project. In your package.json, add a "start" script:

{
	"name": "twitch-chatbot",
	"version": "1.0.0",
	"description": "",
	"main": "index.js",
	"scripts": {
		"start": "node index.js"
	},

Step 3d: Set Up Your .gitignore

Afterwards, we'll tell Git to ignore our dependencies and the .env secrets file we'll be creating shortly. Create a .gitignore file and add the following:

node_modules/
.env

Step 3e: Set Up Environment Variables

Finally, we'll create a .env file to contain our secrets. We'll use the following environment variable keys:

ALGOLIA_APP_ID=
ALGOLIA_API_KEY=
ALGOLIA_INDEX_ID=
TWITCH_BOT_ACCESS_TOKEN=

Go to your Algolia index and the Twitch Token Generator and fill out the relevant secrets for each environment variables.

Step 4: Create a Twitch Bot

After three steps, our setup is done (😅) and we're ready to start building a chatbot that interacts with Twitch (🥳)!

This section and any others concerning building and deploying a Twitch chatbot could not have been possible without Colby Fayock's article about building Twitch bots! It's a really well article, and if anything in this article is confusing, please go check out Colby's!

Step 4a: Listening to the Twitch Chat

In your project, create an index.js file and add the following:

const tmi = require('tmi.js');
require('dotenv').config();

const streamerUsername = 'YourStreamingChannelUsernameHere';
const botUsername = 'YourBotUsernameHere';

const client = new tmi.Client({
	connection: {
		secure: true,
		reconnect: true
	},
	channels: [streamerUsername],
	identity: {
		username: botUsername,
		password: process.env.TWITCH_BOT_ACCESS_TOKEN
	}
});

client.connect();

client.on('message', (channel, tags, message, self) => {
	console.log(channel, tags, message);
});

Don't forget to replace the strings on lines 4 and 5 with your own accounts' usernames!

This code uses tmi.js (short for Twitch Messaging Interface) to set up a connection to your Twitch chat. Then, it establishes a callback to execute whenever a message is sent to your channel's chat — in this case, logging out the details received about that particular message.

Let's go ahead and test this! In your terminal, run npm start. Then, go to your channel's chatbox and send a few messages. Check your terminal and confirm that the message details are logged successfully.

Step 4b: Responding in the Chat

We can now read the chat successfully. Let's have our bot respond whenever anyone sends a message that starts with "!blog."

Inside your 'message' callback, add the following:

const tmi = require('tmi.js');
require('dotenv').config();

const streamerUsername = 'YourStreamingChannelUsernameHere';
const botUsername = 'YourBotUsernameHere';

const client = new tmi.Client({
	connection: {
		secure: true,
		reconnect: true
	},
	channels: [streamerUsername],
	identity: {
		username: botUsername,
		password: process.env.TWITCH_BOT_ACCESS_TOKEN
	}
});

client.connect();

client.on('message', (channel, tags, message, self) => {
	if (message.startsWith('!blog')) {
		client.say(streamerUsername, 'Howdy!');
	}
});

Restart your bot in the terminal. Go back to your Twitch chat, and send a message starting with !blog. With any luck, your bot account should reply "Howdy!"

Step 5: Search For Content with Algolia

Now that our bot can communicate in your chat, let's make sure it can take a search query and return a relevant result with Algolia!

Step 5a: Get the Search Query

Here, we'll strip the message of the !blog command at the beginning, so that we're left with just the query itself!

const tmi = require('tmi.js');
require('dotenv').config();

const streamerUsername = 'YourStreamingChannelUsernameHere';
const botUsername = 'YourBotUsernameHere';

const client = new tmi.Client({
	connection: {
		secure: true,
		reconnect: true
	},
	channels: [streamerUsername],
	identity: {
		username: botUsername,
		password: process.env.TWITCH_BOT_ACCESS_TOKEN
	}
});

client.connect();

client.on('message', (channel, tags, message, self) => {
	if (message.startsWith('!blog')) {
		const query = message.replace('!blog ', '');
	}
});

Step 5b: Set Up an Algolia Client

Next, we're going to instantiate an Algolia client that can handle search operations on our behalf.

const tmi = require('tmi.js');
const algoliaSearch = require('algoliasearch');
require('dotenv').config();

const algoliaClient = algoliaSearch(
	process.env.ALGOLIA_APP_ID,
	process.env.ALGOLIA_API_KEY
);
const index = algoliaClient.initIndex(process.env.ALGOLIA_INDEX_ID);

const streamerUsername = 'YourStreamingChannelUsernameHere';
const botUsername = 'YourBotUsernameHere';
// …

Step 5c: Search the Algolia Index

index is now a programmatic representation of our Algolia index. We can use it to search the Algolia index for the chatter's query. Let's go back down to our 'message' callback:

const tmi = require('tmi.js');
const algoliaSearch = require('algoliasearch');
require('dotenv').config();

const algoliaClient = algoliaSearch(
	process.env.ALGOLIA_APP_ID,
	process.env.ALGOLIA_API_KEY
);
const index = algoliaClient.initIndex(process.env.ALGOLIA_INDEX_ID);

const streamerUsername = 'YourStreamingChannelUsernameHere';
const botUsername = 'YourBotUsernameHere';

const client = new tmi.Client({
	connection: {
		secure: true,
		reconnect: true
	},
	channels: [streamerUsername],
	identity: {
		username: botUsername,
		password: process.env.TWITCH_BOT_ACCESS_TOKEN
	}
});

client.connect();

client.on('message', (channel, tags, message, self) => {
	if (message.startsWith('!blog')) {
		const query = message.replace('!blog ', '');

		try {
			index.search(query, {
				attributesToRetrieve: ['url', 'title'],
				hitsPerPage: 1
			}).then(({hits} => {
				console.log(hits);
			}))
		} catch (error) {
			console.error(error);
		}
	}
});

Restart your bot in the terminal again. Go to your Twitch chat, and send a !blog command, this time adding a few words that should correspond to a piece of content in your index. Check your terminal, and confirm that the bot has logged an array with one object, and that object contains a title and url of the most relevant piece of content.

If we successfully get a piece of content, we want to send it back in the chat! We've previously used client.say() to send messages, so let's do it again:

const tmi = require('tmi.js');
const algoliaSearch = require('algoliasearch');
require('dotenv').config();

const algoliaClient = algoliaSearch(
	process.env.ALGOLIA_APP_ID,
	process.env.ALGOLIA_API_KEY
);
const index = algoliaClient.initIndex(process.env.ALGOLIA_INDEX_ID);

const streamerUsername = 'YourStreamingChannelUsernameHere';
const botUsername = 'YourBotUsernameHere';

const client = new tmi.Client({
	connection: {
		secure: true,
		reconnect: true
	},
	channels: [streamerUsername],
	identity: {
		username: botUsername,
		password: process.env.TWITCH_BOT_ACCESS_TOKEN
	}
});

client.connect();

client.on('message', (channel, tags, message, self) => {
	if (message.startsWith('!blog')) {
		const query = message.replace('!blog ', '');

		try {
			index.search(query, {
				attributesToRetrieve: ['url', 'title'],
				hitsPerPage: 1
			}).then(({hits} => {
				if (!hits || hits.length === 0) return;

				const {title, url} = hits[0];
				const reply = `Check out "${title}" at ${url}!`;
				client.say(streamerUsername, reply);
			}))
		} catch (error) {
			console.error(error);
		}
	}
});

Moment of truth: let's see if it works! Restart your bot, and then return to your Twitch chat. Run a !blog command with a search query again, and confirm that the Twitch bot replies with a handy link to your content!

Step 6: Deploy the Chatbot

At this point, you have a finished Twitch chatbot! There's just one problem: it's up to you to make sure it's running whenever you're live. Let's deploy our chatbot to Heroku so it can be up 24/7 without us needing to run it ourselves.

Step 6a: Add a Procfile

Heroku runs processes in containers they call dynos. You can configure the kind of processes running in dynos with a file called Procfile.

In your code, create a file called Procfile, and add the following:

worker: npm start

This file tells Heroku that this project may want to use a worker dyno, which tends to be for long-running processes such as bots. That long-running process, in this case, is the process created by running npm start.

Step 6b: Push Code to GitHub

If you haven't already, create a remote repository for your code on GitHub. Commit your code, and push it up.

The following files should be pushed up to GitHub:

  • .gitignore
  • index.js
  • package.json
  • package-lock.json
  • Procfile

The following files should be ignored, and should not be pushed up to GitHub:

  • The node_modules/ directory
  • .env

Step 6c: Create the Heroku App

If you haven't already, create an account on Heroku. To run your worker dyno 24/7, you'll need to give Heroku your billing details to get the extra free job minutes — you won't have to pay for anything we're doing here, but you will likely need these extra free minutes.

Log into Heroku and navigate to your dashboard. Now, we'll create a new Heroku app:

  1. In the top-right corner of your dashboard, click New.
  2. In the dropdown, click Create new app.
  3. Name your app something descriptive and unique.
  4. Click Create app.
  5. In the Deployment method section of your new app's page, click the GitHub: Connect to GitHub option (If you haven't used Heroku before, you may need to authenticate Heroku with GitHub).
  6. Search for your bot's repository, and click Connect.

Step 6d: Configure the Heroku App

Now, we need to configure our Heroku app to handle deploys, leverage our secrets, and run 24/7 as a worker dyno.

  1. In the Automatic deploys section of your app's page, click the Enable Automatic Deploys button.
  2. At the bottom of this page, in the Manual deploys section, click the Deploy Branch button. This deploy will fail, since our project doesn't have its secrets yet, but it will seed the Heroku app with some information we'll need for some upcoming steps.
  3. At the top of this page, click the Settings tab.
  4. In the Config Vars section, click Reveal Config Vars.
  5. Add each key–value pair from your .env file.
  6. At the top of this page, click the Resources tab. This is where we'll tell Heroku to use a worker dyno (as opposed to the default web dyno, which receives HTTP requests).
  7. This page should list a (toggled on) "web" dyno and a (toggled off) "worker" dyno. If it doesn't yet, go back to the Deploy tab and trigger another manual deploy again, and then return to this Resources tab.
  8. Click the pencil button next to "web" to enable edit mode for your "web" dyno. Toggle the "web" dyno off, and click Confirm.
  9. Similarly, turn the "worker" dyno on.
  10. Navigate to the Deploy tab once more and manually redeploy this app.

Once your app redeploys successfully, return to your Twitch chat, and confirm that the !blog command works, even when your local bot process is shut down.

Voilà!

At this point, you have a fully functional Twitch chatbot, deployed 24/7 on Heroku, that can take an audience member's search query and return the most relevant blogpost!

There's tons of room to improve this, make it more robust, and to make it your own — but hopefully, this is a solid starting point!