Make a Node.js Twitter Bot: Bogus Definition
Tags: node, beginners, api, bot
Published: 2017-10-12 16:54:09

---
title: "Make a Node.js Twitter Bot: Bogus Definition"
published: true
description: A detailed instruction on how to create a Twitter bot with Node.js.
tags: nodejs, beginners, api, bot
cover_image: https://thepracticaldev.s3.amazonaws.com/i/mc88suo3kz1eqxzzs3n7.png
---
Wassup wassup, I'm glad you're here! We'll be discussing how to create a Twitter bot. A Twitter bot is an account that is connected to a hosted app that uses the [Twitter API](https://developer.twitter.com/en/docs/tweets/post-and-engage/overview) in order to make requests for that Twitter bot, such as tweeting, retweeting, liking, and more. Bots are a fun way to improve your coding skills through creativity, there are a bunch of different [APIs](https://en.wikipedia.org/wiki/Application_programming_interface) available that you can use to make your bot do all sorts of things. Throughout the article, I'll be referencing the setup of one of my bots, [@BogusDefinition](https://twitter.com/bogusdefinition). This bot tweets out random words with definitions that seem correct, but actually aren't.

You can find all the code used in this article on [GitHub](https://github.com/henness17/SweetCode/tree/master/004-Node-Twitter-Bot).

---
# Contents
There are three main sections to this article. Following the article will walk you through the entire setup of a Twitter bot - from setting up your bot's app with Twitter, to hosting the bot with its upgraded functionality on a live server:

- Hello, Twitter Bot
- Making the Bot Smarter
- Deploying the Bot

---
# Hello, Twitter Bot
### Creating the App
The first thing that you want to do is create the Twitter account for your bot. After you've got the account set up, you can head on over to the Twitter's [Application Management](https://apps.twitter.com/) page. You should see something like this, click on "Create New App".

![TwitterApps](https://thepracticaldev.s3.amazonaws.com/i/u13o1nqgup7bd8iqovpn.PNG)

It will prompt you for some information, such as name, description, and website. For the website, you can use your GitHub account's URL - or some other URL that you feel is valid. Check the "Developer Agreement" checkbox, and click "Create your Twitter application".

### Your Keys & Access Tokens
In order for your Node.js app to be authenticated when requesting from the Twitter API, you've got to include keys and access tokens. These keys and access tokens work a lot like a username and password, they allow your Node.js app to "login" to the Twitter account. On your Twitter app's page, you should see an "Application Settings" tab, with a "Consumer Key" section.

![ApplicationSettings](https://thepracticaldev.s3.amazonaws.com/i/kchlk9vlwfo5f483946b.PNG)

Click on the highlighted link, "manage keys and access tokens". You'll see the following page. Copy down the Consumer Key (API Key), and Consumer Secret (API Secret). Then go ahead and click "Create my access token" at the bottom. Copy the Access Token, and Access Token Secret.

![KeysAndTokens](https://thepracticaldev.s3.amazonaws.com/i/0vg21ld3ywvdw5cs2h9e.PNG)

### The Bot's First Tweet
Now that you have your keys and access tokens, you can start developing the code for your bot. Create a new folder on your computer for your bot's code. Inside of the folder, create a file called *env.js*. This file should be at the root of your project and is going to contain your environment variables, which will include the keys and access tokens you just created. :) *env.js* should look like this:
```javascript
    process.env['TWITTER_CONSUMER_KEY'] = '1mFxGkYQPEuxqbtHd55cb89';
    process.env['TWITTER_CONSUMER_SECRET'] = 'IK1ytDHhtzF4AUvh5fkmoaBC1mTvmWT18WjAhl3Y9bQv5y8o';
    process.env['TWITTER_ACCESS_TOKEN'] = '2057253019-IpiUHS9FuJqPQSQbaChVHvEza1J08QZlJYY3Z';
    process.env['TWITTER_ACCESS_TOKEN_SECRET'] = 'jHj81H3qYUOrOSXNg6RMVAPaKgMv6pz0ogs1YWeJ7pa';
```
>Be sure to use your own keys and access tokens!

The next thing you need to do is create a file called *index.js*. This file is going to be the main file for your bot, it will start your bot. First, I'll break this file down by each section, then show a shot of its entire content.

You want to set up your required Node packages at the top of the file. The packages that we are using are [Express](https://www.npmjs.com/package/express), [Request](https://www.npmjs.com/package/request), and [Twit](https://www.npmjs.com/package/twit). Express allows us to set up a minimalist web framework. Request will give us the capability to make easy HTTP requests in order to make some API calls. Last but not least, Twit will make it easy for us to access the Twitter API:

```javascript
var express = require('express');
var twit = require('twit');
var request = require('request');

```

The next bit is pretty simple, it creates the Express app and sets the port:

```javascript
var app = express();
app.set('port', process.env.PORT || 5000);
```

The next part is where you're going to use the the *env.js* variables, these are the variables that let our app receive authentication when connecting to Twitter. When initializing your bot with the Twit package, you've got to pass your keys and tokens as parameters like this:

```javascript
if(process.env.TWITTER_CONSUMER_KEY == undefined){
  require('./env.js');
}
var bot = new twit({
  consumer_key:         process.env.TWITTER_CONSUMER_KEY,
  consumer_secret:      process.env.TWITTER_CONSUMER_SECRET,
  access_token:         process.env.TWITTER_ACCESS_TOKEN,
  access_token_secret:  process.env.TWITTER_ACCESS_TOKEN_SECRET,
  timeout_ms:           60*1000,  // optional HTTP request timeout to apply to all requests. 
});
```

>The if-statement is used in order to figure out whether the application is running locally or on a server. If TWITTER_CONSUMER_KEY is defined, the app knows it's running on a server that has the definition already. If it is not defined, then the app knows it needs to include the *env.js* file that we have locally. This *env.js* file will not be a part of the GitHub repo.

Taking a look at the [Twit documentation](https://www.npmjs.com/package/twit) you can see that you're able to post a tweet quite easily. There is a `.post` function that you are able to call. Make your bot tweet "Beep boop bop!" with this block:

```javascript
bot.post('statuses/update', { status: 'Beep boop bop!' }, function(err, data, response) {
  console.log('Success!');
});
```

Almost there! You just need to tell your app what port to listen to:

```javascript
app.listen(app.get('port'), function() {
  console.log('Bot is running on port', app.get('port'));
});
```

Your entire *index.js* file should look like this:

```javascript
var express = require('express');
var twit = require('twit');
var request = require('request');

var app = express();
app.set('port', process.env.PORT || 5000);

if(process.env.TWITTER_CONSUMER_KEY == undefined){
  require('./env.js');
}
var bot = new twit({
  consumer_key:         process.env.TWITTER_CONSUMER_KEY,
  consumer_secret:      process.env.TWITTER_CONSUMER_SECRET,
  access_token:         process.env.TWITTER_ACCESS_TOKEN,
  access_token_secret:  process.env.TWITTER_ACCESS_TOKEN_SECRET,
  timeout_ms:           60*1000,  // optional HTTP request timeout to apply to all requests. 
});

bot.post('statuses/update', { status: 'Beep boop bop!' }, function(err, data, response) {
  console.log('Success!');
});

app.listen(app.get('port'), function() {
  console.log('Bot is running on port', app.get('port'));
});

```

### Running Your Bot
Before you're able to run your bot locally, you've got to `install --save` each of the Node packages that are being used. So, from your app's root folder, run the commands `npm install --save express`, `npm install --save request`, `npm install --save twit`. If you haven't run a Node.js application locally before, you can give my article [Node.js + Express Server Setup](https://dev.to/ryhenness/nodejs--express-server-setup-6ch) a glance.

Okay, cool! Your bot is ready to send its first tweet! Start up your bot with `node index.js`, and you should see "Success!" on the command-line. Check out your bot's Twitter profile, and you'll see that it tweeted "Beep boop bop!"

---
# Making the Bot Smarter
It's dope that you've got the bot tweeting, but there's more to do now! This is where the fun part starts. Your bot can't actually get smarter, but we can make people believe it is with some simple API calls. ;) Bogus Definition is a bot that tweets a random word, with a definition that sounds kind of right, but isn't. This is accomplished by requesting a random word from [Wordnik](http://www.wordnik.com/), then a rhyming word of that random word, then a definition of that rhyming word, and then finally combining the random word with the rhyming word's definition. 

> Tweet -- "randomWord: rhyming word's definition"

In this section, I'll show you how I did that.

### API Calls
The Request package is going to make it easy for us, too! Let's kick things off by tweeting out a random word. You might ask, how are we going to get a random word? Well, check out the [Wordnik API](http://developer.wordnik.com/docs.html). Under *words*, there's a *randomWord* endpoint that we can access with an HTTP request, and it will give us a random word. In order to do that, we need to use *request*. You can get the request URL by clicking the "Try it out!" button under the Wordnik endpoint section. 

![TryItOut](https://thepracticaldev.s3.amazonaws.com/i/kcnpai8k5wqpqoeddmlg.png)

Here's the randomWord request:

```javascript
randomWord = 'http://api.wordnik.com:80/v4/words.json/randomWord?hasDictionaryDef=true&minCorpusCount=0&maxCorpusCount=-1&minDictionaryCount=1&maxDictionaryCount=-1&minLength=5&maxLength=-1&api_key=' + process.env.WORDNIK_KEY;
  request(randomWord, function (error, response, body) {
    randomWord = JSON.parse(body).word;
  });
```
>I've signed up for a Wordnik account, and added my Wordnik API key to my *env.js* file as WORDNIK_KEY. You can do this too [here](http://developer.wordnik.com/). After you sign up, you can find your API key under your [account settings](http://www.wordnik.com/users/edit) at the bottom of the page.

Now inside of that `request` call, we can tweet the word that we received. You'll notice this line `JSON.parse(body).word`. We are receiving `body` as a JSON-formatted text response, and parsing it to a JSON object. This `body` is the response from the server, which contains our random word, under the `.word` attribute. If you haven't heard of JSON, or parsing it, you can learn more [here](https://www.w3schools.com/js/js_json.asp). It's very useful, so be sure you understand! After you've parsed your random word, you can tweet it by putting a `.post` call inside of your request callback (the second parameter of the request function call):

```javascript
request(randomWord, function (error, response, body) {
    // When the request finishes, this post will be called
    bot.post('statuses/update', { status: randomWord}, function(err, data, response) {
        console.log("Success!");
    });
});
```

Go ahead and run your bot with the command-line. Your *index.js* should look like this now:

```javascript
var express = require('express');
var twit = require('twit');
var request = require('request');

var app = express();
app.set('port', process.env.PORT || 5000);

var bot = new twit({
  consumer_key:         process.env.TWITTER_CONSUMER_KEY,
  consumer_secret:      process.env.TWITTER_CONSUMER_SECRET,
  access_token:         process.env.TWITTER_ACCESS_TOKEN,
  access_token_secret:  process.env.TWITTER_ACCESS_TOKEN_SECRET,
  timeout_ms:           60*1000,  // optional HTTP request timeout to apply to all requests. 
});

randomWord = 'http://api.wordnik.com:80/v4/words.json/randomWord?hasDictionaryDef=true&minCorpusCount=0&maxCorpusCount=-1&minDictionaryCount=1&maxDictionaryCount=-1&minLength=5&maxLength=-1&api_key=' + process.env.WORDNIK_KEY;
request(randomWord, function (error, response, body) {
    // When the request finishes, this post will be called
    bot.post('statuses/update', { status: randomWord}, function(err, data, response) {
        console.log("Success!");
    });
});

app.listen(app.get('port'), function() {
  console.log('Bot is running on port', app.get('port'));
});
```

### Asynchronous Problem
Okay, you've got a random word, and now want a rhyming word. So, all you need to do is make another request with the `randomWord` variable, right? Kind of, but there's a problem. Making a request is an [asynchronous call](https://en.wikipedia.org/wiki/Asynchrony_(computer_programming)). This means that when we make the request, that our request gets sent to the API, and at the same time our code continues to run. The problem with this is that our code is not waiting for the API response before continuing. So, it's possible that our code will try to make the second API request for the rhyming word before the random word is even returned, which will result in an undefined return value. How do we fix this? If you want a solution better than callbacks, check out [The Path to Conquering Async JavaScript](https://dev.to/ryhenness/the-path-to-conquering-async-javascript-1pl).

### Callbacks
There are multiple ways to solve the asynchronous problem depending on what exactly you're doing, you can check out [async functions](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function), and [promises](https://developers.google.com/web/fundamentals/primers/promises). I'm going to solve the problem using [callbacks](https://developer.mozilla.org/en-US/docs/Glossary/Callback_function). A callback function is a function passed into another function as an argument, which is then called once the outer function is done executing. 

Here's an example, first `runThisFunctionFirst` is called, and it gets passed `runThisFunctionSecond` as the parameter `callback`. So, `runThisFunctionFirst` will add 2 to the `number` parameter, and once it's finished doing that, it will call `callback` which is `runThisFunctionSecond` and pass it `plusTwo`. Then, `runThisFunctionSecond` will log the value after `runThisFunctionFirst` is done doing its work.

```javascript
function runThisFunctionSecond(plusTwo) {
  console.log('The input number + 2 is: ' + plusTwo);
}

function runThisFunctionFirst(number, callback) {
  var plusTwo = number + 2;
  callback(plusTwo); // this runs theCallbackFunction because it was passed as an arguement
}

runThisFunctionFirst(number, runThisFunctionSecond);
```
### Bogus Definition Chain

So how is this going to solve our problem? Well, we can make a callback chain similar to this with three functions: `getWord`, `getRhymingWord`, and `getRhymingDef`. These will be called just like `runThisFunctionFirst` and `runThisFunctionSecond`, but we will have a third function that would be equivalent to `runThisFunctionThird`.

Let's go ahead and create some global variables that will hold our content, and create our first function:

```javascript
// Global variables needed to create the tweet
var randomWord;
var rhymingWord;
var rhymingDef;

function getWord(callback){
  randomWord = 'http://api.wordnik.com:80/v4/words.json/randomWord?hasDictionaryDef=true&minCorpusCount=0&maxCorpusCount=-1&minDictionaryCount=1&maxDictionaryCount=-1&minLength=5&maxLength=-1&api_key=' + process.env.WORDNIK_KEY;
  request(randomWord, function (error, response, body) {
    randomWord = JSON.parse(body).word;
    callback(randomWord);
  });
};
```

Now, `getWord` will need to be passed a function `getRhymingWord` so that it can call it once it's finished making its request.
>Notice that `callback` is being called within the second parameter of the `request` call, it needs to be there because the second parameter is called once the API returns a value.

You can see `getRhymingWord` needs two parameters: one for the `randomWord` that is returned by the `request` and another for the callback function, which will be `getRhymingDef`.

```javascript
function getRhymingWord(randomWord, callback){
  rhymingWord = 'http://api.wordnik.com:80/v4/word.json/' + randomWord + '/relatedWords?useCanonical=false&relationshipTypes=rhyme&limitPerRelationshipType=10&api_key=' + process.env.WORDNIK_KEY;
    request(rhymingWord, function (error, response, body) {
    try{
      rhymingWord = JSON.parse(body)[0].words[0];
      callback(rhymingWord);
    }catch(err){
      sendTweet(); // The word didn't rhyme with anything... restart
    }
  });
};
```

Now we can add the third function:

```javascript
function getRhymingDef(rhymingWord, callback){
  rhymingDef = 'http://api.wordnik.com:80/v4/word.json/' + rhymingWord + '/definitions?limit=200&includeRelated=true&useCanonical=false&includeTags=false&api_key=' + process.env.WORDNIK_KEY;
  request(rhymingDef, function (error, response, body) {
    rhymingDef = JSON.parse(body)[0].text;
    callback(rhymingDef);
  });  
};
```

We still aren't calling the functions yet, so let's create a function called `sendTweet` that will be called once our bot starts up. This function will implement the callback chain. It will work just like the example that we used. First it calls `getWord` and passes `nowGetRhymingWord` as the callback function. Then inside of `getWord` it calls `callback` which is `nowGetRhymingWord` and passes it `nowGetRhymingDef`, and so on:

```javascript
var sendTweet = function(){
  getWord(nowGetRhymingWord);
  function nowGetRhymingWord(randomWord){
    getRhymingWord(randomWord, nowGetRhymingDef);
    function nowGetRhymingDef(rhymingWord){
      getRhymingDef(rhymingWord, nowReturnTweet);
      function nowReturnTweet(definition){
        var wordAndDef = capitalizeFirstLetter(randomWord) + ": " + rhymingDef;
        bot.post('statuses/update', { status: wordAndDef }, function(err, data, response) {
          console.log("Success!");
        });
      }
    }
  }
}
```
>Notice there is a helper function `capitalizeFirstLetter`. I added this because the response from Wordnik is not capitalized when getting a word. The function looks like this:

```javascript
function capitalizeFirstLetter(string) {
    return string.charAt(0).toUpperCase() + string.slice(1);
}
```

Okay... but `sendTweet` still isn't being called! Let's call it, and set an interval for it to be called:

```javascript
// Send tweet every 28 minutes, and on start
setInterval(function() {
  sendTweet();
}, 1700000);
sendTweet();
```
So your entire *index.js* file should now look like this:

```javascript
var express = require('express');
var twit = require('twit');
var request = require('request');

var app = express();
app.set('port', process.env.PORT || 5000);

if(process.env.TWITTER_CONSUMER_KEY == undefined){
  require('./env.js');
}

var bot = new twit({
  consumer_key:         process.env.TWITTER_CONSUMER_KEY,
  consumer_secret:      process.env.TWITTER_CONSUMER_SECRET,
  access_token:         process.env.TWITTER_ACCESS_TOKEN,
  access_token_secret:  process.env.TWITTER_ACCESS_TOKEN_SECRET,
  timeout_ms:           60*1000,  // optional HTTP request timeout to apply to all requests. 
})
///////////////////////////////////

// Global variables needed to create the tweet
var randomWord;
var rhymingWord;
var rhymingDef;

// Callback chain
var sendTweet = function(){
  getWord(nowGetRhymingWord);
  function nowGetRhymingWord(randomWord){
    getRhymingWord(randomWord, nowGetRhymingDef);
    function nowGetRhymingDef(rhymingWord){
      getRhymingDef(rhymingWord, nowReturnTweet);
      function nowReturnTweet(definition){
        var wordAndDef = capitalizeFirstLetter(randomWord) + ": " + rhymingDef;
        bot.post('statuses/update', { status: wordAndDef }, function(err, data, response) {
          console.log("Success!");
        });
      }
    }
  }
}
 
// Send tweet every 28 minutes, and on start
setInterval(function() {
  sendTweet();
}, 1700000);
sendTweet();

function getWord(callback){
  randomWord = 'http://api.wordnik.com:80/v4/words.json/randomWord?hasDictionaryDef=true&minCorpusCount=0&maxCorpusCount=-1&minDictionaryCount=1&maxDictionaryCount=-1&minLength=5&maxLength=-1&api_key=' + process.env.WORDNIK_KEY;
  request(randomWord, function (error, response, body) {
    randomWord = JSON.parse(body).word;
    callback(randomWord);
  });
};

function getRhymingWord(randomWord, callback){
  rhymingWord = 'http://api.wordnik.com:80/v4/word.json/' + randomWord + '/relatedWords?useCanonical=false&relationshipTypes=rhyme&limitPerRelationshipType=10&api_key=' + process.env.WORDNIK_KEY;
    request(rhymingWord, function (error, response, body) {
    try{
      rhymingWord = JSON.parse(body)[0].words[0];
      callback(rhymingWord);
    }catch(err){
      sendTweet(); // The word didn't rhyme with anything... restart
    }
  });
};

function getRhymingDef(rhymingWord, callback){
  rhymingDef = 'http://api.wordnik.com:80/v4/word.json/' + rhymingWord + '/definitions?limit=200&includeRelated=true&useCanonical=false&includeTags=false&api_key=' + process.env.WORDNIK_KEY;
  request(rhymingDef, function (error, response, body) {
    rhymingDef = JSON.parse(body)[0].text;
    callback(rhymingDef);
  });  
};

// Helper function for to capitalize the random word
function capitalizeFirstLetter(string) {
    return string.charAt(0).toUpperCase() + string.slice(1);
}

// Tells the Express app to listen to a port
app.listen(app.get('port'), function() {
  console.log('Node app is running on port', app.get('port'));
});
```
---
# Deploying the Bot
Hey hey! Your bot can now run from the command-line. But you don't want it to have to run on the command-line for its entire life, so let's deploy it to Heroku. If you haven't deployed a Heroku app before, check out my article [Auto-Deploy a Node.js Server: Heroku + GitHub](https://dev.to/ryhenness/auto-deploy-a-nodejs-server-heroku--github-em). It will give you all of the steps you need to get the code connected, but you will still need to get *config variables* going on your Heroku app. I'll talk about that next here.
>When deploying your app, don't include *env.js* in your GitHub repo.

### Heroku Config Vars
Remember making the *env.js* file? Well, Heroku doesn't have it because it's not included in your GitHub repo. We do this because we don't want the entire internet knowing your Twitter app's authentication information! If someone were to find those keys and tokens, they could go ahead and start using your bot for their own purposes. But there's good news, under your Heroku app's "Settings" tab, there is a "Config Variables" section. Click on "Reveal Config Vars", and here you can add all of the information that is inside your *env.js* file, and it will stay hidden within Heroku, out of sight from the rest of the internet. :)

### Sleepy Bot Problem
Heroku will put your app to sleep if it hasn't been pinged in a while. That means, if the address of your Heroku app isn't visited, that your bot won't work anymore. You're in luck though, under the Heroku app's "Resources" tab, there is an "Add-ons" section. Here you can search for and add "Heroku Scheduler" to your app. 

![Scheduler](https://thepracticaldev.s3.amazonaws.com/i/e9syv7337f3qoq37zqsy.png)

This allows you to schedule command-line calls of your choice. With this, you can have the scheduler call `node index.js` every day, hour, or 10 minutes to make your bot stay awake and tweet!

---
# Review
Congratulations! You've successfully built and deployed a Node.js Twitter bot! You did this by using multiple Node packages, making multiple API calls using a callback chain, and deploying to Heroku. Now it's time to get creative with some other APIs to make your bot do different things. [Here's a fun list](http://www.tracyctran.com/code/2016/06/21/10-fun-apis-to-inspire-your-next-project/) of APIs that you can check out to inspire your bot's next function. :) Please comment below if you have any comments, questions, or concerns! I'll be happy to reply.

---
# Other Twitter Bots
I've created some other bots, you can find them all at:
- [Bogus Definition](https://twitter.com/bogusdefinition): Tweets bogus definitions.
- [Just Doing Junk](https://twitter.com/justdoingjunk): Retweets people who are just doing things.
- [Spots Of Midnight](https://twitter.com/SpotsOfMidnight): Tweets the spots of midnight around the world.