Einen Telegram-Bot in R schreiben (Teil 4): Aufbau eines kontinuierlichen, logischen Dialogs mit dem Bot

Wenn Sie sich bereits mit den vorherigen drei Artikeln dieser Reihe vertraut gemacht haben, wissen Sie bereits, wie man vollständige Telegram-Bots mit Tastatur schreibt.

In diesem Artikel werden wir lernen, einen Bot zu erstellen, der einen fortlaufenden Dialog unterstützt. Das heißt, der Bot wird Ihnen Fragen stellen und auf Ihre Eingaben warten. Je nach den von Ihnen eingegebenen Daten wird der Bot bestimmte Aktionen ausführen.

Außerdem werden wir in diesem Artikel lernen, wie man unter der Haube des Bots Datenbanken verwendet; in unserem Beispiel wird dies SQLite sein, aber Sie können jede andere DBMS verwenden. Detailliertere Informationen über die Interaktion mit Datenbanken in der Programmiersprache R finden Sie in in diesem Artikel.

Einen Telegram-Bot in R schreiben (Teil 4): Aufbau eines kontinuierlichen, logischen Dialogs mit dem Bot

Allen Artikeln der Serie "Wir schreiben einen Telegram-Bot in der Programmiersprache R"

  1. Wir erstellen einen Bot und senden mit ihm Nachrichten in Telegram
  2. Wir fügen dem Bot Unterstützung für Befehle und Nachrichtenfilter hinzu
  3. Wie man den Bot mit Tastaturunterstützung ausstattet
  4. Aufbau eines fortlaufenden, logischen Dialogs mit dem Bot

Inhalt

Falls Sie sich für Datenanalyse interessieren, könnten meine telegram und youtube Kanäle für Sie von Interesse sein. Der Großteil der Inhalte widmet sich der R-Sprache.

  1. Einführung
  2. Der Prozess der Bot-Erstellung
  3. Die Struktur des Bot-Projekts
  4. Bot-Konfiguration
  5. Umgebungsvariable erstellen
  6. Datenbank erstellen
  7. Wir schreiben Funktionen zur Arbeit mit der Datenbank
  8. Methoden des Bots
  9. Nachrichtenfilter
  10. Handler
  11. Bot-Startcode
  12. Fazit

Einführung

Damit der Bot Daten von Ihnen anfordern und auf die Eingabe von Informationen warten kann, müssen Sie den aktuellen Zustand des Dialogs speichern. Der beste Weg, dies zu tun, ist die Verwendung einer eingebetteten Datenbank, wie z.B. SQLite.

Das heißt, die Logik wird folgendermaßen aussehen. Wir rufen die Methode des Bots auf, und der Bot fragt uns nacheinander nach bestimmten Informationen. Dabei wartet er in jedem Schritt auf die Eingabe dieser Informationen und kann deren Überprüfung durchführen.

Wir werden einen möglichst einfachen Bot schreiben, der zuerst nach Ihrem Namen und dann nach Ihrem Alter fragt. Die erhaltenen Daten werden in einer Datenbank gespeichert. Bei der Abfrage des Alters wird überprüft, dass die eingegebenen Daten eine Zahl und kein Text sind.

Ein solcher einfacher Dialog wird insgesamt drei Zustände haben:

  1. start — der normale Zustand des Bots, in dem er keine Informationen von Ihnen erwartet
  2. wait_name — der Zustand, in dem der Bot auf die Eingabe des Namens wartet
  3. wait_age — der Zustand, in dem der Bot auf die Eingabe Ihres Alters wartet, die Anzahl der vollen Jahre.

Der Prozess der Bot-Erstellung

Im Verlauf des Artikels werden wir Schritt für Schritt einen Bot entwickeln. Der gesamte Prozess kann schematisch wie folgt dargestellt werden:
Einen Telegram-Bot in R schreiben (Teil 4): Aufbau eines kontinuierlichen, logischen Dialogs mit dem Bot

  1. Wir erstellen die Bot-Konfiguration, in der wir einige Einstellungen speichern. In unserem Fall sind das 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 sowie eine Reihe von Funktionen, damit der Bot mit ihr interagieren kann.
  4. Wir schreiben die Methoden des Bots, das sind die Funktionen, die er ausführen wird.
  5. Wir fügen Nachrichtenfilter hinzu, mittels derer der Bot je nach aktuellem Status des Chats auf die erforderlichen Methoden zugreifen kann.
  6. Wir fügen Handler hinzu, die die Kommandos und Nachrichten mit den entsprechenden Methoden des Bots verknüpfen.
  7. Wir starten den Bot.

