2014年9月9日火曜日

RubyでTwitter Botをつくる

ここ最近RubyでTwitterのBotを書いていたのですが、タイムラインの取得や特定のワードに反応してふぁぼったりリプライをするなど、そういう基本的な動作をするまでをまとめてみました。

拙いコードですがRubyとTwitterでなんかしたいなんて思ってる人の参考になればと思います。

※2015/06/21 説明を部分的に編集しました。(内容は変わっていません)



登録

まずTwitterAPIを使うためにアプリケーションの登録をするのですが、以下のサイトがとてもわかりやすかったです。
Twitter APIの使い方 アプリケーションの登録

こちらでもかいつまんで簡単に説明すると、以下のリンクへ行ってBotにしたいアカウントでログインします。
https://dev.twitter.com/
アプリケーションの名前はクライアント名として表示されるので重要ですが、それ以外(説明やWebsite)は適当でいいと思います。

アプリを作るボタンを押したらいろいろ設定する画面に行くのですが、Permissionsの設定でアクセスレベルを必ず「Read and Write」以上にしてください。設定の変更が反映されるまで数分かかるので更新されるまで待ちます。

更新されたらAPIkeysのタブを選択し、一番下にある[Create my access token]ボタンを押します。(これも押したあと更新まで数分待ちます。)
その後ページに表示された
・API key
・API secret
・Access token
・Access token secret
をどこかにメモって大切に管理してください。

これで登録の手順は終了です。


Ruby Gem

今回使用するGemは以下のふたつです。
github twitter
github tweetstream
1
2
$ gem install twitter
$ gem install tweetstream
で導入可能ですが、特にTwitterのGemの方がバージョンアップでキーを設定する部分がコロコロ変更されるので、ちょくちょくソースのページを確認しにいったりバージョンを意識して使うとよいかと思います。(Gemfileでバージョンを指定しておくとか)

ちなみに今使っているのは
twitter(5.11.0)
tweetstream(2.6.1)
で、以下もそのバージョンを使った紹介になります。


RubyでBotを書く

これから
  • config.yml
  • bot.rb
  • reply.rb
の3つのファイルを作ります。

config.yml

これは先ほど取得したキーを書いておくファイルです。ファイルを分けておくとコードを公開するときこれだけ除外すればよいし、今後何かと便利です。
config.yml
1
2
3
4
api_key: xxxxxxxx
api_secret: xxxxxxxx
access_token: xxxxxxxx
access_token_secret: xxxxxxxx
xxxxxxxxには自分のものを書いてください。


bot.rb

このファイルには、Botクラスを書いておきます。ふたつのgemを簡単に扱えるようにしたものですが、クラスのまとめ方は人それぞれだと思うので参考までに。
bot.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# coding:utf-8
 
require 'yaml'
require 'twitter'
require 'tweetstream'
 
class Bot
  attr_accessor :client, :timeline
 
  def initialize
    keys = YAML.load_file('./config.yml')
 
    @client = Twitter::REST::Client.new do |config|
      config.consumer_key = keys["api_key"]
      config.consumer_secret = keys["api_secret"]
      config.access_token = keys["access_token"]
      config.access_token_secret = keys["access_token_secret"]
    end
 
    TweetStream.configure do |config|
      config.consumer_key = keys["api_key"]
      config.consumer_secret = keys["api_secret"]
      config.oauth_token = keys["access_token"]
      config.oauth_token_secret = keys["access_token_secret"]
      config.auth_method = :oauth
    end
 
    @timeline = TweetStream::Client.new
  end
 
  def post(text = "", twitter_id:nil, status_id:nil)
    if status_id
      rep_text = "@#{twitter_id} #{text}"
      @client.update(rep_text, {:in_reply_to_status_id => status_id})
      puts "#{rep_text}"
    else
      @client.update(text)
      puts "#{text}"
    end
  end
 
  def fav(status_id:nil)
    if status_id
      @client.favorite(status_id)
    end
  end
 
  def retweet(status_id:nil)
    if status_id
      @client.retweet(status_id)
    end
  end
end


reply.rb

Botクラスを使用する実行ファイルです。
以下のコード例では
  • 「おーい」とつぶやくと、Botがそのつぶやきをリツイートしリプライをする
  • 「ほげ」とリプライを送ると、Botがそのつぶやきをお気に入り登録しリプライをする
という挙動をします。
reply.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#!/usr/bin/env ruby
# coding:utf-8
 
require './bot.rb'
 
bot = Bot.new
 
