How to create a Twitter client in Scala

This is an excerpt from the Scala Cookbook (partially modified for the internet). This is Recipe 15.10, “How to create a Twitter client in Scala.”

Problem

You want to create a client to connect to Twitter to access the information you want, such as showing timelines and trends.

Solution

You could write web service client code to connect to Twitter, but the Java Twitter4J library provides an easy-to-use API that wraps the Twitter REST API. This example shows how to get information from your friend’s timelines using Twitter4J:

import twitter4j.TwitterFactory
import twitter4j.Twitter
import twitter4j.conf.ConfigurationBuilder

object ScalaTwitterClientExample {

    def main(args : Array[String]) {

        // (1) config work to create a twitter object
        val cb = new ConfigurationBuilder
        cb.setDebugEnabled(true)
            .setOAuthConsumerKey("YOUR KEY HERE")
            .setOAuthConsumerSecret("YOUR SECRET HERE")
            .setOAuthAccessToken("YOUR ACCESS TOKEN")
            .setOAuthAccessTokenSecret("YOUR ACCESS TOKEN SECRET")
        val tf = new TwitterFactory(cb.build)
        val twitter = tf.getInstance

        // (2) use the twitter object to get your friend's timeline
        val statuses = twitter.getFriendsTimeline
        println("Showing friends timeline.")
        val it = statuses.iterator
        while (it.hasNext()) {
            val status = it.next
            println(status.getUser.getName + ":" + status.getText);
        }
    }

}

The Twitter4J library is a custom web services library meant for interacting with the Twitter developer’s API. The library methods wrap the Twitter REST API, and because all of these methods have been created, it’s easier to use than manually writing the equivalent web service requests. The code shown here is a straight Scala port of the Java example at the Twitter4J documentation page.

The code also shows that you’ll need a series of developer tokens to work with the Twitter API. You can get those tokens at the Twitter developer website.

Discussion

As a more advanced example, the following code demonstrates how to retrieve Twitter daily trends and location trends. Besides using Twitter4J, it also uses the Akka actor library to make the calls in parallel:

import twitter4j.Twitter
import akka.dispatch.{Await, Future}
import akka.util.duration._
import akka.actor.ActorSystem

object GetTwitterTrendsWithAkka extends TwitterBase {

  val propertiesFile = "/Users/Al/TwitterScripts/twitter.properties"
  val woeidWorld = 1
  val woeidUnitedStates = 23424977
  val emailSubject = "Twitter Trends"

  def main(args : Array[String]) {

      // read the properties file and create a Twitter instance
      populatePropertiesFromConfigFile(propertiesFile)
      val twitter = getTwitterInstance

      // get an ActorSystem in scope for the futures
      implicit val system = ActorSystem("TwitterFutureSystem")

      // make the calls in parallel using Future objects
      val dailyTrendsFuture = Future { getDailyTrends(twitter) }
      val worldFuture = Future { getLocationTrends(twitter, woeidWorld) }

      // wait for the calls to return before moving on
      val dailyTrends = Await.result(dailyTrendsFuture, 5 seconds)
      val worldTrends = Await.result(worldFuture, 5 seconds)
      println(dailyTrends)
      println(worldTrends)
  }

  def getDailyTrends(twitter: Twitter): String = {
    var sb = new StringBuilder
    val dailyTrends = twitter.getDailyTrends
    val trends = dailyTrends.get(1)
    for (trend <- trends.getTrends) {
        sb.append(trend.getName + "\n")
    }
    sb.toString
  }

  def getLocationTrends(twitter: Twitter, loc: Int): String = {
      var sb = new StringBuilder
      val locationTrends = twitter.getLocationTrends(loc)
      val trends = locationTrends.getTrends
      for (trend <- trends) {
          sb.append(trend.getName + "\n")
      }
      sb.toString
  }
}

This object extends a TwitterBase class, which is shown here:

import twitter4j.conf.ConfigurationBuilder
import twitter4j.Twitter
import twitter4j.TwitterFactory

/**
  * A base class to handle Twitter setup needs.
  */
class TwitterBase {

  // twitter
  var consumerKey = ""
  var consumerSecret = ""
  var accessToken = ""
  var accessTokenSecret = ""
  var twitterUsername = ""

  // email
  var emailSendTo = ""
  var emailFrom = ""
  var emailSmtpHost = ""

  def getTwitterInstance: Twitter = {
      val cb = new ConfigurationBuilder()
      cb.setDebugEnabled(true)
          .setOAuthConsumerKey(consumerKey)
          .setOAuthConsumerSecret(consumerSecret)
          .setOAuthAccessToken(accessToken)
          .setOAuthAccessTokenSecret(accessTokenSecret)
      return new TwitterFactory(cb.build()).getInstance
  }

  def populatePropertiesFromConfigFile(propertiesFilename: String) {
      val properties = Utils.loadPropertiesFile(propertiesFilename)
      consumerKey = properties.getProperty("oauth.consumerKey")
      consumerSecret = properties.getProperty("oauth.consumerSecret")
      accessToken = properties.getProperty("oauth.accessToken")
      accessTokenSecret = properties.getProperty("oauth.accessTokenSecret")
      twitterUsername = properties.getProperty("twitter_username")
      emailSendTo = properties.getProperty("email_send_to")
      emailFrom = properties.getProperty("email_from")
      emailSmtpHost = properties.getProperty("email_smtp_host")
  }
}

I created that base class so I could write a series of small Twitter scripts and keep my Twitter token information in a properties file. I execute those scripts using the cron daemon on one of my Linux servers and mail the results to myself once or twice a day. I named the properties file twitter.properties, and it contains contents like this:

debug=true
oauth.consumerKey=KEY1
oauth.consumerSecret=KEY2
oauth.accessToken=KEY3
oauth.accessTokenSecret=KEY4
twitter_username=YOUR_USERNAME

As shown in this file, you’ll need a series of developer tokens for these scripts to work. You can get those tokens at the Twitter developer website.

Though that may seem like a lot of code, most of it is setup work. Once the Twitter instance is configured, the work of connecting to the Twitter web service happens in these two lines of code:

val dailyTrends = twitter.getDailyTrends
val locationTrends = twitter.getLocationTrends(loc)

The code also shows how to use the TwitterBase class to handle the basic configuration. As shown in the GetTwitterTrendsWithAkka object, you can extend this class, and then call these two lines to create a Twitter instance:

populatePropertiesFromConfigFile(propertiesFile)
val twitter = getTwitterInstance

Once you have that, use the rest of the Twitter4J library API to accomplish whatever tasks you want.

See Also