DEV Community

Cover image for My 1st Chatbot, what we built...
Gerald Hirsch
Gerald Hirsch

Posted on • Updated on

My 1st Chatbot, what we built...

Well, here goes, my first ever blog post. Like really, I have never posted an original blog post in my 43 years on this planet. It was actually through a Twitch streamer from the LiveCoders Team, LaylaCodesIt who encouraged me to post about my recent little Twilio-Twitch chat bot.

It started out with having something to stream on my CodeHustle channel where I live code. So we began with going through the documentation on Twitch to see how to make a chat bot. It walks you through how to make a simple NodeJS app that will take in a command sent from your channels chat using a special trigger command i.e. !dice.

Code from Twitch IRC bot tutorial

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

// Define configuration options
const opts = {
  identity: {
    username: <BOT_USERNAME>,
    password: <OAUTH_TOKEN>
  },
  channels: [
    <CHANNEL_NAME>
  ]
};

// Create a client with our options
const client = new tmi.client(opts);

// Register our event handlers (defined below)
client.on('message', onMessageHandler);
client.on('connected', onConnectedHandler);

// Connect to Twitch:
client.connect();

// Called every time a message comes in
function onMessageHandler (target, context, msg, self) {
  if (self) { return; } // Ignore messages from the bot

  // Remove whitespace from chat message
  const commandName = msg.trim();

  // If the command is known, let's execute it
  if (commandName === '!dice') {
    const num = rollDice();
    client.say(target, `You rolled a ${num}`);
    console.log(`* Executed ${commandName} command`);
  } else {
    console.log(`* Unknown command ${commandName}`);
  }
}

// Function called when the "dice" command is issued
function rollDice () {
  const sides = 6;
  return Math.floor(Math.random() * sides) + 1;
}

// Called every time the bot connects to Twitch chat
function onConnectedHandler (addr, port) {
  console.log(`* Connected to ${addr}:${port}`);
}

With this small amount of code we were able to send a chat command and get a response and off to the races we went. So with that working and feeling stocked that we had accomplished this we were ready to expand on it. I had remembered working through a tutorial at an earlier time leveraging Twilio and thought, hmmm 🤔 I wonder if I could send messages from the chat to my phone with Twilio's SMS API? So off we went to go see. First stop Twilio Docs, second stop Twilio Docs for Node because we 💛 JS! We already had running code, so how do we integrate these together? Well this is what the docs gives us for sending an SMS message. So let's think about this...

Code sample from Twilio docs

const accountSid = 'ACXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX';
const authToken = 'your_auth_token';
const client = require('twilio')(accountSid, authToken);

client.messages
  .create({
     body: 'This is the ship that made the Kessel Run in fourteen parsecs?',
     from: '+15017122661',
     to: '+15558675310'
   })
  .then(message => console.log(message.sid));

Right off the bat I am thinking, we need to use some environment variables for those secrets up there. Let's use the ever famous dotenv NPM package to set some up in our local project. The idea here is to have your secrets in a config file for development storing configuration in the environment separate from code which follows, The Twelve-Factor App methodology. So our new code now looks like this.

require('dotenv').config();

const accountSid = process.env.TWILIO_ID;
const authToken = process.env.TWILIO_TOKEN;
const client = require('twilio')(accountSid, authToken);

client.messages
  .create({
     body: 'This is the ship that made the Kessel Run in fourteen parsecs?',
     from: '+15017122661',
     to: '+15558675310'
   })
  .then(message => console.log(message.sid));

While we are at it, let's go ahead and make some environment variables for our Twitch bot code as well. So the top portion of our Twitch bot code now looks like this.

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

// Define configuration options
const opts = {
  identity: {
    username: process.env.BOT_USERNAME,
    password: process.env.OAUTH_TOKEN,
  },
  channels: [process.env.CHANNEL_NAME],
};
// code hidden below this line

Cool, but wait, where do we keep those secrets at? Oh yeah, forgot that small detail 🤦. Let's fix that! We create a file in our root of our application with a special name called, ".env". Yep that's correct! You will start to see other files like this pop up as you get more into developing with NodeJS. Another example of something like this would be a .gitignore, which you put directories, files etc. into that, you guessed it, ignores those when pushing to Github. So what does our .env look like? Well, I'm going to show you, but note none of the info is real. Darn I know how bad you wanted to snag my creds for these platforms (sarcasm inferred)! Let's take a look.

