GAS(Google Apps Script)でGoogleフォームを作成-選ばれた選択肢でメール送信先を変更

つい最近、Google Apps Script(以下GAS)というGoogleフォームを操作・カスタマイズできる機能を知ったので、備忘録。

GAS公式 https://developers.google.com/apps-script

今回やりたいこと

  • Googleフォームで用意した選択肢の内容によって、連絡先を変更する。

店舗をいくつか持っている会社があるとしましょう。ユーザからの問い合わせは選択された店舗の担当者に対応してもらいたいので、回答は店舗担当者に送信されるようにカスタマイズします。

 

そもそもGASとはなんぞ

公式の日本語ドキュメントがないので(ないよね?)アレルギー起こしている人も多いのではないかと思います。

冒頭ではGoogle Formを操作できる〜と申し上げましたが、公式を見ると他の色々なGoogleアプリを操作・カスタマイズできる機能のようですね。以下公式から引用。

Google Apps Script is a rapid application development platform that makes it fast and easy to create business applications that integrate with Google Workspace. You write code in modern JavaScript and have access to built-in libraries for favorite Google Workspace applications like Gmail, Calendar, Drive, and more. There’s nothing to install—we give you a code editor right in your browser, and your scripts run on Google’s servers.

GメールやGoogleカレンダー、ドライブ、といったGoogleのワークスペースと統合できるアプリ開発のプラットフォーム、とありますね。アプリのインストール不要、ブラウザ上で開発できて、みんな大好きJavaScriptで書けるので便利だよ!というわけです。

ご丁寧に、JavaScriptをご存じない方にはレッスンの案内もされています。

Codecademy https://www.codecademy.com/catalog/language/javascript

GASで具体的に何ができるのか

こちらも公式からの引用ですが、

  • GoogleドキュメントやGoogleスプレッドシート、Googleフォームでのオリジナルメニュー、ダイアログ、サイドバーの作成
  • Googleスプレッドシートでのオリジナル機能、マクロの作成
  • Webアプリの開発…Googleサイトでの独立した、あるいは埋め込みのアプリを作ってローンチ
  • Google AdSenseやGoogleカレンダー、Googleドライブ、Gメール、Googleマップとの連携
  • Google Workspace Marketplaceでアドオンを作成して公開

(意訳ですので参考程度でお願いします)

Googleフォームを作成

できることは色々あれど、今回はこのGASを使ってGoogleフォームをカスタマイズ。

  • Googleフォームで用意した選択肢の内容によって、連絡先を変更する

のですが、細分化するとこんな感じ

  1. Googleフォームの送信
  2. 回答者にメール送信
  3. 管理者側へのメール送信(回答の選択肢によってメール振り分け)

1,2まではGAS使わなくてもできますね。ただ、GASで用意しておくと同じフォームを秒で作れるようになるのでその点でも便利です。

Google フォームを作成

今回は、Googleフォームで受け取った値を元に色々操作してみたいと思いますので、一旦Googleフォームを作成して、返ってくる値を確かめてみようと思います。

フォームの内容はこんな感じ

冒頭のメールアドレス設定はタブの設定>回答>メールアドレスを収集するのトグルをONにするとできます。

GASを作成

フォームのメニュー3点リーダからスクリプトメニューを選択します。

初期状態はこれ。

function myFunction(e) {

}

function myFunction() {}とありますね。この{}の中に、カスタマイズしたい処理を書いていきます。myFunctionの部分は、好きな名前に変更してもOKです。今回はフォーム送信時に、連絡のない店舗へお知らせが届くようにしたいのでautoNotifyとします。

ここで()内に引数(今回はeとします。eventのeですが、なんでもOKです)を入れておくと、送信ボタンが押された際に、回答データがeに入り、あらかじめ用意されている関数onSubmitに渡されます。では実際にどのような内容が入っているのか確認してみましょう。

function autoNotify(e) {
  console.log(e)
}

Google フォームとGASを紐付け

Googleフォームの送信ボタンが押された時にスクリプトが実行されるよう、Google フォームとGASを紐付けします。

トリガーを設定

左のサイドメニュー時計アイコンのトリガーを選択し、

トリガーを追加。

トリガーの設定内容は以下の通り。設定できたら保存ボタンを押下します。

  • 実行する関数を選択 -> autoNotify
  • 実行するデプロイを選択 -> Head
  • イベントのソースを選択 -> フォームから
  • イベントの種類を選択 -> フォーム送信時
  • エラー通知設定 -> 毎日通知を受け取る

保存ボタンを押すとGASを承認するためのポップアップが出ますので、任意のGoogleアカウントを選択して次に進んでください。

これでトリガーが設定できました。

フォームが送信された時に入っている値を見てみよう

先ほどGASで書いたeの部分を確認してみましょう。実際にフォームを入力して、送信します。

フォーム画面右上の送信ボタンから、フォーム回答のための共有リンクを取得できます。

個人の別Googleアカウントからフォームを入力し、回答を送信しました。

GASの編集画面に戻り、左サイドメニュー実行数から、ログを確認できます。

オブジェクトで色々な値が取れていますね。

ちなみにconsole.logではなくLogger.logを使うとオブジェクトが返ってきていることしかわかりません。

console.logとLogger.logの使い分けは下の記事で詳しく解説していただいていました(謝謝!)。

GASでログ出力する2つの方法(Logger.logとconsole.log)の紹介と使い分け

回答データeはこのようにオブジェクトで返ってきているので、その返ってきている値から必要な情報を取り出し、処理を書きます。

今回は、

2. 回答者にメール送信
3. 管理者側へのメール送信(回答の選択肢によってメール振り分け)

をするので、回答者のメールアドレスと、選択された店舗情報を取り出す必要があります。回答内容は、Googleフォーム送信ボタンを押して返ってきた情報(e)の中の、response内、getItemResponses部分から取得できます(e.response.getItemResponses)。

{
toString: [Function],
authMode: {
toString: [Function: toString],
name: [Function: toString],
toJSON: [Function: toString],
ordinal: [Function: ordinal],
compareTo: [Function: compareTo],

// ちょっと省略

FULL: [Circular]
},
response: {
toString: [Function],
submit: [Function],
getId: [Function],
getTimestamp: [Function],
getRespondentEmail: [Function],
getItemResponses: [Function],
getGradableItemResponses: [Function],
getResponseForItem: [Function],
getGradableResponseForItem: [Function],
withItemResponse: [Function],
withItemGrade: [Function],
toPrefilledUrl: [Function],
getEditResponseUrl: [Function]
},

// だいぶ省略

triggerUid: '11025413'
}

フォームのレスポンス(e.response)から値を取り出すメソッドは公式にまとめられています。

https://developers.google.com/apps-script/reference/forms/form-response

getItemResponses() ItemResponse[] Gets all item responses contained in a form response, in the same order that the items appear in the form.

*フォームから受け取った値の全てのresponseアイテムを取得。値はフォームに入っているのと同様の順番で格納されています。

便利ですね〜

では早速これで値を取り出して見てみましょう。

GASのエディタに戻り、コードを書き換えます。

function autoNotify(e) {
  // 質問内容と回答を取得
  const itemResponse = e.response.getItemResponses();

  console.log(itemResponse);
}

スクリプトを保存(⌘+s)したら、フォームをもう一度送信し、GASメニューのログを確認します。

[{
toString: [Function],
getScore: [Function],
setScore: [Function],
getFeedback: [Function],
getResponse: [Function],
getItem: [Function],
setFeedback: [Function]
},
{
toString: [Function],
getScore: [Function],
setScore: [Function],
getFeedback: [Function],
getResponse: [Function],
getItem: [Function],
setFeedback: [Function]
},
{
toString: [Function],
getScore: [Function],
setScore: [Function],
getFeedback: [Function],
getResponse: [Function],
getItem: [Function],
setFeedback: [Function]
},
{
toString: [Function],
getScore: [Function],
setScore: [Function],
getFeedback: [Function],
getResponse: [Function],
getItem: [Function],
setFeedback: [Function]
},
{
toString: [Function],
getScore: [Function],
setScore: [Function],
getFeedback: [Function],
getResponse: [Function],
getItem: [Function],
setFeedback: [Function]
}
]

配列の中に、それぞれの質問内容と回答の情報がオブジェクトに格納されています。

1つ目の質問・回答群から順に、配列の[0],[1],[2]…と格納されています。

最初のメールアドレスはフォーム作成時の設定内のトグルから作成したため、e.response.getItemResponsesとは別の場所に格納されています(後述)。

 

フォームで送信された質問と回答をログ出力してみる

では、空の配列 answerListを用意して、フォームから得られた質問と回答を入れてみましょう。

function autoNotify(e) {
  // 質問内容と回答を取得
  const itemResponse = e.response.getItemResponses();

  let answerList = [];

  // 答えた質問の数だけ繰り返し
  for (let i=0; i<itemResponse.length; i++) {
    let question = itemResponse[i].getItem().getTitle();
    let answer = itemResponse[i].getResponse();

    // 回答が入力されていればその値を、そうでなければ未回答を入れる
    answer ? answerList.push(question + ':' + answer)
      : answerList.push(question + ':' + '未回答');
  }
console.log(answerList);
}

スクリプトを保存(⌘+s)したら、フォームをもう一度送信し、GASメニューのログを確認します。

いい感じですね!

回答者にメール送信

フォームの回答ボタンが押されたらまず回答者にお礼メールを送りたいですね。

