ProHoster > Blog > uprava > Pisanje bota za telegram u R (dio 4): Izgradnja dosljednog, logičnog dijaloga s botom
Pisanje bota za telegram u R (dio 4): Izgradnja dosljednog, logičnog dijaloga s botom
Ako ste već pročitali prethodni tri članka iz ove serije, onda već znate kako pisati potpune telegram botove s tipkovnicom.
U ovom ćemo članku naučiti kako napisati bota koji će održavati dosljedan dijalog. Oni. Bot će vam postavljati pitanja i čekati da unesete neke podatke. Ovisno o podacima koje unesete, bot će izvršiti neke radnje.
Također u ovom članku naučit ćemo kako koristiti bazu podataka ispod poklopca bota, u našem primjeru to će biti SQLite, ali možete koristiti bilo koji drugi DBMS. O interakciji s bazama podataka u jeziku R pisao sam detaljnije u ovaj članak.
Kako bi bot od vas tražio podatke i čekao da unesete bilo kakve podatke, morat ćete zabilježiti trenutno stanje dijaloga. Najbolji način da to učinite je korištenje neke vrste ugrađene baze podataka, kao što je SQLite.
Oni. Logika će biti sljedeća. Pozivamo bot metodu, a bot sekvencijalno od nas traži neke podatke, te u svakom koraku čeka da se ti podaci unesu i može ih provjeriti.
Napisat ćemo najjednostavniji mogući bot, prvo će vas pitati za ime, zatim vaše godine, te će primljene podatke spremiti u bazu. Prilikom upita za dob, provjerit će jesu li uneseni podaci broj, a ne tekst.
Takav jednostavan dijalog imat će samo tri stanja:
start je normalno stanje bota, u kojem ne očekuje nikakve informacije od vas
wait_name - stanje u kojem bot čeka da se unese ime
wait_age je stanje u kojem bot čeka da se upiše vaša dob, broj punih godina.
Proces izgradnje bota
Tijekom članka gradit ćemo bot korak po korak; cijeli proces može se shematski prikazati na sljedeći način:
Kreiramo konfiguraciju bota u koju ćemo pohraniti neke postavke. U našem slučaju token bota i put do datoteke baze podataka.
Kreiramo varijablu okoline u kojoj će biti pohranjena staza do projekta s botom.
Izrađujemo samu bazu podataka i brojne funkcije kako bi bot mogao komunicirati s njom.
Pišemo bot metode, tj. funkcije koje će obavljati.
Dodavanje filtera poruka. Uz pomoć kojih će bot pristupiti potrebnim metodama, ovisno o trenutnom stanju chata.
Dodajemo rukovatelje koji će povezivati naredbe i poruke s potrebnim bot metodama.
Pokrenimo bot.
Struktura bot projekta
Radi praktičnosti, podijelit ćemo kod našeg bota i druge povezane datoteke u sljedeću strukturu.
bot.R — glavni kod našeg bota
db_bot_funkcija.R — blok koda s funkcijama za rad s bazom podataka
bot_methods.R — kod bot metoda
filtri_poruka.R — filteri poruka
rukovatelji.R - rukovatelji
config.cfg - konfiguracija bota
create_db_data.sql — SQL skripta za izradu tablice s chat podacima u bazi podataka
create_db_state.sql — SQL skripta za kreiranje tablice trenutnog stanja chata u bazi podataka
U konfiguraciji pišemo token bota i put do baze podataka, tj. u datoteku bot.db; kreirat ćemo samu datoteku u sljedećem koraku.
Za složenije botove možete izraditi složenije konfiguracije, osim toga, nije potrebno pisati ini konfiguraciju, možete koristiti bilo koji drugi format uključujući JSON.
Stvorite varijablu okoline
Na svakom računalu, mapa s projektom bota može se nalaziti u različitim direktorijima i na različitim pogonima, tako da će u kodu put do mape projekta biti postavljen preko varijable okruženja TG_BOT_PATH.
Postoji nekoliko načina za kreiranje varijable okoline, najjednostavniji je da je zapišete u datoteku .Renviron.
Ovu datoteku možete izraditi ili urediti pomoću naredbe file.edit(path.expand(file.path("~", ".Renviron"))). Izvršite ga i dodajte jedan red u datoteku:
TG_BOT_PATH=C:/ПУТЬ/К/ВАШЕМУ/ПРОЕКТУ
Zatim spremite datoteku .Renviron i ponovno pokrenite RStudio.
Izrada baze podataka
Sljedeći korak je izrada baze podataka. Trebat će nam 2 tablice:
chat_data — podaci koje je bot tražio od korisnika
chat_state — trenutno stanje svih chatova
Ove tablice možete izraditi pomoću sljedećeg SQL upita:
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
);
Ako ste preuzeli projekt bota s GitHub, tada za stvaranje baze podataka možete koristiti sljedeći kod u 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'))
Pisanje funkcija za rad s bazom podataka
Već imamo spremnu konfiguracijsku datoteku i kreiranu bazu podataka. Sada trebate napisati funkcije za čitanje i pisanje podataka u ovu bazu podataka.
Ako ste projekt preuzeli s GitHub, tada možete pronaći funkcije u datoteci db_bot_funkcija.R.
Funkcijski kod za rad s bazom podataka
# ###########################################################
# 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]])
}
Napravili smo 4 jednostavne funkcije:
get_state() — dobiti trenutno stanje chata iz baze podataka
set_state() — zapišite trenutno stanje razgovora u bazu podataka
get_chat_data() — primanje podataka koje šalje korisnik
set_chat_data() — zapis podataka primljenih od korisnika
Sve su funkcije vrlo jednostavne, one ili čitaju podatke iz baze pomoću naredbe dbGetQuery(), ili počiniti UPSERT operacija (promjena postojećih podataka ili upisivanje novih podataka u bazu), korištenje funkcije dbExecute().
Sintaksa za operaciju UPSERT je sljedeća:
INSERT INTO chat_data (chat_id, ${field})
VALUES(${chat_id}, '${value}')
ON CONFLICT(chat_id)
DO UPDATE SET ${field}='${value}';
Oni. u našem polju tablica chat_id ima ograničenje jedinstvenosti i primarni je ključ tablica. U početku pokušavamo dodati informacije u tablicu i dobivamo pogrešku ako su podaci za trenutni chat već prisutni, u kojem slučaju jednostavno ažuriramo informacije za ovaj chat.
Zatim ćemo koristiti ove funkcije u metodama i filtrima bota.
Botovske metode
Sljedeći korak u izgradnji našeg bota je stvaranje metoda. Ako ste projekt preuzeli s GitHub, tada su sve metode u datoteci bot_methods.R.
Kod metode bota
# ###########################################################
# 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')
}
}
Kreirali smo 5 metoda:
početak — Pokrenite dijaloški okvir
stanje — Dobijte trenutno stanje razgovora
reset — Ponovno postavite trenutno stanje chata
enter_name — Bot traži vaše ime
enter_age — Bot traži vaše godine
način start traži vaše ime i mijenja stanje razgovora u čekaj_ime, tj. na čekanje za unos vašeg imena.
Zatim šaljete ime i ono se obrađuje metodom enter_name, pozdravlja vas bot, upisuje primljeno ime u bazu i prebacuje chat u stanje dob_čekanja.
U ovoj fazi bot očekuje da unesete svoju dob. Pošalješ svoje godine, bot provjeri poruku, ako si poslao tekst umjesto broja, reći će: Ты ввёл некорректные данные, введи число, te će čekati da ponovno unesete svoje podatke. Ako ste poslali broj, bot će javiti da je prihvatio vašu dob, upisati primljene podatke u bazu podataka, prijaviti sve podatke primljene od vas i vratiti stanje chata u prvobitni položaj, tj. V start.
Pozivom metode state možete zatražiti trenutni status chata u bilo kojem trenutku, a pomoću reset vratite chat u prvobitno stanje.
Filtri poruka
U našem slučaju, ovo je jedan od najvažnijih dijelova u izgradnji bota. Upravo uz pomoć filtera poruka bot će shvatiti koje informacije očekuje od vas i kako ih treba obraditi.
U projektu na GitHub filtri su registrirani u datoteci filtri_poruka.R.
Kôd filtra poruka:
# ###########################################################
# 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"
}
)
U filterima koristimo prethodno napisanu funkciju get_state(), kako biste zatražili trenutno stanje chata. Ova funkcija zahtijeva samo 1 argument, ID chata.
Sljedeći filter čekaj_ime obrađuje poruke kada je chat u stanju wait_name, a shodno tome i filter dob_čekanja obrađuje poruke kada je chat u stanju wait_age.
Rukovatelji
Poziva se datoteka s rukovateljima rukovatelji.R, i ima sljedeći kod:
# ###########################################################
# 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)
Prvo stvaramo rukovatelje naredbama koji će vam omogućiti pokretanje metoda za pokretanje dijaloškog okvira, njegovo ponovno postavljanje i ispitivanje trenutnog stanja.
Zatim stvaramo 2 rukovatelja porukama pomoću filtara stvorenih u prethodnom koraku i dodajemo im filtar !MessageFilters$command, tako da možemo koristiti naredbe u bilo kojem stanju chata.
Kod za pokretanje bota
Sada imamo sve spremno za pokretanje, glavni kod za pokretanje bota je u datoteci 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()
Kao rezultat, dobili smo ovog bota:
U bilo kojem trenutku pomoću naredbe /state možemo ispitati trenutno stanje chata i pomoću naredbe /reset vratite chat u prvobitno stanje i ponovno pokrenite dijalog.
Zaključak
U ovom smo članku shvatili kako koristiti bazu podataka unutar bota i kako izgraditi sekvencijalne logičke dijaloge snimanjem stanja chata.
U ovom slučaju, pogledali smo najprimitivniji primjer, kako biste lakše razumjeli ideju izgradnje takvih botova; u praksi možete izgraditi mnogo složenije dijaloge.
U sljedećem članku u ovoj seriji naučit ćemo kako ograničiti prava korisnicima bota da koriste razne njegove metode.