Create a Twitch Chat Bot with Bitmart Integration

Kyle MacQuin
Geek Culture
Published in
6 min readJun 17, 2021

--

Photo by Pierre Borthiry on Unsplash

Introduction

In this article, we investigate how to create a Twitch chatbot using node.js and tmi.js. We cover both application and theory to understand the underlying technologies while learning how to extend the examples provided into other projects.

Project Setup & Key Generation

If you haven’t download and install Node.js for your OS of choice. Create an empty folder on your system called ‘project’ or use the naming of your choice and run npm init -y to create a clean slate for your Node project.

We proceed by creating twitchscript.js in the root directory of the project directory along with a file named .env for storing environment variables. To create this file:

Windows: create a blank file in the root directory of your project and rename it .env

Mac: navigate to the root directory in terminal using the ‘cd’ command, followed by,

$ touch .env

Command + Shift + . will allow the file to appear in Finder. The basic file structure should look as follows:

project/
.env
twitchscript.js
node_modules

We will be appending three lines of text to.env, being username, password, and channels.

Username is the display name of the bot account, password corresponds to the API secret key, and channels is a comma-separated list of channels to be interacted with by the client. https://twitchapps.com/tmi/ generates a token for your account. Note that ‘oauth: ’ is prepended to the key in creds.txt. It is easier in most cases to create an additional account to easily differentiate between developer and personal preferences.

Your file should look something like:

username=’username’
password=’password’
channels=’channels’

In my case:

username=tbot8001
password=oauth:KEY
channel=tbot8001

Dependencies

Node.js is a common javascript runtime with a range of use-cases. Described on the project’s homepage: ‘As an asynchronous event-driven JavaScript runtime, Node.js is designed to build scalable network applications.’ The framework tmi.js provides a bridge between Twitch’s IRC API and Node, making it easy to declare multiple event listeners for a range of chat actions.

This guide uses tmi’s client class to listen for chat messages to return an integer between one and six when ‘!dice’ is typed into chat. Node.js executes functions to qualify which actions to take based on chat input. Lastly, we use dotenv for managing environment variables we want hidden, like our API key. If not done already, be sure to import dotenv, node-fetch, and tmi by running

npm install dotenv node-fetch tmi

Note that these commands should be run in the root directory of the project. To install these packages globally so that they may be used in any node project, append — global to the end of the command, e.g. npm install package_name — global.

Authentication

After all that background, we can get to the functionality portion of the tutorial. Open twitchbot.js and begin by writing the below.

//import dependencies
const tmi = require('tmi.js');
const fetch = require("node-fetch");
require('dotenv').config();

//setting up tmi client connection
const client = new tmi.Client({
connection: {
secure: true,
reconnect: true
},
identity: {
username: process.env.username,
password: process.env.password
},
channels: [process.env.channel]
});
client.connect();

This client abstracts the work of authentication and connection to Twitch, with dotenv pulling your credentials from the env object in the current Node process.

Chat Events

Chat events trigger an anonymous function that outputs information in response to chat. The function is written in arrow notation; if you are unfamiliar you can check out a quick reference here. Any standard function notation will suffice, so feel free to use the convention you are most familiar with.

As a debugging feature, we add the following code snippet to echo all chat messages to the console in which Node is triggered. Moreover, the function handles all instances of chat events going forward.

