Существует множество приложений календаря для синхронизации данных календаря. Стоит изучить, как использовать Rust для получения данных CalDav.
Задний план
В нашей повседневной работе мы можем использовать несколько календарей одновременно, один для групповой работы, один для самостоятельной работы, один для семейного TODO и т. д. Иногда трудно следовать расписанию. Итак, в моей компании мы создали небольшой скрипт на Python, чтобы получать все события и TODO, которые произойдут сегодня, и отправлять электронное письмо всем утром. Это действительно полезно, так как это напомнит нам о расписании, особенно нас заметят заранее, а затем организуют их более гибко.
Мы могли бы сделать это, потому что легко создать клиент CalDav для получения данных с сервера CalDav. Это полностью то же самое, что Thunderbird Lightning или другие приложения календаря, они просто подписываются на целевой календарь через URL-адрес календаря. Затем приложения будут автоматически получать новые данные или отправлять данные на сервер. Но в нашем случае нам нужно только получить данные с сервера, отфильтровать события, а затем отправить электронное письмо.
В Python модуль caldav
прост в использовании и получении событий из календаря. Всего несколько строк, мы могли бы получить сегодняшние события. Я не буду публиковать туториал для модуля caldav
. Пожалуйста, проверьте, если вы заинтересованы.
Но после изучения исходного кода модуля Python caldav
он использует модуль requests
для получения данных с сервера, который используется для получения/отправки данных с/на URL-адрес HTTP. Тогда почему бы нам не попробовать создать клиент CalDav через Rust? requests
в Python работает, тогда reqwest
в Rust будет работать.
Подготовка
Есть три вещи, которые необходимо подготовить перед началом получения данных.
- URL-адрес CalDav
- Имя пользователя для доступа
- Пароль для указанного выше имени пользователя
Запросы
CalDav основан на WebDav, который является расширением HTTP. Таким образом, в CalDav используются не только некоторые методы GET
, PUT
и DELETE
, но также реализованы некоторые другие расширенные методы, такие как PROPFIND
, REPORT
и т. д. Основные методы, которые мы продемонстрируем здесь, — это расширенные методы.
Ниже приведен пример кода для получения событий через модуль Python caldav
, и мы проделаем те же шаги в Rust.
client = caldav.DAVClient(url=url, username=username, password=password) my_principal = client.principal() calendars = my_principal.calendars() for cal in calendars: events = cal.events()
Запросить принципала
Нам нужны данные о событиях из нашего URL-адреса, но почему нам нужно здесь. На самом деле URL-адрес CalDav — это просто начальный URL-адрес, который поможет нам получить все URL-адреса календаря. Затем мы будем использовать URL-адреса календаря для получения событий из каждого из них. Причина, по которой у нас разные календари, заключается в том, что они используются совместно с текущей учетной записью. Блок-схема получения данных будет выглядеть следующим образом:
Для получения основного ответа основные вещи, которые мы изменим по сравнению с HTTP-запросом, это:
- Метод запроса, это будет
PROPFIND
- Тип контента запроса, это будет
application/xml
- Дополнительное значение заголовка запроса
Depth
со значением0
(Depth
сообщит серверу, насколько глубоко мы хотим рекурсивно получать данные). - Тело запроса, это будут данные в формате XML с полем запроса, которое называется тегами.
Ниже приведен демонстрационный код того, как выполнить вышеуказанный запрос. Я добавил комментарии для объяснения.
После запуска вышеуказанной функции сервер отправит ответ XML, который будет выглядеть следующим образом:
<d:multistatus xmlns:d=”DAV:” xmlns:s=”http://sabredav.org/ns" xmlns:cal=”urn:ietf:params:xml:ns:caldav” xmlns:cs=”http://calendarserver.org/ns/" xmlns:oc=”http://owncloud.org/ns"> <d:response> <d:href>/remote.php/dav/calendars/USERNAME/</d:href> <d:propstat> <d:prop> <d:current-user-principal> <d:href> /remote.php/dav/principals/users/USERNAME/ </d:href> </d:current-user-principal> </d:prop> <d:status>HTTP/1.1 200 OK</d:status> </d:propstat> </d:response> </d:multistatus>
Обратите внимание, что используемый URL-адрес и URL-адрес, возвращаемый сервером, могут отличаться от моего примера.
Получив ответ от сервера, нам проще получить главный URL. Я буду использовать крейт minidom
, потому что minidom
будет анализировать XML в древовидную структуру и будет прост в использовании. Я не буду здесь писать код для парсинга XML. Пожалуйста, не стесняйтесь читать minidom
doc или проверить ссылку на мой репозиторий, которая указана в конце статьи.
Запросы calendar-home-set
URL и Calendars URLs
используют тот же метод, что и principal
. Я не буду приводить здесь полный код, а покажу только тело запроса.
// request body to PROPFIND calendar-home-set static HOMESET_BODY: &str = r#” <d:propfind xmlns:d=”DAV:” xmlns:c=”urn:ietf:params:xml:ns:caldav” > <d:self/> <d:prop> <c:calendar-home-set /> </d:prop> </d:propfind> “#; // request body to PROPFIND calendars static CAL_BODY: &str = r#” <d:propfind xmlns:d=”DAV:” xmlns:c=”urn:ietf:params:xml:ns:caldav” > <d:prop> <d:displayname /> <d:resourcetype /> <c:supported-calendar-component-set /> </d:prop> </d:propfind> “#;
В ответе есть список календарей со свойствами запроса после получения данных из запроса календарей. Например, ниже приведена одна запись в моем ответном ответе:
<d:response> <d:href>/remote.php/dav/calendars/USERNAME/personal/</d:href> <d:propstat> <d:prop> <d:displayname>Personal</d:displayname> <d:resourcetype> <d:collection/> <cal:calendar/> </d:resourcetype> <cal:supported-calendar-component-set> <cal:comp name=”VEVENT”/> <cal:comp name=”VTODO”/> </cal:supported-calendar-component-set> </d:prop> <d:status>HTTP/1.1 200 OK</d:status> </d:propstat> </d:response>
Обратите внимание, что некоторые ответы могут быть пустыми, мы можем просто отфильтровать их при анализе.
Запросить события
Теперь мы нашли URL-адреса календаря в приведенном выше запросе календаря. Мы могли бы использовать его для получения наших событий. Прежде чем начать загрузку, проверьте cal:supported-calendar-component-set
. В нем перечислены два типа календаря, которые мы можем получить с сервера: VEVENT
и VTODO
. Это важно, поскольку говорит нам, какие данные мы можем получить.
Для получения событий с одного URL-адреса календаря у нас будут следующие изменения:
- Используйте метод
REPORT
- Используйте
1
в качестве значенияDepth
- Добавьте фильтр
VEVENT
в тело XML (мы будем получать толькоVEVENT
данных) - Добавить фильтр временного диапазона в тело XML (цель на определенный временной диапазон)
Ниже приведен демонстрационный код для получения событий из URL-адреса календаря.
После запуска кода вывод, как показано ниже. Все данные события отображаются в теге calendar-data
. В нем есть сводка, дата, статус и т. д.
<d:multistatus xmlns:d=”DAV:” xmlns:s=”http://sabredav.org/ns" xmlns:cal=”urn:ietf:params:xml:ns:caldav” xmlns:cs=”http://calendarserver.org/ns/" xmlns:oc=”http://owncloud.org/ns"> <d:response> <d:href>/remote.php/dav/calendars/USERNAME/personal/ownCloud-ICSIDSTRING.ics</d:href> <d:propstat> <d:prop> <d:getetag>"da86fa060d06bfdedf79ea7b6f050af4" </d:getetag> <cal:calendar-data> BEGIN:VCALENDAR PRODID:-//ownCloud calendar v1.6.3 VERSION:2.0 CALSCALE:GREGORIAN BEGIN:VEVENT CREATED:20200323T021525Z DTSTAMP:20200323T021836Z LAST-MODIFIED:20200323T021836Z UID:OG2XT87F6F6MWLXYVWD3N SUMMARY:test event CLASS:CONFIDENTIAL STATUS:CONFIRMED SEQUENCE:4 RRULE:FREQ=DAILY DTSTART;TZID=Asia/Hong_Kong:20200316T000000 DTEND;TZID=Asia/Hong_Kong:20200316T010000 END:VEVENT BEGIN:VTIMEZONE TZID:Asia/Hong_Kong BEGIN:STANDARD TZOFFSETFROM:+0800 TZOFFSETTO:+0800 TZNAME:HKT DTSTART:19700101T000000 END:STANDARD END:VTIMEZONE END:VCALENDAR </cal:calendar-data> </d:prop> <d:status>HTTP/1.1 200 OK</d:status> </d:propstat> </d:response> </d:multistatus>
Теперь мы успешно получили события из исходного URL-адреса CalDav. Если мы хотим отправить электронное письмо или SMS себе, мы можем добавить содержимое этого события в электронное письмо или сообщение.
Вывод
После приведенного выше объяснения становится ясно, что клиент CalDav полностью аналогичен внешнему приложению в мире HTTP. Еще 10 лет назад мы использовали формат XML (SOAP) для обмена данными между службами. XML на самом деле является одним из стандартных форматов данных. Итак, отличия заключаются в расширенных методах CalDav. Но способ использования полностью такой же, как и другие методы HTTP, просто убедитесь, что BODY является определенным форматом или тегами в CalDav. Для получения более подробной информации о CalDav, пожалуйста, прочитайте RFC4791 или Создание клиента calDav, в которых описаны подробности о клиенте CalDav.
Вы можете найти мой демонстрационный код CalDav в репозитории marshalshi/caldav-client-rust.