Γράφοντας ένα bot τηλεγραφήματος σε R (μέρος 4): Δημιουργία ενός συνεπούς, λογικού διαλόγου με το ρομπότ

Αν έχετε ήδη διαβάσει το προηγούμενο τρία άρθρα από αυτήν τη σειρά, τότε ξέρετε ήδη πώς να γράφετε πλήρεις ρομπότ τηλεγραφήματος με πληκτρολόγιο.

Σε αυτό το άρθρο, θα μάθουμε πώς να γράφουμε ένα bot που θα διατηρεί έναν συνεπή διάλογο. Εκείνοι. Το bot θα σας κάνει ερωτήσεις και θα σας περιμένει να εισάγετε κάποιες πληροφορίες. Ανάλογα με τα δεδομένα που εισάγετε, το bot θα εκτελέσει ορισμένες ενέργειες.

Επίσης σε αυτό το άρθρο θα μάθουμε πώς να χρησιμοποιούμε μια βάση δεδομένων κάτω από την κουκούλα του bot, στο παράδειγμά μας θα είναι SQLite, αλλά μπορείτε να χρησιμοποιήσετε οποιοδήποτε άλλο DBMS. Έγραψα με περισσότερες λεπτομέρειες σχετικά με την αλληλεπίδραση με βάσεις δεδομένων στη γλώσσα R στο Αυτό το άρθρο.

Γράφοντας ένα bot τηλεγραφήματος σε R (μέρος 4): Δημιουργία ενός συνεπούς, λογικού διαλόγου με το ρομπότ

Όλα τα άρθρα από τη σειρά "Γράφοντας ένα bot τηλεγραφήματος στο R"

  1. Δημιουργούμε ένα bot και το χρησιμοποιούμε για να στείλουμε μηνύματα στο τηλεγράφημα
  2. Προσθέστε υποστήριξη εντολών και φίλτρα μηνυμάτων στο bot
  3. Πώς να προσθέσετε υποστήριξη πληκτρολογίου σε ένα bot
  4. Χτίζοντας έναν συνεπή, λογικό διάλογο με το bot

περιεχόμενο

Εάν ενδιαφέρεστε για την ανάλυση δεδομένων, μπορεί να σας ενδιαφέρει το δικό μου τηλεγράφημα и YouTube καναλιών. Το μεγαλύτερο μέρος του περιεχομένου του οποίου είναι αφιερωμένο στη γλώσσα R.

  1. Εισαγωγή
  2. Διαδικασία κατασκευής bot
  3. Δομή έργου bot
  4. Διαμόρφωση ρομπότ
  5. Δημιουργήστε μια μεταβλητή περιβάλλοντος
  6. Δημιουργία βάσης δεδομένων
  7. Σύνταξη συναρτήσεων για εργασία με τη βάση δεδομένων
  8. Μέθοδοι bot
  9. Φίλτρα μηνυμάτων
  10. Χειριστές
  11. Κωδικός εκκίνησης bot
  12. Συμπέρασμα

Εισαγωγή

Προκειμένου το bot να ζητήσει δεδομένα από εσάς και να περιμένει να εισαγάγετε οποιαδήποτε πληροφορία, θα πρέπει να καταγράψετε την τρέχουσα κατάσταση του διαλόγου. Ο καλύτερος τρόπος για να το κάνετε αυτό είναι να χρησιμοποιήσετε κάποιο είδος ενσωματωμένης βάσης δεδομένων, όπως το SQLite.

Εκείνοι. Η λογική θα είναι η εξής. Καλούμε τη μέθοδο bot και το bot ζητά διαδοχικά κάποιες πληροφορίες από εμάς και σε κάθε βήμα περιμένει να εισαχθούν αυτές οι πληροφορίες και μπορεί να τις ελέγξει.

