ProHoster > Blogs > Administrācija > Telegrammas robota rakstīšana programmā R (4. daļa): konsekventa, loģiska dialoga veidošana ar robotu
Telegrammas robota rakstīšana programmā R (4. daļa): konsekventa, loģiska dialoga veidošana ar robotu
Ja jau esi izlasījis iepriekšējo trīs raksti no šīs sērijas, tad jūs jau zināt, kā rakstīt pilnvērtīgus telegrammu botus ar tastatūru.
Šajā rakstā mēs uzzināsim, kā uzrakstīt robotu, kas uzturēs konsekventu dialogu. Tie. Bots uzdos jums jautājumus un gaidīs, kamēr ievadīsit kādu informāciju. Atkarībā no ievadītajiem datiem robots veiks dažas darbības.
Arī šajā rakstā mēs uzzināsim, kā izmantot datubāzi zem robota pārsega, mūsu piemērā tas būs SQLite, bet jūs varat izmantot jebkuru citu DBVS. Es rakstīju sīkāk par mijiedarbību ar datu bāzēm R valodā Šis raksts.
Visi raksti no sērijas “Telegrammas robota rakstīšana R”
Lai robots varētu pieprasīt no jums datus un gaidīt, kamēr jūs ievadīsit jebkādu informāciju, jums būs jāreģistrē pašreizējais dialoga stāvoklis. Labākais veids, kā to izdarīt, ir izmantot kādu iegultu datubāzi, piemēram, SQLite.
Tie. Loģika būs šāda. Mēs izsaucam robota metodi, un robots secīgi pieprasa no mums kādu informāciju un katrā solī gaida, līdz šī informācija tiks ievadīta, un var to pārbaudīt.
Mēs uzrakstīsim pēc iespējas vienkāršāku botu, vispirms tas prasīs jūsu vārdu, pēc tam vecumu, un saņemtos datus saglabās datu bāzē. Pieprasot vecumu, tā pārbaudīs, vai ievadītie dati ir cipars, nevis teksts.
Šādam vienkāršam dialogam būs tikai trīs stāvokļi:
start ir parastais robota stāvoklis, kurā tas negaida no jums nekādu informāciju
gaidīt_nosaukums — stāvoklis, kurā robots gaida vārda ievadīšanu
gaid_vecums ir stāvoklis, kurā robotprogrammatūra gaida, kad tiks ievadīts jūsu vecums, pilno gadu skaits.
Botu veidošanas process
Raksta laikā mēs soli pa solim izveidosim botu, visu procesu var shematiski attēlot šādi:
Mēs izveidojam robota konfigurāciju, kurā saglabāsim dažus iestatījumus. Mūsu gadījumā robota marķieris un ceļš uz datu bāzes failu.
Mēs izveidojam vides mainīgo, kurā tiks saglabāts ceļš uz projektu ar robotprogrammatūru.
Mēs izveidojam pašu datu bāzi un vairākas funkcijas, lai robots varētu ar to mijiedarboties.
Mēs rakstām botu metodes, t.i. funkcijas, kuras tas veiks.
Ziņojumu filtru pievienošana. Ar kuru palīdzību robots piekļūs nepieciešamajām metodēm atkarībā no pašreizējā tērzēšanas stāvokļa.
Mēs pievienojam apstrādātājus, kas savienos komandas un ziņojumus ar nepieciešamajām robotu metodēm.
Palaidīsim botu.
Botu projekta struktūra
Ērtības labad mēs sadalīsim mūsu robotprogrammatūras kodu un citus saistītos failus šādā struktūrā.
bot.R — mūsu bota galvenais kods
db_bot_function.R — koda bloks ar funkcijām darbam ar datu bāzi
bot_methods.R — robotu metožu kods
ziņu_filtri.R — ziņojumu filtri
apdarinātāji.R - apdarinātāji
config.cfg - robotprogrammatūras konfigurācija
Create_db_data.sql — SQL skripts tabulas izveidei ar tērzēšanas datiem datu bāzē
Create_db_state.sql — SQL skripts pašreizējā tērzēšanas stāvokļa tabulas izveidei datu bāzē
Konfigurācijā ierakstām bota marķieri un ceļu uz datu bāzi, t.i. uz failu bot.db; nākamajā darbībā mēs izveidosim pašu failu.
Sarežģītākiem robotiem varat izveidot sarežģītākas konfigurācijas, turklāt nav nepieciešams rakstīt ini konfigurāciju, varat izmantot jebkuru citu formātu, ieskaitot JSON.
Izveidojiet vides mainīgo
Katrā datorā mape ar robotprojektu var atrasties dažādos direktorijos un dažādos diskos, tāpēc kodā ceļš uz projekta mapi tiks iestatīts, izmantojot vides mainīgo. TG_BOT_PATH.
Ir vairāki veidi, kā izveidot vides mainīgo, visvienkāršākais ir ierakstīt to failā .Renviron.
Varat izveidot vai rediģēt šo failu, izmantojot komandu file.edit(path.expand(file.path("~", ".Renviron"))). Izpildiet to un pievienojiet failam vienu rindiņu:
TG_BOT_PATH=C:/ПУТЬ/К/ВАШЕМУ/ПРОЕКТУ
Tālāk saglabājiet failu .Renviron un restartējiet RStudio.
Datu bāzes izveide
Nākamais solis ir izveidot datubāzi. Mums būs nepieciešamas 2 tabulas:
chat_data — dati, ko robots pieprasīja lietotājam
chat_state — visu tērzēšanas sarunu pašreizējais stāvoklis
Šīs tabulas var izveidot, izmantojot šādu SQL vaicājumu:
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
);
Ja lejupielādējāt bota projektu no GitHub, pēc tam, lai izveidotu datu bāzi, varat izmantot šādu kodu 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'))
Rakstīšanas funkcijas darbam ar datu bāzi
Mums jau ir gatavs konfigurācijas fails un izveidota datu bāze. Tagad jums ir jāraksta funkcijas, lai lasītu un ierakstītu datus šajā datu bāzē.
Ja lejupielādējāt projektu no GitHub, tad varat atrast funkcijas failā db_bot_function.R.
Funkcijas kods darbam ar datu bāzi
# ###########################################################
# 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]])
}
Mēs izveidojām 4 vienkāršas funkcijas:
get_state() — iegūt pašreizējo tērzēšanas stāvokli no datu bāzes
set_state() — ierakstiet datu bāzē pašreizējo tērzēšanas stāvokli
get_chat_data() — saņemt lietotāja nosūtītos datus
set_chat_data() — ierakstīt datus, kas saņemti no lietotāja
Visas funkcijas ir diezgan vienkāršas, tās vai nu nolasa datus no datu bāzes, izmantojot komandu dbGetQuery(), vai apņemties UPSERT darbība (esošo datu maiņa vai jaunu datu ierakstīšana datu bāzē), izmantojot funkciju dbExecute().
UPSERT darbības sintakse ir šāda:
INSERT INTO chat_data (chat_id, ${field})
VALUES(${chat_id}, '${value}')
ON CONFLICT(chat_id)
DO UPDATE SET ${field}='${value}';
Tie. mūsu tabulu laukā tērzēšanas_id ir unikalitātes ierobežojums, un tā ir tabulu primārā atslēga. Sākotnēji mēs cenšamies pievienot informāciju tabulai, un mēs saņemam kļūdu, ja dati par pašreizējo tērzēšanu jau ir, un tādā gadījumā mēs vienkārši atjauninām informāciju par šo tērzēšanu.
Tālāk mēs izmantosim šīs funkcijas bota metodēs un filtros.
Botu metodes
Nākamais solis mūsu robotprogrammatūras veidošanā ir metožu izveide. Ja lejupielādējāt projektu no GitHub, tad visas metodes ir failā bot_methods.R.
Bota metodes kods
# ###########################################################
# 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')
}
}
Mēs izveidojām 5 metodes:
start — lai sāktu dialogu
status — iegūstiet pašreizējo tērzēšanas stāvokli
atiestatīt — lai atiestatītu pašreizējo tērzēšanas stāvokli
enter_name — robots jautā jūsu vārdu
enter_age — robots jautā jūsu vecumu
Metode start jautā jūsu vārdu un maina tērzēšanas statusu uz gaidīt_nosaukums, t.i. gaidīšanas režīmā, lai ievadītu savu vārdu.
Pēc tam jūs nosūtāt vārdu, un tas tiek apstrādāts ar metodi enter_name, robots sveicina jūs, ieraksta saņemto vārdu datu bāzē un pārslēdz tērzēšanu uz stāvokli gaidīšanas_vecums.
Šajā posmā robots sagaida, ka ievadīsit savu vecumu. Jūs nosūtāt savu vecumu, robots pārbauda ziņojumu, ja numura vietā nosūtījāt tekstu, tas teiks: Ты ввёл некорректные данные, введи число, un gaidīs, kamēr atkārtoti ievadīsiet savus datus. Ja jūs nosūtījāt numuru, bots ziņos, ka ir pieņēmis jūsu vecumu, ierakstīs saņemtos datus datu bāzē, ziņos par visiem no jums saņemtajiem datiem un atgriezīs tērzēšanas stāvokli sākotnējā pozīcijā, t.i. V start.
Izsaucot metodi state jūs varat pieprasīt pašreizējo tērzēšanas statusu jebkurā laikā un izmantojot reset atgriezt tērzēšanu sākotnējā stāvoklī.
Ziņojumu filtri
Mūsu gadījumā šī ir viena no vissvarīgākajām robota izveides daļām. Tieši ar ziņojumu filtru palīdzību bots sapratīs, kādu informāciju tas no jums sagaida un kā tā jāapstrādā.
Projektā uz GitHub filtri ir reģistrēti failā ziņu_filtri.R.
Ziņojumu filtra kods:
# ###########################################################
# 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"
}
)
Filtros mēs izmantojam iepriekš uzrakstīto funkciju get_state(), lai pieprasītu pašreizējo tērzēšanas stāvokli. Šai funkcijai ir nepieciešams tikai 1 arguments, tērzēšanas ID.
Nākamais filtrs gaidīt_nosaukums apstrādā ziņojumus, kad tērzēšana ir stāvoklī wait_name, un attiecīgi filtru gaidīšanas_vecums apstrādā ziņojumus, kad tērzēšana ir stāvoklī wait_age.
Apdarinātāji
Tiek izsaukts fails ar apstrādātājiem apdarinātāji.R, un tam ir šāds kods:
# ###########################################################
# 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)
Vispirms mēs izveidojam komandu apdarinātājus, kas ļaus palaist metodes dialoga palaišanai, atiestatīšanai un pašreizējā stāvokļa vaicāšanai.
Pēc tam mēs izveidojam 2 ziņojumu apdarinātājus, izmantojot iepriekšējā darbībā izveidotos filtrus, un pievienojam tiem filtru !MessageFilters$command, lai mēs varētu izmantot komandas jebkurā tērzēšanas stāvoklī.
Bota palaišanas kods
Tagad mums viss ir gatavs palaišanai, galvenais kods bota palaišanai ir failā bot.R.
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()
Rezultātā mēs saņēmām šo robotprogrammatūru:
Jebkurā laikā, izmantojot komandu /state mēs varam jautāt par pašreizējo tērzēšanas stāvokli un izmantojot komandu /reset atgrieziet tērzēšanas sarunu sākotnējā stāvoklī un sāciet dialogu no jauna.
Secinājums
Šajā rakstā mēs noskaidrojām, kā izmantot datubāzi robotā un kā izveidot secīgus loģiskus dialogus, ierakstot tērzēšanas stāvokli.
Šajā gadījumā mēs apskatījām primitīvāko piemēru, lai jums būtu vieglāk saprast ideju par šādu robotprogrammatūru izveidi; praksē jūs varat veidot daudz sarežģītākus dialogus.
Nākamajā šīs sērijas rakstā uzzināsim, kā ierobežot botu lietotāju tiesības izmantot dažādas tā metodes.