Gmailの自動ラベルが競合する前に確認|GASで仕分けルールをdry-run監査
ツールおじ|GoogleアカウントをAIエージェント化
「請求」と「請求書」のような似た条件を増やすと、同じメールが複数ルールに当たり、どのラベルを優先するか分からなくなることがあります。いきなり受信トレイへ適用する前に、まずルール表だけを検査して止める方法を紹介します。
この記事のコードはGmailを読み取らず、ラベルも作成・変更しません。スプレッドシート上の仕分けルールから、空欄、優先順位の重複、キーワードの重なりを確認する無料範囲の例です。
先に知っておきたいこと
- できること:ルール表の入力不備と競合候補をdry-runで洗い出す
- しないこと:メール検索、本文取得、ラベル作成、ラベル付与、送信、削除
- 必要なもの:GoogleスプレッドシートとApps Script
- 安全設計:ルール表を更新せず、キーワードやラベル名をログへ出さない
準備するシート
シート名を「LabelRules」にして、1行目へ次の列を用意します。
- A列:priority(1から999の整数。小さいほど先に判定)
- B列:keyword(件名判定に使う文字列)
- C列:label(付けたいラベル名)
- D列:enabled(TRUEまたはFALSE)
- E列:note(任意のメモ)
サンプルは「10/請求書/経理/請求/TRUE」「20/請求/経理/確認/TRUE」のように入力します。この2行はキーワードが重なるため、監査結果では競合候補になります。
コピーして使える最小コード
function auditGmailLabelRuleConflictsFree() {
const CONFIG = {
SHEET_NAME: 'LabelRules',
MAX_RULES: 200,
DRY_RUN: true
};
if (CONFIG.DRY_RUN !== true) {
throw new Error('安全のためDRY_RUN=trueで実行してください。');
}
const sheet = SpreadsheetApp.getActiveSpreadsheet()
.getSheetByName(CONFIG.SHEET_NAME);
if (!sheet) {
throw new Error('LabelRulesシートが見つかりません。');
}
const values = sheet.getDataRange().getDisplayValues();
if (values.length < 2) {
throw new Error('監査するルールがありません。');
}
const headers = values[0].map(normalizeRuleText_);
const required = ['priority', 'keyword', 'label', 'enabled'];
required.forEach(function(name, index) {
if (headers[index] !== name) {
throw new Error('1行目の列名または列順を確認してください。');
}
});
const rows = values.slice(1);
if (rows.length > CONFIG.MAX_RULES) {
throw new Error('一度に監査できるのは200行までです。');
}
const issues = [];
const activeRules = [];
const priorityRows = new Map();
const keywordRows = new Map();
rows.forEach(function(row, index) {
const rowNumber = index + 2;
const priorityText = String(row[0] || '').trim();
const keyword = String(row[1] || '').trim();
const label = String(row[2] || '').trim();
const enabledText = normalizeRuleText_(row[3]);
const isBlank = !priorityText && !keyword && !label && !enabledText;
if (isBlank) return;
if (!['true', 'false'].includes(enabledText)) {
issues.push({ row: rowNumber, type: 'ENABLED_FORMAT' });
return;
}
if (enabledText === 'false') return;
const priority = Number(priorityText);
if (!Number.isInteger(priority) || priority < 1 || priority > 999) {
issues.push({ row: rowNumber, type: 'PRIORITY_INVALID' });
}
if (!keyword || keyword.length > 100) {
issues.push({ row: rowNumber, type: 'KEYWORD_INVALID' });
}
if (!label || label.length > 225 || /[\u0000-\u001F]/.test(label)) {
issues.push({ row: rowNumber, type: 'LABEL_INVALID' });
}
if (!Number.isInteger(priority) || !keyword || !label) return;
const normalizedKeyword = normalizeRuleText_(keyword);
if (priorityRows.has(priority)) {
issues.push({ row: rowNumber, type: 'PRIORITY_DUPLICATE', withRow: priorityRows.get(priority) });
} else {
priorityRows.set(priority, rowNumber);
}
if (keywordRows.has(normalizedKeyword)) {
issues.push({ row: rowNumber, type: 'KEYWORD_DUPLICATE', withRow: keywordRows.get(normalizedKeyword) });
} else {
keywordRows.set(normalizedKeyword, rowNumber);
}
activeRules.push({ row: rowNumber, priority: priority, keyword: normalizedKeyword });
});
for (let i = 0; i < activeRules.length; i++) {
for (let j = i + 1; j < activeRules.length; j++) {
const a = activeRules[i];
const b = activeRules[j];
if (a.keyword !== b.keyword && (a.keyword.includes(b.keyword) || b.keyword.includes(a.keyword))) {
issues.push({ row: b.row, type: 'KEYWORD_OVERLAP', withRow: a.row });
}
}
}
const summary = {
dryRun: true,
scannedRows: rows.length,
activeRules: activeRules.length,
issueCount: issues.length,
issues: issues
};
Logger.log(JSON.stringify(summary));
return summary;
}
function normalizeRuleText_(value) {
return String(value || '').normalize('NFKC').trim().toLowerCase();
}実行前の準備
1. 実データではなく、シートをコピーして少量のサンプルを入力します。
2. スプレッドシートの「拡張機能」からApps Scriptを開き、コードを貼り付けます。
3. auditGmailLabelRuleConflictsFree を選び、実行します。
4. 実行ログのissueCountと、issuesに出た行番号・種類を確認します。
5. 元のルール表を手作業で直し、issueCountが0になるまで再実行します。
結果の読み方
- PRIORITY_INVALID:優先順位が整数ではない、または範囲外
- PRIORITY_DUPLICATE:同じ優先順位が複数ある
- KEYWORD_INVALID:キーワードが空欄、または長すぎる
- KEYWORD_DUPLICATE:表記を正規化すると同じキーワードになる
- KEYWORD_OVERLAP:「請求」と「請求書」のように包含関係がある
- LABEL_INVALID:ラベル名が空欄、長すぎる、または制御文字を含む
- ENABLED_FORMAT:TRUE/FALSE以外が入力されている
ログには行番号とエラー種別だけを出します。キーワードやラベル名をそのまま記録しないため、社内用の分類語がログへ残りにくい構成です。
制限と安全上の注意
- このコードはGmail検索条件として正しいかを判定しません。
- Gmail上に同名ラベルが存在するか、実際のメールが何件一致するかは確認しません。
- キーワードの包含は候補を広めに出すため、意図した重なりも警告されます。
- 大文字・小文字、全角・半角を正規化して比較しますが、言葉の意味までは判断しません。
- 実際に自動ラベルを付ける処理へ進む場合も、少量のテスト、件数上限、停止条件、手動確認を別途用意してください。
- メール本文、差出人、宛先、認証情報をログへ出さないでください。
停止条件
- issueCountが1件以上なら自動適用へ進まない
- ルールが200行を超えるなら対象を分割する
- 権限要求や対象シートが想定と違う場合は実行を止める
- 競合の優先順位を説明できない場合は担当者へ確認する
まとめ
Gmailの自動仕分けは、メールへ触る前にルール表の競合を減らすと安全に始められます。まずは読み取り専用のdry-runで行番号と問題の種類だけを確認し、意図しない多重一致を手作業で解消してください。
メール作業を「送信せず」下書きから整える有料Tipsを見る
Gmail下書きテンプレGAS Proを見る

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

※学習・検証用の例です。実データへ適用する前にコピー環境で確認してください。Google公式・Google公認の商品ではありません。
