🧵

同じ件名のメールが処理されない?Gmailスレッド処理の落とし穴を解決

に公開

はじめに

この記事は、Google Apps Scriptを少し触ったことがある方を対象にしています。

Gmail自動化スクリプトを作成中に「同じ件名のメールが複数あるのに、1通しか処理されない」という問題に遭遇しました。原因はGmailのスレッド機能にありました。

この記事では、スレッド処理の落とし穴と、その解決法について実体験をもとに詳しく解説します。

完成したシステムの概要

function sendEmailsAsPDF() {
  var label = GmailApp.getUserLabelByName("send to freee");
  var lastRun = PropertiesService.getScriptProperties().getProperty('last_run');
  var currentTime = new Date().toISOString();
  
  var threads = label.getThreads();
  var processedCount = 0;
  
  for (var i = 0; i < threads.length; i++) {
    var messages = threads[i].getMessages();
    
    // 🔥 重要:スレッド内の全メッセージを処理
    for (var j = 0; j < messages.length; j++) {
      var message = messages[j];
      var date = message.getDate();
      
      if (lastRun && new Date(lastRun) >= date) {
        continue;
      }
      
      var subject = message.getSubject();
      
      try {
        var htmlBody = message.getBody();
        var timestamp = Utilities.formatDate(date, "Asia/Tokyo", "yyyyMMdd_HHmmss");
        var blob = HtmlService.createHtmlOutput(htmlBody)
          .getBlob()
          .getAs('application/pdf')
          .setName(subject + "_" + timestamp + ".pdf");

        var emailTo = PropertiesService.getScriptProperties().getProperty('EMAIL_TO');

        MailApp.sendEmail({
          to: emailTo,
          subject: "SendToFreee: " + subject,
          body: "こちらのメールに添付されているPDFファイルをご覧ください。",
          attachments: [blob]
        });
        
        processedCount++;
        
      } catch (error) {
        console.log("❌ エラー (" + subject + "): " + error.toString());
      }
    }
  }
  
  PropertiesService.getScriptProperties().setProperty('last_run', currentTime);
}

スレッド処理の落とし穴と解決法

問題の発見

最初のコードでは、このような処理をしていました:

// ❌ 問題のあるコード
for (var i = 0; i < threads.length; i++) {
  var messages = threads[i].getMessages();
  var message = messages[messages.length - 1]; // 最新のメッセージのみ取得
  
  // 処理...
}

結果:同じ件名のメールが複数あるのに、1通しか処理されない

原因の調査

デバッグ用コードで詳細を確認:

function debugSubscriptionEmails() {
  var label = GmailApp.getUserLabelByName("send to freee");
  var threads = label.getThreads();
  
  for (var i = 0; i < threads.length; i++) {
    var messages = threads[i].getMessages();
    
    for (var j = 0; j < messages.length; j++) {
      var message = messages[j];
      var subject = message.getSubject();
      var date = message.getDate();
      
      if (subject.includes("サブスクリプション更新")) {
        console.log("📨 サブスクメール発見:");
        console.log("  スレッド: " + (i+1) + ", メッセージ: " + (j+1) + "/" + messages.length);
        console.log("  件名: " + subject);
        console.log("  受信日時: " + date);
        console.log("  最新メッセージ?: " + (j === messages.length - 1 ? "Yes" : "No"));
      }
    }
  }
}

実行結果:

📨 サブスクメール発見:
  スレッド: 3, メッセージ: 1/2
  件名: サブスクリプション更新のお知らせ
  受信日時: Tue Jul 01 2025 10:56:09 GMT+0900
  最新メッセージ?: No

📨 サブスクメール発見:
  スレッド: 3, メッセージ: 2/2
  件名: サブスクリプション更新のお知らせ
  受信日時: Tue Jul 01 2025 12:14:14 GMT+0900
  最新メッセージ?: Yes

判明した事実:

  • 同じ件名のメールが1つのスレッドにまとめられている
  • 最新メッセージのみを処理していたため、古いメールが無視されていた

解決策:二重ループによる全メッセージ処理

// ✅ 解決後のコード
for (var i = 0; i < threads.length; i++) {
  var messages = threads[i].getMessages();
  
  // スレッド内の全メッセージを処理
  for (var j = 0; j < messages.length; j++) {
    var message = messages[j];
    // 各メッセージを個別に処理
  }
}

学んだこと:

  • Gmailのスレッド機能により、同じ件名のメールは1つのスレッドにまとめられる
  • getMessages()で取得できる配列には、スレッド内の全メッセージが含まれる
  • 全てのメールを処理したい場合は、二重ループが必要

実装時のベストプラクティス

1. エラーハンドリング

var emailTo = PropertiesService.getScriptProperties().getProperty('EMAIL_TO');

if (!emailTo) {
  console.log("❌ エラー: EMAIL_TO が設定されていません");
  continue; // または return
}

2. 重複処理防止

var lastRun = PropertiesService.getScriptProperties().getProperty('last_run');
var currentTime = new Date().toISOString();

// 処理対象の判定
if (lastRun && new Date(lastRun) >= date) {
  continue; // 前回実行後に受信したメールのみ処理
}

// 処理完了後に実行時刻を更新
PropertiesService.getScriptProperties().setProperty('last_run', currentTime);

3. デバッグ機能の充実

function debugAllEmails() {
  console.log("=== 全メール確認 ===");
  
  var label = GmailApp.getUserLabelByName("send to freee");
  var threads = label.getThreads();
  var lastRun = PropertiesService.getScriptProperties().getProperty('last_run');
  
  console.log("総スレッド数: " + threads.length);
  console.log("前回実行時刻: " + lastRun);
  
  // 処理対象メールの一覧表示
  // ...
}

まとめ

Gmail自動化における スレッド処理の落とし穴 は、頭の痛い問題でしたが、AI を使うことでサクッと解決までいけました。

重要なポイント

  • Gmailのスレッド機能を理解する:同じ件名のメールは1つのスレッドにまとめられる
  • messages[messages.length - 1]の罠:最新メッセージのみでは不十分
  • 二重ループの必要性:スレッド内の全メッセージを処理するため

この知識があれば、Gmail処理で「なぜか一部のメールが処理されない」という問題を回避できます。

GitHubで編集を提案

Discussion