Tuesday 26 September 2017

Using twitters user timeline API in Node JS

Having continued to create my dashboard that allows me to access information thats useful to me in a single, easy to access location, I began to think about how I could get my twitter timeline to display on screen (messages that I post and messages people I follow post).

Normally I like to try to just do a curl GET of the webpage I'm looking to use but twitter requires an oauth key to do even a get on your own profile. *sigh*.

Since I've started developing Twitter have changed their entire web based content to a flash new website, a few new APIs and a new "feel".
New Twitter Site

The first thing we need to become a twitter developer is to create an application on https://apps.twitter.com which requires a login and for your account to have a linked mobile number. You'll need to give the application a name and a website address where the application will later ve accessible (this doesn't have to be real and can be changed later).

You will then be given application settings information complete with access token, and security keys, specifically within the "Keys and access tokens" tab.
Application Settings
-----------------------------------------------------
What I specifically wanted to do in my dashboard was display a few of the latest tweets from my home timeline which was surprisingly difficult to figure out as most use cases seem to be around mass production of tweets and making "bots" and a lot of the documentation for node module implementation on node js didn't really describe what was happening.

It's worth noting that when thinking about how I wanted to achieve this I had to consider twitters rate limiting for per access token usage. Which comes to 15 calls per application/access token per 15 minute window, meaning realistically we could make one call a minute.

Initially I just wanted to get set up and the easiest way to do so seemed to be with an existing node module called (wait for it...) twitter npm. https://www.npmjs.com/package/twitter. Following their instructions I took a plain old Node Js application and added the following code using the oauth tokens we get in our application settings:
var Twitter = require('twitter');
var client = new Twitter({
      consumer_key: 'XXX',
      consumer_secret: 'XXX',
      access_token_key: 'XXX',
      access_token_secret: 'XXX'
    });
At this point there are two options for receiving tweets from our timeline and this is to use the Stream API or GET API.

STREAM: Creates a single connection that receives data as it is pushed from twitter, this allows us to process the data in real time and not make continuous calls to twitter.

GET: Consists of standard HTTPS GET calls as and when we want to get information from twitter.

Whilst streaming gives us the most up to date data from twitter, it comes with additional complexity of having an open connection between your server and twitter that must be secured and kept alive which will generally require a bit more thought than an evenings coding. GET on the other hand suits my purpose well and allows me to call and handle data as and when I want.

For fullness I will show how to implement both:
----------------------------------------------------------------------------------------------------------
STREAM User Timeline:
client.stream('user', {track: 'GallagherAiden'}, function(stream) {
      stream.on('data', function(tweet) {
        console.log("got the following twitter message: " + JSON.stringify(tweet));
        var createdTime = tweet.created_at;
        createdTime = createdTime.substring(0,16);
        var tweetStream = createdTime + " " + tweet.user.name + " : " + tweet.text;
      });
      stream.on('error', function(error) {
        if(error){
          log.dashErr(error);
        }
         console.err("Could not get any twitter feeds");
        });
      });
    });
Here we use the twitter npm ".stream" functionality with the client defining that we want a "user"s stream and that that users handle is "GallagherAiden".

