⏲️

Google Apps Script の時間駆動トリガー設定を効率的に行う

2023/08/02に公開

Abstract

Google Apps Script は、時間駆動のトリガーによって実行できます。Ref これはクラウドコンピューティングを利用する際の非常に重要なポイントの 1 つです。時間駆動トリガーを使用する状況はユーザー毎に異なります。 しかしながら、時間駆動トリガーを実装するためのスクリプトを開発しようとすると、各スクリプトは大きく異なる上、複雑です。 このレポートでは、これを効率的に行うための方法を Google Apps Script ライブラリを使用して紹介します。

Introduction

このレポートでは、Google Apps Script のための時間駆動トリガーを効率的に設定する方法を紹介します。

Google Apps Script は手動での実行だけでなく、複数のトリガーで実行することができます。 時間駆動トリガーもその 1 つです。 時間駆動トリガーを使用すると、設定した時間にGoogle Apps Script を実行できます。 時間駆動トリガーを使用する状況はさまざまです。 そして、それぞれの状況毎にスクリプトを作成する場合、スクリプトが複雑になりやすいと思われます。Ref, Ref 実際、いくつかのシナリオで時間駆動トリガーを実行するためのスクリプトを作成する際、それぞれのスクリプトが大きく異なった経験があります。その際、Google Apps Script の時間駆動トリガーを効率的に使用できるロジックがあればと、Google Apps Script ライブラリを作成しました。Ref このライブラリを使用すると、時間駆動トリガーを使用したさまざまな状況のスクリプトを効率的に作成できます。ここではサンプルとなるシナリオと、各シナリオを実現するためのサンプルスクリプトを紹介します。

TriggerApp のインストール

サンプルスクリプトをテストする前に、Google Apps Script ライブラリの TriggerApp をインストールします。インストール方法は、こちらでご確認いただけます。

ライブラリをインストールするためのライブラリキーは、下記のとおりです。

1LihDPPHWBCcadYVBI3oZ4vOt7XqlowoHyBLdaDgRIx_5OpRBREA7Z1QB

アルゴリズム

このライブラリの基本アルゴリズムは非常に単純で、すでに一般に行われているような内容です。このアルゴリズムの重要な点は、一つのオブジェクトを使ってさまざまなトリガータスクを効率的に管理することです。 これを説明するために、次の図示したサンプルのトリガープロセスを使用します。

この図はサンプルとして、一つのトリガープロセスを示しています。 例えば、「00:00」が「2024–01–01 00:00:00」の場合、「workFunction1」、「workFunction2」、「workFunction3」は以下のように実行されます。

  • 「workFunction1」: 「2024–01–01T00:00:00」、「2024–01–01T06:00:00」
  • 「workFunction2」: 「2024–01–01T00:30:00」~「2024–01–01T02:30:00」、「2024–01–01T03:30:00」~「2024–01–01T05:30:00」、「2024–01–01T06:30:00」~「2024–01–01T08:30:00」
  • 「workFunction3」: 「2024–01–01T03:00:00」、「2024–01–01T09:00:00」

このプロセスを実行するためには、現時点の次のトリガー時刻だけを設定することで、実行可能ではないかと考えました。上の画像を使用すると、

  • 現在日時が「2023–12–31T23:00:00」の場合、「workFunction1」が実行する次のトリガー時刻は「2024–01–01T00:00:00」です。
  • 現在日時が「2024–01–01T00:00:00」の場合、「workFunction2」が実行する次のトリガー時刻は「2024–01–01T00:30:00」です。
  • これは最後のタスクでも繰り返されます。
  • 現在日時が「2024–01–01T09:00:00」の場合、次のトリガーは設定されません。

上記のフローでは、次の2つの時間駆動トリガーだけでこのサンプルプロセスを実現できます。

  • Work function を実行するための時間駆動トリガー
  • 次の work function を実行するためのトリガーを設定するための時間駆動トリガー

Work function が1つだけの場合、時間駆動トリガーが 1 つだけでも、このプロセスは達成できると思います。 しかし、私の実際の状況では、複数の Work function を使用する可能性を考慮した結果、2つの時間駆動トリガーを必要とする仕様としました。

