DispatchでOAuth

Twitter APIのBASIC認証は2010年6月に「廃止予定」というのが気になっている。
今、Scalaの本を読んで勉強しているので、Scalaで何か書いてみたい。
Twitter APIについては、これまでJava Twitter4Jを使用してプログラムを書いていた。Scalaの場合でもTwitter4Jを使えば良いのだが、それだとScalaの勉強にはならないと思って、Scala用のOAuthライブラリを探してみた。
その結果、Dispatchというものしか見つけられなかったので、これを使ってstatus更新処理を書いてみると、以下のようになった。(出力されるAuthorize URLにアクセスして、そこから表示されるPINを入力する)

import dispatch._
import oauth._
import oauth.OAuth._

object TwitterTest {
  import Http._

  val CONSUMER_KEY    = "XXX...."
  val CONSUMER_SECRET = "XXX...."
  val CONSUMER = Consumer(CONSUMER_KEY, CONSUMER_SECRET)

  def main(args: Array[String]) {
    val http = fix(new Http)

    val req_token = http(Auth.request_token(CONSUMER))
    println(req_token)
    val authorize_url = Auth.authorize_url(req_token)
    println("Authorize URL: " + authorize_url.to_uri)
    print("PIN: ")
    var PIN = readLine

    val (access_token: Token, user_id: String, screen_name: String)
      = http(Auth.access_token(CONSUMER, req_token, PIN))
    println(access_token)
    println("ACCESS_TOKEN value: "  + access_token.value)
    println("ACCESS_TOKEN secret: " + access_token.secret)
    println("user_id: "     + user_id)
    println("screen_name: " + screen_name)

    val status = "更新テスト#1です。"
    val res = http(Status.update(status, CONSUMER, access_token))
    println(res)
  }

  def fix(http: Http): Http = {
    // WARNING 
    // Invalid cookie header: "Set-Cookie: .....
    // .... Unable to parse expires attribute: ....
    import org.apache.http.cookie.params.CookieSpecPNames

    http.client.getParams().
        setParameter(CookieSpecPNames.DATE_PATTERNS,
            java.util.Arrays.asList("EEE, dd MMM-yyyy-HH:mm:ss z",
                                    "EEE, dd MMM yyyy HH:mm:ss z"))
    http
  }
}

import json._
import JsHttp._

object Twitter {
  val host = :/("twitter.com")
}

object Auth {
  val svc = Twitter.host / "oauth"

  def request_token(consumer: Consumer): Handler[Token]
    = request_token(consumer, OAuth.oob)

  def request_token(consumer: Consumer, callback_url: String) = 
    svc.secure / "request_token" << OAuth.callback(callback_url) <@ consumer as_token
    
  def authorize_url(token: Token)    = svc / "authorize" <<? token
  def authenticate_url(token: Token) = svc / "authenticate" <<? token
  
  def access_token(consumer: Consumer, token: Token, verifier: String) = 
    svc.secure.POST / "access_token" <@ (consumer, token, verifier) >% { m =>
      (Token(m).get, m("user_id"), m("screen_name"))
    }
}

object Status extends Request(Twitter.host / "statuses") {
  def update(status: String, consumer: Consumer, token: Token) = 
        new UpdateStatusBuilder(status, consumer, token)

  class UpdateStatusBuilder(status: String, consumer: Consumer, token: Token) 
          extends Builder[Handler[JsObject]] {
    def product = Status / "update.json" << Map("status" -> status) <@
                    (consumer, token) ># obj
  }
}

fix()の処理は省いても動作するが、警告が表示されるので追加しておいた。
Dispatchの使い方自体がよくわからないので苦労したが、型情報を頼りに試行錯誤した結果、とにかく動いた。