Глава 5 Управление правами пользователей бота

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

5.1 Введение

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

Т.е. бот может использоваться в многопользовательском режиме. При этом, вам может понадобиться разграничить права на использование бота. Например, некоторые пользователи смогут использовать абсолютно все возможности бота, а некоторым вы предоставите ограниченные права.

Ограничить права можно не только на уровне пользователя, но и на уровне отдельных чатов.

Мы создадим простейшего бота, у которого в арсенале будет всего 2 метода:

  • say_hello - команда приветствия
  • what_time - команда, по которой бот сообщает текущую дату и время
library(telegram.bot)

# создаём экземпляр класса Updater
updater <- Updater('ТОКЕН ВАШЕГО БОТА')

# Пишем метод для приветсвия
## команда приветствия
say_hello <- function(bot, update) {
  
  # Имя пользователя с которым надо поздароваться
  user_name <- update$effective_user()$first_name
  
  # Отправка сообщения
  bot$sendMessage(update$from_chat_id(), 
                  text = paste0("Моё почтение, ", user_name, "!"),
                  parse_mode = "Markdown",
                  reply_to_message_id = update$effective_message()$message_id)

}

## команда по которой бот возвращает системную дату и время
what_time <- function(bot, update) {
  
  # Запрашиваем текущее время
  cur_time <- as.character(Sys.time())
  
  # Отправка сообщения
  bot$sendMessage(update$from_chat_id(), 
                  text = paste0("Текущее время, ", cur_time),
                                parse_mode = "Markdown",
                                reply_to_message_id = update$effective_message()$message_id)
                  
  
}

# обработчики
h_hello <- CommandHandler('say_hello', say_hello)
h_time  <- CommandHandler('what_time', what_time)

# добавляем обработчики в диспетчер
updater <- updater + h_hello + h_time

# запускаем бота 
updater$start_polling()

Запустите приведённый выше пример кода, предварительно заменив ‘ТОКЕН ВАШЕГО БОТА’ на реальный токен, который вы получили при создании бота через BotFather (о создании бота я рассказывал в первой статье).

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

5.2 Ограничиваем права пользователя с помощью фильтров сообщений

Из предыдущих публикаций мы уже разобрались с тем, что такое фильтры сообщений. Но ранее мы использовали их в основном для, того, что бы вызывать какие-то методы бота через обычное сообщение, а не команду и для прослушиваний сообщений в определённом состоянии чата.

В этот раз мы научимся с помощью фильтров ограничивать возможности по использованию методов бота, на разных уровнях.

5.2.1 Ограничиваем права на уровне имени пользователя

Для создания собственных фильтров вам необходимо с помощью функции BaseFilter() добавить новый элемент в объект MessageFilters. Более подробно об этом я рассказывал во второй статье из данной серии.

В анонимную функцию, которую вы прописываете внутри BaseFilter() передаётся всего один аргумент - message. Это сообщение которое вы, или другой пользователей отправляет боту, со всеми его метаданными. Данный объект имеет следующую структуру:

$message_id
[1] 1174

$from
$from$id
[1] 194336771

$from$is_bot
[1] FALSE

$from$first_name
[1] "Alexey"

$from$last_name
[1] "Seleznev"

$from$username
[1] "AlexeySeleznev"

$from$language_code
[1] "ru"


$chat
$chat$id
[1] 194336771

$chat$first_name
[1] "Alexey"

$chat$last_name
[1] "Seleznev"

$chat$username
[1] "AlexeySeleznev"

$chat$type
[1] "private"


$date
[1] 1601295189

$text
[1] "отправленный пользователем текст"

$chat_id
[1] 194336771

$from_user
[1] 194336771

Более подробно описание ответа и всех его компонентов можно узнать из официальной документации:

  • User - Этот объект представляет бота или пользователя Telegram.
  • Chat - Этот объект представляет собой чат.
  • Message - Этот объект представляет собой сообщение.

Соответственно вы можете разделять права пользователей бота, и его методов используя любую, отправляемую с сообщением информацию, т.е. любые элементы объекта message. Для того, что бы ограничить круг пользователей которые могут использовать методы вашего бота достаточно создать фильтр:

## список пользователей, с полными правами
MessageFilters$admins <- BaseFilter(
  function(message) {
    
    # проверяем от кого отправлено сообщение
    message$effective_user()$username %in% c('AlexeySeleznev', 'user1', 'user2')
    
 }
)

Где c('AlexeySeleznev', 'user1', 'user2') - вектор, с именами пользователей, которые могут использовать все функции бота. Далее этот фильтр мы используем при создании обработчиков.

## фильтр для вызова команды say_hello
MessageFilters$say_hello <- BaseFilter(
  function(message) {
    
    # проверяем от кого отправлено сообщение
    message$text == '/say_hallo'
    
  }
)

## фильтр для вызова команды what_time
MessageFilters$what_time <- BaseFilter(
  function(message) {
    
    # проверяем от кого отправлено сообщение
    message$text == '/what_time'
    
  }
)


