At skrive en telegram-bot i R (del 4): Opbygning af en konsekvent, logisk dialog med botten

Hvis du allerede har læst det foregående tre artikler fra denne serie, så ved du allerede, hvordan man skriver fuldgyldige telegram-bots med et tastatur.

I denne artikel vil vi lære, hvordan man skriver en bot, der vil opretholde en konsekvent dialog. De der. Botten vil stille dig spørgsmål og vente på, at du indtaster nogle oplysninger. Afhængigt af de data, du indtaster, vil botten udføre nogle handlinger.

Også i denne artikel vil vi lære, hvordan man bruger en database under hætten af ​​botten, i vores eksempel vil det være SQLite, men du kan bruge enhver anden DBMS. Jeg skrev mere detaljeret om interaktion med databaser på R-sproget i denne artikel.

At skrive en telegram-bot i R (del 4): Opbygning af en konsekvent, logisk dialog med botten

Alle artikler fra serien "Skriv en telegrambot i R"

  1. Vi opretter en bot og bruger den til at sende beskeder i telegram
  2. Tilføj kommandostøtte og beskedfiltre til botten
  3. Sådan tilføjer du tastaturunderstøttelse til en bot
  4. Opbygning af en konsekvent, logisk dialog med botten

Indhold

Hvis du er interesseret i dataanalyse, er du måske interesseret i min telegram и youtube kanaler. Det meste af indholdet er afsat til R-sproget.

  1. Indledning
  2. Bot byggeproces
  3. Bot projekt struktur
  4. Bot-konfiguration
  5. Opret en miljøvariabel
  6. Oprettelse af en database
  7. Skrivefunktioner til at arbejde med databasen
  8. Bot metoder
  9. Meddelelsesfiltre
  10. Håndtere
  11. Bot-lanceringskode
  12. Konklusion

Indledning

For at botten kan anmode om data fra dig og vente på, at du indtaster oplysninger, skal du registrere den aktuelle tilstand af dialogen. Den bedste måde at gøre dette på er at bruge en form for indlejret database, såsom SQLite.

De der. Logikken vil være som følger. Vi kalder bot-metoden, og botten anmoder sekventielt om nogle oplysninger fra os, og ved hvert trin venter den på, at disse oplysninger bliver indtastet og kan kontrollere dem.

Vi vil skrive den enklest mulige bot, først vil den bede om dit navn, derefter din alder og gemme de modtagne data i databasen. Når du spørger om alder, vil den kontrollere, at de indtastede data er et tal og ikke tekst.

En sådan simpel dialog vil kun have tre tilstande:

  1. start er bottens normale tilstand, hvor den ikke forventer nogen information fra dig
  2. wait_name - tilstand, hvor botten venter på, at et navn skal indtastes
  3. wait_age er den tilstand, hvor botten venter på, at din alder bliver indtastet, antallet af hele år.

Bot byggeproces

I løbet af artiklen vil vi bygge en bot trin for trin; hele processen kan skematisk afbildes som følger:
At skrive en telegram-bot i R (del 4): Opbygning af en konsekvent, logisk dialog med botten

  1. Vi opretter en bot-konfiguration, hvor vi gemmer nogle indstillinger. I vores tilfælde, bot-tokenet og stien til databasefilen.
  2. Vi opretter en miljøvariabel, hvor stien til projektet med botten vil blive gemt.
  3. Vi laver selve databasen, og en række funktioner, så botten kan interagere med den.
  4. Vi skriver bot-metoder, dvs. de funktioner, den vil udføre.
  5. Tilføjelse af beskedfiltre. Ved hjælp af hvilken botten får adgang til de nødvendige metoder, afhængigt af chattens aktuelle tilstand.
  6. Vi tilføjer handlere, der forbinder kommandoer og beskeder med de nødvendige bot-metoder.
  7. Lad os starte botten.

Bot projekt struktur

For nemheds skyld opdeler vi koden for vores bot og andre relaterede filer i følgende struktur.

  • bot.R — hovedkoden for vores bot
  • db_bot_function.R — en kodeblok med funktioner til at arbejde med databasen
  • bot_metoder.R — kode for bot-metoder
  • besked_filtre.R — beskedfiltre
  • handlere.R - handlere
  • config.cfg - bot config
  • create_db_data.sql — SQL-script til oprettelse af en tabel med chatdata i databasen
  • create_db_state.sql — SQL-script til oprettelse af en tabel over den aktuelle chattilstand i databasen
  • bot.db - bot database

Du kan se hele bot-projektet, eller скачать fra min repository på GitHub.

Bot-konfiguration

Vi vil bruge den sædvanlige som en konfiguration ini fil, følgende formular:

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

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

I konfigurationen skriver vi bot-tokenet og stien til databasen, dvs. til bot.db-filen; vi opretter selve filen i næste trin.

For mere komplekse bots kan du oprette mere komplekse konfigurationer, desuden er det ikke nødvendigt at skrive en ini-konfiguration, du kan bruge et hvilket som helst andet format inklusive JSON.

Opret en miljøvariabel

På hver pc kan mappen med botprojektet ligge i forskellige mapper og på forskellige drev, så i koden vil stien til projektmappen blive sat via en miljøvariabel TG_BOT_PATH.

Der er flere måder at oprette en miljøvariabel på, den enkleste er at skrive den i en fil .Renviron.

Du kan oprette eller redigere denne fil ved hjælp af kommandoen file.edit(path.expand(file.path("~", ".Renviron"))). Udfør det og tilføj en linje til filen:

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

