会社の議事録の準備とSlackでのリマインドを自動化してみた

##やりたいこと

当社では毎週水曜日に事業部会議というものがありまして、各事業部から一周間の出来事や、数字の共有、課題の相談、などなどを行う会があります。
発表者はGoogleSpreadSheetにトピックを書き込み、毎週交代制で議事録担当者がおり議事録を書くという形です。なぜか当社の取締役が毎度会議前にスラックでリマインドをしていることが多く、『取締役にさせるお仕事ではないな、、、これはいかん！』と思い、ＧＡＳを利用して自動化をいたしました。

##やること

まず当社の議事録は以下のような形でフォーマットが決まっており、F4に議事録の順番、C4に当日の議事録担当者、がそれぞれ書かれてあり、C2の部分に事業部会議が開かれた日付が書かれている形です。シートの名前も会議が開かれた日付がyyyymmddという名前になっています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/271600/2293905e-08ca-c79a-d66f-420cb79a2f0a.png)

毎週水曜日の会議後に、
①：来週分のシートをコピーする
②：シート名を変更する
③：シート内の日付や議事録担当者の項目を更新する
④：スラックのチャンネルにリマインドを投稿
⑤：来週水曜日の会議前に再度リマインド
⑥：①へ戻る

を行いたいと思います。

##完成形はこちら

一つ一つを解説すると膨大な量になってしまうので、簡単にコメントで解説を入れております。書いていたらなぜかプログラムの記述量が100行を超えてしまいました。。。　今見返しても、リファクタリングできそうな部分はたくさんありますが、このままアップしようと思います。本当はもっと短くできる部分とか、工夫できるところがあると思うのですが、しっかりと要望を満たす挙動になりましたので良しとします。

```js


//最新のスプレッドシートを獲得するメソッド
function getLatestSpreadSheet() {
  let mySheet     = SpreadsheetApp.getActiveSpreadsheet();
  let arrayLength = mySheet.getSheets().length; //存在するシートの一覧を配列で取得
  let latestSheet = mySheet.getSheets()[arrayLength - 1]; //配列の一番後ろのシートを取得
  return latestSheet;
}

//次の議事録の担当者を取得するメソッド
function getNextGijirokuPerson() {
  let latestSheet                 = getLatestSpreadSheet();
  let gijirokuPersonList          = latestSheet.getRange("F4").getValue().split("→");
  let previousGijirokuPerson      = latestSheet.getRange("C4").getValue();
  let previousGijirokuPersonIndex = gijirokuPersonList.indexOf(previousGijirokuPerson);
  let lastIndex                   = gijirokuPersonList.length - 1;
  let nextPerson                  = "";
  if (lastIndex == previousGijirokuPersonIndex) {
    nextPerson = gijirokuPersonList[0];
  } else {
    nextPerson = gijirokuPersonList[previousGijirokuPersonIndex + 1];
  }
  return nextPerson;
}

//今日の議事録の担当者を取得するメソッド
function getTodayGijirokuPerson() {
  let latestSheet         = getLatestSpreadSheet();
  let todayGijirokuPerson = latestSheet.getRange("C4").getValue();
  return todayGijirokuPerson;
}

//会議終了後にスラックに次回の議事録担当者を通知するためのメソッド
function postMessageAfterMtg(nextGijirokuPerson, newSheetUrl) {

  var url = "https://slack.com/api/chat.postMessage";

  var payload = {
    "token": "あなたのスラックのトークン",
    "channel": "投稿したスラックチャンネルのID",
    "text": "<!here> \n お疲れ様です。奥野です！" + "\n次回のSaaS事業部会議の議事録担当者は" + nextGijirokuPerson + "さんです \n" + "\n また水曜日の朝になりましたらリマインドします！" + "https://docs.google.com/spreadsheets/d/hogehogehogehogehoge/edit#gid=" + newSheetUrl,
    "as_user": true
  };

  var params = {
    "method": "post",
    "payload": payload
  };

  // Slackに投稿する
  UrlFetchApp.fetch(url, params);
}


//今回のメインとなるメソッド。前回の議事録をコピーするメソッド。
function copynNextGijiroku() {
  let latestSheet = getLatestSpreadSheet(); //最新のスプレッドシートを取得
  let newSheet    = latestSheet.copyTo(SpreadsheetApp.getActiveSpreadsheet()); //最新のシートをコピーして新しいシートを作る
  let zero        = "0";
  let today       = new Date(); //今日の日付を生成
  today.setDate(today.getDate() + 7); //7日後の日付をセットし、年、月、日付をそれぞれ取得
  let year        = today.getFullYear().toString();
  let month       = today.getMonth() + 1;
  let date        = today.getDate();
  if (month <= 9) { //月が9以下の場合は09という形にする必要があるので、if文で対応
    month         = zero.concat(month);
  }
  if (date <= 9) { //日付が9以下の場合は09という形にする必要があるので、if文で対応
    date = zero.concat(date);
  }
  let fullDate    = year.concat(month).concat(date); //シート名を設定するために、数字を文字列としてつなげる
  newSheet.setName(fullDate); //シート名を20210901といった具合に変更
  let nextPerson  = getNextGijirokuPerson();
  newSheet.getRange("C4").setValue(nextPerson);
  newSheet.getRange("F7:F15").clearContent();
  newSheet.getRange("B8:E15").clearContent();
  newSheet.getRange("C2").setValue(year + "/" + month.toString() + "/" + date.toString());
  let newSheetUrl = newSheet.getSheetId();
  postMessageAfterMtg(nextPerson, newSheetUrl); //コピー作成後、スラックにてリマインドをするために、引数と一緒にメソッドを呼ぶ
}

//会議が始まる前にスラックにリマインドを投稿するメソッド
function postMessageBeforeMtg() {
  var todayGijirokuPerson = getTodayGijirokuPerson();
  var latestSheet         = getLatestSpreadSheet();
  var newSheetUrl         = latestSheet.getSheetId();

  var url = "https://slack.com/api/chat.postMessage";

  var payload = {
    "token": "あなたのスラックトークン",
    "channel": "投稿したいチャンネルのID",
    "text": "<!here> \n お疲れ様です。奥野です！" + "\n本日のSaaS事業部会議の議事録担当者は" + todayGijirokuPerson + "さんです \n" + "\n また、本日発表のトピックがある方は入力をお願いいたします！" + "https://docs.google.com/spreadsheets/d/hogehogehogehoge=" + newSheetUrl,
    "as_user": true
  };

  var params = {
    "method": "post",
    "payload": payload
  };

  // Slackに投稿する
  UrlFetchApp.fetch(url, params);
}




```

