How to add Tweets to your blog and debugging basic JavaScript: Why did Twitter.com/JavaScript/Blogger JSONP Widget stop working?
I have a little yellow bar at the top of my blog that is supposed to show my latest Tweet. It's a nice unobtrusive way to show that I'm out there and I'm active, and maybe if the tweet is interesting to you, you'll stop by Twitter and follow me.
However, I noticed it stopped working recently. It was blank:
Weird. It'd worked great for years, plural. I searched around and found a few posts on GetSatisfaction asking about it, some recent, some not recent at all. Some people are having the code work on some accounts and not others.
Unfortunately, Twitter seems to have quietly deprecated the code I was using. It's still there but there are no pages on Twitter on how to add tweets to your blog in a low-level controlled way.
Twitter wants you to visit http://twitter.com/badges and use their existing Twitter Widgets. These are very typical of other sites, in that they are boxes with your tweets in them.
For example, here's a box of Tweets to the right, however, that's a little garish for my tastes.
Here's the code I was using to show just the very first tweet on the top of page. First at the top of the page I have a div that will hold my most recent tweet.
<div id="twitter_div">
<a href="http://twitter.com/shanselman" id="twitter-link" >Latest Tweet: </a>
<span id="twitter_update_list"></span>
</div>
Then later at the bottom I have these two scripts. These are what appear to be either not supported or not advertised by Twitter anymore.
<script type="text/javascript" src="http://twitter.com/javascripts/blogger.js"></script>
<script type="text/javascript" src="http://twitter.com/statuses/user_timeline/shanselman.json?callback=twitterCallback2&count=1"></script>
Now, don't use it just like that if you want to get only 1 original tweet as I do. Because it's this very code that stopped working recently. Except it stopped working sometimes. But did it?
To figure it out quickly, first I visited this URL in my browser. It returns JSON of all my tweets.
http://twitter.com/statuses/user_timeline/shanselman.json?count=1
However sometimes I get this:
[]
Yes, really. An empty JSON array. But why? Well at this moment in time my "most recent tweet" is actually a native retweet. It's not a tweet from me, it's a retweet of a YouTube video. See below?
Perhaps this JSON Twitter API was created before the Native Retweet was invented. But, if I tweet something fresh, at hit http://twitter.com/statuses/user_timeline/shanselman.json?count=1 again, I'll see this minimized JSON:
[{"in_reply_to_status_id":null,"text":"Testing for a blog post. Move along. This tweet never happened.","created_at":"Thu May 12 23:44:13 +0000 2011","favorited":false,"retweet_count":0,"source":"web","in_reply_to_screen_name":null,"in_reply_to_status_id_str":null,"id_str":"68823826439487488","contributors":null,"retweeted":false,"in_reply_to_user_id_str":null,"place":null,"coordinates":null,"geo":null,"in_reply_to_user_id":null,"truncated":false,"user":{"is_translator":false,"notifications":false,"created_at":"Tue May 01 05:55:26 +0000 2007","profile_sidebar_border_color":"b8aa9c","listed_count":3909,"following":true,"description":"Tech, Diabetes, Parenting, Race, Linguistics, Fashion, Podcasting, Media, Culture, Code, Ratchet.","show_all_inline_media":true,"geo_enabled":true,"profile_use_background_image":true,"profile_image_url":"http:\/\/a1.twimg.com\/profile_images\/1344567304\/image_normal.jpg","contributors_enabled":false,"verified":false,"profile_background_color":"d1cdc1","profile_background_image_url":"http:\/\/a0.twimg.com\/profile_background_images\/157325454\/twilk_background_4ca8e4832a970.jpg","screen_name":"shanselman","default_profile_image":false,"statuses_count":40900,"id_str":"5676102","default_profile":false,"friends_count":2616,"profile_text_color":"696969","lang":"en","profile_sidebar_fill_color":"b8aa9c","followers_count":36994,"protected":false,"location":"Portland, Oregon","follow_request_sent":false,"profile_background_tile":true,"favourites_count":2089,"name":"Scott Hanselman","url":"http:\/\/hanselman.com","id":5676102,"time_zone":"Pacific Time (US & Canada)","utc_offset":-28800,"profile_link_color":"72412c"},"id":68823826439487488}]
I can run it by the http://jsbeautifier.org and it looks lovely...
[{
"in_reply_to_status_id": null,
"text": "Testing for a blog post. Move along. This tweet never happened.",
"created_at": "Thu May 12 23:44:13 +0000 2011",
"favorited": false,
"retweet_count": 0,
"source": "web",
"in_reply_to_screen_name": null,
"in_reply_to_status_id_str": null,
"id_str": "68823826439487488",
"contributors": null,
"retweeted": false,
"in_reply_to_user_id_str": null,
"place": null,
"coordinates": null,
"geo": null,
"in_reply_to_user_id": null,
"truncated": false,
"user": { ..... SNIP! .....
}
}]
So, Twitter has a nice JSON API that is old and doesn't support Native Retweets but I want the most recent tweet, my way.
Bam. http://twitter.com/statuses/user_timeline/shanselman.json?count=5
Of course, if my last five tweets are all Native Retweets then it all falls part, but you get the idea.
If I was really serious, I could even remove @replies, only showing original tweets, remembering that this JSON API doesn't include Native Retweets in its results.
I found some code on BarneyB's blog that is a modification of Twitter's original code. He mentions in the comments that he's removing replies in his code. I just took his code and stop the code as soon as a valid original non-replied tweet is found. In this case, I search the 5 tweets I requested that were returned from Twitter. If it turns out I reply a lot and get an empty payload, maybe I'll increase that number.
The conclusion here is that this code still "works," it just doesn't see Native Retweets:
<div id="twitter_div">
<a href="http://twitter.com/shanselman" id="twitter-link" >Latest Tweet: </a>
<span id="twitter_update_list"></span>
</div>
Then later...
<script type="text/javascript" src="http://twitter.com/javascripts/blogger.js"></script>
<script type="text/javascript" src="http://twitter.com/statuses/user_timeline/shanselman.json?callback=twitterCallback2&count=1"></script>
My issue was that I want a single tweet, specifically the first original, non-native-retweet, non-reply tweet. I solved it by taking BarneyB's @reply-filtering code and adding a check that breaks out of the loop. I ask Twitter for 5 tweets, but as soon as I find one from that lists of candidates, that's the one. Feel free to View Source if you like, or just be aware of the limitation of Twitter's (possibly deprecated) JSONP API.
It was a trivial but fun lunch hour, indeed.
UPDATE: Great comment below from Dave Ward. He points out that I could have my cake and eat it too by using the new API and a "include_rts" flag like this:
Thanks Dave!
About Scott
Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.
About Newsletter
https://api.twitter.com/1/statuses/user_timeline.json?screen_name=shanselman&include_rts=true&count=10&callback=twitterCallback2
I compared its result with the API call you're currently using, and the only change is the addition of a native retweet that was missing from the old one: http://encosia.com/attachment/1115/
response is:
twitterCallback2({"request":"\/1\/statuses\/user_timeline.json?screen_name=shanselman&include_rts=true&count=10&callback=twitterCallback2","error":"Rate limit exceeded. Clients may not make more than 150 requests per hour."})
is it normal ? I do not think so.
use this post to fix it..
http://www.hanselman.com/blog/HowToAddTweetsToYourBlogAndDebuggingBasicJavaScriptWhyDidTwittercomJavaScriptBloggerJSONPWidgetStopWorking.aspx
recursion :)
Since the rate limiting is isolated to a few users, I punted on the server-side caching approach myself, and append my latest status in a way that doesn't look broken if something goes wrong with the API call. So, a handful of people each day probably don't see my latest update, but they'll also never know there was an issue.
My script is here, if anyone's interested: http://encosia.com/blog/includes/encosia.js?v=4
Thanks for digging into the Twitter consumption logic and sharing what you found! I decided to go for it on 4th and 1 instead of punting (risky, I know, but sometimes devs need all four plays to get a first down too) and wrote a post about how to consume the Twitter feed server-side in ASP.NET MVC 3.
Write a Twitter user timeline controller action in MVC3
Comments are closed.
-SM