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(); | |
} | |
} | |
} |
Добрый день.
Ситуация абсолютно аналогичная @armen1313, только последней ошибки нет, скрипт запускается. Думал я один такой с сумасшедшим количеством записей в книжке.
Перенести удаление в начало хорошая идея. Я же сделал отдельный триггер для удаления.
Выручайте.
@armen1313 такая ошибка может быть, если не сделать эту часть инструкции
https://gist.github.com/Gvozd/84f9c5ee011fc1344f21ac16db3c58b6?permalink_comment_id=3747641#gistcomment-3747641
Недавно Гугл стал сам предлагать включать уведомления о днях рождения. Но, как я понимаю, индивидуально для каждого человека? Это не особо удобно
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
С праздниками!
Попробовал новый скрипт.
Первый запуск не избавил от дубликатов и не удаленных временных календарей, остановился так же по таймауту 360сек.
Запустил функцию удаление временных календарей. Пришлось несколько раз запускать... :) так их много у меня оказалось.
Удалил основной календарь, т.к. там было по 20+ дубликатов.
Запустил main после создания основного календаря остановил скрипт и поменял часовой пояс на свой.
Запустил main повторно он остановился отработав январь и не удалив за собой временный календарь и осталась пара дубликатов начала января, видимо те, что до смены часового пояса затясались. Добавил строчку с функцией от @Winand с удалением временных календарей в начало main'a, чтоб сначала удалялись временные календари, а только потом создавались новые.
Повторные запуски отрабатывали с каждым разом всё меньше, но не создавали дубликатов в основном и не плодились временные календари. Сейчас остановился на ноябре и дальше не продвигается т.к. почти всё время процесса занимает scriptProperties
21:17:02 Примечание Выполнение начато
21:17:03 Информация del cal l7sk8ndlххххххх069b404@group.calendar.google.com
21:17:06 Информация scriptProperties: {toCalendarId=cgtххххххххххххххххххххххх00@group.calendar.google.com}
21:22:03 Информация Up to date "Алексей – день рождения"
21:22:03 Информация Up to date " – Антон "
......
21:23:00 Информация Up to date "Даниил – день рождения"
21:23:00 Информация Create "Екатерина – день рождения" Tue Nov 14 00:00:00 GMT+03:00 2023
21:23:02 Ошибка
Exceeded maximum execution time
Всегда заканчивается на Екатерине и она не добавляется по итогу.
upd:
Скрипт перестал вообще отрабатывать вот что пишет:
00:46:05 Примечание Выполнение начато
00:46:06 Информация scriptProperties: {toCalendarId=cgххххххххххх0@group.calendar.google.com}
00:46:06 Информация Exported calendar not founded
00:46:07 Ошибка TypeError: Cannot destructure property 'tmp' of 'getCalendars(...)' as it is undefined.
main @ Notifications.gs:10
У меня 10-ая строчка это:
8 function main() {
9 deleteTempCals();
10 const {tmp, from, to} = getCalendars();
11 const syncedEvents = getEvents(to)