CodeNewbie Community

Cover image for A Node.js Twitter bot guide for beginners
Pj Metz
Pj Metz

Posted on

A Node.js Twitter bot guide for beginners

“TWITTER BOTS ARE RUINING TWITTER” is a very accurate, very true statement. I’ve had countless people instinctively flinch when I tell them I make Twitter bots. Bots are often responsible for spreading disinformation, hurting artists’ merch sales, and basically ruining the human experience of the bird site.

So why are we making one? To make a nice bot, of course!

YOUR bot isn’t going to sow discord, or do anything malicious, right?

Answer me.

David Bowie looking at the camera with a straight unimpressed face.

If you won't talk to me, perhaps you'll talk to David.

Ok, you’re good. So, let’s talk about how to do this and why. Twitter bots are a great way to practice using an API and developing some coding skills along the way. You can usually get the bot going for under 100 lines of code, so it’s especially good for beginners. In this tutorial, I’ll show you how to use Node.js and a few npms to have your bot tweeting positivity and fun to counteract all those other nasty bots.

We’ll be using:
Node.js
Twit
node-schedule
DotEnv
Twitter
Twitter Developer portal
Heroku
GitLab
Gitpod through GitLab

Setting up a Twitter Account

This is where you decide what you want your account to be named and what it will do automatically. This bot I'm showing you code for will tweet a link to a video game soundtrack once a week (pulled randomly from an array of links) and will respond to a user who asks for a link with a random link from the same array. We’ll call our bot @SNESSoundtracks. Go to Twitter and follow the instructions for making a brand new account for your bot.

Twitter Developer Portal

The developer portal is where you register your new account to be able to use the Twitter API. This is an important step because it unlocks the account's ability to tweet by using the API with code instead of manually tweeting. You know, the whole point of a bot?

So here’s the process: get logged in to your bot’s twitter and you’ll head to the developer page. Once there, you’ll see a link near the profile picture on the top right that says “apply.”

Click that, and it’ll take you to a page where you apply to have your Twitter profile become an active developer profile. Follow the instructions on screen until you have an authenticated developer account. Once that’s done, create the app and fill out the descriptions for your app. Really, all you have to say is that you’re doing it as a hobby and it’s just for learning to code or practicing a bot. You’re not giving info to the government or really consuming streaming data for analytical purposes. When you’ve finished, the developer portal should look like this.

The Twitter Developer page

The next thing to do, once the app is created, is to go into the settings and make sure that the app can read and write. This is a little more complicated these days, but here's the new process. Go into the settings for the app and scroll down till you see "User Authentication Settings." Head into that and turn on the toggle for Oauth 1.0a. Check the Read and Write box underneath that, and fill in only Calback URI and Website URL with…

literally whatever.

Indiana Janes saying, “I don’t know I’m this up as I go along!"

If it’s good enough for Indy, it’s good enough for our bot

It actually doesn't matter what you put there because all we need is read and write access for our keys, there is no callback URL, but they require one. I know someone who just puts example.com in that space so they can change the Auth setting and get their bot to write. Save these settings at the bottom.

The Keys and the Code and the Node Package Manager

Ok, so you’ve created the app, verified your email, and now you’re reaching for the keys. I’m gonna stop you right there.

Eddie Murphy from Coming to America saying "HALT!"

HALT

It’s easier to get the keys after you have somewhere to put them. We’re gonna be keeping them in a dotenv file for safekeeping from people who would try to get access to your keys. We'll come back to the keys later. For now, let's set up GitLab

Getting Started with GitLab

For today, we're using GitLab, the DevOps platform. This article has a quick little getting started guide to get your repo up and running, but there's more details in this video I made going over some introductory stuff. The sequel to it is here. These videos go through the same lessons GitLab provides for you when you sign up. Now, since this is for beginners, the important thing here is your repository, a place to store your code online. This uses git, a type of source control that is an important part of every developer's knowledge.

Sign up for a new account and follow the instructions until you get to the main page. Use the blue button to create a new project, then select "blank project." Fill in the information on this next page and make sure that "Initialize with a readme" is selected. This is an important step so you can immediately clone your repo.

