【TwitterAPI】「贅沢な名だねえ。今からお前の名前は○○だ。」【Ruby】


動機

  • 2019/8/16に金曜ロードショーで「千と千尋の神隠し」が放送された
  • 前に少し流行ったupdate_name(ツイッター上で、リプライに反応して自分のTwitterネームを変更するもの)がUserStreamの廃止に伴い実装が困難になっていて、どうにかREST APIだけで再現できないか考えてた
  • この記事を見て、似たようなものをRubyで書いてみようと思ってた

みんな僕のTwitterの名前を自由に変えていいよ

環境と使ったもの

  • macOS 10.14.5
  • Ruby 2.5.1
  • TwitterAPI … Twitter Developersに登録済みであることが前提です。

仕様

  1. 「いいかい、今から@fyhcu の名前は○○だ。」というリプライを、最新50件のリプライから正規表現で見つける。台詞は少し変えてます。(できるだけ簡単な文章にしたかったので)
  2. Twitterの制限上、名前が50文字以上になるものは設定できないのでこれを除外。さらに、以前に候補に上がったものも除外。候補になったかどうかはいいねしているかどうかで判断します。
  3. 候補となったツイートを配列に格納し、そのツイートをいいねする。(これで次動いた時の候補に挙がらなくなります)
  4. 格納した候補からランダムに一つ選ぶ。
  5. 名前をそのワードに変更。変更したことをツイート。

結果

実装

メイン部分

main.rb
require_relative '../Privatekey/oauth_twitter'
require_relative 'isfavorited'
require 'timers' #定期実行用

timers = Timers::Group.new

timers.every(600){ #600秒 = 10分ごとに処理

  reply_array = [] #リプライを格納する配列
  random = 0 #改名候補ツイートから無作為に選ぶため

  reg1 = /^いいかい、今から@fyhcu の名前は(.*)だ。$/ #湯婆婆

  @client.mentions(count: 60).each do |reply|
    if (reg1 =~ reply.text) #湯婆婆になっているリプライを探す
      puts "-----------------------------------------------------------"
      puts reply.text

      #名前が50文字以上に設定されているツイートは除外
      if ($1.length >= 50)
        next #次の繰り返しに移動
      end

      #既に自分がふぁぼってるツイートは除外
      if isfavorited(reply.id)  == true
        puts "<<<<<<<この返信は既にいいねされています>>>>>>>>"
        next
      end

      reply_array << $1 #改名候補を格納

      @client.favorite(reply.id) #ツイートをいいね

    end
  end

  puts "-----------------------------------------------------------"
  time_f = Time.now.strftime("%F %X") #実行時間を記録

  if reply_array.size > 0 #改名候補がある時のみ

    random = rand(reply_array.size) #ランダムに数字を一つ選ぶ
    aftername = reply_array[random] #変更後の名前を決定
    puts "#{aftername}に改名決定です"

    @client.update_profile(:name => aftername) #改名

    tweet = "湯婆婆によって名前が#{aftername}に変えられました。"
    @client.update tweet

    #ログに書き込み
    File.open("../log/yubaba-app-log.txt", "a") do |text|
      text.puts(" => #{aftername}に改名 [#{time_f}]")
    end

  else #改名候補がない時(配列に何も格納されていない時)

    puts "改名候補がありませんでした。"

    #ログに書き込み
    File.open("../log/yubaba-app-log.txt", "a") do |text|   
      text.puts(" => 変化なし [#{time_f}]")
    end
  end
}

1000.times{ #1000回実行(この実装は良くなさそう)
  timers.wait
  puts Time.now.strftime("****************** %Rに実行しました ******************")
 }

湯婆婆構文は正規表現を使って拾っています。正規表現で一致し、かつ特定の条件を満たしたリプライの変更後の名前部分を配列に格納しています。正規表現のグルーピング()と後方参照$1,$2,...がとても役に立ちました。次以降の候補に入らないように、@client.favorite(id)でいいねしています。UserStreamが動いていたあの頃のupdate_nameに近付けたいので、10分毎に定期実行をかけています。Timerを使っていますが、多分良くない気がするので近いうちにHeroku Schedulerとか使えたらなと思います。

また、main.rbを実行すると、ターミナル上に正規表現によって拾われたツイートとそれがいいねされているかどうかが表示されます。実行時に改名の有無にかかわらずログを書くようにもしました。

※このコードのままだと一定時間内に2回同じ名前に変更されると2回目以降改名した旨のツイートか重複によりツイートできない状況になってしまいます。例外処理でなんとかできれば…。(気が向いたら実装します)

isfavoriteメソッド

「自分があるツイートに対していいねしているか」をbooleanで返すメソッドがなかったので自作しました。JSON.parseが便利でした。

isfavorited.rb
require_relative '../Privatekey/oauth_twitter'
require 'json' #JSON扱うためのgem
require 'oauth'

# ツイートに対して、自分がいいねしているか調べるメソッド

def isfavorited(tweet_id)
  consumer = OAuth::Consumer.new(
    @client.consumer_key,
    @client.consumer_secret,
  )

  endpoint = OAuth::AccessToken.new(consumer, @client.access_token, @client.access_token_secret)
  responce = endpoint.get("https://api.twitter.com/1.1/statuses/show/#{tweet_id.to_s}.json")
  result = JSON.parse(responce.body)
  fav = result["favorited"]

end

https://api.twitter.com/1.1/statuses/show/#{tweet_id.to_s}.json" は、ツイート(ID指定)の個別情報を取得できるエンドポイントで、レスポンスはJSONです。

GET statuses/show/:id - TwitterDevelopers

レスポンスボディの中にあるfavorited: true(false)が自身がいいねしたかどうかを示しています。これを引っ張り出してきて最終的にbooleanをそのまま返すようにしてます。実際、この2行があるだけで結構いろんなことができるなあと思っています。

responce = endpoint.get("https://api.twitter.com/1.1/statuses/show/#{tweet_id.to_s}.json")
result = JSON.parse(responce.body)

感想

意外と簡単に実装できました。いっぱい遊んでもらえたので満足です。これからもくだらないものを実装してみようと思います。