時計

GASで前回のスクリプト実行日時から今回の実行までの期間をどう指定するか

freeeに関係ない話題ですが、Google Apps Script(以下GAS)でできる効率化の例として番外編としてfreee API以外のGASでの活用に関しても記事にしています。

今回は、「GASで前回のスクリプト実行日時から今回の実行までの期間をどう指定するか」です。

関係ないといいつつも、APIでPOSレジの売上データなど時間経過とともに増えていくデータを定期的に最新分のみ集計してレポートしたり、Slackなどチャットツールに通知したいとなると最終の処理済みデータから次回のスクリプト実行時までの期間のデータのみを取得する必要があります。

API以外にも決まった件名のメールをSlackなどチャットツールに通知する場合に、前回通知したメール以降のメールを取得して通知したいとなり、前回実行(データ取得)日時から今回実行までの期間を指定する必要があります。

Dateオブジェクトをプロパティストアに格納する

今回のポイントは、いきなり登場するこれです。

POSレジの売上データであれば最終注文日時、メールであれば最後に取得し処理したメールの受信日時をプロパティストアに格納して期間のスタートとして設定したいのですが、これらはDateオブジェクトとして取得されます。

そのため、まずこのDateオブジェクトをプロパティストアに格納することができるかテストをしてみました。

function date2Properties() {
  const now = new Date();
  PropertiesService.getScriptProperties().setProperty('TIME', now);

  console.log(PropertiesService.getScriptProperties().getProperty('TIME')); // Sat Aug 20 11:43:26 GMT+09:00 2022
  console.log(typeof PropertiesService.getScriptProperties().getProperty('TIME')); // string

  const date = new Date(PropertiesService.getScriptProperties().getProperty('TIME'));
  console.log(date); // Sat Aug 20 2022 11:50:37 GMT+0900 (Japan Standard Time)
  console.log(typeof date); // object
  console.log(date.getFullYear()); // 2022
  console.log(date.getMonth() + 1); // 8 getMonth() メソッドは、指定された日付の「月」を表す 0 を基点とした値 (0-11) を返します。
  console.log(date.getDate()); // 20
}

上記の関数を実行していただけるとわかりますが、プロパティストアにDateオブジェクトを直接格納した場合、オブジェクトではなく文字列として格納されます。

それを再度Dateオブジェクトに戻すには、プロパティストアから呼び出した文字列をnew Date()の引数に指定することでDateオブジェクトが新たに生成されます。

起算日以降に受信したメールのみに処理を実行するサンプルスクリプト

前回実行日時等の起算日さえプロパティストアに格納してしまえば、あとはその日時以降の条件で取得すべきデータがあるかどうかの判定を行うだけです。

Gmailを利用したメールの例を考えてみましょう。特定のアドレスから「商品が購入されました」という定型の件名のメールが定期的に届くとします。このメールの本文をSlackなどチャットツールに通知するケースを考えます。

  • 差出人:noreply@shop.com
  • 件名:商品が購入されました
  • 15分に1度の頻度でトリガーで定期実行
  • 前回通知したメール以降に新しいメールが来ていればすべて通知する

という前提でスクリプトを作成します。

function mail2Chat() {

  // プロパティストアからメール絞り込みの起算日時を取得
  const dateLastMail = new Date(PropertiesService.getScriptProperties().getProperty('LAST_DATE'));

  // Gmailの検索演算子とGmailAppクラスを利用して条件に該当するスレッドを取得
  const query = 'from:(noreply@shop.com) subject:("商品が購入されました") newer_than:1d';
  const threads = GmailApp.search(query);
  const threadMessages = GmailApp.getMessagesForThreads(threads);

  // 取得した各スレッドのインデックス0=最初のメッセージのみを配列で取得
  const firstThreadMessages = threadMessages.map(thread => thread[0]);
  // 起算日時以降のメールのみに絞り込み
  const aryNewMessage = firstThreadMessages.filter(message => message.getDate() > dateLastMail);

  // 取得すべきメッセージがなければ処理を中止
  if (aryNewMessage.length === 0) { return };

  // GmailMessageクラスのメソッドを利用してメールから必要な情報を取得してチャットツールに通知
  aryNewMessage.forEach(messageNew => console.log(messageNew.getSubject() + '|このログ出力部分にチャットツールに通知する処理を書く'));

  // 起算日時以降のメールを日付で降順に並び替え
  aryNewMessage.sort((a, b) => b.getDate() - a.getDate());

  // 最新のメールから受信日時を取得してプロパティストアに次回の起算日時として保存
  const messageLast = aryNewMessage[0];
  const dateLast = messageLast.getDate();
  PropertiesService.getScriptProperties().setProperty('LAST_DATE', dateLast);

}

