【Rails】Twitterでサンタコスbotを作る

#概要

せっかくのAdvent Calendarなので、クリスマスに関連するアプリを作りたいですね

ちなみに、TECH::CAMP Advent Calenderでは、二日目に以下のような記事がすでに投稿されております！

[【Swift】Twitter APIを使ってサンタコス女子を探す](http://qiita.com/kinopontas/items/546df17a963d71f50d4e)

二日目の記事では、サンタコスを探すアプリだったので、今回はサンタコスの画像をTwitterに発信しようかと！

つまり
###Twitter bot
ですね！

ちなみに、検索してもらうとわかるのですが、Twitterのbotの作り方の記事ってたくさんあるのですね！もちろん、rubyを使ったものも多いです。。

そこで、この記事では、特に以下のような点を意識して書きます！

####1. Railアプリであること
まず、rubyで動かすbotの記事は多いですが、railsまで使う記事はあまりありませんでした。特に、データベースを活用したRailsアプリによるbotがあまりありませんでした。

そのため、この記事では、Railsを使用して、以下のようにツイートを画像URL付きで保存する機能をつけます。
そこから、二段階に分けて、最初は手動でのランダム投稿、その次に自動化するようにします！


![a61b6fc981c70bc12f29e1b7c6931130.gif](https://qiita-image-store.s3.amazonaws.com/0/100027/ffbf3bc8-4dc7-7cdc-49e1-c299639b873f.gif)


これが最初につくる手動でのランダム投稿
![3f0a513aead578f146e8fd68978226fb.gif](https://qiita-image-store.s3.amazonaws.com/0/100027/a81688d3-8612-972e-6767-bc13d5214489.gif)



そうすると、、、

![35050eb53d29609b26ec1fdef7e3608a.png](https://qiita-image-store.s3.amazonaws.com/0/100027/4211329e-3eba-3f3c-7ce5-3c5342dbf735.png)






この記事では、最初に、基本的な手続きを踏んで、railsアプリを作成するので、是非Railsの基礎だけ学んだような人にも見てもらいたいです

####2. wheneverを使用した自動投稿

最初は手動でツイートできるようにしますが、やはりbotなので自動で動かしたいですね！
そこで、wheneverを使用して、一定時間ごとにデータベースに保存したツイートをtwitterに自動投稿する機能を作ってみます。


####3. twitterのgemの最新版を使用

twitterのgemは、最新版で少し記述の仕方が変わったようです。そのため、古い記事通りに作るとうまくいかないこともあるようです。ですので、ここでは最新版の書き方に従って書きます。

####4. 画像付きツイートのbot

まあこれはそれ用のメソッド使えばできます笑


####5. サンタコスbot

季節限定感出します（サンタコス以外のbotでも作り方は全く一緒です！　実際、最初は飯テロアプリのつもりでした笑）

####6. おまけ（サンタコスツイートを検索してお気に入り登録（いいね）する）付き
個人的にはおまけの項が一番やりたかったことです笑



#環境

・Ruby 2.1.3
・Ruby on Rails　4.2.4

#目次
Ⅰ　Railsアプリにツイートを保存する機能を作る

Ⅱ　RailsアプリとTwitterを連動させる

Ⅲ　wheneverを使用して自動投稿できるようにする

Ⅳ　完成コード

Ⅴ　おまけ（サンタコスツイートを検索してお気に入り登録（いいね）する）
　


######それでは作っていきましょう(ﾟ∀ﾟ)



##Ⅰ　Railsアプリにツイートを保存する機能を作る

この章では、Railsの基礎的な話なのでさくさくいきます！わかる人は飛ばしてください！

####1. アプリを作り始める（慣れているのでmysqlでいきます）

```
$ rails new santacosbot -d mysql

$ cd santacosbot

```

最新版（2015年12月6日現在）のmysqlがエラーするので、mysqlの記述を上書き

```gemfile
gem 'mysql2', '0.3.18'

```
それで

```
$ bundle install
$ rake db:create
```

これで最低限の準備は整いました。


####2.ツイートの投稿と保存まで



ルーティング

```route.rb
Rails.application.routes.draw do
  root 'tweets#new'
  resources :tweets, only: [:new, :create]
end
```

コントローラー

```
$ rails g controller tweets
```
できたコントローラーファイルを以下のように編集

```tweets_controller.rb
class TweetsController < ApplicationController
  def new
    @tweet = Tweet.new
  end

  def create
    Tweet.create(create_params)
    redirect_to :root
  end

  private
  def create_params
    params.require(:tweet).permit(:text, :image)
  end
end
```



ビューファイル(自分しか使わないから見栄えは気にしない方針で)
一応twitterなので140文字の制限だけつけておきます！(追記：画像付きツイートしか保存しない方針なら、116文字で大丈夫だそうです。画像へのリンクが付くことによって入力できる文字数が減るからです。)

```new.html.erb
<%= form_for @tweet do |f| %>
  <%= f.text_area :text, cols: 40, rows: 5 ,maxlength: 140 %>
  <%= f.text_field :image %>
  <%= f.submit '保存' %>
<% end %>
```

テーブル・モデル

```
$ rails g model tweet
```

で、マイグレーションファイルを以下のように編集

``` lang:201oooooooooo_create_tweets.rb
class CreateTweets < ActiveRecord::Migration
  def change
    create_table :tweets do |t|
      t.text :text
      t.text :image
      t.timestamps
    end
  end
end
```
そして、実行
```
$ rake db:migrate
```


これで、以下のようなフォームからツイートを保存をすることだけはできるようになったはずです！！

![cef0d90571bdbf6e8a8e45e043359c2b.png](https://qiita-image-store.s3.amazonaws.com/0/100027/fb5df5aa-e0a4-cbd2-5150-b0a31c150728.png)


##Ⅱ　RailsアプリとTwitterを連動させる



###1.Twitterアカウントを作る（電話番号も登録する）

・すでにあるアカウントを使用しない場合は以下から新規アカウントを作成。
[Twitterをはじめましょう](https://twitter.com/signup)
![8ac66bda93cc1b95873010be5fe0cb54.png](https://qiita-image-store.s3.amazonaws.com/0/100027/b4401d7a-8018-8c25-bbbb-026325649669.png)


＊ちなみに電話番号の登録と認証をしておかないと、下のアプリ登録が出来ないので登録しておきましょう
＊一つの電話番号を複数のアカウントに登録できるので、他のアカウントとの兼ね合いの心配は要りません
*電話番号の登録がうまくいかない人は下記を参照
[Twitterで携帯電話番号の登録に失敗する](http://qiita.com/tk1024/items/644ead20793a6e869b83)

###2.Twitterのアカウントにアプリ登録をする
[Twitter Applicant Management](https://apps.twitter.com/![50c46cc1f562439527f7cce5276bb1fe.png](https://qiita-image-store.s3.amazonaws.com/0/100027/f9fe7848-03bc-a20e-7dc6-1a50d8e227fc.png)
)

「Create New App」ボタンを押して、設定をはじめましょう。

![028fc0c5b6a0853237c5c34a4e7fed57.png](https://qiita-image-store.s3.amazonaws.com/0/100027/94a1a12d-5be6-01af-e270-c2a9881b2e52.png)




![a94cfd429de15caabc913d854fc2769a.png](https://qiita-image-store.s3.amazonaws.com/0/100027/135ca890-90c2-409c-77a2-0388ee2acb9d.png)


Name(アプリの名前)、Description(アプリの説明、10文字以上必要)、Website(自分のウェブサイト等)は必須なので入力しましょう。

このうち、Nameはアプリの名前なのでちゃんと書きたいのですが、他はあまり重要でないのでとりあえず埋めておきましょう。

WebsiteはもはやTwitterのURLでもいいです（笑）

###3.必要なキーを入手しよう

・Consumer Key(API key)
・Consumer Secret(API secret)
・Access token
・Access token secret

この４つが、アプリから連携する上で必要なのでこの4つのキーを取得して、記録しておきます。



登録済みアプリのページの「Keys and Access Tokens」のタブからすべて取得できます。

「Keys and Access Tokens」のタブの一番下の「Create my access token」を押します。
そうすると、下記のように必要なキーが取得できます。

ちなみに、access levelの設定が「Read and Write」以上であることを確認してください。違っていたら、「permission」タブから読み込みと書き込みができるように設定を変更しておいてください。


![70429643e89f8ab610f9cec8688489b3.png](https://qiita-image-store.s3.amazonaws.com/0/100027/c12cbcaf-bbfd-7196-97bc-fb09f579b1d3.png)

手続きに困ったら下記のサイトを参考にしてください。
[RubyでTwitter Botをつくる](http://syoshinsyakangeisagi.blogspot.jp/2014/09/rubytwitter-bot.html)

これでTwitter側の設定は完了となります！


###4. RailsアプリにTwitterと連携の設定をする

まず、以下のgemを追加

```gemfile
gem 'twitter'
```
```
$ bundle install
```
これでtwitterのgemが使用可能になりました！

基本的に下記の記述をどこかに書けば、twitterと連携することができます。（古いversionのtwitter gemと記述の仕方が異なります。）

```
client = Twitter::REST::Client.new do |config|
  config.consumer_key = "自分のConsumer Key(API key)"
  config.consumer_secret = "自分のConsumer Secret(API secret)"
  config.access_token = "自分のAccess token"
  config.access_token_secret = "自分のAccess token secret"
end
```

今回は下記のように、controller内に書いてbefore_actionで動かそうかと思います。

```tweets_controller.rb
class TweetsController < ApplicationController
  before_action :twitter_client, except: :new
  
  def twitter_client
    @client = Twitter::REST::Client.new do |config|
      config.consumer_key = "自分のConsumer Key(API key)"
      config.consumer_secret = "自分のConsumer Secret(API secret)"
      config.access_token = "自分のAccess token"
      config.access_token_secret = "自分のAccess token secret"
    end
  end
end
```

これで、@clientに対してツイートするメソッドを実行することで、実際に投稿できるようになります。

そして、投稿するメソッドも書いてルーティングを設定してあげましょう。

```tweets_controller.rb
def post
  tweet = Tweet.order('rand()').first
  status = tweet.text
  media = open(tweet.image)
  @client.update_with_media(status, media)
  redirect_to :root
end
```

```route.rb
Rails.application.routes.draw do
  root 'tweets#new'
  resources :tweets, only: [:new, :create] do
    collection do
      get 'post'
    end
  end
end
```
そして、とりあえず手動で動かしたいので、ツイートをデータベースに投稿する画面に「ツイート」ボタンを作りましょう。

```ruby:new.html.erb
<hr>
<button type="submit" style = "width: 70px; height: 25px;"><%= link_to 'ツイート', post_tweets_path %></button>
```

これで「ツイートボタンをおすことで、tweetsテーブルに保存されたツイートからランダムでtwitterに投稿されます。



##基本機能完成

これでとりあえず、ローカルで自分で叩けば動くようになりました（自分しか使わない前提なので見栄えは気にしないでください笑）。

上部に、ツイートしたい文章と、画像のURLを貼ります！


これで、ツイートが保存されます

![a61b6fc981c70bc12f29e1b7c6931130.gif](https://qiita-image-store.s3.amazonaws.com/0/100027/ffbf3bc8-4dc7-7cdc-49e1-c299639b873f.gif)

そして、「ツイート」ボタンを押すと、保存されたツイートからランダムに一件ツイートされます！

![3f0a513aead578f146e8fd68978226fb.gif](https://qiita-image-store.s3.amazonaws.com/0/100027/a81688d3-8612-972e-6767-bc13d5214489.gif)

そうすると、、、

###Twitterに投稿できました！！ﾜｰｲヽ(ﾟ∀ﾟ)ﾒ(ﾟ∀ﾟ)ﾒ(ﾟ∀ﾟ)ﾉﾜｰｲ
![2c6a845fa3edb1ffca4a75fa383b8bce.png](https://qiita-image-store.s3.amazonaws.com/0/100027/d0fdb629-e18e-53db-dd4b-0dfd680a21dc.png)



##Ⅲ　wheneverを使用して自動投稿できるようにする

ここまでは手動でのツイートでしたが、せっかくのbotなので自動でもできるようにしたいですね。ですので、ここからはwheneverのgemを使用します。

まず、以下のgemを追加

```gemfile
gem 'whenever', require: false
```
```
$ bundle install
```

次に、wheneverで動かしやすいようにrakeタスクを作りましょう

```
$ rails g task twitter
```

そうすると、lib/tasks以下に'twitter.rake'という名前のファイルができるので編集していきます。

```lib/tasks/twitter.rake
namespace :twitter do
  desc "random_tweet"
  task :tweet => :environment do
    twitter_client
    tweet = Tweet.order('rand()').first
    status = tweet.text
    media = open(tweet.image)
    @client.update_with_media(status, media)
  end
end

def twitter_client
  @client = Twitter::REST::Client.new do |config|
    config.consumer_key = "自分のConsumer Key(API key)"
    config.consumer_secret = "自分のConsumer Secret(API secret)"
    config.access_token = "自分のAccess token"
    config.access_token_secret = "自分のAccess token secret"
  end
end
```

基本的にはコントローラにのpostに書いた内容と同じです。これで

```
$ rake twitter:tweet
```

のコマンドでツイートができるか確認してみましょう


そして、wheneverでこれを自動化していきましょう

```
$ wheneverize .
```
のコマンドを入力して、configにschedule.rbのファイルを作ります。

それを以下のように編集

```config/schedule.rb
require File.expand_path(File.dirname(__FILE__) + "/environment")
set :output, 'log/crontab.log'
set :environment, :development


#2時間ごとに動かす
every 2.hours do
  rake 'twitter:tweet'
end
```

とりあえず二時間ごとに設定するために以下のようにしましたが、時間の指定の仕方はいろいろあるので以下を参考にカスタマイズしてください

[Wheneverは導入が超簡単なcrontab管理ライブラリGemです！[Rails4.2]](http://morizyun.github.io/blog/whenever-gem-rails-ruby-capistrano/)

これで、あとは、このファイルを起動させれば自動ツイートされます。

```
$ whenever --update-cron　#起動
```



```
$ whenever　#起動中に起動内容確認
```



```
$ whenever --clear-crontab　#終了

```

ですので、一番上の起動のコマンドを打てば動きます！

以上です！！！

これで完成となりますﾜｰｲヽ(ﾟ∀ﾟ)ﾒ(ﾟ∀ﾟ)ﾒ(ﾟ∀ﾟ)ﾉﾜｰｲ

完全に全自動になるようにしたい方は、オンラインサーバーにもあげましょう（Herokuを使用する方は一番下の注意の項を読んでください）

#Ⅳ　完成コード

```routes.rb
Rails.application.routes.draw do
  root 'tweets#new'
  resources :tweets, only: [:new, :create] do
    collection do
      get 'post'
    end
  end
end

```

```tweets_controller.rb
class TweetsController < ApplicationController
  before_action :twitter_client, except: :new
  def new
    @tweet = Tweet.new
  end

  def create
    Tweet.create(create_params)
    redirect_to :root
  end

  def post
    tweet = Tweet.order('rand()').first
    status = tweet.text
    media = open(tweet.image)
    @client.update_with_media(status, media)
    redirect_to :root
  end

  def twitter_client
    @client = Twitter::REST::Client.new do |config|
      config.consumer_key = "自分のConsumer Key(API key)"
      config.consumer_secret = "自分のConsumer Secret(API secret)"
      config.access_token = "自分のAccess token"
      config.access_token_secret = "自分のAccess token secret"
    end
  end

  

  private
  def create_params
    params.require(:tweet).permit(:text, :image)
  end
end

```

```new.html.erb
<%= form_for @tweet do |f| %>
  <%= f.text_area :text, cols: 40, rows: 5 ,maxlength: 140 %>
  <%= f.text_field :image %>
  <%= f.submit '保存' %>
<% end %>
<hr>
<button type="submit" style = "width: 70px; height: 25px;"><%= link_to 'ツイート', post_tweets_path %></button>
```

```lang:gemfile
gem 'mysql2', '0.3.18'
gem 'twitter'
gem 'whenever', require: false
```

```20oooooooo_create_tweets.rb
class CreateTweets < ActiveRecord::Migration
  def change
    create_table :tweets do |t|
      t.text :text
      t.string :image
      t.timestamps
    end
  end
end
```

```lib/tasks/twitter.rake
namespace :twitter do
  desc "random_tweet"
  task :tweet => :environment do
    twitter_client
    tweet = Tweet.order('rand()').first
    status = tweet.text
    media = open(tweet.image)
    @client.update_with_media(status, media)
  end
end


def twitter_client
  @client = Twitter::REST::Client.new do |config|
    config.consumer_key = "自分のConsumer Key(API key)"
    config.consumer_secret = "自分のConsumer Secret(API secret)"
    config.access_token = "自分のAccess token"
    config.access_token_secret = "自分のAccess token secret"
  end
end
```

```schedule.rb
require File.expand_path(File.dirname(__FILE__) + "/environment")
set :output, 'log/crontab.log'
set :environment, :development


every 2.hours do
  rake 'twitter:tweet'
end
```

##Ⅴ　おまけ（サンタコスツイートを検索してお気に入り登録（いいね）する）

せっかくなので、railsで旬なサンタコスツイートを検索してお気に入り登録する方法も書いておきますね
~~個人的にはこれが一番やりたかった~~

rakeファイルなりコントローラーなりに以下の記述すればできます（ここではrakeファイルに書いた場合）

```lib/tasks/twitter_search.rake
namespace :twitter_search do
  desc "favorite_santa_tweet"
  task :tweet_search => :environment do
    twitter_client
    query = "#サンタコス"
    results = @client.search(query, count: 100, result_type: "recent",  exclude: "retweets", filter: "images")
    results.take(100).each do |tw|
      @client.favorite(tw.id)
    end
  end
end

def twitter_client
  @client = Twitter::REST::Client.new do |config|
    config.consumer_key = "自分のConsumer Key(API key)"
    config.consumer_secret = "自分のConsumer Secret(API secret)"
    config.access_token = "自分のAccess token"
    config.access_token_secret = "自分のAccess token secret"
  end
end
```


基本的にコードみればわかると思いますが、searchメソッドで検索して、takeメソッドで件数の指定をした上でお気に入り登録していきます。

ちなみにお気づきの方もいるかと思いますが、このgemのメソッドを使用すれば、ツイートを検索して取得できます。
つまり、そこから画像を取得してデータベースに保存することも、、、いわゆるスクレイピングですね笑
そうすればそこからその画像を再利用してツイートすることもできちゃいますね笑


ただ、それを本当にやっていいのかわからないのと、同じadvent calenderの[【Swift】Twitter APIを使ってサンタコス女子を探す](http://qiita.com/kinopontas/items/546df17a963d71f50d4e)とやってることがかなりかぶるのでここでは書きません笑
やりたい方は、上記のリンクを見てみてください（swiftわからなくてもわかります笑）




以上で終了となります。このように、Twitterのgemを使うと遊べることがたくさんありますね笑 
~~(ネットストーカー御用達ですね)~~

とりあえず、今回はこれで終わりにしますが、またTwitter関連で記事を書こうかなと思います！

#注意

・アカウント凍結に注意しましょう。
Twitterでは、一定時間内に投稿できる件数が決まっております。また、一定時間内に同じツイートを連続投稿するのもまずいみたいです。うまく動かなくてツイートボタンを連打してたらアカウント凍結されてたみたいにならないようにしましょう笑

・このままHerokuなどのサーバーにアップする場合は、ビューのツイートボタンにパスワードの入力フォームを入れて、postアクションにパスワード認証をつける

これは上記のアカウント凍結を避けるためです。Herokuなどにあげると、もし第三者がアクセスできてしまった場合に、ツイートボタンを押されないようにするためです。

・Herokuでは、wheneverは機能しない
wheneverによる自動ツイートを残したままオンラインサーバーにあげたいときはさくらVPSあたりを使用しましょう
Herokuでやる場合は、アドオンのschedulerを使用しましょう。
[heroku schedulerの使い方(Rails,sinatra)](http://qiita.com/kyohei8/items/5a7d7db3838728a04140)





#参考

[RubyでTwitter Botをつくる](http://syoshinsyakangeisagi.blogspot.jp/2014/09/rubytwitter-bot.html)

[[Rails4] wheneverを使ってcrontabを管理して、cronを回す](http://qiita.com/susan_mamma/items/68d9b2bb816fc32138dd)


[3分でスッキリ! Oauth 1.0図解とRuby Twitter bot開発](http://b.ruyaka.com/2013/12/02/ruby-twitter-bot-with-oath1-0a/)

[rubyでtwitterのbotを実装する(第5世代のやり方)](http://qiita.com/gosshys/items/d31b4ef37f7614363029)

[Wheneverは導入が超簡単なcrontab管理ライブラリGemです！[Rails4.2]](http://morizyun.github.io/blog/whenever-gem-rails-ruby-capistrano/)

[Twitter gemでツイート検索する場合の要点、及び:since_id指定を有効にするモンキーパッチ](http://qiita.com/riocampos/items/6999a52460dd7df941ea)

[Twitterで携帯電話番号の登録に失敗する](http://qiita.com/tk1024/items/644ead20793a6e869b83)