A screenshot of the create project page. The fields are filled in and the readme box is checked.

Once you've created the project, you'll be on the main repository page where only a readme exists. I recommend writing a description of your bot in the readme: what you used to make it, what it does, and any other information that will help people learn about you or the bot. You can do this by clicking the readme and clicking the edit button on that page. The readme is written in markdown, a specialized markup language that allows for changing aspects of the visual presentation of the text. For more on markdown, go here.

The readme file screen with the edit button highlighted

It’s almost code time…

Doing the work RIGHT IN YOUR BROWSER

This is the part of the article where we talk about Gitpod. Gitpod is a full instance of VSCode right in your browser (Chrome, Edge, Bravo, Safari, Firefox). This means you don’t need to move anything local, because GitLab and Gitpod already work together to give you a great development environment right in your browser. To use Gitpod, follow these instructions to make sure the Gitpod option is turned on. Right in your repository’s main page, there is a dropdown menu that may say Web IDE or Gitpod.

showing where the gitpod dropdown menu is

Ensure it says Gitpod then click it. After a few seconds, Gitpod will open in your browser and you’ll see the readme in the left column. Now you can start creating files!

Major Key (Twitter keys)

Remember the Twitter keys from earlier? We’re gonna create some variables for them. You’ll have four keys you need in order to connect your app to the code you’ve written. Create a file in your project called .env (sometimes written as dotenv). Inside, add four variables: access_token, access_token_secret, consumer_secret, consumer_key.

The consumer keys will pair with the API keys from the Developer Twitter site. NOTE: You will need to regenerate all four of your keys before using them. This is because you changed the permissions earlier. So when you go to the keys page on the twitter developer page (click the key icon in your settings) there will be a button to regenerate them. Do so when you’re ready to put them in the .env file.

Something important about a dotenv file, you won’t need quotes around the values of your variables, as long as there aren’t spaces. Make sure to write it like this: consumer_key=whateveryourkeyis. Your dotenv file is going to be your one source of truth for the keys. The config file will point to the .env values, and because you created a gitignore, you won’t upload your keys to GitLab.

Up next, we’re going to install all the npm packages we need, so you'l need to enter some commands into your terminal in Gitpod. Note: to get the terminal open, there should be a tab on the window called "Terminal". Just in case, here's the docs on the terminal from VSCode and gitpod

terminal location on gitpod

Input the following commands one by one into your terminal. In the future, you may learn quicker ways to do this, but for now I recommend doing it one by one so you can see what the process looks like.

"Npm init -y"

"Npm install node"

"Npm install node-schedule"

"Npm install twit"

"Npm install dotenv"
Enter fullscreen mode Exit fullscreen mode

This installs all the dependencies we’ll need to be able to have a functional Twitter bot that can read the API, post on a schedule, use the .env file, and use node.js. We have one more thing to adjust, and that’s to create some scripts inside the package.json file code block. Open it and ensure the scripts section looks like this.

scripts:{
      "start": "node index.js",
      "develop": "NODE_ENV=develop node index.js"
      }
Enter fullscreen mode Exit fullscreen mode


That index.js file doesn’t exist yet, so you'll need to create it and name it whatever you like. There’s an icon that looks like a rectangle with a plus sign on it. That’s the add file icon. Click it and name your file. You can call this file whatever you want instead of index.js as long as you remember what you called it (I called mine snes.js).

Add a new file button with arrows pointing at it near the top of a VSCode app
Go ahead and make a config.js at the same time and input the following into it.

module.exports = { 
    consumer_key: process.env.consumer_key, 
    consumer_secret: process.env.consumer_secret,
    access_token: process.env.access_token,
    access_token_secret: process.env.access_token_secret,
};
Enter fullscreen mode Exit fullscreen mode

Let’s write some code.

A young Julia Stiles saying "Do you know anything about hackers?

You best start believing in Hackers, Mrs. Turner. You are one

Code It Up.

console.log("SNES Soundtracks booting up");

//making sure npm run develop works
if (process.env.NODE_ENV === "develop") {
    require("dotenv").config();
};