We then use "stream.on" to handle the incoming data which I then stringify and make suitable for my needs. i.e. in the following format:
Tue Sep 26 19:04 Gary Lineker - Gorgeous finish from @GarethBale11 gives Real Madrid the lead in Dortmund.
----------------------------------------------------------------------------------------------------------
GET User Timeline:
client.get('statuses/home_timeline', function(error, tweets, response) {
      if(error){
       console.log(error);
      }
var thisTweet = tweets[0];
var createdTime = thisTweet.created_at;
createdTime = createdTime.substring(0,16);
var tweetGet = createdTime + " " + thisTweet.user.name + " - " + thisTweet.text;
console.log(tweetGet);

Here we use 'statuses/home_timeline' which responds either with the tweets, an error or the response. This relates directly to the home timeline of the user who is linked to the application. There is no way to alter this.
The above code would give the same output as the stream in terms of formatting.
----------------------------------------------------------------------------------------------------------

The output of a single tweet is extremely large! 
{
    "created_at": "Mon Sep 25 06:22:53 +0000 2017",
    "id": 912200715552124900,
    "id_str": "912200715552124928",
    "text": "Photographing the Peak District in autumn \u2013 in pictures https://t.co/mHSVxMpNt2",
    "truncated": false,
    "entities": {
      "hashtags": [],
      "symbols": [],
      "user_mentions": [],
      "urls": [
        {
          "url": "https://t.co/mHSVxMpNt2",
          "expanded_url": "https://trib.al/hCIqP6P",
          "display_url": "trib.al/hCIqP6P",
          "indices": [
            56,
            79
          ]
        }
      ]
    },
    "source": "<a href=\"http://www.socialflow.com\" rel=\"nofollow\">SocialFlow</a>",
    "in_reply_to_status_id": null,
    "in_reply_to_status_id_str": null,
    "in_reply_to_user_id": null,
    "in_reply_to_user_id_str": null,
    "in_reply_to_screen_name": null,
    "user": {
      "id": 87818409,
      "id_str": "87818409",
      "name": "The Guardian",
      "screen_name": "guardian",
      "location": "London",
      "description": "The need for independent journalism has never been greater. Become a Guardian supporter: https://t.co/EWg9aqA7PQ",
      "url": "https://t.co/c53pnmnuIT",
      "entities": {
        "url": {
          "urls": [
            {
              "url": "https://t.co/c53pnmnuIT",
              "expanded_url": "https://www.theguardian.com",
              "display_url": "theguardian.com",
              "indices": [
                0,
                23
              ]
            }
          ]
        },
        "description": {
          "urls": [
            {
              "url": "https://t.co/EWg9aqA7PQ",
              "expanded_url": "http://gu.com/supporter/twitter",
              "display_url": "gu.com/supporter/twit\u2026",
              "indices": [
                89,
                112
              ]
            }
          ]
        }
      },
      "protected": false,
      "followers_count": 6791634,
      "friends_count": 1113,
      "listed_count": 54290,
      "created_at": "Thu Nov 05 23:49:19 +0000 2009",
      "favourites_count": 152,
      "utc_offset": 3600,
      "time_zone": "London",
      "geo_enabled": false,
      "verified": true,
      "statuses_count": 378671,
      "lang": "en",
      "contributors_enabled": false,
      "is_translator": false,
      "is_translation_enabled": true,
      "profile_background_color": "FFFFFF",
      "profile_background_image_url": "http://pbs.twimg.com/profile_background_images/704160749/ff996aa3bc2009a2f9b97cdd43e8b5b7.png",
      "profile_background_image_url_https": "https://pbs.twimg.com/profile_background_images/704160749/ff996aa3bc2009a2f9b97cdd43e8b5b7.png",
      "profile_background_tile": false,
      "profile_image_url": "http://pbs.twimg.com/profile_images/877153924637175809/deHwf3Qu_normal.jpg",
      "profile_image_url_https": "https://pbs.twimg.com/profile_images/877153924637175809/deHwf3Qu_normal.jpg",
      "profile_banner_url": "https://pbs.twimg.com/profile_banners/87818409/1491899430",
      "profile_link_color": "005789",
      "profile_sidebar_border_color": "FFFFFF",
      "profile_sidebar_fill_color": "CAE3F3",
      "profile_text_color": "333333",
      "profile_use_background_image": false,
      "has_extended_profile": false,
      "default_profile": false,
      "default_profile_image": false,
      "following": true,
      "follow_request_sent": false,
      "notifications": false,
      "translator_type": "regular"
    },
    "geo": null,
    "coordinates": null,
    "place": null,
    "contributors": null,
    "is_quote_status": false,
    "retweet_count": 1,
    "favorite_count": 3,
    "favorited": false,
    "retweeted": false,
    "possibly_sensitive": false,
    "possibly_sensitive_appealable": false,
    "lang": "en"
  }
 ----------------------------------------------------------------------------------------------------------
I have implemented this in a way that calls from the server using a "setInterval" ever 60 seconds which keeps me within my rate limit:
setInterval(function(){
  getTwitterFeed();
}, 60000);
I then do a web client GET of this data in order to populate my dashboard. I'm currently working with the last five tweets and will probably post how I'm doing that in a later post.

For now. Adieu. I hope you enjoyed reading, feel free to tell me where I'm wrong and how I can improve. you can email me at aiden.g@live.co.uk  or leave me a comment.

Twitter Feed on my dashboard, reloading every sixty seconds

No comments:

Post a Comment