ProHoster > Blog > Amministrazione > Scrivite un telegramma bot in R (parte 4): Custruì un dialogu coherente è logicu cù u bot
Scrivite un telegramma bot in R (parte 4): Custruì un dialogu coherente è logicu cù u bot
Sè avete digià lettu u precedente trè articuli da sta serie, allora sapete digià cumu scrive bots di telegramma cumpletu cù un teclatu.
In questu articulu, ampararemu à scrive un bot chì mantene un dialogu coherente. Quelli. U bot vi farà dumande è aspittà per voi per entre qualchì infurmazione. Sicondu i dati chì inserite, u bot farà alcune azzioni.
Ancu in questu articulu avemu da amparà cumu utilizà una basa di dati sottu u cappucciu di u bot, in u nostru esempiu serà SQLite, ma pudete aduprà qualsiasi altru DBMS. Aghju scrittu in più dettagliu nantu à l'interazzione cù e basa di dati in a lingua R in stu articulu.
Tutti l'articuli di a serie "Scrivu un telegram bot in R"
Sè vo site interessatu in l'analisi di dati, pudete esse interessatu in u mo telegram и fratii canali. A maiò parte di u cuntenutu hè dedicatu à a lingua R.
In ordine per u bot à dumandà dati da voi è aspittà per voi à entre ogni infurmazione, vi tuccherà à registrà u statu attuale di u dialogu. U megliu modu per fà questu hè di utilizà un tipu di basa di dati integrata, cum'è SQLite.
Quelli. A logica serà a siguenti. Chjamemu u metudu di u bot, è u bot sequentially dumanda qualchì infurmazione da noi, è à ogni passu aspetta chì sta informazione sia inserita è pò verificà.
Scriveremu u bot più simplice pussibule, prima dumandarà u vostru nome, dopu a vostra età, è salverà e dati ricevuti in a basa di dati. Quandu dumandate l'età, verificarà chì i dati inseriti sò un numeru è micca un testu.
Un tali dialogu simplice averà solu trè stati:
iniziu hè u statu normale di u bot, in quale ùn aspetta micca infurmazione da voi
wait_name - statu in quale u bot aspetta chì un nome sia inseritu
wait_age hè u statu in quale u bot aspetta chì a vostra età sia inserita, u numeru di anni sanu.
U prucessu di custruzione di u bot
Duranti l'articulu, custruiremu un bot passu à passu, tuttu u prucessu pò esse schematicamente illustratu cum'è:
Creemu una cunfigurazione di bot in quale guardemu alcune paràmetri. In u nostru casu, u bot token, è u percorsu à u schedariu di basa di dati.
Creemu una variabile d'ambiente in quale u percorsu à u prugettu cù u bot serà guardatu.
Creemu a basa di dati stessu, è una quantità di funzioni per chì u bot pò interagisce cun ellu.
Scrivemu i metudi di bot, i.e. e funzioni chì hà da esse realizatu.
Aghjunghje i filtri di messagi. Cù l'aiutu di quale u bot accede à i metudi necessarii, secondu u statu attuale di u chat.
Aghjunghjemu handlers chì cunnetteranu cumandamenti è missaghji cù i metudi di bot necessarii.
Lanciamu u bot.
Struttura di u prugettu Bot
Per comodità, divideremu u codice di u nostru bot, è altri schedarii cunnessi, in a struttura seguente.
bot.R - u codice principale di u nostru bot
db_bot_function.R - un bloccu di codice cù funzioni per travaglià cù a basa di dati
bot_methods.R - codice di i metudi di bot
message_filters.R - filtri di messagi
i maniscalchi.R - gestori
config.cfg - bot config
create_db_data.sql - Script SQL per creà una tabella cù dati di chat in a basa di dati
create_db_state.sql - Script SQL per creà una tabella di u statu di chat attuale in a basa di dati
In a cunfigurazione scrivemu u token di bot è u percorsu à a basa di dati, i.e. à u schedariu bot.db avemu da creà u schedariu stessu in u prossimu passu.
Per i bots più cumplessi, pudete creà cunfigurazioni più cumplessi, in più, ùn hè micca necessariu di scrive un ini config, pudete aduprà qualsiasi altru formatu cumpresu JSON.
Crea una variabile d'ambiente
In ogni PC, u cartulare cù u prughjettu di u bot pò esse situatu in diversi cartulari è in diverse unità, cusì in u codice u percorsu à u cartulare di u prughjettu serà stabilitu via una variabile di l'ambiente. TG_BOT_PATH.
Ci hè parechje manere di creà una variabile d'ambiente, u più simplice hè di scrive in un schedariu .Renvironment.
Pudete creà o edità stu schedariu cù u cumandimu file.edit(path.expand(file.path("~", ".Renviron"))). Eseguite è aghjunghje una linea à u schedariu:
TG_BOT_PATH=C:/ПУТЬ/К/ВАШЕМУ/ПРОЕКТУ
Dopu salvà u schedariu .Renvironment è riavvia RStudio.
Crià una basa di dati
U prossimu passu hè di creà una basa di dati. Avemu bisognu di 2 tavule:
chat_data - dati chì u bot hà dumandatu da l'utilizatore
chat_state - u statu attuale di tutti i chats
Pudete creà sti tavule cù a seguente query SQL:
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
);
Se avete scaricatu u prughjettu di u bot da GitHub, dopu per creà a basa di dati pudete aduprà u codice seguente in 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'))
Funzioni di scrittura per travaglià cù a basa di dati
Avemu digià un schedariu di cunfigurazione pronta è una basa di dati creata. Avà avete bisognu di scrive funzioni per leghje è scrive dati à sta basa di dati.
Sè avete scaricatu u prugettu da GitHub, allura pudete truvà e funzioni in u schedariu db_bot_function.R.
Codice di funzione per travaglià cù a basa di dati
# ###########################################################
# 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]])
}
Avemu creatu 4 funzioni simplici:
get_state() - uttene u statu di chat attuale da a basa di dati
set_state() - scrivite u statu di chat attuale à a basa di dati
get_chat_data() - riceve dati mandati da l'utilizatore
set_chat_data() - arregistrà i dati ricevuti da l'utilizatore
Tutte e funzioni sò abbastanza sèmplice, o leghjenu dati da a basa di dati cù u cumandimu dbGetQuery(), o impegnà UPSERT operazione (cambià dati esistenti o scrivite novi dati à a basa di dati), utilizendu a funzione dbExecute().
A sintassi per l'operazione UPSERT hè a siguenti:
INSERT INTO chat_data (chat_id, ${field})
VALUES(${chat_id}, '${value}')
ON CONFLICT(chat_id)
DO UPDATE SET ${field}='${value}';
Quelli. in u nostru campu di tavule chat_id hà una limitazione di unicità è hè a chjave primaria di e tavule. In principiu, pruvemu à aghjunghje infurmazione à a tavula, è avemu un errore se i dati per u chat attuale sò digià presente, in quale casu avemu solu aghjurnà l'infurmazioni per questa chat.
Dopu, avemu aduprà sti funzioni in i metudi di u bot è filtri.
I metudi di bot
U prossimu passu à custruisce u nostru bot hè di creà metudi. Sè avete scaricatu u prugettu da GitHub, allura tutti i metudi sò in u schedariu bot_methods.R.
Codice di u metudu Bot
# ###########################################################
# 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')
}
}
Avemu creatu 5 metudi:
start - Start un dialogu
state - Ottene u statu di chat attuale
reset - Resetta u statu di chat attuale
enter_name - U bot dumanda u vostru nome
enter_age - U bot dumanda a vostra età
Metu start dumanda u vostru nome, è cambia u statu di chat à wait_name, i.e. in standby per inserisce u vostru nome.
In seguitu, mandate u nome è hè trattatu da u metudu enter_name, u bot ti saluta, scrive u nome ricevutu in a basa di dati, è cambia u chat à u statu aspetta_età.
À questu stadiu, u bot aspetta chì entre in a vostra età. Mandate a vostra età, u bot cuntrolla u missaghju, se avete mandatu qualchì testu invece di un numeru, dicerà: Ты ввёл некорректные данные, введи число, è vi aspittà per voi à ritruvà i vostri dati. Se avete mandatu un numeru, u bot hà dettu chì hà accettatu a vostra età, scrivite i dati ricivuti à a basa di dati, rappurtate tutti i dati ricevuti da voi è torna u statu di chat à a so pusizioni originale, i.e. V start.
Chjamendu u metudu state pudete dumandà u statu di chat attuale in ogni mumentu, è utilizendu u reset torna u chat à u so statu originale.
Filtri di messagi
In u nostru casu, questu hè una di e parti più impurtanti in a custruzione di un bot. Hè cù l'aiutu di i filtri di missaghju chì u bot hà da capisce quale infurmazione aspetta da voi è cumu si deve esse trattatu.
In u prughjettu nantu GitHub i filtri sò registrati in u schedariu message_filters.R.
Codice di filtru di messagiu:
# ###########################################################
# 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 i filtri usemu a funzione scritta prima get_state(), per dumandà u statu attuale di u chat. Questa funzione richiede solu 1 argumentu, chat id.
Filtru prossimu wait_name processa missaghji quandu u chat hè in un statu wait_name, è dunque u filtru aspetta_età processa missaghji quandu u chat hè in un statu wait_age.
Manipulatori
U schedariu cù handlers hè chjamatu i maniscalchi.R, è hà u codice seguente:
# ###########################################################
# 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)
Prima avemu criatu cumandanti di cumandamenti chì vi permettenu di eseguisce metudi per inizià un dialogu, resettallu, è dumandate u statu attuale.
In seguitu, creamu 2 gestori di missaghju cù i filtri creati in u passu precedente, è aghjunghje un filtru à elli !MessageFilters$command, cusì chì pudemu usà cumandamenti in ogni statu di chat.
Bot codice di lanciamentu
Avà avemu tuttu prontu per lancià, u codice principale per lancià u bot hè in u schedariu 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()
In u risultatu, avemu avutu stu bot:
In ogni mumentu usendu u cumandamentu /state pudemu interrogà u statu di chat attuale, è usendu u cumandamentu /reset torna u chat à u so statu uriginale è principià u dialogu novu.
cunchiusioni
In questu articulu, avemu capitu cumu utilizà una basa di dati in un bot, è cumu custruisce dialoghi lògichi sequenziali arregistrendu u statu di chat.
In questu casu, avemu vistu l'esempiu più primitivu, perchè esse più faciule per voi per capiscenu l'idea di custruisce tali bots in pratica, pudete custruisce dialoghi assai più cumplessi.
In u prossimu articulu in questa serie, avemu da amparà à limità i diritti di l'utilizatori di u bot per utilizà diversi di i so metudi.