使用 GAS 删除 Google 日历中的活动后,通过电子表格将内容发送到任何电子邮件地址

问题描述 投票:0回答:1

目前,当您在 Google 日历上创建或更新活动时,系统会向任何电子邮件地址发送电子邮件通知,但当您删除活动时,您不会收到电子邮件通知。

规格如下。

  • 即使您更改现有时间表,您现在也会收到通知。
  • 作为避免收到两封具有相同通知的电子邮件的解决方法, 即使使用一个事件对象启动多个脚本,后续脚本也会被中断。
  • 以前和当前通知的日期和时间、日历名称以及每个单独计划的更新日期和时间现在都显示在电子邮件中。 -即使有多个日程更新,它们也会合并到一封电子邮件中。
  • 仅处理设置了触发器的日历。

以下是GAS代码。

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 + '\n\n';
    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 + '\n\n';
    }
    MailApp.sendEmail(recipientEmail, subject, body);
    Logger.log('メールが送信されました: ' + body);
  } catch (error) {
    // メール送信中にエラーが発生した場合、エラーメッセージをログに出力する
    Logger.log('メールの送信中にエラーが発生しました: ' + error);
  }
}

如果deletedEvents没有定义,可以在触发控制台查看,但是这里的处理我不知道怎么写

确保脚本完成后保存它。 …①(稍后描述) 当脚本启动时,它会检索所有未来的时间表并且......② 接下来,从上次启动时保存的①中恢复计划。 比较从启动日期和时间起 2 周内的日程中的①和② 对于②中未列出的任何计划,我们将通过电子邮件通知您。 最后保存②中的所有计划。 …新建①(下次启动时恢复)

*由于不知道下次启动是什么时候, 获取并保存所有未来计划,而不仅仅是两周。 *删除后,您将无法再找回。 在①中,保存通知所需的所有项目,例如每个事件的ID、标题、开始日期和时间、结束日期和时间、地点和详细信息。

此外,一个日历更新触发器会发生多个事件对象,因此 也有必要解决这个问题。 将所有约会保存在电子表格中,整理所有新旧约会等。 根据计划的数量,执行可能需要一些时间。

以上是目标,但我们的目标是即使消息被“删除”也能收到电子邮件通知,因此我们感谢您的合作。

javascript email google-sheets google-apps-script google-calendar-api
1个回答
0
投票

从您的以下回复来看,

  1. →创建事件时添加到电子表格。此时,事件 ID 将显示在 A 列中。当您删除 Google 日历上的事件时,如果它与已删除的事件 ID 匹配,则会发送该电子邮件。 2. →与1. 含义相同。 3. →由于我没有收到“删除”通知,因此本文的目的是能够通知您这一点。

在这种情况下,为了检索已删除的事件列表,我想使用日历API。当使用Calendar API时,可以检索已删除的事件。当这反映在您的脚本中时,请按如下方式修改您的显示脚本。

在测试此修改之前,请在高级 Google 服务中启用日历 API

来自:

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++; // 通知されるイベントの数を増やす
    }
  }
}

致:

const savedEvents = sheet.getDataRange().getValues();
if (currentTime.getFullYear() - lastUpdated.getFullYear() <= 1) {
  const delEventObj = Calendar.Events.list(e.calendarId, { axResults: 2500, orderBy: "updated", updatedMin: lastUpdated.toISOString() }).items.reduce((o, { id, status }) => {
    if (status == "cancelled") {
      o[`${id}@google.com`] = true;
    }
    return o;
  }, {});
  const deletedEvents = savedEvents.filter(r => delEventObj[r[0]]);
  if (deletedEvents.length > 0) {
    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++; // 通知されるイベントの数を増やす
    }
  }
}
  • 通过此修改,当从 Google 日历中手动删除事件时,日历 API 会检索已删除的事件。并且,Google 电子表格中的值将按已删除的事件列表进行过滤。这是脚本中的
    deletedEvents
    。这样,当删除事件时,会发送一封电子邮件。

注:

  • 在此修改中,您的函数
    monitorMyCalendar
    已作为日历事件触发器安装。请注意这一点。

参考:

© www.soinside.com 2019 - 2024. All rights reserved.