diff --git a/api/build.gradle b/api/build.gradle index bad823ac19d26cd4b9f17b6467b948e39535fe43..4ed4fc3a2b728eb7cb19b9df3132e8def80592c9 100644 --- a/api/build.gradle +++ b/api/build.gradle @@ -8,7 +8,13 @@ repositories { mavenCentral() } +def telegram_version = '5.7.1' +def junit_version = '5.8.2' +def lombok_version = '1.18.22' +def log4j_version = '1.2.17' + dependencies { + implementation project(":base") implementation 'org.mongodb:mongodb-driver-sync:4.4.1' implementation 'org.springframework.boot:spring-boot-starter-data-mongodb' implementation 'org.springframework.boot:spring-boot-starter-data-rest' @@ -19,12 +25,26 @@ dependencies { implementation 'org.hibernate:hibernate-core:5.6.5.Final' implementation 'org.hibernate.javax.persistence:hibernate-jpa-2.1-api:1.0.2.Final' implementation 'org.springframework.data:spring-data-mongodb:3.3.1' + + // Telegram api + implementation "org.telegram:telegrambots:$telegram_version" + implementation "org.telegram:telegrambotsextensions:$telegram_version" + + // Lombok + implementation "org.projectlombok:lombok:$lombok_version" + annotationProcessor "org.projectlombok:lombok:$lombok_version" + + // Logger log4j + implementation "log4j:log4j:$log4j_version" + + // Test spring dependencies testImplementation 'org.springframework.boot:spring-boot-starter-test' testImplementation 'de.flapdoodle.embed:de.flapdoodle.embed.mongo' testImplementation 'org.springframework.security:spring-security-test' - testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1' - testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.1' + // Junit tests + testImplementation "org.junit.jupiter:junit-jupiter-api:$junit_version" + testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:$junit_version" } test { diff --git a/api/src/main/java/cz/cvut/fit/sp/bot_builder/text_bot/BaseTextBotHandler.java b/api/src/main/java/cz/cvut/fit/sp/bot_builder/text_bot/BaseTextBotHandler.java new file mode 100644 index 0000000000000000000000000000000000000000..f8c8b0f60ebb6ee85a22fbc7189ea8746816d7d1 --- /dev/null +++ b/api/src/main/java/cz/cvut/fit/sp/bot_builder/text_bot/BaseTextBotHandler.java @@ -0,0 +1,44 @@ +package cz.cvut.fit.sp.bot_builder.text_bot; + +import cz.cvut.fit.base.bot_builder.base.base_bot.BaseTextHandlerInterface; +import cz.cvut.fit.base.bot_builder.base.base_bot.ChatBotTextAnswer; +import cz.cvut.fit.base.bot_builder.base.base_bot.UserRequest; +import lombok.Getter; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; +import lombok.Setter; +import org.telegram.telegrambots.meta.api.methods.send.SendMessage; +import org.telegram.telegrambots.meta.api.objects.Message; + +import java.util.Map; +import java.util.Optional; + +@Getter +@Setter +@RequiredArgsConstructor +public class BaseTextBotHandler implements BaseTextHandlerInterface { + + @NonNull + private Map<UserRequest, ChatBotTextAnswer> botCommands; + + @Override + public Optional<SendMessage> executeRequest(String chatId, String username, Message message) { + // Try to find user request in available requests list + Optional<Map.Entry<UserRequest, ChatBotTextAnswer>> request + = botCommands + .entrySet() + .stream() + .filter((req) -> req.getKey().equals(new UserRequest(message.getText()))).findFirst(); + if (request.isEmpty()) { + // Request not found... + return Optional.empty(); + } + + return produceAnswer(request.get(), chatId); + } + + @Override + public Optional<SendMessage> produceAnswer(Map.Entry<UserRequest, ChatBotTextAnswer> requestOptional, String chatId) { + return Optional.ofNullable(requestOptional.getValue().convertToMessage(chatId)); + } +} diff --git a/api/src/main/java/cz/cvut/fit/sp/bot_builder/text_bot/TelegramTextBot.java b/api/src/main/java/cz/cvut/fit/sp/bot_builder/text_bot/TelegramTextBot.java new file mode 100644 index 0000000000000000000000000000000000000000..80ef185dd676da64dce1bb5ba86539d6b4ebf49d --- /dev/null +++ b/api/src/main/java/cz/cvut/fit/sp/bot_builder/text_bot/TelegramTextBot.java @@ -0,0 +1,54 @@ +package cz.cvut.fit.sp.bot_builder.text_bot; + +import cz.cvut.fit.base.bot_builder.base.base_bot.BaseTelegramBot; +import cz.cvut.fit.base.bot_builder.base.base_bot.ChatBotTextAnswer; +import cz.cvut.fit.base.bot_builder.base.base_bot.UserRequest; +import org.telegram.telegrambots.meta.api.methods.send.SendMessage; +import org.telegram.telegrambots.meta.api.objects.Message; +import org.telegram.telegrambots.meta.api.objects.Update; +import org.telegram.telegrambots.meta.api.objects.User; +import org.telegram.telegrambots.meta.exceptions.TelegramApiException; + +import java.util.HashMap; +import java.util.Map; + +public class TelegramTextBot extends BaseTelegramBot { + + private final BaseTextBotHandler textBotHandler; + + public TelegramTextBot(Map<UserRequest, ChatBotTextAnswer> commands, String botName, String token) { + super(botName, token); + this.textBotHandler = new BaseTextBotHandler(commands); + } + + public TelegramTextBot(String botName, String token) { + super(botName, token); + this.textBotHandler = new BaseTextBotHandler(new HashMap<>()); + } + + @Override + public void processNonCommandUpdate(Update update) { + Message message = update.getMessage(); + + SendMessage answer = textBotHandler + .executeRequest(message.getChatId().toString() + , getUsername(message), message) + .orElse(new SendMessage()); + + try { + execute(answer); + } catch (TelegramApiException e) { + e.printStackTrace(); + } + + } + + private String getUsername(Message message) { + User user = message.getFrom(); + String username = user.getUserName(); + if (username == null) { + return defaultUsername; + } + return username; + } +} diff --git a/api/src/main/java/cz/cvut/fit/sp/bot_builder/text_bot/TextBotFactory.java b/api/src/main/java/cz/cvut/fit/sp/bot_builder/text_bot/TextBotFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..149e0845109acdf8785968ab650557db937a1939 --- /dev/null +++ b/api/src/main/java/cz/cvut/fit/sp/bot_builder/text_bot/TextBotFactory.java @@ -0,0 +1,25 @@ +package cz.cvut.fit.sp.bot_builder.text_bot; + +import cz.cvut.fit.base.bot_builder.base.base_bot.BaseBotFactory; +import cz.cvut.fit.base.bot_builder.base.base_bot.BotType; +import cz.cvut.fit.base.bot_builder.base.base_bot.ChatBotTextAnswer; +import cz.cvut.fit.base.bot_builder.base.base_bot.UserRequest; +import lombok.AllArgsConstructor; +import org.telegram.telegrambots.extensions.bots.commandbot.TelegramLongPollingCommandBot; + +import java.util.Map; + +@AllArgsConstructor +public class TextBotFactory implements BaseBotFactory<TelegramLongPollingCommandBot> { + + private Map<UserRequest, ChatBotTextAnswer> userCommands; + + public TelegramLongPollingCommandBot getBot(BotType type, String botName, String token) { + switch (type) { + case TEXT_BOT: + return new TelegramTextBot(userCommands, botName, token); + default: + throw new IllegalStateException("Such bot type is not acceptable%s".formatted(type)); + } + } +} diff --git a/base/build.gradle b/base/build.gradle index df216b08ccc36da3c74d18d4adc6bf1fb1cdf1e7..3753ad48ee4216d7a1c5de5f3a81eb3f2a2841a2 100644 --- a/base/build.gradle +++ b/base/build.gradle @@ -6,10 +6,25 @@ repositories { mavenCentral() } +def telegram_version = '5.7.1' +def lombok_version = '1.18.10' +def log4j_version = '1.2.17' + dependencies { implementation 'org.hibernate:hibernate-core:5.6.5.Final' implementation 'org.hibernate.javax.persistence:hibernate-jpa-2.1-api:1.0.2.Final' implementation 'org.springframework.data:spring-data-mongodb:3.3.1' + + // Telegram api + implementation "org.telegram:telegrambots:$telegram_version" + implementation "org.telegram:telegrambotsextensions:$telegram_version" + + // Lombok + implementation "org.projectlombok:lombok:$lombok_version" + + // Logger log4j + implementation "log4j:log4j:$log4j_version" + testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1' testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.1' } diff --git a/base/src/main/java/cz/cvut/fit/base/bot_builder/base/base_bot/BaseBotFactory.java b/base/src/main/java/cz/cvut/fit/base/bot_builder/base/base_bot/BaseBotFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..f059286956a0d18717ed3f5fe547664faff0693b --- /dev/null +++ b/base/src/main/java/cz/cvut/fit/base/bot_builder/base/base_bot/BaseBotFactory.java @@ -0,0 +1,7 @@ +package cz.cvut.fit.base.bot_builder.base.base_bot; + +import org.telegram.telegrambots.extensions.bots.commandbot.TelegramLongPollingCommandBot; + +public interface BaseBotFactory<T extends TelegramLongPollingCommandBot> { + T getBot(BotType type, String botName, String token); +} diff --git a/base/src/main/java/cz/cvut/fit/base/bot_builder/base/base_bot/BaseTelegramBot.java b/base/src/main/java/cz/cvut/fit/base/bot_builder/base/base_bot/BaseTelegramBot.java new file mode 100644 index 0000000000000000000000000000000000000000..62882daf1ea10dda614d778fa585b6019782ca2d --- /dev/null +++ b/base/src/main/java/cz/cvut/fit/base/bot_builder/base/base_bot/BaseTelegramBot.java @@ -0,0 +1,48 @@ +package cz.cvut.fit.base.bot_builder.base.base_bot; + +import lombok.Getter; +import lombok.Setter; +import org.telegram.telegrambots.extensions.bots.commandbot.TelegramLongPollingCommandBot; +import org.telegram.telegrambots.meta.api.objects.Update; + +import java.util.List; +import java.util.logging.Logger; + + +@Getter +@Setter +public abstract class BaseTelegramBot extends TelegramLongPollingCommandBot { + + protected static final Logger logger = Logger.getLogger(BaseTelegramBot.class.toString()); + + protected static final String defaultUsername = "Unknown"; + + private final String botName; + private final String token; + + protected BaseTelegramBot(String botName, String token) { + this.botName = botName; + this.token = token; + } + + @Override + public String getBotUsername() { + return botName; + } + + + @Override + public String getBotToken() { + return token; + } + + @Override + public void onRegister() { + super.onRegister(); + } + + @Override + public void onUpdatesReceived(List<Update> updates) { + super.onUpdatesReceived(updates); + } +} diff --git a/base/src/main/java/cz/cvut/fit/base/bot_builder/base/base_bot/BaseTextHandlerInterface.java b/base/src/main/java/cz/cvut/fit/base/bot_builder/base/base_bot/BaseTextHandlerInterface.java new file mode 100644 index 0000000000000000000000000000000000000000..3d13301d88ac8dddd882ca821ad950fe08a51ccf --- /dev/null +++ b/base/src/main/java/cz/cvut/fit/base/bot_builder/base/base_bot/BaseTextHandlerInterface.java @@ -0,0 +1,21 @@ +package cz.cvut.fit.base.bot_builder.base.base_bot; + +import org.telegram.telegrambots.meta.api.methods.send.SendMessage; +import org.telegram.telegrambots.meta.api.objects.Message; + +import java.util.Map; +import java.util.Optional; + +public interface BaseTextHandlerInterface { + /** + * Identify user response and execute its command + * + * @param message - sender's request + */ + Optional<SendMessage> executeRequest(String chatId, String username, Message message); + + /** + * Produce answer for user command + */ + Optional<SendMessage> produceAnswer(Map.Entry<UserRequest, ChatBotTextAnswer> requestOptional, String chatId); +} diff --git a/base/src/main/java/cz/cvut/fit/base/bot_builder/base/base_bot/BotType.java b/base/src/main/java/cz/cvut/fit/base/bot_builder/base/base_bot/BotType.java new file mode 100644 index 0000000000000000000000000000000000000000..fb8418f86f5f6eb4afb826c2230d1d32b2c9f0a0 --- /dev/null +++ b/base/src/main/java/cz/cvut/fit/base/bot_builder/base/base_bot/BotType.java @@ -0,0 +1,5 @@ +package cz.cvut.fit.base.bot_builder.base.base_bot; + +public enum BotType { + TEXT_BOT +} diff --git a/base/src/main/java/cz/cvut/fit/base/bot_builder/base/base_bot/ChatBotTextAnswer.java b/base/src/main/java/cz/cvut/fit/base/bot_builder/base/base_bot/ChatBotTextAnswer.java new file mode 100644 index 0000000000000000000000000000000000000000..0dc8b5c7d64dd86ba316b05891b0b160c7eca089 --- /dev/null +++ b/base/src/main/java/cz/cvut/fit/base/bot_builder/base/base_bot/ChatBotTextAnswer.java @@ -0,0 +1,21 @@ +package cz.cvut.fit.base.bot_builder.base.base_bot; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.Setter; +import org.telegram.telegrambots.meta.api.methods.send.SendMessage; + +import javax.validation.constraints.NotNull; + +@Getter +@Setter +@RequiredArgsConstructor +public record ChatBotTextAnswer(@NotNull String answer) { + public SendMessage convertToMessage(@NotNull String chatId) { + SendMessage msg = new SendMessage(); + msg.setChatId(chatId); + msg.setText(answer); + return msg; + } + +} diff --git a/base/src/main/java/cz/cvut/fit/base/bot_builder/base/base_bot/UserRequest.java b/base/src/main/java/cz/cvut/fit/base/bot_builder/base/base_bot/UserRequest.java new file mode 100644 index 0000000000000000000000000000000000000000..e0923e7b8031fb2418be41d5312a3d03a09bba83 --- /dev/null +++ b/base/src/main/java/cz/cvut/fit/base/bot_builder/base/base_bot/UserRequest.java @@ -0,0 +1,11 @@ +package cz.cvut.fit.base.bot_builder.base.base_bot; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@RequiredArgsConstructor +public record UserRequest(String textRequest) { +} diff --git a/settings.gradle b/settings.gradle index 3a06b4cb4c17cec90c01c5664c847bf0d19cb6b6..513851ed4e29d3b7d3127ea8a73f30d54e2c2ad9 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,4 +1,3 @@ rootProject.name = 'bot_builder' include 'base', 'api', 'authentication' -include 'authentication'