Twitter のタイムラインを Evernote に送る


Twitter は使おうとするとなぜか気が滅入ってくる、なぜか分からないけどとても疲れる。

なので全然使っていないのですが、Evernote にちょっと保存しておきたいものがあったとき、
Twitter を間に入れるのが一番手っ取り早いような気がしていました。

Evernote に直接送れなくても、Twitter 連携しているサービスは多いですからね。
Shazam のタグとか。

で、Twitter と Evernote の連携ぐらい誰でも考えるだろうし、
さぞ色んなやりかたがあるだろうと思ったら、あんまりないみたいでした。
ツイエバ、っていうのがあるようだけれど、登録とかが必要で、
とても大変そうなので作った方が早そうでしたので作りました。

必要なものの準備
----------------

### Ruby

Ruby で書いたので、動作には Ruby が必要です。
今のところ最新の 2.1 で動かしていますが、1.9 までなら動くんじゃないかな・・・

それと、以下の gem を使っているので bundler などで準備しておいてください。

- twitter
- evernote_oauth

### Twitter API Key

Twitter へアプリケーションからアクセスするためのキーが必要です。
この辺りのやり方は変わると思いますが、現在のやり方だと、

1. [Twitter Developers](http://dev.twitter.com) に Twitter アカウントでログイン
2. アバターアイコンから "My Application" を選択
3. "Create New App" を選択
4. 情報を適切に(テキトーに)埋める

すると、新しいアプリケーションが登録されるので、

- API key
- API secret

を控えておいてください。

### Twitter Access Token 

上記で作成したアプリケーションから、"Create my Access Token" を選択すると、
以下が生成されるので、控えておきます。

- Access token
- Access token secret

これで、アプリケーションから Twitter に OAuth 認証する準備ができました。

### Evernote Developer Key

Evernote の API を利用する場合、個人的に使うだけであれば、Developer Key を利用すると簡単です。
Developer Token は Evernote にログインした状態で、以下にアクセスすると取得できます。  
[https://www.evernote.com/api/DeveloperToken.action](https://www.evernote.com/api/DeveloperToken.action)

これで、アプリケーションから Evernote に認証する準備ができました。

スクリプトの実行
----------------

### 仕様

ここで紹介するスクリプトは以下を実行します。

- 自分のツイートのタイムラインから、引数に指定したキーワードを含むツイートを抽出
- Evernote にキーワード名のノートブックを作成
- ノートブックに1ツイートを1ノートとして作成

### 実行方法

コードの以下の部分を、取得した認証キーに書き換えてください。

```ruby
YOUR_TWITTER_CONSUMER_KEY        = 'Enter your API key'
YOUR_TWITTER_CONSUMER_SECRET     = 'Enter yout API secret'
YOUR_TWITTER_ACCESS_TOKEN        = 'Enter your Access token'
YOUR_TWITTER_ACCESS_TOKEN_SECRET = 'Enter your Access token secret'
YOUR_EVENOTE_DEVELOPER_KEY       = 'Enter your Developer key'
```

### 実行

引数に抽出したいキーワードを指定して実行します。
例えば、「もういやだ」というキーワードで検索する場合は以下のようにします。

```text
$ ruby twieve.rb もういやだ
```

### 注意

- 実行環境が utf-8 以外の文字コードでの動作は未確認です。
- 複数回の抽出でツイートが重複しないよう、スクリプトと同フォルダに  
  `history.yaml` というファイルを作成し、取得済みのツイートのIDを記録しています。  
  再取得したい場合には、`history.yaml` の該当行か、ファイルごと削除してください。

スクリプト
----------

名前が思いっきり「ツイエバ」パクリですが気にしないでくださいね！！！  

```ruby
#!/usr/bin/env ruby
# coding: utf-8

require 'twitter'
require "evernote_oauth"
require 'yaml'
require 'cgi'

YOUR_TWITTER_CONSUMER_KEY        = 'Enter your API key'
YOUR_TWITTER_CONSUMER_SECRET     = 'Enter yout API secret'
YOUR_TWITTER_ACCESS_TOKEN        = 'Enter your Access token'
YOUR_TWITTER_ACCESS_TOKEN_SECRET = 'Enter your Access token secret'
YOUR_EVENOTE_DEVELOPER_KEY       = 'Enter your Developer key'

class Twieve

  attr_accessor :config
  attr_reader :twitter_user

  def initialize
    @config = {}
  end

  def authorize
    # Twitterクライアントを設定
    begin
      @twitter_client = Twitter::REST::Client.new do |config|
        config.consumer_key = @config[:twitter_consumer_key]
        config.consumer_secret = @config[:twitter_consumer_secret]
        config.access_token = @config[:twitter_access_token]
        config.access_token_secret = @config[:twitter_access_token_secret]
      end
      @twitter_user = @twitter_client.user
    rescue => e
      puts "ERROR: Cannot setup Twitter client. Please make sure you have entered valid access credentials. [#{e}]"
      return false
    end

    # Evernoteクライアントを設定
    begin
      # Developer Tokenをセット
      token = @config[:evernote_developer_token]
      client = EvernoteOAuth::Client.new(:token => token, :sandbox => false)
      @evernote_note_store = client.note_store
    rescue => e
      puts "ERROR: Cannot setup Evernote client. Please make sure you have entered valid access credentials. [#{e}]"
      return false
    end
  end

  def get_tweets_match_pattern(user_screen_name, pattern, since_id)
    # ユーザーのタイムラインからパターンにマッチするツイートのみ抽出
    begin
      tweets = @twitter_client.user_timeline(user_screen_name,
                                             :since_id=>since_id,
                                             :count=>300).select do |tweet|
      tweet.text =~ pattern
    end
    rescue => e
      puts "ERROR: Cannot fetch timeline data of #{user_screen_name}. [#{e}]"
      return false
    end
  end

  def get_notebook_guid(notebook_name)
    begin
      notebooks = @evernote_note_store.listNotebooks.select do |notebook|
        notebook.name == notebook_name
      end
    rescue => e
      puts "ERROR: Cannot fetch notebooks. [#{e}]"
      return false
    end
    # ノートブックが無かったら作る
    if notebooks.empty?
      begin
        notebook = Evernote::EDAM::Type::Notebook.new
        notebook.name = notebook_name
        @evernote_note_store.createNotebook(@config[:evernote_developer_token],
                                            notebook).guid
      rescue => e
        puts "ERROR: Cannot create notebook. [#{e}]"
        return false
      end
    else
      notebooks.first.guid
    end
  end

  def create_evernote_note(notebook_name, note_title, note_content)
    notebook_guid = get_notebook_guid(notebook_name)
    return false unless notebook_guid

    # ノートのタイトル
    note_title = note_title

    # ノートのENMLヘッダ
    enml_header = <<-HEADER
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE en-note SYSTEM "http://xml.evernote.com/pub/enml2.dtd">
    HEADER

    # ノートの本文
    note_content = <<-CONTENT
#{enml_header}
<en-note>#{CGI.escapeHTML(note_content)}</en-note>
    CONTENT

    note = Evernote::EDAM::Type::Note.new(:title => note_title,
                                          :notebookGuid => notebook_guid,
                                          :content => note_content)

    # ノートの作成
    begin
      @evernote_note_store.createNote(@config[:evernote_developer_token], note)
    rescue => e
      puts "ERROR: Cannot create a note. [#{e}]"
      return false
    end
  end
end

unless ARGV[0]
  puts 'ERRPOR: You must specify a search word.'
  exit 1
end

twieve = Twieve.new
twieve.config[:twitter_consumer_key]        = YOUR_TWITTER_CONSUMER_KEY
twieve.config[:twitter_consumer_secret]     = YOUR_TWITTER_CONSUMER_SECRET
twieve.config[:twitter_access_token]        = YOUR_TWITTER_ACCESS_TOKEN
twieve.config[:twitter_access_token_secret] = YOUR_TWITTER_ACCESS_TOKEN_SECRET
twieve.config[:evernote_developer_token]    = YOUR_EVENOTE_DEVELOPER_KEY
unless twieve.authorize
  puts 'ERROR: Failed to authorize.'
  exit 1
end

pattern = Regexp.new(ARGV[0].downcase, Regexp::IGNORECASE)

history_file = Pathname.new(File.expand_path(File.dirname($PROGRAM_NAME))) + 'history.yaml'
if File.exist?(history_file)
  yaml_data = YAML.load_file(history_file)
end

if yaml_data && yaml_data.has_key?(pattern.source)
  since_id = yaml_data[pattern.source]
else
  since_id = 1
  yaml_data = {}
end

tweets = twieve.get_tweets_match_pattern(twieve.twitter_user.id,
                                         pattern,
                                         since_id)

unless tweets
  puts 'ERROR: Failed to get Twitter timeline.'
  exit 1
end

unless tweets.empty?
  tweets.reverse_each do |tweet|
    #puts "DEBUG: #{tweet.text}... (ID: #{tweet.id})"
    unless twieve.create_evernote_note("#{twieve.twitter_user.name}'s Tweets (#{pattern.source})",
                                "#{tweet.created_at} (ID: #{tweet.id})",
                                tweet.full_text)
      puts 'ERROR: Failed to create Evernote note.'
      exit 1
    end
    puts "INFO: #{tweet.text[0, 10]}... (ID: #{tweet.id}) has been successfully created."
  end
  # 取得したIDの一番新しいものを保存
  yaml_data[pattern.source] = tweets.first.id
  open(history_file, "w") do |file|
    file.write(YAML.dump(yaml_data))
  end
end
```