Skip to content
Open
Show file tree
Hide file tree
Changes from 16 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<groupId>org.telegram</groupId>
<artifactId>Bots</artifactId>
<packaging>pom</packaging>
<version>8.3.0</version>
<version>9.0.0</version>

<modules>
<module>telegrambots-meta</module>
Expand All @@ -20,6 +20,7 @@
<module>telegrambots-extensions</module>
<module>telegrambots-abilities</module>
<module>telegrambots-test-reports</module>
<module>telegrambost-springboot-mapping-starter</module>
</modules>

<licenses>
Expand Down
99 changes: 99 additions & 0 deletions telegrambost-springboot-mapping-starter/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
# Telegram Bot Auto Configuration

## Overview
This module provides auto-configuration for integrating Telegram Long Polling Bots into a Spring Boot application. It simplifies the setup and management of Telegram bots by automatically registering necessary beans and components.

## Features
- Auto-configuration for Telegram Long Polling Bots
- Spring-managed `SpringLongPollingBot` instances
- Centralized `BotRequestProcessor` for handling incoming updates
- Custom annotation-based bot request mapping (`@BotRequestMapping`)
- Integration with `OkHttpClient` for network communication

## Installation
### Maven
Add the following dependency to your `pom.xml`:

```xml
<dependency>
<groupId>org.telegram</groupId>
<artifactId>telegrambost-springboot-mapping-starter</artifactId>
<version>${current.version}</version>
</dependency>
```

### Gradle
Add the dependency to your `build.gradle`:

```gradle
dependencies {
implementation 'org.telegram:telegrambost-springboot-mapping-starter:1.0.0'
}
```

## Configuration
### Properties
Define your bot token in `application.yml`:

```yaml
telegram:
bot:
token: YOUR_BOT_TOKEN
```

or in `application.properties`:

```properties
telegram.bot.token=YOUR_BOT_TOKEN
```

## Usage
### Defining a Bot Request Controller
To handle bot commands, define a class annotated with `@Component` and use `@BotRequestMapping`:

```java
import com.flux.spring.boot.telegram.mapping.annotation.BotRequestMapping;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.telegram.telegrambots.meta.api.objects.Update;

@Slf4j
@Component
public class BotRequestController {

@BotRequestMapping("/start")
public void startCommand(Update update) {
log.info("/start command received from: {}", update.getMessage().getFrom().getUserName());
}

@BotRequestMapping("/help")
public void helpCommand(Update update) {
log.info("/help command received from: {}", update.getMessage().getFrom().getUserName());
}
}
```

## Components
### `SpringLongPollingBot`
This interface is implemented as a Spring Bean and manages long polling updates.

### `BotRequestProcessor`
Handles incoming bot updates and routes them to appropriate handlers.

### `@BotRequestMapping`
Custom annotation for defining bot command handlers.

## Running the Application
Start the Spring Boot application as usual:
```sh
mvn spring-boot:run
```
or
```sh
./gradlew bootRun
```

Once the application is running, the bot will listen for incoming Telegram updates and handle commands accordingly.

## License
This project is licensed under the MIT License.
82 changes: 82 additions & 0 deletions telegrambost-springboot-mapping-starter/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.telegram</groupId>
<artifactId>Bots</artifactId>
<version>8.2.0</version>
</parent>

<artifactId>telegrambost-springboot-mapping-starter</artifactId>

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo: telegrambots


<properties>
<spring-boot.version>3.2.3</spring-boot.version>
<jackson.version>2.17.2</jackson.version>
</properties>

<dependencies>
<dependency>
<groupId>org.telegram</groupId>
<artifactId>telegrambots-springboot-longpolling-starter</artifactId>
<version>${project.version}</version>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
Comment on lines +26 to +29

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

spring-boot-starter dependency is redundant in this module

<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>


<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

<build>
<directory>${project.basedir}/target</directory>
<outputDirectory>${project.build.directory}/classes</outputDirectory>
<finalName>${project.artifactId}-${project.version}</finalName>
<testOutputDirectory>${project.build.directory}/test-classes</testOutputDirectory>
<sourceDirectory>${project.basedir}/src/main/java</sourceDirectory>
<plugins>
<plugin>
<artifactId>maven-clean-plugin</artifactId>
</plugin>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
</plugin>
</plugins>
</build>

</project>

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. Missing value attribute.
  2. I believe the alias should be commands instead of command.

Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package org.telegram.telegrambots.mapping.starter.annotation;

