雨の日に傘を持つよう通知するLINEbotを作ってみた

#きっかけ
「あ〜、今日雨なら傘を持って来れば良かった。」と思う時、ありませんか？
私は稀によくあります。

天気予報は毎朝確認しなくとも、雨の日だけ分かれば。
ということで、雨の日には「傘を持てよ！」と通知してくれるbotを、毎朝必ずと言って良いほど確認するLINEで作ってみました。

#概要
##実装する機能
以下の２点を機能として実装します。

　・傘が必要な日のみ、毎朝6:00〜7:00の間に天気予報をLINEにて通知する。
　・予報内容は、当日9:00〜21:00までの3時間刻みの天気情報とする。


##使用するサービス
今回使用するサービスは以下の３点です。

　・LINE - Messaging API
　　　https://developers.line.me/ja/services/messaging-api/
　・Google Apps Script（GAS）
　　　https://developers.google.com/apps-script/
　・OpenWeatherMap - Weather API
　　　https://openweathermap.org

##システムの全体像
<img width="911" alt="スクリーンショット 2018-04-25 0.38.53.png" src="https://qiita-image-store.s3.amazonaws.com/0/221691/80671e23-b231-d2b9-665e-2687c2eed877.png">

処理は、① → ② → ③ という流れで進みます。

①・・・毎朝6:00〜7:00の間にGASが実行され、Weather API（OpenWeatherMap）より天気予報を取得する。
②・・・Weather APIより天気予報が返ってくる。
③・・・LINE送信用にメッセージを編集し、Messaging API（LINE）に送信する。

#各種サービスの事前準備
##Weather APIの登録
下記のサイトでFreeプランで登録します。（「Get API key and Start」を押下)

https://openweathermap.org/price
<img width="517" alt="スクリーンショット 2018-04-25 1.40.03.png" src="https://qiita-image-store.s3.amazonaws.com/0/221691/c98796dc-e0d3-1309-88b0-db22b1bc6d19.png">

登録後、取得したAPIkeyをメモります。


##GASの登録
GASはgoogleのアカウントがあれば利用可能です。
以下、利用方法の参考記事です。スクリプトファイルの新規作成まで実施してください。

http://ascii.jp/elem/000/000/871/871501/

##LINE developersの登録
Messaging APIを利用するにあたり、LINE developersへの登録が必要になります。
以下の記事を参考に、Channelの設定まで実施してください。（プランは「Developer Trial」）

https://qiita.com/nkjm/items/38808bbc97d6927837cd


#実装

##コード全体
実装に移ります。以下、GASのコードの全てです。（GAS以外のコーディングは不要です。）
詳細は後ほど小分けで説明します。

```Javascript
//諸々の設定
var channel_access_token = 'LINEdevelopersのChannelより取得'
var user_id = 'LINEからuserIdを取得'
var line_url = 'https://api.line.me/v2/bot/message/push'
var openweathermap_url = 'http://api.openweathermap.org/data/2.5/forecast?id=1850147' //idで東京を指定
var openweathermap_appid = 'Weather APIで取得したAPIkey'
var text
var text_jp = []
var rain_info
var final_text

//毎日AM6:00〜7:00に以下のメソッドを起動
function weatherforecast() {
  //openweathermapから東京の天気予報を取得
  var weatherforecast_finalurl = openweathermap_url + '&APPID=' + openweathermap_appid
  var response = UrlFetchApp.fetch(weatherforecast_finalurl)
  var json = [JSON.parse(response.getContentText())]
  Logger.log(json[0]) //意図した場所の天気が取得できているか確認
  
  //天気情報を日本語に変換
  for (var i = 0; i <= 4; i++) {
    text = JSON.stringify((json[0].list[3 + i].weather[0].icon))
    Logger.log(json[0].list[3 + i]) //意図した時間の天気が取得できているか確認
    start_weatherforecast(text)
    text_jp[i] = text
  }
  //天気情報をline送信用に編集する
  text_edit()
  //lineへ送信する
  weatherforecast_to_line(channel_access_token,user_id,final_text);
}

//天気情報を日本語に変換
function start_weatherforecast(weather) {
  if (weather == '"01n"' || weather == '"01d"'){
    text = '快晴'
  }
  if (weather == '"02n"' || weather == '"02d"'){
    text = '晴れ'
  }
  if (weather == '"03n"' || weather == '"03d"'){
    text = '曇り'
  }
  if (weather == '"04n"' || weather == '"04d"'){
    text = '曇り'
  }
  if (weather == '"09n"' || weather == '"09d"'){
    text = '小雨'
    rain_info = '今日は傘を持ちましょう。\n'
  }
  if (weather == '"10n"' || weather == '"10d"'){
    text = '雨'
    rain_info = '今日は傘を持ちましょう。\n'
  }
  if (weather == '"11n"' || weather == '"11d"'){
    text = '雷雨'
    rain_info = '今日は傘を持ちましょう。\n'
  }
  if (weather == '"13n"' || weather == '"13d"'){
    text = '雪'
    rain_info = '今日は傘を持ちましょう。\n'
  }
  if (weather == '"50n"' || weather == '"50d"'){
    text = '霧'
  }
}

//line送信用にテキストを編集
function text_edit() {
  final_text = '天気予報です。\n' + rain_info + '\n09:00  ' + text_jp[0] + '\n12:00  ' + text_jp[1] + '\n15:00  ' + text_jp[2] + '\n18:00  '
              + text_jp[3]+ '\n21:00  ' + text_jp[4] + '\n\n今日も一日頑張りましょう！'
}

//LINEへ送信
function weatherforecast_to_line(channel_access_token,user_id,text){
  if (rain_info == '今日は傘を持ちましょう。\n') {
    //LINEに取得結果を送る
    UrlFetchApp.fetch(line_url,{
      'headers': {
          'Content-Type': 'application/json; charset=UTF-8',
          'Authorization': 'Bearer ' + channel_access_token,
      },
      'method': 'post',
      'payload': JSON.stringify({
        'to': user_id,
        'messages' : [
          {
            'type':'text',
            'text':final_text,
          }
        ]
      })
    });
  }
}

```

