JavaRush /Java blogi /Random-UZ /Keling, bot bilan ishlash uchun buyruq naqshini amalga os...

Keling, bot bilan ishlash uchun buyruq naqshini amalga oshiramiz. (1-qism) - "Java loyihasi A dan Z gacha"

Guruhda nashr etilgan
Hammaga salom aziz do'stlar. Bugun biz ehtiyojlarimiz uchun buyruq dizayni shablonini (shablon - bu naqsh, bizning kontekstimizda u xuddi shunday) amalga oshiramiz. Ushbu shablondan foydalanib, biz bot buyruqlarini qayta ishlash bilan qulay va to'g'ri ishlaymiz. "A dan Zgacha Java loyihasi": bot bilan ishlash uchun buyruq naqshini amalga oshirish.  1-1-qism
Do'stlar, sizga Javarush Telegram Bot loyihasi yoqdimi ? Dangasa bo'lmang: unga yulduz bering . Shunday qilib, uning qiziqarli ekanligi aniq bo'ladi va uni rivojlantirish yanada yoqimli bo'ladi!
Boshlash uchun, bu qanday naqsh haqida gapirish yaxshi bo'lardi - Buyruq. Lekin men buni qilsam, maqola juda katta va noqulay bo'ladi. Shuning uchun men mustaqil o'rganish uchun materiallarni tanladim:
  1. Bu mening 4 yil oldingi maqolam . Men buni yoshligimda yozganman, shuning uchun uni qattiq hukm qilmang.
  2. YouTube'da juda hissiy va interaktiv shved videosi. Men buni juda tavsiya qilaman. U chiroyli gapiradi, ingliz tili aniq va tushunarli. Va umuman olganda, u boshqa dizayn naqshlari haqida videoga ega.
  3. Mening maqolamga sharhlarda kimdir Nullptr35 ushbu videoni tavsiya qildi .
Bu mavzuga kirishish va men bilan bir sahifada bo'lish uchun etarli bo'lishi kerak. Xo'sh, ushbu dizayn namunasi bilan tanish bo'lganlar xavfsiz tarzda o'tishlari va davom etishlari mumkin.

Biz JRTB-3 yozamiz

Hammasi avvalgidek:
  1. Biz asosiy filialni yangilaymiz.
  2. Yangilangan asosiy filialga asoslanib, biz yangi JRTB-3 ni yaratamiz .
  3. Keling, naqshni amalga oshiramiz.
  4. Biz bajarilgan ishlarni tavsiflovchi yangi majburiyat yaratamiz.
  5. Biz tortishish so'rovini yaratamiz, uni tekshiramiz va agar hamma narsa yaxshi bo'lsa, biz ishimizni birlashtiramiz.