# обработчики
h_hello <- MessageHandler(say_hello, MessageFilters$admins & MessageFilters$say_hello)
h_time  <- MessageHandler(what_time, MessageFilters$admins & MessageFilters$what_time)

Теперь нашего бота могут использовать пользователи с логинами AlexeySeleznev, user1, user2. На сообщения отправленные боту другими пользователями он никак не будет реагировать.

Изменённый код нашего бота на данный момент выглядит вот так:

library(telegram.bot)


# создаём экземпляр класса Updater
updater <- Updater('ТОКЕН ВАШЕГО БОТА')

# Пишем метод для приветсвия
## команда приветствия
say_hello <- function(bot, update) {
  
  # Имя пользователя с которым надо поздароваться
  user_name <- update$effective_user()$first_name
  
  # Отправка сообщения
  bot$sendMessage(update$from_chat_id(), 
                  text = paste0("Моё почтение, ", user_name, "!"),
                  parse_mode = "Markdown",
                  reply_to_message_id = update$effective_message()$message_id)

}

## команда по которой бот возвращает системную дату и время
what_time <- function(bot, update) {
  
  # Запрашиваем текущее время
  cur_time <- as.character(Sys.time())
  
  # Отправка сообщения
  bot$sendMessage(update$from_chat_id(), 
                  text = paste0("Текущее время, ", cur_time),
                                parse_mode = "Markdown",
                                reply_to_message_id = update$effective_message()$message_id)
                  
  
}

# фильтры
## список пользователей, с полными правами
MessageFilters$admins <- BaseFilter(
  function(message) {
    
    # проверяем от кого отправлено сообщение
    message$from$username %in% c('AlexeySeleznev', 'user1', 'user2')
    
 }
)

## фильтр для вызова команды say_hello
MessageFilters$say_hello <- BaseFilter(
  function(message) {
    
    # проверяем от кого отправлено сообщение
    message$text == '/say_hallo'
    
  }
)

## фильтр для вызова команды what_time
MessageFilters$what_time <- BaseFilter(
  function(message) {
    
    # проверяем от кого отправлено сообщение
    message$text == '/what_time'
    
  }
)


# обработчики
h_hello <- MessageHandler(say_hello, MessageFilters$admins & MessageFilters$say_hello)
h_time  <- MessageHandler(what_time, MessageFilters$admins & MessageFilters$what_time)

# добавляем обработчики в диспетчер
updater <- updater + h_hello + h_time

# запускаем бота 
updater$start_polling()

5.2.2 Ограничиваем права на уровне чата

Тем же способом мы можем создать фильтр не только по списку пользователей, но и по конкретному чату. Для этого достаточно создать ещё один фильтр:

## список чатов в которых разрешено использовать бота
MessageFilters$chats <- BaseFilter(
  function(message) {
    
    # проверяем от кого отправлено сообщение
    message$chat_id %in% c(194336771, 0, 1)
    
  }
)


## фильтр для вызова команды say_hello
MessageFilters$say_hello <- BaseFilter(
  function(message) {
    
    # проверяем от кого отправлено сообщение
    message$text == '/say_hallo'
    
  }
)

## фильтр для вызова команды what_time
MessageFilters$what_time <- BaseFilter(
  function(message) {
    
    # проверяем от кого отправлено сообщение
    message$text == '/what_time'
    
  }
)


# обработчики
h_hello <- MessageHandler(say_hello, MessageFilters$admins & MessageFilters$chats & MessageFilters$say_hello)
h_time  <- MessageHandler(what_time, MessageFilters$admins & MessageFilters$chats & MessageFilters$what_time)

5.3 Ограничиваем права пользователя внутри кода методов

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

5.3.1 Ограничиваем права на уровне имени пользователя

Давайте создадим функцию, которая будет принимать данные для проверки имени пользователя.

# функция для проверки прав пользователя
bot_check_usernames <- 
  function(admins, username) {
  
   username %in% admins 
  
}

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

Далее в каждый метод, который мы хотим защитить с помощью конструкции IF добавляем проверку, разрешено пользователю использовать данный метод или нет. В случае если у пользователя нет таких прав бот будет сообщать об этом в чате.

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

library(telegram.bot)

# создаём экземпляр класса Updater
updater <- Updater('ТОКЕН ВАШЕГО БОТА')

# Пишем метод для приветсвия
## команда приветствия
say_hello <- function(bot, update) {
  
  # Имя пользователя с которым надо поздароваться
  user_name <- update$effective_user()$username
  
  # проверяем разрешено ли использовать данному пользователю этот метод
  if ( bot_check_usernames(c('AlexeySeleznev', 'user1', 'user2'), user_name) ) {
    
    # Отправка сообщения
    bot$sendMessage(update$from_chat_id(), 
                    text = paste0("Моё почтение, ", user_name, "!"),
                    parse_mode = "Markdown",
                    reply_to_message_id = update$effective_message()$message_id)
      
  } else {
    
    # Отправка сообщения
    bot$sendMessage(update$from_chat_id(), 
                    text = paste0("У вас нет прав для использования этого метода!"),
                    parse_mode = "Markdown",
                    reply_to_message_id = update$effective_message()$message_id)
    
  }

}

