ProHoster > بلوق > إدارة > كتابة روبوت برقية بلغة R (الجزء 4): بناء حوار منطقي ومتسق مع الروبوت
كتابة روبوت برقية بلغة R (الجزء 4): بناء حوار منطقي ومتسق مع الروبوت
إذا كنت قد قرأت بالفعل السابق ثلاث مقالات من هذه السلسلة، فأنت تعرف بالفعل كيفية كتابة روبوتات برقية كاملة باستخدام لوحة المفاتيح.
في هذه المقالة، سوف نتعلم كيفية كتابة روبوت يحافظ على حوار ثابت. أولئك. سيطرح عليك الروبوت أسئلة وينتظر منك إدخال بعض المعلومات. اعتمادًا على البيانات التي تدخلها، سيقوم الروبوت بتنفيذ بعض الإجراءات.
سنتعلم في هذه المقالة أيضًا كيفية استخدام قاعدة بيانات تحت غطاء الروبوت، في مثالنا سيكون SQLite، ولكن يمكنك استخدام أي نظام إدارة قواعد بيانات آخر. لقد كتبت بمزيد من التفاصيل حول التفاعل مع قواعد البيانات بلغة R في هذا المقال.
لكي يطلب الروبوت منك البيانات وينتظر منك إدخال أي معلومات، ستحتاج إلى تسجيل الحالة الحالية للحوار. أفضل طريقة للقيام بذلك هي استخدام نوع ما من قواعد البيانات المضمنة، مثل SQLite.
أولئك. المنطق سيكون على النحو التالي. نطلق على طريقة الروبوت، ويطلب الروبوت منا بعض المعلومات بشكل متسلسل، وفي كل خطوة ينتظر إدخال هذه المعلومات ويمكنه التحقق منها.
سنكتب أبسط روبوت ممكن، أولاً سيسألك عن اسمك، ثم عمرك، وسيحفظ البيانات المستلمة في قاعدة البيانات. عند السؤال عن العمر سيتم التأكد من أن البيانات المدخلة هي رقم وليست نصية.
مثل هذا الحوار البسيط سيكون له ثلاث حالات فقط:
البداية هي الحالة الطبيعية للروبوت، حيث لا يتوقع منك أي معلومات
wait_name - الحالة التي ينتظر فيها الروبوت إدخال الاسم
wait_age هي الحالة التي ينتظر فيها الروبوت إدخال عمرك، أي عدد السنوات الكاملة.
عملية بناء البوت
خلال هذه المقالة، سنقوم ببناء الروبوت خطوة بخطوة، ويمكن تصوير العملية برمتها بشكل تخطيطي على النحو التالي:
نقوم بإنشاء تكوين روبوت حيث سنقوم بتخزين بعض الإعدادات. في حالتنا، رمز الروبوت، والمسار إلى ملف قاعدة البيانات.
نقوم بإنشاء متغير بيئة يتم فيه تخزين المسار إلى المشروع مع الروبوت.
نقوم بإنشاء قاعدة البيانات نفسها، وعدد من الوظائف حتى يتمكن الروبوت من التفاعل معها.
نكتب أساليب الروبوت، أي. الوظائف التي سوف تؤديها.
إضافة مرشحات الرسائل. بمساعدة الروبوت، سيتمكن الروبوت من الوصول إلى الطرق الضرورية، اعتمادًا على الحالة الحالية للدردشة.
في التكوين نكتب رمز الروبوت والمسار إلى قاعدة البيانات، أي. إلى ملف bot.db، وسنقوم بإنشاء الملف نفسه في الخطوة التالية.
بالنسبة للروبوتات الأكثر تعقيدًا، يمكنك إنشاء تكوينات أكثر تعقيدًا، بالإضافة إلى أنه ليس من الضروري كتابة تكوين ini، يمكنك استخدام أي تنسيق آخر بما في ذلك JSON.
إنشاء متغير البيئة
على كل جهاز كمبيوتر، يمكن وضع المجلد الذي يحتوي على مشروع الروبوت في أدلة مختلفة وعلى محركات أقراص مختلفة، لذلك في التعليمات البرمجية سيتم تعيين المسار إلى مجلد المشروع عبر متغير البيئة TG_BOT_PATH.
هناك عدة طرق لإنشاء متغير بيئة، أبسطها هو كتابته في ملف .رينفيرون.
يمكنك إنشاء هذا الملف أو تحريره باستخدام الأمر file.edit(path.expand(file.path("~", ".Renviron"))). قم بتنفيذه وأضف سطرًا واحدًا إلى الملف:
TG_BOT_PATH=C:/ПУТЬ/К/ВАШЕМУ/ПРОЕКТУ
التالي احفظ الملف .رينفيرون وأعد تشغيل RStudio.
إنشاء قاعدة بيانات
والخطوة التالية هي إنشاء قاعدة بيانات. سنحتاج إلى جدولين:
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}';
أولئك. في مجال الجداول لدينا معرف الدردشة لديه قيد التفرد وهو المفتاح الأساسي للجداول. في البداية، نحاول إضافة معلومات إلى الجدول، ونحصل على خطأ إذا كانت بيانات الدردشة الحالية موجودة بالفعل، وفي هذه الحالة نقوم ببساطة بتحديث المعلومات الخاصة بهذه الدردشة.
بعد ذلك، سوف نستخدم هذه الوظائف في أساليب ومرشحات الروبوت.
أساليب البوت
الخطوة التالية في بناء الروبوت الخاص بنا هي إنشاء الأساليب. إذا قمت بتنزيل المشروع من 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، يرحب بك الروبوت، ويكتب الاسم المستلم في قاعدة البيانات، ويحول الدردشة إلى الحالة wait_age.
في هذه المرحلة، يتوقع منك الروبوت أن تدخل عمرك. ترسل عمرك، ويتحقق الروبوت من الرسالة، وإذا أرسلت بعض الرسائل النصية بدلاً من الرقم، فسوف يقول: Ты ввёл некорректные данные, введи число، وسوف ينتظرك لإعادة إدخال بياناتك. إذا قمت بإرسال رقم، فسيقوم الروبوت بالإبلاغ عن قبوله لعمرك، وكتابة البيانات المستلمة إلى قاعدة البيانات، والإبلاغ عن جميع البيانات الواردة منك وإعادة حالة الدردشة إلى وضعها الأصلي، أي. الخامس 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()، من أجل طلب الحالة الحالية للدردشة. تتطلب هذه الوظيفة وسيطة واحدة فقط، معرف الدردشة.
الفلتر التالي wait_name يعالج الرسائل عندما تكون الدردشة في حالة wait_name، وبالتالي التصفية wait_age يعالج الرسائل عندما تكون الدردشة في حالة 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)
نقوم أولاً بإنشاء معالجات أوامر تسمح لك بتشغيل طرق لبدء مربع حوار وإعادة تعيينه والاستعلام عن الحالة الحالية.
بعد ذلك، نقوم بإنشاء معالجي رسائل باستخدام المرشحات التي تم إنشاؤها في الخطوة السابقة، ونضيف مرشحًا إليهما !MessageFilters$command، حتى نتمكن من استخدام الأوامر في أي حالة دردشة.
كود إطلاق البوت
الآن أصبح لدينا كل شيء جاهزًا للتشغيل، والرمز الرئيسي لتشغيل الروبوت موجود في الملف بوت.ر.
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()
ونتيجة لذلك، حصلنا على هذا الروبوت:
في أي وقت باستخدام الأمر /state يمكننا الاستعلام عن حالة الدردشة الحالية، واستخدام الأمر /reset أعد الدردشة إلى حالتها الأصلية وابدأ الحوار مرة أخرى.
اختتام
تعرفنا في هذه المقالة على كيفية استخدام قاعدة بيانات داخل الروبوت، وكيفية بناء حوارات منطقية متسلسلة من خلال تسجيل حالة الدردشة.
في هذه الحالة، نظرنا إلى المثال الأكثر بدائية، بحيث يكون من الأسهل عليك فهم فكرة بناء مثل هذه الروبوتات، ولكن في الممارسة العملية، يمكنك بناء حوارات أكثر تعقيدًا.
وفي المقالة التالية من هذه السلسلة، سنتعلم كيفية تقييد حقوق مستخدمي الروبوت في استخدام أساليبه المختلفة.