Pinboard にブックマークしたらはてなブックマークに同期するやつを作った。
なんで Pinboard を使うのか、どうしてはてブに一本化しないのかというと、洋物のサービスを使うときに Pinboard の方が使いやすいから。 IFTTT に Pinboard 連携機能はあってもはてブ連携機能はないし、 Delibar や Reeder や ReadKit も Pinboard には対応しているけどはてブには対応してない。あと Pinboard は広告出ないしホッテントリ的なものもないので気がついたら Amazon で買い物してたとかホッテントリの海に溺れてた、ということも起こらない。とはいえはてブのコメントでキャッキャッウフフはしたい。なのでブックマークするときは両方にしたい。パソコンの Chrome からブックマークするときは Taberareloo でクロスポストできるのだけど、最近 iPhone からブックマークすることが増えて(iPhone 進化し過ぎて仕事するときしかパホコン使わなくなった) Pinboard とはてブにそれぞれブックマークするのがだるかった。そういうわけで Pinboard をメインにしつつはてブに同期を試みた。
はてなスタッフの aereal さんが作ってるはてブ API 用の gem (aereal/hatena-bookmark-restful: A client library for Hatena::Bookmark RESTful API)あって使わせてもらったのだけど、これはそのままだと使えない。タグが複数あるケースに対応してない& User-Agent を送らないので API 側から 401 Unauthorized
が返ってくる。なので雑にモンキーパッチした。
はてなブックマークの API は tag を 10 個まで設定できるけど、以下のように Request Body が Encode されるのを期待しているっぽい。
"comment=&tags=ruby&tags=http&url=https%3A%2F%2Fgithub.com%2Flostisland%2Ffaraday"
この様な Request パラメーターを Ruby で表現すると以下のような Hash になると思う。
params = {
comment: '',
tags: ['ruby', 'http'],
url: 'https://github.com/lostisland/faraday'
}
Hash は当然のことながら同じキーを複数持つことはできないから、 tags は配列として表現される。これをこのまま Faraday (Ruby の HTTP クライアント。上述の gem でも使われてる)に渡して Request Body を生成するとエラーになってしまうのだった。
最近の Faraday には encode option が追加されて FlatParamsEncoder というのを選べるようになってた。こいつを使うと配列を value に持つ Hash をシリアライズしたときに tags=foo&tags=bar
みたいな形式にしてくれる。加えて User-Agent も載せるようにもした。こんな感じ。
class Hatena::Bookmark::Restful::V1
def create_bookmark(bookmark_params)
res = connection.post("/#{api_version}/my/bookmark") {|req|
req.params = bookmark_params
}
attrs = JSON.parse(res.body)
bookmark = Bookmark.new_from_response(attrs)
end
private
def connection
@connection ||= Faraday.new(url: 'http://api.b.hatena.ne.jp/') do |conn|
conn.request :url_encoded
conn.options.params_encoder = Faraday::FlatParamsEncoder
conn.request :oauth, {
consumer_key: @credentials.consumer_key,
consumer_secret: @credentials.consumer_secret,
token: @credentials.access_token,
token_secret: @credentials.access_token_secret
}
conn.headers['User-Agent'] = 'Hatena::Bookmark::Restful Client'
conn.adapter Faraday.default_adapter
end
end
end
なおはてブの API はリクエストパラメーターが不正なとき 400 Bad Request
を返すのではなく 401 Unauthorized
を返す。のみならず User-Agent なしのリクエストに対しても 401 を返す。一方で OAuth ヘッダーが不正なときは 400 を返す。原因の切り分けがむずかしくなるので、リクエストパラメータが不正なときは 400 を返して欲しいし認証できないときは 401 を返して欲しい。
追記 2017-05-22 18:34:38
作者の aereal さんに気づいてもらってパッチを取り込んでもらったんだけど、なんかやっぱりタグありの記事をブックマークしようとするとエラーになるっぽい。相変わらず 401 Unauthorized が返ってくる…。