TwitterのAPIを使った天気予報botを作成してみよう

Ruby

こんにちは、kazutoです。今回は、TwitterのAPIを使ってTwitter botを作成していきます。

※なお「天気予報APIを用いて5日間の天気情報を取得しよう」でプログラムを作成している方が対象です。もしプログラムを作成していない方は、お手数ですが一度、対象の記事を閲覧しプログラムを作成してから「TwitterのAPIを使った天気予報botを作成してみよう」をご覧ください。

天気予報APIを用いて5日間の天気情報を取得しよう

事前準備

まずは、事前準備をしていきましょう。

  • ディレクトリ構成を整える
  • Twitter APIに登録

ディレクトリ構成を整える

ディレクトリ構成については、「天気予報APIを用いて5日間の天気情報を取得しよう」でほぼ作成しているので、

  • tweet.rbファイルを付け足す
  • gemをインストール

のみで大丈夫です。

では、早速、Linuxコマンドを用いて開発環境を整えていきましょう。

touch tweet.rb

ファイルが作成できたか、確認をしたい方は、

ls 
  • テキストエディタで確認する
  • lsコマンドで確認する

を行ってください。

ファイルが作成できているのが確認できた方は、ご自身の開発環境で、botに必要なgemをインストールをします。gemとは、RubyGemsが公開しているRubyのパッケージのことを指します。要するにRubyのライブラリです。gemを用いる事により、複雑な機能も簡単に実装可能になります。

今回は、twitterというgemをインストールを行っていきます。開発環境のディレクトリで下記のコマンドを打ちましょう。

gem install twitter

※インストール中は、パソコンを触らずに待ちましょう。

インストールが終わったら、正しくgemがインストールをされたかを確認をするためにgemコマンドを打って確認しましょう。

gem search twitter

色々とgemの名前が出てきたらインストールが成功になります。

Twitter APIに登録

まずは、任意なアカウントを作成してください。(アカウント名はなんでも良いです。)

アカウントが作成できたら、下記のリンクを参照してください。下記の画像のページに飛ぶはずです。

https://developer.twitter.com/en

[手順1]

Productsにマウスカーソルを当てるとメニューが表示され、その中のtwitter APIをクリックし、ページが遷移したら、

  1. 「Apply for access」をクリックして申請ページに飛ぶ。
  2. 「Apply for a developer account」クリック、開発アカウントページに飛ぶ。

[手順2]

Tweeter botを作成したいので「Making a bot 」をクリック。その後、入力フォームがありますので、全て入力して申請してください。申請後、しばらくすると登録したメールアドレス宛てに申請結果が届くと思いますので、メールが届くのをお待ちください。

以上で「事前準備」のトピックは終わりです。次のトピックに進む前に必ず申請結果を確認しておきましょう。万が一、申請が通らなかった場合は、再度、申請しましょう。

環境変数の設定

続いて、環境変数の設定をしましょう。まずはこちらにリンクにアクセスしてください。

https://developer.twitter.com/en

「Developer Portal」をクリックしてダッシュボードにアクセスしましょう。ページに遷移ができたら、まず初めに「 PROJECT APP」の項目に作成したプロジェクトがあるか確認をしましょう。

その後、カギアイコンをクリックして「Keys and tokens」に遷移しましょう。このページにてAPIKEYなどを生成していきます。

では、vimで環境変数設定をしていきましょう。

vim ~/.zshrc
export CONSUMER_KEY          ='(API key)を入力します'
export CONSUMER_SECRET     ='(API key secret)を入力します'
export ACCESS_TOKEN           ='(Access token)を入力します'
export ACCESS_TOKEN_SECRET    ='(Access token secret)を入力します'

上記のAPIKEYなどを生成をして、vimを用いてzshrcファイルに記述していきましょう。

zshrcファイル内に環境変数の設定ができましたら、config.rbファイルで環境変数を読み込みましょう。

