Gmail を定期的に削除する GAS を作ってみた
Gmail を定期的に削除する GAS を作ってみた
Gmail は容量が 15GB まで無料で利用できますが、それを超えると有料プランに切り替える必要があります。
この容量は Gmail の他に、Google ドライブ、Google フォトと共用の容量となっています。
不要なメールでこれらの容量を圧迫するのを防ぐために、定期的にメールを削除する必要に迫られましたが、
Gmail のフィルターを利用してメールを削除する場合は、受信時に削除されるため、メールを確認する前に削除されてしまう可能性があります。
そこで、メールを削除するまでの日数を指定することで、一定期間経過後にメールを削除する処理を GAS を利用して作成しました。
本記事では、前半にユーザーとして使うための方法を記載し、後半に GAS の説明をします。
ソースコード及び GAS 含むスプレッドシートは下記で公開しています。
ツールの概要
特徴
ユーザー定義のラベルに基づいてメールを削除します。
ネストされたラベルをサポートします。
スター付きのメールは削除されません。
自動削除を設定した後、1 時間につき 100 件の対象メールが削除されます。
スプレッドシートの言語設定に基づき、日本語と英語で動作します。サポート外の言語の場合は英語で動作します。
使い方
1 Gmail でラベルを定義します。ラベル名の後には '@' と、メールを削除するまでの日数を指定します。
例えば、'general@365'のラベルがつけられたメールは、受信後 365 日以上経過した場合に削除されます。
カテゴリに分類されているメールについては、下記のように、カテゴリというラベルにネストしたラベルを用意します。
カテゴリ/プロモーション@30
2 下記のスプレッドシートを開き、自分の Google ドライブへコピーをします。
スプレッドシートをコピー後は、スプレッドシートのメニュー>設定を開き、言語と地域が日本以外の場合は日本に設定してください。
または、スプレッドシートを開いた後、本スクリプトを Google スプレッドシートのスクリプトエディタにコピーします。
対象は下記の 3 つのファイルです。
src/client.js
src/main.js
src/const.js
3 スプレッドシートを開き、メニューバーの一番右にある 'Gmail 削除ツール' をクリックします。
下記のメニューが表示されます。
自動削除をセットする
自動削除をストップする
一回だけ削除する
最初に「一回だけ削除する」を実行し、動作を確認してください。
なお、初めて実行する際は、アクセス許可を求められますので、下記ページを参考に、許可をしてください。
認証を行った後、再度「一回だけ削除する」を実行してください。終了のダイアログが表示されれば、正常に動 作しています。
その後に「自動削除をセットする」を実行すると、自動削除が開始されます。
制限事項
タイムアウトを防ぐため、1 回の実行で最大 100 通削除します。
ラベルがネストされている場合、スクリプトは子ラベルのみを対象とします。
'@' がないラベルや '@' の後に正の整数以外があるラベルは処理されません。
GAS の概要
ソースコードは下記の GitHub にあります。
どのメールを削除するか?
メールを自動で削除するという性質上、ユーザーが意図していないメールを削除するおそれがあります。
そのため、ラベルがついているメールのみを削除するようにしました。
また、削除対象のラベルと、どの程度の期間経過したメールを削除するかについて、ユーザーが定義できるようにしたいと考えていました。
当初、スプレッドシートでリストを定義することを考えましたが、Gmail とスプレッドシートを行ったり来たりしながら設定するのは面倒くさいと考え、Gmail のラベル名自体に日数を入れ込んでおくという仕様にしました。
general@365 というように、ラベル名の後ろに@と削除するまでの日数を指定します。
関数の説明
下記の関数で実現しています。
これ以外に、スプレッドシートのメニューを表示する関数もありますが、本筋ではないため省略します。
ラベルのを取得し、削除するメインの関数
/**
* Gmail deletion process
* Deletes the target email based on the label
*/
function deleteMails() {
// Get user labels
const labels = GmailApp.getUserLabels();
// Total number of deleted emails
let count = 0;
// Perform deletion process for each label name
for (const label of labels) {
count = deleteMailByLabel_(label.getName(), count);
// If the deletion limit is reached, the process is terminated
if (count >= LIMIT_DELETE_MAIL) {
break;
}
}
logAndToast_(MESSAGE_TABLE[lang].deleteMails.toast + count);
}
Gmail で使用しているラベルは、GmailApp.getLabels()で取得できます。すごく簡単です。
後は各ラベルで、削除対象のメールがあるかを判定していきます。
ラベルがついたメールを取得し、削除する関数
/**
* Get and delete mail with specific label in gmail
* If the label is nested, the parent label will not respond, and only the child label will be the target of this function.
* If multiple labels are attached to the target mail, the stricter one will be deleted.
*
* @param {string} labelName Label name. There should be the number of days to delete after @.
* @param {number} count Total number of deleted emails
* @return {number} Total number of deleted emails
*/
function deleteMailByLabel_(labelName, count) {
// Create search condition
const condition = makeSearchCondition_(labelName);
// If the search condition is null, the process is terminated
if (condition === null) {
return count;
}
// The total number of emails to be deleted is limited to LIMIT_DELETE_MAIL to prevent timeout
const threads = GmailApp.search(condition, 0, LIMIT_DELETE_MAIL - count);
logAndToast_(
labelName +
'\n' +
MESSAGE_TABLE[lang].deleteMailByLabel.toast +
threads.length
);
count += threads.length;
// Delete all threads obtained by search
GmailApp.moveThreadsToTrash(threads);
return count;
}
後述する、検索条件を作成する関数を使って、ラベルがついたメールを取得します。
GAS の制限時間に引っかからないように、1 回の実行で削除する件数をカウントするために、削除するメールの件数をカウントしています。
threads として取得したメールを一括で削除する moveThreadsToTrash 関数があるので、まとめて削除しています。
ラベルがついたメールを取得するための検索条件を作成する関数
/**
* Create search condition
* Returns null if the label name does not meet the search criteria.
*
* @param {string} labelName Label name. There should be the number of days to delete after @.
* @return {string|null} Search condition string if valid, null otherwise
*/
function makeSearchCondition_(labelName) {
const labelInfo = checkLabelNameValidity_(labelName);
if (labelInfo === null) {
return null;
}
// If the label name is not in the CATEGORY_TABLE, it is a user label
// CATEGORY_TABLE has labels under the category label, such as Social, New, Promotion, and Forum, without @
const categoryLabelTable = CATEGORY_TABLE;
if (!categoryLabelTable[labelInfo.name]) {
categoryLabelTable[labelInfo.name] = 'label: ' + labelName;
}
const condition =
categoryLabelTable[labelInfo.name] +
' older_than:' +
labelInfo.days +
'd -is:starred ';
console.log('search condition:' + condition);
return condition;
}
ラベルが条件を満たしいてるかの判定と、カテゴリを指定されているかの判定をした後に、検索条件を作成します。
デフォルトで用意されているカテゴリと、ユーザーが作るラベルでは検索演算子が異なるため、下記のようなカテゴリと検索演算子の対応表を作成し、不一致ならユーザーラベルと判断、テーブルに追加する方法にしています。
// category table
// User needs to create labels under the category label, such as Social, Updates, Promotions, and Forums with @.
// This table key is the label name without @, and the value is the search condition.
// If you want to add other languages, please add them here.
const CATEGORY_TABLE = {
'カテゴリ/ソーシャル': 'category:social',
'カテゴリ/新着': 'category:updates',
'カテゴリ/プロモーション': 'category:promotions',
'カテゴリ/フォーラム': 'category:forums',
'categories/Social': 'category:social',
'categories/Updates': 'category:updates',
'categories/Promotions': 'category:promotions',
'categories/Forums': 'category:forums',
};
ラベルが条件を満たしているかの判定をする関数
/**
* Check the validity of the label name
* Returns null if the label name does not meet the criteria.
*
* @param {string} labelName Label name. There should be the number of days to delete after @.
* @return {object|null} labelInfo An object containing the name and days if valid, null otherwise
* @property {string} labelInfo.name Label name
* @property {number} labelInfo.days Number of days to delete
*/
function checkLabelNameValidity_(labelName) {
const labelNameArray = labelName.split('@');
// If the label name does not contain @, it is not a target
if (labelNameArray.length === 1) {
logAndToast_(
labelName + '\n' + MESSAGE_TABLE[lang].checkLabelNameValidity.noAt
);
return null;
}
const days = labelNameArray[labelNameArray.length - 1];
// If the number of days to delete is not a positive integer, it is not a target
if (!Number.isInteger(Number(days)) || Number(days) <= 0) {
logAndToast_(
labelName + '\n' + MESSAGE_TABLE[lang].checkLabelNameValidity.noInteger
);
return null;
}
// Remove the number of days from the label name
labelNameArray.pop();
const name = labelNameArray.join('@');
return { name: name, days: days };
}
ラベルに@が含まれているか?@の以降に数字があるか?を判定します。
削除対象となるラベルだった場合は、削除する日数も取得して返却します。
ログとトースト
/**
* Log and toast
*
* @param {string} str String to log and toast
*/
function logAndToast_(str) {
console.log(str);
SpreadsheetApp.getActiveSpreadsheet().toast(str);
}
これは見ての通りです。
まとめ
GAS で Gmail を定期的に削除するツールを作成しました。
GAS では、Gmail を簡単に操作するためのサービスが色々ありますので、このように簡単なスクリプトで便利なツールを作ることができます。
Google の各種サービスを使用して、ちょっと面倒くさい作業がある場合や、定期的に実施したい作業がある場合、このようなツールを作成することで、作業の効率化ができます。
Google Workspace をお使いの方、Gmail の容量で困っている方、GAS の勉強をされている方の参考になれば幸いです。
この記事が気に入ったらサポートをしてみませんか?