Телеграм‑бот дёшево и сердито

Есть некоммерческий спортивный клуб, в котором хотелось бы отслеживать регулярность тренировок участников, осуждать за её отсутствие и составлять рейтинг спортсменов. Для исключительно беговых тренировок можно было бы взять одно из существующих приложений, но у нас есть и другие виды тренировок, а также разработчики с неуёмным энтузиамом.
Первым техническим решением были Google форма + Google таблица с App Scripts. Для любых операций обработки данных нужно было вычитать все строки листа с результатами формы. На определенном количестве строк расчет рейтинга стал тяжелой операцией, которая на слабых клиентских устройствах (а App Scripts выполняются в браузере/приложении таблиц при каждом открытии) выполнялась минутами и часто завершалась по таймауту не выдав результат.
Проблемой оказался и пользовательский опыт ввода тренировки: несколько раз в неделю раз открывать форму, искать себя в списке пользователей и заполнять информацию в многоступенчатой форме — сложное нудное действие, на которое участники клуба часто забивали.
Возможно какие‑то оптимизации работы с таблицей или подключение вместо формы собственного фронта могли бы продлить успешное существование таблицы, глубоко в это мы не погрузились, отказавшись от системы с рейтингом вцелом.
Спустя несколько лет захотелось попробовать сделать лучшую версию этой системы. Требования сформулированы такие:
Основное — надежное долгосрочное хранение данных, при этом бесперебойная работа ввода/получения данных и рейтинга — не критична.
Дополнительное — минимум хранения данных юзеров, при этом — пользователь должен вводить информацию только про себя и как можно проще.
Вытекающее из бесплатности всей затеи — поддержка сервиса должна занимать минимум времени и при необходимости легко передаваться другому человеку, денежные вложения конечно же тоже должны быть минимальными.
Выбор технологий
Главное требование, надежность хранения данных, вместе с тем что я изначально фронтендер, привело к тому что вариант развернуть собственную БД на сервере даже не рассматривался. Дешевенькие vps кажутся недостаточно надежными, особенно без настройки сервера и БД опытными руками.
Долго выбирала между тем чтобы остаться на Google таблицах или взять БД Яндекс Облака, захотелось все же больше гибкости и пощупать что‑то новое.
За что выбран Яндекс:
Надежное и в то же время бесплатное при малом использовании хранилище данных, простое в использовании. Бесплатно Яндекс предоставляет свою YDB — услуга называется «Managed Service for YDB»
Полностью бесплатный при маленькой нагрузке сервер сразу с публичным доменным именем, правда «некрасивым» (называется «технический домен» функции)
Удобный интерфейс для поддержки сервиса (БД, версионирование кода) — см. третье требование
Одна Yandex Function
Официальная документация по созданию телеграм бота на Яндекс Функции от Облака
Написание кода на Python было доверено нейронке. Использованные библиотеки — aiogram и ydb.
Получилась простая схема:

Схема не заработала. По логам функции я видела что запросы обрываются по таймауту. Оказалось что время, необходимое на холодный старт функции в облаке, больше чем телеграм готов ждать ответа на webhook.
Схема с двумя Yandex Functions
Логичное решение проблемы на тот момент — поставить перед основной функцией с aiogram и подключением к БД легковесную, которая бы отвечала телеграму 200 и запускала основную тяжелую.
Новая схема:

Схема выше тоже не заработала. Webhook телеграма доходил до функции (что я видела по логам) через два раза на третий. Бот при этом был скорее мёртв чем жив, присылая ответы на команды батчем спустя несколько часов. Это оcобенности работы телеграма — при недоступности сервера он делает отложенные перезапросы с большими интервалами.
Привязка функции к телеграму с помощью прокси
Вооружившись поисковиками и нейронками попробовала такие варианты решения проблемы:
Убрать функцию за Api Gateway от Яндекса. Это по сути прокси‑балансер внутри облака. Его использование рекомендуется в документации, как я поняла — исключительно ради гибкости в будущем, при масштабировании сервиса. Проблему с сетевой доступностью он не решил.
Использовать доменное имя. От старого проекта у меня осталось собственное оплаченное доменное имя в зоне .ru. Перенесла домен в Яндекс облако и привязала к функции (это делается через тот же Api Gateway). Оказалось что пользоваться доменом в яндексе так как мне нужно — платно. На тесты ушло рублей 5 и стоимость должна была вырасти при реальном пользовании, это в мои планы не входило. К тому же сам домен был продлен последний раз на год дорого, и подлевать его снова не планируется. Так что вариант был попробован «для очистки совести». К тому же домен не помог.
Наконец пришла к мысли что сервера яндекса попросту плохо доступны серверам телеграма на сетевом уровне, что сейчас не удивительно. Мысль оказалась верной, потому что использование vps в подходящей стране как прокси трафика к боту и от него решил проблему.
Тут не стану вдаваться в подробности, но в общем стоимость деплоя бота состоит у меня исключительно из стоимости vps, сейчас порядка 200 рублей в месяц.
Вот получившаяся схема с прокси (и Api Gateway):

Теперь запросы доходят до функции каждый раз! Ура, ботом можно пользоваться!
Код варианта с двумя функциями
Бот работал стабильно, но меня не устроила скорость ответов. Задержка прокси‑функции до отправки ответа — порядка 20 секунд. Все‑таки функции от Яндекса больше про редкие долгие вычисления, а не про обслуживание клиентских запросов.
При наличии nginx перед Яндекс Облаком вторую функцию (быстрого ответа) можно убрать, быстрый ответ 200 поручить nginx. Так как в итоге я отказалась от Яндекс Функций вовсе, не стала этого реализовывать в коде. Получилась бы такая упрощенная схема:

Возможно для каких‑то целей этот вариант подойдет.
Перенос бота на свой сервер
С помощью нейронки поправила код под обычный веб‑сервер, в качестве БД осталась та же YDB в Яндекс облаке (она доступна по сети). Итоговая схема:

Так как vps у меня очень дешевый и маленький, разворачиваю сервер прямо из исходников на Python с помощью systemd. Nginx используется для мгновенного ответа 200 телеграму.
Стандартно подключила ssl от Let's Encrypt с помощью Certbot.
Для закрытия всех лишних портов использовала утилиту UFW (Uncomplicated Firewall).
Заключение
Спасибо всем кто дочитал! Надеюсь чем‑то кому‑то будет полезно. Пишите комментарии
KioskNews shows a cleaned-up reading view extracted from the publisher’s page — the original always lives on their site, not ours.