CONSUMER_KEY          =  ENV['CONSUMER_KEY']
CONSUMER_SECRET       =  ENV['CONSUMER_SECRET']
ACCESS_TOKEN          =  ENV['ACCESS_TOKEN']
ACCESS_TOKEN_SECRET   =  ENV['ACCESS_TOKEN_SECRET']

環境変数を定数に格納できましたら、念のために正しく値を格納されているかデバックしてみましょう。

[CONSUMER_KEY,CONSUMER_SECRET,ACCESS_TOKEN,ACCESS_TOKEN_SECRET ].each do |value|
  puts value
end

環境変数に格納されている値と一致しているのが確認できたら次のトピックに進みましょう。

TwitterのAPIを使った天気予報botを作成してみよう[実践編]

それでは、実際に天気予報botを作成していきましょう。

  • Wetaherクラスを実装しよう
  • Tweetクラスを実装しよう
  • execution.rbで実行処理を実装しよう

Wetaherクラスを実装しよう

以前、「天気予報APIを用いて5日間の天気情報を取得しよう」で5日間の天気予報を取得してくるプログラムを作成しましたが、今回は、あくまでも、ツイッターで天気予報をツイートするという動作を実現したいので、既存のプログラムのままですと、ツイートできる文字数(140)の制限があるツイッターでは相性が悪いです。

したがって、現在時刻を取得し、観測時間を評価して一番、現在時刻に近い観測時間の天気情報を取得するという挙動に変更をすると、ツイートできる文字数に制限があるツイッターに上手く機能を組み込む事ができるはずです。なので、新たにcurrent_time_weatherメソッドを作成します。

class Wetaher
  def current_time_weather(wethers)

    wethers[:list].map{|wether|wether['dt_txt'] = Time.parse(wether['dt_txt'])}
       current_weather  = wethers[:list].select{|wether|wether['dt_txt']>=Time.now}.first
       dt_txt = current_weather['dt_txt'].strftime('%Y年%m月%d日 %H時%M分%S秒' )

       data =  {
         city:wethers[:city_name] ,
         dt_txt:  dt_txt,
         description:current_weather['weather'][0]['description'],
         temperature: current_weather['main']['temp'],
        }
   end
end

こちらのソースコードがcurrent_time_weatherメソッドになります。最終的には、変数dataを戻り値として返します。

wethers[:list].map{|wether|wether['dt_txt'] = Time.parse(wether['dt_txt'])}

まず、初めに5日間の天気情報がハッシュ上で格納されている、wethers[:list]に対して、mapメソッドを用いて、[‘dt_txt’]に格納されている観測時間をString型からparseメソッドを用いて、Time型に変更しています。

この作業を行う理由は、比較演算子を用いたいからです。String型のままだと比較演算子が使えないので、Time型に型変換をしています。比較演算子を用いて、現在時刻より前の観測時間を省き、範囲を絞り込みデータを整形していきます。

  current_weather  = wethers[:list].select{|wether|wether['dt_txt']>=Time.now}.first

こちらは、selectメソッドを用いて、現在時刻を取得し、観測時間を評価して一番、現在時刻に近い観測時間の天気情報を取得し返り値をcurrent_weatherに格納するという事を行っております。

selectメソッドは、返り値として、条件式に一致した要素を配列で返します。なので、メソッドチェーンを組んでfirstメソッドを用いる事で、現在時刻に一番近い観測時間の天気情報を取得する事ができます。

 dt_txt = current_weather['dt_txt'].strftime('%Y年%m月%d日 %H時%M分%S秒' )

こちらは、先ほどTime型に変換した気象時間のデータを再度、strftimeメソッドを用いて、String型に型変換をして、「✖︎✖︎年✖︎月✖︎日 ✖︎時✖︎分✖︎秒」と日本語の表記にしています。
最終的に整形されたデータを変数dt_txtに格納します。

 data =  {
         city:wethers[:city_name] ,
         dt_txt:  dt_txt,
         description:current_weather['weather'][0]['description'],
         temperature: current_weather['main']['temp'],
        }