制限事項

  • 時間駆動のトリガーは、「Google サービスの割り当ての現在の制限」に従います。 Ref
    TriggerAppのオブジェクトのtoDayを使用しない場合、トリガーサイクルは無限に繰り返されます。 ただし、Google 側でエラーが発生するとトリガーが停止します。 その際は、再度 main 関数を実行してください。 これにより、トリガ処理が再開されます。
  • このライブラリでは、トリガー間の最小間隔を 60 秒に設定しています。 現段階では、最小インターバル時間が60秒未満の場合、時間駆動トリガーで実行される関数によって別の時間駆動トリガを設定することができないことが原因です。この状況は将来のアップデートで解決される可能性があります。
  • このライブラリを使って時間駆動トリガーを何度もテストしてみたところ、例えば「00:00:00」に関数を実行しようとしても、少なくともスクリプトは「00:00:00」以降に実行されます。ただし、「00:01:30」に関数が実行される場合がありました。 この問題の原因は Google 側にあるのではないかと推測しました。 これも制限となる可能性があります。
  • これを制限事項セクションに含める必要があるかどうかはわかりません。 「everyYear」プロパティを使用して関数を実行したい場合、Google Apps Scriptプロジェクトで長時間スクリプトが実行されない場合、スコープの再認可が必要になるのではないかと心配です。 しかし、現段階ではこの問題については経験がありません。
  • このライブラリは、時間駆動トリガーを使用する多くのシナリオに適応できると思います。 しかし、その一方で、このライブラリがすべてのシナリオで使用できるわけではないと思います。 ご注意ください。

シナリオ

このセクションでは、時間駆動トリガーを使用するためのサンプルシナリオを紹介します。

ここでは、サンプルの現在日時を2024 年 1 月 1 日 00:00:00であると仮定します。

1. 特定の日時に2種類の関数を実行

トリガー日

トリガー時間

  • 「2024–01–15T09:00:00」に「sampleFunction1」を実行します。
  • 「2024–02–25T10:00:00」に「sampleFunction2」を実行します。

スクリプト

function sample(e) {
  const obj = [
    {
      ownFunctionName: "sample",
      functionName: "sampleFunction1",
      everyMonth: [15],
      atTimes: ["09:00:00"],
      fromDay: "2024-01-15T00:00:00.000",
      toDay: "2024-01-16T00:00:00.000",
    },
    {
      ownFunctionName: "sample",
      functionName: "sampleFunction2",
      everyMonth: [25],
      atTimes: ["10:00:00"],
      fromDay: "2024-02-25T00:00:00.000",
      toDay: "2024-02-26T00:00:00.000",
    }
  ];
  const res = TriggerApp.setEventObject(e).installTriggers(obj, console.log);
  console.log(res);
}

ownFunctionName に TriggerApp を実行する関数の関数名を設定し、setEventobject にイベントオブジェクトeを設定してください。

たとえば、const res = TriggerApp.setEventObject(e).installTriggers(obj, console.log) を const res = TriggerApp.setEventObject(e).simulateTriggers(obj) に置き換えると、このプロセスをシミュレートできます。 上記の obj を使用すると以下の結果が返されます。

[
  { triggerTime: '2024-01-15T09:00:00', executeFunction: 'sampleFunction1' },
  { triggerTime: '2024-02-25T10:00:00', executeFunction: 'sampleFunction2' }
]

この結果から、obj の値は installTriggers を使用する場合に有効な値であり、期待した処理が実現できることがわかります。

2. 特定の日の特定の時間に特定の周期で関数を実行

トリガー 日

トリガー時間

  • 「2024–02–01T00:00:00」から「2024–08–01T00:00:00」まで、毎日「09:00」から「17:00」まで 10 分ごとに「sampleFunction1」を実行します。

スクリプト

function sample(e) {
  const obj = [{
    ownFunctionName: "sample",
    functionName: "sampleFunction1",
    everyDay: true,
    interval: 600,
    fromTime: "09:00",
    toTime: "17:00",
    fromDay: "2024-02-01T00:00:00.000",
    toDay: "2024-08-01T00:00:00.000",
  }];
  const res = TriggerApp.setEventObject(e).installTriggers(obj, console.log);
  console.log(res);
}

このスクリプトでは、obj[0] から toDay プロパティが削除されると、このサイクルは終了せずに実行され続けます。 最終日を設定したくない場合は、toDay プロパティを含める必要はありません。

3. 平日の特定の時間帯に特定の周期で3種類の関数を実行

トリガー日

トリガー時間

これらのトリガー タスクは、毎週月曜日、火曜日、水曜日、木曜日、金曜日に実行されます。 1日の発動時間は以下の通りです。

  • 「sampleFunction1」は「09:00:00」に実行されます。
  • 「sampleFunction2」は「09:10:00」から「12:00:00」まで10分ごとに実行されます。
  • 「sampleFunction2」は「13:00:00」から「16:50:00」まで10分ごとに実行されます。
  • 「sampleFunction3」は「17:00:00」に実行されます。

