Se hai già letto i precedenti di questa serie, allora sai già come scrivere bot di Telegram completi con tastiera.
In questo articolo impareremo a scrivere un bot che supporterà un dialogo sequenziale. Cioè, il bot ti farà domande e aspetterà che tu inserisca alcune informazioni. A seconda dei dati che inserisci, il bot eseguirà alcune azioni.
Inoltre, in questo articolo impareremo a utilizzare un database sotto il cofano del bot, nel nostro esempio utilizzeremo SQLite, ma puoi utilizzare qualsiasi altro DBMS. Maggiori dettagli su come interagire con i database in R li ho scritti in .

Tutti gli articoli della serie "Scriviamo un bot telegram in R"
Contenuto
Se sei interessato all'analisi dei dati, potresti trovare interessanti i miei e canali. Gran parte del contenuto è dedicato al linguaggio R.
Introduzione
Perché il bot possa richiedere dati da te e aspettare l'inserimento di alcune informazioni, sarà necessario tenere traccia dello stato attuale del dialogo. Il modo migliore per farlo è utilizzare un database incorporato, come SQLite.
Cioè, la logica sarà la seguente. Chiamiamo il metodo del bot e il bot chiede sequenzialmente alcune informazioni, aspettando l'inserimento di queste informazioni a ogni passo, e può controllarle.
Scriveremo un bot il più semplice possibile, inizialmente chiederà il tuo nome, poi l'età, e salverà i dati ottenuti nel database. Al momento della richiesta dell'età, controllerà che i dati inseriti siano un numero e non un testo.
Un tale dialogo semplice avrà solo tre stati:
- start — stato normale del bot, in cui non aspetta alcuna informazione da te
- wait_name — stato in cui il bot aspetta l'inserimento del nome
- wait_age — stato in cui il bot aspetta l'inserimento della tua età, il numero di anni compiuti.
Il processo di costruzione del bot
Nel corso dell'articolo costruiremo il bot passo dopo passo, l'intero processo può essere schematicamente rappresentato come segue:

