Schreiben eines Telegram-Bots in R (Teil 4): Aufbau eines konsistenten, logischen Dialogs mit dem Bot

Falls Sie das Vorherige bereits gelesen haben drei Artikel aus dieser Serie, dann wissen Sie bereits, wie man vollwertige Telegram-Bots mit einer Tastatur schreibt.

In diesem Artikel lernen wir, wie man einen Bot schreibt, der einen konsistenten Dialog aufrechterhält. Diese. Der Bot wird Ihnen Fragen stellen und darauf warten, dass Sie einige Informationen eingeben. Abhängig von den von Ihnen eingegebenen Daten führt der Bot einige Aktionen aus.

Außerdem erfahren Sie in diesem Artikel, wie Sie eine Datenbank unter der Haube des Bots verwenden. In unserem Beispiel handelt es sich um SQLite, Sie können jedoch auch jedes andere DBMS verwenden. Ich habe ausführlicher über die Interaktion mit Datenbanken in der R-Sprache geschrieben Dieser Artikel.

Schreiben eines Telegram-Bots in R (Teil 4): Aufbau eines konsistenten, logischen Dialogs mit dem Bot

Alle Artikel aus der Serie „Einen Telegram-Bot in R schreiben“

  1. Wir erstellen einen Bot und verwenden ihn, um Nachrichten per Telegram zu versenden
  2. Fügen Sie dem Bot Befehlsunterstützung und Nachrichtenfilter hinzu
  3. So fügen Sie einem Bot Tastaturunterstützung hinzu
  4. Aufbau eines konsistenten, logischen Dialogs mit dem Bot

Inhalt

Wenn Sie sich für Datenanalyse interessieren, könnten Sie an meiner interessiert sein Telegram mit и Youtube Kanäle. Der größte Teil des Inhalts ist der R-Sprache gewidmet.

  1. Einführung
  2. Bot-Erstellungsprozess
  3. Bot-Projektstruktur
  4. Bot-Konfiguration
  5. Erstellen Sie eine Umgebungsvariable
  6. Erstellen einer Datenbank
  7. Schreiben von Funktionen zur Arbeit mit der Datenbank
  8. Bot-Methoden
  9. Nachrichtenfilter
  10. Handler
  11. Bot-Startcode
  12. Abschluss

Einführung

Damit der Bot Daten von Ihnen anfordern und darauf warten kann, dass Sie Informationen eingeben, müssen Sie den aktuellen Status des Dialogs aufzeichnen. Der beste Weg, dies zu tun, ist die Verwendung einer eingebetteten Datenbank wie SQLite.

Diese. Die Logik wird wie folgt sein. Wir rufen die Bot-Methode auf, und der Bot fordert nacheinander einige Informationen von uns an und wartet bei jedem Schritt auf die Eingabe dieser Informationen und kann sie überprüfen.

Wir schreiben den einfachsten Bot, der zuerst nach Ihrem Namen und dann nach Ihrem Alter fragt und die empfangenen Daten in der Datenbank speichert. Bei der Abfrage des Alters wird geprüft, ob es sich bei den eingegebenen Daten um eine Zahl und nicht um Text handelt.

Ein solch einfacher Dialog wird nur drei Zustände haben:

  1. Start ist der Normalzustand des Bots, in dem er keine Informationen von Ihnen erwartet
  2. wait_name – Zustand, in dem der Bot auf die Eingabe eines Namens wartet
  3. wait_age ist der Zustand, in dem der Bot auf die Eingabe Ihres Alters wartet, also die Anzahl der vollen Jahre.

Bot-Erstellungsprozess

Im Laufe des Artikels werden wir Schritt für Schritt einen Bot erstellen; der gesamte Prozess lässt sich schematisch wie folgt darstellen:
Schreiben eines Telegram-Bots in R (Teil 4): Aufbau eines konsistenten, logischen Dialogs mit dem Bot

  1. Wir erstellen eine Bot-Konfiguration, in der wir einige Einstellungen speichern. In unserem Fall das Bot-Token und der Pfad zur Datenbankdatei.
  2. Wir erstellen eine Umgebungsvariable, in der der Pfad zum Projekt mit dem Bot gespeichert wird.
  3. Wir erstellen die Datenbank selbst und eine Reihe von Funktionen, damit der Bot damit interagieren kann.
  4. Wir schreiben Bot-Methoden, d.h. welche Funktionen es ausführen wird.
  5. Nachrichtenfilter hinzufügen. Mit deren Hilfe greift der Bot je nach aktuellem Stand des Chats auf die notwendigen Methoden zu.
  6. Wir fügen Handler hinzu, die Befehle und Nachrichten mit den erforderlichen Bot-Methoden verbinden.
  7. Lassen Sie uns den Bot starten.

