Ekri yon bot telegram nan R (pati 4): Bati yon dyalòg ki konsistan, ki lojik ak bot la

Si ou te deja li anvan an twa atik soti nan seri sa a, Lè sa a, ou deja konnen ki jan yo ekri robo telegram plen véritable ak yon klavye.

Nan atik sa a, nou pral aprann ki jan yo ekri yon bot ki pral kenbe yon dyalòg ki konsistan. Moun sa yo. Bot la ap poze w kesyon epi tann pou w antre kèk enfòmasyon. Tou depan de done ou antre, bot la pral fè kèk aksyon.

Epitou nan atik sa a nou pral aprann ki jan yo sèvi ak yon baz done anba kapo a nan bot la, nan egzanp nou an li pral SQLite, men ou ka itilize nenpòt lòt DBMS. Mwen te ekri an plis detay sou kominike avèk baz done nan lang R nan atik sa a.

Ekri yon bot telegram nan R (pati 4): Bati yon dyalòg ki konsistan, ki lojik ak bot la

Tout atik ki soti nan seri "Ekri yon telegram bot nan R"

  1. Nou kreye yon bot epi sèvi ak li pou voye mesaj nan telegram
  2. Ajoute sipò kòmand ak filtè mesaj nan bot la
  3. Ki jan yo ajoute sipò klavye nan yon bot
  4. Bati yon dyalòg ki konsistan, ki lojik ak bot la

Content

Si w enterese nan analiz done, ou ta ka enterese nan mwen an telegram и YouTube chanèl. Pifò nan kontni an dedye a lang R la.

  1. Entwodiksyon
  2. Pwosesis bati bot
  3. Estrikti pwojè bot
  4. Bot konfigirasyon
  5. Kreye yon varyab anviwònman
  6. Kreye yon baz done
  7. Ekri fonksyon pou travay ak baz done a
  8. Metòd bot
  9. Filtè mesaj
  10. Moun kap okipe yo
  11. Kòd lansman bot
  12. Konklizyon

Entwodiksyon

Nan lòd pou bot la mande done nan men ou epi tann pou ou antre nenpòt enfòmasyon, w ap bezwen anrejistre eta aktyèl la nan dyalòg la. Pi bon fason pou fè sa se sèvi ak kèk kalite baz done entegre, tankou SQLite.

Moun sa yo. Lojik la pral jan sa a. Nou rele metòd bot la, ak bot la sekans mande kèk enfòmasyon nan men nou, epi nan chak etap li tann pou enfòmasyon sa a yo dwe antre epi li ka tcheke li.

Nou pral ekri bot ki pi senp posib, premye li pral mande pou non ou, Lè sa a, laj ou, epi yo pral sove done yo resevwa nan baz done a. Lè w ap mande laj, li pral tcheke si done yo antre yo se yon nimewo epi yo pa tèks.

Yon dyalòg senp konsa pral gen sèlman twa eta:

  1. kòmanse se eta nòmal bot la, nan ki li pa atann okenn enfòmasyon nan men ou
  2. wait_name - eta kote bot la ap tann pou yon non yo dwe antre
  3. wait_age se eta kote bot la ap tann pou yo antre laj ou a, kantite ane konplè.

Pwosesis bati bot

Pandan atik la, nou pral bati yon bot etap pa etap; tout pwosesis la ka deskripsyon schematik jan sa a:
Ekri yon bot telegram nan R (pati 4): Bati yon dyalòg ki konsistan, ki lojik ak bot la

  1. Nou kreye yon konfigirasyon bot kote nou pral estoke kèk paramèt. Nan ka nou an, siy bot la, ak chemen an nan dosye a baz done.
  2. Nou kreye yon varyab anviwònman kote yo pral estoke chemen an nan pwojè a ak bot la.
  3. Nou kreye baz done a tèt li, ak yon kantite fonksyon pou bot la ka kominike avèk li.
  4. Nou ekri metòd bot, i.e. fonksyon li pral fè.
  5. Ajoute filtè mesaj. Avèk èd nan ki bot la pral jwenn aksè nan metòd ki nesesè yo, tou depann de eta aktyèl la nan chat la.
  6. Nou ajoute moun kap okipe yo ki pral konekte kòmandman ak mesaj ak metòd bot ki nesesè yo.
  7. Ann lanse bot la.

