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.

10 March 2010 ~ Comments

TweetSharp Preview 24a – Mea Culpa

Unfortunately, we weren’t doing the right thing when it came to deserializing geo-location data in TweetSharp, relying on a rigid regular expression and a struct rather than treating it like a true object with fault tolerant JSON deserialization. The result of that error was a breaking build when Twitter started pushing out updated geo-location data as part of an upcoming enhancement. We will take extra care to stamp out these kinds of issues for the upcoming V1 release.

This preview was not scheduled, but is necessary to fix the geo-location data. As part of our roadmap, this preview also contains a breaking change: all namespaces previously marked “Dimebrain.TweetSharp” are now simply “TweetSharp”. We did this to cut down on keystrokes for some users as well as reflect the slight licensing change of ownership (from a company to individuals) that occurred recently.

Beyond the geo-location fix and the namespace change, this build also handles deserialization of collections in a more optimistic manner; if the library can’t deserialize a handful of Twitter objects, it won’t return null, but will instead return all of the successfully deserialized objects, sending the failed elements out in a Trace for you to watch for with your own listeners.

The Preview 24a download is available here.

Note: If you prefer not to take on the task of converting your namespaces now, you can optionally download the patch that only addresses the geo-location fix.

Tags: , ,

17 February 2010 ~ Comments

TweetSharp Preview 24 – Silverlight and Mono Support, and Introducing Yammer

In this preview, TweetSharp finally becomes a true cross-platform micro-blogging library, while adding two more .NET target platforms into the fold!

New Features

TweetSharp for Silverlight

We’ve had experimental support for awhile, but we’ve finally honed in on official support for using TweetSharp directly in Silverlight 3, with support for Silverlight 2 and 4 currently available by building from source for your particular environment. Up until now, the story for developing Silverlight applications with TweetSharp involved using the regular library server-side, and then using a proxy mechanism like WCF or RIA Data Services to provide a pipeline of data from your server, hiding the library there. In many cases, especially when you care about storing OAuth credentials in .XAP distributables, this may still be your preferred option, but now you can reference the Dimebrain.TweetSharp.Silverlight.dll library and build Twitter queries directly in Silverlight code.

  • You still need a proxy, because Twitter does not provide a liberal cross-domain policy. More on that shortly.
  • Only asynchronous calls are supported as per Silverlight’s architecture, so you must always specify a CallbackTo method and call RequestAsync
  • If you’ve built a Silverlight 2 version from source, due to browser-based HTTP operation all proxy calls will return with a TwitterResult with ResponseHttpStatusCode set to 200 if the proxy itself successfull transferred the message rather than show the proxy’s results. You will need to account for this by checking the response.
  • If you’ve built a Silverlight 4 version from source, and you are running out-of-browser in full trust, no proxy is needed and TweetSharp will adjust automatically.

Using TweetSharp with a Transparent Proxy

TweetSharp’s source code now includes a Demo project showing an OAuth walkthrough in Silverlight. This project also demonstrates the use of a TweetSharp-compatible proxy HttpModule that is used to convert Silverlight-constricted HttpWebRequest calls from your Silverlight client to Twitter itself through the proxy. This proxy works with Basic and OAuth authentication, and can be used with TweetSharp or any Silverlight library that obeys our header strategy. In the interests of making this available to others who may not have switched over to a professional-grade library but still want to build Twitter apps in Silverlight, you can grab this code for TweetSharpProxyModule.cs, but you’ll want to keep up to date with any changes through our repository.

TweetSharp for Mono

We now have a Mono build for the standard library that covers the vast majority of TweetSharp! Currently, we are unable to support the AsSearchTrends() deserializer method (and therefore the search trends methods in TwitterService), but we are working on a resolution for this. If you’ve been waiting to build a TweetSharp-powered application for Mono or MonoTouch, now’s your chance.

Introducing Yammer

Our new Yammer support represents a whole new application platform target for TweetSharp. We’ve included full coverage of the Yammer API using the fluent interface approach you’re already familiar with.

