How to make a Twitter bot with node.js and Cloud9 (and OpenShift)

Need help with this tutorial? Join the Botmakers community!

In this tutorial, I’m going to guide you through the creation of a simple Twitter bot that will randomly select and post a country flag and wait for someone to respond with the correct name of the matching country. You can check out the finished bot on Twitter.

We are going to use the following tools:

Quick note: You might need to disable your adblocking addon/plugin for the Cloud9 website. At least I had a small problem when adding a new project.

Also: I wrote this tutorial to help beginner coders join the Monthly Bot Challenge. Be sure to join the Botmakers community to share ideas and ask for help, and follow @botwikidotorg for updates!

Creating a Twitter app

The way you make bots on Twitter is that you first create a new account, which is going to be the actual bot, and a Twitter app, through which you will control this bot.

So let’s start by creating a new Twitter account. This is relatively straightforward. The only tricky part is that if you want your app to be able to post to Twitter, rather than just read from it, you will need to add a phone number to your account. If you already associated your phone number with your main account, see the Note on needing a phone number section of the Twitter bot tutorials page for possible solutions.

After you create your account, go to and create a new app.

The information here won’t really show up anywhere that’s relevant for us. Once you’re done here and land on the next page, switch to the Keys and Access Tokens tab. Under Application Settings, make sure that it says Read and write for your app’s Access level.

In a moment, we are going to need four things from this page:

  • Consumer Key
  • Consumer Secret
  • Access Token
  • Access Token Secret

You need to click the button in the Your Access Token section to generate the last two. Done? Perfect, let’s keep this page open for now.

Signing up for Cloud9

As explained earlier, Cloud9 is a browser-based IDE, or Integrated Development Environment. All this means is just that you can use it to write code, and most of the other things, like setting up your working environment and deploying your app to a server will be taken care of for you.

Cloud9 offers a free plan, which gives us more than enough for our simple Twitter bot.

So let’s sign up for our free account. After we fill out all the necessary information, we should land in our Workspaces view.

Creating a Cloud9 project

Click Create a new workspace and fill out your app’s information.

Make sure to select the Private option for your workspace, and under Choose a template, click Node.js. Finally, hit the Create workspace button. As a reminder, if the site takes a while to create your project, make sure your adblocker is disabled and try again.

After a few seconds, your IDE should load and you should see something like this:

I will add links that go into more details, but for now, I’m just going you quickly run you through the basics so you can get an idea what making bots is all about.

The most important things to know are: you can see your files and folders on the left. server.js is where you will write your Twitter bot code. The tab at the bottom where it says “bash” is the command line, and we will use that to install the Twit module.

Installing the Twit package

