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:

https://twitter.com/signup

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.

Twitter01
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

Twitter02
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

Twitter03
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.

Twitter04
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.

Twitter05
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.

Twitter06
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.

Twitter07
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.

Twitter08
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.

Twitter09
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:

Twitter10
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.

Twitter11
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



Related Articles

Comments

  • Twitter access token

    Posted by Antony on 11/25/2016 05:01am

    Hi, using LinqToTwitter, I'm writing a web app to generate access token, by passing the Consumer key and secrete. it works fine. As far as I know, Access Token for Twitter and Twitter DM (direct Message) are different. My Question is, how can I make a request to generate access token for Twitter DM.

    Reply
  • Rate limit exceeded - Please visit the LINQ to Twitter FAQ (at the HelpLink) for help on resolving this error

    Posted by Vikash Kumar on 09/20/2016 12:54am

    Hi i am getting this error. How do i go about fixing it?

    Reply
  • A question!

    Posted by Haidy on 05/24/2016 02:38am

    How can i get more tweets than 200 , i need to search for a specific topic it works properly, but i need more tweets could you help me?

    Reply
  • Tweets with Location

    Posted by maani on 05/10/2016 01:04pm

    How can i get the tweets from a specific location, for example I want to get the tweets posted from "London"...

    Reply
  • TwitterData

    Posted by awesomechrist on 04/18/2016 08:23am

    Hi, i wanna ask for something. I had made it but i found some errors when i was debugging this program.. it was written that "SingleUserAuthorization" is not be found? I'm confused or I must change using a directive or an assembly reference? Please Help me...

    Reply
  • Appreciation

    Posted by Radz on 04/10/2016 11:39pm

    You are the best programmer in the world

    Reply
  • Exception thrown: 'System.AggregateException' in mscorlib.dll

    Posted by Kingsley on 12/22/2015 11:42pm

    the problem is occur at this line (currentTweets = tweets.ToList();)

    • System.AggregateExeption - Response

      Posted by Hunter on 01/10/2016 12:46pm

      I was having this same issue. I spent a couple days trying to figure it out. I was surprised when i saw that my consumer and access keys all had changed for no reason. Try double checking to make sure your keys are the same. Once I recopied mine it worked flawlessly. Hope this works!

      • System.AggregateExeption Problem

        Posted by Behi on 08/10/2016 08:00am

        Hi i checked keys again but problem is not resolve , how can resolve it ?

        • System.AggregateExeption Problem

          Posted by Maria on 12/06/2016 08:39am

          Hi! It didn't help me too. Have you resolved this problem?

          Reply
        Reply
      Reply
    Reply
  • 1sttweetlist data type

    Posted by 123 on 12/15/2015 02:00pm

    1sttweetlist is listbox in your form

    Reply
  • Its really helpful

    Posted by NABEEL REHAN on 06/03/2015 10:36am

    Its really helpful thnaks

    Reply
  • 1sttweetlist data type ?

    Posted by nencor on 05/04/2015 06:26am

    i cant find your 1sttweetlist data type on your code. what is the data type ?

    Reply
Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

Most Popular Programming Stories

More for Developers

RSS Feeds

Thanks for your registration, follow us on our social networks to keep up-to-date