koba::blog

小林聡: プログラマです

OAuth::LiteではてなのOAuth対応APIを使う

OAuth::Liteを使って、はてなでOAuth認証 - koba::blogで認証はできたので、はてなのOAuth対応APIを使ってみる。

JSONを扱う必要があるので、以下のPerlモジュールをインストールした。

OAuth対応API

なぜかはてなAPIAtomインタフェースのものが多いのだが、JSONに対応している以下のAPIを使ってみた。

ユーザー情報を表示して、はてなハイクに投稿してみる。

これだけでは芸がないので、以下を参考にGoogle画像検索も使ってみた。

処理の流れ

今回はコードをちょっと整理して、path_info で処理を振分けるようにしました。

/ ユーザ情報を取得して、ハイクのお題入力フォームを表示する。
/image お題でGoogle画像検索し、画像選択とハイク本文入力フォームを表示する。
/post ハイクを投稿し、ハイクのページにリダイレクトする。
/initiate Request Token を取得する。(前回とほぼ同じなので説明は省略)
/token Access Token を取得する。(前回とほぼ同じなので説明は省略)

/

request() を使ってAPIをコールする。

    my $response = $consumer->request(
            method  => 'GET',
            url     => 'http://n.hatena.com/applications/my.json',
            token   => $access_token,
        );

LWP::UserAgent とだいたい同じだけど、token に取得済みの Access Token を設定するとこが違う。
応答はJSON形式なので、from_json() を使ってデコードする。応答の本体を取得するときに decoded_content() を使用するのを忘れずに。

    my $user = from_json($response->decoded_content);

以下の値が取得できます。

url_name はてなid
display_name ニックネーム
profile_image_url プロフィール画像のURL

/image

パラメータは以下。

keyword
ハイクのお題

request() を使ってGoogleAPIをコールする。

    my $response = $consumer->request(
            method  => 'GET',
            url     => 'http://ajax.googleapis.com/ajax/services/search/images',
            params  => { v => '1.0', q => $cgi->param('keyword') },
        );               

Access Token は指定してはいけません(Access Token がGoogleにもれてしまうので)。本来は接続先ごとに LWP::UserAgent オブジェクトを用意するのが正しい作法だけど、今回は手抜き。

ごちゃごちゃとたくさん情報が返ってくるので、検索結果の配列(4つ返ってきます)だけを取得する。

        my $results = from_json($response->decoded_content)
                                        ->{responseData}->{results};

配列内の以下の情報を使う。

unescapedUrl 画像のURL(エスケープされていない)
originalContextUrl 画像を含むページのURL
tbUrl サムネイルのURL

/post

パラメータは以下。

keyword
ハイクのお題
image
画像のURLと画像を含むページのURLを改行で連結したもの
body
ハイク本文

はてなハイクAPIをコールする。

    my $response = $consumer->request(
            method  => 'POST',
            url     => 'http://h.hatena.ne.jp/api/statuses/update.json',
            params  => { keyword    => $keyword,
                         status     => "$image\n$body",
                         source     => $title   },
            token   => $access_token,
        );

応答に投稿したハイクのURLがあるのでこれを取得してリダイレクトする。

    my $url = from_json($response->decoded_content)->{link};

    print $cgi->redirect($url);

全体のコード

#!/usr/bin/perl -T

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

my $consumer_key   = '________________';
my $consumer_secret    = '____________________________';

