Pagsusulat ng telegram bot sa R ​​(bahagi 4): Pagbuo ng pare-pareho, lohikal na dialogue sa bot

Kung nabasa mo na ang nakaraan tatlong artikulo mula sa seryeng ito, alam mo na kung paano magsulat ng mga ganap na telegram bot na may keyboard.

Sa artikulong ito, matututunan natin kung paano magsulat ng isang bot na magpapanatili ng pare-parehong diyalogo. Yung. Tatanungin ka ng bot ng mga tanong at hihintayin kang magpasok ng ilang impormasyon. Depende sa data na iyong ipinasok, ang bot ay magsasagawa ng ilang mga aksyon.

Gayundin sa artikulong ito matututunan natin kung paano gumamit ng isang database sa ilalim ng hood ng bot, sa aming halimbawa ito ay magiging SQLite, ngunit maaari kang gumamit ng anumang iba pang DBMS. Sumulat ako nang mas detalyado tungkol sa pakikipag-ugnayan sa mga database sa wikang R sa artikulong ito.

Pagsusulat ng telegram bot sa R ​​(bahagi 4): Pagbuo ng pare-pareho, lohikal na dialogue sa bot

Lahat ng mga artikulo mula sa seryeng "Pagsusulat ng telegram bot sa R"

  1. Lumilikha kami ng bot at ginagamit ito upang magpadala ng mga mensahe sa telegrama
  2. Magdagdag ng suporta sa command at mga filter ng mensahe sa bot
  3. Paano magdagdag ng suporta sa keyboard sa isang bot
  4. Bumuo ng pare-pareho, lohikal na pag-uusap sa bot

nilalaman

Kung interesado ka sa pagsusuri ng data, maaaring interesado ka sa aking telegram и youtube mga channel. Karamihan sa nilalaman ay nakatuon sa wikang R.

  1. Pagpapakilala
  2. Proseso ng pagbuo ng bot
  3. Istruktura ng proyekto ng bot
  4. Bot config
  5. Lumikha ng variable ng kapaligiran
  6. Paglikha ng isang database
  7. Pagsusulat ng mga function upang gumana sa database
  8. Mga pamamaraan ng bot
  9. Mga filter ng mensahe
  10. Mga Handler
  11. Code ng paglulunsad ng bot
  12. Konklusyon

Pagpapakilala

Upang humiling ang bot ng data mula sa iyo at hintayin kang magpasok ng anumang impormasyon, kakailanganin mong i-record ang kasalukuyang estado ng diyalogo. Ang pinakamahusay na paraan upang gawin ito ay ang paggamit ng ilang uri ng naka-embed na database, tulad ng SQLite.

Yung. Ang lohika ay magiging ganito. Tinatawag namin ang paraan ng bot, at sunud-sunod na humihiling ang bot ng ilang impormasyon mula sa amin, at sa bawat hakbang ay hinihintay nitong maipasok ang impormasyong ito at masusuri ito.

Isusulat namin ang pinakasimpleng posibleng bot, unang hihilingin nito ang iyong pangalan, pagkatapos ay ang iyong edad, at ise-save ang natanggap na data sa database. Kapag humihingi ng edad, susuriin nito kung numero ang inilagay na data at hindi text.

Ang gayong simpleng pag-uusap ay magkakaroon lamang ng tatlong estado:

  1. Ang simula ay ang normal na estado ng bot, kung saan hindi ito umaasa ng anumang impormasyon mula sa iyo
  2. wait_name - estado kung saan naghihintay ang bot para sa isang pangalan na maipasok
  3. wait_age ay ang estado kung saan hinihintay ng bot ang iyong edad na maipasok, ang bilang ng buong taon.

Proseso ng pagbuo ng bot

Sa panahon ng artikulo, bubuo kami ng isang bot nang sunud-sunod; ang buong proseso ay maaaring ilarawan sa eskematiko tulad ng sumusunod:
Pagsusulat ng telegram bot sa R ​​(bahagi 4): Pagbuo ng pare-pareho, lohikal na dialogue sa bot

  1. Lumilikha kami ng bot config kung saan kami ay mag-iimbak ng ilang mga setting. Sa aming kaso, ang bot token, at ang path sa database file.
  2. Gumagawa kami ng variable ng kapaligiran kung saan iimbak ang landas patungo sa proyekto na may bot.
  3. Ginagawa namin ang database mismo, at ilang mga function upang ang bot ay maaaring makipag-ugnayan dito.
  4. Nagsusulat kami ng mga pamamaraan ng bot, i.e. ang mga function na gagawin nito.
  5. Pagdaragdag ng mga filter ng mensahe. Sa tulong kung saan maa-access ng bot ang mga kinakailangang pamamaraan, depende sa kasalukuyang estado ng chat.
  6. Nagdaragdag kami ng mga tagapangasiwa na magkokonekta ng mga command at mensahe gamit ang mga kinakailangang pamamaraan ng bot.
  7. Ilunsad natin ang bot.

