Een telegrambot schrijven in R (deel 4): Een consistente, logische dialoog opbouwen met de bot

Mocht je het vorige al gelezen hebben drie artikelen uit deze serie, dan weet je al hoe je volwaardige telegrambots met een toetsenbord schrijft.

In dit artikel zullen we leren hoe we een bot kunnen schrijven die een consistente dialoog onderhoudt. Die. De bot zal u vragen stellen en wachten tot u wat informatie invoert. Afhankelijk van de gegevens die u invoert, zal de bot enkele acties uitvoeren.

Ook in dit artikel zullen we leren hoe we een database onder de motorkap van de bot kunnen gebruiken, in ons voorbeeld zal het SQLite zijn, maar je kunt elk ander DBMS gebruiken. Ik heb meer gedetailleerd geschreven over de interactie met databases in de R-taal in dit artikel.

Een telegrambot schrijven in R (deel 4): Een consistente, logische dialoog opbouwen met de bot

Alle artikelen uit de serie “Een telegrambot schrijven in R”

  1. We maken een bot en gebruiken deze om berichten in telegram te verzenden
  2. Voeg opdrachtondersteuning en berichtfilters toe aan de bot
  3. Hoe toetsenbordondersteuning aan een bot toe te voegen
  4. Een consistente, logische dialoog opbouwen met de bot

Inhoud

Als u geïnteresseerd bent in data-analyse, bent u wellicht geïnteresseerd in mijn telegram и youtube kanalen. Het grootste deel van de inhoud is gewijd aan de R-taal.

  1. Introductie
  2. Bot-bouwproces
  3. Bot-projectstructuur
  4. Bot-configuratie
  5. Maak een omgevingsvariabele
  6. Een databank maken
  7. Functies schrijven om met de database te werken
  8. Bot-methoden
  9. Berichtfilters
  10. Behandelaars
  11. Bot-lanceringscode
  12. Conclusie

Introductie

Om ervoor te zorgen dat de bot gegevens van u opvraagt ​​en wacht tot u informatie invoert, moet u de huidige status van de dialoog vastleggen. De beste manier om dit te doen is door een soort ingebedde database te gebruiken, zoals SQLite.

Die. De logica zal als volgt zijn. We noemen de botmethode en de bot vraagt ​​achtereenvolgens wat informatie van ons, en bij elke stap wacht hij tot deze informatie is ingevoerd en kan hij deze controleren.

We zullen de eenvoudigst mogelijke bot schrijven, eerst zal deze om uw naam vragen, vervolgens uw leeftijd, en de ontvangen gegevens in de database opslaan. Wanneer naar de leeftijd wordt gevraagd, wordt gecontroleerd of de ingevoerde gegevens een getal zijn en geen tekst.

Zo’n eenvoudige dialoog zal slechts drie toestanden kennen:

  1. start is de normale toestand van de bot, waarin deze geen informatie van u verwacht
  2. wait_name - status waarin de bot wacht tot een naam wordt ingevoerd
  3. wait_age is de staat waarin de bot wacht tot uw leeftijd is ingevoerd, het aantal volledige jaren.

Bot-bouwproces

Tijdens het artikel gaan we stap voor stap een bot bouwen; het hele proces kan als volgt schematisch worden weergegeven:
Een telegrambot schrijven in R (deel 4): Een consistente, logische dialoog opbouwen met de bot

  1. We maken een botconfiguratie waarin we enkele instellingen opslaan. In ons geval het bottoken en het pad naar het databasebestand.
  2. We maken een omgevingsvariabele aan waarin het pad naar het project met de bot wordt opgeslagen.
  3. We creëren de database zelf en een aantal functies zodat de bot ermee kan communiceren.
  4. We schrijven botmethoden, d.w.z. de functies die het zal uitvoeren.
  5. Berichtfilters toevoegen. Met behulp waarvan de bot toegang krijgt tot de noodzakelijke methoden, afhankelijk van de huidige status van de chat.
  6. We voegen handlers toe die opdrachten en berichten verbinden met de benodigde botmethoden.
  7. Laten we de bot lanceren.

