كتابة روبوت برقية بلغة R (الجزء 4): بناء حوار منطقي ومتسق مع الروبوت

إذا كنت قد قرأت بالفعل السابق ثلاث مقالات من هذه السلسلة، فأنت تعرف بالفعل كيفية كتابة روبوتات برقية كاملة باستخدام لوحة المفاتيح.

في هذه المقالة، سوف نتعلم كيفية كتابة روبوت يحافظ على حوار ثابت. أولئك. سيطرح عليك الروبوت أسئلة وينتظر منك إدخال بعض المعلومات. اعتمادًا على البيانات التي تدخلها، سيقوم الروبوت بتنفيذ بعض الإجراءات.

سنتعلم في هذه المقالة أيضًا كيفية استخدام قاعدة بيانات تحت غطاء الروبوت، في مثالنا سيكون SQLite، ولكن يمكنك استخدام أي نظام إدارة قواعد بيانات آخر. لقد كتبت بمزيد من التفاصيل حول التفاعل مع قواعد البيانات بلغة R في هذا المقال.

كتابة روبوت برقية بلغة R (الجزء 4): بناء حوار منطقي ومتسق مع الروبوت

جميع المقالات من سلسلة "كتابة بوت برقية باللغة R"

  1. نقوم بإنشاء روبوت ونستخدمه لإرسال الرسائل في التليجرام
  2. أضف دعم الأوامر ومرشحات الرسائل إلى الروبوت
  3. كيفية إضافة دعم لوحة المفاتيح إلى الروبوت
  4. بناء حوار متسق ومنطقي مع الروبوت

محتوى

إذا كنت مهتمًا بتحليل البيانات ، فقد تكون مهتمًا ببرنامجي تيليجرام и موقع YouTube القنوات. معظم محتوياته مخصص للغة R.

  1. مقدمة
  2. عملية بناء البوت
  3. هيكل المشروع بوت
  4. تكوين بوت
  5. إنشاء متغير البيئة
  6. إنشاء قاعدة بيانات
  7. وظائف الكتابة للعمل مع قاعدة البيانات
  8. أساليب البوت
  9. مرشحات الرسائل
  10. معالجات
  11. كود إطلاق البوت
  12. اختتام

مقدمة

لكي يطلب الروبوت منك البيانات وينتظر منك إدخال أي معلومات، ستحتاج إلى تسجيل الحالة الحالية للحوار. أفضل طريقة للقيام بذلك هي استخدام نوع ما من قواعد البيانات المضمنة، مثل SQLite.

أولئك. المنطق سيكون على النحو التالي. نطلق على طريقة الروبوت، ويطلب الروبوت منا بعض المعلومات بشكل متسلسل، وفي كل خطوة ينتظر إدخال هذه المعلومات ويمكنه التحقق منها.

سنكتب أبسط روبوت ممكن، أولاً سيسألك عن اسمك، ثم عمرك، وسيحفظ البيانات المستلمة في قاعدة البيانات. عند السؤال عن العمر سيتم التأكد من أن البيانات المدخلة هي رقم وليست نصية.

مثل هذا الحوار البسيط سيكون له ثلاث حالات فقط:

  1. البداية هي الحالة الطبيعية للروبوت، حيث لا يتوقع منك أي معلومات
  2. wait_name - الحالة التي ينتظر فيها الروبوت إدخال الاسم
  3. wait_age هي الحالة التي ينتظر فيها الروبوت إدخال عمرك، أي عدد السنوات الكاملة.

عملية بناء البوت

خلال هذه المقالة، سنقوم ببناء الروبوت خطوة بخطوة، ويمكن تصوير العملية برمتها بشكل تخطيطي على النحو التالي:
كتابة روبوت برقية بلغة R (الجزء 4): بناء حوار منطقي ومتسق مع الروبوت

  1. نقوم بإنشاء تكوين روبوت حيث سنقوم بتخزين بعض الإعدادات. في حالتنا، رمز الروبوت، والمسار إلى ملف قاعدة البيانات.
  2. نقوم بإنشاء متغير بيئة يتم فيه تخزين المسار إلى المشروع مع الروبوت.
  3. نقوم بإنشاء قاعدة البيانات نفسها، وعدد من الوظائف حتى يتمكن الروبوت من التفاعل معها.
  4. نكتب أساليب الروبوت، أي. الوظائف التي سوف تؤديها.
  5. إضافة مرشحات الرسائل. بمساعدة الروبوت، سيتمكن الروبوت من الوصول إلى الطرق الضرورية، اعتمادًا على الحالة الحالية للدردشة.
  6. نضيف معالجات تربط الأوامر والرسائل بطرق الروبوت الضرورية.
  7. دعونا نطلق الروبوت.

هيكل المشروع بوت

لتسهيل الأمر، سنقوم بتقسيم كود الروبوت الخاص بنا والملفات الأخرى ذات الصلة إلى البنية التالية.

  • بوت.ر — الكود الرئيسي لروبوتنا
  • 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 - قاعدة بيانات بوت

يمكنك عرض مشروع الروبوت بأكمله، أو تحميل من وجهة نظري المستودع على جيثب.

تكوين بوت

سوف نستخدم المعتاد كتكوين ملف ini، النموذج التالي:

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

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

في التكوين نكتب رمز الروبوت والمسار إلى قاعدة البيانات، أي. إلى ملف 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()

ونتيجة لذلك، حصلنا على هذا الروبوت:
كتابة روبوت برقية بلغة R (الجزء 4): بناء حوار منطقي ومتسق مع الروبوت

في أي وقت باستخدام الأمر /state يمكننا الاستعلام عن حالة الدردشة الحالية، واستخدام الأمر /reset أعد الدردشة إلى حالتها الأصلية وابدأ الحوار مرة أخرى.

اختتام

تعرفنا في هذه المقالة على كيفية استخدام قاعدة بيانات داخل الروبوت، وكيفية بناء حوارات منطقية متسلسلة من خلال تسجيل حالة الدردشة.

في هذه الحالة، نظرنا إلى المثال الأكثر بدائية، بحيث يكون من الأسهل عليك فهم فكرة بناء مثل هذه الروبوتات، ولكن في الممارسة العملية، يمكنك بناء حوارات أكثر تعقيدًا.

وفي المقالة التالية من هذه السلسلة، سنتعلم كيفية تقييد حقوق مستخدمي الروبوت في استخدام أساليبه المختلفة.

المصدر: www.habr.com

إضافة تعليق