Shkrimi i një roboti telegram në R (pjesa 4): Ndërtimi i një dialogu të qëndrueshëm dhe logjik me robotin

Nëse e keni lexuar tashmë të mëparshmen tre nene nga kjo seri, atëherë ju tashmë dini se si të shkruani bots të plota të telegramit me një tastierë.

Në këtë artikull, ne do të mësojmë se si të shkruajmë një bot që do të mbajë një dialog të qëndrueshëm. ato. Bot do t'ju bëjë pyetje dhe do të presë që ju të futni disa informacione. Në varësi të të dhënave që futni, roboti do të kryejë disa veprime.

Gjithashtu në këtë artikull do të mësojmë se si të përdorim një bazë të dhënash nën kapuçin e robotit, në shembullin tonë do të jetë SQLite, por mund të përdorni çdo DBMS tjetër. Kam shkruar më në detaje rreth ndërveprimit me bazat e të dhënave në gjuhën R në Ky artikull.

Shkrimi i një roboti telegram në R (pjesa 4): Ndërtimi i një dialogu të qëndrueshëm dhe logjik me robotin

Të gjithë artikujt nga seria "Të shkruajmë një bot telegram në R"

  1. Ne krijojmë një bot dhe e përdorim për të dërguar mesazhe në telegram
  2. Shtoni mbështetjen e komandës dhe filtrat e mesazheve në bot
  3. Si të shtoni mbështetjen e tastierës në një robot
  4. Ndërtimi i një dialogu të qëndrueshëm dhe logjik me robotin

Përmbajtje

Nëse jeni të interesuar për analizën e të dhënave, mund të jeni të interesuar për mua telegram и youtube kanalet. Pjesa më e madhe e përmbajtjes i kushtohet gjuhës R.

  1. Paraqitje
  2. Procesi i ndërtimit të botit
  3. Struktura e projektit bot
  4. Konfigurimi i robotit
  5. Krijoni një variabël mjedisi
  6. Krijimi i një baze të dhënash
  7. Shkrimi i funksioneve për të punuar me bazën e të dhënave
  8. Metodat bot
  9. Filtrat e mesazheve
  10. Trajtuesit
  11. Kodi i nisjes së robotit
  12. Përfundim

Paraqitje

Në mënyrë që roboti të kërkojë të dhëna nga ju dhe të presë që ju të futni ndonjë informacion, do t'ju duhet të regjistroni gjendjen aktuale të dialogut. Mënyra më e mirë për ta bërë këtë është përdorimi i një lloji të bazës së të dhënave të integruar, siç është SQLite.

ato. Logjika do të jetë si më poshtë. Ne thërrasim metodën bot dhe roboti kërkon në mënyrë sekuenciale disa informacione nga ne dhe në çdo hap ai pret që ky informacion të futet dhe mund ta kontrollojë atë.

Ne do të shkruajmë botin më të thjeshtë të mundshëm, fillimisht ai do të kërkojë emrin tuaj, pastaj moshën tuaj dhe do të ruajë të dhënat e marra në bazën e të dhënave. Kur kërkon moshën, do të kontrollojë që të dhënat e futura të jenë numër dhe jo tekst.

Një dialog kaq i thjeshtë do të ketë vetëm tre gjendje:

  1. starti është gjendja normale e robotit, në të cilin ai nuk pret asnjë informacion nga ju
  2. Emri i pritjes - gjendja në të cilën roboti pret që të futet një emër
  3. Wash_mosha është gjendja në të cilën roboti pret që të futet mosha juaj, numri i viteve të plota.

Procesi i ndërtimit të botit

Gjatë artikullit, ne do të ndërtojmë një bot hap pas hapi; i gjithë procesi mund të përshkruhet në mënyrë skematike si më poshtë:
Shkrimi i një roboti telegram në R (pjesa 4): Ndërtimi i një dialogu të qëndrueshëm dhe logjik me robotin

  1. Ne krijojmë një konfigurim bot në të cilin do të ruajmë disa cilësime. Në rastin tonë, simboli bot dhe shtegu për në skedarin e bazës së të dhënave.
  2. Ne krijojmë një variabël mjedisi në të cilin do të ruhet shtegu i projektit me bot.
  3. Ne krijojmë vetë bazën e të dhënave dhe një numër funksionesh në mënyrë që roboti të mund të ndërveprojë me të.
  4. Ne shkruajmë metoda bot, d.m.th. funksionet që do të kryejë.
  5. Shtimi i filtrave të mesazheve. Me ndihmën e të cilit roboti do të aksesojë metodat e nevojshme, në varësi të gjendjes aktuale të bisedës.
  6. Ne shtojmë mbajtës që do të lidhin komandat dhe mesazhet me metodat e nevojshme të botit.
  7. Le të hapim robotin.

Struktura e projektit bot

Për lehtësi, ne do ta ndajmë kodin e botit tonë dhe skedarët e tjerë të lidhur, në strukturën e mëposhtme.

  • bot.R — kodi kryesor i botit tonë
  • db_bot_function.R — një bllok kodi me funksione për të punuar me bazën e të dhënave
  • metodat_bot.R — kodi i metodave bot
  • mesazhet_filtrat.R — filtrat e mesazheve
  • mbajtësit.R - mbajtësit
  • konfiguro.cfg - konfigurimi i botit
  • create_db_data.sql — Skript SQL për krijimin e një tabele me të dhëna chat në bazën e të dhënave
  • create_db_state.sql — Skript SQL për krijimin e një tabele të gjendjes aktuale të bisedës në bazën e të dhënave
  • bot.db - baza e të dhënave bot

Ju mund të shikoni të gjithë projektin bot, ose shkarko nga imja depo në GitHub.

Konfigurimi i robotit

Ne do të përdorim atë të zakonshmen si konfigurim skedar ini, forma e mëposhtme:

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

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

Në konfigurim shkruajmë tokenin bot dhe shtegun për në bazën e të dhënave, d.m.th. në skedarin bot.db; ne do ta krijojmë vetë skedarin në hapin tjetër.

Për bot më komplekse, mund të krijoni konfigurime më komplekse, përveç kësaj, nuk është e nevojshme të shkruani një konfigurim ini, mund të përdorni çdo format tjetër duke përfshirë JSON.

Krijoni një variabël mjedisi

Në çdo PC, dosja me projektin bot mund të vendoset në drejtori të ndryshme dhe në disqe të ndryshëm, kështu që në kod shtegu për në dosjen e projektit do të vendoset nëpërmjet një ndryshoreje mjedisi. TG_BOT_PATH.

Ka disa mënyra për të krijuar një variabël mjedisi, më e thjeshta është ta shkruani atë në një skedar .Renviron.

Ju mund të krijoni ose modifikoni këtë skedar duke përdorur komandën file.edit(path.expand(file.path("~", ".Renviron"))). Ekzekutoni atë dhe shtoni një rresht në skedar:

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

Më pas ruani skedarin .Renviron dhe rinisni RStudio.

Krijimi i një baze të dhënash

Hapi tjetër është krijimi i një baze të dhënash. Do të na duhen 2 tabela:

  • chat_data — të dhëna që roboti i kërkoi përdoruesit
  • chat_state — gjendja aktuale e të gjitha bisedave

Ju mund t'i krijoni këto tabela duke përdorur pyetjen e mëposhtme 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
);

Nëse keni shkarkuar projektin bot nga GitHub, më pas për të krijuar bazën e të dhënave mund të përdorni kodin e mëposhtëm në 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'))

Shkrimi i funksioneve për të punuar me bazën e të dhënave

Ne tashmë kemi një skedar konfigurimi gati dhe një bazë të dhënash të krijuar. Tani ju duhet të shkruani funksione për të lexuar dhe shkruar të dhëna në këtë bazë të dhënash.

Nëse e keni shkarkuar projektin nga GitHub, atëherë mund të gjeni funksionet në skedar db_bot_function.R.

Kodi i funksionit për të punuar me bazën e të dhënave

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

}

Ne krijuam 4 funksione të thjeshta:

  • get_state() — merrni gjendjen aktuale të bisedës nga baza e të dhënave
  • set_state() — shkruani gjendjen aktuale të bisedës në bazën e të dhënave
  • get_chat_data() — merrni të dhëna të dërguara nga përdoruesi
  • set_chat_data() — regjistroni të dhënat e marra nga përdoruesi

Të gjitha funksionet janë mjaft të thjeshta, ato ose lexojnë të dhëna nga baza e të dhënave duke përdorur komandën dbGetQuery(), ose angazhohen UPSERT operacion (ndryshimi i të dhënave ekzistuese ose shkrimi i të dhënave të reja në bazën e të dhënave), duke përdorur funksionin dbExecute().

Sintaksa për operacionin UPSERT është si më poshtë:

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

Ato. në fushën e tabelave tona chat_id ka një kufizim unike dhe është çelësi kryesor i tabelave. Fillimisht, ne përpiqemi të shtojmë informacion në tabelë dhe marrim një gabim nëse të dhënat për bisedën aktuale janë tashmë të pranishme, në këtë rast thjesht përditësojmë informacionin për këtë bisedë.

Më pas, ne do t'i përdorim këto funksione në metodat dhe filtrat e robotit.

Metodat bot

Hapi tjetër në ndërtimin e botit tonë është krijimi i metodave. Nëse e keni shkarkuar projektin nga GitHub, atëherë të gjitha metodat janë në skedar metodat_bot.R.

Kodi i metodës 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')
  }

}

Ne krijuam 5 metoda:

  • start - Filloni një dialog
  • gjendje — Merrni gjendjen aktuale të bisedës
  • reset — Rivendosni gjendjen aktuale të bisedës
  • enter_name — Roti kërkon emrin tuaj
  • enter_age - Boti kërkon moshën tuaj

Метод start kërkon emrin tuaj dhe ndryshon gjendjen e bisedës në emri i pritjes, d.m.th. në gatishmëri për të futur emrin tuaj.

Më pas, ju dërgoni emrin dhe ai përpunohet sipas metodës enter_name, roboti ju përshëndet, shkruan emrin e marrë në bazën e të dhënave dhe e kalon bisedën në gjendje mosha e pritjes.

Në këtë fazë, roboti pret që ju të shkruani moshën tuaj. Ju dërgoni moshën tuaj, roboti kontrollon mesazhin, nëse keni dërguar një tekst në vend të një numri, ai do të thotë: Ты ввёл некорректные данные, введи число, dhe do të presë që ju të rifusni të dhënat tuaja. Nëse keni dërguar një numër, roboti do të raportojë se ka pranuar moshën tuaj, do t'i shkruajë të dhënat e marra në bazën e të dhënave, do të raportojë të gjitha të dhënat e marra nga ju dhe do ta kthejë gjendjen e bisedës në pozicionin e tij origjinal, d.m.th. V start.

Duke thirrur metodën state mund të kërkoni statusin aktual të bisedës në çdo kohë dhe duke përdorur reset kthejeni bisedën në gjendjen e tij origjinale.

Filtrat e mesazheve

Në rastin tonë, kjo është një nga pjesët më të rëndësishme në ndërtimin e një boti. Është me ndihmën e filtrave të mesazheve që roboti do të kuptojë se çfarë informacioni pret nga ju dhe si duhet të përpunohet.

Në projektin mbi GitHub filtrat regjistrohen në skedar mesazhet_filtrat.R.

Kodi i filtrit të mesazhit:

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

Në filtra përdorim funksionin e shkruar më parë get_state(), për të kërkuar gjendjen aktuale të bisedës. Ky funksion kërkon vetëm 1 argument, id chat.

Filtri tjetër emri i pritjes përpunon mesazhet kur biseda është në gjendje wait_name, dhe në përputhje me rrethanat filtri mosha e pritjes përpunon mesazhet kur biseda është në gjendje wait_age.

Trajtuesit

Skedari me mbajtës thirret mbajtësit.R, dhe ka kodin e mëposhtëm:

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

Së pari ne krijojmë mbajtës të komandave që do t'ju lejojnë të ekzekutoni metoda për të nisur një dialog, për ta rivendosur atë dhe për të kërkuar gjendjen aktuale.

Më pas, ne krijojmë 2 mbajtës mesazhesh duke përdorur filtrat e krijuar në hapin e mëparshëm dhe shtojmë një filtër në to !MessageFilters$command, në mënyrë që të mund të përdorim komanda në çdo gjendje bisede.

Kodi i nisjes së robotit

Tani kemi gjithçka gati për të nisur, kodi kryesor për nisjen e robotit është në skedar 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()

Si rezultat, ne morëm këtë robot:
Shkrimi i një roboti telegram në R (pjesa 4): Ndërtimi i një dialogu të qëndrueshëm dhe logjik me robotin

Në çdo kohë duke përdorur komandën /state ne mund të kërkojmë gjendjen aktuale të bisedës dhe duke përdorur komandën /reset kthejeni bisedën në gjendjen e tij origjinale dhe filloni përsëri dialogun.

Përfundim

Në këtë artikull, ne kuptuam se si të përdorim një bazë të dhënash brenda një roboti dhe si të ndërtojmë dialogë logjikë të njëpasnjëshëm duke regjistruar gjendjen e bisedës.

Në këtë rast, ne shikuam shembullin më primitiv, në mënyrë që ta kuptoni më lehtë idenë e ndërtimit të robotëve të tillë; në praktikë, mund të ndërtoni dialogë shumë më kompleksë.

Në artikullin tjetër të kësaj serie, do të mësojmë se si të kufizojmë të drejtat e përdoruesve të robotëve për të përdorur metoda të ndryshme të tij.

Burimi: www.habr.com

Shto një koment