GoogleフォームからApps Scriptを呼び出す方法は2つある

Googleフォームって便利ですよね。「何をどう回答すればよいか」が一目で分かるので、回答する人が迷わず入力できます。

ただ、フォームに回答があったことの通知はとてもお粗末です。フォームの設定からメールで通知できるのですが、メール本文には「回答がありました」という一言と回答結果へのリンクが掲載されているだけ。どんな回答があったのかを知るためには、リンクをわざわざクリックしなければなりません。

そこでよくやるのが、Apps Scriptを使った通知のカスタマイズです。フォーム送信イベントをトリガーにしてApps Scriptを起動し、通知内容をいい感じに整えたり、通知先をSlackやGoogle Chatに変更します。

このフォーム送信イベントですが、回答を保存する場所に応じて、Googleフォームのイベントと、Googleスプレッドシートのイベントの2種類があります。この記事では、それぞれのイベントについて解説します。また、Apps Scriptのテストを楽にする方法も追記します。

# 回答を保存する場所に応じて2種類あるフォーム送信イベント

Googleフォームへの回答は、もちろん、Googleフォームに保存されます。さらに、Googleスプレッドシートを紐づけて、そちらにも保存することもできます。スプレッドシートへの紐づけは、フォームの回答タブの右上にあるスプレッドシートアイコンをクリックすると実行できます。

![Screenshot 2022-12-18 08.00.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/6760/07135038-8eed-2e00-50d1-19b088331bc1.png)

回答を保存する場所に応じて、フォーム送信イベントがそれぞれ準備されています。イベントを処理するApps Scriptも、それぞれフォームもしくはスプレッドシートのスクリプトエディターで記述します。

| | 回答をGoogleフォームに保存 | 回答をGoogleスプレッドシートに保存 |
|--|--|--|
| フォーム送信イベント | https://developers.google.com/apps-script/guides/triggers/events#form-submit_1 | https://developers.google.com/apps-script/guides/triggers/events#form-submit |
| イベントオブジェクトの属性 | authMode, response, source, triggerUid | authMode, namedValues, range, triggerUid, values | 
| 回答へのアクセス方法 | [FormResponseクラス](https://developers.google.com/apps-script/reference/forms/form-response)のresponse属性。[このコード](https://qiita.com/takatama/items/d5ae76811ff96f34114b)でJSON形式に変換できる | JSON形式のnamedValues、もしくは、配列のvalues |
| 処理するApps Scriptが記述するスクリプトディター | Googleフォームから起動 | Googleスプレッドシートから起動 |
| メリット | 回答を編集するURLなど、Googleフォームで管理する情報を参照できる | 回答データを簡単に参照できる |
| デメリット | 回答データを参照するのにひと手間かかる | Googleフォームで管理する情報を参照できない |
 
## Googleフォームのフォーム送信イベント

回答のデータにアクセスするためにひと手間かける必要がありますが、回答を編集するためのURLなど、Googleフォームで管理しているすべての情報にアクセスすることができます。

利用するためには、Googleフォームからスクリプトエディターを起動します。

![Screenshot 2022-12-18 08.10.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/6760/70f6be84-6f32-5a4a-e843-9ff9297cf884.png)

フォーム送信イベントを受け取るために、スクリプトエディターでトリガーを設定します。「イベントのソースを選択」は「フォームから」にします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/6760/5a8b08be-8b1e-2ac5-16af-47da068b8c76.png)