FluentYammer is completely independent of FluentTwitter so you can choose to target either or both services in your applications as your needs warrant, but the two share many features such as caching, automatic retries, compression, and asynchronous processing.

Like FluentTwitter, the structure of the Yammer queries, and the terminology mirrors the REST API published by Yammer, so if you want to get started, the best place to do so is with the Yammer API document itself.

Another thing worth mentioning is that Yammer uses OAuth only for authentication, so if you’ve been putting off learning the ins and outs of OAuth, now might be a good time to start.

To get you started, here’s a couple of code samples that cover the two most common activities, fetching and posting messages:

//gets (up to) 20 of the most recently posted messages in the network.
 var yammer = FluentYammer.CreateRequest()
                .AuthenticateWith(consumerKey, consumerSecret, accessToken, tokenSecret)
                .Messages()
                .All()
                .AsJson();

var response = yammer.Request();
var messages = response.AsMessages();

And, to post a message:

var token = LoadToken("access");
var yammer = FluentYammer.CreateRequest()
	.AuthenticateWith(consumerKey, consumerSecret, accessToken, tokenSecret)
	.Messages()
	.Post("Yams are delicious!")
	.AsJson();

var response = yammer.Request();
var message = response.AsMessage();

For examples of how to do pretty much anything, check out the unit tests in the source code. There’s a test for every API.

For those who prefer to not use the fluent interface, we will release a YammerService to complement FluentYammer in the future.

06 February 2010 ~ Comments

TweetSharp Preview 23 – Streaming, Local Trends, Rate Limiting, Optimized Simple API

This preview brings TweetSharp within striking distance of the V1 roadmap we’ve been working on for nearly a year. Since TweetSharp is designed to make application developers lives easier, we’re happy to announce that we’ve completed work on rate limiting features that will allow you to maintain control over your user’s experience when consuming their API allotment.

In addition, you can now take advantage of Twitter’s native streaming to achieve the speed of push-style updates in your client. Last but not least, for those who aren’t comfortable with fluent interfaces (or perhaps just ours), we are introducing the TwitterService class, which is an optimized API layer that is powered by TweetSharp but hides most of the details from you: no more deserialization responses or guessing when to use GZIP, TwitterService will use the most efficient configuration. If you’re currently using a library like Twitterizer and want to make the switch to TweetSharp without picking up a fluent style, now you can. Right off the bat, TwitterService covers 100% of the Twitter API.

Breaking Changes

Changing class name

Because local trends are now supported, and they differ from previous trends associated with search, the AsTrends() extension method is now called AsSearchTrends() which returns the usual TwitterSearchTrends object, and a new AsLocalTrends() will return the TwitterLocalTrends object, respectively.

Changing property types

Previously, some values we had in our model that are actually nullable due to responses received from Twitter, we treated as non-nullable. To bring our model classes closer to fidelity with Twitter’s response elements we have made the following class properties nullable: TwitterStatus (InReplyToUserId, InReplyToStatusId), and TwitterUser (IsProtected).

New Features

Parsing Helpers

We added new read-only properties TextHtml, TextMentions, TextLinks, and TextHashtags on model classes TwitterStatus, TwitterDirectMessage, and TwitterSearchStatus to make it easy to parse Twitter artifacts for application customization. This means that you don’t have to write code to parse @mentions, #hashtags, or URLs in tweets. TextHtml will produce a HTMLified text with links back to Twitter, and if you need more control, TextMentions, TextHashtags, and TextLinks will return strings or Uris, respectively, to allow you to present them how you like.

Twitter Streaming API

If you are interested in using Twitter’s streaming API, your first stop should be Twitter’s API wiki, so that you fully understand the permissions and limitations of each streaming endpoint. In TweetSharp, streaming is supported through the asynchronous API, and is a little more strict with input parameters in order to do its best to obey the rules of the streaming API and therefore not get you banned. While we intend to put out more documentation this month on streaming as part of the V1 roadmap, you can get started with streaming now by following this example that targets the Filter stream, the most feature-packed endpoint.

