Gmail の罠
Gmailのメールをスケジュール実行で取得し、自動処理させているときにソレは起こりました。
一度読んだメールを、また読み込んでしまうのです。
はじめにコードを書いたときはスレッドをまたがって重複が起こってしまうとは思いもよりませんでした。

スレッドについて
メールへの返信が繰り返されるときに、Gmailがいい感じにひとまとめにして表示してくれる機能です。ただスレッドにされる条件は明示されておらず、返信でなくともスレッドでまとめられることがあります。
Google Apps Script で Gmail を扱うときはこのスレッド
単位で処理することになります。
スレッド
の中にはメッセージ
(メール)が複数存在します。※単一の場合もありますが
またスレッド
も複数存在する場合があります。
よって以下のようなコードを記述し、各スレッド
・各メッセージ
でループ処理することになります。
// 検索条件
const query = '"tunneltype="ssl-web" reason="login successfully" newer_than:2d -label:logged';
// スレッド取得
const myThreads = GmailApp.search(query);
// スレッド単位でループ
myThreads.forEach((myThread, indexThr) => {
// スレッドからメッセージ取得
const myMessages = myThread.getMessages();
// スレッド内の各メッセージでループ
myMessages.forEach((myMessage, indexMsg) => {
// メール本文を取得
const mailBody = myMessage.getPlainBody();
重複問題
前述のように各スレッド内のメッセージを読み出すことで、GAS からメールを見ることができます。
ここで複数スレッドが存在した場合、何故か異なるスレッドに同一のメッセージが含まれる
事があります。普通にWebブラウザでメールを見たときはそんなことはないはずなのですが、何故か GAS からだとそのような動きになります。
以下はデバッグ用にログを採ったものですが、同じメッセージIDが異なるスレッドに存在していることが分かります。
実は「処理済みフラグを」付けるラベルを設定して重複対策しているのですが、ラベルではこの重複を回避できません。
回避策
処理済みのメッセージID
をどこかに記録しておき、重複チェックすることにしました。
今回はGoogleスプレッドシートにメッセージID
を記録します。
// メッセージIDを取得
const msgId = myMessage.getId();
// 重複メッセージ判定
const textFinder = mySheet.createTextFinder(msgId);
const ranges = textFinder.findAll();
// 重複メッセージが存在しないことを確認(件数ゼロ)
if (ranges.length == 0) {
// メールに対する処理
// 1行目の前に空行を追加
mySheet.insertRowBefore(1);
//メッセージIDをG1セルに記録
mySheet.getRange("G1").setValue(msgId);
}
このコードをつっこむことで無事重複を回避することができました。

考察
メール件数が増えてきたり、処理が重くなったりしたら Google スプレッドシートではなくデータベースなどを利用したほうがいいかもしれませんが、しばらくこれで様子を見てみます。
メッセージIDは世界で一つだけということになっているので、メッセージIDの重複は考えてません。
今回対象にしていたメールがネットワーク機器からのログで、syslog 経由で通知メールとして Gmail で受け取っていたものでした。このような宛先、送信元、件名などが毎回同じになるメールが重複の原因になりやすいのかもしれません。通常のメールであればここまでの重複チェックは不要かも?
以上