##コード詳細の説明
###諸々の設定
まずは諸々の設定から説明します。

```Javascript

//諸々の設定
var channel_access_token = 'LINEdevelopersのChannelより取得'
var user_id = 'LINEからuserIdを取得'
var line_url = 'https://api.line.me/v2/bot/message/push'
var openweathermap_url = 'http://api.openweathermap.org/data/2.5/forecast?id=1850147' //idで東京を指定
var openweathermap_appid = 'Weather APIで取得したAPIkey'
var text
var text_jp = []
var rain_info
var final_text

```

❶ 「channel_accsess_token」の宣言
LINE developersのChannel画面で取得します。

❷ 「user_id」の宣言
LINEからpostされたときに一緒に送信される情報で、pushメッセージを送信するときに必要になります。user_idはLINEからのメッセージに含まれているため、LINEからのメッセージに対してuser_idを返すプログラムを作成して取得しました。（もっと効率の良い取得方法があるかと思います。。）
LINEからのメッセージをおうむ返しするという以下の記事を参考に、user_idを返すプログラムを作成できます。
https://qiita.com/nogizakaJinro/items/e2a178c1cb7a0699a80f

❸ 「openweathermap_url」の宣言
3時間ごとの天気予報が取得できるurlの一部です。"id=〜"の部分を変更することで、特定の都市の天気予報を取得できます。天気予報を都市名で取得するなど、取得方法は色々ありますので、詳しくは公式サイトをご参照ください。

❹ 「openweathermap_appid」の宣言
weather API登録時にメモしたAPIkeyを設定します。openweathermap_urlに結合することで、実際に天気予報を取得することができるurlが完成します。

❺ その他変数の宣言
後ほど使用しますので、ここでは説明を割愛します。

###メインメソッド
次に、毎朝起動するメインメソッドを説明します。

```Javascript

//毎日AM6:00〜7:00に以下のメソッドを起動
function weatherforecast() {
  //openweathermapから東京の天気予報を取得
  var weatherforecast_finalurl = openweathermap_url + '&APPID=' + openweathermap_appid
  var response = UrlFetchApp.fetch(weatherforecast_finalurl)
  var json = [JSON.parse(response.getContentText())]
  Logger.log(json[0]) //意図した場所の天気が取得できているか確認
  
  //天気情報を日本語に変換
  for (var i = 0; i <= 4; i++) {
    text = JSON.stringify((json[0].list[3 + i].weather[0].icon))
    Logger.log(json[0].list[3 + i]) //意図した時間の天気が取得できているか確認
    start_weatherforecast(text)
    text_jp[i] = text
  }
  //天気情報をline送信用に編集する
  text_edit()
  //lineへ送信する
  weatherforecast_to_line(channel_access_token,user_id,final_text);
}
```
❶ 天気予報取得用のurl作成
諸々の設定で宣言した「openweathermap_url」と「openweathermap_appid」を組み合わせて、天気予報取得用のurl「weatherforecast_finalurl」を作成します。

❷ 天気予報の取得
「UrlFetchApp.fetch」を使用し、「weatherforecast_finalurl」より取得した天気予報をjson形式に変換し、「json」に格納します。

❸取得した天気予報から必要な情報を抜き取る
欲しい情報は「6:00〜7:00の間で天気予報を取得した時の、9:00・12:00・15:00・18:00・21:00時点の天気5つ」です。以下のページで各天気のコードを確認します。
https://openweathermap.org/weather-conditions

ログを残す「Logger.log(data)」を使用して「json」の中身を確認し、天気のコードを探します。どうやら天気のコードは「json[0].list[3〜７].weather[0].icon」に格納されているようなので、for文で各時間帯の天気ごとに処理を実行します。
for文の中では取得した天気の情報を日本語に変換し、配列である「text_jp」に格納します。（日本語への変換は「start_weatherforecast(text)」で実施。中身は後ほど説明します。）

