R'de bir telgraf botu yazmak (bölüm 4): Botla tutarlı, mantıksal bir diyalog oluşturmak

Daha önce okuduysanız üç makale bu seriden, tam teşekküllü telgraf botlarının klavyeyle nasıl yazılacağını zaten biliyorsunuz.

Bu yazıda tutarlı bir diyaloğu sürdürecek bir botun nasıl yazılacağını öğreneceğiz. Onlar. Bot size sorular soracak ve bazı bilgileri girmenizi bekleyecektir. Girdiğiniz verilere bağlı olarak bot bazı eylemler gerçekleştirecektir.

Ayrıca bu makalede bot başlığı altında bir veritabanının nasıl kullanılacağını öğreneceğiz, örneğimizde bu SQLite olacaktır, ancak başka herhangi bir DBMS'yi kullanabilirsiniz. R dilindeki veritabanlarıyla etkileşim hakkında daha ayrıntılı olarak yazdım. Bu makalede.

R'de bir telgraf botu yazmak (bölüm 4): Botla tutarlı, mantıksal bir diyalog oluşturmak

“R'de telgraf botu yazma” serisindeki tüm makaleler

  1. Bir bot oluşturuyoruz ve onu telgrafta mesaj göndermek için kullanıyoruz
  2. Bot'a komut desteği ve mesaj filtreleri ekleyin
  3. Bir bota klavye desteği nasıl eklenir?
  4. Botla tutarlı, mantıklı bir diyalog kurmak

Içerik

Veri analiziyle ilgileniyorsanız, benim ilginizi çekebilir telgraf и Youtube kanallar. İçeriğin çoğu R diline ayrılmıştır.

  1. Giriş
  2. Bot oluşturma süreci
  3. Bot proje yapısı
  4. Bot yapılandırması
  5. Ortam değişkeni oluşturma
  6. Veritabanı oluşturma
  7. Veritabanıyla çalışacak fonksiyonların yazılması
  8. Bot yöntemleri
  9. Mesaj filtreleri
  10. İşleyiciler
  11. Bot başlatma kodu
  12. Sonuç

Giriş

Botun sizden veri isteyebilmesi ve herhangi bir bilgi girmenizi bekleyebilmesi için diyaloğun mevcut durumunu kaydetmeniz gerekecektir. Bunu yapmanın en iyi yolu SQLite gibi bir tür gömülü veritabanı kullanmaktır.

Onlar. Mantık şu şekilde olacaktır. Bot metodunu çağırıyoruz ve bot sırayla bizden bazı bilgiler istiyor ve her adımda bu bilgilerin girilmesini bekliyor ve kontrol edebiliyor.

Mümkün olan en basit botu yazacağız, önce adınızı, sonra yaşınızı soracak ve alınan verileri veritabanına kaydedecektir. Yaş sorulduğunda girilen verinin metin değil sayı olup olmadığı kontrol edilecektir.

Böyle basit bir diyalogun yalnızca üç durumu olacaktır:

  1. start, botun sizden herhangi bir bilgi beklemediği normal durumudur
  2. wait_name - botun bir adın girilmesini beklediği durum
  3. wait_age, botun yaşınızın, yani tam yıl sayısının girilmesini beklediği durumdur.

Bot oluşturma süreci

Yazı boyunca adım adım bir bot oluşturacağız; tüm süreç şematik olarak şu şekilde tasvir edilebilir:
R'de bir telgraf botu yazmak (bölüm 4): Botla tutarlı, mantıksal bir diyalog oluşturmak

  1. Bazı ayarları saklayacağımız bir bot yapılandırması oluşturuyoruz. Bizim durumumuzda bot belirteci ve veritabanı dosyasının yolu.
  2. Bot ile projeye giden yolun saklanacağı bir ortam değişkeni oluşturuyoruz.
  3. Botun onunla etkileşime girebilmesi için veritabanının kendisini ve bir dizi işlevi oluşturuyoruz.
  4. Bot yöntemlerini yazıyoruz, yani. gerçekleştireceği işlevler.
  5. Mesaj filtreleri ekleme. Sohbetin mevcut durumuna bağlı olarak botun gerekli yöntemlere erişmesinin yardımıyla.
  6. Komutları ve mesajları gerekli bot yöntemleriyle bağlayacak işleyiciler ekliyoruz.
  7. Botu başlatalım.

