MooTools TwitterGitter Plugin

By  on  
TwitterGitter

Everyone loves Twitter. Everyone loves MooTools. That's why everyone should love TwitterGitter, a MooTools plugin that retrieves a user's recent tweets and allows the user to format them however the user would like. TwitterGitter allows the user to choose the number of tweets to retrieve and returns an object containing the data provided by Twitter.

Twitter's Response Object

JsonP.requestors.request_0.handleResults(
	[
		{
			"created_at":"Mon Feb 09 23:38:33 +0000 2009",
			"user": {
				"description":"",
				"url":"http:\/\/davidwalsh.name",
				"name":"davidwalshblog",
				"protected":false,
				"followers_count":639,
				"profile_image_url":"http:\/\/s3.amazonaws.com\/twitter_production\/profile_images\/57860553\/footer-logo_normal.jpg",
				"location":"Madison, WI, US",
				"screen_name":"davidwalshblog",
				"id":15759583
			},
			"in_reply_to_user_id":null,
			"in_reply_to_status_id":null,
			"in_reply_to_screen_name":null,
			"truncated":false,
			"text":"Just got home from work.  Enjoying a Heineken.  Going to write some articles.",
			"id":1193654096,
			"favorited":false,
			"source":"<a href=\"http:\/\/www.netvibes.com\/subscribe.php?module=Twitter\">Netvibes<\/a>"
		},
		{
			"in_reply_to_screen_name":null,
			"user": {
				"description":"",
				"screen_name":"davidwalshblog",
				"followers_count":639,
				"url":"http:\/\/davidwalsh.name",
				"name":"davidwalshblog",
				"protected":false,
				"profile_image_url":"http:\/\/s3.amazonaws.com\/twitter_production\/profile_images\/57860553\/footer-logo_normal.jpg",
				"location":"Madison, WI, US",
				"id":15759583
			},
			"created_at":"Mon Feb 09 21:58:00 +0000 2009",
			"truncated":false,
			"favorited":false,
			"in_reply_to_user_id":null,
			"text":"Yep, it's decided...writing articles tonight.",
			"id":1193352388,
			"in_reply_to_status_id":null,
			"source":"web"
		},
		//....
	]
);

Above is a sample of what is returned by Twitter.

The MooTools Plugin

