Écrire un bot télégramme en R (partie 4) : Construire un dialogue cohérent et logique avec le bot

Si vous avez déjà lu le précédent trois articles de cette série, alors vous savez déjà comment écrire des robots télégrammes à part entière avec un clavier.

Dans cet article, nous apprendrons comment écrire un bot qui maintiendra un dialogue cohérent. Ceux. Le bot vous posera des questions et attendra que vous saisissiez certaines informations. En fonction des données que vous saisissez, le bot effectuera certaines actions.

Également dans cet article, nous apprendrons comment utiliser une base de données sous le capot du bot, dans notre exemple ce sera SQLite, mais vous pouvez utiliser n'importe quel autre SGBD. J'ai écrit plus en détail sur l'interaction avec les bases de données en langage R dans cet article.

Écrire un bot télégramme en R (partie 4) : Construire un dialogue cohérent et logique avec le bot

Tous les articles de la série « Écrire un bot télégramme en R »

  1. Nous créons un bot et l'utilisons pour envoyer des messages par télégramme
  2. Ajouter la prise en charge des commandes et des filtres de messages au bot
  3. Comment ajouter la prise en charge du clavier à un bot
  4. Construire un dialogue cohérent et logique avec le bot

Teneur

Si vous êtes intéressé par l'analyse des données, vous pourriez être intéressé par mon télégramme и Youtube canaux. La plupart du contenu est dédié au langage R.

  1. introduction
  2. Processus de création de robots
  3. Structure du projet de robot
  4. Configuration du robot
  5. Créer une variable d'environnement
  6. Création d'une base de données
  7. Écriture de fonctions pour travailler avec la base de données
  8. Méthodes de robots
  9. Filtres de messages
  10. Gestionnaires
  11. Code de lancement du robot
  12. Conclusion

introduction

Pour que le bot vous demande des données et attende que vous saisissiez des informations, vous devrez enregistrer l'état actuel du dialogue. La meilleure façon d’y parvenir est d’utiliser une sorte de base de données intégrée, telle que SQLite.

Ceux. La logique sera la suivante. Nous appelons la méthode bot, et le bot nous demande séquentiellement certaines informations, et à chaque étape, il attend que ces informations soient saisies et peut les vérifier.

Nous écrirons le bot le plus simple possible, il vous demandera d'abord votre nom, puis votre âge, et enregistrera les données reçues dans la base de données. Lors de la demande d'âge, il vérifiera que les données saisies sont un nombre et non du texte.

Un dialogue aussi simple n'aura que trois états :

  1. start est l'état normal du bot, dans lequel il n'attend aucune information de votre part
  2. wait_name - état dans lequel le bot attend qu'un nom soit saisi
  3. wait_age est l'état dans lequel le bot attend que votre âge soit saisi, le nombre d'années complètes.

Processus de création de robots

Au cours de cet article, nous allons construire un bot étape par étape ; l'ensemble du processus peut être schématiquement représenté comme suit :
Écrire un bot télégramme en R (partie 4) : Construire un dialogue cohérent et logique avec le bot

  1. Nous créons une configuration de bot dans laquelle nous stockerons certains paramètres. Dans notre cas, le jeton du bot et le chemin d'accès au fichier de base de données.
  2. Nous créons une variable d'environnement dans laquelle le chemin d'accès au projet avec le bot sera stocké.
  3. Nous créons la base de données elle-même et un certain nombre de fonctions afin que le bot puisse interagir avec elle.
  4. Nous écrivons des méthodes de bot, c'est-à-dire les fonctions qu'il remplira.
  5. Ajout de filtres de messages. A l'aide duquel le bot accédera aux méthodes nécessaires, en fonction de l'état actuel du chat.
  6. Nous ajoutons des gestionnaires qui connecteront les commandes et les messages aux méthodes de bot nécessaires.
  7. Lançons le bot.

Structure du projet de robot

Pour plus de commodité, nous diviserons le code de notre bot et les autres fichiers associés dans la structure suivante.

  • bot.R — le code principal de notre bot
  • db_bot_function.R — un bloc de code avec des fonctions pour travailler avec la base de données
  • bot_methods.R — code des méthodes du bot
  • message_filters.R — filtres de messages
  • gestionnaires.R - les gestionnaires
  • config.cfg - configuration du robot
  • create_db_data.sql — Script SQL pour créer une table avec des données de discussion 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 de robots

Vous pouvez afficher l'intégralité du projet de bot, ou скачать de mon dépôt sur GitHub.

Configuration du robot

Nous utiliserons l'habituel comme configuration fichier ini, de la forme suivante:

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

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

Dans la configuration, nous écrivons le jeton du bot et le chemin d'accès à la base de données, c'est-à-dire au fichier bot.db ; nous créerons le fichier lui-même à l’étape suivante.

Pour les robots plus complexes, vous pouvez créer des configurations plus complexes, de plus, il n'est pas nécessaire d'écrire une configuration ini, vous pouvez utiliser n'importe quel autre format, y compris JSON.

Créer une variable d'environnement

Sur chaque PC, le dossier contenant le projet du bot peut être situé dans différents répertoires et sur différents lecteurs, donc dans le code le chemin d'accès au dossier du projet sera défini via une variable d'environnement TG_BOT_PATH.

Il existe plusieurs manières de créer une variable d'environnement, la plus simple est de l'écrire dans un fichier .Renviron.

