Googleフォームからメールを自動返信する①
アンケートやお問い合わせ窓口、出欠確認のツールなど様々な用途で簡単に導入できるGoogleフォーム。
使っている個人や企業の方も多いのではないでしょうか。
ところで、フォームからレスポンスしてくださったクライエントさんに対して、フォローアップはどうされていますか?
フォーム受信のたびにサンクスメールを手動で準備して返信されていますか?
1通、2通ならばそれでも構わないでしょうが、何十通、何百通への対応となると、手作業では負担大です。
そこで、こんなシーンこそ、GASでの自動化に打って付けです。
実は、フォームの回答内容をそのままコピーして回答者に返信するだけの機能、であればGoogleフォームに標準で備わっています。
が、文面をカスタマイズしたり、回答内容によって文章を使い分けたり、といったことをしたいとなると、標準機能だけでは残念ながらできません。
しかし、Google Apps Script(GAS)を使えばそれらのことが実現可能となります。
それでは、GASを使って、サンクスメールの自動返信プログラムを作っていきましょう!
この記事では、アプリを作りながらGASの基礎や関連知識も学ぶことができます。
フォームを準備
下図のようなサンプルフォームを用意します。
今回の用途は「イベント出欠確認」とします。
質問項目は
1. メールアドレス
2. お名前
3. 参加されますか?
の3項目です。
すべての項目を入力必須とします。

※フォーム作成にはGoogleアカウントが必要です。
フォームの作成ページです。
右上の目のアイコン(プレビュー)をクリックすることで、フォーム本体を開くことができます。

フォームに紐づいたスプレッドシートを準備
フォーム作成ページの上部には質問・回答・設定の3つのタブがあります。
真ん中の回答タブをクリックしてみましょう。
図のようなページに変わります。
そして、「スプレッドシートで表示」という項目が現れます。

続いて、「スプレッドシートで表示」をクリックしましょう。
下図のようなスプレッドシートが別タブで開きました。
タイトルは「イベント出席確認(回答)」となっています。
つまり、フォーム「イベント出席確認」から送信された回答データが、フォームに紐づいたこのスプレッドシートに自動で追加保存されていく仕組みになっています。
シートのB〜D列の1行目を見ると、フォームの質問項目がそのままカラムの見出しとして記入されています。
この見出しの下の行にそれぞれの回答データが順次追加されて行きます。
A列の「タイムスタンプ」は質問項目にはありませんが、これはA列に自動で追加される仕様です。

試しに、
- メールアドレス:test@sample.com
- お名前:ボット花子
- 参加されますか?:はい、参加します
とフォームに入力して送信してみると、図のようにスプレッドシートに記録されました。
タイムスタンプも送信日時で自動で記録されています。

GASでプログラミングする
さて、いよいよ本題に入ります。
回答者に対してサンクスメールを送るためには、まずはその送信先のメールアドレスが何より必要ですが、
それは、今回の場合ですと、スプレッドシートのB2のセルから取得できそうです。
というわけで、まずはその値を取得するコードを書いていこうと思うのですが、
その前に、どこにそのコードを書いていくのかの説明が必要ですので、そこから行きましょう。
Apps Scriptを開く
スプレッドシートのメニュー「拡張機能」から「Apps Script」をクリックします。

すると、下図のように Apps Scriptが開きました。
ここがGASのコードを書いていく場所です。
(このように、他のアプリに紐づいたApps Scriptのことを「コンテナバインド・スクリプト」と呼びます。)
function myFunction( ) { } という関数名がデフォルトで記述されています。この{ }の中にコードを書いていきます。
まだタイトルがありませんので、
「無題のプロジェクト」の箇所をクリックして、名前変更ダイアログからタイトルを変更しましょう。
今回は「サンクスメールの自動返信」とします。

シートを取得する SpreadsheetApp.getActiveSheet( )
さて、いよいよ最初のコードです。
SpreadsheetApp.getActiveSheet( )と入力しましょう。
お分かりのように、スペルを打ち込んでいくと、単語補完機能がサポートしてくれます。便利ですね。
また改行をすると自動で字下げ(インデント)が行われます。

SpreadsheetApp.getActiveSheet( )の働きは、スプレッドシート「イベント出席確認(回答)」の中のアクティブなシート、つまり、シート名「フォームの回答 1」のシートを取得します。

