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

Ako ste već pročitali prethodni tri članka iz ove serije, onda već znate kako pisati punopravne telegram botove pomoću tastature.

U ovom članku ćemo naučiti kako napisati bota koji će održavati dosljedan dijalog. One. 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 ćemo naučiti kako koristiti bazu podataka ispod haube bota, u našem primjeru to će biti SQLite, ali možete koristiti bilo koji drugi DBMS. Pisao sam detaljnije o interakciji sa bazama podataka u jeziku R ovaj članak.

Pisanje telegram bota u R (4. dio): 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 komande i filtere poruka botu
  3. Kako dodati podršku za tastaturu botu
  4. Izgradnja dosljednog, logičnog dijaloga s botom

Sadržaj

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

  1. Uvod
  2. Proces izgradnje bota
  3. Struktura bot projekta
  4. Bot config
  5. Kreirajte varijablu okruženja
  6. Kreiranje baze podataka
  7. Pisanje funkcija za rad sa bazom podataka
  8. Bot metode
  9. Filteri za poruke
  10. Rukovaoci
  11. Kod za pokretanje bota
  12. zaključak

Uvod

Da bi bot od vas tražio podatke i čekao da unesete bilo koju informaciju, morat ćete snimiti trenutno stanje dijaloga. Najbolji način da to učinite je korištenje neke vrste ugrađene baze podataka, kao što je SQLite.

One. Logika će biti sljedeća. Pozivamo bot metodu, a bot uzastopno traži neke informacije od nas i na svakom koraku čeka da se ta informacija unese i može ih provjeriti.

Napisaćemo najjednostavniji mogući bot, prvo će tražiti vaše ime, zatim vaše godine, a primljene podatke će sačuvati u bazi podataka. Prilikom upita za starost, provjerit će da li su uneseni podaci broj, a ne tekst.

Takav jednostavan dijalog će imati 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 unese vaša starost, broj punih godina.

Proces izgradnje bota

Tokom članka ćemo napraviti bot korak po korak; cijeli proces se može shematski prikazati na sljedeći način:
Pisanje telegram bota u R (4. dio): 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 putanja do datoteke baze podataka.
  2. Kreiramo varijablu okruženja u kojoj će biti pohranjena putanja do projekta sa botom.
  3. Kreiramo 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 razgovora.
  6. Dodamo rukovaoce koji će povezati komande i poruke sa potrebnim bot metodama.
  7. Pokrenimo bota.

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_function.R — blok koda sa funkcijama za rad sa bazom podataka
  • bot_methods.R — kod bot metoda
  • message_filters.R — filteri poruka
  • rukovaoci.R - rukovaoce
  • config.cfg - konfiguracija bota
  • create_db_data.sql — SQL skripta za kreiranje tabele sa podacima za ćaskanje u bazi podataka
  • create_db_state.sql — SQL skripta za kreiranje tabele trenutnog stanja razgovora u bazi podataka
  • bot.db - bot baza podataka

Možete pogledati cijeli bot projekat, ili скачать od mog spremište na GitHubu.

Bot config

Koristićemo uobičajeni kao konfiguraciju ini fajl, sljedeći oblik:

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

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

U konfiguraciji upisujemo bot token i putanju do baze podataka, tj. u datoteku bot.db; samu datoteku ćemo kreirati u sljedećem koraku.

Za složenije botove možete kreirati složenije konfiguracije, osim toga nije potrebno pisati ini konfiguraciju, možete koristiti bilo koji drugi format uključujući JSON.

Kreirajte varijablu okruženja

Na svakom računaru fascikla sa projektom bot može se nalaziti u različitim direktorijumima i na različitim diskovima, tako da će u kodu put do fascikle projekta biti postavljen preko varijable okruženja TG_BOT_PATH.

Postoji nekoliko načina za kreiranje varijable okruženja, najjednostavniji je da je zapišete u datoteku .Renviron.

Možete kreirati ili uređivati ​​ovu datoteku pomoću naredbe file.edit(path.expand(file.path("~", ".Renviron"))). Izvršite ga i dodajte jedan red u datoteku:

TG_BOT_PATH=C:/ПУТЬ/К/ВАШЕМУ/ПРОЕКТУ

Zatim sačuvajte datoteku .Renviron i ponovo pokrenite RStudio.

Kreiranje baze podataka

Sljedeći korak je kreiranje baze podataka. Trebat će nam 2 stola:

  • chat_data — podaci koje je bot zatražio od korisnika
  • chat_state — trenutno stanje svih razgovora

