Making a Twitter Bot and Replying to Tweets with Node
Traducciones al EspañolEstamos traduciendo nuestros guías y tutoriales al Español. Es posible que usted esté viendo una traducción generada automáticamente. Estamos trabajando con traductores profesionales para verificar las traducciones de nuestro sitio web. Este proyecto es un trabajo en curso.
Introduction
Twitter bots are a great way to practice using an API and develop 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. This tutorial shows how to make a Twitter bot that tweets a link to a video game soundtrack once a week (pulled randomly from an array of YouTube links). It also responds to other Twitter users who ask for a link with a random link from the same array.
The tutorial demonstrates how to use Node.js, a framework for JavaScript, and a few packages from npm, the Node Package Manager, to have your bot tweeting in no time. As well, the guide shows how to set up a GitHub repository for your bot’s code. At the end of the guide, we demonstrate how to host the bot on a Linode cloud instance.
Before You Begin
Create Twitter, GitHub, and Linode Accounts
To follow along with this guide, accounts with the following websites are required:
Twitter: If you already have a Twitter account, you can use that existing account to access the Twitter Developer portal. You may instead want to create a new, separate Twitter account to use for this tutorial. If you sign up for a new account, Twitter requires a unique email address and phone number to register the account.
This is where you decide what you want your account to be named and what it does automatically. The tutorial names the bot @SNESSoundtracks, but your Twitter account name needs to be unique.
GitHub: This tutorial shows how to record the bot’s application code in a repository on GitHub. If you already have a GitHub account, you can use that one. If you do not, create a new one now.
Linode: At the end of the tutorial, the Node.js app is installed and run on a Linode instance. If you already have a Linode account, you can use that one. If you do not, create a new one now. For new accounts, there is a Sign Up link at the bottom of this tutorial that offers a free credit.
Install Software on your Workstation
The following software is needed on your workstation to complete the tutorial:
A plain-text editor. Visual Studio Code (abbreviated VS Code) is a recommended desktop text editor, but any other plain-text editor can be used as well. VS Code also includes a built-in terminal.
Git: The Git command line software, or GitHub Desktop.
These tools are used to download a copy of the GitHub repository that is created in this guide. The GitHub Desktop software can be more user-friendly for beginners, but you might prefer to work out of the command line. Instructions for using both are provided in this tutorial.
To install the command line software, follow our How to Install Git on Linux, Mac or Windows guide. Then, follow the Configure Git section of our Getting Started with Git guide. When doing this, you don’t need to set the
core.editor
option, but it is important to set your username and email for Git.Node.js and the Node Package Manager (npm), which are used to initialize a new npm package for the Twitter bot.
There are a number of different ways to install Node.js and npm. Our How to Install Node.js guide outlines some of these options. For this tutorial, we recommend using the Node Version Manager (nvm), which can manage multiple versions of Node.js and npm. Follow the next Install Node.js and npm via the Node Version Manager section for instructions.
Install Node.js and npm via the Node Version Manager
The original nvm software project is used on Unix, Linux, and macOS:
Follow the Installing and Configuring NVM section of our How to Install and Use the Node Version Manager guide to install nvm.
Run this command from your terminal. This installs the latest version of Node.js and npm:
nvm install --lts
A separate nvm-windows project was created for native installation on Windows:
Follow the instructions in the project’s README to install nvm-windows.
Run this command from your terminal. This installs the latest version of Node.js and npm:
nvm install latest
Apply for Access to the Twitter Developer Portal
Twitter’s 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?
Follow these steps to register for developer access:
Head to the developer page. Once there you’ll see a link near the profile picture on the top right that says Apply.
Click the Apply for a developer account button on the page that appears:
If you are not already signed in, Twitter prompts you to sign in to your account.
Twitter first asks what your intended use for the developer account is. For this tutorial, we have chosen the Hobbyist option, followed by the Making a bot option.
Complete the remaining questions in the Basic info, Indended use, Review, and Terms screens. The Indended use screen asks you for more detail about your application. For this tutorial, these are the answers that were entered for each prompt:
In your words: Let Twitter know that you’re applying for developer access as a hobby, and it’s just for learning to code and practicing a bot.
Are you planning to analyze Twitter data?: No
Will your app use Tweet, Retweet, Like, Follow, or Direct Message functionality?: Yes. In the description, let Twitter know that your bot responds to users that @ mention it, and that it also periodically sends out Tweets on its own. The bot tweets and respond to Tweets with links to video game soundtracks on YouTube.
Do you plan to display Tweets or aggregate data about Twitter content outside Twitter?: No
Will your product, service, or analysis make Twitter content or derived information available to a government entity?: No
Because you’re not giving information to the government or consuming streaming data for analytical purposes, the application should be more straightforward. Follow the instructions on screen until you have an authenticated developer account.
A later section in this guide shows how to create an app within the Twitter developer portal and fill out the descriptions for it.
Create and Clone a GitHub Repository
As mentioned in the Before You Begin section, this tutorial records the code for the Twitter bot in a Git repository. Specifically, we first create a new repository on GitHub.com and then pull it down to your workstation. By doing this, we can populate the repository with some helpful files, like a .gitignore and a README:
Visit GitHub.com, then follow the Create a repository section in GitHub’s official Create a repo document. When creating the repository, make these selections:
You can name the repository whatever you would like, but later code examples in the tutorial assume it is named
snes-soundtracks
.The repository can be public or private. If you make it private, and if you also want to use the Git command line tools, then you need to authenticate with GitHub inside your terminal. This authentication requires a few extra steps, and these steps are outlined in the note below. For a simpler experience, it’s recommended to make the repository public.
Note
To authenticate with Github inside the terminal, see the Authenticating with the command line section of GitHub’s About authentication to GitHub document.
Specifically, personal access tokens are the recommended method of authenticating for this tutorial. Working with public keys is not demonstrated.
Enable the README file toggle.
Choose a license. choosealicense.com has helpful recommendations for which license to use. This tutorial recommends the MIT License.
Enable the
.gitignore
toggle and choose Node from the available options.Note
A Git repository’s.gitignore
file allows you to specify certain files or file types that should not be checked into source control. Node.js projects feature some of these files. GitHub’s.gitignore
template for Node.js sets up some appropriate default files to exclude. For more information about.gitignore
file options, see this document from git-scm.com.
After you finish the create repository form, the new repository appears in your browser. From this page, download a copy of the repository to your workstation:
To use GitHub Desktop, follow GitHub’s Cloning a repository from GitHub to GitHub Desktop document. Here’s an example screenshot of where the Open with GitHub Desktop option is in your repository:
To use the command line, run the
git clone
command in your terminal as follows:git clone https://github.com/your-github-username/snes-soundtracks.git
Before running the command, be sure to substitute your own GitHub username in for
your-github-username
. If you chose a different name for your repository, substitute that in forsnes-soundtracks
.Note
If you created a private repository, remember to set up a personal access token for GitHub. When referencing a private repository, the abovegit clone
command prompts for a GitHub username and password. You should enter the personal access token (not your normal GitHub password) when prompted for the password.
At this point, your source control is present on both your machine and on GitHub.
Create an App in the Twitter Developer Portal
After your developer account has been registered, you can create an app within the developer portal. When you create the app, Twitter creates a set of keys that your Node.js project uses to authenticate with the service. This section shows how to create your Twitter app and where to store the app’s keys.
Create a file named
.env
inside your code repository on your workstation. Paste the following snippet into the file.- File: .env
1 2 3 4
consumer_key= consumer_secret= access_token= access_token_secret=
Later in this section, the keys that are provided by the Twitter developer portal are recorded in this file. Your
.env
file serves as your one source of truth for these keys. The.gitignore
for your project excludes the.env
file from source control, so you won’t accidentally upload your keys to GitHub.Note
In a later section, a Node.js module nameddotenv
is used by your application code to read the secret keys in the.env
file.Return to the Twitter developer portal dashboard in your browser.
Click the Create Project button.
Twitter’s developer portal has two concepts that are used to manage your access to the Twitter API: projects and apps. A project contains an app. For earlier versions of the Twitter API, standalone apps were created, and projects were not required. A project and corresponding app are both required to access the v2 Twitter API, which this tutorial uses.
Note
Review Twitter’s Developer Apps documentation for more information about projects and apps.Complete the forms that appear:
Name your Project: The name of your project needs to be unique.
Which bests describes you?: Choose Making a bot.
Describe your new Project: Enter a brief description of the bot’s abilities: periodically tweeting links to video game soundtracks on YouTube, and responding to users that @ mention the bot.
Add your App: You may see a screen that looks like the following, which asks you to pick an app to add to the new project:
Because you don’t have any apps yet, the dropdown menu on this page is empty. Click the Create new App instead button above the dropdown menu. A text field appears that allows you to enter an app name. Enter a unique name in this field.
Note
You may not see the dropdown menu, and the text field might appear right away instead.
After completing the forms, a page with secret keys appears:
Copy and paste the keys into your
.env
file as follows:Copy the API Key and paste it at the end of the
consumer_key=
line. No extra spaces or quotes should be added. For example, if your API Key wasdvF6z55HXwpSE3qBVFNONV2DN
, then the line would be written likeconsumer_key=dvF6z55HXwpSE3qBVFNONV2DN
.Copy the API Secret Key and paste it at the end of the
consumer_secret=
line. As with the previous line, do not add extra spaces or quotes.
Note
This tutorial does not need the Bearer Token, but you can record that somewhere outside of your project if you’d like to. It can also be regenerated from inside the developer portal later if you don’t record it.Close the dialog that displays the secret keys. The developer portal should now look like this:
Give your Twitter App Read and Write Permissions
By default, the app that you created has Read Only permissions. Apps that are set to Read Only are not able to create Tweets, which is the primary function of the bot in this tutorial. With Read and Write permissions, your bot is able to create Tweets. Follow these instructions to give your bot Read and Write permissions:
Navigate to the app (not the project) in the Twitter developer portal. The app is listed under the project in the sidebar navigation of the developer portal.
Scroll down to the App permissions heading. This section should display your app as Read Only. Click on the Edit button in this section.
In the form that appears, choose the Read and Write option and then click Save.
Navigate to the Keys and Tokens tab at the top of the page.
Scroll down to the Access Token and Secret pane. Click the Generate keys button for the access token and secret.
A dialog appears that displays secret keys. Copy and paste the keys into your
.env
file as follows:Copy the Access Token and paste it at the end of the
access_token=
line in your.env
file, with no extra spaces or quotes.Copy the Access Token Secret and paste it at the end of the
access_token_secret=
line in your.env
file, with no extra spaces or quotes.
Dismiss the dialog.
Initialize the Node Project and Install npm Dependencies
Right now, your local repository contains four files: your .env
secrets, a .gitignore
, a README.md
, and a LICENSE
file. It does not have any Node.js project files yet. To initialize your Node.js project:
Navigate to your local code repository in your terminal:
cd snes-soundtracks
Note
If you are using VS Code’s built-in terminal and have your code repository folder open, then you do not need to run thiscd
command. This is because VS Code’s terminal automatically starts inside the folder for your editor’s workspace.Run npm’s
init
command:npm init -y
The following output appears:
npm init -y Wrote to /home/username/snes-soundtracks/package.json: { "name": "snes-soundtracks", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "repository": { "type": "git", "url": "git+https://github.com/username/snes-soundtracks.git" }, "keywords": [], "author": "", "license": "ISC", "bugs": { "url": "https://github.com/username/snes-soundtracks/issues" }, "homepage": "https://github.com/username/snes-soundtracks#readme" }
This output shows the contents of a new
package.json
file that was created by thenpm init
command. Thepackage.json
file contains metadata about your Node.js project. For more information about this file, see the What is the filepackage.json
article in the official Node.js documentation, and thepackage.json
reference in the npm documentation.The application code for your bot relies on a few different dependencies from npm. To install these dependencies, enter these commands in your terminal while still inside the code repository:
npm install node npm install node-schedule npm install twit npm install dotenv
These dependencies provide everything needed to have a functional Twitter bot:
node: Installing the
node
package provides a local Node.js binary inside your project folder. This is installed locally so that the project doesn’t depend on your system-wide version of Node.js. This makes your project more portable, because other systems that you install the project on may run a different version of Node.js.node-schedule: This package is used by the tutorial to schedule new Tweets periodically.
dotenv: This package is used to read the secrets that you stored in your
.env
file.twit: This is a Twitter API client for Node.js.
At this point, the contents of your directory should now look like the following:
ls -a
. .env .gitignore README.md package-lock.json .. .git LICENSE node_modules package.json
Update Scripts in package.json
Before we can start adding our code, we should adjust a few parts of the package.json
file in the new Node.js project.
Currently, the
scripts
section of yourpackage.json
should look like:- File: package.json
1 2 3
"scripts": { "test": "echo \"Error: no test specified\" && exit 1" },
The
scripts
section contains commands that can be used to invoke your project. For more information about this part ofpackage.json
, see thescripts
reference in the npm documentation.Open this file and modify the
scripts
section to look like the following snippet:- File: package.json
1 2 3 4 5
"scripts": { "test": "snes.js", "start": "node ./snes.js", "develop": "NODE_ENV=develop node snes.js" },
Scroll to the
main
section of the same file. This should currently look like:- File: package.json
1
"main": "index.js",
Update this line so that it instead says:
- File: package.json
1
"main": "snes.js",
These changes reference an
snes.js
file that doesn’t exist yet. Create an empty placeholder file with this name in your code repository. Thetouch
command can do this from your terminal (or you can just create the empty file with your text editor):touch snes.js
Note
Thesnes.js
file can be named whatever you want it to be. If you want to use a different name, just make sure that yourpackage.json
is updated to reference that new name.
Add the Twitter Bot Application Code
Your Node.js project is now prepared for the Twitter bot application code. Open snes.js
in your text editor, then proceed through the next sections:
Reading .env Secrets, Setting Up node-schedule, and Specifying YouTube Videos
Copy and paste this snippet into snes.js
. After copying and pasting, make sure to replace the @SnesSoundtracks
string on line 3 with your own Twitter username:
- File: snes.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
console.log("SNES Soundtracks booting up"); const twitterUsername = '@SnesSoundtracks'; //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.minute = 0; rule.tz = "Etc/GMT+4"; //array to pull soundtracks from var soundtrackArray = [ "https://www.youtube.com/watch?v=85u34SUh05Y", // Legend of Zelda "https://www.youtube.com/watch?v=85u34SUh05Y", // Chrono Trigger "https://www.youtube.com/watch?v=UyNufyV3VCo", // Super Metroid "https://www.youtube.com/watch?v=Y2VJeZDejtc", // Final Fantasy VI "https://www.youtube.com/watch?v=wgUmFPnkoHU", // Super Mario World "https://www.youtube.com/watch?v=-QsysJwzod4", // Super Street Fighter II "https://www.youtube.com/watch?v=oRxgYC5zrV4", // Super Mario World 2: Yoshi's Island "https://www.youtube.com/watch?v=rJJk9Zk2h_U", // Super Mario Kart "https://www.youtube.com/watch?v=byIjMomjWkA", // Star Fox "https://www.youtube.com/watch?v=wpchBo75N68", // Super Mario RPG: Legend of the Seven Stars ]; var soundtrackArrayLength = soundtrackArray.length;
Here’s what the code does:
On line 1, the code logs a start up message to the console just so we know it’s running.
On line 7, the dotenv module is included using the built-in Node.js
require()
function. The dotenv module’sconfig()
function is used on the same line to read the values from your.env
file. The values are placed in theprocess.env
Node.js property. For example, theconsumer_key
secret can be accessed insideprocess.env.consumer_key
within the code.The
if
statement on lines 6-8 ensures this only happens when the Node.js environment is set todevelop
. This is handled by the"develop": "NODE_ENV=develop node snes.js"
line in thescripts
section of your package.json file.On lines 11-15, we set up a few variables for the node-schedule package. These variables declare the day and time when the bot should periodically tweet. Specifically, the scheduled time is every Monday at 10 am Eastern Daylight Savings Time. A
RecurrenceRule
is created to facilitate this.Note
Documentation for this method of scheduling is found in the Recurrence Rule Scheduling section of the node-schedule package’s README. Creating theRecurrenceRule
does not actually schedule the bot’s tweeting function. This function is scheduled at the end of the bot’s application code, in the Scheduling Tweets section.Lines 18-30 set up an array of soundtrack links for the bot to pull from at random.
Authenticating with Twitter and Listening for Tweets
Append this snippet to the bottom of
snes.js
:- File: snes.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
// ... append to bottom of file: // Create a Twitter client object to connect to the 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: twitterUsername }); // Now looking for Tweet events // See: https://dev.Twitter.com/streaming/userstreams stream.on('tweet', pressStart);
We start using Twit in this section of code:
Line 4 includes the
twit
Twitter API client module.Line 7 uses another
config.js
file to create a new configuration object. Theconfig.js
file is created in the next step of this section.Line 9 uses JavaScript’s
new
operator to create an instance of the Twitter API client. The configuration object from line 7 is passed to the constructor function for the new instance.This instance is stored in the variable
T
. From now on, when we want to use something from thetwit
module, we simply reference theT.propertyName
syntax. This syntax allows us to access any property, field, or method we need from the API client’s library.Line 12 sets up a stream that monitors for Tweet mentions of your Twitter username from other Twitter users while the code is running.
Line 16 creates an event listener with the
stream.on
method. Thestream.on
method accepts two arguments:A string parameter that represents the type of event that should be listened for.
Another function that handles the events from the stream. The function that handles the events is called
pressStart
. ThepressStart
function is defined in the next section of this tutorial.
In your code repository, make a file named
config.js
and paste this snippet into it:- File: config.js
1 2 3 4 5 6
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, };
This file exports an object that matches the configuration object accepted by the constructor function in the
twit
module. Specifically,twit
expects a configuration object with the following form, as described in thetwit
README on GitHub:{ consumer_key: '...', consumer_secret: '...', access_token: '...', access_token_secret: '...' }
In order to generate the configuration object, this file reads the values encoded in the
process.env
property by the dotenv module.
Replying to Tweets
Append this snippet to the bottom of snes.js
:
- File: snes.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
// ... append to bottom of file: 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, so you can 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(twitterUsername) && playerTwo === true) { // Start a reply back to the sender var soundtrackArrayElement = Math.floor(Math.random() * soundtrackArrayLength); 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); } }; }
This section of code defines a pressStart
function that’s called when another Twitter user mentions the bot. It contains a few local variables, a bit of logic, and another callback function that must be included in the T.post
method:
On line 3, the
pressStart
function is defined. It takes atweet
object as a parameter. This represents a Tweet that another Twitter user has mentioned the bot in. Thetweet
object has tons of data attached to it, and the Twit client helps us parse through this data.Lines 5-7 parse three variables from this object: the id of the Tweet, the text of the Tweet, and the username of the person who wrote the Tweet. We need those three variables in order to respond as a comment to the original Tweet.
Lines 9-13 define and use a regular expression (abbreviated regex) to analyze the text of the Tweet object. This regex looks for a keyword that you want to be present in Tweet mentions. If it matches the text of the Tweet, then the bot is activated and creates a reply Tweet. The tutorial code checks for the word
please
, but you can choose another word if you prefer. As long as other Twitter users are polite and include this word, they’ll get a random soundtrack in response.The
gi
characters at the end of the/(please)/gi
regex definition represent two different options that are used when executing the expression:By default, a regex only looks for the first match that it finds in the text string that it examines. The
g
option tells the regex to return all matches throughout the string. For example, if a Tweet includes the wordplease
three times, then the regex in this tutorial returns all three matches.The
i
option ignores capitalization.
The
playerOne
variable is set to an array of the regex’s matches. If there are no matches, then it is set to an empty array.The
playerTwo
variable is a boolean that verifies whether the arrayplayerOne
has anything in it. In other words,playerTwo
is set to true if the regex matched with the Tweet text.
The
if
block on line 21-30 composes the reply Tweet:The
if
statement requires that the Tweet text contains the bot’s name and the keyword from the regex (via theplayerTwo
boolean).If both of these conditions are true, then we proceed with creating the reply Tweet. The variable
replyText
is a text string created with a random element of the soundtrack list array, the Twitter username of the person being replied to, and a short message.On line 28, the
T.post
method sends a POST request to thestatuses/update
Twitter API endpoint.An object with a
status
property and anin_reply_to_status_id
property is passed to theT.post
method.status
is the actual text to be posted in the reply Tweet. In our code, it is set to thereplyText
variable.in_reply_to_status_id
specifies a unique identifier of a Tweet from Twitter’s API. This allows Twit to identify which Tweet the bot replies to as a comment. In our code, it is set to theid
variable from the beginning of thepressStart
function.A
gameOver
callback function is also passed to theT.post
method, and this function is defined on lines 34-41. This function prints the reply Tweet to your console, or it prints any errors that the Twitter API may have returned from the POST request.Note
Where thegameOver
function is passed toT.post
, you could use an anonymous function instead, and it would do the same thing. The tutorial code defines it separately for better readability.The
else
block on lines 30-32 is invoked if the Tweet mention doesn’t sayplease
. It logs a quote from Jurassic Park to the console, just so we can see why the bot didn’t tweet back.
Scheduling Tweets
Append this snippet to the bottom of snes.js
:
- File: snes.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
// ... append to bottom of file: function pressSelect() { var now = new Date(); const dateOptions = { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' }; const timeOptions = { timeZoneName: 'short' }; const dateString = now.toLocaleDateString("en-US", dateOptions) + " at " + now.toLocaleTimeString("en-US", timeOptions); var soundtrackArrayElement = Math.floor(Math.random() * soundtrackArrayLength); var weeklyText = soundtrackArray[soundtrackArrayElement] + " Here's your soundtrack for " + dateString; T.post('statuses/update', { status: weeklyText }, 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);
This section of code defines a pressSelect
function that sends new Tweets with a link to a soundtrack. It then schedules the function to be called periodically:
Lines 4-10 compose a
weeklyText
string, which is similar to thereplyText
variable from thepressStart
function. The text is slightly changed to be an original Tweet, rather than a comment on a different Tweet. It also pulls an element from the same soundtrack list array.On line 11, The
T.post
method is invoked with the samestatuses/update
API endpoint. There is noin_reply_to_status_id
property passed, because the function composes an original Tweet and not a reply.The
gameOver
callback function is also present on lines 13-20, but it has been renamed togameOver2
to avoid name conflicts. SincegameOver
andgameOver2
are local variables within their respective functions, there shouldn’t be any issues. But, we’ve changed the name just to be safe.After the
pressSelect
definition, line 23 invokes the node-schedule module’sscheduleJob
function and stores the result in thejob1
variable. This is the last step needed to set up periodic scheduled Tweets. This function accepts two arguments:The
rule
variable created earlier in the code, which describes when the scheduled job should be run (once a week on Monday).The
pressSelect
function.
Commit the Code and Push to GitHub
Now that the bot code has been added to your workstation’s copy of the repository, we should make a new commit. A commit in Git records your file changes in the version control history. After making the new commit, you can then push it to your repository on GitHub.
NoteA commit that you make locally is not automatically synced to GitHub. The push operation demonstrated in this tutorial needs to be done manually.
In the next two sections, this tutorial demonstrates two methods for creating a commit and pushing it to GitHub. Use the one you prefer: Commit and Push with GitHub Desktop, or Commit and Push with the Git Command Line.
Commit and Push with GitHub Desktop
Open GitHub Desktop on your workstation. The new files that you created are shown under the Changes tab on the left, and they are automatically selected for the new commit.
Note
Not all of the files that are in your Node.js project, like thenode_modules
folder, or the.env
secrets file, are shown. This is because they are excluded by the.gitignore
file.Below the changed files, enter a brief summary message for the new commit. For example:
Add Twitter bot application code
.Press the Commit to main button below the summary and description fields. This creates the new commit on the main branch of your repository, which is the default branch name for repositories created on GitHub.
Note
Branches in Git allow you to work on new features for your projects without altering the files in your other branches.After creating the commit, the Changes tab is empty:
Click on the Push origin that appears in the top navigation. This pushes the new commit to your GitHub repository.
Note
Origin is how Git is referring to the remote repository on GitHub. This is a naming convention for Git.If you visit your repository on GitHub, the new files for your Node.js project are present.
Commit and Push with the Git Command Line
Return to your local code repository in your terminal, then run:
git status
The following output appears:
On branch main Your branch is up to date with 'origin/main'. Untracked files: (use "git add <file>..." to include in what will be committed) config.js package-lock.json package.json snes.js
Your new application files are listed under the
Untracked files
line, which means that they don’t exist yet in Git’s version history.Note
Not all of the files that are in your Node.js project, like thenode_modules
folder, or the.env
secrets file, are shown. This is because they are excluded by the.gitignore
file.Before you can commit the files, you need to stage them. Staging tells Git that you want to include the files in the next commit that you make. To stage your files, run:
git add .
Note
The period at the end of this command tells git to add all the files in your current directory to the stage.Then run the
status
command again:git status
The output now appears as:
On branch main Your branch is up to date with 'origin/main'. Changes to be committed: (use "git restore --staged <file>..." to unstage) new file: config.js new file: package-lock.json new file: package.json new file: snes.js
The output above mentions that you are on the
main
branch, which is the default branch name for repositories created on GitHub.Note
Branches in Git allow you to work on new features for your projects without altering the files in your other branches.Git now knows to include the new files in your next commit. To make the commit, run:
git commit -m "Add Twitter bot application code"
This output appears:
[main e5d6839] Add Twitter bot application code 4 files changed, 599 insertions(+) create mode 100644 config.js create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 snes.js
Next, push the new commit to your repository on GitHub:
git push origin main
Note
Origin is how Git is referring to the remote repository on GitHub. This is a naming convention for Git.This output appears:
Enumerating objects: 7, done. Counting objects: 100% (7/7), done. Delta compression using up to 12 threads Compressing objects: 100% (6/6), done. Writing objects: 100% (6/6), 7.57 KiB | 1.08 MiB/s, done. Total 6 (delta 0), reused 0 (delta 0) To https://github.com/your-github-username/snes-soundtracks.git 735ecd1..e5d6839 main -> main
If you visit your repository on GitHub, the new files for your Node.js project are present.
Run the Code
To test your code for the first time and ensure it works, type npm run develop
into the terminal:
npm run develop
If successful, the following output appears:
> snes-soundtracks@1.0.0 develop
> NODE_ENV=develop node snes.js
SNES Soundtracks booting up
If any errors appear, they are likely related to an authentication failure with the Twitter API. If this happens, then your .env
file might have syntax errors, or your secret keys may have been incorrectly pasted into it. Review the
Troubleshooting consumer_key Twit Config Error and
Troubleshooting Twitter Error 401 Unauthorized sections for solutions.
Test the Tweet Function
At this point, the bot is running, and if you wait long enough, then it should send a scheduled Tweet. We can also test pressSelect
on its own and make sure the tweeting function works, To do this, you need to modify the code slightly.
Comment out the last line of the code in
snes.js
and callpressSelect
directly:- File: snes.js
1 2 3 4 5
// ... modify end of file as follows: // const job1 = schedule.scheduleJob(rule, pressSelect); pressSelect();
By doing this, the tweeting function runs immediately, and you don’t have to wait until the next scheduled Tweet.
Type
CTRL-c
to interrupt and stop the bot. Then run the code again:npm run develop
If successful, output like the following output appears:
> snes-soundtracks@1.0.0 develop > NODE_ENV=develop node snes.js SNES Soundtracks booting up Tweeted: https://t.co/Y2AOzzkUm0 Here's your soundtrack for Tuesday, June 1, 2021 at 10:59:19 AM UTC
Note
Note that Twitter has automatically shortened the link to the soundtrack in the Tweet.If you don’t see the above output, you might instead see this error:
Read-only application cannot POST.
. If so, then your app’s permissions are set to Read Only in the Twitter developer portal. Revisit the earlier Give your Twitter App Read and Write Permissions section and then return to this section.
Test Tweet Replies
Lastly, you can test the way the bot responds to other users:
Undo the code changes you made in the Test the Tweet Function section: remove the
pressSelect();
line that was added, uncomment the last line, and save the file.Type
CTRL-c
to interrupt and stop the bot. Then run the code again:npm run develop
Log in to another Twitter account and tweet at your bot, including the keyword (
please
). You should be able to see some action in the terminal that tells you it’s working, followed by the response on Twitter.
Host the Code
You could certainly let this code run for a long while from your local machine, but it’s better if you host it somewhere else. This section shows how to create a compute instance on Linode, install the bot on it, and then run it.
Create a Linode
Visit the Linode Cloud Manager. If you haven’t created any Linodes before, the home page looks like this:
Follow the Creating a Compute Instance guide. When creating your instance, use the following options:
Pick Ubuntu 20.04 as your Linux distribution.
Select the region closest to you.
Choose Nanode (the smallest option) as your Linode plan. It’s only $5 a month and has more than enough space and RAM for your Twitter bot and any other projects you’d want on it.
Pick a long, unique, and complex root user password. The root user for your server can take any administrative action on the system. Using a strong root password is important to prevent malicious attackers from accessing your system.
When it asks for a Linode label, name it after your project.
After you create the Linode, it takes a few moments to boot up. Once it boots up it’s time to get inside and clean up a bit.
Log In and Secure the Server
To log into the server, follow the Connect to Your Linode via SSH section of our Getting Started guide. If you do not have access to an SSH client, or if SSH connections are firewalled on your local network, you can also opt to use the Lish console from the Cloud Manager in your web browser. To do so, follow the Use a Web Browser instructions in our Lish guide.
Note
Our Connecting to a Remote Server Over SSH guide series has a few other options for SSH clients, like SSH extension for the Chrome web browser.When logging in for the first time, you use the
root
username and the root password that you set when creating the Linode. However, it’s important to create a non-root-user with limited permissions to run your programs from.Caution
If you were to run your programs as root, and if they were to be compromised by someone malicious, then the rest of your server could be compromised. If your server isn’t running anything else, then it may not be important to you. However, an attacker could install malicious programs that target other people’s servers, like a botnet script that sends denial-of-service attacks.To create a non-root-user, follow the Add a Limited User Account section of our Setting Up and Securing a Compute Instance guide. This guide assumes that the name of the new user is
tutorialbotuser
, but you can name it whatever you’d like. The instructions in this section also show how to give your usersudo
privileges, so it is still able to perform software updates and other administrative tasks.Note
Here’s a video that also shows how to create a limited user: Tech Republic: How to create a new user with admin privileges.After you have created the limited user, log out of your current SSH (or Lish) session by running the
exit
command. Then, log back in as the new user.Note
The rest of the steps in the Setting Up and Securing a Compute Instance guide are optional for this tutorial, but they are still recommended if you intend to leave your server running.
Perform Software Updates and Prepare for the Bot
Once you’re logged in with your limited user, you should run
sudo apt update
to check for updates available, and thensudo apt dist-upgrade.
Node.js and NPM are not preinstalled on Ubuntu 20.04, so you need to install those in order to run the bot. As on your workstation, the recommended way is to use nvm:
On the Linode, run the nvm installer:
wget -qO- https://raw.githubusercontent.com/nvm-sh/nvm/v0.38.0/install.sh | bash
Log out of the Linode and then log back in again. This is needed to start using the nvm command.
Install the latest Node.js and npm via nvm:
nvm install --lts
The server is now ready to run the bot, but you still need to upload the bot’s code to the server. We present two ways to do this:
Filezilla: You can transfer your code to the Linux instance from your workstation, via the Filezilla GUI desktop application. Because you’re using a desktop application, this can be a little more user-friendly for beginners. Follow the Upload the Bot Using Filezilla section to do this.
git clone: You can directly clone your repository on GitHub to your Linode with the Git command line tool. Follow the Upload the Bot with Git Clone section to do this.
Upload the Bot Using Filezilla
Install the Filezilla client by downloading the installer for your workstation from their downloads page.
Here’s how Filezilla looks once you open it:
Inside Filezilla, you can see input fields for Host, Username, Password, and Port. Set values for these fields as follows:
Field Value Host The IP Address for your Linode. Use the Find your Linode’s IP Address guide to locate your IP address in the Cloud Manager. Username This should be the name for the limited non-root-user you created. Password The password for the limited non-root-user you created. Port This should be set to 22. Upon connecting, you’ll get a message saying the key is unknown. This is normal, but you should double-check that the IP address is correct. If it is, check the box to remember your Linode in Filezilla.
The left half of the Filezilla window shows your local files, and the right half of the window shows your Linode’s files. On the left side, go to the directory where you’re keeping the files for your Twitter bot. You’ll need the
.env
file,config.js
file,package.json
file,package-lock.json
file,node_modules
folder, andsnes.js
application code file for your bot. The files we’re moving over are highlighted in the following picture.By default, the right side of the FileZilla window shows the files in the home directory of your Linode’s user. This is also where we want to install the bot. Once you highlight the files you want to transfer on the left side, click and drag them to the right.
After finishing the file transfer, proceed to the run the bot section.
Upload the Bot with Git Clone
Git is preinstalled on Ubuntu 20.04, but you still need to configure it like you did on your workstation. While inside your SSH or Lish connection to your Linode, follow the Configure Git section of our Getting Started with Git guide. When doing this, you don’t need to set the
core.editor
option, but it is important to set your username and email for Git.To clone your GitHub repository to your Linode, run the
git clone
command from your SSH or Lish connection. Be sure to substitute your own GitHub username in foryour-github-username
and the name you chose for the repository forsnes-soundtracks
before running the command:git clone https://github.com/your-github-username/snes-soundtracks.git
Note
If you previously created a private GitHub repository, then the above command prompts for your password. You should enter the personal access token (and not your normal GitHub password) that you created when setting up command line authentication for GitHub earlier.Cloning the repository does not install the NPM dependencies for your project. This is because they were contained in the
node_modules
folder of your project, which was excluded from version control in your.gitignore
file. The required dependencies are still outlined in yourpackage.json
, which we can use to reinstall them on your Linode. Navigate to the new cloned repository and then install them:cd snes-soundtracks npm install
The
npm install
command looks at your package.json and installs every dependency that it finds into thenode_modules
folder.The
.env
file was also excluded from version control, so it needs to be copied to your Linode separately. On your workstation, open your local.env
and copy its contents to your clipboard. Then inside the code repository on the Linode, create a new.env
file. You can use thenano
command line text editor to do this:nano .env
The
nano
editor’s interface looks like this when first opened:Paste the contents of your clipboard into the editor.
Type
CTRL-o
to save the file, and hitEnter
when the editor prompts you to confirm the.env
filename.Type
CTRL-x
to exit the editor.Remove file system read and write file permissions for groups and other users on the Linode for the
.env
file:chmod go-rw .env
Doing this ensures that your secret keys can’t be read by other users on the system.
After cloning the repository and installing the dependencies, proceed to the run the bot section.
Run the Bot on your Linode
Once you’ve transferred the files, you can use your SSH connection or Lish console to run the bot in the same way as on your workstation:
npm run develop
However, the bot does not stay running if you close your SSH connection (for example, if you put your workstation to sleep), or if you need to perform some other action in the Lish console.
To keep your bot running uninterrupted, you can start a Screen session. Screen is a useful tool for creating terminal sessions that stay alive and that can be accessed from any SSH or Lish connection. It’s also preinstalled on Ubuntu 20.04, so you can start using it immediately:
If you ran
npm run develop
on your Linode, typeCTRL-c
to stop the bot.From your Linode, run:
screen
This creates a new Screen session. A welcome message for the new session is presented. Close this message by hitting
Enter
on your keyboard.You are now in your Screen session. Run the bot again:
npm run develop
Then, detach from the Screen session by typing
CTRL-a
, followed by thed
key. This brings you back to your previous terminal history. After detaching, a message like this appears:[detached from 38905.pts-0.localhost]
Your bot continues to run, and it runs even if you close your SSH or Lish connection. You can later return to your bot from the same SSH or Lish connection, or from a new connection. This command reattaches to your screen session:
screen -r
If you want to halt your bot, reattach to the screen session, then type
CTRL-c
.
Troubleshooting
Troubleshooting consumer_key Twit Config Error
When starting the bot, you may see an error like this:
> snes-soundtracks@1.0.0 develop
> NODE_ENV=develop node snes.js
SNES Soundtracks booting up
/home/tutorialbotuser/snes-soundtracks/node_modules/twit/lib/twitter.js:511
throw new Error(err_msg)
^
Error: Twit config must include `consumer_key` when using user auth.
at /home/tutorialbotuser/snes-soundtracks/node_modules/twit/lib/twitter.js:511:13
at Array.forEach (<anonymous>)
at Twitter._validateConfigOrThrow (/home/tutorialbotuser/snes-soundtracks/node_modules/twit/lib/twitter.js:508:17)
at new Twitter (/home/tutorialbotuser/snes-soundtracks/node_modules/twit/lib/twitter.js:58:8)
at Object.<anonymous> (/home/tutorialbotuser/snes-soundtracks/snes.js:26:9)
at Module._compile (node:internal/modules/cjs/loader:1109:14)
at Object.Module._extensions..js (node:internal/modules/cjs/loader:1138:10)
at Module.load (node:internal/modules/cjs/loader:989:32)
at Function.Module._load (node:internal/modules/cjs/loader:829:14)
at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:76:12)
at node:internal/main/run_main_module:17:47
This Twit error about your consumer_key
means there may be a syntax error in your .env
file. In your .env
, ensure there are no spaces between the variable, equals sign, and key itself in your .env file. As an example, here’s what your .env
might look like when correctly formatted (your keys are different):
- File: .env
1 2 3 4
consumer_key=rA8yWhr4ZnuWoKGDmmdbhF4Su consumer_secret=BVMRjkQRTzlkBRC8qVZQKkWsEIEzPE9XAV1egY2D86yrQqadXH access_token=1399573085406306304-PkDdzGOQu6ikxbwbhpmsLoP4W87jaE access_token_secret=gf6ohLUT06RqcvfB4H6qQRZstcc6UgkouszIiLxHLGtGj
If the error persists after correcting any syntax issues, you may have copied your keys incorrectly. Review the Troubleshooting Twitter Error 401 Unauthorized section for further guidance.
Troubleshooting Twitter Error 401 Unauthorized
When starting the bot, you may see an error like this:
> snes-soundtracks@1.0.0 develop
> NODE_ENV=develop node snes.js
SNES Soundtracks booting up
node:events:342
throw er; // Unhandled 'error' event
^
Error: Bad Twitter streaming request: 401
at Object.exports.makeTwitError (/home/tutorialbotuser/snes-soundtracks/node_modules/twit/lib/helpers.js:74:13)
at Request.<anonymous> (/home/tutorialbotuser/snes-soundtracks/node_modules/twit/lib/streaming-api-connection.js:96:29)
at Request.emit (node:events:377:35)
at Gunzip.<anonymous> (/home/tutorialbotuser/snes-soundtracks/node_modules/request/request.js:1076:12)
at Object.onceWrapper (node:events:471:28)
at Gunzip.emit (node:events:365:28)
at endReadableNT (node:internal/streams/readable:1312:12)
at processTicksAndRejections (node:internal/process/task_queues:83:21)
Emitted 'error' event on StreamingAPIConnection instance at:
at Request.<anonymous> (/home/tutorialbotuser/snes-soundtracks/node_modules/twit/lib/streaming-api-connection.js:99:14)
at Request.emit (node:events:377:35)
[... lines matching original stack trace ...]
at processTicksAndRejections (node:internal/process/task_queues:83:21) {
code: null,
allErrors: [],
twitterReply: '<html>\\n<head>\\n<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>\\n<title>Error 401 Unauthorized</title>\n' +
'</head>\n' +
'<body>\n' +
'<h2>HTTP ERROR: 401</h2>\n' +
"<p>Problem accessing '/1.1/statuses/filter.json'. Reason:\n" +
'<pre> Unauthorized</pre>\n' +
'</body>\n' +
'</html>\n',
statusCode: 401
}
This error indicates that incorrect keys were copied into your .env
file. You may have also associated the .env
key names with the wrong credential from the Twitter developer portal. Review the table below for the correct associations between the .env
file and the Twitter developer portal credentials.
.env Key Name | Twitter Developer Portal Credential Name |
---|---|
consumer_key | API Key |
consumer_secret | API Secret Key |
access_token | Access Token |
access_token_secret | Access Token Secret |
If you’re not sure whether your keys are correct, you can regenerate them inside the Twitter developer portal. See the Regenerate API Keys and Tokens section of Twitter’s documentation for instructions.
Troubleshooting Twitter Read Only Application Cannot POST Error
When your bot attempts to tweet, you may see an error like this in its logs:
Read-only application cannot POST.
This error indicates that your app’s permissions are set to Read Only in the Twitter developer portal. Follow the instructions in the Give your Twitter App Read and Write Permissions. This section shows how to switch to Read and Write permissions and how to regenerate your Access Token and Access Token Secret.
More Information
You may wish to consult the following resources for additional information on this topic. While these are provided in the hope that they will be useful, please note that we cannot vouch for the accuracy or timeliness of externally hosted materials.
This page was originally published on