/**
* 定型メール自動送信テンプレート(Googleスプレッドシート + Apps Script)
* 配布:ミチシルベ業務ラボ(無料・改変自由)
*
* 【準備】
* 1. スプレッドシートに「送信リスト」シートを作り、1行目に見出しを入れる
* 必須:メールアドレス / 名前 / 送信状況 任意:会社名 など自由に追加OK
* 2. 拡張機能 → Apps Script を開き、このコードを貼り付けて保存
* 3. まず DRY_RUN = true のまま sendMails を実行 → 実行ログで文面を確認
* 4. 問題なければ DRY_RUN = false にして実行(送信した行に「送信済み」が入る)
* 5. 毎朝自動で送るなら setupDailyTrigger を1回だけ実行
*
* 件名・本文の {見出し名} は、その行の値に置き換わります(例:{名前})。
* 「送信状況」が空の行だけ送るので、同じ人に二重に送りません。
*/
// ===== ここを書き換える =====
var SHEET_NAME = '送信リスト';
var SUBJECT = '【ご案内】{名前}様 来週の定例会について';
var BODY = [
'{会社名}',
'{名前} 様',
'',
'いつもお世話になっております。',
'来週の定例会の資料をお送りします。',
'ご不明点があれば、このメールにご返信ください。',
'',
'よろしくお願いいたします。'
].join('\n');
var DRY_RUN = true; // true の間は送信せず、実行ログに文面を出すだけ
var MAX_PER_RUN = 20; // 1回の実行で送る上限(送りすぎ防止)
var TRIGGER_HOUR = 9; // setupDailyTrigger で毎日実行する時(0〜23)
// ===== 本体 =====
function sendMails() {
var lock = LockService.getScriptLock();
if (!lock.tryLock(10000)) {
console.log('前回の処理が実行中のため、スキップしました');
return;
}
try {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(SHEET_NAME);
if (!sheet) throw new Error('「' + SHEET_NAME + '」シートがありません');
var values = sheet.getDataRange().getValues();
var header = values[0].map(function (h) { return String(h).trim(); });
var col = {};
header.forEach(function (h, i) { col[h] = i; });
['メールアドレス', '名前', '送信状況'].forEach(function (k) {
if (!(k in col)) throw new Error('1行目に見出し「' + k + '」がありません');
});
var limit = Math.min(MAX_PER_RUN, MailApp.getRemainingDailyQuota());
var count = 0;
for (var r = 1; r < values.length && count < limit; r++) {
var row = values[r];
var to = String(row[col['メールアドレス']]).trim();
if (!to || String(row[col['送信状況']]).trim() !== '') continue;
var statusCell = sheet.getRange(r + 1, col['送信状況'] + 1);
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(to)) {
if (!DRY_RUN) statusCell.setValue('エラー:アドレスの形式');
console.log((r + 1) + '行目:アドレスの形式が不正のためスキップ');
continue;
}
var subject = fill_(SUBJECT, header, row);
var body = fill_(BODY, header, row);
count++;
if (DRY_RUN) {
console.log('[テスト・未送信] ' + (r + 1) + '行目 宛先:' + to + '\n件名:' + subject + '\n' + body);
continue;
}
try {
MailApp.sendEmail(to, subject, body);
statusCell.setValue('送信済み ' + Utilities.formatDate(new Date(), 'Asia/Tokyo', 'yyyy/MM/dd HH:mm'));
} catch (e) {
statusCell.setValue('エラー:' + e.message);
}
}
console.log((DRY_RUN ? 'テスト完了(未送信):' : '送信完了:') + count + '件');
} finally {
lock.releaseLock();
}
}
// {見出し名} をその行の値に置き換える(見出しにない名前はそのまま残す)
function fill_(text, header, row) {
return text.replace(/\{([^{}]+)\}/g, function (m, key) {
var i = header.indexOf(key.trim());
return i === -1 ? m : String(row[i]);
});
}
// 毎日 TRIGGER_HOUR 時台に sendMails を自動実行する(重複しないよう先に削除)
function setupDailyTrigger() {
deleteTriggers();
ScriptApp.newTrigger('sendMails').timeBased().everyDays(1).atHour(TRIGGER_HOUR).create();
console.log('毎日' + TRIGGER_HOUR + '時台に自動実行します');
}
// 自動実行をやめる
function deleteTriggers() {
ScriptApp.getProjectTriggers().forEach(function (t) {
if (t.getHandlerFunction() === 'sendMails') ScriptApp.deleteTrigger(t);
});
}