current_time_weatherメソッドの返り値は、ハッシュ形式のデータを格納している変数dataを返します。tweetクラスを実装する際に、変数dataの値を取得するので、このタイミングで、 どのキーに、何のバリューが、入っているのかを確認をしておきましょう。

  • city
    →都市名
  • dt_txt
    →観測した時間
  • description
    →天気の詳細
  • temperature
    →気温

となります。

以上で、Wetaherクラスの実装を終了します。current_time_weatherメソッドは天気予報botを作成するにあたり、とても大切なメソッドです。なので処理内容については理解をしておいてください。

Tweetクラスを実装しよう

それでは、Tweetクラスを実装して、実際に取得した天気情報をツイートしていきましょう。

require 'twitter'
require './config.rb'

class Tweet

  def initialize(current_time_weather)
    @client = Twitter::REST::Client.new do |config|
      config.consumer_key        =  CONSUMER_KEY
      config.consumer_secret     =  CONSUMER_SECRET
      config.access_token        =  ACCESS_TOKEN
      config.access_token_secret =  ACCESS_TOKEN_SECRET
    end
    @current_time_weather =  current_time_weather
  end

  def updata
    text = "#{@current_time_weather[:dt_txt]}\n#{@current_time_weather[:city]}の天気\n天気:#{@current_time_weather[:description]}\n気温:#{@current_time_weather[:temperature]}"
    @client.update(text)
  end

end

ソースコードが長いのでメソッド事に解説をしていきます。、抜粋して解説していきます。

まずは、initializeメソッドについて解説していきます。

def initialize(current_time_weather)
    @client = Twitter::REST::Client.new do |config|
      config.consumer_key        =  CONSUMER_KEY
      config.consumer_secret     =  CONSUMER_SECRET
      config.access_token        =  ACCESS_TOKEN
      config.access_token_secret =  ACCESS_TOKEN_SECRET
    end
    @current_time_weather =  current_time_weather
  end

initializeメソッドは、オブジェクトを初期化をするメソッドです。なのでインスタンス生成時にインスタンス自身に持たせておきたい、属性(インスタンス変数)を定義をしていきます。

@client = Twitter::REST::Client.new do |config|
      config.consumer_key        =  CONSUMER_KEY
      config.consumer_secret     =  CONSUMER_SECRET
      config.access_token        =  ACCESS_TOKEN
      config.access_token_secret =  ACCESS_TOKEN_SECRET
    end

Twitter::REST::Clientクラスのインスタンスを生成をしています。

@clientの中では、コンシューマーキーとアクセストークンを設定しています。簡単に解説すると、外部のアプリから対象のツイッターアカウントで

  • ツイート
  • いいね
  • フォロー
  • プロフィール設定

などを行う権限を取得しています。

コンシューマーキーとはコンシューマーを利用するために必要な鍵で、コンシューマーとは、「消費者」という意味で、サービスを受ける人を指します。まとめると、Twitter内にはコンシューマ(アプリケーション)というサービスがあり、コンシューマキー を取得する事により、コンシューマ(消費者)に対して新しいサービスを提供する事が可能になるという事ですね。

アクセストークンとは、Webサービスを利用するためにサーバーがユーザーを認証するために払い出した認証情報です。まあ、ユーザ認証をするときに必要な、ユーザーを識別するための文字列と覚えておきましょう。

 @current_time_weather =  current_time_weather

こちらは先程、Wetherクラス内で作成したcurrent_time_weatherメソッドの返り値をインスタンス変数@current_time_weatherの値の中に格納しています。インスタンス変数に定義をする事で円滑にツイートができる様になります。

以上でinitializeメソッドの解説を終了します。ユーザの権限を取得する処理などが入ってきたため、難しく感じると思いますが、要点を抑え、まずは大枠を理解していきましょう。

続いてupdataメソッドの解説をしていきます。

def updata
    text = "#{@current_time_weather[:dt_txt]}\n#{@current_time_weather[:city]}の天気\n天気:#{@current_time_weather[:description]}\n気温:#{@current_time_weather[:temperature]}"
    @client.update(text)
  end

