DEV Community

Cover image for Build an automatically updating Twitter Header with NodeJS and Twitter API
Dom the dev
Dom the dev

Posted on

Build an automatically updating Twitter Header with NodeJS and Twitter API

This tutorial shows you how to build an application which automatically updates your Twitter Header with your latest
followers.

GitHub Files: https://github.com/dom-the-dev/twitter-banner

Subscribe to my YouTube Channel

I also made Step-by-Step Video

List of contents

Twitter Elevated Access

Before we can start, we have to create an application on
the Twitter Developer's Platform (twitter account required). On
your Dashboard you should now see a small teaser asking you "Want more API access?". Click on "View products".
Alternatively, in the left panel click on "Products" and from the dropdown select Twitter API v2. You will then
automatically get to the Tab where you can apply for the elevated access. If not, in the top center of the page, choose
the elevated tab to see the button which says "Apply for Elevated".

Then you have to go through some steps where you need to fill out some information about you and your application. After
confirming the terms on the last page, you either get elevated access granted or not. So make sure to fill out the
information wisely.

Setup application

Now, we are ready to set up our node application. Make sure that node is installed.

Run node --version in your terminal. If you get a node version number printed, node is installed. If not, you need to
go to the node homepage and download the installer.

Once node is installed, we can create a new node application. In your terminal, create a new directory and switch into
it.

mkdir twitter-banner && cd twitter-banner
Enter fullscreen mode Exit fullscreen mode

Then run the following command to initialize a new npm project. This will create a package.json inside the project
directory.

npm init -y 
Enter fullscreen mode Exit fullscreen mode

Now you can open the project in your text-editor/IDEA.

In the root of the directory create a new file called index.js and add a simple console.log("hello world") in order
to test if node is running correctly.

Then in your terminal run the following command

node index.js
Enter fullscreen mode Exit fullscreen mode

Your terminal should now prompt "Hello world".

Twitter Client

Let's now create a Twitter Client, so we can simply communicate with the Twitter API. For that install a npm module
called twitter-api-v2.

npm i twitter-api-v2
Enter fullscreen mode Exit fullscreen mode

After the installation we can create our client. Create a file called twitterClient.js
and open it. Now we need to require the module and instantiate a new object with our twitter keys, which we get in a
second.

The code should look like this, where the current keys will be replaced by the actual keys.

const {TwitterApi} = require("twitter-api-v2")

module.exports = new TwitterApi({
    appKey: "<your-api-key>",
    appSecret: "<your-api-secret>",
    accessToken: "<your-access-token>",
    accessSecret: "<your-access-secret>"
})

Enter fullscreen mode Exit fullscreen mode

To get the keys we need to switch back to the developers platform of twitter. On the Dashboard you can click on "+
Create Project" and walk through the steps. On the last step you get the first keys displayed.

Copy and replace them with the keys in your client.

API Key = appKey

API Key Secret = appSecret.

In order to get the access token and the access secret we have to adjust some settings. On the overview page where your
project are listed click on the gear icon to get to the settings page of your app and click on Edit in the "User
authentication settings"-Section.

To be on the safe side, activate OAuth 2.0 and OAuth 1.0 and fill out the rest of the form. App permissions need to be
set at least to "Read AND Write permissions".

Important: You can't set the Website URL to localhost, instead you need to set your local ip address. You can find it
with your terminal with the following command:

Windows

ipconfig
Enter fullscreen mode Exit fullscreen mode

Linux/Mac

ifconfig
Enter fullscreen mode Exit fullscreen mode

At the end of the page click on save. You won't need the client ID and Secret which will be shown now. Instead go back
to the settings page of your app and choose the "Keys and Tokens" tab. In the section "Autentication Tokens" you now can
generate the Access Token and Secret with Read/Write persmissions. Click on generate and copy/paste the keys to your
client.

To test if the twitter client is working, let's create a test-tweet. Go to index.js and require the twitter client at
the top of the file.

