JavaRush /Java Blog /Random EN /We add the ability to work as an administrator and statis...

We add the ability to work as an administrator 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 working and sending notifications about new articles. If you don't use it yet, here's the link: Javarush Telegram Bot . Well, today we’ll talk about adding commands that only work for admins. One of these commands is statistics and help board. Why is this necessary? At the moment, it is more interesting to describe the work with annotations within the framework of this task than the actual need for it. "Java project from A to Z": Adding the ability to work as an administrator 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...)

Let's understand adding admins and commands for them

We begin our work by updating the main branch and creating a new one based on it - STEP_9_JRTB-10. To figure out which command applies to admins and which applies to everyone, you need to label the team. To do this, let's create an annotation. What does it mean? We haven't done this before. This can be selected when creating a class in IDEA. I will show you now. In the command package, create a new annotation package and in it the AdminCommand annotation: "Java project from A to Z": Adding the ability to work as an admin and statistics for him.  Part 1 - 2The annotation itself will be like this:
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 {
}
We don't need anything else here. Next, we add it to our StatCommand command: "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)) 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. We will use his username in Telegram as the admin identifier. It is unique and easier to read than chat_id. How to get it? It's in the Update object, which comes with the message:
update.getMessage().getFrom().getUserName()
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 added the isAdminCommand method , which checks whether there is an AdminCommand annotation on the supplied command. And if it's an admin-only command, we check to see if we have that username in our collection of available admins. By the way, here it is, OOP, in all its glory: we simply pass an interface, which can be any implementation. But we can only pass the class that implements 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 line in which all usernames of telegram users who will be administrators are indicated, separated by commas. To do this, add to application.properties:
bot.admins: robeskman,romankh3
In the CommandContainer 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,
                           JavaRushGroupClient javaRushGroupClient, GroupSubService groupSubService,
                           List<String> admins) {

       this.admins = admins;
And already in JavaRushTelegramBot there will be all the magic of getting a collection from a string in properties:
@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);
}
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, adding the admin is over. Now, when a non-admin wants to get data on the bot’s statistics, he will receive the following answer: I don’t understand you 😟, write /help to find out what I understand. This way we differentiated the roles for the bot commands.

Adding a help command for admins

Next, it would be logical to create a separate help command for administrators. In the future, this part may grow significantly. Add the admin help value to the CommandName:
ADMIN_HELP("/ahelp")
Create the AdminHelpCommand class in the command package:
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);
   }
}
So far it is very simple. It may well grow quite well in the future. For this command, a test from our template:
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);
   }
}
Of course, the command needs to be added to the CommandContainer in our map:
.put(ADMIN_HELP.getCommandName(), new AdminHelpCommand(sendBotMessageService))

Adding a description of commands to the bot

Telegram bots have another interesting feature: you can add values ​​and descriptions 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 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? So I want to do the same here. I will do it the best way I can - through the Telegram application. I know this can be done programmatically. But i can not. This is not necessary for the purposes of this series of articles. If anyone knows how to do this, write to me, we will add it. I will gladly accept any help with this. I once read that this can be done through the command pattern that works for us. Now I’ll show you how I can do this: we need to find BotFather in Telegram, select the bot we want to configure. Next, select editing the bot and the section about commands. Now I will show everything using the example of my test bot for Javarush. At BotFather we write the command: /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_javarush_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 view 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 recorded as commands. Or if we want to remove them all, write /empty. For this purpose, I will create a file in the root of the project SET_UP_COMMANDS_BOT_FATHER , 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/restore 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 to which you are subscribed help - get help working with me
It is clear that we do not carry admin commands here. Only admins should know about them. Let's take this message and pass it on to BotFather: "Java project from A to Z": Adding the ability to work as an admin and statistics for him.  Part 1 - 10As is usually the case, it didn't work out the first time. After a few minutes of thought, I passed all the commands in lower case, and not in CamelCase as before, and everything went well. We update in our file: SET_UP_COMMANDS_BOT_FATHER:
start - start/restore 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 to which you are subscribed help - get help working with me
Now you can go to our bot and see if the commands have been loaded automatically: "Java project from A to Z": Adding the ability to work as an admin and statistics for him.  Part 1 - 11Look how beautiful it is now! I also wanted to expand the functionality of statistics within the framework of this article, but the material was already voluminous both in meaning and in content. Therefore, we will postpone this for next time. That is, the task of JRTB-10 is not completely done: 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 , my GitHub account and write your opinion about them in articles here. This feedback is important to me, so I understand that they are read and interested in them.

conclusions

Let's summarize what we went through today:
  1. We discussed how to add your own annotation and how it can be used as a marker to delineate roles in teams. By the way, this could have been done using the interface. In the same way, we would create a marker interface and then check whether the object that arrives implements this interface or not.
  2. Added Help command for admins. As for me, this is also an important part in the development of this bot.
  3. We discussed how to add a description and popup of 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, as usual: like - subscribe - bell , star for our project, comment and rate the article! See you in the next article!

A list of all materials in the series is at the beginning of this article.

Comments
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION