Gmailの予約送信ミスを送信前に発見|GASで宛先・日時をdry-run監査
Gasおじ|GoogleアカウントをAIエージェント化
予約送信の事故は、送信処理そのものより「宛先の空欄」「過去日時」「同じ内容の二重登録」から起きがちです。この記事ではメールを送らず、スプレッドシート上の送信計画だけを検査する無料GAS例を紹介します。
先に知っておきたいこと
- このコードはメールを送信せず、下書きも作りません。
- 宛先、件名、予約日時、実行可否を入力検証し、結果を監査ログへ出します。
- メール本文や認証情報はログへ残しません。
- 実運用へ移す前に、必ずコピーしたシートと少量の架空データで確認してください。
準備するシート
送信計画シートの1行目へ、to、subject、scheduled_at、enabled の4列を用意します。scheduled_at はスプレッドシートの日時形式、enabled は TRUE または FALSE にしてください。
監査ログという名前のシートも作ります。存在しない場合はコードが作成しますが、送信計画の値は変更しません。
コピーして使える最小コード
const DRY_RUN = true;
function auditGmailSchedulePlanFree() {
if (!DRY_RUN) throw new Error('この無料例はdry-run専用です');
const ss = SpreadsheetApp.getActiveSpreadsheet();
const sheet = ss.getSheetByName('送信計画');
if (!sheet) throw new Error('送信計画シートがありません');
const values = sheet.getDataRange().getValues();
if (values.length < 2) throw new Error('検査するデータ行がありません');
const headers = values[0].map(String);
const required = ['to', 'subject', 'scheduled_at', 'enabled'];
const index = Object.fromEntries(required.map(name => [name, headers.indexOf(name)]));
const missing = required.filter(name => index[name] < 0);
if (missing.length) throw new Error('不足列: ' + missing.join(', '));
const now = new Date();
const seen = new Set();
const results = [];
values.slice(1).forEach((row, offset) => {
const rowNumber = offset + 2;
const to = String(row[index.to] || '').trim().toLowerCase();
const subject = String(row[index.subject] || '').trim();
const scheduledAt = row[index.scheduled_at];
const enabled = row[index.enabled] === true;
const reasons = [];
if (!to || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(to)) reasons.push('宛先形式');
if (!subject || subject.length > 100) reasons.push('件名');
if (!(scheduledAt instanceof Date) || isNaN(scheduledAt.getTime())) reasons.push('日時形式');
if (scheduledAt instanceof Date && scheduledAt <= now) reasons.push('過去日時');
if (!enabled) reasons.push('実行対象外');
const key = [to, subject, scheduledAt instanceof Date ? scheduledAt.toISOString() : 'invalid'].join('|');
if (seen.has(key)) reasons.push('重複候補');
seen.add(key);
results.push([new Date(), rowNumber, reasons.length ? 'HOLD' : 'READY', reasons.join(' / ')]);
});
const log = ss.getSheetByName('監査ログ') || ss.insertSheet('監査ログ');
log.clearContents();
log.getRange(1, 1, 1, 4).setValues([['checked_at', 'row', 'status', 'reason']]);
log.getRange(2, 1, results.length, 4).setValues(results);
console.log(JSON.stringify({ checked: results.length, hold: results.filter(r => r[2] === 'HOLD').length }));
}実行手順
1. 元ファイルをコピーし、架空の宛先と件名を2〜3行だけ入力します。
2. 拡張機能からApps Scriptを開き、コードを貼り付けます。
3. auditGmailSchedulePlanFree を選び、実行します。
4. 監査ログのHOLD理由を確認し、送信計画側を手動修正します。
5. READYでも自動送信へ直結させず、宛先・日時・タイムゾーンを人が再確認します。
失敗しやすい点と停止条件
- スプレッドシートとApps Scriptのタイムゾーンが違う場合は停止します。
- 実在する個人情報を検証用シートへ入れないでください。
- 監査ログには本文を出しませんが、宛先も出力しない設計です。
- GmailやApps Scriptの利用上限、組織ポリシーを別途確認してください。
- この例へ送信命令や時間主導トリガーを足す場合は別の安全審査が必要です。
公開前チェックリスト
- 元データのコピーで試した
- DRY_RUN が true のままになっている
- 過去日時、空欄、重複候補がHOLDになる
- ログへ本文・認証情報・宛先を残していない
- 自動送信や自動削除を追加していない
まとめ
予約送信を安全に始める第一歩は、送信機能を作ることではなく、入力の不備を送信前に止めることです。まずはdry-run監査で宛先・件名・日時・重複を確認し、最終判断を人に残してください。
下書きを安全に整える関連Tipsを見る

GasおじのTips商品一覧を見る

※学習・検証用の例です。Google公式・Google公認の商品ではありません。実データへ適用する前に、権限、利用規約、組織ポリシー、送信先の同意を確認してください。
