見出し画像

【GAS】Googleカレンダーの「招待された予定」を自動で承認する【Claude Sonnet 3.5】

背景

Googleカレンダーの予定の招待を自動で承認する公式機能があるが…

Googleカレンダーに送られてきた「予定の招待」は自動承認する機能が純正で用意されています。

が、これはメインのカレンダーには適用されないんです。。「施設予約用」みたいにサブカレンダーを設定して初めて使えます。

弊社では「参加しない人にはカレンダーを送らない」という文化なので、基本的にカレンダーは「参加」することになります。なので、メインカレンダーで「自動承認」できるようになってほしい。でも、公式機能では対応できませんでした。

でも放置できない

となると、諦めて「放置する」選択肢がでてきます。でも放置できない理由があります。Googleカレンダーの予約スケジュール機能を使えなくなるからです。

Googleカレンダーの予約スケジュール機能は、日程調整をする際に、Googleカレンダーの空いている日時をリンクで人に渡せる機能です。リンクを受け取った人は空いている日時を確認して、ボタンを押すだけで、お互いのカレンダーに予定が記入されます。

ただ、この機能、他の予定を「参加」処理しておかないと「予定が入っている」と見做されないんです。なので、予約スケジュール機能を使いたい場合は参加する予定はちゃんと「参加」と返答しておかなければいけないんですね。

そこで自動承認処理をするGASを作った

今回GASコードを書いていただく相手はClaude Sonnet 3.5さん。まずは雑にお願いをします。

Gmailに送られてきたGoogleカレンダーの会議招待メールに埋め込まれている「承認」ボタンを自動で押すGASプログラム

するとJavaScriptでコードを書いてくれます。これをGASプロジェクトにコピペして動かしてみます。

