Писане на бот за телеграма в R (част 4): Изграждане на последователен, логичен диалог с бота

Ако вече сте прочели предишния три статии от тази поредица, тогава вече знаете как да пишете пълноценни ботове за телеграми с клавиатура.

В тази статия ще научим как да напишем бот, който ще поддържа последователен диалог. Тези. Ботът ще ви задава въпроси и ще чака да въведете информация. В зависимост от данните, които въвеждате, ботът ще извърши някои действия.

Също така в тази статия ще научим как да използваме база данни под капака на бота, в нашия пример това ще бъде SQLite, но можете да използвате всяка друга СУБД. Писах по-подробно за взаимодействието с бази данни на езика R в тази статия.

Писане на бот за телеграма в R (част 4): Изграждане на последователен, логичен диалог с бота

Всички статии от поредицата “Писане на телеграм бот в R”

  1. Ние създаваме бот и го използваме за изпращане на съобщения в telegram
  2. Добавете поддръжка на команди и филтри за съобщения към бота
  3. Как да добавите поддръжка на клавиатура към бот
  4. Изграждане на последователен, логичен диалог с бота

Съдържание

Ако се интересувате от анализ на данни, може да се интересувате от моя телеграма и YouTube канали. По-голямата част от съдържанието на който е посветено на езика R.

  1. въведение
  2. Процес на изграждане на бот
  3. Структура на бот проект
  4. Конфигурация на бот
  5. Създайте променлива на средата
  6. Създаване на база данни
  7. Писане на функции за работа с база данни
  8. Бот методи
  9. Филтри за съобщения
  10. Манипулатори
  11. Код за стартиране на бот
  12. Заключение

въведение

За да може ботът да поиска данни от вас и да изчака да въведете информация, ще трябва да запишете текущото състояние на диалога. Най-добрият начин да направите това е да използвате някакъв вид вградена база данни, като SQLite.

Тези. Логиката ще бъде следната. Извикваме метода на бота и ботът последователно изисква някаква информация от нас и на всяка стъпка чака тази информация да бъде въведена и може да я провери.

Ще напишем най-простия възможен бот, като първо ще поиска вашето име, след това вашата възраст и ще запази получените данни в базата данни. При запитване за възраст ще провери дали въведените данни са число, а не текст.

Такъв прост диалог ще има само три състояния:

  1. start е нормалното състояние на бота, в което той не очаква никаква информация от вас
  2. wait_name - състояние, в което ботът чака да бъде въведено име
  3. wait_age е състоянието, в което ботът чака да бъде въведена вашата възраст, броят на пълните години.

Процес на изграждане на бот

По време на статията ще изградим бот стъпка по стъпка; целият процес може да бъде схематично изобразен, както следва:
Писане на бот за телеграма в R (част 4): Изграждане на последователен, логичен диалог с бота

  1. Създаваме конфигурация на бот, в която ще съхраняваме някои настройки. В нашия случай токенът на бот и пътя до файла на базата данни.
  2. Създаваме променлива на средата, в която ще се съхранява пътя до проекта с бота.
  3. Ние създаваме самата база данни и редица функции, така че ботът да може да взаимодейства с нея.
  4. Ние пишем бот методи, т.е. функциите, които ще изпълнява.
  5. Добавяне на филтри за съобщения. С помощта на които ботът ще получи достъп до необходимите методи, в зависимост от текущото състояние на чата.
  6. Добавяме манипулатори, които ще свързват команди и съобщения с необходимите бот методи.
  7. Да стартираме бота.

Структура на бот проект

За удобство ще разделим кода на нашия бот и други свързани файлове в следната структура.

  • бот.Р — основният код на нашия бот
  • db_bot_function.R — блок от код с функции за работа с базата данни
  • bot_methods.R — код на бот методи
  • филтри_съобщения.R — филтри за съобщения
  • манипулатори.R - манипулатори
  • config.cfg - конфигурация на бот
  • create_db_data.sql — SQL скрипт за създаване на таблица с чат данни в базата данни
  • create_db_state.sql — SQL скрипт за създаване на таблица на текущото състояние на чат в базата данни
  • bot.db - бот база данни

Можете да видите целия проект на бот или изтеглите от моя хранилище в GitHub.

Конфигурация на бот

Ще използваме обичайния като конфигурация ini файл, следната форма:

[bot_settings]
bot_token=ТОКЕН_ВАШЕГО_БОТА

