昨日、bit.lyのAPIのふりをする短縮URLを作ってふと気付いたのだが、
あまりメジャーでないためクライアントが対応していない短縮URLもこの方法で使いやすくなるのではないかと考えた。
跳.jpは日本語ドメインを使った短縮URLで非常に短くなるのが特徴。
http://丁嗟.跳.jp のようになる。確かに短い
p.tlはPixivが提供している短縮URLサービスで、主にPixivの投稿画像についてTwitterにPostするときに使う。
けどそれ以外に使っていけない決まりはないし、ちゃんとAPIも提供されている。
http://p.tl/3WuT のようになる。利用者が少ないからか短い。なんか昨日作ったサービスが負けた気になる。
ただ、どちらも比較的新しいサービスなのでなかなか対応していない。
というわけでbit.lyのAPIのふりをするサーバーをlocalhostに作ろうというわけだ。
bit.lyとj.mpは同じAPIを使っているので、使い分けもできるかもしれない。
Rubyのsinatraを使って作ってみた。
まずgemでいろいろ入れた。
gem install sinatra gem install json gem install rubygems-update update_rubygems gem install activesupport
そして、sinatraでサーバーを作った。その前にIISを切っておく必要があったが。
# -*- coding: utf-8 -*-
require 'rubygems'
require 'sinatra/base'
require 'json'
require 'net/http'
require 'active_support/all'
Net::HTTP.version_1_2
class MyApp < Sinatra::Base
@@jmp=:tobi; @@bitly=:tobi; #:tobiまたは:ptl
@@ptlkey='*******';
def tobi(url)
Net::HTTP.start('xn--vt3a.jp',80) {|http|
response = http.get("/api?url=#{url}")
response.body.force_encoding('utf-8')
}
end
def ptl(url)
Net::HTTP.start('p.tl',80) {|http|
response = http.get("/api/api_simple.php?key=#{@@ptlkey}&url=#{url}")
res=JSON::parse(response.body)
res['status']=='ok' ? res['short_url'] : nil
}
end
def shorten(params)
shorten=request.host=='api.j.mp'?self.__send__(@@jmp,params[:longUrl]):self.__send__(@@bitly,params[:longUrl])
p shorten
if shorten
if params[:version]=='2.0.1'
res={'errorCode'=>0,'errorMessage'=>'','result'=>{
params[:longUrl]=>{'userHash'=>0,'hash'=>0,'shortUrl'=>shorten,'shortCNAMEUrl'=>shorten,
'shortKeywordUrl'=>''}},'statusCode'=>'OK'};
else
res={'status_code'=>200,'status_txt'=>'OK','data'=>{'hash'=>0,'global_hash'=>0,'url'=>shorten,
'long_url'=>params[:longUrl],'new_hash'=>0}};
end
if params[:format]=='xml'
if params[:login]=='twit' #twitはなぜかShift_JISで解釈する
res.to_xml(:root=>'response',:skip_types=>true,:dasherize=>false,:skip_instruct=>true).
gsub(/&#(?:(\d*?)|(?:[xX]([0-9a-fA-F]{4})));/){[$1.nil? ? $2.to_i(16) : $1.to_i].pack('U')}.
encode('shift_jis')
else
res.to_xml(:root=>'response',:skip_types=>true,:dasherize=>false).
gsub(/&#(?:(\d*?)|(?:[xX]([0-9a-fA-F]{4})));/){[$1.nil? ? $2.to_i(16) : $1.to_i].pack('U')}
end
else
res.to_json.gsub(/\\u(\h{4})/){[$1.to_i(16)].pack('U')}.gsub(/\\\//,'/').gsub('":','": ')
end
end
end
get '/shorten' do
shorten(params);
end
post '/shorten' do
shorten(params);
end
get '/v3/shorten' do
shorten(params);
end
post '/v3/shorten' do
shorten(params);
end
end
MyApp.run! :host => 'localhost', :port => 80
これでOKというのは不思議な感じだが、それがsinatraだ。
あとhostsにbit.lyとj.mpをlocalhostにしておくよう書いておかないと実験できんな。
127.0.0.1 api.bit.ly api.j.mp
下にget ‘/shorten’ など並んでいるが、これ1つ1つがアクセスを受けるブロックだ。
このブロックで受けてとやかく処理してレスポンスを返す。それだけで作れる。
ruby sinatobi.rb などすれば待ち受け状態になる。そしてCtrl+Cで止まる。
短縮の処理だが、api.j.mpにアクセスされた場合と api.bit.ly にアクセスされた場合で跳.jpとp.tlを使い分けられるようにした。
@@jmpと@@bitlyにそれぞれ跳.jpを使いたければ:tobi、p.tlを使いたい時は:ptlを入れておけばいい。
p.tlを使う時はあらかじめAPIキーを取得して@ptlkeyに代入しておく。(cf. p.tl Web API )
実際の短縮の処理はtobiメソッドかptlメソッドでやってる。どっちもHTTPでアクセスしているだけ。
p.tlの場合はJSONのパースを行っている。跳.jpの場合、マルチバイト文字列の問題があるのでUTF-8だとする処理を行っている。
あとはこの結果をbit.lyのAPIの出力にすればいい。JSONはHash#to_jsonがjsonに入ってたから問題なかったが、
to_xmlがなかなか見あたらなかった。Rails関係のactive_supportにあるのを使えば良かったようだ。
エスケープされた文字列を元に戻すとか処理をすればとりあえずOK。
Tweenではちょっと調整すればうまくいったのだが、Twitでは跳.jpがなぜか文字化けする。
XMLのエンコードは正しそうだけど、どうもTwitはそんなのおかまいなしでShift_JISとして解釈しているようだ。
まぁbit.lyはマルチバイト文字使わんけどさ。というわけでTwitの場合はbit.lyのIDにtwitと入れて使ってください。
本当はこれをサービス化したかったのだが、MongrelというHTTPサーバの実行環境が入らないので断念した。
標準ではWEBrickってのを使ってんだけど、Windowsではデーモン化できんらしい。UNIX系OSでは、
$ ruby sinatobi.rb -e production -d
とすればいいらしいんですけどね。どうもうまくいかんのでこれは後回し。
プロンプトが邪魔だけどスタートアップにでも登録すればいいんじゃないですかね。