Skrive en telegram-bot i R (del 4): Bygge en konsistent, logisk dialog med boten

Hvis du allerede har lest forrige tre artikler fra denne serien, så vet du allerede hvordan du skriver fullverdige telegramroboter med et tastatur.

I denne artikkelen vil vi lære hvordan du skriver en bot som vil opprettholde en konsistent dialog. De. Boten vil stille deg spørsmål og vente på at du legger inn informasjon. Avhengig av dataene du legger inn, vil boten utføre noen handlinger.

Også i denne artikkelen vil vi lære hvordan du bruker en database under panseret til boten, i vårt eksempel vil det være SQLite, men du kan bruke hvilken som helst annen DBMS. Jeg skrev mer detaljert om samhandling med databaser på R-språket i denne artikkelen.

Skrive en telegram-bot i R (del 4): Bygge en konsistent, logisk dialog med boten

Alle artikler fra serien "Skrive en telegram-bot i R"

  1. Vi lager en bot og bruker den til å sende meldinger i telegram
  2. Legg til kommandostøtte og meldingsfiltre til boten
  3. Hvordan legge til tastaturstøtte til en bot
  4. Bygge en konsistent, logisk dialog med boten

Innhold

Hvis du er interessert i dataanalyse, kan du være interessert i min telegram и youtube kanaler. Det meste av innholdet er dedikert til R-språket.

  1. Innledning
  2. Bot byggeprosess
  3. Bot prosjektstruktur
  4. Bot konfig
  5. Lag en miljøvariabel
  6. Opprette en database
  7. Skrivefunksjoner for å jobbe med databasen
  8. Bot metoder
  9. Meldingsfiltre
  10. Håndtere
  11. Bot lanseringskode
  12. Konklusjon

Innledning

For at boten skal be om data fra deg og vente på at du legger inn informasjon, må du registrere den nåværende statusen til dialogen. Den beste måten å gjøre dette på er å bruke en slags innebygd database, for eksempel SQLite.

De. Logikken vil være som følger. Vi kaller bot-metoden, og boten ber sekvensielt om litt informasjon fra oss, og ved hvert trinn venter den på at denne informasjonen skal legges inn og kan sjekke den.

Vi vil skrive den enkleste mulige boten, først vil den spørre etter navnet ditt, deretter alderen din, og vil lagre de mottatte dataene i databasen. Når du spør om alder, vil den sjekke at de angitte dataene er et tall og ikke tekst.

En slik enkel dialog vil bare ha tre tilstander:

  1. start er den normale tilstanden til boten, der den ikke forventer noen informasjon fra deg
  2. wait_name - tilstand der boten venter på at et navn skal skrives inn
  3. wait_age er tilstanden der boten venter på at alderen din skal legges inn, antall hele år.

Bot byggeprosess

I løpet av artikkelen vil vi bygge en bot trinn for trinn; hele prosessen kan skjematisk avbildes som følger:
Skrive en telegram-bot i R (del 4): Bygge en konsistent, logisk dialog med boten

  1. Vi lager en bot-konfigurasjon der vi vil lagre noen innstillinger. I vårt tilfelle, bot-tokenet og banen til databasefilen.
  2. Vi lager en miljøvariabel der banen til prosjektet med boten vil bli lagret.
  3. Vi lager selve databasen, og en rekke funksjoner slik at boten kan samhandle med den.
  4. Vi skriver botmetoder, dvs. funksjonene den skal utføre.
  5. Legger til meldingsfiltre. Ved hjelp av hvilken vil boten få tilgang til de nødvendige metodene, avhengig av den nåværende statusen til chatten.
  6. Vi legger til behandlere som vil koble kommandoer og meldinger med de nødvendige botmetodene.
  7. La oss starte boten.

Bot prosjektstruktur

For enkelhets skyld deler vi koden til boten vår og andre relaterte filer i følgende struktur.

  • bot.R — hovedkoden til boten vår
  • db_bot_function.R — en kodeblokk med funksjoner for arbeid med en database
  • bot_metoder.R — kode for bot-metoder
  • meldingsfiltre.R — meldingsfiltre
  • handlere.R - behandlere
  • config.cfg - bot konfig
  • create_db_data.sql — SQL-skript for å lage en tabell med chatdata i databasen
  • create_db_state.sql — SQL-skript for å lage en tabell over gjeldende chat-status i databasen
  • bot.db - bot database

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

Bot konfig

Vi vil bruke den vanlige som en konfigurasjon ini filen, følgende skjema:

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

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

I konfigurasjonen skriver vi bot-tokenet og banen til databasen, dvs. til bot.db-filen; vi lager selve filen i neste trinn.

For mer komplekse roboter kan du lage mer komplekse konfigurasjoner, dessuten er det ikke nødvendig å skrive en ini-konfigurasjon, du kan bruke et hvilket som helst annet format inkludert JSON.

Lag en miljøvariabel

På hver PC kan mappen med botprosjektet ligge i forskjellige kataloger og på forskjellige stasjoner, så i koden vil banen til prosjektmappen bli satt via en miljøvariabel TG_BOT_PATH.

Det er flere måter å lage en miljøvariabel på, den enkleste er å skrive den i en fil .Renviron.

Du kan opprette eller redigere denne filen ved å bruke kommandoen file.edit(path.expand(file.path("~", ".Renviron"))). Kjør den og legg til en linje i filen:

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

