Menulis bot telegram dalam R (bahagian 4): Membina dialog logik yang konsisten dengan bot

Jika anda sudah membaca sebelumnya tiga artikel daripada siri ini, maka anda sudah tahu cara menulis bot telegram sepenuhnya dengan papan kekunci.

Dalam artikel ini, kita akan belajar cara menulis bot yang akan mengekalkan dialog yang konsisten. Itu. Bot akan bertanya kepada anda soalan dan menunggu anda memasukkan beberapa maklumat. Bergantung pada data yang anda masukkan, bot akan melakukan beberapa tindakan.

Juga dalam artikel ini kita akan belajar cara menggunakan pangkalan data di bawah hud bot, dalam contoh kami ia akan menjadi SQLite, tetapi anda boleh menggunakan mana-mana DBMS lain. Saya menulis dengan lebih terperinci tentang berinteraksi dengan pangkalan data dalam bahasa R dalam artikel ini.

Menulis bot telegram dalam R (bahagian 4): Membina dialog logik yang konsisten dengan bot

Semua artikel dari siri "Menulis bot telegram dalam R"

  1. Kami mencipta bot dan menggunakannya untuk menghantar mesej dalam telegram
  2. Tambahkan sokongan arahan dan penapis mesej pada bot
  3. Cara menambah sokongan papan kekunci pada bot
  4. Membina dialog yang konsisten dan logik dengan bot

Содержание

Jika anda berminat dengan analisis data, anda mungkin berminat dengan saya telegram и youtube saluran. Kebanyakan kandungan didedikasikan untuk bahasa R.

  1. Pengenalan
  2. Proses pembinaan bot
  3. Struktur projek bot
  4. Konfigurasi bot
  5. Buat pembolehubah persekitaran
  6. Mencipta pangkalan data
  7. Fungsi penulisan untuk bekerja dengan pangkalan data
  8. Kaedah bot
  9. Penapis mesej
  10. Pengendali
  11. Kod pelancaran bot
  12. Kesimpulan

Pengenalan

Untuk membolehkan bot meminta data daripada anda dan menunggu anda memasukkan sebarang maklumat, anda perlu merekodkan keadaan semasa dialog. Cara terbaik untuk melakukan ini ialah menggunakan beberapa jenis pangkalan data terbenam, seperti SQLite.

Itu. Logiknya adalah seperti berikut. Kami memanggil kaedah bot, dan bot secara berurutan meminta beberapa maklumat daripada kami, dan pada setiap langkah ia menunggu untuk maklumat ini dimasukkan dan boleh menyemaknya.

Kami akan menulis bot yang paling mudah, mula-mula ia akan meminta nama anda, kemudian umur anda, dan akan menyimpan data yang diterima ke pangkalan data. Apabila meminta umur, ia akan menyemak bahawa data yang dimasukkan adalah nombor dan bukan teks.

Dialog mudah sedemikian hanya akan mempunyai tiga keadaan:

  1. mula ialah keadaan biasa bot, di mana ia tidak mengharapkan sebarang maklumat daripada anda
  2. wait_name - keadaan di mana bot menunggu untuk nama dimasukkan
  3. wait_age ialah keadaan di mana bot menunggu umur anda untuk dimasukkan, bilangan tahun penuh.

Proses pembinaan bot

Semasa artikel, kami akan membina bot langkah demi langkah; keseluruhan proses boleh digambarkan secara skematik seperti berikut:
Menulis bot telegram dalam R (bahagian 4): Membina dialog logik yang konsisten dengan bot

  1. Kami mencipta konfigurasi bot di mana kami akan menyimpan beberapa tetapan. Dalam kes kami, token bot dan laluan ke fail pangkalan data.
  2. Kami mencipta pembolehubah persekitaran di mana laluan ke projek dengan bot akan disimpan.
  3. Kami mencipta pangkalan data itu sendiri, dan beberapa fungsi supaya bot boleh berinteraksi dengannya.
  4. Kami menulis kaedah bot, i.e. fungsi yang akan dilakukannya.
  5. Menambah penapis mesej. Dengan bantuan bot itu akan mengakses kaedah yang diperlukan, bergantung pada keadaan semasa sembang.
  6. Kami menambah pengendali yang akan menyambungkan arahan dan mesej dengan kaedah bot yang diperlukan.
  7. Mari lancarkan bot.

Struktur projek bot

Untuk kemudahan, kami akan membahagikan kod bot kami, dan fail lain yang berkaitan, ke dalam struktur berikut.

  • bot.R — kod utama bot kami
  • db_bot_function.R — blok kod dengan fungsi untuk bekerja dengan pangkalan data
  • bot_methods.R — kod kaedah bot
  • message_filters.R — penapis mesej
  • pengendali.R - pengendali
  • config.cfg - konfigurasi bot
  • create_db_data.sql — Skrip SQL untuk mencipta jadual dengan data sembang dalam pangkalan data
  • create_db_state.sql — Skrip SQL untuk mencipta jadual keadaan sembang semasa dalam pangkalan data
  • bot.db - pangkalan data bot

Anda boleh melihat keseluruhan projek bot, atau memuat turun daripada saya repositori di GitHub.

Konfigurasi bot

Kami akan menggunakan yang biasa sebagai konfigurasi fail ini, borang berikut:

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

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

Dalam konfigurasi kami menulis token bot dan laluan ke pangkalan data, i.e. ke fail bot.db; kami akan mencipta fail itu sendiri dalam langkah seterusnya.

Untuk bot yang lebih kompleks, anda boleh membuat konfigurasi yang lebih kompleks, selain itu, tidak perlu menulis konfigurasi ini, anda boleh menggunakan mana-mana format lain termasuk JSON.

Buat pembolehubah persekitaran

Pada setiap PC, folder dengan projek bot boleh terletak dalam direktori berbeza dan pada pemacu yang berbeza, jadi dalam kod laluan ke folder projek akan ditetapkan melalui pembolehubah persekitaran TG_BOT_PATH.

Terdapat beberapa cara untuk mencipta pembolehubah persekitaran, yang paling mudah ialah menulisnya dalam fail .Persekitaran.

Anda boleh membuat atau mengedit fail ini menggunakan arahan file.edit(path.expand(file.path("~", ".Renviron"))). Laksanakannya dan tambah satu baris pada fail:

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

Seterusnya simpan fail tersebut .Persekitaran dan mulakan semula RStudio.

Mencipta pangkalan data

Langkah seterusnya ialah membuat pangkalan data. Kami memerlukan 2 jadual:

  • chat_data — data yang bot minta daripada pengguna
  • chat_state — keadaan semasa semua sembang

Anda boleh membuat jadual ini menggunakan pertanyaan SQL berikut:

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

Jika anda memuat turun projek bot dari GitHub, kemudian untuk mencipta pangkalan data anda boleh menggunakan kod berikut dalam 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'))

Fungsi penulisan untuk bekerja dengan pangkalan data

Kami telah menyediakan fail konfigurasi dan pangkalan data telah dibuat. Kini anda perlu menulis fungsi untuk membaca dan menulis data ke pangkalan data ini.

Jika anda memuat turun projek dari GitHub, maka anda boleh mencari fungsi dalam fail db_bot_function.R.

Kod fungsi untuk bekerja dengan pangkalan data

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

}

Kami mencipta 4 fungsi mudah:

  • get_state() — dapatkan keadaan sembang semasa daripada pangkalan data
  • set_state() — tulis keadaan sembang semasa ke pangkalan data
  • get_chat_data() — menerima data yang dihantar oleh pengguna
  • set_chat_data() — rekod data yang diterima daripada pengguna

Semua fungsi adalah agak mudah, mereka sama ada membaca data dari pangkalan data menggunakan arahan dbGetQuery(), atau komited UPSERT operasi (menukar data sedia ada atau menulis data baru ke pangkalan data), menggunakan fungsi dbExecute().

Sintaks untuk operasi UPSERT adalah seperti berikut:

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

Itu. dalam medan jadual kami chat_id mempunyai kekangan keunikan dan merupakan kunci utama jadual. Pada mulanya, kami cuba menambah maklumat pada jadual, dan kami mendapat ralat jika data untuk sembang semasa sudah ada, dalam hal ini kami hanya mengemas kini maklumat untuk sembang ini.

