Pisanje bota za telegram v R (4. del): gradnja doslednega, logičnega dialoga z botom

Če ste že prebrali prejšnjo trije članki iz te serije, potem že veste, kako napisati polnopravne telegramske bote s tipkovnico.

V tem članku se bomo naučili, kako napisati bota, ki bo vzdrževal dosleden dialog. Tisti. Bot vam bo zastavil vprašanja in čakal, da vnesete nekaj informacij. Glede na podatke, ki jih vnesete, bo bot izvedel nekaj dejanj.

Tudi v tem članku se bomo naučili uporabljati bazo podatkov pod pokrovom bota, v našem primeru bo to SQLite, vendar lahko uporabite katero koli drugo DBMS. O interakciji z bazami podatkov v jeziku R sem podrobneje pisal v ta članek.

Pisanje bota za telegram v R (4. del): gradnja doslednega, logičnega dialoga z botom

Vsi članki iz serije “Pisanje telegram bota v R”

  1. Ustvarimo bota in ga uporabimo za pošiljanje sporočil v telegramu
  2. Botu dodajte podporo za ukaze in filtre sporočil
  3. Kako botu dodati podporo za tipkovnico
  4. Vzpostavljanje doslednega, logičnega dialoga z botom

Vsebina

Če vas zanima analiza podatkov, vas bo morda zanimal moj telegram и youtube kanalov. Večina vsebine je posvečena jeziku R.

  1. Predstavitev
  2. Postopek gradnje botov
  3. Struktura projekta Bot
  4. Konfiguracija bota
  5. Ustvarite spremenljivko okolja
  6. Ustvarjanje baze podatkov
  7. Pisanje funkcij za delo z bazo podatkov
  8. Botovske metode
  9. Filtri sporočil
  10. Vodniki
  11. Koda za zagon bota
  12. Zaključek

Predstavitev

Da bo bot od vas zahteval podatke in čakal, da vnesete kakršne koli informacije, boste morali zabeležiti trenutno stanje dialoga. Najboljši način za to je uporaba neke vrste vdelane baze podatkov, kot je SQLite.

Tisti. Logika bo naslednja. Pokličemo metodo bot, bot pa od nas zaporedno zahteva nekaj podatkov in na vsakem koraku počaka na vnos teh podatkov in jih lahko preveri.

Napisali bomo najpreprostejšega možnega bota, najprej vas bo vprašal za ime, nato starost in prejete podatke shranil v bazo. Pri vprašanju starosti bo preveril, ali so vneseni podatki številka in ne besedilo.

Tako preprost dialog bo imel samo tri stanja:

  1. start je običajno stanje bota, v katerem od vas ne pričakuje nobenih informacij
  2. wait_name - stanje, v katerem bot čaka na vnos imena
  3. wait_age je stanje, v katerem bot čaka na vnos vaše starosti, število polnih let.

Postopek gradnje botov

V članku bomo korak za korakom zgradili bota; celoten postopek lahko shematično prikažemo na naslednji način:
Pisanje bota za telegram v R (4. del): gradnja doslednega, logičnega dialoga z botom

  1. Ustvarimo konfiguracijo bota, v kateri bomo shranili nekaj nastavitev. V našem primeru žeton bota in pot do datoteke baze podatkov.
  2. Ustvarimo spremenljivko okolja, v kateri bo shranjena pot do projekta z botom.
  3. Ustvarimo samo bazo podatkov in številne funkcije, da lahko bot komunicira z njo.
  4. Pišemo bot metode, tj. funkcije, ki jih bo opravljal.
  5. Dodajanje filtrov za sporočila. S pomočjo katerega bo bot dostopal do potrebnih metod, odvisno od trenutnega stanja klepeta.
  6. Dodamo upravljalnike, ki bodo povezali ukaze in sporočila s potrebnimi metodami botov.
  7. Zaženimo bota.

Struktura projekta Bot

