R-da telegramma botini yozish (4-qism): Bot bilan izchil, mantiqiy dialog qurish

Agar siz avvalgisini o'qigan bo'lsangiz uchta maqola ushbu seriyadan, keyin siz klaviatura bilan to'liq huquqli telegram botlarini qanday yozishni allaqachon bilasiz.

Ushbu maqolada biz izchil dialogni davom ettiradigan botni qanday yozishni o'rganamiz. Bular. Bot sizga savollar beradi va ba'zi ma'lumotlarni kiritishingizni kutadi. Siz kiritgan ma'lumotlarga qarab, bot ba'zi amallarni bajaradi.

Shuningdek, ushbu maqolada biz bot qopqog'i ostidagi ma'lumotlar bazasidan qanday foydalanishni o'rganamiz, bizning misolimizda u SQLite bo'ladi, lekin siz boshqa har qanday DBMS dan foydalanishingiz mumkin. Men R tilida ma'lumotlar bazalari bilan ishlash haqida batafsilroq yozdim Ushbu maqola.

R-da telegramma botini yozish (4-qism): Bot bilan izchil, mantiqiy dialog qurish

“Telegram botini R-da yozish” turkumidagi barcha maqolalar

  1. Biz bot yaratamiz va undan telegramda xabar yuborish uchun foydalanamiz
  2. Botga buyruqlarni qo'llab-quvvatlash va xabar filtrlarini qo'shing
  3. Botga klaviatura yordamini qanday qo'shish mumkin
  4. Bot bilan izchil, mantiqiy suhbat qurish

Mundarija

Agar siz ma'lumotlarni tahlil qilish bilan qiziqsangiz, meni qiziqtirishi mumkin telegramma и youtube kanallar. Kontentning katta qismi R tiliga bag'ishlangan.

  1. kirish
  2. Bot yaratish jarayoni
  3. Bot loyihasi tuzilishi
  4. Bot konfiguratsiyasi
  5. Atrof-muhit o'zgaruvchisini yarating
  6. Ma'lumotlar bazasini yaratish
  7. Ma'lumotlar bazasi bilan ishlash funksiyalarini yozish
  8. Bot usullari
  9. Xabar filtrlari
  10. Ishlovchilar
  11. Botni ishga tushirish kodi
  12. xulosa

kirish

Bot sizdan ma'lumot so'rashi va istalgan ma'lumotni kiritishingizni kutishi uchun siz dialogning joriy holatini yozib olishingiz kerak bo'ladi. Buning eng yaxshi usuli SQLite kabi o'rnatilgan ma'lumotlar bazasidan foydalanishdir.

Bular. Mantiq quyidagicha bo'ladi. Biz bot usulini chaqiramiz va bot bizdan ketma-ket ba'zi ma'lumotlarni so'raydi va har bir qadamda bu ma'lumot kiritilishini kutadi va uni tekshirishi mumkin.

Biz eng oddiy botni yozamiz, avval u sizning ismingizni, keyin yoshingizni so'raydi va olingan ma'lumotlarni ma'lumotlar bazasiga saqlaydi. Yoshni so'raganda, kiritilgan ma'lumotlar matn emas, raqam ekanligini tekshiradi.

Bunday oddiy dialog faqat uchta holatga ega bo'ladi:

  1. start - bu botning oddiy holati bo'lib, unda u sizdan hech qanday ma'lumot kutmaydi
  2. wait_name - bot ism kiritilishini kutayotgan holat
  3. wait_age - bot sizning yoshingiz kiritilishini kutadigan holat, to'liq yillar soni.

Bot yaratish jarayoni

Maqola davomida biz bosqichma-bosqich bot quramiz, butun jarayonni sxematik tarzda quyidagicha tasvirlash mumkin:
R-da telegramma botini yozish (4-qism): Bot bilan izchil, mantiqiy dialog qurish

  1. Biz ba'zi sozlamalarni saqlaydigan bot konfiguratsiyasini yaratamiz. Bizning holatda, bot tokeni va ma'lumotlar bazasi fayliga yo'l.
  2. Biz atrof-muhit o'zgaruvchisini yaratamiz, unda bot bilan loyihaga yo'l saqlanadi.
  3. Biz ma'lumotlar bazasini o'zi va bot u bilan o'zaro aloqada bo'lishi uchun bir qator funktsiyalarni yaratamiz.
  4. Biz bot usullarini yozamiz, ya'ni. bajaradigan funktsiyalari.
  5. Xabar filtrlarini qo'shish. Uning yordami bilan bot suhbatning hozirgi holatiga qarab kerakli usullarga kirishi mumkin.
  6. Biz buyruqlar va xabarlarni kerakli bot usullari bilan bog'laydigan ishlov beruvchilarni qo'shamiz.
  7. Keling, botni ishga tushiramiz.

