0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Google Apps Script で Gmail を自動監視し、LINE と Discord に転送する方法

Last updated at Posted at 2025-02-20

全体の紹介

本記事では、Google Apps Script (GAS) を使って、Gmail の特定のスレッドを自動で監視し、本文や件名をクリーニングして Discord に転送する方法を紹介します。
LINE Notify でも同様の処理が可能ですが、LINE Notify は今年度末(2023年度末)でサービス終了が予定されているため、本記事では主に Discord の Webhook を使った通知手法を中心に解説します。

なんで作ったか

筆者(私)は学生の頃、メールチェックが習慣化できておらず、つい見落としてしまうことが多々ありました。しかし、就職活動が始まると、選考や面接の詳細連絡など、重要なやり取りがメールで届くようになります。そこで「メールを見逃さないようにしたい」と思い、いつも使っている Discord へ自動転送する仕組みを作りました。

Line Notify が今年度終わること

2025年3月31日をもってLINE Notifyはサービス終了になるため、今後は使えなくなります。もしどうしてもLINE Notifyで通知を受け取りたい場合は、ご自身で API キーの取得方法をお調べください。

動作方法

Discord Webhook URL の取り方

  1. Discord の任意のサーバーで、管理権限またはチャンネル管理権限を持っているテキストチャンネルを用意します。
  2. テキストチャンネルの設定を開き、「連携のサービス」 → 「ウェブフック」 から新しいウェブフックをクリックします。
  3. 作成した Webhook の 「Webhook URL」 をコピーしておきます。このURLが今回のスクリプトで使う DISCORD_WEBHOOK_URL となります。

検索条件について

スクリプト内では SEARCH_QUERY を使用して検索条件を指定しています。
Gmail の検索演算子や記号(from:-label: など)は非常に柔軟に使えるので、以下の Google ヘルプを参考にカスタマイズしてみてください。

const SEARCH_QUERY = [
  "{面接 選考 試験 東京}", // 検索に含ませたい文言を大括弧内に空白をあけて書く (OR検索)
  "-{楽天ポイントカード 楽天toto}", // 検索に含ませたくない文言を大括弧内に空白をあけて書く
  "-label:BotRead", // この行は変更しない
].join(" ");

使い方のポイント

  1. "{面接 選考 試験 東京}"

    • {} 内に空白区切りでキーワードを並べると、いずれかのキーワードを含む(OR 検索)スレッドを探せます。
    • 例:面接 OR 選考 OR 試験 OR 東京 という意味になります。
  2. "-{楽天ポイントカード 楽天toto}"

    • 先頭に -(マイナス)を付けると、キーワードを含むメールを検索結果から除外します。
    • ここでは「楽天ポイントカード」または「楽天toto」のどちらかを含むスレッドは除外します。

GAS にコードを貼り付け

GAS のエディタ(script.google.com/home)を開き、新規プロジェクトを作成して以下のサンプルコードを貼り付けます。

忘れずに【あなたの Discord Webhook URL】部分を取得したURLで置き換えてください。
すみつき括弧【】も必要ありません

サンプルコード
/***************************************
 * 定数宣言
 ***************************************/
// すでに BotRead がついているスレッドは除外し、
// キーワードに該当するスレッドを検索する
const SEARCH_QUERY = [
  "{面接 選考 試験 東京}", // 検索に含ませたい文言を大括弧内に空白をあけて書く (OR検索)
  "-{楽天ポイントカード 楽天toto}", // 検索に含ませたくない文言を大括弧内に空白をあけて書く
  "-label:BotRead", // この行は変更しない
].join(" ");

// スレッド処理後につけるラベル名
const BOT_READ_LABEL_NAME = "BotRead";  // この行は変更しない

// 送信先
const DISCORD_WEBHOOK_URL = "【あなたの Discord Webhook URL】";
// const LINE_TOKEN = "【Line Notify API Key】";  // Line Notify を使用する場合

// Discord 制限用
const DISCORD_MAX_LENGTH = 2000;

/***************************************
 * メイン処理
 ***************************************/
function mailBotMain() {
  // BotRead が付いていないスレッドを検索
  const threads = GmailApp.search(SEARCH_QUERY);

  threads.forEach(thread => {
    // スレッド内のメール一覧を取得
    const messages = thread.getMessages();

    messages.forEach(message => {
      // すでに既読のメールはスキップしたい場合は以下を有効化
      // if (!message.isUnread()) return;

      // 件名・本文の取得とクリーニング
      const subject = message.getSubject();
      let plainBody = message.getPlainBody();
      plainBody = clean(plainBody);

      // 件名+本文をまとめて送信
      const text = "# " + subject + "\n\n" + plainBody;
      sendMessage(text);
    });

    // スレッドに BotRead ラベルを付与
    let label = GmailApp.getUserLabelByName(BOT_READ_LABEL_NAME);
    if (!label) {
      // ラベルが存在しない場合は新規作成
      label = GmailApp.createLabel(BOT_READ_LABEL_NAME);
    }
    thread.addLabel(label);
  });
}

/***************************************
 * テキスト整形関連
 ***************************************/
/**
 * メール本文から不要な空白や改行を整理する
 */
function clean(text) {
  // 改行コード統一
  text = text.replace(/\r\n/g, '\n');
  // 3つ以上連続する改行は2つにする
  text = text.replace(/\n{3,}/g, '\n\n');
  // 各行末尾の空白を除去
  text = text.replace(/[ \t]+$/gm, '');
  return text;
}

/***************************************
 * メッセージ送信関連
 ***************************************/