Bot-Projektstruktur

Der Einfachheit halber werden wir den Code unseres Bots und andere zugehörige Dateien in die folgende Struktur unterteilen.

  • bot.R – der Hauptcode unseres Bots
  • db_bot_function.R — ein Codeblock mit Funktionen zum Arbeiten mit der Datenbank
  • bot_methods.R — Code der Bot-Methoden
  • message_filters.R — Nachrichtenfilter
  • Handler.R - Handler
  • config.cfg - Bot-Konfiguration
  • create_db_data.sql — SQL-Skript zum Erstellen einer Tabelle mit Chat-Daten in der Datenbank
  • create_db_state.sql — SQL-Skript zum Erstellen einer Tabelle des aktuellen Chat-Status in der Datenbank
  • bot.db - Bot-Datenbank

Sie können das gesamte Bot-Projekt anzeigen oder скачать Von meinem Repository auf GitHub.

Bot-Konfiguration

Wir werden das übliche als Konfiguration verwenden ini-Datei, das folgende Formular:

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

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

In die Config schreiben wir den Bot-Token und den Pfad zur Datenbank, also in die Datei bot.db; die Datei selbst erstellen wir im nächsten Schritt.

Für komplexere Bots können Sie komplexere Konfigurationen erstellen. Außerdem ist es nicht erforderlich, eine INI-Konfiguration zu schreiben, Sie können jedes andere Format einschließlich JSON verwenden.

Erstellen Sie eine Umgebungsvariable

Auf jedem PC kann der Ordner mit dem Bot-Projekt in verschiedenen Verzeichnissen und auf verschiedenen Laufwerken liegen, daher wird im Code der Pfad zum Projektordner über eine Umgebungsvariable festgelegt TG_BOT_PATH.

Es gibt mehrere Möglichkeiten, eine Umgebungsvariable zu erstellen. Die einfachste besteht darin, sie in eine Datei zu schreiben .Renviron.

Sie können diese Datei mit dem Befehl erstellen oder bearbeiten file.edit(path.expand(file.path("~", ".Renviron"))). Führen Sie es aus und fügen Sie der Datei eine Zeile hinzu:

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

Speichern Sie anschließend die Datei .Renviron und starten Sie RStudio neu.

Erstellen einer Datenbank

Der nächste Schritt besteht darin, eine Datenbank zu erstellen. Wir benötigen 2 Tische:

  • chat_data – Daten, die der Bot vom Benutzer angefordert hat
  • chat_state – aktueller Status aller Chats

Sie können diese Tabellen mit der folgenden SQL-Abfrage erstellen:

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

Wenn Sie das Bot-Projekt von heruntergeladen haben GitHub, dann können Sie zum Erstellen der Datenbank den folgenden Code in R verwenden.

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

Schreiben von Funktionen zur Arbeit mit der Datenbank

Wir haben bereits eine Konfigurationsdatei bereit und eine Datenbank erstellt. Jetzt müssen Sie Funktionen schreiben, um Daten in diese Datenbank zu lesen und zu schreiben.

Wenn Sie das Projekt heruntergeladen haben von GitHub, dann finden Sie die Funktionen in der Datei db_bot_function.R.

Funktionscode für die Arbeit mit der Datenbank

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

}

Wir haben 4 einfache Funktionen erstellt:

  • get_state() – Holen Sie sich den aktuellen Chat-Status aus der Datenbank
  • set_state() — Schreiben Sie den aktuellen Chat-Status in die Datenbank
  • get_chat_data() — Vom Benutzer gesendete Daten empfangen
  • set_chat_data() — Vom Benutzer empfangene Daten aufzeichnen

Alle Funktionen sind recht einfach, sie lesen entweder Daten aus der Datenbank mit dem Befehl dbGetQuery(), oder verpflichten UPSERT Operation (Ändern vorhandener Daten oder Schreiben neuer Daten in die Datenbank) mithilfe der Funktion dbExecute().

Die Syntax für die UPSERT-Operation lautet wie folgt:

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

Diese. in unserem Tabellenbereich chat_id hat eine Eindeutigkeitsbeschränkung und ist der Primärschlüssel von Tabellen. Zunächst versuchen wir, Informationen zur Tabelle hinzuzufügen. Wenn Daten für den aktuellen Chat bereits vorhanden sind, erhalten wir eine Fehlermeldung. In diesem Fall aktualisieren wir einfach die Informationen für diesen Chat.

