TweetSharp is a complete .NET library for microblogging platforms that allows you to write short and sweet expressions that convert automatically to web queries and fly to Twitter on your behalf. We strive to cover 100% of the Twitter API and actively keep up with enhancements and changes from Twitter itself. TweetSharp was developed by Daniel Crenna and Jason Diller.

20 December 2009 ~ Comments

TweetSharp Preview 21 – Retry and Timeout

The holidays bring good things, and for our users that means new features! Preview 21 has been released and contains two significant new features you can use to make your apps more robust.

First, we have implemented configurable automatic retries. If your Twitter API call fails for reasons out of your control (such as the infamous FailWhale), you can have TweetSharp automatically retry the request for you a specific number of times. If it exhausts the retries before succeeding, you will get back the same type of response object as you have in the past when the query failed on the first try.

The failures TweetSharp can detect and retry on are as enumerated in the “RetryOn” enum:

[Flags]
public enum RetryOn
{
    Never = 0,
    FailWhale = 1,
    TwitterError = 2,
    Timeout = 4,
    Network = 12, //includes Timeout
    FailWhaleOrNetwork = FailWhale | Network
}

For the most part, you’ll probably want to use “FailWhaleOrNetwork” for your queries. “TwitterError” is any HTTP response code other than 200 OK, or 502 (FailWhale) and generally means something is wrong with your query, so retrying on that probably won’t yield a different result – but hey, it’s your app, not ours, so it’s in there if you need it for something.

“Network” includes stuff like DNS and connection failures, basically anything that is neither a FailWhale nor an non-success HTTP response. It also includes timeouts (see below).

Timeouts are..well, timeouts. The request was sent off to the Twitter servers, but there has been no response for some time and TweetSharp gave up. The default timeout is determined by the system, but is somewhere in the neighbourhood of 2 minutes on most systems, so you probably won’t want to set your retry limit to more than a couple of times if you don’t set a shorter timeout (more on that later).

Here’s some sample code that sets up a query with desired retry behavior:

const int FIVE_TIMES = 5; 

var twitter = FluentTwitter.CreateRequest()
    .Configuration.UseAutomaticRetries(RetryOn.FailWhaleOrNetwork, FIVE_TIMES)
    .Statuses().OnPublicTimeline().AsJson();

var result = twitter.Request();
//the TwitterResult object contains the count of retries required
Console.WriteLine("Query was retried {0} times", result.Retries);
if (result.Retries > 0)
{
    //requests that were retried contain the TwitterResult
    //object for the previous, failed attempt
    var previousResult = result.PreviousResult;
    while(previousResult != null)
    {
        //note we also now provide the full WebException object for failed requests
        Console.WriteLine("Previous Attempt Result:{0}", previousResult.Exception);
        previousResult = result.PreviousResult;
    }
}

Retries work on all types of queries.

Secondly, we’ve made the timeout value configurable, so if the system default is too long for you, you can tell TweetSharp to use a shorter timeout value and it will happily oblige.

Usage is pretty simple:

var twitter = FluentTwitter.CreateRequest()
    .Configuration.TimeoutAfter(5.Seconds())
    .Statuses().OnPublicTimeline().AsJson();

That’s it. This obviously works well in conjunction with retries, but doesn’t require that you set up for automatic retries, you can add this on a non-retrying query as well.

Lastly, if you want to test out these features (or your app’s error handling in general), we set up a resource to test these features that you can use as well. If you make your normal twitter requests though the Transparent Proxy at http://failwhale.diller.ca you will ALWAYS get back a FailWhale response. And while it’s not as clean as using a transparent proxy, http://failwhale.diller.ca/timeout.php?delay=x can be used to simulate a timeout. Just pass the number of seconds you want it to stall before responding as the ‘delay’ parameter in place of x (works with both GET and POST). To do this via TweetSharp you need to use the .Direct method:

 var twitter = FluentTwitter.CreateRequest()
            .AuthenticateAs(TWITTER_USERNAME, TWITTER_PASSWORD)
            .Configuration.UseTransparentProxy("http://failwhale.diller.ca/")
            .Configuration.TimeoutAfter(5.Seconds())
            .Direct("/timeout.php?delay=6")
            .Post();

