After deleting an event in Google Calendar using GAS, send the contents to any email address through a spreadsheet

Currently, when you create or update an event on Google Calendar, an email notification is sent to any email address, but when you delete an event, you do not receive an email notification.

The specifications are as follows.

-You will now receive a notification even if you change an existing schedule.
・As a workaround to avoid receiving two emails with the same notification,
Even if multiple scripts are started with one event object, the subsequent scripts will be interrupted.

  • The date and time of the previous and current notifications, the calendar name, and the update date and time for each individual schedule are now displayed in the email.
    -Even if there are multiple schedule updates, they will be combined into one email.
  • Only calendars with triggers set are processed.

Below is the GAS code.

function monitorMyCalendar(e) {
  if (e) {
    try {
      // 表示用の文字列
      const isNotExist = 'が設定されていません';

      // 過去にイベントオブジェクトが複数発生(スクリプトが複数起動)したことへの対応
      const lock = LockService.getScriptLock();
      lock.waitLock(0);

      // プロパティサービスから前回実行日時を取得
      const properties = PropertiesService.getScriptProperties();
      const lastUpdated = new Date(properties.getProperty('lastUpdated'));
      const noticedDate = formatDate_(lastUpdated);

      // スクリプトの実行日時を取得
      const currentTime = new Date();
      const currentDate = formatDate_(currentTime);

      // 実行日と2週間後及び6ヶ月後の日付を生成
      const today = new Date();
      today.setHours(0, 0, 0, 0); // 時刻をクリア
      const twoWeeksLater = new Date(today);
      twoWeeksLater.setDate(twoWeeksLater.getDate() + 14); // 2週間後の日付
      const sixMonthsLater = new Date(today);
      sixMonthsLater.setMonth(today.getMonth() + 6); // 6ヶ月後の日付

      // 更新されたカレンダーを6ヶ月後まで取得
      const calendar = CalendarApp.getCalendarById(e.calendarId);
      const events = calendar.getEvents(today, sixMonthsLater); // 6ヶ月後までの予定を取得

      let noticeCount = 0; // 通知されるイベントの数をカウントする変数
      const mailBodies = []; // 通知内容を蓄積する配列
      const twoWeeksMap = new Map();

      // 追加・更新された予定を検出
      for (const event of events) {
        const eventUpdated = event.getLastUpdated();
        if (eventUpdated > twoWeeksLater) {
          break;
        } else if (eventUpdated > lastUpdated) {
          twoWeeksMap.set(event.getId(), eventUpdated);
          // メール通知項目を生成
          const eventDetails = {
            title: event.getTitle() || 'タイトル' + isNotExist,
            startTime: event.getStartTime() ? formatDate_(event.getStartTime()) : '開始日時' + isNotExist,
            endTime: event.getEndTime() ? formatDate_(event.getEndTime()) : '終了日時' + isNotExist,
            updateTime: formatDate_(eventUpdated),
            description: event.getDescription() || '詳細' + isNotExist,
            location: event.getLocation() || '場所' + isNotExist,
            url: 'https://www.google.com/calendar/event?eid=' + Utilities.base64Encode(event.getId().split('@')[0] + ' ' + e.calendarId), // URL を直接指定
            calendarId: e.calendarId,
            calendarName: calendar.getName(),
          };
          // メール本文を蓄積する
          mailBodies.push(eventDetails);
          noticeCount++; // 通知されるイベントの数を増やす
        }
      }

      // 削除確認用の予定の控えをスプレッドシートから復元し、2週間分のみ抽出(シート名はカレンダーIDの最初の16文字)
      const SPREADSHEET_ID = "1YvJVA74M8otxcrGXbXbc7KAPKUwPqGrSHBdCR86yaxA";
      const ss = SpreadsheetApp.openById(SPREADSHEET_ID);
      const sheet = ss.getSheetByName(e.calendarId.slice(0, 16)) ?? ss.insertSheet(e.calendarId.slice(0, 16));
      const savedEvents = sheet.getDataRange().getValues();
      const twoWeeksSavedEvents = savedEvents.filter(data => data[5] >= today && data[5] <= twoWeeksLater);
      for (const data of twoWeeksSavedEvents) {
        if (!twoWeeksMap.has(data[0])) {
          // 削除された予定を検出
          Logger.log('削除された予定を検出');
          for (const data of deletedEvents) {
            // メール通知項目を生成
            const eventDetails = {
              title: data[3] || 'タイトル' + isNotExist,
              startTime: data[5] ? formatDate_(data[5]) : '開始日時' + isNotExist,
              endTime: data[6] ? formatDate_(data[6]) : '終了日時' + isNotExist,
              updateTime: formatDate_(calendar.getEventById(data[0]).getLastUpdated()),
              description: data[8] || '詳細' + isNotExist,
              location: data[7] || '場所' + isNotExist,
              url: '',
              calendarId: e.calendarId,
              calendarName: calendar.getName(),
            };
            // メール本文を蓄積する
            mailBodies.push(eventDetails)
            noticeCount++; // 通知されるイベントの数を増やす
          }
        }
      }

      if (mailBodies.length > 0) {
        // 開始日時順に並び替え
        mailBodies.sort((a, b) => new Date(a.startTime) - new Date(b.startTime));
        // メールを送信
        sendEmailNotification_(mailBodies, { noticedDate, currentDate });
        // 最後の通知時刻を保存
        properties.setProperty('lastUpdated', currentTime.toISOString());
      }
      Logger.log('通知されるイベントの数: ' + noticeCount);

      // 6ヶ月分の予定の控えを更新(保存)
      if (events.length > 0) {
        const values = events.map(event => [
          event.getId(),  // イベントID[0]
          calendar.getName(), // カレンダー名[1]
          e.calendarId,  // カレンダーID[2]
          event.getTitle(),  // タイトル[3]
          event.getLastUpdated(), // 最終更新日時[4]
          event.getStartTime(), // 開始日時[5]
          event.getEndTime(), // 終了日時[6]
          event.getLocation(), // 場所[7]
          event.getDescription(), // 詳細[8]
        ]);
        sheet.clearContents();
        sheet.getRange(1, 1, values.length, values[0].length).setValues(values);
      }

      // スクリプトのロックを解放
      Utilities.sleep(300);
      lock.releaseLock();

    } catch (error) {
      if (error.toString().includes('Lock timeout')) {
        Logger.log('実行中のスクリプトが重複しているので処理を中断しました');
      } else {
        Logger.log('予定の確認中に次のエラーが発生しました: ' + error);
      }
    }
  } else {
    console.log('エディタからは実行できません');
  }
}

