【GAS初歩】会員登録（メールアドレスを登録）したかをメールで教えてくれるフォームを作成する

登録会員だけが申し込めるイベントを立てると、かならず明らかに登録していない会員が申し込んで来ることがある。また、会員登録しているけど登録時と異なる情報を入力してきて申し込めなくて行き詰まる会員がいる。
問い合わせをもらえたら対応するが、そういう問い合わせに限ってイベント開始３０分前〜イベント開催中に来る。そういう行動をしてしまう人達それぞれによんどころない事情があるのは分かるが、ワンオペで無償イベントを運営している時にそれをされるのは本気で勘弁して欲しい…[^1][^2]。
そこで、会員が自ら登録会員か否かを調べることのできる仕組み（フォーム）を作成した。

# 完成見本
簡単な説明書きとメールアドレスの入力欄と送信ボタンがあるだけのシンプルなフォーム。
![04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/62270/7fc8ff3f-cbc4-c45c-8a44-ff7cf4913803.png)

# 作業すること（全体の流れ）
1. メアドを入力して送信するだけのGoogleフォームをつくる
2. 登録アドレスの一覧（とNGアドレス一覧）をつくる
3. コードを書く
4. フォーム送信時のトリガを作成する

## メアドを入力して送信するだけのGoogleフォームをつくる
Google フォームを新規作成して、「質問」と「回答」タブは特にいじらずに「設定」へ。（今回はなんとなく先にテーマカラーを設定済）
![02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/62270/a813c24c-d51f-36a0-5f74-19afb2330d48.png)
「確認メッセージ」は、手続きが無事に終わったことが分かるようなソコソコ気の利いた文章を入れておく。
![03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/62270/a9f19a9e-a466-8b8f-a5ff-c9cdeafbf05e.png)
実際に送信完了するとこんな感じに表示される。
![12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/62270/84f08211-480c-ef89-5994-627563337761.png)
「質問」タブのタイトルや説明書きを書く。
![01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/62270/9af3e331-ba3e-d584-a65f-333082a267bd.png)

一通り設定し終わったら、右上の目のマークをクリックして実際の表示を確認する。なお、「Google にログインすると作業用を保存できます」と表示されているが、保存しない設定にしているので、実際にログインしても保存されない（この表示紛らわしいからやめてほしい…）。
![04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/62270/7fc8ff3f-cbc4-c45c-8a44-ff7cf4913803.png)

## 登録アドレスの一覧（とNGアドレス一覧）をつくる
会員登録は事前に**何かしらの方法で行っている**ものとし、新規作成したスプレッドシートに情報を整理していく。
1つめのシートに登録会員のアドレス一覧…要するにホワイトリストと、2つめのシートに凍結アカウントのアドレス一覧…要するにブラックリストを作成する（安全面に関する懸案事項は脚注で…[^3]）。
今回、ホワイトリストのシートは「AllowUser」、ブラックリストのシートは「DisallowUser」という名前にしたが、適宜お好みで（ただしソースコードに影響するので、名前を変えたらソースコードもそのように変更を!）。
### 登録会員のアドレス一覧（ホワイトリスト）
今回は簡便のためにA列にユーザ名、B列にメールアドレスというフォーマットにした。
![05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/62270/9866eeb7-b927-7a38-8bef-a18e7112378a.png)
### 凍結アカウントのアドレス一覧（ブラックリスト）
基本的には不要だが、必要に応じて作成を。
私の場合、過去に素行に問題のあった参加者に対して、自主的に参加をご検討頂く時間が欲しかったので用意した。
A、B列はホワイトリストと同じだが、「なぜ凍結したか」の可視化のために、C列に**0以外**の数字を適宜入力。分類のための値なので、好みの数字を自由に。
![06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/62270/7f2332b0-d49f-0f64-f4a9-910a2dd6bdd5.png)

## コードを書く
コードを書く必要があるのでスクリプトエディタを開く。
![15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/62270/b2da6012-fd1a-94b8-a662-e72c02158ac6.png)
フォームを送信したらメールアドレスを取得してあれやこれやの処理をする関数 `FormAction()` を作成する。
（ソースコード全体はページ最下部に有）

```javascript:コード.gs
function FormAction(e) {
  if(!e) return;
  const em = e.response.getRespondentEmail();
  e.source.deleteResponse(e.response.getId());
  //（中略）
}
```

