Глава 6 Повышаем стабильность работы бота

К этому моменту вы знаете уже достаточно для того, что бы решить значительную часть своих задач по ботостроению. Простой бот будет работать достаточно стабильно, но всё равно иногда сервера API Telegram могут давать сбой. Даже если в вашем коде нет ошибок, и пользователи используют его правильно, иногда он может падать.

В этой главе мы поговорим о том, как повысить работоспособность бота за счёт отлавливания и обработки ошибок пуллинга.

6.1 Конструкция tryCatch()

Повысить работоспособность вашего бота поможет конструкция tryCatch(). Данная конструкция имеет следующий синтаксис:

tryCatch(expr = {
  
    ~ Тут код который будет выполняться ~
  
}, 
  error = function(err) {
    
    ~ код который будет выполняться в случае возникновения ошибки в блоке expr ~
    
  }, 
  finally = {
    
    ~ Код который будет выполняться в любом случае, не зависимо от того закончилось выражение expr ошибкой или нет ~
    
  })

6.2 Логика работы конструкции tryCatch()

Из описанного синтаксиса понятно, что вам необходимо завернуть выражение в фигурные скобки в аргументе expr. Это выражение будет выполняться либо до тех пор, пока не встретится ошибка, либо если ошибки нет, оно будет выполнено полностью.

Если в выражении переданном в expr встречается ошибка, то конструкция tryCath() запустит анонимную функцию, которую вы передали в блоке error.

В любом случае, не зависимо от того, встретилась в выражении expr ошибка или нет, в завершении выполнения будет выполнен код, переданный в аргумент finally.

Если вы хотите более подробно узнать про конструкцию tryCatch() посмотрите этот видео урок.

6.3 Используем tryCatch() внутри бота

По большому счёту вы можете использовать tryCatch() внутри каждой функции вашего бота. Но можно убить всех зайцев одним выстрелом.

В разработке ботов слабым местом является пуллинг, т.е. метод updater$start_polling(). Пуллинг - это бесконечный цикл, именно он выполняется всё время работы бота, и даёт сбой если пользователь неправильно использовал бота, или API Telegram не отправил вам ответ. Соответственно если завернуть пуллинг в tryCatch(), и перезапускать вашего бота в бота в блоке finally то при любой ошибке он будет самостоятельно перезапускаться.

Перед перезапуском бота не забывайте очистить его апдейты, что бы избавиться от ошибки, которая вызвала падение бота.

Выглядеть такой пуллинг будет следующим образом:

tryCatch(
  
  # запускаем пуллинг
  expr = updater$start_polling(), 
  
  # действия при ошибке пуллинга
  error = function(err) {
    
    # бот для оповещения
    bot <- Bot(token = bot_token("Токен вашего бота"))
    
    # чат для оповещения
    chat_id <- "Идентификатор чата в который необходимо отправить сообщение"
    
    # сообщение
    msg <- str_glue("*Бот упал*: Ошибка (_{err$message}_).")
    
    bot$sendMessage(chat_id = chat_id, 
                    text = msg,
                    parse_mode = 'Markdown')
    
    # очищаем полученный апдейт бота, который вызвал ошибку
    updater$bot$clean_updates()
    
    # информация о том, что бот будет перезапущен
    bot$sendMessage(chat_id = chat_id, 
                    text = str_glue('*Перезапускаю бота* в {Sys.time()}'),
                    parse_mode = 'Markdown')

    
  }, 
  # действия которые будут выполненны в любом случае
  finally = {
    
    # останавливаем пулинг
    updater$stop_polling()
        
    # перезапускаем скрипт бота
    source('C:\\telegram_bot\\my_bot.R') 

  }
)

В приведённом выше коде вам необходимо подставить токен созданного вами бота, и указать ID чата, в который бот будет отправлять уведомление о падении пуллинга.

В блок expr мы завернули процесс пуллинга, таким образом он постоянно контролируется конструкцией tryCatch.

Далее в блок error мы передали безымянную функцию, которая принимает всего один аргумент err, т.е. саму ошибку. Сообщение об ошибке мы получаем через err$message, и отправляем в указанный чат. С помощью updater$bot$clean_updates() мы очищаем очередь апдейтов бота, т.к. последний апдейт вызвал ошибку и падение нашего бота.

В блоке finally мы останавливаем пуллинг, и командой source('C:\\telegram_bot\\my_bot.R') занова запускаем скрипт с ботом.

Такая схема позволяет боту очищаться и подниматься при любой ошибке пуллинга.

Очищать апдейты бота с помощью комманды updater$bot$clean_updates() можно так же и при запуске бота, указав эту команду сразу, после инициализации объекта бота.