※この記事は「身の回りの困りごとを楽しく解決! by Works Human Intelligence Advent Calendar 2024」の参加記事です。
はじめに
背景
メールって、気付くと大量に溜まってしまって、Google Driveの容量を圧迫してしまったりしませんか?
不要なメールは減らしたいとは思いつつも、メールマガジンやアラートメール、ワンタイムパスワードのメールなど、結構な数のメールが日々届くため、毎日整理するのは大変です。
また、通知系のメールなどは、届かないのも困ってしまうので、これまではある程度溜まったら検索して一括削除、といったことをやっていました。
今回、もっと楽に整理をしたいと思い、Google Apps Script(GAS)を使って古いメールの削除を自動化する仕組みを構築しました。
この記事では、その仕組の内容とスクリプトを紹介します。
作成した仕組の特徴
とはいえ、GASを用いたメール整理自動化は、比較的ネット上にも情報が多い内容です。
自分が作成した仕組みは、もうちょっと応用的な、以下の特徴を持った仕組みです。
-
指定したメールアドレスから届いたメールのみを対象とする
- メールマガジンや通知メールなど、特定のメールを対象にできるようにしました
- 対象のメールアドレス毎に保持期間を指定できる
- このメールマガジンは2週間、この通知メールは1年など、保持期間を指定できます。
-
Gmailアプリ上だけで完結できる
- 対象のメールアドレスや保持期間をSpreadsheetで管理しますが、Spreadsheetを開かずにGmailアプリ上の操作だけで対象の追加、保持期間の変更などの操作を実施できます
特に3つ目が重要で、メール整理のためにSpreadsheetにメールアドレスを転記して・・・といった作業はやりたくありませんでした。
Gmailアプリ上で、しかもスマホからも操作できるようにしたため、整理がとても楽に行えるようになりました。
作成した仕組の使い方
仕組の使い方はこうです。
すべてGmailアプリ上で操作を行います。
- 一定期間後に削除したいメールを選んで「Cleaner-xx(xxは期間指定)」というラベルを付与する
- 一定時間ごとにスクリプトが動作し、「Cleaner-xx」のラベルが付いたものを取得、Spreadsheetに記録して「AutoClean-xx」というラベルに付け替える
基本的にはこれだけです。
画像のように、「Cleaner-14d」とラベルをつけると、14日間保持する設定になります(「Cleaner」ラベルは「Cleaner-1m」と同等です)。
スクリプトが動作したあとは、2枚目のようにAutoClean-xxにラベルが置き換わります。
また、このAutoClean-xxラベルは、対象のメールから届いたもの全てにラベルが付くため、「このメールは削除対象にしていたっけ?」といったことを調べる必要はありません。
ラベルを付けたものは、以下のようにSpreadsheetに転記されていきます。
期間の指定はラベルの末尾xxで行います。
dは日数、mは月数、yは年数です。
Gmailの検索仕様のolder_thanの仕様に準ずるため、詳しくは以下のページを御覧ください。
期間指定は自由に増やすことが可能なので、「10日保持するようにしたい」と思ったら、「Cleaner-10d」というラベルを新たに作って付与するだけです。
Spreadsheetを開いて設定する、といったことはしなくて良いのです。
唯一、対象を除外する場合のみ、Spreadsheetを開いて対象行を削除してやる必要があります。
(そこもラベルで指定できるようにすることも可能ですが、頻繁にある作業ではないのでやめました)
コードの概要
機能説明
-
main
関数:
日中定期的に起動し、ラベル「Cleaner-xx」を元にメールを整理し、管理情報をスプレッドシートに記録します。 -
deleteThreads
関数:
毎日0時頃に起動し、指定期間以上前のメールを検索してゴミ箱に移動します。
それぞれGASのトリガーを設定し、main関数の方は10分おきに実行、deleteThereads関数の方は1日おきに0時-1時に実行するようにしています。
コード
実際のコードは以下のとおりです。
// Compiled using gmail-cleaner 1.0.0 (TypeScript 4.9.5)
"use strict";
/**
* Gmail Cleaner
* 1. ラベル「Cleaner-xx」が付与されたメールアドレスを取得
* 2. メールアドレスをスプレッドシートに記録
* 3. 指定期間以上前のメールをゴミ箱に移動
*/
function main() {
const sheet = SpreadsheetApp.getActiveSheet();
// 削除対象のラベルを取得
const labels = GmailApp.getUserLabels();
const cleanerLabels = labels.filter((l) => l.getName().toLowerCase().includes("cleaner"));
// ラベル「Cleaner-xx」が付与されたメールアドレスを取得
cleanerLabels.forEach((label) => {
const labelName = label.getName();
const olderThan = labelName.split("-")[1] || "1m";
// Cleaner-xxから付け替えるためのAutoClean-xxの取得・作成
const autoCleanLabel = GmailApp.getUserLabelByName("AutoClean-" + olderThan) ||
GmailApp.createLabel("AutoClean-" + olderThan);
// ラベル「Cleaner-xx」が付与されたメールアドレスを取得
const cleanerMailAddress = GmailApp.search(`label:${labelName}`);
cleanerMailAddress.forEach((mail) => {
mail.getMessages().forEach((message) => {
// メールアドレスが記録されていなければスプレッドシートに記録
const mailAddress = message.getFrom();
var textFinder = sheet.createTextFinder(mailAddress);
var cells = textFinder.findAll();
// すでに記録されている場合は、AutoClean-xxを付け替える
if (cells.length > 0) {
const nextAutoCleanLabel = GmailApp.getUserLabelByName("AutoClean-" + olderThan) ||
GmailApp.createLabel("AutoClean-" + olderThan);
cells.forEach((cell) => {
const range = sheet.getRange(cell.getRowIndex(), 2);
const prevOlderThan = range.getValue();
const prevAutoCleanLabel = GmailApp.getUserLabelByName("AutoClean-" + prevOlderThan) ||
GmailApp.createLabel("AutoClean-" + prevOlderThan);
range.setValue(olderThan);
// おなじラベルの場合はスキップ
if (prevAutoCleanLabel.getName() === nextAutoCleanLabel.getName()) {
return;
}
// 前のラベルが付与されたスレッドを取得
const labelAddThreads = GmailApp.search(`from:(${mailAddress}) label:${prevAutoCleanLabel.getName()} -is:starred`);
if (labelAddThreads.length === 0) {
return;
}
// 配列を100件ずつに分割(addToThreadsは1度に100件まで)
const chunk = 100;
for (let i = 0; i < labelAddThreads.length; i += chunk) {
const threads = labelAddThreads.slice(i, i + chunk);
// ラベルの付け替え
prevAutoCleanLabel.removeFromThreads(threads);
nextAutoCleanLabel.addToThreads(threads);
}
});
}
else {
sheet.appendRow([mailAddress, olderThan]);
}
});
// ラベル「Cleaner-xx」を削除し、AutoClean-xxを付与
mail.removeLabel(label);
mail.addLabel(autoCleanLabel);
});
});
// 削除対象のFromメールアドレスを取得
const targetMailAddressRange = sheet.getDataRange();
const targetMailAddressList = targetMailAddressRange.getValues();
targetMailAddressList.forEach(([targetMailAddress, olderThan]) => {
//検索条件:指定されたメールアドレス && ラベルなし && ☆スターなし
const labelAddThreads = GmailApp.search(`from:(${targetMailAddress}) has:nouserlabels -is:starred`);
if (labelAddThreads.length === 0) {
return;
}
//検索条件に合致するメールに「AutoClean-xx」ラベルを付与
const label = GmailApp.getUserLabelByName("AutoClean-" + olderThan) ||
GmailApp.createLabel("AutoClean-" + olderThan);
// 配列を100件ずつに分割
const chunk = 100;
for (let i = 0; i < labelAddThreads.length; i += chunk) {
const threads = labelAddThreads.slice(i, i + chunk);
label.addToThreads(threads);
}
});
}
/**
* 指定期間以上前のメールをゴミ箱に移動
*/
function deleteThreads() {
const sheet = SpreadsheetApp.getActiveSheet();
const targetMailAddressRange = sheet.getDataRange();
const targetMailAddressList = targetMailAddressRange.getValues();
targetMailAddressList.forEach(([targetMailAddress, delayDays]) => {
//検索条件:指定されたメールアドレス && 受信日が指定期間より前 && ☆スターなし
const deleteThreads = GmailApp.search(`from:(${targetMailAddress}) older_than:${delayDays} -is:starred`);
console.log(targetMailAddress, delayDays, deleteThreads.length, "件");
//検索条件に合致するメールをゴミ箱に移動
// 配列を100件ずつに分割
const chunk = 100;
for (let i = 0; i < deleteThreads.length; i += chunk) {
const threads = deleteThreads.slice(i, i + chunk);
GmailApp.moveThreadsToTrash(threads);
}
});
}
制約事項
GASはいくつかの制約事項があります。
今回のスクリプトで影響を受けるのはこのあたりです。
- タイムアウト時間(6分 or 30分)
- Gmail APIの1日の最大実行回数
基本的には削除できずにエラーが出てメールが溜まってしまうだけなので、致命的な影響はないと思います。
気になる方は、実行数の管理なども加えてあげると良いでしょう。
終わりに
今回はネット上で見かける自動化の仕組みを、より自分の使いやすいようにカスタマイズをしてみました。
ネット上の情報は参考になったり便利なものも多いですが、そのままでは使いづらいことも多いです。
自分の欲しい機能とのギャップをいかに埋めるか、試行錯誤して解決できたときの満足感はひとしおですね。
今後も、日常のちょっとした手間を簡単に解消する方法を、日々探していきたいと思います。
参考