##最後の仕上げ

後はメソッドをトリガーさせてあげれば大丈夫ですので、GASの左のメニュー画面からトリガーの設定をしてあげればOkです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/271600/f05b4da4-8afd-65b3-26b7-152d7df9fbbe.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/271600/6b2caaf0-4274-7b14-4456-005752889900.png)

##実際の挙動
テストのために設定したチャンネルのIDを入れてテストしてみたところ、無事会議後リマインドと、会議前のリマインドと、どちらも送信がされてあります。もちろん次のMTGの議事録のコピーも作成されてあります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/271600/4ab7a7c6-edfe-491f-412b-9c684a0b5106.png)


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/271600/bba57686-c08e-10ca-432e-a28a007b940c.png)




##今回の実装をする上で参考にした記事

最後に今回の実装をする上で参考にさせていただいた記事を紹介します。

１：　[【SlackAPI】GASでpostMessageを叩いて、Slackにメッセージを投稿する方法まとめ](https://tanuhack.com/gas-slackapi-postmessage/#ID)がかなりわかりやすかったです。WEBhookでの方法を紹介している記事が多い印象なのですが、トークンを使っての実装方法なので、非常におすすめの記事。

２：[Google Apps Scriptで文字列の結合・連結する方法(+演算子とconcatメソッド](https://auto-worker.com/blog/?p=914)もわかりやすかったです。久々にプログラミングをするとやり方を忘れてしまいますｗ　テンプレートリテラルとかって使えるんだっけ？みたいな。今回はテンプレートリテラル使っておりません。

３：[Google apps scriptでスプレッドシートのIDを取得する](https://spreadsheet.blue/google-apps-script/sheet/getid/258/)も意外と使う場面があるかと思います。今回はスラックに新しい議事録のシートのURLを投稿したいときに利用しました。

４：[Google Apps Scriptで配列の検索をするindexOfメソッドとlastIndexOfメソッド](https://tonari-it.com/gas-array-indexof-lastindexof/)は安定の『いつも隣にITのお仕事』の記事。次の議事録担当者を配列から見つける時にindexOfを利用しています。正直、GAS関係はこのサイトだけ見てればほぼ大丈夫だとは思います。

５：[【GAS】今日から「Nヶ月後の日付」「N日後の日付」を求める](https://moripro.net/gas-month-day-later/)は一週間後のDateオブジェクトの生成方法を調べてる時に辿り着きました。