Migrating from 0.0.2 to 0.0.3
Overview
Version 0.0.3 adds edit-message support to all reply types, improves the markup system with automatic state-bound keyboards, refines the handler invocation pipeline, and fixes synchronous Telegram API execution. Two changes require action if you have custom SPI implementations — everything else is backward-compatible.
Breaking Changes
1. @BotDefaultHandler and @BotTextDefault no longer usable on parameters or types
What changed: Both annotations are now restricted to ElementType.METHOD only.
@BotDefaultHandler previously allowed METHOD, PARAMETER, and TYPE.
@BotTextDefault previously allowed METHOD and PARAMETER.
Who is affected: Any code that placed either annotation on a parameter or a class. This was never supported by the framework at runtime; the extra targets were noise.
Action required: Remove the annotation from any non-method targets. If you relied on these as parameter-level hints, use the method-level annotation on the handler method instead.
2. BotHandler.handle(), BotHandlerInvocationFilter.invoke(), and BotHandlerInvocationChain.proceed() now declare checked exceptions
What changed: All three SPI methods now declare
throws InvocationTargetException, IllegalAccessException.
Previously the pipeline swallowed reflection exceptions by wrapping them in
BotHandlerException.
Who is affected: Any code that:
- implements a custom
BotHandler - implements a custom
BotHandlerInvocationFilter - calls
BotHandlerInvocationChain.proceed()directly
Action required: Add throws InvocationTargetException, IllegalAccessException to your
handle() / invoke() / proceed() signatures, or catch and handle the exceptions.
Before (0.0.2):
@Override
public void handle(BotRequest botRequest, BotResponse botResponse) {
// invocation
}
After (0.0.3):
@Override
public void handle(BotRequest botRequest, BotResponse botResponse)
throws InvocationTargetException, IllegalAccessException {
// invocation
}
3. BotHandler SPI gains info() method
What changed: BotHandler now requires an info() method returning a human-readable
description of the handler (used for DEBUG logging in the dispatcher).
Who is affected: Any custom BotHandler implementation.
Action required: Implement info():
@Override
public String info() {
return "MyCustomHandler[" + someDescriptor + "]";
}
New Features
Edit-message support on all reply types
All four reply types now carry an editMessage flag. When set to true and the current
request originates from a callback query, the framework sends
EditMessageText + EditMessageReplyMarkup instead of SendMessage.
@BotCallbackQuery("edit_profile")
public PlainReply onEdit() {
return PlainReply.builder()
.text("Profile updated!")
.editMessage(true) // edits the message the button was on
.build();
}
The wither withEditMessage() is also available for immutable copy-on-modify flows:
return someReply.withEditMessage();
Keyboard constraint in edit context: only InlineKeyboardMarkup is supported by
Telegram's EditMessageReplyMarkup API. Non-inline keyboards are silently skipped.
See Return Types — Edit-Message for full
details and examples.
State-bound keyboard auto-attachment via @BotMarkup + @BotChatState
Placing @BotChatState on a @BotMarkup method inside a @BotConfiguration class
registers the keyboard as the default keyboard for that state. Handlers entering the
state receive it automatically — no explicit @BotReplyMarkup needed.
@BotConfiguration
public class MyKeyboards {
@BotMarkup("registration_kb")
@BotChatState("REGISTRATION")
public ReplyKeyboardMarkup registrationKeyboard() {
return ReplyKeyboardMarkup.builder()...build();
}
}
See Markup System — State-Bound Keyboards for the full guide.
Optional<T> support for all argument resolvers
Every built-in parameter type can now be declared as Optional<T>. The framework resolves
the inner type T and wraps the result in Optional.ofNullable(). If no value is available,
Optional.empty() is injected instead of null.
@BotCommand("/start")
public String start(Optional<User> user) {
return user.map(u -> "Hello, " + u.getFirstName()).orElse("Hello!");
}
@BotText("input")
public String onInput(@BotTextValue Optional<String> text) {
return text.orElse("(no text provided)");
}
Custom resolvers: if you implement BotArgumentResolver with a type-based check, replace
parameter.getType() with ParameterUtils.effectiveType(parameter) to support Optional<T>
declarations automatically. See Parameter Injection — Optional Parameters.
Fixes
Telegram API calls are now synchronous
BotApiMethodsSenderFilter previously called TelegramClient.executeAsync(), which
fires-and-forgets the request. It now calls the synchronous execute(), so Telegram errors
are surfaced immediately and response ordering is preserved within a single update cycle.
No user action required unless you relied on the fire-and-forget behaviour intentionally.
Summary table
| Change | Type | Action required |
|---|---|---|
@BotDefaultHandler / @BotTextDefault targets narrowed to METHOD | Breaking | Remove non-method usages |
handle() / invoke() / proceed() throw checked exceptions | Breaking | Update custom SPI implementations |
BotHandler.info() added to SPI | Breaking | Implement info() in custom handlers |
editMessage flag on PlainReply, PlainTextTemplate, LocalizedReply, LocalizedTemplate | New feature | Optional opt-in |
State-bound keyboard via @BotMarkup + @BotChatState | New feature | Optional opt-in |
Optional<T> wrapping for all parameter types | New feature | None (transparent) |
Custom BotArgumentResolver type checks: use ParameterUtils.effectiveType() | Convention update | Update custom resolvers to support Optional<T> |
| Telegram API execution switched to synchronous | Fix | None |