Men 1-2 bandlarni ko'rsatmayman: men ularni oldingi maqolalarda juda diqqat bilan tasvirlab berganman, shuning uchun shablonni to'g'ridan-to'g'ri amalga oshirishga o'tamiz. Nima uchun bu shablon bizga mos keladi? Ha, chunki biz har safar buyruqni bajarganimizda, biz onUpdateReceived(Update update) usuliga o'tamiz va buyruqqa qarab biz har xil mantiqni bajaramiz. Ushbu naqshsiz bizda if-else if iboralarining butun majmuasi bo'lar edi. Shunga o'xshash narsa:
if (message.startsWith("/start")) {
   doStartCommand();
} else if(message.startsWith("/stop")) {
   doStopCommand();
} else if(message.startsWith("/addUser")) {
   doAddUserCommand();
}
...
else if(message.startsWith("/makeMeHappy")) {
   doMakeMeHappyCommand();
}
Bundan tashqari, ellips bo'lgan joyda yana bir necha o'nlab jamoalar bo'lishi mumkin. Va buni odatdagidek qanday hal qilish kerak? Qanday qo'llab-quvvatlash kerak? Qiyin va qiyin. Bu shuni anglatadiki, bu variant bizga mos kelmaydi. Bu shunday ko'rinishi kerak:
if (message.startsWith(COMMAND_PREFIX)) {
   String commandIdentifier = message.split(" ")[0].toLowerCase();
   commandContainer.getCommand(commandIdentifier, userName).execute(update);
} else {
   commandContainer.getCommand(NO.getCommand(), userName).execute(update);
}
Va tamom! Biz qancha buyruq qo'shmasak ham, kodning bu qismi o'zgarishsiz qoladi. U nima qilyapti? Birinchi if, xabarning "/" buyruq prefiksi bilan boshlanishiga ishonch hosil qiladi. Agar shunday bo'lsa, biz birinchi bo'sh joygacha bo'lgan qatorni tanlaymiz va CommandContainer-dan mos keladigan buyruqni qidiramiz; uni topishimiz bilan biz buyruqni bajaramiz. Va bu hammasi ...) Agar sizda xohish va vaqt bo'lsa, siz jamoalar bilan ishlashni, birinchi navbatda, bir vaqtning o'zida bir sinfda, bir nechta shartlar va bularning barchasi bilan, keyin esa shablondan foydalanishni amalga oshirishingiz mumkin. Siz farqni ko'rasiz. Qanday go'zallik bo'ladi! Birinchidan, bot paketi yonida paket yaratamiz, u buyruq deb ataladi . "A dan Zgacha Java loyihasi": bot bilan ishlash uchun buyruq naqshini amalga oshirish.  1-2 qismVa allaqachon ushbu paketda buyruqni bajarish bilan bog'liq barcha sinflar bo'ladi. Buyruqlar bilan ishlash uchun bizga bitta interfeys kerak. Bu holatda, keling, uni yaratamiz:
package com.github.javarushcommunity.jrtb.command;

import org.telegram.telegrambots.meta.api.objects.Update;

/**
* Command interface for handling telegram-bot commands.
*/
public interface Command {

