新人でも、楽がしたい！ ~議事録の準備~

「この業務は新人がやる」
誰が言い出したのか分かりませんが、どんな会社でも
「新人に担当させる仕事」
があると思います。

さらに、
「新人への評価は、仕事をいかに効率よく処理するかで決まる」
というのも間違いありません。

今回は、面倒なこと ~~特に雑務~~ が嫌いな私が楽をするために、**新人業務を自動化**したお話をご紹介します。

最後までお読みいただくと、以下のアウトプットを自動化するノウハウが得られます。
こちらは、MTGの時間になったら自動で議事録を準備してくれるアプリケーションです。
<img width="262" alt="Screen Shot 2019-12-12 at 1.58.31.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/261058/488455c4-3578-a5e2-b6b8-4611cfc45d8f.png">

# はじめに
この記事に興味を持っていただき、誠にありがとうございます。
私は訳あって2019年7月に新卒入社し、3ヶ月間の研修の後、10月に現場配属された新米エンジニアです。

2ヶ月ほど働いていると、
「いつも同じことやってるな」
「これって自動化できそうだな」
と感じるネタが溜まってきたので、それらを「自動化した話」をご紹介します。

新人の仕事 ≒「議事録の準備」を自動化したお話です。

# 新人 → 議事録の準備
社内外を問わず、MTGが開かれれば議事録、議事メモの準備は必須です。

私には「MTGが開かれる前までに、議事録のフォーマットを整備し、MTG開始後すぐにメモ取れる状況にする」というタスクが与えられたので、Googleカレンダーにリマインドをセットして、毎回手作業で議事録を準備していました。

手作業での準備手順は、大まかには以下のプロセスです。
1. GoogleドライブにMTG用のフォルダを作成
2. フォルダ内にGoogleドキュメントを新規作成
2. ヘッダーとフッターに Credential を記入
3. MTGの基本情報（日時、場所、出席者）を記入
4. ドキュメントへのURLをSlackに投稿


配属当初は難なくこなしていたのですが
- だんだん面倒になってくる
- 移動がバタバタしていると、準備を忘れる
- いつも「前回のフォーマットをコピー」して流用している

というマンネリ状態になりました。
そこで、議事録の準備を**オール自動化**しました。

# 用意するもの

- Slack の Incoming webhook
- 議事録のテンプレート（Google ドキュメント）
- 議事録を作成する会議通知名の一覧（Google スプレッドシート）
- Google App Script の実行環境
- （このプログラム以降は、もう議事録は手動で準備しないという意思）

# 全体の流れ
大まかな処理の流れは、
1. GAS を定期実行
2. Google カレンダーの、直近のイベントを取得
3. 議事録を作成する会議通知名の一覧を取得
4. [2] == [3] となるイベントが存在したら、議事録準備開始
5. 準備した議事録へのURLを Slack に投稿する

となります。
作業的に依存関係のない部分から順に説明します。

# 作業開始
以下、議事録の準備を自動化するための、環境整備についてです。

## 議事録を作る会議通知名の一覧
Googleアカウントと共に仕事をされる方であれば、Google カレンダーを確認すれば、多種多様な「会議通知」が登録されていると思います。それらの会議通知のうち、「議事録を作成するMTG」のイベント名をスプレットシートに書き出します。

ex)
議事録を作成する会議通知名が
- インフラ進捗報告
- アプリ進捗報告
- 【PJ】週次定例会
- 【内部】隔週会
の場合
![Screen Shot 2019-12-12 at 0.23.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/261058/6684524e-1a63-bc50-75b4-a35d66a0c6d2.png)

この時、作成したスプレッドシートのURLは
`https://docs.google.com/spreadsheets/d/abcedfg12345/edit?folder=hogefuga#gid=0`
のようになります。この時の `id` は `abcedfg12345` になります。id は Google App Scriptとの連携時に用いるので、必要になり次第確認してください。

## 議事録のテンプレート
議事録は「毎回ゼロから」作成するのではなく「ある程度のフォーマットが決まって」おり、それに従って作成するものだと思います。過去の議事録を参照すれば「毎回必ず記入する部分」が見つかるので、それらを網羅した議事録のテンプレートを Google ドキュメントで作成します。

毎回必ず記入する部分の候補としては、以下が考えられます。
- ヘッダーとフッターのCredential
- ページ番号
- 会議名記入欄
- 会議場所記入欄
- 出席者記入欄
- 決定事項/TODOの記入欄

私が作成したテンプレートでは、「出席者記入欄」には **MTGに出席する可能性のある全員の名前** を記入しています。それにより、実際のMTG中は「いない人を出席者から削除する」のみで済み「あの人誰？→ 名刺リストを確認 → この人か！」を回避できます。

Google ドキュメントで作成した議事録のテンプレートにも `id` があります。
こちらも後の作業で利用しますので、必要になり次第確認してください。

