Twitter APIを使ってみる

OAuth::LiteではてなのOAuth対応APIを使う - koba::blog で、はてなのOAuth対応APIは使ってみたので、今度はTwitterAPIにチャレンジ。

Twitter APIのドキュメントは以下にある。

AP登録

Twitterの参照系のAPIにはAP登録しなくても使えるものもあるけれど、更新系のAPIは当然ながらAP登録が必要。以下のURLから登録する。

Callback URL を指定しないと、xAuthでの認証になるので要注意。Callback URL は Request Token 取得時に指定できるので、WebSite欄と同じものを記入しておけばOK。*1

Name にはAP名を指定するのだけど、重複が許されないので空いている名前を探すのがちょっと面倒。

ここでの設定値は後で変更することもできるので、あまり悩まずに登録すればいい。

登録が済むと、以下の設定値が表示されるので、これを認証時に使う。

Consumer key (AP固有の値)
Consumer secret (AP固有の値)
Request token URL https://api.twitter.com/oauth/request_token
Authorize URL https://api.twitter.com/oauth/authorize
Access token URL https://api.twitter.com/oauth/access_token

認証のプログラムは OAuth::Liteを使って、はてなでOAuth認証 - koba::blog とほぼ同じなので省略。

account/verify_credentials

認証者の情報を取得するAPI。認証がうまくいったか確認するために、まずアクセスするとよい。いろんな値が返って来るけど、今回はユーザ名、アイコン画像のURL、自己紹介を使う。

    my $response = $consumer->request(
            method  => 'GET',
            url     => 'http://api.twitter.com/1/account/verify_credentials.json',
            token   => $access_token,
        );
    my $user = from_json($response->decoded_content);
    my $screen_name = $user->{screen_name};
    my $image_url   = $user->{profile_image_url};
    my $description = $user->{description};

statuses/home_timeline

