Wenn Sie bereits die vorherigen aus dieser Reihe gelesen haben, können Sie bereits vollständige Telegram-Bots mit Tastatur erstellen.
In diesem Artikel lernen wir, einen Bot zu schreiben, der einen fortlaufenden Dialog unterstützt. Das heißt, der Bot wird Ihnen Fragen stellen und auf Ihre Eingabe von Informationen warten. Abhängig von den von Ihnen eingegebenen Daten wird der Bot bestimmte Aktionen durchführen.
In diesem Artikel lernen wir auch, wie wir unter der Haube des Bots Datenbanken nutzen. In unserem Beispiel wird das SQLite sein, aber Sie können jede andere DBMS verwenden. Mehr Details zur Interaktion mit Datenbanken in R habe ich geschrieben in .

Alle Artikel der Serie "Wir schreiben einen Telegram-Bot in der Programmiersprache R"
Inhalt
Wenn Sie sich für Datenanalyse interessieren, könnten Ihnen meine und Kanäle interessieren. Der Großteil des Inhalts widmet sich der Programmiersprache R.
Einführung
Damit der Bot von Ihnen Daten anfordern und auf die Eingabe von Informationen warten kann, müssen Sie den aktuellen Zustand des Dialogs festhalten. Der beste Weg, dies zu tun, ist, eine eingebettete Datenbank zu verwenden, zum Beispiel SQLite.
Die Logik wird folgendermaßen sein: Wir rufen die Methode des Bots auf, und der Bot fragt uns der Reihe nach nach bestimmten Informationen, wobei er in jedem Schritt auf die Eingabe dieser Informationen wartet und deren Überprüfung durchführen kann.
Wir werden einen möglichst einfachen Bot schreiben, der zunächst Ihren Namen abfragt, dann Ihr Alter, die erhaltenen Daten in der Datenbank speichert. Bei der Anfrage nach dem Alter wird überprüft, dass die eingegebenen Daten eine Zahl und kein Text sind.
Ein solcher einfacher Dialog wird nur drei Zustände haben:
- start — der normale Zustand des Bots, in dem er keine Informationen von Ihnen erwartet
- wait_name — der Zustand, in dem der Bot auf die Eingabe des Namens wartet
- wait_age — der Zustand, in dem der Bot auf die Eingabe Ihres Alters wartet, die Anzahl der vollen Jahre.
Der Prozess des Aufbaus des Bots
Im Verlauf des Artikels werden wir Schritt für Schritt den Bot aufbauen, und der gesamte Prozess kann schematisch wie folgt dargestellt werden:

