GASでWebhookを記録しSlackとメールで通知する

##背景
Livestreamを使ってライブ配信を定期的に行なっているのですが、アーカイブをダウンロードして後処理を行うのに、準備完了の通知が欲しいと思い、組んでみました。

https://livestream.com/

Livestreamでは Webhookが利用でき、ライブ配信の開始や終了、ダウンロード準備完了などのステータス更新イベントを受信することができます。

https://livestream.com/developers/docs/api/#webhooks

webhookは受信するサーバが必要ですが、GAS（Google Apps Script）を使ってAPIをデプロイすることもできますので、下記の記事を参考にSpreadsheetでコードを書いていきました。

　中継サーバを立ててみる　https://qiita.com/kunichiko/items/7f64c7c80b44b15371a3
　Slackのwebhookを呼ぶ　https://www.lisz-works.com/entry/gas2slack

##GASスクリプト

Webhookを受信するdoPost()はこんな感じ

```js
// POSTリクエストに対する処理
function doPost(e) {

  // JSONをパース
  if (e == null || e.postData == null || e.postData.contents == null) {
    return;
  }
  var requestJSON = e.postData.contents;
  var requestObj = JSON.parse(requestJSON);

  date = new Date();
  mimeType = e.type;
  
  var accountId = requestObj["accountId"];
  var eventId = requestObj["eventId"];
  var eventName = getEventName(eventId);

  // 対象のLivestreamイベントなら通知
  
  if (eventName != "") {

    var eventType = requestObj["type"];
    var text = eventName + ": ";
    var message = "";
    var link = "";
    switch(eventType) {
      case "event_live":
        message = "ライブ配信が開始されました。";
        link = "https://livestream.com/accounts/" + accountId + "/events/" + eventId;
        text += "<" + link + "|" + message + ">";
        break;
      case "event_ended":
        message = "ライブ配信が終了しました。";
        link = "https://livestream.com/accounts/" + accountId + "/events/" + eventId;
        text += "<" + link + "|" + message + ">";
        break;
      case "vod_downloads_ready":
        message = "ダウンロードの準備ができました。";
        link = "https://livestream.com/accounts/" + accountId + "/events/" + eventId + "/pending_posts";
        text += "<" + link + "|" + message + ">";
        break;
      default:
        text += "Unknown Event(" + eventType + ")";
        break;
    }
    
    // Slackに通知する
    notifyToSlack(e, text);
    
    // Mailに通知する
    if ( eventName == "prd: LiveStreaming" ) {
      notifyToMail(e, eventName + ": " + message, link);
    }
  }
    
  //  
  // 結果をスプレッドシートに追記
  //

  var ss = SpreadsheetApp.getActive()
  var sheet = ss.getActiveSheet();

  // ヘッダ行を取得
  var headers = sheet.getRange(1,1,1,sheet.getLastColumn()).getValues()[0];

  // ヘッダに対応するデータを取得
  var values = [];
  for (i in headers){
    var header = headers[i];
    var val = "";
    switch(header) {
      case "eventName":
        val = eventName;
        break;
      case "date":
        val = new Date();
        break;
      case "mimeType":
        val = e.postData.type;
        break;
      case "body":
        val = JSON.stringify(e.postData);
        break;
      default:
        val = requestObj[header];
        if (val == undefined) {
          val = "";
        }
        break;
    }
    values.push(val);
  }

  // 行を追加
  sheet.appendRow(values);
}

```

Livestreamで作成したイベントを通知やログに読みやすく記録するための getEventName()はベタベタ

```js
// 通知対象のイベントなら、イベント名を返す
function getEventName(eventId) {
  var val = "";
  switch(eventId) {
    case 875342:
      val = "prd: LiveStreaming";
      break;
    case 537323:
      val = "stg: Live Streaming";
      break;
    case 465126:
      val = "TEST WEBHOOK";
      break;
    default:
      val = "";
  }
  return val;
}
```

Mailへの通知

```js

function notifyToMail(e, text, link) {
     Logger.log('sendMailFromForm() debug start');

    //------------------------------------------------------------
    // 設定エリアここから
    //------------------------------------------------------------

    // 件名、本文、フッター
    var subject = text; 
    var body        
        ="Livestream webhook received.\n\n"
        + text + "\n\n"
        + link + "\n\n";
    var footer
        = "-------------------------------------------------------------------\n"

    // メール送信先
    var admin = "admin@example.com"; // 管理者（必須）
    var cc    = ""; // Cc:
    var bcc   = "";
    var reply = ""; // Reply-To:
    var to    = "hoge@example.com"; // To:
  
    //------------------------------------------------------------
    // 設定エリアここまで
    //------------------------------------------------------------

    try{
        // 送信先オプション
        var options = {};
        if ( cc )    options.cc      = cc;
        if ( bcc )   options.bcc     = bcc;
        if ( reply ) options.replyTo = reply;
      
        // 送信元
        options.name = "Support Test";

        // メール送信
        if ( to ) {
            MailApp.sendEmail(to, subject, body, options);
        }else{
            MailApp.sendEmail(admin, "【失敗】メールアドレスが指定されていません", body);
        }
    }catch(e){
        MailApp.sendEmail(admin, "【失敗】メール送信中にエラーが発生", e.message);
    } 
}
```

MailAppで送信できるメールの通数は１００通だったと思います。

Slackへの通知

```js
// Slackに通知する
function notifyToSlack(e, text) {

  // 他のサービスのAPIを呼び出す
  var payload = {
    "text": text + "```" + JSON.stringify(e.postData.contents) + "```",
  };
  var options = {
    "method" : "POST",
    "contentType": "application/json",
    "payload" : JSON.stringify(payload)
  };
  var url = "https://hooks.slack.com/services/Txxxxxxxx/Byyyyyyyy/glhreulhgrh4hai3goursl";
  var r = UrlFetchApp.fetch(url, options);
  var result = JSON.stringify(r);
  return result;
}
```

UrlFetchAppもMailAppと同様に呼び出せる回数の制限があります。が、そこまで激しく呼び出される用途ではないのでサクッと使っています。

## APIとしてデプロイ

Google Apps Scriptで書いたメソッドをPOSTで反応するAPIとしてデプロイしていきます。

スクリプトを保存したら、右上にある青いデプロイボタンから「新しいデプロイ」を選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3381/f8e75237-368c-c996-787e-8a27d63c783d.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3381/96ababc9-8cb1-6d37-31b1-206d43b1941a.png)

「説明」　には、簡潔なAPIの説明を書きます。

「ウェブアプリ」は、自分を選択します。Googleの機能にアクセスする際、このアカウントの権限で処理が行われることになります。

「アクセスできるユーザー」は全員を選択します。Webhookで呼び出されますからね。