LoginSignup
3
3

More than 1 year has passed since last update.

[GAS×Google Chat]雨が降りそうな時に通知するbotを作った

Last updated at Posted at 2020-09-09

#★使えなくなりました★
一般データ保護規則(GDPR)の関係でYahoo!のサービスが欧州から繋がらなくなりました。
GASから気象情報APIを叩くと「欧州からは使えねーよ」と403エラーが出るように…。
エラー出ない時もあるので、多分GASの接続元が欧州じゃない時もあるのかな?
接続元のリージョンを指定出来れば良いのですが…。そんなのあるのかな。

#作ったきっかけ
リアルタイムに「今から雨降るよ!!」って通知してくれるbotが欲しくて作りました。
前日や当日に一日の天気を通知するbotはちょこちょこあるんですが、リアルタイムに監視するbotがなかったので。

#まぁスマホアプリとかで簡単に実現出来ますけど
社内でGoogle Chatを活用して紙やメールを減らしていきましょうって流れがあり、
Google Chatを定期的に見る仕組みを用意したかったので…。
他にも社食のメニューをお知らせするbotとかもあります。

#ざっくりとした仕組み
気象情報APIはYahoo!のを使いました。

Yahoo! Open Local Platform(YOLP) 気象情報API
https://developer.yahoo.co.jp/webapi/map/openlocalplatform/v1/weather.html

気象情報APIから予報を取得
     ↓
スプレッドーシートに書き出す
     ↓
前回と今回の取得時の観測値に変化があったら
「雨がやみました/降り始めました」と通知
     ↓
現在の観測値と15分後の天気を比べて差があったら
「雨がやみそうです/降り始めそうです」と通知
     ↓
メッセージと一緒に雨雲レーダーの画像チャットにポスト
     ↓
15分毎にループする

#気象情報APIの使い方
APIを使うためにはアプリケーションIDを取得する必要があります。
https://e.developer.yahoo.co.jp/register
アプリケーションの種類はサーバーサイドで良いっぽい。

天気を知りたい場所の緯度、経度、取得したアプリケーションIDを以下のように記述して使います。
https://map.yahooapis.jp/weather/V1/place?coordinates=<経度>,<緯度>&appid=<アプリケーションID>

#スプレッドシート
こんな感じにしてます。

実行時刻と観測時刻を見比べて貰ったら分かると思いますが、結構ラグがあります。

#Google Chatの通知はこんな感じ

☆残念なお知らせ☆
【重要】YOLP Web APIにおける一部API・SDK 提供終了のお知らせ
雨雲レーダーのAPIは2020/10/31で提供終了になるそうです。代替サービスあるんか…?
終了しちゃったらImageなしで通知するしかないですね。

#コード

getJson.gs
//YOLPのapiを叩くのに必要なパラメータ
let appId = "<取得したAPIのAPP ID>";
let lon = "139.7649361"; //経度
let lat = "35.6812362"; //緯度
let interval = 5; //予報の間隔(分)
let apiUrl = "https://map.yahooapis.jp/weather/V1/place?coordinates=" + lon + "," + lat + "&output=json&appid=" + appId + "&interval=" + interval;

//Google ChatのWebhook
let webhookUrl = 'https://chat.googleapis.com/v1/spaces/~';

//スプシとシートの設定
let spreadSheet = SpreadsheetApp.getActiveSpreadsheet();
let sheet = SpreadsheetApp.getActiveSheet();