メール送信には、GmailAppというGメールアプリへのアクセスを提供するクラスを使用します。メソッド一覧はこちら

https://developers.google.com/apps-script/reference/gmail/gmail-app

上記リファレンスのsendEmailメソッドを使います。

まずはsubjectに件名、bodyに本文を定義します。

// 回答者にメール送信
// 回答者のメールアドレスを取得
const recipient = e.response.getRespondentEmail();
// 件名
const subject = '【回答者側 自動通知】お問い合わせ受付完了'
 
// 本文
const body = 'お問い合わせ受付が完了しました。\n\n内容にお間違いがないかご確認頂きますようお願いいたします。\n\n============\n' + answerList.join('\n') + '\n============\n';

その下にメソッドを追加

Gmailapp.sendEmail(recipient, subject, body);

実はGASのスクリプト内でGmailApp.sendEmail(...と入力すると文法も出てきてくれます。便利!

再びフォームを入力してデバッグしましょう!

スクリプトを保存(⌘+s)したら、フォームをもう一度送信し、スクリプトを保存(⌘+s)したら、入力したメールアドレスに届いたメールを確認します。

私はこの際、マニフェストを登録しておらず引っかかりました。ログから下記のようなエラーが出る場合は、マニフェストを追記しましょう。

やり方は以下の記事にまとめました。

GASのメール送信ができない…それならマニフェスト設定だ(appsscript.json)

のですが、調べていたらどうやらGmailAppよりMailAppの方が良さそうだ(配信速度が速いとかなんとか)…ということでメール送信部分は下記のようにしました。

Mailapp.sendEmail(recipient, subject, body);

管理者側へのメール送信(回答の選択肢によってメール振り分け)

さて、メール送信ができたところで今度は、設問1で選ばれた値によって、メール送信先の店舗先を変える…という機能を追加します。

設問1

設問1はitemResponse[0]に格納されています。getResponseメソッドでユーザーの選択した回答を取得し、mailGetShopに代入しました。Array.isArrayで、返ってきた値が配列であることを確認します。

// 管理者側へのメール送信(回答の選択肢によってメール振り分け)
let mailGetShop = itemResponse[0].getResponse();

// console.log(mailGetShop); // ['◯◯工務店','□□ハウス']
// console.log(Array.isArray(mailGetShop)); // true

今回は試しに'◯◯工務店','□□ハウス'の2店舗にチェックを入れて、テストをします。店舗ごとに、名前とメールアドレスがセットになったオブジェクトを作成し、配列shopListに代入。

今回は'◯◯工務店','□□ハウス'の2つがチェックされたので、hogehoge@hogemail.comunchi@hogemail.comにメールが配信されるようにしたいです。

let adminRecipient = [];
const shopList = [
{'◯◯工務店':'hogehoge@hogemail.com'},
{'□□ハウス':'unchi@hogemail.com'},
{'△△不動産':'nekoinochi@hogemail.com'}
];

shopListの数だけ、mailGetShopに入っている値を検索します。やり方はいろいろありましょうが…私は、indexOfで検索した値がなかった場合は-1を返すという特性を利用して、trueだった時にmailGetShopのメールアドレスをadminRecipientへpushする方法を取りました。


for (let i=0; i<shopList.length;i++) {
let target = Object.keys(shopList[i]).toString();
let result = mailGetShop.indexOf(target);
0<=result ? adminRecipient.push(Object.values(shopList[i])): null;
}
console.log(adminRecipient);  // ['hogehoge@hogemail.com','unchi@hogemail.com']

次は、adminRecipient内のアドレスへお知らせメールを送信します。これは先ほどの、回答者へのメール送信と同様ですね。

// 管理者へのメール文
let adminSubject = "【管理者側 自動通知】Googleフォームで新規回答を受け付けました";

let adminBody = 'Googleフォームで新規回答を受け付けました。\n\n内容を確認しご対応をお願いいたします。\n\n============\n'
+ answerList.join('\n')
+ '\n============\n';

//管理者にメール送信
MailApp.sendEmail(strAdminRecipient, adminSubject, adminBody);

ただ、先ほど得られたメールアドレスですが、配列の方がその後色々いじりやすいかと思っていたものの、MailApp.sendEmail()のrecipient(第一引数)はカンマ区切りのStringで渡さなければいけないらしいです。

recipient String the addresses of the recipients, separated by comma

https://developers.google.com/apps-script/reference/mail/mail-app#sendemailrecipient,-subject,-body

というわけで、MailApp.sendEmail(strAdminRecipient, adminSubject, adminBody);の前に以下を1行足します。

let strAdminRecipient = adminRecipient.join(',');

コードがかけたら例に倣って保存し、フォームを送信してデバッグ内容を確認します。

無事通りましたか?通ってくれ!