Estrikti pwojè bot

Pou konvenyans, nou pral divize kòd la nan bot nou an, ak lòt dosye ki gen rapò, nan estrikti sa a.

  • bot.R - kòd prensipal bot nou an
  • db_bot_function.R — yon blòk nan kòd ak fonksyon pou travay ak baz done a
  • bot_methods.R - kòd metòd bot
  • message_filters.R - filtè mesaj
  • moun kap okipe yo.R - moun kap okipe yo
  • config.cfg - bot konfigirasyon
  • create_db_data.sql — Script SQL pou kreye yon tab ak done chat nan baz done a
  • create_db_state.sql — Script SQL pou kreye yon tablo sou eta aktyèl chat nan baz done a
  • bot.db - baz done bot

Ou ka wè tout pwojè bot la, oswa telechaje soti nan mwen depo sou GitHub.

Bot konfigirasyon

Nou pral sèvi ak yon sèl abityèl kòm yon konfigirasyon ini dosye, fòm sa a:

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

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

Nan konfigirasyon an nou ekri siy bot la ak chemen ki mennen nan baz done a, i.e. nan dosye a bot.db; nou pral kreye dosye a tèt li nan pwochen etap la.

Pou robo ki pi konplèks, ou ka kreye konfigirasyon pi konplèks, san konte, li pa nesesè pou ekri yon konfigirasyon ini, ou ka itilize nenpòt lòt fòma ki gen ladan JSON.

Kreye yon varyab anviwònman

Sou chak PC, katab la ak pwojè bot la ka lokalize nan repèrtwar diferan ak sou kondui diferan, kidonk nan kòd la chemen an nan katab pwojè a pral mete atravè yon varyab anviwònman. TG_BOT_PATH.

Gen plizyè fason yo kreye yon varyab anviwònman, pi senp la se ekri li nan yon dosye .Renviron.

Ou ka kreye oswa modifye dosye sa a lè l sèvi avèk kòmandman an file.edit(path.expand(file.path("~", ".Renviron"))). Egzekite li epi ajoute yon liy nan dosye a:

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

Apre sa, sove fichye a .Renviron epi rekòmanse RStudio.

Kreye yon baz done

Pwochen etap la se kreye yon baz done. Nou pral bezwen 2 tab:

  • chat_data - done ke bot la mande nan men itilizatè a
  • chat_state — eta aktyèl tout chat

Ou ka kreye tab sa yo lè l sèvi avèk rechèch SQL sa a:

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

Si ou telechaje pwojè bot la soti nan GitHub, Lè sa a, yo kreye baz done a ou ka itilize kòd sa a nan 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'))

Ekri fonksyon pou travay ak baz done a

Nou deja gen yon dosye konfigirasyon pare ak yon baz done kreye. Koulye a, ou bezwen ekri fonksyon pou li ak ekri done nan baz done sa a.

Si ou telechaje pwojè a soti nan GitHub, Lè sa a, ou ka jwenn fonksyon yo nan dosye a db_bot_function.R.

Kòd fonksyon pou travay ak baz done a

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

}

Nou kreye 4 fonksyon senp:

  • get_state() — jwenn eta aktyèl chat la nan baz done a
  • set_state() — ekri eta aktyèl chat la nan baz done a
  • get_chat_data() — resevwa done itilizatè a voye
  • set_chat_data() — dosye done yo resevwa nan men itilizatè a

Tout fonksyon yo byen senp, yo swa li done ki sòti nan baz done a lè l sèvi avèk lòd la dbGetQuery(), oswa komèt UPSERT operasyon (chanje done ki egziste deja oswa ekri nouvo done nan baz done a), lè l sèvi avèk fonksyon an dbExecute().

Sentaks pou operasyon UPSERT la se jan sa a:

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

Moun sa yo. nan jaden tab nou an chat_id gen yon kontrent inik epi li se kle prensipal tab yo. Okòmansman, nou eseye ajoute enfòmasyon sou tab la, epi nou jwenn yon erè si done pou chat aktyèl la deja prezan, nan ka sa a nou tou senpleman mete ajou enfòmasyon pou chat sa a.

