com.github.kshashov.telegram.TelegramAutoConfiguration Maven / Gradle / Ivy
package com.github.kshashov.telegram;
import com.github.kshashov.telegram.api.TelegramMvcController;
import com.github.kshashov.telegram.api.TelegramSession;
import com.github.kshashov.telegram.config.TelegramBotGlobalProperties;
import com.github.kshashov.telegram.config.TelegramBotGlobalPropertiesConfiguration;
import com.github.kshashov.telegram.config.TelegramBotProperties;
import com.github.kshashov.telegram.handler.*;
import com.github.kshashov.telegram.handler.processor.RequestDispatcher;
import com.github.kshashov.telegram.handler.processor.arguments.BotHandlerMethodArgumentResolver;
import com.github.kshashov.telegram.handler.processor.arguments.BotHandlerMethodArgumentResolverComposite;
import com.github.kshashov.telegram.handler.processor.response.BotHandlerMethodReturnValueHandler;
import com.github.kshashov.telegram.handler.processor.response.BotHandlerMethodReturnValueHandlerComposite;
import com.github.kshashov.telegram.metrics.MetricsConfiguration;
import com.github.kshashov.telegram.metrics.MetricsService;
import com.pengrad.telegrambot.Callback;
import com.pengrad.telegrambot.TelegramBot;
import com.pengrad.telegrambot.request.BaseRequest;
import com.pengrad.telegrambot.response.BaseResponse;
import io.javalin.Javalin;
import lombok.extern.slf4j.Slf4j;
import okhttp3.Dispatcher;
import okhttp3.OkHttpClient;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationListener;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.*;
import org.springframework.context.event.ContextClosedEvent;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.core.env.Environment;
import javax.validation.constraints.NotNull;
import java.io.IOException;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
/**
* Main configuration for telegram mvc.
*/
@Slf4j
@Configuration
@Import({MethodProcessorsConfiguration.class, MetricsConfiguration.class})
@EnableConfigurationProperties(TelegramConfigurationProperties.class)
public class TelegramAutoConfiguration implements BeanFactoryPostProcessor, EnvironmentAware {
private Environment environment;
@Bean
Javalin javalinServer(@Qualifier("telegramBotPropertiesList") List botProperties, TelegramBotGlobalProperties globalProperties) {
boolean hasWebhook = botProperties.stream().anyMatch(p -> p.getWebhook() != null);
if (!hasWebhook) return null;
try {
Javalin server = Javalin.create().start(globalProperties.getWebserverPort());
log.info("Javalin server has been started on {} port", globalProperties.getWebserverPort());
return server;
} catch (Exception ex) {
log.error("An unexpected error occured while starting Javalin server", ex);
return null;
}
}
@Bean
TelegramUpdatesHandler telegramUpdatesHandler(TelegramBotGlobalProperties globalProperties, RequestDispatcher requestDispatcher, MetricsService metricsService) {
return new DefaultTelegramUpdatesHandler(requestDispatcher, globalProperties, metricsService);
}
@Bean
@Qualifier("telegramServicesList")
List telegramServices(@Qualifier("telegramBotPropertiesList") List botProperties, TelegramUpdatesHandler updatesHandler, TelegramBotGlobalProperties globalProperties, Optional server) {
List services = botProperties.stream()
.map(p -> {
// Register TelegramBot bean
TelegramBot bot = p.getBotBuilder().build();
// Let user process bot instance
if (globalProperties.getBotProcessors().containsKey(p.getToken())) {
globalProperties.getBotProcessors().get(p.getToken()).accept(bot);
}
// Create bot service
if (p.getWebhook() != null) {
return new TelegramWebhookService(p, bot, updatesHandler, server.get());
} else {
return new TelegramPollingService(p, bot, updatesHandler);
}
}).collect(Collectors.toList());
if (services.isEmpty()) {
log.error("No bot configurations found");
} else {
log.info("Finished Telegram controllers scanning. Found {} bots", services.size());
}
return services;
}
@Bean
@Qualifier("telegramBotPropertiesList")
List telegramBotPropertiesList(List controllers, TelegramBotGlobalProperties globalProperties) {
return controllers.stream()
.map(TelegramMvcController::getToken)
.distinct()
.map(token -> {
TelegramBotProperties.Builder defaultBuilder = createDefaultBotPropertiesBuilder(token, globalProperties);
if (globalProperties.getBotProperties().containsKey(token)) {
globalProperties.getBotProperties().get(token).accept(defaultBuilder);
}
return defaultBuilder.build();
}).collect(Collectors.toList());
}
@Bean
RequestDispatcher requestDispatcher(
HandlerMethodContainer handlerMethodContainer,
TelegramSessionResolver sessionResolver,
TelegramBotGlobalProperties botGlobalProperties,
MetricsService metricsService) {
BotHandlerMethodArgumentResolverComposite argumentResolver = new BotHandlerMethodArgumentResolverComposite(botGlobalProperties.getArgumentResolvers());
BotHandlerMethodReturnValueHandlerComposite returnValueHandler = new BotHandlerMethodReturnValueHandlerComposite(botGlobalProperties.getReturnValueHandlers());
return new RequestDispatcher(handlerMethodContainer, sessionResolver, argumentResolver, returnValueHandler, metricsService);
}
@Bean
TelegramBotGlobalProperties telegramBotGlobalProperties(
TelegramBotGlobalPropertiesConfiguration botGlobalPropertiesConfiguration,
RequestMappingsMatcherStrategy matcherStrategy,
List argumentResolvers,
List returnValueHandlers,
TelegramConfigurationProperties properties) {
TelegramBotGlobalProperties.Builder defaultBuilder = createDefaultBotGlobalPropertiesBuilder(matcherStrategy, argumentResolvers, returnValueHandlers, properties);
botGlobalPropertiesConfiguration.configure(defaultBuilder);
return defaultBuilder.build();
}
@Bean
TelegramSessionResolver telegramSessionResolver(ApplicationContext context) {
return new TelegramSessionResolver(context);
}
@Bean
@ConditionalOnMissingBean(TelegramBotGlobalPropertiesConfiguration.class)
TelegramBotGlobalPropertiesConfiguration telegramBotGlobalPropertiesConfiguration() {
return builder -> {
};
}
@Bean
@ConditionalOnMissingBean(RequestMappingsMatcherStrategy.class)
RequestMappingsMatcherStrategy defaultHandLerMethodsComparator() {
return new DefaultRequestMappingsMatcherStrategy();
}
@Bean
@Scope(value = TelegramScope.SCOPE, proxyMode = ScopedProxyMode.TARGET_CLASS)
TelegramSession telegramSession() {
return new TelegramSession();
}
@Bean
HandlerMethodContainer handlerMethodContainer() {
return new HandlerMethodContainer();
}
@Bean
TelegramControllerBeanPostProcessor telegramControllerBeanPostProcessor(HandlerMethodContainer handlerMethodContainer, MetricsService metricsService) {
return new TelegramControllerBeanPostProcessor(handlerMethodContainer, metricsService);
}
@Bean
ApplicationListener onContextRefreshed(@Qualifier("telegramServicesList") List telegramServices, TelegramBotGlobalProperties globalProperties, HandlerMethodContainer handlerMethodContainer) {
handlerMethodContainer.setMatcherStrategy(globalProperties.getMatcherStrategy());
return event -> telegramServices.forEach((s) -> globalProperties.getTaskExecutor().execute(s::start));
}
@Bean
ApplicationListener onContextClosed(TelegramBotGlobalProperties globalProperties, @Qualifier("telegramServicesList") List telegramServices) {
return event -> {
telegramServices.forEach(TelegramService::stop);
if (globalProperties.getTaskExecutor() != null) {
log.info("Shutting down ThreadPoolExecutor");
globalProperties.getTaskExecutor().shutdown();
log.info("ThreadPoolExecutor has been shut down");
}
};
}
private TelegramBotGlobalProperties.Builder createDefaultBotGlobalPropertiesBuilder(
@NotNull RequestMappingsMatcherStrategy matcherStrategy,
@NotNull List argumentResolvers,
@NotNull List returnValueHandlers,
@NotNull TelegramConfigurationProperties properties) {
return TelegramBotGlobalProperties.builder()
.matcherStrategy(matcherStrategy)
.argumentResolvers(argumentResolvers)
.returnValueHandlers(returnValueHandlers)
.setWebserverPort(properties.getServerPort())
.taskExecutor(new ThreadPoolExecutor(properties.getCorePoolSize(), properties.getMaxPoolSize(), 0L, TimeUnit.SECONDS, new SynchronousQueue<>()))
.responseCallback(new Callback() {
@Override
public void onResponse(BaseRequest request, BaseResponse response) {
}
@Override
public void onFailure(BaseRequest request, IOException e) {
}
});
}
private TelegramBotProperties.Builder createDefaultBotPropertiesBuilder(@NotNull String token, @NotNull TelegramBotGlobalProperties globalProperties) {
return TelegramBotProperties.builder(token)
.configure(builder -> builder
.apiUrl("https://api.telegram.org/bot")
.updateListenerSleep(environment.getProperty("telegram.bot.update-listener-sleep", Long.class, 300L))
.okHttpClient(new OkHttpClient.Builder()
.dispatcher(new Dispatcher(globalProperties.getTaskExecutor()))
.build()));
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
beanFactory.registerScope(TelegramScope.SCOPE,
new TelegramScope(beanFactory, environment.getProperty("telegram.bot.session-seconds", Integer.class, 3600)));
}
@Override
public void setEnvironment(Environment environment) {
this.environment = environment;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy