Si vous avez déjà pris connaissance des précédents de cette série, vous savez déjà comment écrire des bots Telegram complets avec clavier.
Dans cet article, nous allons apprendre à écrire un bot qui maintiendra un dialogue séquentiel. C'est-à-dire que le bot posera des questions et attendra que vous lui fournissiez des informations. En fonction des données que vous saisissez, le bot effectuera certaines actions.
Nous allons également apprendre à utiliser une base de données derrière le bot, dans notre exemple, cela sera SQLite, mais vous pouvez utiliser n'importe quel autre SGBD. J'ai parlé plus en détail de l'interaction avec les bases de données en R dans .

Tous les articles de la série "Écrire un bot Telegram en R"
Contenu
Si vous vous intéressez à l'analyse de données, mes et canaux pourraient vous intéresser. La majeure partie du contenu y est consacrée au langage R.
Introduction
Pour que le bot puisse vous demander des données et attendre que vous en fournissiez, vous devrez garder une trace de l'état actuel du dialogue. Le meilleur moyen de le faire est d'utiliser une base de données intégrée, par exemple SQLite.
C'est-à-dire que la logique sera la suivante. Nous appelons la méthode du bot, et le bot demande successivement certaines informations, tout en attendant à chaque étape que ces informations soient fournies, et peut les vérifier.
Nous allons écrire un bot aussi simple que possible, d'abord il vous demandera votre nom, puis votre âge, et il enregistrera les données dans la base de données. Lors de la demande de l'âge, il vérifiera que les données saisies sont des nombres et non du texte.
Ce dialogue simple aura seulement trois états :
- start — l'état normal du bot, où il n'attend aucune information de votre part
- wait_name — l'état où le bot attend que vous saisissiez votre nom
- wait_age — l'état où le bot attend que vous saisissiez votre âge, en années complètes.
Le processus de création du bot
Dans cet article, nous allons construire un bot étape par étape, et le processus peut être schématisé de la manière suivante :

