api
GoogleAppsScript
OpenWeatherMap
linebot

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

きっかけ

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

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

概要

実装する機能

以下の2点を機能として実装します。

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

使用するサービス

今回使用するサービスは以下の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

システムの全体像

スクリーンショット 2018-04-25 0.38.53.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
スクリーンショット 2018-04-25 1.40.03.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以外のコーディングは不要です。)
詳細は後ほど小分けで説明します。

//諸々の設定
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,
          }
        ]
      })
    });
  }
}

コード詳細の説明

諸々の設定

まずは諸々の設定から説明します。

//諸々の設定
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が完成します。

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

メインメソッド

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

//毎日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〜7].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))を説明します。

//天気情報を日本語に変換
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())の説明です。

//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))の説明です。

//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にラインを確認すると、以下の通知がありました。

スクリーンショット 2018-04-25 22.46.08.png

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

作り終えてみての感想

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

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

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

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

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