検索演算子とGmailAppクラスのメソッドで絞り込み

まず、Gmailの検索演算子とGmailAppクラスのメソッドを利用して対象となるスレッド(メールの集まり)を絞り込んで取得していますが、このテーマに関しては以下の記事が参考になります。

取得した各スレッドのインデックス0=最初のメールのみを取得

続いてスレッドの最初のメール(GmailMessageオブジェクト)を配列で取得しています。スレッドの最初のメールとは、最初に受信したメールであったり、送信したメールです。Gmailではそのメールに返信などを行うと返信メールなどがスレッドに連なっていきます。

Gmailのスレッドに関しては以下の記事が参考になります。

いつも隣にITのお仕事|【GAS】Gmailの特定条件で検索したスレッドの全メールを取得してスプレッドシートに書き出す

起算日時以降のメールのみに絞り込み

続いて、取得した各メールの受信日時と前回までに最後に処理したメールの受信日時を比較して、起算日時以降のメールのみに絞り込んで新たに配列を作成します。

これにはArrayオブジェクトに対して使用できるfilter()メソッドを使用します。

const aryNewMessage = firstThreadMessages.filter(message => message.getDate() > dateLastMail);

message.getDate() > dateLastMail

この部分が、今回のハイライトです。Dateオブジェクト同士を比較して、前回受信日時より新しいメールかを判定しています。

この絞り込みさえできてしまえば、後は該当するメールから必要な情報を抜粋してチャットツールに通知するだけです。

条件に該当するメールがなければ処理を中止する

起算日時以降のメールのみに絞り込んだ時点で、条件に該当するメールがなければこの後の処理を中止してスクリプトを終了する必要があります。

この場合、定数aryNewMessageの要素数は0(該当するメールがない)になるので、これを条件に分岐を行い、スクリプトを終了させます。

if (aryNewMessage.length === 0) { return };

起算日時以降のメールを日付で降順に並び替える

チャットツールへ通知した後は、新しい起算日をプロパティストアに保存しなくてはいけません。そのためにGmailを利用したサンプルスクリプトの例だと最新のメールの受信日時を取得する必要があります。

起算日時以降のメールを格納した配列である定数aryNewMessageの各要素(GmailMessageオブジェクト)を受信日時の降順に並び替える処理を行います。

aryNewMessage.sort((a, b) => b.getDate() - a.getDate());

Arrayオブジェクトに対して使用できるsort() メソッドを使用します。

最新のメールの受信日時を次回の起算日時としてプロパティストアに保存

最後に最新のメールの受信日時を取得して、次回の起算日時としてプロパティストアに保存します。

const messageLast = aryNewMessage[0];
const dateLast = messageLast.getDate();
PropertiesService.getScriptProperties().setProperty('LAST_DATE', dateLast);

これでスクリプトが完成しました。

トリガーの設定

最後にこのmail2Chat関数を定期的に自動実行するためのトリガー設定を行います。こまめにチャットツールに通知したい場合などは15分ごとなど分単位で設定すると良いでしょう。ただ、トリガーの実行数にも上限があるので不必要に細かい間隔にするのは避けたほうが良いでしょう。

  • Googleアカウント(無料):90 min / day
  • Google Workspace ユーザー: 6 hr / day

おわりに

今回は、Gmailで受信したメールをチャットツールに通知するを例に「前回のスクリプト実行日時から今回の実行までの期間をどう指定するか」をお届けしました。

POSレジからの売上データなどをAPIでスプレッドシートに記録している場合などは、プロパティストアを使わずに、スプレッドシートの最終レコード(売上)の発生日時を起算日として取得するようにスクリプトを書けば良いでしょう。

もし実際に活用された方がいらっしゃったらTwitterなどで感想いただけると嬉しいです。

Google Apps Scriptを勉強したい方へ

この記事を見て、GASを勉強したいなと思われた方はぜひノンプログラマーのためのスキルアップ研究会(通称 ノンプロ研)にご参加ください。私も未経験からこの学習コミュニティに参加し、講座を受講したことでGASが書けるようになりました。

学習コミュニティ「ノンプログラマーのためのスキルアップ研究会」

挫折しがちなプログラミングの学習も、コミュニティの力で継続できます。ノンプロ研でお待ちしております!

タグ: ,
Share on:
Previous Post
debby-hudson-STo4ziY5g7A-unsplash
GAS活用法

タイムトラッカーアプリClockifyのタイムエントリ(時間記録) をAPIでGoogleカレンダーにコピーする その1 – APIの認証を通してサンプルリクエストを送る

Next Post
クッキング
freeeAPI

GAS x freeeAPIライブラリのトリセツ「自動同期で取得した取引に自動でタグを付与しよう」その3 – 取引を加工してPUT(更新)しよう