Escribir un bot de Telegram en R (parte 4): construir un diálogo lógico y coherente con el bot

Si ya has leído lo anterior tres artículos de esta serie, entonces ya sabes cómo escribir bots de Telegram completos con un teclado.

En este artículo, aprenderemos cómo escribir un bot que mantendrá un diálogo constante. Aquellos. El bot te hará preguntas y esperará a que ingreses cierta información. Dependiendo de los datos que ingreses, el bot realizará algunas acciones.

También en este artículo aprenderemos cómo usar una base de datos bajo el capó del bot, en nuestro ejemplo será SQLite, pero puedes usar cualquier otro DBMS. Escribí con más detalle sobre la interacción con bases de datos en el lenguaje R en este artículo.

Escribir un bot de Telegram en R (parte 4): construir un diálogo lógico y coherente con el bot

Todos los artículos de la serie "Escribir un bot de Telegram en R"

  1. Creamos un bot y lo usamos para enviar mensajes en Telegram.
  2. Agregue soporte de comandos y filtros de mensajes al bot
  3. Cómo agregar soporte de teclado a un bot
  4. Construyendo un diálogo consistente y lógico con el bot

contenido

Si te interesa el análisis de datos, quizás te interese mi Telegram. и Youtube canales La mayor parte del contenido está dedicado al lenguaje R.

  1. introducción
  2. Proceso de construcción de robots
  3. Estructura del proyecto de bot
  4. Configuración de bot
  5. Crear una variable de entorno
  6. Creando una base de datos
  7. Funciones de escritura para trabajar con la base de datos.
  8. Métodos de robots
  9. Filtros de mensajes
  10. Manipuladores
  11. Código de lanzamiento del bot
  12. Conclusión

introducción

Para que el bot le solicite datos y espere a que ingrese cualquier información, deberá registrar el estado actual del diálogo. La mejor manera de hacerlo es utilizar algún tipo de base de datos integrada, como SQLite.

Aquellos. La lógica será la siguiente. Llamamos al método del bot, y el bot nos solicita secuencialmente cierta información, y en cada paso espera a que se ingrese esta información y pueda verificarla.

Escribiremos el bot más simple posible, primero le preguntará su nombre, luego su edad y guardará los datos recibidos en la base de datos. Al pedir edad comprobará que el dato introducido sea un número y no texto.

Un diálogo tan simple tendrá sólo tres estados:

  1. El inicio es el estado normal del bot, en el que no espera ninguna información de usted.
  2. wait_name: estado en el que el bot espera que se ingrese un nombre
  3. wait_age es el estado en el que el bot espera que se ingrese tu edad, la cantidad de años completos.

Proceso de construcción de robots

A lo largo del artículo, construiremos un bot paso a paso; todo el proceso se puede representar esquemáticamente de la siguiente manera:
Escribir un bot de Telegram en R (parte 4): construir un diálogo lógico y coherente con el bot

  1. Creamos una configuración de bot en la que almacenaremos algunas configuraciones. En nuestro caso, el token del bot y la ruta al archivo de la base de datos.
  2. Creamos una variable de entorno en la que se almacenará la ruta al proyecto con el bot.
  3. Creamos la propia base de datos y una serie de funciones para que el bot pueda interactuar con ella.
  4. Escribimos métodos de bot, es decir. las funciones que realizará.
  5. Agregar filtros de mensajes. Con la ayuda del cual el bot accederá a los métodos necesarios, dependiendo del estado actual del chat.
  6. Agregamos controladores que conectarán comandos y mensajes con los métodos de bot necesarios.
  7. Lancemos el bot.

Estructura del proyecto de bot

Para mayor comodidad, dividiremos el código de nuestro bot y otros archivos relacionados en la siguiente estructura.

  • bot.R — el código principal de nuestro bot
  • db_bot_function.R — un bloque de código con funciones para trabajar con la base de datos
  • métodos_bot.R — código de métodos de bot
  • filtros_mensaje.R — filtros de mensajes
  • manejadores.R - manipuladores
  • configuración.cfg - configuración del robot
  • crear_db_data.sql — Script SQL para crear una tabla con datos de chat en la base de datos
  • crear_db_state.sql — Script SQL para crear una tabla del estado actual del chat en la base de datos
  • bot.db - base de datos de robots

Puede ver todo el proyecto del bot, o descargar de mi repositorio en GitHub.

Configuración de bot

Usaremos el habitual como configuración. archivo ini, la siguiente forma:

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

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

En la configuración escribimos el token del bot y la ruta a la base de datos, es decir. al archivo bot.db; crearemos el archivo en sí en el siguiente paso.

Para bots más complejos, puede crear configuraciones más complejas; además, no es necesario escribir una configuración ini, puede usar cualquier otro formato, incluido JSON.

Crear una variable de entorno

En cada PC, la carpeta con el proyecto del bot se puede ubicar en diferentes directorios y en diferentes unidades, por lo que en el código la ruta a la carpeta del proyecto se establecerá mediante una variable de entorno. TG_BOT_PATH.

Hay varias formas de crear una variable de entorno, la más sencilla es escribirla en un archivo .Renviron.

Puede crear o editar este archivo usando el comando file.edit(path.expand(file.path("~", ".Renviron"))). Ejecútelo y agregue una línea al archivo:

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