Bot loyihasi tuzilishi

Qulaylik uchun biz botimiz kodini va boshqa tegishli fayllarni quyidagi tuzilishga ajratamiz.

  • bot.R — botimizning asosiy kodi
  • db_bot_function.R — maʼlumotlar bazasi bilan ishlash funksiyalariga ega kod bloki
  • bot_methods.R — bot usullari kodi
  • message_filters.R — xabar filtrlari
  • ishlovchilar.R - ishlovchilar
  • config.cfg - bot konfiguratsiyasi
  • create_db_data.sql — Maʼlumotlar bazasida suhbat maʼlumotlari bilan jadval yaratish uchun SQL skripti
  • create_db_state.sql — Maʼlumotlar bazasida joriy suhbat holati jadvalini yaratish uchun SQL skripti
  • bot.db - bot ma'lumotlar bazasi

Siz butun bot loyihasini ko'rishingiz mumkin, yoki скачать mening GitHub-dagi ombor.

Bot konfiguratsiyasi

Konfiguratsiya sifatida biz odatdagidan foydalanamiz ini fayli, quyidagi shakl:

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

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

Konfiguratsiyada biz bot tokenini va ma'lumotlar bazasiga yo'lni yozamiz, ya'ni. bot.db fayliga; keyingi bosqichda faylning o'zini yaratamiz.

Murakkab botlar uchun siz murakkabroq konfiguratsiyalarni yaratishingiz mumkin, bundan tashqari, ini konfiguratsiyasini yozish shart emas, siz boshqa har qanday formatdan, shu jumladan JSONdan ham foydalanishingiz mumkin.

Atrof-muhit o'zgaruvchisini yarating

Har bir shaxsiy kompyuterda bot loyihasi bo'lgan papka turli kataloglarda va turli disklarda joylashgan bo'lishi mumkin, shuning uchun kodda loyiha papkasiga yo'l muhit o'zgaruvchisi orqali o'rnatiladi. TG_BOT_PATH.

Muhit o'zgaruvchisini yaratishning bir necha usullari mavjud, eng oddiyi uni faylga yozishdir .Renviron.

Buyruq yordamida ushbu faylni yaratishingiz yoki tahrirlashingiz mumkin file.edit(path.expand(file.path("~", ".Renviron"))). Uni bajaring va faylga bitta qator qo'shing:

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

Keyin faylni saqlang .Renviron va RStudio-ni qayta ishga tushiring.

Ma'lumotlar bazasini yaratish

Keyingi qadam ma'lumotlar bazasini yaratishdir. Bizga 2 ta jadval kerak bo'ladi:

  • chat_data - bot foydalanuvchidan so'ragan ma'lumotlar
  • chat_state — barcha chatlarning joriy holati

Ushbu jadvallarni quyidagi SQL so'rovi yordamida yaratishingiz mumkin:

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

Agar siz bot loyihasini yuklab olgan bo'lsangiz GitHub, keyin ma'lumotlar bazasini yaratish uchun R da quyidagi koddan foydalanishingiz mumkin.

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

Ma'lumotlar bazasi bilan ishlash funksiyalarini yozish

Bizda allaqachon konfiguratsiya fayli va yaratilgan ma'lumotlar bazasi mavjud. Endi siz ushbu ma'lumotlar bazasiga ma'lumotlarni o'qish va yozish uchun funktsiyalarni yozishingiz kerak.

Agar siz loyihani yuklab olgan bo'lsangiz GitHub, keyin fayldagi funktsiyalarni topishingiz mumkin db_bot_function.R.

Ma'lumotlar bazasi bilan ishlash uchun funktsiya kodi

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

}

Biz 4 ta oddiy funktsiyani yaratdik:

  • get_state() — maʼlumotlar bazasidan joriy suhbat holatini oling
  • set_state() — maʼlumotlar bazasiga joriy suhbat holatini yozish
  • get_chat_data() — foydalanuvchi tomonidan yuborilgan maʼlumotlarni qabul qilish
  • set_chat_data() — foydalanuvchidan olingan maʼlumotlarni yozib olish

Barcha funktsiyalar juda oddiy, ular buyruq yordamida ma'lumotlar bazasidan ma'lumotlarni o'qiydilar dbGetQuery(), yoki topshiring UPSERT operatsiya (mavjud ma'lumotlarni o'zgartirish yoki ma'lumotlar bazasiga yangi ma'lumotlarni yozish), funktsiyadan foydalanish dbExecute().

UPSERT operatsiyasining sintaksisi quyidagicha:

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

Bular. bizning jadvallar maydonida chat_id noyoblik chekloviga ega va jadvallarning asosiy kalitidir. Dastlab, biz jadvalga ma'lumot qo'shishga harakat qilamiz va agar joriy chat uchun ma'lumotlar allaqachon mavjud bo'lsa, biz xatoga yo'l qo'yamiz, bu holda biz ushbu suhbat uchun ma'lumotni yangilaymiz.

