Écrire un robot de tĂ©lĂ©gramme en langage R (partie 4): Construire un dialogue cohĂ©rent et logique avec un bot

Si vous avez déjà lu les trois articles précédents de cette série, vous savez déjà comment écrire des robots de télégramme à part entiÚre avec un clavier.



Dans cet article, nous allons apprendre à écrire un bot qui maintiendra un dialogue cohérent. Ceux. le bot vous posera des questions et attendra que vous saisissiez des informations. En fonction des données que vous avez saisies, le bot effectuera certaines actions.



Également dans cet article, nous allons apprendre Ă  utiliser une base de donnĂ©es sous le capot d'un bot, dans notre exemple ce sera SQLite, mais vous pouvez utiliser n'importe quel autre SGBD. J'ai Ă©crit plus en dĂ©tail sur l'interaction avec les bases de donnĂ©es dans le langage R dans cet article .





Tous les articles de la série "Ecrire un robot de télégramme en langage R"



  1. Nous créons un bot et envoyons des messages au télégramme en l'utilisant
  2. Ajouter la prise en charge des commandes et des filtres de messages au bot
  3. Comment ajouter la prise en charge du clavier Ă  votre bot
  4. Construire un dialogue cohérent et logique avec le bot
  5. Gestion des droits des utilisateurs du bot


Contenu



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


, :

image



/state , /reset .





, .



Dans ce cas, nous avons examiné l'exemple le plus primitif, afin de vous faciliter la compréhension de l'idée de construire de tels robots, en pratique, vous pouvez construire des dialogues beaucoup plus complexes.



Dans le prochain article de cette série, nous apprendrons comment restreindre les droits des utilisateurs de bot à utiliser diverses méthodes.




All Articles