GASの特定時間定期実行を真剣に考えてみた

GAS定期実行

はじめに

Google Apps Script(以下、GAS)は非常に便利ですね😁
弊社もGoogle Work Space(以下、GWS)を導入しており、GWSの様々なリソースを活用するのに大変便利で、頻繁に使用しています。
今回は、特定の時間にGASを定期的に実行したいというニーズに応えるためのちょっとしたコツをお教えします。
最後までお読みください。

なにがしたいの?

GASのあるスクリプトを特定の時間に定期実行したいと考えています。
かみ砕いて言うと、「特定時刻ピッタリに関数を実行させる」というのが今回の主題。
例えば、以下のようなケースが考えられます。

  • 平日、10時から18時の間、毎時00分に実行したい
  • 毎日、10時00分に実行したい
  • and more…

GASのUI上でトリガーを設定して、特定時間に繰り返して関数を実行することはできます。
ですが、「時間ベースのタイマー」というのを選択したとしても、1時間の間のいつに実行かをユーザーが設定することはできません。(「毎時00分に実行」という風な設定はできません。)

AWS Lambdaでは、トリガーをcron形式でシンプルに設定できますが、GASでの最適な方法は何でしょうか?🤔

実現方法の概要

以下のようにGASを用いて処理を記述すれば、期待する動作を実現できます。

特定の時間を指定
最初のトリガー
処理開始
自身が呼びだされたトリガーを削除
メインの処理
次のトリガーを作成
処理終了

ドミノ倒しをイメージしてください。
最初のトリガーが、関数を呼び出します。
まず、自分が起動されたトリガーを削除して、処理を開始。
最後に、特定の時間に実行される次のトリガーを生成します。

実現方法

Step1: トリガーを生成する関数

次のような関数を作成します。

/**
 * トリガーをGASに設定する関数
 * 
 * @param String: functionName
 * @param DateTime: time
 * 
 */
function setTrigger(functionName, time) {
  ScriptApp.newTrigger(functionName)
    .timeBased()
    .at(time)
    .create();
}

Step2: 正規表現を利用して、特定の時間を指定する関数

Step1で作成した関数を使用して、特定の時間にトリガーを設定します。
特定の時間を汎用的に設定できるように、今回は正規表現を利用しています。
ただし、下の例のように特定期間にトリガーを絞り込む場合は、日を跨ぐトリガー生成に注意です。 この場合、GASのGUI上から設定できる時間駆動型のトリガーと上手く組み合わせると良いでしょう。

/**
 * 特定の時間でトリガーを設定する関数
 * 
 */
function setFixedIntervalTrigger() {
  const functionName = 'mainHandler';
  // 現在時刻を取得
  const time = new Date();

  // 月曜~金曜を判定する正規表現
  const regWeekDay = /[1-5]/; 

  // 9時~17時の間を判定する正規表現
  const regWorkHours =  /^(1[0-7]|9)$/;

  // トリガーを設定する分
  const minutes = 45;

  // 曜日と時間の条件を満たした場合にトリガーを設定
  if (regWorkHours.test(time.getHours()) && regWeekDay.test(time.getDay())) {
    // 次の時間 (hour) の固定分(minutes)でトリガーを設定
    time.setHours(time.getHours() + 1); 
    time.setMinutes(minutes);
    setTrigger(functionName, time);
  }
}

Step3: トリガーを削除する関数

特定の関数を呼び出すトリガーがGAS上に蓄積してしまうので、これらを削除する関数も用意します。

/**
 * トリガーを削除する関数
 * 
 * @param String: functionName
 * 
 */ 
function deleteTrigger(functionName){
  const triggers = ScriptApp.getProjectTriggers();
  for (const trigger of triggers) {
    if (trigger.getHandlerFunction() === functionName) {
      ScriptApp.deleteTrigger(trigger);
    }
  }
}

これをせずにでも動作はします。
しかし、トリガーが溜まってしまうと予期せぬ動作になってしまう恐れがあるので、
使用済みトリガーは削除しておくことを推奨します。

Step4: これらを組み合わせる

/**
 * メインの処理を実行する関数
 * 
 */
function mainHandler() {
 // 自身が呼び出されたトリガーを削除
  deleteTrigger('mainHandler');
 
  // メインの処理を記述
  console.log('Hello world')

 // 次のトリガーを設定
 setFixedIntervalTrigger(); 
}

// その他の関数は上記の通り

一番最初のトリガーが必要になりますが、適当にGASのエディタで mainHandlerを実行して下さい。

最後に

特定の時間にGASを定期実行するという要件は意外と多いです。
この記事が実装の参考になれば幸いです。

Next Post Previous Post