Nëse tashmë keni lexuar artikujt e mëparshëm nga kjo seri, atëherë tashmë dini si të shkruani bota të plota në telegram me tastierë.
Në këtë artikull do të mësojmë të shkruajmë një botë që do të mbështesë një dialog të vazhdueshëm. Të thënë ndryshe, bota do t'ju bëjë pyetje dhe do të presë që ju të jepni ndonjë informacion. Në varësi të të dhënave që jepni, bota do të kryejë disa veprime.
Po ashtu, në këtë artikull do të mësojmë të përdorim një bazë të dhënash për botën, në shembullin tonë do të përdorim SQLite, por ju mund të përdorni çdo DBMS tjetër. Më shumë për ndërveprimin me bazat e të dhënave në gjuhën R kam shkruar në .

Të gjitha artikujt nga seria "Shkruajmë bota në telegram me gjuhën R"
Përmbajtja
Nëse jeni të interesuar për analizën e të dhënave, ndoshta do t'ju interesojnë kanalet e mia në dhe Shumica e përmbajtjes së të cilave i kushtohet gjuhës R.
Hyrje
Për të lejuar që bota të kërkojë të dhëna nga ju dhe të presë që të jepni ndonjë informacion, do t'ju duhet të regjistroni gjendjen aktuale të dialogut. Mënyra më e mirë për ta bërë këtë është të përdorni ndonjë bazë të dhënash të inkorporuar, si p.sh. SQLite.
Pra, logjika do të jetë si më poshtë. Ne thërrasim metodën e botës dhe bota pyet rregullisht për ndonjë informacion, duke pritur që në çdo hap të jepet ky informacion dhe duke mundësuar edhe verifikimin e tij.
Ne do të shkruajmë një botë sa më të thjeshtë, fillimisht do të pyesë emrin tuaj, pastaj moshën, të dhënat e marra do t'i ruajë në bazën e të dhënave. Ndërsa kërkon moshën, do të verifikojë që të dhënat e futura të jenë numra dhe jo tekst.
Një dialog i tillë i thjeshtë do të ketë vetëm tre gjendje:
- start — gjendja e zakonshme të botës, në të cilën ajo nuk pret informacione nga ju
- wait_name — gjendja kur bota pret që ju të jepni emrin
- wait_age — gjendja kur bota pret që ju të jepni moshën tuaj, numrin e plotë të viteve.
Procesi i ndërtimit të botës
Gjatë artikullit, ne do të ndjekim hap pas hapi procesin e ndërtimit të një boti, dhe tërë procesi mund të ilustrohet në mënyrë schematike si më poshtë:

- Krijojmë konfigurimin e botit, në të cilin do të ruajmë disa cilësime. Në rastin tonë, token e botit dhe rrugën e skedarit të bazës së të dhënave.
- Krijojmë një variabël mjedisi, në të cilin do të ruhet rruga e projektit me botin.
- Krijojmë vetë bazën e të dhënave dhe një sërë funksionesh për të siguruar që boti të mund të ndërveprojë me të.
- Shkruajmë metodat e botit, pra funksionet që ai do të kryejë.
- Shtojmë filtër për mesazhet, të cilat do të lejojnë botin të thërrasë metodat e duhura, në varësi të gjendjes aktuale të bisedës.
- Shtojmë trajtuesit, të cilët do të lidhin komandat dhe mesazhet me metodat e duhura të botit.
- E nisim botin.
Struktura e projektit të botës
Për lehtësi, ne do ta ndajmë kodin e botit tonë dhe skedarët e tjerë të lidhur me të në strukturën e mëposhtme.
- bot.R — kodi kryesor i botit tonë
- db_bot_function.R — blloku i kodit me funksionet për punë me bazën e të dhënave
- bot_methods.R — kodi i metodave të botit
- message_filters.R — filtrat e mesazheve
- handlers.R — trajtuesit
- config.cfg — konfigurimi i botit
- create_db_data.sql — skripti SQL për krijimin e tabelës me të dhënat e bisedës në bazën e të dhënave
- create_db_state.sql — skripti SQL për krijimin e tabelës së gjendjes aktuale të bisedës në bazën e të dhënave
- bot.db — baza e të dhënave të botit
Projektin e plotë të botit mund ta shikoni ose nga repo imi .
Konfigurimi i botës
Si një konfigurim, ne do të përdorim një , në formën e mëposhtme:
[bot_settings]
bot_token=TOKENI_JUAJ
[db_settings]
db_path=C:/RRUGA/TE/PAPKA/PROJEKTIT/bot.dbNë konfigurim regjistruam tokenin e botit dhe rrugën drejt bazës së të dhënave, pra drejt skedarit bot.db, skedari vetë do ta krijojmë në hapin e ardhshëm.
Për bota më të komplikuar, mund të krijoni dhe konfigurime më të sofistikuara, gjithashtu nuk është e domosdoshme të shkruani pikërisht një konfigurim ini, mund të përdorni çdo format tjetër duke përfshirë JSON.
Krijojmë një variabël ambienti
Në çdo PC, dosja me projektin e botit mund të ndodhet në drejtime të ndryshme dhe në disqe të ndryshme, prandaj në kod rruga drejt dosjes së projektit do të jepet përmes një variabli mjedisor TG_BOT_PATH.
Krijimi i një variabli mjedisor mund të bëhet në disa mënyra, më e thjeshta — ta shkruani atë në skedarin .Renviron.
Krijoni ose redaktoni këtë skedar duke përdorur komandën file.edit(path.expand(file.path("~", ".Renviron"))). Kryeni atë dhe shtoni në skedar një rresht:
TG_BOT_PATH=C:/RRUGA/TË/PROJEKTIT/ tuajMë pas ruani skedarin .Renviron dhe rindezni RStudio.
Krijojmë një bazë të dhënash
Hapi tjetër është krijimi i bazës së të dhënave. Na nevojiten 2 tabela:
- chat_data — të dhënat që boti kërkoi nga përdoruesi
- chat_state — gjendja aktuale e të gjithë bisedave
Këto tabela mund të krijohen me anë të pyetjes SQL më poshtë:
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
);
Nëse keni shkarkuar projektin e botit nga , së pari për krijimin e bazës mund të përdorni kodin më poshtë në gjuhën 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'))
Shkruajmë funksione për të punuar me bazën e të dhënave
Ne tashmë kemi një skedë konfigurimi dhe është krijuar një bazë e dhënash. Tani duhet të shkruajmë funksione për leximin dhe shkrimin e të dhënave në këtë bazë.
Nëse keni shkarkuar projektin nga , funksionet do t'i gjeni në skedën db_bot_function.R.
Kodi i funksioneve për punën me bazën e të dhënave
# ###########################################################
# 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]])
}Ne krijuam 4 funksione të thjeshta:
get_state()— marr gjendjen aktuale të bisedës nga BDset_state()— regjistro gjendjen aktuale të bisedës në BDget_chat_data()— merr të dhënat e dërguara nga përdoruesiset_chat_data()— regjistro të dhënat e marrë nga përdoruesi
Të gjitha funksionet janë mjaft të thjeshta, ato ose lexojnë të dhëna nga baza përmes komandës dbGetQuery(), ose realizojnë UPSERT operacionin (ndryshimi i të dhënave ekzistuese ose regjistrimi i të dhënave të reja në BD), me anë të funksionit dbExecute().
Sintaksa e operacionit UPSERT duket si më poshtë:
INSERT INTO chat_data (chat_id, ${field})
VALUES (${chat_id}, '${value}')
ON CONFLICT(chat_id)
DO UPDATE SET ${field}='${value}';Pra, në tabelat tona, fusha chat_id ka një kufizim për unikallitetin dhe është çelësi kryesor i tabelave. Fillimisht përpiqemi të shtojmë informacion në tabelë dhe marrim një gabim nëse të dhënat për bisedën aktuale janë tashmë të pranishme, në këtë rast ne thjesht përditësojmë informacionin për këtë bisedë.
Më pas, këto funksione do t'i përdorim në metodat dhe filtrat e botit.
Metodat e botës
Hapi tjetër në ndërtimin e botit tonë do të jetë krijimi i metodave. Nëse keni shkarkuar projektin nga , të gjitha metodat ndodhen në skedën bot_methods.R.
Kodi i metodave të botit
# ###########################################################
# 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')
}
}Ne krijuam 5 metoda:
- start — Nis bisedën
- state — Merr gjendjen aktuale të bisedës
- reset — Riset gjendja aktuale e bisedës
- enter_name — Boti kërkon emrin tuaj
- enter_age — Boti kërkon moshën tuaj
Sanitizer.replaceElementWithChildren() start i kërkon emrin tuaj dhe e kalon gjendjen e bisedës në wait_name, pra, në një mënyrë pritjeje për futjen e emrit tuaj.
Më pas, ju dërgoni emrin dhe ai përpunoset nga metoda enter_name, boti ju përshëndet, regjistron emrin e marrë në bazë dhe e kalon bisedën në gjendjen wait_age.
Në këtë etapë, boti po pret që ju të jepni moshën tuaj. Ju dërgoni moshën tuaj, boti kontrollon mesazhin, nëse dërgoni ndonjë tekst në vend të numrit, ai do të thotë: Keni futur të dhëna të papërshtatshme, ju lutem jepni një numër, dhe do të presë nga ju një ri-input të të dhënave. Në rast se dërgoni një numër, boti do t'ju njoftojë se ka pranuar moshën tuaj, do të regjistrojë të dhënat e marra dhe do t'ju tregojë të gjitha të dhënat që keni dërguar, duke e rikthyer gjendjen e bisedës në pozitat fillestare, dmth në start.
Duke thirrur metodën state ju në çdo moment mund të kërkoni gjendjen aktuale të bisedës, dhe me metodën reset të riktheni bisedën në gjendjen origjinale.
Filtro të mesazheve
Në rastin tonë, kjo është një nga pjesët më të rëndësishme në ndërtimin e botit. Pikërisht me ndihmën e filtrave të mesazheve, boti do të kuptojë çfarë informacionesh po pret nga ju dhe si duhet t'i përpunojë ato.
Në projektin në filtrat janë përcaktuar në skedarin message_filters.R.
Kodi i filtrave të mesazheve:
# ###########################################################
# 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"
}
)Në filtrat ne përdorim funksionin e shkruar më parë get_state(), për të kërkuar gjendjen aktuale të bisedës. Ky funksion kërkon vetëm 1 argument, id-në e bisedës.
Më pas, filtri wait_name përpunon mesazhet kur biseda është në gjendjen wait_name, dhe përkatësisht, filtri wait_age përpunon mesazhet kur biseda është në gjendjen wait_age.
Trajtuesit
Skedari me përpunuesit quhet handlers.R, dhe ka këtë 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)Së pari krijojmë përpunuesit e komandave, të cilat do t'ju lejojnë të nisni metoda për fillimin e dialogut, rivendosjen e tij, dhe kërkimin e gjendjes aktuale.
Më pas krijojmë 2 përpunues mesazhesh duke përdorur filtrat e krijuar në hapin e kaluar, dhe i shtojmë filtrin !MessageFilters$command, për të siguruar që mund të përdorim komandat në çdo gjendje të bisedës.
Kodi për të filluar botën
Tani gjithçka është gati për nisje, kodi kryesor i nisjes së botit është në skedarin bot.R.
library(telegram.bot)
library(tidyverse)
library(RSQLite)
library(DBI)
library(configr)
# kalojmë në folderin e projektit
setwd(Sys.getenv('TG_BOT_PATH'))
# lexojmë konfigurimin
cfg <- read.config('config.cfg')
# krijojmë një instancë të botit
updater <- Updater(cfg$bot_settings$bot_token)
# Ngarkimi i komponenteve të botit
source('db_bot_function.R') # funksione për punë me DB
source('bot_methods.R') # metodat e botit
source('message_filters.R') # filtrat e mesazheve
source('handlers.R') # përpunuesit e mesazheve
# Shtojmë përpunuesit në menaxher
updater <- updater +
start_h +
wait_age_h +
wait_name_h +
state_h +
reset_h
# Nisnim botin
updater$start_polling()Si rezultat, na doli ky bot:

Në çdo moment ne mund të kërkojmë gjendjen aktuale të bisedës, me anë të komandës /state мы можем запрашивать текущее состояние чата, а с помощью команды /reset rikthe bisedën në gjendjen e saj origjinale dhe filloni një dialog të ri.
Përfundim
Në këtë artikull, ne shqyrtuam se si të përdorim bazat e të dhënave brenda botit, dhe si të ndërtosh dialoge logjike të njëpasnjëshme duke ruajtur gjendjen e bisedës.
Në këtë rast, ne shqyrtuam shembullin më primitiv, për të ndihmuar në kuptimin e idesë së ndërtimit të tillë të botëve; në praktikë mund të ndërtosh dialoge shumë më të komplikuara.
Në artikullin e ardhshëm të kësaj serie, do të mësojmë si të kufizojmë përdoruesit e botit në përdorimin e metodave të ndryshme.
Burimi: habr.com