var TwitterGitter = new Class({
	
	//implements
	Implements: [Options,Events],

	//options
	options: {
		count: 2,
		sinceID: 1,
		link: true,
		onRequest: $empty,
		onComplete: $empty
	},
	
	//initialization
	initialize: function(username,options) {
		//set options
		this.setOptions(options);
		this.info = {};
		this.username = username;
	},
	
	//get it!
	retrieve: function() {
		new JsonP('http://twitter.com/statuses/user_timeline/' + this.username + '.json',{
			data: {
				count: this.options.count,
				since_id: this.options.sinceID
			},
			onRequest: this.fireEvent('request'),
			onComplete: function(data) {
				//linkify?
				if(this.options.link) {
					data.each(function(tweet) { tweet.text = this.linkify(tweet.text); },this);
				}
				//complete!
				this.fireEvent('complete',[data,data[0].user]);
			}.bind(this)
		}).request();
		return this;
	},
	
	//format
	linkify: function(text) {
		//courtesy of Jeremy Parrish (rrish.org)
		return text.replace(/(https?:\/\/\S+)/gi,'<a href="$1">$1</a>').replace(/(^|\s)@(\w+)/g,'$1<a href="http://twitter.com/$2">@$2</a>').replace(/(^|\s)#(\w+)/g,'$1#<a href="http://search.twitter.com/search?q=%23$2">$2</a>');
	}
});

Note: you will need to download Aaron Newton's JSONP plugin here.

Parameters include:

  • username: Your Twitter user handle.

Options of the TwitterGitter class include:

  • count: (defaults to 2) The number of tweets you would like returned.
  • sinceID: (defaults to 1) The baseline for the tweets to be returned.
  • link:: (defaults to true) Want the class to linkify URLs, @ replies, and #topics?

Events include:

  • onRequest: The function to execute when the TwitterGitter request is made.
  • onComplete: The function to execute when the TwitterGitter request is complete. This is where you want to put your tweet formatting.

Sample Usage

window.addEvent('domready',function() {
	$('git').addEvent('click',function(e) {
		e.stop();
		$('tweets-here').set('html','');
		//get information
		var myTwitterGitter = new TwitterGitter($('username').value,{
			count: 5,
			onComplete: function(tweets,user) {
				tweets.each(function(tweet,i) {
					new Element('div',{
						html: '<img src="' + user.profile_image_url.replace("\\",'') + '" align="left" alt="' + user.name + '" /> <strong>' + user.name + '</strong><br />' + tweet.text + '<br /><span>' + tweet.created_at + ' via ' + tweet.source.replace("\\",'') + '</span>',
						'class': 'tweet clear'
					}).inject('tweets-here');
				});
			}
		}).retrieve();
	});
});

Make sure to check out the demo. If you happen to use this, be sure to post a link -- I'd love to see what you've done. Happy tweeting!

Recent Features

Incredible Demos

  • By
    Face Detection with jQuery

    I've always been intrigued by recognition software because I cannot imagine the logic that goes into all of the algorithms. Whether it's voice, face, or other types of detection, people look and sound so different, pictures are shot differently, and from different angles, I...

  • By
    Google Font API

    Google recently debuted a new web service called the Font API.  Google's Font API provides developers a means by which they may quickly and painlessly add custom fonts to their website.  Let's take a quick look at the ways by which the Google Font...

Discussion

  1. While I like this little class the obvious problem is that you don’t cache the results on your server making a request to twitter on every pageload by any user. This probably gets your domain blocked fast if you have a high volume website.

  2. So everytime the git link is clicked, a new instance of TiwtterGetter is created? Even if you abstract that portion out, and just call myTwitterGitter. retrieve() on every click, a new instance of jsonp is created.

    You should probably write a second class to handle the links’ interaction with the twittergetter class, it would be very small, but it would separate some of the concerns.

    Valerio talks about reusing the objects in this blog entry http://mootools.net/blog/2008/02/05/mootools-classes-how-to-use-them/ – If I understand correctly, moo.fx was created because he wanted to reuse instances instead of creating new ones (scriptaculous’ [old]method).

  3. @Chris: Good point with caching. I’ll look for a solution.

    @EmEhRKay: I understand your concern, but when putting this together, my thought was how bloggers could use this to pull their updates into their blog — NOT using this for a ton of users. I meant to keep this simple.

  4. @Chris: All requests are initiated on the client side with this script. Your server never contacts Twitter, so there is no danger there.

  5. Alelo

    a little ajax in the class with a php script+mysql should solve it or?

  6. @Jeremy Parrish: Good call.

    @Alelo: I was thinking the same thing.

  7. Trying to cache something on your server is pointless for this. The only way I could see caching anything would be to use cookies, but why bother?

  8. Yes, an optional php file for login and password and user agent would be greatly appreciated by the lazy.

  9. @David – I understand that, and I love your blog for it (stalking remember? lol), and I love the fact that you come up with so many cool concepts and techniques. The thing that gets me is that I feel sometimes the examples do not necessarily program to the pattern.

    It would be very easy for you to create and reuse a single instance of the class — you’re pretty much a MooTools front man, your code is go beyond “oh this is cool” and add “this is the correct way its done.” With your example, I see no need to create the TwitterGitter class if a new one is created everytime an update is requested from the Twitter server, it could just be a function if it is giong to act in that manor. If I remember correctly, there is a way to bind a class instance with an event allowing for great reuse, but maybe you can write about that some more in the future.

    Anyway, keep it up — I’m a fan and avid page count ++er :)

  10. Nice work as usual!

  11. @EmEhRKay: I may create a 1.1 very soon that allows you to pass the username to the “retrieve” method — that will make it much more extendable.

  12. I get what EmEhRKay is saying. If you reorganize the initialize function, we can have only one JsonP object, and simply called retrieve on it:

    //initialization
    initialize: function(username,options) {
        //set options
        this.setOptions(options);
        this.info = {};
        this.username = username;
        this.jsonP = new JsonP('http://twitter.com/statuses/user_timeline/' + this.username + '.json',{
            data: {
                count: this.options.count,
                since_id: this.options.sinceID
            },
            onRequest: this.fireEvent('request'),
            onComplete: function(data) {
                //linkify?
                if(this.options.link) {
                    data.each(function(tweet) { tweet.text = this.linkify(tweet.text); },this);
                }
                //complete!
                this.fireEvent('complete',[data,data[0].user]);
            }.bind(this)
        });
    },  
    //get it!
    retrieve: function() {
        this.jsonP.request();
        return this;
    }
    

    and on domready, create a single instance of TwitterGitter, and have the onclick only call retrieve:

    window.addEvent('domready',function() {
    var myTwitterGitter = new TwitterGitter($('username').value,{
        count: 5,
        onComplete: function(tweets,user) {
            tweets.each(function(tweet,i) {
                new Element('div',{
                    html: '<img src="%27%20+%20user.profile_image_url.replace%28" \\="" ,="" )="" +="" alt="' + user.name + '" align="left"> <strong>' + user.name + '</strong><br>' + tweet.text + '<br><span>' + tweet.created_at + ' via ' + tweet.source.replace("\\",'') + '</span>',
                    'class': 'tweet clear'
                }).inject('tweets-here');
            });
        }
    });
    
    $('git').addEvent('click',function(e) {
        e.stop();
        $('tweets-here').set('html','');
        //get information
        myTwitterGitter.retrieve();
    });
    });
  13. Nice Job. The caching issue, as it has already been pointed out is a non issue. The only thing that twitter requests to play friendly is to send since_id. How the class currently works I don’t see this as an issue. If the class was modified to auto refresh tweets ( a periodical ). It would be necessary to update the oncomplete to update the since_id.

  14. Longtime reader, first time poster.
    Just wanted to say keep up the good work. Great blog with unique content!

    -Michael

  15. @Jeremy Parrish

    You don’t understand my concerns. The Referrer is most probably sent with the request or at least it is possible for twitter to identify the domain from which the calls come from. If you, for example, integrate this script into the sidebar of digg (=a high volume website) and show the five latest messages of a user on every page request by every user of digg you’d certainly get banned by the twitter api as you make to many requests to the twitter api. That is why you always should cache such things on your server and request it from there. Another thing is when the service wents down (= quite usual with twitter), you’d still have a cached version to show.

    See: “I keep hitting the rate limit. How do I get more requests per hour?” on Twitter APIs

  16. @Chis: Maybe you’re right. It looks like they might limit requests per Twitter account, too. I thought it was only by IP address. They could also check the referrer, but I doubt they would since it can be easily spoofed. AFAIK, there is no fool proof way to tell where the request came from other than by client IP address.

    Either way, I think if you have high enough volume for it to matter, you should probably be doing this on the server side anyway. Caching is easy in that case. Caching doesn’t make sense at all for this plugin.

  17. So why no just use hash cookie to write the last 5 messages you receive to a cookie as an object and date stamp it? I know cookies are ‘precious’ and should not be abused, but this is 5 x 140 char so that seems pretty sensible to me…

  18. Elkalidi Abdelkader

    Thanks for sharing David, but I think the Cross-Domain is not a good idea, especially when we tried to call a page that we do not control, XSS will be a cochmar.

  19. Alelo

    the simple way is to make a php script with a 2? field table 1. Timestamp 2. twitter request
    class -> ajax -> php script
    1. if yet dif to timestamp is > 5mins? new request and insert request+ timestamp into table
    2. if yet dif to timestamp is < 5mins? request is taken of table

    so it saves bandwith if request is not older then 5 mins f.e.

    and 5 mins should be long enouth or?

  20. so what is wrong with storing 700 bytes in a cookie, i guess with 20 cookies per domain I fail so see why the simple solution is to not just “cache” the stuff in an object there?

  21. @Daniel Buchner: You could certainly cache with cookies, but there’s not much benefit. The only way it would help is if a user were to reload the page within the caching time window. It wouldn’t save many requests to twitter.com overall. I.e. there is no way for multiple users to share the cache.

    You could cache like @Alelo suggests on the server side, but if you do that, why would you even bother using this plugin?

  22. Hi David,

    Love all your work. Thanks for sharing and giving so much to the MooTools community. I’ve been gleaning a lot lately. I thought I’d let you know, I made this little site the other day based on your plugin:
    http://simpletweet.com/

    It’s pretty straightforward, but it works for what I wanted it to do. I was previously doing it with a totally different method, but “TwitterGitter” does the job! So thanks!

    -Ben

  23. I would definitely recommend keeping a cache there is no reason not to; although, you wouldn’t want to use a cookie as it is pointless.

    So with a proper cache, you update most frequently at 1min making 60 calls to twitter in 1 hour. (There is no reason to hit if more often as it is cached by twitter anyways)

    Using the method without cache you could be hitting the Twitter server for thousands of hits per minute depending on the site’s traffic.

    Be considerate of Twitter’s servers.

  24. h-a-r-v

    How about doing it this way? http://www.widgetbox.com/widget/twidget – looks very reasonable – simple and functional. Slidewhow of all messages – unlimited, takes very little space and you can set it to show particular twitter by default. Would be great to see a mootools version.. As we all know, flash sucks.

  25. @EmEhRKay: I’m going to leave the plugin as-is. Multiple instances for multiple users.

  26. Just a little gotcha: The regex behind the ‘linkify’ method will cause problems if the link is contained within parentheses or brackets (the end bracket will become part of the link). If you want to fix this, replace the \S in the first group with [\w\-:;?&=+.%#\/]

  27. NM

    Hi,

    Is there anyway to download whole package? Seems like “download” button doesn’t respond normally…

    Thanks

  28. lancia

    very interesting!

  29. Hey David,
    Just wanted to let you know that TwitterGitter conflicts with the Mootools Swiff class. Not that it matters, I just wanted to save anybody who actually wanted to use the two together (like I tried doing) the headache of what the problem might be. If I come up with a solution I’ll email it or post it.

  30. @Nathan Sweet: That sounds very weird — what’s happening?

  31. @David I’m not exactly sure right now, I’m still messing around with it, but TwitterGitter simply doesn’t work, and if I execute it in the DOMready event before the swiff object then the SWF simply doesn’t inject into the document. If I implement it after then it still doesn’t work, but at least the swf is injected into the document. I’m still working on it. I’ll let you know.

  32. Hi David,
    great work as ever – would you know of a way to extend this to gather tweets that mention a given @username or #hashtag too?

  33. okay – the simplest of javascript beats me (a mental block of the syntax i guess)
    with ref to mey last post on including tweets that include a #hashtag or @username would be simple search…

    new JsonP(“http://search.twitter.com/search.json?q=” + this.keyword + “&rpp=” + this.count,{

    and the results are an object… data[‘results’][ ][]

    but how you loop the results and put them back in the tweets i can’t figure out.

    any help on thin one David? anyone?

    as always – greatly appreciated.

  34. as with nm, wondering about downloading the whole package.

    i saw twittergitter on another website (http://www.epitonic.com/) and really like it, but really am an amateur website builder. i need an installation package for dummies i guess -that shows exactly what i need to put in the head, all the files to upload and what to put in the body, all in the context they actually go in.

    if this is possible, i would very much appreciate it. i’ve tried following a few examples, but can’t figure out.

    tnx, wanda.

  35. kolin

    not sure if this is an issue, or i’m getting the wrong json response, but the response doesn’t seem to include retweets, but includes them in the count.

    for example if i have a count of 5, but there are 3 retweets in there, it only actually shows 2 tweets.

    anyone know if this is by design? the .json also doesnt show retweets in there.

  36. I am having the same issue with retweets not showing. I just increased the count from 1 to 3 so it just show the next tweet.

  37. to fix the retweet issue change part of the class to this

    new JsonP('http://api.twitter.com/1/statuses/user_timeline.json?screen_name=' + this.username,{
    			data: {
    				since_id: this.options.sinceID,
    				include_rts:1,
    				count:this.options.count
    			},
    

    The new user and include_rts fixes the issue

  38. Ciul

    I read this and read this other (http://appden.com/javascript/get-tweets-with-mootools/) and

    then I wrote this which is a modified twitter request class

    http://www.mooforum.net/script-showcase-f25/modified-twitter-request-t3782.html

    on comments there I thanks to both sources, I’m newie to MooTools so don’t be that rude with my code…

    I’d like to hear opinions anyway

  39. pSouper

    Hi David et al,
    I have a come up with a strange problem with twitterGitter – for one twitter account it is only getting tweets up until a date two months ago…

    1: the tweets are not being cached in anyway
    2: the tweets just seemed to stop at that date and not progressed since (for this account only)
    3: ths account has plenty of tweets since this date
    4: when i change the twitter account it shows them up until the latest tweet.
    5: this problem persists using your demo code too.

    Is it possible to that this account (a quite prolific tweeter) has so many tweets that the twitterGitter/twitterAPI has a limit of some kind that was met/exceeded at this date?
    – such as get 64k tweets from tweet#1 and this happens to be the tweets at this date.

    any help on this strange problem is very welcome.

    twitter account concerned is available on request :)

    thanks D,

  40. 2G

    Hello,
    Your class work done, on my website.
    But I have got simply one question, How do you change format date on ccs or js ?

    Tue Mar 06 16:59:59 +0000 2012 via web
    :/

    thanks

Wrap your code in <pre class="{language}"></pre> tags, link to a GitHub gist, JSFiddle fiddle, or CodePen pen to embed!