Zaradi udobja bomo kodo našega bota in druge povezane datoteke razdelili v naslednjo strukturo.

  • bot.R — glavna koda našega bota
  • db_bot_function.R — blok kode s funkcijami za delo z bazo podatkov
  • bot_methods.R — koda botovskih metod
  • filtri_sporočil.R — filtri sporočil
  • vodniki.R - upravljavci
  • config.cfg - konfiguracija bota
  • create_db_data.sql — SQL skript za ustvarjanje tabele s podatki klepeta v bazi podatkov
  • create_db_state.sql — SQL skript za ustvarjanje tabele trenutnega stanja klepeta v bazi podatkov
  • bot.db - baza podatkov botov

Ogledate si lahko celoten bot projekt, oz prenos od mojega repozitorij na GitHubu.

Konfiguracija bota

Kot konfiguracijo bomo uporabili običajno ini datoteka, naslednji obrazec:

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

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

V config zapišemo žeton bota in pot do baze, tj. v datoteko bot.db; samo datoteko bomo ustvarili v naslednjem koraku.

Za bolj zapletene robote lahko ustvarite bolj zapletene konfiguracije, poleg tega ni potrebno napisati ini konfiguracije, lahko uporabite katero koli drugo obliko, vključno z JSON.

Ustvarite spremenljivko okolja

Na vsakem računalniku se lahko mapa s projektom bota nahaja v različnih imenikih in na različnih pogonih, zato bo v kodi pot do mape projekta nastavljena prek spremenljivke okolja TG_BOT_PATH.

Spremenljivko okolja lahko ustvarite na več načinov, najenostavnejši je, da jo zapišete v datoteko .Renviron.

To datoteko lahko ustvarite ali uredite z ukazom file.edit(path.expand(file.path("~", ".Renviron"))). Izvedite ga in dodajte eno vrstico v datoteko:

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

Nato shranite datoteko .Renviron in znova zaženite RStudio.

Ustvarjanje baze podatkov

Naslednji korak je izdelava baze podatkov. Potrebovali bomo 2 mizi:

  • chat_data — podatki, ki jih je bot zahteval od uporabnika
  • chat_state — trenutno stanje vseh klepetov

Te tabele lahko ustvarite z naslednjo poizvedbo 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
);

Če ste projekt bota prenesli iz GitHub, potem lahko za ustvarjanje baze podatkov uporabite naslednjo kodo v 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 funkcij za delo z bazo podatkov

Imamo že pripravljeno konfiguracijsko datoteko in izdelano bazo podatkov. Zdaj morate napisati funkcije za branje in pisanje podatkov v to bazo podatkov.

Če ste projekt prenesli iz GitHub, potem lahko najdete funkcije v datoteki db_bot_function.R.

Funkcijska koda za delo z bazo podatkov

# ###########################################################
# 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]])

}

Ustvarili smo 4 preproste funkcije:

  • get_state() — pridobite trenutno stanje klepeta iz baze podatkov
  • set_state() — zapišite trenutno stanje klepeta v bazo podatkov
  • get_chat_data() — prejemanje podatkov, ki jih pošlje uporabnik
  • set_chat_data() — beleženje podatkov, prejetih od uporabnika

Vse funkcije so precej preproste, bodisi preberejo podatke iz baze podatkov z ukazom dbGetQuery(), ali zavezati UPSERT delovanje (spreminjanje obstoječih podatkov ali pisanje novih podatkov v bazo), uporaba funkcije dbExecute().

Sintaksa za operacijo UPSERT je naslednja:

INSERT INTO chat_data (chat_id, ${field})
VALUES(${chat_id}, '${value}') 
ON CONFLICT(chat_id) 
DO UPDATE SET ${field}='${value}';

Tisti. v našem polju tabel chat_id ima omejitev edinstvenosti in je primarni ključ tabel. Na začetku poskušamo v tabelo dodati informacije in prejmemo napako, če so podatki za trenutni klepet že prisotni, v tem primeru preprosto posodobimo informacije za ta klepet.

