Twitter & mixi API はじめ

2010/02/06

PHPライブラリは誰かの「twitter2mixivoice.php」もあったけど、

(1) PEAR依存 (2) 自前だと修正・拡張ができる

なんで、ライブラリも自前で。

■問題点 (1) mixiボイスのAPIがない →よって、無理矢理ログインしてHTMLパース以外方法なし

(2) クロスドメイン制限 →swfで直接通信が理想なんだけど、mixiもtwitterもクロスドメイン制限があるので断念  サーバかまして処理する事に

■主なポイント (1) AMF通信 →高速かつFlexとオブジェクトの親和性が高いAMFを採用  JSON、XML変換できるけど、今は必要なし  てか、XMLって遅いし面倒だし通信においてメリットあるのか?といつも疑問に思う。

(2) データのマージ  twitterはJSON、mixiはテキスト取得して日付ソートしながらデータマージ  (超暫定で最低限のタイトルと日付だけ)

とりあえずデータ取得&マージまで作ったので、あとはマルチ投稿の仕様考える。 ある程度まとったら機能毎にクラス化へ。

途中までのソース うーん、デザイン的にコードは別途コンポーネント作った方がいいなw swf側ソースは、色々ネタが多いので別途記事にする予定(?)

■VoiceService.php

require_once 'Service.php';

class VoiceService extends Service {

    /**
     * データ取得
     **/
    public function getVoices() {
        $voices['mixi'] = $this->mixi_voices();
        $voices['twitter'] = $this->twitter_user_timeline();
        $results = $this->_marge_voices($voices);
        return $results;
    }

    /**
     * Twitterタイムライン取得
     **/
    private function twitter_user_timeline()
    {
        $this->_twitter_auth();
        $url = "http://twitter.com/statuses/user_timeline/{$this->twitter_user_name}.json";
        $url.= "?since=".urlencode(date('D, d M Y G:i:s GMT', strtotime('-10 day')));
        $results = $this->_twitter_request($url);
        return $results;
    }

    /**
     * Twitter投稿
     **/
    private function post_twitter()
    {
        if ($_POST['message']) {
            $this->_twitter_auth();
            $message = rawurlencode($_POST['message']);
            $url = "http://twitter.com/statuses/update.json";
            $posts = "status={$message}";
            $results = $this->_twitter_request($url, $posts);
            return $results;
        }
    }

    /**
     * mixiボイス取得
     **/
    private function mixi_voices()
    {
        $this->_mixi_login();
        $url = 'http://mixi.jp/list_echo.pl';
        $headers[] = "Cookie: {$this->cookie}";

        $html = $this->_mixi_request($url, $headers, 'POST');
        $results = $this->_parse_mixi_voice($html);
        return json_encode($results);
    }

    /**
     * mixiボイス投稿
     **/
    private function post_mixi_voice()
    {
        $this->_mixi_login();
        $url = 'http://mixi.jp/list_echo.pl';
        $headers[] = "Cookie: {$this->cookie}";
        $html = $this->_mixi_request($url, $headers, 'GET');
        $post_key = $this->_get_mixi_voice_post_key($html);

        $url = 'http://mixi.jp/add_echo.pl';
        $body = 'てすとー'.date('Y/m/d H:i');
        $posts = array(
            'body' => $body,
            'post_key' => $post_key,
            'redirect' => 'recent_echo',
            'default_value' => $body,
        );
        $html = $this->_mixi_request($url, $headers, 'POST', $posts);
        $results = mb_convert_encoding($html, 'UTF-8', 'EUC-JP');
        echo($results);
    }

    /**
     * データ形式の統一
     **/
    private function _marge_voices($values)
    {
        if (is_array($values)) {
            foreach ($values as $type => $value) {
                $voices = json_decode($value);
        foreach ($voices as $key => $voice) {
                    $time = strtotime($voice->created_at);
                    $sort_times['time'] = $time;
                    $result['type'] = $type; 
                    $result['time'] = $time;
                    $result['created_at'] = date('Y-m-d H:i:s', $time);
                    $result['text'] = $voice->text;
                    $results[] = $result;
               }
            }
        }
    if (is_array($result)) {
            array_multisort($sort_times, SORT_DESC, $results);
        json_encode($results);
        return $results;                            }
    }

    /**
     * twitter認証設定
     **/
    private function _twitter_auth()
    {
        $this->twitter_user_name = TWITTER_USER_NAME;
        $this->twitter_email = TWITTER_EMAIL;
        $this->twitter_password = TWITTER_PASS;
    }

