🎃

Gmailを定期的に削除するCloud Functionsを作成

2022/10/24に公開

Gmail

Gmailはみなさん高確率で利用されているのではないでしょうか。
私の場合結構なウェイトを占めている利用方法があります。
会社でグループウェアを使用してメール処理をしてますが、保管用にGmailへ自動転送しています。

グループウェアだけで管理できれば良いのですが、メール検索が貧弱なため
転送先のGmailで検索してたりしています。(ファイル内容も検索してくれるので重宝しています)。
ただ、Gmail無料枠は10GBですので、転送し続けれるわけなく私の場合は90%を超えてきていました。
システムメール(ログ)なども全転送してるので、塵も積もれば山となるという感じです。
そこで、うまいこと定期的にメール削除したいと考え、Cloud Functonsで処理させることにしました。

プロジェクトの作成

事前にfirebaseプロジェクト作成

お決まりfirebase-toolsを使ってローカル環境設定

 mkdir gmail-operation
 firebase init

必要なパッケージをインストール

  npm install -D prettier eslint-config-prettier  eslint-plugin-import
  npm install googleapis@105 @google-cloud/local-auth@2.1.0

GmailApi有効化

今回は先にインストールしたgoogleapisを利用しますので、
下記のURLの通りに従ってAPIを有効化およびCredentialを設定します。

https://developers.google.com/gmail/api/quickstart/nodejs#enable_the_api

なお今回利用したいAPIの機能としてはメールのゴミ箱移動(削除も今後入れる予定)なため、
必要なスコープを設定します。
https://mail.google.com/
https://developers.google.com/gmail/api/reference/rest/v1/users.messages/trash

実装

基本部分は上記のリンク先にあるサンプルソースを利用して実装

functions/src/index.ts
import { initializeApp } from 'firebase-admin/app';
import { getFirestore } from 'firebase-admin/firestore';
import * as functions from 'firebase-functions';

import { authorize, getGmail, listLabels, mails, mailTrash } from './libs/google';

initializeApp();

const baseFunctionWithRegion = functions.region('asia-northeast1');

// 毎日0時に実行
export const GmailTrash = baseFunctionWithRegion.pubsub
  .schedule('0 0 * * *')
  .timeZone('Asia/Tokyo')
  .onRun(async (ctx) => {
    functions.logger.info(`----- GmailTrash Function Start -----`);
    const auth = await authorize();
    const gmail = getGmail(auth);

    // ラベル(フォルダ)取得
    const db = getFirestore();
    const snapshot = await db.collection('labels').get();
    const labelNames = snapshot.docs.map(doc => doc.data().label as string);
    const allLabelList = await listLabels(gmail);
    const targetLabelList = allLabelList?.filter((e) => labelNames.includes(e.name?.toLowerCase() ?? ''));
    if (!targetLabelList) return;

    const nonNullable = <T>(value: T): value is NonNullable<T> => !!value;
    const labelIds = targetLabelList.map((e) => e.id).filter(nonNullable);

    // メール取得
    const mailList = await mails(gmail, labelIds);
    functions.logger.info('mailList count:', mailList?.length);
    if (!mailList) {
      return;
    }

    for (const row of mailList) {
      if (row.id) {
        await mailTrash(gmail, row.id);
      }
      functions.logger.info(`----- trash mail -----`);
    }

    functions.logger.info(`----- GmailTrash Function End -----`);
  });

サンプルから追加、修正した部分抜粋

functions/src/libs/google.ts
import * as fs from 'node:fs/promises';
import * as path from 'path';
import * as process from 'process';

import { authenticate } from '@google-cloud/local-auth';
import * as dayjs from 'dayjs';
import * as ja from 'dayjs/locale/ja';
import { gmail_v1, google } from 'googleapis';
import { BaseExternalAccountClient, OAuth2Client } from 'googleapis-common';

dayjs.locale(ja);

const SCOPES = ['https://mail.google.com/'];
const TOKEN_PATH = path.join(process.cwd(), 'token.json');
const CREDENTIALS_PATH = path.join(process.cwd(), 'credentials.json');

export const getGmail = (auth: OAuth2Client | BaseExternalAccountClient) => google.gmail({ version: 'v1', auth });

// メール取得
export const mails = async (gmail: gmail_v1.Gmail, labelIds: string[]) => {
  // 7ヶ月以上前のメール取得
  const before = dayjs().add(-7, 'month');
  // 7ヶ月以上前から更に1年
  const after = before.add(-1, 'year');

  const mails = await gmail.users.messages.list({
    userId: 'me',
    // default
    // maxResults: 100,
    labelIds,
    // qはgmail画面の検索クエリと同等なので、Gmailで実際に検索してコピペ
    q: `after:${after.format('YYYY/MM/DD')} before:${before.format('YYYY/MM/DD')}`,
  });

  return mails.data.messages;
};

// ゴミ箱移動処理
export const mailTrash = async (gmail: gmail_v1.Gmail, id: string) => {
  try {
    await gmail.users.messages.trash({
      userId: 'me',
      id,
    });
  } catch (error) {
    console.warn(error);
    throw new Error('delete error');
  }
};

あとは、ローカルで一度実行し認証してトークンファイル作成し、本番デプロイ

 npm run serve
 npm run deploy

稼働状況確認

ログもちゃんととれてますし、実際ゴミ箱へ移動されており問題なし

今までは定期的に自分で処理してて結構めんどくさいなぁと思ってましたが、
簡単に実装できて満足です。

Gmailを無料枠で利用されている方で容量逼迫している方がいましたらぜひ実装してみてください!

Discussion