Istruktura ng proyekto ng bot

Para sa kaginhawahan, hahatiin namin ang code ng aming bot, at iba pang nauugnay na mga file, sa sumusunod na istraktura.

  • bot.R — ang pangunahing code ng aming bot
  • db_bot_function.R — isang bloke ng code na may mga function para sa pagtatrabaho sa database
  • bot_methods.R — code ng mga pamamaraan ng bot
  • message_filters.R — mga filter ng mensahe
  • mga humahawak.R - mga humahawak
  • config.cfg - bot config
  • create_db_data.sql — SQL script para sa paglikha ng isang talahanayan na may data ng chat sa database
  • create_db_state.sql — SQL script para sa paglikha ng isang talahanayan ng kasalukuyang estado ng chat sa database
  • bot.db - database ng bot

Maaari mong tingnan ang buong proyekto ng bot, o i-download mula sa aking imbakan sa GitHub.

Bot config

Gagamitin namin ang karaniwan bilang isang config ini file, ang sumusunod na anyo:

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

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

Sa config isinulat namin ang bot token at ang path sa database, i.e. sa bot.db file; gagawa kami ng file mismo sa susunod na hakbang.

Para sa mas kumplikadong mga bot, maaari kang lumikha ng mas kumplikadong mga config, bukod pa, hindi kinakailangan na magsulat ng isang config, maaari kang gumamit ng anumang iba pang format kasama ang JSON.

Lumikha ng variable ng kapaligiran

Sa bawat PC, ang folder na may bot project ay maaaring matatagpuan sa iba't ibang mga direktoryo at sa iba't ibang mga drive, kaya sa code ang landas sa folder ng proyekto ay itatakda sa pamamagitan ng isang variable ng kapaligiran TG_BOT_PATH.

Mayroong ilang mga paraan upang lumikha ng isang variable ng kapaligiran, ang pinakasimpleng ay isulat ito sa isang file .Renviron.

Maaari mong gawin o i-edit ang file na ito gamit ang command file.edit(path.expand(file.path("~", ".Renviron"))). Isagawa ito at magdagdag ng isang linya sa file:

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

Susunod na i-save ang file .Renviron at i-restart ang RStudio.

Paglikha ng isang database

Ang susunod na hakbang ay upang lumikha ng isang database. Kakailanganin namin ang 2 talahanayan:

  • chat_data — data na hiniling ng bot mula sa user
  • chat_state — kasalukuyang estado ng lahat ng mga chat

Maaari kang lumikha ng mga talahanayang ito gamit ang sumusunod na query sa SQL:

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

Kung na-download mo ang proyekto ng bot mula sa GitHub, pagkatapos ay upang lumikha ng database maaari mong gamitin ang sumusunod na code sa 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'))

Pagsusulat ng mga function upang gumana sa database

Mayroon na kaming nakahanda na configuration file at nakagawa na ng database. Ngayon ay kailangan mong magsulat ng mga function upang basahin at isulat ang data sa database na ito.

Kung na-download mo ang proyekto mula sa GitHub, pagkatapos ay mahahanap mo ang mga function sa file db_bot_function.R.

Function code para sa pagtatrabaho sa 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]])

}

Gumawa kami ng 4 na simpleng function:

  • get_state() — makuha ang kasalukuyang status ng chat mula sa database
  • set_state() — isulat ang kasalukuyang status ng chat sa database
  • get_chat_data() — tumanggap ng data na ipinadala ng gumagamit
  • set_chat_data() — itala ang data na natanggap mula sa gumagamit

Ang lahat ng mga pag-andar ay medyo simple, binabasa nila ang data mula sa database gamit ang command dbGetQuery(), o mangako UPSERT operasyon (pagbabago ng umiiral na data o pagsulat ng bagong data sa database), gamit ang function dbExecute().

Ang syntax para sa UPSERT operation ay ang mga sumusunod:

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

Yung. sa field ng table namin chat_id ay may limitasyon sa pagiging natatangi at ito ang pangunahing susi ng mga talahanayan. Sa una, sinusubukan naming magdagdag ng impormasyon sa talahanayan, at nakakakuha kami ng isang error kung ang data para sa kasalukuyang chat ay mayroon na, kung saan ina-update lang namin ang impormasyon para sa chat na ito.

Susunod, gagamitin namin ang mga function na ito sa mga pamamaraan at filter ng bot.

Mga pamamaraan ng bot

