In telegrambot skriuwe yn R (diel 4): Bouwe in konsekwint, logyske dialooch mei de bot

As jo ​​hawwe al lêzen de foarige trije artikels út dizze searje, dan witte jo al hoe't jo folweardige telegrambots skriuwe mei in toetseboerd.

Yn dit artikel sille wy leare hoe't jo in bot skriuwe dy't in konsekwint dialooch sil behâlde. Dy. De bot sil jo fragen stelle en wachtsje oant jo wat ynformaasje ynfiere. Ofhinklik fan 'e gegevens dy't jo ynfiere, sil de bot guon aksjes útfiere.

Ek yn dit artikel sille wy leare hoe't jo in databank brûke ûnder de kap fan 'e bot, yn ús foarbyld sil it SQLite wêze, mar jo kinne elke oare DBMS brûke. Ik skreau yn mear detail oer ynteraksje mei databases yn 'e R-taal yn dit artikel.

In telegrambot skriuwe yn R (diel 4): Bouwe in konsekwint, logyske dialooch mei de bot

Alle artikels út 'e searje "In telegrambot skriuwe yn R"

  1. Wy meitsje in bot en brûke it om berjochten te ferstjoeren yn telegram
  2. Foegje kommando-stipe en berjochtfilters ta oan de bot
  3. Hoe kinne jo toetseboerdstipe tafoegje oan in bot
  4. Bouwe in konsekwint, logyske dialooch mei de bot

Ynhâld

As jo ​​​​ynteressearre binne yn gegevensanalyse, kinne jo miskien wêze ynteressearre yn myn telegram и youtube kanalen. It grutste part fan de ynhâld is wijd oan de R-taal.

  1. Ynlieding
  2. Bot gebou proses
  3. Bot projekt struktuer
  4. Bot konfiguraasje
  5. Meitsje in omjouwingsfariabele
  6. It meitsjen fan in databank
  7. Skriuwfunksjes om te wurkjen mei de databank
  8. Bot metoaden
  9. Berjochtfilters
  10. Handlers
  11. Bot lansearring koade
  12. konklúzje

Ynlieding

Om de bot gegevens fan jo te freegjen en te wachtsjen oant jo alle ynformaasje ynfiere, moatte jo de aktuele steat fan it dialooch opnimme. De bêste manier om dit te dwaan is in soarte fan ynbêde databank te brûken, lykas SQLite.

Dy. De logika sil as folgjend wêze. Wy neame de bot-metoade, en de bot freget opfolgjend wat ynformaasje fan ús, en by elke stap wachtet it op dizze ynformaasje om yn te fieren en kin it kontrolearje.

Wy sille de ienfâldichste mooglike bot skriuwe, earst sil it om jo namme freegje, dan jo leeftyd, en sil de ûntfongen gegevens opslaan yn 'e databank. As jo ​​​​nei leeftyd freegje, sil it kontrolearje dat de ynfierde gegevens in nûmer binne en gjin tekst.

Sa'n ienfâldige dialooch sil mar trije steaten hawwe:

  1. start is de normale tastân fan 'e bot, wêryn't it gjin ynformaasje fan jo ferwachtet
  2. wait_name - steat wêryn de bot wachtet op in namme om te wurde ynfierd
  3. wait_age is de steat wêryn de bot wachtet op jo leeftyd om yn te fieren, it oantal folsleine jierren.

Bot gebou proses

Tidens it artikel sille wy stap foar stap in bot bouwe; it heule proses kin skematysk as folgjend wurde ôfbylde:
In telegrambot skriuwe yn R (diel 4): Bouwe in konsekwint, logyske dialooch mei de bot

  1. Wy meitsje in bot-konfiguraasje wêryn wy guon ynstellings sille opslaan. Yn ús gefal, it bot token, en it paad nei it databankbestân.
  2. Wy meitsje in omjouwingsfariabele wêryn it paad nei it projekt mei de bot wurdt opslein.
  3. Wy meitsje de databank sels, en in oantal funksjes sadat de bot kin ynteraksje mei it.
  4. Wy skriuwe bot metoaden, d.w.s. de funksjes it sil útfiere.
  5. Berjochtfilters tafoegje. Mei de help wêrfan de bot tagong sil ta de nedige metoaden, ôfhinklik fan de aktuele steat fan it petear.
  6. Wy foegje handlers ta dy't kommando's en berjochten ferbine mei de nedige botmetoaden.
  7. Litte wy de bot starte.

Bot projekt struktuer

Foar gemak sille wy de koade fan ús bot, en oare relatearre bestannen, ferdiele yn 'e folgjende struktuer.

  • bot.R - de haadkoade fan ús bot
  • db_bot_function.R - in blok koade mei funksjes foar wurkjen mei in databank
  • bot_methods.R - koade fan botmetoaden
  • message_filters.R - berjochtfilters
  • handlers.R - hannelers
  • config.cfg - bot konfiguraasje
  • create_db_data.sql - SQL-skript foar it meitsjen fan in tabel mei peteargegevens yn 'e databank
  • create_db_state.sql - SQL-skript foar it meitsjen fan in tabel fan 'e hjoeddeistige petearstatus yn' e databank
  • bot.db - bot databank

Jo kinne it hiele botprojekt besjen, of скачать fan myn repository op GitHub.

Bot konfiguraasje

Wy sille de gewoane brûke as konfiguraasje ini triem, de folgjende foarm:

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

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

Yn 'e konfiguraasje skriuwe wy it bot token en it paad nei de databank, d.w.s. nei it bot.db-bestân; wy sille it bestân sels yn 'e folgjende stap oanmeitsje.

Foar mear komplekse bots kinne jo mear komplekse konfiguraasjes oanmeitsje, boppedat is it net nedich om in ini-konfiguraasje te skriuwen, jo kinne elke oare opmaak brûke, ynklusyf JSON.

Meitsje in omjouwingsfariabele

Op elke PC kin de map mei it botprojekt yn ferskate mappen en op ferskate skiven lizze, dus yn 'e koade sil it paad nei de projektmap ynsteld wurde fia in omjouwingsfariabele TG_BOT_PATH.

D'r binne ferskate manieren om in omjouwingsfariabele te meitsjen, de ienfâldichste is it te skriuwen yn in bestân .Renviron.

Jo kinne dit bestân oanmeitsje of bewurkje mei it kommando file.edit(path.expand(file.path("~", ".Renviron"))). Fier it út en foegje ien rigel ta oan it bestân:

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

Folgjende bewarje de triem .Renviron en opnij starte RStudio.

It meitsjen fan in databank

De folgjende stap is om in database te meitsjen. Wy sille 2 tabellen nedich wêze:

  • chat_data - gegevens dy't de bot frege fan 'e brûker
  • chat_state - aktuele steat fan alle petearen

Jo kinne dizze tabellen oanmeitsje mei de folgjende SQL-query:

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

As jo ​​ynladen it bot projekt út GitHub, dan kinne jo de folgjende koade brûke yn 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'))

Skriuwfunksjes om te wurkjen mei de databank

Wy hawwe al in konfiguraasjetriem klear en in database makke. No moatte jo funksjes skriuwe om gegevens te lêzen en te skriuwen nei dizze databank.

As jo ​​ynladen it projekt út GitHub, dan kinne jo de funksjes fine yn it bestân db_bot_function.R.

Funksje koade foar wurkjen mei de databank

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

}

Wy hawwe 4 ienfâldige funksjes makke:

  • get_state() - krije de hjoeddeistige petearstatus fan 'e database
  • set_state() - skriuw de hjoeddeistige petearstatus nei de databank
  • get_chat_data() - ûntfange gegevens ferstjoerd troch de brûker
  • set_chat_data() - record gegevens ûntfongen fan 'e brûker

Alle funksjes binne frij ienfâldich, se lêze gegevens út 'e databank mei it kommando dbGetQuery(), of ynsette UPSERT operaasje (feroarje besteande gegevens of it skriuwen fan nije gegevens nei de databank), mei help fan de funksje dbExecute().

De syntaksis foar de UPSERT-operaasje is as folget:

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

Dy. yn ús tabellen fjild chat_id hat in unike beheining en is de primêre kaai fan tabellen. Yn earste ynstânsje besykje wy ynformaasje oan 'e tabel ta te foegjen, en wy krije in flater as gegevens foar it aktuele petear al oanwêzich binne, yn dat gefal aktualisearje wy gewoan de ynformaasje foar dit petear.

Folgjende sille wy dizze funksjes brûke yn 'e metoaden en filters fan' e bot.

Bot metoaden

De folgjende stap yn it bouwen fan ús bot is om metoaden te meitsjen. As jo ​​ynladen it projekt út GitHub, dan binne alle metoaden yn it bestân bot_methods.R.

Bot metoade koade

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

}

Wy hawwe 5 metoaden makke:

  • start - Begjin in dialooch
  • steat - Krij de hjoeddeistige petearstatus
  • weromsette - Reset de hjoeddeiske petear tastân
  • enter_name - De bot freget om jo namme
  • enter_age - De bot freget om jo leeftyd

Metoade start freget om jo namme, en feroaret de petearstatus nei wait_name, d.w.s. standby foar it ynfieren fan jo namme.

Dêrnei stjoere jo de namme en it wurdt ferwurke troch de metoade enter_name, de bot begroet jo, skriuwt de ûntfongen namme yn 'e databank, en skeakelet it petear nei de steat wachtsje_leeftyd.

Op dit stadium ferwachtet de bot dat jo jo leeftyd ynfiere. Jo stjoere jo leeftyd, de bot kontroleart it berjocht, as jo wat tekst stjoere ynstee fan in nûmer, sil it sizze: Ты ввёл некорректные данные, введи число, en sil wachtsje oant jo jo gegevens opnij ynfiere. As jo ​​​​in nûmer stjoerd hawwe, sil de bot melde dat it jo leeftyd hat akseptearre, skriuw de ûntfongen gegevens yn 'e databank, rapportearje alle gegevens ûntfongen fan jo en bringt de petearstatus werom nei syn oarspronklike posysje, d.w.s. V start.

Troch de metoade te roppen state kinne jo oanfreegje de hjoeddeiske petear status op elts momint, en mei help fan de reset bring it petear werom nei syn oarspronklike steat.

Berjochtfilters

Yn ús gefal is dit ien fan 'e wichtichste dielen by it bouwen fan in bot. It is mei help fan berjochtfilters dat de bot sil begripe hokker ynformaasje hy fan jo ferwachtet en hoe't it moat wurde ferwurke.

Yn it projekt op GitHub filters wurde registrearre yn de triem message_filters.R.

Berjochtfilterkoade:

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

Yn filters brûke wy de earder skreaune funksje get_state(), om de aktuele steat fan it petear op te freegjen. Dizze funksje fereasket mar 1 argumint, petear id.

Folgjende filter wait_name ferwurket berjochten as it petear yn in steat is wait_name, en dêrmei it filter wachtsje_leeftyd ferwurket berjochten as it petear yn in steat is wait_age.

Handlers

De triem mei handlers wurdt neamd handlers.R, en hat de folgjende koade:

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

Earst meitsje wy kommando-handlers wêrmei jo metoaden kinne útfiere om in dialooch te begjinnen, it werom te setten en de aktuele steat te freegjen.

Folgjende, wy meitsje 2 berjocht handlers mei help fan de filters makke yn de foarige stap, en heakje in filter oan harren !MessageFilters$command, sadat wy kommando's kinne brûke yn elke petearstatus.

Bot lansearring koade

No hawwe wy alles klear om te starten, de haadkoade foar it starten fan de bot is yn it bestân 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()

As resultaat krigen wy dizze bot:
In telegrambot skriuwe yn R (diel 4): Bouwe in konsekwint, logyske dialooch mei de bot

Op elk momint mei help fan it kommando /state wy kinne query de hjoeddeiske petear steat, en mei help fan it kommando /reset bring it petear werom nei syn oarspronklike steat en begjin de dialooch opnij.

konklúzje

Yn dit artikel hawwe wy útfûn hoe't jo in databank yn in bot kinne brûke, en hoe't jo opfolgjende logyske dialogen kinne bouwe troch de petearstatus op te nimmen.

Yn dit gefal seagen wy nei it meast primitive foarbyld, sadat it makliker wêze soe foar jo om it idee fan it bouwen fan sokke bots te begripen; yn 'e praktyk kinne jo folle kompleksere dialogen bouwe.

Yn it folgjende artikel yn dizze searje sille wy leare hoe't jo de rjochten fan bot-brûkers kinne beheine om ferskate fan har metoaden te brûken.

Boarne: www.habr.com

Add a comment