begin
  bot.timeline.userstream do |status|
 
    twitter_id = status.user.screen_name
    name = status.user.name
    contents = status.text
    status_id = status.id
 
    # リツイート以外を取得
    if !contents.index("RT")
      str_time = Time.now.strftime("[%Y-%m-%d %H:%M]")
 
      # botを呼び出す(他人へのリプを無視)
      if !(/^@\w*/.match(contents))
        if contents =~ /おーい/
          text = "はい\n#{str_time}"
          bot.retweet(status_id:status_id)
          bot.post(text, twitter_id:twitter_id, status_id:status_id)
        end
      end
 
      # 自分へのリプであれば
      if contents =~ /^@hoge\s*/
        if contents =~ /ほげ/
          text = "ほげほげ\n#{str_time}"
          bot.fav(status_id:status_id)
          bot.post(text, twitter_id:twitter_id, status_id:status_id)
        end
      end
    end
    sleep 2
  end
 
rescue => em
  puts Time.now
  p em
  sleep 2
  retry
 
rescue Interrupt
  exit 1
end


reply.rbについて

実行ファイルで何をやっているかを簡単に書きます。何気に行番号が対応してたりします。

タイムラインの取得
  botというインスタンスを生成したら、下記のブロックがタイムラインが流れる度に処理されます。
9
10
11
bot.timeline.userstream do |status|
  # タイムラインが流れる度に処理される
end


ブロック変数
  次に、statusと名前をつけたブロック変数ですが、下記のような情報を得ることができます。ツイート自体のIDというのはつぶやきごとに与えられたIDで、これを使ってリプライやふぁぼ、リツイートを行います。
(この他の情報も得ることができますが、とりあえずはこれくらいで大丈夫だと思います)
11
12
13
14
twitter_id = status.user.screen_name #アカウントのID
name = status.user.name              # アカウントの名前
contents = status.text               # つぶやき内容
status_id = status.id                # ツイート自体のID


リツイートに反応しないようにする
  これは好みの問題ですが、リツイートしたものの中に反応ワードが入っているとBotが反応してしまうので、除外します。
  公式リツイートを取得した場合、contentsは 「RT @リツイートされたカウントのID: 内容」 という文字列になるため、contents.index("RT") は普通のツイートである場合 nil を、リツイートである場合 0 を返すことになります。よって、nil は false0 は trueであるため !contents.index("RT") はリツイート以外のときtrueとなり、それ以降の処理をします。ちょっとややこしいですね。
(実は status.retweeted_status とかでも判断できたりする)
17
18
# リツイート以外を取得
if !contents.index("RT")


自分以外へのリプライを除外
  自分以外のアカウントへのリプライに呼び出しワードが含まれていた場合、それに反応するのを防ぎます。先頭に@があって以降[a-zA-Z0-9_]が続いているものを除外しています。
  このif文中でさらにもうひとつの条件、「おーい」という文字にマッチすればそのツイートはBotによってリツイートされ、「はい\n#{str_time}」というリプライが返ってくるという処理がされます。
20
21
# botを呼び出す(他人へのリプを無視)
if !(/^@\w*/.match(contents))


自分へのリプライには反応する
  一方、自分へのリプには反応したいので、別のif文を用意します。(@hogeにはBotのIDを入れてください。
  このif文中でさらにもうひとつの条件、「ほげ」という文字にマッチすればそのツイートはBotによってふぁぼられ、「ほげほげ\n#{str_time}」というリプライが返ってくるという処理がされます。
29
30
# 自分へのリプであれば
if contents =~ /^@hoge\s*/


各メソッドについて
  bot.rbでつくった各メッソドについて、postメソッドを例にとって説明します。
  postメソッドにはつぶやく内容と、相手のアカウントIDとそのつぶやき自体のIDを渡しています。リプライでなければ status_id は不要ですが、このIDを与えることにより、お馴染み「会話を表示」などでリプライが繋がります。
  fav、retweetメッソドでも同様にこのつぶやきのIDを与えることでふぁぼやリツイートを行います。
25
bot.post(text, twitter_id:twitter_id, status_id:status_id)


最後に

拙いコードで、わかりづらい部分もあったかと思いますが最後まで読んでいただきありがとうございました。
もし説明が間違ってる箇所等ありましたらご指摘ください。

また自分はRubyでBotを作成するにあたって
大五郎(U^ω^)BOTを参考にさせていただきました。
この場を借りてお礼申し上げます。ありがとうございました。

ついでに私が書いたBotも紹介だけしておきます。
OE_bot
機能としてはある部屋の入退室管理なので、完全に身内ものになっていて関係者以外がフォヨーしても意味がありませんが、もしよければコードだけでも見てみて、これまたご指摘等お待ちしております。

0 件のコメント:

コメントを投稿