Die Struktur des Bot-Projekts

Zur Vereinfachung werden wir den Code unseres Bots und andere damit verbundene Dateien in die folgende Struktur aufteilen.

  • bot.R — der Hauptcode unseres Bots
  • db_bot_function.R — der Codeblock mit Funktionen zur Arbeit mit der Datenbank
  • bot_methods.R — der Code der Bot-Methoden
  • message_filters.R — Nachrichtenfilter
  • handlers.R — Handler
  • config.cfg — Bot-Konfiguration
  • create_db_data.sql — SQL-Skript zur Erstellung der Tabelle mit den Chatdaten in der Datenbank
  • create_db_state.sql — SQL-Skript zur Erstellung der Tabelle des aktuellen Status des Chats in der Datenbank
  • bot.db — die Datenbank des Bots

Das gesamte Projekt des Bots kann angesehen werden oder heruntergeladen aus meinem dem GitHub-Repository.

Bot-Konfiguration

Wir werden eine Standardkonfiguration verwenden ini-Datei, im folgenden Format:

[bot_settings]
bot_token=IHRE_BOT_TOKEN

[db_settings]
db_path=C:/PFAD/ZUM/ORDNER/PROJEKT/bot.db

In die Konfiguration tragen wir das Token des Bots und den Pfad zur Datenbank ein, also zur Datei bot.db, die wir im nächsten Schritt erstellen werden.

Für komplexere Bots können auch aufwendigere Konfigurationen erstellt werden; es ist nicht notwendig, genau eine ini-Konfiguration zu verwenden. Sie können jedes andere Format, einschließlich JSON, verwenden.

Umgebungsvariable erstellen

Auf jedem PC kann sich der Projektordner des Bots in verschiedenen Verzeichnissen und auf unterschiedlichen Laufwerken befinden, daher wird der Pfad zum Projektordner im Code über die Umgebungsvariable TG_BOT_PATH.

erstellt. Eine Umgebungsvariable kann auf mehrere Arten erstellt werden, die einfachste ist, sie in der Datei .Renviron.

zu definieren oder zu bearbeiten, was Sie mit dem Befehl file.edit(path.expand(file.path("~", ".Renviron")))tun können. Führen Sie diesen Befehl aus und fügen Sie eine Zeile in die Datei ein:

TG_BOT_PATH=C:/PFAD/ZU/IHREM/PROJEKT

Speichern Sie dann die Datei .Renviron und starten Sie RStudio neu.

Datenbank erstellen

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

  • chat_data — die Daten, die der Bot vom Benutzer angefordert hat
  • chat_state — der aktuelle Zustand 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 GitHub, heruntergeladen haben, können Sie den folgenden R-Code zur Erstellung der Datenbank 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'))

Wir schreiben Funktionen zur Arbeit mit der Datenbank

Wir haben bereits die Konfigurationsdatei und die Datenbank erstellt. Nun müssen Funktionen zum Lesen und Schreiben von Daten in diese Datenbank geschrieben werden.

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

Der Code der Funktionen zur 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() — um den aktuellen Zustand des Chats aus der DB abzurufen
  • set_state() — um den aktuellen Zustand des Chats in der DB zu speichern
  • get_chat_data() — um die vom Benutzer gesendeten Daten abzurufen
  • set_chat_data() — um die vom Benutzer erhaltenen Daten zu speichern

Alle Funktionen sind recht einfach; sie lesen entweder Daten aus der Datenbank mit dem Befehl dbGetQuery(), oder führen eine UPSERT Operation (Änderung bestehender Daten oder Einfügen neuer Daten in die DB) mit der Funktion dbExecute().

Die Syntax der UPSERT-Operation sieht folgendermaßen aus:

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

In unseren Tabellen hat das Feld chat_id eine Einschränkung bezüglich der Einzigartigkeit und ist der Primärschlüssel der Tabellen. Zunächst versuchen wir, Informationen in die Tabelle einzufügen, und erhalten einen Fehler, wenn die Daten für den aktuellen Chat bereits vorhanden sind. In diesem Fall aktualisieren wir einfach die Informationen für diesen Chat.

Diese Funktionen werden wir später in den Methoden und Filtern des Bots verwenden.

Methoden des Bots

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

Der Code der Bot-Methoden

# ###########################################################
# 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 — Dialog starten
  • state — Aktuellen Zustand des Chats abrufen
  • reset — Aktuellen Zustand des Chats zurücksetzen
  • enter_name — Bot fragt nach Ihrem Namen
  • enter_age — Bot fragt nach Ihrem Alter