//rules for node-schedule
var schedule = require("node-schedule");
var rule = new schedule.RecurrenceRule();
  rule.dayOfWeek = 1,
  rule.hour = 10;
  rule.tz = "Etc/GMT+4";

//array to pull soundtracks from
var soundtrackArray = [ "an array of youtube video URLs"];
var soundtrackArrayLength = soundtrackArray.length;
var soundtrackArrayElement = Math.floor(Math.random() * soundtrackArrayLength);

Enter fullscreen mode Exit fullscreen mode

At the beginning, I log a start up message to the console just so I know it’s running. Next is an if statement to use the dotenv when the node environment is ‘develop’, which is handled in the scripts of the json file from earlier. We set up a few variables for the node-schedule so the bot can tweet on a set day and time. I pulled this directly from the docs for node-schedule. Basically, it will tweet every Monday at 10 am Eastern Daylight Savings Time. Finally, I set up an array for the bot to pull from at random with the last three lines of this section. I removed the URLs in the interest of length.

EDIT: I DISCOVERED A BUG HERE, and I'm leaving it in to show that bugs happen all the time and that what's important is watching your code.

Chris Farley stressing out and saying "Oh god they're gonna know I'm dumb"

I assumed you already knew...

By running the random number at this point in the code, I create a problem whereby the random number is selected at runtime rather than every time the function runs. This meant the bot was tweeting the same soundtrack every time it tweeted. To fix this, I moved var soundtrackArrayElement = Math.floor(Math.random() * soundtrackArrayLength);

inside each the two functions, pressStart and pressSelect. This allows the number to be chosen randomly upon the function running, rather than when the bot is first run. Now, back to the code!

// Create a Twitter object to connect to Twitter API
var Twit = require('twit');

// Pulling keys from another file
var config = require('./config.js');
// Making a Twit object for connection to the API
var T = new Twit(config);

// Setting up a user stream
var stream = T.stream('statuses/filter', { track: '@SnesSoundtracks' });

// Now looking for tweet events
// See: https://dev.Twitter.com/streaming/userstreams
stream.on('tweet', pressStart);
Enter fullscreen mode Exit fullscreen mode

This is where we start using Twit. We create an object called Twit that requires the twit code, and then pass a configure that requires the configuration file into the object. We then use new to create “T”, an instance of the object from before. From now on, when we want to use something from Twit, we simply use T.whatever in order to call up the property, field, or method we need from their library. We set up a stream so that we are monitoring specifically @SnesSoundtracks while the code is running. Finally, we create an event listener with stream.on, and use a string parameter to name it, and input a function we’ve called, “pressStart”. pressStart is defined in the next set of code.

function pressStart(tweet) {

    var id = tweet.id_str;
    var text = tweet.text;
    var name = tweet.user.screen_name;

    let regex = /(please)/gi;


    let playerOne = text.match(regex) || [];
    let playerTwo = playerOne.length > 0;

    //this helps with errors, but isn't really best practice. It's ok, we're new. This let lets you see if the regex matched and if playerTwo is true or false
    console.log(playerOne);
    console.log(playerTwo);


    // checks text of tweet for mention of SNESSoundtracks
    if (text.includes('@SnesSoundtracks') && playerTwo === true) {

        // Start a reply back to the sender
        var replyText = ("@" + name + " Here's your soundtrack!" + soundtrackArray[soundtrackArrayElement]);

        // Post that tweet
        T.post('statuses/update', { status: replyText, in_reply_to_status_id: id }, gameOver);

    } else {
        console.log("uh-uh-uh, they didn't say the magic word.");
    };

    function gameOver(err, reply) {
        if (err) {
            console.log(err.message);
            console.log("Game Over");
        } else {
            console.log('Tweeted: ' + reply.text);
        }
    };
}
Enter fullscreen mode Exit fullscreen mode

pressStart contains a few local variables, a bit of logic, and a final function that must be included in the T.post method. You can use an unnamed function there and it will do the same thing, but I went ahead and wrote on separately for readability. Essentially, the function gameOver gives us a chance to log an error if it occurs or to log the tweet that was sent out.

