Ruby素人でもできた！Herokuで動かすTwitter Bot！

# 背景
HerokuでBot作りたかったので，下記サイトの記述通りやった．
一昨日はじめてRuby触って文法すらわからなかったけど，コピペでなんとかなるだろと思っていた．

- [Herokuを使って1日1回名言をツイートするTwitter Botの作り方 - アインシュタインの電話番号](http://blog.ruedap.com/2011/02/09/ruby-heroku-twitter-bot)

記述が丁寧で大変参考になる記事なのだが，執筆時が2011年と古く，ソフトウェアの仕様変更のために結局コピペだけでは動かなかった．Ruby素人（筆者）が動くようにするまでの試行錯誤をまとめる．

# ソースコード
[Gist](https://gist.github.com/motoso/dd2a5f2797db5a6c5a83)に，本記事で指摘する反映点を全て含んだコードをおいておきました．
なお，組んだBotは[こいつ](https://twitter.com/motosonobot)．実装時点では，1日1回朝9時につぶやく．事前にハードコードした31日分の文を繰り返しつぶやく．

## 注意
参考記事では，まず手動でBotの動作を確認してから自動化していますが，Gistにあげたソースコードは最初から自動でツイートすることが前提の設定になっているので注意．


# 本記事の使い方
この資料はあくまで上記事実装を2014年11月時点で動かすことを目的とした補足です．詳細な手順は[上述のサイト](http://blog.ruedap.com/2011/02/09/ruby-heroku-twitter-bot)を参照してください．
先に本記事の目次に目を通しておき，元記事の通りに進め，本記事で指摘された項目が出てきた時点で本記事を読むという使い方を推奨します．

## 補足：本ソースコードを利用した際の手順の概要（自動ツイートするBotの作成）

1. Twitter側の設定
    1. Twitterアカウントを作成する
    1. Twitterのアプリケーション登録を行い，Consumer keyとConsumer secretをメモする．
    3. Twitter GemでOAuthに必要なトークンを取得する．
2. 環境設定
    3. Ruby環境を整える（Gemのインストールなど）．
    3. Heroku Toolbertのインストール．
    2. 作業ディレクトリを作って`$ git init`する．
3. コーディング
  1. 上記の全てのソースコードを入れる．
  3. "tweet.rb"のトークン部分を自分のトークンに差し替え，つぶやかせたい文を31行分書く．また"Gemfile"のRubyのバージョンを自分の利用しているバージョンに置き換える．
  4. `$ bundle install`を実行して依存関係のあるパッケージを導入．パッケージの情報が記録されたファイル（Gemfile.lock）を作成．  
  参考：[Gem - Bundler概要 - Qiita](http://qiita.com/hisonl/items/162f70e612e8e96dba50)
4. Herokuでの公開
  5. Herokuにアプリを作成する（`heroku create [app. name]`）．
  6. Herokuにローカルで作ったアプリを公開する（`$ git push heroku master`）．
  7. Herokuにアドオンを導入して自動ツイートのための設定を行う．


では実際にBotを作っていこう．基本的には[上記サイト](http://blog.ruedap.com/2011/02/09/ruby-heroku-twitter-bot)のとおりに環境を設定していく．2014年11月時点で，ソフトウェアのバージョンの差異によって詰まった点とその解決策を，上記サイトの節に沿って解説します．


# 「1. プロジェクトの準備」に関する注意点
## Ruby環境を導入する際の注意
複数バージョンのRubyを入れたい場合，バージョン管理ソフトrbenv入れておくと便利．RVMという管理ソフトもあるらしいがrbrnvの方が軽量のよう．詳しくは参考サイトを参照されたい．
Homebrewを使ってrbenvを導入する．

```
$ brew install rbenv
```

表示される指示に従って.bash_profileや.bashrcの適切な場所に以下を書き込む．僕はzshを使っているので，とりあえず.zshrcに全部ぶち込んだ．

```:.zshrc
### rbenv
# To use Homebrew's directories rather than ~/.rbenv
export RBENV_ROOT=/usr/local/var/rbenv

# To enable shims and autocompletion
if which rbenv > /dev/null; then eval "$(rbenv init -)"; fi
export RBENV_ROOT=/usr/local/var/rbenv
```

### 参考
- [passingloop • rbenv と RVM との違い](http://passingloop.tumblr.com/post/10512902196/difference-between-rbenv-and-rvm)

## 「必要なgemのインストール」に関する注意点
元記事ではgemをインストールする際一気に入れていて，その中で`$ gem install heroku`としてHerokuでのアプリを作成するために必要なツールを入れているが，
[ドキュメント](https://devcenter.heroku.com/articles/heroku-command)を読むと[Heroku Toolbelt](https://toolbelt.heroku.com/)からインストールせよとあるので従う．

```
$ gem install sinatra bundler get-twitter-oauth-token --no-rdoc --no-ri
```
なお，ドキュメントが必要な場合`--no-rdoc --no-ri`を外しておく．

# 「3. プログラミング」に関する注意点

## 成功した際に利用したソフトウェアのバージョン
- ruby 2.1.4p265 (2014-10-27 revision 48166) [x86_64-darwin14.0]
- RubyGems(2.2.2)
  - twitter (5.12.0)
  - get-twitter-oauth-token (1.1.0)
  - sinatra (1.4.5)


## 問題1. requireで指定したファイルが見つからない

```
$ heroku logs | grep Error                                       (git)-[master]
2014-11-06T00:18:39.206506+00:00 app[web.1]: /app/config.ru:1:in `require': cannot load such file -- app.rb (LoadError)
```
### 原因：Rubyのバージョンが新しい
> Ruby 1.9.2 以降は $LOAD_PATH にカレントディレクトリが含まれない
[Rubyのrequireで相対的にパスを指定する方法 - QA@IT](http://qa.atmarkit.co.jp/q/2356)

参考にした記事ではrubyのバージョンは1.8.7だったので，カレントディレクトリがちゃんと読み込まれていた．

### 解決策：require_relativeを使う
自作のコードを`require`している部分を全て`require_relative './tweet.rb'`のように書き換える．

### 参考
- [Error with Ruby Twitter API - Stack Overflow](http://stackoverflow.com/questions/20247712/error-with-ruby-twitter-api)
- [Ruby 1.9.2p0 で requireとrequire_relativeについて調べてみた - tagomorisのメモ置き場](http://d.hatena.ne.jp/tagomoris/20100927/1285559238)

## 問題2. Twitter認証ができない
```
$ heroku logs | grep Error                                       (git)-[master]
2014-11-06T00:43:15.473638+00:00 app[web.1]: NoMethodError - undefined method `configure' for Twitter:Module:
```

### 原因：Twitterライブラリの仕様変更

> RubyGems ライブラリ Twitter のバージョン 5.0.0 以上（？）ではエラーが発生する。
[Ruby - Twitter Gem 不具合！ - mk-mode BLOG](http://www.mk-mode.com/octopress/2013/12/02/ruby-gem-twitter-failure/)

```
$ gem list | grep twitter
twitter (5.12.0)
```

### 解決策（不完全）
[Twitter（RubyGems）v.5.12.0のドキュメント](http://www.rubydoc.info/gems/twitter)を見ながら直していこう．
まず，TweetクラスのTwitter認証に関する以下の部分を修正する．

```:tweet.rb
# 修正前
Twitter.configure do |config|
  config.consumer_key       = 'メモったConsumer keyを挿入する'
  config.consumer_secret    = 'メモったConsumer secretを挿入する'
  config.oauth_token        = 'メモったTokenを挿入する'
  config.oauth_token_secret = 'メモったSecretを挿入する'
end
```

修正後のコードは次のようになる．

```:tweet.rb
# 修正後
client = Twitter::REST::Client.new do |config|
  config.consumer_key        = "YOUR_CONSUMER_KEY"
  config.consumer_secret     = "YOUR_CONSUMER_SECRET"
  config.access_token        = "YOUR_ACCESS_TOKEN"
  config.access_token_secret = "YOUR_ACCESS_SECRET"
end
```
`update`メソッドの`Twitter.update(tweet.chomp)`を以下のように変更．

```:tweet.rb
client.update(tweet.chomp)
```
この状態で再度`git push heroku master`すると，Herokuのページには何も表示されず，混乱する．

> 何も表示されず真っ白なページなら失敗！

という[元のページ](http://blog.ruedap.com/2011/02/09/ruby-heroku-twitter-bot)の文章が頭をよぎる．

とりあえず`$ heroku logs | grep Error`するも該当なし．
rubyの文法わからないけどメンバ変数とかの問題と当たりをつけてググる．インスタンス変数の頭には@が付いているらしい．これがないからか！

### 続・解決策(完全)
インスタンス変数に@をつける．

```:tweet.rb
@client = Twitter::REST::Client.new do |config|
```

```:tweet.rb
@client.update(tweet.chomp)
```

再度`git push heroku master`してrandom_tweetページに移動すると，無事動作することが確認できる．ただしTwitter gemの仕様変更によりページは白紙のままである．なぜページが白紙のままなのかについての詳細は，コメント欄の[riocamposさんによる丁寧な解説](http://qiita.com/motoso/items/da93faeea8dba9260e2b#comment-77837f2b9e02acb149e3)を参照ください．

# 「4. 公開」に関する注意点
## Herokuに公開鍵を設定しようとすると怒られる問題
`heroku keys:add`するとApplication Errorが出る．

### 解決策：連続2回トライする
これで通ってしまった．なぜ？

### 参考
- [`heroku push origin master` でPermission deniedエラー - Qiita](http://qiita.com/ikedahidenori/items/23cda9fb0abb9cc29b12)


## 問題3. rubygemsが古い
```
$ heroku run bundle exec ruby task.rb                         (git)-[master]
Running `bundle exec ruby task.rb` attached to terminal... up, run.8527
The source :rubygems is deprecated because HTTP requests are insecure.
Please change your source to 'https://rubygems.org' if possible, or 'http://rubygems.org' if not.
```
### 解決策
rubygemはセキュアじゃないので廃止予定だから変更しろと言われている．
Gemfileの`source :rubygems`を言われたとおりに変更する．

```:Gemfile
source 'https://rubygems.org'
```
これでエラーは出なくなりました．

## 問題4. 連投できない
二回以上連続で`ruby task.rb`を実行しても（つまり内容の同じツイートを二回以上つぶやこうとしても）できませんでした．これは内容が重複している場合にTwitter側が投稿を制限するためです．
一定時間内に同じツイートをすることができないようです．直前の発言のみに制限されるわけではありません．例えば内容hogeをつぶやいた後にhogeをつぶやくのはもちろんだめですが，hoge→hage→hogeとワンクッション程度おいても制限されます．
なお，[Twitterヘルプセンター](https://support.twitter.com/groups/55-troubleshooting/topics/231-tweets-direct-messages/articles/20043311-)には制限の解除されるために必要な時間は書いてありませんでした．

### 解決策：連続で同一投稿を投稿をしない
問題解決になっていませんが，とりあえず同一投稿をするのを避けましょう．
> 10ツイート挟むと同じ投稿が可能

という[実験をした人がいます](http://www.web-ecs.com/blog/2011/05/31/687)（2011年時点）.
24時間おけば同一内容でも大丈夫という意見も見つけましたがソースが見つからず．
※コメント欄で「6時間ぐらい空けば同一ツイート連投もOKという印象」という意見も頂いています．要テスト．
### 参考
- [【実験】ツイッターで同じ内容の投稿をするには？｜株式会社ECS 社長ブログ](http://www.web-ecs.com/blog/2011/05/31/687)
- [同内容ツイート規制を避けるために - 似非プログラマの覚え書き](http://d.hatena.ne.jp/redcat_prog/20131126/1385475137)

## 問題5. Herokuにcronがない
## 解決策：Heroku Schedulerを使う
他にもいくつかあったので気になる方はググられたし．
なお，常時データを監視する際には[clockworkというGemを使うのがいいっぽい](http://blog.takuros.net/entry/20130319/1363671409)です．
※コメント欄で教えていただいた情報によると，[rufus-scheduler](https://github.com/jmettraux/rufus-scheduler)というgemを使うと秒単位まで指定できる．

インストールする．

```
$ heroku addons:add scheduler:standard
```
スケジューラーを開いて設定する．

```
$ heroku addons:open scheduler
```
GUIが開かれるので，jobを追加して次のコマンドを入れる

```
bundle exec ruby task.rb
```
"task.rb"が実行されるたびにツイートされるので，好きな時刻を設定する．
実行時間のタイムゾーンがUTC（世界協定時）固定なので，JST（日本標準時）にするためには時差を足す必要があります．` UTC = JST - 9h`です．

例えば日本時間19時にツイートしたければUTCの10時に設定します．

なお，schedulerの実行時間のデフォルトをJSTにしたくて，HerokuのタイムゾーンをJSTにしても変わりませんでした．できた人教えてください．

### 参考
- [Heroku Scheduler | Heroku Dev Center](https://devcenter.heroku.com/articles/scheduler#installing-the-add-on)
- [heroku schedulerの使い方(Rails,sinatra) - Qiita](http://qiita.com/kt_Biz/items/5a7d7db3838728a04140#2-2)
- [Herokuを使って無料のジョブサーバを構築する - プログラマになりたい](http://blog.takuros.net/entry/20130319/1363671409)
- [Salesforce Developers Japan Blog](http://blogjp.sforce.com/2011/11/heroku-schedule-dc69.html)