2023-05-15に更新

【GAS】地震情報を取得してSlackに通知する

GAS Google Apps Script(GAS) 地震速報 公開下書き

地震情報を取得してSlackに通知したかったので、自分用にやったことをまとめました。

システム概要

main.gs

/*
  東京で震度5弱以上の地震が確認された場合に安否確認を行うシステムです。
*/
// Slackのトークン
const TOKEN  = PropertiesService.getScriptProperties().getProperty('TOKEN');
// 地震情報のURL
const URL = PropertiesService.getScriptProperties().getProperty('URL');
const date = new Date();
// 震度5弱
const judgeLevel = 10;
// 投稿するチャンネル
const channelId = "#accall";
// エラー報告用チャンネル
const errorChannel = "#accfree";

// Main関数
async function main(){
  // 地震情報を取得する
  let info = await getInfo();
  // 地震情報が発信対象か判定
  let [time,maxElm,isSend] = judgeSend(info);
  // 地震情報からメッセージを作成
  if(isSend){
    // Googleフォームを作成
    let formUrl = createForm();
    // 送信メッセージの作成
    let message = createMessage([time,maxElm],formUrl);
    // Slackにメッセージを送信
    sendMessage(message);
    console.log("【正常終了】地震情報あり");
  }else{
    console.log("【正常終了】地震情報なし");
  };
}

// 地震情報を取得
async function getInfo(){
  let result = new Array();
  let data = new Array();
  try{
    let res = await UrlFetchApp.fetch(URL).getContentText('UTF-8');
    let json = JSON.parse(res);
    let date = json['Head']['ReportDateTime'];
    while(date.includes('-')){
      date = date.replace('-','/');
    }
    date = new Date(date);
    result.push(date);
    json['Body']['Intensity']['Observation']['Pref'].forEach(elm =>{
      let obj = new earthquakeInfoModel(changeLevel(elm['MaxInt']),elm['Name']);
      data.push(obj);
    });
    result.push(data);
  }catch(err){
    sendErr(err);
    deleteTrigger();
    writeLog(err);
  } 
  return result;
};

// 地震情報から過去5分以内、かつ関東の情報、かつ震度が5弱以上のものがあるかどうかを判定
function judgeSend(info){
  // 5分をミリ秒に変換
  const fiveMins = 5 * 60 * 1000;
  const KANTO = ["東京都","埼玉県","神奈川県","千葉県","群馬県","茨城県","栃木県"];
  let maxElm = new earthquakeInfoModel(0,null,null);
  let isSend = false;
  let result = new Array();
  let time = null;
  try{
      time = Utilities.formatDate(info[0], 'Asia/Tokyo', 'yyyy-MM-dd HH:mm:ss');
      info[1].forEach(elm=>{
      if((date - fiveMins) < info[0]){
      if(KANTO.includes(elm.place)){
          if(elm.level >= judgeLevel){
            if(maxElm.level < elm.level){
              maxElm = elm;
              isSend = true;
            };
          };
        };
      };
      });
  }catch(err){
    sendErr(err);
    deleteTrigger();
    writeLog(err);
  }
  return [time,maxElm,isSend];
}

// 地震情報からメッセージを生成
function createMessage([time,maxElm],formUrl){
  let notifyEveryone = "<!everyone>\n"
  let message = notifyEveryone.concat(PropertiesService.getScriptProperties().getProperty('MESSAGE').concat("\n",formUrl));
  while(message.includes('</br>')){
    message = message.replace('</br>', '\n');
  }
  return message.replace("<time>",time).replace("<place>",maxElm.place).replace("<level>",convertScale(maxElm.level));
}

