実現したいこと
表題の件ですがGASを使用してGoogleカレンダーでイベントを作成、更新、削除した場合、
任意のメールアドレスに送信されるという仕組みです。
発生している問題・分からないこと
今回の場合、
以下の点で困っています。
・なぜかメール通知がなされない
・削除の場合の送信ができない
該当のソースコード
GAS
1function monitorMyCalendar(e) { // 関数名も変えました 2 if (e) { 3 try { 4 // 表示用の文字列 5 const isNotExist = 'が設定されていません'; 6 7 // 過去にイベントオブジェクトが複数発生(スクリプトが複数起動)したことへの対応 8 const lock = LockService.getScriptLock(); 9 lock.waitLock(0); 10 11 // プロパティサービスから前回実行日時を取得 12 const properties = PropertiesService.getScriptProperties(); 13 const lastUpdated = new Date(properties.getProperty('lastUpdated')); 14 const noticedDate = formatDate_(lastUpdated); 15 16 // スクリプトの実行日時を取得 17 const currentTime = new Date(); 18 const currentDate = formatDate_(currentTime); 19 20 // 実行日と2週間後及び6ヶ月後の日付を生成 21 const today = new Date(); 22 today.setHours(0, 0, 0, 0); // 時刻をクリア 23 const twoWeeksLater = new Date(today); 24 twoWeeksLater.setDate(twoWeeksLater.getDate() + 14); // 2週間後の日付 25 const sixMonthsLater = new Date(today); 26 sixMonthsLater.setMonth(today.getMonth() + 6); // 6ヶ月後の日付 27 28 // 更新されたカレンダーを6ヶ月後まで取得 29 const calendar = CalendarApp.getCalendarById(e.calendarId); 30 const events = calendar.getEvents(today, sixMonthsLater); // 6ヶ月後までの予定を取得 31 32 let noticeCount = 0; // 通知されるイベントの数をカウントする変数 33 const mailBodies = []; // 通知内容を蓄積する配列 34 const twoWeeksMap = new Map(); 35 36 // 追加・更新された予定を検出 37 for (const event of events) { 38 const eventUpdated = event.getLastUpdated(); 39 const isNewEvent = eventUpdated > lastUpdated || event.getStartTime() > lastUpdated; // 新しいイベントまたは更新されたイベントかどうかを判定 40 if (eventUpdated > twoWeeksLater) { 41 break; 42 } else if (isNewEvent) { 43 twoWeeksMap.set(event.getId(), eventUpdated); 44 // メール通知項目を生成 45 const eventDetails = { 46 title: event.getTitle() || 'タイトル' + isNotExist, 47 startTime: event.getStartTime() ? formatDate_(event.getStartTime()) : '開始日時' + isNotExist, 48 endTime: event.getEndTime() ? formatDate_(event.getEndTime()) : '終了日時' + isNotExist, 49 updateTime: formatDate_(eventUpdated), 50 description: event.getDescription() || '詳細' + isNotExist, 51 location: event.getLocation() || '場所' + isNotExist, 52 url: 'https://www.google.com/calendar/event?eid=' + Utilities.base64Encode(event.getId().split('@')[0] + ' ' + e.calendarId), // URL を直接指定 53 calendarId: e.calendarId, 54 calendarName: calendar.getName(), 55 }; 56 // メール本文を蓄積する 57 mailBodies.push(eventDetails); 58 noticeCount++; // 通知されるイベントの数を増やす 59 } 60 } 61 62 // 削除確認用の予定の控えをスプレッドシートから復元し、2週間分のみ抽出(シート名はカレンダーIDの最初の16文字) 63 const ss = SpreadsheetApp.openById("シートID"); 64 const sheet = ss.getSheetByName(e.calendarId.slice(0, 16)) ?? ss.insertSheet(e.calendarId.slice(0, 16)); 65 const savedEvents = sheet.getDataRange().getValues(); 66 const twoWeeksSavedEvents = savedEvents.filter(data => data[5] >= today && data[5] <= twoWeeksLater); 67 68 // 削除されたイベントIDのリストを格納するための配列 69 const deletedEventIds = []; 70 71 // 2週間分の保存されたイベントを反復処理 72 for (const data of twoWeeksSavedEvents) { 73 if (!twoWeeksMap.has(data[0])) { 74 // 削除されたイベントIDを検出し、deletedEventIds に追加 75 deletedEventIds.push(data[0]); 76 } 77 } 78 79 // deletedEventIds を元に削除されたイベントの詳細を取得し、メール通知の準備を行う 80 for (const eventId of deletedEventIds) { 81 // 削除されたイベントの詳細を取得 82 const deletedEventData = savedEvents.find(data => data[0] === eventId); 83 84 // 削除されたイベントの詳細が見つかった場合、メール通知項目を生成して mailBodies に追加 85 if (deletedEventData) { 86 const eventDetails = { 87 title: deletedEventData[3] || 'タイトル' + isNotExist, 88 startTime: deletedEventData[5] ? formatDate_(deletedEventData[5]) : '開始日時' + isNotExist, 89 endTime: deletedEventData[6] ? formatDate_(deletedEventData[6]) : '終了日時' + isNotExist, 90 updateTime: formatDate_(calendar.getEventById(eventId).getLastUpdated()), 91 description: deletedEventData[8] || '詳細' + isNotExist, 92 location: deletedEventData[7] || '場所' + isNotExist, 93 url: '', 94 calendarId: e.calendarId, 95 calendarName: calendar.getName(), 96 }; 97 // メール本文を蓄積する 98 mailBodies.push(eventDetails); 99 noticeCount++; // 通知されるイベントの数を増やす 100 } 101 } 102 103 if (mailBodies.length > 0) { 104 // 開始日時順に並び替え 105 mailBodies.sort((a, b) => new Date(a.startTime) - new Date(b.startTime)); 106 // メールを送信 107 sendEmailNotification_(mailBodies, { noticedDate, currentDate }, properties, lastUpdated); 108 // 最後の通知時刻を保存 109 properties.setProperty('lastUpdated', currentTime.toISOString()); 110 } 111 Logger.log('通知されるイベントの数: ' + noticeCount); 112 113 // 6ヶ月分の予定の控えを更新(保存) 114 if (events.length > 0) { 115 const values = events.map(event => [ 116 event.getId(), // イベントID[0] 117 calendar.getName(), // カレンダー名[1] 118 e.calendarId, // カレンダーID[2] 119 event.getTitle(), // タイトル[3] 120 event.getLastUpdated(), // 最終更新日時[4] 121 event.getStartTime(), // 開始日時[5] 122 event.getEndTime(), // 終了日時[6] 123 event.getLocation(), // 場所[7] 124 event.getDescription(), // 詳細[8] 125 ]); 126 sheet.clearContents(); 127 sheet.getRange(1, 1, values.length, values[0].length).setValues(values); 128 } 129 130 // スクリプトのロックを解放 131 Utilities.sleep(300); 132 lock.releaseLock(); 133 134 } catch (error) { 135 if (error.toString().includes('Lock timeout')) { 136 Logger.log('実行中のスクリプトが重複しているので処理を中断しました'); 137 } else { 138 Logger.log('予定の確認中に次のエラーが発生しました: ' + error); 139 } 140 } 141 } else { 142 console.log('エディタからは実行できません'); 143 } 144} 145 146// 日付を指定された形式に整形(日付が文字列の場合に対応) 147function formatDate_(date) { 148 return Utilities.formatDate(new Date(date), 'JST', 'yyyy/MM/dd HH:mm'); // 日本時間で表示 149} 150 151// 通知を送信 152function sendEmailNotification_(mailBodies, date, properties, lastUpdated, isNewEvent, isDeletedEvent) { 153 try { 154 const lastNotificationDate = new Date(properties.getProperty('lastNotificationDate')); 155 const currentTime = new Date(); 156 157 // 前回の通知日時と現在の日時を比較し、新しいイベントまたは削除されたイベントがある場合にメール通知を送信する 158 if (lastNotificationDate < lastUpdated && currentTime >= lastUpdated && (isNewEvent || isDeletedEvent)) { 159 const recipientEmail = 'test@gmail.com'; // 送信先のメールアドレスを設定してください 160 161 // 新しいイベントの情報を含む通知メール 162 if (isNewEvent) { 163 const subject = "Googleカレンダーに新しいイベントが追加されました"; 164 let body = '前回の通知(' + date.noticedDate + ')以降、Googleカレンダーに新しいイベントが追加されました。\n' + 165 '今回の通知(' + date.currentDate + ')対象のカレンダー名: ' + mailBodies[0].calendarName + '\n\n'; 166 167 for (const item of mailBodies) { 168 body += 169 '作成日時: ' + item.updateTime + '\n' + 170 'タイトル: ' + item.title + '\n' + 171 '開始日時: ' + item.startTime + '\n' + 172 '終了日時: ' + item.endTime + '\n' + 173 '場所: ' + item.location + '\n' + 174 '詳細: ' + item.description + '\n' + 175 'URL: ' + item.url + '\n\n'; 176 } 177 MailApp.sendEmail(recipientEmail, subject, body); 178 Logger.log('新しいイベントの通知メールが送信されました: ' + body); 179 } 180 181 // 削除されたイベントの情報を含む通知メール 182 if (isDeletedEvent) { 183 const subject = "Googleカレンダーのイベントが削除されました"; 184 let body = '前回の通知(' + date.noticedDate + ')以降、Googleカレンダーからイベントが削除されました。\n' + 185 '今回の通知(' + date.currentDate + ')対象のカレンダー名: ' + mailBodies[0].calendarName + '\n\n'; 186 187 for (const item of mailBodies) { 188 body += 189 '削除日時: ' + item.updateTime + '\n' + 190 'タイトル: ' + item.title + '\n' + 191 '開始日時: ' + item.startTime + '\n' + 192 '終了日時: ' + item.endTime + '\n' + 193 '場所: ' + item.location + '\n' + 194 '詳細: ' + item.description + '\n\n'; 195 } 196 MailApp.sendEmail(recipientEmail, subject, body); 197 Logger.log('削除されたイベントの通知メールが送信されました: ' + body); 198 } 199 200 // 最後の通知時刻を保存 201 properties.setProperty('lastNotificationDate', currentTime.toISOString()); 202 } else { 203 Logger.log('新しいイベントまたは削除されたイベントがないか、前回の通知以降に新しいイベントまたは削除されたイベントがありません。メールを送信しませんでした。'); 204 } 205 } catch (error) { 206 // メール送信中にエラーが発生した場合、エラーメッセージをログに出力する 207 Logger.log('通知メールの送信中にエラーが発生しました: ' + error); 208 } 209} 210 211
試したこと・調べたこと
- teratailやGoogle等で検索した
- ソースコードを自分なりに変更した
- 知人に聞いた
- その他
上記の詳細・結果
まずはトリガーの設定は以下に設定しています。
今回はいくつか同じ内容が来てしまうのでその場合は
removeDuplicatesFromSheet関数で削除を行っていますが
一度のメールがこのコードですと「0通」になってしまいます。
if (!lastEmailSent || (currentTime - lastEmailSent) >= emailInterval) {
上記の条件分岐が当てはまっていない気がします。
すみませんが皆様からのご教示のほどよろしくお願いいたします。
回答3件
あなたの回答
tips
プレビュー