Bot proje yapısı

Kolaylık sağlamak için botumuzun kodunu ve diğer ilgili dosyaları aşağıdaki yapıya böleceğiz.

  • bot.R — botumuzun ana kodu
  • db_bot_function.R — bir veritabanıyla çalışmaya yönelik işlevlere sahip bir kod bloğu
  • bot_methods.R — bot yöntemlerinin kodu
  • message_filters.R — mesaj filtreleri
  • işleyiciler.R - işleyiciler
  • yapılandırma.cfg - bot yapılandırması
  • create_db_data.sql — Veritabanında sohbet verilerini içeren bir tablo oluşturmak için SQL betiği
  • create_db_state.sql — Veritabanındaki mevcut sohbet durumunun bir tablosunu oluşturmak için SQL betiği
  • bot.db - bot veritabanı

Bot projesinin tamamını görüntüleyebilir veya indirmek benden GitHub'daki depo.

Bot yapılandırması

Yapılandırma olarak olağan olanı kullanacağız ini dosyası, aşağıdaki form:

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

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

Yapılandırmada bot jetonunu ve veritabanının yolunu yazıyoruz, yani. bot.db dosyasına; bir sonraki adımda dosyayı kendisi oluşturacağız.

Daha karmaşık botlar için daha karmaşık yapılandırmalar oluşturabilirsiniz, ayrıca ini config yazmaya gerek yoktur, JSON dahil başka herhangi bir formatı kullanabilirsiniz.

Ortam değişkeni oluşturma

Her bilgisayarda, bot projesinin bulunduğu klasör farklı dizinlerde ve farklı sürücülerde bulunabilir, böylece kodda proje klasörünün yolu bir ortam değişkeni aracılığıyla ayarlanacaktır. TG_BOT_PATH.

Ortam değişkeni oluşturmanın birkaç yolu vardır; en basiti onu bir dosyaya yazmaktır. .Renviron.

Komutu kullanarak bu dosyayı oluşturabilir veya düzenleyebilirsiniz. file.edit(path.expand(file.path("~", ".Renviron"))). Çalıştırın ve dosyaya bir satır ekleyin:

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

Daha sonra dosyayı kaydedin .Renviron ve RStudio'yu yeniden başlatın.

Veritabanı oluşturma

Bir sonraki adım bir veritabanı oluşturmaktır. 2 tabloya ihtiyacımız olacak:

  • chat_data — botun kullanıcıdan istediği veriler
  • chat_state — tüm sohbetlerin mevcut durumu

Bu tabloları aşağıdaki SQL sorgusunu kullanarak oluşturabilirsiniz:

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 projesini şuradan indirdiyseniz GitHub, daha sonra veritabanını oluşturmak için R'de aşağıdaki kodu kullanabilirsiniz.

# Скрипт создания базы данных
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'))

Veritabanıyla çalışacak fonksiyonların yazılması

Zaten hazır bir konfigürasyon dosyamız var ve oluşturulmuş bir veritabanımız var. Şimdi bu veritabanına veri okumak ve yazmak için işlevler yazmanız gerekiyor.

Projeyi şuradan indirdiyseniz GitHub, ardından dosyadaki işlevleri bulabilirsiniz db_bot_function.R.

Veritabanıyla çalışmak için işlev kodu

# ###########################################################
# 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 basit fonksiyon oluşturduk:

  • get_state() - mevcut sohbet durumunu veritabanından alın
  • set_state() — mevcut sohbet durumunu veritabanına yaz
  • get_chat_data() — kullanıcı tarafından gönderilen verileri alın
  • set_chat_data() - kullanıcıdan alınan verileri kaydedin

Tüm işlevler oldukça basittir; ya komutu kullanarak veri tabanından veri okurlar dbGetQuery(), veya taahhüt et UPSERT işlevi kullanarak işlem (mevcut verileri değiştirmek veya veritabanına yeni veriler yazmak) dbExecute().

UPSERT işleminin sözdizimi aşağıdaki gibidir:

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

Onlar. tablo alanımızda chat_id benzersizlik kısıtlamasına sahiptir ve tabloların birincil anahtarıdır. Başlangıçta tabloya bilgi eklemeye çalışıyoruz ve mevcut sohbetin verileri zaten mevcutsa bir hata alıyoruz, bu durumda bu sohbetin bilgilerini güncellememiz yeterli.