.env file example

BOT_USERNAME=jd_hirsch
OAUTH_TOKEN=nr878r34v8ryb3rycyr93478ry3ryc238ry
CHANNEL_NAME=codehustle
TWILIO_TOKEN=nff9jf84h3r873yr83yy3823d2drtyyv3cn3rcn
TWILIO_ID=AJFIE94727HD342F
TO=+2102568766
FROM=+19155606454

Here is a small explanation of each property.

BOT_USERNAME= (The account (username) that the chatbot uses to send chat messages. This can be your Twitch account. Alternately, many developers choose to create a second Twitch account for their bot, so it's clear from whom the messages originate.)

OAUTH_TOKEN= (The token to authenticate your chatbot with Twitch's servers. Generate this with https://twitchapps.com/tmi/ (a Twitch community-driven wrapper around the Twitch API), while logged in to your chatbot account. The token will be an alphanumeric string.)

CHANNEL_NAME= (The Twitch channel name where you want to run the bot. Usually this is your main Twitch account.)

TWILIO_TOKEN= (Token is from Twilio console after account creation and phone number selection.)

TWILIO_ID= (Account ID from Twilio console)

TO= (Assigned or selected Twilio phone number)

FROM= (Registered and validated phone number with Twilio. Typically your mobile number.)

With that we now needed to merge these together and update some code. We created an send-sms.js file to do just that. So let's start at the top with our imports and config's.

require('dotenv').config();
const accountSid = process.env.TWILIO_ID;
const authToken = process.env.TWILIO_TOKEN;
const twilioClient = require('twilio')(accountSid, authToken);
const tmi = require('tmi.js');

Here we have all the required packages and config to implement our bot. A lot of the code remains the same so I am going to just point out the areas that I changed from the base code from the Twilio bot. I will then show it all together. So I move down to our, "onMessageHandler" from the original code and update it to handle the send message based on a chat command. Previously we were looking for, "!dice", but we will be matching on, "!sms". Let's see what this function looks like.

// Called every time a message comes in
function onMessageHandler(target, context, msg, self) {
  if (self) {
    return;
  } // Ignore messages from the bot

  // Remove whitespace from chat message
  const chatMessage = msg.substr(4);
  const userName = context.username;
  const commandName = msg.substr(0, 4);
  console.log(chatMessage);
  // If the command is known, let's execute it
  if (commandName === '!sms') {
    const smsMsg = sendSMS(chatMessage, userName);
    client.say(target, `Your SMS "${chatMessage}" was sent to @CodeHustle!`);
    console.log(`* Executed ${commandName} command`);
  } else {
    console.log(`* Unknown command ${commandName}`);
  }
}

So we needed to add a way to extrapolate the message away from the command. There is probably a better way, feel free to comment, but this is what we came up with on the fly. We use the substr() javascript method to create a variable to give us all the text after the first 4 characters in the passed in string. We then use the substr method once again to store the first 4 character of the passed in string which is our command we are looking for. We then run a quick test on the commandName variable to see if it matches, "!sms" and if so continue with sending a message. This is done within the if by calling a function we created called, "sendSMS". Here is what that function looks like.

// Function called when the "sms" command is issued
function sendSMS(chatMessage, userName) {
  twilioClient.messages
    .create({
      body: `https://twitch.tv/${userName} sent: ${chatMessage}`,
      from: process.env.FROM,
      to: process.env.TO,
    })
    .then((message) => console.log(message.body));
}

Here you can see our sendSMS function takes two parameters, the message to be sent and who from chat is sending it. We then call the twilioClient create function to formulate our message object. I am creating a string here in the body that will send me the chat username so I can link to their account directly from the SMS message along with the message itself. Bam! If all went well a message will get sent to my mobile.

SMS Success

SUCCESS! Yes, we did it. And was not event that bad. Who knows what's next, the possibilities are endless. Keep up the #CodeHustle 🕺! #BuildUpDevs 🧑🏿‍💻👩‍💻👨🏾‍💻🧑‍💻👩🏻‍💻🧑🏾‍💻👩🏿‍💻👨🏻‍💻👩🏽‍💻

Git repo: twitch-twilio-sms

Twitch channel: CodeHustle

Top comments (0)