Vous pouvez créer ou modifier ce fichier à l'aide de la commande file.edit(path.expand(file.path("~", ".Renviron"))). Exécutez-le et ajoutez une ligne au fichier :

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

Enregistrez ensuite le fichier .Renviron et redémarrez RStudio.

Création d'une base de données

L'étape suivante consiste à créer une base de données. Nous aurons besoin de 2 tableaux :

  • chat_data — données que le bot a demandées à l'utilisateur
  • chat_state — état actuel de toutes les discussions

Vous pouvez créer ces tables à 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 de bot depuis GitHub, puis pour créer la base de données, vous pouvez utiliser le code suivant dans R.

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

Écriture de fonctions pour travailler avec la base de données

Nous avons déjà un fichier de configuration prêt et une base de données créée. Vous devez maintenant écrire des fonctions pour lire et écrire des données dans cette base de données.

Si vous avez téléchargé le projet depuis GitHub, alors vous pouvez trouver les fonctions dans le fichier db_bot_function.R.

Code de fonction 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 à partir de la base de données
  • set_state() — écrire l'état actuel du chat dans la base de données
  • get_chat_data() — recevoir 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, soit elles lisent les données de la base de données à l'aide de la commande dbGetQuery(), ou commettre UPSERT opération (modification des données existantes ou écriture de nouvelles données dans la base de données), à 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}';

Ceux. dans notre champ de tables chat_id a une contrainte d'unicité et est la clé primaire des tables. Initialement, nous essayons d'ajouter des informations au tableau, et nous obtenons une erreur si les données du chat en cours sont déjà présentes, auquel cas nous mettons simplement à jour les informations de ce chat.

Ensuite, nous utiliserons ces fonctions dans les méthodes et les filtres du bot.

Méthodes de robots

La prochaine étape dans la création de notre bot consiste à créer des méthodes. Si vous avez téléchargé le projet depuis GitHub, alors toutes les méthodes sont dans le fichier bot_methods.R.

Code de la méthode du robot

# ###########################################################
# 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 une boîte de dialogue
  • state — Obtenez l'état actuel du chat
  • réinitialiser — Réinitialiser l'état actuel du chat
  • enter_name — Le bot vous demande votre nom
  • enter_age — Le bot vous demande votre âge

méthode start vous demande votre nom et change l'état du chat en nom_attente, c'est à dire. en attente de saisie de votre nom.

Ensuite, vous envoyez le nom et il est traité par la méthode enter_name, le bot vous salue, écrit le nom reçu dans la base de données et fait passer le chat à l'état attendre_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é un texte au lieu d'un numéro, il dira : Ты ввёл некорректные данные, введи число, et attendra que vous ressaisissiez vos données. Si vous avez envoyé un numéro, le robot signalera qu'il a accepté votre âge, écrira les données reçues dans la base de données, signalera toutes les données reçues de votre part et ramènera l'état du chat à sa position d'origine, c'est-à-dire V start.

En appelant la méthode state vous pouvez demander l'état actuel du chat à tout moment et en utilisant le reset remettre le chat à son état d'origine.

Filtres de messages

Dans notre cas, il s’agit de l’une des parties les plus importantes de la création d’un bot. C'est à l'aide de filtres de messages que le bot comprendra quelles informations il attend de vous et comment elles doivent être traitées.

Dans le projet sur GitHub les filtres sont enregistrés dans le fichier message_filters.R.

Code du filtre 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(), afin de demander l'état actuel du chat. Cette fonction ne nécessite qu'un seul argument, l'identifiant du chat.

Filtre suivant nom_attente traite les messages lorsque le chat est dans un état wait_name, et par conséquent le filtre attendre_age traite les messages lorsque le chat est dans un état wait_age.

Gestionnaires

Le fichier avec les gestionnaires s'appelle gestionnaires.R, et a 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)

Nous créons d’abord des gestionnaires de commandes qui vous permettront d’exécuter des méthodes pour démarrer une boîte de dialogue, la réinitialiser et interroger l’état actuel.

Ensuite, nous créons 2 gestionnaires de messages en utilisant les filtres créés à l'étape précédente et leur ajoutons un filtre !MessageFilters$command, afin que nous puissions utiliser les commandes dans n'importe quel état de discussion.

Code de lancement du robot

Maintenant nous avons tout prêt à lancer, le code principal pour lancer le bot est dans le fichier 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()

En conséquence, nous avons obtenu ce bot :
Écrire un bot télégramme en R (partie 4) : Construire un dialogue cohérent et logique avec le bot

A tout moment en utilisant la commande /state nous pouvons interroger l'état actuel du chat et utiliser la commande /reset remettez le chat à son état d'origine et recommencez le dialogue.

Conclusion

Dans cet article, nous avons découvert comment utiliser une base de données dans un bot et comment créer des dialogues logiques séquentiels en enregistrant l'état du chat.

Dans ce cas, nous avons examiné l'exemple le plus primitif, afin qu'il vous soit plus facile de comprendre l'idée de construire de tels robots ; en pratique, vous pouvez construire des dialogues beaucoup plus complexes.

Dans le prochain article de cette série, nous apprendrons comment restreindre les droits des utilisateurs du bot d’utiliser diverses de ses méthodes.

Source: habr.com

Ajouter un commentaire