my $title = '絵俳句';

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('HATENA_OAUTH'));
    my $response = $consumer->request(
            method  => 'GET',
            url     => 'http://n.hatena.com/applications/my.json',
            token   => $access_token,
        );
    if (! $response->is_success) {
        print $cgi->redirect($cgi->url.'/initiate');
        exit;
    }
    my $user = from_json($response->decoded_content);

    print $cgi->header,
          $cgi->start_html(
                -title      => $title,
                -encoding   => 'utf-8',
                -lang       => 'ja-JP',
            ),
          $cgi->h1($cgi->a({href=>$cgi->url},$title)),
          $cgi->img({src=>$user->{profile_image_url}}),
          $cgi->h2("$user->{display_name} (id:$user->{url_name})"),
          $cgi->start_form('GET', './image'),
          $cgi->textfield('keyword'),
          $cgi->submit,
          $cgi->end_form,
          $cgi->end_html;
}
elsif ($cgi->path_info eq '/image') {

    if (! $cgi->param('keyword')) {
        print $cgi->redirect($cgi->url.'/');
        exit;
    }

    my $access_token = OAuth::Lite::Token->from_encoded(
                                            $cgi->cookie('HATENA_OAUTH'));
    my $response = $consumer->request(
            method  => 'GET',
            url     => 'http://n.hatena.com/applications/my.json',
            token   => $access_token,
        );
    if (! $response->is_success) {
        print $cgi->redirect($cgi->url.'/initiate');
        exit;
    }
    my $user = from_json($response->decoded_content);

    my $image;
    my $response = $consumer->request(
            method  => 'GET',
            url     => 'http://ajax.googleapis.com/ajax/services/search/images',
            params  => { v => '1.0', q => $cgi->param('keyword') },
        );
    if (! $response->is_success) {
        $image = $cgi->p($response->status_line);
    }
    else {
        my $results = from_json($response->decoded_content)
                                        ->{responseData}->{results};
        foreach (@$results) {
            my $value = $_->{unescapedUrl}."\n".$_->{originalContextUrl};
            $image .= $cgi->label(
                            $cgi->input(
                                {name=>'image',value=>$value,type=>'radio'}),
                            $cgi->img({src=>$_->{tbUrl}}),
                            $cgi->a({href=>$_->{originalContextUrl}},'(site)'),
                            "\n",
                    );
        }
    }

    print $cgi->header,
          $cgi->start_html(
                -title      => $title,
                -encoding   => 'utf-8',
                -lang       => 'ja-JP',
            ),
          $cgi->h1($cgi->a({href=>$cgi->url},$title)),
          $cgi->img({src=>$user->{profile_image_url}}),
          $cgi->h2("$user->{display_name} (id:$user->{url_name})"),
          $cgi->h3($cgi->param('keyword')),
          $cgi->start_form('POST', './post'),
          $cgi->hidden('keyword'),
          $image, $cgi->br,
          $cgi->textarea('body', '', 4, 40),    $cgi->br,
          $cgi->submit,
          $cgi->end_form,
          $cgi->end_html;
}
elsif ($cgi->path_info eq '/post') {

    my $keyword = $cgi->param('keyword');
    my $image   = $cgi->param('image');
    my $body    = $cgi->param('body');

    my $access_token = OAuth::Lite::Token->from_encoded(
                                            $cgi->cookie('HATENA_OAUTH'));
    my $response = $consumer->request(
            method  => 'POST',
            url     => 'http://h.hatena.ne.jp/api/statuses/update.json',
            params  => { keyword    => $keyword,
                         status     => "$image\n$body",
                         source     => $title   },
            token   => $access_token,
        );

    if (! $response->is_success) {

        print $cgi->header,
            $cgi->start_html(
                    -title      => $title,
                    -encoding   => 'utf-8',
                    -lang       => 'ja-JP',
                ),
            $cgi->h1($cgi->a({href=>$cgi->url},$title)),
            $cgi->h2($response->status_line),
            $cgi->p($response->decoded_content),
            $cgi->end_html;
        exit;
    }

    my $url = from_json($response->decoded_content)->{link};

    print $cgi->redirect($url);
}
elsif ($cgi->path_info eq '/initiate') {

    my $request_token = $consumer->get_request_token(
            url             => 'https://www.hatena.com/oauth/initiate',
            callback_url    => $cgi->url.'/token',
            scope           => 'read_public,write_public',
        )   or die $consumer->errstr."\n";
    my $cookie = $cgi->cookie(
            -name       => 'HATENA_OAUTH',
            -value      => $request_token->as_encoded,
            -path       => $cgi->url(-absolute=>1),
        );
    my $url = $consumer->url_to_authorize(
            url     => 'https://www.hatena.ne.jp/oauth/authorize',
            token   => $request_token,
        );
    print $cgi->redirect(
            -url        => $url,
            -cookie     => $cookie,
        );
    exit;
}
elsif ($cgi->path_info eq '/token') {

    my $oauth_verifier  = $cgi->param('oauth_verifier');
    my $request_token = OAuth::Lite::Token->from_encoded(
                                            $cgi->cookie('HATENA_OAUTH'));

    my $access_token = $consumer->get_access_token(
            url         => 'https://www.hatena.com/oauth/token',
            token       => $request_token,
            verifier    => $oauth_verifier,
        );
    my $cookie = $cgi->cookie(
            -name       => 'HATENA_OAUTH',
            -value      => $access_token->as_encoded,
            -path       => $cgi->url(-absolute=>1),
            -expires    => '+14d',
        );
    print $cgi->redirect(
            -url        => $cgi->url.'/',
            -cookie     => $cookie,
        );
}
else {

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

今回は Request Token と Access Token を Cooklie に保存してますが、これだとネットワークに流れてしまうので、いけないやり方です。