Accessing Twitter with C#
These days, social media rules the web waves, so being able to get access to your day to day activities on your various networks is perhaps one of the most common tasks that most .NET developers have to do these days. In this article, we'll go through the basics of accessing a Twitter account and look at the ways we can search and interact with the data available to you.
Twitter is perhaps one of the most important social media channels available today. With its constant stream of posts, photos, and videos from just about every well known person on the planet, the volume of information available is absolutely staggering. From A list film & pop stars to lowly newbie software developers, anything you could potentially want to search for can easily be found.
If you have your own Twitter account, you'll be all too familiar with just how difficult it is to keep up with the Twitter accounts you follow. I know from firsthand experience just how easy it is to miss a snippet of news amongst all the noise and rapidly changing Tweet landscape, even if you are a wizard using the Twitter web interface.
What We Will Build in This Article
We won't be creating anything too fancy, but we will be building a relatively functional application that will allow you to retrieve your own time line, and then search it for interesting keywords. We'll create this as a desktop application, but the techniques we cover will work just as well in a web application. Creating a desktop application allows me to focus on the topic of the article, rather than on doing things like setting up web servers and application pools to run our application.
Before we get started, however, you need an application key, and to get an application key you need a Twitter account. If you don't already have a Twitter account, the first thing you need to do is create one, so head to:
and create yourself a new account. I'll wait until you get back. :-)
Once you've created your new account and logged in, you should see something similar to the following.
Figure 1: Your new Twitter account
You (especially if it's a new account) may have somewhat fewer contents. If you don't yet have anyone you're following, you'll need to find some Twitter folks to follow; otherwise, you'll have nothing in your timeline when it comes to writing some code. My Twitter handle is shawty_ds if you want somewhere to start. All of the big names in .NET, such as Scott Hanselman and Scott Gutherie, have Twitter accounts, as do most of the .NET development teams and the MSDN Magazine. A quick search should get you a few to get you started.
Stage 2
Once you have a valid account and some names to follow, the next thing you need to do is to create an API key for your application. With your log-in active, head to:
https://dev.twitter.com/overview/documentation
and you should see the Twitter developer documentation page
Figure 2: The Twitter Developer documentation page
The second menu option from the bottom is "Manage My Apps". Click this to get access to any application keys associated with your account. This should give you access to the following
Figure 3: The Twitter Apps management page
As you can see from my account I already have a test key registered. For this project, we'll create a new one, however. If you click "Create New App" you'll be taken to the create application key form, allowing you to fill in the details of the application we are going to create.
On the form, you'll have four fields as follows:
- Name: This is the name you want to give to your key. For this article, we'll call it 'Blog Test'
- Description: This is a description of your application. If you're accessing Twitter by using a web-based OAuth call back, this and the name field WILL be shown on the Twitter authorization page when your application's user requests use of your application and authorizes it to their account.
- Website: This is the fully qualified web site address of your application, and is displayed in the sign-on dialog for your user to go to, to find out more about your application prior to allowing it to have access to their account.
- Callback URL: This is a fully qualified URL for applications that need to use the full OAuth flow when authenticating their use of the Twitter API.
Standalone Apps Versus Web Apps
Before you continue and fill the form in, there's a concept you need to understand. Twitter applications can come in two different formats. The first is the traditional web app format, where a user might authenticate your application to use their own Twitter account. Web applications such as klout.com, sumall.com, and others all use this model.
If you're planning to write an application that works this way, you MUST specify a callback URL for your application. This URL will need to point to a valid URL in your web application for the Twitter API server to talk to your application during the authentication process, and it will need to be HTTPS enabled.
A standalone application works in a different way. A standalone application creates a static key for a single use, and that single use is to allow it to log in to a single account only (usually the account belonging to the application's author).
In this articcle, we'll be using a standalone key, so we WON'T be putting a callback URL into the form, but we do need to provide the other three bits of information. In my case, I'll set the name & description to "Blog Post Demo" and the Website URL to a dummy URL that points to nothing.
Figure 4: Creating an application
Remember to agree to the developer agreement, and then click "Create your Twitter Application" and, if all goes okay, you should get a screen showing you the keys you just generated.
Figure 5: The Twitter account has been crated
Initially, the key will be set to read only, and will only have consumer keys, so the next thing we need to do is head to the "Keys and Access tokens" tab, and generate the extra two keys needed for single use access.
At the bottom of the keys and access tokens tab, you'll see a button marked "create my access token". Once you do this, you'll see that you get two extra tokens generated. To use your single use application key, you'll need to provide ALL four of these keys to your application.
Figure 6: Providing your keys
You'll notice also that the access level is set to read only. For our purposes, this is fine as all we will be doing is reading and searching the time line. However, if you wanted your application to post statuses and update details on your Twitter account, you'll need to make it a read/write application.
If you do intend to make the key a read/write one, you need to be sure that NONE of your four keys can be gotten hold of by someone outside your control. With a writable access key and the four keys extracted from your application, any third party that has access to them can make your account post ANY status they want to. Note that there is also a clause in your developer agreement that means if these keys do get leaked, you might LOSE or have your Twitter account shut down, so guard them with your life.
Once you have the keys generated, you're ready to start issuing requests to the API, so head over to
https://dev.twitter.com/rest/public
and be prepared to take a big gulp of air as you realise just how big this API is.
Fortunately, we'll not be working directly against the Twitter Rest API. We'll be using a very easy-to-use NuGet package that does ALL the heavy lifting for us, but more on that in a moment. For now, fire up Visual Studio and create a new Winforms or WPF app. For simplicity, I'll be creating a Winforms app; it's up to you, however, which you prefer.
Accessing the API is the same no matter which you decide to use.
Figure 7: Accessing the Twitter API
I'm calling my project "TwitterBlogPost". Once you have your project loaded, we need to resize the main form, and add two list boxes: one to act as a Twitter message list and one to act as a name list of people you follow.
We also need a text box to use as a search box and a button to activate the search. My Form Layout for our demo app looks like the following.
Figure 8: A sample Form Layout
The left most list box will be used to hold the names of the Twitter accounts I'm following, the right most box will list the tweets currently selected for display. The search box and button at the top will be used to provide keyword searches to find tweets containing a given keyword. For now, however, all we'll be doing is displaying text; I'll leave it as an exercise to the reader to show things like avatar icons and inline photos & videos.
Getting Access to Twitter Data
Now that we have a basic application skeleton created, we need to start writing some code. The first thing to do is to add the LinqToTwitter NuGet Package by Joe Mayo. You can find the website and documentation at:
https://linqtotwitter.codeplex.com/
and you can follow Joe via his Twitter stream at
https://twitter.com/joemayo or https://twitter.com/Linq2Twitr
For our use, however, head to the NuGet package console or GUI and search for, then install, the 'LinqToTwitter' package.
Figure 9: Installing the 'LinqToTwitter' package
Once we have the LinqToTwitter package installed, we need to add a single user authorization object to our form, containing the four keys we created earlier on in this article. Press F7 in your VS editor to go to code mode, and make sure that your win-form code looks like the following:
using System.Windows.Forms; using LinqToTwitter; namespace TwitterBlogPost { public partial class Form1 : Form { private SingleUserAuthorizer authorizer = new SingleUserAuthorizer { CredentialStore = new SingleUserInMemoryCredentialStore { ConsumerKey = "xxxxxxxxxxxxxxxxxxxxxxxxCB", ConsumerSecret = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxzG", AccessToken = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxgT", AccessTokenSecret = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxWE" } }; public Form1() { InitializeComponent(); } } }
The four keys are as labeled in your Twitter API applications page. Normally, you'd encrypt or hide these in the application config file or similar; for now, we're just putting them in as static strings.
After we create the authentication object, the next thing we'll do is grab the most recent 200 statuses from your home time line.
Why Only 200?
The Twitter API is heavily rate limited. As a result, the maximum amount of entries you can obtain in one go from any API call is 200. If you have more than 200 to get, you need to start recursively calling the API setting various parameters such as MaxID and SinceID. There are various articles on the LinqToTwitter site and on the author's own personal site that explain how all of this works, and is a topic we could easily write an entire series of article about.
To keep things simple, we therefore only grab the maximum amount we can. If you're planning on getting more, it's worth reading the LinqToTwitter docs before you start.
With the above in mind, add the following method to your main form class:
private void GetMostRecent200HomeTimeLine() { var twitterContext = new TwitterContext(authorizer); var tweets = from tweet in twitterContext.Status where tweet.Type == StatusType.Home && tweet.Count == 200 select tweet; currentTweets = tweets.ToList(); }
Then, ensure that you also add the following private member, just after the declaration for your authorizer.
private List<Status> currentTweets;
Once you've added the above, finally make a call to the new method in your form's constructor, followed by assignment of the data to the main tweets list box on your form.
public Form1() { InitializeComponent(); GetMostRecent200HomeTimeLine(); lstTweetList.Items.Clear(); currentTweets.ForEach(tweet => lstTweetList.Items.Add(tweet.Text)); }
If you then run your app by pressing F5, you should see there's a pause of about 30 seconds or so while LinqToTwitter retrieves the initial tweets from your home timeline, followed by your form displaying, with a list of tweets in the main list.
Note: You may not get 200 tweets in your list, especially if it's a new account. The 200 is a maximum count, and if fewer exist, LinqToTwitter will retrieve as many as it is able to. |
If everything has worked as expected, you should be greeted by the following:
Figure 10: Retrieving the tweets
Yes, it's ugly, but it works. :-)
The next thing we're going to do is to populate our side list with a list of Twitter users who you're following from your own Twitter account. We start by adding the following method to our form class.
private List<string> GetFollowers() { List<string> results = new List<string>(); var twitterContext = new TwitterContext(authorizer); var temp = Enumerable.FirstOrDefault(from friend in twitterContext.Friendship where friend.Type == FriendshipType.FollowersList && friend.ScreenName == "shawty_ds" && friend.Count == 200 select friend); if (temp != null) { temp.Users.ToList().ForEach(user => results.Add(user.Name)); while (temp != null && temp.CursorMovement.Next != 0) { temp = Enumerable.FirstOrDefault(from friend in twitterContext.Friendship where friend.Type == FriendshipType.FollowersList && friend.ScreenName == "shawty_ds" && friend.Count == 200 && friend.Cursor == temp.CursorMovement.Next select friend); if (temp != null) temp.Users.ToList().ForEach(user => results.Add(user.Name)); } } return results; }
Like the status tweets in your home timeline, this will only return 200 entries at a time, so to get your full list of followers you need to repeat the process several times. Unlike the status tweets, however, a simpler single cursor based method is employed to repeatedly retrieve successive pages.
With each request, a 'Next Cursor' will be provided. Simply keep checking that cursor for 0, and while it's greater than 0, copy the value into the 'Cursor' field of successive calls to the API and add the results to your followers list as the above method does.
Once we have the main follower list method added, we add one more method, which simply goes through the follower list and formats it with a "(x)" after the name where the x shows a count of how many tweets there are in the home time line by that user. The method that achieves this is as follows:
private List<string> GetSideBarList(List<string> inputNames)d { List<string> results = new List<string>(); foreach (string name in inputNames) { int tweetCount = currentTweets.Count(tweet => tweet.User.Name == name); if(tweetCount > 0) { results.Add(string.Format("{0} ({1})", name, tweetCount)); } else { results.Add(string.Format("{0}", name)); } } return results; }
After we add the above two methods, we add the following line to our form constructor:
GetSideBarList(GetFollowers()).ForEach(name => lstFollowNames.Items.Add(name));
and then, after pressing F5 and waiting while the Twitter data is collected, we should now see that we have a list of followers in our side list and where those followers have any tweets in the main list a bracketed number should follow the name.
Figure 11: Now, friends' names show
It's still not the best looking app in the world, but in less than 100 lines of C# we have a not too unreasonable amount of data available. If we now add an event handler to our followers name list, we can easily filter the tweets in the main view to only show tweets by the selected name.
Add the following method to your form class:
private void LstFollowNamesSelectedIndexChanged(object sender, System.EventArgs e) { lstTweetList.Items.Clear(); var selectedName = (sender as ListBox).SelectedItem.ToString(); string pattern = @"^(.*)\s\(\d{0,4}\)$"; Match match = Regex.Match(selectedName, pattern); if(match.Success) { // We have a name with a count appended selectedName = match.Groups[1].Value.Trim(); } foreach(var tweet in currentTweets.Where(tweet => tweet.User.Name == selectedName)) { lstTweetList.Items.Add(tweet.User.Name + ":" + tweet.Text); } }
and then hook it up by using the IDE so that it becomes the "Selected Index Changed" event handler for your left name list. This will take the name selected, determine if it needs to remove the parenthesis part of the text, and then use the name as a filter for the tweets we already have in memory.
Let's also add a new button to the search bar area at the top and label it "Show All" so that we once again can show all our timeline tweets with no filtering. To make this work, add the following code to the new buttons click event:
private void BtnShowAllClick(object sender, System.EventArgs e) { lstTweetList.Items.Clear(); currentTweets.ForEach(tweet => lstTweetList.Items.Add(tweet.User.Name + ":" + tweet.Text)); }
The only thing we have left to do now is implement the search.
We'll perform searches directly on Twitter's live data, so you can perform a search, but you won't be able to filter it using the names on the left. You will, however, be able to use the new button just added to return to your original home timeline list.
Add the following method to your form class to allow you to perform searches using Twitter:
private List<Status> SearchTwitter(string searchTerm) { var twitterContext = new TwitterContext(authorizer); var srch = Enumerable.SingleOrDefault((from search in twitterContext.Search where search.Type == SearchType.Search && search.Query == searchTerm && search.Count == 200 select search)); if(srch != null && srch.Statuses.Count > 0) { return srch.Statuses.ToList(); } return new List<Status>(); }
Then, wire it up to your search button by using the following:
private void btnSearch_Click(object sender, System.EventArgs e) { if(string.IsNullOrEmpty(txtSearchTerm.Text)) { MessageBox.Show("Please enter a term to search for"); return; } var results = SearchTwitter(txtSearchTerm.Text); lstTweetList.Items.Clear(); results.ForEach(tweet => lstTweetList.Items.Add(tweet.User.Name + ":" + tweet.Text)); }
And there we have it. It's not elegant by any stretch of the imagination, but you should be able to get the most recent 200 statuses from your home/following timeline, filter on them, and perform random searches on the general Twitter stream. All I've shown here is how to get started; I'm sure by this point you can think of 100s of ways this can now be expanded. A word of warning, though; when you're testing, try to be as conservative as possible.
Twitters Rate limits WILL bite you, especially if you're coding, testing, and debugging repeatedly. At one point during writing this article, I had to put things down for an hour until my API rate reset itself. Where possible, try to cache the data in some fashion, and then develop your routines using that cache. It'll make your development task much easier.
The full code for the entire window class is as follows:
using System.Collections.Generic; using System.Linq; using System.Text.RegularExpressions; using System.Windows.Forms; using LinqToTwitter; namespace TwitterBlogPost { public partial class Form1 : Form { private SingleUserAuthorizer authorizer = new SingleUserAuthorizer { CredentialStore = new SingleUserInMemoryCredentialStore { ConsumerKey = "r0Rx4l1dJVferCCzGlfFDqbCB", ConsumerSecret = "TNiIDnS2BPQpDicaOXvAwNAiZXAQBf8v4AuXOCjRasAbS0ohzG", AccessToken = "172766492-cpMwxsYFvWGNfDFGdbgjRRj67G41ZUoA8IFobfgT", AccessTokenSecret = "gnuCI070LOQBArjRX744LUgzkIpg1XWDO3vtdjStHAAWE" } }; private List<Status> currentTweets; public Form1() { InitializeComponent(); GetMostRecent200HomeTimeLine(); lstTweetList.Items.Clear(); currentTweets.ForEach(tweet => lstTweetList.Items.Add(tweet.User.Name + ":" + tweet.Text)); GetSideBarList(GetFollowers()).ForEach(name => lstFollowNames.Items.Add(name)); } private void GetMostRecent200HomeTimeLine() { var twitterContext = new TwitterContext(authorizer); var tweets = from tweet in twitterContext.Status where tweet.Type == StatusType.Home && tweet.Count == 200 select tweet; currentTweets = tweets.ToList(); } private List<string> GetFollowers() { List<string> results = new List<string>(); var twitterContext = new TwitterContext(authorizer); var temp = Enumerable.FirstOrDefault(from friend in twitterContext.Friendship where friend.Type == FriendshipType.FollowersList && friend.ScreenName == "shawty_ds" && friend.Count == 200 select friend); if (temp != null) { temp.Users.ToList().ForEach(user => results.Add(user.Name)); while (temp != null && temp.CursorMovement.Next != 0) { temp = Enumerable.FirstOrDefault(from friend in twitterContext.Friendship where friend.Type == FriendshipType.FollowersList && friend.ScreenName == "shawty_ds" && friend.Count == 200 && friend.Cursor == temp.CursorMovement.Next select friend); if (temp != null) temp.Users.ToList().ForEach(user => results.Add(user.Name)); } } return results; } private List<string> GetSideBarList(List<string> inputNames) { List<string> results = new List<string>(); foreach (string name in inputNames) { int tweetCount = currentTweets.Count(tweet => tweet.User.Name == name); if (tweetCount > 0) { results.Add(string.Format("{0} ({1})", name, tweetCount)); } else { results.Add(string.Format("{0}", name)); } } return results; } private void LstFollowNamesSelectedIndexChanged(object sender, System.EventArgs e) { lstTweetList.Items.Clear(); var selectedName = (sender as ListBox).SelectedItem.ToString(); string pattern = @"^(.*)\s\(\d{0,4}\)$"; Match match = Regex.Match(selectedName, pattern); if (match.Success) { // We have a name with a count appended selectedName = match.Groups[1].Value.Trim(); } foreach (var tweet in currentTweets.Where(tweet => tweet.User.Name == selectedName)) { lstTweetList.Items.Add(tweet.User.Name + ":" + tweet.Text); } } private void BtnShowAllClick(object sender, System.EventArgs e) { lstTweetList.Items.Clear(); currentTweets.ForEach(tweet => lstTweetList.Items.Add(tweet.User.Name + ":" + tweet.Text)); } private List<Status> SearchTwitter(string searchTerm) { var twitterContext = new TwitterContext(authorizer); var srch = Enumerable.SingleOrDefault((from search in twitterContext.Search where search.Type == SearchType.Search && search.Query == searchTerm && search.Count == 200 select search)); if(srch != null && srch.Statuses.Count > 0) { return srch.Statuses.ToList(); } return new List<Status>(); } private void btnSearch_Click(object sender, System.EventArgs e) { if(string.IsNullOrEmpty(txtSearchTerm.Text)) { MessageBox.Show("Please enter a term to search for"); return; } var results = SearchTwitter(txtSearchTerm.Text); lstTweetList.Items.Clear(); results.ForEach(tweet => lstTweetList.Items.Add(tweet.User.Name + ":" + tweet.Text)); } } }
If you have a burning subject you'd like me to cover, or something in .NET you'd love to know more about, feel free to stalk me on Twitter (@shawty_ds) or come and find me in the Lidnug .NET users group on the Linked-In platform and let me know your thoughts.
Happy Tweeting!
Shawty