## команда по которой бот возвращает системную дату и время
what_time <- function(bot, update) {
  
  # проверяем разрешено ли использовать данному пользователю этот метод
  if ( bot_check_usernames(c('user1', 'user2'), update$effective_user()$username) ) {
      
    # Запрашиваем текущее время
    cur_time <- as.character(Sys.time())
    
    # Отправка сообщения о том что у пользователя не достаточно прав
    bot$sendMessage(update$message$chat_id, 
                    text = paste0("Текущее время, ", cur_time),
                                  parse_mode = "Markdown",
                                  reply_to_message_id = update$effective_message()$message_id)
  } else {
    
    # Отправка сообщения о том что у пользователя не достаточно прав
    bot$sendMessage(update$message$chat_id, 
                    text = paste0("У вас нет прав для использования этого метода!"),
                    parse_mode = "Markdown",
                    reply_to_message_id = update$effective_message()$message_id)
    
  }
                  
  
}

# обработчики
h_hello <- CommandHandler('say_hello', say_hello)
h_time  <- CommandHandler('what_time', what_time)

# добавляем обработчики в диспетчер
updater <- updater + h_hello + h_time

# запускаем бота 
updater$start_polling()

Результат будет следующим:

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

5.3.2 Ограничиваем права на уровне чата

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

Напишем функцию, которая будет проверять входит ли текущий чат в список разрешенных.

bot_check_chat_id <- 
  function(allowed_chats, current_chat) {

     current_chat %in% allowed_chats 
    
}

Далее используем эту функции внутри наших методов:

library(telegram.bot)


# создаём экземпляр класса Updater
updater <- Updater('ТОКЕН ВАШЕГО БОТА')

# Пишем метод для приветсвия
## команда приветствия
say_hello <- function(bot, update) {
  
  # Имя пользователя с которым надо поздароваться
  user_name <- update$message$from$username
  
  # проверяем разрешено ли использовать данному пользователю этот метод
  if ( bot_check_usernames(c('AlexeySeleznev', 'user1', 'user2'), user_name) 
       &
       bot_check_chat_id(c(194336771, 1, 2), update$from_chat_id())) {
    
    # Отправка сообщения
    bot$sendMessage(update$from_chat_id(), 
                    text = paste0("Моё почтение, ", user_name, "!"),
                    parse_mode = "Markdown",
                    reply_to_message_id = update$effective_message()$message_id)
      
  } else {
    
    # Отправка сообщения
    bot$sendMessage(update$from_chat_id(), 
                    text = paste0("У вас нет прав для использования этого метода!"),
                    parse_mode = "Markdown",
                    reply_to_message_id = update$effective_message()$message_id)
    
  }

}

## команда по которой бот возвращает системную дату и время
what_time <- function(bot, update) {
  
  # проверяем разрешено ли использовать данному пользователю этот метод
  if ( bot_check_usernames(c('AlexeySeleznev', 'user1', 'user2'), update$message$from$username)
       &
       bot_check_chat_id(c(194336771, 1, 2), update$message$chat_id)) {
      
    # Запрашиваем текущее время
    cur_time <- as.character(Sys.time())
    
    # Отправка сообщения о том что у пользователя не достаточно прав
    bot$sendMessage(update$from_chat_id(), 
                    text = paste0("Текущее время, ", cur_time),
                                  parse_mode = "Markdown",
                                  reply_to_message_id = update$effective_message()$message_id)
  } else {
    
    # Отправка сообщения о том что у пользователя не достаточно прав
    bot$sendMessage(update$from_chat_id(), 
                    text = paste0("У вас нет прав для использования этого метода!"),
                    parse_mode = "Markdown",
                    reply_to_message_id = update$effective_message()$message_id)
    
  }
                  
  
}

# обработчики
h_hello <- CommandHandler('say_hello', say_hello)
h_time  <- CommandHandler('what_time', what_time)

# добавляем обработчики в диспетчер
updater <- updater + h_hello + h_time

# запускаем бота 
updater$start_polling()

5.4 Заключение

На этом серия статей о построении telegram ботов завершается. Я старался структурировать и подавать материал достаточно сжато, убрав всю воду, но при этом сделать так, что бы материал был вам понятен. Очень надеюсь на то, что мне это удалось.

Успехов вам в ботостроении. В комментариях можете написать примеры ваших ботов, и как вы их на практике используете.

5.5 Тесты и задания

5.5.1 Тесты

Для закрепления материла рекомендую вам пройти тест доступный по ссылке.

5.5.2 Задания

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