Telegraft

Интеграция

Google Calendar и гонка, из-за которой возникает двойная запись

Интеграция с Google Calendar даёт Telegram-боту записи возможность читать реальную занятость и писать приёмы в те календари, которыми специалисты уже пользуются. Трудность не в API — она в промежутке между проверкой слота и его записью, и именно оттуда берётся двойная запись.

Google Calendar — интеграция: доступ, лимиты и доступность

Модель доступа
OAuth 2.0
Квота
На проект и на пользователя, запись расходует её сильнее чтения
Доступность в Заливе
Без региональных ограничений; для Workspace предпочтительно делегирование на домен
Поток данных
5 переходов, всё через воркер

As of 2025-10-01, Telegram Bot API 13.4

Зачем нужна эта интеграция

Почти любая клиника, салон и консалтинговая практика в Заливе уже живёт в Google Calendar. Персонал видит его в телефоне, он синхронизируется со всем, чем эти люди пользуются, и никого не нужно обучать. Система записи, которая ведёт собственный отдельный график рядом с ним, создаёт второй источник истины, и через несколько недель эти двое начинают расходиться в чём-то важном.

Поэтому правильная архитектура такая: Google Calendar остаётся главным, а бот только читает из него и пишет в него. Вручную заблокированный час соблюдается, потому что бот его прочитал. Созданный ботом приём появляется в телефоне специалиста, потому что бот записал его именно туда. Никто ничего не сверяет, потому что сверять нечего.

Дефект, который вылезает в небрежных реализациях, — это гонка. Бот показывает свободные слоты, клиент двадцать секунд выбирает, и за это окно то же время занимает кто-то другой — через бота или прямо в календаре. Повторная проверка в момент записи сужает окно, но не закрывает его, потому что проверка и вставка — две отдельные операции. Проектный ответ здесь один: временная бронь в собственном хранилище с коротким сроком жизни, взятая до того, как клиента попросят подтвердить, и сверенная с календарём в момент записи.

Как на самом деле движутся данные

Клиент в чатеTelegramзапрос слотовВоркер ботазапрос занятости freebusyКалендарьGoogleрезерв слотаБронь в D1создание событияКалендарьGoogle
Бронь ставится в D1 до того, как клиента просят подтвердить, и событие в календаре создаётся только после подтверждения. Без брони промежуток между предложением слота и его записью — это двойная запись, которая просто ждёт своего часа.

Доступ выдаётся по OAuth 2.0 с офлайн-режимом, так что бот держит refresh-токен на каждый подключённый календарь, а не живую сессию пользователя. Токены лежат в секретах воркера и никогда не попадают на клиент. Для клиента Google Workspace лучше устроен сервисный аккаунт с делегированием на весь домен: он переживает уход того сотрудника, который однажды дал согласие.

Модель доступа: OAuth 2.0

Их лимиты и что они означают для вас

Calendar API ограничивает частоту обращений на проект и на пользователя, причём запись расходует квоту заметно сильнее чтения.

Загруженная клиника с несколькими специалистами способна подойти к этим границам за утренний пик. Поэтому чтение занятости кэшируется на короткое время, а запись ставится в очередь с нарастающей паузой, а не отправляется с той скоростью, с какой поступает.

Каналы push-уведомлений об изменениях в календаре истекают и требуют продления.

Молча протухший канал означает, что бот перестал видеть ручные правки, и доступность потихоньку расходится с реальностью. Продление выполняется по расписанию, а неудачное продление поднимает оповещение.

Эндпоинт `freebusy` возвращает интервалы занятости, а не содержимое событий.

Для расчёта доступности это верный выбор: дешевле и не заставляет бота читать содержание приёмов, которое его не касается. Всё, чему действительно нужны детали события, оформляется отдельным, более узким запросом.

Повторяющиеся события разворачиваются в отдельные экземпляры, и исключение для одного экземпляра — это собственная запись.

Логика доступности работает с развёрнутыми экземплярами, а не с правилом повторения. Если считать еженедельный блок одним событием, получится бот, который предлагает слоты ровно на той неделе, которую отменили.

Как это ломается и что происходит потом

Двум клиентам с разницей в секунды предложен один и тот же слот.

Первый выбор ставит бронь, второму сразу сообщается об этом и предлагается ближайшее свободное время. Без брони оба считают, что слот их, и один узнаёт правду уже на пороге.

Refresh-токен отозван, потому что выдавший его сотрудник уволился или сменил пароль.

