Sinatra で Google Reader の共有アイテムをつぶやくアプリを作ってみた
Google Reader の共有アイテムは PubSubHubbub というプロトコルに対応しており、リアルタイムで更新の通知を受け取る事ができます。
これを利用したアプリに、Reader2Twitter があります。
Google Reader の共有アイテムを追加すると、その内容を即座に Twitter へつぶやいてくれます。
ただ、Reader2Twitter は結構な頻度で止まってしまいます。
そこで、Sinatra で同じようなアプリを作ってみました。
PubSubHubbub の Subscriber の構築
PubSubHubbub は、
- Publisher (コンテンツの発行者)
- Subscriber (コンテンツの購読者)
- Hub (両者の仲介役)
で構成されます。
Publisher は、Hub を介して Subscriber に新着フィードをプッシュします。
ここでは、3 者のうちの Subscriber を作ってみます。
(残り 2 つは Google から提供されているので、それを使います。)
PubSubHubbub の詳細についてはググッていただくとして、簡単に言うと、
Subscriber は、
- 対応する Publisher (フィード) の URL を
- Hub に対して購読要求し
- 認証手続きが済むと
- Hub から新着フィードの publish を受けることができる
というものです。
この 1 - 4 の流れを追って書いていきたいと思います。
1. Publisher (フィード) の URL
少しわかりにくいですが、URL は以下のように調べられます。
リーダー設定 -> フォルダとタグ -> 共有設定
と辿ります。
カスタム URL の下側のアドレスにアクセスします。
この "Atom フィード" の URL です。
2. 購読要求
Google の提供している Hub には、購読要求のための Web インタフェースがあるので、これを使います。
Hub - Subscription debug
Callback
にアプリを動かす URLTopic
に 1 で調べた URLVerify token
にパスワードHMAC secret
に新着フィード署名用のキー
を入力します。
(Callback の URL に .rb が付いているのは、Ruby CGI で作っていた頃の名残です。)
3. 認証手続き
2 で "Do it" ボタンを押すと、"Callback" で指定した URL に、HTTP GET で認証手続きが飛んできます。
手続きに必要なパラメータは、URL のクエリストリングに格納されてくるので、適切に判断し、レスポンスを返します。
# 購読要求時の入力情報
greaderid = '01234567890123456789'
verify_token = 'homuhomu'
get '/sub.rb' do
# 各パラメータの取得
p_mode = params['hub.mode']
p_topic = params['hub.topic']
p_challenge = params['hub.challenge']
p_verify_token = params['hub.verify_token']
if p_mode == 'subscribe' && p_topic.include?(greaderid) && p_verify_token == verify_token then
# レスポンスボディにチャレンジを格納
p_challenge
else
# サーバエラーを返す
status 500
end
end
上記が、認証手続きに対応するためのコードです。
"greaderid" には、(1) で調べた URL の番号部分を指定してください。
"Topic" で指定した URL、"Verify token" で指定したパスワードが共に正しければ、クエリストリングの hub.challenge をボディに格納してレスポンスを返します。
この認証手続きは、定期的に行われます。
4. 新着フィードの受信
新着フィードは、HTTP POST されます。
ボディにフィードの xml が格納されているので、これを解析して使います。
# 購読要求時の入力情報
secret = 'foofoo'
post '/sub.rb' do
# リクエストボディの取得
payload = request.body.read
# X_Hub_Signature ヘッダの値を取得
hub_sig = request.env['HTTP_X_HUB_SIGNATURE']
# HMAC-SHA1 の計算
my_sig = OpenSSL::HMAC::hexdigest(OpenSSL::Digest::SHA1.new, secret, payload)
if hub_sig.include?(my_sig) then
# xml の解析
doc = REXML::Document.new(payload.gsub('<br>', '<br />'))
title = doc.elements['/feed/entry/title'].text
link = doc.elements['/feed/entry/link[@rel="alternate"]'].attributes['href']
# コメントはない場合もあるので分けて処理
comment_ele = doc.elements['/feed/entry/gr:annotation/content']
comment = comment_ele ? comment_ele.text : ''
# つぶやく
t = Tweet.new
begin
t.tweet(title, link, comment)
rescue
true
end
end
# 成功を返す
status 200
end
上記がフィードを処理する部分です。
購読要求時、"HMAC secret" に入力した値をキーにして、X-Hub-Signature ヘッダに、ペイロードの HMAC-SHA1 ハッシュが付加されます。
ヘッダのハッシュと、自分で計算したハッシュが一致する場合、続く処理を行います。
ハッシュが違っていても、status 200 を返す必要があるので、注意。
さいごに
subscriber の実装についての記事は、調べてみるとちょくちょく見つかります。
でも、publish 時の署名についてまで言及しているものは少なかったので、書いてみました次第です。
Reader2Twitter さん今までお世話になりました。
感謝の気持ちを込めて、Donate しておきました。:D