an odd fellow

仕事のメモ

clojureからtwitter4jを使う

春休みなのでtwitterクライアント作ります。

手始めにストリーミングAPIでユーザーストリームを取得しました。

手順は以下

  1. project.cljにtwitter4j使えるようゴニョゴニョ書く

  2. ConsumerKeyやらAccessTokenやら設定してやってConfigurationBuilderを作る

  3. UserStreamListenerを定義してやる

  4. TwitterStreamFactoryクラスのインスタンスを作成する

1. project.cljにtwitter4j使えるようゴニョゴニョ書く

clojarsとか覗くとtwitter4jのラッパーっぽいライブラリもあったけど、clojureからtwitter4jを直接叩こうと思います。

project.cljに以下を追加

[org.twitter4j/twitter4j-core "[3.0,)"]
[org.twitter4j/twitter4j-stream "[3.0,)"]
[org.twitter4j/twitter4j-async "[3.0,)"]

2. ConsumerKeyやらAccessTokenやら設定してやってConfigurationBuilderを作る

コード書いてきます。

twitter4jのコード例を眺めると最初にConsumerKeyなどの設定が必要なようです。

具体的な設定はここに書いてありました。

今回はWebページの2番のConfigurationBuilderを用いてプログラムから設定しようと思います。

(def consumerKey "******************************")
(def consumerSecret "***************************")
(def accessToken "******************************")
(def accessTokenSecret "************************")

(defn make-configurationBuilder []
  (-> (doto (new ConfigurationBuilder)
    (.setDebugEnabled true)
    (.setOAuthConsumerKey consumerKey)
    (.setOAuthConsumerSecret consumerSecret)
    (.setOAuthAccessToken accessToken)
    (.setOAuthAccessTokenSecret accessTokenSecret))
    (.build)))

consumeKeyやらaccessTokenはTwitter Developpersであらかじめ取得しました。

3. UserStreamListenerを定義してやる

UserStreamListenerはタイムラインをストリームで取得していくなかで、新しいツイートを取得したり、ふぁぼられたり、そういう各イベントが起こったときにあれやれこれやれと設定しておくクラス、だと思います。多分。

とりあえず新しいツイートを取得したらスクリーンネームとツイートを表示するように設定しました。

onStatusってところがそうです。

(def listener
  (reify
    UserStreamListener
    (onStatus [this status]
      (println (-> (-> status (.getUser)) (.getScreenName)))
      (println (-> status (.getText)))
      (println))
    (onDeletionNotice [this statusDeletionNotice] nil)
    (onTrackLimitationNotice [this numberOfLimitedStatuses] nil)
    (onScrubGeo [this userId upToStatusId] nil)
    (onStallWarning [this warning] this)
    (onFriendList [this friendIds] nil)
    (onFavorite [this source target favoritedStatus] nil)
    (onUnfavorite [this source rarget favoritedStatus] nil)
    (onFollow [this source followedUser] nil)
    (onDirectMessage [this directMessge])
    (onUserListMemberAddition [this addedMember listOwner alist] nil)
    (onUserListSubscription [this subscriver listOwner alist] nil)
    (onUserListUnsubscription [this subscriber listOwner alist] nil)
    (onUserListCreation [this listOwner alist] nil)
    (onUserListUpdate [this listOwner alist] nil)
    (onUserListDeletion [this listOwner alist] nil)
    (onUserProfileUpdate [this updateUser] nil)
    (onBlock [this source blockedUser] nil)
    (onUnblock [this source unblockedUser] nil)
    (onException [this ex] nil)))

UserStreamListenerはインタフェースで具体的な実装は無いのでreifyマクロでメソッドを定義してやれば良いっぽいです。

※onStatusメソッドだけ定義してやれば表示はするのだけど、たとえばストリーミングでツイート取得しているときにどれかのツイートがふぁぼられたりすると例外吐いて止まってしまったので、とりあえずすべてのメソッドにnilを返すようにしてます。

4. TwitterStreamFactoryクラスのインスタンスを作成する

ストリーミングするのに必要な設定やクラスは用意ができたのでそれらをTwitterStreamFactoryクラスになげつけてやればストリーミングでツイートが取得できます。

(defn make-twitterStream []
  (-> (new TwitterStreamFactory (make-configurationBuilder)) (.getInstance)))

(defn -main []
  (let [twitterStream (make-twitterStream)]
  (-> twitterStream (.addListener listener))
  (-> twitterStream (.user))))

こんな感じ。

全体通すとこんな感じ。

(ns ronyatter-clj.core
  (:require clojure.string)
  (:import [twitter4j TwitterFactory TwitterStreamFactory StatusListener StatusAdapter UserStreamListener])
  (:import [twitter4j.conf ConfigurationBuilder]))

(def consumerKey "kI2ijJO8ODuWZ819Zrk7Q")
(def consumerSecret "IQ70vDqXVIthJmV70ieb1RNMdpP0YpYj191HgHZjKyM")
(def accessToken "107718872-9Jiv5HhdrOeh3NtAP3Dt1vMVErKe5MWTmVaXRlmZ")
(def accessTokenSecret "jECQWJ0Palrnxd9egf27ywSPgiagE46xMIH9MR9up7xlP")

(defn make-configurationBuilder []
  (-> (doto (new ConfigurationBuilder)
  (.setDebugEnabled true)
  (.setOAuthConsumerKey consumerKey)
  (.setOAuthConsumerSecret consumerSecret)
  (.setOAuthAccessToken accessToken)
  (.setOAuthAccessTokenSecret accessTokenSecret))
  (.build)))

(def listener
  (reify
  UserStreamListener
  (onStatus [this status]
    (println (-> (-> status (.getUser)) (.getScreenName)))
    (println (-> status (.getText)))
    (println))
  (onDeletionNotice [this statusDeletionNotice] nil)
  (onTrackLimitationNotice [this numberOfLimitedStatuses] nil)
  (onScrubGeo [this userId upToStatusId] nil)
  (onStallWarning [this warning] this)
  (onFriendList [this friendIds] nil)
  (onFavorite [this source target favoritedStatus] nil)
  (onUnfavorite [this source rarget favoritedStatus] nil)
  (onFollow [this source followedUser] nil)
  (onDirectMessage [this directMessge])
  (onUserListMemberAddition [this addedMember listOwner alist] nil)
  (onUserListSubscription [this subscriver listOwner alist] nil)
  (onUserListUnsubscription [this subscriber listOwner alist] nil)
  (onUserListCreation [this listOwner alist] nil)
  (onUserListUpdate [this listOwner alist] nil)
  (onUserListDeletion [this listOwner alist] nil)
  (onUserProfileUpdate [this updateUser] nil)
  (onBlock [this source blockedUser] nil)
  (onUnblock [this source unblockedUser] nil)
  (onException [this ex] nil)))

(defn make-twitterStream []
  (-> (new TwitterStreamFactory (make-configurationBuilder)) (.getInstance)))

(defn -main []
  (let [twitterStream (make-twitterStream)]
    (-> twitterStream (.addListener listener))
    (-> twitterStream (.user))))

(-main)

実行例

f:id:roronya:20140205123113p:plain

何故か二重出力・・・。 この記事みたひとで原因わかるひといたらおしえてくだちぃ。

2013.02.14追記

二重出力の原因はmainが二重で実行されてたせい。

一番最後の(-main)っていう記述がいらないんですね。