//handles all events caused by chat input
client.on('chat', (channel, tags, message) => {
//log all chat messages to console
console.log(`${tags['display-name']}: ${message}`);
}

The first interactive example we implement is a dice function. Any chat message matching the string, ‘!dice’ exactly will trigger tbot8001 to post a number between one and six. Write the below after the console.log call above.

//logic for dice rolls. Returns a random integer from 1-6
if (message == '!dice') {
client.say(channel, (1 + Math.floor(Math.random() * 6)).toString());
}

By this point, twitchscript.js should look like:

//import dependencies
const tmi = require('tmi.js');
const fetch = require("node-fetch");
require('dotenv').config();

//setting up tmi client connection

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

//handles all events caused by chat input
client.on('chat', (channel, tags, message) => {
//log all chat messages to console
console.log(`${tags['display-name']}: ${message}`);

//logic for dice rolls. Returns a random integer from 1-6
if (message == '!dice') {
client.say(channel, (1 + Math.floor(Math.random() * 6)).toString());
}
})

To ensure things are going as we’ve planned, navigate to the root directory of your project in your preferred CLI and run node twitchscript.js. Then, navigate to your Twitch chat and type !dice. You should see something like this:

Congratulations for making it this far, you’ve created your first functioning Twitch bot! Before continuing to Bitmart integration, here is another useful function for your bot:

if (message == '!clear' and username == 'tbot8001') {
client.clear(channel);
}

Just be sure to change ‘tbot8001’ to your username.

Bitmart Integration

Let’s implement a function that communicates with Bitmart’s public Spot Trading API. Bitmart is a popular cryptocurrency exchange offering their API for free to both users and non-users alike. The below code snippet contains an abundance of information to break down but provides the full functionality for users to be able to type ‘!price CUR’ to obtain an asset price.

//logic for returning a specific price on command
if (message.startsWith('!price ')) {
ccy = message.substring(7)
fetch(`https://api-cloud.bitmart.com/spot/v1/ticker?symbol=${ccy}`)
.then(response => response.json())
.then((data) => {
pair_info = data['data']['tickers'][0]
client.say(client.channels[0], `${pair_info['symbol']} | ${pair_info['last_price']}`);
})
.catch((err) => {
client.say(client.channels[0], `Invalid input.`)
console.log('Fetch Error :-S', err);
})
}

Simply enough, we scan for messages beginning with ‘!price ’ in order to trigger our decision tree. The variable ccy reads the substring following ‘!price ’ which should be the name of a cryptocurrency supported by the platform. Finally, we implement our connection to Bitmart.

Before jumping into the fetch portion where we ping Bitmart’s servers, we define a promise object. Per MDN Web Docs:

‘The Promise object represents the eventual completion (or failure) of an asynchronous operation and its resulting value.’

Moreover,

A Promise is in one of these states:

  • pending: initial state, neither fulfilled nor rejected.
  • fulfilled: meaning that the operation was completed successfully.
  • rejected: meaning that the operation failed.

In our code the call to fetch returns a promise which when resolved either returns the price of the asset, or a failure message to both console and chat. The reason we must use fetch is because the data is not returned from some function in our code, but from an external server which will not return data immediately, or reliably.

Testing this function we see:

Conclusion

We now have a functioning Twitch bot with connectivity to third party services. This tutorial should serve as a quick introduction to creating a chatbot and API interaction which can be extended to other platforms and use cases. By the end of this guide, your code should look something like:

//import dependencies
const tmi = require('tmi.js');
const fetch = require("node-fetch");
require('dotenv').config();

//setting up tmi client connection

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

//handles all events caused by chat input
client.on('chat', (channel, tags, message) => {
//log all chat messages to console
console.log(`${tags['display-name']}: ${message}`);

//logic for dice rolls. Returns a random integer from 1-6
if (message == '!dice') {
client.say(channel, (1 + Math.floor(Math.random() * 6)).toString());
}

if (message == '!clear' and username == 'tbot8001') {
client.clear(channel);
}

//logic for returning a specific price on command
if (message.startsWith('!price ')) {
ccy = message.substring(7)
fetch(`https://api-cloud.bitmart.com/spot/v1/ticker?symbol=${ccy}`)
.then(response => response.json())
.then((data) => {
pair_info = data['data']['tickers'][0]
client.say(client.channels[0], `${pair_info['symbol']} | ${pair_info['last_price']}`);
})
.catch((err) => {
client.say(client.channels[0], `Invalid input.`)
console.log('Fetch Error: ', err);
})

}
});

Thanks for reading, and I will see you next time.

--

--

Kyle MacQuin
Geek Culture

Writing about tech and words — and sometimes the combination of the two.