We’re going to use our command line to install Twit, which, as I mentioned, is a node.js module for interacting with Twitter through their API (Application Programming Interface. Simply put, Twit is a sort of a gateway between Twitter and your bot.

Node comes with a very useful tool called npm (What is npm?) that lets you easily download modules for your project.

Go to the bash tab and type npm install twit. This command asks npm to install a module called twit. Yep, it’s simple as that.

Now for the fun part!

Writing code

Right-click into the area with files and folders on the left and click New file. Name it config.js. This will be your configuration file and we’ll put your Twitter API keys here. (Note that you can easily right-click the file and click Rename if you make a mistake.)

You can think of API keys as, well, keys for your bot to access the API, which, again, in turn lets you access data from Twitter and post to it.

bot (server.js) -> Twit -> API keys -> Twitter API -> Twitter

Copy and paste this into your config.js file:

var config = {
  consumer_key:         'XXXXX',
  consumer_secret:      'XXXXX',
  access_token:         'XXXXX',
  access_token_secret:  'XXXXX'

module.exports = config;

Remember the Twitter tab we left open? Go back to it and copy the Consumer Key, Consumer Secret, Access Token and Access Token Secret and match them up with the code above, for example, replace the XXXXX after consumer_key with the value for Consumer Key, and so on.

If you closed the tab in the meantime, you can go to your Twitter apps page, open your app and switch to the Keys and Access Tokens tab.

Now, switch to your server.js file and delete everything inside it and replace it with the following.

var fs = require('fs'),
    path = require('path'),
    Twit = require('twit'),
    config = require(path.join(__dirname, 'config.js'));

var T = new Twit(config);'statuses/update', {
    status: 'Hello world!'
  function(err, data, response) {
    if (err){

Let me explain a few things. require is a command in node.js that loads libaries and files. This allows us to load the Twit library we installed, but also the configuration file with our API keys. fs and path are node.js modules that will help our script find our files, so that we can, for example, easily load our configuration file.

I will include links to some JavaScript and node.js tutorials at the end, but for now, the code above can be simplified to something like this:



  text: 'Hello world!'
}, and_when_you_re_done(){

When you’re ready, click the Run button at the top of the page.

If you followed the tutorial closely, you should see your bot make its first tweet.


If something went wrong, you will see an error message, for example, if you don’t copy the API keys correctly, you won’t be able to log into Twitter and you will see something like this:

Adding images

So we decided that our bot will upload a flag of a country and wait until someone responds to our tweet with the name of the capital of the matching country.

I found a pretty good site called, which lets you download over 200 flags, for free.

In the top menu, click File, then Upload Local Files. Go back to the .zip archive with the flags, extract the files and upload them to Cloud9. Make sure to name the folder flags.

Using a neat little library called countries-list, I created a list of about 200 countries and their capitals. Download the list from here and also upload it to your project.

And here is my finished and annotated server.js file. Let me quickly explain the main parts of the code.

We are going to load the list of countries similarly to how we loaded our API keys.

var Twit = require('twit'),
    config = require('/home/ubuntu/workspace/config.js'),
    list_of_countries = require('/home/ubuntu/workspace/list_of_countries.js')

var fs = require('fs'),
    path = require('path'),
    Twit = require('twit'),
    list_of_countries = require(path.join(__dirname, 'list_of_countries.js')),
    config = require(path.join(__dirname, 'config.js'));

This function will choose a random country from our list. I could go a bit further and keep a list of already used countries to avoid using the same country twice, but this is good enough for our tutorial.

function pickRandomCountry() {
  return list_of_countries[Math.floor(Math.random() * list_of_countries.length)];

To make things simple, we’re going to keep a global variable current_country which will hold the most recently randomly picked country, and this function will check if the tweeted response matches the answer.

function checkAnswer(answer){
  return (answer.toLowerCase().indexOf( > -1);

As I explained earlier, the Twit library lets us communicate with Twitter by using its API. Here’s an example:

    screen_name: 'tolga_tezel'
  }, function (err, data, response) {

Here, we are using the GET followers/ids method. So we need to find a method that lets us upload images: POST media/upload.

Now, this part is a bit more advanced, but essentially works the same way. The POST media/upload method requires to pass in the uploaded image as a base64-encoded file. This is really easy to do using the fs and path modules.

current_country = pickRandomCountry();

var imagePath = path.join(__dirname, '/flags/' + + '.png'),
    b64content = fs.readFileSync(imagePath, { encoding: 'base64' });

console.log('Uploading flag...');'media/upload', { media_data: b64content }, function (err, data, response) {
  if (err){
    console.log('Posting the flag...');'statuses/update', {
      media_ids: new Array(data.media_id_string)
      function(err, data, response) {
        if (err){
          console.log('Waiting for someone to answer...');

Some more ideas to explore:

  • you could save the score for each player into a Google Sheets spreadsheet (see the google-spreadsheet module); then you could post the leaderboard after each game
  • you could add more responses to both when the answer is correct and incorrect (using the same technique as for picking a random country)
  • you could let the player know if they probably just misspelled the answer (see the levenshtein module, or learn more about Levenshtein distance on Wikipedia)

This bot can also be “repurposed” and turned into similar games, for example, you could make a bot that posts outlines of countries and the players need to identify them. You could post images of famous people, landmarks, animals, etc. You could even make the game fully text-based and create something like @TheRiddlerBot.

Further reading


Earlier I mentioned the need for making your workspace “private”. This is mainly so that you don’t expose your private Twitter API keys. Anyone who can see those could easily take over your bot and use it to spam people.

Using the free plan, Cloud9 only allows you to have one private workspace, which is still very generous. If you want to use Cloud9 to host another bot, you will have to make your additional workspaces public.

Another complication comes from the fact that on the free Cloud9 plan, your app will become idle if it doesn’t get any web traffic for a while. You have two options here: you can subscribe to the $19/month plan, or you could use OpenShift to actually host and run your app, while you’d still edit the code using Cloud9. Note that you will have to “upgrade” to the free Bronze plan, which, again, is free, but it does require you to add your credit card information anyway.

Deploying to OpenShift

This section is work in progress.

Integrating with OpenShift is actually fairly straightforward. I recommend downloading your config.js file, deleting your current Cloud9 project and creating a new one. (Below I will add a link to an updated version of @what_capital).

Also, we’re going to be using git, so here is a very quick overview of what git is.

  1. Sign up for a new OpenShift account and switch to the free Bronze plan.
  2. Create a new node.js app.
  3. Copy your Cloud9 SSH key and add it to your OpenShift account (this is essentially needed for the two sites to communicate).
  4. When you open your OpenShift app page, on the right side, you will see a text field under Source Code and above Pass this URL to ‘git clone’ to copy the repository locally. It will look something like ssh://
  5. Go back to the Cloud9 command line, after you create a new project, and insert the code below, updated with the correct ssh://... line:
git init
git remote add openshift -m master ssh://
  1. Next, we’re going to copy over the OpenShift default node.js project:
git pull openshift master
  1. Download the source code for @what_capital (slightly modified to run on OpenShift), upload it to your project folder and run:
git add -A
git commit
git push openshift master
  1. I highly recommend looking at some git tutorials, but for simplicity, you can use the commands from the above step every time you want to update your bot, after you update your code.

More tutorials

A tinted, zoomed in screenshot of a JSON object showing server information about a Mastodon instance.
A tinted screenshot of two charts, one showing the popularity of various fediverse platforms (with Mastodon far ahead of the rest), and the other chart showing distribution of domain creation dates, mostly clustered around 2023.
A tinted screenshot showing the @mtaupdates Mastodon profile and a few example posts with subway status alerts.

💻 Browse all