Всім привіт, дорогі друзі. Отже, бот вже працює і надсилає повідомлення про нові статті. Якщо ви ще не використовуєте його, ось посилання: Javarush Telegram Bot . Ну а сьогодні ми поговоримо про додавання команд, які працюють лише для адмінів. Одна з таких команд – статистика та дошка допомоги. Навіщо це потрібно? На даний момент цікавіше описати роботу з інструкціями в рамках цього завдання, ніж реальну необхідність цього. Ну а якщо ми йдемо в команду статистики, то можна її розширити і зробити більш інформативною. Після MVP можна буде повернути статистику для авторів, наприклад. Але про це вже потім…)
лайк - передплата - дзвіночок , зірку нашому проекту, коментар та оцінити статтю! До зустрічі у наступній статті!
Розбираємося з додаванням адмінів та команд для них
Починаємо нашу роботу з того, що оновлюємо main гілку та її основі створюємо нову STEP_9_JRTB-10. Щоб розібратися, яка команда належить до адмінів, а яка до всіх, потрібно промаркувати команду. Для цього створимо інструкцію. Що це означає? Такого ми ще не робабо. Це можна вибрати під час створення класу в IDEA. Зараз покажу. У пакеті command створимо новий пакет annotation і в ньому – анотацію AdminCommand: Сама анотація буде такою: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.
*/
@Retention(RUNTIME)
public @interface AdminCommand {
}
Тут нам більше нічого не потрібно. Далі додаємо її нашій команді StatCommand: Тепер все має заробити... Чи ні? Ні, звичайно)) Потрібно навчити наш CommandContainer правильно видавати результат. Для цього оновимо метод retrieveCommand , який видає команду для запуску в залежності від того, що йому передають. Як ідентифікатор адміна будемо використовувати його username у Телеграмі. Він унікальний і легше читаємо, ніж chat_id. Як його одержати? Він знаходиться в об'єкті Update, який надходить із повідомленням:
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,
CodeGymGroupClient codeGymGroupClient, GroupSubService groupSubService,
List<String> admins) {
this.admins = admins;
А вже в CodeGymTelegramBot буде вся магія з отримання колекції з рядка в пертях:
@Autowired
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);
}
Як видно з конструктора вище, знову використовуємо інструкцію Value , в яку передаємо логіку створення колекції. І все, таким чином, додавання адміну закінчилося. Тепер, коли адмін не захоче отримати дані зі статистики бота, він отримає таку відповідь: Не розумію тебе 😟, напиши /help щоб дізнатися що я розумію. Таким чином, ми розмежували ролі для команд бота.
Додаємо команду help для адмінів
Далі логічно було б створити окрему команду help для адмінів. У майбутньому ця частина може значно зрости. Додаємо значення адмінського help в CommandName:ADMIN_HELP("/ahelp")
Створюємо клас AdminHelpCommand у пакеті command:
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",
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.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 {
@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 — найголовнішого робота Телеграма. Якщо почати писати повідомлення зі слєша /, бот запропонує варіанти: І якщо продовжити писати, він відфільтрує та покаже релевантні варіанти:Класний функціонал, так? От я таке саме хочу зробити і в нас. Я робитиму так, як вмію — через додаток Телеграма. Я знаю, що це можна зробити програмно. Але я не вмію. У рамках цієї серії статей це не потрібно. Якщо хтось уміє це робити — пишіть мені, додаватимемо. Я із задоволенням прийму будь-яку допомогу в цьому. Я якось читав, що це можна зробити через патерн команда, яка у нас працює. Зараз я покажу, як я це вмію: нам потрібно знайти у Телеграмі BotFather, вибрати в нього бота, якого хочемо налаштувати. Далі вибрати редагування робота і секцію для команди. Зараз я покажу все на прикладі мого тестового робота для Javarush. У BotFather пишемо команду: /mybots Далі вибираємо потрібного нам бота, в моєму випадку це буде test_codegym_community_bot:Як видно з переліку кнопок тут можна і подивитися токен, і видалити бота, і передати його комусь іншому. Нас цікавить редагування бота, тому вибираємо Edit Bot : І тут вибираємо Edit Commands : Нам потрібно просто надати у конкретному форматі повідомлення, і воно буде записано як команди. Або якщо хочемо прибрати їх усі, написати /empty. Для цієї справи створимо в корені проекту файл SET_UP_COMMANDS_BOT_FATHER , в якому напишу всі наші команди, щоб легко відновити або оновити в разі чого. SET_UP_COMMANDS_BOT_FATHER:
start - почати/відновити роботу з ботом stop - призупинити роботу з ботом addGroupSub - підписатися на групу статей deleteGroupSub - відписатися від групи статей listGroupSub - список груп, на які підписано
Зрозуміло, що адмінські команди ми не виносимо сюди. Про них мають знати лише адміни. Беремо це повідомлення та передаємо його BotFather: Як це зазвичай водиться, з першого разу не вийшло. Після декількох хвабон роздумів, передав усі команди в нижньому регістрі, а не в CamelCase, як було до цього, і все пройшло успішно. Оновлюємо у нашому файлі: SET_UP_COMMANDS_BOT_FATHER:
start - почати/відновити роботу з ботом stop - призупинити роботу з ботом addgroupsub - підписатися на групу статей deletegroupsub - відписатися від групи статей listgroupsub - список груп, на які підписано help - отримати допомогу в роботі зі мною
Тепер можна піти в нашого бота і посморіти, чи підтяглося підвантаження команд автоматично: Подивіться, яка тепер краса! Хотів у рамках цієї статті ще й розширити функціонал статистики, але матеріал і так вийшов об'ємним і за змістом, і за змістом. Тому перенесемо це наступного разу. Тобто завдання JRTB-10 зроблено не повністю: ми його доробимо в рамках наступної статті. Водночас усі зміни, які вже є, я додам до основного робота. Бажаєте підтримати автора, але не знаєте як? Це дуже просто – підписуйтесь на мій ТГ-канал , на мій GitHub аккаунт і пишіть тут у статтях свою думку про них. Цей зворотний зв'язок для мене важливий, так я розумію, що їх читають і ними цікавляться.
Висновки
Підсумуємо те, що ми сьогодні пройшли:- Обговорабо, як додавати власну інструкцію, як її можна використовувати як маркер для розмежування ролей у командах. До речі, так можна було зробити і за допомогою інтерфейсу. Так само створабо б інтерфейс-маркер і потім перевіряли б, чи реалізує цей інтерфейс ні об'єкт, який приходить.
- Додали Help команду для адмінів. Як на мене — також важлива частина розвитку цього бота.
- Обговорабо, як додати опис та сплив команд при написанні їх у боті. Цікава фіча, напевно, варто було її додати.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