import org.springframework.core.annotation.AliasFor;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface BotRequestMapping {

@AliasFor("command")
String[] value() default {};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package org.telegram.telegrambots.mapping.starter.config;

import lombok.AllArgsConstructor;
import okhttp3.OkHttpClient;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.telegram.telegrambots.longpolling.TelegramBotsLongPollingApplication;
import org.telegram.telegrambots.longpolling.interfaces.LongPollingUpdateConsumer;
import org.telegram.telegrambots.longpolling.starter.SpringLongPollingBot;
import org.telegram.telegrambots.longpolling.starter.TelegramBotInitializer;
import org.telegram.telegrambots.mapping.starter.core.BotRequestProcessor;

import java.util.Collections;
import java.util.List;

@Configuration
@AllArgsConstructor
@EnableConfigurationProperties(TelegramBotProperties.class)
public class TelegramBotAutoConfiguration {

@Bean
@ConditionalOnMissingBean
public BotRequestProcessor botRequestProcessor() {
return new BotRequestProcessor();
}

@Bean
@ConditionalOnMissingBean
public OkHttpClient okHttpClient() {
return new OkHttpClient();
}
Comment on lines +30 to +34

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is OkHttpClient bean created but never used?


@Bean
public SpringLongPollingBot springLongPollingBot(TelegramBotProperties properties,

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would add @ConditionalOnMissingBean for extension opportunity

BotRequestProcessor botRequestProcessor) {
return new SpringLongPollingBot() {
@Override
public String getBotToken() {
return properties.getToken();
}

@Override
public LongPollingUpdateConsumer getUpdatesConsumer() {
return updates -> updates.forEach(botRequestProcessor::handleUpdate);
}
};
}

@Bean
@ConditionalOnMissingBean(TelegramBotsLongPollingApplication.class)
public TelegramBotsLongPollingApplication telegramBotsApplication() {
return new TelegramBotsLongPollingApplication();
}

@Bean
@ConditionalOnMissingBean
public TelegramBotInitializer telegramBotInitializer(TelegramBotsLongPollingApplication telegramBotsApplication,
ObjectProvider<List<SpringLongPollingBot>> longPollingBots) {
return new TelegramBotInitializer(telegramBotsApplication,
longPollingBots.getIfAvailable(Collections::emptyList));
}
Comment on lines +52 to +64

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since telegrambots-springboot-longpolling-starter is transitively included into into telegrambost-springboot-mapping-starter, no need to define TelegramBotsLongPollingApplication & TelegramBotInitializer beans explicitly

}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can be a record

Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package org.telegram.telegrambots.mapping.starter.config;

import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;

@Getter
@Setter
@ConfigurationProperties(prefix = "telegram.bot")
public class TelegramBotProperties {

private String token;
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. Better to leverage ReflectionUtils.doWithMethods & ReflectionUtils.invokeMethod.
  2. Needs unit testing.

Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package org.telegram.telegrambots.mapping.starter.core;

import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;
import org.telegram.telegrambots.mapping.starter.annotation.BotRequestMapping;
import org.telegram.telegrambots.meta.api.objects.Update;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

@Slf4j
@Component
public class BotRequestProcessor implements BeanPostProcessor {

private final Map<String, BotHandlerMethod> handlers = new HashMap<>();

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would replace Map<String, BotHandlerMethod> with Map<String, Consumer<Update>>


@Override
public Object postProcessAfterInitialization(Object bean, @NotNull String beanName) {
for (Method method : bean.getClass().getDeclaredMethods()) {
BotRequestMapping annotation = AnnotationUtils.findAnnotation(method, BotRequestMapping.class);
if (annotation != null) {
for (String command : annotation.value()) {
Copy link

@yvasyliev yvasyliev Jun 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What will happen if a user creates a method annotated with @BotRequestMapping without value?

handlers.put(command, new BotHandlerMethod(bean, method));

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we check if a method has exactly one parameter of Update?

log.debug("✅ Registered command handler '{}': {}.{}", command, bean.getClass().getSimpleName(), method.getName());
}
}
}
return bean;
}

public void handleUpdate(Update update) {
if (update.hasMessage() && update.getMessage().hasText()) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's Message.isCommand() method

String text = update.getMessage().getText().trim();
BotHandlerMethod handlerMethod = handlers.get(text);
if (handlerMethod != null) {
try {
handlerMethod.invoke(update);
} catch (Exception e) {
log.error("Error on calling handler '{}': {}", text, e.getMessage(), e);
}
} else {
log.warn("No handler found for: {}", text);
}
}
}

private record BotHandlerMethod(Object bean, Method method) {

void invoke(Update update) throws Exception {
method.invoke(bean, update);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
com.flux.spring.boot.telegram.mapping.config.TelegramBotAutoConfiguration
6 changes: 3 additions & 3 deletions telegrambots-abilities/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<parent>
<groupId>org.telegram</groupId>
<artifactId>Bots</artifactId>
<version>8.3.0</version>
<version>9.0.0</version>
</parent>

<artifactId>telegrambots-abilities</artifactId>
Expand Down Expand Up @@ -104,12 +104,12 @@
<dependency>
<groupId>org.telegram</groupId>
<artifactId>telegrambots-webhook</artifactId>
<version>8.3.0</version>
<version>9.0.0</version>
</dependency>
<dependency>
<groupId>org.telegram</groupId>
<artifactId>telegrambots-longpolling</artifactId>
<version>8.3.0</version>
<version>9.0.0</version>
</dependency>

<dependency>
Expand Down
2 changes: 1 addition & 1 deletion telegrambots-client-jetty-adapter/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<parent>
<groupId>org.telegram</groupId>
<artifactId>Bots</artifactId>
<version>8.3.0</version>
<version>9.0.0</version>
</parent>

<name>Telegram Bots Client Jetty HttpClient adapter</name>
Expand Down
Loading