Apre sa, nou pral sèvi ak fonksyon sa yo nan metòd bot la ak filtè.

Metòd bot

Pwochen etap la nan bati bot nou an se kreye metòd. Si ou telechaje pwojè a soti nan GitHub, Lè sa a, tout metòd yo nan dosye a bot_methods.R.

Kòd metòd bot

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

}

Nou kreye 5 metòd:

  • kòmanse — Kòmanse yon dyalòg
  • eta — Jwenn eta aktyèl la chat
  • reset — Reyajiste eta chat aktyèl la
  • enter_name — Bot la mande non w
  • enter_age — Bot la mande laj ou

Metòd start mande non ou, epi chanje eta chat la tann_non, i.e. sibstiti pou antre non ou.

Apre sa, ou voye non an epi li trete pa metòd la enter_name, bot la salye ou, ekri non yo resevwa nan baz done a, epi chanje chat la nan eta a tann_laj.

Nan etap sa a, bot la espere ou antre nan laj ou. Ou voye laj ou, bot la tcheke mesaj la, si ou voye kèk tèks olye de yon nimewo, li pral di: Ты ввёл некорректные данные, введи число, epi yo pral tann pou ou re-antre done ou yo. Si ou voye yon nimewo, bot la pral rapòte ke li te aksepte laj ou, ekri done yo resevwa nan baz done a, rapòte tout done yo resevwa nan men ou epi retounen eta chat la nan pozisyon orijinal li, i.e. V start.

Lè w rele metòd la state ou ka mande estati chat aktyèl la nenpòt ki lè, epi itilize reset retounen chat la nan eta orijinal li.

Filtè mesaj

Nan ka nou an, sa a se youn nan pati ki pi enpòtan nan bati yon bot. Se avèk èd filtè mesaj ke bot la pral konprann ki enfòmasyon li espere nan men ou ak ki jan li ta dwe trete.

Nan pwojè a sou GitHub filtè yo anrejistre nan dosye a message_filters.R.

Kòd filtre mesaj:

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

Nan filtè nou itilize fonksyon ki te ekri anvan an get_state(), yo nan lòd yo mande eta aktyèl la nan chat la. Fonksyon sa a mande sèlman 1 agiman, id chat.

Next filtre tann_non trete mesaj lè chat la nan yon eta wait_name, ak kòmsadwa filtè a tann_laj trete mesaj lè chat la nan yon eta wait_age.

Moun kap okipe yo

Yo rele dosye a ak moun kap okipe yo moun kap okipe yo.R, epi li gen kòd sa a:

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

Premyèman, nou kreye moun kap okipe kòmand ki pral pèmèt ou kouri metòd pou kòmanse yon dyalòg, reset li, ak rechèch eta aktyèl la.

Apre sa, nou kreye 2 moun kap okipe mesaj lè l sèvi avèk filtè yo te kreye nan etap anvan an, epi ajoute yon filtè nan yo !MessageFilters$command, pou nou ka itilize kòmandman nan nenpòt eta chat.

Kòd lansman bot

Koulye a, nou gen tout bagay pare yo lanse, kòd prensipal la pou lanse bot la se nan dosye a 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()

Kòm yon rezilta, nou te resevwa bot sa a:
Ekri yon bot telegram nan R (pati 4): Bati yon dyalòg ki konsistan, ki lojik ak bot la

A nenpòt ki lè lè l sèvi avèk lòd la /state nou ka mande eta aktyèl la chat, epi lè l sèvi avèk lòd la /reset retounen chat la nan eta orijinal li epi kòmanse dyalòg la ankò.

Konklizyon

Nan atik sa a, nou te konnen ki jan yo sèvi ak yon baz done andedan yon bot, ak ki jan yo bati sekans dyalòg lojik pa anrejistre eta a chat.

Nan ka sa a, nou te gade egzanp ki pi primitif la, pou li ta pi fasil pou ou konprann lide pou konstwi bots sa yo; an pratik, ou ka bati dyalòg pi konplèks.

Nan pwochen atik nan seri sa a, nou pral aprann kijan pou mete restriksyon sou dwa itilizatè bot yo sèvi ak divès metòd li yo.

Sous: www.habr.com

Add nouvo kòmantè