大人になったら、もう勉強しなくてもいいと思ってた。
これが公開されるころは夏休みも佳境というところでしょうか(執筆時は 7 月後半)。あの 8 月末日、友人宅でひたすら宿題を「処理」していたときのひぐらしの鳴く声は未だに悪夢で見ます。こんにちは。バックエンドエンジニアのまさくにです。
さて大人になるとペンとノートに向かうこと自体は減りましたが、日常的に様々な記事を読んで頭に入れておく必要が出てきました。読まなきゃ追いついていけない。そこで皆様も後で読む記事をストックしておけるように Pocket を使用されているのではないでしょうか。
でも気がついたら Pocket にも記事がいっぱい……。そもそも これポイポイ気軽に Pocket に入れているけれど、よく考えてみるとそもそもそんな重要なものってあったっけ? 何か定期的に一定数 Slack に URL 投げてくれれば、毎日少しずつ読むのになー。
それ作りましょう。API で。
名付けて、リボルビング読み。
目次
本稿のために作成したコードは こちら !
開発環境
- Sierra: 10.12.5
- 今回の言語は ruby を使用
- iTerm2 で実行
$ ruby -v ruby 2.3.3p222 (2016-11-21 revision 56859) [x86_64-darwin15]
Pocketの管理画面でアプリケーションを登録する
Pocket の API を使用するものは Pocket で認められたアプリケーションである必要があります。そのため、Pocket の管理画面からアプリケーションの登録をしましょう。
ここ からアプリケーションの登録をします。
「 CREATE NEW APP 」でアプリケーションを作成します。
項目を埋めましょう。Permissions はこのアプリケーションに Pocket の何を操作する権限を与えるかという設定です。すべてチェックしておきます。自分だけが使う想定ですし。
アプリケーションが作成されました。
ここに記載されている Web の CONSUMER_KEY がアプリケーションの ID になります。控えておいてください。
あれ? 何か Platforms は Web だけを選んだのに、全部のアプリケーションが作成されてますね? すみません、よく分かりません。
アクセストークンを取得する流れ
Pocket も最近の Web サービスと同様に API の認可に OAuth 2.0 を使用しています。そのため、API を使用するのに取得しなければならないのはアクセストークンです。先ほどの CONSUMER_KEY を物々交換することによって取得しなければなりません。
今回は自分の Mac 上に Web サーバーを立てます。Pocket からアクセストークンを取得する際は、下記のような流れになります。
アクセストークンを取得するコードを書く
今回は自分だけが使用するので、Twitter のようにどこかでアクセストークンくれないかなと思っていたのですが、見当たりませんでした。自分のアクセストークンも自分で GET しなければならないのですね。仕方ない。
再開発気味ですが、ローカルで Web サーバー( Webrick )を立てて、Pocket から正式にアクセストークンを取得したいと思います。
書いたのは下記のスクリプト。
▼server.rb:ビルトインサーバーの起動
require 'webrick' system("export CONSUMER_KEY=#{ENV['CONSUMER_KEY']}") srv = WEBrick::HTTPServer.new({ :DocumentRoot => './', :BindAddress => '127.0.0.1', :Port => 8000, :CGIInterpreter => `which ruby`.strip!, :Logger => WEBrick::Log::new(STDOUT, WEBrick::Log::DEBUG), }) srv.mount('/', WEBrick::HTTPServlet::CGIHandler, 'request.rb') srv.mount('/authorize', WEBrick::HTTPServlet::CGIHandler, 'authorize.rb') trap("INT"){ srv.shutdown } srv.start
▼request.rb:リクエストトークンの取得とリダイレクト
require 'json' require 'net/https' require 'cgi' require 'cgi/session' cgi = CGI.new consumer_key = nil if cgi.params.has_key?('consumer_key') consumer_key = cgi.params['consumer_key'].first.to_s end # get request_token headers = { 'Content-Type' =>'application/json; charset=UTF-8', 'X-Accept' => 'application/json' } params = {:consumer_key => consumer_key, :redirect_uri => 'http://localhost:8000/authorize'} data = params.map { |k, v| [k, v.to_s.encode('utf-8')] }.to_h http = Net::HTTP.new('getpocket.com', 443) http.use_ssl = true req = Net::HTTP::Post.new('/v3/oauth/request', initheader = headers) req.set_form_data(data) res = http.request(req) raise "error: cannot get response." unless res.is_a?(Net::HTTPOK) # get code res_json = JSON.parse(res.body) # save code in session session = CGI::Session.new(cgi) session['consumer_key'] = consumer_key session['code'] = res_json['code'] # redirect to pocket authorization link = URI.escape("https://getpocket.com/auth/authorize?request_token=#{res_json['code']}&redirect_uri=http://localhost:8000/authorize") print cgi.header({ "status" => "REDIRECT", "Location" => link })
▼authorize.rb:アクセストークンの表示
require 'json' require 'net/https' require 'cgi' require 'cgi/session' cgi = CGI.new session = CGI::Session.new(cgi) headers = { 'Content-Type' =>'application/json; charset=UTF-8', 'X-Accept' => 'application/json' } params = {:consumer_key => session['consumer_key'], :code => session['code']} data = params.map { |k, v| [k, v.to_s.encode('utf-8')] }.to_h http = Net::HTTP.new('getpocket.com', 443) http.use_ssl = true req = Net::HTTP::Post.new('/v3/oauth/authorize', initheader = headers) req.set_form_data(data) res = http.request(req) raise "error: cannot get response." unless res.is_a?(Net::HTTPOK) # show acces_code res_json = JSON.parse(res.body) cgi.out(:type => 'text/plain', :charset => 'UTF-8') { "access_token: " << res_json['access_token'] }
アクセストークンを取得する
下記で Web サーバーが起動します。
$ ruby server.rb [2017-07-26 18:14:28] INFO WEBrick 1.3.1 [2017-07-26 18:14:28] INFO ruby 2.3.3 (2016-11-21) [x86_64-darwin15] [2017-07-26 18:14:28] DEBUG WEBrick::HTTPServlet::FileHandler is mounted on /. [2017-07-26 18:14:28] DEBUG WEBrick::HTTPServlet::CGIHandler is mounted on /. [2017-07-26 18:14:28] DEBUG WEBrick::HTTPServlet::CGIHandler is mounted on /authorize. [2017-07-26 18:14:28] INFO WEBrick::HTTPServer#start: pid=21846 port=8000
Web サーバーが起動したら、下記 URL の YOUR_CONSUMER_KEY の部分を、先ほど取得した CONSUMER_KEY に書き換えて、ブラウザで開いてください。
http://localhost:8000/?consumer_key=YOUR_CONSUMER_KEY
上記のように Pocket へログインしていれば、認可画面が表示されるかと思います。アイコンは設定しないと表示されないのかな……?
「認可」をクリックすると localhost に戻り、アクセストークンが表示されればひとまず成功です。表示されているアクセストークンは後で使用するので、これも控えてください。Web サーバが動いていると思うので、Ctrl + C で終了してください。
毎回ここまでが長いんだよな……。
Pocket内の記事を取得する
先ほど取得した CONSUMER_KEY とアクセストークンを使用すると、自分が Pocket してある記事を取得できます。
▼get_articles.rb
require 'json' require 'net/https' CONSUMER_KEY=ENV['POCKET_CONSUMER_KEY'] POCKET_ACCESS_TOKEN=ENV['POCKET_ACCESS_TOKEN'] headers = { 'Content-Type' =>'application/json; charset=UTF-8', 'X-Accept' => 'application/json' } params = {:consumer_key => CONSUMER_KEY, :access_token => POCKET_ACCESS_TOKEN, :sort => "newest", :count => 2} data = params.map { |k, v| [k, v.to_s.encode('utf-8')] }.to_h http = Net::HTTP.new('getpocket.com', 443) http.use_ssl = true req = Net::HTTP::Post.new('/v3/get', initheader = headers) req.set_form_data(data) res = http.request(req) raise "error: cannot get response." unless res.is_a?(Net::HTTPOK) res_json = JSON.parse(res.body) puts JSON.pretty_generate(res_json)
上記のスクリプトをコマンドラインで実行してみましょう。YOUR_CONSUMER_KEY に加え、YOUR_ACCESS_TOKEN は先ほどブラウザ上で取得したアクセストークンに書き換えてください。
$ POCKET_ACCESS_TOKEN='YOUR_ACCESS_TOKEN' POCKET_CUSTOMER_KEY='YOUR_CONSUMER_KEY' ruby get_articles.rb { "status": 1, "complete": 1, "list": { "1825534674": { "item_id": "1825534674", "resolved_id": "1825534674", "given_url": "https://liginc.co.jp/362231", "given_title": "", "favorite": "0", "status": "0", "time_added": "1501046770", "time_updated": "1501046770", "time_read": "0", "time_favorited": "0", "sort_id": 0, "resolved_title": "それはぜんぶ夏のせい。怖かった体験を英語で話す #TOEIC350の英語力 #英会話 | スタートアップイングリッシュ", "resolved_url": "https://liginc.co.jp/362231", "excerpt": "いや……この暑さで英語とか……。2017、夏、暑すぎません? これ南国のフィリピンより暑いと思うから、先生たち絶対 “Japanese summer crazy” とか思うだろ。それから、まだ固着してない僕の英語脳が溶ける。阿呆の融点", "is_article": "1", "is_index": "0", "has_video": "0", "has_image": "1", "word_count": "527" }, ・ ・ (中略) ・ ・ }, "error": null, "search_meta": { "search_type": "normal" }, "since": 1501051487 }
取得した記事をSlackに投げる
これまでで取得できた記事を 2 つ、Slack へ投げるように機能追加します。
また、Slack に投げたものは自動的に Archive 化します。Slack への投稿は Gem を使わせてください。あと、Slack の使い方は割愛させていただきます。
▼Gemfile
source 'https://rubygems.org' gem 'slack-api'
▼revolve_articles.rb
require 'json' require 'net/https' require 'slack' CONSUMER_KEY=ENV['POCKET_CONSUMER_KEY'] POCKET_ACCESS_TOKEN=ENV['POCKET_ACCESS_TOKEN'] Slack.configure do |config| config.token = ENV['SLACK_API_TOKEN'] end def request(host, path, params = {}, headers = {}) data = params.map { |k, v| [k, v.to_s.encode('utf-8')] }.to_h http = Net::HTTP.new(host, 443) http.use_ssl = true req = Net::HTTP::Post.new(path, initheader = headers) req.set_form_data(data) res = http.request(req) end headers = { 'Content-Type' =>'application/json; charset=UTF-8', 'X-Accept' => 'application/json' } params = {:consumer_key => CONSUMER_KEY, :access_token => POCKET_ACCESS_TOKEN, :sort => "newest", :count => 2} res = request('getpocket.com', '/v3/get', params, headers) raise "error: cannot get response." unless res.is_a?(Net::HTTPOK) res_json = JSON.parse(res.body) unless res_json.has_key?('list') exit end res_json['list'].each do |id, article| # post to slack text = "Don't forget this biscuit in Pocket\n" << article['given_url'] Slack.chat_postMessage(text: text, channel: '#news', as_user: true) # archive actions = JSON.generate([{ "action" => "archive", "item_id" => id.to_s }]) params = {:consumer_key => CONSUMER_KEY, :access_token => POCKET_ACCESS_TOKEN, :actions => actions.to_s} res = request('getpocket.com', '/v3/send', params, headers) sleep(1) # pray end
実行
それでは実行します。実行方法がとても頭悪い感じになっているので、本当に使おうと思ったら 適宜環境変数の管理をお願いします。
$ bundle install --path ./vendor # install $ POCKET_ACCESS_TOKEN='YOUR_ACCESS_TOKEN' POCKET_CONSUMER_KEY='YOUR_CONSUMER_KEY' SLACK_API_TOKEN='YOUR_SLACK_API_KEY' bundle exec ruby revolve_articles.rb
▼Slackの指定されたチャンネル
できたーーー!
実用的にはこれを cron などで回して、自分の隙間時間を見計らって日々通知してしまいましょう。毎日少しずつ読めば、無理なく「後で読む」を消化できるはずです。夏休みの宿題と同じですね。また Slack の有料会員になると、全ログ検索できますし、幸せかもしれません。
まとめ
ここまで書いてアレですけど、似たようなことなら PocketRocket でもできるようです。あと IFTTT 使っても同じようなことはできそうですよね。何となく初めから分かってはいました。
それでは車輪の再開発はやめて、良い API ライフを!
LIGはWebサイト制作を支援しています。ご興味のある方は事業ぺージをぜひご覧ください。