[db_settings]
db_path=C:/ПУТЬ/К/ПАПКЕ/ПРОЕКТА/bot.db

В конфигурацията записваме токена на бота и пътя до базата данни, т.е. към файла bot.db; ще създадем самия файл в следващата стъпка.

За по-сложни ботове можете да създадете по-сложни конфигурации, освен това не е необходимо да пишете ini конфигурация, можете да използвате всеки друг формат, включително JSON.

Създайте променлива на средата

На всеки компютър папката с проекта на бот може да се намира в различни директории и на различни дискове, така че в кода пътят до папката на проекта ще бъде зададен чрез променлива на средата TG_BOT_PATH.

Има няколко начина за създаване на променлива на средата, най-простият е да я запишете във файл .Renviron.

Можете да създадете или редактирате този файл с помощта на командата file.edit(path.expand(file.path("~", ".Renviron"))). Изпълнете го и добавете един ред към файла:

TG_BOT_PATH=C:/ПУТЬ/К/ВАШЕМУ/ПРОЕКТУ

След това запазете файла .Renviron и рестартирайте RStudio.

Създаване на база данни

Следващата стъпка е да създадете база данни. Ще ни трябват 2 маси:

  • chat_data — данни, които ботът е поискал от потребителя
  • chat_state — текущо състояние на всички чатове

Можете да създадете тези таблици, като използвате следната SQL заявка:

CREATE TABLE chat_data (
    chat_id BIGINT  PRIMARY KEY
                    UNIQUE,
    name    TEXT,
    age     INTEGER
);

CREATE TABLE chat_state (
    chat_id BIGINT PRIMARY KEY
                   UNIQUE,
    state   TEXT
);

Ако сте изтеглили проекта за бот от GitHub, тогава за да създадете базата данни, можете да използвате следния код в R.

# Скрипт создания базы данных
library(DBI)     # интерфейс для работы с СУБД
library(configr) # чтение конфига
library(readr)   # чтение текстовых SQL файлов
library(RSQLite) # драйвер для подключения к SQLite

# директория проекта
setwd(Sys.getenv('TG_BOT_PATH'))

# чтение конфига
cfg <- read.config('config.cfg')

# подключение к SQLite
con <- dbConnect(SQLite(), cfg$db_settings$db_path)

# Создание таблиц в базе
dbExecute(con, statement = read_file('create_db_data.sql'))
dbExecute(con, statement = read_file('create_db_state.sql'))

Писане на функции за работа с база данни

Вече имаме готов конфигурационен файл и създадена база данни. Сега трябва да напишете функции за четене и запис на данни в тази база данни.

Ако сте изтеглили проекта от GitHub, тогава можете да намерите функциите във файла db_bot_function.R.

Функционален код за работа с базата данни

# ###########################################################
# Function for work bot with database

# получить текущее состояние чата
get_state <- function(chat_id) {

  con <- dbConnect(SQLite(), cfg$db_settings$db_path)

  chat_state <- dbGetQuery(con, str_interp("SELECT state FROM chat_state WHERE chat_id == ${chat_id}"))$state

  return(unlist(chat_state))

  dbDisconnect(con)
}

