ロリポップで動く簡易Twitterブラウザアプリを作ってみる。

前提

以下のモジュールで立ち上げます。

デプロイ

ここにかなり時間を要してしまった。
上記の2つのモジュール以外に用意したモジュールは以下の通り。

  • JSON::Any
  • Net::OAuth
  • Class::Accessor

Twitterの開発者ページでアプリケーションを登録します。

  • ローカル環境で試験する場合も適当なURLを指定する必要があります。
    • localhostとかだと登録できなかった気がしますが、実際に公開するまでは使いません。
  • 今回はブラウザアプリケーションで登録しました。(違いは認証後ユーザがPINの入力が必要なのがクライアントアプリケーション, コールバックのURLで認証処理を行うのがブラウザアプリケーションだと思います)
  • さらに今回は読み取り専用にしてあります。(登録後に変更可能ですが現在読み書きで試してもすぐには更新されませんでした)
  • ここで登録した時に表示される「Consumer key」と「Consumer secret」をメモる。

OAuthの流れ

OAuthの簡単な流れはこんな感じ。

  1. Twitterの認証ページを取得する。(コールバックURLを指定する)
    • ここでのコールバックURLは登録したもの以外でも可能なためデバッグの際などはlocalhostなども使用可能です。
  2. 認証ページへリダイレクトする。
  3. ユーザがTwitterの認証ページで「許可する」を選択すると上で指定したコールバックURLへリダイレクトされてきます。
  4. コールバックURLではパラメータで渡されてきたoauth_verifierの値を再度Twitterへ提供することでユーザの認証コード(Access token/Access token secret)を取得することができます。
  5. 以降はこのAccess token/Access token secretを使用することで認証処理をスルーすることができます。
コーディング開始
  1. 基本的なルートを3つ( index, auth, auth_cb )とLogout用のルート(logout)を用意しました。
use Mojolicious::Lite;
use Net::Twitter::Lite;
use utf8;

my $nt = Net::Twitter::Lite->new(
    consumer_key    => YOUR-CONSUMER-KEY,
    consumer_secret => YOUR-CONSUMER-SECRET,
);

# コールバック
get '/auth_cb' => sub {
    my $self = shift;
    my $verifier = $self->param('oauth_verifier') || '';
    my $token = $self->session('token') || '';
    my $token_secret = $self->session('token_secret') || '';

    $nt->request_token( $token );
    $nt->request_token_secret( $token_secret );

    # Access token取得
    my ($access_token, $access_token_secret, $user_id, $screen_name)
        = $nt->request_access_token( verifier => $verifier );

    # Sessionに格納
    $self->session( access_token => $access_token );
    $self->session( access_token_secret => $access_token_secret );

    $self->redirect_to( 'index' );
} => 'auth_cb';

# 認証
get '/auth' => sub {
    my $self = shift;
    my $cb_url = YOUR-CALLBACK-URL;
    my $url = $nt->get_authorization_url( callback => $cb_url );

    $self->session( token => $nt->request_token );
    $self->session( token_secret => $nt->request_token_secret );

    $self->redirect_to( $url );
} => 'auth';

# Session削除
get '/logout' => sub {
    my $self = shift;
    $self->session( expires => 1 );
    $self->render;
} => 'logout';

# Index
get '/' => sub {
    my $self = shift;
    my $access_token = $self->session( 'access_token' ) || '';
    my $access_token_secret = $self->session( 'access_token_secret' ) || '';

    # セッションにaccess_token/access_token_secretが残ってなければ認証処理へ
    return $self->redirect_to( 'auth' ) unless ($access_token && $access_token_secret);

    $nt->access_token( $access_token );
    $nt->access_token_secret( $access_token_secret );

    # タイムラインを取得
    my $timeline = $nt->home_timeline;
    $self->render( timeline => $timeline );
} => 'index';

app->secret(YOUR-SECRET-CODE); # セッション管理のために付けておく
app->start;

これに対してテンプレートは以下の通り。

@@ layouts/default.html.ep
<!DOCTYPE HTML>
<html>
<head>
    <meta charset="UTF-8">
    <title>Mojolicious with Twitter!</title>
    <script type="text/javascript">
function update_page() {
    location.reload();
}
    </script>
    <style type="text/css">
    * { padding: 0px; margin: 0px; }
    #header {
        background-color: #320;
	position:absolute;
	width:600px;
	left:50%;
	margin-left:-300px;
    }
    #header h1 {
	position:relative;
        font-size: 42px;
        color: bisque;
        width: 100%;
        text-align:center;
    }
    #header p {
	position:relative;
	font-size: 16px;
	width: 100%;
	text-align:center;
    }
    #wrapper {
	position:relative;
	width: 600px;
	left: 50%;
	margin-left: -300px;
	border: 1px solid #320;
    }
    .tweet {
	position:relative;
	width: 100%;
	height: 120px;
	background-color: antiquewhite;
	border-bottom: 1px solid #320;
	padding: 0px;
    }
    .tweet :last-cd {
	border-width: 0;
    }
    .tweet img {
	height: 50px;
	width: 50px;
	margin-left: 0px;
    }
    .tweet ul {
	list-style:none;
	position:absolute;
	top: 0px;
	margin-left: 80px;
        padding-top: 5px;
	width: 500px;
    }
    </style>
</head>
<body onload="setTimeout( 'update_page()', 30000 )">
    <div id="header">
        <h1>Mojo!</h1>
        <p>
            <%= link_to 'logout' => begin %>
                Logout
            <% end %>
        </p>
    </div>
    <div id='wrapper'>
        <%= content %>
    </div>
</body>
</html>

@@ index.html.ep
% layout 'default';
<% foreach my $tw (@$timeline) { %>
    <div class='tweet'>
        <img src='<%= $tw->{user}->{profile_image_url} %>' />
        <ul>
            <li><%= $tw->{user}->{screen_name} %></li>
            <li><%= $tw->{text} %></li>
        </ul>
    </div>
<% } %>

@@ logout.html.ep
See you!<br/>
<%= link_to 'index' => begin %>
    Home
<% end %>

@@ exception.html.ep
<%# エラー発生時に便利 %>
<%= $exception %>

こんな感じで作ったサンプルがここ
(30秒間隔で更新するようになっています)