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.

Pisanje bota za telegram u R (dio 4): Izgradnja dosljednog, logičnog dijaloga s botom

Svi članci iz serije “Pisanje telegram bota u R”

  1. Kreiramo bota i koristimo ga za slanje poruka u telegramu
  2. Dodajte podršku za naredbe i filtre poruka botu
  3. Kako dodati podršku za tipkovnicu botu
  4. Izgradnja dosljednog, logičnog dijaloga s botom

sadržaj

Ako vas zanima analiza podataka, možda će vas zanimati moja telegram и youtube kanala. Većina sadržaja posvećena je jeziku R.

  1. Uvod
  2. Proces izgradnje bota
  3. Struktura bot projekta
  4. Konfiguracija bota
  5. Stvorite varijablu okoline
  6. Izrada baze podataka
  7. Pisanje funkcija za rad s bazom podataka
  8. Botovske metode
  9. Filtri poruka
  10. Rukovatelji
  11. Kod za pokretanje bota
  12. Zaključak

Uvod

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:

  1. start je normalno stanje bota, u kojem ne očekuje nikakve informacije od vas
  2. wait_name - stanje u kojem bot čeka da se unese ime
  3. 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:
Pisanje bota za telegram u R (dio 4): Izgradnja dosljednog, logičnog dijaloga s botom

  1. Kreiramo konfiguraciju bota u koju ćemo pohraniti neke postavke. U našem slučaju token bota i put do datoteke baze podataka.
  2. Kreiramo varijablu okoline u kojoj će biti pohranjena staza do projekta s botom.
  3. Izrađujemo samu bazu podataka i brojne funkcije kako bi bot mogao komunicirati s njom.
  4. Pišemo bot metode, tj. funkcije koje će obavljati.
  5. Dodavanje filtera poruka. Uz pomoć kojih će bot pristupiti potrebnim metodama, ovisno o trenutnom stanju chata.
  6. Dodajemo rukovatelje koji će povezivati ​​naredbe i poruke s potrebnim bot metodama.
  7. 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
  • bot.db - bot baza podataka

Možete pogledati cijeli bot projekt, ili preuzimanje od mog repozitorij na GitHubu.

Konfiguracija bota

Koristit ćemo uobičajeni kao konfiguraciju ini datoteka, sljedeći obrazac:

[bot_settings]
bot_token=ТОКЕН_ВАШЕГО_БОТА

[db_settings]
db_path=C:/ПУТЬ/К/ПАПКЕ/ПРОЕКТА/bot.db

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:
Pisanje bota za telegram u R (dio 4): Izgradnja dosljednog, logičnog dijaloga s botom

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.

Izvor: www.habr.com

Dodajte komentar