   /**
    * Main method, which is executing command logic.
    *
    * @param update provided {@link Update} object with all the needed data for command.
    */
   void execute(Update update);
}
Ushbu nuqtada biz buyruqning teskari ishini amalga oshirishimiz shart emas, shuning uchun biz bu usulni o'tkazib yuboramiz (bajarilmaydi). Amalga oshirish usulida Update ob'ekti argument sifatida keladi - aynan botdagi asosiy usulimizga keladigan narsa. Ushbu ob'ekt buyruqni qayta ishlash uchun zarur bo'lgan hamma narsani o'z ichiga oladi. Keyinchalik, buyruq qiymatlarini saqlaydigan raqam qo'shamiz (boshlash, to'xtatish va boshqalar). Nega bizga bu kerak? Shunday qilib, bizda jamoa nomlari uchun faqat bitta haqiqat manbasi bor. Biz uni buyruqlar paketimizda ham yaratamiz . Keling, uni CommandName deb ataymiz :
package com.github.javarushcommunity.jrtb.command;

/**
* Enumeration for {@link Command}'s.
*/
public enum CommandName {

   START("/start"),
   STOP("/stop");

   private final String commandName;

   CommandName(String commandName) {
       this.commandName = commandName;
   }

   public String getCommandName() {
       return commandName;
   }

}
Shuningdek, bizga bot orqali xabar yuboradigan xizmat kerak. Buni amalga oshirish uchun biz buyruqlar to'plami yonida xizmat paketini yaratamiz , unga barcha kerakli xizmatlarni qo'shamiz. Bu erda men bu holatda xizmat so'zi bilan nimani nazarda tutayotganimga e'tibor qaratishga arziydi. Agar ilovani ko'rib chiqsak, u ko'pincha bir necha qatlamlarga bo'linadi: so'nggi nuqtalar bilan ishlash uchun qatlam - kontrollerlar, biznes mantig'i qatlami - xizmatlar va ma'lumotlar bazasi bilan ishlash uchun qatlam - ombor. Shuning uchun, bizning holatlarimizda, xizmat - bu qandaydir biznes mantiqini amalga oshiradigan sinf. Xizmatni qanday qilib to'g'ri yaratish kerak? Birinchidan, u uchun interfeys va dasturni yarating. Bizning SpringBoot ilovamizning Ilova kontekstiga `@Service` izohidan foydalanib dasturni qo'shing va agar kerak bo'lsa, `@Autowired` izohi yordamida uni mahkamlang. Shuning uchun biz SendBotMessageService interfeysini yaratamiz (nomlash xizmatlarida ular odatda nomning oxiriga Xizmatni qo'shadilar):
package com.github.javarushcommunity.jrtb.service;

/**
* Service for sending messages via telegram-bot.
*/
public interface SendBotMessageService {

   /**
    * Send message via telegram bot.
    *
    * @param chatId provided chatId in which messages would be sent.
    * @param message provided message to be sent.
    */
   void sendMessage(String chatId, String message);
}
Keyinchalik, biz uni amalga oshirishni yaratamiz:
package com.github.javarushcommunity.jrtb.service;

import com.github.javarushcommunity.jrtb.bot.JavarushTelegramBot;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.telegram.telegrambots.meta.api.methods.send.SendMessage;
import org.telegram.telegrambots.meta.exceptions.TelegramApiException;

/**
* Implementation of {@link SendBotMessageService} interface.
*/
@Service
public class SendBotMessageServiceImpl implements SendBotMessageService {

   private final JavarushTelegramBot javarushBot;

   @Autowired
   public SendBotMessageServiceImpl(JavarushTelegramBot javarushBot) {
       this.javarushBot = javarushBot;
   }

   @Override
   public void sendMessage(String chatId, String message) {
       SendMessage sendMessage = new SendMessage();
       sendMessage.setChatId(chatId);
       sendMessage.enableHtml(true);
       sendMessage.setText(message);

       try {
           javarushBot.execute(sendMessage);
       } catch (TelegramApiException e) {
           //todo add logging to the project.
           e.printStackTrace();
       }
   }
}
Amalga oshirish shunday ko'rinadi. Eng muhim sehr - bu dizaynerning yaratilgan joyi. Konstruktordagi @Autowired izohidan foydalanib, SpringBoot ilova kontekstida ushbu sinf ob'ektini qidiradi. Va u allaqachon u erda. Bu shunday ishlaydi: bizning ilovamizda istalgan joyda botga kirishimiz va biror narsa qilishimiz mumkin. Va bu xizmat xabarlarni yuborish uchun javobgardir. Shunday qilib, biz har doim hamma joyda bunday narsalarni yozmasligimiz uchun:
SendMessage sendMessage = new SendMessage();
sendMessage.setChatId(chatId);
sendMessage.setText(message);

try {
   javarushBot.execute(sendMessage);
} catch (TelegramApiException e) {
   //todo add logging to the project.
   e.printStackTrace();
}
Biz bu mantiqni alohida sinfga o'tkazdik va kerak bo'lsa undan foydalanamiz. Endi biz uchta buyruqni bajarishimiz kerak: StartCommand, StopCommand va UnknownCommand. Buyruqlar uchun konteynerimizni to'ldirishimiz uchun bizga ular kerak. Hozircha matnlar quruq va ma'lumotsiz bo'ladi, bu vazifa uchun bu juda muhim emas. Shunday qilib, StartCommand:
package com.github.javarushcommunity.jrtb.command;

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

/**
* Start {@link Command}.
*/
public class StartCommand implements Command {

   private final SendBotMessageService sendBotMessageService;

   public final static String START_MESSAGE = "Привет. Я Javarush Telegram Bot. Я помогу тебе быть в курсе последних " +
           "статей тех авторов, котрые тебе интересны. Я еще маленький и только учусь.";

   // Здесь не добавляем сервис через получение из Application Context.
   // Потому что если это сделать так, то будет циклическая зависимость, которая
   // ломает работу applications.
   public StartCommand(SendBotMessageService sendBotMessageService) {
       this.sendBotMessageService = sendBotMessageService;
   }

   @Override
   public void execute(Update update) {
       sendBotMessageService.sendMessage(update.getMessage().getChatId().toString(), START_MESSAGE);
   }
}
Iltimos, dizaynerdan oldin sharhlarni diqqat bilan o'qing. Doiraviy bog'liqlik ( doiraviy qaramlik ) to'g'ri bo'lmagan arxitektura tufayli yuzaga kelishi mumkin. Bizning holatda, biz hamma narsa ishlayotganiga va to'g'ri ekanligiga ishonch hosil qilamiz. Ilova kontekstidagi haqiqiy ob'ekt CommandContainer-da allaqachon buyruqni yaratishda qo'shiladi. To'xtatish buyrug'i:
package com.github.javarushcommunity.jrtb.command;

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

/**
* Stop {@link Command}.
*/
public class StopCommand implements Command {

   private final SendBotMessageService sendBotMessageService;

   public static final String STOP_MESSAGE = "Деактивировал все ваши подписки \uD83D\uDE1F.";

   public StopCommand(SendBotMessageService sendBotMessageService) {
       this.sendBotMessageService = sendBotMessageService;
   }

   @Override
   public void execute(Update update) {
       sendBotMessageService.sendMessage(update.getMessage().getChatId().toString(), STOP_MESSAGE);
   }
}
Va noma'lum buyruq. Nega bizga kerak? Biz uchun bu bizga berilgan buyruqni topa olmasak, javob beradigan muhim buyruqdir. Shuningdek, bizga NoCommand va HelpCommand kerak bo'ladi.
  • NoCommand - xabar umuman buyruq bilan boshlanmagan vaziyat uchun javobgar bo'ladi;
  • HelpCommand foydalanuvchi uchun qo'llanma, o'ziga xos hujjat bo'ladi.
Yordam buyrug'ini qo'shamiz:
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.*;

/**
* Help {@link Command}.
*/
public class HelpCommand implements Command {

   private final SendBotMessageService sendBotMessageService;

   public static final String HELP_MESSAGE = String.format("✨<b>Дотупные команды</b>✨\n\n"

                   + "<b>Начать\\закончить работу с ботом</b>\n"
                   + "%s - начать работу со мной\n"
                   + "%s - приостановить работу со мной\n\n"
                   + "%s - получить помощь в работе со мной\n",
           START.getCommandName(), STOP.getCommandName(), HELP.getCommandName());

   public HelpCommand(SendBotMessageService sendBotMessageService) {
       this.sendBotMessageService = sendBotMessageService;
   }

   @Override
   public void execute(Update update) {
       sendBotMessageService.sendMessage(update.getMessage().getChatId().toString(), HELP_MESSAGE);
   }
}
Buyruqsiz:
package com.github.javarushcommunity.jrtb.command;

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

/**
* No {@link Command}.
*/
public class NoCommand implements Command {

   private final SendBotMessageService sendBotMessageService;

   public static final String NO_MESSAGE = "Я поддерживаю команды, начинающиеся со слеша(/).\n"
           + "Whatбы посмотреть список команд введите /help";

   public NoCommand(SendBotMessageService sendBotMessageService) {
       this.sendBotMessageService = sendBotMessageService;
   }

   @Override
   public void execute(Update update) {
       sendBotMessageService.sendMessage(update.getMessage().getChatId().toString(), NO_MESSAGE);
   }
}
Va bu vazifa uchun hali ham UnknownCommand mavjud:
package com.github.javarushcommunity.jrtb.command;

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

/**
* Unknown {@link Command}.
*/
public class UnknownCommand implements Command {

   public static final String UNKNOWN_MESSAGE = "Не понимаю вас \uD83D\uDE1F, напишите /help чтобы узнать что я понимаю.";

   private final SendBotMessageService sendBotMessageService;

   public UnknownCommand(SendBotMessageService sendBotMessageService) {
       this.sendBotMessageService = sendBotMessageService;
   }

   @Override
   public void execute(Update update) {
       sendBotMessageService.sendMessage(update.getMessage().getChatId().toString(), UNKNOWN_MESSAGE);
   }
}
Keyin buyruqlarimiz uchun konteyner qo'shamiz. U bizning buyruq obyektlarimizni saqlaydi va so'rov bo'yicha biz kerakli buyruqni olishni kutamiz. Keling, uni CommandContainer deb ataymiz :
package com.github.javarushcommunity.jrtb.command;

import com.github.javarushcommunity.jrtb.service.SendBotMessageService;
import com.google.common.collect.ImmutableMap;

import static com.github.javarushcommunity.jrtb.command.CommandName.*;

/**
* Container of the {@link Command}s, which are using for handling telegram commands.
*/
public class CommandContainer {

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

   public CommandContainer(SendBotMessageService sendBotMessageService) {

       commandMap = ImmutableMap.<string, command="">builder()
               .put(START.getCommandName(), new StartCommand(sendBotMessageService))
               .put(STOP.getCommandName(), new StopCommand(sendBotMessageService))
               .put(HELP.getCommandName(), new HelpCommand(sendBotMessageService))
               .put(NO.getCommandName(), new NoCommand(sendBotMessageService))
               .build();

       unknownCommand = new UnknownCommand(sendBotMessageService);
   }

   public Command retrieveCommand(String commandIdentifier) {
       return commandMap.getOrDefault(commandIdentifier, unknownCommand);
   }

}
Ko'rib turganingizdek, hamma narsa oddiygina qilingan. Bizda buyruq qiymati ko'rinishidagi kalit va Buyruq tipidagi buyruq ob'ekti ko'rinishidagi qiymatga ega o'zgarmas xarita mavjud. Konstruktorda biz o'zgarmas xaritani bir marta to'ldiramiz va unga ilovaning butun faoliyati davomida kiramiz. Konteyner bilan ishlashning asosiy va yagona usuli bu retrieveCommand(String commandIdentifier) . UnknownCommand deb nomlangan buyruq mavjud bo'lib, biz tegishli buyruqni topa olmagan holatlar uchun javob beradi. Endi biz konteynerni JavaRushTelegramBot-da bot sinfimizga kiritishga tayyormiz: Endi bizning bot sinfimiz shunday ko'rinadi:
package com.github.javarushcommunity.jrtb.bot;

import com.github.javarushcommunity.jrtb.command.CommandContainer;
import com.github.javarushcommunity.jrtb.service.SendBotMessageServiceImpl;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.telegram.telegrambots.bots.TelegramLongPollingBot;
import org.telegram.telegrambots.meta.api.objects.Update;

import static com.github.javarushcommunity.jrtb.command.CommandName.NO;

/**
* Telegram bot for Javarush Community from Javarush community.
*/
@Component
public class JavarushTelegramBot extends TelegramLongPollingBot {

   public static String COMMAND_PREFIX = "/";

   @Value("${bot.username}")
   private String username;

   @Value("${bot.token}")
   private String token;

   private final CommandContainer commandContainer;

   public JavarushTelegramBot() {
       this.commandContainer = new CommandContainer(new SendBotMessageServiceImpl(this));
   }

   @Override
   public void onUpdateReceived(Update update) {
       if (update.hasMessage() && update.getMessage().hasText()) {
           String message = update.getMessage().getText().trim();
           if (message.startsWith(COMMAND_PREFIX)) {
               String commandIdentifier = message.split(" ")[0].toLowerCase();

               commandContainer.retrieveCommand(commandIdentifier).execute(update);
           } else {
               commandContainer.retrieveCommand(NO.getCommandName()).execute(update);
           }
       }
   }

   @Override
   public String getBotUsername() {
       return username;
   }

   @Override
   public String getBotToken() {
       return token;
   }
}
Va bu, kodga o'zgartirishlar yakunlandi. Buni qanday tekshirishim mumkin? Siz botni ishga tushirishingiz va hamma narsa ishlayotganligini tekshirishingiz kerak. Buning uchun application.properties-dagi tokenni yangilayman, to'g'risini o'rnataman va JavarushTelegramBotApplication sinfida dasturni ishga tushiraman: "A dan Zgacha Java loyihasi": bot bilan ishlash uchun buyruq naqshini amalga oshirish.  1-3 qismEndi biz buyruqlar kutilganidek ishlashini tekshirishimiz kerak. Men buni bosqichma-bosqich tekshiraman:
  • To'xtatish buyrug'i;
  • Boshlash buyrug'i;
  • Yordam buyrug'i;
  • NoCommand;
  • Noma'lum buyruq.
Mana nima bo'ldi: "A dan Zgacha Java loyihasi": bot bilan ishlash uchun buyruq naqshini amalga oshirish.  1-4 qismbot biz kutgandek ishladi. Davomi havola orqali .

Seriyadagi barcha materiallar ro'yxati ushbu maqolaning boshida.

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