## Slack Incoming Webhook の発行
[slack api](https://api.slack.com/) から、incoming webhook を発行してください。
incoming webhook の値は厳重管理してください。

## Google App Script のコーディング
事前準備が完了したので、議事録作成を代行してくれるコードの作成を開始します。

今回は GAS の処理を役割ごとに関数化したので、関数ごとに各機能を説明します。
- カレンダーから直近のイベント名を取得
- スプレッドシートから議事録を作る会議通知名を取得
- 議事録を作成するかの判定
- 議事録を新規作成し、slackにURLを投稿

### カレンダーから直近のイベント名を取得
Google アカウントユーザの、直近１時間の予定一覧を取得します。

```js
function getCalendarEventList() {
  
  var now = new Date();
  var oneHourFromNow = new Date(now.getTime() + (1 * 60 * 60 * 1000));
  var events = CalendarApp.getDefaultCalendar().getEvents(now, oneHourFromNow);
  
  var eventList = [];
  for (i = 0; i < events.length; i++) {
    eventList.push(events[i]);
  }

  // Googleカレンダーに登録された「直近１時間分」のイベントの配列を返す
  return eventList;  
}
```

### スプレッドシートから議事録を作る会議通知名を取得
議事録を作成するMTG名が記入されたスプレッドシートから、そのMTG名一覧を取得します。スプレッドシートの読み込みは「関数の実行ごと」に行うので、MTG名の追加/削除は自由に実施できます。

```js
function getEventTitleFromSpredsheet() {
  
  // 議事録を作成するカレンダーの会議通知名一覧が記載されたスプレッドシートのID
  var SPREDSHEETPATH = "<spredsheet_id>"

  var ss = SpreadsheetApp.openById(SPREDSHEETPATH);
  var sheet = ss.getSheets()[0];
  var range = sheet.getRange(1, 1, ss.getLastRow());
  var values = range.getValues();
  
  var meethingList = [];
  for (i = 0; i < values.length; i++) {
    value = values[i];
    meethingList.push(value[0]);
  }
  
  // スプレッドシートに記載されたMTG名の配列を返す
  return meethingList;
}
```

### 議事録を作成するかの判定
「議事録を準備するか」を判定してくれるロジックを実装します。
会議１つに議事録１つが基本なので、会議の開始時間を判定軸としたロジックも追加します。

```js
function start(){
  
  // 議事録自動準備プログラムで、最初に起動する関数
  // 「直近のイベント一覧」と「議事録を作成するMTG名」を取得し、一致する名前があれば議事録を準備する
  
  meethingList = getEventTitleFromSpredsheet();
  eventList = getCalendarEventList();
  
  var createFlag = false;
  var meethingName = '';
  
  for (i = 0; i < meethingList.length; i++) {
    for (j = 0; j < eventList.length; j++) {
      if (meethingList[i] == eventList[j].getTitle()) {
        
        var functionTriggeredTime = new Date();
        var meethingStartTime = eventList[j].getStartTime();
        
        // 会議の開始時間が、プログラム起動時間より後ならば、createFlag=trueにする。
        // 会議中にプログラムが起動した場合には、議事録は作成されない。
        if (functionTriggeredTime < meethingStartTime){
          createFlag = true;
          meethingName = eventList[j].getTitle();
        }
        
      }
    }
  }
  
  if (createFlag) {
    createMinutes(meethingName);
    Logger.log('議事録を準備しました。');
    Logger.log('MTG名：' + meethingName);
  } else {
    Logger.log('議事録は準備されませんでした。');
  }
}
```

### 議事録を新規作成し、slackにURLを投稿
「議事録を作成する」と判定された場合の、議事録を作成するロジックを組みます。
`<folder_id>` には、新規作成された議事録を保存するフォルダの `id` を入力してください。

```js
function prepareMinutes(meethingName){
    
  // 議事録を作成するフォルダのID
  var FOLDERID = '<folder_id>';
  // 議事録のフォーマットファイルのID
  var FILEID = '<document_id>';
  // slack Incoming Webhook
  var POSTURL = '<slack_incoming_webhook>';
  
  // 議事録のフォーマット、作業フォルダを取得する
  var folderPath = DriveApp.getFolderById(FOLDERID);
  var formatFilePath = DriveApp.getFileById(FILEID);
  
  // 現在時刻から「YYYYddmm_(会議名)」 の文字列を作成する
  var today = new Date();
  var year = today.getFullYear();
  var month = '' + (today.getMonth() + 1);
  var day = '' + today.getDate();
  if (month.length < 2)
    month = '0' + month;
  if (day.length < 2)
    day = '0' + day;

  var YYYYmmdd = year + month + day;
  var minutesName = YYYYmmdd + '_' + meethingName; // 議事録のファイル名：「YYYYmmdd_MTG名」
  
  // 議事録フォーマットをコピーして、指定のフォルダに議事録を新規作成する
  var newFile = formatFilePath.makeCopy(minutesName, folderPath);
  var fileUrl = newFile.getUrl();
  
  // slackに議事録へのurlを投稿する
  var message = '議事録を準備しました。' + '\n\n' + '会議名：' + minutesName + '\n' + 'URL : ' + fileUrl;
  var jsonData = {
    'text':message
  };
  var payload = JSON.stringify(jsonData);
  var options = {
    'method':'post',
    'contentType':'application/json',
    'payload':payload
  };
  
  UrlFetchApp.fetch(POSTURL, options);
}
```

以上の処理により、「議事録を自動で準備させる」プログラムの完成です。
最後に、プログラム定期実行の設定をします。

## GAS の定期実行
Google App Script の Current project's triggers からプログラムの定期実行が設定できます。
Add Trigger で以下の項目を入力し、GAS を１時間ごとに起動させます。

![Screen Shot 2019-12-12 at 1.32.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/261058/16106b52-9b94-6237-6c0e-4d9ec4b713d8.png)

# slackへの議事録URLの投稿
正しく動作すれば、MTG時間になると、slackで以下の投稿が得られます。
<img width="262" alt="Screen Shot 2019-12-12 at 1.58.31.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/261058/488455c4-3578-a5e2-b6b8-4611cfc45d8f.png">


# まとめ
以上により、議事録の準備はオール自動化されました。
事前準備、コーディング作業は大変ですが、１度作ってしまえば、もう議事録準備に費やす工数は無くなると思われます。

以上、長文にお付き合いいただき、有難うございました。