Уже почти 2 года я активно использую Notion в своей жизни. Я регулярно пополняю его своими личными автоматизациями, которые облегчают жизнь мне и моей команде. Иногда я публикую в своем блоге разные кейсы, которые удается оформить в виде библиотеки или примера кода. Данный пост также доступен в формате видео на моем канале.
Сегодня я решил поделиться тем, как я интегрировал календарь из Notion в Google Calendar через ics. Таким способом события из календаря Notion можно добавить в любой календарь, поддерживающий ics формат (то есть, практически, любой)
Немного предыстории
Обычно необходимость и суть любой интеграции и/или автоматизации проще понять, если понять зачем именно она была создана. Если хотите перейти, без предысторий, сразу к технике, листайте вниз до заголовка "Реализация".
Сейчас процесс общения с новыми запросами ко мне или моей компании происходит через моего Telegram бота или помощника. Но так оно работает только последний год…
Как оно работало раньше
Запрос может быть любой — могут начать предлагать какие-то “прорывные маркетинговые” услуги моей компании, обратиться ко мне за платными консультациями, предложить выступить на какой-нибудь конференции и тд.
Я старался вносить все в свой гугл-календарь. Ключевое слово — старался. Иногда меня даже хватало на целую неделю стабильных побед над моей ленью в этом вопросе. Поражение в этой схватке заканчивались накладками в созвонах и потерей репутации.
Мне достаточно быстро надоело обрабатывать подобное самому. В итоге начал приводить это в порядок. Среди наших клиентов есть серьезные люди, у которых есть помощники и все они, как один, ведут их календарь со всеми встречами и созвонами. Решил не придумывать велосипед и сделать тоже самое.
Причем я сразу решил идти не по пути предоставления доступа к гугл-календарю. Я хотел, чтобы я мог подвязывать свои задачи и заметки из моего Notion. Поэтому созвоны я решил хранить там же. Я создал базу в Notion с представлением (view) “календарь” и дал к нему доступ на внесение изменений.
В момент, когда приходит понимание, что необходимо устроить созвон создается (мной, ботом или помощником) сущность в Notion. Как велась моя база созвонов в мае 2022:
Указывается дата и время созвона;
В название, обычно, пишется имя фамилия человека и компании для идентификации в будущем;
В поле Info добавляется информация, необходимая для отображения внутри уведомления в Telegram боте. Обычно тут краткое описание о чем планируется разговор;
В тело уже размещается информация любого рода. Обычно помощник, после созвона, пишет расшифровки, договоренности и детали, которые могут быть полезны в будущем.
Желаемый функционал
Меня все устраивало кроме того, что для просмотра всех созвонов было необходимо идти в notion и смотреть что у меня по планам на следующий день. Приложение Notion на Android работает крайне медленно и я решил сделать так, чтобы все созвоны появлялись внутри моего гугл календаря, который я вел и веду по своим личным делам.
То есть хотелось, чтобы я/бот/помощник вносил информацию только в Notion и чтобы только Notion был единым источником информации для всех. Что еще более важно — люди достаточно переменчивы и, иногда, звонки переносятся еще до начала. Важно, чтобы можно было подвинуть карточку в Notion и все встало на свои места.
Реализация
Первое что мне пришло в голову для реализации функционала — отправлять события через Google Calendar Api. Я даже успел прочитать их Api Reference и примерно накидать идею как создавать события и отслеживать их синхронизацию. Следующим этапом были разборки с источником данных — я принялся смотреть что умеет Notion Api и был расстроен.
Сложности на стороне Notion
Работа с Notion происходит только через request-response подход. То есть нам, для получения информации и обновлений, нужно каждый раз забирать к себе всю информацию и уже, на своей стороне, понимать что поменялось, а что нет и реагировать на изменения.
Процесс актуализации пришлось бы делать следующим образом:
Нужно проходить по всем элементам базы созвонов и искать изменения;
Если добавили поле, то надо будет делать на N раз больше проверок (N – количество созвонов в базе);
Со временем база созвонов растет (1-2 созвона в день) = время обработки тоже ;
Достаточно тяжело поддерживать стабильную работу...
К великому сожалению, на момент написания поста, Notion совсем совсем не умеет в webhook. В идеале было бы подписаться на какие-то обновления и чтобы Notion оповещал наш сервер о каждом изменении на своей стороне.
Webhook — достаточно распостраненная практика в мире CRM и прочих сервисов, но Notion пока ее не освоил (на момент написания данного поста). Надеюсь, что скоро такой функционал порадует разработчиков. Ну а пока пришлось решить задачу чуть более лакончино
Найденное решение. Генерация ics на nodejs
В общем я посмотрел на этот предстоящий челлендж с API и оставил эту идею до лучших времен, которые наступили через пару месяцев. Я понял, что я могу экспортировать все элементы из Notion в ics, отдавать это через express и сразу же импортировать в Google Calendar как сторонний календарь.
Прежде всего необходимо получить notion token. Долго на этом останавливаться не буду т.к. описывал это в своем блоге. После получения токена нужно создать базу данных с Calendar View — можно взять мой шаблон. После создания базы фиксируем database_id из ссылки. Им заканчивается ссылка перед началом query_string (перед знаком вопроса).
Получение данных из Notion
Дальше приходим непосредственно к реализации. Внутри кода используются переменные notionToken и calendarDb — это как раз токен и id базы данных соответственно.
const notion = new Client({
auth: notionToken,
});
const Events = await notion.databases.query({
database_id: calendarDb,
});
Из notion приходит объект, содержащий все элементы из этой базы данных. Нам для заполнения гугл-календаря нужны следующие поля:
Event.properties['Name']['title'][0]?.['text']['content']
— название событияEvent.properties['Info']['rich_text'][0]?.['text']['content']
— краткое описание событияEvent.properties['Date']
— собственно дата события.
С полем даты события есть некоторые особенности:
Есть поля start и end.
Если не задать end то в нем будет null
Если поставить, в интерфейсе Notion, флажок “Include time” то формат даты изменится, но будет писаться в то же поле — надо смотреть на длину этого поля и тогда можно понять включено ли время.
Если у нас включено время, то end также может быть пустым и, следовательно, надо ко времени начала добавлять час если не указано время окончания.
Все эти кейсы ничего сложного из себя не представляют, но они познаются “в беде”. То есть сначала настроил систему только для работы с днями, запустил и оставил. Проходит время и случайно в одном месте ставишь дату со временем и все — система сломалась. Я все эти кейсы обработал. Получилось достаточно костыльно, но задачу поставленную решает.
Формирование ICS
После получения массива объектов из Notion их необходимо привести к формату, который можно конвертировать в ics. Само формирование ics строки реализовано в одноименном npm пакете.
Получить ics из массива событий можно следующим образом:
const icsEvent = ics.createEvents(Events)
if (icsEvent.error) {
console.error(icsEvent.error)
return
}
icsEvent.value // конечная ics строка
Массив Events
представляет собой массив из следующих элементов:
const Events = [
{
title: "Title",
description: "Короткое описание из info",
start: [2022,10,10,12,00],
end: [2022,10,10,13,00],
url: "ссылка на элемент в notion"
}
];
Дальше уже дело техники: передать эту строчку в express (или любой другой http) сервер и передать ссылку в Google Calendar
После чего Google Calendar сам зайдет по ссылке, выкачает все события и добавит их в мой календарь. Все работает более чем стабильно и события появляются в календаре не более чем через 10 минут после добавления в Notion.
Если я, вдруг, чего-то поменяю в одном событии, то, при следующей закачке, вся информация актуализируется и в календаре.
Чтобы не держать сервер под такую мелочь я разместил этот простой скрипт на pipedream. Можете брать к себе, в env переменных указывать свои токен и id базы данных и добавлять в календарь ссылку.
Также сделал небольшой npm пакет, где все "костылизации" с датами спрятал в одном методе createIcsString
, который на вход принимает токен и id базы данных (примеры оформил отдельно).
melnalex
Чем вы такие гифки делали? )
amorev Автор
Из powerpoint. Вот инструкция для моего помощника, которую я как раз сделал на примере одной из гифок с этой статьи — https://amorev.notion.site/GIF-78344ec0cd8743a08e31c46bbb6b11cf
Пользуйтесь на здоровье:)