- Wir erstellen die Bot-Konfiguration, in der wir einige Einstellungen speichern werden. In unserem Fall ist es das Token des Bots und der Pfad zur Datenbankdatei.
- Wir erstellen eine Umgebungsvariable, in der der Pfad zum Projekt mit dem Bot gespeichert wird.
- Wir erstellen die eigentliche Datenbank und eine Reihe von Funktionen, damit der Bot mit ihr interagieren kann.
- Wir schreiben die Methoden des Bots, d.h. Funktionen, die er ausführen wird.
- Wir fügen Nachrichtenfilter hinzu, mit deren Hilfe der Bot die richtigen Methoden basierend auf dem aktuellen Zustand des Chats aufrufen wird.
- Wir fügen Handler hinzu, die die Befehle und Nachrichten mit den entsprechenden Methoden des Bots verknüpfen.
- Wir starten den Bot.
Struktur des Bot-Projekts
Zur besseren Übersicht gliedern wir den Code unseres Bots und andere damit verbundene Dateien in die folgende Struktur.
- bot.R — der Hauptcode unseres Bots
- db_bot_function.R — Block mit Funktionen zur Arbeit mit der Datenbank
- bot_methods.R — Code der Bot-Methoden
- message_filters.R — Nachrichtenfilter
- handlers.R — Handler
- config.cfg — Konfiguration des Bots
- create_db_data.sql — SQL-Skript zur Erstellung der Tabelle mit Chatdaten in der Datenbank
- create_db_state.sql — SQL-Skript zur Erstellung der Tabelle mit dem aktuellen Zustand des Chats in der Datenbank
- bot.db — Datenbank des Bots
Das gesamte Botprojekt kann angesehen werden oder aus meinem .
Bot-Konfiguration
Als Konfiguration verwenden wir eine einfache , in folgender Form:
[bot_settings]
bot_token=DEIN_BOT_TOKEN
[db_settings]
db_path=C:/PFAD/ZUM/PROJEKTORDNER/bot.dbIn die Konfiguration schreiben wir den Bot-Token und den Pfad zur Datenbank, d.h. zur Datei bot.db, die wir im nächsten Schritt erstellen werden.
Für komplexere Bots können auch kompliziertere Konfigurationen erstellt werden, darüber hinaus ist es nicht unbedingt erforderlich, genau ini-Konfigurationen zu verwenden; jeder andere Format, einschließlich JSON, kann verwendet werden.
Umgebungsvariable erstellen
Auf jedem PC kann der Projektordner des Bots in unterschiedlichen Verzeichnissen und auf verschiedenen Laufwerken liegen, daher wird der Pfad zum Projektordner im Code über die Umgebungsvariable TG_BOT_PATH.
erstellt. Eine Umgebungsvariable kann auf verschiedene Arten erstellt werden, der einfachste Weg ist, sie in der Datei .Renviron.
zu definieren. Diese Datei kann mit dem Befehl file.edit(path.expand(file.path("~", ".Renviron")))erstellt oder bearbeitet werden. Führen Sie diesen Befehl aus und fügen Sie in die Datei eine Zeile ein:
TG_BOT_PATH=C:/PFAD/ZU/IHREM/PROJEKTSpeichern Sie die Datei anschließend .Renviron und starten Sie RStudio neu.
Datenbank erstellen
Der nächste Schritt ist die Erstellung der Datenbank. Wir benötigen 2 Tabellen:
- chat_data — Daten, die der Bot vom Benutzer abgefragt hat
- chat_state — aktueller Zustand aller Chats
Diese Tabellen können mit der folgenden SQL-Abfrage erstellt werden:
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 Botprojekt heruntergeladen haben, mit , um eine Datenbank zu erstellen, können Sie 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'))
Funktionen zum Arbeiten mit der Datenbank schreiben
Wir haben bereits eine Konfigurationsdatei und eine Datenbank erstellt. Jetzt müssen wir Funktionen zum Lesen und Schreiben von Daten in diese Datenbank schreiben.
Wenn Sie das Projekt von , 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 abzurufenset_state()– um den aktuellen Zustand des Chats in die DB zu schreibenget_chat_data()– um die vom Benutzer gesendeten Daten abzurufenset_chat_data()– um die vom Benutzer erhaltenen Daten zu speichern
Alle Funktionen sind relativ einfach, sie lesen entweder Daten aus der DB mit dem Befehl dbGetQuery(), oder führen eine UPSERT Operation (Änderung bestehender Daten oder Schreiben neuer Daten in die DB) mithilfe der Funktion dbExecute().
Die Syntax der UPSERT-Operation sieht wie folgt aus:
INSERT INTO chat_data (chat_id, ${field})
VALUES(${chat_id}, '${value}')
ON CONFLICT(chat_id)
DO UPDATE SET ${field}='${value}';Das heißt, in unseren Tabellen hat das Feld chat_id eine Einschränkung auf die 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 zur Erstellung unseres Bots wird die Erstellung von Methoden sein. Wenn Sie das Projekt von , finden Sie 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 – Der Bot fragt nach Ihrem Namen
- enter_age – Der Bot fragt nach Ihrem Alter
Methode start fragt nach Ihrem Namen und versetzt den Zustand des Chats in wait_name, d.h. in den Wartemodus für die Eingabe Ihres Namens.
Wenn Sie dann Ihren Namen senden, wird er von der Methode enter_nameverarbeitet, der Bot begrüßt Sie, speichert den erhaltenen Namen in der Datenbank und versetzt den Chat in den Zustand wait_age.
In dieser Phase wartet der Bot auf die Eingabe Ihres Alters. Sie senden Ihr Alter, der Bot überprüft die Nachricht; wenn Sie anstelle einer Zahl einen Text gesendet haben, wird er sagen: Sie haben ungültige Daten eingegeben, bitte geben Sie eine Zahl ein., und wird auf eine erneute Eingabe von Ihnen warten. Falls Sie eine Zahl geschickt haben, wird der Bot Ihnen mitteilen, dass er Ihr Alter entgegengenommen hat, die erhaltenen Daten in der Datenbank speichern, alle von Ihnen erhaltenen Informationen mitteilen und den Chatzustand in den ursprünglichen Zustand zurückversetzen, d.h. in start.
Den Methodenaufruf state können Sie jederzeit den aktuellen Zustand des Chats anfordern, und mit der Methode reset den Chat in den ursprünglichen Zustand zurücksetzen.
Nachrichtensfilter
In unserem Fall ist dies einer der wichtigsten Aspekte beim Aufbau des Bots. Mithilfe von Nachrichtenfiltern wird der Bot verstehen, welche Informationen er von Ihnen erwartet und wie er diese verarbeiten soll.
Im Projekt auf 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 Zustand des Chats abzufragen. Diese Funktion benötigt nur ein Argument, die ID des Chats.
Der Filter wait_name bearbeitet Nachrichten, wenn der Chat im Zustand wait_name, und dementsprechend der Filter wait_age bearbeitet Nachrichten, wenn der Chat im Zustand wait_age.
Handler
Die Datei mit den Handhabungen heißt handlers.R, und enthält 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 die Handler für die Befehle, die es Ihnen ermöglichen, Methoden für den Dialogstart, dessen Rücksetzung und die Abfrage des aktuellen Zustands auszuführen.
Dann erstellen wir 2 Nachrichtenhandler mit Hilfe der in der vorherigen Schritt erstellten Filter und fügen ihnen den Filter !MessageFilters$command, hinzu, damit wir in jedem Zustand des Chats 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)
# Wir wechseln in das Projektverzeichnis
setwd(Sys.getenv('TG_BOT_PATH'))
# Wir lesen die Konfiguration
cfg <- read.config('config.cfg')
# Wir erstellen eine Bot-Instanz
updater <- Updater(cfg$bot_settings$bot_token)
# Laden der Bot-Komponenten
source('db_bot_function.R') # Funktionen zur Arbeit mit DB
source('bot_methods.R') # Bot-Methoden
source('message_filters.R') # Nachrichtenfilter
source('handlers.R') # Nachrichtenhandler
# Fügen Sie die Handler dem Dispatcher hinzu
updater <- updater +
start_h +
wait_age_h +
wait_name_h +
state_h +
reset_h
# Starten Sie den Bot
updater$start_polling()Infolge dessen haben wir diesen Bot erhalten:

Jederzeit können wir mit dem Befehl /state den aktuellen Zustand des Chats anfordern, und mit dem Befehl /reset den Chat in den ursprünglichen Zustand zurückversetzen und den Dialog von vorne beginnen.
Fazit
In diesem Artikel haben wir gelernt, wie man in einem Bot Datenbanken verwendet und wie man aufeinanderfolgende logische Dialoge durch die Feststellung des Chatzustands aufbaut.
In diesem Fall haben wir das einfachste Beispiel betrachtet, um Ihnen das Verständnis der Idee des Aufbaus solcher Bots zu erleichtern. In der Praxis können Sie viel komplexere Dialoge erstellen.
Im nächsten Artikel dieser Serie lernen wir, die Rechte der Bot-Nutzer bei der Nutzung seiner verschiedenen Methoden zu beschränken.
Quelle: habr.com
