Scrivere un bot Telegram in R (parte 4): Creazione di un dialogo sequenziale e logico con il bot

Se hai già letto i precedenti tre articoli 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 questo articolo.

Scrivere un bot Telegram in R (parte 4): Creazione di un dialogo sequenziale e logico con il bot

Tutti gli articoli della serie "Scriviamo un bot di Telegram in R"

  1. Creiamo un bot e inviamo messaggi tramite esso su Telegram
  2. Aggiungiamo supporto per comandi e filtri dei messaggi
  3. Come aggiungere supporto per la tastiera al bot
  4. Costruzione di un dialogo sequenziale e logico con il bot

Contenuto

Se siete interessati all'analisi dei dati, potrebbero interessarvi i miei telegram e youtube canali. Gran parte del contenuto è dedicato al linguaggio R.

  1. Introduzione
  2. Il processo di costruzione del bot
  3. Struttura del progetto del bot
  4. Configurazione del bot
  5. Creiamo una variabile d'ambiente
  6. Creiamo un database
  7. Scriviamo funzioni per interagire con il database
  8. Metodi del bot
  9. Filtri di messaggi
  10. Gestori
  11. Codice per avviare il bot
  12. Conclusione

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:

  1. start — stato normale del bot, in cui non attende informazioni da te
  2. wait_name — stato in cui il bot attende l'inserimento del nome
  3. 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:
Scrivere un bot Telegram in R (parte 4): Creazione di un dialogo sequenziale e logico con il bot

  1. Creiamo la configurazione del bot, in cui conserveremo alcune impostazioni. Nel nostro caso, il token del bot e il percorso del file del database.
  2. Creiamo una variabile d'ambiente, in cui sarà memorizzato il percorso del progetto con il bot.
  3. Creiamo il database stesso e una serie di funzioni affinché il bot possa interagire con esso.
  4. Scriviamo i metodi del bot, cioè le funzioni che eseguirà.
  5. Aggiungiamo i filtri di messaggi, con i quali il bot si riferirà ai metodi appropriati, a seconda dello stato attuale della chat.
  6. Aggiungiamo i gestori, che collegheranno comandi e messaggi ai metodi corretti del bot.
  7. 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 scaricare dal mio un repository su GitHub.

Configurazione del bot

Come configurazione utilizzeremo un normale file ini, del seguente tipo:

[bot_settings]
bot_token=IL_TUO_TOKEN_DEL_BOT

[db_settings]
db_path=C:/PERCORSO/ALLA/FOGLIA/DEL/PROGETTO/bot.db

Nella 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/PROGETTO

Dopo 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 GitHub, 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 GitHub, 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 database
  • set_state() — per registrare lo stato attuale della chat nel database
  • get_chat_data() — per ottenere i dati inviati dall'utente
  • set_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 GitHub, 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 GitHub 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:
Scrivere un bot Telegram in R (parte 4): Creazione di un dialogo sequenziale e logico con il bot

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

Acquista hosting affidabile per siti web con protezione DDoS, server VPS VDS 🔥 Acquista hosting affidabile per siti web con protezione DDoS, server VPS VDS | ProHoster