JavaRush /Blog Jawa /Random-JV /Ayo ngleksanakake Pola Komando kanggo nggarap bot. (Bagia...

Ayo ngleksanakake Pola Komando kanggo nggarap bot. (Bagian 1) - "Proyek Jawa saka A nganti Z"

Diterbitake ing grup
Halo kabeh, kanca-kanca sing ditresnani. Dina iki kita bakal ngleksanakake cithakan (template minangka pola, ing konteks kita iku padha) desain Command kanggo kabutuhan kita. Nggunakake cithakan iki, kita bakal kanthi trep lan bener nggarap pangolahan perintah bot kita. "Proyek Jawa saka A nganti Z": Ngleksanakake Pola Perintah kanggo nggarap bot.  Bagean 1 - 1
Kanca-kanca, apa sampeyan seneng karo proyek Javarush Telegram Bot ? Aja kesed: menehi lintang . Kanthi cara iki bakal cetha yen dheweke menarik, lan bakal luwih nyenengake kanggo ngembangake dheweke!
Kanggo miwiti, iku bakal apik kanggo pirembagan bab apa jenis pola iki - Command. Nanging yen aku nindakake iki, artikel kasebut bakal gedhe banget lan rumit. Mulane, aku milih bahan kanggo sinau mandiri:
  1. Iki artikelku saka 4 taun kepungkur. Aku nulis iki nalika aku isih enom, supaya ora menehi kritik banget.
  2. Video saka Swedia banget emosional lan interaktif ing YouTube. Aku banget nyaranake. Dheweke ngomong apik, basa Inggrise jelas lan bisa dingerteni. Lan umume, dheweke duwe video babagan pola desain liyane.
  3. Ing komentar ing artikelku, wong Nullptr35 nyaranake video iki .
Iki kudu cukup kanggo nyemplungake dhewe ing topik kasebut lan ana ing kaca sing padha karo aku. Inggih, wong-wong sing kenal karo pola desain iki bisa kanthi aman ngliwati lan nerusake.

Kita nulis JRTB-3

Kabeh padha kaya sadurunge:
  1. Kita nganyari cabang utama.
  2. Adhedhasar cabang utama sing dianyari, kita nggawe JRTB-3 anyar .
  3. Ayo dileksanakake pola kasebut.
  4. Kita nggawe komitmen anyar sing nggambarake karya sing wis rampung.
  5. Kita nggawe panjalukan narik, mriksa, lan yen kabeh iku ok, kita gabungke karya kita.
Aku ora bakal nuduhake TCTerms 1-2: Aku njlèntrèhaké kanthi ati-ati ing artikel sadurunge, supaya kita nerusake langsung kanggo ngleksanakake cithakan. Napa cithakan iki cocog kanggo kita? Ya, amarga saben-saben kita nglakokake prentah, kita bakal pindhah menyang metode onUpdateReceived(Update update) , lan gumantung saka perintah kasebut, kita bakal nglakokake logika sing beda. Tanpa pola iki, kita bakal duwe host kabeh if-else if statements. Kaya iki:
if (message.startsWith("/start")) {
   doStartCommand();
} else if(message.startsWith("/stop")) {
   doStopCommand();
} else if(message.startsWith("/addUser")) {
   doAddUserCommand();
}
...
else if(message.startsWith("/makeMeHappy")) {
   doMakeMeHappyCommand();
}
Kajaba iku, ing ngendi ana elipsis, bisa uga ana sawetara rolas tim liyane. Lan carane nangani iki biasane? Carane ndhukung? Susah lan angel. Iki tegese pilihan iki ora cocog karo kita. Iku kudu katon kaya iki:
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);
}
Iku kabeh! Lan ora ketompo carane akeh perintah sing ditambahake, bagean kode iki bakal tetep ora owah. Dheweke nyapo? Pisanan yen nggawe manawa pesen kasebut diwiwiti kanthi prefiks printah "/". Yen ngono, banjur pilih baris nganti spasi pisanan lan goleki printah sing cocog ing CommandContainer; sanalika kita nemokake, kita mbukak printah kasebut. Lan iku kabeh ...) Yen sampeyan duwe kepinginan lan wektu, sampeyan bisa ngleksanakake nggarap tim, pisanan ing siji kelas bebarengan, karo Bunch saka kahanan lan kabeh sing, lan banjur nggunakake cithakan. Sampeyan bakal weruh prabédan. Apa bakal dadi kaendahan! Pisanan, ayo nggawe paket ing jejere paket bot, sing bakal diarani command . "Proyek Jawa saka A nganti Z": Ngleksanakake Pola Perintah kanggo nggarap bot.  Bagean 1 - 2Lan ing paket iki bakal ana kabeh kelas sing ana hubungane karo implementasine perintah kasebut. Kita butuh siji antarmuka kanggo nggarap perintah. Kanggo kasus iki, ayo nggawe:
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);
}
Ing titik iki, kita ora perlu ngleksanakake operasi mbalikke printah, supaya kita bakal ngliwati cara iki (unexecute). Ing metode eksekusi, obyek Update teka minangka argumen - persis sing ana ing metode utama kita ing bot. Objek iki bakal ngemot kabeh sing dibutuhake kanggo ngolah perintah kasebut. Sabanjure, kita bakal nambah enum sing bakal nyimpen nilai printah (miwiti, mandheg, lan liya-liyane). Napa kita butuh iki? Supaya kita mung duwe siji sumber bebener kanggo jeneng tim. Kita uga nggawe ing paket printah kita . Ayo diarani CommandName :
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;
   }

}
Kita uga butuh layanan sing bakal ngirim pesen liwat bot. Kanggo nindakake iki, kita bakal nggawe paket layanan ing jejere paket printah , sing bakal ditambahake kabeh layanan sing dibutuhake. Kene iku worth fokus ing apa sing dakkarepake kanthi tembung layanan ing kasus iki. Yen kita nganggep aplikasi, asring dipérang dadi sawetara lapisan: lapisan kanggo nggarap titik pungkasan - pengontrol, lapisan logika bisnis - layanan, lan lapisan kanggo nggarap database - gudang. Mulane, ing kasus kita, layanan minangka kelas sing ngetrapake sawetara logika bisnis. Kepiye carane nggawe layanan kanthi bener? Pisanan, nggawe antarmuka lan implementasine. Tambah implementasine nggunakake anotasi `@Service` menyang Konteks Aplikasi saka aplikasi SpringBoot kita, lan, yen perlu, kenceng nganggo anotasi `@Autowired`. Mula, kita nggawe antarmuka SendBotMessageService (ing layanan jeneng biasane nambah Layanan ing mburi jeneng):
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);
}
Sabanjure, kita nggawe implementasine:
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();
       }
   }
}
Iki minangka implementasine. Piandel sing paling penting yaiku ing ngendi desainer digawe. Nggunakake anotasi @Autowired ing konstruktor, SpringBoot bakal nggoleki obyek kelas iki ing Konteks Aplikasi. Lan dheweke wis ana. Kerjane kaya iki: ing aplikasi kita, ing ngendi wae kita bisa ngakses bot lan nindakake apa wae. Lan layanan iki tanggung jawab kanggo ngirim pesen. Supaya kita ora nulis kaya mangkene ing saben papan:
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();
}
Kita wis dipindhah logika iki menyang kelas kapisah lan bakal nggunakake yen perlu. Saiki kita kudu ngleksanakake telung printah: StartCommand, StopCommand lan UnknownCommand. We kudu wong-wong mau supaya kita duwe soko kanggo isi wadhah kita kanggo printah. Saiki, teks bakal garing lan ora informatif; kanggo tujuan tugas iki, iki ora penting banget. Dadi, 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);
   }
}
Mangga maca komentar kasebut kanthi teliti sadurunge desainer. Ketergantungan sirkular ( sirkular dependency ) bisa kedadeyan amarga arsitektur sing ora bener. Ing kasus kita, kita bakal nggawe manawa kabeh bisa digunakake lan bener. Objek nyata saka Konteks Aplikasi bakal ditambahake nalika nggawe printah sing wis ana ing CommandContainer. StopCommand:
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);
   }
}
Lan UnknownCommand. Yagene kita butuh? Kanggo kita, iki minangka prentah penting sing bakal nanggapi yen kita ora bisa nemokake prentah sing diwenehake marang kita. Kita uga butuh NoCommand lan HelpCommand.
  • NoCommand - bakal tanggung jawab kanggo kahanan nalika pesen ora diwiwiti kanthi prentah;
  • HelpCommand bakal dadi pandhuan kanggo pangguna, sejenis dokumentasi.
Ayo ditambahake HelpCommand:
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);
   }
}
Ora Komando:
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);
   }
}
Lan kanggo tugas iki isih ana UnknownCommand:
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);
   }
}
Sabanjure, ayo nambah wadhah kanggo printah kita. Iku bakal nyimpen obyek printah kita lan ing panyuwunan kita ngarep-arep kanggo nampa printah sing dibutuhake. Ayo diarani CommandContainer :
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);
   }

}
Nalika sampeyan bisa ndeleng, kabeh wis rampung mung. We duwe peta immutable karo tombol ing wangun Nilai printah lan nilai ing wangun obyek printah saka jinis Command. Ing konstruktor, kita ngisi peta immutable sapisan lan ngakses ing saindhenging operasi aplikasi. Cara utama lan mung kanggo nggarap wadhah yaiku retrieveCommand(String commandIdentifier) ​​​​. Ana prentah sing diarani UnknownCommand, sing tanggung jawab kanggo kasus nalika kita ora bisa nemokake perintah sing cocog. Saiki kita wis siyap kanggo ngleksanakake wadhah menyang kelas bot kita - ing JavaRushTelegramBot: Iki minangka kelas bot kita saiki:
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;
   }
}
Lan iku, owah-owahan kanggo kode wis rampung. Carane aku bisa mriksa iki? Sampeyan kudu miwiti bot lan priksa manawa kabeh bisa digunakake. Kanggo nindakake iki, aku nganyari token ing application.properties, nyetel sing bener, lan miwiti aplikasi ing kelas JavarushTelegramBotApplication: "Proyek Jawa saka A nganti Z": Ngleksanakake Pola Perintah kanggo nggarap bot.  Bagean 1 - 3Saiki kita kudu mriksa manawa printah kasebut bisa digunakake kaya sing dikarepake. Aku mriksa langkah demi langkah:
  • StopCommand;
  • StartCommand;
  • PitulungCommand;
  • NoCommand;
  • UnknownCommand.
Mangkene kedadeyane: "Proyek Jawa saka A nganti Z": Ngleksanakake Pola Perintah kanggo nggarap bot.  Bagean 1 - 4Bot kasebut kerjane kaya sing dikarepake. Diterusake liwat link .

Dhaptar kabeh materi ing seri kasebut ana ing wiwitan artikel iki.

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