【初心者向けTips】単純作業はもう嫌だ！GASツール作りの4つの技

この記事は[モチベーションクラウドシリーズアドベントカレンダー2020の9日目](https://qiita.com/advent-calendar/2020/mcs)の記事です。

最近GASでツールを作成したので、その経験を基に、GASツールを作成するための4つの技を紹介します。
この4つの技を覚えれば、GASツールで煩わしい作業から解放されます！

# 4つの技
GASツールを作成するために覚える4つの技は「関数」「イベントトリガー」「スプレッドシート連携」「外部API連携」です。
この4つを覚えればGASツールを作成できます。

関数：プログラムを定義する技
イベントトリガー：関数を起動する技
スプレッドシート連携：スプレッドシートを読み込んだり書き込んだりする技
外部API連携：Slackなどの外部APIと連携する技

## 関数：プログラムを定義する技
プログラムを定義するために関数を作成します。GASは定義した関数を実行することができます。

スプレッドシートのメニューバーから**ツール＞スクリプトエディタ**の順にクリックすると、別タブにスクリプトエディタが表示されます。
![スクリーンショット 2020-12-08 17.50.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/114918/5f5d2289-0516-72b0-b07c-4c8f382ff834.png)
![スクリーンショット 2020-12-08 17.52.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/114918/15d8e16d-ec9f-1a4e-816b-0f857a43cfe1.png)

スクリプトエディタからgsファイルを編集して、以下のように関数を定義します。

```javascript
function hello() {
  // 実行するプログラム
}
```

例ではhello関数を定義しました。helloは定義したい関数の名前にあわせて入力してください。

## イベントトリガー：関数を起動する技
作成した関数も起動しなければ動きません。作成したプログラムを起動するための3つのイベントトリガーを紹介します。

### ボタン
関数を起動するためのボタンを作成して、ボタンがクリックされたら関数が起動するようにできます。

**ボタンの作成方法**
スプレッドシートのメニューバーから**挿入＞図形描画**をクリックすると、**図形描画モーダル**が表示されます。
図形描画モーダルでボタンを作成することができます。
![スクリーンショット 2020-12-08 18.04.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/114918/78ab2418-510f-0ac3-c6b4-559c40a4ffa2.png)
![スクリーンショット 2020-12-08 18.04.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/114918/d07c360e-1309-bbd6-2ed9-d50e21fc9659.png)

図形描画モーダルのメニューバーから**図形＞図形＞ボタンのような図形**を選択して図形描画モーダルにボタンを追加します。

![スクリーンショット 2020-12-08 18.05.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/114918/66592748-07e5-dfb7-0dd0-30581334b47c.png)
![スクリーンショット 2020-12-08 19.03.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/114918/fce99a31-1839-06f6-b7b3-e199d6515ac2.png)

図形描画モーダルにボタンを追加したら、ボタンに文言を入力しましょう。
**保存して終了ボタン**をクリックすると、シートにボタンを作成できます。
![スクリーンショット 2020-12-08 19.04.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/114918/1a7a1b87-7c79-0795-c7ae-592f52c8791a.png)

作成したボタンを右クリックすると、ボタンの右上に**︙**が表示されます。
**︙＞スクリプトを割り当て**をクリックすると、**スクリプトを割り当てモーダル**が表示されます。
![スクリーンショット 2020-12-08 19.05.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/114918/89d88941-9c8d-fa15-0aba-e5870ad02ee3.png)

**スクリプトを割り当てモーダル**で関数を設定できます。
![スクリーンショット 2020-12-08 19.05.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/114918/f476a63d-aadf-21f4-36f3-f90e7f21d906.png)

hello関数を割り当てる場合は、helloと入力して**OK**ボタンをクリックすれば、hello関数を割り当てることができます。

### タイマー
タイマーを設定して、関数を特定の時間に起動するようにできます。

**タイマーの設定方法**
スプレッドシートのメニューバーから**ツール＞スクリプトエディタ**の順にクリックすると、別タブにスクリプトエディタが表示されます。
スクリプトエディタのメニューバーから**現在のプロジェクトトリガー**をクリックすると、別タブにトリガー画面が表示されます。
![スクリーンショット 2020-12-08 19.10.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/114918/444749aa-1346-f304-88a0-3337aec18e19.png)
![スクリーンショット 2020-12-08 19.11.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/114918/11d296f2-9e01-68de-c97a-d1cbf73ff2ef.png)

**トリガーを追加**ボタンをクリックすると、**GAS Botsのトリガーを追加**モーダルが表示されます。
![スクリーンショット 2020-12-08 19.12.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/114918/cafa5b67-fe06-5a6a-301d-9bf1ffcb4981.png)

|設定項目|設定内容|
|---|---|
|実行する関数を選択|タイマーで起動したい関数を選択します。|
|実行するデプロイを選択|**Head**を選択します。|
|イベントのソースを選択|タイマーを設定したいので**時間主導型**を選択します。|

イベントのソースを選択で時間主導型を選択すると、**時間ベースのトリガーのタイプを選択**が設定できるようになります。

* 特定の日時・・・特定の日時に1回関数を起動します。
* 分ベースのタイマー・・・指定の分おき毎に関数を起動します。**1分おき**から**30分おき**の範囲で選択できます。
* 時間ベースのタイマー・・・指定の時間おき毎に関数を起動します。**1時間おき**から**12時間おき**の範囲で選択できます。
* 日付ベースのタイマー・・・特定の時刻に関数を起動します。**午前0時〜１時**から**午後11時〜午前0時**の範囲で選択できます。
* 週ベースのタイマー・・・特定の曜日の特定の時刻に関数を起動します。週は**毎週月曜日**から**毎週日曜日**の範囲で選択できます。時刻は**午前0時〜１時**から**午後11時〜午前0時**の範囲で選択できます。
* 月ベースのタイマー・・・特定の日の特定の時刻に関数を起動します。日は**1日**から**31日**の範囲で選択できます。時刻は**午前0時〜１時**から**午後11時〜午前0時**の範囲で選択できます。

保存ボタンをクリックすると、タイマーの設定が完了します。
![スクリーンショット 2020-12-08 19.13.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/114918/bc910aa5-ed72-d843-0c20-0f69562b3a78.png)

### Webhook
WebhookとはSlackなどの外部サービスのイベントを受信するための仕組みです。
WebhookでイベントをHTTPリクエストとして受信して、関数を起動するようにできます。

**Webhookの実装方法**
Webhookは**doGet関数**/**doPost関数**で実装します。
doGet関数は連携する外部サービスがGETメソッドでリクエストを送信する場合に利用します。
doPost関数は連携する外部サービスがPOSTメソッドでリクエストを送信する場合に利用します。
連携する外部サービスがどのようなリクエストを送信するかによってdoGet関数/doPost関数のどちらを利用するかは変わります。

**doGetの実装例**

```javascript
function doGet(event) {
  const target = event.parameter.target;
  hello(target)
}
```

例ではリクエストのtargetパラメータを取得して、hello関数を実行します。
event.parameterプロパティからリクエストのパラメータを取得できます。

**doPostの実装例**

```javascript
function doPost(event) {
  const parameter = JSON.parse(event.postData.contents);
  const target = parameter.target;
  hello(target)
}
```

例ではリクエストのtargetプロパティを取得して、hello関数を実行します。
event.postData.contentsからリクエストのbodyを取得できるので、JSON.parseでパースしてオブジェクトに変換して利用します。

**複数の外部サービスからリクエストを受け付ける場合**
doGet関数/doPost関数は1つしか実装できないため、doGet関数/doPost関数内でif文などを利用して外部サービス毎に処理を切り替える必要があります。

**Webhook URLの生成方法**
Webhookの関数を作成した後、Webhookを起動するためのURLを生成する必要があります。
スプレッドシートのメニューバーから**ツール＞スクリプトエディタ**の順にクリックすると、別タブにスクリプトエディタが表示されます。
スクリプトエディタのメニューバーから**公開＞ウェブ アプリケーションとして導入…**をクリックすると、「Deploy as web app」モーダルが表示されます。
![スクリーンショット 2020-12-08 19.15.01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/114918/717ad285-352f-2877-200a-e930664e3435.png)
![スクリーンショット 2020-12-08 19.18.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/114918/6954bedc-7980-e882-e6a3-61fe7573f1b6.png)

|設定項目|設定内容|
|---|---|
|Project version|**New**を選択します。|
|Execute the app as|**自分のメールアドレス**を選択します。|
|Who has access to the app:|**Anyone, even anonymous**を選択します。|

`Deploy`ボタンをクリックします。

`Current web app URL`が表示されます。このURLが外部サービスからリクエストを送信するとWebhookが実行できる**Webhook用のURL**です。
![スクリーンショット 2020-12-08 19.19.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/114918/69cc8293-f203-2db3-317d-10dbe2bf5fc5.png)

「非公開」にはできませんので、第三者に漏洩しないように取り扱いには十分にご注意ください。

**Webhook用のURLを生成した後に修正した場合**
修正を反映するためにWebhook用のURLを再生成する必要があります。

## スプレッドシート連携：スプレッドシートを読み込んだり書き込んだりする技
スプレッドシートと連携して、関数の実行結果をスプレッドシートに書き込んだり、関数を実行する用の設定をスプレッドシートから読み込んだりすることができます。

**対象のセルを取得**
スプレッドシートに書き込みをする場合でも読み込みをする場合でも、操作対象のセル範囲を取得する必要があります。
操作対象のセル範囲を取得するには、以下のメソッドを利用します。

* `SpreadsheetApp.getActiveSpreadsheet()`・・・対象のスプレッドシートを取得します。
* `Spreadsheet#getSheetByName(name)`・・・指定のシート名に該当するシートを取得します。
* `Sheet#getRange(row, column)`・・・指定の引数に該当するセル範囲を取得します。
* `Sheet#getRange(row, column, numRows)`・・・指定の引数に該当するセル範囲を取得します。`row, column`で範囲の開始位置を指定して、`numRows`で範囲の終了位置を開始位置から相対的に指定します。
* `Sheet#getRange(row, column, numRows, numColumns)`・・・指定の引数に該当するセル範囲を取得します。`row, column`で範囲の開始位置を指定して、`numRows, numColumns`で範囲の終了位置を開始位置から相対的に指定します。
* `Sheet#getRange(a1Notation)`・・・指定の引数に該当するセル範囲を取得します。文字列で`getRange("A1")`、`getRange("A1:B2")`、`getRange("A2:A")`などと指定します。

`Sheet#getRange(*)`の指定方法が分かりづらい場合、[こちら](https://qiita.com/cazimayaa/items/224daebe536799e5a8a2)が分かりやすいかもしれません。

**書き込み**
対象のセル範囲に値を書き込むには、`Range#setValues(values)`メソッドを利用します。valuesは値の２次元配列です。

以下の例シートのように値を設定する場合は、

||A|B|C|
|---|---|---|---|
|1|A1|B1|C1|
|2|A2|B2|C2|

以下のように実装します。

```javascript
const spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
const sheet = spreadsheet.getSheetByName("例");
const range = sheet.getRange("A1:C2");

range.setValues([
  ["A1", "B1", "C1"],
  ["A2", "B2", "C2"]
]);
```

**読み込み**
対象のセル範囲から値を読み込むには、`Range#getValues()`メソッドを利用します。戻り値は値の２次元配列です。

以下の例シートから値を設定する場合は、

||A|B|C|
|---|---|---|---|
|1|A1|B1|C1|
|2|A2|B2|C2|

以下のように実装します。

```javascript
const spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
const sheet = spreadsheet.getSheetByName("例");
const range = sheet.getRange("A1:C2");

const values = range.getValues();

console.log(values); // output: [["A1", "B1", "C1"], ["A2", "B2", "C2"]]
```

## 外部API連携：Slackなどの外部APIと連携する技
関数から外部APIにHTTPリクエストを送信することができます。
外部API連携することでGASからSlackにメッセージを送信するなどができます。

**URL Fetch App**
GASから外部APIにHTTPリクエストを送信するには、`UrlFetchApp.fetch(url[, params])`メソッドを利用します。戻り値はHTTPResponseです。

引数は主に以下の値を指定します。

|引数|型|説明|
|---|---|---|
|url|String|HTTPリクエストを送信する宛先のURL|
|params.contentType|String|リクエストのContent-Typeを指定します。JSONを送信する場合は`"application/json"`を指定します。|
|params.headers|Object|リクエストヘッダをkey/valueのオブジェクトで指定します。|
|params.method|String|HTTPメソッドを指定します。デフォルトは`"get"`です。|
|params.payload|String|リクエストボディを指定します。GETメソッドの場合は指定できません。オブジェクトを指定すると`application/x-www-form-urlencoded`か`multipart/form-data`かとして扱われます。|

HTTPResponseは主に以下のメソッドを利用します。

|メソッド|戻り値の型|説明|
|---|---|---|
|getContentText()|String|レスポンスボディを取得します。|
|getHeaders()|Object|レスポンスヘッダを取得します。|
|getResponseCode()|Integer|`200`などのHTTPステータスコードを取得します。|

Slackのsampleチャンネルに"hello"というメッセージを送信する場合は、以下のように実装します。

```javascript
const data = {
  channel: "sample",
  text: "hello"
};

response = UrlFetchApp.fetch(
  "https://slack.com/api/chat.postMessage",
  {
    contentType: "application/json"
    headers: {
      "Authorization": "Bearer xoxp-xxxxxxxxx-xxxx"
    },
    method: "post",
    payload: JSON.stringify(data)
  });

console.log(response.getResponseCode()); // output: 200
console.log(response.getContentText()); // output: '{ "OK": true, ... }'
```

# まとめ
GASでツールを作成するための4つの技を紹介しました。
ほとんどのツールは4つの技の組み合わせで作成することができます。
日々の業務やプライベートで自動化したい作業があれば、ぜひ自動化して楽になってもらえれば嬉しいです！

私は4つの技を駆使して、単純作業だった勤怠連絡をツール化して、楽をしてます！

皆様もGASでツールを作成してエンジョイライフを過ごしましょう！

# 【補足】なぜ、GAS？
**GUI/CUIを作るコストが少ない**
GUIはスプレッドシートの機能で簡単に作成することができます。

**実行環境を準備する必要がない**
GASはGoogleのスプレッドシート上で動作するため、実行環境を準備する必要がありません。

**簡単に共有できる**
GASはGoogleのスプレッドシート上で動作するため、他のユーザーにも簡単に共有できます。

# 【おまけ】GASで何を作った？
私は4つの技を駆使して、毎日Slackに勤怠連絡していた作業をツール化しました。

**勤怠連絡**
ワンクリックで勤怠連絡を行いたかったので、ボタンをクリックしたらSlackに勤怠連絡できるようにしました。
**イベントトリガー**のボタンを作成して、ボタンクリックに割り当てた**関数**から**外部API連携**を実行して、Slackに勤怠連絡メッセージを送信できるようにしました。
ボタンは「勤務開始ボタン/勤務終了ボタン/休憩開始ボタン/休憩終了ボタン」を実装し、ボタン毎に勤怠連絡メッセージが変わります。
ツールを作る前はSlackでメッセージと勤怠日時を手動入力してました。

![スクリーンショット 2020-12-08 19.21.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/114918/bd536f40-6e37-91af-1634-2e4e8ebe6127.png)
![スクリーンショット 2020-12-08 19.24.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/114918/bdd91980-83e8-1f1d-ef28-e0941dc17199.png)

**ユーザ設定**
ツールは他の開発メンバーにも共有したかったので、ユーザ設定シートを作って、勤怠連絡メッセージのユーザ名とアイコンを開発メンバー毎に設定できるようにしました。
**スプレッドシート連携**でユーザ設定シートと連携して、ユーザ設定シートからユーザ名とアイコンを読み込んで、勤怠連絡メッセージのパラメータに設定するようにしました。
![スクリーンショット 2020-12-08 19.22.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/114918/b7c95119-7dd2-0891-63b9-b1f72f8894b9.png)

**タイムカード**
勤怠連絡した日時を簡単に視認できるようにしたかったので、タイムカードシートを作成して、タイムカードに勤怠日時を記録するようにしました。
勤怠連絡の操作時に、**スプレッドシート連携**でタイムカードシートと連携して、タイムカードシートに勤怠日時を書き込みました。
タイムカードシートに日々の稼働状況が記録されているため、「日付/出勤時間/退勤時間/非稼働時間」を一覧で見ることができます。
ツールを作る前はSlackに連絡した内容をスクロールで遡って確認してました。月次の稼働報告のために1ヶ月分も遡っていたので大変でした。
![スクリーンショット 2020-12-08 19.21.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/114918/50b66a86-29ef-249e-d6e4-3706ecdfcb96.png)