Skip to content

Instantly share code, notes, and snippets.

@MakoTano
Last active March 14, 2025 04:23
Show Gist options
  • Save MakoTano/e1cad3bdb1a84d5b72f87be697372c49 to your computer and use it in GitHub Desktop.
Save MakoTano/e1cad3bdb1a84d5b72f87be697372c49 to your computer and use it in GitHub Desktop.
どこかの組織に所属したときに、個人のGoogleカレンダーを所属した組織のカレンダーにコピーする
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