Psaní telegramového robota v R (část 4): Budování konzistentního, logického dialogu s robotem

Pokud jste již četli předchozí tři články z této série, pak už víte, jak psát plnohodnotné telegramové roboty pomocí klávesnice.

V tomto článku se naučíme, jak napsat bota, který bude udržovat konzistentní dialog. Tito. Robot vám položí otázky a počká, až zadáte nějaké informace. V závislosti na údajích, které zadáte, robot provede některé akce.

Také v tomto článku se dozvíme, jak používat databázi pod kapotou bota, v našem příkladu to bude SQLite, ale můžete použít jakýkoli jiný DBMS. Podrobněji jsem psal o interakci s databázemi v jazyce R v tento článek.

Psaní telegramového robota v R (část 4): Budování konzistentního, logického dialogu s robotem

Všechny články ze série „Psaní telegramového robota v R“

  1. Vytvoříme robota a použijeme jej k odesílání zpráv v telegramu
  2. Přidejte do robota podporu příkazů a filtry zpráv
  3. Jak přidat podporu klávesnice do robota
  4. Budování konzistentního, logického dialogu s robotem

Obsah

Pokud vás zajímá analýza dat, mohla by vás zajímat moje telegram и Youtube kanály. Většina obsahu je věnována jazyku R.

  1. úvod
  2. Proces stavby botů
  3. Struktura projektu robota
  4. Konfigurace robota
  5. Vytvořte proměnnou prostředí
  6. Vytvoření databáze
  7. Zápis funkcí pro práci s databází
  8. Bot metody
  9. Filtry zpráv
  10. Psovodi
  11. Spouštěcí kód robota
  12. Závěr

úvod

Aby si od vás robot vyžádal data a čekal, až zadáte jakékoli informace, budete muset zaznamenat aktuální stav dialogu. Nejlepší způsob, jak toho dosáhnout, je použít nějaký druh vestavěné databáze, jako je SQLite.

Tito. Logika bude následující. Zavoláme metodu bot a bot od nás postupně požaduje nějaké informace a v každém kroku čeká na zadání těchto informací a může je zkontrolovat.

Napíšeme nejjednoduššího možného bota, nejprve se vás zeptá na jméno, poté na váš věk a přijatá data uloží do databáze. Při dotazu na věk zkontroluje, že zadaný údaj je číslo a ne text.

Takový jednoduchý dialog bude mít pouze tři stavy:

  1. start je normální stav robota, ve kterém od vás neočekává žádné informace
  2. wait_name - stav, ve kterém robot čeká na zadání jména
  3. wait_age je stav, ve kterém robot čeká na zadání vašeho věku, počet celých let.

Proces stavby botů

V průběhu článku si krok za krokem postavíme bota, celý proces lze schematicky znázornit takto:
Psaní telegramového robota v R (část 4): Budování konzistentního, logického dialogu s robotem

  1. Vytvoříme konfiguraci bota, do které uložíme některá nastavení. V našem případě token bota a cesta k souboru databáze.
  2. Vytvoříme proměnnou prostředí, ve které bude uložena cesta k projektu s botem.
  3. Vytváříme samotnou databázi a řadu funkcí, aby s ní bot mohl interagovat.
  4. Píšeme bot metody, tzn. funkce, které bude vykonávat.
  5. Přidávání filtrů zpráv. S pomocí kterých bude bot přistupovat k potřebným metodám v závislosti na aktuálním stavu chatu.
  6. Přidáváme handlery, které propojí příkazy a zprávy s nezbytnými metodami botů.
  7. Spustíme robota.

Struktura projektu robota

Pro usnadnění rozdělíme kód našeho bota a další související soubory do následující struktury.

  • bot.R — hlavní kód našeho robota
  • db_bot_function.R — blok kódu s funkcemi pro práci s databází
  • bot_methods.R — kód metod bot
  • filtry_zpráv.R — filtry zpráv
  • manipulátoři.R - manipulátory
  • config.cfg - konfigurace robota
  • create_db_data.sql — SQL skript pro vytvoření tabulky s daty chatu v databázi
  • create_db_state.sql — SQL skript pro vytvoření tabulky aktuálního stavu chatu v databázi
  • bot.db - databáze botů

Můžete si prohlédnout celý projekt bota, popř ke stažení Z mého úložiště na GitHubu.

Konfigurace robota

Jako konfiguraci použijeme obvyklou ini soubor, následující formulář:

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

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

V configu napíšeme bot token a cestu k databázi, tzn. do souboru bot.db, samotný soubor vytvoříme v dalším kroku.

Pro složitější roboty můžete vytvářet složitější konfigurace, kromě toho není nutné psát ini konfiguraci, můžete použít jakýkoli jiný formát včetně JSON.

Vytvořte proměnnou prostředí

Na každém PC může být složka s projektem bot umístěna v různých adresářích a na různých discích, takže v kódu bude cesta ke složce projektu nastavena pomocí proměnné prostředí TG_BOT_PATH.

Existuje několik způsobů, jak vytvořit proměnnou prostředí, nejjednodušší je zapsat ji do souboru .Renviron.

Tento soubor můžete vytvořit nebo upravit pomocí příkazu file.edit(path.expand(file.path("~", ".Renviron"))). Spusťte jej a přidejte do souboru jeden řádek:

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

Dále soubor uložte .Renviron a restartujte RStudio.