Nato bomo te funkcije uporabili v metodah in filtrih bota.

Botovske metode

Naslednji korak pri gradnji našega bota je ustvarjanje metod. Če ste projekt prenesli iz GitHub, potem so vse metode v datoteki bot_methods.R.

Koda 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')
  }

}

Ustvarili smo 5 metod:

  • start — Začnite pogovorno okno
  • stanje — Pridobite trenutno stanje klepeta
  • ponastavi — Ponastavite trenutno stanje klepeta
  • enter_name — Bot zahteva vaše ime
  • enter_age — Bot vas vpraša za vašo starost

Metoda start vpraša za vaše ime in spremeni stanje klepeta v čakalno ime, tj. v pripravljenosti za vnos vašega imena.

Nato pošljete ime in obdela ga metoda enter_name, vas bot pozdravi, prejeto ime zapiše v bazo podatkov in klepet preklopi v stanje čakalna_doba.

Na tej stopnji bot pričakuje, da vnesete svojo starost. Pošljete svojo starost, bot preveri sporočilo, če ste namesto številke poslali besedilo, bo pisalo: Ты ввёл некорректные данные, введи число, in bo počakal, da ponovno vnesete svoje podatke. Če ste poslali številko, bo bot sporočil, da je sprejel vašo starost, zapisal prejete podatke v bazo podatkov, sporočil vse podatke, ki jih je prejel od vas, in vrnil stanje klepeta v prvotni položaj, tj. V start.

S klicem metode state kadar koli lahko zahtevate trenutno stanje klepeta in uporabite reset vrnite klepet v prvotno stanje.

Filtri sporočil

V našem primeru je to eden najpomembnejših delov pri izdelavi bota. S pomočjo filtrov za sporočila bo bot razumel, katere informacije od vas pričakuje in kako jih je treba obdelati.

V projektu na GitHub filtri so registrirani v datoteki filtri_sporočil.R.

Koda filtra sporočil:

# ###########################################################
# 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"
}
)

V filtrih uporabljamo prej napisano funkcijo get_state(), da zahtevate trenutno stanje klepeta. Ta funkcija zahteva samo 1 argument, ID klepeta.

Naslednji filter čakalno ime obdeluje sporočila, ko je klepet v stanju wait_name, in temu primerno filter čakalna_doba obdeluje sporočila, ko je klepet v stanju wait_age.

Vodniki

Pokliče se datoteka z obdelovalci vodniki.Rin ima naslednjo kodo:

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

Najprej izdelamo upravljalnike ukazov, ki vam bodo omogočili zagon metod za zagon pogovornega okna, njegovo ponastavitev in poizvedbo o trenutnem stanju.

Nato ustvarimo 2 upravljalnika sporočil z uporabo filtrov, ustvarjenih v prejšnjem koraku, in jima dodamo filter !MessageFilters$command, tako da lahko uporabljamo ukaze v katerem koli stanju klepeta.

Koda za zagon bota

Zdaj imamo vse pripravljeno za zagon, glavna koda za zagon bota je v datoteki 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()

Kot rezultat smo dobili tega bota:
Pisanje bota za telegram v R (4. del): gradnja doslednega, logičnega dialoga z botom

Kadar koli z uporabo ukaza /state lahko poizvedujemo o trenutnem stanju klepeta in uporabimo ukaz /reset vrnite klepet v prvotno stanje in znova začnite dialog.

Zaključek

V tem članku smo ugotovili, kako uporabiti bazo podatkov znotraj bota in kako zgraditi zaporedna logična dialoga s snemanjem stanja klepeta.

V tem primeru smo pogledali najbolj primitiven primer, da bi vam bilo lažje razumeti idejo o izdelavi takšnih botov; v praksi lahko zgradite veliko bolj zapletene dialoge.

V naslednjem članku v tej seriji se bomo naučili, kako uporabnikom botov omejiti pravice do uporabe različnih njegovih metod.

Vir: www.habr.com

Dodaj komentar