GASを使用してGoogle CalendarのLocationに文字列を記録したイベントを削除する


スポンサードリンク

はじめに

MMORPGドラゴンクエスト10のエンドコンテンツ、常闇の聖戦をかなり頑張ってやっていた時期がありました。日によって常闇ボスの強さのレベルが変わっており、それを10年分Google Calendarに登録していたのですが、常闇は殆どやらなくなり、カレンダーで目にするのが煩わしくなっておりました。

カレンダー自体を消して作り直しても良かったのですが、常闇以外の予定は備忘録として残したかったので、GASを使用して特定のイベントをカレンダーから削除するスクリプトを作りました。参考にしたのは下記のURLの記事です。

teratail.com

kido0617.github.io

www.xenos.jp

macprimary.white-doll.net

注意点

GASは無償ライセンスですと、使用するにあたって制限事項があります。仕様書を読み込んでいないのでうろ覚えですが、1実行は6分以内、多くのカレンダーイベントの削除?ができないなどです。

10年分のイベントなので約3650件で、スクリプト1回では処理しきれません。1実行を6分以内に収める時間判定、遅延処理、トリガー、プロパティサービスという仕組みを使用しました。

トリガーというのはWindowsで言うところのタスクスケジューラみたいなものです。手動実行して5分後にトリガーを登録し、トリガーは更に1分後に関数を再帰呼出ししております。

プロパティサービスはグローバル変数というか、ローカルストレージみたいなものです。日付の範囲のどこまでを削除実行したのかは永続的に参照する必要があったので使用しています。

日付の範囲を作成→時間判定→トリガー処理→文字列検索・カレンダーのイベント削除→遅延処理というステップで処理します。

コード

function myFunction() {
  //開始時間
  var startTime = new Date();
  //カレンダーIdから取得
  var cal = CalendarApp.getCalendarById("ここにはカレンダーIdを入れます");
  //途中経過保存用
  var properties = PropertiesService.getScriptProperties();
  //何日目まで処理したかを保存するときに使用するkey
  var startDateKey = "startDate";
  //トリガーIDを保存するときに使用するkey
  var triggerKey = "trigger";
  //開始日を取得
  var startDate = properties.getProperty(startDateKey);
  if (!startDate) {
  //初めて実行する場合は指定日付で初期化
    startDate = new Date("ここには開始日付を入れます");
  } else startDate = new Date(startDate);
  //終了日を入力
  var endDate = new Date("ここには終了日付を入れます");
  var events = cal.getEvents(startDate, endDate);
  for (var n = 0; n < events.length; n++) {
    var diff = parseInt((new Date() - startTime) / (1000 * 60));
      //5分経過していたら処理を中断
    if (diff >= 5) {
      //処理日を保存
      var dateStr = Utilities.formatDate(events[n].getStartTime(), "JST", "yyyy/MM/dd");
      properties.setProperty(startDateKey, dateStr);
      //トリガーを発行
      setTrigger(triggerKey, "myFunction");
      return;
    }
    if (events[n].getLocation().match(/ここには検索文字列を入れます/)) {
      //イベント削除
      events[n].deleteEvent();
    }
    //1秒遅延
    Utilities.sleep(1000);
  }
  //全て実行終えたらトリガーと何日目まで実行したかを削除する
  deleteTrigger(triggerKey);
  properties.deleteProperty(startDateKey);
}
//指定したkeyに保存されているトリガーIDを使って、トリガーを削除する
function deleteTrigger(triggerKey) {
  var triggerId = PropertiesService.getScriptProperties().getProperty(triggerKey);
  
  if(!triggerId) return;
  
  ScriptApp.getProjectTriggers().filter(function(trigger){
    return trigger.getUniqueId() == triggerId;
  })
  .forEach(function(trigger) {
    ScriptApp.deleteTrigger(trigger);
  });
  PropertiesService.getScriptProperties().deleteProperty(triggerKey);
}
 
//トリガーを発行
function setTrigger(triggerKey, funcName){
  //保存しているトリガーがあったら削除
  deleteTrigger(triggerKey);
  var dt = new Date();
  //1分後に再実行
  dt.setMinutes(dt.getMinutes() + 1);
  var triggerId = ScriptApp.newTrigger(funcName).timeBased().at(dt).create().getUniqueId();
  //あとでトリガーを削除するためにトリガーIDを保存しておく
  PropertiesService.getScriptProperties().setProperty(triggerKey, triggerId);
}

解決できなかったこと

何故かトリガーが途中で記録されずに終了してしまうことがありました。何度かスクリプトを手動で実行して全てのイベントを削除できました。

2019/08/11
全体的に変数の命名やインデント、セミコロンの入れ忘れ、ループ処理の負荷を見直し、コードを書き換えました。

免責事項

本ページに掲載されているスクリプトの使用、または使用不具合等により生じたいかなる損害に関しましてブログ管理人は一切責任を負いません。