Créer un bot Telegram en R (partie 4) : Construire un dialogue séquentiel et logique avec le bot

Si vous avez déjà pris connaissance des précédents trois articles 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 cet article.

Créer un bot Telegram en R (partie 4) : Construire un dialogue séquentiel et logique avec le bot

Tous les articles de la série "Écrire un bot Telegram en R"

  1. Créer un bot et envoyer des messages via Telegram
  2. Ajouter le support des commandes et des filtres de messages au bot
  3. Comment ajouter le support du clavier au bot
  4. Établissement d'un dialogue séquentiel et logique avec le bot

Contenu

Si vous vous intéressez à l'analyse de données, mes telegram et youtube canaux pourraient vous intéresser. La majeure partie du contenu y est consacrée au langage R.

  1. Introduction
  2. Le processus de création du bot
  3. La structure du projet du bot
  4. Configuration du bot
  5. Créer une variable d'environnement
  6. Créer une base de données
  7. Écrire des fonctions pour travailler avec la base de données
  8. Méthodes du bot
  9. Filtres de messages
  10. Gestionnaires
  11. Code de démarrage du bot
  12. Conclusion

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 :

  1. start — l'état normal du bot, où il n'attend aucune information de votre part
  2. wait_name — l'état où le bot attend que vous saisissiez votre nom
  3. 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éer un bot Telegram en R (partie 4) : Construire un dialogue séquentiel et logique avec le bot

  1. 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.
  2. Créons une variable d'environnement qui stockera le chemin du projet avec le bot.
  3. Créons la base de données elle-même et plusieurs fonctions permettant au bot d'interagir avec celle-ci.
  4. Écrivons les méthodes du bot, c'est-à-dire les fonctions qu'il exécutera.
  5. 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.
  6. Ajoutons des gestionnaires qui relieront les commandes et les messages aux bonnes méthodes du bot.
  7. 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 télécharger de mon le dépôt GitHub.

Configuration du bot

Nous allons utiliser un simple fichier ini, de la forme suivante :

[bot_settings]
bot_token=VOTRE_TOKEN_DU_BOT

[db_settings]
db_path=C:/CHEMIN/VERS/DOSSIER/PROJET/bot.db

Dans 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/PROJET

Ensuite, 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 GitHub, 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 GitHub, 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 BDD
  • set_state() — enregistrer l'état actuel du chat dans la BDD
  • get_chat_data() — obtenir les données envoyées par l'utilisateur
  • set_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 GitHub, 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 GitHub 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 :
Créer un bot Telegram en R (partie 4) : Construire un dialogue séquentiel et logique avec le 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

Acheter un hébergement fiable pour les sites avec protection DDoS, serveurs VPS VDS 🔥 Acheter un hébergement fiable pour les sites avec protection DDoS, serveurs VPS VDS | ProHoster