Se você já se familiarizou com os três artigos anteriores Neste artigo, vamos aprender a escrever um bot que suportará um diálogo sequencial. Ou seja, o bot fará perguntas e aguardará que você insira alguma informação. Dependendo dos dados que você inserir, o bot executará algumas ações.
Neste artigo, também aprenderemos a usar um banco de dados por trás do bot, no nosso exemplo será o SQLite, mas você pode usar qualquer outro SGBD. Escrevi mais detalhadamente sobre a interação com bancos de dados na linguagem R em
Todos os artigos da série "Escrevendo um bot do telegram na linguagem R" .

Criando um bot e enviando mensagens através dele no telegram
Conteúdo
youtube e O processo de construção do bot
Introdução
Ou seja, a lógica será a seguinte. Chamamos o método do bot, e o bot solicita sequencialmente alguma informação, aguardando a entrada dessa informação a cada passo, e pode verificar sua validade.
Vamos escrever um bot o mais simples possível, que primeiro perguntará seu nome e depois sua idade, salvando esses dados no banco de dados. Ao solicitar a idade, ele verificará se os dados inseridos são um número e não texto.
Esse diálogo simples terá apenas três estados:
start — estado normal do bot, no qual ele não espera nenhuma informação sua
- wait_name — estado em que o bot aguarda a inserção do nome
- wait_age — estado em que o bot aguarda a inserção da sua idade, em anos completos.
- wait_age — o estado em que o bot aguarda a entrada da sua idade, o número de anos completos.
Estrutura do projeto do bot
Neste artigo, iremos passo a passo construir um bot, todo o processo pode ser representado esquematicamente da seguinte forma:

- Criamos a configuração do bot, onde armazenaremos algumas configurações. No nosso caso, o token do bot e o caminho para o arquivo do banco de dados.
- Criamos uma variável de ambiente, onde será armazenado o caminho do projeto do bot.
- Criamos o banco de dados em si e uma série de funções para que o bot possa interagir com ele.
- Escrevemos os métodos do bot, ou seja, as funções que ele irá executar.
- Adicionamos filtros de mensagens, através dos quais o bot chamará os métodos necessários, dependendo do estado atual do chat.
- Adicionamos manipuladores que conectarão os comandos e mensagens aos métodos apropriados do bot.
- Iniciamos o bot.
Configuração do bot
Para facilitar, dividiremos o código do nosso bot e outros arquivos relacionados na seguinte estrutura.
- bot.R — código principal do nosso bot
- db_bot_function.R — bloco de código com funções para trabalhar com o banco de dados
- bot_methods.R — código dos métodos do bot
- message_filters.R — filtros de mensagens
- handlers.R — manipuladores
- config.cfg — configuração do bot
- create_db_data.sql — script SQL para criar a tabela com os dados do chat no banco de dados
- create_db_state.sql — script SQL para criar a tabela do estado atual do chat no banco de dados
- bot.db — banco de dados do bot
Todo o projeto do bot pode ser visualizado, ou do meu .
Criando variável de ambiente
Como configuração, vamos usar um simples , do seguinte formato:
[bot_settings]
bot_token=SEU_TOKEN_AQUI
[db_settings]
db_path=C:/CAMINHO/PARA/PASTA/DO/PROJETO/bot.dbNa configuração, registramos o token do bot e o caminho para o banco de dados, ou seja, para o arquivo bot.db, que criaremos na próxima etapa.
Para bots mais complexos, é possível criar configurações mais elaboradas; além disso, não é obrigatório usar apenas configurações ini, você pode utilizar qualquer outro formato, incluindo JSON.
Criando banco de dados
Em cada PC, a pasta do projeto do bot pode estar localizada em diferentes diretórios e em diferentes discos, por isso, no código, o caminho para a pasta do projeto será definido através de uma variável de ambiente TG_BOT_PATH.
Criar uma variável de ambiente pode ser feito de várias maneiras, a mais simples é descrevê-la no arquivo .Renviron.
Criar ou editar esse arquivo pode ser feito com o comando file.edit(path.expand(file.path("~", ".Renviron"))). Execute-o e adicione ao arquivo uma linha:
TG_BOT_PATH=C:/CAMINHO/PARA/SEU/PROJETOEm seguida, salve o arquivo .Renviron e reinicie o RStudio.
Escrevendo funções para trabalhar com o banco de dados
O próximo passo é criar o banco de dados. Precisaremos de 2 tabelas:
- chat_data — dados que o bot solicitou ao usuário
- chat_state — estado atual de todos os chats
Essas tabelas podem ser criadas com a seguinte 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
);
Se você baixou o projeto do bot em , você pode usar o seguinte código em R para criar o banco.
# Скрипт создания базы данных
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'))
Métodos do bot
Já temos um arquivo de configuração e o banco de dados criado. Agora precisamos escrever funções para ler e gravar dados nesse banco.
Se você baixou o projeto de , você pode encontrar as funções no arquivo db_bot_function.R.
Código das funções para operação com o banco de dados
# ###########################################################
# 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]])
}Criamos 4 funções simples:
get_state()— obter o estado atual do chat do BDset_state()— gravar o estado atual do chat no BDget_chat_data()— obter dados enviados pelo usuárioset_chat_data()— gravar dados recebidos do usuário
Todas as funções são bastante simples, elas ou leem dados do banco usando o comando dbGetQuery(), ou realizam UPSERT a operação (modificação de dados existentes ou gravação de novos dados no BD), usando a função dbExecute().
A sintaxe da operação UPSERT é a seguinte:
INSERT INTO chat_data (chat_id, ${field})
VALUES(${chat_id}, '${value}')
ON CONFLICT(chat_id)
DO UPDATE SET ${field}='${value}';Ou seja, em nossas tabelas, o campo chat_id tem uma restrição de exclusividade e é a chave primária das tabelas. Inicialmente, tentamos adicionar informações à tabela e recebemos um erro se os dados do chat atual já estiverem presentes, nesse caso, apenas atualizamos as informações desse chat.
Em seguida, usaremos essas funções nos métodos e filtros do bot.
Filtros de mensagens
O próximo passo na construção do nosso bot será a criação de métodos. Se você baixou o projeto de , todos os métodos estão no arquivo bot_methods.R.
Código dos métodos do 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')
}
}Criamos 5 métodos:
- start — Iniciar o diálogo
- state — Obter o estado atual do chat
- reset — Redefinir o estado atual do chat
- enter_name — O bot solicita seu nome
- enter_age — O bot solicita sua idade
Método start solicita seu nome e muda o estado do chat para wait_name, ou seja, em modo de espera para a entrada do seu nome.
Em seguida, você envia o nome e ele é processado pelo método enter_name, o bot cumprimenta você, grava o nome recebido no banco e muda o chat para o estado wait_age.
Neste estágio, o bot aguarda que você insira a sua idade. Você envia sua idade, o bot verifica a mensagem; se em vez de um número você enviar algum texto, ele dirá: Você inseriu dados incorretos, insira um número, e aguardará que você insira os dados novamente. Caso você tenha enviado um número, o bot informará que aceitou sua idade, registrará os dados recebidos e colocará o estado do chat de volta à posição inicial, ou seja, em start.
Chamando o método state você pode solicitar a qualquer momento o estado atual do chat, e com o método resetar reiniciar o chat para o estado inicial.
Manipuladores
No nosso caso, esta é uma das partes mais importantes na construção do bot. É exatamente por meio de filtros de mensagens que o bot entenderá qual informação ele espera de você e como deve processá-la.
No projeto em os filtros estão definidos no arquivo message_filters.R.
Código dos filtros de mensagens:
# ###########################################################
# 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"
}
)Nos filtros, utilizamos a função escrita anteriormente get_state(), para solicitar o estado atual do chat. Essa função requer apenas 1 argumento, o id do chat.
Em seguida, o filtro wait_name processa mensagens quando o chat está no estado wait_name, e, consequentemente, o filtro wait_age processa mensagens quando o chat está no estado wait_age.
Código para iniciar o bot
O arquivo com os manipuladores chama-se handlers.R, e possui o seguinte 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)Primeiro, criamos manipuladores de comandos, que permitirão que você inicie métodos para iniciar o diálogo, redefini-lo e solicitar o estado atual.
Depois, criamos 2 manipuladores de mensagens utilizando os filtros criados na etapa anterior, e adicionamos a eles o filtro !MessageFilters$command, para que possamos usar comandos em qualquer estado do chat.
Para que o bot possa solicitar dados de você e aguardar a inserção de alguma informação, será necessário registrar o estado atual do diálogo. A melhor maneira de fazer isso é usar um banco de dados embutido, como o SQLite.
Agora está tudo pronto para o início; o código principal para iniciar o bot está no arquivo bot.R.
library(telegram.bot)
library(tidyverse)
library(RSQLite)
library(DBI)
library(configr)
# Vamos para a pasta do projeto
setwd(Sys.getenv('TG_BOT_PATH'))
# Lendo a configuração
cfg <- read.config('config.cfg')
# Criando a instância do bot
updater <- Updater(cfg$bot_settings$bot_token)
# Carregando os componentes do bot
source('db_bot_function.R') # funções para trabalhar com o BD
source('bot_methods.R') # métodos do bot
source('message_filters.R') # filtros de mensagens
source('handlers.R') # manipuladores de mensagens
# Adicionando manipuladores ao dispatcher
updater <- updater +
start_h +
wait_age_h +
wait_name_h +
state_h +
reset_h
# Iniciando o bot
updater$start_polling()Como resultado, nosso bot ficou assim:

A qualquer momento, com o comando /state podemos solicitar o estado atual do chat, e com o comando /reset restaurar o chat ao estado original e iniciar o diálogo novamente.
Conclusão
Neste artigo, discutimos como usar bancos de dados dentro do bot e como construir diálogos lógicos sequenciais por meio da fixação do estado do chat.
Neste caso, examinamos o exemplo mais primitivo, para que você possa entender melhor a ideia de construir esses bots; na prática, você pode criar diálogos muito mais complexos.
No próximo artigo desta série, aprenderemos a limitar os direitos dos usuários do bot sobre o uso de seus diferentes métodos.
Fonte: habr.com
