Long-Polling Transport Guide
The long-polling transport is the default and simplest way to run your bot. It continuously polls the Telegram API for new updates using the getUpdates endpoint — no public HTTPS URL required.
How It Works
- Your bot connects to the Telegram API
- Calls
getUpdateswith a long-timeout (the bot library handles retries automatically) - Receives a batch of updates
- Processes each update through your handler pipeline
- Calls
getUpdatesagain immediately
Configuration
Long-polling is the default transport — omit the update block entirely and your bot uses long-polling automatically.
application.yml (minimal — recommended):
easygram:
token: ${BOT_TOKEN}
application.yml (explicit):
easygram:
token: ${BOT_TOKEN}
update:
transport: LONG_POLLING
application.properties:
easygram.token=${BOT_TOKEN}
# easygram.update.transport=LONG_POLLING # optional; this is the default
Configuration Options
| Property | Required | Default | Description |
|---|---|---|---|
easygram.token | Yes | — | Bot API token from @BotFather |
easygram.update.transport | No | LONG_POLLING | Update delivery mechanism |
Long-polling has no additional Easygram-specific properties. The underlying telegrambots library manages polling timeout and retry logic.
Advantages
- ✅ Zero infrastructure — no HTTPS certificate, no domain, no firewall rules
- ✅ Works locally and behind NAT
- ✅ Ideal for development and testing
- ✅ Simplest setup — just provide a token
Disadvantages
- ⚠️ Higher latency than webhook (one polling cycle between user message and dispatch)
- ⚠️ More Telegram API requests per unit time
- ⚠️ Less suitable for very high-traffic bots
When to Use
| Scenario | Recommendation |
|---|---|
| Local development | ✅ Long-polling |
| Single-instance bots | ✅ Long-polling |
| Behind NAT / corporate firewall | ✅ Long-polling |
| Prototyping / MVP | ✅ Long-polling |
| Production high-traffic | ⚠️ Consider webhook |
| Multi-instance scaling | ⚠️ Consider Kafka/RabbitMQ consumer |
Example: Echo Bot
@SpringBootApplication
public class LongPollingBotApp {
public static void main(String[] args) {
SpringApplication.run(LongPollingBotApp.class, args);
}
}
@BotController
public class EchoBot {
@BotCommand("/start")
public String onStart(User user) {
return "Hello, " + user.getFirstName() + "! Send me any message.";
}
@BotDefaultHandler
public String echo(@BotTextValue String text) {
return "You said: " + text;
}
}
application.yml:
spring:
application:
name: echo-bot
easygram:
token: ${BOT_TOKEN}
logging:
level:
uz.osoncode.easygram: INFO
Run:
export BOT_TOKEN="your_bot_token"
mvn spring-boot:run
Docker Deployment
Dockerfile:
FROM eclipse-temurin:17-jdk-alpine AS build
WORKDIR /build
COPY pom.xml .
COPY src ./src
RUN mvn -q install -DskipTests
FROM eclipse-temurin:17-jre-alpine
RUN addgroup -S bot && adduser -S bot -G bot
USER bot
COPY --from=build /build/target/*.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-XX:+UseContainerSupport", "-XX:MaxRAMPercentage=75", "-jar", "app.jar"]
docker-compose.yml:
services:
bot:
build: .
environment:
easygram.token: ${BOT_TOKEN}
restart: unless-stopped
Spring Boot reads easygram.token, easygram.update.transport, etc. directly from container
environment variables — no uppercase conversion needed. Pass property names exactly as they
appear in application.yml.
Run:
BOT_TOKEN="your_token" docker compose up
Production Considerations
- State persistence — Use Redis-backed
BotChatStateServiceso state survives restarts - Health checks — Expose Spring Actuator (
/actuator/health) for orchestration platforms - Log correlation — MDC keys
bot.update.id,bot.chat.id,bot.user.idare set automatically; configure your Logback pattern to include them - Graceful shutdown — Spring Boot handles
SIGTERM; the bot library drains in-flight updates before stopping - Rate limits — Telegram's
getUpdateshas generous limits; single-instance bots rarely hit them
Next: Learn about Webhook Transport for lower-latency production deployments.