Escrevendo um bot de telegrama na linguagem R (parte 4): Construindo um diálogo consistente e lógico com um bot

Se você já leu os três artigos anteriores desta série, então já sabe como escrever bots de telegramas completos com um teclado.



Neste artigo, aprenderemos como escrever um bot que manterá um diálogo consistente. Essa. o bot fará perguntas e esperará que você insira qualquer informação. Dependendo dos dados inseridos, o bot realizará algumas ações.



Também neste artigo, aprenderemos como usar um banco de dados sob o capô de um bot, em nosso exemplo será SQLite, mas você pode usar qualquer outro DBMS. Escrevi com mais detalhes sobre como interagir com bancos de dados na linguagem R neste artigo .





Todos os artigos da série "Escrevendo um bot de telegrama na linguagem R"



  1. Nós criamos um bot e enviamos mensagens para telegrama com ele
  2. Adicionar suporte de comando e filtros de mensagem ao bot
  3. Como adicionar suporte de teclado ao seu bot
  4. Construir um diálogo consistente e lógico com o bot
  5. Gerenciamento de direitos de usuário de bot


Conteúdo



telegram youtube . R.







, , - . , , SQLite.



.. . , - , , .



, , , . , , .



:



  1. start — ,
  2. wait_name — ,
  3. wait_age — , , .




, :



  1. , . , .
  2. , .
  3. , , .
  4. , .. .
  5. . , .
  6. , .
  7. .




, .



  • bot.R
  • db_bot_function.R
  • bot_methods.R
  • message_filters.R
  • handlers.R
  • config.cfg
  • create_db_data.sql — SQL
  • create_db_state.sql — SQL
  • bot.db


, GitHub.





ini , :



[bot_settings]
bot_token=__

[db_settings]
db_path=C://///bot.db


, , .. bot.db, .



, ini , JSON.





, , TG_BOT_PATH.



, — .Renviron.



, file.edit(path.expand(file.path("~", ".Renviron"))). :



TG_BOT_PATH=C:////


.Renviron RStudio.





— . 2 :



  • chat_data —
  • chat_state —


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


GitHub, 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'))




. .



GitHub, db_bot_function.R.



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

}


4 :



  • get_state()
  • set_state()
  • get_chat_data()
  • set_chat_data()


, dbGetQuery(), UPSERT ( ), dbExecute().



UPSERT :



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


.. chat_id . , , .



.





. GitHub, bot_methods.R.



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

}


5 :



  • start —
  • state —
  • reset —
  • enter_name —
  • enter_age —


start , wait_name, .. .



, enter_name, , , wait_age.



. , , - : , , . , , , , , .. start.



state , reset .





. , .



GitHub message_filters.R.



:



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


get_state(), , . 1 , id .



wait_name wait_name, wait_age wait_age.





handlers.R, :



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


, , , .



2 , !MessageFilters$command, , .





, 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()


, :

imagem



/state , /reset .





, .



Neste caso, consideramos o exemplo mais primitivo, a fim de tornar mais fácil para você entender a ideia de construir tais bots, na prática você pode construir diálogos muito mais complexos.



No próximo artigo desta série, aprenderemos como restringir os direitos dos usuários de bot de usar vários métodos.




All Articles