Google forms отличный инструмент для решения простых организационных задач, например, сбор информации для регистрации людей на небольшое мероприятие. Но что если мы попытаемся решить эту задачу по другому? Например через telegram.  Одна из отличительных особенностей telegram - это, на мой взгляд, простое и удобное bot api. К тому же формат диалога с ботом как нельзя лучше подходит для сбора данных.  Итак, что за задачу мы будем решать? У нас есть ежегодное мероприятие: танцевальный выезд на день рождение студии танцев. Необходимо собрать следующую информацию от участников для организации расселения и планирования разных шумных активностей: Номер телефона для связи ФИО Адрес электронной почты Пол (пригодится для расселения участников в большие номера) Категория номера (2-х местный, 3-местный и тд) Исповедуемое танцевальное направление  Соседи Для того чтобы не сломать существующий процесс регистрации заполненную заявку мы будем выгружать в гугл таблицы Сразу оговорюсь, что предложенную задачу даже с использованием ботов можно решать различными способами, тут я расскажу про свой вариант, который был использован некоторое время назад и отлично зарегистрировал всех желающих.  Полностью рабочий бот лежит вот тут. Вы можете использовать его как угодно в своих целях, буду рад если решение кому-то принесёт пользу.  О том как зарегистрировать бота, советую обратится к документации telegram - это очень простой и быстрый процесс.  А для сохранения данных в гугл таблицу необходимо будет создать сервисный аккаунт в cloud google platform и предоставить ему доступ к нужной таблице.   А наше приложение будет написано на kotlin + spring-boot + postgresql. Для хранения данных, как уже упоминалось выше, мы будем использовать самую обычную postgresql со следующей схемой: Пробежимся по полям таблицы: Id - уникальный идентификатор запроса в рамках таблицы user_id - идентификатор пользователя в telegram. Эта информация прилетает к нам в каждом из запросов.  telegram_login - «человекочитаемый» идентификатор пользователя в telegram. Вы можете сами назначить его себе в профиле приложения, а можете и не назначать. Но если он есть скрыть его не получится и он будет прилетать к нам в каждом запросе. И именно по такому логину мы можем написать конкретному человеку, но уже со своего аккаунта. Бот инициировать общение с произвольным незнакомым человеком не может.  full_name - фио участника.  email - адрес электронной почты.  phone -  телефон gender - пол участника room_type - категория номера. Тут будет номер категории из конфигурации.  dance_type - танцевальное направление  neighbors - список соседи в свободной форме.  state - статус заявки. На статусной модели остановимся подробнее чуть позже.  insstmp - время создания записи.  updstmp - время последнего обновления.  Диалог с ботом в нашем приложении будет строится в формате «вопрос - ответ», что означает при получении ответа от пользователя боту необходимо восстановить контекст предыдущего общения, для этого нам нужна будет определенная статусная модель.  В нашей задаче будут следующие статусы: Начнём по порядку. Первый контакт участника с ботом случается в тот момент, когда новый человек отправляет команду /start нашему боту на что бот отвечает приветственным сообщением и рассказываем что тут вообще происходит.  Тут мы имеем возможность сохранить публично доступную информацию о пользователе(в нашем случае tg_user_id и telegram_login) в свою бд, как минимум для статистики конверсии.  Статусы с PHONE_STATE по NEIGHBORS_STATE покрывают заполнение соответствующих шагов начиная с номера телефона до выбора соседей по комнате.  Промежуточный статус REQUEST_READY будет полезен нам для того чтобы показать пользователю его ответы все вместе и дать возможность принять решение отправить заявку либо удалить черновик и заполнить все заново. Кстати возможность удалить черновик на мой взгляд будет полезна на любом из промежуточных вопросов просто потому что удаление черновика и заполнение заново реализовать будет гораздо проще чем редактирование.  После того как пользователь окончательно одобрил заполненную заявку в нашей модели есть ещё один статус REQUEST_APPROVED, который будет полезен если случится какая-нибудь нескладуха при выгрузке данных в гугл таблицу и позволит нам в ручном или автоматическом режиме довыгрузить застрявшие заявки.  И окончательный статус EXPORTED проставляется в тот момент когда заявка улетела в гугл таблицу.  Последние два статуса являются техническими, т.е. пользователь на них повлиять никак не может и мы можем дать ему возможность заполнить ещё одну заявку за кого-нибудь ещё.  Модель черновика реализуем следующим образом: будем сбрасывать статус заполняемой заявки до PHONE_STATE.  Какая же архитектура кода нас ждёт? Тут для меня был интересный челенж. Хотелось выстроить структуру кода приложения в простом и понятном виде, которую легко можно будет расширять и тестировать. Ну в общем ничего нового, простое программистское счастье.  Структуру «вопрос - ответ» я взял за базовый интерфейс:  Как можно понять из названий ask() - метод в котором мы задаём пользователю интересующий нас вопрос handle() - метод для обработки ответа Ненадолго остановимся на параметрах методов: userId - идентификатор пользователя в telegram уже упоминаемый ранее chatId - ещё один идентификатор от telegram, но уже для конкретного чата с пользователем  absSender - это абстракция из библиотеки обертки над telegram bot api, позволяющая отправлять пользователю сообщения и многое другое. В данном приложении я использую вот эту библиотеку:  https://github.com/rubenlagus/TelegramBots update - также абстракция из библиотеки-обертки, содержащая информацию с ответом пользователя, а также всю необходимую мета-информацию о самом пользователе, а именно userId и chatId.  Таким образом на каждый из шагов статусной модели нам необходимо создать отдельный класс имплементирующий интерфейс State.  Для примера давайте рассмотрим заполнение номера телефона.  И опять начнём с параметров конструктора: MessageService - класс который вернёт нам сообщение на русском или английском языке в зависимости от локали установленной в конфигурации BotRepository - тонкая обертка над спринговым NamedJdbcTemplate поговорим подробнее о нем чуть позже State - следующий шаг, который будет выполнен после успешного прохождения текущего Перед тем как задать пользователю вопрос мы устанавливаем соответствующий статус для заявки пользователя и только после этого отправляем сообщение с вопросом.  После того как мы получаем ответ от пользователя и перед тем как сохранить его в базу мы делаем минимальную проверку того, что мы получили в ответе. После успешной валидации сохраняем ответ и переходим на следующий шаг. Если же мы получили в ответе что-то неожиданное, мы можем попросить пользователя подкорректировать свой ответ.  Тут у читателя может возникнуть справедливый вопрос: а как же мы понимаем какой именно обработчик нужно вызвать для конкретного ответа? Для этого взглянем на конфигурацию. Полностью шаги выполнения сконфигурированны в классе FlowConfiguration. А вот фрагмент конкретно для нашего шага с номером телефона: А вот фабрика, которая позволяет по userId получить нужный нам бин для обработки  Ну а как сама фабрика контактирует с библиотекой-оберткой (уже спринговой) можно наблюдать в классе RegistrationBot По задумке приложение должно получиться достаточно легковесным для того чтобы запускать его на разных площадках (heroku, aws, google cloud) без выделение значительных ресурсов. Но при этом приложение основано на spring-boot. Поэтому нужно не затаскивать ничего лишнего. И в данном варианте я решил использовать тонкую самописную обертку над спринговым NamedJdbcTemplate, чтобы отказаться от толстых абстракций при работе с базой. Вот она: Для взаимодействия с базой у нас есть два метода: execute() - для модификации данных query() - для получения данных Каждый из методов в качестве параметров принимает тип содержащий информацию о запросе.  Это позволяет нам собрать в конкретном классе все что может относить к какому-то запросу Вот как выглядит запрос на установку статуса из примера выше Вызов: На мой взгляд у ботов есть значительное преимущество по сравнению с гугл формами в части возможной автоматизации.  Например, в будущем мы можем добавить фичу оплаты как отдельный шаг регистрации или возможность коммуницировать с участниками по разным организационным вопросам уже после завершения регистрации.  Также новые горизонты открываются при работе с постоянными участниками, которые могут регистрироваться на другие мероприятие без однотипного заполнения данных и «в один клик».  И как можно заметить разработка бота требует заметно меньше ресурсов по сравнению например с мобильным приложением.  Ссылка на исходники Enjoy!