From ad195d8d599ec422b5d256d1a0ec01eb57ee9204 Mon Sep 17 00:00:00 2001 From: chase Date: Wed, 19 Dec 2018 22:09:04 +0100 Subject: [PATCH] Add annotation to allow bot to get botSession A method annotated with @AfterBotRegistration will be invoked after the bot is registered. Optionally the method can have a parameter of type BotSession to get passed the BotSession the bot was created with. --- .../starter/AfterBotRegistration.java | 20 +++++ .../starter/TelegramBotInitializer.java | 59 ++++++++++++- ...stTelegramBotStarterRegistrationHooks.java | 88 +++++++++++++++++++ 3 files changed, 163 insertions(+), 4 deletions(-) create mode 100644 telegrambots-spring-boot-starter/src/main/java/org/telegram/telegrambots/starter/AfterBotRegistration.java create mode 100644 telegrambots-spring-boot-starter/src/test/java/org/telegram/telegrambots/starter/TestTelegramBotStarterRegistrationHooks.java diff --git a/telegrambots-spring-boot-starter/src/main/java/org/telegram/telegrambots/starter/AfterBotRegistration.java b/telegrambots-spring-boot-starter/src/main/java/org/telegram/telegrambots/starter/AfterBotRegistration.java new file mode 100644 index 00000000..f613783b --- /dev/null +++ b/telegrambots-spring-boot-starter/src/main/java/org/telegram/telegrambots/starter/AfterBotRegistration.java @@ -0,0 +1,20 @@ +package org.telegram.telegrambots.starter; + +import org.telegram.telegrambots.meta.TelegramBotsApi; +import org.telegram.telegrambots.meta.generics.BotSession; +import org.telegram.telegrambots.meta.generics.LongPollingBot; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Indicated that the Method of a Class extending {@link LongPollingBot} will be called after the bot was registered + * If the Method has a single Parameter of type {@link BotSession}, the method get passed the bot session the bot was registered with + *

+ *