Daha sonra bu fonksiyonları botun metodlarında ve filtrelerinde kullanacağız.

Bot yöntemleri

Botumuzu oluşturmanın bir sonraki adımı yöntemler oluşturmaktır. Projeyi şuradan indirdiyseniz GitHub, o zaman tüm yöntemler dosyadadır bot_methods.R.

Bot yöntemi kodu

# ###########################################################
# 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 yöntem oluşturduk:

  • start — Bir diyalog başlatır
  • state — Geçerli sohbet durumunu alır
  • reset — Geçerli sohbet durumunu sıfırlar
  • enter_name — Bot adınızı sorar
  • enter_age — Bot yaşınızı sorar

yöntem start adınızı sorar ve sohbet durumunu şu şekilde değiştirir: wait_nameyani Adınızı girmek için beklemeye alın.

Daha sonra adı gönderirsiniz ve bu yöntemle işlenir. enter_namebot sizi selamlar, alınan adı veritabanına yazar ve sohbeti duruma geçirir wait_age.

Bu aşamada bot sizden yaşınızı girmenizi bekliyor. Yaşınızı gönderiyorsunuz, bot mesajı kontrol ediyor, numara yerine mesaj gönderdiyseniz şunu söyleyecektir: Ты ввёл некорректные данные, введи числоve verilerinizi yeniden girmenizi bekleyecektir. Numara gönderdiyseniz bot yaşınızı kabul ettiğini bildirecek, alınan verileri veritabanına yazacak, sizden alınan tüm verileri raporlayacak ve sohbet durumunu orijinal konumuna döndürecektir yani. V start.

Yöntemi çağırarak state Mevcut sohbet durumunu istediğiniz zaman talep edebilirsiniz ve reset sohbeti orijinal durumuna döndürün.

Mesaj filtreleri

Bizim durumumuzda bu, bir bot oluşturmanın en önemli parçalarından biridir. Botun sizden hangi bilgileri beklediğini ve bunların nasıl işlenmesi gerektiğini anlaması mesaj filtreleri yardımıyla olur.

Projede GitHub filtreler dosyaya kayıtlıdır message_filters.R.

Mesaj filtre kodu:

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

Filtrelerde önceden yazılan işlevi kullanırız get_state()Sohbetin mevcut durumunu istemek için. Bu işlev yalnızca 1 argüman gerektirir, sohbet kimliği.

Sonraki filtre wait_name sohbet bir durumdayken mesajları işler wait_nameve buna göre filtre wait_age sohbet bir durumdayken mesajları işler wait_age.

İşleyiciler

İşleyicilerin bulunduğu dosya çağrılır işleyiciler.R, ve aşağıdaki koda sahiptir:

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

Öncelikle bir diyalog başlatmak, sıfırlamak ve mevcut durumu sorgulamak için yöntemler çalıştırmanıza izin verecek komut işleyicileri oluşturuyoruz.

Daha sonra, önceki adımda oluşturduğumuz filtreleri kullanarak 2 mesaj işleyicisi oluşturup bunlara bir filtre ekliyoruz. !MessageFilters$command, böylece komutları herhangi bir sohbet durumunda kullanabiliriz.

Bot başlatma kodu

Artık başlatmaya hazır her şeye sahibiz, botu başlatmak için gereken ana kod dosyada 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()

Sonuç olarak, bu botu aldık:
R'de bir telgraf botu yazmak (bölüm 4): Botla tutarlı, mantıksal bir diyalog oluşturmak

Komutu kullanarak herhangi bir zamanda /state mevcut sohbet durumunu sorgulayabiliriz ve komutu kullanarak /reset sohbeti orijinal durumuna döndürün ve diyaloğu yeniden başlatın.

Sonuç

Bu yazımızda bot içerisinde veritabanının nasıl kullanılacağını ve sohbet durumunu kaydederek sıralı mantıksal diyalogların nasıl oluşturulacağını öğrendik.

Bu durumda, bu tür botlar oluşturma fikrini daha kolay anlamanız için en ilkel örneğe baktık; pratikte çok daha karmaşık diyaloglar oluşturabilirsiniz.

Bu serideki bir sonraki makalede, bot kullanıcılarının çeşitli yöntemleri kullanma haklarını nasıl kısıtlayacağımızı öğreneceğiz.

Kaynak: habr.com

Yorum ekle