function autoAcceptCalendarInvites() {
  // 過去24時間以内に受信した未読メールを検索
  var threads = GmailApp.search('is:unread newer_than:1d');
  
  for (var i = 0; i < threads.length; i++) {
    var messages = threads[i].getMessages();
    
    for (var j = 0; j < messages.length; j++) {
      var message = messages[j];
      var body = message.getBody();
      
      // Googleカレンダーの招待メールかどうかを確認
      if (body.indexOf('calendar.google.com/calendar/event?action=RESPOND&eid=') > -1) {
        // 承認リンクを抽出
        var acceptLink = body.match(/https:\/\/www\.google\.com\/calendar\/event\?action=RESPOND&(?:amp;)?eid=[^&"]+&(?:amp;)?rst=1/);
        
        if (acceptLink) {
          // 承認リンクにアクセス
          var response = UrlFetchApp.fetch(acceptLink[0]);
          
          if (response.getResponseCode() == 200) {
            Logger.log('会議招待を承認しました: ' + message.getSubject());
            message.markRead(); // メールを既読にする
          } else {
            Logger.log('会議招待の承認に失敗しました: ' + message.getSubject());
          }
        }
      }
    }
  }
}

// トリガーを設定して定期的に実行
function setTrigger() {
  ScriptApp.newTrigger('autoAcceptCalendarInvites')
    .timeBased()
    .everyHours(1) // 1時間ごとに実行
    .create();
}

するとエラーが出てきてしまいました。何往復かエラー文をそのまま投げてみてやりとりをしましたが、解決せず。

どうやら、Gmailの承認リンクを見つけてHTTPリクエストを送信してくれているみたいですが、そこで弾かれてしまっているようです。

というわけで方針を転換して、Googleカレンダーを直接触ってもらうように頼みました。

メールを介さずカレンダーを直接操作して

するとGoogle Calendar APIを叩くと言って次のコードを出してくれました。

function autoAcceptCalendarInvites() {
  var calendars = CalendarApp.getAllCalendars();
  var now = new Date();
  var oneWeekFromNow = new Date(now.getTime() + 7 * 24 * 60 * 60 * 1000);
  
  for (var i = 0; i < calendars.length; i++) {
    var calendar = calendars[i];
    var events = calendar.getEvents(now, oneWeekFromNow);
    
    for (var j = 0; j < events.length; j++) {
      var event = events[j];
      if (event.getMyStatus() == CalendarApp.GuestStatus.INVITED) {
        event.setMyStatus(CalendarApp.GuestStatus.YES);
        Logger.log('予定を承認しました: ' + event.getTitle() + ' (' + event.getStartTime() + ')');
      }
    }
  }
  
  Logger.log('スクリプトの実行が完了しました。');
}

// トリガーを設定して定期的に実行
function setTrigger() {
  ScriptApp.newTrigger('autoAcceptCalendarInvites')
    .timeBased()
    .everyHours(1) // 1時間ごとに実行
    .create();
  Logger.log('1時間ごとの実行トリガーを設定しました。');
}

ただ、これもエラーが発生しました。しかし、その後数往復のエラーのやりとりを経て…

招待された予定を自動承認するプログラムが書けた

function autoAcceptCalendarInvites() {
  var calendar = CalendarApp.getDefaultCalendar(); // デフォルト(主要)カレンダーのみを取得
  var now = new Date();
  var oneMonthLater = new Date(now.getTime() + 30 * 24 * 60 * 60 * 1000); // 30日後
  
  Logger.log('処理開始: カレンダー = ' + calendar.getName());
  Logger.log('処理期間: ' + now.toLocaleString() + ' から ' + oneMonthLater.toLocaleString());
  
  try {
    var events = calendar.getEvents(now, oneMonthLater);
    Logger.log('イベント数: ' + events.length);
    
    for (var i = 0; i < events.length; i++) {
      var event = events[i];
      Logger.log('イベント処理中: ' + event.getTitle() + ' (' + event.getStartTime().toLocaleString() + ')');
      
      try {
        if (event.getMyStatus() == CalendarApp.GuestStatus.INVITED) {
          Logger.log('招待状態のイベントを検出: ' + event.getTitle());
          event.setMyStatus(CalendarApp.GuestStatus.YES);
          Logger.log('予定を承認しました: ' + event.getTitle() + ' (' + event.getStartTime().toLocaleString() + ')');
        }
      } catch (eventError) {
        Logger.log('イベント処理中にエラーが発生しました: ' + eventError.toString());
        Logger.log('イベントID: ' + event.getId());
        Logger.log('イベントタイトル: ' + event.getTitle());
        Logger.log('イベント開始時刻: ' + event.getStartTime().toLocaleString());
      }
    }
  } catch (calendarError) {
    Logger.log('カレンダー処理中にエラーが発生しました: ' + calendarError.toString());
  }
  
  Logger.log('スクリプトの実行が完了しました。');
}

// トリガーを設定して定期的に実行
function setHourlyTrigger() {
  // 既存のトリガーをすべて削除
  var triggers = ScriptApp.getProjectTriggers();
  for (var i = 0; i < triggers.length; i++) {
    ScriptApp.deleteTrigger(triggers[i]);
  }
  
  // 新しい1時間ごとのトリガーを設定
  ScriptApp.newTrigger('autoAcceptCalendarInvites')
    .timeBased()
    .everyHours(1)
    .create();
  Logger.log('1時間ごとの実行トリガーを設定しました。');
}
GASにコピペすれば実装完了。あとは適切にトリガーを設定すればOK

プログラムの説明

  1. autoAcceptCalendarInvites() 関数: この関数は、カレンダーの招待を自動的に承認するメイン処理を行います。 a. カレンダーと日付の設定:

    • ユーザーのデフォルトカレンダーを取得します。

    • 現在の日時と1ヶ月後の日時を設定します。

  2. setHourlyTrigger() 関数: この関数は、autoAcceptCalendarInvites() 関数を定期的に実行するためのトリガーを設定します。 a. 既存のトリガーの削除:

    • プロジェクト内の全ての既存トリガーを取得し、削除します。

    • これにより、重複トリガーの作成を防ぎます。

このスクリプトの主な特徴:

  1. 自動化: カレンダーの招待を自動的に承認します。

  2. 範囲: 現在から1ヶ月先までのイベントを対象とします。

  3. 定期実行: 1時間ごとに実行され、新しい招待に迅速に対応します。

  4. エラー耐性: 個々のイベント処理でエラーが発生しても、全体の処理は続行します。

  5. ログ記録: 詳細な処理状況をログに記録し、後で確認できるようにしています。

ただし、全ての招待を自動的に承認してしまうため、用法・容量を守ってお使いください。

では、今日はここまで。

この記事が気に入ったらサポートをしてみませんか?