The bot session passed is the ones returned by {@link TelegramBotsApi#registerBot(LongPollingBot)}

+ */ +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface AfterBotRegistration {} diff --git a/telegrambots-spring-boot-starter/src/main/java/org/telegram/telegrambots/starter/TelegramBotInitializer.java b/telegrambots-spring-boot-starter/src/main/java/org/telegram/telegrambots/starter/TelegramBotInitializer.java index 4d9a1ec7..37b5ed46 100644 --- a/telegrambots-spring-boot-starter/src/main/java/org/telegram/telegrambots/starter/TelegramBotInitializer.java +++ b/telegrambots-spring-boot-starter/src/main/java/org/telegram/telegrambots/starter/TelegramBotInitializer.java @@ -1,13 +1,20 @@ package org.telegram.telegrambots.starter; -import java.util.List; -import java.util.Objects; - import org.springframework.beans.factory.InitializingBean; import org.telegram.telegrambots.meta.TelegramBotsApi; import org.telegram.telegrambots.meta.exceptions.TelegramApiException; +import org.telegram.telegrambots.meta.generics.BotSession; import org.telegram.telegrambots.meta.generics.LongPollingBot; import org.telegram.telegrambots.meta.generics.WebhookBot; +import org.telegram.telegrambots.meta.logging.BotLogger; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.List; +import java.util.Objects; +import java.util.stream.Stream; + +import static java.lang.String.format; /** * Receives all beand which are #LongPollingBot and #WebhookBot and register them in #TelegramBotsApi. @@ -33,7 +40,8 @@ public class TelegramBotInitializer implements InitializingBean { public void afterPropertiesSet() throws Exception { try { for (LongPollingBot bot : longPollingBots) { - telegramBotsApi.registerBot(bot); + BotSession session = telegramBotsApi.registerBot(bot); + handleAfterRegistrationHook(bot, session); } for (WebhookBot bot : webHookBots) { telegramBotsApi.registerBot(bot); @@ -42,4 +50,47 @@ public class TelegramBotInitializer implements InitializingBean { throw new RuntimeException(e); } } + + private void handleAnnotatedMethod(Object bot, Method method, BotSession session) throws InvocationTargetException, IllegalAccessException { + if (method.getParameterCount() > 1) { + BotLogger.warn(this.getClass().getSimpleName(), + format("Method %s of Type %s has too many parameters", + method.getName(), + method.getDeclaringClass().getCanonicalName() + ) + ); + return; + } + if (method.getParameterCount() == 0) { + method.invoke(bot); + return; + } + if (method.getParameterTypes()[0].equals(BotSession.class)) { + method.invoke(bot, session); + return; + } + BotLogger.warn(this.getClass().getSimpleName(), + format("Method %s of Type %s has invalid parameter type", + method.getName(), + method.getDeclaringClass().getCanonicalName() + ) + ); + } + + private void handleAfterRegistrationHook(Object bot, BotSession botSession) { + for (Method m : bot.getClass().getMethods()) { + Stream.of(m.getAnnotations()).forEach(annotation -> System.out.println(annotation.annotationType().getName())); + if (m.getAnnotation(AfterBotRegistration.class) != null) { + try { + handleAnnotatedMethod(bot, m, botSession); + } catch (InvocationTargetException | IllegalAccessException e) { + BotLogger.error(this.getClass().getSimpleName(), + format("Couldn't invoke Method %s of Type %s", + m.getName(), m.getDeclaringClass().getCanonicalName() + ) + ); + } + } + } + } } diff --git a/telegrambots-spring-boot-starter/src/test/java/org/telegram/telegrambots/starter/TestTelegramBotStarterRegistrationHooks.java b/telegrambots-spring-boot-starter/src/test/java/org/telegram/telegrambots/starter/TestTelegramBotStarterRegistrationHooks.java new file mode 100644 index 00000000..aba90dad --- /dev/null +++ b/telegrambots-spring-boot-starter/src/test/java/org/telegram/telegrambots/starter/TestTelegramBotStarterRegistrationHooks.java @@ -0,0 +1,88 @@ +package org.telegram.telegrambots.starter; + + +import org.junit.Test; +import org.springframework.boot.autoconfigure.AutoConfigurations; +import org.springframework.boot.test.context.runner.ApplicationContextRunner; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.telegram.telegrambots.bots.TelegramLongPollingBot; +import org.telegram.telegrambots.meta.TelegramBotsApi; +import org.telegram.telegrambots.meta.api.objects.Update; +import org.telegram.telegrambots.meta.exceptions.TelegramApiRequestException; +import org.telegram.telegrambots.meta.generics.BotSession; +import org.telegram.telegrambots.meta.generics.LongPollingBot; +import org.telegram.telegrambots.updatesreceivers.DefaultBotSession; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.*; + +public class TestTelegramBotStarterRegistrationHooks { + + private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() + .withConfiguration(AutoConfigurations.of(MockTelegramBotsApi.class, TelegramBotStarterConfiguration.class)); + + // Terrible workaround for mockito loosing annotations on methods + private static boolean hookCalled = false; + private static boolean hookCalledWithSession = false; + private static final DefaultBotSession someBotSession = new DefaultBotSession(); + + private static final TelegramBotsApi mockTelegramBotsApi = mock(TelegramBotsApi.class); + + @Test + public void longPollingBotWithAnnotatedMethodshouldBeCalled() throws TelegramApiRequestException { + + when(mockTelegramBotsApi.registerBot(any(LongPollingBot.class))).thenReturn(someBotSession); + + this.contextRunner.withUserConfiguration(LongPollingBotConfig.class) + .run((context) -> { + assertThat(context).hasSingleBean(AnnotatedLongPollingBot.class); + + final LongPollingBot bot = context.getBean(LongPollingBot.class); + final TelegramBotsApi telegramBotsApi = context.getBean(TelegramBotsApi.class); + + assertThat(hookCalled).isTrue(); + assertThat(hookCalledWithSession).isTrue(); + verify(telegramBotsApi, times(1)).registerBot(bot); + verifyNoMoreInteractions(telegramBotsApi); + }); + } + + + @Configuration + static class MockTelegramBotsApi{ + + @Bean + public TelegramBotsApi telegramBotsApi() { + return mockTelegramBotsApi; + } + } + + @Configuration + static class LongPollingBotConfig{ + @Bean + public LongPollingBot longPollingBot() { return new AnnotatedLongPollingBot(); } + } + + static class AnnotatedLongPollingBot extends TelegramLongPollingBot { + + @Override + public void onUpdateReceived(final Update update) {} + + @Override + public String getBotUsername() { return null; } + + @Override + public String getBotToken() { return null; } + + @AfterBotRegistration + public void afterBotHook() { + hookCalled = true; + } + + @AfterBotRegistration + public void afterBotHookWithSession(BotSession session) { + hookCalledWithSession = session.equals(someBotSession); + } + } +}