フォーム送信イベントから回答データを参照するには、[FormResponseクラス](https://developers.google.com/apps-script/reference/forms/form-response)の`response`属性にアクセスします。ただ、フォームの種別ごとにクラスが分かれていて、公式ドキュメントをいちいち調べる必要があります。そこで、FormResponseクラスをJSON形式に変換するコードを作りました。クラスが分かれているよりはずっと使いやすいと思います。よろしければご活用ください。


https://qiita.com/takatama/items/d5ae76811ff96f34114b


フォームの種別ごとに、以下のようなJSON形式の回答データに変換されます：

```json
{
  "記述式は？": "記述式です。",
  "段落は？": "段落です。",
  "ラジオボタンは？": "ラジオボタン1",
  "チェックボックスは？": [
    "チェックボックス1"
  ],
  "プルダウンは？": "プルダウン1",
  "均等目盛は？": "1",
  "選択式（グリッド）は？": {
    "行1": "列1",
    "行2": null
  },
  "チェックボックス（グリッド）は？": {
    "行1": [
      "列1"
    ],
    "行2": null
  },
  "日付は？": "2022-02-03",
  "時刻は？": "22:00"
}
```

## Googleスプレッドシートのフォーム送信イベント

Googleスプレッドシートに記録される回答には簡単にアクセスできますが、回答を編集するためのURLなど、Googleフォーム側で管理している情報にアクセスすることが難しい、という特徴があります。

利用するためには、Googleフォームに紐づいたGoogleスプレッドシートからスクリプトエディターを起動します。

![Screenshot 2022-12-18 08.14.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/6760/77e9f089-ab5b-e69e-8009-09e1614a3cf1.png)

フォーム送信イベントを受け取るために、スクリプトエディターでトリガーを設定します。「イベントのソースを選択」は「スプレッドシートから」にします。

![Screenshot 2022-12-18 08.15.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/6760/d4be3d02-f836-cf40-795e-9d37060dc085.png)

フォーム送信イベントから回答データを参照するには、`namedValues`属性もしくは`values`属性にアクセスします。公式ドキュメントに記載された例は次の通りです：

`namedValues`はフォーム送信の質問の名前と値を含むオブジェクトです。値は配列になっています。

```json: namedValues
{
  "First Name": ["Jane"],
  "Timestamp": ["6/7/2015 20:54:13"],
  "Last Name": ["Doe"]
}
```

`values`はスプレッドシートに表示されるのと同じ順序で値の配列です。

```json: values
["2015/05/04 15:00", "amin@example.com", "Bob", "27", "Bill", "28", "Susan", "25"]
```

# JSON形式のフォーム送信イベントならテストが楽になる

Apps Scriptが正しく動作するかテストする時に、手入力でいちいちフォームに回答するのは大変です。そこで、フォーム送信イベントを予め準備しておき、そのイベントを使ったコードをテストすると便利です。

上記したやり方だとフォーム送信イベントをJSON形式で記述できるので、テストを書くのも楽になります。

以下は、フォームへの回答をSlackやGoogle Chatに送信する例です。

## Googleフォームのフォーム送信イベントのテスト

`onFormSubmit()`でフォーム送信イベントを受信して、`handleResponse_()`で通知します。`onFormSubmit()`で[こちらのコード](https://qiita.com/takatama/items/d5ae76811ff96f34114b)を使い、フォーム送信イベントをJSON形式に変換しているのがポイントです。

`testNotify()`はフォーム送信イベントをJSON形式で受信したものとして記述しています。`testNotify()`を実行するだけでSlackやGoogle Chatへの通知をテストできます。

```javascript
// SlackやGoogle Chatへ通知
const WEB_HOOK_URL = 'https://chat.googleapis.com/v1/spaces/xxx/messages?key=xxx&token=xxx';
function notify(text) {
  const response = UrlFetchApp.fetch(WEB_HOOK_URL, {
    method: 'post',
    headers: {
      contentType: "application/json; charset=UTF-8"
    },
    payload: JSON.stringify({ text })
  });
  console.log(response);
}

// Googleフォームの送信イベントをJSON形式に変換
// https://qiita.com/takatama/items/d5ae76811ff96f34114b
// トリガーにこの関数を設定する
function onFormSubmit(e) {
  ...
  handleResponse_(items);
}

// フォーム送信イベントを処理（Google Chatに通知）
function handleResponse_(items) {
  const text = JSON.stringify(items, null, 2);
  notify(text);
}

// SlackやGoogle Chatへ通知するテスト
function testNotify() {
  const items = {
    "email": "hogehogefugafuga@example.com",
    "氏名": "ほげほげ ふがふが",
    "種別": "業務開始",
    "時刻": "9:00"
  };
  handleResponse_(items);
}
```

## Googleスプレッドシートのフォーム送信イベントのテスト

前記したコードに対して`onFormSubmit(e)`だけが異なります。フォーム送信イベントのnamedValuesを参照し、値を配列から文字列に変換しています。他の関数は同じなので省略します。

```javascript
// トリガーにこの関数を登録する
function onFormSubmit(e) {
  const v = e.namedValues;
  const items = {}
  Object.keys(v).forEach(key => items[key] = v[key].join(','));
  handleResponse_(items);
}
```

こちらも`testNotify()`を実行するだけでSlackやGoogle Chatへの通知をテストできて便利です。