見出し画像

Gmailの通知をLINEで受け取る方法と、GASの実行制限に達してしまったときの解決策

はじめに: Gmail通知をLINEで受け取る設定


私はGmailからの通知をLINEでも受け取れるように設定しています。 受け取っているのは、子どもたちの小学校の正門通過通知や習い事の入退室通知のメールです。 Gmailだと通知を見逃してしまうことがあるので、確実に把握できるようLINEにも届ける設定にしたのです。
以下のnoteでその設定方法を書いて共有したところ、多くの方に読んでいただき、とても便利!などととても嬉しいコメントやDMをいただきました。ありがとうございました!

noteからも「先週最も多く読まれた記事の一つ」と通知が届き、とても驚きました。



問題の発生: Google Apps Scriptの実行限界に達する


Google Apps Scriptでこの処理に関するプログラムを書いているのですが、1日あたりの利用回数の上限に達したことを意味する「Exception: Service invoked too many times for one day: gmail.」というエラーメッセージが出るようになってしまい、困惑していました。

今回の記事では、このエラーメッセージが出る原因とその解決方法について共有したいと思います。

解決策の検討: スクリプト実行時間の最適化


Google Apps Scriptは、無料アカウントの場合、スクリプトの1日の実行時間に5400秒(90分)の制限があります。 そのため、無料アカウントで1分ごとに24時間分のトリガー(1日1440回の実行)を設定したい場合、1回のスクリプト実行時間を3.75秒以内に収める必要があります。

私が書いたこれまでのスクリプトは、1回あたり4~7秒程度の処理時間がかかっていたため、1日の総実行時間が5400秒を大きく超えて、制限に引っかかってしまっていたのです。

1回あたり4〜7秒くらいかかっていました

処理時間の調べ方は、左側の「実行数」のところから確認できます。


実践的なアプローチ: 未読メッセージの効率的な取得

この問題を解決するためには、スクリプトの実行効率を上げる必要があります。具体的には以下のようなアプローチで処理時間の短縮を図りました。

  • 未読メッセージのみ取得: 最初は指定したラベルの全てのスレッドを取得していましたが、これは多くのメッセージを不要に処理してしまうため時間の浪費でした。そこで指定ラベルの未読メッセージのみを取得するよう変更した結果、処理時間を大幅短縮でき、1回あたり1秒未満に抑えられるようになりました。

1回あたり1秒未満に抑えることができた!

改良後のスクリプト: 効率的な通知システム

Google Apps Scriptで未読メッセージのみを効率的に取得するには、GmailAppのsearchメソッドを使って特定のクエリを実行します。この方法では、Gmailの検索オペレーター「is:unread」を使用して未読メッセージのフィルタリングをします。以下のように、特定のラベルに属する未読メッセージのみを取得できます。

// 「my_label」ラベルの未読メッセージを取得
var threads = GmailApp.search('label:my_label is:unread');

このようにsearchでクエリを指定することで、不要なメッセージを事前に除外でき、処理効率を上げられます。


こちらが改良後の全スクリプトです。


function sendNotificationForUnreadMessages() {
  var accessToken = '****************; // LINE Notifyのアクセストークンをここに置き換える
  var labelName = 'ラベル名'; // 通知を受けたいGmailのラベル名
  var searchQuery = 'label:' + labelName + ' is:unread'; // ラベルと未読の条件を指定
  var threads = GmailApp.search(searchQuery); // 検索クエリに基づいてスレッドを取得
  var messages = GmailApp.getMessagesForThreads(threads); // スレッド内のメッセージを取得

  for (var i = 0; i < messages.length; i++) {
    for (var j = 0; j < messages[i].length; j++) {
      var message = messages[i][j];
      if (!message.isUnread()) continue; // 念のため未読のメッセージのみを確認(この行は基本的に不要)

      var subject = message.getSubject();
      var bodyText = message.getPlainBody();
      var snippet = bodyText.substring(0, 70); // 本文の最初の70文字を取得

      // LINE Notifyに送信するメッセージを準備
      var payload = {
        'message': '新しいメール: ' + subject + '\n' + snippet
      };

      // LINE Notifyにメッセージを送信するためのオプションを設定
      var options = {
        'method': 'post',
        'payload': payload,
        'headers': {'Authorization': 'Bearer ' + accessToken}
      };

      // LINE Notify APIを呼び出して通知を送信
      UrlFetchApp.fetch('https://notify-api.line.me/api/notify', options);

      // 処理したメッセージを既読にする
      message.markRead();
    }
  }
}

もとのスクリプトとの比較

元のスクリプトでは、指定したラベルに関連するすべてのスレッドを取得していましたが、改良後のスクリプトでは未読メッセージのみを対象に絞り込むことで、実行時間を短縮しました。

function sendNotification() {
  var accessToken = '***************; // LINE Notifyのアクセストークン
  var label = GmailApp.getUserLabelByName('ラベル名'); // 通知を受けたいGmailのラベル名
  var threads = label.getThreads();

  // メッセージをスレッドごとに一括取得
  var messages = GmailApp.getMessagesForThreads(threads);

  for (var i = 0; i < messages.length; i++) {
    for (var j = 0; j < messages[i].length; j++) {
      var message = messages[i][j];
      if (!message.isUnread()) continue; // 未読のメッセージのみ処理
      var subject = message.getSubject();
      var bodyText = message.getPlainBody(); // メールのプレーンテキスト本文
      var snippet = bodyText.substring(0, 70); // 本文の最初の70文字を取得
      var payload = {
        'message': '新しいメール: ' + subject + '\n' + snippet
      };
      var options = {
        'method' : 'post',
        'payload' : payload,
        'headers' : {'Authorization': 'Bearer ' + accessToken}
      };
      
      UrlFetchApp.fetch('https://notify-api.line.me/api/notify', options);
      message.markRead(); // 処理したメッセージを既読にする
    }
  }
}

ーーーーーーーーーー
私の書いた本が出版されました!
\Twitterからうまれた/
ちょっと理系なおうち遊びの本

Amazon 売れ筋ランキング  
幼児教育 カテゴリー  有料Top100
ベストセラー1位✨ になりました!
ありがとうございます!

KindleUnlimited, ペーパーバックでも読めます!




この記事が参加している募集

noteでよかったこと

この記事が気に入ったらサポートをしてみませんか?