Capybara PhantomJSによるSHOWROOM自動星集めツール作成

#概要

勉強も兼ねてSHOWROOMの自動星集めツールを作成しました。

#環境構築

ruby、関連ライブラリ(capybara、poltergeist)のインストール

```file:macOS
$ brew install rbenv ruby-build
$ echo 'eval "$(rbenv init -)"' >> ~/.zshrc
$ rbenv install (rubyのバージョンを指定)
$ rbenv global (rubyのバージョンを指定)
$ rbenv rehash
$ rbenv exec gem install bundler
$ rbenv rehash
$ rbenv exec bundle init
$ vi Gemfile

  //以下行を追加
  gem 'capybara'
  gem 'poltergeist'

$ bundle install --path vendor/bundle
```

>Windowsでのrbenv使用に関する投稿がございましたので、Windowsにて上記手順を実施する場合は下記が参考になるかと思います。
[Windowsのコマンドラインからrbenvを使いたい](https://qiita.com/nak1114/items/0cbf4797d95422b04569)

ヘッドレスブラウザ(PhantomJS)のインストール

```file:macOS
$ brew install phantomjs
```


#プログラム作成

```rb:collect_star.rb
require 'capybara'
require 'capybara/poltergeist'
require 'nokogiri'
require 'net/http'
require 'uri'
require 'json'

Capybara.default_max_wait_time = 5

URL = "https://www.showroom-live.com/"

# SR連携しているTwitterのIDとPASS
LOGIN_ID	= 'XXXXXXX'
LOGIN_PASS 	= 'XXXXXXX'

# 入力値取得
private def accept_input
  puts "星集めの場合「1」種集めの場合は「2」を入力して下さい"
  collect_type = gets.gsub(/^[　\s]*(.*?)[　\s]*$/, '\1').tr("０-９", "0-9")
  unless  collect_type =~ /^0*[12]$/
    puts "\n入力値不正「1」または「2」どちらかの数値を入力して下さい"
    return nil
  end

  puts "いくつのルームから集めますか？(1〜20まで)"
  collect_num = gets.gsub(/^[　\s]*(.*?)[　\s]*$/, '\1').tr("０-９", "0-9")
  unless  collect_num =~ /^(0*[1-9]|0*1[0-9]|0*20)$/
    puts "\n入力値不正 1〜20までの数値を入力して下さい"
    return nil
  end

  input_param = [collect_type.to_i, collect_num.to_i]
end

# Capybaraセッションの生成
private def access
  Capybara.register_driver(:poltergeist) do |app|
    Capybara::Poltergeist::Driver.new(app, {
      timeout: 120,
      # JSに問題があったとき例外を吐かせない
      js_errors: false,
      # console.logも捨てる
      phantomjs_logger: Logger.new('/dev/null')
      # 実行環境がWindowsの場合
#    phantomjs_logger: Logger.new('NUL')
    })
  end
  
  @session = Capybara::Session.new(:poltergeist)
end

# ログインの実施
private def login
  # ログイン画面にアクセス
  @session.visit(URL)
  @session.click_link('ログイン')
  @session.click_link('Twitterでログイン')
       
  # 認証画面に遷移
  parent_window = @session.current_window
  @session.switch_to_window { @session.title == 'Twitter / アプリケーション認証' } 
       
   # IDとPASSを入力し認証ボタン押下
  @session.fill_in('username_or_email',  with: LOGIN_ID)
  @session.fill_in('password',  with: LOGIN_PASS)
  @session.click_button('連携アプリを認証')
       
  @session.switch_to_window(parent_window)

  # ログイン失敗
  unless @session.has_xpath?('//div[@class="side-user-data"]')
    @session.reset!
    @session.driver.quit
    puts "\nログインに失敗しました、IDとパスワードが正しくない恐れがあります。"
    return false
  end
  
  true
end

# ツイートボーナスの取得
private def get_point(collect_type, collect_num)
  # 配信中一覧(JSON)
  uri = URI.parse('https://www.showroom-live.com/api/live/onlives')
  json = Net::HTTP.get(uri)
  response = JSON.parse(json)
  
  # JSONからルームのURLパスとルーム名を取得しHashに詰める
  # response['onlives'][0]… 人気          (取得対象外)
  # response['onlives'][1]… アイドル
  # response['onlives'][2]… タレント・モデル
  # response['onlives'][3]… ミュージック
  # response['onlives'][4]… 声優・アニメ
  # response['onlives'][5]… お笑い・トーク
  # response['onlives'][6]… バーチャル
  # response['onlives'][7]… アマチュア
  hash = {}  
  if collect_type == 1
    # 星集めの場合
    for i in 1..7 do
      response['onlives'][i]['lives'].each { | live |
        unless live['room_url_key'].nil?
          # live['room_url_key']→配信ルームのURLパス
          # live['main_name']→配信ルーム名
          hash.store(live['room_url_key'], live['main_name'])
        end
      }
    end
  else
    # 種集めの場合
    response['onlives'][7]['lives'].each { | live |
      unless live['room_url_key'].nil?
        hash.store(live['room_url_key'], live['main_name'])
      end
    }
  end

  # 一度アクセスしたルームをなるべく回避するべくランダムソート
  hash = hash.sort_by{rand}

  # 繰り返し処理でアクセスしていく   
  count = 0  
  hash.each{| key, value |
    result = to_tweet(key, value)
    
    if result ==1
      count += 1
    elsif result == 2
      # 時間制限の場合は処理終了
      break
    else
      # 連続投稿等の場合は次のルームへスキップ
      next
    end
        
    break if count == collect_num
  }
end

# ツイートの実施
private def to_tweet(room_url_key, main_name)
  result = 0
  
   # 配信ルームを開く
  @session.visit(URL + room_url_key)

  # ツイートボタン押下(ルームがオフラインの時は例外発生)
  begin
    @session.find(:xpath, '//a[@id="icon-room-twitter-post"]').click
    
    # TwitterのFFへのTLに表示させないようトスツイにする
    doc = Nokogiri::HTML.parse(@session.html, nil, "UTF-8")
    @session.fill_in('twitter-textarea',  with: "@tos " \
      + doc.xpath('//textarea[@id="twitter-textarea"]').text)

    @session.click_button('ツイート')
  rescue Capybara::ElementNotFound => ex
  else
    puts "\n" + main_name
 
    # レスポンスメッセージを取得し、表示   
    if @session.has_xpath?('//div[@class="toast-message"]')
      doc = Nokogiri::HTML.parse(@session.html, nil, "UTF-8")
      message   = doc.xpath('//div[@class="toast-message"]').text
      
      puts message
      
      if ! doc.xpath('//div[@class="bonus-title"]').empty?
        # ツイートボーナス取得成功
        puts doc.xpath('//div[@class="bonus-title"]').text
        result = 1
      elsif message =~  /無料ギフトの獲得は\d{2}:\d{2}まで制限されています/
        # 時間制限
        result = 2
      end
    end
  end 

  result
end

input_param  = accept_input
exit if input_param == nil
 
access
exit if login == false    

get_point(input_param[0], input_param[1])

# セッションを閉じる
@session.reset!
@session.driver.quit
```

#プログラムの実行
```file:macOS
$ bundle exec ruby collect_star.rb
星集めの場合「1」種集めの場合は「2」を入力して下さい
> 1
いくつのルームから集めますか？(1〜20まで)
> 20
```

標準入力で「星集めor種集め」の選択、取得対象となるルーム数を指定します。

```file:macOS
凡ヲタクの凡配信部屋
twitterへ投稿しました!
ツイートボーナスGET!

ガチ恋拗らせヲタクの限界配信部屋
無料ギフトの獲得は00:08まで制限されています
```

アクセスしたルーム名とレスポンスメッセージが標準出力されます。
取得制限時間を超えた場合は処理を終了します。


#まとめ
だいぶやっつけ記事ですが備忘録も兼ねてアップしてみました。
手探りな状態で作成したためいろいろと目に余る場合はご容赦下さい笑