クラスとメソッド
フォームやGmail、そしてスプレッドシートなど、Googleの各サービスをGASで操作するためには、まずそのサービスの最上位である親クラスにアクセスする必要があります。
では、スプレッドシート・サービスの親クラスは?というと、それがSpreadsheetAppクラスです。
教室のクラスにはクラスを構成するメンバー(生徒)がいるように、GASのクラスにもクラスを構成するメンバーがいます。
そのメンバーはプロパティと言われます。そして関数でできたプロパティのことをメソッドと呼びます。
メソッドはメソッドごとに仕事が異なります。操作したい目的に応じてメソッドを組み立てていく、それがプログラミングと言ってもよいでしょう。
getActiveSheet( )メソッドは、その名の通りシートの中からアクティブ(有効)なシートを取り出すSpreadsheetAppクラスの中のメソッドです。(このように、関数名はその仕事の内容が分かるように付けられています。自分で関数を作る場合もそれを意識しましょう。)
クラスの中からメソッドを指定する時には、ドット(.)でつなぎます(ドット記法)。
もっとロジカルに学習したい方はご自分で調べてみてください。
Google公式リファレンスはこちら
セルの値を取得する getRange( ).getValue( )
スプレッドシートアプリにアクセスして、有効なシートを選択できました。第一目標のメールアドレスの取得まであと一歩です。
そのために登場していただく次なるメソッドが、getRange( )とgetValue( )メソッドです。
SpreadsheetApp.getActiveSheet( ).getRange(2, 2).getValue( )とし、全体をconsole.logの( );の中に入れます。
そして、スクリプトを保存、関数名が< myFunction >であることを確認して実行ボタンを押します。
実行ログの情報に< test@sample.com >と表示されました。

⚠️ ここで「承認が必要です」というダイアログが表示された場合は、下の記事を参考にアクセス権限の承認を行なってください。
GASを使って見事にフォームから送られたメールアドレスを取得できました!!
getRange( )メソッドでセルの範囲を指定して、getValue( )メソッドでそのセル内の値(情報)を取得したわけです。
ちなみに、
getRange( )はSheetクラスのメソッド
getValue( )はRangeクラスのメソッドです。
親クラスを知るには、公式リファレンスの「戻り値の型」を見ると分かります。
そして、戻り値の型のリンクからそのクラスに属するメソッド一覧を見ることができます。

