Evernote APIを使ってアプリケーションを作る例 (Ruby)

14th Jan, 2010 | evernote ruby

すっかりEvernoteブログになってるなー。

最初のエントリでも書いたように、このブログはEvernoteで書いてる(この文章自体も)。

APIキーの取得については以前書いたのでそちらを参照ください。

Evernoteからブログへのデータ取り込み部分のソースを出しておく。クラス構造とか設定ファイルとかは端折って、実際に動くところのみ。Evernote公式のrubyライブラリはいまいち使いにくい。もうちょっとrubyっぽいサードパーティのwrapperとかあってもよさそうだけど、今のところなさそうだし作るのもだるい。

処理の流れとしては、

  1. 自分のアカウントで認証
  2. "blog"というタグが含まれている全ノートを抽出
  3. ノートを一つずつ舐めて、自分のWebアプリにデータ取り込み

認証

とりあえず、関連するライブラリをrequireしてから認証処理。この辺はサンプルのまま。

require "thrift"
require "Evernote/EDAM/user_store"
require "Evernote/EDAM/user_store_constants.rb"
require "Evernote/EDAM/note_store"
require "Evernote/EDAM/limits_constants.rb"

userStoreTransport = Thrift::HTTPClientTransport.new("https://www.evernote.com/edam/user")
userStoreProtocol = Thrift::BinaryProtocol.new(userStoreTransport)
userStore = Evernote::EDAM::UserStore::UserStore::Client.new(userStoreProtocol)

authResult = userStore.authenticate("YOUR_USER_NAME", "YOUR_PASSWORD",
                                    "CONSUMER_KEY", "CONUMER_SECRET")
evernote_user = authResult.user
authToken = authResult.authenticationToken

ここで作ったauthTokenを持ち回してAPIで使う。

ノートにアクセスするための準備

noteStoreUrl = "http://www.evernote.com/edam/note/" + evernote_user.shardId
noteStoreTransport = Thrift::HTTPClientTransport.new(noteStoreUrl)
noteStoreProtocol = Thrift::BinaryProtocol.new(noteStoreTransport)
noteStore = Evernote::EDAM::NoteStore::NoteStore::Client.new(noteStoreProtocol)

ここで作ったnoteStoreでノートにアクセスする。

ノートを検索

filter_tag = "blog"
filter = Evernote::EDAM::NoteStore::NoteFilter.new
filter.words = "tag:#{filter_tag}"
res = noteStore.findNotes(authToken, filter, 0, 100)

このfilter.wordsにはEvernoteクライアントでも使える検索式が書ける。たとえば、tag:で始まるのはタグだし、単に文字列を入れるとその文字列で検索される。

後は、上の検索で引っかかったノートの情報をmongoDBにコピーする処理。同時に、添付されている画像ファイルもローカルに落としてる。mongoDBへのアクセスはいまのところMongoMapperを使っている。見てわかるようにActiveRecordとほぼ同じインターフェース

XSLTを使ってるのは、Evernoteのノートは独自のXMLで保管されているので、それをhtml変換するため。XSLTの中身は以前紹介したやつ。

note2blog = Nokogiri::XSLT(File.read('xslt/note2blog.xslt'))
res.notes.each do |note|
  entry = Entry.find_or_initialize_by_evernote_key(note.guid)
  next if entry.updated_timestamp.to_i == Time.at(note.updated / 1000).to_i
  if note.resources
    note.resources.each do |resource|
      data = noteStore.getResource(authToken, resource.guid, true, true, true, true)
      hex = data.data.bodyHash.unpack('H*').first
      ext = case data.mime
            when 'image/png'
              'png'
            when 'image/jpeg'
              'jpg'
            else
              raise "Unknown mime type: #{data.mime}"
            end
      File.open("publichttp://blog.madoro.org/mn/images/#{hex}.#{ext}", 'w') {|f| f.write(data.data.body) }
    end
  end

  entry.user = user
  entry.title = note.title

  tags = noteStore.getNoteTagNames(authToken, note.guid)
  tags.delete_if {|a| a == filter_tag}

  entry.tags = tags
  content = noteStore.getNoteContent(authToken, note.guid)
  entry.text = note2blog.transform(Nokogiri::XML(content)).to_s
 }

  entry.updated_timestamp = Time.at(note.updated / 1000)
  entry.published_timestamp ||= Time.at(note.updated / 1000)
  entry.status = Entry::Published
  entry.save!
end


記事の内容についての質問、苦情、間違いの指摘等なんでもtwitterでどうぞ。