スクリプト

function sample(e) {
  const obj = [
    {
      ownFunctionName: "sample",
      functionName: "sampleFunction1",
      everyWeek: ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"],
      atTimes: ["09:00:00"],
    },
    {
      ownFunctionName: "sample",
      functionName: "sampleFunction2",
      everyWeek: ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"],
      interval: 600,
      fromTime: "09:10",
      toTime: "12:00",
    },
    {
      ownFunctionName: "sample",
      functionName: "sampleFunction2",
      everyWeek: ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"],
      interval: 600,
      fromTime: "13:00",
      toTime: "16:50",
    },
    {
      ownFunctionName: "sample",
      functionName: "sampleFunction3",
      everyWeek: ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"],
      atTimes: ["17:00:00"],
    },
  ];
  const res = TriggerApp.setEventObject(e).installTriggers(obj, console.log);
  console.log(res);
}

このスクリプトには、toDay プロパティは含まれていません。 これにより、このサイクルは最終日なしで実行され続けます。

4. 毎年誕生日にメールを送信

サンプルとして誕生日を10月1日とします。

トリガー日

トリガー時間

  • 「sampleFunction1」は毎年10月1日の「09:00:00」に実行されます。 1回目の実行は「2024–10–01T09:00:00」です。

スクリプト

function sample(e) {
  const obj = [{
    ownFunctionName: "sample",
    functionName: "sampleFunction1",
    everyYear: ["2024-10-01"],
    atTimes: ["09:00:00"],
  }];
  const res = TriggerApp.setEventObject(e).installTriggers(obj, console.log);
  console.log(res);
}

このスクリプトでは、関数「sampleFunction1」が毎年 10 月 1 日に実行されます。

5. 週の特定の曜日に特定の関数を実行

トリガー日

この場合、2024 年 1 月のみが使用されます。

トリガー時間

  • 「sampleFunction1」は月、水、金の「09:00:00」から「12:00:00」まで10分間隔で実行されます。
  • 「sampleFunction2」は火、木、土曜日の「13:00:00」から「17:00:00」まで10分間隔で実行されます。

スクリプト

function sample(e) {
  const obj = [
    {
      ownFunctionName: "sample",
      functionName: "sampleFunction1",
      everyWeek: ["Monday", "Wednesday", "Friday"],
      interval: 600,
      fromTime: "09:00",
      toTime: "12:00",
      fromDay: "2024-01-01T00:00:00.000",
      toDay: "2024-02-01T00:00:00.000"
    },
    {
      ownFunctionName: "sample",
      functionName: "sampleFunction2",
      everyWeek: ["Tuesday", "Thursday", "Saturday"],
      interval: 600,
      fromTime: "13:00",
      toTime: "17:00",
      fromDay: "2024-01-01T00:00:00.000",
      toDay: "2024-02-01T00:00:00.000"
    },
  ];
  const res = TriggerApp.setEventObject(e).installTriggers(obj, console.log);
  console.log(res);
}

このスクリプトには toDay プロパティが含まれています。 これにより、このサイクルは最終日の 2024 年 2 月 1 日まで実行されます。 toDay プロパティが削除されると、このサイクルは終了せずに実行され続けます。

6. 6種類の関数を「09:00:00」から「11:50:00」まで10分ごとに順に実行

トリガー日

この場合、現在時刻は「2024–01–01T00:00:00.000」で、toDay プロパティは「2024–01–04T00:00:00.000」に設定されます。 この条件から「2024–01–01」から「2024–01–03」が選択されます。

トリガー時間

この場合、以下のようなフローが実行されます。

  • 「2024–01–01」~「2024–01–03」の09:00:00~11:50:00の間に「sampleFunction1」~「sampleFunction6」の6つの関数を10分間隔で順番に実行します。

スクリプト

function sample(e) {
  const n = 18;
  let start = new Date();
  start.setHours(9, 0, 0, 0);
  const ar1 = Array(n).fill(null);
  const obj = [...Array(Math.ceil(ar1.length / 6))].flatMap((_, i) =>
    ar1.splice(0, 6).map((_, j) => {
      const temp = {
        ownFunctionName: "sample",
        functionName: `sampleFunction${j + 1}`,
        everyDay: true,
        atTimes: [Utilities.formatDate(start, Session.getScriptTimeZone(), "HH:mm:ss")],
        toDay: "2024-01-04T00:00:00.000"
      }
      start = new Date(start.getTime() + 10 * 60 * 1000);
      return temp;
    })
  );
  const res = TriggerApp.setEventObject(e).installTriggers(obj, console.log);
  console.log(res);
}