function getJson() {
  //お天気api叩く
  let response = UrlFetchApp.fetch(apiUrl);
  //let response = spreadSheet.getSheetByName('シート3').getRange('A1').getValue();
  let date = '';
  let rainfall = '';
  let previouszObservation = sheet.getRange(3,3).getValue(); //前回天気を確認した時の雨量
  let currentObservation = '';
  let after15minForecast = '';
  let minDiff = '';
  let now = new Date();
  let str = '';

  //現時刻を書き込み
  sheet.getRange(1,2).setValue(Utilities.formatDate( now, 'Asia/Tokyo', 'HH:mm'));

  Logger.log(response.getContentText());
  //JOSNオブジェクトに変換
  let json=JSON.parse(response.getContentText());
  //観測結果と5分毎の予報を取得(13回)
  for(let i=0;i<=12;i++){
    //JSONから日時を取得
    date = json['Feature'][0]['Property']['WeatherList']['Weather'][i]['Date'];
    //'yyyy/MM/dd hh:mm'に変換
    date = date.slice(0,4)+'/'+date.slice(4,6)+'/'+date.slice(6,8)+' '+date.slice(8,10)+':'+date.slice(-2);
    //雨量を取得
    rainfall = json['Feature'][0]['Property']['WeatherList']['Weather'][i]['Rainfall'];
    //type = json['Feature'][0]['Property']['WeatherList']['Weather'][i]['Type'];
    //日時と雨量を書き出す
    sheet.getRange(i+3,2).setValue(date);
    sheet.getRange(i+3,3).setValue(rainfall);
  }
  //現在の観測雨量
  currentObservation = sheet.getRange(3,3).getValue();
  //観測時刻から25分後の予測雨量
  //apiを叩くと約10分前の観測と予報が得られるので、api叩いた時刻の15分後の予報が知りたい時はJSONから25分後のデータを取る
  after15minForecast = sheet.getRange(8,3).getValue();
  //観測から25分後の時刻と、スクリプト実行時刻との時間差を計算
  minDiff = String(Math.round((sheet.getRange(8,2).getValue() - now)/60000)); //単純に引き算するとミリ秒で出て来るので60000で割ると分になる
  
  //雨の観測結果の変化チェック
  if(previouszObservation!=currentObservation){ //前回の雨量と現在の雨量が一致しない時
    if(currentObservation==0){ //現在の雨量が0なら
      str += '雨がやみました。';
    }else if(previouszObservation==0) { //前回の雨量が0なら
      str += '雨が降り始めました。';
    }
  }
  
  //約15分後の予報チェック
  //現在の雨量と15分後の雨量の差異で判断
  if(currentObservation==0){
    if(after15minForecast>0){
      if(str!=''){
        str += '<br>';
      }
      str += '' + minDiff + '分後に雨が降り始めるようです。'
    }
  }else {
    if(after15minForecast==0){
      if(str!=''){
        str += '<br>';
      }
      str += '' + minDiff + '分後に雨がやむようです。'
    }
  }
  Logger.log(str);
  Logger.log('前回の観測値:'+ previouszObservation);
  Logger.log('今回の観測値:'+ currentObservation);
  Logger.log('15分後の予報:'+ after15minForecast);
  //通知内容があればGoogle Chatにpost
  if(str!=''){
    let payload  = {
      "cards": [
        {
          "sections": [
            {
              "widgets": [
                {
                  "textParagraph": {
                    "text": str
                  }
                },
                {
                  "image": { "imageUrl": "https://map.yahooapis.jp/map/V1/static?appid="+ appId + "&lat=" + lat + "&lon=" + lon + "&z=11&width=300&height=200&pointer=on&overlay=type:rainfall|date:"+Utilities.formatDate( now, 'Asia/Tokyo', 'yyyyMMddHHmm') }
                },
                {
                  "buttons": [
                    {
                      "textButton": {
                        "text": "雨雲レーダーを見る",
                        "onClick": {
                          "openLink": {
                            "url": "https://weather.yahoo.co.jp/weather/zoomradar/?lat=" + lat + "&lon="+ lon + "&z=12"
                          }
                        }
                      }
                    }
                  ]
                }
              ]
            }
          ]
        }
      ]
    };
  
    let options = {
      "method" : "POST",
      "contentType" : 'application/json; charset=utf-8',
      "payload" : JSON.stringify(payload)
    };
    //Google Chatにポスト
    let dayOfWeek = now.getDay();
    let hour = now.getHours();
    //土日じゃない&7時~21時の時チャットに通知
    if(dayOfWeek != 0 && dayOfWeek != 6){
      if(hour>6 && hour<22){
        UrlFetchApp.fetch(webhookUrl, options);
      }
    }
  }
}

職場にいない時に通知されるのも邪魔なので、土日と夜間/早朝は通知しないようにしています。
#作ってみて
天気予報の精度に因るところもあるんですけど、結構「降りそう」って通知があっても降らなかったりとか外れる事も多いです。笑
あと、Google Chatのカード形式メッセージを初めて使ってみました。
見た目がシュッとするので良い感じですね。(もうちょっと自由度欲しいけど)

3
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
3