var twitter = FluentTwitter.CreateRequest()
	.AuthenticateAs(TWITTER_USERNAME, TWITTER_PASSWORD)
	.Configuration.TimeoutAfter(1.Minute())
	.Configuration.UseAutomaticRetries(RetryOn.ConnectionClosed, 2)
	.Stream().FromFilter()
        .For(10.Seconds()).Take(100)
        .Tracking("Twitter") // Add other filter options here...
	.CallbackTo((sender, result) =>
	{
		var statuses = result.AsStatuses();
		foreach (var status in statuses)
		{
			Console.WriteLine("{0}: {1}", status.User.ScreenName, status.Text);
		}
	});
twitter.RequestAsync();

A few key considerations for the example above:

  • You must use RequestAsync() with streaming or TweetSharp will throw an exception
  • You need to specify a CallbackTo expression or TweetSharp will throw an exception
  • Connections close often, so using the existing retry policy set to ConnectionClosed is helpful
  • You need to specify a timeout which will function as the stream reader’s ReadWrite timeout
  • The optional For() extension method will close the stream after a specified time span; if it’s unspecified the stream will continue until it fails
  • The optional Take() extension method will bundle streamed tweets into groups by the given number before raising a response event
  • Be sure to check the API wiki for parameter ranges; TweetSharp will auto-correct these to make sure you are sending valid values

Twitter Local Trends

We now support Twitter’s recent local trends API. This API uses Yahoo’s Where On Earth (WOE) specification to enumerate and identify locations where trending data is desired. The following code example retrieves a list of all localities Twitter has trending data on.

var query = FluentTwitter.CreateRequest()
    .AuthenticateAs(TWITTER_USERNAME, TWITTER_PASSWORD)
    .Trends().GetAvailable();

TwitterResult result = query.Request();
IEnumerable<WhereOnEarthLocation> locations = result.AsWhereOnEarthLocations();

Once you obtain a WOE location, you can pass its ID to retrieve the trending topics for that location.

var sanFrancisco = 2487956;
var query = FluentTwitter.CreateRequest()
    .AuthenticateAs(TWITTER_USERNAME, TWITTER_PASSWORD)
    .Trends().ByLocation(sanFrancisco);

TwitterResult result = query.Request();
TwitterLocalTrends trends = result.AsLocalTrends();
foreach (var trend in trends.Trends)
{
     Console.WriteLine(trend.Query);
}

TwitterService Optimized API

The optimized API layer is a one-method-per-API-call style interface that should be intuitive to use. Method prefixes for Intellisense discovery include List, Send, Create, Delete, Search, and Verify. Authentication, caching, retry and timeout policies all function similar to the Fluent style but exist on methods on the TwitterService class itself. Here are a few examples of using the API.

var service = new TwitterService();
service.AuthenticateAs("username", "password");

IEnumerable<TwitterStatus> mentions = service.ListTweetsMentioningMe();

IEnumerable<TwitterDirectMessage> directMessages = service.ListDirectMessagesReceived();

TwitterStatus tweet = service.SendTweet("The new optimized API rocks!");

There are many overloads for methods to allow passing additional options like paging, cursors, and geo locations. You’ll notice there’s no deserialization to work out here, all the results are provided in the method return. If you want to use the streaming API, you can hook up an event handler like this:

var service = new TwitterService();
service.AuthenticateAs("username", "password");
service.StreamResult += service_StreamResult; // Your callback
service.StreamSample(10.Minutes()); // This is asynchronous

We really hope this API is useful for those who prefer a verbose method-style interface. Let us know if you have any suggestions for this new API and if you run into any issues with it, and stay tuned for more examples.

Rate Limiting

We now have two ways for you to dynamically throttle your recurring calls to avoid hitting the Twitter rate limit. The first is a simple percentage-based metric that will adjust the repeat rate of a recurring query so that it will always use a set percentage of the remaining calls. It’s a pretty simple algorithm, but it’s powerful in that it is constantly updating itself to take into account the current conditions of the user’s rate limit.