The failwhale server is unofficial, unsupported, may be down at any time, and may go down some day and never come back. It’s easy to set up your own, so if you prefer a locally-controlled version of your own with guaranteed availability and need a hand configuring it, just ask a question on the forum and we’ll be happy to help.

Happy holidays to all our users. We have even more features planned for 2010, and I’m sure Twitter does too, so it should be another busy year for us all. Best Wishes.

26 November 2009 ~ Comments

TweetSharp Preview 20 – The TwitterResult Object and JSON by default

This update contains breaking changes!

TweetSharp Preview 20 has been released and is available for download now from Google Code

While we don’t like introducing breaking changes, we think this one is worthwhile and represents a pretty significant improvement; one that you can use to make your applications better.

First off, if you don’t specify the format of the result you want, we now request JSON by default (previously it was XML). JSON is more compact and we figure most users are going to convert the result to our data objects anyway, so JSON is a better choice in that instance. If you need XML and aren’t already, you’ll have to add AsXml() to your queries from this point forward.

Secondly, all TweetSharp queries now return a TwitterResult object. Previously, Request() returned a string with whatever response we got back from the Twitter API. That was usually the requested data, but it may also have been the HTML for the infamous FailWhale page, or some other HTML-formatted error message.

The TwitterResult object now wraps that text response with a bunch of useful metadata like the HTTP status code, the start and end time of the query, and the current RateLimitStatus object for the account making the request, for authenticated and unauthenticated requests where rate limits apply.

Here’s an example:

//setting up a request is unchanged
var twitter = FluentTwitter.CreateRequest()
    .AuthenticateAs(user, password)
    .Statuses().OnHomeTimeline().AsXml();

// In past releases 'response' used to be a string, now it's a TwitterResult object
var response = twitter.Request();

Error handling is now easier:

// Check for error using the result
if (response.IsTwitterError)
{
    Console.WriteLine("Twitter Returned Error {0} - {1}",
                response.ResponseHttpStatusCode,
                response.ResponseHttpStatusDescription);

    // You can still call 'AsError" to get the TwitterError object
    Console.WriteLine("Details: {0}", response.AsError());
    return;
}

We can now use the result object to view and track more interesting data:

// Calculate the time the query took
var elapsed = response.ResponseDate - response.RequestDate;
Console.WriteLine("Query took {0}ms", elapsed.Value.Milliseconds);

// Get the size of the response
var responseLength = response.ResponseLength;
Console.WriteLine("Query returned {0} bytes", responseLength);

// Get the remaining API rate limit info for the authenticating user
var rateLimitData = response.RateLimitStatus;
Console.WriteLine("API Limit {0}/{1} calls remain. Resets at {2}",
                    rateLimitData.RemainingHits,
                    rateLimitData.HourlyLimit,
                    rateLimitData.ResetTime.ToShortTimeString());

You could use that data to determine which of your queries would benefit from Gzip Compression, for example.

Lastly, the conversion extension methods AsUser(), AsStatuses(), AsSearchResult() etc. that were previously defined as extension methods on string have been redefined as extension methods of the TwitterResult object. This is a more appropriate place for them, and it means they won’t show up in the IntelliSense dropdown for all of the other strings in your application that aren’t responses from Twitter.

Other than the object you’re operating on, conversion is unchanged:

// Convert to statuses
var statuses = response.AsStatuses();
Console.WriteLine("{0} statuses received.", statuses.Count());
// , or get the raw XML
var xml = response.Response;
// , or convert it to JSON
var json = response.ToJson();