認証者のタイムラインを取得するAPI。ツイート内容と、投稿者の情報を使う。

    $response = $consumer->request(
            method  => 'GET',
            url     => 'http://api.twitter.com/1/statuses/home_timeline.json',
            token   => $access_token,
        );
    my @status
        = map {
                my $text        = $_->{text};
                my $screen_name = $_->{user}{screen_name};
                my $image_url   = $_->{user}{profile_image_url};
                $text =~ s|(http://\S+)|<a href="$1">$1</a>|g;
                $cgi->div(
                    $cgi->img({-src=>$image_url,-alt=>'',-style=>'float:left'}),
                    $cgi->div({-style=>'margin-left:60px'},
                        $cgi->strong($screen_name),
                        $cgi->p($text)),
                    $cgi->hr({-style=>'clear:left'}),
                );
            } @{from_json($response->decoded_content)};

本当はリツイートを正しく表示すべき*2だけど、今回は手抜き。

statuses/update

ツイートするAPI。当然ながらメソッドはPOST。

    my $response = $consumer->request(
            method  => 'POST',
            url     => 'http://api.twitter.com/1/statuses/update.json',
            params  => { status => $status },
            token   => $access_token,
        );

全体のコード

#!/usr/bin/perl -T

use strict;
use warnings;
use CGI;
use CGI::Carp qw(fatalsToBrowser);
use OAuth::Lite::Consumer;
use OAuth::Lite::Token;
use JSON;

my $consumer_key    = '_____________________';
my $consumer_secret = '_________________________________________';

my $title = 'Twitter';

my $cgi = new CGI;
$cgi->charset('utf-8');

my $consumer = new OAuth::Lite::Consumer(
        consumer_key    => $consumer_key,
        consumer_secret => $consumer_secret,
    );

if ($cgi->path_info eq '/') {

    my $access_token = OAuth::Lite::Token->from_encoded(
                                            $cgi->cookie('TWITTER_OAUTH'));
    my $response = $consumer->request(
            method  => 'GET',
            url     => 'http://api.twitter.com/1/account/verify_credentials.json',
            token   => $access_token,
        );
    if (! $response->is_success) {
        print $cgi->redirect($cgi->url.'/initiate');
        exit;
    }
    my $user = from_json($response->decoded_content);
    my $screen_name = $user->{screen_name};
    my $image_url   = $user->{profile_image_url};
    my $description = $user->{description};

    $response = $consumer->request(
            method  => 'GET',
            url     => 'http://api.twitter.com/1/statuses/home_timeline.json',
            token   => $access_token,
        );
    my @status
        = map {
                my $text        = $_->{text};
                my $screen_name = $_->{user}{screen_name};
                my $image_url   = $_->{user}{profile_image_url};
                $text =~ s|(http://\S+)|<a href="$1">$1</a>|gs;
                $cgi->div(
                    $cgi->img({-src=>$image_url,-alt=>'',-style=>'float:left'}),
                    $cgi->div({-style=>'margin-left:60px'},
                        $cgi->strong($screen_name),
                        $cgi->p($text)),
                    $cgi->hr({-style=>'clear:left'}),
                );
            } @{from_json($response->decoded_content)};

    print   $cgi->header,
            $cgi->start_html(
                -title      => "[$title] $screen_name",
                -encoding   => $cgi->charset,
                -lang       => 'ja-JP',
            ),
            $cgi->img({-src=>$image_url,-alt=>'',-style=>'float:left'}),
            $cgi->div({-style=>'margin-left:60px'},
                $cgi->a({-href=>$cgi->url.'/'},
                        $cgi->h1({-style=>'font-size:120%'},$screen_name)),
                $cgi->p($cgi->em($description))),
            $cgi->start_form('POST',$cgi->url.'/post'),
            $cgi->textarea('status','',3,40),
            $cgi->div($cgi->submit),
            $cgi->endform,
            $cgi->hr,
            $cgi->div(join("\n", @status)),
            $cgi->end_html;
}
elsif ($cgi->path_info eq '/post') {

    my $status = $cgi->param('status');
    my $access_token = OAuth::Lite::Token->from_encoded(
                                            $cgi->cookie('TWITTER_OAUTH'));
    my $response = $consumer->request(
            method  => 'POST',
            url     => 'http://api.twitter.com/1/statuses/update.json',
            params  => { status => $status },
            token   => $access_token,
        );
    print $cgi->redirect($cgi->url.'/');
}
elsif ($cgi->path_info eq '/initiate') {
   
    my $request_token = $consumer->get_request_token(
            url             => 'https://api.twitter.com/oauth/request_token',
            callback_url    => $cgi->url.'/token',
        )   or die $consumer->errstr."\n";
    my $cookie = $cgi->cookie(
            -name   => 'TWITTER_OAUTH',
            -value  => $request_token->as_encoded,
            -path   => $cgi->url(-absolute=>1),
        );
    my $url = $consumer->url_to_authorize(
            url     => 'https://api.twitter.com/oauth/authorize',
            token   => $request_token,
        );
    print $cgi->redirect(
            -url    => $url,
            -cookie => $cookie,
        );
}
elsif ($cgi->path_info eq '/token') {

    my $oauth_verifier  = $cgi->param('oauth_verifier');
    my $request_token   = OAuth::Lite::Token->from_encoded(
                                            $cgi->cookie('TWITTER_OAUTH'));
    my $access_token = $consumer->get_access_token(
            url         => 'https://api.twitter.com/oauth/access_token',
            token       => $request_token,
            verifier    => $oauth_verifier,
        )   or die Dumper([$consumer,$request_token]);
    my $cookie = $cgi->cookie(
            -name   => 'TWITTER_OAUTH',
            -value      => $access_token->as_encoded,
            -path       => $cgi->url(-absolute=>1),
            -expires    => '+90d',
        );
    print $cgi->redirect(
            -url    => $cgi->url.'/',
            -cookie => $cookie,
        );
}
else {

    print $cgi->redirect($cgi->url.'/');
}

今回も Access Token を cookie に保存するという荒っぽいやり方なので、ご注意を。

*1:2018年6月に規約が変更され、現在はCallback URLのチェックが行われるようになっています

*2:リツイートはオリジナルの情報が $_->{retweeted_status}に入っている