Methode start fragt nach Ihrem Namen und versetzt den Chat in wait_name, das heißt, in den Wartemodus für die Eingabe Ihres Namens.

Danach senden Sie den Namen, und dieser wird vom enter_name-Methode verarbeitet, der Bot begrüßt Sie, speichert den erhaltenen Namen in der Datenbank und versetzt den Chat in den Zustand wait_age.

An diesem Punkt wartet der Bot darauf, dass Sie Ihr Alter eingeben. Sie senden Ihr Alter, der Bot überprüft die Nachricht. Wenn Sie anstelle einer Zahl einen Text senden, wird er sagen: Sie haben ungültige Daten eingegeben, bitte geben Sie eine Zahl ein., und wird auf Ihre erneute Eingabe warten. Falls Sie eine Zahl eingegeben haben, wird der Bot Ihnen mitteilen, dass Ihr Alter angenommen wurde, die erhaltenen Daten in der Datenbank speichern, Ihnen alle erhaltenen Daten mitteilen und den Chat zurücksetzen, d.h. auf start.

Den Methodenaufruf state können Sie jederzeit den aktuellen Status des Chats anfordern, und mit der Methode reset den Chat in den ursprünglichen Zustand versetzen.

Nachrichtenfilter

In unserem Fall ist dies einer der wichtigsten Teile beim Aufbau des Bots. Durch die Nachrichtenfilter versteht der Bot, welche Informationen er von Ihnen erwartet und wie diese zu verarbeiten sind.

Im Projekt auf GitHub sind die Filter in der Datei message_filters.R.

Der Code der Nachrichtenfilter:

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

Danach verarbeitet der Filter wait_name Nachrichten, wenn der Chat sich im Zustand wait_name, und entsprechend der Filter wait_age Nachrichten, wenn der Chat sich im Zustand wait_age.

Handler

Die Datei mit den Handlern heißt handlers.R, und hat 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 die Command-Handler, mit denen Sie Methoden für den Dialogstart, den Reset und die Abfrage des aktuellen Zustands ausführen können.

Danach erstellen wir 2 Nachrichten-Handler mit den im vorherigen Schritt erstellten Filtern und fügen ihnen den Filter hinzu !MessageFilters$command, damit wir in jedem Zustand des Chats die Befehle verwenden können.

Bot-Startcode

Jetzt sind wir bereit für den Start, der Hauptcode zum Starten des Bots befindet sich in der Datei bot.R.

library(telegram.bot)
library(tidyverse)
library(RSQLite)
library(DBI)
library(configr)

# Wechseln Sie in den Projektordner
setwd(Sys.getenv('TG_BOT_PATH'))

# Konfiguration lesen
cfg <- read.config('config.cfg')

# Bot-Instanz erstellen
updater <- Updater(cfg$bot_settings$bot_token)

# Laden der Bot-Komponenten
source('db_bot_function.R') # Funktionen für die Datenbankbearbeitung
source('bot_methods.R')     # Bot-Methoden
source('message_filters.R') # Nachrichtenfilter
source('handlers.R') # Nachrichtenhandler

# Fügen Sie die Handler zum Dispatcher hinzu
updater <- updater +
  start_h +
  wait_age_h +
  wait_name_h +
  state_h +
  reset_h

# Starten Sie den Bot
updater$start_polling()

Das Ergebnis ist ein Bot wie folgt:
Einen Telegram-Bot in R schreiben (Teil 4): Aufbau eines kontinuierlichen, logischen Dialogs mit dem Bot

Jederzeit können wir mit dem Befehl /state den aktuellen Zustand des Chats abfragen, und mit dem Befehl /reset Setzen Sie den Chat zurück und starten Sie das Gespräch neu.

Fazit

In diesem Artikel haben wir besprochen, wie man innerhalb des Bots Datenbanken verwendet und wie man logische Dialoge durch das Speichern des Chat-Zustands erstellt.

In diesem Fall haben wir das einfachste Beispiel betrachtet, damit es Ihnen leichter fällt, das Konzept des Aufbaus solcher Bots zu verstehen. In der Praxis können Sie viel komplexere Dialoge erstellen.

Im nächsten Artikel dieser Serie lernen wir, die Rechte der Bots-Nutzer bezüglich der Nutzung verschiedener Methoden einzuschränken.

Quelle: habr.com

Kaufen Sie zuverlässiges Hosting für Websites mit DDoS-Schutz, VPS VDS-Server 🔥 Kaufen Sie zuverlässiges Hosting für Websites mit DDoS-Schutz, VPS VDS-Server | ProHoster