- Créons la configuration du bot, où nous allons stocker certains paramètres. Dans notre cas, le token du bot et le chemin d'accès au fichier de la base de données.
- Créons une variable d'environnement qui stockera le chemin du projet avec le bot.
- Créons la base de données elle-même et plusieurs fonctions permettant au bot d'interagir avec celle-ci.
- Écrivons les méthodes du bot, c'est-à-dire les fonctions qu'il exécutera.
- Ajoutons des filtres de messages. Grâce à ceux-ci, le bot pourra appeler les bonnes méthodes en fonction de l'état actuel du chat.
- Ajoutons des gestionnaires qui relieront les commandes et les messages aux bonnes méthodes du bot.
- Lançons le bot.
La structure du projet du bot
Pour plus de commodité, nous allons structurer le code de notre bot et les fichiers qui y sont liés comme suit.
- bot.R — le code principal de notre bot
- db_bot_function.R — bloc de code avec des fonctions pour interagir avec la base de données
- bot_methods.R — code des méthodes du bot
- message_filters.R — filtres de messages
- handlers.R — gestionnaires
- config.cfg — configuration du bot
- create_db_data.sql — script SQL pour créer une table avec les données du chat dans la base de données
- create_db_state.sql — script SQL pour créer une table de l'état actuel du chat dans la base de données
- bot.db — base de données du bot
Vous pouvez consulter l'ensemble du projet du bot ou de mon .
Configuration du bot
Nous allons utiliser un simple , de la forme suivante :
[bot_settings]
bot_token=VOTRE_TOKEN_DU_BOT
[db_settings]
db_path=C:/CHEMIN/VERS/DOSSIER/PROJET/bot.dbDans la configuration, nous notons le token du bot et le chemin vers la base de données, c'est-à-dire vers le fichier bot.db. Nous allons créer le fichier lors de l'étape suivante.
Pour des bots plus complexes, il est possible de créer des configurations plus élaborées, et il n'est pas nécessaire d'écrire exactement en ini, vous pouvez utiliser n'importe quel autre format, y compris JSON.
Créer une variable d'environnement
Sur chaque PC, le dossier du projet du bot peut se trouver dans différents répertoires et sur différents disques, c'est pourquoi dans le code, le chemin vers le dossier du projet sera défini par une variable d'environnement. TG_BOT_PATH.
Vous pouvez créer une variable d'environnement de plusieurs manières, la plus simple étant de la définir dans le fichier .Renviron.
Vous pouvez créer ou éditer ce fichier en utilisant la commande file.edit(path.expand(file.path("~", ".Renviron"))). Exécutez-la et ajoutez une ligne au fichier :
TG_BOT_PATH=C:/CHEMIN/VERS/VOTRE/PROJETEnsuite, enregistrez le fichier .Renviron et redémarrez RStudio.
Créer une base de données
La prochaine étape consiste à créer la base de données. Nous aurons besoin de 2 tables :
- chat_data — les données que le bot a demandées à l'utilisateur
- chat_state — l'état actuel de tous les chats
Ces tables peuvent être créées à l'aide de la requête SQL suivante :
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
);
Si vous avez téléchargé le projet du bot depuis , vous pouvez utiliser le code suivant en R pour créer la base.
# Скрипт создания базы данных
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'))
Écrire des fonctions pour travailler avec la base de données
Nous avons déjà un fichier de configuration et une base de données créés. Maintenant, il est nécessaire d'écrire des fonctions pour lire et écrire des données dans cette base.
Si vous avez téléchargé le projet depuis , vous pouvez trouver les fonctions dans le fichier db_bot_function.R.
Code des fonctions pour travailler avec la base de données
# ###########################################################
# 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]])
}Nous avons créé 4 fonctions simples :
get_state()— obtenir l'état actuel du chat depuis la BDDset_state()— enregistrer l'état actuel du chat dans la BDDget_chat_data()— obtenir les données envoyées par l'utilisateurset_chat_data()— enregistrer les données reçues de l'utilisateur
Toutes les fonctions sont assez simples, elles lisent soit les données de la base à l'aide de la commande dbGetQuery(), soit effectuent une UPSERT opération (modification des données existantes ou enregistrement de nouvelles données dans la BDD), à l'aide de la fonction dbExecute().
La syntaxe de l'opération UPSERT est la suivante :
INSERT INTO chat_data (chat_id, ${field})
VALUES(${chat_id}, '${value}')
ON CONFLICT(chat_id)
DO UPDATE SET ${field}='${value}';C'est-à-dire que dans nos tables, le champ chat_id a une contrainte d'unicité et est la clé primaire des tables. Initialement, nous essayons d'ajouter des informations dans la table, et nous recevons une erreur si les données pour le chat actuel sont déjà présentes, dans ce cas, nous mettons simplement à jour les informations pour ce chat.
Ensuite, nous utiliserons ces fonctions dans les méthodes et filtres du bot.
Méthodes du bot
La prochaine étape dans la construction de notre bot sera la création de méthodes. Si vous avez téléchargé le projet depuis , toutes les méthodes se trouvent dans le fichier bot_methods.R.
Code des méthodes du bot
# ###########################################################
# 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')
}
}Nous avons créé 5 méthodes :
- start — Démarrer le dialogue
- state — Obtenir l'état actuel du chat
- reset — Réinitialiser l'état actuel du chat
- enter_name — Le bot demande votre nom
- enter_age — Le bot demande votre âge
Méthode start demande votre nom et change l'état du chat en wait_name, c'est-à-dire en mode d'attente de votre nom.
Ensuite, vous envoyez votre nom et il est traité par la méthode enter_name, le bot vous salue, enregistre le nom reçu dans la base, et change l'état du chat en wait_age.
À ce stade, le bot attend que vous saisissiez votre âge. Vous envoyez votre âge, le bot vérifie le message; si vous avez envoyé du texte au lieu d'un nombre, il vous dira : Vous avez saisi des données incorrectes, veuillez entrer un chiffre, et attendra votre nouvelle saisie. Si vous avez envoyé un nombre, le bot vous informera qu'il a enregistré votre âge, enregistrera les données reçues, vous communiquera toutes les informations obtenues de vous et ramènera l'état du chat à sa position initiale, c'est-à-dire à start.
En appelant la méthode state vous pouvez à tout moment demander l'état actuel du chat, et avec la méthode réinitialisation ramener le chat à l'état initial.
Filtres de messages
Dans notre cas, c'est l'une des parties les plus essentielles dans la construction du bot. C'est grâce aux filtres de messages que le bot comprendra quelle information il attend de vous et comment la traiter.
Dans le projet sur les filtres sont définis dans le fichier message_filters.R.
Code des filtres de messages :
# ###########################################################
# 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"
}
)Dans les filtres, nous utilisons la fonction écrite précédemment get_state(), pour demander l'état actuel du chat. Cette fonction nécessite seulement 1 argument, l'id du chat.
Ensuite, le filtre wait_name gère les messages lorsque le chat est dans l'état wait_name, et donc le filtre wait_age gère les messages lorsque le chat est dans l'état wait_age.
Gestionnaires
Le fichier avec les gestionnaires s'appelle handlers.R, et contient le code suivant :
# ###########################################################
# 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)D'abord, nous créons des gestionnaires de commandes qui vous permettront de lancer des méthodes pour commencer le dialogue, le réinitialiser et demander l'état actuel.
Ensuite, nous créons 2 gestionnaires de messages en utilisant les filtres créés à l'étape précédente, et nous ajoutons au filtre !MessageFilters$command, pour que nous puissions utiliser des commandes dans n'importe quel état du chat.
Code de démarrage du bot
Nous sommes maintenant prêts à démarrer; le code principal pour lancer le bot se trouve dans le fichier bot.R.
library(telegram.bot)
library(tidyverse)
library(RSQLite)
library(DBI)
library(configr)
# nous allons dans le dossier du projet
setwd(Sys.getenv('TG_BOT_PATH'))
# lisons la configuration
cfg <- read.config('config.cfg')
# créons une instance du bot
updater <- Updater(cfg$bot_settings$bot_token)
# Chargement des composants du bot
source('db_bot_function.R') # fonctions pour interagir avec la base de données
source('bot_methods.R') # méthodes du bot
source('message_filters.R') # filtres de messages
source('handlers.R') # gestionnaires de messages
# Ajoutons les gestionnaires au dispatcher
updater <- updater +
start_h +
wait_age_h +
wait_name_h +
state_h +
reset_h
# Démarrons le bot
updater$start_polling()En conséquence, nous avons obtenu ce bot :

À tout moment, avec la commande /state nous pouvons demander l'état actuel du chat, et avec la commande /reset réinitialiser le chat et recommencer le dialogue.
Conclusion
Dans cet article, nous avons examiné comment utiliser des bases de données au sein du bot, et comment construire des dialogues logiques séquentiels en fixant l'état du chat.
Dans ce cas, nous avons considéré un exemple très basique, afin que vous puissiez mieux comprendre l'idée de construction de tels bots, vous pouvez créer des dialogues beaucoup plus complexes en pratique.
Dans le prochain article de cette série, nous apprendrons à restreindre les utilisateurs du bot dans l'utilisation de ses différentes méthodes.
Source : habr.com