(Remove the console.log() if you want)

const client = require("./twitterClient");
Enter fullscreen mode Exit fullscreen mode

And create a asynchronous function which calls the tweet method of our client where we paste the message to tweet. This
could look like this

async function testTweet() {
    await client.v2.tweet("Hi, I am building a twitter bot!")
}

testTweet()
Enter fullscreen mode Exit fullscreen mode

Now run node index.js again in your terminal. If everything works fine you can check your twitter account for this
tweet. You can now delete or comment this function out.

Get Followers

Now, let's create a new file twitterController.js where we gonna collect the methods, talking with the Twitter API. In
this file again require our twitter client:

const client = require("./twitterClient");
Enter fullscreen mode Exit fullscreen mode

So, the first step of creating our banner will be to get our latest followers. Let's create an async function called
getFollowers() where we call the followers method of our client and return the latest 5 followers:

async function getFollowers() {
    const followers = await client.v2.followers("<YOU-TWITTER-ID>")
    return followers.data.slice(0, 5)
}
Enter fullscreen mode Exit fullscreen mode

As you see here we need to paste the twitter-ID from our account. To find this, we can go
to tweeterid.com. Paste your twitter @ in the input field and click on convert. Then
copy/paste the ID to the followers function.

At the end of the file need to export this method:

module.exports = {getFollowers}
Enter fullscreen mode Exit fullscreen mode

Switch to index.js and require this method:

const {getFollowers} = require("./twitterController")
Enter fullscreen mode Exit fullscreen mode

Let's already create the wrapper function for the banner-process where we are going to save the followers in a variable:

async function generateBanner() {
    const followers = await getFollowers()
}
Enter fullscreen mode Exit fullscreen mode

Save Follower Images

The next step is to get the avatars of your followers, and save them to the filesystem. Let's do so by create a new
async function so called getProfileImageUrl in the twitterController.js

This function accepts one parameter, the user ID of a follower, and returns the profile image URL.

async function getProfileImageUrl(user_id) {
    const {profile_image_url} = await client.v1.user({user_id})
    return profile_image_url
}
Enter fullscreen mode Exit fullscreen mode

Add this function to the exports:

module.exports = {getFollowers, getProfileImageUrl}
Enter fullscreen mode Exit fullscreen mode

Now create a new file called imageController.js, here we are going to collect all methods which manipulate images.

For this steü, we need to install two more packages, axios
and sharp

npm i axios sharp
Enter fullscreen mode Exit fullscreen mode

Require them at the top of imageController.js

const axios = require("axios")
const sharp = require("sharp")
Enter fullscreen mode Exit fullscreen mode

Now the next function is going to fetch the images with axios as an arrayBuffer and paste this to the sharp
method, which helps us to the images to our file system with the resolution 100x100.
The function gets two params, a url and a name. The url will be the profile-image-url we get from the previous function. The name is used to save the image.
Don't forget to export this function.

async function saveImage(name, url) {
    const {data} = await axios.get(url, {
        responseType: "arraybuffer"
    })

    await sharp(data).resize(100, 100).toFile(`./images/${name}.png`)
}

module.exports = {saveImage}
Enter fullscreen mode Exit fullscreen mode

Let's combine the two methods in index.js to finally save the follower avatars.

In the generateBanner() method, we are going to iterate over the followers array which we already have with a for/of loop.
We use the for/of since we can you async inside of it. For each follower we gonna get the profile-image-url and paste it to the saveImage() method in order to save the images.

async function generateBanner() {
    const followers = await getFollowers()

    for(const follower of followers) {
        const url = await getProfileImageUrl(follower.id)
        await saveImage(follower.id, url)
    }
}
Enter fullscreen mode Exit fullscreen mode

Before you run this, you need to create an images directory. Otherwise sharp doesn't know where to save the images.
If you run generateBanner() now you should see the images saved to your file system.

Create Banner