例えば、simulateTriggersメソッドでこのobjを使用すると、次のような結果が得られます。

[ 
  { triggerTime: '2024-01-01T09:00:00', executeFunction: 'sampleFunction1' },
  { triggerTime: '2024-01-01T09:10:00', executeFunction: 'sampleFunction2' },
  { triggerTime: '2024-01-01T09:20:00', executeFunction: 'sampleFunction3' },
  { triggerTime: '2024-01-01T09:30:00', executeFunction: 'sampleFunction4' },
  { triggerTime: '2024-01-01T09:40:00', executeFunction: 'sampleFunction5' },
  { triggerTime: '2024-01-01T09:50:00', executeFunction: 'sampleFunction6' },
  { triggerTime: '2024-01-01T10:00:00', executeFunction: 'sampleFunction1' },
  { triggerTime: '2024-01-01T10:10:00', executeFunction: 'sampleFunction2' },
  { triggerTime: '2024-01-01T10:20:00', executeFunction: 'sampleFunction3' },
  { triggerTime: '2024-01-01T10:30:00', executeFunction: 'sampleFunction4' },
  { triggerTime: '2024-01-01T10:40:00', executeFunction: 'sampleFunction5' },
  { triggerTime: '2024-01-01T10:50:00', executeFunction: 'sampleFunction6' },
  { triggerTime: '2024-01-01T11:00:00', executeFunction: 'sampleFunction1' },
  { triggerTime: '2024-01-01T11:10:00', executeFunction: 'sampleFunction2' },
  { triggerTime: '2024-01-01T11:20:00', executeFunction: 'sampleFunction3' },
  { triggerTime: '2024-01-01T11:30:00', executeFunction: 'sampleFunction4' },
  { triggerTime: '2024-01-01T11:40:00', executeFunction: 'sampleFunction5' },
  { triggerTime: '2024-01-01T11:50:00', executeFunction: 'sampleFunction6' },
  { triggerTime: '2024-01-02T09:00:00', executeFunction: 'sampleFunction1' },
  { triggerTime: '2024-01-02T09:10:00', executeFunction: 'sampleFunction2' },
  ,
  ,
  ,
]

このライブラリでは、simulateTriggersメソッドを利用することでトリガータスクの詳細な処理を確認できることが分かります。

7. Googleフォームの受付時間を平日9:00~17:00に設定

トリガー日

トリガー時間

  • Googleフォームは平日9時にオープンされます。
  • Googleフォームは平日17時にクローズされます。

スクリプト

このサンプルを使用する場合は、以下のスクリプトを Google フォームのスクリプト エディタにコピー&ペーストしてください。

const openForm = _ => FormApp.getActiveForm().setAcceptingResponses(true);
const closeForm = _ => FormApp.getActiveForm().setAcceptingResponses(false).setCustomClosedFormMessage("off duty");

function sample(e) {
  const obj = [
    {
      ownFunctionName: "sample",
      functionName: "openForm",
      everyWeek: ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"],
      atTimes: ["9:00:00"],
    },
    {
      ownFunctionName: "sample",
      functionName: "closeForm",
      everyWeek: ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"],
      atTimes: ["17:00:00"],
    },
  ];
  const res = TriggerApp.setEventObject(e).installTriggers(obj, console.log);
  console.log(res);
}

8. Google スプレッドシートの使用時間を平日 9:00~17:00 に設定

トリガー日

トリガー時間

  • Google スプレッドシートは平日の 9 時に使用可能になります。
  • Google スプレッドシートは平日 17 時で使用不可(書き込み不可)になります。

スクリプト

このサンプルを使用する場合は、次のスクリプトをコピーしてスタンドアロンスクリプトのスクリプトエディターに貼り付けてください。 そして、以下のスクリプトにスプレッドシート ID を設定してください。 また、Google の Advanced Google services で Drive API を有効にしてください。

const openSpreadsheet = _ => Drive.Files.patch({ contentRestrictions: [{ readOnly: false }] }, "###spreadsheetId###");
const closeSpreadsheet = _ => Drive.Files.patch({ contentRestrictions: [{ readOnly: true, reason: "off duty" }] }, "###spreadsheetId###");