For example, if you set up a recurring task to pull down “mentions” and set it to use 10% of the user’s rate limit, it will initially calculate:

Rate Limit : 150 calls /hr
x Desired percent : 10%
= 15 calls / hr or 1 call every 4 minutes.

Each time the query runs, it looks at the rate limit status that it gets back from Twitter and re-applies that formula, taking into account the remaining rate limit, and the time until the next limit reset.

Rate Limit remaining: 50 calls
x Desired percent : 10%
Time until reset : 10 minutes
= 5 calls / 10 minutes or 1 call every 2 minutes.

TweetSharp does not track the total percentage across instances, so it’s possible to specify much more or much less than 100%. If you specify a total that’s over 100%, it will have the effect of slowing down and possibly eventually stopping your queries until the next reset as the rate limit is used up. Specifying less than 100% will result in your queries iterating faster near the end of the cycle as there will be more limit available to be used up in a shorter time. Since you will likely use a mixture of recurring and ad-hoc queries, you’ll likely want to experiment a bit to find the combination that’s right for you.

Here’s a code example for setting up a recurring task to check for mentions (aka ‘replies’) with a throttling value of 20% of the total rate limit.

 var twitter = FluentTwitter.CreateRequest()
		.AuthenticateAs(USER, PASSWORD)
		.Statuses().Mentions()
		.Configuration.UseRateLimiting(20.Percent())
		.RepeatEvery(5.Seconds()); // initial value, will be adjusted automatically
var count = 0;
twitter.CallbackTo((s, e) =>
{
    // do stuff with the response
});
twitter.RequestAsync(); //recurring tasks must be async

For those of you who want even more control, we have included advanced rate limiting, whereby you pass TweetSharp some delegates to call to determine if a query should run. Unlike the previous example, where the rate limiting is adjusted following each call, this method calls a predicate delegate just before the query is scheduled to run (as specified by the RepeatEvery(timespan) call) which determines whether or not to proceed. The predicate is passed a RateLimitStatus object that it can use to evaluate whether or not the query should run. If the predicate returns false, the query is skipped until the next iteration, if it returns true, the query is run as scheduled.

Because the predicate is evaluated before the query is run, it cannot rely on the rate limit status that Twitter returned from the previous iteration, as many subsequent calls may have occurred in the interim. To deal with that, you must also specify another delegate to the query that returns a TwitterRateLimitStatus object. That object is then passed to the evaluation predicate.

Here’s a code example that explains it – again, we want to periodically get mentions:

// function to get current rate limit
private TwitterRateLimitStatus getRateLimit()
{
	var rateLimitQuery = FluentTwitter.CreateRequest()
		.AuthenticateAs(user,password)
		.Account().GetRateLimitStatus().AsJson();
	var response = rateLimitQuery.Request();
	var limit = response.AsRateLimitStatus();
	Assert.IsNotNull(limit);
	return limit;
}

// create a recurring task that is skipped when
// there are fewer than 20 remaining API calls
var twitter = FluentTwitter.CreateRequest()
	.AuthenticateAs(user,password)
	.Statuses().Mentions()
	.Configuration.UseRateLimiting(rateLimit => rateLimit.RemainingHits >= 20, getRateLimit)
	.RepeatEvery(30.Seconds()); // predicate is evaluated this often

twitter.CallbackTo((s, r)   =>
    {
          if (!r.SkippedDueToRateLimiting)
          {
              //do something meaningful
          }
    });
twitter.RequestAsync();

The above example makes a call to Twitter to get the current rate limit before each iteration of the repeating tasks. Obviously, if you have lots of recurring tasks, or even just a few that iterate frequently, you probably want to provide a better mechanism for getting the rate limit that doesn’t require a network trip every time. Remember that recurring tasks run on background threads, so if you’re sharing rate limit state amongst multiple instances, you’ll need to take thread safety and concurrency into account.