Lagre deretter filen .Renviron og start RStudio på nytt.

Opprette en database

Det neste trinnet er å lage en database. Vi trenger 2 bord:

  • chat_data — data som boten ba om fra brukeren
  • chat_state — gjeldende status for alle chatter

Du kan opprette disse tabellene ved å bruke følgende SQL-spørring:

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 lastet ned bot-prosjektet fra GitHub, for å lage databasen kan du bruke 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'))

Skrivefunksjoner for å jobbe med databasen

Vi har allerede en konfigurasjonsfil klar og en database opprettet. Nå må du skrive funksjoner for å lese og skrive data til denne databasen.

Hvis du lastet ned prosjektet fra GitHub, så kan du finne funksjonene i filen db_bot_function.R.

Funksjonskode for arbeid 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 laget 4 enkle funksjoner:

  • get_state() — få gjeldende chat-status fra databasen
  • set_state() — skriv gjeldende chat-status til databasen
  • get_chat_data() — motta data sendt av brukeren
  • set_chat_data() — registrere data mottatt fra brukeren

Alle funksjoner er ganske enkle, de leser enten data fra databasen ved hjelp av kommandoen dbGetQuery(), eller forplikte seg UPSERT operasjon (endre eksisterende data eller skrive nye data til databasen), ved å bruke funksjonen dbExecute().

Syntaksen for UPSERT-operasjonen 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. i vårt tabellfelt chat_id har en unikhetsbegrensning og er hovednøkkelen til tabeller. Til å begynne med prøver vi å legge til informasjon i tabellen, og vi får en feilmelding hvis data for gjeldende chat allerede er tilstede, i så fall oppdaterer vi ganske enkelt informasjonen for denne chatten.

Deretter vil vi bruke disse funksjonene i botens metoder og filtre.

Bot metoder

Det neste trinnet i å bygge vår bot er å lage metoder. Hvis du lastet ned prosjektet fra GitHub, så er alle metodene i filen bot_metoder.R.

Bot metodekode

# ###########################################################
# 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 laget 5 metoder:

  • start — Start en dialogboks
  • tilstand — Få gjeldende chatstatus
  • tilbakestill — Tilbakestill gjeldende chat-status
  • enter_name — Boten spør etter navnet ditt
  • enter_age — Boten spør om alderen din

metode start ber om navnet ditt, og endrer chat-tilstanden til wait_name, dvs. til standby for å skrive inn navnet ditt.

Deretter sender du navnet og det behandles av metoden enter_name, boten hilser deg, skriver det mottatte navnet inn i databasen og bytter chatten til tilstanden vente_alder.

På dette stadiet forventer boten at du skriver inn alderen din. Du sender alderen din, boten sjekker meldingen, hvis du sendte litt tekst i stedet for et nummer, vil den si: Ты ввёл некорректные данные, введи число, og vil vente på at du legger inn dataene dine på nytt. Hvis du sendte et nummer, vil boten rapportere at den har akseptert alderen din, skrive de mottatte dataene til databasen, rapportere alle dataene mottatt fra deg og returnere chat-tilstanden til sin opprinnelige posisjon, dvs. V start.

Ved å kalle metoden state du kan be om gjeldende chat-status når som helst, og ved å bruke reset tilbakestille chatten til dens opprinnelige tilstand.

Meldingsfiltre

I vårt tilfelle er dette en av de viktigste delene i å bygge en bot. Det er ved hjelp av meldingsfiltre at boten vil forstå hvilken informasjon den forventer av deg og hvordan den skal behandles.

I prosjektet på GitHub filtre er registrert i filen meldingsfiltre.R.

Meldingsfilterkode:

# ###########################################################
# 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 bruker vi den tidligere skrevne funksjonen get_state(), for å be om gjeldende status for chatten. Denne funksjonen krever kun 1 argument, chat-ID.

Neste filter wait_name behandler meldinger når chatten er i en tilstand wait_name, og følgelig filteret vente_alder behandler meldinger når chatten er i en tilstand wait_age.

Håndtere

Filen med behandlere kalles 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 lager vi kommandobehandlere som lar deg kjøre metoder for å starte en dialogboks, tilbakestille den og spørre om gjeldende tilstand.

Deretter oppretter vi 2 meldingsbehandlere ved å bruke filtrene som ble opprettet i forrige trinn, og legger til et filter til dem !MessageFilters$command, slik at vi kan bruke kommandoer i enhver chat-tilstand.

Bot lanseringskode

Nå har vi alt klart for lansering, hovedkoden for å starte boten 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 fikk vi denne boten:
Skrive en telegram-bot i R (del 4): Bygge en konsistent, logisk dialog med boten

Når som helst ved å bruke kommandoen /state vi kan spørre om gjeldende chat-status og bruke kommandoen /reset returner chatten til dens opprinnelige tilstand og start dialogen på nytt.

Konklusjon

I denne artikkelen fant vi ut hvordan du bruker en database i en bot, og hvordan du bygger sekvensielle logiske dialoger ved å registrere chat-tilstanden.

I dette tilfellet så vi på det mest primitive eksemplet, slik at det ville være lettere for deg å forstå ideen om å bygge slike roboter; i praksis kan du bygge mye mer komplekse dialoger.

I den neste artikkelen i denne serien vil vi lære hvordan du begrenser rettighetene til bot-brukere til å bruke ulike metoder.

Kilde: www.habr.com

Legg til en kommentar