Gem derefter filen .Renviron og genstart RStudio.

Oprettelse af en database

Det næste trin er at oprette en database. Vi skal bruge 2 borde:

  • chat_data — data, som botten anmodede om fra brugeren
  • chat_state — aktuelle tilstand for alle chats

Du kan oprette disse tabeller ved hjælp af følgende SQL-forespørgsel:

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

Hvis du downloadede bot-projektet fra GitHub, så for at oprette databasen kan du bruge følgende kode i 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'))

Skrivefunktioner til at arbejde med databasen

Vi har allerede en konfigurationsfil klar og en database oprettet. Nu skal du skrive funktioner for at læse og skrive data til denne database.

Hvis du har downloadet projektet fra GitHub, så kan du finde funktionerne i filen db_bot_function.R.

Funktionskode til at arbejde med databasen

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

}

Vi har lavet 4 simple funktioner:

  • get_state() — Hent den aktuelle chattilstand fra databasen
  • set_state() — skriv den aktuelle chattilstand til databasen
  • get_chat_data() — modtage data sendt af brugeren
  • set_chat_data() — registrere data modtaget fra brugeren

Alle funktioner er ret enkle, de læser enten data fra databasen ved hjælp af kommandoen dbGetQuery(), eller forpligte sig UPSERT operation (ændring af eksisterende data eller skrivning af nye data til databasen), ved hjælp af funktionen dbExecute().

Syntaksen for UPSERT-operationen er som følger:

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

De der. i vores tabelfelt chat_id har en unikhedsbegrænsning og er den primære nøgle for tabeller. I første omgang forsøger vi at tilføje oplysninger til tabellen, og vi får en fejl, hvis data for den aktuelle chat allerede er til stede, i så fald opdaterer vi blot oplysningerne for denne chat.

Dernæst vil vi bruge disse funktioner i botens metoder og filtre.

Bot metoder

Det næste trin i opbygningen af ​​vores bot er at skabe metoder. Hvis du har downloadet projektet fra GitHub, så er alle metoder i filen bot_metoder.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')
  }

}

Vi har lavet 5 metoder:

  • start — Start en dialog
  • tilstand — Hent den aktuelle chattilstand
  • nulstil — Nulstil den aktuelle chattilstand
  • enter_name — Botten beder om dit navn
  • enter_age — Botten beder om din alder

fremgangsmåde start beder om dit navn og ændrer chattilstanden til vente_navn, dvs. til standby for at indtaste dit navn.

Dernæst sender du navnet, og det behandles efter metoden enter_name, botten hilser dig, skriver det modtagne navn ind i databasen og skifter chatten til tilstanden vente_alder.

På dette stadium forventer botten, at du indtaster din alder. Du sender din alder, botten tjekker beskeden, hvis du har sendt noget tekst i stedet for et nummer, vil den sige: Ты ввёл некорректные данные, введи число, og vil vente på, at du indtaster dine data igen. Hvis du har sendt et nummer, vil botten rapportere, at den har accepteret din alder, skrive de modtagne data til databasen, rapportere alle data modtaget fra dig og returnere chattilstanden til dens oprindelige position, dvs. V start.

Ved at kalde metoden state du kan til enhver tid anmode om den aktuelle chatstatus og ved hjælp af reset returnere chatten til dens oprindelige tilstand.

Meddelelsesfiltre

I vores tilfælde er dette en af ​​de vigtigste dele i at bygge en bot. Det er ved hjælp af beskedfiltre, at botten vil forstå, hvilken information den forventer af dig, og hvordan den skal behandles.

I projektet vedr GitHub filtre er registreret i filen besked_filtre.R.

Meddelelsesfilterkode:

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

I filtre bruger vi den tidligere skrevne funktion get_state(), for at anmode om den aktuelle status for chatten. Denne funktion kræver kun 1 argument, chat-id.

Næste filter vente_navn behandler beskeder, når chatten er i en tilstand wait_nameog dermed filteret vente_alder behandler beskeder, når chatten er i en tilstand wait_age.

Håndtere

Filen med handlere kaldes handlere.R, og har følgende 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)

Først opretter vi kommandobehandlere, der giver dig mulighed for at køre metoder til at starte en dialog, nulstille den og forespørge om den aktuelle tilstand.

Dernæst opretter vi 2 meddelelsesbehandlere ved hjælp af de filtre, der blev oprettet i det foregående trin, og tilføjer et filter til dem !MessageFilters$command, så vi kan bruge kommandoer i enhver chattilstand.

Bot-lanceringskode

Nu har vi alt klar til at starte, hovedkoden til at starte botten er i filen 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()

Som et resultat fik vi denne bot:
At skrive en telegram-bot i R (del 4): Opbygning af en konsekvent, logisk dialog med botten

Når som helst ved hjælp af kommandoen /state vi kan forespørge om den aktuelle chattilstand og bruge kommandoen /reset returner chatten til dens oprindelige tilstand og start dialogen igen.

Konklusion

I denne artikel fandt vi ud af, hvordan man bruger en database inde i en bot, og hvordan man bygger sekventielle logiske dialoger ved at registrere chattilstanden.

I dette tilfælde kiggede vi på det mest primitive eksempel, så det ville være lettere for dig at forstå ideen om at bygge sådanne bots; i praksis kan du bygge meget mere komplekse dialoger.

I den næste artikel i denne serie vil vi lære, hvordan man begrænser botbrugeres rettigheder til at bruge forskellige af dets metoder.

Kilde: www.habr.com

Tilføj en kommentar