pressStart takes “tweet” as a parameter. This is the tweet that another user writes that tags SnesSoundtracks. That tweet has tons of data attached to it, data that Twit helps us parse through. The first three variables are the id of the tweet, the text of the tweet, and the username of the person who wrote the tweet. We will need those three in order to respond accurately as a comment to the original tweet by the other user.

Up next is a regex for whatever word you want to activate the bot to reply. I chose “please,” so that as long as the user is polite, they’ll get a random soundtrack.

Two gentlemen in black and white endlessly tipping their hats to each other

Good day to you, No good day to you, I say good day to you, sir, a very good day to you...

The regex has “i” and “g” at the end so it ignores capitalization and checks globally for the word please. playerOne is a variable that can either be an empty array or will use .match to create an array with one element, the word “please”. playerTwo is a boolean that verifies whether the array playerOne has an element or not.

The logic dictates that the tweet text contains the bot’s name and an array of at least one element was passed into playerTwo. If both of these come back as true, then we proceed to an area where the variable replyText is created, which includes a random element of the array, as well as the username of the person being replied to and a short message. replyText is passed into an object that contains two properties: status and in_reply_to_status_id. Status is the actual text to be posted to the tweet, in our case the variable replyText is our status. In_reply_to_status_id is defined as id, which is a variable from the beginning of the pressStart function. Id is a unique identifier of a tweet from Twitter’s API. This allows Twit to identify which tweet the bot will reply to as a comment. Finally, the else statement at the end will log a quote from Jurassic Park to the console if the user doesn’t say please. I thought about having the bot tweet this to the user but decided against it. Instead, it’s a little fun just for me.

Uh uh uh, you didn't say the magic word

NEWMAN?


function pressSelect() {

    var weeklyReplyText = soundtrackArray[soundtrackArrayElement] + " Here's your soundtrack for the week!";
    T.post('statuses/update', { status: weeklyReplyText }, gameOver2);

    function gameOver2(err, reply) {
        if (err) {
            console.log(err.message);
            console.log("Game Over");
        } else {
            console.log('Tweeted: ' + reply.text);
        }
    }
}

 const job1 = schedule.scheduleJob(rule, pressSelect);

 job1.on("Every Day Tweet", pressSelect);
Enter fullscreen mode Exit fullscreen mode

Here is the function used to tweet on a schedule, which I’ve named pressSelect. pressSelect has the replyText variable, slightly changed to be a tweet rather than a comment, but uses the same array to pull from. The gameOver function is also present, though renamed just to be safe. Since gameOver and gameOver2 are local variables within their respective functions, there shouldn’t be any issues. However, they both do the same thing.

The final part of this code is creating a variable called job1. job1 is the scheduleJob method from the node-schedule object at the top of the code. I pass in the rule created and pressSelect as parameters. We then use an event listener with pressSelect passed in again.

Running the Code

To test your code and ensure it works, type “npm run develop” into the terminal. If you get a Twit error about consumer keys, ensure there are no spaces between the variable, equals sign, and key itself in your .env file. If the error persists, you may have copied your keys wrong. You can always generate them again and copy them directly into the .env file. If you’d like to test pressSelect on its own and make sure it works, you can just comment out the last two lines of the code and call pressSelect directly. This way, you don’t have to wait for whatever day and hour you scheduled node-schedule for.

Once it’s running, to test the way it responds to other users, log in to another Twitter account and tweet at your bot. You should be able to see some action in the terminal that tells you it’s working, followed by the response on Twitter.

You did it!

Your bot runs from your code! but what if you want it to run all the time? and not on your own machine? Well, to do that, we have to deploy it. Check out this article for more on how to deploy using Heroku!

Finished!

I hope this article has been helpful! I'm always trying to improve, and this article was certainly a challenge. I tried to include every step and think of possible pitfalls you might experience, but if you're stuck, you can always reach out to me on Twitter and I'd be happy to chat about your bot! Here’s a copy of my snes repo so you can check and see what’s working for me!

Discussion (0)