Ove tabele možete kreirati koristeći sledeći SQL upit:

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 bot projekat sa GitHub, zatim za kreiranje 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 sa bazom podataka

Već imamo spreman konfiguracijski fajl i kreiranu bazu podataka. Sada morate napisati funkcije za čitanje i pisanje podataka u ovu bazu podataka.

Ako ste projekat preuzeli sa GitHub, tada možete pronaći funkcije u datoteci db_bot_function.R.

Funkcijski kod za rad sa 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 razgovora iz baze podataka
  • set_state() — upišite trenutno stanje ćaskanja u bazu podataka
  • get_chat_data() — primati podatke koje šalje korisnik
  • set_chat_data() — zapis podataka primljenih od korisnika

Sve funkcije su prilično jednostavne, ili čitaju podatke iz baze podataka pomoću naredbe dbGetQuery(), ili urezivanje UPSERT operacija (promjena postojećih podataka ili upisivanje novih podataka u bazu podataka), 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}';

One. u polju naših tabela chat_id ima ograničenje jedinstvenosti i primarni je ključ tabela. U početku pokušavamo da dodamo informacije u tabelu, a dobijamo grešku ako su podaci za trenutni chat već prisutni, u kom slučaju jednostavno ažuriramo informacije za ovaj chat.

Zatim ćemo koristiti ove funkcije u botovim metodama i filterima.

Bot metode

Sljedeći korak u izgradnji našeg bota je kreiranje metoda. Ako ste projekat preuzeli sa GitHub, tada su sve metode u datoteci bot_methods.R.

Šifra bot metode

# ###########################################################
# 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:

  • start — Pokrenite dijalog
  • stanje — Dobijte trenutno stanje ćaskanja
  • reset — Resetujte trenutno stanje ćaskanja
  • enter_name — bot traži vaše ime
  • enter_age — bot pita za vaše godine

Metoda start traži vaše ime i mijenja stanje razgovora u čekaj_ime, tj. u stanje pripravnosti za unos vašeg imena.

Zatim šaljete ime i ono se obrađuje metodom enter_name, bot vas pozdravlja, upisuje primljeno ime u bazu podataka i prebacuje chat u stanje wait_age.

U ovoj fazi, bot očekuje da unesete svoje godine. Vi pošaljete svoje godine, bot provjerava poruku, ako ste poslali neki tekst umjesto broja, reći će: Ты ввёл некорректные данные, введи число, i čekat će da ponovo unesete svoje podatke. Ako ste poslali broj, bot će prijaviti da je prihvatio vaše godine, upisati primljene podatke u bazu podataka, prijaviti sve primljene podatke od vas i vratiti stanje chata na prvobitnu poziciju, tj. V start.

Pozivanjem metode state možete zatražiti trenutni status ćaskanja u bilo kojem trenutku, a koristeći reset vratite chat u prvobitno stanje.

Filteri za poruke

U našem slučaju, ovo je jedan od najvažnijih dijelova u izgradnji bota. Uz pomoć filtera poruka bot će shvatiti koje informacije očekuje od vas i kako ih treba obraditi.

U projektu na GitHub filteri su registrovani u datoteci message_filters.R.

Kôd filtera 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 razgovora. Ova funkcija zahtijeva samo 1 argument, chat id.

Sljedeći filter čekaj_ime obrađuje poruke kada je chat u stanju wait_name, a shodno tome i filter wait_age obrađuje poruke kada je chat u stanju wait_age.

Rukovaoci

Poziva se fajl sa rukovaocima rukovaoci.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 kreiramo rukovaoce komandama koji će vam omogućiti da pokrenete metode za pokretanje dijaloga, resetujete ga i upitate trenutno stanje.

Zatim kreiramo 2 rukovaoca porukama koristeći filtere kreirane u prethodnom koraku i dodajemo im filter !MessageFilters$command, tako da možemo koristiti komande u bilo kojem stanju razgovora.

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

U bilo kom trenutku koristeći naredbu /state možemo upitati trenutno stanje ćaskanja i pomoću naredbe /reset vratite razgovor u prvobitno stanje i ponovo započnite dijalog.

zaključak

U ovom članku smo shvatili kako koristiti bazu podataka unutar bota i kako napraviti sekvencijalne logičke dijaloge snimanjem stanja razgovora.

U ovom slučaju, pogledali smo najprimitivniji primjer, kako bi vam bilo lakše razumjeti ideju izgradnje ovakvih botova; u praksi možete graditi mnogo složenije dijaloge.

U sljedećem članku iz ove serije naučit ćemo kako ograničiti prava korisnika botova da koriste različite njegove metode.

izvor: www.habr.com

Dodajte komentar