نوشتن ربات تلگرام به زبان R (قسمت 4): ایجاد یک گفتگوی منسجم و منطقی با ربات

اگر قبلاً مطالب قبلی را خوانده اید سه مقاله از این سری، پس شما قبلاً می دانید که چگونه ربات های تلگرام کامل را با صفحه کلید بنویسید.

در این مقاله یاد می گیریم که چگونه یک ربات بنویسیم که یک گفتگوی ثابت را حفظ کند. آن ها ربات از شما سوالاتی می پرسد و منتظر می ماند تا شما اطلاعاتی را وارد کنید. بسته به داده هایی که وارد می کنید، ربات اقداماتی را انجام می دهد.

همچنین در این مقاله یاد می گیریم که چگونه از یک پایگاه داده در زیر هود ربات استفاده کنیم، در مثال ما SQLite خواهد بود، اما می توانید از هر DBMS دیگری استفاده کنید. من با جزئیات بیشتری در مورد تعامل با پایگاه های داده به زبان R نوشتم این مقاله.

نوشتن ربات تلگرام به زبان R (قسمت 4): ایجاد یک گفتگوی منسجم و منطقی با ربات

تمامی مطالب از سری “نوشتن ربات تلگرام به زبان R”

  1. ما یک ربات ایجاد می کنیم و از آن برای ارسال پیام در تلگرام استفاده می کنیم
  2. پشتیبانی دستورات و فیلترهای پیام را به ربات اضافه کنید
  3. نحوه اضافه کردن پشتیبانی از صفحه کلید به ربات
  4. ایجاد یک گفتگوی منسجم و منطقی با ربات

مقدار

اگر به تجزیه و تحلیل داده ها علاقه دارید، ممکن است به من علاقه مند شوید تلگراف и یوتیوب کانال ها بیشتر مطالب به زبان R اختصاص داده شده است.

  1. معرفی
  2. فرآیند ساخت ربات
  3. ساختار پروژه ربات
  4. پیکربندی ربات
  5. یک متغیر محیطی ایجاد کنید
  6. ایجاد پایگاه داده
  7. نوشتن توابع برای کار با پایگاه داده
  8. روش های ربات
  9. فیلترهای پیام
  10. هندلرها
  11. کد راه اندازی ربات
  12. نتیجه

معرفی

برای اینکه ربات اطلاعاتی را از شما درخواست کند و منتظر بماند تا شما اطلاعاتی را وارد کنید، باید وضعیت فعلی گفتگو را ضبط کنید. بهترین راه برای انجام این کار استفاده از نوعی پایگاه داده جاسازی شده مانند SQLite است.

آن ها منطق به شرح زیر خواهد بود. ما متد ربات را فراخوانی می کنیم و ربات به صورت متوالی اطلاعاتی را از ما درخواست می کند و در هر مرحله منتظر وارد شدن این اطلاعات می شود و می تواند آن را بررسی کند.

ساده ترین ربات ممکن را می نویسیم، ابتدا نام شما و سپس سن شما را می پرسد و داده های دریافتی را در پایگاه داده ذخیره می کند. وقتی سن را می‌پرسد، بررسی می‌کند که داده‌های وارد شده یک عدد است و نه متن.

چنین گفتگوی ساده ای تنها سه حالت خواهد داشت:

  1. start حالت عادی ربات است که در آن هیچ اطلاعاتی از شما انتظار ندارد
  2. Wait_name - حالتی که ربات منتظر است تا نامی وارد شود
  3. Wait_age حالتی است که ربات منتظر می ماند تا سن شما، تعداد سال های کامل وارد شود.

فرآیند ساخت ربات

در طول مقاله، ما یک ربات را مرحله به مرحله می سازیم؛ کل فرآیند را می توان به صورت شماتیک به شرح زیر نشان داد:
نوشتن ربات تلگرام به زبان R (قسمت 4): ایجاد یک گفتگوی منسجم و منطقی با ربات

  1. ما یک پیکربندی ربات ایجاد می کنیم که در آن تنظیماتی را ذخیره می کنیم. در مورد ما، نشانه ربات، و مسیر فایل پایگاه داده.
  2. یک متغیر محیطی ایجاد می کنیم که مسیر پروژه با ربات در آن ذخیره می شود.
  3. ما خود پایگاه داده و تعدادی توابع را ایجاد می کنیم تا ربات بتواند با آن تعامل داشته باشد.
  4. ما متدهای ربات را می نویسیم، یعنی. عملکردهایی که انجام خواهد داد
  5. افزودن فیلترهای پیام با کمک آن ربات بسته به وضعیت فعلی چت به روش های لازم دسترسی پیدا می کند.
  6. ما کنترل کننده هایی را اضافه می کنیم که دستورات و پیام ها را با روش های ربات لازم به هم متصل می کنند.
  7. بیایید ربات را راه اندازی کنیم.

ساختار پروژه ربات

برای سهولت، کد ربات خود و سایر فایل های مرتبط را به ساختار زیر تقسیم می کنیم.

  • bot.R - کد اصلی ربات ما
  • db_bot_function.R - یک بلوک کد با توابع برای کار با پایگاه داده
  • bot_methods.R - کد روش های ربات
  • message_filters.R - فیلترهای پیام
  • گردانندگان.R - کنترل کننده ها
  • config.cfg - پیکربندی ربات
  • create_db_data.sql - اسکریپت SQL برای ایجاد جدول با داده های چت در پایگاه داده
  • create_db_state.sql - اسکریپت SQL برای ایجاد جدولی از وضعیت چت فعلی در پایگاه داده
  • bot.db - پایگاه داده ربات

می توانید کل پروژه ربات را مشاهده کنید، یا دانلود از من مخزن در GitHub.

پیکربندی ربات

ما از حالت معمولی به عنوان پیکربندی استفاده خواهیم کرد فایل ini، فرم زیر:

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

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

در کانفیگ ما توکن ربات و مسیر پایگاه داده را می نویسیم، i.e. به فایل bot.db؛ در مرحله بعد خود فایل را ایجاد می کنیم.

برای ربات های پیچیده تر، می توانید تنظیمات پیچیده تری ایجاد کنید، علاوه بر این، نیازی به نوشتن یک پیکربندی ini نیست، می توانید از هر فرمت دیگری از جمله JSON استفاده کنید.

یک متغیر محیطی ایجاد کنید

در هر رایانه شخصی، پوشه با پروژه ربات می تواند در دایرکتوری های مختلف و در درایوهای مختلف قرار گیرد، بنابراین در کد مسیر پوشه پروژه از طریق یک متغیر محیطی تنظیم می شود. TG_BOT_PATH.

راه های مختلفی برای ایجاد یک متغیر محیطی وجود دارد که ساده ترین آنها نوشتن آن در یک فایل است .رنویرون.

با استفاده از دستور می توانید این فایل را ایجاد یا ویرایش کنید file.edit(path.expand(file.path("~", ".Renviron"))). آن را اجرا کنید و یک خط به فایل اضافه کنید:

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

بعد فایل را ذخیره کنید .رنویرون و RStudio را مجددا راه اندازی کنید.

ایجاد پایگاه داده

مرحله بعدی ایجاد یک پایگاه داده است. ما به 2 جدول نیاز داریم:

  • chat_data - داده‌هایی که ربات از کاربر درخواست کرده است
  • chat_state - وضعیت فعلی همه چت ها

می توانید این جداول را با استفاده از پرس و جوی 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
);

اگر پروژه ربات را از GitHub، سپس برای ایجاد پایگاه داده می توانید از کد زیر در 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'))

نوشتن توابع برای کار با پایگاه داده

ما قبلاً یک فایل پیکربندی آماده و یک پایگاه داده ایجاد کرده ایم. اکنون برای خواندن و نوشتن داده ها در این پایگاه داده باید توابعی بنویسید.

اگر پروژه را از GitHub، سپس می توانید توابع موجود در فایل را پیدا کنید db_bot_function.R.

کد تابع برای کار با پایگاه داده

# ###########################################################
# 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 تابع ساده ایجاد کردیم:

  • get_state() - وضعیت چت فعلی را از پایگاه داده دریافت کنید
  • set_state() - وضعیت چت فعلی را در پایگاه داده بنویسید
  • get_chat_data() - دریافت داده های ارسال شده توسط کاربر
  • set_chat_data() - ثبت داده های دریافتی از کاربر

همه توابع بسیار ساده هستند، آنها یا داده ها را از پایگاه داده با استفاده از دستور می خوانند dbGetQuery()، یا متعهد شوید UPSERT عملیات (تغییر داده های موجود یا نوشتن داده های جدید در پایگاه داده)، با استفاده از تابع dbExecute().

سینتکس عملیات UPSERT به شرح زیر است:

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

آن ها در قسمت جداول ما chat_id دارای یک محدودیت منحصر به فرد است و کلید اصلی جداول است. در ابتدا سعی می کنیم اطلاعاتی را به جدول اضافه کنیم و اگر داده های چت فعلی از قبل موجود باشد با خطا مواجه می شویم که در این صورت به سادگی اطلاعات این چت را به روز می کنیم.