    /**
     * mixi認証設定
     **/
    private function _mixi_auth()
    {
        $this->mixi_id = MIXI_ID;
        $this->mixi_email = MIXI_EMAIL;
        $this->mixi_password = MIXI_PASS;
    }

    /**
     * mixiログイン処理
     **/
    private function _mixi_login()
    {
        $this->_mixi_auth();

        $posts = array(
                'email' => $this->mixi_email,
                'password' => $this->mixi_password,
                'next_url' => 'http://mixi.jp/home.pl'
            );
        $headers[] = 'Content-Type: application/x-www-form-urlencoded';
        $url = 'http://mixi.jp/login.pl';
        $this->_mixi_request($url, $headers, 'POST', $posts);
    }

    /**
     * mixiボイスpost_key取得
     **/
    private function _get_mixi_voice_post_key($html)
    {
        if ($html) {
            $html = mb_convert_encoding($html, 'UTF-8', 'EUC-JP');
        }
        $rows = explode("\n", $html);
        if (is_array($rows)) {
            foreach ($rows as $key => $row) {
                $pattern = '|]+>(.*)]+>|U';
                preg_match("{$pattern}", $row, $values);

                preg_match('/post_key=(.+)&/', $row, $params);
                if ($params) {
                    $post_key = $params[1];
                    if ($post_key) {
                        return $post_key;
                    }
                }
            }
        }
    }

    /**
     * mixiボイスパース
     **/
    private function _parse_mixi_voice($html)
    {
        if ($html) {
            $html = mb_convert_encoding($html, 'UTF-8', 'EUC-JP');
        }
        $rows = explode("\n", $html);
        if (is_array($rows)) {
            foreach ($rows as $key => $row) {
                $pattern = '|]+>(.*)]+>|U';
                preg_match("{$pattern}", $row, $values);

                $pattern_posttime = '';
                if (preg_match("{$pattern_posttime}", $values[0])) {
                    $voice['created_at'] = $values[1];
                    $voice['type'] = 'mixi';
                }

                $pattern_body = '';
                if (preg_match("{$pattern_body}", $values[0])) {
                    $voice['text'] = $values[1];
                    $voices[] = $voice;
                    $voice = null;
                    $values = null;
                }
            }
        }
        return $voices;
    }

    /**
     * mixi共通リクエスト
     **/
    private function _mixi_request($url, $headers, $method, $posts=null) {
        if (is_array($posts)) {
            $content = http_build_query($posts, '', '&');
            $content_length = strlen($content);
            $headers[] = "Content-Length: {$content_length}";

            $params['http']['content'] = $content;
        }

        $params['http']['method'] = $method;
        $params['http']['header'] = implode("\r\n", $headers);

        $context = stream_context_create($params);
        $contents = file_get_contents($url, false, $context);

        //cookie設定
        $this->_set_cokkie_for_response_header($http_response_header);
        return $contents;
    }

    private function _set_cokkie_for_response_header($http_response_header) {
        if (is_array($http_response_header)) {
            foreach ($http_response_header as $r) {
                if (strpos($r, 'Set-Cookie') === false) {
                    continue;
                }
                $params = explode(' ', $r);
                $this->cookies[] = str_replace(';', '', $params[1]);
            }
        if (is_array($this->cookies)) {
               $this->cookie = implode('; ', $this->cookies);
            }
        }
    }

    /**
     * twitter共通リクエスト
     **/
    private function _twitter_request($url, $posts=null)
    {
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 2);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        if ($posts) {
            curl_setopt($ch, CURLOPT_POST, 1);
            curl_setopt($ch, CURLOPT_POSTFIELDS, $posts);
        }
        curl_setopt($ch, CURLOPT_USERPWD, "{$this->twitter_email}:{$this->twitter_password}");
        $results = curl_exec($ch);
        curl_close($ch);
        return $results;
    }

    // WSSE Authentication
    function _mixi_wsse_authentication_header() {
        $nonce       = pack('H*', sha1(md5(time().rand().posix_getpid())));
        $created     = date('Y-m-d\TH:i:s\Z');
        $digest      = base64_encode(pack('H*', sha1($nonce . $created . $this->mixi_password)));
        $wsse_format   = 'UsernameToken Username="%s", PasswordDigest="%s", Nonce="%s", Created="%s"';
        $wsse = sprintf($wsse_format, $this->mixi_email, $digest, base64_encode($nonce), $created);
        $header = "X-WSSE: {$wsse}";

        return $header;
    }       
}