Bot-projectstructuur

Voor het gemak zullen we de code van onze bot en andere gerelateerde bestanden in de volgende structuur verdelen.

  • bot.R — de hoofdcode van onze bot
  • db_bot_function.R — een codeblok met functies voor het werken met een database
  • bot_methods.R - code van botmethoden
  • message_filters.R — berichtfilters
  • begeleiders.R - behandelaars
  • config.cfg - bot-configuratie
  • create_db_data.sql — SQL-script voor het maken van een tabel met chatgegevens in de database
  • create_db_state.sql — SQL-script voor het maken van een tabel met de huidige chatstatus in de database
  • bot.db - botdatabase

U kunt het volledige botproject bekijken, of скачать van mijn opslagplaats op GitHub.

Bot-configuratie

We zullen de gebruikelijke gebruiken als configuratie ini-bestand, het volgende formulier:

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

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

In de configuratie schrijven we het bottoken en het pad naar de database, d.w.z. naar het bot.db-bestand; we zullen het bestand zelf in de volgende stap maken.

Voor complexere bots kun je complexere configuraties maken. Bovendien is het niet nodig om een ​​ini-configuratie te schrijven, je kunt elk ander formaat gebruiken, inclusief JSON.

Maak een omgevingsvariabele

Op elke pc kan de map met het botproject zich in verschillende mappen en op verschillende schijven bevinden, dus in de code wordt het pad naar de projectmap ingesteld via een omgevingsvariabele TG_BOT_PATH.

Er zijn verschillende manieren om een ​​omgevingsvariabele te maken; de eenvoudigste is om deze in een bestand te schrijven .Reviron.

U kunt dit bestand maken of bewerken met de opdracht file.edit(path.expand(file.path("~", ".Renviron"))). Voer het uit en voeg één regel toe aan het bestand:

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

Sla het bestand vervolgens op .Reviron en start RStudio opnieuw.

Een databank maken

De volgende stap is het maken van een database. We hebben 2 tabellen nodig:

  • chat_data — gegevens die de bot bij de gebruiker heeft opgevraagd
  • chat_state — huidige status van alle chats

U kunt deze tabellen maken met behulp van de volgende SQL-query:

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

Als u het botproject hebt gedownload van GitHub, en om de database te maken, kunt u de volgende code in R gebruiken.

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

Functies schrijven om met de database te werken

We hebben al een configuratiebestand klaar en een database gemaakt. Nu moet u functies schrijven om gegevens naar deze database te lezen en te schrijven.

Als u het project hebt gedownload van GitHub, dan kun je de functies in het bestand vinden db_bot_function.R.

Functiecode voor het werken met de database

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

}

We hebben 4 eenvoudige functies gemaakt:

  • get_state() — haal de huidige chatstatus op uit de database
  • set_state() — schrijf de huidige chatstatus naar de database
  • get_chat_data() — gegevens ontvangen die door de gebruiker zijn verzonden
  • set_chat_data() — gegevens registreren die zijn ontvangen van de gebruiker

Alle functies zijn vrij eenvoudig: ze lezen gegevens uit de database met behulp van de opdracht dbGetQuery(), of plegen UPSERT bewerking (bestaande gegevens wijzigen of nieuwe gegevens naar de database schrijven), met behulp van de functie dbExecute().

De syntaxis voor de UPSERT-bewerking is als volgt:

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

Die. in ons tafelsveld chat_id heeft een uniciteitsbeperking en is de primaire sleutel van tabellen. In eerste instantie proberen we informatie aan de tabel toe te voegen en krijgen we een foutmelding als er al gegevens voor de huidige chat aanwezig zijn. In dat geval werken we eenvoudigweg de informatie voor deze chat bij.

Vervolgens zullen we deze functies gebruiken in de methoden en filters van de bot.

Bot-methoden