Запись начинает падать только для этого календаря. Сбой показывается персоналу, а не повторяется молча, и это главный аргумент в пользу сервисного аккаунта против согласия от конкретного человека.

Google возвращает ошибку превышения лимита посреди записи.

Запись фиксируется у нас, а обращение к календарю ставится в очередь с нарастающей паузой. Клиент получает подтверждение, потому что его приём существует; говорить ему, что всё сорвалось из-за чужой квоты, недопустимо.

Специалист удаляет созданное ботом событие прямо в Google Calendar.

Канал уведомлений сообщает об этом, запись помечается отменённой, а клиент получает оповещение. Считать бота единственным, кто пишет в календарь, — верный способ разойтись с реальностью в первую же неделю, когда кто-то возьмётся за привычный ему инструмент.

Доступность в ОАЭ и остальных странах Залива

Весь мир

Региональных ограничений нет. Подключить можно любой аккаунт Google или домен Workspace, и отчасти поэтому на этом рынке он и стоит по умолчанию.

Клиенты Google Workspace

Сервисный аккаунт с делегированием на весь домен здесь сильно предпочтительнее: он переживает смену персонала, а персональное согласие по OAuth — нет.

Личные аккаунты Gmail

Работают через персональный OAuth. Для одного специалиста нормально, для компании хрупко, потому что согласие принадлежит человеку, а не организации.

Требования к хранению данных внутри страны

Данные о приёмах лежат в инфраструктуре Google. Там, где регулятор требует хранить их локально, это решение принимается с юристами, а не настройкой в коде.

Когда эту интеграцию делать не нужно

  • Ваши специалисты на самом деле не ведут Google Calendar. Интеграция с календарём, в который никто не смотрит, даёт систему формально верную и бесполезную.
  • Длительность приёма определяется врачебным суждением. Календарь примет всё, что в него напишет бот, включая график, который выглядит заполненным и едет по времени.
  • Регулятор требует, чтобы данные о приёмах не покидали страну. Это вопрос к юристам, а не настройка интеграции.
  • Вам нужна полноценная система управления практикой. Здесь синхронизируется расписание — не выставление счетов, не медицинские карты и не клинические процессы.

На чём это работает

КомпонентВерсияЗачем
Cloudflare WorkerscurrentОбновление токенов OAuth, запросы занятости и отложенная запись.
Cloudflare D1currentВременные брони, карточки записей и состояние каналов уведомлений.
Cloudflare KVcurrentКороткоживущий кэш занятости, чтобы удержаться в квоте на утреннем пике.
Zod4.4Проверка ответов API и содержимого push-уведомлений на границе.

Вопросы, которые возникают при оценке

Откуда на самом деле берётся двойная запись?

Из промежутка между тем, как слот предложили, и тем, как его записали. Повторная проверка перед записью сужает этот промежуток, но закрыть его не может: проверка и вставка — две операции. Закрывает его короткоживущая временная бронь, взятая до того, как клиента попросили подтвердить.

Что выбрать: сервисный аккаунт или согласие каждого пользователя?

Если вы на Google Workspace — сервисный аккаунт с делегированием на весь домен. Персональное согласие принадлежит тому, кто его дал, и ломается вместе с его уходом, а уход обычно случается в самый неподходящий момент.

Читает ли бот содержимое уже существующих приёмов?

Нет. Доступность считается через эндпоинт freebusy, который отдаёт интервалы занятости без деталей события. Это дешевле, это избавляет бота от чтения сведений о пациентах и клиентах, которые ему незачем видеть, и это гораздо проще объяснить любому проверяющему.

Что происходит, когда персонал правит календарь напрямую?

Бот узнаёт об этом через канал push-уведомлений и обновляет собственное состояние, в том числе предупреждает клиента, чей приём удалили. Допущение, что в календарь пишет только бот, — это то, из-за чего две картины расходятся за неделю.

Что будет, если Google ограничит нас по частоте в утренний пик?

Запись фиксируется локально, а обращение к календарю ставится в очередь с нарастающей паузой, поэтому клиент всё равно получает подтверждение. Его приём существует, календарь догонит. Подводить клиента из-за чужой квоты — неправильный размен.

Как обрабатываются повторяющиеся блоки в расписании?

Доступность считается по развёрнутым экземплярам, а не по правилам повторения, потому что исключение для одного вхождения — это отдельная запись. Если обработать только правило, получится бот, который бодро предлагает ту самую неделю, которую отменили.

Что почитать дальше