function sample(e) {
  const obj = [
    {
      ownFunctionName: "sample",
      functionName: "openSpreadsheet",
      everyWeek: ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"],
      atTimes: ["9:00:00"],
    },
    {
      ownFunctionName: "sample",
      functionName: "closeSpreadsheet",
      everyWeek: ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"],
      atTimes: ["17:00:00"],
    },
  ];
  const res = TriggerApp.setEventObject(e).installTriggers(obj, console.log);
  console.log(res);
}

「closeSpreadsheet」を実行すると、スプレッドシートがロックされます。 これにより、スプレッドシートの所有者を含む全てのユーザーはスプレッドシートを編集することができなくなります。閲覧は可能です。「openSpreadsheet」を使用すると、スプレッドシートのロックが解除されます。

9. 特定の月初めの月曜日から金曜日の 9 時に特定の関数を実行

トリガー日

トリガー時間

このサンプルでは、1 月、3 月、5 月が使用されます。

  • 「sampleFunction1」は、1月1日から1月5日のそれぞれ09:00に実行されます。
  • 「sampleFunction1」は、3月4日から3月8日のそれぞれ09:00に実行されます。
  • 「sampleFunction1」は、5月6日から5月10日のそれぞれ09:00に実行されます。

スクリプト

function sample(e) {
  const executeMonth = ["2024-01-01", "2024-03-01", "2024-05-01"];
  const obj = executeMonth.map(e => {
    const date = new Date(e);
    const topMonday = [...Array(7)].map((_, i) => {
      const now = new Date(date.getTime());
      now.setDate(i + 1);
      return now;
    }).find(d => d.getDay() == 1 ? true : false);
    const dates = [...Array(5)].map((_, i) => {
      const temp = new Date(topMonday.getTime());
      temp.setDate(temp.getDate() + i);
      return temp.getDate();
    });
    date.setMonth(date.getMonth() + 1)
    const toDay = Utilities.formatDate(date, Session.getScriptTimeZone(), "yyyy-MM-dd");
    return {
      ownFunctionName: "sample",
      functionName: "sampleFunction1",
      everyMonth: dates,
      atTimes: ["09:00:00"],
      fromDay: e + "T00:00:00",
      toDay: toDay + "T00:00:00",
    };
  });
  const res = TriggerApp.setEventObject(e).installTriggers(obj, console.log);
  console.log(res);
}

例えば、simulateTriggersメソッドでこのobjを使用すると、以下の結果が得られます。このサンプルでは現在が「2024–01–01T00:00:00」であるとします。ご注意ください。

[ 
  { triggerTime: '2024-01-01T09:00:00',
    executeFunction: 'sampleFunction1' },
  { triggerTime: '2024-01-02T09:00:00',
    executeFunction: 'sampleFunction1' },
  { triggerTime: '2024-01-03T09:00:00',
    executeFunction: 'sampleFunction1' },
  { triggerTime: '2024-01-04T09:00:00',
    executeFunction: 'sampleFunction1' },
  { triggerTime: '2024-01-05T09:00:00',
    executeFunction: 'sampleFunction1' },
  { triggerTime: '2024-03-04T09:00:00',
    executeFunction: 'sampleFunction1' },
  { triggerTime: '2024-03-05T09:00:00',
    executeFunction: 'sampleFunction1' },
  { triggerTime: '2024-03-06T09:00:00',
    executeFunction: 'sampleFunction1' },
  { triggerTime: '2024-03-07T09:00:00',
    executeFunction: 'sampleFunction1' },
  { triggerTime: '2024-03-08T09:00:00',
    executeFunction: 'sampleFunction1' },
  { triggerTime: '2024-05-06T09:00:00',
    executeFunction: 'sampleFunction1' },
  { triggerTime: '2024-05-07T09:00:00',
    executeFunction: 'sampleFunction1' },
  { triggerTime: '2024-05-08T09:00:00',
    executeFunction: 'sampleFunction1' },
  { triggerTime: '2024-05-09T09:00:00',
    executeFunction: 'sampleFunction1' },
  { triggerTime: '2024-05-10T09:00:00',
    executeFunction: 'sampleFunction1' }
]

参考文献

最後に

上記のサンプルシナリオは多数あるシナリオのいくつかです。さまざまなトリガータスクを効率的に使用するために TriggerApp を作成しました。 このライブラリは、上記のサンプルシナリオ以外にもさまざまなシーンで使用できると考えています。 これが皆様にもお役に立てれば幸いです。

Google Cloud Japan

Discussion