Seterusnya, kami akan menggunakan fungsi ini dalam kaedah dan penapis bot.

Kaedah bot

Langkah seterusnya dalam membina bot kami ialah mencipta kaedah. Jika anda memuat turun projek dari GitHub, maka semua kaedah berada dalam fail bot_methods.R.

Kod kaedah 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')
  }

}

Kami mencipta 5 kaedah:

  • mula — Mulakan dialog
  • keadaan — Dapatkan keadaan sembang semasa
  • set semula — Tetapkan semula keadaan sembang semasa
  • enter_name — Bot meminta nama anda
  • enter_age — Bot menanyakan umur anda

Kaedah start meminta nama anda dan menukar keadaan sembang kepada tunggu_nama, iaitu untuk bersiap sedia untuk memasukkan nama anda.

Seterusnya, anda menghantar nama dan ia diproses mengikut kaedah enter_name, bot menyapa anda, menulis nama yang diterima ke dalam pangkalan data dan menukar sembang kepada keadaan umur_tunggu.

Pada peringkat ini, bot mengharapkan anda memasuki umur anda. Anda menghantar umur anda, bot menyemak mesej, jika anda menghantar beberapa teks dan bukannya nombor, ia akan berkata: Ты ввёл некорректные данные, введи число, dan akan menunggu anda memasukkan semula data anda. Jika anda menghantar nombor, bot akan melaporkan bahawa ia telah menerima umur anda, menulis data yang diterima ke pangkalan data, melaporkan semua data yang diterima daripada anda dan mengembalikan keadaan sembang ke kedudukan asalnya, i.e. V start.

Dengan memanggil kaedah state anda boleh meminta status sembang semasa pada bila-bila masa, dan menggunakan reset kembalikan sembang kepada keadaan asalnya.

Penapis mesej

Dalam kes kami, ini adalah salah satu bahagian terpenting dalam membina bot. Dengan bantuan penapis mesej, bot akan memahami maklumat yang diharapkan daripada anda dan cara ia harus diproses.

Dalam projek pada GitHub penapis didaftarkan dalam fail message_filters.R.

Kod penapis mesej:

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

Dalam penapis kami menggunakan fungsi yang ditulis sebelum ini get_state(), untuk meminta keadaan semasa sembang. Fungsi ini hanya memerlukan 1 hujah, id sembang.

Penapis seterusnya tunggu_nama memproses mesej apabila sembang dalam keadaan wait_name, dan sewajarnya penapis umur_tunggu memproses mesej apabila sembang dalam keadaan wait_age.

Pengendali

Fail dengan pengendali dipanggil pengendali.R, dan mempunyai kod berikut:

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

Mula-mula kami mencipta pengendali arahan yang akan membolehkan anda menjalankan kaedah untuk memulakan dialog, menetapkan semula dan menanyakan keadaan semasa.

Seterusnya, kami mencipta 2 pengendali mesej menggunakan penapis yang dibuat pada langkah sebelumnya dan menambah penapis pada mereka !MessageFilters$command, supaya kita boleh menggunakan arahan dalam mana-mana keadaan sembang.

Kod pelancaran bot

Kini kami mempunyai segala-galanya sedia untuk dilancarkan, kod utama untuk melancarkan bot adalah dalam fail 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()

Hasilnya, kami mendapat bot ini:
Menulis bot telegram dalam R (bahagian 4): Membina dialog logik yang konsisten dengan bot

Pada bila-bila masa menggunakan arahan /state kita boleh menanyakan keadaan sembang semasa, dan menggunakan arahan /reset kembalikan sembang kepada keadaan asalnya dan mulakan dialog semula.

Kesimpulan

Dalam artikel ini, kami mengetahui cara menggunakan pangkalan data di dalam bot, dan cara membina dialog logik berjujukan dengan merekodkan keadaan sembang.

Dalam kes ini, kami melihat contoh yang paling primitif, supaya lebih mudah bagi anda untuk memahami idea membina bot tersebut; dalam praktiknya, anda boleh membina dialog yang lebih kompleks.

Dalam artikel seterusnya dalam siri ini, kita akan belajar cara menyekat hak pengguna bot untuk menggunakan pelbagai kaedahnya.

Sumber: www.habr.com

Tambah komen