こんにちは、凛です。本業の看護師と並行して、GASでちょっとした業務自動化を請け負っています。
お客さま一人ひとりに合わせたメールを送りたい。でも人数分を1通ずつ手書きするのは現実的じゃないし、かといって一斉送信で「お客様各位」にすると途端に事務的になってしまう。このジレンマに、私はずいぶん長いこと悩まされてきました。
手書きしていた頃と、自動化した後で
私には副業のクライアント向けに、毎月レポートを送る仕事があります。最初のうちは、これを全部手作業でやっていました。
Before——スプレッドシートの顧客リストを開いて、一人分の名前をコピーして、Gmailの新規作成画面に貼り付けて、その人の契約商品を確認しながら本文を書き換えて、送信。それを人数分。名前の貼り間違いがないか毎回ドキドキしながらの作業で、月の後半はこれだけで半日つぶれていました。ざっと10時間くらいでしょうか。
After——今は、顧客リストのスプシに行を追加して、ボタン一つでGASを走らせるだけ。人数が何人いようと、一人ひとり名前と商品名が差し込まれたメールが順番に飛んでいきます。作業時間は月30分あれば足りるようになりました。
差し込みメール、いわゆるメールマージというと身構える方が多いのですが、やっていることは案外シンプルです。スプレッドシートのデータを1行ずつ読み取って、テンプレートの穴に流し込んで送る。ただそれだけなんです。ここからは、私が実際に使っている5つのパターンを、簡単なものから順に紹介していきます。
パターン1:まずは一番シンプルな形から
スプシを上から1行ずつ読んで、名前と商品名を本文に差し込んで送る。これが土台です。
function sendMailMerge() {
const sheet = SpreadsheetApp.getActiveSheet();
const data = sheet.getDataRange().getValues();
const header = data.shift();
data.forEach(row => {
const [email, name, product] = row;
const subject = `${name}様、ご注文ありがとうございます`;
const body = `${name} 様
${product}のご注文ありがとうございます。
3営業日以内に発送いたします。
GAS Recipe`;
GmailApp.sendEmail(email, subject, body);
});
}
data.shift() で1行目のヘッダーを外してから、残りの行を1件ずつ処理しています。これだけで「宛先ごとに名前が変わる個別メール」が完成します。
パターン2:テンプレートを別に切り出す
本文をコードの中に直書きしていると、文言を直したいたびにコードを触ることになります。テンプレートを一枚のひな形として切り出しておくと、ぐっと扱いやすくなります。
const TEMPLATE = `{name} 様
{product}のご注文受付ました。
お届け予定: {delivery}
ありがとうございます。`;
function fillTemplate(template, data) {
return template.replace(/\{(\w+)\}/g, (_, key) => data[key] || '');
}
// 使い方
const body = fillTemplate(TEMPLATE, {
name: '田中',
product: '商品A',
delivery: '5月10日'
});
{name} のような目印を置いておいて、それをまとめて差し替える方式です。文面を変えたいときはテンプレート部分だけ直せばいいので、運用がずっと楽になります。
パターン3:見栄えを整えたいときはHTMLメール
プレーンテキストでは味気ない、ボタンやリンクを目立たせたい、という場面ではHTMLメールにします。
const html = `
<div style="font-family: sans-serif;">
<h2 style="color: #3b82f6;">${name}様</h2>
<p>${product}のご注文ありがとうございます。</p>
<a href="https://example.com/order/${id}"
style="background: #3b82f6; color: white; padding: 10px 20px; text-decoration: none; border-radius: 5px;">
注文を確認
</a>
</div>`;
GmailApp.sendEmail(email, subject, '', { htmlBody: html });
htmlBody オプションにHTMLを渡すだけです。ボタンを一つ置けるだけで、受け取った側の印象はだいぶ変わります。
パターン4:請求書などを添付する
PDFの請求書や明細を一緒に送りたいときは、Driveのファイルを添付します。
const pdfBlob = DriveApp.getFileById('PDF_FILE_ID').getBlob();
GmailApp.sendEmail(email, subject, body, {
attachments: [pdfBlob],
name: 'GAS Recipe 運営'
});
DriveApp.getFileById() でファイルを取ってきて、attachments に渡すだけ。請求書の一括送付が一気に片づきます。
パターン5:送ったかどうかを記録する
件数が増えてくると、「この人にはもう送ったっけ?」が分からなくなります。送信のたびにスプシへ結果を書き戻しておくと、この不安から解放されます。
data.forEach((row, i) => {
try {
GmailApp.sendEmail(email, subject, body);
sheet.getRange(i + 2, 4).setValue('送信済 ' + new Date());
} catch (e) {
sheet.getRange(i + 2, 4).setValue('失敗: ' + e.message);
}
});
スプシに「送信済」列を作っておけば、途中で止まっても「未送信」の行だけを再実行できます。うっかり同じ人に2回送ってしまう事故も防げます。
ここから先は、運用しながら身についた工夫
5パターンをそのまま使うだけでも十分回りますが、実際に毎月動かしていると「もう一歩」が欲しくなる場面が出てきます。私が応用として足したものを3つ。
目印は {{name}} のように二重の括弧で
パターン2では {name} と書きましたが、実運用では {{name}}・{{company}} のように二重の括弧にしています。シングルの中括弧はGASのコード中でオブジェクトや配列に使われることが多く、うっかり誤認識するのを避けたいからです。
テンプレートを別シートで持たせておくのもおすすめです。スプシのA1セルにひな形を書いておいて、GAS側から読み込む。こうしておくと、文面の修正がコードを触らずに完結します。
本番の前に、必ず自分宛のテスト送信
これはもう鉄則にしています。全員に送る前に、自分宛だけに一通投げてみる関数を用意しておくんです。
function testSend() {
const testRow = ['自分のメールアドレス', '凛', 'テスト商品'];
const [email, name, product] = testRow;
// 本番と同じロジックでテスト送信
GmailApp.sendEmail(email, `[テスト] ${name}様`, `テスト送信`);
}
差し込みがちゃんと効いているか、改行が変な位置で折れていないか。ここで確認できます。お客さま全員に誤った本文が届いてしまってからでは取り返しがつきません。地味な一手間ですが、これだけは絶対に飛ばさないでください。
送信履歴を残しておくと後がラク
「日時 / 送信先 / 件名 / ステータス」をスプシに残しておくと、あとで「あの人にいつ送ったっけ?」がすぐわかります。特に月次で送るものは、どのクライアントに何月分を送ったかの記録が命綱です。最終列に「送信済み」と書いておいて、次回はその行を飛ばす——これで二重送信ともお別れできます。
つまずきやすいところ
HTMLとプレーンテキストは両方入れておくと安心
HTMLメールは、古いメーラーだと表示が崩れることがあります。念のため htmlBody と body の両方を設定しておくと、HTMLを表示できない環境でもプレーンテキストで読んでもらえます。
GmailApp.sendEmail(email, subject, plainBody, { htmlBody: htmlBody });
body にプレーンテキスト版、htmlBody にHTML版。両方渡しておくだけです。
送信上限にぶつかる
GASのMailAppは1日100件が上限です。GmailAppも同じ。件数が多いなら日をまたいで分散させるか、Google Workspaceアカウントなら1日1500件まで拡張できるので、そちらを検討してください。
残り何件送れるかは MailApp.getRemainingDailyQuota() で確認できます。実行前に残数をチェックして、足りなければ翌日に持ち越す設計にしておくと安全です。
ヘッダー行をうっかり送ってしまう
data.shift() を書き忘れると、1行目の「名前」「メールアドレス」というヘッダーそのものが宛先や本文に入り込んでしまいます。ループに入る前に必ず shift() でヘッダーを外す。これを癖にしておきましょう。
送信上限まわりは、あとで慌てないよう表で頭に入れておくと安心です。
| アカウント種別 | 1日の送信数上限 |
|---|---|
| 無料Googleアカウント | 100通/日 |
| Google Workspace | 1500通/日 |
| 受信者数上限(無料) | 100アドレス/日 |
100通を超えそうなら、MailApp.getRemainingDailyQuota() で事前に確認しておいてください。
手作業に戻れなくなった話
一度この仕組みに慣れてしまうと、もう名前を手でコピペしていた頃には戻れません。顧客が100名いても、コーヒーを一杯淹れている間に全員へ個別メールが飛んでいく。あの月末の半日は一体何だったんだろう、と思います。
用途に合わせてパターンを組み合わせてください。基本の差し込みにテスト送信と送信記録を足すだけでも、実務でじゅうぶん戦えるレベルになります。「個別に送りたいけど時間がない」で止まっている方の、背中を押せたらうれしいです。
関連記事
Gmail自動化をもっと深めたい方は、以下の記事もおすすめです。
- Gmail自動返信をGASで実装する完全手順 — 不在時の自動応答に
- Gmail添付ファイルを自動でDriveに保存 — Drive連携
- フォーム送信者へ自動返信メールを送るGAS — フォームと組み合わせた応用
これらと組み合わせると、Gmailの自動処理が一気に整います。
この記事を書いた人:凛
2児2児のママで現役ナース。夜勤明けの細切れ時間を副業GASに投じ、月5〜8万円の副収入を継続中。「看護師でもコードは書ける」を合言葉に、家事育児とプログラミングを両立する等身大の情報を発信しています。掲載コードは構文とAPI仕様を確認していますが、実際に送る前にお使いの環境でテスト送信してください。