Nëse tashmë keni lexuar artikujt e mëparshëm të kësaj serie, atëherë tashmë dini si të shkruani bota Telegram të plota me tastierë.
Në këtë artikull do të mësojmë si të shkruajmë një bot që do të mbështesë një dialog në vazhdim. Kështu, boti do t'ju bëjë pyetje dhe do të presë përgjigjen tuaj. Në varësi të informacioneve që jepni, boti do të kryejë disa veprime.
Gjithashtu, në këtë artikull do të mësojmë si të përdorim një bazë të dhënash pas kulisave të botit, në shembullin tonë do të përdorim SQLite, por mund të përdorni çdo sistem tjetër të menaxhimit të të dhënave. Më shumë informacion rreth bashkëpunimit me bazat e të dhënave në gjuhën R kam shkruar në .

Të gjithë artikujt e serisë "Po shkruajmë një bot telegram në 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 dhe në të cilat shumica e përmbajtjes është e kushtuar gjuhës R.
Hyrje
Për të lejuar që boti të mund të kërkojë të dhëna nga ju dhe të presë përgjigje, do t'ju nevojitet të regjistroni gjendjen aktuale të dialogut. Mënyra më e mirë për ta bërë këtë është të përdorni një bazë të integruar të dhënash, siç është SQLite.
Domethënë, logjika do të jetë si në vijim. Ne thërrasim metodën e botit, dhe boti me radhë do të kërkojë disa informacione nga ne, duke pritur përgjigjen në çdo hap, si dhe duke kryer verifikimin e saj.
Ne do të shkruajmë një bot sa më të thjeshtë, fillimisht ai do të kërkojë emrin tuaj, pastaj moshën, dhe të dhënat e marra do të ruhen në bazën e të dhënave. Në kërkesën për moshën do të verifikojë që të dhënat e futura janë numra, jo tekst.
Një dialog i tillë do të ketë vetëm tre gjendje:
- start — gjendja e zakonshme e botit, në të cilën ai nuk pret informacione nga ju
- wait_name — gjendja në të cilën boti pret që të shkruani emrin
- wait_age — gjendja në të cilën boti pret që të shkruani moshën tuaj, numrin e plotë të viteve.
Procesi i ndërtimit të botit
Në përfundim të artikullit ne do ta ndërtojmë boti hap pas hapi, dhe e gjithë procedura mund të përshkruhet në mënyrë grafike si më poshtë:

- Krijoni një konfigurim të 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.
- Krijoni një variabël mjedisi, në të cilin do të ruhet rruga e projektit me botin.
- Krijoni vetë bazën e të dhënave dhe një sërë funksionesh në mënyrë që boti të mund të bashkëpunojë me të.
- Shkruajmë metodat e botit, domethënë funksionet që ai do të kryejë.
- Shtojmë filtrat e mesazheve. Me ndihmën e tyre boti do të lidhet me metodat e nevojshme, në varësi të gjendjes aktuale të bisedës.
- Shtojmë trajtuesit, të cilët do të lidhin komandat dhe mesazhet me metodat e nevojshme të botit.
- Nisni botin.
Struktura e projektit të botit
Për lehtësinë e përdorimit, ne do ta ndajmë kodin e botit tonë dhe skedarët e 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 të punuar 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 — skenari 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 — skenari 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 e botit
E gjithë projekti i botit mund të shihet, ose nga unë .
Konfiguro boti
Si konfigurim do të përdorim një të zakonshëm , në formën në vazhdim:
[bot_settings]
bot_token=TOKEN_I_BOTIT_TUAJ
[db_settings]
db_path=C:/RRUGA/KU/PAPKA/PROJEKTIT/bot.dbNë konfigurim regjistrojmë token e botit dhe rrugën e bazës së të dhënave, domethënë skedarin bot.db, skedari do të krijohet në hapin tjetër.
Për bota më të komplikuara, mund të krijoni dhe më të ndërlikuar, për më tepër nuk është e domosdoshme të shkruani saktësisht konfigurimin ini, mund të përdorni çdo format tjetër, duke përfshirë JSON.
Krijo një variabël mjedisi
Në çdo PC, carpeta e projektit të botit mund të ndodhet në drejtime të ndryshme dhe në disqe të ndryshme, prandaj në kod, rruga drejt carpeta e projektit do të jepet përmes një varianti mjedisi TG_BOT_PATH.
Krijimi i një variabli mjedisi mund të bëhet në disa mënyra, mënyra më e lehtë është ta regjistroni atë në skedarin .Renviron.
Krijo ose redakto këtë skedar duke përdorur komandën file.edit(path.expand(file.path("~", ".Renviron"))). Ekzekutoni atë dhe shtoni në skedar një rresht:
TG_BOT_PATH=C:/RRUGA/KU/PAPKA/PROJEKTITMë pas ruani skedarin .Renviron dhe rinisni RStudio.
Krijo bazën e të dhënave
Hapi tjetër — krijimi i bazës së të dhënave. Na duhen 2 tabela:
- chat_data — të dhënat që boti ka kërkuar nga përdoruesi
- chat_state — gjendja aktuale e të gjitha bisedave
Këto tabela mund të krijohen me anë të kërkesës SQL të mëposhtme:
KRIJONI TABELË chat_data (
chat_id BIGINT PRIMARY KEY
UNIKE,
name TEXT,
age INTEGER
);
KRIJONI TABELË chat_state (
chat_id BIGINT PRIMARY KEY
UNIKE,
state TEXT
);
Nëse keni shkarkuar projektin e botit nga , mund të përdorni kodin e mëposhtëm në gjuhën R për të krijuar bazën.
# Скрипт создания базы данных
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'))
Shkruaj funksione për të punuar me bazën e të dhënave
Tani kemi dosjen e konfigurimit gati dhe baza e të dhënave është krijuar. Tani duhet të shkruajmë funksionet për të lexuar dhe shkruar të dhëna në këtë bazë.
Nëse e keni shkarkuar projektin nga , funksionet mund t’i gjeni në dosjen 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 kemi krijuar 4 funksione të thjeshta:
get_state()— merr gjendjen aktuale të bisedës nga Baza e të Dhënaveset_state()— shkruan gjendjen aktuale të bisedës në Bazën e të Dhënaveget_chat_data()— merr të dhënat e dërguara nga përdoruesiset_chat_data()— shkruan të dhënat e marra nga përdoruesi
Të gjitha funksionet janë mjaft të thjeshta, ato ose lexojnë të dhëna nga baza përmes komandës dbGetQuery(), ose kryejnë UPSERT operasyon (ndryshimi i të dhënave ekzistuese ose shkruarja e të dhënave të reja në Baza), përmes 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}';Kështu, në tabelat tona, fusha chat_id ka kufizim unik dhe është çelësi kryesor i tabelave. Në fillim, ne përpiqemi të shtojmë informacionin në tabelë dhe marrëve hata nëse të dhënat për bisedën aktuale janë tashmë të pranishme, në atë rast ne thjesht përditësojmë informacionin për këtë bisedë.
Më vonë, këto funksione do t'i përdorim në metodat dhe filtrat e botit.
Metodat e botit
Hapi tjetër për ndërtimin e botit tonë do të jetë krijimi i metodave. Nëse e keni shkarkuar projektin nga , të gjitha metodat ndodhen në dosjen 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 kemi krijuar 5 metoda:
- start — Aktivizo dialogun
- state — Merr gjendjen aktuale të bisedës
- reset — Rindërto gjendjen aktuale të bisedës
- enter_name — Boti kërkon emrin tuaj
- enter_age — Boti kërkon moshën tuaj
Metoda start kërkon emrin tuaj dhe e kalon gjendjen e bisedës në wait_name, domethënë, në modus pritjeje për të dhënë emrin tuaj.
Më pas, ju dërgoni emrin dhe ai përpunsohet 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 pret që ju të jepni moshën tuaj. Ju dërgoni moshën tuaj, boti kontrollon mesazhin, nëse keni dërguar një tekst në vend të një numri, ai do të thotë: Keni dhënë të dhëna të papërshtatshme, jepni një numër, dhe do të presë për një dërgim të përsëritur të të dhënave. Nëse dërgoni një numër, boti do të njoftojë se ka pranuar moshën tuaj, do të regjistrojë të dhënat e marra në bazë, do të njoftojë të gjitha të dhënat që ka marrë nga ju dhe do ta kthejë gjendjen e bisedës në pozitat fillestare, dmth në start.
Duke thirrur metodën gjendja ju në çdo moment mund të kërkoni gjendjen aktuale të bisedës, ndërsa me metodën rishtim ta ktheni bisedën në gjendjen fillestare.
Filtrat e mesazheve
Në rastin tonë, kjo është një nga pjesët më të rëndësishme në ndërtimin e botit. Pikërisht përmes filtrave të mesazheve, boti do të kuptojë se çfarë informacioni pret nga ju dhe se si duhet ta përpunojë atë.
Në projektin në filtrat janë të regjistruar në dosjen 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ë filtra ne përdorim funksionin e shkruar më parë get_state(), për t'u kërkuar gjendjen aktuale të bisedës. Ky funksion kërkon vetëm 1 argument, id e bisedës.
Më pas, filtri wait_name përpunon mesazhet kur biseda ndodhet në gjendjen wait_name, dhe përkatësisht filtri wait_age përpunon mesazhet kur biseda ndodhet në gjendjen wait_age.
Trajtuesit
Skedi me trajtuesit quhet handlers.R, dhe ka kodin e mëposhtëm:
# ###########################################################
# 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)Në fillim, ne krijojmë trajtuesit e komandave që do t'ju lejojnë të aktivizoni metodat për të filluar dialogun, për ta rikthyer atë, dhe për të kërkuar gjendjen aktuale.
Më pas krijojmë 2 trajtues të mesazheve duke përdorur filtrat e krijuar në hapin e kaluar dhe u shtojmë filtrin !MessageFilters$command, që të mund të përdorim komanda në çdo gjendje bisedash.
Kodi për nisjen e botit
Tani kemi gjithçka gati për aktivizim, kodi kryesor për aktivizimin e botit ndodhet në dosjen bot.R.
library(telegram.bot)
library(tidyverse)
library(RSQLite)
library(DBI)
library(configr)
# kalojmë në dosjen e projektit
setwd(Sys.getenv('TG_BOT_PATH'))
# lexojmë konfigurimin
cfg <- read.config('config.cfg')
# krijojmë një shembull të botit
updater <- Updater(cfg$bot_settings$bot_token)
# Ngarko komponentët e botit
source('db_bot_function.R') # funksionet për punën me B.D.
source('bot_methods.R') # metodat e botit
source('message_filters.R') # filtrat e mesazheve
source('handlers.R') # trajtuesit e mesazheve
# Shtojmë trajtuesit në menaxherin
updater <- updater +
start_h +
wait_age_h +
wait_name_h +
state_h +
reset_h
# Aktivizojmë botin
updater$start_polling()Si rezultat, ne morëm një bot të tillë:

Në çdo moment, me ndihmën e komandës /state ne mund të kërkojmë gjendjen aktuale të bisedës, dhe me ndihmën e komandës /reset kemi mundësinë të rikthejmë bisedën në gjendjen fillestare dhe të fillojmë dialogun nga e para.
Përfundimi
Në këtë artikull, ne kuptuam se si të përdorim bazat e të dhënave brenda botit dhe si të ndërtojmë biseda logjike të rregullta përmes ruajtjes së gjendjes së bisedës.
Në këtë rast, ne shqyrtuam një shembull shumë primitiv, për ta bërë më të lehtë kuptimin e ideve për ndërtimin e tillë të botëve; në praktikë, ju mund të ndërtoni biseda shumë më të tjera komplekse.
Në artikullin e ardhshëm të kësaj serie do të mësojmë si të kufizojmë të drejtat e përdoruesve të botit për të përdorur metoda të ndryshme.
Burimi: habr.com