Vytvoření databáze

Dalším krokem je vytvoření databáze. Budeme potřebovat 2 tabulky:

  • chat_data — data, která si robot vyžádal od uživatele
  • chat_state — aktuální stav všech chatů

Tyto tabulky můžete vytvořit pomocí následujícího SQL dotazu:

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

Pokud jste si stáhli projekt bota z GitHub, pak k vytvoření databáze můžete použít následující kód 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'))

Zápis funkcí pro práci s databází

Již máme připravený konfigurační soubor a vytvořenou databázi. Nyní musíte napsat funkce pro čtení a zápis dat do této databáze.

Pokud jste projekt stáhli z GitHub, pak funkce najdete v souboru db_bot_function.R.

Funkční kód pro práci s databází

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

}

Vytvořili jsme 4 jednoduché funkce:

  • get_state() — získat aktuální stav chatu z databáze
  • set_state() — zapsat aktuální stav chatu do databáze
  • get_chat_data() — přijímat data odeslaná uživatelem
  • set_chat_data() — zaznamenávat data přijatá od uživatele

Všechny funkce jsou vcelku jednoduché, buď čtou data z databáze pomocí příkazu dbGetQuery()nebo se zavázat UPSERT operace (změna stávajících dat nebo zápis nových dat do databáze), pomocí funkce dbExecute().

Syntaxe operace UPSERT je následující:

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

Tito. v našem poli tabulky chat_id má omezení jedinečnosti a je primárním klíčem tabulek. Nejprve se pokusíme přidat informace do tabulky a pokud již existují data pro aktuální chat, zobrazí se chyba. V takovém případě jednoduše aktualizujeme informace pro tento chat.

Dále tyto funkce použijeme v metodách a filtrech robota.

Bot metody

Dalším krokem při budování našeho robota je vytvoření metod. Pokud jste projekt stáhli z GitHub, pak jsou všechny metody v souboru bot_methods.R.

Kód metody 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')
  }

}

Vytvořili jsme 5 metod:

  • start — Spuštění dialogu
  • stav — Získejte aktuální stav chatu
  • reset — Obnovení aktuálního stavu chatu
  • enter_name – robot se zeptá na vaše jméno
  • enter_age — Robot se zeptá na váš věk

metoda start požádá o vaše jméno a změní stav chatu na wait_name, tj. do pohotovostního režimu pro zadání vašeho jména.

Dále odešlete jméno a je zpracováno metodou enter_name, bot vás pozdraví, zapíše přijaté jméno do databáze a přepne chat do stavu wait_age.

V této fázi robot očekává, že zadáte svůj věk. Odešlete svůj věk, robot zkontroluje zprávu, pokud jste místo čísla poslali nějaký text, řekne: Ты ввёл некорректные данные, введи числоa počká, až znovu zadáte svá data. Pokud jste poslali číslo, bot ohlásí, že akceptoval váš věk, zapíše přijatá data do databáze, nahlásí všechna od vás přijatá data a vrátí stav chatu do původní polohy, tzn. PROTI start.

Voláním metody state můžete kdykoli požádat o aktuální stav chatu pomocí reset vrátit chat do původního stavu.

Filtry zpráv

V našem případě je to jedna z nejdůležitějších částí při budování bota. Právě pomocí filtrů zpráv bot pochopí, jaké informace od vás očekává a jak by měly být zpracovány.

V projektu na GitHub filtry jsou registrovány v souboru filtry_zpráv.R.

Kód filtru zpráv:

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

Ve filtrech používáme dříve napsanou funkci get_state(), za účelem dotazu na aktuální stav chatu. Tato funkce vyžaduje pouze 1 argument, ID chatu.

Další filtr wait_name zpracovává zprávy, když je chat ve stavu wait_namea podle toho i filtr wait_age zpracovává zprávy, když je chat ve stavu wait_age.

Psovodi

Soubor s handlery se nazývá manipulátoři.Ra má následující kód:

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

Nejprve vytvoříme obslužné rutiny příkazů, které vám umožní spouštět metody pro spuštění dialogu, jeho resetování a dotazování na aktuální stav.

Dále vytvoříme 2 obslužné rutiny zpráv pomocí filtrů vytvořených v předchozím kroku a přidáme k nim filtr !MessageFilters$command, abychom mohli používat příkazy v jakémkoli stavu chatu.

Spouštěcí kód robota

Nyní máme vše připraveno ke spuštění, hlavní kód pro spuštění bota je v souboru 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()

V důsledku toho jsme získali tohoto robota:
Psaní telegramového robota v R (část 4): Budování konzistentního, logického dialogu s robotem

Kdykoli pomocí příkazu /state můžeme se zeptat na aktuální stav chatu a pomocí příkazu /reset vrátit chat do původního stavu a znovu zahájit dialog.

Závěr

V tomto článku jsme přišli na to, jak používat databázi uvnitř robota a jak vytvářet sekvenční logické dialogy zaznamenáváním stavu chatu.

V tomto případě jsme se podívali na nejprimitivnější příklad, aby bylo pro vás snazší pochopit myšlenku budování takových botů; v praxi můžete vytvářet mnohem složitější dialogy.

V dalším článku této série se naučíme, jak omezit práva uživatelů botů na používání různých jeho metod.

Zdroj: www.habr.com

Přidat komentář