При обновлении календаря (раз в 13 минут) старая выгрузка сравнивается с новой по UID события.
Бот присылает одно сообщение со списком изменений: новое событие, удалено, перенесено (было/стало).
Учитываются и отдельные вхождения повторяющихся событий — перенос (RECURRENCE-ID) и удаление
(EXDATE). Прошедшие события пропускаются: Яндекс выгружает всю историю календаря. В сообщении —
не больше 10 изменений, остальные одной строкой «…и ещё N».
Команда /changes включает и выключает эти уведомления; по умолчанию включены у всех, колонка
alarms.changes добавляется при запуске. При подписке и замене ссылки сравнения нет.
Closes#2
code/sql.py на psycopg 3 с пулом соединений (переподключается сам); таблицы users, urls,
alarms — в PostgreSQL user и url зарезервированы. Внутренние номера пользователей сохраняются:
на них ссылаются файлы календарей data/icals/<номер>.ics. scripts/migrate_sqlite.py переносит
старые базы со сверкой (проверено на копии рабочих: 25 пользователей, 9 подписок).
Тесты хранилища и переноса — на PostgreSQL в CI; compose поднимает бота вместе с базой.