diff --git a/spring-boot-modules/spring-boot-telegram/.gitignore b/spring-boot-modules/spring-boot-telegram/.gitignore new file mode 100644 index 0000000000..a8e6c9dbce --- /dev/null +++ b/spring-boot-modules/spring-boot-telegram/.gitignore @@ -0,0 +1,35 @@ +HELP.md +target/ +.mvn +mvnw +mvnw.cmd +!**/src/main/**/target/ +!**/src/test/**/target/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ diff --git a/spring-boot-modules/spring-boot-telegram/pom.xml b/spring-boot-modules/spring-boot-telegram/pom.xml new file mode 100644 index 0000000000..b960137449 --- /dev/null +++ b/spring-boot-modules/spring-boot-telegram/pom.xml @@ -0,0 +1,45 @@ + + + 4.0.0 + com.baelding + spring-boot-telegram + 0.0.1-SNAPSHOT + spring-boot-telegram + Demo project for Spring Boot with Spring Data Redis + + + com.baeldung + parent-boot-3 + 0.0.1-SNAPSHOT + ../../parent-boot-3 + + + + + org.telegram + telegrambots-spring-boot-starter + 6.7.0 + + + org.telegram + telegrambots-abilities + 6.7.0 + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + + 17 + + + diff --git a/spring-boot-modules/spring-boot-telegram/src/main/java/com/baeldung/telegram/Constants.java b/spring-boot-modules/spring-boot-telegram/src/main/java/com/baeldung/telegram/Constants.java new file mode 100644 index 0000000000..5c529bf15c --- /dev/null +++ b/spring-boot-modules/spring-boot-telegram/src/main/java/com/baeldung/telegram/Constants.java @@ -0,0 +1,9 @@ +package com.baeldung.telegram; + +public class Constants { + + public static final String START_DESCRIPTION = "Starts the bot"; + + public static final String CHAT_STATES = "chatStates"; + public static final String START_TEXT = "Welcome to Baeldung Pizza Bot.\nPlease enter your name"; +} diff --git a/spring-boot-modules/spring-boot-telegram/src/main/java/com/baeldung/telegram/KeyboardFactory.java b/spring-boot-modules/spring-boot-telegram/src/main/java/com/baeldung/telegram/KeyboardFactory.java new file mode 100644 index 0000000000..367c5a4c7c --- /dev/null +++ b/spring-boot-modules/spring-boot-telegram/src/main/java/com/baeldung/telegram/KeyboardFactory.java @@ -0,0 +1,30 @@ +package com.baeldung.telegram; + +import org.telegram.telegrambots.meta.api.objects.replykeyboard.ReplyKeyboard; +import org.telegram.telegrambots.meta.api.objects.replykeyboard.ReplyKeyboardMarkup; +import org.telegram.telegrambots.meta.api.objects.replykeyboard.buttons.KeyboardRow; + +import java.util.List; + +public class KeyboardFactory { +public static ReplyKeyboard getPizzaToppingsKeyboard() { + KeyboardRow row = new KeyboardRow(); + row.add("Margherita"); + row.add("Pepperoni"); + return new ReplyKeyboardMarkup(List.of(row)); +} + + public static ReplyKeyboard getPizzaOrDrinkKeyboard(){ + KeyboardRow row = new KeyboardRow(); + row.add("Pizza"); + row.add("Drink"); + return new ReplyKeyboardMarkup(List.of(row)); + } + + public static ReplyKeyboard getYesOrNo() { + KeyboardRow row = new KeyboardRow(); + row.add("Yes"); + row.add("No"); + return new ReplyKeyboardMarkup(List.of(row)); + } +} diff --git a/spring-boot-modules/spring-boot-telegram/src/main/java/com/baeldung/telegram/PizzaBot.java b/spring-boot-modules/spring-boot-telegram/src/main/java/com/baeldung/telegram/PizzaBot.java new file mode 100644 index 0000000000..802cb586d7 --- /dev/null +++ b/spring-boot-modules/spring-boot-telegram/src/main/java/com/baeldung/telegram/PizzaBot.java @@ -0,0 +1,48 @@ +package com.baeldung.telegram; + +import org.springframework.core.env.Environment; +import org.springframework.stereotype.Component; +import org.telegram.abilitybots.api.bot.AbilityBot; +import org.telegram.abilitybots.api.bot.BaseAbilityBot; +import org.telegram.abilitybots.api.objects.Ability; +import org.telegram.abilitybots.api.objects.Flag; +import org.telegram.abilitybots.api.objects.Reply; +import org.telegram.telegrambots.meta.api.objects.Update; + +import java.util.function.BiConsumer; + +import static org.telegram.abilitybots.api.objects.Locality.USER; +import static org.telegram.abilitybots.api.objects.Privacy.PUBLIC; +import static org.telegram.abilitybots.api.util.AbilityUtils.getChatId; + +@Component +public class PizzaBot extends AbilityBot { + + private final ResponseHandler responseHandler; + + public PizzaBot(Environment environment) { + super(environment.getProperty("BOT_TOKEN"), "baeldungbot"); + responseHandler = new ResponseHandler(silent, db); + } + +public Ability startBot() { + return Ability + .builder() + .name("start") + .info(Constants.START_DESCRIPTION) + .locality(USER) + .privacy(PUBLIC) + .action(ctx -> responseHandler.replyToStart(ctx.chatId())) + .build(); +} + +public Reply replyToButtons() { + BiConsumer action = (abilityBot, upd) -> responseHandler.replyToButtons(getChatId(upd), upd.getMessage()); + return Reply.of(action, Flag.TEXT,upd -> responseHandler.userIsActive(getChatId(upd))); +} + +@Override +public long creatorId() { + return 1L; +} +} diff --git a/spring-boot-modules/spring-boot-telegram/src/main/java/com/baeldung/telegram/ResponseHandler.java b/spring-boot-modules/spring-boot-telegram/src/main/java/com/baeldung/telegram/ResponseHandler.java new file mode 100644 index 0000000000..862edc962c --- /dev/null +++ b/spring-boot-modules/spring-boot-telegram/src/main/java/com/baeldung/telegram/ResponseHandler.java @@ -0,0 +1,132 @@ +package com.baeldung.telegram; + +import org.telegram.abilitybots.api.db.DBContext; +import org.telegram.abilitybots.api.sender.SilentSender; +import org.telegram.telegrambots.meta.api.methods.send.SendMessage; +import org.telegram.telegrambots.meta.api.objects.Message; +import org.telegram.telegrambots.meta.api.objects.replykeyboard.ReplyKeyboard; +import org.telegram.telegrambots.meta.api.objects.replykeyboard.ReplyKeyboardRemove; + +import java.util.Map; + +import static com.baeldung.telegram.Constants.START_TEXT; +import static com.baeldung.telegram.UserState.*; + +public class ResponseHandler { + private final SilentSender sender; + private final Map chatStates; + + public ResponseHandler(SilentSender sender, DBContext db) { + this.sender = sender; + chatStates = db.getMap(Constants.CHAT_STATES); + } + + public void replyToStart(long chatId) { + SendMessage message = new SendMessage(); + message.setChatId(chatId); + message.setText(START_TEXT); + sender.execute(message); + chatStates.put(chatId, AWAITING_NAME); + } + +public void replyToButtons(long chatId, Message message) { + if (message.getText().equalsIgnoreCase("/stop")) { + stopChat(chatId); + } + + switch (chatStates.get(chatId)) { + case AWAITING_NAME -> replyToName(chatId, message); + case FOOD_DRINK_SELECTION -> replyToFoodDrinkSelection(chatId, message); + case PIZZA_TOPPINGS -> replyToPizzaToppings(chatId, message); + case AWAITING_CONFIRMATION -> replyToOrder(chatId, message); + default -> unexpectedMessage(chatId); + } +} + + private void unexpectedMessage(long chatId) { + SendMessage sendMessage = new SendMessage(); + sendMessage.setChatId(chatId); + sendMessage.setText("I did not expect that."); + sender.execute(sendMessage); + } + +private void stopChat(long chatId) { + SendMessage sendMessage = new SendMessage(); + sendMessage.setChatId(chatId); + sendMessage.setText("Thank you for your order. See you soon!\nPress /start to order again"); + chatStates.remove(chatId); + sendMessage.setReplyMarkup(new ReplyKeyboardRemove(true)); + sender.execute(sendMessage); +} + + private void replyToOrder(long chatId, Message message) { + SendMessage sendMessage = new SendMessage(); + sendMessage.setChatId(chatId); + if ("yes".equalsIgnoreCase(message.getText())) { + sendMessage.setText("We will deliver it soon. Thank you!\nOrder another?"); + sendMessage.setReplyMarkup(KeyboardFactory.getPizzaOrDrinkKeyboard()); + sender.execute(sendMessage); + chatStates.put(chatId, FOOD_DRINK_SELECTION); + } else if ("no".equalsIgnoreCase(message.getText())) { + stopChat(chatId); + } else { + sendMessage.setText("Please select yes or no"); + sendMessage.setReplyMarkup(KeyboardFactory.getYesOrNo()); + sender.execute(sendMessage); + } + } + + private void replyToPizzaToppings(long chatId, Message message) { + if ("margherita".equalsIgnoreCase(message.getText())) { + promptWithKeyboardForState(chatId, "You selected Margherita Pizza.\nWe will deliver it soon. Thank you!\nOrder again?", + KeyboardFactory.getYesOrNo(), AWAITING_CONFIRMATION); + } else if ("pepperoni".equalsIgnoreCase(message.getText())) { + promptWithKeyboardForState(chatId, "We finished the Pepperoni Pizza.\nSelect another Topping", + KeyboardFactory.getPizzaToppingsKeyboard(), PIZZA_TOPPINGS); + } else { + SendMessage sendMessage = new SendMessage(); + sendMessage.setChatId(chatId); + sendMessage.setText("We don't sell " + message.getText() + " Pizza.\nSelect the toppings!"); + sendMessage.setReplyMarkup(KeyboardFactory.getPizzaToppingsKeyboard()); + sender.execute(sendMessage); + } + } + + private void promptWithKeyboardForState(long chatId, String text, ReplyKeyboard YesOrNo, UserState awaitingReorder) { + SendMessage sendMessage = new SendMessage(); + sendMessage.setChatId(chatId); + sendMessage.setText(text); + sendMessage.setReplyMarkup(YesOrNo); + sender.execute(sendMessage); + chatStates.put(chatId, awaitingReorder); + } + +private void replyToFoodDrinkSelection(long chatId, Message message) { + SendMessage sendMessage = new SendMessage(); + sendMessage.setChatId(chatId); + if ("drink".equalsIgnoreCase(message.getText())) { + sendMessage.setText("We don't sell drinks.\nBring your own drink!! :)"); + sendMessage.setReplyMarkup(KeyboardFactory.getPizzaOrDrinkKeyboard()); + sender.execute(sendMessage); + } else if ("pizza".equalsIgnoreCase(message.getText())) { + sendMessage.setText("We love Pizza in here.\nSelect the toppings!"); + sendMessage.setReplyMarkup(KeyboardFactory.getPizzaToppingsKeyboard()); + sender.execute(sendMessage); + chatStates.put(chatId, UserState.PIZZA_TOPPINGS); + } else { + sendMessage.setText("We don't sell " + message.getText() + ". Please select from the options below."); + sendMessage.setReplyMarkup(KeyboardFactory.getPizzaOrDrinkKeyboard()); + sender.execute(sendMessage); + } +} + +private void replyToName(long chatId, Message message) { + promptWithKeyboardForState(chatId, "Hello " + message.getText() + ". What would you like to have?", + KeyboardFactory.getPizzaOrDrinkKeyboard(), + UserState.FOOD_DRINK_SELECTION); +} + + public boolean userIsActive(Long chatId) { + return chatStates.containsKey(chatId); + } +} \ No newline at end of file diff --git a/spring-boot-modules/spring-boot-telegram/src/main/java/com/baeldung/telegram/SpringBootTelegramApplication.java b/spring-boot-modules/spring-boot-telegram/src/main/java/com/baeldung/telegram/SpringBootTelegramApplication.java new file mode 100644 index 0000000000..041478f42b --- /dev/null +++ b/spring-boot-modules/spring-boot-telegram/src/main/java/com/baeldung/telegram/SpringBootTelegramApplication.java @@ -0,0 +1,24 @@ +package com.baeldung.telegram; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.ConfigurableApplicationContext; +import org.telegram.abilitybots.api.bot.AbilityBot; +import org.telegram.telegrambots.meta.TelegramBotsApi; +import org.telegram.telegrambots.meta.exceptions.TelegramApiException; +import org.telegram.telegrambots.updatesreceivers.DefaultBotSession; + +@SpringBootApplication +public class SpringBootTelegramApplication { + + public static void main(String[] args) { + ConfigurableApplicationContext ctx = SpringApplication.run(SpringBootTelegramApplication.class, args); + try { + TelegramBotsApi botsApi = new TelegramBotsApi(DefaultBotSession.class); + botsApi.registerBot(ctx.getBean("pizzaBot", AbilityBot.class)); + } catch (TelegramApiException e) { + throw new RuntimeException(e); + } + } + +} diff --git a/spring-boot-modules/spring-boot-telegram/src/main/java/com/baeldung/telegram/UserState.java b/spring-boot-modules/spring-boot-telegram/src/main/java/com/baeldung/telegram/UserState.java new file mode 100644 index 0000000000..a0b53a6a02 --- /dev/null +++ b/spring-boot-modules/spring-boot-telegram/src/main/java/com/baeldung/telegram/UserState.java @@ -0,0 +1,5 @@ +package com.baeldung.telegram; + +public enum UserState { + AWAITING_NAME, FOOD_DRINK_SELECTION, PIZZA_TOPPINGS, AWAITING_CONFIRMATION +} diff --git a/spring-boot-modules/spring-boot-telegram/src/main/resources/application.properties b/spring-boot-modules/spring-boot-telegram/src/main/resources/application.properties new file mode 100644 index 0000000000..6b2753c3a8 --- /dev/null +++ b/spring-boot-modules/spring-boot-telegram/src/main/resources/application.properties @@ -0,0 +1,2 @@ +server.port=8081 +BOT_TOKEN=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX \ No newline at end of file