RubyでTwitterBotを作って天気予報をツイートする

#はじめに
WebAPIとRubyの勉強を目的に、TwitterBotを作りました。

WebアプリとしてHerokuにデプロイし、HerokuSchedulerを用いて、毎日7:30に東京の天気を画像付きでツイートします。

https://twitter.com/OzoraOtenkiBot

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1009493/e36763b5-83bf-5331-44ee-191c77994951.png)

#背景
女児向けアニメの『アイカツ！』という作品内で、主人公の大空あかりちゃんがお天気キャスターとして活躍するのですが、それをBotとして作った次第です。
朝に東京の天気もわかるし、推しの画像も見られるので一石二鳥です。

#TwitterBotの導入
下記の記事を参考にしました。
・[導入記事１](https://b0npu.hatenablog.com/entry/2016/06/15/230101)
・[導入記事２](https://qiita.com/shimisunet/items/c3a0b93fb13fa82ed8be)

#インストールしたGem
```Gemfile
source 'https://rubygems.org'
ruby '2.5.1'

gem 'sinatra'
gem 'twitter'    //TwitterAPI通信用
gem 'httpclient' //WebAPI通信用
gem 'dotenv'     //環境変数設定用
```

#TwitterBotのコード
###ディレクトリの構成
(一部抜粋)

```
~twitter_bot
   |- images 　　　　　　 // ツイートに添付する画像フォルダ
      |- otenki0.jpg
      |- ...
      |- otenki22.jpg    // 23枚ほど用意
   |- src                // 天気情報を取得するフォルダ
      |- Weather.rb      // API接続からデータ加工処理を行うファイル
      |- WeatherInfo.rb　// Weather.rbの関数ファイル
   |- .env               // TwitterAPIのコンシューマーキーやアクセストークンを格納
   |- app.rb             // Webアプリのメインファイル
   |- config.ru          // Webアプリの設定ファイル
   |- Gemfile            // 使用するgemを管理するファイル
   |- tweet.rb 　　　　 　// ツイート処理ファイル
```

###tweet.rb
ツイート処理ファイルです。
画像を添付してツイートするのに加えて、srcフォルダから天気情報を取得しています。

.envに書いたキーとトークンをENV['hoge_hoge']で呼び出しています。

```tweet.rb
require 'date'
require 'twitter'
require 'dotenv/load'

require "./src/Weather"
require "./src/WeatherInfo"

class Tweet  
  def initialize
    # 投稿内容の初期化
    @text = ""
    # クライアントの生成
    @client = Twitter::REST::Client.new do |config|
      config.consumer_key        = ENV['CONSUMER_KEY']
      config.consumer_secret     = ENV['CONSUMER_SECRET']
      config.access_token        = ENV['ACCESS_TOKEN']
      config.access_token_secret = ENV['ACCESS_TOKEN_SECRET']
    end
  end

  # Tweetの投稿処理呼び出し
  def send_tweet
    create_text
    update
  end

  # ツイート本文の生成
  def create_text
    # 天気情報取得
    weatherobj = Weather.new
    info = weatherobj.doProcess()

    # ツイート部分
    @text = "みなさんおはようございます！時刻は7時30分！\n"
    @text += "今日のお空はどんな空～❓\n大空お天気の時間です✨\n"
    @text += "今日の都心部は#{info.todayTelop()}、最高気温は#{info.todayTempMax()}℃です！\n"
    @text += "それでは通勤・通学気をつけて、行ってらっしゃ～い！"
  end

  private

  # Tweet投稿処理
  def update
    begin
      # 画像添付部
      images = []

      range = 0..22
      range.each{|i|    
        images << File.new("./images/otenki#{i}.jpg")
      }
      
      @client.update_with_media(@text,images.sample)
    rescue => e
      p e # エラー時はログを出力
    end
  end
end

# ツイートを実行
if __FILE__ == $0
  Tweet.new.send_tweet
end
```

###srcフォルダ
[天気予報API](https://weather.tsukumijima.net/)から東京の天気をJSON形式で取得します。
HTTPClientライブラリを用いることで、外部へHTTPリクエストを送ることができます。

・Weather.rb: API接続からデータ加工処理を行うクラス
・WeatherInfo.rb: Weather.rbの関数ファイル

```Weather.rb
# API接続からデータ加工処理を行うクラス
require 'httpclient'
require 'resolv'
require 'json'
require './src/WeatherInfo'

class Weather

  @@DESCRIPTION = "description"
  @@TEXT = "text"
  @@FORECASTS = "forecasts"
  @@TELOP = "telop"
  @@DATE = "date"
  @@TEMPERATURE = "temperature"
  @@CELSIUS = "celsius"
  @@MIN = "min"
  @@MAX = "max"
  @@TODAY = 0
  @@TMRW = 1

  # コンストラクタ
  def initialize()
  end

  # メイン処理メソッド
  def doProcess()
    # 拠点コード(東京)
    keyWord = '130010'
    # 天気API URL取得
    url = 'https://weather.tsukumijima.net/api/forecast'
    return analysisWeather(connectionAPI(keyWord, url))
  end

  # API接続部
  # 戻り値:ハッシュ化されたレスポンス
  def connectionAPI(keyWord, url)
    # http接続クライアントの生成
    client = HTTPClient.new
    # 指定した拠点のコードをリクエストに設定する。
    query = { 'city' => keyWord }
    # APIリクエスト
    res = client.get(url, query)
    # ハッシュ化して返却
    return JSON.parse(res.body)
  end

  # 天気情報をHashより解析する
  def analysisWeather(hash)

    info = WeatherInfo.new

    # 概要の取得
    info.description=(convertNil(hash.dig(@@DESCRIPTION, @@TEXT)))

    # 今日の天気情報
    info.todayTelop=(convertNil(hash.dig(@@FORECASTS, @@TODAY, @@TELOP)))
    info.today=(convertNil(hash.dig(@@FORECASTS, @@TODAY, @@DATE)))
    info.todayTempMin=(convertNil(hash.dig(@@FORECASTS, @@TODAY, @@TEMPERATURE, @@MIN, @@CELSIUS)))
    info.todayTempMax=(convertNil(hash.dig(@@FORECASTS, @@TODAY, @@TEMPERATURE, @@MAX, @@CELSIUS)))

    # 明日の天気情報
    info.tmrwTelop=(convertNil(hash.dig(@@FORECASTS, @@TMRW, @@TELOP)))
    info.tmrw=(convertNil(hash.dig(@@FORECASTS, @@TMRW, @@DATE)))
    info.tmrwTempMin=(convertNil(hash.dig(@@FORECASTS, @@TMRW, @@TEMPERATURE, @@MIN, @@CELSIUS)))
    info.tmrwTempMax=(convertNil(hash.dig(@@FORECASTS, @@TMRW, @@TEMPERATURE, @@MAX, @@CELSIUS)))

    return info
  end

  # nil判定、nilの場合は「-」を返却
  def convertNil(value)
    return value == nil ? "－" : value
  end
end
```

```WeatherInfo.rb
class WeatherInfo
  attr_accessor :description,
  :todayTelop,
  :today,
  :todayTempMin,
  :todayTempMax,
  :tmrwTelop,
  :tmrw,
  :tmrwTempMin,
  :tmrwTempMax
  :comment

  # コンストラクタ
  def initialize()
  end

  # 概要
  def description()
    @description
  end

  # 概要
  def description=(value)
    @description = value
  end

  # 本日テロップ
  def todayTelop()
    @todayTelop
  end

  # 本日テロップ
  def todayTelop=(value)
    @todayTelop = value
  end

  # 本日最低気温
  def todayTempMin()
    @todayTempMin
  end

  # 本日最低気温
  def todayTempMin=(value)
    @todayTempMin = value
  end

  # 本日最高気温
  def todayTempMax()
    @todayTempMax
  end

  # 本日最高気温
  def todayTempMax=(value)
    @todayTempMax = value
  end

  # 明日テロップ
  def tmrwTelop()
    @tmrwTelop
  end

  # 明日テロップ
  def tmrwTelop=(value)
    @tmrwTelop = value
  end

  # 明日最低気温
  def tmrwTempMin()
    @tmrwTempMin
  end

  # 明日最低気温
  def tmrwTempMin=(value)
    @tmrwTempMin = value
  end

  # 明日最高気温
  def tmrwTempMax()
    @tmrwTempMax
  end

  # 明日最高気温
  def tmrwTempMax=(value)
    @tmrwTempMax = value
  end

  # コメント
  def comment()
    @comment
  end

  # 明日最高気温
  def comment=(value)
    @comment = value
  end

end
```

###app.rb
Webアプリのメインファイルです。
TwitterBotをHerokuで動かすためにSinatraでWebアプリにしています。
/send_tweetを実行でツイートされます。

```app.rb
require 'sinatra'
require_relative 'tweet.rb'

get '/' do
  'under constraction'
end

get '/send_tweet' do
  'Closed'
end
```

###config.ru
Sinatraということを認識させる記述を行っています。

```config.ru
require_relative 'app.rb'
run Sinatra::Application
```

###imagesフォルダ
添付する画像フォルダ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1009493/dd047790-39f2-3ed1-9218-a6f5465367dc.png)

#HerokuSchedulerで定期的に動かす
Herokuにデプロイし、Schedulerアドオンを使って毎日7:30に動くようにします。
コマンドの欄は、bundle exec ruby tweet.rb
[参考記事](https://trap.jp/post/326/)

またHerokuにデプロイ後、Heroku側でも環境変数の設定が必要とのことで設定します。
[参考記事](https://e-tec-memo.herokuapp.com/article/277/)

#さいごに
以上が、天気情報を毎日ツイートするTwitterBotの制作過程でした。
TwitterBotの制作記事はたくさんあるので、参考記事には困りませんでした。
WebAPIを使うのは初めてでしたが、特に問題もなく導入できたので良かったと思います。

#参考
・TwitterBot

https://qiita.com/shimisunet/items/c3a0b93fb13fa82ed8be

https://b0npu.hatenablog.com/entry/2016/06/15/230101

https://trap.jp/post/326/

・天気情報取得方法

https://qiita.com/Co_Pilot/items/02255167813013dd4075

http://ottosan84.hatenablog.com/entry/2016/02/26/005731


・HTTPClient

https://qiita.com/terufumi1122/items/b86230448793ad9e8fd6

・Heroku 環境変数

https://e-tec-memo.herokuapp.com/article/277/