getRange( )のセル範囲指定方法
1 | getRange(row, col) | row(行番号)、col(列番号)で指定。例えばB2セルであれば、getRange(2, 2) |
2 | getRange(row, col, numRows, numColumns) | row・colを始点としてnumRows行分、numColumns列分の範囲を指定 例えば、getRange(2, 1, 1, 4)であれば、2行1列目を始点として1行4列分となる。 複数のセルを取得できる。 |
3 | getRange(a1Notation) | 例えばB2セルであれば、getRange( “B2" ) |
関数の( )内のrowやcolなどのことを仮引数と言い、引数を格納するための変数です。
getRange(1, 3)と書けば、引数1が仮引数の変数rowに、引数3が仮引数の変数colに格納されます。
変数の名前は自由に決められますが、関数名と同様、変数名も内容が判別しやすいことが大切です。
では次に、今回のスクリプトで、getRange(2, 1, 1, 4)とした場合、どんな結果になるか?
実際にやってみましょう。
下図のような結果となりました。
実行ログの情報を見ると、タイムスタンプの時刻、メールアドレス、名前、参加されますか?の答え、が一度に取得できていることがわかります。
シートの2行1列目を始点として1行4列分のセルデータが一度に取得できたわけです。
メールアドレスだけを取得した場合と異なる点は、
getValue( )をgetValues( )と複数形にしなければなりません。
そして、getValues( )はセルの値を二次元配列で取得します。
図でデータが二重括弧[[ ]]で括られているのはその意味です。
スクリプト全体の変更点としは、変数sheetとvaluesを宣言し、取得したシートと値をそれぞれに代入して、操作性を高めています。こうすることで、console.logの( )内にvaluesとだけ書けばOKです。

変数
変数とは、数値、文字列、オブジェクトなどのデータを格納する「入れ物」のことです。
変数を使うことで、データを一時的に保管したり、データをわかりやすい名前で取り扱ったりすることができるようになります。
変数を使うには、まずその変数を「宣言」する必要があり、変数キーワードの let または const を使います。
let 変数名 または const 変数名 のかたちで宣言します。
上のスクリプトでは、const sheet、const values が宣言の部分になります。
宣言した変数に値を格納することを代入と言い、代入するには、イコール記号「=」を使います。
変数の宣言と値の代入を同時に行うことを初期化と言います。
letとconstの異なる点は、1つには値の再代入ができるかできないかです。
letは再代入ができますが、constはできません。
constは一度代入した値を変えられないため、定数とも言われます。
データを守り、思わぬバグを防ぐ上では、基本的にconstを使うことが推奨されています。
配列
配列を使うことで複数のデータをまとめて扱うことが可能となります。
例えば図のように、3人の名前を出力するのに、データを1つ1つ管理した場合は3回の処理が必要となりますが、配列でまとめて管理すれば1度の処理で済みます。つまり、配列を使うことで処理速度を上げることができます。
配列は複数の要素をカンマで区切り、全体を角括弧[ ]で括ります。要素が文字列の場合は、シングルクォーテーション(')またはダブルクォーテーション(“)で要素を囲います。
配列は複数の入れ物が連結しているような構造になっています。それぞれの入れ物には「0」から順番に番号が振られていて、この番号をインデックスと呼びます。インデックスは1からではなく、0から始まるので注意が必要です。
そして入れ物に格納する値を要素と呼びます。
配列は変数に代入することが可能です。
この例では、const namesに代入しています。
また上のスクリプトでは、const valuesに代入しました。

要素を取り出す | 配列[インデックス] |
要素の代入・上書き | 配列[インデックス] = 値 |

ではこの要領で、配列valuesからメールアドレス、名前、回答の各要素を取り出してみましょう。
まず二次元配列のvaluesを一次元配列に加工してデータを扱いやすくします。
そのためにflat( )を使います。
該当するインデックスを使って各要素を取り出し、それぞれを変数に代入します。
関数名をmyFunctionから仕事内容にふさわしいものに変えましょう。サンクスメールの自動返信なので、sendThanksMailとします。

flat( )はGAS固有のメソッドではなく、JavaScriptのメソッドです。
GASはJavaScriptをベースとしたプログラミング言語であるので、JavaScriptの基本部分を利用することができます。
メールを送信する MailApp.sendEmail( )
さて、いよいよ、取り出したメールアドレスに対して実際にメールを送ってみましょう。
MailAppクラスのsendEmail( )メソッドを使います。
メールアドレスの他にタイトルと本文を変数で初期化します。
sendEmail( )の引数として、email、title、bodyを渡します。
メールアドレスがダミーの場合は、スプレッドシートのB2セルを送受信可能なメールアドレスに書き変えてください。
スクリプトを保存して実行します。

メールの受信トレイを開いてみると、
設定したタイトル、本文のメールが、スクリプト実行と同時刻にちゃんと届いています。

最終行、最終列を取得する getLastRow( )、getLastColumn( )
現在のコードで実際にメールを送信できることが確認できました。
ただし、getRange( )の引数を直書きしている現在のコードでは、(シート2行目の)1人の回答者にしか返信することができません。フォームから次々と送られてくる複数の回答者に対して、自動で返信メールを送ることはこのままではできません。
では、自動化するにはどうすればよいでしょう?
もうお分かりかもしれませんが、引数を直書きするのではなく、仮引数(変数)に格納することで自動化が実現できます。
フォームからの回答はスプレッドシートの最終行に追加されていきますので、最終行を変数化して仮引数とすればOKです。
最終行を取得するgetLastRow( )と最終列を取得するgetLastColumn( )の2つのメソッドを使います。

区別しやすいように、タイトルに②を加えました。
スクリプトを保存して実行します。
受信トレイを見ると、シートからきちんと値を取得して、メール送信が実行できています。

トリガーを設定する
さて、いよいよアプリの最終段階です。
ここまでは、sendThanksMail( )関数をスクリプトから直接実行してサンクスメールを送信させてきましたが、最終目標は、フォームから回答が送られた時に、回答者に対して自動でサンクスメールを送ることです。
そのために必要なのが、トリガーの設定です。
「フォーム送信時」をトリガーとして、sendThanksMail( )関数を実行させます。
では、やっていきましょう。
Apps Scriptの左メニューから「トリガー」をクリック

つづいて、右下の「トリガーを追加」をクリック

つづいて「実行する関数」を確認
「イベントの種類を選択」から「フォーム送信時」を選択
「保存」する

トリガーが設定されました。

フォームからテスト送信
では、最終テストです。
でもその前に、スクリプトを少し修正します。
本文の中に名前と回答も取り入れましょう。
便利なテンプレートリテラルを使います。
・バッククォート( ` )で文字列を囲う
・${ 変数 }のかたちで、変数を文字列に埋め込める
・改行が文字列に含まれる

では、スクリプトを保存して、フォームから送ってみましょう。
・メールアドレス:有効なメールアドレス
・お名前:ボット太郎
・参加されますか?:いいえ、参加できません
で送信します。

受信トレイを開いてみると、、、
無事、サンクスメールが届いています!
トリガーの設定通り、フォーム送信時にsendThanksMail( )関数が作動しました。
本文に名前、回答もちゃんと取り込まれています。

サンクスメールの自動返信プログラムの完成です!
お疲れ様でした。
完成コード
function sendThanksMail() {
// スプレッドシートの最終行と最終列を取得
const sheet = SpreadsheetApp.getActiveSheet();
const lastrow = sheet.getLastRow();
const lastcol = sheet.getLastColumn();
// フォームの最新の回答を取得
const values = sheet.getRange(lastrow,1,1,lastcol).getValues().flat();
const email = values[1];
const name = values[2];
const answer = values[3];
// メール本文を作成して回答者に送信
const title = '【イベント出欠確認】ご回答ありがとうございます';
const body =
`
${name} 様
フォームよりご回答いただきありがとうございます。
◯月◯日のイベント出欠について下記の通り確認しました。
ご回答:${answer}
`;
MailApp.sendEmail(email, title, body);
}