در مرحله بعد، از این توابع در متدها و فیلترهای ربات استفاده خواهیم کرد.

روش های ربات

قدم بعدی در ساخت ربات ما ایجاد متدهاست. اگر پروژه را از GitHub، سپس تمام روش ها در فایل موجود است bot_methods.R.

کد روش ربات

# ###########################################################
# 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 روش ایجاد کردیم:

  • شروع - یک گفتگو را شروع کنید
  • حالت - وضعیت فعلی چت را دریافت کنید
  • بازنشانی — وضعیت گپ فعلی را بازنشانی کنید
  • enter_name - ربات نام شما را می پرسد
  • enter_age - ربات سن شما را می پرسد

روش start نام شما را می پرسد و وضعیت چت را به تغییر می دهد wait_name، یعنی در حالت آماده به کار برای وارد کردن نام خود.

بعد شما نام را ارسال می کنید و با روش پردازش می شود enter_name، ربات به شما خوش آمد می گوید، نام دریافتی را در پایگاه داده می نویسد و چت را به حالت تغییر می دهد. انتظار_سن.

در این مرحله، ربات انتظار دارد که سن خود را وارد کنید. شما سن خود را ارسال می کنید، ربات پیام را بررسی می کند، اگر به جای عدد، متنی ارسال کنید، می گوید: Ты ввёл некорректные данные, введи числоو منتظر می ماند تا دوباره داده های خود را وارد کنید. اگر شماره ای ارسال کردید، ربات گزارش می دهد که سن شما را پذیرفته است، داده های دریافتی را در پایگاه داده می نویسد، تمام داده های دریافتی از شما را گزارش می دهد و وضعیت چت را به موقعیت اصلی خود باز می گرداند. V start.

با فراخوانی روش state شما می توانید در هر زمان وضعیت فعلی چت را درخواست کنید و با استفاده از reset چت را به حالت اولیه برگردانید.

فیلترهای پیام

در مورد ما، این یکی از مهمترین بخش ها در ساخت ربات است. با کمک فیلترهای پیام است که ربات متوجه خواهد شد که چه اطلاعاتی از شما انتظار دارد و چگونه باید پردازش شود.

در پروژه در GitHub فیلترها در فایل ثبت می شوند message_filters.R.

کد فیلتر پیام:

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

در فیلترها از تابع نوشته شده قبلی استفاده می کنیم get_state()، به منظور درخواست وضعیت فعلی چت. این تابع فقط به 1 آرگومان، شناسه چت نیاز دارد.

فیلتر بعدی wait_name هنگامی که چت در حالت است، پیام ها را پردازش می کند wait_name، و بر این اساس فیلتر انتظار_سن هنگامی که چت در حالت است، پیام ها را پردازش می کند wait_age.

هندلرها

فایل با هندلرها نامیده می شود گردانندگان.Rو دارای کد زیر است:

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

ابتدا کنترل‌کننده‌های دستوری ایجاد می‌کنیم که به شما امکان می‌دهد روش‌هایی را برای شروع یک گفتگو، تنظیم مجدد و پرس و جوی وضعیت فعلی اجرا کنید.

در مرحله بعد با استفاده از فیلترهای ایجاد شده در مرحله قبل 2 کنترل کننده پیام ایجاد می کنیم و یک فیلتر به آنها اضافه می کنیم !MessageFilters$command، به طوری که می توانیم در هر حالت چت از دستورات استفاده کنیم.

کد راه اندازی ربات

اکنون همه چیز برای راه اندازی آماده است، کد اصلی راه اندازی ربات در فایل موجود است 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()

در نتیجه، این ربات را دریافت کردیم:
نوشتن ربات تلگرام به زبان R (قسمت 4): ایجاد یک گفتگوی منسجم و منطقی با ربات

در هر زمان با استفاده از دستور /state ما می توانیم وضعیت چت فعلی را پرس و جو کنیم و با استفاده از دستور /reset چت را به حالت اولیه برگردانید و گفتگو را دوباره شروع کنید.

نتیجه

در این مقاله، نحوه استفاده از پایگاه داده در یک ربات و نحوه ساخت دیالوگ های منطقی متوالی با ضبط حالت چت را فهمیدیم.

در این مورد، ما به ابتدایی‌ترین مثال نگاه کردیم تا درک ایده ساخت چنین ربات‌هایی برای شما آسان‌تر باشد؛ در عمل، می‌توانید دیالوگ‌های بسیار پیچیده‌تری بسازید.

در مقاله بعدی این مجموعه یاد می گیریم که چگونه حقوق کاربران ربات را برای استفاده از روش های مختلف آن محدود کنیم.

منبع: www.habr.com

اضافه کردن نظر