Als Nächstes werden wir diese Funktionen in den Methoden und Filtern des Bots verwenden.

Bot-Methoden

Der nächste Schritt beim Aufbau unseres Bots besteht darin, Methoden zu erstellen. Wenn Sie das Projekt heruntergeladen haben von GitHub, dann sind alle Methoden in der Datei bot_methods.R.

Bot-Methodencode

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

}

Wir haben 5 Methoden erstellt:

  • start – Starten Sie einen Dialog
  • state – Rufen Sie den aktuellen Chat-Status ab
  • Zurücksetzen – Setzen Sie den aktuellen Chat-Status zurück
  • enter_name – Der Bot fragt nach Ihrem Namen
  • enter_age – Der Bot fragt nach Ihrem Alter

Verfahren start fragt nach Ihrem Namen und ändert den Chatstatus in warte_name, d.h. in den Standby-Modus für die Eingabe Ihres Namens.

Als nächstes senden Sie den Namen und er wird von der Methode verarbeitet enter_name, der Bot begrüßt Sie, schreibt den erhaltenen Namen in die Datenbank und schaltet den Chat in den Status warte_alter.

Zu diesem Zeitpunkt erwartet der Bot, dass Sie Ihr Alter eingeben. Sie senden Ihr Alter, der Bot prüft die Nachricht. Wenn Sie statt einer Nummer einen Text gesendet haben, heißt es: Ты ввёл некорректные данные, введи число, und wartet darauf, dass Sie Ihre Daten erneut eingeben. Wenn Sie eine Nummer gesendet haben, meldet der Bot, dass er Ihr Alter akzeptiert hat, schreibt die empfangenen Daten in die Datenbank, meldet alle von Ihnen erhaltenen Daten und setzt den Chat-Status auf die ursprüngliche Position zurück, d. h. V start.

Durch den Aufruf der Methode state Sie können jederzeit den aktuellen Chat-Status abfragen und über den reset Bringen Sie den Chat in seinen ursprünglichen Zustand zurück.

Nachrichtenfilter

In unserem Fall ist dies einer der wichtigsten Teile beim Aufbau eines Bots. Mithilfe von Nachrichtenfiltern versteht der Bot, welche Informationen er von Ihnen erwartet und wie diese verarbeitet werden sollen.

Im Projekt am GitHub Filter werden in der Datei registriert message_filters.R.

Nachrichtenfiltercode:

# ###########################################################
# 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 Filtern verwenden wir die zuvor geschriebene Funktion get_state(), um den aktuellen Status des Chats abzufragen. Diese Funktion erfordert nur 1 Argument, Chat-ID.

Nächster Filter warte_name Verarbeitet Nachrichten, wenn sich der Chat in einem Status befindet wait_name, und dementsprechend der Filter warte_alter Verarbeitet Nachrichten, wenn sich der Chat in einem Status befindet wait_age.

Handler

Die Datei mit Handlern wird aufgerufen Handler.R, und hat den folgenden 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)

Zuerst erstellen wir Befehlshandler, mit denen Sie Methoden ausführen können, um einen Dialog zu starten, ihn zurückzusetzen und den aktuellen Status abzufragen.

Als Nächstes erstellen wir mithilfe der im vorherigen Schritt erstellten Filter zwei Nachrichtenhandler und fügen ihnen einen Filter hinzu !MessageFilters$command, sodass wir Befehle in jedem Chat-Status verwenden können.

Bot-Startcode

Jetzt haben wir alles startbereit, der Hauptcode zum Starten des Bots befindet sich in der Datei 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 Ergebnis haben wir diesen Bot bekommen:
Schreiben eines Telegram-Bots in R (Teil 4): Aufbau eines konsistenten, logischen Dialogs mit dem Bot

Jederzeit über den Befehl /state Wir können den aktuellen Chat-Status abfragen und den Befehl verwenden /reset Bringen Sie den Chat in seinen ursprünglichen Zustand zurück und starten Sie den Dialog erneut.

Abschluss

In diesem Artikel haben wir herausgefunden, wie man eine Datenbank innerhalb eines Bots verwendet und wie man durch die Aufzeichnung des Chat-Status sequentielle logische Dialoge erstellt.

In diesem Fall haben wir uns das einfachste Beispiel angesehen, damit Sie die Idee, solche Bots zu bauen, leichter verstehen; in der Praxis können Sie viel komplexere Dialoge erstellen.

Im nächsten Artikel dieser Serie erfahren Sie, wie Sie die Rechte von Bot-Benutzern zur Verwendung verschiedener seiner Methoden einschränken können.

Source: habr.com

Kommentar hinzufügen