Se hai già letto i precedenti di questa serie, sai già come scrivere bot di telegram completi con tastiera.
In questo articolo impariamo a scrivere un bot che supporterà un dialogo sequenziale. Cioè, il bot ti farà domande e attenderà che tu fornisca delle informazioni. A seconda dei dati che inserisci, il bot eseguirà alcune azioni.
In questo articolo impareremo anche a utilizzare un database sotto il cofano del bot; nel nostro esempio utilizzeremo SQLite, ma puoi scegliere qualsiasi altro DBMS. Ho scritto in modo più dettagliato sull'interazione con i database nel linguaggio R in .

Tutti gli articoli della serie "Scriviamo un bot di Telegram in R"
Contenuto
Se siete interessati all'analisi dei dati, potrebbero interessarvi i miei e canali. Gran parte del contenuto è dedicato al linguaggio R.
Introduzione
Per consentire al bot di richiedere dati e attendere l'inserimento di informazioni, è necessario tenere traccia dello stato attuale del dialogo. Il modo migliore per fare ciò è utilizzare un database integrato, come SQLite.
Cioè, la logica sarà la seguente. Chiamiamo il metodo del bot, e il bot chiederà in modo sequenziale un'informazione, aspettando l'inserimento di queste informazioni, e potrà anche verificarle.
Scriveremo un bot molto semplice; inizialmente, chiederà il tuo nome e poi l'età, memorizzando i dati nel database. Quando chiede l'età, verificherà che i dati inseriti siano numeri, non testo.
Un dialogo così semplice avrà solo tre stati:
- start — stato normale del bot, in cui non attende informazioni da te
- wait_name — stato in cui il bot attende l'inserimento del nome
- wait_age — stato in cui il bot attende 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 del file del database.
- Creiamo una variabile d'ambiente, in cui sarà memorizzato il percorso del progetto con il bot.
- Creiamo il database stesso e una serie di funzioni affinché il bot possa interagire con esso.
- Scriviamo i metodi del bot, cioè le funzioni che eseguirà.
- Aggiungiamo i filtri di messaggi, con i quali il bot si riferirà ai metodi appropriati, a seconda dello stato attuale della chat.
- Aggiungiamo i gestori, che collegheranno comandi e messaggi ai metodi corretti del bot.
- Avviamo il bot.
Struttura del progetto del bot
Per comodità, divideremo il codice del nostro bot e 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 di messaggi
- handlers.R — gestori
- config.cfg — configurazione del bot
- create_db_data.sql — script SQL per creare una tabella con i dati della chat nel database
- create_db_state.sql — script SQL per creare una tabella dello stato attuale della chat nel database
- bot.db — database del bot
Puoi visualizzare l'intero progetto del bot o dal mio .
Configurazione del bot
Come configurazione utilizzeremo un normale , del seguente tipo:
[bot_settings]
bot_token=IL_TUO_TOKEN_DEL_BOT
[db_settings]
db_path=C:/PERCORSO/ALLA/FOGLIA/DEL/PROGETTO/bot.dbNella configurazione scriviamo il token del bot e il percorso del database, cioè il file bot.db, che creeremo al passo successivo.
Per bot più complessi, è possibile creare configurazioni più articolate; inoltre, non è obbligatorio scrivere una configurazione in formato ini; puoi utilizzare qualsiasi altro formato, incluso JSON.
Creiamo una variabile d'ambiente
Su ogni PC, la cartella del progetto del bot può trovarsi in directory diverse e su dischi diversi, quindi nel codice il percorso della cartella del progetto sarà fornito tramite la variabile d'ambiente TG_BOT_PATH.
Puoi creare una variabile d'ambiente in vari modi; il modo più semplice è definirla in un file .Renviron.
Puoi creare o modificare questo file usando 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/PROGETTODopo di che, salva il file .Renviron e riavvia RStudio.
Creiamo un database
Il passo successivo è la creazione del database. Avremo bisogno di 2 tabelle:
- chat_data — dati richiesti dal bot all'utente
- chat_state — stato attuale di tutte le chat
Possiamo creare queste tabelle utilizzando 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 , puoi utilizzare il seguente codice in R per creare il database.
# Скрипт создания базы данных
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 interagire con il database
Abbiamo già preparato il file di configurazione e creato il database. 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 funzioni semplici:
get_state()— per ottenere lo stato attuale della chat dal databaseset_state()— per registrare lo stato attuale della chat nel databaseget_chat_data()— per ottenere i dati inviati dall'utenteset_chat_data()— per registrare i dati ricevuti dall'utente
Tutte le funzioni sono piuttosto semplici, esse leggono dati dal database utilizzando il comando dbGetQuery(), oppure eseguono UPSERT un'operazione (modifica dei dati esistenti o registrazione di nuovi dati nel database), tramite la funzione dbExecute().
La sintassi dell'operazione UPSERT è la seguente:
INSERT INTO chat_data (chat_id, ${field})
VALUES(${chat_id}, '${value}')
ON CONFLICT(chat_id)
DO UPDATE SET ${field}='${value}';Cioè, nei nostri tavoli il campo chat_id ha una restrizione di unicità ed è la chiave primaria delle tabelle. Inizialmente proviamo ad aggiungere informazioni alla tabella e otteniamo un errore se i dati per la chat corrente sono già presenti; in tal caso, aggiorniamo semplicemente le informazioni per quella chat.
Queste funzioni verranno utilizzate nei metodi e nei filtri del bot.
Metodi del bot
Il prossimo passo nella costruzione del nostro bot sarà la creazione dei metodi. Se hai scaricato il progetto da , tutti i metodi si trovano 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 — Avvio del 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à
Metodo start chiede il tuo nome e cambia lo stato della chat in wait_name, cioè in modalità in attesa dell'inserimento del tuo nome.
Successivamente, invii il nome e questo viene elaborato dal metodo enter_name, il bot ti saluta, registra il nome ricevuto nel database e cambia lo stato della chat in wait_age.
A questo punto, il bot aspetta che tu inserisca la tua età. Inviando la tua età, il bot verifica il messaggio e, se hai inviato un testo invece di un numero, dirà: Hai inserito dati non corretti, per favore inserisci un numero, e attenderà nuovamente i tuoi dati. Se hai inviato un numero, il bot notificherà di aver registrato la tua età, registrerà i dati ricevuti nel database, comunicherà tutti i dati che hai fornito e riporterà lo stato della chat alla condizione iniziale, cioè in start.
Chiamando il metodo state puoi richiedere in qualsiasi momento lo stato attuale della chat, e con il metodo reset riportare la chat allo stato iniziale.
Filtri di messaggi
Nel nostro caso, questa è una delle parti più importanti nella costruzione del bot. Grazie ai filtri dei messaggi, il bot comprenderà quale informazione sta aspettando da te e come deve elaborarla.
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 è in stato wait_name, e di conseguenza il filtro wait_age elabora i messaggi quando la chat è in 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)Iniziamo creando i gestori dei comandi, che ti permetteranno di avviare metodi per avviare il dialogo, ripristinarlo e richiederne lo stato attuale.
Successivamente, creiamo 2 gestori dei messaggi utilizzando i filtri creati nel passaggio precedente e aggiungiamo a essi il filtro !MessageFilters$command, affinché possiamo utilizzare i comandi in qualsiasi stato della chat.
Codice per avviare il bot
Ora abbiamo tutto pronto per l'avvio; il codice principale per avviare il bot si trova nel file bot.R.
library(telegram.bot)
library(tidyverse)
library(RSQLite)
library(DBI)
library(configr)
# passiamo alla 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 come questo:

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.
Conclusione
In questo articolo abbiamo compreso come utilizzare i database all'interno del bot e come costruire dialoghi logici sequenziali attraverso la registrazione dello stato della chat.
In questo caso abbiamo esaminato un esempio molto primitivo, per renderti più facile comprendere l'idea di costruzione di tali bot; nella pratica puoi creare dialoghi molto più complessi.
Nell'articolo seguente di questa serie, impareremo a limitare agli utenti del bot i diritti di utilizzo dei suoi vari metodi.
Fonte: habr.com