❹天気情報をLINE送信用に編集
「text_edit()」を呼び出し、LINE送信用のメッセージを作成します。（中身は後ほど説明します。）

❺LINEにメッセージを送信
「weatherforecast_to_line(channel_access_token,user_id,final_text)」を呼び出し、LINEにメッセージを送信します。（中身は後ほど説明します。）

❻メインメソッドの自動起動を設定
以下の記事を参考に、毎日AM6:00〜7:00の間に自動で起動するよう設定します。
https://tonari-it.com/gas-trigger-set/

###天気コードの日本語変換
次に、メインメソッドの❸で呼び出したメソッド（start_weatherforecast(text)）を説明します。

```Javascript
//天気情報を日本語に変換
function start_weatherforecast(weather) {
  if (weather == '"01n"' || weather == '"01d"'){
    text = '快晴'
  }
  if (weather == '"02n"' || weather == '"02d"'){
    text = '晴れ'
  }
  if (weather == '"03n"' || weather == '"03d"'){
    text = '曇り'
  }
  if (weather == '"04n"' || weather == '"04d"'){
    text = '曇り'
  }
　 if (weather == '"09n"' || weather == '"09d"'){
    text = '小雨'
    rain_info = '今日は傘を持ちましょう。\n'
  }
  if (weather == '"10n"' || weather == '"10d"'){
    text = '雨'
    rain_info = '今日は傘を持ちましょう。\n'
  }
  if (weather == '"11n"' || weather == '"11d"'){
    text = '雷雨'
    rain_info = '今日は傘を持ちましょう。\n'
  }
  if (weather == '"13n"' || weather == '"13d"'){
    text = '雪'
    rain_info = '今日は傘を持ちましょう。\n'
  }
  if (weather == '"50n"' || weather == '"50d"'){
    text = '霧'
  }
}
```

「text」に格納された天気コード（"01n"など）を判断し、日本語に変換します。また、雨・雪が振る場合は"傘を持ちましょう。"という文言を「rain_info」に格納します。（理由は後ほど説明します。）

###天気情報をLINE送信用に編集
次に、メインメソッドの❹で呼び出したメソッド（text_edit()）の説明です。

```Javascript
//line送信用にテキストを編集
function text_edit() {
  final_text = '天気予報です。\n' + rain_info + '\n09:00  ' + text_jp[0] + '\n12:00  ' + text_jp[1] + '\n15:00  ' + text_jp[2] + '\n18:00  '
              + text_jp[3]+ '\n21:00  ' + text_jp[4] + '\n\n今日も一日頑張りましょう！'
}
```

日本語に変換した天気情報（text_jp）を使用してLINE送信用のメッセージを編集し、「final_text」に格納します。なお、”\n”は改行を意味します。

###LINEにメッセージを送信
最後に、メインメソッドの❺で呼び出したメソッド（weatherforecast_to_line(channel_access_token,user_id,final_text)）の説明です。

```Javascript
//LINEへ送信
function weatherforecast_to_line(channel_access_token,user_id,text){
  if (rain_info == '今日は傘を持ちましょう。\n') {
    //LINEに取得結果を送る
    UrlFetchApp.fetch(line_url,{
      'headers': {
          'Content-Type': 'application/json; charset=UTF-8',
          'Authorization': 'Bearer ' + channel_access_token,
      },
      'method': 'post',
      'payload': JSON.stringify({
        'to': user_id,
        'messages' : [
          {
            'type':'text',
            'text':final_text,
          }
        ]
      })
    });
  }
}
```

以下のMessaging APIのAPIリファレンスより、プッシュメッセージの送信に必要な情報を確認し、実装しています。
https://developers.line.me/ja/docs/messaging-api/reference/
　→「プッシュメッセージを送る」を参照

「if (rain_info == '今日は傘を持ちましょう。\n')」で傘が必要な場合だけLINEに送信するようにしていますが、このif文を外せば毎朝天気予報をLINEに送信するようになります。（雨が振る場合だけ「rain_info」に格納していたのはこのためです。）

#テスト
さて、実装が完了したので、意図した通りに動くかテストします。
8:00にラインを確認すると、以下の通知がありました。

<img width="322" alt="スクリーンショット 2018-04-25 22.46.08.png" src="https://qiita-image-store.s3.amazonaws.com/0/221691/d8b4cc50-5143-621e-98b6-d6485c3b885a.png">

なんとか無事に天気予報をpush送信することができたようです。
これにて傘通知bot、完成となります。

#作り終えてみての感想
小さなシステムではありますが、プログラミング初心者、かつ、使用した３つのサービス全てが初めてであったため、作り終えるのに苦戦しました。（なんだかんだで着手から１週間ほどかかりました。）

そんな苦戦の中で以下の２点を痛感しています。

・何かしら実現したいものを考え、実現に向けて思考することで体系的な知識が身につく。
・プログラミングはワクワクの塊。

ということで、引き続き高いモチベーションをキープして学習したいと思います。
お読みいただき、ありがとうございました。

※記事にミスや改善点等ありましたら、ご指摘いただけると幸いです。