updateメソッドは、ツイートするメソッドです。

 text = "#{@current_time_weather[:dt_txt]}\n#{@current_time_weather[:city]}の天気\n天気:#{@current_time_weather[:description]}\n気温:#{@current_time_weather[:temperature]}"

まず初めにツイートする文を作成し、textという変数内に格納します。ちなみに上記の文字列がどの様にツイッター上に表示されるか気になると思いますので、下記の画像をご覧ください。

上記の様に各項目事に改行が入っており、綺麗に表示がされている事が分かります。

 @client.update(text)

こちらのソースコードでツイートを行っております。ちなみに呼び出しているupdateメソッドはあくまでもTwitter::REST::Clientクラスのメソッドですので、僕が作成したupdateメソッドとは全く関係ないので注意してください。

なおTwitter::REST::Clientクラスにはどの様なメソッドがあるか知りたい方は、こちらのドキュメントでご確認ください。

https://www.rubydoc.info/gems/twitter/Twitter/REST/Client

以上で、Tweetクラスを実装を終了します。

execution.rbで実行処理を実装しよう

では、トピックの最後で、execution.rb(実行ファイル)に実行処理を実装していきましょう。

#末尾に追加
def wetaher
  citys=CSV.read("./citys.csv",headers: true)
  wether = Wetaher.new(citys)
  city=wether.select_city
  datas=wether.get_weather_forecast(city)
  wether.current_time_weather(datas)
end
#末尾に追加
def tweet(current_time_weather,num)
    tweet = Tweet.new(current_time_weather)
    tweet.updata()
    puts "ツイートが完了しました。"
 end
require "./config.rb"
require "json"
require "open-uri"
require "csv"
require "./weather_class.rb"
require "./tweet.rb"

def execution
    current_time_weather  =  wetaher()
    tweet(current_time_weather)
end

execution()

それでは、処理の解説をしていきます。

 def wetaher
  citys=CSV.read("./citys.csv",headers: true)
  wether = Wetaher.new(citys)
  city=wether.select_city
  datas=wether.get_weather_forecast(city)
  wether.current_time_weather(datas)
end
current_time_weather  =  wetaher()

まずは、wetaherメソッドを呼び出して、天気情報を取得して、返り値を変数current_time_weatherに格納します。

def tweet(current_time_weather,num)
    tweet = Tweet.new(current_time_weather)
    tweet.updata()
    puts "ツイートが完了しました。"
 end 
tweet(current_time_weather)

その後、変数current_time_weatherを引数に渡し、tweetメソッドを呼び出し、tweetというインスタンスを生成して、updateメソッドを呼び出す事でツイートが完了します。

以上で、execution.rbファイルの実装を終了します。

実際にツイートしてみよう

最後に、実際にツイートしてみましょう。その前に完成したソースコードを貼り付けておきますので、エラーが起きた際は参考にしてください。

WEATHER_API_KEY       =  ENV['WEATHER_API_KEY']
WEATHER_URL           =  ENV['WEATHER_URL']

CONSUMER_KEY          =  ENV['CONSUMER_KEY']
CONSUMER_SECRET       =  ENV['CONSUMER_SECRET']
ACCESS_TOKEN          =  ENV['ACCESS_TOKEN']
ACCESS_TOKEN_SECRET   =  ENV['ACCESS_TOKEN_SECRET']
id,city,english
1,東京都,Tokyo
2,大阪,Osaka
3,沖縄,Okinawa
4,北海道,Hokkaido
require 'twitter'
require './config.rb'
class Tweet

  def initialize(current_time_weather)
    @client = Twitter::REST::Client.new do |config|
      config.consumer_key        =  CONSUMER_KEY
      config.consumer_secret     =  CONSUMER_SECRET
      config.access_token        =  ACCESS_TOKEN
      config.access_token_secret =  ACCESS_TOKEN_SECRET
    end
    @current_time_weather =  current_time_weather
  end

  def updata
    text = "#{@current_time_weather[:dt_txt]}\n#{@current_time_weather[:city]}の天気\n天気:#{@current_time_weather[:description]}\n気温:#{@current_time_weather[:temperature]}"
    @client.update(text)
  end