If you used type inference and declared all of your variables as var as all of our examples and unit tests do, then you likely won’t have to do much more than grab the latest preview binary and recompile your app since the compiler will do most of the conversion work for you. If you declared all the responses as string, then the compiler should alert you to all the places you need to change.

Since these are breaking changes, we do apologize for making you do extra work, but we hope you’ll agree that the end result is a better one.

16 November 2009 ~ Comments

TweetSharp Preview 18 – Twitter Lists API

The often requested Twitter Lists API support is now complete.
You can download the binary from the Google Code site.

Note that most of the list APIs require the screen name of the list’s owning user as a parameter even if that user is the same as the authenticating user. Aside from that, the usage should be fairly straightforward for anyone used to TweetSharp.

Sample Code

Creating, updating, and deleting lists
Create a new, public list:

 //note that you have to pass the username of the
 //owning user to the 'CreatePublicList' method.
 var query = FluentTwitter.CreateRequest()
                .AuthenticateAs(username, password)
                .Lists().CreatePublicList(username, "TweetsharpList")
                .AsJson();

 var response = query.Request();
 var theList = response.AsList();

Update the list to make it private:

 theList.Mode = "private";

var updateListModeQuery = FluentTwitter.CreateRequest()
                .AuthenticateAs(username, password)
                .Lists().UpdateList(list).AsXml();

var updateListModeResponse = updateListModeQuery.Request();
theList = updateListModeResponse.AsList();

Delete the List:

 var deleteQuery = FluentTwitter.CreateRequest()
                .AuthenticateAs(username, password)
                .Lists().DeleteList(username, theList.Id)
                .AsJson();

var deleteResponse = deleteQuery.Request();
var deletedList = deleteResponse.AsList();

Adding and removing list members

Add the TweetSharp user to the list.

 var user = FluentTwitter.CreateRequest()
                .Users().ShowProfileFor("tweetsharp")
                .Request().AsUser();

 // note you need to pass the user ID to this method,
 // you can't use screen name and that. And once again
 // you need to provide the name of the list owner
 var addUserQuery = FluentTwitter.CreateRequest()
                .AuthenticateAs(username, password)
                .Lists().AddMemberTo(username, theList.Id, user.Id)
                .AsJson();
 var addUserResponse = addUserQuery.Request();
 theList = addUserResponse.AsList();

The list APIs also allow you to identify the list by its “Slug” instead of its “Id”:

 //so in the previous example
.Lists().AddMemberTo(username, theList.Id, user.Id)
//could also be
.Lists().AddMemberTo(username, theList.Slug, user.Id)

Removal is very much the same:

 var removeUserQuery= FluentTwitter.CreateRequest()
                    .AuthenticateAs(username, password)
                    .Lists().RemoveMemberFrom(username, theList.Id, user.Id)
                    .AsJson();

 var removeUserResponse = removeUserQuery.Request();
 theList = removeUserResponse.AsList();

Following, getting tweets from, and unfollowing Lists
Start by following Jason’s list of people he thinks are funny (possibly NSFW language in the tweets)

var followQuery = FluentTwitter.CreateRequest()
                .AuthenticateAs(username, password)
                .Lists().Follow("jdiller", "funny")
                .AsJson();

var followResponse = followQuery.Request();
var funnyList = followResponse.AsList();

Now get some tweets:

var query = FluentTwitter.CreateRequest()
                .AuthenticateAs(username, password)
                .Lists().GetStatuses(funnyList.User.ScreenName, funnyList.Id).AsJson();

var response = query.Request();
var tweets = query.AsStatuses();

Meh, it wasn’t that funny. Unfollow!


var unfollowQuery = FluentTwitter.CreateRequest()
              .AuthenticateAs(username, password)
              .Lists().Unfollow(funnyList.User.ScreenName, funnyList.Id)
              .AsJson();

var unfollowResponse = unfollowQuery.Request();
var notFunnyList = unfollowResponse.AsList();