Θα γράψουμε το απλούστερο δυνατό bot, πρώτα θα ζητήσει το όνομά σας, μετά την ηλικία σας και θα αποθηκεύσει τα ληφθέντα δεδομένα στη βάση δεδομένων. Όταν ζητά την ηλικία, θα ελέγξει ότι τα δεδομένα που εισάγατε είναι αριθμός και όχι κείμενο.

Ένας τόσο απλός διάλογος θα έχει μόνο τρεις καταστάσεις:

  1. start είναι η κανονική κατάσταση του bot, στην οποία δεν περιμένει καμία πληροφορία από εσάς
  2. wait_name - κατάσταση στην οποία το bot περιμένει να εισαχθεί ένα όνομα
  3. Wait_age είναι η κατάσταση στην οποία το bot περιμένει να εισαχθεί η ηλικία σας, ο αριθμός των πλήρων ετών.

Διαδικασία κατασκευής bot

Κατά τη διάρκεια του άρθρου, θα δημιουργήσουμε ένα bot βήμα προς βήμα· ολόκληρη η διαδικασία μπορεί να απεικονιστεί σχηματικά ως εξής:
Γράφοντας ένα bot τηλεγραφήματος σε R (μέρος 4): Δημιουργία ενός συνεπούς, λογικού διαλόγου με το ρομπότ

  1. Δημιουργούμε ένα bot config στο οποίο θα αποθηκεύσουμε κάποιες ρυθμίσεις. Στην περίπτωσή μας, το διακριτικό bot και η διαδρομή προς το αρχείο της βάσης δεδομένων.
  2. Δημιουργούμε μια μεταβλητή περιβάλλοντος στην οποία θα αποθηκευτεί η διαδρομή προς το έργο με το bot.
  3. Δημιουργούμε την ίδια τη βάση δεδομένων και μια σειρά από λειτουργίες ώστε το bot να μπορεί να αλληλεπιδρά μαζί της.
  4. Γράφουμε μεθόδους bot, δηλ. τις λειτουργίες που θα εκτελέσει.
  5. Προσθήκη φίλτρων μηνυμάτων. Με τη βοήθεια του οποίου το bot θα έχει πρόσβαση στις απαραίτητες μεθόδους, ανάλογα με την τρέχουσα κατάσταση της συνομιλίας.
  6. Προσθέτουμε χειριστές που θα συνδέουν εντολές και μηνύματα με τις απαραίτητες μεθόδους bot.
  7. Ας εκκινήσουμε το bot.

Δομή έργου bot

Για ευκολία, θα χωρίσουμε τον κώδικα του bot μας, και άλλα σχετικά αρχεία, στην ακόλουθη δομή.

  • bot.R — ο κύριος κωδικός του bot μας
  • db_bot_function.R — ένα μπλοκ κώδικα με συναρτήσεις για εργασία με βάση δεδομένων
  • bot_methods.R — κώδικας μεθόδων bot
  • message_filters.R — φίλτρα μηνυμάτων
  • χειριστές.R - χειριστές
  • config.cfg - διαμόρφωση bot
  • create_db_data.sql — Σενάριο SQL για τη δημιουργία πίνακα με δεδομένα συνομιλίας στη βάση δεδομένων
  • create_db_state.sql — Σενάριο SQL για τη δημιουργία πίνακα της τρέχουσας κατάστασης συνομιλίας στη βάση δεδομένων
  • bot.db - βάση δεδομένων bot

Μπορείτε να δείτε ολόκληρο το έργο bot ή κατεβάσετε από το δικό μου αποθετήριο στο GitHub.

Διαμόρφωση ρομπότ

Θα χρησιμοποιήσουμε το συνηθισμένο ως config αρχείο ini, την ακόλουθη μορφή:

[bot_settings]
bot_token=ТОКЕН_ВАШЕГО_БОТА

[db_settings]
db_path=C:/ПУТЬ/К/ПАПКЕ/ПРОЕКТА/bot.db

Στο config γράφουμε το bot token και τη διαδρομή προς τη βάση δεδομένων, δηλ. στο αρχείο bot.db, θα δημιουργήσουμε το ίδιο το αρχείο στο επόμενο βήμα.

Για πιο σύνθετα bot, μπορείτε να δημιουργήσετε πιο σύνθετες ρυθμίσεις παραμέτρων, εκτός αυτού, δεν είναι απαραίτητο να γράψετε μια ρύθμιση ini, μπορείτε να χρησιμοποιήσετε οποιαδήποτε άλλη μορφή συμπεριλαμβανομένου του JSON.

Δημιουργήστε μια μεταβλητή περιβάλλοντος

Σε κάθε υπολογιστή, ο φάκελος με το έργο bot μπορεί να βρίσκεται σε διαφορετικούς καταλόγους και σε διαφορετικούς δίσκους, επομένως στον κώδικα η διαδρομή προς το φάκελο του έργου θα οριστεί μέσω μιας μεταβλητής περιβάλλοντος TG_BOT_PATH.

Υπάρχουν διάφοροι τρόποι για να δημιουργήσετε μια μεταβλητή περιβάλλοντος, ο πιο απλός είναι να τη γράψετε σε ένα αρχείο .Ρενβίρον.

Μπορείτε να δημιουργήσετε ή να επεξεργαστείτε αυτό το αρχείο χρησιμοποιώντας την εντολή file.edit(path.expand(file.path("~", ".Renviron"))). Εκτελέστε το και προσθέστε μία γραμμή στο αρχείο:

TG_BOT_PATH=C:/ПУТЬ/К/ВАШЕМУ/ПРОЕКТУ

Στη συνέχεια αποθηκεύστε το αρχείο .Ρενβίρον και επανεκκινήστε το RStudio.

Δημιουργία βάσης δεδομένων

Το επόμενο βήμα είναι να δημιουργήσετε μια βάση δεδομένων. Θα χρειαστούμε 2 πίνακες:

  • chat_data — δεδομένα που ζήτησε το bot από τον χρήστη
  • 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
);

Εάν κατεβάσατε το έργο bot από 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 έχει περιορισμό μοναδικότητας και είναι το κύριο κλειδί των πινάκων. Αρχικά, προσπαθούμε να προσθέσουμε πληροφορίες στον πίνακα και λαμβάνουμε ένα σφάλμα εάν υπάρχουν ήδη δεδομένα για την τρέχουσα συνομιλία, οπότε απλώς ενημερώνουμε τις πληροφορίες για αυτήν τη συνομιλία.

Στη συνέχεια, θα χρησιμοποιήσουμε αυτές τις λειτουργίες στις μεθόδους και τα φίλτρα του bot.

Μέθοδοι bot

Το επόμενο βήμα στη δημιουργία του bot μας είναι να δημιουργήσουμε μεθόδους. Εάν κατεβάσατε το έργο από GitHub, τότε όλες οι μέθοδοι βρίσκονται στο αρχείο bot_methods.R.

Κωδικός μεθόδου 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')
  }

}

Δημιουργήσαμε 5 μεθόδους:

  • έναρξη — Έναρξη διαλόγου
  • κατάσταση — Λάβετε την τρέχουσα κατάσταση συνομιλίας
  • επαναφορά — Επαναφέρετε την τρέχουσα κατάσταση συνομιλίας
  • enter_name — Το ρομπότ ζητά το όνομά σας
  • enter_age — Το ρομπότ ζητά την ηλικία σας

Μέθοδος start ζητά το όνομά σας και αλλάζει την κατάσταση συνομιλίας σε αναμονή_όνομα, δηλ. σε κατάσταση αναμονής για την εισαγωγή του ονόματός σας.

Στη συνέχεια, στέλνετε το όνομα και επεξεργάζεται με τη μέθοδο enter_name, το bot σας χαιρετάει, γράφει το όνομα που λάβατε στη βάση δεδομένων και αλλάζει τη συνομιλία στην κατάσταση αναμονή_ηλικίας.

