目前,当您在 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、标题、开始日期和时间、结束日期和时间、地点和详细信息。
此外,一个日历更新触发器会发生多个事件对象,因此 也有必要解决这个问题。 将所有约会保存在电子表格中,整理所有新旧约会等。 根据计划的数量,执行可能需要一些时间。
以上是目标,但我们的目标是即使消息被“删除”也能收到电子邮件通知,因此我们感谢您的合作。
从您的以下回复来看,
- →创建事件时添加到电子表格。此时,事件 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++; // 通知されるイベントの数を増やす
}
}
}
deletedEvents
。这样,当删除事件时,会发送一封电子邮件。monitorMyCalendar
已作为日历事件触发器安装。请注意这一点。