JavaRush /Java Blog /Random EN /Adding the ability to work as an admin and statistics for...

Adding the ability to work as an admin and statistics for him - "Java project from A to Z"

Published in the Random EN group
Hello everyone, my dear friends. So, the bot is already running and sending notifications about new articles. If you are not using it yet, here is the link: Javarush Telegram Bot . Well, today we will talk about adding commands that work only for admins. One of these commands is statistics and help board. Why is this needed? At the moment, it is more interesting to describe the work with annotations within the framework of this task than the real need for it. "Java project from A to Z": Adding the ability to work as an admin and statistics for him - 1Well, since we are going to the statistics team, we can expand it and make it more informative. After the MVP, it will be possible to return statistics for authors, for example. But more on that later...)

Dealing with adding admins and commands for them

We start our work by updating the main branch and creating a new one based on it - STEP_9_JRTB-10. To figure out which team belongs to admins and which to everyone, you need to mark the team. To do this, we will create an annotation. What does it mean? We haven't done this yet. This can be selected when creating a class in IDEA. I will show you now. Let's create a new annotation package in the command package and the AdminCommand annotation in it: "Java-project from A to Z": Adding the ability to work as an admin and statistics for him.  Part 1 - 2The annotation itself will look like this:
package com.github.codegymcommunity.jrtb.command.annotation;

import com.github.codegymcommunity.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.
public @interface AdminCommand {
We don't need anything else here. Next, we add it to our StatCommand team: "Java-project from A to Z": Adding the ability to work as an admin and statistics for him.  Part 1 - 3Now everything should work ... Or not? No, of course not)) We need to teach our CommandContainer to correctly produce the result. To do this, let's update the retrieveCommand method , which issues a command to run depending on what is passed to it. As an admin identifier, we will use his username in Telegram. It is unique and more readable than chat_id. How to get it? It's in the Update object that comes with the message:
Summarizing all the above, let's update the CommandContainer#retrieveCommand method :
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));
As you can see here, I've added an isAdminCommand method that checks if there is an AdminCommand annotation on the supplied command. And if it's an admin-only command, we check if we have that username in the collection of available admins. By the way, here it is, OOP, in all its glory: we just pass the interface, which can be any implementation. But we can only pass the class that implemented the Command interface . And everything seems to be clear, except for one thing: where did the admins come from? I will show you now. For now, I want to pass admins as an environment variable so that it can be easily configured. This variable will contain a string in which, separated by commas, all usernames of telegrams of users who will be admins are indicated. To do this, add to
bot.admins: robeskman,romankh3
In the CommandContainer in the constructor, we will pass the collection of admins and initialize it:
public class CommandContainer {

   private final ImmutableMap<String, Command> commandMap;
   private final Command unknownCommand;
   private final List<String> admins;