function sendMessage(text) {
  // sendLine(text); // LINE Notifyを使う場合
  sendDiscord(text);
}

/**
 * Discord Webhook にメッセージを送信
 * content フィールドには 2000 文字制限があるため、長文は分割。
 */
function sendDiscord(text) {
  // Discord は 2000文字制限があるので、必要なら分割して送る
  const chunks = splitByLengthWithNewline(text, DISCORD_MAX_LENGTH);

  chunks.forEach(chunk => {
    const payload = {
      content: chunk,
      username: "mail GAS Bot"  // 任意
    };

    const options = {
      method: "post",
      contentType: "application/json",
      payload: JSON.stringify(payload)
    };

    try {
      UrlFetchApp.fetch(DISCORD_WEBHOOK_URL, options);
      Logger.log("メッセージが送信されました。");
    } catch (error) {
      Logger.log("エラーが発生しました: " + error);
      // 例:400 (2000文字以上), 429 (Rate Limit)など
    }
  });
}

/**
 * 指定した文字数以内の改行でテキストを分割するためのユーティリティ
 * 改行が見つからない場合は maxLength で強制的に分割します
 */
function splitByLengthWithNewline(text, maxLength) {
  const result = [];
  let startIndex = 0;

  while (startIndex < text.length) {
    let endIndex = startIndex + maxLength;
    if (endIndex > text.length) {
      endIndex = text.length;
    }

    // 指定範囲内で最後の改行位置を探す
    const lastNewlineIndex = text.lastIndexOf('\n', endIndex);

    if (lastNewlineIndex > startIndex) {
      // 改行が見つかった場合、その位置で分割
      result.push(text.substring(startIndex, lastNewlineIndex));
      startIndex = lastNewlineIndex + 1; // 改行文字の次から開始
    } else {
      // 改行が見つからない場合、maxLengthで分割
      result.push(text.substring(startIndex, endIndex));
      startIndex = endIndex;
    }
  }

  return result;
}

/**
 * LINE Notify にメッセージを送信
 */
function sendLine(text) {
  const options = {
    method: "post",
    headers: {
      Authorization: "Bearer " + LINE_TOKEN
    },
    payload: {
      message: text
    }
  };
  UrlFetchApp.fetch("https://notify-api.line.me/api/notify", options);
}

GAS 定期実行のさせ方

  1. 「時計アイコン(トリガー)」を選択します。
  2. 「トリガーを追加」ボタンから以下の設定で保存します。
    • 実行する関数を選択
      • mailBotMain
    • 実行するデプロイを選択
      • Head
    • イベントのソースを選択
      • 時間主導型
    • 時間ベースのトリガーのタイプを選択
      • 分ベースのタイマー
    • 時間の間隔を選択(分)
      • 1分おき

このようにすれば、定期的に Gmail をチェックし、新着メールを検出して Discord に送信する自動化が実現できます。

以上で動作させられます

初回動作時の注意

GAS には 1回あたり6分 という実行制限があります。
初回に大量の未処理メールを取得すると、6分以内に完了せずエラーになる場合があります。その際は、手動で古いメールを読んだ扱いにしたり、最初だけ検索キーワードを絞るなど調整しましょう。

主な流れ

  1. 検索クエリで Gメール スレッドを取得
    • SEARCH_QUERY に指定したキーワード/ラベル等にマッチするスレッドのみを取得します。
  2. スレッド内のメールを順次処理
    • 件名・本文を取得。
    • clean() 関数で余分な改行や空白を削除。
    • sendMessage() 関数に渡し、Discord へ投稿。
  3. ラベル付与
    • 処理済みのスレッドに BotRead ラベルを付け、次回以降重複して送信しないようにする。

コード解説

  • 定数宣言

    • SEARCH_QUERY … Gmail の検索クエリ文字列。
    • DISCORD_WEBHOOK_URL … Discord の Webhook URL。
    • DISCORD_MAX_LENGTH … Discord メッセージの最大文字数 (2000)。
  • mailBotMain()

    • メイン関数。GmailApp.search() で検索したスレッドごとにメールを読み出し、件名と本文を取得 → 整形 → Discordへ送信 → ラベル付与、の流れを処理します。
  • clean(text)

    • メール本文の不要な改行や空白を整形する。
    • \r\n\n に統一し、行末の空白を削除。連続する改行を1行に減らすなどして見やすくします。
  • sendMessage(text)

    • 将来的に複数送信先を管理したい場合の拡張ポイント。
    • ここでは sendDiscord(text) を呼び出しています。
    • LINE Notify を使いたい場合はここに sendLine(text) も追加します。
  • sendDiscord(text)

    • Discord の Webhook へメッセージを送信する。
    • 1回の送信文字数制限が 2000 文字のため、splitByLengthWithNewline() でテキストを分割してから複数回送信します。
  • splitByLengthWithNewline(text, maxLength)

    • Discord の文字数制限に対応するため、指定した長さを超えないように分割する。
    • できる限り改行で区切って読みやすさを保つ仕組みです。

まとめ

  • Google Apps Script を使うことで、Gmail の任意のメールを自動収集し、Discord (または LINE Notify) へ転送できます。
  • LINE Notify は終了予定のため、今から導入するなら Discord Webhook がおすすめです。
  • 大量のメールがある場合、初回実行は 6 分制限に注意が必要です。
  • 定期トリガーを設定することで、常に新着メールを確認し、見逃しのない運用を実現します。

ぜひ活用して、メールを見逃さない環境を整えてみてください。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?