Last active
March 14, 2025 04:23
-
-
Save MakoTano/e1cad3bdb1a84d5b72f87be697372c49 to your computer and use it in GitHub Desktop.
どこかの組織に所属したときに、個人のGoogleカレンダーを所属した組織のカレンダーにコピーする
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const CALENDAR_SETTINGS = { | |
SYNC: { | |
TARGET_CALENDAR_ID: '[email protected]', // コピー先カレンダーID | |
SOURCE_CALENDAR_IDS: ['[email protected]'], // コピー元カレンダーIDリスト | |
EVENT_MARKER: { | |
PROPERTY_NAME: 'private', // イベントマーカーのプロパティ名 | |
PROPERTY_VALUE: 'copied', // イベントマーカーのプロパティ値 | |
}, | |
SYNC_PERIOD_DAYS: 30, // 同期期間 (日数) | |
EVENT_PROPERTIES: { | |
VISIBILITY: CalendarApp.Visibility.PRIVATE, // 公開設定 | |
COLOR: CalendarApp.EventColor.GRAY, // 色 | |
TITLE_PREFIX: 'Copied:', // タイトルプレフィックス | |
MASKED_TITLE: '予定あり', // マスクされたタイトル | |
SKIP_KEYWORDS: ['シークレット'], // スキップキーワード | |
SKIP_WEEK: ['Sat', 'Sun'], // スキップ曜日 | |
SKIP_AFTER_DATE: '2025-04-01', // この日付以降の予定はスキップ | |
}, | |
}, | |
API_THROTTLE_MS: 1000, // API制限 (ミリ秒) | |
}; | |
/** | |
* カレンダーイベント同期処理 | |
*/ | |
function synchronizeCalendarEvents() { | |
const targetCalendar = CalendarApp.getCalendarById( | |
CALENDAR_SETTINGS.SYNC.TARGET_CALENDAR_ID | |
); | |
if (!targetCalendar) { | |
console.error('コピー先カレンダーが見つかりません。'); | |
return; | |
} | |
const { currentDateStart, syncEndDate } = getSyncDateRange(); | |
removeSyncedEventsInRange(targetCalendar, currentDateStart, syncEndDate); | |
CALENDAR_SETTINGS.SYNC.SOURCE_CALENDAR_IDS.forEach((sourceCalendarId) => { | |
const sourceCalendar = CalendarApp.getCalendarById(sourceCalendarId); | |
if (!sourceCalendar) { | |
console.error(`コピー元カレンダーが見つかりません: ${sourceCalendarId}`); | |
return; | |
} | |
const sourceEvents = sourceCalendar.getEvents( | |
currentDateStart, | |
syncEndDate | |
); | |
sourceEvents.forEach((sourceEvent) => { | |
syncEvent(sourceEvent, targetCalendar); | |
}); | |
}); | |
} | |
/** | |
* 同期日付範囲取得 | |
* @returns {object} { currentDateStart, syncEndDate } | |
*/ | |
function getSyncDateRange() { | |
const now = new Date(); | |
const currentDateStart = new Date( | |
now.getFullYear(), | |
now.getMonth(), | |
now.getDate() | |
); | |
const syncEndDate = new Date( | |
currentDateStart.getTime() + | |
CALENDAR_SETTINGS.SYNC.SYNC_PERIOD_DAYS * 86400000 | |
); // 86400000ミリ秒 = 1日 | |
return { currentDateStart, syncEndDate }; | |
} | |
/** | |
* 指定期間内の同期済みイベント削除 | |
* @param {CalendarApp.Calendar} calendar 削除対象カレンダー | |
* @param {Date} startDate 削除期間開始日 | |
* @param {Date} endDate 削除期間終了日 | |
*/ | |
function removeSyncedEventsInRange(calendar, startDate, endDate) { | |
const { PROPERTY_NAME, PROPERTY_VALUE } = CALENDAR_SETTINGS.SYNC.EVENT_MARKER; | |
const events = calendar.getEvents(startDate, endDate); | |
events.forEach((event) => { | |
if (event.getTag(PROPERTY_NAME) === PROPERTY_VALUE) { | |
event.deleteEvent(); | |
Utilities.sleep(CALENDAR_SETTINGS.API_THROTTLE_MS); | |
} | |
}); | |
} | |
/** | |
* イベント同期 | |
* @param {CalendarApp.Event} sourceEvent コピー元イベント | |
* @param {CalendarApp.Calendar} targetCalendar コピー先カレンダー | |
*/ | |
function syncEvent(sourceEvent, targetCalendar) { | |
if (shouldSkipEvent(sourceEvent)) { | |
return; | |
} | |
const newEvent = copyEvent(sourceEvent, targetCalendar); | |
applyEventSettings(newEvent); | |
Utilities.sleep(CALENDAR_SETTINGS.API_THROTTLE_MS); | |
} | |
/** | |
* スキップ対象イベント判定 | |
* @param {CalendarApp.Event} event 判定対象イベント | |
* @returns {boolean} スキップ対象の場合はtrue | |
*/ | |
function shouldSkipEvent(event) { | |
// タイトルに指定文字が入っていたらスキップする - SKIP_KEYWORDS | |
const title = event.getTitle(); | |
let keywordSkip = false; | |
if (CALENDAR_SETTINGS.SYNC.EVENT_PROPERTIES.SKIP_KEYWORDS.length > 0){ | |
keywordSkip = | |
CALENDAR_SETTINGS.SYNC.EVENT_PROPERTIES.SKIP_KEYWORDS.some((keyword) => | |
title.includes(keyword) | |
); | |
} | |
// 特定の曜日の予定もスキップする - SKIP_WEEK | |
let weekSkip = false | |
if(CALENDAR_SETTINGS.SYNC.EVENT_PROPERTIES.SKIP_WEEK.length > 0){ | |
weekSkip = | |
CALENDAR_SETTINGS.SYNC.EVENT_PROPERTIES.SKIP_WEEK.includes( | |
Utilities.formatDate(event.getStartTime(), Session.getScriptTimeZone(), 'E') | |
); | |
} | |
// 指定日以降の予定はスキップする - SKIP_AFTER_DATE | |
let afterSkip = false; | |
if (!CALENDAR_SETTINGS.SYNC.EVENT_PROPERTIES.SKIP_AFTER_DATE){ | |
afterSkip = event.getStartTime() >= new Date(CALENDAR_SETTINGS.SYNC.EVENT_PROPERTIES.SKIP_AFTER_DATE); | |
} | |
return keywordSkip || weekSkip || afterSkip; | |
} | |
/** | |
* イベントコピー | |
* @param {CalendarApp.Event} sourceEvent コピー元イベント | |
* @param {CalendarApp.Calendar} targetCalendar コピー先カレンダー | |
* @returns {CalendarApp.Event} コピー先イベント | |
*/ | |
function copyEvent(sourceEvent, targetCalendar) { | |
const title = `${CALENDAR_SETTINGS.SYNC.EVENT_PROPERTIES.TITLE_PREFIX}${CALENDAR_SETTINGS.SYNC.EVENT_PROPERTIES.MASKED_TITLE}`; | |
const startTime = sourceEvent.getStartTime(); | |
const endTime = sourceEvent.getEndTime(); | |
if (sourceEvent.isAllDayEvent()) { | |
return targetCalendar.createAllDayEvent( | |
title, | |
sourceEvent.getAllDayStartDate(), | |
sourceEvent.getAllDayEndDate() | |
); | |
} else { | |
return targetCalendar.createEvent(title, startTime, endTime); | |
} | |
} | |
/** | |
* イベント属性設定 | |
* @param {CalendarApp.Event} event 設定対象イベント | |
*/ | |
function applyEventSettings(event) { | |
const { PROPERTY_NAME, PROPERTY_VALUE } = CALENDAR_SETTINGS.SYNC.EVENT_MARKER; | |
const { VISIBILITY, COLOR } = CALENDAR_SETTINGS.SYNC.EVENT_PROPERTIES; | |
event.setTag(PROPERTY_NAME, PROPERTY_VALUE); | |
event.setVisibility(VISIBILITY); | |
event.setColor(COLOR); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment