Всем привет, мои дорогие друзья.
Итак, бот уже работает и отправляет уведомления о новых статьях. Если вы еще не используете его — вот ссылка: Javarush Telegram Bot.
Ну а сегодня мы поговорим о добавлении команд, которые работают только для админов. Одна из таких команд — статистика и доска помощи. Зачем это нужно? На данный момент интереснее описать работу с аннотациями в рамках этой задачи, чем реальную необходимость в этом.
Ну а раз уж мы идем в команду статистики, то можно ее расширить и сделать более информативной.
Уже после MVP можно будет вернуть статистику для авторов, например. Но об этом уже потом…)
Сама аннотация будет такой:
Теперь-то все должно заработать... Или нет? Нет, конечно))
Нужно научить наш CommandContainer правильно выдавать результат. Для этого обновим метод retrieveCommand, который выдает команду для запуска в зависимости от того, что ему передают.
В качестве идентификатора админа будем использовать его username в Телеграме. Он уникален и более легко читаем, чем chat_id.
Как его получить? Он находится в объекте Update, который приходит с сообщением:
И если продолжить писать, он отфильтрует и покажет релевантные варианты:
Классный функционал, да? Вот я такое же хочу сделать и у нас. Буду я делать так, как умею — через приложение Телеграма.
Я знаю, что это можно сделать программно. Но я не умею. В рамках этой серии статей это и не нужно. Если кто-то умеет это делать — пишите мне, будем добавлять. Я с удовольствием приму любую помощь в этом. Я как-то читал, что это можно сделать через паттерн команда, который у нас работает.
Сейчас я покажу, как я это умею: нам нужно найти в Телеграме BotFather, выбрать у него того бота, которого хотим настроить. Далее выбрать редактирование бота и секцию про команды.
Сейчас я покажу все на примере моего тестового бота для Javarush.
У BotFather пишем команду:
/mybots
Далее, выбираем нужного нам бота, в моем случае это будет test_javarush_community_bot:
Как видно из перечня кнопок здесь можно и токен посмотреть, и удалить бота, и передать его кому-то другому. Нас интересует редактирование бота, поэтому выбираем Edit Bot:
И здесь выбираем Edit Commands:
Нам нужно просто предоставить в конкретном формате сообщение, и оно будет записано как команды. Или если хотим убрать их все, написать /empty. Для этого дела создам в корне проекта файл SET_UP_COMMANDS_BOT_FATHER, в котором напишу все наши команды, чтобы было легко восстановить или обновить в случае чего.
SET_UP_COMMANDS_BOT_FATHER:
Как это обычно водится, с первого раза не получилось. После нескольких минут раздумий, передал все команды в нижнем регистре, а не в CamelCase как было до этого, и все прошло успешно.
Обновляем в нашем файле:
SET_UP_COMMANDS_BOT_FATHER:
Посмотрите, какая теперь красота!
Хотел в рамках этой статьи еще и расширить функционал статистики, но материал и так вышел объемным и по смыслу, и по содержанию. Поэтому перенесем это на следующий раз.
То есть, задача JRTB-10 сделана не полностью: мы ее доделаем в рамках следующей статьи. Вместе с тем все изменения, которые уже есть, я добавлю в основного бота.
Хотите поддержать автора, но не знаете как? Это очень просто — подписывайтесь на мой тг-канал, на мой GitHub аккаунт и пишите здесь в статьях свое мнение о них. Эта обратная связь для меня важна, так я понимаю что их читают и ими интересуются.лайк - подписка - колокольчик, звезду нашему проекту, комментарий и оценить статью!
До встречи в следующей статье!![“Java-проект от А до Я”: Добавляем Spring Scheduler - 2]()

Разбираемся с добавлением админов и команд для них
Начинаем нашу работу с того, что обновляем main ветку и ее основе создаем новую — STEP_9_JRTB-10. Чтобы разобраться, какая команда относится к админам, а какая — ко всем, нужно промаркировать команду. Для этого создадим аннотацию. Что это значит? Такого мы еще не делали. Это можно выбрать при создании класса в IDEA. Сейчас покажу. В пакете command создадим новый пакет annotation и в нем — аннотацию AdminCommand:
package com.github.javarushcommunity.jrtb.command.annotation;
import com.github.javarushcommunity.jrtb.command.Command;
import java.lang.annotation.Retention;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* Mark if {@link Command} can be viewed only by admins.
*/
@Retention(RUNTIME)
public @interface AdminCommand {
}
Здесь нам больше ничего не нужно.
Далее добавляем ее нашей команде StatCommand:
update.getMessage().getFrom().getUserName()
Суммируя все сказанное выше, обновим метод CommandContainer#retrieveCommand:
public Command retrieveCommand(String commandIdentifier, String username) {
Command orDefault = commandMap.getOrDefault(commandIdentifier, unknownCommand);
if (isAdminCommand(orDefault)) {
if (admins.contains(username)) {
return orDefault;
} else {
return unknownCommand;
}
}
return orDefault;
}
private boolean isAdminCommand(Command command) {
return nonNull(command.getClass().getAnnotation(AdminCommand.class));
}
Как видно здесь, я добавил метод isAdminCommand, который проверяет, есть ли аннотация AdminCommand в предоставляемой команде. И если это команда, предназначенная только для админов, мы проверяем, есть ли этот username у нас в коллекции доступных админов.
К слову, вот он, ООП, во все красе: мы просто передаем интерфейс, который может быть любой реализацией. Но передать можем только тот класс, что реализовал интерфейс Command.
И вроде все понятно, кроме одного: откуда взялись админы? Сейчас покажу.
Пока что я хочу передавать админов как переменную окружения, чтобы ее можно было легко настраивать. В этой переменной будет строка, в которой через запятую указаны все username телеграм пользователей, которые будут админами.
Для этого добавим в application.properties:
bot.admins: robeskman,romankh3
В CommandContainer в конструкторе передадим коллекцию админов и проинициализируем ее:
public class CommandContainer {
private final ImmutableMap<String, Command> commandMap;
private final Command unknownCommand;
private final List<String> admins;
public CommandContainer(SendBotMessageService sendBotMessageService, TelegramUserService telegramUserService,
JavaRushGroupClient javaRushGroupClient, GroupSubService groupSubService,
List<String> admins) {
this.admins = admins;
А уже в JavaRushTelegramBot будет вся магия по получению коллекции из строки в пропертях:
@Autowired
public JavarushTelegramBot(TelegramUserService telegramUserService, JavaRushGroupClient groupClient, GroupSubService groupSubService,
@Value("#{'${bot.admins}'.split(',')}") List<String> admins) {
this.commandContainer =
new CommandContainer(new SendBotMessageServiceImpl(this),
telegramUserService, groupClient, groupSubService, admins);
}
Как видно из конструктора выше, опять используем аннотацию Value, в которую передаем логику по созданию коллекции. И все, таким образом добавление админа закончилось.
Теперь, когда не админ захочет получить данные по статистике бота, он получит такой ответ:
Не понимаю тебя 😟, напиши /help чтобы узнать что я понимаю.
Таким образом мы разграничили роли для команд бота.Добавляем команду help для админов
Далее логично было бы создать и отдельную команду help для админов. В будущем эта часть может значительно вырасти. Добавляем значение админского help в CommandName:
ADMIN_HELP("/ahelp")
Создаем класс AdminHelpCommand в пакете command:
package com.github.javarushcommunity.jrtb.command;
import com.github.javarushcommunity.jrtb.service.SendBotMessageService;
import org.telegram.telegrambots.meta.api.objects.Update;
import static com.github.javarushcommunity.jrtb.command.CommandName.STAT;
import static java.lang.String.format;
/**
* Admin Help {@link Command}.
*/
public class AdminHelpCommand implements Command {
public static final String ADMIN_HELP_MESSAGE = format("✨<b>Доступные команды админа</b>✨\n\n"
+ "<b>Получить статистику</b>\n"
+ "%s - статистика бота\n",
STAT.getCommandName());
private final SendBotMessageService sendBotMessageService;
public AdminHelpCommand(SendBotMessageService sendBotMessageService) {
this.sendBotMessageService = sendBotMessageService;
}
@Override
public void execute(Update update) {
sendBotMessageService.sendMessage(update.getMessage().getChatId().toString(), ADMIN_HELP_MESSAGE);
}
}
Пока что он очень простой. В будущем может вполне себе неплохо разрастись.
Для этой команды — тест из нашего шаблона:
package com.github.javarushcommunity.jrtb.command;
import org.junit.jupiter.api.DisplayName;
import static com.github.javarushcommunity.jrtb.command.AdminHelpCommand.ADMIN_HELP_MESSAGE;
import static com.github.javarushcommunity.jrtb.command.CommandName.ADMIN_HELP;
@DisplayName("Unit-level testing for AdminHelpCommand")
public class AdminHelpCommandTest extends AbstractCommandTest {
@Override
String getCommandName() {
return ADMIN_HELP.getCommandName();
}
@Override
String getCommandMessage() {
return ADMIN_HELP_MESSAGE;
}
@Override
Command getCommand() {
return new AdminHelpCommand(sendBotMessageService);
}
}
Разумеется, команду нужно добавить в CommandContainer в нашу мапу:
.put(ADMIN_HELP.getCommandName(), new AdminHelpCommand(sendBotMessageService))
Добавляем описание команд в бота
У телеграм-ботов есть еще интересная особенность: можно добавить значения и описание команд, которые он принимает, чтобы пользователю было легче использовать команды. Как это выглядит? За примером пойдем к BotFather — самому главному боту Телеграма. Если начать писать сообщение со слеша /, бот предложит варианты:





start - начать/восстановить работу с ботом
stop - приостановить работу с ботом
addGroupSub - подписаться на группу статей
deleteGroupSub - отписаться от группы статей
listGroupSub - список групп, на которые подписан
help - получить помощь в работе со мной
Ясно, что админские команды мы не выносим сюда. О них должны знать только админы.
Берем это сообщение и передаем его BotFather:
start - начать/восстановить работу с ботом
stop - приостановить работу с ботом
addgroupsub - подписаться на группу статей
deletegroupsub - отписаться от группы статей
listgroupsub - список групп, на которые подписан
help - получить помощь в работе со мной
Теперь можно пойти в нашего бота и посмореть, подтянулась ли подгрузка команд автоматически:
Выводы
Просуммируем то, что мы сегодня прошли:- Обсудили, как добавлять собственную аннотацию, как ее можно использовать в качестве маркера для разграничения ролей в командах. К слову, подобным образом можно было сделать и при помощи интерфейса. Точно так же создали бы интерфейс-маркер и потом проверяли бы, реализует этот интерфейс или нет объект, который приходит.
- Добавили Help команду для админов. Как по мне — также важная часть в развитии этого бота.
- Обсудили, как добавить описание и всплывание команд при написании их в боте. Интересная фича, определенно стоило ее добавить.

ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