   public CommandContainer(SendBotMessageService sendBotMessageService, TelegramUserService telegramUserService,
                           CodeGymGroupClient codeGymGroupClient, GroupSubService groupSubService,
                           List<String> admins) {

       this.admins = admins;
And already in CodeGymTelegramBot there will be all the magic for getting a collection from a string in properties:
public JavarushTelegramBot(TelegramUserService telegramUserService, CodeGymGroupClient groupClient, GroupSubService groupSubService,
                          @Value("#{'${bot.admins}'.split(',')}") List<String> admins) {
   this.commandContainer =
           new CommandContainer(new SendBotMessageServiceImpl(this),
                   telegramUserService, groupClient, groupSubService, admins);
As you can see from the constructor above, we again use the Value annotation , into which we pass the logic for creating the collection. And that's it, so adding the admin is over. Now, when a non-admin wants to get data on the bot's statistics, he will receive this answer: I don't understand you 😟, type /help to find out what I understand. Thus, we have delimited the roles for the bot commands.

Adding help command for admins

Further, it would be logical to create a separate help command for admins. In the future, this part may grow significantly. Add the value of the admin help to the CommandName:
Create the AdminHelpCommand class in the command package:
package com.github.codegymcommunity.jrtb.command;

import com.github.codegymcommunity.jrtb.service.SendBotMessageService;
import org.telegram.telegrambots.meta.api.objects.Update;

import static com.github.codegymcommunity.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",

   private final SendBotMessageService sendBotMessageService;

   public AdminHelpCommand(SendBotMessageService sendBotMessageService) {
       this.sendBotMessageService = sendBotMessageService;

   public void execute(Update update) {
       sendBotMessageService.sendMessage(update.getMessage().getChatId().toString(), ADMIN_HELP_MESSAGE);
So far, it's very simple. In the future, it may grow quite well. For this command, a test from our template:
package com.github.codegymcommunity.jrtb.command;

import org.junit.jupiter.api.DisplayName;

import static com.github.codegymcommunity.jrtb.command.AdminHelpCommand.ADMIN_HELP_MESSAGE;
import static com.github.codegymcommunity.jrtb.command.CommandName.ADMIN_HELP;

@DisplayName("Unit-level testing for AdminHelpCommand")
public class AdminHelpCommandTest extends AbstractCommandTest {

   String getCommandName() {
       return ADMIN_HELP.getCommandName();

   String getCommandMessage() {
       return ADMIN_HELP_MESSAGE;

   Command getCommand() {
       return new AdminHelpCommand(sendBotMessageService);
Of course, the command needs to be added to the CommandContainer in our map:
.put(ADMIN_HELP.getCommandName(), new AdminHelpCommand(sendBotMessageService))

Adding command descriptions to the bot

Telegram bots have another interesting feature: you can add values ​​and a description of the commands it accepts to make it easier for the user to use the commands. What does it look like? For an example, let's go to BotFather - the most important Telegram bot. If you start writing a message with a slash /, the bot will offer options: "Java-project from A to Z": Adding the ability to work as an admin and statistics for him.  Part 1 - 4And if you continue writing, it will filter and show the relevant options:"Java-project from A to Z": Adding the ability to work as an admin and statistics for him.  Part 1 - 5Cool functionality, right? This is what I want to do with us. I will do what I can - through the Telegram application. I know it can be done programmatically. But i can not. In this series of articles, this is not necessary. If someone knows how to do this - write to me, we will add. I will gladly accept any help on this. I once read that this can be done through the command pattern, which works for us. Now I will show how I can do it: we need to find BotFather in Telegram, select the bot we want to configure from it. Next, select editing the bot and the section about commands. Now I will show everything using the example of my test bot for Javarush. We write the command for BotFather: /mybots"Java-project from A to Z": Adding the ability to work as an admin and statistics for him.  Part 1 - 6 Next, select the bot we need, in my case it will be test_codegym_community_bot:"Java-project from A to Z": Adding the ability to work as an admin and statistics for him.  Part 1 - 7As you can see from the list of buttons, here you can see the token, delete the bot, and transfer it to someone else. We are interested in editing the bot, so we select Edit Bot : "Java-project from A to Z": Adding the ability to work as an admin and statistics for him.  Part 1 - 8And here we select Edit Commands : "Java-project from A to Z": Adding the ability to work as an admin and statistics for him.  Part 1 - 9We just need to provide a message in a specific format, and it will be written as commands. Or if we want to remove them all, write /empty. To do this, I will create a SET_UP_COMMANDS_BOT_FATHER file in the root of the project , in which I will write all our commands, so that it is easy to restore or update if something happens. SET_UP_COMMANDS_BOT_FATHER:
start - start/resume work with the bot stop - pause work with the bot addGroupSub - subscribe to a group of articles deleteGroupSub - unsubscribe from a group of articles listGroupSub - list of groups subscribed to help - get help working with me
It is clear that we do not take out admin commands here. Only admins should know about them. We take this message and pass it to BotFather: "Java-project from A to Z": Adding the ability to work as an admin and statistics for him.  Part 1 - 10As usual, it didn't work the first time. After a few minutes of thinking, I sent all the commands in lower case, and not in CamelCase as it was before, and everything went well. Update in our file: SET_UP_COMMANDS_BOT_FATHER:
start - start/resume work with the bot stop - pause work with the bot addgroupsub - subscribe to a group of articles deletegroupsub - unsubscribe from a group of articles listgroupsub - list of groups subscribed to help - get help in working with me
Now you can go to our bot and see if the loading of commands is automatically pulled up: "Java-project from A to Z": Adding the ability to work as an admin and statistics for him.  Part 1 - 11Look, what a beauty now! I also wanted to expand the functionality of statistics within the framework of this article, but the material already turned out to be voluminous both in meaning and in content. So let's move that to next time. That is, the JRTB-10 task has not been completed completely: we will complete it in the next article. At the same time, I will add all the changes that already exist to the main bot. Want to support the author but don't know how? It's very simple - subscribe to my tg-channel , to my GitHub account and write your opinion about them here in the articles. This feedback is important to me, so I understand that they are read and interested in them.


Let's summarize what we've been through today:
  1. We discussed how to add your own annotation, how it can be used as a marker to delimit roles in teams. By the way, it could be done in a similar way using the interface. In the same way, we would create a marker interface and then check whether the object that comes in implements this interface or not.
  2. Added Help command for admins. As for me, it is also an important part in the development of this bot.
  3. Discussed how to add a description and popup commands when writing them in a bot. Interesting feature, definitely worth adding.
Based on this article, I created a pull request , you can see its details. Thank you all for your attention, from you as usual: like - subscribe - bell , star to our project, comment and rate the article! See you in the next article!“Java project from A to Z”: Adding Spring Scheduler - 2

List of all materials in the series at the beginning of this article.