- Creiamo la configurazione del bot, in cui conserveremo alcune impostazioni. Nel nostro caso, il token del bot e il percorso al file del database.
- Creiamo una variabile ambiente, in cui sarà memorizzato il percorso al progetto con il bot.
- Creiamo il database e una serie di funzioni affinché il bot possa interagire con esso.
- Scriviamo i metodi del bot, ossia le funzioni che eseguirà.
- Aggiungiamo filtri per i messaggi. Con cui il bot potrà fare riferimento ai metodi appropriati, a seconda dello stato attuale della chat.
- Aggiungiamo gestori che collegheranno i comandi e i messaggi ai metodi pertinenti del bot.
- Avviamo il bot.
Struttura del progetto del bot
Per comodità, suddivideremo il codice del nostro bot e gli altri file correlati nella seguente struttura.
- bot.R — codice principale del nostro bot
- db_bot_function.R — blocco di codice con funzioni per lavorare con il database
- bot_methods.R — codice dei metodi del bot
- message_filters.R — filtri per i messaggi
- handlers.R — gestori
- config.cfg — configurazione del bot
- create_db_data.sql — script SQL per la creazione della tabella con i dati della chat nel database
- create_db_state.sql — script SQL per la creazione della tabella dello stato attuale della chat nel database
- bot.db — database del bot
Tutto il progetto del bot può essere visualizzato, o dal mio .
Configurazione del bot
Come configurazione, utilizzeremo un normale , del seguente tipo:
[bot_settings]
bot_token=IL_TOKEN_DEL_TUO_BOT
[db_settings]
db_path=C:\/PERCORSO\/A\/FOLDER\/PROGETTO\/bot.dbNella configurazione registriamo il token del bot e il percorso al database, ossia al file bot.db, che creeremo nel passaggio successivo.
Per bot più complessi si possono creare anche configurazioni più elaborate; inoltre non è necessario utilizzare esclusivamente una configurazione ini, è possibile utilizzare un altro formato incluso il JSON.
Creiamo una variabile ambiente
In ogni PC, la cartella del progetto del bot può trovarsi in directory diverse e su dischi diversi, pertanto nel codice il percorso alla cartella del progetto sarà assegnato tramite una variabile d'ambiente TG_BOT_PATH.
Creare una variabile d'ambiente è possibile in vari modi; il più semplice è dichiararla nel file .Renviron.
Questo file può essere creato o modificato utilizzando il comando file.edit(path.expand(file.path("~", ".Renviron"))). Esegui questo comando e aggiungi nel file una riga:
TG_BOT_PATH=C:\/PERCORSO\/AL\/TUO\/PROGETTOPoi salva il file .Renviron e riavvia RStudio.
Creiamo un database
Il passo successivo è la creazione del database. Avremo bisogno di 2 tabelle:
- chat_data — i dati che il bot ha richiesto all'utente
- chat_state — lo stato attuale di tutte le chat
Queste tabelle possono essere create con la seguente query 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 hai scaricato il progetto del bot da , quindi per creare un database puoi utilizzare il seguente codice in 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'))
Scriviamo funzioni per lavorare con il database
Abbiamo già un file di configurazione pronto e un database creato. Ora è necessario scrivere le funzioni per leggere e scrivere i dati in questo database.
Se hai scaricato il progetto da , puoi trovare le funzioni nel file db_bot_function.R.
Codice delle funzioni per lavorare con il database
# ###########################################################
# 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]])
}Abbiamo creato 4 semplici funzioni:
get_state()— ottenere lo stato attuale della chat dal DBset_state()— registrare lo stato attuale della chat nel DBget_chat_data()— ottenere i dati inviati dall'utenteset_chat_data()— registrare i dati ricevuti dall'utente
Tutte le funzioni sono piuttosto semplici, esse leggono i dati dal database utilizzando il comando dbGetQuery(), oppure eseguono UPSERT un'operazione (modifica di dati esistenti o registrazione di nuovi dati nel DB), utilizzando la funzione dbExecute().
La sintassi dell'operazione UPSERT appare come segue:
INSERT INTO chat_data (chat_id, ${field})
VALUES(${chat_id}, '${value}')
ON CONFLICT(chat_id)
DO UPDATE SET ${field}='${value}';Cioè, nel nostro database, il campo chat_id ha una restrizione di unicità e funge da chiave primaria delle tabelle. Inizialmente proviamo ad aggiungere informazioni nella tabella e otteniamo un errore se i dati per la chat attuale sono già presenti; in tal caso, aggiorniamo semplicemente le informazioni per questa chat.
Queste funzioni verranno utilizzate nei metodi e nei filtri del bot.
Metodi del bot
Il passo successivo nella creazione del nostro bot sarà la scrittura dei metodi. Se hai scaricato il progetto da , tutti i metodi sono nel file bot_methods.R.
Codice dei metodi del 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')
}
}Abbiamo creato 5 metodi:
- start — Avviare il dialogo
- state — Ottenere lo stato attuale della chat
- reset — Ripristinare lo stato attuale della chat
- enter_name — Il bot chiede il tuo nome
- enter_age — Il bot chiede la tua età
Sanitizer.replaceElementWithChildren() start chiede il tuo nome e cambia lo stato della chat in wait_name, cioè in modalità attesa per l'inserimento del tuo nome.
Successivamente, invii il nome e viene elaborato dal metodo enter_name, il bot ti saluta, registra il nome ottenuto nel database e cambia la chat nello stato wait_age.
A questo punto, il bot aspetta che tu inserisca la tua età. Quando invii la tua età, il bot controlla il messaggio; se hai inviato del testo invece di un numero, dirà: Hai inserito dati non validi, inserisci un numero, e aspetterà un nuovo input da parte vostra. Se inviate un numero, il bot comunicherà che ha ricevuto la vostra età, registrerà i dati ricevuti nel database, mostrerà tutte le informazioni ricevute da voi e riporterà lo stato della chat alla posizione iniziale, ossia a start.
Chiamando il metodo state potete in qualsiasi momento richiedere lo stato attuale della chat, e con il metodo reset riportare la chat allo stato iniziale.
Filtri dei messaggi
Nel nostro caso, questa è una delle parti più importanti nella costruzione del bot. Proprio grazie ai filtri dei messaggi, il bot comprenderà quali informazioni sta aspettando da voi e come devono essere elaborate.
Nel progetto su i filtri sono definiti nel file message_filters.R.
Codice dei filtri dei messaggi:
# ###########################################################
# 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"
}
)Nei filtri utilizziamo la funzione scritta in precedenza get_state(), per richiedere lo stato attuale della chat. Questa funzione richiede solo 1 argomento, l'id della chat.
Successivamente, il filtro wait_name elabora i messaggi quando la chat si trova nello stato wait_name, e di conseguenza il filtro wait_age elabora i messaggi quando la chat si trova nello stato wait_age.
Gestori
Il file con i gestori si chiama handlers.R, e ha il seguente codice:
# ###########################################################
# 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)Per prima cosa creiamo i gestori dei comandi, che vi permetteranno di avviare metodi per iniziare un dialogo, resettarlo e richiedere lo stato attuale.
Successivamente, creiamo 2 gestori dei messaggi utilizzando i filtri creati nel passo precedente, e aggiungiamo ad essi il filtro !MessageFilters$command, in modo da poter utilizzare i comandi in qualsiasi stato della chat.
Codice di avvio del bot
Ora abbiamo tutto pronto per il lancio, il codice principale per avviare il bot si trova nel file bot.R.
library(telegram.bot)
library(tidyverse)
library(RSQLite)
library(DBI)
library(configr)
# cambiamo nella cartella del progetto
setwd(Sys.getenv('TG_BOT_PATH'))
# leggiamo la configurazione
cfg <- read.config('config.cfg')
# creiamo un'istanza del bot
updater <- Updater(cfg$bot_settings$bot_token)
# Caricamento dei componenti del bot
source('db_bot_function.R') # funzioni per lavorare con il database
source('bot_methods.R') # metodi del bot
source('message_filters.R') # filtri dei messaggi
source('handlers.R') # gestori dei messaggi
# Aggiungiamo i gestori al dispatcher
updater <- updater +
start_h +
wait_age_h +
wait_name_h +
state_h +
reset_h
# Avviamo il bot
updater$start_polling()Di conseguenza, abbiamo ottenuto un bot di questo tipo:

In qualsiasi momento, con il comando /state possiamo richiedere lo stato attuale della chat, e con il comando /reset riportare la chat allo stato iniziale e ricominciare il dialogo da capo.
Conclusione
In questo articolo abbiamo esaminato come utilizzare i database all'interno del bot e come costruire dialoghi logici sequenziali grazie alla fissazione dello stato della chat.
In questo caso abbiamo esaminato il più semplice degli esempi, per rendere più chiara l'idea della creazione di tali bot; nella pratica puoi costruire dialoghi molto più complessi.
Nell'articolo successivo di questa serie impareremo a limitare i diritti degli utenti del bot sull'utilizzo dei suoi diversi metodi.
Fonte: habr.com
