スクリプトトリガーは、スクリプトエディタの画面から入り、手動で登録するのが通常のフローです。しかし、「スクリプト内でテンポラリで時間トリガーを設置したい」であったり「トリガーの設置し直し」など、スクリプトエディタにいちいち入らず設定したいシーンがボチボチあります。そういった場合には、スクリプトからトリガーの設置や削除が出来ると便利です。二重に登録してしまったりすると、二回発動したり、片方しか発動しなかったりするので、慎重に設置をしましょう。
スプレッドシートでは、時間ベーストリガーの他にも、スプレッドシート特有のトリガーを設置する事が出来ます。使用することの出来る特有のトリガーは、「onChange」、「onEdit」、「onFormSubmit」、「onOpen」の合計4つとなります。それぞれ、「変更時」「編集時」「フォーム送信時」「起動時」の4つに該当します。以下に各トリガー設置のスクリプトを記載します。
onChange(変更時)
変更時とは、構造変更や内容変更が発生した時に発動するトリガーです。VBAで言う所のAfterUpdateイベントと言えます。
function createSpreadsheetTrigger() { var onChangeTrigger = ScriptApp.newTrigger("実行する関数名") .forSpreadsheet(ssId) .onChange() .create();onEdit(編集時)
編集時とは、編集時に毎回発動するトリガーです。VBAで言う所のBeforeUpdateイベントと言えます
var onChangeTrigger = ScriptApp.newTrigger("実行する関数名")
.forSpreadsheet(ssId) .onEdit() .create();onFormSubmit(フォーム送信時)
フォーム送信時とは、スプレッドシート側から作成したフォーム上でデータが送信された時に発動するトリガーです。
var onChangeTrigger = ScriptApp.newTrigger("実行する関数名")
.forSpreadsheet(ssId) .onFormSubmit() .create();onOpen(起動時)
起動時とは、そのスプレッドシートが開かれた時に発動するトリガーである。通常はメニューの登録などでよく使われている。
var onChangeTrigger = ScriptApp.newTrigger("実行する関数名")
.forSpreadsheet(ssId) .onOpen() .create();フォームでは、時間ベーストリガーは使用することが出来ません。また、使用できるトリガーはリファレンス上では、「onFormSubmit」、「onOpen」しかありません(しかし、実際にトリガー設置画面だと、onEditがあったりします)。殆ど、スプレッドシートと同じですので、ここでは省略します。変更箇所は、.forSpreadsheetが .forFormになるだけです。
スクリプトトリガーで最も使用する機会が多く、また、設置できるパターンの多いトリガーです。結構細かく時間トリガーを設置することが出来ますが、あまりにも短い時間にバンバンデータを取り込むようなトリガーを設置してしまうと、GoogleのサーバーにBANを食らったり、正常に動作しなくなることも考えられるので、設置に当たっては気をつけましょう。また、このトリガーが設置されたものは、例えファイルがゴミ箱に行こうともトリガーは動き続けますので、捨てる場合には、トリガーを削除してから捨てるようにしましょう。
これらのトリガーは、すべて±15分のラグを持って作動するようなので、分単位での綿密なトリガー作動は期待してはいけません。また、時間ベーストリガーの場合、SpreadsheetAppなどを使う時にはなるべくOpenByIdを利用するようにしましょう。getActiveSpreadsheetの場合エラーになることが多いようです。また、同様の理由でgetuiなども無意味ですのでメッセージボックス関係などのコードは外しておきましょう。
ミリセカンド後に実行
単位がミリセカンド(1/1000秒)なので、通常は数秒後という形で設定して使う時間トリガーです。下記の例では10秒後に実行するよう設定しています。
var onChangeTrigger = ScriptApp.newTrigger("実行する関数名").timeBased()
.after(10 * 1000) .create();特定日に実行
単位が特定日のスポット実行なので、日付型でデータを受け取っておき、atに続けて引数で渡してあげます。
var triggerDay = new Date(2012, 11, 1);var onChangeTrigger = ScriptApp.newTrigger("実行する関数名") .timeBased() .at(triggerDay) .create();特定日に実行(深夜に実行)
特定日に実行のものと殆ど同じですが、こちらは、深夜付近で実行がされるトリガーです。atDateに続けて引数で渡してあげます。
var onChangeTrigger = ScriptApp.newTrigger("実行する関数名") .timeBased() .atDate(2013,1,1) .create();○○時に実行
引数で取った数字(時間)に従って、その時になったら発動するトリガー。このトリガーは、この後に出てくる別のトリガーと組み合わせて使用します。下記ではeverydaysと組み合わせて使用しています。サンプルの例の場合、3日置きに午前5時〜6時の間でトリガーが発動します。
var onChangeTrigger = ScriptApp.newTrigger("実行する関数名") .timeBased() .atHour(5) .everyDays(3) .create();毎日○○時間毎に実行
引数で取った数字(時間)に従って、毎日その時間にトリガーが発動します。下記の例では、毎日12時にスクリプトが実行されます。
var onChangeTrigger = ScriptApp.newTrigger("実行する関数名") .timeBased() .everyHours(12) .create();毎分○○ごとに実行
引数で取った数字(分)に従って、○○分毎にトリガーが発動しつづけます。指定できる引数は、1分・5分・10分・15分・30分が指定可能です。
var onChangeTrigger = ScriptApp.newTrigger("実行する関数名") .timeBased() .everyMinutes(10) .create();指定週毎に指定曜日の指定時刻に実行
everyWeeksで毎週なのですが、引数に指定した数字にて、2週毎といった指定ができます。また、この指定オプションは、必ず、指定時刻とどの曜日なのか?を指定するatHourとonWeekDayの指定が必要です。
var onChangeTrigger = ScriptApp.newTrigger("実行する関数名").timeBased()
.atHour(9)
.everyWeeks(1)
.onWeekDay(ScriptApp.WeekDay.MONDAY)
.create();
タイムゾーンの指定
Googleドキュメント全般には、それぞれプロパティを見るとわかるのですが、アメリカ合衆国になっていたりすることがあります。スクリプトでもそれがあり、場合によっては、変更しておかないと妙な挙動をするスクリプトを作る羽目になります。
このオプションは、そのタイムゾーンを指定することの出来るメソッドです。
var onChangeTrigger = ScriptApp.newTrigger("実行する関数名").timeBased()
.atHour(12)
.everyDays(1)
.inTimezone("America/Los_Angeles")
.create();
トリガーの修正・削除は、いずれもまずは削除から始まります。そして、修正だけはこの後に改めて設置をするメソッドを発行することになります。しかし、スクリプトトリガーは設置者以外のトリガーが見えなかったりするので、Google Styleで紹介されているトリガー削除ルーチンがとても便利です。このルーチンの後に設置などのルーチンをつなげてあげれば、トリガーの設置し直しが完了するわけです。
function deleteTrigger(triggerId) { var allTriggers = ScriptApp.getScriptTriggers(); for(var i=0; i < allTriggers.length; i++) { if (allTriggers[i].getUniqueId() == triggerId) { ScriptApp.deleteTrigger(allTriggers[i]); break; } } }
しかし、このルーチンは、triggerIdが引数で必要であるため、自分の場合、引数をなくして、以下のようにし「トリガー全削除ルーチン」に改造して使います。
function deleteTrigger() { var allTriggers = ScriptApp.getScriptTriggers(); for(var i=0; i < allTriggers.length; i++) { ScriptApp.deleteTrigger(allTriggers[i]); } }
但し、当たり前ですが、トリガー全削除ルーチンは、根こそぎ全部削除してしまいますので、キメ細かくトリガーセッティングをしているケースでは、何か別の仕組みが必要になってくると思います。
トリガー対象となっているスクリプト内では、自動的にそれらが動くわけなのですが、通常とはちょっとだけ異なる挙動になるのと、タイムアウトの5分を意識して作らなければなりません。もし、スクリプトの実行が失敗しますと、サーバーからメールが飛んできます。
一見すると便利そうなスクリプトトリガーですが、実はめちゃめちゃ融通が効きません。細く設定できそうなトリガーの条件設定なのですが、モノすごくアバウトにしか設定できません。いい事例が、毎日10:30に発動とか、そういったことが出来ません。30分っていう設定は、分トリガーにしかなく、他は時間トリガーしかないわけです。そうなると、時間トリガーしかなく、割りと大きな情報収集トリガーを2本発動させた後にPDF化して送信というスクリプトを書くとなると、3時間後とかになってしまいます。そこで、これを何とかしようというのが今回の目的。
※尚、2本の情報収集トリガーは同時に発動すると、データが壊れるので時間を空けて挙げなければならない。
解決したい課題(自分の事例)
作成するコードとトリガー
で、結果的には・・・
毎日、AM1時にspecialdays関数が発動して全トリガーとデータの消去が行われた後、settingAllTrigger関数が呼ばれて新たに、以下のトリガーが設置されます
の5本が、毎日設置されなおされて継続していくわけです。こうすることで、非常に柔軟性のあるトリガーを設置して細かく挙動をコントロールすることが可能になります。
function onOpen() { /* ここに起動時のコードを記述する */}function silentget1() { /* ここにデータ取得1本目のコードを記述する */}function silentget2() { /* ここにデータ取得2本目のコードを記述する */}function sheet2pdf() { /* ここに特定のシートをPDF化するコードを記述する */}//今日の日付と指定された時刻で整形して返す関数function getDate(clockman){var date = new Date();
var year = date.getFullYear();
var month = date.getMonth() + 1;
var date = date.getDate();
if (month < 10) {
month = "0" + month;
}
if (date < 10) {
date = "0" + date;
}
var strDate = year + "/" + month + "/" +date + " " + clockman;return strDate;
}//自分自身のIDを取得するコードfunction setup(){ var sheet = SpreadsheetApp.getActiveSpreadsheet(); var myid = sheet.getId(); var scriptProperties = PropertiesService.getScriptProperties(); scriptProperties.setProperty("mysheetid", myid); return myid;}//トリガー設置の為の準備をする関数function specialdays(){//トリガー全削除 deleteTrigger(); //日付の変更 filterdays(); //シートのデータを全クリア clearsheet(); //トリガーを再設置する settingAllTrigger();}//トリガーを全削除するルーチンfunction deleteTrigger() { var allTriggers = ScriptApp.getScriptTriggers(); for(var i=0; i < allTriggers.length; i++) { ScriptApp.deleteTrigger(allTriggers[i]); }}//トリガーを設置しなおすルーチンfunction settingAllTrigger(){ //このスプレッドシートのIDを取得する var Properties = PropertiesService.getScriptProperties(); var myid = Properties.getProperty("mysheetid"); //トリガー用の日付の作成 var trigger1 = new Date(Utilities.formatDate(new Date() , 'JST' , getDate("10:00"))); var trigger2 = new Date(Utilities.formatDate(new Date() , 'JST' , getDate("10:30"))); var trigger3 = new Date(Utilities.formatDate(new Date() , 'JST' , getDate("11:00"))); //設置済みトリガーの数を計測する var Triggers = ScriptApp.getProjectTriggers(); var TriLength = Triggers.length; //設置済みトリガーの数によって作業を分岐 if(TriLength == ""){ //トリガーがなにもない場合 var onChangeTrigger = ScriptApp.newTrigger("onOpen") .forSpreadsheet(myid) .onOpen() .create(); var onChangeTrigger = ScriptApp.newTrigger("specialdays") .timeBased() .atHour(1) .everyDays(1) .create(); var onChangeTrigger = ScriptApp.newTrigger("silentget1") .timeBased() .at(trigger1) .create(); var onChangeTrigger = ScriptApp.newTrigger("silentget2") .timeBased() .at(trigger2) .create(); var onChangeTrigger = ScriptApp.newTrigger("sheet2pdf") .timeBased() .at(trigger3) .create(); }else{ //トリガーがある場合 var deleteman = deleteTrigger(); var onChangeTrigger = ScriptApp.newTrigger("onOpen") .forSpreadsheet(getMySheetId()) .onOpen() .create(); var onChangeTrigger = ScriptApp.newTrigger("specialdays") .timeBased() .atHour(1) .everyDays(1) .create(); var onChangeTrigger = ScriptApp.newTrigger("silentget1") .timeBased() .at(trigger1) .create(); var onChangeTrigger = ScriptApp.newTrigger("silentget2") .timeBased() .at(trigger2) .create(); var onChangeTrigger = ScriptApp.newTrigger("sheet2pdf") .timeBased() .at(trigger3) .create(); } }//シートのデータをクリアする関数function clearsheet(){ //このスプレッドシートのIDを取得する var Properties = PropertiesService.getScriptProperties(); var myid = Properties.getProperty("mysheetid"); //スクリプト本体 var ss = SpreadsheetApp.openById(myid); var sheet = ss.getSheetByName("database").getRange("A2:A18").getValues(); for(var i = 0;i<sheet.length;i++){ var sheetname = sheet[i][0]; var sheetrange = ss.getSheetByName(sheetname); var lastColumn = sheetrange.getLastColumn(); var lastRow = sheetrange.getLastRow(); sheetrange.getRange(2,1,lastRow,lastColumn).clear(); }}//データ出力日付を変更するルーチンfunction filterdays(){ //このファイルのIDを取得する var mybookid = PropertiesService.getScriptProperties(); var myid = mybookid.getProperty("mysheetid"); var sheet = SpreadsheetApp.openById(myid); var todayman = new Date(); var enddate = new Date(todayman.setDate(todayman.getDate() - 1)); var startdate = new Date(todayman.setDate(todayman.getDate() - 6)); //特定のセルに値を格納する sheet.getRangeByName("startday").setValue(getDate(startdate)); sheet.getRangeByName("endday").setValue(getDate(enddate)); sheet.getRangeByName("tokutoday").setValue(getDate(enddate)); }毎度毎度のことなのですが、Googleのリファレンスに載っていないので、色々情報を集めて、特定の日付・時刻によるトリガーの設置の為に、getDate関数を作成しました。引数に時間(HH:MM形式)を受けて、返り値として(YYYY/MM/DD HH:MM)の形で返すようにしています。getDate("10:30")とやると、返り値として本日の日付の10:30の日付型で値が返ってくるので、これをスクリプトトリガーのatに渡してあげるわけです。整形は、Utilities.formatDateで行っています。当たりまえですが、更にこれをnew Dateで日付型に戻して挙げるわけです。
下記は、今日の日付の10:40の日付型の値をtrigger1変数に受け取るコードです。時刻部分を、ユーザに入れさせるように、何かギミックを用意するのも悪くありませんね。
var trigger1 = new Date(Utilities.formatDate(new Date() , 'JST' , getDate("10:40")));下記は、受け取ったtrigger1の値をもとに、Script Triggerのat()をつかって、特定の日付・時刻を指定しています。
var onChangeTrigger = ScriptApp.newTrigger("silentget1") .timeBased()
.at(trigger1)
.create();
直接、at()の中に、【YYYY-MM-DD HH:MM】で書けたら楽なのにね・・・