Σε αυτό το στάδιο, το bot περιμένει από εσάς να εισάγετε την ηλικία σας. Στέλνεις την ηλικία σου, το ρομπότ ελέγχει το μήνυμα, αν στείλατε κάποιο κείμενο αντί για αριθμό, θα λέει: Ты ввёл некорректные данные, введи число, και θα περιμένει να εισαγάγετε ξανά τα δεδομένα σας. Εάν στείλατε έναν αριθμό, το bot θα αναφέρει ότι έχει αποδεχτεί την ηλικία σας, θα γράψει τα δεδομένα που έλαβε στη βάση δεδομένων, θα αναφέρει όλα τα δεδομένα που έλαβε από εσάς και θα επιστρέψει την κατάσταση συνομιλίας στην αρχική της θέση, π.χ. V start.

Καλώντας τη μέθοδο state μπορείτε να ζητήσετε την τρέχουσα κατάσταση συνομιλίας ανά πάσα στιγμή και χρησιμοποιώντας το reset επαναφέρετε τη συνομιλία στην αρχική της κατάσταση.

Φίλτρα μηνυμάτων

Στην περίπτωσή μας, αυτό είναι ένα από τα πιο σημαντικά μέρη στην κατασκευή ενός bot. Είναι με τη βοήθεια φίλτρων μηνυμάτων που το bot θα καταλάβει ποιες πληροφορίες περιμένει από εσάς και πώς πρέπει να υποβληθούν σε επεξεργασία.

Στο έργο για 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 όρισμα, αναγνωριστικό συνομιλίας.

Επόμενο φίλτρο αναμονή_όνομα επεξεργάζεται μηνύματα όταν η συνομιλία βρίσκεται σε κατάσταση wait_name, και κατά συνέπεια το φίλτρο αναμονή_ηλικίας επεξεργάζεται μηνύματα όταν η συνομιλία βρίσκεται σε κατάσταση wait_age.

Χειριστές

Το αρχείο με χειριστές καλείται χειριστές.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

Τώρα έχουμε τα πάντα έτοιμα για εκκίνηση, ο κύριος κώδικας για την εκκίνηση του bot βρίσκεται στο αρχείο 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()

Ως αποτέλεσμα, πήραμε αυτό το bot:
Γράφοντας ένα bot τηλεγραφήματος σε R (μέρος 4): Δημιουργία ενός συνεπούς, λογικού διαλόγου με το ρομπότ

Ανά πάσα στιγμή χρησιμοποιώντας την εντολή /state μπορούμε να ρωτήσουμε την τρέχουσα κατάσταση συνομιλίας και χρησιμοποιώντας την εντολή /reset επαναφέρετε τη συνομιλία στην αρχική της κατάσταση και ξεκινήστε ξανά το διάλογο.

Συμπέρασμα

Σε αυτό το άρθρο, καταλάβαμε πώς να χρησιμοποιήσετε μια βάση δεδομένων μέσα σε ένα bot και πώς να δημιουργήσετε διαδοχικούς λογικούς διαλόγους καταγράφοντας την κατάσταση συνομιλίας.

Σε αυτήν την περίπτωση, εξετάσαμε το πιο πρωτόγονο παράδειγμα, ώστε να είναι πιο εύκολο για εσάς να κατανοήσετε την ιδέα της δημιουργίας τέτοιων bots· στην πράξη, μπορείτε να δημιουργήσετε πολύ πιο σύνθετους διαλόγους.

Στο επόμενο άρθρο αυτής της σειράς, θα μάθουμε πώς να περιορίζουμε τα δικαιώματα των χρηστών bot να χρησιμοποιούν διάφορες από τις μεθόδους του.

Πηγή: www.habr.com

Προσθέστε ένα σχόλιο