// 日付を指定された形式に整形(日付が文字列の場合に対応)
function formatDate_(date) {
  return Utilities.formatDate(new Date(date), 'JST', 'yyyy/MM/dd HH:mm'); // 日本時間で表示
}

// 通知を送信
function sendEmailNotification_(mailBodies, date) {
  try {
    const recipientEmail = '[email protected]'; // 送信先のメールアドレスを設定してください
    const subject = "Googleカレンダーのイベントが更新されました";
    let body = '前回の通知(' + date.noticedDate + ')以降、Googleカレンダーのイベントが更新されました。n' +
      '今回の通知(' + date.currentDate + ')対象のカレンダー名: ' + mailBodies[0].calendarName + 'nn';
    for (const item of mailBodies) {
      body +=
        '作成・更新日時: ' + item.updateTime + 'n' +
        'タイトル: ' + item.title + 'n' +
        '開始日時: ' + item.startTime + 'n' +
        '終了日時: ' + item.endTime + 'n' +
        '場所: ' + item.location + 'n' +
        '詳細: ' + item.description + 'n' +
        'URL: ' + item.url + 'nn';
    }
    MailApp.sendEmail(recipientEmail, subject, body);
    Logger.log('メールが送信されました: ' + body);
  } catch (error) {
    // メール送信中にエラーが発生した場合、エラーメッセージをログに出力する
    Logger.log('メールの送信中にエラーが発生しました: ' + error);
  }
}

If deletedEvents is not defined, you can check it in the trigger console, but I am at a loss as to how to write the processing here.

Make sure to save it when the script finishes. …① (described later)
When the script starts, it retrieves all future schedules and…②
Next, restore the schedule from ① that was saved at the last startup.
Comparing ① with ②, among the schedules within 2 weeks from the startup date and time
We will notify you by email of any plans that are not listed in ②.
Finally, save all the plans in ②. …New ① (restored at next startup)

*Since it is unknown when the next startup will be,
Get and save all future plans, not just for two weeks.
*After deletion, you will no longer be able to retrieve it.
In ①, save all the items necessary for notifications, such as the ID of each event, title, start date and time, end date and time, location, and details.

Also, multiple event objects occur with one calendar update trigger, so
It will also be necessary to address this issue.
Save all appointments in a spreadsheet, collate all old and new appointments, etc.
Depending on the number of schedules, it may take some time to execute.

The above is the goal, but our goal is to receive email notifications even when a message is “deleted,” so we appreciate your cooperation.

New contributor

KKS is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật