Menulis bot telegram di R (bagian 4): Membangun dialog yang konsisten dan logis dengan bot

Jika Anda sudah membaca sebelumnya tiga artikel dari seri ini maka anda sudah mengetahui cara menulis bot telegram lengkap dengan keyboard.

Pada artikel ini, kita akan mempelajari cara menulis bot yang akan menjaga dialog tetap konsisten. Itu. Bot akan mengajukan pertanyaan kepada Anda dan menunggu Anda memasukkan beberapa informasi. Tergantung pada data yang Anda masukkan, bot akan melakukan beberapa tindakan.

Juga di artikel ini kita akan mempelajari cara menggunakan database di bawah tenda bot, dalam contoh kita ini adalah SQLite, tetapi Anda dapat menggunakan DBMS lainnya. Saya menulis lebih detail tentang interaksi dengan database dalam bahasa R di Artikel ini.

Menulis bot telegram di R (bagian 4): Membangun dialog yang konsisten dan logis dengan bot

Semua artikel dari seri “Menulis bot telegram di R”

  1. Kami membuat bot dan menggunakannya untuk mengirim pesan di telegram
  2. Tambahkan dukungan perintah dan filter pesan ke bot
  3. Bagaimana cara menambahkan dukungan keyboard ke bot
  4. Membangun dialog yang konsisten dan logis dengan bot

kadar

Jika Anda tertarik dengan analisis data, Anda mungkin tertarik dengan saya Telegram и Youtube saluran. Sebagian besar konten didedikasikan untuk bahasa R.

  1. pengenalan
  2. Proses pembuatan bot
  3. Struktur proyek bot
  4. Konfigurasi bot
  5. Buat variabel lingkungan
  6. Membuat basis data
  7. Menulis berfungsi untuk bekerja dengan database
  8. Metode bot
  9. Filter pesan
  10. Penangan
  11. Kode peluncuran bot
  12. Kesimpulan

pengenalan

Agar bot meminta data dari Anda dan menunggu Anda memasukkan informasi apa pun, Anda perlu mencatat status dialog saat ini. Cara terbaik untuk melakukannya adalah dengan menggunakan beberapa jenis database tertanam, seperti SQLite.

Itu. Logikanya adalah sebagai berikut. Kami memanggil metode bot, dan bot secara berurutan meminta beberapa informasi dari kami, dan pada setiap langkah ia menunggu informasi ini dimasukkan dan dapat memeriksanya.

Kami akan menulis bot yang paling sederhana, pertama ia akan menanyakan nama Anda, lalu usia Anda, dan akan menyimpan data yang diterima ke database. Saat menanyakan usia, ia akan memeriksa bahwa data yang dimasukkan adalah angka dan bukan teks.

Dialog sederhana seperti itu hanya akan memiliki tiga keadaan:

  1. start adalah keadaan normal bot, di mana ia tidak mengharapkan informasi apa pun dari Anda
  2. wait_name - keadaan di mana bot menunggu nama dimasukkan
  3. wait_age adalah keadaan di mana bot menunggu usia Anda dimasukkan, jumlah tahun penuh.

Proses pembuatan bot

Dalam artikel ini, kami akan membuat bot selangkah demi selangkah; keseluruhan prosesnya dapat digambarkan secara skematis sebagai berikut:
Menulis bot telegram di R (bagian 4): Membangun dialog yang konsisten dan logis dengan bot

  1. Kami membuat konfigurasi bot di mana kami akan menyimpan beberapa pengaturan. Dalam kasus kami, token bot, dan jalur ke file database.
  2. Kami membuat variabel lingkungan di mana jalur ke proyek dengan bot akan disimpan.
  3. Kami membuat database itu sendiri, dan sejumlah fungsi agar bot dapat berinteraksi dengannya.
  4. Kami menulis metode bot, mis. fungsi-fungsi yang akan dijalankannya.
  5. Menambahkan filter pesan. Dengan bantuannya bot akan mengakses metode yang diperlukan, tergantung pada keadaan obrolan saat ini.
  6. Kami menambahkan penangan yang akan menghubungkan perintah dan pesan dengan metode bot yang diperlukan.
  7. Ayo luncurkan botnya.