// Googleフォームの生成
function createForm(){
  const folderName = "安否確認";
  let targetFolder;
  let title = "【安否確認】"+ date.toLocaleString('ja-JP');
  let desc = "安否確認用フォームです。 \n24時間以内に回答してください。";
  let question1 = "社員番号";
  let question2 = "氏名";
  let question3 = "被害状況"
  let question3Ans= ["無事","被害あり"];
  let question4 = "現在地";
  let question5 = "伝達事項";

  try{
      // フォームの作成
      let form = FormApp.create(title);
      form.setTitle(title);
      form.setDescription(desc);
      form.addTextItem().setTitle(question1).setRequired(false);
      form.addTextItem().setTitle(question2).setRequired(true);
      form.addMultipleChoiceItem().setTitle(question3).setChoiceValues(question3Ans).setRequired(true);
      form.addTextItem().setTitle(question4);
      form.addTextItem().setTitle(question5);

      // 作成したフォームを「安否確認」フォルダに移動
      let formId = DriveApp.getFileById(form.getId());
      let folderIter = DriveApp.getRootFolder().getFoldersByName(folderName);
      if(folderIter.hasNext()){
      targetFolder = folderIter.next();
      }else{
      targetFolder = DriveApp.getRootFolder().createFolder(folderName);
      }
      targetFolder.addFile(formId);
      DriveApp.getRootFolder().removeFile(formId);
      return form.shortenFormUrl(form.getPublishedUrl());
   }catch(err){
     sendErr(err);
     deleteTrigger();
     writeLog(err);
    }
}

// Slackにメッセージを送信
function sendMessage(message){
  // ライブラリから導入したSlackAppを定義し、トークンを設定する
  let slackApp = SlackApp.create(TOKEN);
  try{
    // SlackAppオブジェクトのpostMessageメソッドでボット投稿を行う
    slackApp.postMessage(channelId, message);
  }catch(err){
    sendErr(err);
    deleteTrigger();
    writeLog(err);
  }
}

// 受信した震度を数値に置き換える(比較するため)
function changeLevel(level){
  switch(level){
        case "1":
        return 10;
        case "2":
        return 20;
        case "3":
        return 30;
        case "4":
        return 40;
        case "5-":
        return 45;
        case "5+":
        return 50;
        case "6-":
        return 55;
        case "6+":
        return 60;
        case "7":
        return 70;
        default:
        return 999;
    }
}

//震度を日本語表記に変換する
function convertScale(num){
    switch(num){
        case 10:
        return "震度1";
        case 20:
        return "震度2";
        case 30:
        return "震度3";
        case 40:
        return "震度4";
        case 45:
        return "震度5弱";
        case 50:
        return "震度5強";
        case 55:
        return "震度6弱";
        case 60:
        return "震度6強";
        case 70:
        return "震度7";
        default:
        return "震度不明";
    }
}

// Slackにエラーメッセージを送信
function sendErr(err){
  // ライブラリから導入したSlackAppを定義し、トークンを設定する
  let slackApp = SlackApp.create(TOKEN);
  // 報告用エラーメッセージ
  let message = "エラーが発生しました。\n".concat(err);
  try{
    // SlackAppオブジェクトのpostMessageメソッドでボット投稿を行う
    slackApp.postMessage(errorChannel, message);
  }catch(err){
    sendErr(err);
    deleteTrigger();
    writeLog(err);
  }
}

// エラー発生時にトリガーをすべて削除する
function deleteTrigger(){
  let triggers = ScriptApp.getProjectTriggers();
  writeLog("%s件のトリガーをすべて削除します。",triggers.length);
  triggers.forEach(trg=>{
    ScriptApp.deleteTrigger(trg);
  });
}

// ログを出力する
function writeLog(log){
  const sheetId = "1yrQDPyR9klug5p_oEsO71W8F77F_JgnVvfZP7SUp2wc";
  let sheetName = "log_sheet";   // SpreadSheetName

  // スプレットシートにログ出力
  var MySheet = SpreadsheetApp.openById(sheetId);
  MySheet.getSheetByName(sheetName).appendRow(
    [new Date(), 'Log出力:',log]
  );
}

earthquakeInfoModel.gs

/*
    地震情報のModel
*/
class earthquakeInfoModel{
    constructor(level,place,time){
        this.level = level;
        this.place = place;
        this,time = time;
    }

    get getLevel(){
        return this.level;
    }
    set setLevel(level){
        this.level = level;
    }

    get getPlace(){
        return this.place;
    }
    set setPlace(place){
        this.place = place;
    }

    get getTime(){
      return this.time;
    }
    set setTime(time){
      this.time = time;
    }
}

各メソッドの動き

テスト

反省点

何度でもクリック!→

HelloWorld

Crieitは個人で開発中です。 興味がある方は是非記事の投稿をお願いします! どんな軽い内容でも嬉しいです。
なぜCrieitを作ろうと思ったか

また、「こんな記事が読みたいけど見つからない!」という方は是非記事投稿リクエストボードへ!

こじんまりと作業ログやメモ、進捗を書き残しておきたい方はボード機能をご利用ください!

ボードとは?

HelloWorld の最近の記事