De volgende stap bij het bouwen van onze bot is het creëren van methoden. Als u het project hebt gedownload van GitHub, dan staan ​​alle methoden in het bestand bot_methods.R.

Bot-methodecode

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

}

We hebben 5 methoden gemaakt:

  • start — Start een dialoog
  • status — Haal de huidige chatstatus op
  • reset — Reset de huidige chatstatus
  • enter_name — De bot vraagt ​​om uw naam
  • enter_age — De bot vraagt ​​naar je leeftijd

werkwijze start vraagt ​​om je naam en verandert de chatstatus in wacht_naam, d.w.z. stand-by staan ​​voor het invoeren van uw naam.

Vervolgens verzend je de naam en deze wordt door de methode verwerkt enter_name, begroet de bot je, schrijft de ontvangen naam in de database en schakelt de chat over naar de status wacht_leeftijd.

In dit stadium verwacht de bot dat u uw leeftijd invoert. U verzendt uw leeftijd, de bot controleert het bericht, als u een sms verzendt in plaats van een nummer, staat er: Ты ввёл некорректные данные, введи числоen wacht tot u uw gegevens opnieuw invoert. Als u een nummer heeft verzonden, meldt de bot dat hij uw leeftijd heeft geaccepteerd, schrijft hij de ontvangen gegevens naar de database, rapporteert hij alle van u ontvangen gegevens en zet hij de chatstatus terug naar zijn oorspronkelijke positie, d.w.z. V start.

Door de methode aan te roepen state u kunt op elk moment de huidige chatstatus opvragen via de reset breng de chat terug naar de oorspronkelijke staat.

Berichtfilters

In ons geval is dit een van de belangrijkste onderdelen bij het bouwen van een bot. Met behulp van berichtenfilters begrijpt de bot welke informatie hij van u verwacht en hoe deze moet worden verwerkt.

In het project op GitHub filters worden in het bestand geregistreerd message_filters.R.

Berichtfiltercode:

# ###########################################################
# 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 gebruiken we de eerder geschreven functie get_state(), om de huidige status van de chat op te vragen. Deze functie vereist slechts 1 argument, chat-ID.

Volgende filter wacht_naam verwerkt berichten wanneer de chat zich in een status bevindt wait_name, en dienovereenkomstig het filter wacht_leeftijd verwerkt berichten wanneer de chat zich in een status bevindt wait_age.

Behandelaars

Het bestand met handlers wordt aangeroepen begeleiders.R, en heeft de volgende code:

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

Eerst maken we opdrachthandlers waarmee u methoden kunt uitvoeren om een ​​dialoogvenster te starten, het opnieuw in te stellen en de huidige status op te vragen.

Vervolgens maken we twee berichthandlers met behulp van de filters die in de vorige stap zijn gemaakt, en voegen we er een filter aan toe !MessageFilters$command, zodat we opdrachten in elke chatstatus kunnen gebruiken.

Bot-lanceringscode

Nu hebben we alles klaar om te starten, de hoofdcode voor het starten van de bot staat in het bestand 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()

Als resultaat kregen we deze bot:
Een telegrambot schrijven in R (deel 4): Een consistente, logische dialoog opbouwen met de bot

Gebruik op elk moment de opdracht /state we kunnen de huidige chatstatus opvragen en de opdracht gebruiken /reset breng de chat terug naar de oorspronkelijke staat en start de dialoog opnieuw.

Conclusie

In dit artikel hebben we ontdekt hoe we een database in een bot kunnen gebruiken en hoe we opeenvolgende logische dialogen kunnen opbouwen door de chatstatus vast te leggen.

In dit geval hebben we naar het meest primitieve voorbeeld gekeken, zodat het voor u gemakkelijker zou zijn om het idee van het bouwen van dergelijke bots te begrijpen; in de praktijk kun je veel complexere dialogen bouwen.

In het volgende artikel in deze serie zullen we leren hoe we de rechten van botgebruikers kunnen beperken om verschillende methoden te gebruiken.

Bron: www.habr.com

Voeg een reactie