Struktur proyek bot

Untuk kenyamanan, kami akan membagi kode bot kami, dan file terkait lainnya, ke dalam struktur berikut.

  • bot.R — kode utama bot kami
  • db_bot_fungsi.R — blok kode dengan fungsi untuk bekerja dengan database
  • bot_methods.R — kode metode bot
  • pesan_filter.R — filter pesan
  • penangan.R - penangan
  • config.cfg - konfigurasi bot
  • buat_db_data.sql — Skrip SQL untuk membuat tabel dengan data obrolan di database
  • buat_db_state.sql — Skrip SQL untuk membuat tabel status obrolan saat ini di database
  • bot.db - basis data bot

Anda dapat melihat keseluruhan proyek bot, atau mendownload dari saya repositori di GitHub.

Konfigurasi bot

Kami akan menggunakan yang biasa sebagai konfigurasi berkas ini, bentuk berikut:

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

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

Dalam konfigurasi kami menulis token bot dan jalur ke database, mis. ke file bot.db; kita akan membuat file itu sendiri di langkah berikutnya.

Untuk bot yang lebih kompleks, Anda dapat membuat konfigurasi yang lebih kompleks, selain itu tidak perlu menulis konfigurasi ini, Anda dapat menggunakan format lain termasuk JSON.

Buat variabel lingkungan

Pada setiap PC, folder dengan proyek bot dapat ditempatkan di direktori yang berbeda dan pada drive yang berbeda, sehingga dalam kode jalur ke folder proyek akan diatur melalui variabel lingkungan TG_BOT_PATH.

Ada beberapa cara untuk membuat variabel lingkungan, yang paling sederhana adalah dengan menuliskannya ke dalam file .Renviron.

Anda dapat membuat atau mengedit file ini menggunakan perintah file.edit(path.expand(file.path("~", ".Renviron"))). Jalankan dan tambahkan satu baris ke file:

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

Selanjutnya simpan file tersebut .Renviron dan mulai ulang RStudio.

Membuat basis data

Langkah selanjutnya adalah membuat database. Kami membutuhkan 2 tabel:

  • chat_data — data yang diminta bot dari pengguna
  • chat_state — status semua obrolan saat ini

Anda dapat membuat tabel ini menggunakan kueri 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 mengunduh proyek bot dari GitHub, lalu untuk membuat databasenya bisa menggunakan kode berikut di 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'))

Menulis berfungsi untuk bekerja dengan database

Kami sudah menyiapkan file konfigurasi dan database telah dibuat. Sekarang Anda perlu menulis fungsi untuk membaca dan menulis data ke database ini.

Jika Anda mengunduh proyek dari GitHub, lalu Anda dapat menemukan fungsinya di file tersebut db_bot_fungsi.R.

Kode fungsi untuk bekerja dengan database

# ###########################################################
# 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 membuat 4 fungsi sederhana:

  • get_state() — dapatkan status obrolan saat ini dari database
  • set_state() — tulis status obrolan saat ini ke database
  • get_chat_data() — menerima data yang dikirim oleh pengguna
  • set_chat_data() — merekam data yang diterima dari pengguna

Semua fungsinya cukup sederhana, yaitu membaca data dari database menggunakan perintah dbGetQuery(), atau melakukan UPSERT operasi (mengubah data yang ada atau menulis data baru ke database), menggunakan fungsi dbExecute().

Sintaks untuk operasi UPSERT adalah sebagai berikut:

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

Itu. di bidang tabel kami obrolan_id memiliki batasan keunikan dan merupakan kunci utama tabel. Awalnya, kami mencoba menambahkan informasi ke tabel, dan kami mendapatkan kesalahan jika data untuk obrolan saat ini sudah ada, dalam hal ini kami cukup memperbarui informasi untuk obrolan ini.

Selanjutnya, kita akan menggunakan fungsi-fungsi ini dalam metode dan filter bot.

Metode bot

Langkah selanjutnya dalam membangun bot kita adalah membuat metode. Jika Anda mengunduh proyek dari GitHub, maka semua metode ada di file bot_methods.R.

Kode metode 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 membuat 5 metode:

  • mulai — Memulai dialog
  • state — Dapatkan status obrolan saat ini
  • reset — Mengatur ulang status obrolan saat ini
  • enter_name — Bot menanyakan nama Anda
  • enter_age — Bot menanyakan usia Anda

metode start menanyakan nama Anda, dan mengubah status obrolan menjadi tunggu_nama, yaitu. untuk bersiap memasukkan nama Anda.

Selanjutnya Anda mengirimkan nama dan diproses oleh metode enter_name, bot menyambut Anda, menulis nama yang diterima ke dalam database, dan mengalihkan obrolan ke negara bagian tunggu_usia.

Pada tahap ini, bot mengharapkan Anda memasukkan usia Anda. Anda mengirimkan usia Anda, bot memeriksa pesan tersebut, jika Anda mengirim beberapa teks alih-alih nomor, ia akan mengatakan: Ты ввёл некорректные данные, введи число, dan akan menunggu Anda memasukkan kembali data Anda. Jika Anda mengirimkan nomor, bot akan melaporkan bahwa ia telah menerima usia Anda, menulis data yang diterima ke database, melaporkan semua data yang diterima dari Anda dan mengembalikan status obrolan ke posisi semula, yaitu. V start.

Dengan memanggil metode tersebut state Anda dapat meminta status obrolan saat ini kapan saja, dan menggunakan reset mengembalikan obrolan ke keadaan semula.

Filter pesan

Dalam kasus kami, ini adalah salah satu bagian terpenting dalam membuat bot. Dengan bantuan filter pesan, bot akan memahami informasi apa yang diharapkan dari Anda dan bagaimana informasi tersebut harus diproses.

Dalam proyek di GitHub filter terdaftar dalam file pesan_filter.R.

Kode filter pesan:

# ###########################################################
# 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 filter kami menggunakan fungsi yang ditulis sebelumnya get_state(), untuk menanyakan status obrolan saat ini. Fungsi ini hanya membutuhkan 1 argumen, id obrolan.

Penyaringan berikutnya tunggu_nama memproses pesan saat obrolan dalam keadaan wait_name, dan karenanya filternya tunggu_usia memproses pesan saat obrolan dalam keadaan wait_age.

Penangan

File dengan penangan disebut penangan.R, dan memiliki kode 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)

Pertama kita membuat pengendali perintah yang memungkinkan Anda menjalankan metode untuk memulai dialog, mengatur ulang, dan menanyakan status saat ini.

Selanjutnya, kita membuat 2 penangan pesan menggunakan filter yang dibuat pada langkah sebelumnya, dan menambahkan filter ke dalamnya !MessageFilters$command, sehingga kita bisa menggunakan perintah dalam status obrolan apa pun.

Kode peluncuran bot

Sekarang semuanya siap untuk diluncurkan, kode utama untuk meluncurkan bot ada di file 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 mendapatkan bot ini:
Menulis bot telegram di R (bagian 4): Membangun dialog yang konsisten dan logis dengan bot

Kapan saja menggunakan perintah /state kita dapat menanyakan status obrolan saat ini, dan menggunakan perintah /reset kembalikan obrolan ke keadaan semula dan mulai dialog lagi.

Kesimpulan

Dalam artikel ini, kami menemukan cara menggunakan database di dalam bot, dan cara membuat dialog logis berurutan dengan merekam status obrolan.

Dalam hal ini, kami melihat contoh paling primitif, sehingga akan lebih mudah bagi Anda untuk memahami gagasan membangun bot semacam itu; dalam praktiknya, Anda dapat membangun dialog yang jauh lebih kompleks.

Pada artikel berikutnya dalam seri ini, kita akan mempelajari cara membatasi hak pengguna bot untuk menggunakan berbagai metodenya.

Sumber: www.habr.com

Tambah komentar