【初心者向け】家計簿の未入力をSlackにお知らせするGASのハンズオン！！

## はじめに
こんにちは！！
**DMM WEBCAMP Advent Calendar 2020**、6日目を担当するメンターの@HidakaRintaroです。
初めてのAdventCalendarですが、気付いたら参加することになっていました？！
\(勝手に僕の参加許可を出した@piyorの記事は[こちら](https://qiita.com/piyor/items/2dfd274502f9415319d8)です\)

### 何を作るの？
今回はGASで家計簿の入力が無い日をSlackでお知らせしてくれるものを作っていきます！
この記事を通してGASの使い方を簡単にでも理解していただけると嬉しいです！！

※ Slack、Googleアカウントは取得済みとして進めます。
※ JavaScriptの基本構文については説明しません。
※ 家計簿の作成については説明いたしません。

### 前提
↓今回使用する家計簿です。「全体」シートは今回使用しないので触れません。
![家計簿](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/563044/a409c088-d649-25c6-dbc9-19cc4dd9b34c.png)



## 手順
1. [GASの記述](#1-gasの記述)
2. [Slack Appの作成](#2-slack-appの作成)
3. [トリガーの設定](#3-トリガーの設定)


## 1. GASの記述

### エディタの準備
まずはGASを記述していく場所(エディタ)の準備です。
↓ スプレッドシートの**ツール**から**スクリプトエディタ**を選択してください。
![エディタを表示](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/563044/1e1b2d76-1332-5769-f973-c4f97a91c205.png)

↓ エディタの簡単な説明です。
![エディタの説明](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/563044/aeed66e0-eefb-5f2b-a665-caf9af7076b3.png)

### 処理の整理
コードを書いてくぞ！！の前にまずは今回の処理を整理しましょう。

- スプレッドシート(家計簿)の情報を取得
- 取得した情報の最新日を調べる
- 取得した情報の最新日と今日を比べる
    - 一致した場合は処理終了
    - 一致しない場合はSlackに通知

今回の処理はざっとこんな感じです。
これをコードに起こしていきましょう！

### コードの記述
それでは実際にコードをエディタに記述していきましょう。
処理を一つ一つ分けて説明していきますので全コードまとめてみたい方は[こちら](#コード全体)へどうぞ！

#### スプレッドシート(家計簿)の情報を取得
```javascript:checkInput()
function checkInput() {
  // シートを取得
  const ss = SpreadsheetApp.getActiveSpreadsheet();
  const sheet = ss.getSheetByName('支出');  // ss.getSheetByName('シート名')
  
  // シート内のすべてのデータを取得
  const data = sheet.getDataRange().getValues();
}
```
<details><summary>ss.getSheetByName('支出')のシート名について</summary><div>
コードの```ss.getSheetByName('シート名')```の**シート名**は、
今回の値を取るシート名なので ↓ で示す「支出」になる。
![シート名](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/563044/342455e7-12a8-eb24-c260-d1a53b7132f1.png)
</div></details>
<details><summary>取得データ(data)について</summary><div>
データ(data)は二次元配列として取得しています。
![二次元配列](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/563044/510ad48b-04e5-aa99-d2ba-551123e2bcf5.png)

</div></details>

#### 取得した情報の最新日を調べる
```javascript:checkInput()
function checkInput() {
  // ～スプレッドシートから情報を取得の処理(上記の処理)～

  const now = new Date();  // 処理を実行したときの日時を取得
  var max = new Date(1999, 0, 1, 0, 0, 0);  // 最新日の初期値(1999/1/1)
  
  // 家計簿の日付の最大値(最新日)を取得
  for (let i = 1; i < data.length; i++) {
    if ( dateComparison(max, data[i][0]) ) { // dateComparisonは後述
      max = data[i][0];
    }
  }

  // ～後述の処理～
}
```
<details><summary>new Date()について</summary><div>
最新日の``new Date()``の引数の月の部分が0になっていますが、月は0から数えるので1月は0となります。(年と日は1から数えます)

</div></details>

```javascript:dateComparison()
function dateComparison(date1, date2) {
  var year1 = date1.getFullYear();
  var month1 = date1.getMonth() + 1;
  var day1 = date1.getDate();
  
  var year2 = date2.getFullYear();
  var month2= date2.getMonth() + 1;
  var day2 = date2.getDate();
  
  if (year1 === year2) {
    if (month1 === month2) {
      return day1 < day2;
    }
    else {
      return month1 < month2;
    }
  } else {
    return year1 < year2;
  }
}
```
<details><summary>dateComparisonについて</summary><div>
dateComparisonは引数の**date1**と**date2**の日付を``date1 < date2``で比較した結果をtrue/falseで返します。
``getMonth()``は0から数える(1月が0になる)ので＋１しています。(年と日は1から数えます。)
<br>
※なぜこのような処理をするかというと、家計簿の入力日は時間が00:00:00になっているのですが、取得した現在の日付は時間も取得しているため比較すると一致になりません。
そのためこのように年月日をそれぞれを比較しております。
</div></details>

#### 取得した情報の最新日と今日を比べる
```javascript:checkInput()
function checkInput() {
  // ～スプレッドシートから情報を取得の処理(上記の処理)～
  // ～取得した情報の最新日を調べる処理(上記の処理)～

  // 最新日と今日の日付を比較する
  // 今日の入力が無い時に処理
  if ( !(now.getFullYear() === max.getFullYear() && now.getMonth() === max.getMonth() && now.getDate() === max.getDate()) ) {
    const msg = `${now.getFullYear()}年${now.getMonth()+1}月${now.getDate()}日\n家計簿の入力がありません。`;
    sendSlack(msg);  // sendSlack()は後述
  }
}
```
<details><summary>日付が一致するときの処理をする場合</summary><div>
if文にelse処理を追加することで日付の一致時の処理も追加することが出来ます。

```javascript
if ( !(now.getFullYear() === max.getFullYear() && now.getMonth() === max.getMonth() && now.getDate() === max.getDate()) ) {
  // 日付が一致しないときの処理
} else {
  // 日付が一致するときの処理
}
```
</div></details>

#### スラックへの送信処理
url代入する値はSlackAppを作成の最後で出てくるので、そちらをコピーして貼り付けます。

```javascript:sendSlack()
function sendSlack(message) {
  const url = '★★★';  // ★★★には後述の内容を張り付ける
  const options = {
    method: 'POST',
    contentType: 'application/json',
    payload: `{"text":"${message}"}`
  };
  UrlFetchApp.fetch(url, options);
}
```
詳しく知りたい方は[こちら](https://qiita.com/kshibata101/items/0e13c420080a993c5d16)をご覧ください。

### コード全体
```javascript
function checkInput() {
  // シートを取得
  const ss = SpreadsheetApp.getActiveSpreadsheet();
  const sheet = ss.getSheetByName('シート名');  // ss.getSheetByName('シート名')
  
  // シート内のすべてのデータを取得
  const data = sheet.getDataRange().getValues();
  
  // 処理を実行したときの日時を取得
  const now = new Date();
  var max = new Date(1999, 0, 1, 0, 0, 0);
  
  // 家計簿の日付の最大値(最新日)を取得
  for (let i = 1; i < data.length; i++) {
    if ( dateComparison(max, data[i][0]) ) {
      max = data[i][0];
    }
  }
  
  // 最新日と今日の日付を比較する
  if ( !(now.getFullYear() === max.getFullYear() && now.getMonth() === max.getMonth() && now.getDate() === max.getDate()) ) {
    // 今日の入力が無い時の処理
    const msg = `${now.getFullYear()}年${now.getMonth()+1}月${now.getDate()}日\n家計簿の入力がありません。`;
    sendSlack(msg);
  }
  
}

function dateComparison(date1, date2) {
  var year1 = date1.getFullYear();
  var month1 = date1.getMonth() + 1;
  var day1 = date1.getDate();
  
  var year2 = date2.getFullYear();
  var month2= date2.getMonth() + 1;
  var day2 = date2.getDate();
  
  if (year1 === year2) {
    if (month1 === month2) {
      return day1 < day2;
    }
    else {
      return month1 < month2;
    }
  } else {
    return year1 < year2;
  }
}

function sendSlack(message) {
  const url = '★★★';
  const options = {
    method: 'POST',
    contentType: 'application/json',
    payload: `{"text":"${message}"}`
  };
  
  UrlFetchApp.fetch(url, options);
}
```


## 2. Slack Appの作成
続いてSlackに通知を送るためにSlackAppを作成していきます。
下記のリンクにアクセスしてください。
[https://api.slack.com/apps](https://api.slack.com/apps)

アクセスしたら**Create New App**をクリックします。
![Appの作成](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/563044/1bd81828-90c0-7393-ecb5-203678808e66.png)

出てきたウィンドウの**App Name**にアプリ名、**Development Slack Workspace**でワークスペースを選択し**Create App**をクリックしてください。
![createApp](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/563044/06e95ac4-54d0-8f01-c0a5-6b60cff731a5.png)

作成出来たら、**Incoming Webhooks**へ移動し、**Activate Incoming Webhooks**をOnにします。
![Incoming webhooks](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/563044/e2691d17-98a1-34fc-5d44-90b895962e11.png)

Onにしたら下にスクロールし**Add New Webhooks to Workspace**をクリックします。
![Add New Webhooks to Workspace](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/563044/ae980994-4d20-d38f-57c8-9895897798b4.png)
上記をクリック後ワークスペースへのアクセス権限とチャンネルの選択が出てきますので、チャンネルを選択し許可してください。

許可が終わったら**Install App**へ移動し、**Webhook URLs for Your Workspace**の**Webhook URL**の**Copy**をクリックしてコピーしてください。
![URL](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/563044/2e0a4f95-1708-3320-6f1d-d68a57024c37.png)

コピーした内容を[こちら](#スラックへの送信処理)のコードの``const url = '★★★';``の★★★ところに張り付けてください。

## 3. トリガーの設定
ここまで終了したら最後は自動で動くように設定します。
### 手動での確認
まずは手動で動くことを確認してみましょう！
エディタで関数を**checkInput**を選択し、実行ボタンを押しましょう。
![実行](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/563044/482d7def-912c-26e2-dd1d-cd8b2d3d435f.png)

初めての場合は認証が必要かもしれません。
下記の写真を見て許可しましょう。
![認証](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/563044/d0b0bcf0-0abf-513f-b05b-75dbc9e238d6.png)

実行に成功するとこのようなメッセージが送られてきます！
![Slack](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/563044/20681832-9314-6314-5155-f531dd416841.png)

---
### 自動化
動くことが確認出来たら自動化をしていきましょう！！

まずはエディタから下記の写真の赤で囲っているところをクリックします。
![トリガーページに遷移](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/563044/d023ac96-2633-0bb4-f383-a33e1768dd04.png)

遷移先のページの**トリガーを追加**をクリックします。
![15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/563044/ce77f9a9-5e8b-a164-3819-5a301a345308.png)

出てきたウィンドウ内容をそれぞれ選択していきます。
「実行する関数を選択」を**checkInput**、「イベントのソースを選択」を**時間主導型**、「時間ベースのトリガーのタイプを選択」を**日付ベースのタイマー**、「時刻を選択」を**午後10時～11時**を選択し保存をクリックしてください。時刻は好きな時刻を選択して大丈夫です！
![トリガー入力](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/563044/528eca5b-0c2d-2fd6-67ba-62f0db53ba4a.png)
これで自動で動くようになりました！！

## おわりに
以上でこの記事の内容は終わりです。
ここまでお付き合いいただき本当にありがとうございました！！
初めてのAdventCalendarでしたが、何とか書ききれたぁ～っていう感じです！

今回はGASでできる事のほんの一部にすぎません。
では他にどんなことが出来るの？？というと...

- Googleフォームの投稿に自動返信
- チャットワーク・LINEへの自動投稿
- メール内容をスプレッドシートに自動転記

などなど書ききれないくらいたくさんあります。

身の回りの業務などが意外と簡単に自動化や、時間短縮できたりするので、
これを機に一度調べてみてGASを活用していただければ嬉しいです！！

## 参考
[https://www.sejuku.net/blog/23115](https://www.sejuku.net/blog/23115)
[https://tonari-it.com/gas-slack-incoming-webhooks-url-fetch/](https://tonari-it.com/gas-slack-incoming-webhooks-url-fetch/)
[https://qiita.com/kshibata101/items/0e13c420080a993c5d16](https://qiita.com/kshibata101/items/0e13c420080a993c5d16)