はてなブックマークをTwitterに投稿するPHPスクリプト hatebu2tweet.php(OAuth, Bit.ly対応)

 TwitterBASIC認証が6月末で廃止されるので、はてなブックマークをWebHook経由でTwitterに投稿するPHPスクリプトをOAuth対応に書き直してみました。Bit.lyの短縮URLにも対応していますので、bit.lyのアクセス解析も利用できます。書き直すにあたって以下のサイトを参考にしました。

PHP+OAuthでTwitter - SDN Project
PHPからbit.lyやtr.im等のURL短縮サービスをまとめて扱える「PEAR::Services_ShortURL」:phpspot開発日誌

 OAuthにtwitteroauth, Bit.lyの短縮URLPEAR::Services_ShortURLを利用したのでコーディングしたのははてブWebHookからのPOSTメッセージを受け取って140文字以内に収めてtwitteroauthに渡すまで。BASIC認証版のコードと比べても大きな変更も無く移行できました。

スクリプトの動作

 はてなブックマークで新規に追加されるブックマークに対して「タグなしコメント /// タイトル bit.ly短縮URL」の形式でTwitterに投稿します。140文字を超える場合は「タグなしコメント /// タイトル」の部分を後ろから削って、短縮URL込みで140文字に収まるように調整します。並び順や区切り文字はcreateMessage関数で変更できます。

        $msg = "$comment_notag /// $title";

        $urllength = mb_strlen($shorturl) + 1;
        $msglength = 140 - $urllength;

        if (mb_strlen($msg) > $msglength) {
                $msg = mb_substr($msg, 0, $msglength, "UTF-8");
        }

        $msg = $msg . " " . $shorturl;

ログ

 はてブを付けてWebHookからスクリプトが呼び出されると以下のようなログを出力します。

2010/05/05 00:44:10 bookmark test 3 /// Google http://bit.ly/3BxKtp

 はてブをつけてもログに日付が出力されない場合はWebHookからの呼び出しが無かったことを示しています。たまにWebHookから呼び出されないことがあるような気がします。

 ブックマークの更新や削除の場合は以下のようなログを出力します。add以外のステータスはすべてログを出力して終了していますが、スクリプトを書き換えればはてなスターなどを処理することも可能です。

2010/05/05 00:40:33 unknown status : update
2010/05/05 00:43:09 unknown status : delete

利用方法

ちょっと手続きが多めですが、大まかな流れはこんな感じです。

  • hatebu2tweet.phpの設置
  • Twitter OAuthクライアント登録 > OAuthキー取得
  • PEAR::Services_ShortURLのインストール > Bit.lyキー取得
  • はてブ WebHookの設定 > WebHookキー取得
  • keys.phpにキーを設定


以下手続きの詳細。

  • hatebu2tweet.phpの設置
    • PHPを利用できるWebサーバに hatebu2tweet.php, keys.phpを設置
    • ログファイルを作成
      • touch hatebu2tweet.log
      • chmod 666 hatebu2tweet.log
  • http://dev.twitter.com/apps/>新しいアプリケーションを登録する からOAuthクライアントを設定
    • (例)
    • アプリケーション名: hatebu2tweet (Twitterクライアント名。任意の名前にできます)
    • アプリケーションの説明: hatebu2tweet
    • アプリケーションのウェブサイトURL: http://twitter.com/mikage_sbmTwitterクライアントのリンク先。任意のURLにできます)
    • 所属会社/団体: mikage014
    • アプリケーションの種類: クライアントアプリケーション
    • Default Access type: Read & Write (投稿するのでWriteが必要です)
  • OAuthキーの取得
    • http://dev.twitter.com/apps>登録したアプリケーションのアイコンをクリック>consumer_key, consumer_secret をkeys.phpに記述(クライアント識別用のキーです)
    • そのページの右側メニューからMy Access Tokenをクリック>access_token, access_token_secret をkeys.phpに記述(このクライアントを利用するユーザー、この場合は自分用のアクセストークンです)
  • GitHub - abraham/twitteroauth: The most popular PHP library for use with the Twitter OAuth REST API.>download sourceからtwitteroauthをダウンロードして「twitteroauth」フォルダ内の「OAuth.php」と「twitteroauth.php」を「hatebu2tweet.php」と同じディレクトリに配置
  • Services_ShortURLをインストール
  • http://bit.ly/pages/tools/developer-tools/でBit.ly APIキーを取得
    • keys.php>bitly_login にログイン名、keys.php>bitly_apikey にAPIキーを記述
  • はてなブックマーク>設定>外部サイト連携>WebHook
    • イベントを受け取るURL → hatebu2tweet.phpのURL
    • キー → 自動生成してkeys.php>hatena_webhook_key にその値を記述
    • 「ブックマークの追加/更新/削除」にチェックを入れる(その他の項目はチェックをつけても無視します)