For this step, you are going to need a twitter template. It needs to have the resolutions 1500x500.
You can find mine example here. Save it to the root of your directy.
In my example i am going to name it banner.jpg.

On this template we are now going to place the follower images at. To do so we need to install another npm package
so called Jimp.

npm i jimp
Enter fullscreen mode Exit fullscreen mode

Besides this module we need to add promise based version of fs from node in order to perform async operations on.
Add them at the top of imageController.js like this:


const axios = require("axios")
const sharp = require("sharp")
const Jimp = require("jimp")
const fs = require("fs")
const fsPromises = fs.promises

Enter fullscreen mode Exit fullscreen mode

The createBanner() function which we now create, uses Jimp to create a new image from our templatebanner.jpg.
Then it iterates over all saved avatars and places them on the new created banner-image.
An index variable will be used to move each picture a bit so they won't be places on top of each other. At the end the new file will be saved.

This function should look like this:

async function createBanner() {
    const banner = await Jimp.read("./banner.jpg")
    const files = await fsPromises.readdir("./images")

    let index = 0;
    for (const avatar of files) {
        const imgPath = `./images/${avatar}`
        const image = await Jimp.read(imgPath)

        const position = 475 + index * (100 + 10);
        banner.composite(image, position, 380);

        index++
    }

    await banner.writeAsync("./final.png");

}

module.exports = {saveImage, createBanner}
Enter fullscreen mode Exit fullscreen mode

Add this it to index.js which now should look like this:


const {getFollowers, getProfileImageUrl} = require("./twitterController")
const {saveImage, createBanner} = require("./imageController");

async function generateBanner() {
    const followers = await getFollowers()

    for(const follower of followers) {
        const url = await getProfileImageUrl(follower.id)
        await saveImage(follower.id, url)
    }

    await createBanner()
} 

generateBanner()

Enter fullscreen mode Exit fullscreen mode

You can again test-run this method to check if the new banner will be created correctly. Look for final.png

Update Banner

Let's upload this cool header to our twitter profile now!

In twitterController.js create the updateBanner() method. Here we are calling the update profile banner method, paste the path to our image and the resolutions:

async function updateBanner() {
    await client.v1.updateAccountProfileBanner("./final.png", {
        width: 1500,
        height: 500
    })
}

Enter fullscreen mode Exit fullscreen mode
module.exports = {getFollowers, getProfileImageUrl, updateBanner}
Enter fullscreen mode Exit fullscreen mode

Require this method and call it in generateBanner() in index.js

const {getFollowers, getProfileImageUrl, updateBanner} = require("./twitterController")
const {saveImage, createBanner} = require("./imageController");

async function generateBanner() {
    const followers = await getFollowers()

    for(const follower of followers) {
        const url = await getProfileImageUrl(follower.id)
        await saveImage(follower.id, url)
    }

    await createBanner()
    await updateBanner()
}

generateBanner()
Enter fullscreen mode Exit fullscreen mode

Again, you can test run this with node index.js in your terminal. When you now switch to your browser and reload your twitter profile,
you should be able to see the new banner.

Cron Job

The last step will be, to create a so called CronJob which performs given actions at a given time. In our example we are going
to check for the latest followers every minute.

Install the npm package cron:

npm i cron
Enter fullscreen mode Exit fullscreen mode

And require it at the top of index.js like that:

const CronJob = require("cron").CronJob
Enter fullscreen mode Exit fullscreen mode

Now we create a new object from the CronJob class, where we pass two params.
The first one is a string and declares, when the job has to run.
A great tool to set up the time is CronTab Guru.
The second parameter is the callback function which we want to be called.
In our case the generateBanner() method. Start the job by calling the start() method of our job object

console.log('starting node app')
const job = new CronJob('* * * * *', async function() {
console.log('start generating banner')
generateBanner()
})

job.start()
Enter fullscreen mode Exit fullscreen mode

And that's it! If you are facing any issues, leave some feedback in the comments or hit me up on twitter @Dom_TheDEv

Top comments (0)