死活監視をGASを使ってSlackへ通知させてみた（後編）

タイトル通り、死活監視をGASで書いてSlackに通知させてみたよというお話です。
綺麗なまとめというより自分自身の思考の流れも書いています。
※まだまだ知識不足で説明不十分や間違っている部分もあるかと思いますので、
もし気になる点があればコメントいただければと思います。


今回は**[前回の記事](https://qiita.com/makky/items/27622502119823f31b98)**の最後に書いた、「課題」の解消まで書ければと思います。

###前編の概要

前編は、WEBサイトの死活監視をGASをSlackに通知までさせてみようという内容です。（タイトル通り、超ざっくり）

ただ、前編の仕様では課題が残っていました。
>今回のコードだと以下の課題があります。
・１つのサイトのみの監視しかできない
・サイトがダウンしている場合、復旧するまでずっと全員にメンションで通知が続く（うるさい）
これを
・複数のサイトの監視
・サーバーが落ちた時に通知が来て、次に復旧するまでは通知が来ないようにする
という部分が次の課題かと思います。

ということでこちらの課題を解決していきたいと思います。


####①複数サイトの監視
今の仕様では

```javascript
// siteURLを定義
const siteUrl = PropertiesService.getScriptProperties().getProperty('SITE_URL');
```

プロパティにサイトの値を入れている状態です。
これだと複数のサイトを定義しようとするとサイトの数だけ定義する必要があります。

↓イメージ

```Javascript
const siteUrl1 = PropertiesService.getScriptProperties().getProperty('SITE_URL1');
const siteUrl2 = PropertiesService.getScriptProperties().getProperty('SITE_URL2');
```

、、、、できなくはない。
ですが、さすがに（名前の付け方も）**イケてない**ことまだ経験値ひよっこの私でも感じます。
この**イケてない**という感覚的な部分をほんの少し言語化すると、**管理が面倒になる**ということかなと。
次にまたサイトを追加する場合、また定義をして、プロパティに値を追加して、、、となり、手間が多いですし、修正が発生した時も面倒なのではと思います。




ここで、どうしたらいいのか、、、

![kangaeruhito.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/883905/0cc658d9-c909-7919-bef4-3bcdc822bfc9.png)

（前回からの流用。以外とお気に入りの画像）

とまた考える人になるところですが、今回はGASを使っているので**スプレッドシートに
URLを入力しておいて、その値を取得させよう**となりました。
あらかじめ入力された値をデータベースから取得させているイメージです。
これであれば、URLの追加修正は、スプレッドシート上で完結するので楽ちんです。
（他にも手段はたくさんあるかと思います）

スプレッドシートの値の取得方法は

```javascript
// スプレッドシートのID
const sheetID = PropertiesService.getScriptProperties().getProperty('SHEET_ID');
// スプレッドシートのIDからシートを定義
const sheet = SpreadsheetApp.openById(sheetID);
```
このIDというのはスプレッドシートのURLの
https://docs.google.com/spreadsheets/d/************/edit#gid=0
の******の部分です！（よくわからない英数字が並んでいるはず）

これで複数サイトのURLは取得できそう！



####②サイトが落ちた時に通知が来て、次に復旧するまでは通知が来ないようにする

例えば、レスポンスコード200=OK、200以外=NGとします。
OK→終了、NG→Slackに通知ですが、例えばトリガーを1分単位にしていると、
OKになるまで1分単位で通知が来てしまう状態です。

サイトが落ちたのはもう分かってるのに通知が来つづけたら、わりとうるさいですよね。

![mimisen_man.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/883905/95b1ad42-04e2-80ae-dc85-bd750fd85ad6.png)

耳栓もしたくなります。

なので、ここは仕様変更を。
こちらも①と合わせてスプレッドシートを使おうとなりました。
各URLの横に「OK」か「NG」か値を入力をしておきます。

![スクリーンショット 2021-06-26 13.57.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/883905/0eb04b22-d4f0-66ec-8584-332b6591b69d.png)


この「OK」「NG」の判定で分岐をさせることにしました。
・もともとOK、今回OK→終了
・もともとOK、今回NG→シートの値をNGに変更をし、Slackへ通知
・もともとNG、今回NG→終了（シートの値も変更なし）
・もともとNG、今回OK→シートの値をOKに変更させ終了

練習のために書いたフローチャートだとこんな感じです。
![GAS -  フローチャート (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/883905/a97e544d-ce2b-2d01-4012-862adeeeac98.png)

（フローチャートの方が分かりづらい説は認めます。）


**よし、これでコード書いてみよう！**
、、、となり書いてみましたが、調べていくと1分置きにトリガーを実行させていると
GASの特性上、時々サイトは落ちていないのにNG判定されることがあるらしい、、（？）


ということで**NG回数=3回になれば通知させる**ことにしました！

![スクリーンショット 2021-06-26 14.16.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/883905/e6f744d2-3d4a-88c3-fafd-2373ba820b9a.png)

3回になれば通知させ、4回以上は通知させない。
NG→OKになれば回数を0に戻す。
（これを図で書くと上の図より分かりづらくなりそうなので省略でございます）

**シートの値をOKにするとか、NGにする**等、シートの値を更新（入力）するのはsetValueを使います。

```Javascript
sheet.getRange(入力するセルの範囲).setValue(入力する値)
```
ここでのsheetは①で定義をしたシートになります。



##実際に書いたコード

```Javascript
//SlackAPIで登録したボットのトークンを設定する
const token = PropertiesService.getScriptProperties().getProperty('SLACK_API_TOKEN');
//ライブラリから導入したSlackAppを定義し、トークンを設定する
const slackApp = SlackApp.create(token);
//Slackボットがメッセージを投稿するチャンネルを定義する
const channelId = PropertiesService.getScriptProperties().getProperty('SLACK_CHANNELID');
// スプレッドシートのID
const sheetID = PropertiesService.getScriptProperties().getProperty('SHEET_ID');
// スプレッドシートのIDからシートを定義
const sheet = SpreadsheetApp.openById(sheetID); 
// 死活監視の時の設定
const options = {
  'contentType': 'application/json',
  'method': 'get',
  'muteHttpExceptions': true
  };


  function isSiteAlive(url, status) {
    let res;
      try{
        res = UrlFetchApp.fetch(url, options);
      }catch(e){
        console.log(e);
        // 3回続ける
       return false;
      }
      if(res != void(0)){
        return res.getResponseCode() == 200;
      }
  return false;
}

function checkUrl(){
  let message = "<!channel>\nダウンしています！至急確認してください！！\n＜サイト一覧＞\n";
  let errorCount = 0;
 //シートのURLが入っている最後の行までの値を取得してくる
　const row = sheet.getSheets()[0].getLastRow();
  let tmp =  sheet.getRange('A2:B'+row).getValues();
  tmp.forEach( function(e,index){
  //配列のため 0スタート、かつ1行目にカラム名を入れているので該当行はindex+2
  const updateIndex = index + 2;
 //シートの値を配列で取得しているため、e[0]のURLを指定
    if (!isSiteAlive(e[0])){
      sheet.getRange("B"+updateIndex).setValue(e[1]+1);
   //配列のため 0スタートで2が3回目。NG回数3回目の時だけerrorCountさせる
      if (e[1] == 2){
　　//メッセージに該当のサイトURLも追加させる
        message += e[0] + "\n";
        errorCount++;
      };
    }else{
      sheet.getRange("B"+updateIndex).setValue(0);
    }
  })
  if(errorCount > 0) {
    slackApp.postMessage(channelId, message);
  }
};
```
※前回は判定OKの時にはメンションなしの通知をさせていましたが、
それは不要という判断になりました。

また、複数のサイトを管理し通知させるので、Slackへの通知メッセージに
どのサイトが落ちているかわかるように追加させています。


##最後に

前編、後編に分けて書いてみましたが、いかがでしたでしょうか。

実際に書いたコード、とは言っていますが
前編までで、割と力尽きてしまった（この後の解決方法が見えなかった）ので
先輩方にヒントや修正をいただいて、やっと完成した状態です。汗

個人としてはアウトプットしていくうちに理解が曖昧な部分に気づけたのでよかったです。
GASは連携すればもっと使える幅が広がりそうだなと。

なんちゃって技術発信第一弾、これにて完結です。以外と時間がかかったぜ。。。
自分のアウトプットのためにも続けて行く予定です。



####参考リンク
https://qiita.com/negito6/items/c64a7a8589faaffcfdcf
https://excel-ubara.com/apps_script1/