Build an automatically updating Twitter Header with NodeJS and Twitter API
Dom the dev
Posted on March 9, 2022
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
I also made Step-by-Step Video
List of contents
- Twitter Elevated Access
- Setup application
- Twitter client
- Get Followers
- Save Follower Images
- Create Banner
- Update Banner
- Cron Job
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
Then run the following command to initialize a new npm project. This will create a package.json inside the project
directory.
npm init -y
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
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
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>"
})
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
Linux/Mac
ifconfig
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");
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()
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");
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)
}
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}
Switch to index.js
and require this method:
const {getFollowers} = require("./twitterController")
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()
}
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
}
Add this function to the exports:
module.exports = {getFollowers, getProfileImageUrl}
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
Require them at the top of imageController.js
const axios = require("axios")
const sharp = require("sharp")
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}
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)
}
}
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
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
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}
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()
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
})
}
module.exports = {getFollowers, getProfileImageUrl, updateBanner}
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()
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
And require it at the top of index.js
like that:
const CronJob = require("cron").CronJob
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()
And that's it! If you are facing any issues, leave some feedback in the comments or hit me up on twitter @Dom_TheDEv
Posted on March 9, 2022
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.