Googleフォームの回答内容をメールで受信し、そのまま返信する方法【Apps Script全文付き】

Googleフォームで問い合わせを受け付けていると、回答内容はGoogleスプレッドシートへ自動で保存されます。
ただし、標準の通知メールだけでは、問い合わせ内容を確認するためにスプレッドシートを開く必要があります。
さらに、問い合わせ者へ返信するときには、スプレッドシートからメールアドレスをコピーし、新しいメールを作成しなければなりません。
この記事では、Googleフォームの回答内容を管理者のメールへ送り、その通知メールに返信するだけで問い合わせ者へ回答できるようにする方法を紹介します。
Apps Scriptの全文も掲載しています。
完成後の問い合わせ対応の流れ
設定後は、次の流れになります。
- お客様がGoogleフォームを送信する
- 回答がGoogleスプレッドシートへ保存される
- 回答内容が管理者のメールへ届く
- 管理者が通知メールの返信ボタンを押す
- 問い合わせ者のメールアドレスが返信先に設定される
これまでのように、毎回スプレッドシートを開いてメールアドレスをコピーする必要がなくなります。
今回使用するGoogleフォームの項目
今回の例では、Googleフォームの回答先スプレッドシートに、次の順番で列が並んでいます。
タイムスタンプ
メールアドレス
電話番号
会社名
お問い合わせ種別
当サイトをどこでお知りになりましたか?
お問い合わせ内容(必須)
資料等のアップロード
公式LINEご登録
Apps Scriptでは、この列の順番に沿って回答内容を取得します。
フォームによって質問項目や並び順は異なるため、ご自身のスプレッドシートに合わせて調整してください。
1. メールアドレス欄に入力制限を設定する
最初に、Googleフォームのメールアドレス欄へ入力制限を設定します。
メールアドレス欄に名前や通常の文章が入力されると、返信先として使用できません。
Googleフォームの編集画面で、メールアドレスの質問を選択します。
質問形式は「記述式」にします。
質問カード右下の3点メニューから「回答の検証」を選択し、次のように設定します。
テキスト
メールアドレス
カスタムエラーテキストには、次のような文章を設定します。
正しいメールアドレスを入力してください。
また、メールアドレス欄は必須にしておきます。
これで、メールアドレスとして正しくない内容が入力された場合は、フォームを送信できなくなります。
2. 回答先のGoogleスプレッドシートを開く
Googleフォームの「回答」画面から、回答を保存しているGoogleスプレッドシートを開きます。
スプレッドシートを開いたら、上部メニューから次へ進みます。
拡張機能
Apps Script
Apps Scriptの編集画面が開きます。
3. Apps Scriptへコードを貼り付ける
Apps Scriptに最初から入っているコードを削除し、以下のスクリプトを全文貼り付けます。
管理者への通知先は、スクリプト上部のADMIN_EMAILで設定します。
最初は、自分が確認できるテスト用メールアドレスを設定してください。
/**
* Googleフォームお問い合わせ通知
*
* Googleフォームが送信されると、
* 回答内容を管理者宛てにメール送信します。
*
* 届いたメールに返信すると、
* フォームへ入力されたメールアドレスが返信先になります。
*/
/**
* 管理者通知メールの送信先
*
* テスト後、本番のメールアドレスへ変更してください。
*/
const ADMIN_EMAIL = 'your-test-email@example.com';
/**
* Googleフォーム送信時に実行される関数
*
* Apps Script画面の実行ボタンから直接実行せず、
* フォーム送信時トリガーから実行してください。
*/
function sendInquiryNotification(e) {
if (!e || !e.values) {
throw new Error(
'フォーム送信時のデータを取得できませんでした。' +
'この関数は直接実行せず、フォーム送信時トリガーから実行してください。'
);
}
/*
* スプレッドシートの列順
*
* 0 タイムスタンプ
* 1 メールアドレス
* 2 電話番号
* 3 会社名
* 4 お問い合わせ種別
* 5 当サイトをどこでお知りになりましたか?
* 6 お問い合わせ内容(必須)
* 7 資料等のアップロード
* 8 公式LINEご登録
*/
const row = e.values;
const timestamp = cleanValue(row[0]);
const email = cleanValue(row[1]);
const tel = cleanValue(row[2]);
const company = cleanValue(row[3]);
const category = cleanValue(row[4]);
const source = cleanValue(row[5]);
const message = cleanValue(row[6]);
const uploadedFiles = cleanValue(row[7]);
const lineRegistration = cleanValue(row[8]);
if (!email) {
throw new Error('メールアドレスを取得できませんでした。');
}
if (!isValidEmail(email)) {
throw new Error(
'メールアドレスの形式が正しくありません:' + email
);
}
const subject = createSubject(company, category);
const textBody = createTextBody({
timestamp: timestamp,
email: email,
tel: tel,
company: company,
category: category,
source: source,
message: message,
uploadedFiles: uploadedFiles,
lineRegistration: lineRegistration
});
const htmlBody = createHtmlBody({
timestamp: timestamp,
email: email,
tel: tel,
company: company,
category: category,
source: source,
message: message,
uploadedFiles: uploadedFiles,
lineRegistration: lineRegistration
});
MailApp.sendEmail({
to: ADMIN_EMAIL,
subject: subject,
body: textBody,
htmlBody: htmlBody,
// 返信ボタンを押すと、
// フォーム入力者のメールアドレスが宛先になります。
replyTo: email,
name: 'Webサイトお問い合わせフォーム'
});
}
/**
* メール件名を作成
*/
function createSubject(company, category) {
const companyText = company || '会社名未入力';
const categoryText = category || 'お問い合わせ';
return (
'【Webサイトお問い合わせ】' +
categoryText +
'|' +
companyText
);
}
/**
* テキスト形式のメール本文を作成
*/
function createTextBody(data) {
return [
'Webサイトより、以下のお問い合わせがありました。',
'',
'================================',
'■ 受付情報',
'================================',
'',
'受付日時:',
data.timestamp || '取得できませんでした',
'',
'================================',
'■ お客様情報',
'================================',
'',
'会社名:',
data.company || '未入力',
'',
'メールアドレス:',
data.email,
'',
'電話番号:',
data.tel || '未入力',
'',
'================================',
'■ お問い合わせ内容',
'================================',
'',
'お問い合わせ種別:',
data.category || '未選択',
'',
'当サイトを知ったきっかけ:',
data.source || '未選択',
'',
'お問い合わせ内容:',
data.message || '未入力',
'',
'================================',
'■ その他',
'================================',
'',
'資料等のアップロード:',
data.uploadedFiles || 'なし',
'',
'公式LINEご登録:',
data.lineRegistration || '未回答',
'',
'================================',
'',
'このメールにそのまま返信すると、',
data.email + ' 宛てに送信されます。'
].join('\n');
}
/**
* HTML形式のメール本文を作成
*/
function createHtmlBody(data) {
return `
<div style="
max-width: 760px;
font-family: Arial, 'Hiragino Kaku Gothic ProN',
'Yu Gothic', Meiryo, sans-serif;
line-height: 1.7;
color: #222;
">
<p>
Webサイトより、以下のお問い合わせがありました。
</p>
${createSectionHtml('受付情報', [
[
'受付日時',
escapeHtml(data.timestamp || '取得できませんでした')
]
])}
${createSectionHtml('お客様情報', [
[
'会社名',
escapeHtml(data.company || '未入力')
],
[
'メールアドレス',
createMailLink(data.email)
],
[
'電話番号',
createTelLink(data.tel || '未入力')
]
])}
${createSectionHtml('お問い合わせ内容', [
[
'お問い合わせ種別',
escapeHtml(data.category || '未選択')
],
[
'当サイトを知ったきっかけ',
escapeHtml(data.source || '未選択')
],
[
'お問い合わせ内容',
nl2br(data.message || '未入力')
]
])}
${createSectionHtml('その他', [
[
'資料等のアップロード',
formatUrls(data.uploadedFiles || 'なし')
],
[
'公式LINEご登録',
escapeHtml(data.lineRegistration || '未回答')
]
])}
<div style="
margin-top: 24px;
padding: 14px 16px;
background: #f5f5f5;
border-left: 4px solid #1f5fa8;
">
このメールにそのまま返信すると、<br>
<strong>${escapeHtml(data.email)}</strong>
宛てに送信されます。
</div>
</div>
`;
}
/**
* HTMLメールの表を作成
*/
function createSectionHtml(title, rows) {
const rowHtml = rows.map(function(row) {
return `
<tr>
<th style="
width: 190px;
padding: 10px;
text-align: left;
vertical-align: top;
background: #f5f5f5;
border: 1px solid #ddd;
">
${escapeHtml(row[0])}
</th>
<td style="
padding: 10px;
vertical-align: top;
border: 1px solid #ddd;
word-break: break-word;
">
${row[1]}
</td>
</tr>
`;
}).join('');
return `
<h2 style="
margin-top: 28px;
margin-bottom: 10px;
padding-left: 10px;
font-size: 18px;
border-left: 4px solid #1f5fa8;
">
${escapeHtml(title)}
</h2>
<table style="
width: 100%;
border-collapse: collapse;
">
${rowHtml}
</table>
`;
}
/**
* 回答データを文字列に整える
*/
function cleanValue(value) {
if (value === undefined || value === null) {
return '';
}
return String(value).trim();
}
/**
* メールアドレスの簡易チェック
*/
function isValidEmail(email) {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
}
/**
* HTMLで使用する文字を無害化
*/
function escapeHtml(value) {
return String(value)
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''');
}
/**
* 改行をHTMLの改行へ変換
*/
function nl2br(value) {
return escapeHtml(value).replace(/\r?\n/g, '<br>');
}
/**
* メールアドレスをクリック可能にする
*/
function createMailLink(email) {
const safeEmail = escapeHtml(email);
return (
'<a href="mailto:' +
safeEmail +
'">' +
safeEmail +
'</a>'
);
}
/**
* 電話番号をクリック可能にする
*/
function createTelLink(tel) {
if (!tel || tel === '未入力') {
return '未入力';
}
const safeTel = escapeHtml(tel);
const telHref = String(tel).replace(/[^\d+]/g, '');
return (
'<a href="tel:' +
telHref +
'">' +
safeTel +
'</a>'
);
}
/**
* Google DriveなどのURLをクリック可能にする
*/
function formatUrls(value) {
if (!value || value === 'なし') {
return 'なし';
}
const escapedValue = escapeHtml(value);
return escapedValue.replace(
/(https?:\/\/[^\s、]+)/g,
'<a href="$1" target="_blank" rel="noopener noreferrer">$1</a>'
);
}
4. 管理者のメールアドレスを設定する
スクリプト上部の次の部分を変更します。
const ADMIN_EMAIL = 'your-test-email@example.com';
最初は、自分が確認できるメールアドレスを設定してください。
動作確認が終わったら、実際に問い合わせを受信するメールアドレスへ変更します。
const ADMIN_EMAIL = 'info@example.com';
5. スクリプトを保存する
Apps Script画面上部の保存ボタンを押します。
プロジェクト名は、後から見て分かる名前にしておくと便利です。
例:
Googleフォームお問い合わせ通知
6. フォーム送信時トリガーを設定する
Apps Script画面左側の時計マークを選び、「トリガーを追加」を押します。
次のように設定します。
実行する関数
sendInquiryNotification
実行するデプロイ
Head
イベントのソース
スプレッドシートから
イベントの種類
フォーム送信時
保存時にGoogleアカウントの権限確認が表示された場合は、内容を確認して許可します。
7. Apps Scriptの実行ボタンは押さない
今回のsendInquiryNotificationは、Googleフォームが送信されたときの情報を受け取って動く関数です。
Apps Script画面上部の実行ボタンから直接実行すると、フォームの回答データが渡されません。
そのため、次のようなエラーになります。
フォーム送信時のデータを取得できませんでした。
これはスクリプトの故障ではありません。
テストするときは、実際のGoogleフォームから回答を送信してください。
8. Googleフォームからテスト送信する
Googleフォームを開き、テスト用の内容を入力して送信します。
確認する項目は次のとおりです。
- スプレッドシートに回答が保存されたか
- 管理者のメールアドレスに通知が届いたか
- メール本文に回答内容が表示されているか
- 返信ボタンを押したとき、問い合わせ者のメールアドレスが宛先になるか
- 迷惑メールフォルダへ入っていないか
問題なく動いたら、ADMIN_EMAILを本番用のメールアドレスへ変更します。
9. 実行履歴を確認する方法
メールが届かない場合は、Apps Script画面左側の「実行数」を開きます。
sendInquiryNotificationのステータスを確認してください。
実行履歴がない場合
フォーム送信時のトリガーが設定されていない可能性があります。
失敗になっている場合
実行履歴を開くと、エラー内容を確認できます。
完了になっている場合
スクリプトは正常に動いている可能性が高いため、迷惑メールフォルダや受信側のメール設定を確認します。
10. メールアドレス欄に名前を入れるとエラーになる
フォームのメールアドレス欄に名前などを入力できる状態では、スクリプトが返信先を設定できません。
例えば、メールアドレスとして次の内容が送られた場合です。
山田太郎
この文字列はメールアドレスではないため、スクリプトは次のようなエラーを出します。
メールアドレスの形式が正しくありません
この問題は、Apps Script側で無理に処理するのではなく、Googleフォーム側でメールアドレスの入力制限を設定して防ぎます。
11. 質問項目を変更した場合はスクリプトも確認する
今回のスクリプトでは、スプレッドシートの列番号を使って回答を取得しています。
const timestamp = cleanValue(row[0]);
const email = cleanValue(row[1]);
const tel = cleanValue(row[2]);
そのため、Googleフォームの質問項目を変更すると、列の順番も変わる可能性があります。
注意が必要なのは、次の変更です。
- 質問項目の追加
- 質問項目の削除
- 質問項目の並び替え
- メールアドレス収集設定の変更
- 回答先スプレッドシートの変更
フォームを変更した後は、スプレッドシートの列順とスクリプトのrow[0]、row[1]などが一致しているか確認してください。
12. Googleフォーム標準の通知は必要に応じて停止する
Googleフォームやスプレッドシートの標準通知を有効にしている場合、Apps Scriptの通知と合わせて2通届くことがあります。
Apps Scriptの通知メールだけで十分な場合は、標準の「新しい回答についてのメール通知」を停止しておくと分かりやすくなります。
Googleフォームの問い合わせ対応をメールだけで完結できる
今回の設定により、問い合わせ対応は次の流れになります。
Googleフォームへ入力
↓
回答内容が管理者のメールへ届く
↓
届いたメールに返信
↓
問い合わせ者へ送信
Googleフォームやスプレッドシートを新しいシステムへ置き換える必要はありません。
今ある仕組みを生かしながら、問い合わせ対応の手間だけを減らせます。
今回の仕組みを導入した背景や、問い合わせ対応の流れをどう改善したかは、こちらの記事で紹介しています。

ご自身での設定が不安な場合や、現在使用しているGoogleフォームに合わせた調整が必要な場合もご相談ください。
大がかりなシステムを導入しなくても、今ある仕組みを少し整えるだけで、仕事が楽になることがあります。
今回のような小さなお困りごとも、ぜひご相談ください。一緒に解決の道を見つけていきましょう。