ソースコード

hatebu2tweet.php

<?php
// before using
// touch hatebu2tweet.log
// chmod 666 hatebu2tweet.log

// twitteroauth
require_once("twitteroauth.php");
// OAuth Keys, Bit.ly Keys, Hatena WebHook Keys
require_once("keys.php");
// Service_ShortURL
require('Services/ShortURL.php');

class hatebu2tweet {
  var $oauth;
  var $bitly;
  var $hatena_webhook_key;

  // ------------------------------------------------------------
  // constructor
  // ------------------------------------------------------------
  function __construct ($keys) {
        // OAuth Object
        $this->oauth = new TwitterOAuth(
                $keys['consumer_key'],
                $keys['consumer_secret'],
                $keys['access_token'],
                $keys['access_token_secret']
        );

        // Bitly Object
        Services_ShortURL::setServiceOptions('Bitly', array(
            'login'  => $keys['bitly_login'],
            'apiKey' => $keys['bitly_apikey']
        ));
        $this->bitly = Services_ShortURL::factory('Bitly');

        // Hatebu WebHook Key
        $this->hatena_webhook_key = $keys['hatena_webhook_key'];
  }

  // ------------------------------------------------------------
  // Check WebHook Key from Hatena Bookmark
  // ------------------------------------------------------------
  function checkWebHookKeyAndStatus($key, $status) {
        if ($key != $this->hatena_webhook_key) {
          $this->writelogln("unknown key : $key");
          return false;
        }
        if ($status != "add") {
          $this->writelogln("unknown status : $status");
          return false;
        }
        return true;
  }

  // ------------------------------------------------------------
  // Create POST Message ( msg < 140 and shorten URL )
  // ------------------------------------------------------------
  function createMessage($comment, $title, $url) {
        $comment_notag = preg_replace("/^(\[.*?\])*/", "", $comment);
        $shorturl = $this->bitly->shorten(urlencode($url));
        $msg = "$comment_notag /// $title";

        $urllength = mb_strlen($shorturl) + 1;
        $msglength = 140 - $urllength;

        if (mb_strlen($msg) > $msglength) {
                $msg = mb_substr($msg, 0, $msglength, "UTF-8");
        }

        $msg = $msg . " " . $shorturl;
        $this->writelogln($msg);

        return $msg;
  }

  // ------------------------------------------------------------
  // send msg to twitter with OAuth
  // ------------------------------------------------------------
  function tweet($msg) {
        $req = $this->oauth->OAuthRequest("https://twitter.com/statuses/update.xml","POST",array("status"=>$msg));
        header("Content-Type: application/xml");
        echo $req;
  }

  // ------------------------------------------------------------
  // LOG
  // ------------------------------------------------------------
  function writelog($log) {
        $fp = fopen("./hatebu2tweet.log","a");
        if (flock($fp, LOCK_EX)) {
          fputs($fp, $log);
          flock($fp, LOCK_UN);
        }
        fclose($fp);
  }
  function writelogln($log) {
        $this->writelog($log . "\n");
  }

}


// ------------------------------------------------------------
// Main section
// ------------------------------------------------------------
// Hatena WebHook Parameters
$is_private = $_POST["is_private"];
$count      = $_POST["count"];
$status     = $_POST["status"];
$key        = $_POST["key"];
$username   = $_POST["username"];
$permalink  = $_POST["permalink"];
$timestamp  = $_POST["timestamp"];
$comment    = $_POST["comment"];
$url        = $_POST["url"];
$title      = $_POST["title"];
# only hatena star
$color      = $_POST["color"];
$quote      = $_POST["quote"];


$hatebu2tweet = new hatebu2tweet($keys);
$hatebu2tweet->writelog(date("Y/m/d H:i:s "));

if ($hatebu2tweet->checkWebHookKeyAndStatus($key, $status) == false) {
  exit;
}

$msg = $hatebu2tweet->createMessage($comment, $title, $url);
$hatebu2tweet->tweet($msg);

?>

keys.php

<?php
// ------------------------------------------------------------------
// Twitter OAuth Keys
// ------------------------------------------------------------------
// Consumer key
$keys['consumer_key'] = "";
// Consumer secret
$keys['consumer_secret'] = "";
// Access Token
$keys['access_token'] = "";
// Access Token Secret
$keys['access_token_secret'] = "";

// ------------------------------------------------------------------
// Bit.ly Keys
// ------------------------------------------------------------------
// Login Name
$keys['bitly_login'] = "";
// API Key
$keys['bitly_apikey'] = "";

// ------------------------------------------------------------------
// Hatena WebHook Keys
// ------------------------------------------------------------------
$keys['hatena_webhook_key'] = "";
?>