Luego guarde el archivo .Renviron y reinicie RStudio.

Creando una base de datos

El siguiente paso es crear una base de datos. Necesitaremos 2 mesas:

  • chat_data — datos que el bot solicitó al usuario
  • chat_state — estado actual de todos los chats

Puede crear estas tablas utilizando la siguiente consulta SQL:

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 descargaste el proyecto del bot desde GitHub, luego para crear la base de datos puedes usar el siguiente código en 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'))

Funciones de escritura para trabajar con la base de datos.

Ya tenemos listo un archivo de configuración y una base de datos creada. Ahora necesita escribir funciones para leer y escribir datos en esta base de datos.

Si descargaste el proyecto desde GitHub, entonces puedes encontrar las funciones en el archivo db_bot_function.R.

Código de función para trabajar con la base de datos.

# ###########################################################
# 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]])

}

Creamos 4 funciones simples:

  • get_state() — obtener el estado actual del chat de la base de datos
  • set_state() — escribe el estado actual del chat en la base de datos
  • get_chat_data() — recibir datos enviados por el usuario
  • set_chat_data() — registrar datos recibidos del usuario

Todas las funciones son bastante simples, leen datos de la base de datos usando el comando dbGetQuery(), o cometer UPSERT operación (cambiar datos existentes o escribir nuevos datos en la base de datos), utilizando la función dbExecute().

La sintaxis de la operación UPSERT es la siguiente:

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

Aquellos. en nuestro campo de tablas id_chat tiene una restricción de unicidad y es la clave principal de las tablas. Inicialmente, intentamos agregar información a la tabla y obtenemos un error si los datos del chat actual ya están presentes, en cuyo caso simplemente actualizamos la información de este chat.

A continuación, usaremos estas funciones en los métodos y filtros del bot.

Métodos de robots

El siguiente paso en la construcción de nuestro bot es crear métodos. Si descargaste el proyecto desde GitHub, entonces todos los métodos están en el archivo métodos_bot.R.

Código del método del 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')
  }

}

Creamos 5 métodos:

  • inicio: iniciar un diálogo
  • estado: obtiene el estado actual del chat
  • reset — Restablecer el estado actual del chat
  • enter_name — El bot te pregunta tu nombre
  • enter_age — El bot pregunta tu edad

método start le pregunta su nombre y cambia el estado del chat a nombre_espera, es decir. en espera para ingresar su nombre.

A continuación se envía el nombre y se procesa mediante el método enter_name, el bot te saluda, escribe el nombre recibido en la base de datos y cambia el chat al estado edad_espera.

En esta etapa, el robot espera que ingreses tu edad. Envías tu edad, el bot revisa el mensaje, si enviaste algún texto en lugar de un número, dirá: Ты ввёл некорректные данные, введи число, y esperará a que vuelvas a introducir tus datos. Si envió un número, el bot informará que aceptó su edad, escribirá los datos recibidos en la base de datos, informará todos los datos recibidos de usted y devolverá el estado del chat a su posición original, es decir. V start.

Llamando al método state Puede solicitar el estado actual del chat en cualquier momento y utilizando el reset devolver el chat a su estado original.

Filtros de mensajes

En nuestro caso, esta es una de las partes más importantes en la creación de un bot. Es con la ayuda de filtros de mensajes que el bot comprenderá qué información espera de usted y cómo debe procesarse.

En el proyecto de GitHub Los filtros están registrados en el archivo. filtros_mensaje.R.

Código de filtro de mensajes:

# ###########################################################
# 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"
}
)

En filtros utilizamos la función previamente escrita get_state(), para solicitar el estado actual del chat. Esta función requiere solo 1 argumento, ID de chat.

Siguiente filtro nombre_espera procesa mensajes cuando el chat está en un estado wait_name, y en consecuencia el filtro edad_espera procesa mensajes cuando el chat está en un estado wait_age.

Manipuladores

El archivo con controladores se llama manejadores.R, y tiene el siguiente código:

# ###########################################################
# 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)

Primero creamos controladores de comandos que le permitirán ejecutar métodos para iniciar un diálogo, restablecerlo y consultar el estado actual.

A continuación, creamos 2 controladores de mensajes usando los filtros creados en el paso anterior y les agregamos un filtro. !MessageFilters$command, para que podamos usar comandos en cualquier estado del chat.

Código de lanzamiento del bot

Ahora tenemos todo listo para iniciar, el código principal para iniciar el bot está en el archivo 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()

Como resultado, obtuvimos este bot:
Escribir un bot de Telegram en R (parte 4): construir un diálogo lógico y coherente con el bot

En cualquier momento usando el comando /state podemos consultar el estado actual del chat y usar el comando /reset Devuelve el chat a su estado original y comienza el diálogo nuevamente.

Conclusión

En este artículo, descubrimos cómo usar una base de datos dentro de un bot y cómo crear diálogos lógicos secuenciales registrando el estado del chat.

En este caso, analizamos el ejemplo más primitivo, para que le resulte más fácil comprender la idea de crear tales bots; en la práctica, puede crear diálogos mucho más complejos.

En el próximo artículo de esta serie, aprenderemos cómo restringir los derechos de los usuarios del bot para utilizar varios de sus métodos.

Fuente: habr.com

Añadir un comentario