読者です 読者をやめる 読者になる 読者になる

ゲンゾウ用ポストイット

Java、Groovy、Linux関連の備忘録

Groovyでfacebookへ近況書き込み

facebook Groovy

先日成功したfacebookのOAuth2.0認証方式をクラス化した。

クラスと使用例をあげる。
OAuth2.0認証にて取得したアクセストークンさえあれば、restfbライブラリを利用してfacebookに自由にアクセスできる。
RestFB - A Lightweight Java Facebook Graph API and Old REST API Client

FacebookOauth.Groovy

#!/usr/bin/env groovy
@Grab("org.mortbay.jetty:jetty-embedded:6.1.26")
import java.lang.reflect.InvocationTargetException
import java.lang.reflect.Method
import javax.servlet.http.*
import javax.servlet.ServletException
import org.mortbay.jetty.Connector
import org.mortbay.jetty.Handler
import org.mortbay.jetty.handler.AbstractHandler
import org.mortbay.jetty.Request
import org.mortbay.jetty.Server

class FacebookOauth {
    def appId
    def localHost = "localhost"
    def localPort = 9999
    def scopeList = []

    def String accessToken = null
    def String expiresIn = null

    def accessTokenFile = new File("access_token.txt")

    void load() {
        if( accessTokenFile.exists() )
            this.accessToken = accessTokenFile.text
    }

    void save() {
            accessTokenFile.write this.accessToken
    }



    String auth() {
        assert appId
        assert localHost
        assert localPort
        assert !accessToken
        assert !expiresIn
        assert scopeList && !scopeList.empty

        def urltext = "https://www.facebook.com/dialog/oauth?" + [
                        "client_id=${appId}",
                        "redirect_uri=" + URLEncoder.encode("http://${localHost}:${localPort}/login_success.html", "utf-8"),
                        "scope=" + URLEncoder.encode(scopeList.join(","), "utf-8"),
                        "response_type=token",
                    ].join("&")

        def browser = { url ->
            switch(System.getProperty("os.name", "")) {
                case ~/^Mac OS/:
                    Class fileMgr = Class.forName("com.apple.eio.FileManager")
                    Method openURL = fileMgr.getDeclaredMethod("openURL", [ String.class ] as Class[])
                    openURL.invoke(null, [ url ] as Object[])
                    break
                case ~/^Windows/:
                    return Runtime.runtime.exec("rundll32 url.dll,FileProtocolHandler " + url)
                    break
                default:
                    def enableBrowser = [ "firefox", "opera", "konqueror", "epiphany", "mozilla", "netscape" ].find{
                        Runtime.runtime.exec([ "which", it ] as String[]).waitFor() == 0
                    }

                    if (!enableBrowser)
                        throw new NoSuchMethodException("Could not find web browser")

                    return Runtime.runtime.exec(enableBrowser + " ${url}")
                    break
            }
        }(urltext)

        def server = new Server(localPort)
        server.with{
            connectors.each{
                it.host = "${localHost}"
            }

            handler = [
                handle:{ String target, HttpServletRequest request, HttpServletResponse response, int dispatch ->
                    switch(target) {
                        case "/login_success.html":
                            response.with{
                                status = HttpServletResponse.SC_OK
                                contentType = "text/html"
                                writer.withWriter{
                                    it.println("<html>")
                                    it.println("<head>")
                                    it.println("<script type='text/javascript'>")
                                    it.println("function init() {")
                                    it.println("    window.location = 'http://localhost:9999/access_token.html?' + window.location.toString().split('#')[1];");
                                    it.println("}")
                                    it.println("</script>")
                                    it.println("</head>")
                                    it.println("<body onload=\"init();\">")
                                    it.println("</body>")
                                    it.println("</html>")
                                }
                            }
                            break
                        case "/access_token.html":
                            synchronized(this){
                                accessToken = request.getParameter("access_token")
                                expiresIn = request.getParameter("expires_in")
                            }
                            request.handled = true
                            break
                        default:
                            response.status = HttpServletResponse.SC_NOT_FOUND
                            break
                    }

                }
            ] as AbstractHandler

        }
        server.start()

        while ( !accessToken ) {
            Thread.sleep(1000)
        }
        browser.destroy()
        server.stop()
        server.join()

        this.accessToken
    }
}

Example.groovy

#!/usr/bin/env groovy
@Grab("com.restfb:restfb:1.6.9")
import com.restfb.*
import com.restfb.types.*

FacebookOauth oauth = new FacebookOauth(
    appId:"XXXXXXXXXX", // acebookに登録したアプリケーションのIDを設定
    scopeList:[ // スコープの設定を配列で列挙。必要なら権限を追加する。
        "email",
        "read_stream",
        "friends_about_me",
        "read_friendlists",
        "user_about_me",
        "publish_stream",
        "offline_access",
        "create_event"
    ],
    // その他、認証開始のためのローカルサーバー用設定値(localHost、localPort)や
    // アクセストークン保存ファイル(accessTokenFile)などのプロパティも設定可能
    // 今回はデフォルトのまま使用。
)

// loadすれば、以前取得したトークンがあればそれを利用しfacebookと通信する。
oauth.load()

// loadしてもトークンがからだった場合(=初めての認証)、認証手続きにてアクセストークン取得
FacebookClient client = new DefaultFacebookClient(oauth.accessToken ?: oauth.auth() )

// 今後も利用したい場合、アクセストークンを保存
oauth.save()

5.times {
    FacebookType publishMessageResponse = client.publish(
        "me/feed",
        FacebookType.class,
        Parameter.with("message", "ゲンゾウ時計:" + new Date().format("yyyy/MM/dd HH:mm:ss"))
    )
    Thread.sleep(5000)
}


近況報告を5秒ごとに登録してみた。


明日はユーザー情報の一覧を取得してみる。
もう少しでようやく当初の目標である、ユーザーごとの近況報告一覧表示ができるようになりそうだ。

広告を非表示にする