# установить текущее состояние чата
set_state <- function(chat_id, state) {

  con <- dbConnect(SQLite(), cfg$db_settings$db_path)

  # upsert состояние чата
  dbExecute(con, 
            str_interp("
            INSERT INTO chat_state (chat_id, state)
                VALUES(${chat_id}, '${state}') 
                ON CONFLICT(chat_id) 
                DO UPDATE SET state='${state}';
            ")
  )

  dbDisconnect(con)

}

# запись полученных данных в базу
set_chat_data <- function(chat_id, field, value) {

  con <- dbConnect(SQLite(), cfg$db_settings$db_path)

  # upsert состояние чата
  dbExecute(con, 
            str_interp("
            INSERT INTO chat_data (chat_id, ${field})
                VALUES(${chat_id}, '${value}') 
                ON CONFLICT(chat_id) 
                DO UPDATE SET ${field}='${value}';
            ")
  )

  dbDisconnect(con)

}

# read chat data
get_chat_data <- function(chat_id, field) {

  con <- dbConnect(SQLite(), cfg$db_settings$db_path)

  # upsert состояние чата
  data <- dbGetQuery(con, 
                     str_interp("
            SELECT ${field}
            FROM chat_data
            WHERE chat_id = ${chat_id};
            ")
  )

  dbDisconnect(con)

  return(data[[field]])

}

Създадохме 4 прости функции:

  • get_state() — вземете текущото състояние на чата от базата данни
  • set_state() — запишете текущото състояние на чата в базата данни
  • get_chat_data() — получаване на данни, изпратени от потребителя
  • set_chat_data() — запис на данни, получени от потребителя

Всички функции са доста прости, те или четат данни от базата данни с помощта на командата dbGetQuery(), или се ангажирайте UPSERT операция (промяна на съществуващи данни или запис на нови данни в базата данни), използвайки функцията dbExecute().

Синтаксисът за операцията UPSERT е както следва:

INSERT INTO chat_data (chat_id, ${field})
VALUES(${chat_id}, '${value}') 
ON CONFLICT(chat_id) 
DO UPDATE SET ${field}='${value}';

Тези. в полето на нашите таблици chat_id има ограничение за уникалност и е първичният ключ на таблиците. Първоначално се опитваме да добавим информация към таблицата и получаваме грешка, ако вече има данни за текущия чат, в който случай просто актуализираме информацията за този чат.

След това ще използваме тези функции в методите и филтрите на бота.

Бот методи

Следващата стъпка в изграждането на нашия бот е създаването на методи. Ако сте изтеглили проекта от GitHub, тогава всички методи са във файла bot_methods.R.

Код на метода на бот

# ###########################################################
# bot methods

# start dialog
start <- function(bot, update) {

  # 

  # Send query
  bot$sendMessage(update$message$chat_id, 
                  text = "Введи своё имя")

  # переключаем состояние диалога в режим ожидания ввода имени
  set_state(chat_id = update$message$chat_id, state = 'wait_name')

}

# get current chat state
state <- function(bot, update) {

  chat_state <- get_state(update$message$chat_id)

  # Send state
  bot$sendMessage(update$message$chat_id, 
                  text = unlist(chat_state))

}

# reset dialog state
reset <- function(bot, update) {

  set_state(chat_id = update$message$chat_id, state = 'start')

}

# enter username
enter_name <- function(bot, update) {

  uname <- update$message$text

  # Send message with name
  bot$sendMessage(update$message$chat_id, 
                  text = paste0(uname, ", приятно познакомится, я бот!"))

  # Записываем имя в глобальную переменную
  #username <<- uname
  set_chat_data(update$message$chat_id, 'name', uname) 

  # Справшиваем возраст
  bot$sendMessage(update$message$chat_id, 
                  text = "Сколько тебе лет?")

  # Меняем состояние на ожидание ввода имени
  set_state(chat_id = update$message$chat_id, state = 'wait_age')

}

# enter user age
enter_age <- function(bot, update) {

  uage <- as.numeric(update$message$text)

  # проверяем было введено число или нет
  if ( is.na(uage) ) {

    # если введено не число то переспрашиваем возраст
    bot$sendMessage(update$message$chat_id, 
                    text = "Ты ввёл некорректные данные, введи число")

  } else {

    # если введено число сообщаем что возраст принят
    bot$sendMessage(update$message$chat_id, 
                    text = "ОК, возраст принят")

    # записываем глобальную переменную с возрастом
    #userage <<- uage
    set_chat_data(update$message$chat_id, 'age', uage) 

    # сообщаем какие данные были собраны
    username <- get_chat_data(update$message$chat_id, 'name')
    userage  <- get_chat_data(update$message$chat_id, 'age')

    bot$sendMessage(update$message$chat_id, 
                    text = paste0("Тебя зовут ", username, " и тебе ", userage, " лет. Будем знакомы"))

    # возвращаем диалог в исходное состояние
    set_state(chat_id = update$message$chat_id, state = 'start')
  }

}

Създадохме 5 метода:

  • начало — Стартиране на диалогов прозорец
  • състояние — Вземете текущото състояние на чата
  • нулиране — Нулиране на текущото състояние на чат
  • enter_name — Ботът пита за вашето име
  • enter_age — Ботът пита за вашата възраст

метод start пита за вашето име и променя състоянието на чат на чакащо_име, т.е. в режим на готовност за въвеждане на вашето име.

След това изпращате името и то се обработва от метода enter_name, ботът ви поздравява, записва полученото име в базата данни и превключва чата към състоянието възраст_на изчакване.

На този етап ботът очаква да въведете възрастта си. Изпращате възрастта си, ботът проверява съобщението, ако сте изпратили някакъв текст вместо номер, ще каже: Ты ввёл некорректные данные, введи число, и ще изчака да въведете отново данните си. Ако сте изпратили номер, ботът ще съобщи, че е приел вашата възраст, ще запише получените данни в базата данни, ще отчете всички данни, получени от вас и ще върне състоянието на чата в първоначалната му позиция, т.е. V start.

Чрез извикване на метода state можете да поискате текущото състояние на чата по всяко време и като използвате reset върнете чата в първоначалното му състояние.

Филтри за съобщения

В нашия случай това е една от най-важните части в изграждането на бот. Именно с помощта на филтри за съобщения ботът ще разбере каква информация очаква от вас и как трябва да бъде обработена.

В проекта на GitHub филтрите са регистрирани във файла филтри_съобщения.R.

Код на филтъра за съобщения:

# ###########################################################
# message state filters

# фильтр сообщений в состоянии ожидания имени
MessageFilters$wait_name <- BaseFilter(function(message) {
  get_state( message$chat_id )  == "wait_name"
}
)

# фильтр сообщений в состоянии ожидания возраста
MessageFilters$wait_age <- BaseFilter(function(message) {
  get_state( message$chat_id )   == "wait_age"
}
)

Във филтрите използваме предварително написаната функция get_state(), за да поискате текущото състояние на чата. Тази функция изисква само 1 аргумент, ID на чата.

Следващ филтър чакащо_име обработва съобщения, когато чатът е в състояние wait_name, и съответно филтъра възраст_на изчакване обработва съобщения, когато чатът е в състояние wait_age.

Манипулатори

Файлът с манипулатори се извиква манипулатори.Rи има следния код:

# ###########################################################
# handlers

# command handlers
start_h <- CommandHandler('start', start)
state_h <- CommandHandler('state', state)
reset_h <- CommandHandler('reset', reset)

# message handlers
## !MessageFilters$command - означает что команды данные обработчики не обрабатывают, 
## только текстовые сообщения
wait_age_h  <- MessageHandler(enter_age,  MessageFilters$wait_age  & !MessageFilters$command)
wait_name_h <- MessageHandler(enter_name, MessageFilters$wait_name & !MessageFilters$command)

Първо създаваме манипулатори на команди, които ще ви позволят да стартирате методи за стартиране на диалог, да го нулирате и да направите заявка за текущото състояние.

След това създаваме 2 манипулатора на съобщения, използвайки филтрите, създадени в предишната стъпка, и добавяме филтър към тях !MessageFilters$command, така че да можем да използваме команди във всяко състояние на чат.

Код за стартиране на бот

Сега имаме всичко готово за стартиране, основният код за стартиране на бота е във файла бот.Р.

library(telegram.bot)
library(tidyverse)
library(RSQLite)
library(DBI)
library(configr)

# переходим в папку проекта
setwd(Sys.getenv('TG_BOT_PATH'))

# читаем конфиг
cfg <- read.config('config.cfg')

# создаём экземпляр бота
updater <- Updater(cfg$bot_settings$bot_token)

# Загрузка компонентов бота
source('db_bot_function.R') # функции для работы с БД
source('bot_methods.R')     # методы бота
source('message_filters.R') # фильтры сообщений
source('handlers.R') # обработчики сообщений

# Добавляем обработчики в диспетчер
updater <- updater +
  start_h +
  wait_age_h +
  wait_name_h +
  state_h +
  reset_h

# Запускаем бота
updater$start_polling()

В резултат на това получихме този бот:
Писане на бот за телеграма в R (част 4): Изграждане на последователен, логичен диалог с бота

По всяко време с помощта на командата /state можем да направим запитване за текущото състояние на чата и да използваме командата /reset върнете чата в първоначалното му състояние и започнете диалога отново.

Заключение

В тази статия разбрахме как да използваме база данни в бот и как да изграждаме последователни логически диалози, като записваме състоянието на чата.

В този случай разгледахме най-примитивния пример, за да ви бъде по-лесно да разберете идеята за изграждане на такива ботове; на практика можете да изградите много по-сложни диалози.

В следващата статия от тази поредица ще научим как да ограничим правата на потребителите на ботове да използват различни от неговите методи.

Източник: www.habr.com

Добавяне на нов коментар