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); + } + } +}