Skryf 'n telegram-bot in R (deel 4): Bou 'n konsekwente, logiese dialoog met die bot

As jy reeds die vorige gelees het drie artikels uit hierdie reeks, dan weet jy reeds hoe om volwaardige telegram-bots met 'n sleutelbord te skryf.

In hierdie artikel sal ons leer hoe om 'n bot te skryf wat 'n konsekwente dialoog sal handhaaf. Dié. Die bot sal jou vrae vra en wag vir jou om inligting in te voer. Afhangende van die data wat jy invoer, sal die bot 'n paar aksies uitvoer.

Ook in hierdie artikel sal ons leer hoe om 'n databasis onder die kap van die bot te gebruik, in ons voorbeeld sal dit SQLite wees, maar jy kan enige ander DBMS gebruik. Ek het in meer besonderhede geskryf oor interaksie met databasisse in die R-taal in Hierdie artikel.

Skryf 'n telegram-bot in R (deel 4): Bou 'n konsekwente, logiese dialoog met die bot

Alle artikels uit die reeks "Skryf 'n telegram-bot in R"

  1. Ons skep 'n bot en gebruik dit om boodskappe in telegram te stuur
  2. Voeg opdragondersteuning en boodskapfilters by die bot
  3. Hoe om sleutelbordondersteuning by 'n bot te voeg
  4. Bou 'n konsekwente, logiese dialoog met die bot

inhoud

As jy belangstel in data-analise, sal jy dalk belangstel in my telegram и YouTube kanale. Die meeste van die inhoud daarvan word aan die R-taal gewy.

  1. Inleiding
  2. Bot bou proses
  3. Bot projek struktuur
  4. Bot config
  5. Skep 'n omgewingsveranderlike
  6. Die skep van 'n databasis
  7. Skryffunksies om met die databasis te werk
  8. Bot metodes
  9. Boodskap filters
  10. Hanteerders
  11. Bot bekendstelling kode
  12. Gevolgtrekking

Inleiding

Om vir die bot data van jou te versoek en te wag vir jou om enige inligting in te voer, sal jy die huidige stand van die dialoog moet opneem. Die beste manier om dit te doen is om 'n soort ingebedde databasis te gebruik, soos SQLite.

Dié. Die logika sal soos volg wees. Ons noem die bot-metode, en die bot versoek opeenvolgend inligting van ons, en by elke stap wag dit vir hierdie inligting om in te voer en kan dit nagaan.

Ons sal die eenvoudigste moontlike bot skryf, eers sal dit jou naam vra, dan jou ouderdom, en sal die ontvangde data in die databasis stoor. Wanneer daar vir ouderdom gevra word, sal dit kontroleer dat die ingevoerde data 'n nommer is en nie teks nie.

So 'n eenvoudige dialoog sal slegs drie toestande hê:

  1. begin is die normale toestand van die bot, waarin dit geen inligting van jou verwag nie
  2. wag_naam - staat waarin die bot wag vir 'n naam om in te voer
  3. wait_age is die toestand waarin die bot wag dat jou ouderdom ingevoer word, die aantal volle jare.

Bot bou proses

Tydens die artikel sal ons stap vir stap 'n bot bou; die hele proses kan skematies soos volg uitgebeeld word:
Skryf 'n telegram-bot in R (deel 4): Bou 'n konsekwente, logiese dialoog met die bot

  1. Ons skep 'n bot-konfigurasie waarin ons 'n paar instellings sal stoor. In ons geval, die bot-token en die pad na die databasislêer.
  2. Ons skep 'n omgewingsveranderlike waarin die pad na die projek met die bot gestoor sal word.
  3. Ons skep die databasis self, en 'n aantal funksies sodat die bot daarmee kan kommunikeer.
  4. Ons skryf botmetodes, m.a.w. die funksies wat dit sal verrig.
  5. Voeg boodskapfilters by. Met die hulp waarvan die bot toegang tot die nodige metodes sal kry, afhangende van die huidige toestand van die klets.
  6. Ons voeg hanteerders by wat opdragte en boodskappe met die nodige botmetodes sal verbind.
  7. Kom ons begin die bot.

Bot projek struktuur

Gerieflikheidshalwe sal ons die kode van ons bot, en ander verwante lêers, in die volgende struktuur verdeel.

  • bot.R - die hoofkode van ons bot
  • db_bot_function.R — 'n blok kode met funksies om met die databasis te werk
  • bot_metodes.R - kode van botmetodes
  • boodskap_filters.R - boodskap filters
  • hanteerders.R - hanteerders
  • config.cfg - bot config
  • skep_db_data.sql - SQL-skrip vir die skep van 'n tabel met kletsdata in die databasis
  • skep_db_staat.sql - SQL script vir die skep van 'n tabel van die huidige klets toestand in die databasis
  • bot.db - bot databasis

Jy kan die hele botprojek sien, of download van my bewaarplek op GitHub.

Bot config

Ons sal die gewone een as 'n konfigurasie gebruik ini lêer, die volgende vorm:

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

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

In die config skryf ons die bot-token en die pad na die databasis, d.w.s. na die bot.db-lêer; ons sal die lêer self in die volgende stap skep.

Vir meer komplekse bots, kan jy meer komplekse konfigurasies skep, buitendien is dit nie nodig om 'n ini config te skryf nie, jy kan enige ander formaat gebruik, insluitend JSON.

Skep 'n omgewingsveranderlike

Op elke rekenaar kan die vouer met die botprojek in verskillende dopgehou en op verskillende aandrywers geleë wees, dus in die kode sal die pad na die projeklêer via 'n omgewingsveranderlike gestel word TG_BOT_PATH.

Daar is verskeie maniere om 'n omgewingsveranderlike te skep, die eenvoudigste is om dit in 'n lêer te skryf .Omgewing.

Jy kan hierdie lêer skep of wysig deur die opdrag te gebruik file.edit(path.expand(file.path("~", ".Renviron"))). Voer dit uit en voeg een reël by die lêer:

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

Stoor dan die lêer .Omgewing en herbegin RStudio.

Die skep van 'n databasis

Die volgende stap is om 'n databasis te skep. Ons benodig 2 tafels:

  • chat_data — data wat die bot van die gebruiker aangevra het
  • chat_state — huidige toestand van alle geselsies

U kan hierdie tabelle skep deur die volgende SQL-navraag te gebruik:

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 jy die botprojek afgelaai het vanaf GitHub, dan kan jy die volgende kode in R gebruik om die databasis te skep.

# Скрипт создания базы данных
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'))

Skryffunksies om met die databasis te werk

Ons het reeds 'n konfigurasielêer gereed en 'n databasis geskep. Nou moet jy funksies skryf om data te lees en na hierdie databasis te skryf.

As jy die projek afgelaai het vanaf GitHub, dan kan jy die funksies in die lêer vind db_bot_function.R.

Funksiekode om met die databasis te werk

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

}

Ons het 4 eenvoudige funksies geskep:

  • get_state() - kry die huidige kletsstatus vanaf die databasis
  • set_state() - skryf die huidige kletstoestand na die databasis
  • get_chat_data() - ontvang data wat deur die gebruiker gestuur is
  • set_chat_data() - rekord data wat van die gebruiker ontvang is

Alle funksies is redelik eenvoudig, hulle lees óf data vanaf die databasis met behulp van die opdrag dbGetQuery(), of pleeg UPSERT bewerking (verander bestaande data of skryf nuwe data na die databasis), met behulp van die funksie dbExecute().

Die sintaksis vir die UPSERT-bewerking is soos volg:

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

Dié. in ons tabelle veld chat_id het 'n uniekheidsbeperking en is die primêre sleutel van tabelle. Aanvanklik probeer ons om inligting by die tabel te voeg, en ons kry 'n fout as data vir die huidige klets reeds teenwoordig is, in welke geval ons bloot die inligting vir hierdie klets bywerk.

Vervolgens sal ons hierdie funksies in die bot se metodes en filters gebruik.

Bot metodes

Die volgende stap in die bou van ons bot is om metodes te skep. As jy die projek afgelaai het vanaf GitHub, dan is alle metodes in die lêer bot_metodes.R.

Bot metode kode

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

}

Ons het 5 metodes geskep:

  • begin — Begin 'n dialoog
  • staat — Kry die huidige kletsstatus
  • stel terug — Stel die huidige kletsstatus terug
  • enter_name — Die bot vra vir jou naam
  • enter_age — Die bot vra vir jou ouderdom

metode start vra vir jou naam, en verander die kletstoestand na wag_naam, d.w.s. na bystand vir die invoer van jou naam.

Vervolgens stuur jy die naam en dit word volgens die metode verwerk enter_name, die bot groet jou, skryf die ontvangde naam in die databasis, en skakel die klets na die toestand wag_ouderdom.

Op hierdie stadium verwag die bot van jou om jou ouderdom in te voer. Jy stuur jou ouderdom, die bot kontroleer die boodskap, as jy 'n teks gestuur het in plaas van 'n nommer, sal dit sê: Ты ввёл некорректные данные, введи число, en sal wag vir jou om jou data weer in te voer. As jy 'n nommer gestuur het, sal die bot rapporteer dat hy jou ouderdom aanvaar het, die ontvangde data na die databasis skryf, al die data wat van jou ontvang is rapporteer en die kletstoestand na sy oorspronklike posisie terugstuur, d.w.s. V start.

Deur die metode te noem state jy kan enige tyd die huidige kletsstatus aanvra, en die gebruik van die reset herstel die klets na sy oorspronklike toestand.

Boodskap filters

In ons geval is dit een van die belangrikste dele in die bou van 'n bot. Dit is met behulp van boodskapfilters dat die bot sal verstaan ​​watter inligting hy van jou verwag en hoe dit verwerk moet word.

In die projek op GitHub filters word in die lêer geregistreer boodskap_filters.R.

Boodskapfilterkode:

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

In filters gebruik ons ​​die voorheen geskrewe funksie get_state(), om die huidige toestand van die klets te versoek. Hierdie funksie vereis slegs 1 argument, klets-ID.

Volgende filter wag_naam verwerk boodskappe wanneer die klets in 'n toestand is wait_name, en dienooreenkomstig die filter wag_ouderdom verwerk boodskappe wanneer die klets in 'n toestand is wait_age.

Hanteerders

Die lêer met hanteerders word genoem hanteerders.R, en het die volgende kode:

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

Eerstens skep ons opdraghanteerders wat jou sal toelaat om metodes uit te voer om 'n dialoog te begin, dit terug te stel en navraag te doen oor die huidige toestand.

Vervolgens skep ons 2 boodskaphanteerders met behulp van die filters wat in die vorige stap geskep is, en voeg 'n filter by hulle !MessageFilters$command, sodat ons opdragte in enige kletstoestand kan gebruik.

Bot bekendstelling kode

Nou het ons alles gereed om te begin, die hoofkode vir die bekendstelling van die bot is in die lêer 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 gevolg hiervan het ons hierdie bot gekry:
Skryf 'n telegram-bot in R (deel 4): Bou 'n konsekwente, logiese dialoog met die bot

Gebruik die opdrag te eniger tyd /state ons kan die huidige kletsstatus navraag doen en die opdrag gebruik /reset bring die klets terug na sy oorspronklike toestand en begin die dialoog weer.

Gevolgtrekking

In hierdie artikel het ons uitgevind hoe om 'n databasis binne 'n bot te gebruik, en hoe om opeenvolgende logiese dialoë te bou deur die kletstoestand op te teken.

In hierdie geval het ons na die mees primitiewe voorbeeld gekyk, sodat dit vir jou makliker sou wees om die idee van die bou van sulke bots te verstaan; in die praktyk kan jy baie meer komplekse dialoë bou.

In die volgende artikel in hierdie reeks sal ons leer hoe om die regte van botgebruikers te beperk om verskeie van sy metodes te gebruik.

Bron: will.com

Voeg 'n opmerking