ProHoster > Blog > Pentadbiran > Menulis bot telegram dalam R (bahagian 4): Membina dialog logik yang konsisten dengan bot
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.
Semua artikel dari siri "Menulis bot telegram dalam R"
Jika anda berminat dengan analisis data, anda mungkin berminat dengan saya telegram и youtube saluran. Kebanyakan kandungan didedikasikan untuk bahasa R.
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:
mula ialah keadaan biasa bot, di mana ia tidak mengharapkan sebarang maklumat daripada anda
wait_name - keadaan di mana bot menunggu untuk nama dimasukkan
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:
Kami mencipta konfigurasi bot di mana kami akan menyimpan beberapa tetapan. Dalam kes kami, token bot dan laluan ke fail pangkalan data.
Kami mencipta pembolehubah persekitaran di mana laluan ke projek dengan bot akan disimpan.
Kami mencipta pangkalan data itu sendiri, dan beberapa fungsi supaya bot boleh berinteraksi dengannya.
Kami menulis kaedah bot, i.e. fungsi yang akan dilakukannya.
Menambah penapis mesej. Dengan bantuan bot itu akan mengakses kaedah yang diperlukan, bergantung pada keadaan semasa sembang.
Kami menambah pengendali yang akan menyambungkan arahan dan mesej dengan kaedah bot yang diperlukan.
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
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:
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.