Kui olete juba tutvunud eelnevate selles seerias, siis oskate juba luua täisfunktsionaalseid Telegrami robote, millel on klaviatuur.
Selles artiklis õpime koos looma roboti, mis toetab järkjärgulist dialooge. See tähendab, et robot esitab teile küsimusi ja ootab teie vastuseid. Olenevalt teie sisestatud andmetest teeb robot teatud toiminguid.
Samuti õpime selles artiklis kasutama roboti taga andmebaase, meie näites on see SQLite, kuid võite kasutada ka mõnda muud andmebaasi. Rohkem teavet andmebaasidega töötamisest R keeles olen kirjutanud artiklis .

Kõik artiklid seeriast "Kirjutame telegrammi boti R keeles"
Sisu
Kui teid huvitab andmeanalüüs, siis võivad teile huvi pakkuda minu ja kanalid, mille suurem osa sisust on pühendatud R keelele.
Sissejuhatus
Kuna bot saaks teilt andmeid küsida ja ootata mingit teavet, peate fikseerima dialooge seisundi. Parim viis selle tegemiseks on kasutada mingit sissebuildud andmebaasi, näiteks SQLite.
See tähendab, et loogika on järgmine. Kutsume bot'i meetodi ja bot küsib meilt järjestikku mingit teavet, samal ajal ootab ta iga sammu juures selle teabe sisestamist ning võib seda kontrollida.
Loome võimalikult lihtsa boti, kõigepealt küsib ta teie nime, seejärel vanuse, saadud andmed salvestab ta andmebaasi. Vanuse küsimisel kontrollib ta, et sisestatud andmed oleks number, mitte tekst.
Selline lihtne dialoog sisaldab vaid kolme seisundit:
- start — bot'i tavaline seisund, kus ta ei oota teilt mingit teavet.
- wait_name — seisund, kus bot ootab nime sisestamist.
- wait_age — seisund, kus bot ootab teie vanuse sisestamist, täis aastate arv.
Roboti loomise protsess
Artikli käigus ehitame koos bot'i samm-sammult, kogu protsessi saab visuaalselt kujutada järgmiselt:

- Loome boti konfiguratsiooni, kus salvestame mõned seaded. Meie puhul bot'i token ja andmefaili tee.
- Loome keskkonnamuutuja, kus säilitatakse bot'iga seotud projekti tee.
- Loome andmebaasi ning rea funktsioone, et bot saaks sellega suhtlemiseks töötada.
- Kirjutame boti meetodid, st funktsioonid, mida ta täidab.
- Lisame sõnumifiltrid, millega bot suunab õigete meetodite poole vastavalt vestluse praegusele olekule.
- Lisame töötlejad, mis seovad käsud ja sõnumid vajalike bot'i meetoditega.
- Käivitame boti.
Roboti projekti struktuur
Kergenduse huvides jagame meie boti koodi ja sellega seotud failid järgmisse struktuuri.
- bot.R — meie boti põhikood
- db_bot_function.R — andmebaasifunktsioonide koodiblokk
- bot_methods.R — boti meetodite kood
- message_filters.R — sõnumifiltrid
- handlers.R — töötlejad
- config.cfg — boti konfigureerimine
- create_db_data.sql — SQL skript andmebaasi vestluse andmete tabeli loomiseks
- create_db_state.sql — SQL skript, et luua andmebaasis vestluse praeguse oleku tabel
- bot.db — boti andmebaas
Kogu boti projekt on nähtav või minu .
Roboti konfiguratsioon
Konfiguratsiooniks kasutame tavalist , järgmist tüüpi:
[bot_settings]
bot_token=TEIE_BOTI_TOKEN
[db_settings]
db_path=C:/TEE/KAUSTA/PROJEKTI/bot.dbKonfiguratsioonifaili kirjutame boti tokeni ja andmebaasi tee, st failile bot.db, mille loome järgmises etapis.
Kasutades keerukamaid bote, saab luua ka keerukamaid konfiguratsioone, ning ei ole tingimata vajalik kirjutada just ini konfiguratsiooni, võite kasutada ka mingit muud formaati, sealhulgas JSON-i.
Loodame keskkonnamuutuja
Iga arvuti puhul võib boti projekti kaust paikneda erinevates direktorites ja erinevates ketastes, seetõttu määratakse koodi sees projekti kausta tee keskkonnamuutuja kaudu TG_BOT_PATH.
Keskkonnamuutuja loomine on võimalik mitmel viisil, kõige lihtsam on see määrata faili .Renviron.
Seda faili saab luua või redigeerida käsuga file.edit(path.expand(file.path("~", ".Renviron"))). Täitke see ja lisage faili üks rida:
TG_BOT_PATH=C:/TEE/KAUSTA/TEIE/PROJEKTSeejärel salvestage fail .Renviron ja taaskäivitage RStudio.
Loome andmebaasi
Järgnev samm on andmebaasi loomine. Me vajame 2 tabelit:
- chat_data — andmed, mille bot küsis kasutajalt
- chat_state — kõigi vestluste praegune olek
Nende tabelite loomine on võimalik järgmise SQL päringu abil:
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
);
Kui olete allalaadinud boti projekti , saate andmebaasi loomiseks kasutada järgmise R-koodi.
# Скрипт создания базы данных
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'))
Kirjutame funktsioonid andmebaasiga töötamiseks
Meil on juba olemas konfiguratsioonifail ja loodud andmebaas. Nüüd tuleb kirjutada funktsioonid andmete lugemiseks ja salvestamiseks sellesse andmebaasi.
Kui olete projekti alla laadinud , leiate funktsioonid failist db_bot_function.R.
Andmebaasiga töötamise funktsioonide kood
# ###########################################################
# 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]])
}Oleme loonud 4 lihtsat funktsiooni:
get_state()— saadud chati praegune seisund andmebaasistset_state()— salvestab praeguse chati seisundi andmebaasiget_chat_data()— saadud kasutaja poolt saadetud andmedset_chat_data()— salvestab kasutajalt saadud andmed
Kõik funktsioonid on piisavalt lihtsad, nad kas loevad andmeid andmebaasist käsuga dbGetQuery(), või teevad UPSERT operatsiooni (muudavad olemasolevaid andmeid või salvestavad uusi andmeid andmebaasi), kasutades funktsiooni dbExecute().
UPSERT operatsiooni süntaks näeb välja järgmiselt:
INSERT INTO chat_data (chat_id, ${field})
VALUES(${chat_id}, '${value}')
ON CONFLICT(chat_id)
DO UPDATE SET ${field}='${value}';See, meie tabelites on väli chat_id ainult unikaalsuse piirang ja see on tabelite primaarvõti. Esiteks proovime lisada teavet tabelisse ja saame vea, kui selle chati andmed on juba olemas, sel juhul värskendame lihtsalt selle chati kohta teavet.
Edasi kasutame neid funktsioone boti meetodites ja filtrites.
Roboti meetodid
Järgmine samm meie boti ehitamisel on meetodite loomine. Kui olete projekti alla laadinud , siis kõik meetodid asuvad failis bot_methods.R.
Boti meetodite kood
# ###########################################################
# 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')
}
}Oleme loonud 5 meetodit:
- start — Dialooge alustamine
- state — Praeguse chati oleku saamine
- reset — Praeguse chati oleku lähtestamine
- enter_name — Bot küsib teie nime
- enter_age — Bot küsib teie vanust
Meetod start küsib teie nime ja muudab chati oleku wait_name, st ooterežiimi teie nime sisestamiseks.
Seejärel saadate nime ja see töödeldakse meetodi enter_name, bot tervitab teid, salvestab saadud nime andmebaasi ning muudab chati olekuks wait_age.
Sel hetkel ootab bot teie vanust. Te saadate oma vanuse, bot kontrollib sõnumit; kui sa saatsid mõne teksti numbri asemel, ütleb ta: Sa sisestasid vale andmed, palun sisesta number, ja ootab teie uue sisendi. Kui sa saatsid numbri, teatab bot, et ta on sinu vanuse vastu võtnud, salvestab saadud andmed andmebaasi, teatab kõikidest sinu antud andmetest ja viib vestluse oleku algsesse seisundisse, st. start.
Kutsudes esile meetodi state võid igal ajal küsida vestluse praegust olekut, ning meetodiga reset saada vestlus algsesse olekusse.
Sõnumifiltrid
Meie puhul on see üks tähtsamaid osi boti ülesehitamisel. Just läbi sõnumifiltrite mõistab bot, millist teavet ta sinult ootab ja kuidas seda töödelda.
Projektis on filtrid määratletud failis message_filters.R.
Sõnumifiltrite kood:
# ###########################################################
# 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"
}
)Filtrites kasutame varem kirjutatud funktsiooni get_state(), et küsida vestluse praegust olekut. See funktsioon nõuab ainult 1 argumenti, vestluse id-d.
Seejärel filtrit wait_name töödeldakse sõnumeid, kui vestlus on olekus wait_name, ja vastavalt filtrit wait_age töödeldakse sõnumeid, kui vestlus on olekus wait_age.
Käivitusprotsessorid
Faili käitlejatega nimeks on handlers.R, ja sellel on järgmine kood:
# ###########################################################
# 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)Esmalt loome käskude käitlejad, mis võimaldavad teil käivitada meetodeid dialooge alustamiseks, taaskäivitamiseks ja praeguse oleku pärimiseks.
Seejärel loome 2 sõnumite käitlejat, kasutades eelnevalt loodud filtreid, ja lisame neile filtreeringu !MessageFilters$command, et saaksime igas vestluse seisus kasutada käske.
Roboti käivitamise kood
Nüüd on meil kõik valmis käivitamiseks, põhikood roboti käivitamiseks asub failis bot.R.
library(telegram.bot)
library(tidyverse)
library(RSQLite)
library(DBI)
library(configr)
# liigume projekti kausta
setwd(Sys.getenv('TG_BOT_PATH'))
# loeme konfigurationi
cfg <- read.config('config.cfg')
# loome roboti instantsi
updater <- Updater(cfg$bot_settings$bot_token)
# Roboti komponentide laadimine
source('db_bot_function.R') # funktsioonid andmebaasi kasutamiseks
source('bot_methods.R') # roboti meetodid
source('message_filters.R') # sõnumite filtrid
source('handlers.R') # sõnumite käitlejad
# Lisame käitlejad dispetšerisse
updater <- updater +
start_h +
wait_age_h +
wait_name_h +
state_h +
reset_h
# Käivitame roboti
updater$start_polling()Kokkuvõtteks saime sellise roboti:

Igal hetkel saame käsu abil /state pärida vestluse praegust olekut, ja käsu abil /reset taasta vestlus algseisundisse ja alustada dialogi nullist.
Kokkuvõte
Selles artiklis uurime, kuidas kasutada botis andmebaase ja kuidas luua järjestikuseid loogilisi dialooge, salvestades vestluse olekut.
Sel juhul vaatasime kõige primitiivsemat näidet, et teil oleks lihtsam mõista selliste botide loomise ideed; praktikas võite luua palju keerulisemaid dialooge.
Selle seeria järgmises artiklis õpime, kuidas piirata botikasutajate õigusi erinevate meetodite kasutamisel.
Allikas: habr.com
