-
Notifications
You must be signed in to change notification settings - Fork 40
/
SyncCalendarsIntoOne.gs
149 lines (122 loc) · 4.67 KB
/
SyncCalendarsIntoOne.gs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
// Calendars to merge from.
// "[X]" is what is placed in front of your calendar event in the shared calendar.
// Use "" if you want none.
const CALENDARS_TO_MERGE = {
"[Personal]": "[email protected]",
"[Work]": "[email protected]",
}
// The ID of the shared calendar
const CALENDAR_TO_MERGE_INTO = "[email protected]"
// Number of days in the past and future to sync.
const SYNC_DAYS_IN_PAST = 7
const SYNC_DAYS_IN_FUTURE = 30
// Default title for events that don't have a title.
const DEFAULT_EVENT_TITLE = "Busy"
// Unique character to use in the title of the event to identify it as a clone.
// This is used to delete the old events.
// https://unicode-table.com/en/200B/
const SEARCH_CHARACTER = "\u200B"
// ----------------------------------------------------------------------------
// DO NOT TOUCH FROM HERE ON
// ----------------------------------------------------------------------------
// Base endpoint for the calendar API
const ENDPOINT_BASE = "https://www.googleapis.com/calendar/v3/calendars"
function SyncCalendarsIntoOne() {
// Start time is today at midnight - SYNC_DAYS_IN_PAST
const startTime = new Date()
startTime.setHours(0, 0, 0, 0)
startTime.setDate(startTime.getDate() - SYNC_DAYS_IN_PAST)
// End time is today at midnight + SYNC_DAYS_IN_FUTURE
const endTime = new Date()
endTime.setHours(0, 0, 0, 0)
endTime.setDate(endTime.getDate() + SYNC_DAYS_IN_FUTURE + 1)
// Delete any old events that have been already cloned over.
const deleteStartTime = new Date()
deleteStartTime.setFullYear(2000, 01, 01)
deleteStartTime.setHours(0, 0, 0, 0)
deleteEvents(deleteStartTime, endTime)
createEvents(startTime, endTime)
}
// Delete any old events that have been already cloned over.
// This is basically a sync w/o finding and updating. Just deleted and recreate.
function deleteEvents(startTime, endTime) {
const sharedCalendar = CalendarApp.getCalendarById(CALENDAR_TO_MERGE_INTO)
// Find events with the search character in the title.
// The `.filter` method is used since the getEvents method seems to return all events at the moment. It's a safety check.
const events = sharedCalendar
.getEvents(startTime, endTime, { search: SEARCH_CHARACTER })
.filter((event) => event.getTitle().includes(SEARCH_CHARACTER))
const requestBody = events.map((e, i) => ({
method: "DELETE",
endpoint: `${ENDPOINT_BASE}/${CALENDAR_TO_MERGE_INTO}/events/${e.getId().replace("@google.com", "")}`,
}))
if (requestBody && requestBody.length) {
const result = new BatchRequest({
useFetchAll: true,
batchPath: "batch/calendar/v3",
requests: requestBody,
})
if (result.length !== requestBody.length) {
console.log(result)
}
console.log(`${result.length} deleted events between ${startTime} and ${endTime}.`)
} else {
console.log("No events to delete.")
}
}
function createEvents(startTime, endTime) {
let requestBody = []
for (let calendarName in CALENDARS_TO_MERGE) {
const calendarId = CALENDARS_TO_MERGE[calendarName]
const calendarToCopy = CalendarApp.getCalendarById(calendarId)
if (!calendarToCopy) {
console.log("Calendar not found: '%s'.", calendarId)
continue
}
// Find events
const events = Calendar.Events.list(calendarId, {
timeMin: startTime.toISOString(),
timeMax: endTime.toISOString(),
singleEvents: true,
orderBy: "startTime",
})
// If nothing find, move to next calendar
if (!(events.items && events.items.length > 0)) {
continue
}
events.items.forEach((event) => {
// Don't copy "free" events.
if (event.transparency && event.transparency === "transparent") {
return
}
// If event.summary is undefined, empty, or null, set it to default title
if (!event.summary || event.summary === "") {
event.summary = DEFAULT_EVENT_TITLE
}
requestBody.push({
method: "POST",
endpoint: `${ENDPOINT_BASE}/${CALENDAR_TO_MERGE_INTO}/events?conferenceDataVersion=1`,
requestBody: {
summary: `${SEARCH_CHARACTER}${calendarName} ${event.summary}`,
location: event.location,
description: event.description,
start: event.start,
end: event.end,
conferenceData: event.conferenceData,
},
})
})
}
if (requestBody && requestBody.length) {
const result = new BatchRequest({
batchPath: "batch/calendar/v3",
requests: requestBody,
})
if (result.length !== requestBody.length) {
console.log(result)
}
console.log(`${result.length} events created between ${startTime} and ${endTime}.`)
} else {
console.log("No events to create.")
}
}