Если вы уже ознакомились с предыдущими тремя статьями из данной серии, то вы уже умеете писать полноценных telegram ботов с клавиатурой.  В этой статье мы с вами научимся писать бота, который будет поддерживать последовательный диалог. Т.е. бот будет задавать вам вопросы, и ждать от вас ввода какой-либо информации. В зависимости от введённых вами данных бот будет выполнять некоторые действия. Также в данной статье мы научимся использовать под капотом бота базы данных, в нашем примере это будет SQLite, но вы можете использовать любую другую СУБД. Более подробно о взаимодействии с базами данных на языке R я писал в этой статье. Если вы интересуетесь анализом данных возможно вам будут интересны мои telegram и youtube каналы. Большая часть контента которых посвящены языку R. Для того, что бы бот мог запрашивать от вас данные, и ждать ввод какой-либо информации вам потребуется фиксировать текущее состояние диалога. Лучший способ это делать, использовать какую нибудь встраиваемую базу данных, например SQLite.  Т.е. логика будет следующей. Мы вызываем метод бота, и бот последовательно запрашивает у нас какую-то информацию, при этом на каждом шаге он ждёт ввод этой информации, и может осуществлять её проверку. Мы напишем максимально простого бота, сначала он будет спрашивать ваше имя, потом возраст, полученные данные будет сохранять в базу данных. При запросе возраста будет проверять, что бы введённые данные были числом, а не текстом.  Такой простой диалог будет иметь всего три состояния: В ходе статьи мы с вами шаг за шагом построим бота, весь процесс схематически можно изобразить следующим образом:
 Для удобства мы разобъём код нашего бота, и прочие связанные с ним файлы на следующую структуру. Весь проект бота можно посмотреть, или скачать из моего репозитория на GitHub. В качестве конфига мы будем использовать обычный ini файл, следующего вида: В конфиг мы записываем токен бота, и путь к базе данных, т.е. к файлу bot.db, сам файл мы будем создавать на следующем шаге. Для более сложных ботов можно создавать и более сложные конфиги, к тому же необязательно писать именно ini конфиг, можете использовать любой другой формат включая JSON. На каждом ПК папка с проектом бота может располагаться в разных директориях, и на разных дисках, поэтому в коде путь к папке проекта будет задан через переменную среды TG_BOT_PATH. Создать переменную среды можно несколькими способами, наиболее простой — прописать её в файле .Renviron. Создать, или редактировать данный файл можно с помощью команды file.edit(path.expand(file.path("~", ".Renviron"))). Выполните её и добавьте в файл одну строку: Далее сохраните файл .Renviron и перезапустите RStudio. Следующий шаг — создание базы данных. Нам понадобится 2 таблицы: Создать эти таблицы можно с помощью следующего SQL запроса: Если вы скачали проект бота с GitHub, то для создания базы можете воспользоваться следующим кодом на языке R.  У нас уже готов файл конфигурации и создана база данных. Теперь необходимо написать функции для чтения и записи данных в эту базу. Если вы скачали проект из GitHub, то функции вы можете найти в файле db_bot_function.R. Мы создали 4 простые функции: Все функции достаточно простые, они либо читают данные из базы с помощью команды dbGetQuery(), либо совершают UPSERT операцию (изменение существующих данных или запись новых данных в БД), с помощью функции dbExecute(). Синтаксис UPSERT операции выглядит следующим образом: Т.е. в наших таблицах поле chat_id имеет ограничение по уникальности и является первичным ключом таблиц. Изначально мы пробуем добавить информацию в таблицу, и получаем ошибку если данные по текущему чату уже присутствуют, в таком случае мы просто обновляем информацию по данному чату. Далее эти функции мы будем использовать в методах и фильтрах бота. Следующим шагом в построении нашего бота будет создание методов. Если вы скачали проект с GitHub, то все методы находятся в файле bot_methods.R. Мы создали 5 методов: Метод start запрашивает ваше имя, и переводит состояние чата в wait_name, т.е. в режим ожидания ввода вашего имени.  Далее, вы отправляете имя и оно обрабатывается методом enter_name, бот с вами здоровается, записывает полученное имя в базу, и переводит чат в состояние wait_age.  На этом этапе бот ждёт от вас ввода вашего возраста. Вы отправляете ваш возраст, бот проверяет сообщение, если вы вместо числа отправили какой-то текст он скажет: Ты ввёл некорректные данные, введи число, и будет ждать от вас повторного ввода данных. В случае если вы отправили число, бот сообщит о том, что он принял ваш возраст, запишет полученные данные в базу, сообщит все полученные от вас данные и переведёт состояние чата в исходное положение, т.е. в start. Вызвав метод state вы в любой момент можете запросить текущее состояние чата, а методом reset перевести чат в исходное состояние. В нашем случае это одна из наиболее важных частей в построении бота. Именно с помощью фильтров сообщений бот будет понимать какую информацию он от вас ждёт, и как её надо обрабатывать. В проекте на GitHub фильтры прописаны в файле message_filters.R. Код фильтров сообщений: В фильтрах мы используем написанную ранее функцию get_state(), для того, что бы запрашивать текущее состояние чата. Данна функция требует всего 1 аргумент, id чата.  Далее фильтр wait_name обрабатывает сообщения когда чат находится в состоянии wait_name, и соответственно фильтр wait_age обрабатывает сообщения когда чат находится в состоянии wait_age. Файл с обработчиками называется handlers.R, и имеет следующий код: Сначала мы создаём обработчики команд, которые позволят вам запускать методы для начала диалога, его сброса, и запроса текущего состояния. Далее мы создаём 2 обработчика сообщений с использованием созданных на прошлом шаге фильтров, и добавляем к ним фильтр !MessageFilters$command, для того, что бы мы в любом состоянии чата могли использовать команды. Теперь у нас всё готово к запуску, основной код запуска бота находится в файле bot.R. В результате, у нас получился вот такой бот:
 В любой момент с помощью команды /state мы можем запрашивать текущее состояние чата, а с помощью команды /reset переводить чат в исходное состояние и начинать диалог заново. В этой статье мы разобрались как использовать внутри бота базы данных, и как строить последовательные логические диалоги за счёт фиксации состояния чата.  В данном случае мы рассмотрели самый примитивный пример, для того, что бы вам проще было понять идею построения таких ботов, на практике вы можете строить гораздо более сложные диалоги. В следующей статье из этой серии мы научимся ограничивать пользователям бота права на использования различных его методов. Руководитель отдела аналитики в Netpeak