Skip to main content
Version: 0.0.6

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

ChangeTypeAction required
@BotDefaultHandler / @BotTextDefault targets narrowed to METHODBreakingRemove non-method usages
handle() / invoke() / proceed() throw checked exceptionsBreakingUpdate custom SPI implementations
BotHandler.info() added to SPIBreakingImplement info() in custom handlers
editMessage flag on PlainReply, PlainTextTemplate, LocalizedReply, LocalizedTemplateNew featureOptional opt-in
State-bound keyboard via @BotMarkup + @BotChatStateNew featureOptional opt-in
Optional<T> wrapping for all parameter typesNew featureNone (transparent)
Custom BotArgumentResolver type checks: use ParameterUtils.effectiveType()Convention updateUpdate custom resolvers to support Optional<T>
Telegram API execution switched to synchronousFixNone