### メールアドレスの取得
「設定」→「回答」→「メールアドレスを収集する」で収集したメールアドレスは [FormResponse クラス](https://developers.google.com/apps-script/reference/forms/form-response)の `getRespondentEmail()` 関数で取得できる。
実際のフォームの回答は[フォームのイベントトリガー](https://developers.google.com/apps-script/guides/triggers/events#google_forms_events)の response （FormResponse クラス）に入っているので `e.response.getRespondentEmail()` となる。

### フォームの回答の削除
回答を収集したあとにフォームの回答を記録したままにしておく必要がないので、即座に消す。
FormResponse クラスの `getId()` で現在処理中の回答を特定することができるので、[Form クラス](https://developers.google.com/apps-script/reference/forms/form)の `deleteResponse()` を呼び出して回答を1件削除する。

### ホワイトリスト、ブラックリストのID?
15行目の `[ID]` にはホワイトリスト、ブラックリストを作成したスプレッドシートのIDを入れる。

```javascript
const ID_ST_DB = '[ID]'; //ホワイトリスト、ブラックリストを管理
```
IDの手っ取り早い確認方法はスプレッドシートのURLの `https://.../d/` と `/edit#...` で挟まれた部分を見ること。
![13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/62270/d524841e-92e8-e329-d608-da6fad58fee1.png)
この場合は `1rlbO0f18KQFmel8U01HhGqGBEvj7hrEsxQAfk2QFMeQ` がIDなので

```javascript
const ID_ST_DB = '1rlbO0f18KQFmel8U01HhGqGBEvj7hrEsxQAfk2QFMeQ'; //ホワイトリスト、ブラックリストを管理
```

…に書き換えればOK。

### ホワイトリストに載っているアドレスか調べる
使い回しが聞くように関数化した。
処理は単純で、引数(em)にメールアドレス（文字列）を与えると、スプレッドシートのB列から em と完全一致なセル（文字列）を探す。一覧にあれば true を、なければ false を返す。

```Javascript
function ChkAllowUser_(em)
{
  let l;
  if(em === '') return false;
  if(l === undefined){
    const st = SpreadsheetApp.openById(ID_ST_DB).getSheetByName(ST_NAM_ALLOW);
    l = st.getRange(`B2:B${st.getLastRow()}`)
                    .getValues().flat();
  }
  return (l.indexOf(em) < 0) ? false : true;
}
```

`ChkAllowUser_()` 呼び出し元（FormAction()内）はこんな感じ。
クオータ（Quotas for Google Services）の "[Current quotas](https://developers.google.com/apps-script/guides/services/quotas#current_quotas)" の通り、1日あたりのGMailの送信回数は制限があり、むやみやたらと問い合わせるいたずらをされたら適わんので、登録者だった場合だけメールを送信するようにした。（実際に登録している人がこの記事を読んで何度も問い合わせる悪戯をしようものならアカウントを凍結する!!😡）

```javascript
  if(!ChkAllowUser_(em)) return;
  try{MailApp.sendEmail({ name: ADMIN_NAME,
                          to: em,
                          subject: `【${PRJ_NAME}】メールアドレスの登録確認`,
                          body: `${em}${ML_BODY_ALW}`});
  }catch(e){Logger.log(`メール送信失敗（登録済）：${e}`);}
```
なお、[try...catch](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Statements/try...catch)は「保険」程度。

### ブラックリストを使わない場合
ブラックリストが不要な場合は、下記33〜40行目のコードを削除またはコメントアウトする。

```javascript:コード.gs
  if(ChkDisallowUser_(em)){
    try{MailApp.sendEmail({ name: ADMIN_NAME,
                            to: em,
                            subject: `【${PRJ_NAME}】メールアドレスの登録確認`,
                            body: `${em}${ML_BODY_DAL}`});
    }catch(e){Logger.log(`メール送信失敗（凍結）：${e}`);}
    return;
  }
```

## フォーム送信時のトリガを作成する
左の時計マークからトリガーを表示して、右下「トリガーを追加」を選ぶ。
![08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/62270/9884c594-1e0a-ad96-ebd1-bf189b856d16.png)
必要な設定をしたら「保存」する。
![09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/62270/d30dd6d0-b950-73a6-17b7-a6ea42dc201c.png)
このイベントトリガーを実行するのはどのアカウントか尋ねるポップアップが表示されるので、**メールの送信元となるアカウント**を選ぶ。
![10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/62270/3b58c82e-12f9-7e95-97fd-3ac328aba431.png)
たまにポップアップブロックで先のダイアログが表示されないことがあるので、その場合はポップアップを許可してから、もう一度「保存」する。
![14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/62270/5746c561-7e81-5fd3-3bde-9867fb8630fa.png)
ああだこうだ尋ねられるので、内容を確認して問題なければ「許可」する。
![11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/62270/fa80c483-1597-8738-3a82-4e0ce95ebbcc.png)

# テスト
登録アドレス、NGアドレス、そうでないアドレスの3通りで動作を確認して意図通りに動けば作成完了。

# コード全体
* [check_registed_mail.gs（登録されたメールアドレスか確認する）](https://github.com/togazo/samples/blob/master/others/GAS/check_registed_mail.gs)

[^1]: しかも「主催者として当然〜な対応すべき」とかなんとかブチギレてくるし…
[^2]: connpassやDoorkeeperやPeatixなどの既存のイベント管理サービスを使えば良いという意見もあるだろうが、既存サービスではどうにもできないような特殊案件に対応できないため、Googleフォームで自作している。そうしないと、例えば「1名参加」で受け付けたはずの参加者が「友達を同伴するかもしれないが直前まで人数が確認できない（結局『3家族』で参加）」「家で兄弟喧嘩が勃発して下の子が落ち着かないので、急遽m人連れてきた」「祖父母両親兄弟でその様子を終始見学したい」となる場合がある。子供向けイベント、こういう謎申し込みが時々来る…。
[^3]: 安全面に精通した卓越した知識と技術力をお持ちの方から「個人情報…メールアドレスをGoogleのスプレッドシートで管理するとは何事か!!漏洩したらどうする!安全ではない!」と、大変なお叱りを受けそう。こちとら無脳な素人が無能なりにあんな注文やこんな注文に**すべて無償で**対応しているので、対応できる限界はこんなところかな、と…。