ProHoster > Log > administrasjon > Skrive en telegram-bot i R (del 4): Bygge en konsistent, logisk dialog med boten
Skrive en telegram-bot i R (del 4): Bygge en konsistent, logisk dialog med boten
Hvis du allerede har lest forrige tre artikler fra denne serien, så vet du allerede hvordan du skriver fullverdige telegramroboter med et tastatur.
I denne artikkelen vil vi lære hvordan du skriver en bot som vil opprettholde en konsistent dialog. De. Boten vil stille deg spørsmål og vente på at du legger inn informasjon. Avhengig av dataene du legger inn, vil boten utføre noen handlinger.
Også i denne artikkelen vil vi lære hvordan du bruker en database under panseret til boten, i vårt eksempel vil det være SQLite, men du kan bruke hvilken som helst annen DBMS. Jeg skrev mer detaljert om samhandling med databaser på R-språket i denne artikkelen.
Alle artikler fra serien "Skrive en telegram-bot i R"
For at boten skal be om data fra deg og vente på at du legger inn informasjon, må du registrere den nåværende statusen til dialogen. Den beste måten å gjøre dette på er å bruke en slags innebygd database, for eksempel SQLite.
De. Logikken vil være som følger. Vi kaller bot-metoden, og boten ber sekvensielt om litt informasjon fra oss, og ved hvert trinn venter den på at denne informasjonen skal legges inn og kan sjekke den.
Vi vil skrive den enkleste mulige boten, først vil den spørre etter navnet ditt, deretter alderen din, og vil lagre de mottatte dataene i databasen. Når du spør om alder, vil den sjekke at de angitte dataene er et tall og ikke tekst.
En slik enkel dialog vil bare ha tre tilstander:
start er den normale tilstanden til boten, der den ikke forventer noen informasjon fra deg
wait_name - tilstand der boten venter på at et navn skal skrives inn
wait_age er tilstanden der boten venter på at alderen din skal legges inn, antall hele år.
Bot byggeprosess
I løpet av artikkelen vil vi bygge en bot trinn for trinn; hele prosessen kan skjematisk avbildes som følger:
Vi lager en bot-konfigurasjon der vi vil lagre noen innstillinger. I vårt tilfelle, bot-tokenet og banen til databasefilen.
Vi lager en miljøvariabel der banen til prosjektet med boten vil bli lagret.
Vi lager selve databasen, og en rekke funksjoner slik at boten kan samhandle med den.
Vi skriver botmetoder, dvs. funksjonene den skal utføre.
Legger til meldingsfiltre. Ved hjelp av hvilken vil boten få tilgang til de nødvendige metodene, avhengig av den nåværende statusen til chatten.
Vi legger til behandlere som vil koble kommandoer og meldinger med de nødvendige botmetodene.
La oss starte boten.
Bot prosjektstruktur
For enkelhets skyld deler vi koden til boten vår og andre relaterte filer i følgende struktur.
bot.R — hovedkoden til boten vår
db_bot_function.R — en kodeblokk med funksjoner for arbeid med en database
bot_metoder.R — kode for bot-metoder
meldingsfiltre.R — meldingsfiltre
handlere.R - behandlere
config.cfg - bot konfig
create_db_data.sql — SQL-skript for å lage en tabell med chatdata i databasen
create_db_state.sql — SQL-skript for å lage en tabell over gjeldende chat-status i databasen
I konfigurasjonen skriver vi bot-tokenet og banen til databasen, dvs. til bot.db-filen; vi lager selve filen i neste trinn.
For mer komplekse roboter kan du lage mer komplekse konfigurasjoner, dessuten er det ikke nødvendig å skrive en ini-konfigurasjon, du kan bruke et hvilket som helst annet format inkludert JSON.
Lag en miljøvariabel
På hver PC kan mappen med botprosjektet ligge i forskjellige kataloger og på forskjellige stasjoner, så i koden vil banen til prosjektmappen bli satt via en miljøvariabel TG_BOT_PATH.
Det er flere måter å lage en miljøvariabel på, den enkleste er å skrive den i en fil .Renviron.
Du kan opprette eller redigere denne filen ved å bruke kommandoen file.edit(path.expand(file.path("~", ".Renviron"))). Kjør den og legg til en linje i filen:
TG_BOT_PATH=C:/ПУТЬ/К/ВАШЕМУ/ПРОЕКТУ
Lagre deretter filen .Renviron og start RStudio på nytt.
Opprette en database
Det neste trinnet er å lage en database. Vi trenger 2 bord:
chat_data — data som boten ba om fra brukeren
chat_state — gjeldende status for alle chatter
Du kan opprette disse tabellene ved å bruke følgende SQL-spørring:
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
);
Hvis du lastet ned bot-prosjektet fra GitHub, for å lage databasen kan du bruke følgende kode i 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'))
Skrivefunksjoner for å jobbe med databasen
Vi har allerede en konfigurasjonsfil klar og en database opprettet. Nå må du skrive funksjoner for å lese og skrive data til denne databasen.
Hvis du lastet ned prosjektet fra GitHub, så kan du finne funksjonene i filen db_bot_function.R.
Funksjonskode for arbeid med databasen
# ###########################################################
# 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]])
}
Vi har laget 4 enkle funksjoner:
get_state() — få gjeldende chat-status fra databasen
set_state() — skriv gjeldende chat-status til databasen
get_chat_data() — motta data sendt av brukeren
set_chat_data() — registrere data mottatt fra brukeren
Alle funksjoner er ganske enkle, de leser enten data fra databasen ved hjelp av kommandoen dbGetQuery(), eller forplikte seg UPSERT operasjon (endre eksisterende data eller skrive nye data til databasen), ved å bruke funksjonen dbExecute().
Syntaksen for UPSERT-operasjonen er som følger:
INSERT INTO chat_data (chat_id, ${field})
VALUES(${chat_id}, '${value}')
ON CONFLICT(chat_id)
DO UPDATE SET ${field}='${value}';
De. i vårt tabellfelt chat_id har en unikhetsbegrensning og er hovednøkkelen til tabeller. Til å begynne med prøver vi å legge til informasjon i tabellen, og vi får en feilmelding hvis data for gjeldende chat allerede er tilstede, i så fall oppdaterer vi ganske enkelt informasjonen for denne chatten.
Deretter vil vi bruke disse funksjonene i botens metoder og filtre.
Bot metoder
Det neste trinnet i å bygge vår bot er å lage metoder. Hvis du lastet ned prosjektet fra GitHub, så er alle metodene i filen bot_metoder.R.
Bot metodekode
# ###########################################################
# 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')
}
}
Vi har laget 5 metoder:
start — Start en dialogboks
tilstand — Få gjeldende chatstatus
tilbakestill — Tilbakestill gjeldende chat-status
enter_name — Boten spør etter navnet ditt
enter_age — Boten spør om alderen din
metode start ber om navnet ditt, og endrer chat-tilstanden til wait_name, dvs. til standby for å skrive inn navnet ditt.
Deretter sender du navnet og det behandles av metoden enter_name, boten hilser deg, skriver det mottatte navnet inn i databasen og bytter chatten til tilstanden vente_alder.
På dette stadiet forventer boten at du skriver inn alderen din. Du sender alderen din, boten sjekker meldingen, hvis du sendte litt tekst i stedet for et nummer, vil den si: Ты ввёл некорректные данные, введи число, og vil vente på at du legger inn dataene dine på nytt. Hvis du sendte et nummer, vil boten rapportere at den har akseptert alderen din, skrive de mottatte dataene til databasen, rapportere alle dataene mottatt fra deg og returnere chat-tilstanden til sin opprinnelige posisjon, dvs. V start.
Ved å kalle metoden state du kan be om gjeldende chat-status når som helst, og ved å bruke reset tilbakestille chatten til dens opprinnelige tilstand.
Meldingsfiltre
I vårt tilfelle er dette en av de viktigste delene i å bygge en bot. Det er ved hjelp av meldingsfiltre at boten vil forstå hvilken informasjon den forventer av deg og hvordan den skal behandles.
I prosjektet på GitHub filtre er registrert i filen meldingsfiltre.R.
Meldingsfilterkode:
# ###########################################################
# 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"
}
)
I filtre bruker vi den tidligere skrevne funksjonen get_state(), for å be om gjeldende status for chatten. Denne funksjonen krever kun 1 argument, chat-ID.
Neste filter wait_name behandler meldinger når chatten er i en tilstand wait_name, og følgelig filteret vente_alder behandler meldinger når chatten er i en tilstand wait_age.
Håndtere
Filen med behandlere kalles handlere.R, og har følgende 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)
Først lager vi kommandobehandlere som lar deg kjøre metoder for å starte en dialogboks, tilbakestille den og spørre om gjeldende tilstand.
Deretter oppretter vi 2 meldingsbehandlere ved å bruke filtrene som ble opprettet i forrige trinn, og legger til et filter til dem !MessageFilters$command, slik at vi kan bruke kommandoer i enhver chat-tilstand.
Bot lanseringskode
Nå har vi alt klart for lansering, hovedkoden for å starte boten er i filen 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()
Som et resultat fikk vi denne boten:
Når som helst ved å bruke kommandoen /state vi kan spørre om gjeldende chat-status og bruke kommandoen /reset returner chatten til dens opprinnelige tilstand og start dialogen på nytt.
Konklusjon
I denne artikkelen fant vi ut hvordan du bruker en database i en bot, og hvordan du bygger sekvensielle logiske dialoger ved å registrere chat-tilstanden.
I dette tilfellet så vi på det mest primitive eksemplet, slik at det ville være lettere for deg å forstå ideen om å bygge slike roboter; i praksis kan du bygge mye mer komplekse dialoger.
I den neste artikkelen i denne serien vil vi lære hvordan du begrenser rettighetene til bot-brukere til å bruke ulike metoder.