end

def tweet(current_time_weather)
  tweet = Tweet.new(current_time_weather)
  tweet.updata()
  puts "ツイートが完了しました。"
end
require 'date'

  class Wetaher
    def initialize(citys)
      @citys=citys
    end

    def select_city
      @citys.each do |city|
        puts "#{city['id']}:#{city['city']}"
      end
      # puts "天気予報を確認したい都市を選択してください"
      puts "天気情報を取得したい都市を選択してください"
      input = gets.to_i-1
      @citys[input]
    end

    def get_weather_forecast(city)
      response = open(WEATHER_URL + "?q=#{city['english']}&appid=#{WEATHER_API_KEY}&units=metric&lang=ja")
      data=JSON.parse(response.read)
      json_to_parse(data['list'])
      datas= {list: data['list'],city_name:data['city']['name']}
    end

    def display_weather_forecast(weathers)
      puts "地区:#{weathers[:city_name]}"
      weathers[:list].each do|weather|
        puts "------------------------------"
        puts "時間#{weather['dt_txt']}"
        puts "天気:#{weather['weather'][0]['description']}"
        puts "気温:#{weather['main']['temp']}℃"
        puts "------------------------------"
      end
    end

    def json_to_parse(weathers)
      weather=JSON.pretty_generate(weathers)
      File.open("tenki.json", mode = "w"){|f|
        f.write(weather)
      }
    end

    def display_weather_text(weathers)
      File.open("tenki.txt", mode = "w"){|f|

        f.write(
          weathers[:list].each do|weather|
            puts "------------------------------"
            puts "時間#{weather['dt_txt']}"
            puts "天気:#{weather['weather'][0]['description']}"
            puts "気温:#{weather['main']['temp']}℃"
            puts "------------------------------"
          end
        )
      }
    end

    def current_time_weather(wethers)

        wethers[:list].map{|wether|wether['dt_txt']= Time.parse(wether['dt_txt'])}
        current_weather  = wethers[:list].select{|wether|wether['dt_txt']>=Time.now}.first
        dt_txt = current_weather['dt_txt'].strftime('%Y年%m月%d日 %H時%M分%S秒' )

        data =  {
          city:wethers[:city_name] ,
          dt_txt:  dt_txt,
          description:current_weather['weather'][0]['description'],
          temperature: current_weather['main']['temp'],
          icon:current_weather['weather'][0]['icon']
          }
    end

  end

  def wetaher
    citys=CSV.read("./citys.csv",headers: true)
    wether = Wetaher.new(citys)
    city=wether.select_city
    datas=wether.get_weather_forecast(city)
    wether.current_time_weather(datas)
  end
require "./config.rb"
require "json"
require "open-uri"
require "csv"
require "./weather_class.rb"
require "./tweet.rb"

def execution
  current_time_weather  =  wetaher()
  tweet(current_time_weather)
end

execution()

開発環境に移動して下記のコマンドを打ちましょう。

ruby execution.rb

「ツイートが完了できました」と表示されたら、ツイートができていると思いますので、ご自身のツイッターアカウントで確認してみてください。ちなみに下記の画像の様にツイートされているはずです。

まとめ:TwitterのAPIを使った天気予報botを作成してみよう

今回は、TwitterのAPIを使って天気予報botを作成してみました。ツイッターという誰もが知っているサービスのAPIを用いる事により、実用性の高いアプリを開発ができました。
ちなみに当ブログの「kazugramming」でもTwitter botを採用しています。SNS運用に取り入れる事ができるのでとても便利ですね。

今回は、ツイート機能のみの実装ですが、

  • いいね
  • フォロー
  • DM
  • サーチ(分析)

なども実装する事が可能です。

以上、kazutoでした。