Ang susunod na hakbang sa pagbuo ng aming bot ay ang paglikha ng mga pamamaraan. Kung na-download mo ang proyekto mula sa GitHub, pagkatapos ang lahat ng mga pamamaraan ay nasa file bot_methods.R.

Code ng paraan ng 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')
  }

}

Gumawa kami ng 5 pamamaraan:

  • simulan — Magsimula ng dialog
  • estado — Kunin ang kasalukuyang status ng chat
  • reset — I-reset ang kasalukuyang status ng chat
  • enter_name — Itatanong ng bot ang iyong pangalan
  • enter_age — Hinihiling ng bot ang iyong edad

pamamaraan start hihilingin ang iyong pangalan, at binago ang status ng chat sa wait_name, ibig sabihin. sa standby para sa paglalagay ng iyong pangalan.

Susunod, ipadala mo ang pangalan at ito ay pinoproseso ng pamamaraan enter_name, binabati ka ng bot, isinusulat ang natanggap na pangalan sa database, at inililipat ang chat sa estado wait_age.

Sa yugtong ito, inaasahan ng bot na ipasok mo ang iyong edad. Ipapadala mo ang iyong edad, susuriin ng bot ang mensahe, kung nagpadala ka ng ilang text sa halip na numero, sasabihin nito: Ты ввёл некорректные данные, введи число, at hihintayin mong ipasok muli ang iyong data. Kung nagpadala ka ng numero, iuulat ng bot na tinanggap nito ang iyong edad, isusulat ang natanggap na data sa database, iulat ang lahat ng data na natanggap mula sa iyo at ibabalik ang status ng chat sa orihinal nitong posisyon, i.e. V start.

Sa pamamagitan ng pagtawag sa pamamaraan state maaari kang humiling ng kasalukuyang status ng chat anumang oras, at gamit ang reset ibalik ang chat sa orihinal nitong estado.

Mga filter ng mensahe

Sa aming kaso, ito ang isa sa pinakamahalagang bahagi sa pagbuo ng bot. Ito ay sa tulong ng mga filter ng mensahe na mauunawaan ng bot kung anong impormasyon ang inaasahan nito mula sa iyo at kung paano ito dapat iproseso.

Sa proyekto sa GitHub ang mga filter ay nakarehistro sa file message_filters.R.

Filter code ng mensahe:

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

Sa mga filter ginagamit namin ang dating nakasulat na function get_state(), upang hilingin ang kasalukuyang estado ng chat. Ang function na ito ay nangangailangan lamang ng 1 argument, chat id.

Susunod na filter wait_name nagpoproseso ng mga mensahe kapag nasa estado ang chat wait_name, at naaayon sa filter wait_age nagpoproseso ng mga mensahe kapag nasa estado ang chat wait_age.

Mga Handler

Ang file na may mga humahawak ay tinatawag mga humahawak.R, at mayroong sumusunod na code:

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

Una, gumawa kami ng mga command handler na magbibigay-daan sa iyo na magpatakbo ng mga pamamaraan para magsimula ng dialog, i-reset ito, at i-query ang kasalukuyang estado.

Susunod, gumawa kami ng 2 tagapangasiwa ng mensahe gamit ang mga filter na ginawa sa nakaraang hakbang, at magdagdag ng filter sa kanila !MessageFilters$command, upang magamit namin ang mga command sa anumang estado ng chat.

Code ng paglulunsad ng bot

Ngayon ay handa na kaming ilunsad, ang pangunahing code para sa paglulunsad ng bot ay nasa 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()

Bilang resulta, nakuha namin ang bot na ito:
Pagsusulat ng telegram bot sa R ​​(bahagi 4): Pagbuo ng pare-pareho, lohikal na dialogue sa bot

Sa anumang oras gamit ang command /state maaari naming i-query ang kasalukuyang estado ng chat, at gamit ang command /reset ibalik ang chat sa orihinal nitong estado at simulan muli ang diyalogo.

Konklusyon

Sa artikulong ito, nalaman namin kung paano gumamit ng database sa loob ng isang bot, at kung paano bumuo ng mga sunud-sunod na lohikal na diyalogo sa pamamagitan ng pagtatala ng status ng chat.

Sa kasong ito, tiningnan namin ang pinaka primitive na halimbawa, upang mas madali para sa iyo na maunawaan ang ideya ng pagbuo ng mga naturang bot; sa pagsasagawa, maaari kang bumuo ng mas kumplikadong mga diyalogo.

Sa susunod na artikulo sa seryeng ito, malalaman natin kung paano higpitan ang mga karapatan ng mga user ng bot na gumamit ng iba't ibang pamamaraan nito.

Pinagmulan: www.habr.com

Magdagdag ng komento