ProHoster > Blog > administrasie > Skryf 'n telegram-bot in R (deel 4): Bou 'n konsekwente, logiese dialoog met die bot
Skryf 'n telegram-bot in R (deel 4): Bou 'n konsekwente, logiese dialoog met die bot
As jy reeds die vorige gelees het drie artikels uit hierdie reeks, dan weet jy reeds hoe om volwaardige telegram-bots met 'n sleutelbord te skryf.
In hierdie artikel sal ons leer hoe om 'n bot te skryf wat 'n konsekwente dialoog sal handhaaf. Dié. Die bot sal jou vrae vra en wag vir jou om inligting in te voer. Afhangende van die data wat jy invoer, sal die bot 'n paar aksies uitvoer.
Ook in hierdie artikel sal ons leer hoe om 'n databasis onder die kap van die bot te gebruik, in ons voorbeeld sal dit SQLite wees, maar jy kan enige ander DBMS gebruik. Ek het in meer besonderhede geskryf oor interaksie met databasisse in die R-taal in Hierdie artikel.
Alle artikels uit die reeks "Skryf 'n telegram-bot in R"
Om vir die bot data van jou te versoek en te wag vir jou om enige inligting in te voer, sal jy die huidige stand van die dialoog moet opneem. Die beste manier om dit te doen is om 'n soort ingebedde databasis te gebruik, soos SQLite.
Dié. Die logika sal soos volg wees. Ons noem die bot-metode, en die bot versoek opeenvolgend inligting van ons, en by elke stap wag dit vir hierdie inligting om in te voer en kan dit nagaan.
Ons sal die eenvoudigste moontlike bot skryf, eers sal dit jou naam vra, dan jou ouderdom, en sal die ontvangde data in die databasis stoor. Wanneer daar vir ouderdom gevra word, sal dit kontroleer dat die ingevoerde data 'n nommer is en nie teks nie.
So 'n eenvoudige dialoog sal slegs drie toestande hê:
begin is die normale toestand van die bot, waarin dit geen inligting van jou verwag nie
wag_naam - staat waarin die bot wag vir 'n naam om in te voer
wait_age is die toestand waarin die bot wag dat jou ouderdom ingevoer word, die aantal volle jare.
Bot bou proses
Tydens die artikel sal ons stap vir stap 'n bot bou; die hele proses kan skematies soos volg uitgebeeld word:
Ons skep 'n bot-konfigurasie waarin ons 'n paar instellings sal stoor. In ons geval, die bot-token en die pad na die databasislêer.
Ons skep 'n omgewingsveranderlike waarin die pad na die projek met die bot gestoor sal word.
Ons skep die databasis self, en 'n aantal funksies sodat die bot daarmee kan kommunikeer.
Ons skryf botmetodes, m.a.w. die funksies wat dit sal verrig.
Voeg boodskapfilters by. Met die hulp waarvan die bot toegang tot die nodige metodes sal kry, afhangende van die huidige toestand van die klets.
Ons voeg hanteerders by wat opdragte en boodskappe met die nodige botmetodes sal verbind.
Kom ons begin die bot.
Bot projek struktuur
Gerieflikheidshalwe sal ons die kode van ons bot, en ander verwante lêers, in die volgende struktuur verdeel.
bot.R - die hoofkode van ons bot
db_bot_function.R — 'n blok kode met funksies om met die databasis te werk
bot_metodes.R - kode van botmetodes
boodskap_filters.R - boodskap filters
hanteerders.R - hanteerders
config.cfg - bot config
skep_db_data.sql - SQL-skrip vir die skep van 'n tabel met kletsdata in die databasis
skep_db_staat.sql - SQL script vir die skep van 'n tabel van die huidige klets toestand in die databasis
In die config skryf ons die bot-token en die pad na die databasis, d.w.s. na die bot.db-lêer; ons sal die lêer self in die volgende stap skep.
Vir meer komplekse bots, kan jy meer komplekse konfigurasies skep, buitendien is dit nie nodig om 'n ini config te skryf nie, jy kan enige ander formaat gebruik, insluitend JSON.
Skep 'n omgewingsveranderlike
Op elke rekenaar kan die vouer met die botprojek in verskillende dopgehou en op verskillende aandrywers geleë wees, dus in die kode sal die pad na die projeklêer via 'n omgewingsveranderlike gestel word TG_BOT_PATH.
Daar is verskeie maniere om 'n omgewingsveranderlike te skep, die eenvoudigste is om dit in 'n lêer te skryf .Omgewing.
Jy kan hierdie lêer skep of wysig deur die opdrag te gebruik file.edit(path.expand(file.path("~", ".Renviron"))). Voer dit uit en voeg een reël by die lêer:
TG_BOT_PATH=C:/ПУТЬ/К/ВАШЕМУ/ПРОЕКТУ
Stoor dan die lêer .Omgewing en herbegin RStudio.
Die skep van 'n databasis
Die volgende stap is om 'n databasis te skep. Ons benodig 2 tafels:
chat_data — data wat die bot van die gebruiker aangevra het
chat_state — huidige toestand van alle geselsies
U kan hierdie tabelle skep deur die volgende SQL-navraag te gebruik:
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
);
As jy die botprojek afgelaai het vanaf GitHub, dan kan jy die volgende kode in R gebruik om die databasis te skep.
# Скрипт создания базы данных
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'))
Skryffunksies om met die databasis te werk
Ons het reeds 'n konfigurasielêer gereed en 'n databasis geskep. Nou moet jy funksies skryf om data te lees en na hierdie databasis te skryf.
As jy die projek afgelaai het vanaf GitHub, dan kan jy die funksies in die lêer vind db_bot_function.R.
Funksiekode om met die databasis te werk
# ###########################################################
# 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]])
}
Ons het 4 eenvoudige funksies geskep:
get_state() - kry die huidige kletsstatus vanaf die databasis
set_state() - skryf die huidige kletstoestand na die databasis
get_chat_data() - ontvang data wat deur die gebruiker gestuur is
set_chat_data() - rekord data wat van die gebruiker ontvang is
Alle funksies is redelik eenvoudig, hulle lees óf data vanaf die databasis met behulp van die opdrag dbGetQuery(), of pleeg UPSERT bewerking (verander bestaande data of skryf nuwe data na die databasis), met behulp van die funksie dbExecute().
Die sintaksis vir die UPSERT-bewerking is soos volg:
INSERT INTO chat_data (chat_id, ${field})
VALUES(${chat_id}, '${value}')
ON CONFLICT(chat_id)
DO UPDATE SET ${field}='${value}';
Dié. in ons tabelle veld chat_id het 'n uniekheidsbeperking en is die primêre sleutel van tabelle. Aanvanklik probeer ons om inligting by die tabel te voeg, en ons kry 'n fout as data vir die huidige klets reeds teenwoordig is, in welke geval ons bloot die inligting vir hierdie klets bywerk.
Vervolgens sal ons hierdie funksies in die bot se metodes en filters gebruik.
Bot metodes
Die volgende stap in die bou van ons bot is om metodes te skep. As jy die projek afgelaai het vanaf GitHub, dan is alle metodes in die lêer bot_metodes.R.
Bot metode kode
# ###########################################################
# 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')
}
}
Ons het 5 metodes geskep:
begin — Begin 'n dialoog
staat — Kry die huidige kletsstatus
stel terug — Stel die huidige kletsstatus terug
enter_name — Die bot vra vir jou naam
enter_age — Die bot vra vir jou ouderdom
metode start vra vir jou naam, en verander die kletstoestand na wag_naam, d.w.s. na bystand vir die invoer van jou naam.
Vervolgens stuur jy die naam en dit word volgens die metode verwerk enter_name, die bot groet jou, skryf die ontvangde naam in die databasis, en skakel die klets na die toestand wag_ouderdom.
Op hierdie stadium verwag die bot van jou om jou ouderdom in te voer. Jy stuur jou ouderdom, die bot kontroleer die boodskap, as jy 'n teks gestuur het in plaas van 'n nommer, sal dit sê: Ты ввёл некорректные данные, введи число, en sal wag vir jou om jou data weer in te voer. As jy 'n nommer gestuur het, sal die bot rapporteer dat hy jou ouderdom aanvaar het, die ontvangde data na die databasis skryf, al die data wat van jou ontvang is rapporteer en die kletstoestand na sy oorspronklike posisie terugstuur, d.w.s. V start.
Deur die metode te noem state jy kan enige tyd die huidige kletsstatus aanvra, en die gebruik van die reset herstel die klets na sy oorspronklike toestand.
Boodskap filters
In ons geval is dit een van die belangrikste dele in die bou van 'n bot. Dit is met behulp van boodskapfilters dat die bot sal verstaan watter inligting hy van jou verwag en hoe dit verwerk moet word.
In die projek op GitHub filters word in die lêer geregistreer boodskap_filters.R.
Boodskapfilterkode:
# ###########################################################
# 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"
}
)
In filters gebruik ons die voorheen geskrewe funksie get_state(), om die huidige toestand van die klets te versoek. Hierdie funksie vereis slegs 1 argument, klets-ID.
Volgende filter wag_naam verwerk boodskappe wanneer die klets in 'n toestand is wait_name, en dienooreenkomstig die filter wag_ouderdom verwerk boodskappe wanneer die klets in 'n toestand is wait_age.
Hanteerders
Die lêer met hanteerders word genoem hanteerders.R, en het die volgende kode:
# ###########################################################
# 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)
Eerstens skep ons opdraghanteerders wat jou sal toelaat om metodes uit te voer om 'n dialoog te begin, dit terug te stel en navraag te doen oor die huidige toestand.
Vervolgens skep ons 2 boodskaphanteerders met behulp van die filters wat in die vorige stap geskep is, en voeg 'n filter by hulle !MessageFilters$command, sodat ons opdragte in enige kletstoestand kan gebruik.
Bot bekendstelling kode
Nou het ons alles gereed om te begin, die hoofkode vir die bekendstelling van die bot is in die lêer 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()
As gevolg hiervan het ons hierdie bot gekry:
Gebruik die opdrag te eniger tyd /state ons kan die huidige kletsstatus navraag doen en die opdrag gebruik /reset bring die klets terug na sy oorspronklike toestand en begin die dialoog weer.
Gevolgtrekking
In hierdie artikel het ons uitgevind hoe om 'n databasis binne 'n bot te gebruik, en hoe om opeenvolgende logiese dialoë te bou deur die kletstoestand op te teken.
In hierdie geval het ons na die mees primitiewe voorbeeld gekyk, sodat dit vir jou makliker sou wees om die idee van die bou van sulke bots te verstaan; in die praktyk kan jy baie meer komplekse dialoë bou.
In die volgende artikel in hierdie reeks sal ons leer hoe om die regte van botgebruikers te beperk om verskeie van sy metodes te gebruik.