Keyinchalik, biz ushbu funktsiyalardan botning usullari va filtrlarida foydalanamiz.

Bot usullari

Botimizni yaratishning keyingi bosqichi usullarni yaratishdir. Agar siz loyihani yuklab olgan bo'lsangiz GitHub, keyin barcha usullar faylda bot_methods.R.

Bot usuli kodi

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

}

Biz 5 ta usulni yaratdik:

  • start — Muloqot oynasini boshlash
  • davlat — joriy suhbat holatini oling
  • reset — Joriy suhbat holatini qayta o'rnatish
  • enter_name - bot sizning ismingizni so'raydi
  • enter_age - bot sizning yoshingizni so'raydi

usul start ismingizni so'raydi va suhbat holatini o'zgartiradi kutish_nomi, ya'ni. ismingizni kiritish uchun kutish uchun.

Keyinchalik, siz nomni yuborasiz va u usul bilan qayta ishlanadi enter_name, bot sizni kutib oladi, olingan nomni ma'lumotlar bazasiga yozadi va chatni holatga o'tkazadi kutish_yoshi.

Ushbu bosqichda bot sizning yoshingizga kirishingizni kutadi. Siz yoshingizni yuborasiz, bot xabarni tekshiradi, agar siz raqam o'rniga matn yuborsangiz, u shunday deydi: Ты ввёл некорректные данные, введи число, va ma'lumotlaringizni qayta kiritishingizni kutadi. Agar siz raqam yuborgan bo'lsangiz, bot sizning yoshingizni qabul qilganligi haqida xabar beradi, olingan ma'lumotlarni ma'lumotlar bazasiga yozadi, sizdan olingan barcha ma'lumotlar haqida xabar beradi va suhbat holatini asl holatiga qaytaradi, ya'ni. V start.

Usulni chaqirish orqali state Siz istalgan vaqtda joriy chat holatini so'rashingiz mumkin reset suhbatni asl holatiga qaytaring.

Xabar filtrlari

Bizning holatlarimizda bu botni yaratishning eng muhim qismlaridan biridir. Xabar filtrlari yordamida bot sizdan qanday ma'lumot kutayotganini va uni qanday qayta ishlash kerakligini tushunadi.

Loyihada GitHub Filtrlar faylda qayd etilgan message_filters.R.

Xabar filtri kodi:

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

Filtrlarda biz avval yozilgan funksiyadan foydalanamiz get_state(), chatning joriy holatini so'rash uchun. Bu funksiya faqat 1 ta argument, chat identifikatorini talab qiladi.

Keyingi filtr kutish_nomi chat holatida bo'lganda xabarlarni qayta ishlaydi wait_name, va shunga mos ravishda filtr kutish_yoshi chat holatida bo'lganda xabarlarni qayta ishlaydi wait_age.

Ishlovchilar

Ishlovchilarga ega fayl chaqiriladi ishlovchilar.R, va quyidagi kodga ega:

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

Avval biz dialogni boshlash, uni qayta o'rnatish va joriy holatni so'rash usullarini ishga tushirish imkonini beruvchi buyruqlar ishlovchilarini yaratamiz.

Keyinchalik, oldingi bosqichda yaratilgan filtrlar yordamida 2 ta xabar ishlov beruvchisini yaratamiz va ularga filtr qo'shamiz !MessageFilters$command, shuning uchun biz har qanday chat holatida buyruqlardan foydalanishimiz mumkin.

Botni ishga tushirish kodi

Endi bizda ishga tushirish uchun hamma narsa tayyor, botni ishga tushirish uchun asosiy kod faylda 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()

Natijada biz ushbu botni oldik:
R-da telegramma botini yozish (4-qism): Bot bilan izchil, mantiqiy dialog qurish

Istalgan vaqtda buyruq yordamida /state biz joriy chat holatini so'rashimiz mumkin va buyruq yordamida /reset suhbatni asl holatiga qaytaring va dialogni qaytadan boshlang.

xulosa

Ushbu maqolada biz bot ichidagi ma'lumotlar bazasidan qanday foydalanishni va suhbat holatini yozib olish orqali ketma-ket mantiqiy dialoglarni qanday qurishni aniqladik.

Bunday holda, biz eng ibtidoiy misolni ko'rib chiqdik, shunda siz bunday botlarni yaratish g'oyasini tushunishingiz osonroq bo'ladi; amalda siz ancha murakkab dialoglarni qurishingiz mumkin.

Ushbu turkumning keyingi maqolasida biz bot foydalanuvchilarining uning turli usullaridan foydalanish huquqlarini qanday cheklashni bilib olamiz.

Manba: www.habr.com

a Izoh qo'shish