Last active
March 27, 2024 10:44
-
-
Save Gvozd/84f9c5ee011fc1344f21ac16db3c58b6 to your computer and use it in GitHub Desktop.
Birthday Notifications in google calendar
This file contains 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
function createTrigger() { | |
ScriptApp.newTrigger('main') | |
.timeBased() | |
.everyDays(1) | |
.create(); | |
} | |
function main() { | |
const {tmp, from, to} = getCalendars(); | |
const syncedEvents = getEvents(to) | |
.map(function(evt) { | |
return { | |
event: evt, | |
eventSeries: run(() => evt.getEventSeries()), | |
fromId: run(() => evt.getDescription()) | |
}; | |
}); | |
getEvents(from).forEach(function(fromEvent) { | |
createEvent(tmp, to, fromEvent, syncedEvents); | |
}); | |
syncedEvents | |
.filter(function({synced}) {return !synced;}) | |
.forEach(function({eventSeries}) { | |
Logger.log('Delete "%s"', run(() => eventSeries.getTitle())); | |
run(() => eventSeries.deleteEventSeries()); | |
}); | |
tmp.deleteCalendar(); | |
deleteTempCals(); | |
} | |
function getEvents(calendar) { | |
const currentYear = new Date().getFullYear(); | |
const fromDate = new Date(currentYear, 0, 1, 1); | |
const toDate = new Date(currentYear + 1, 0, 1, -1); | |
return run(() => calendar.getEvents(fromDate, toDate)); | |
} | |
function createEvent(tmp, cal, evt, syncedEvents) { | |
const evtId = run(() => evt.getEventSeries().getId()) | |
.replace(/^\d{4}_/, '2022_'); | |
const evtTitle = run(() => evt.getTitle()); | |
const evtStartTime = run(() => evt.getAllDayStartDate()); | |
const eventData = syncedEvents.find(function({fromId}) { | |
return fromId === evtId; | |
}) || {}; | |
let {eventSeries, event} = eventData; | |
if( | |
!eventSeries || | |
run(() => eventSeries.getTitle()) !== evtTitle || | |
run(() => event.getStartTime().getTime()) !== run(() => evtStartTime.getTime()) | |
) { | |
Logger.log('%sCreate "%s" %s', eventSeries ? 'Re-' : '', evtTitle, evtStartTime); | |
eventSeries = run(() => tmp.createAllDayEventSeries(evtTitle, evtStartTime, CalendarApp.newRecurrence().addYearlyRule(), { | |
description: evtId | |
})); | |
run( | |
() => eventSeries.setGuestsCanInviteOthers(false) | |
.setGuestsCanModify(false) | |
.setGuestsCanSeeGuests(false) | |
); | |
run(() => | |
Calendar.Events.move(tmp.getId(), eventSeries.getId().split('@')[0], cal.getId()) | |
); | |
} else { | |
Logger.log('Up to date "%s"', evtTitle); | |
eventData.synced = true; | |
} | |
} | |
function getCalendars() { | |
const fromCalendarId = 'addressbook#contacts@group.v.calendar.google.com'; | |
const toCalendarName = 'Birthday Notifications'; | |
const toCalendarIdPropKey = 'toCalendarId'; | |
const scriptProperties = run(() => PropertiesService.getScriptProperties()); | |
Logger.log('scriptProperties: %s', run(() => scriptProperties.getProperties())); | |
let fromCalendar = run(() => CalendarApp.getCalendarById(fromCalendarId)); | |
if (!fromCalendar) { | |
Logger.log('Exported calendar not founded'); | |
return; | |
} | |
let toCalendar = run(() => CalendarApp.getCalendarById(scriptProperties.getProperty(toCalendarIdPropKey))); | |
// Этот календарь нужен дял того чтобы подхватывались дефолтовые напоминания из конечного календаря | |
// у Calendar API куча проблем с заданием напоминаний для all-day событий, особенно в дату самого события(а не до) | |
// Эти события при попытке resetRemindersToDefault() получают напоминания от обычных событий, а не полнодневных. | |
// И в итоге не в календаре не удается задать полнодневное напоминание, и уж тем более в день самого ДР. Только обычные напоминания, и только заранее | |
// Но я обнаружил ХАК. Если созданное через API полнодневное событие передвинуть в другой календарь - оно подхватит его дефолтовые полнодневные напоминания | |
// К сожалению при возвращении в исходный календарь - все равно возвращается итоговая бага | |
// Поэтому создаем событие во временном календаре, а потом двигаем в основной. Все буде ОК | |
// При изменении напоминаний в конечном календаре - они подхвататся для всех событий, и это то, что нужно! | |
let tmpCalendar = run(() => CalendarApp.createCalendar('Temp calendar').setHidden(true)); | |
if (!toCalendar) { | |
Logger.log('Import calendar not founded - create it'); | |
toCalendar = run(() => CalendarApp.createCalendar(toCalendarName)); | |
run(() => scriptProperties.setProperty(toCalendarIdPropKey, toCalendar.getId())); | |
} | |
return {tmp: tmpCalendar, from: fromCalendar, to: toCalendar}; | |
} | |
function run(func) { | |
const timeouts = [10, 50, 100, 250, 500, 1000, 2000]; | |
let error; | |
for(const timeout of timeouts) { | |
let start, end; | |
try { | |
start = Date.now(); | |
return func(); | |
} catch(e) { | |
error = e; | |
} finally { | |
end = Date.now(); | |
Utilities.sleep( | |
Math.max(0, timeout - (end - start)) | |
); | |
} | |
} | |
throw error; | |
} | |
// @author Winand | |
function deleteTempCals() { | |
// Удаление календарей Temp calendar | |
// CalendarApp.getAllOwnedCalendars() не возвращает скрытые календари, | |
// поэтому используется Calendar.CalendarList https://stackoverflow.com/a/32384340 | |
var cals = Calendar.CalendarList.list( | |
{showHidden:true, minAccessRole:'owner', fields:'items(id,summary)'} | |
).items; | |
for(const i of cals) { | |
if(i.summary == 'Temp calendar') { | |
Logger.log('del cal ' + i.id); | |
// Calendar.Calendars.remove(i.id); | |
CalendarApp.getOwnedCalendarById(i.id).deleteCalendar(); | |
} | |
} | |
} |
Недавно Гугл стал сам предлагать включать уведомления о днях рождения. Но, как я понимаю, индивидуально для каждого человека? Это не особо удобно
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@armen1313 такая ошибка может быть, если не сделать эту часть инструкции
https://gist.github.com/Gvozd/84f9c5ee011fc1344f21ac16db3c58b6?permalink_comment_id=3747641#gistcomment-3747641