Annotations
Telebof provides a comprehensive set of annotations for declarative handling of Telegram Bot API updates. These annotations offer a clean, type-safe alternative to programmatic handler registration, making your bot code more maintainable and readable.
Annotation Types
All annotations are defined in io.github.natanimn.telebof.annotations
@MessageHandler
Handles incoming messages from users or groups with comprehensive filtering options.
Parameters:
- commands()- Array of bot commands to match
- texts()- Array of exact text content to match
- chatType()- Array of chat types to filter by
- regex()- Regular expression pattern for text matching
- type()- Array of message types to filter by
- filter()- Custom filter class for advanced logic
- state()- Required conversation state
- priority()- Execution priority (lower = earlier)
@EditedMessageHandler
Handles edited messages across various chat types with the same filtering capabilities as @MessageHandler.
Parameters: Same as @MessageHandler
@ChannelPostHandler
Handles incoming channel posts with filtering options tailored for channel content.
Parameters:
- commands()- Array of bot commands to match
- texts()- Array of exact text content to match
- regex()- Regular expression pattern for text matching
- type()- Array of message types to filter by
- filter()- Custom filter class for advanced logic
- priority()- Execution priority
@EditedChannelPostHandler
Handles edited channel posts with comprehensive filtering options.
Parameters: Same as @ChannelPostHandler
@BusinessMessageHandler
Handles business message events with comprehensive filtering options tailored for Telegram Business accounts.
Parameters: Same as @MessageHandler
@EditedBusinessMessageHandler
Handles edited business message events with the same comprehensive filtering capabilities.
Parameters: Same as @MessageHandler
@DeletedBusinessMessageHandler
Handles deleted business message events, particularly useful for audit logging and compliance.
Parameters:
- filter()- Custom filter class for advanced logic
- priority()- Execution priority
@CallbackHandler
Handles incoming callback queries from inline keyboards.
Parameters:
- data()- Array of callback data values to match
- chatType()- Array of chat types to filter by
- regex()- Regular expression pattern for callback data
- filter()- Custom filter class for advanced logic
- state()- Required conversation state
- priority()- Execution priority
@InlineHandler
Handles inline query events with filtering options for query content and chat types.
Parameters:
-query() - Array of query text to match
- chatType() - Array of chat types to filter by
- regex() - Regular expression pattern for query matching
- filter() - Custom filter class for advanced logic
- priority() - Execution priority
@ChosenInlineHandler
Handles chosen inline result events when users select results from inline queries.
Parameters:
- resultId()- Array of result IDs to match
- query()- Array of original query text to match
- filter()- Custom filter class for advanced logic
- priority()- Execution priority
@ChatMemberHandler
Handles chat member status updates for user role changes.
Parameters:
- status()- Array of chat member status changes to match
- filter()- Custom filter class for advanced logic
- priority()- Execution priority
@MyChatMemberHandler
Handles the bot's own chat member status updates.
Parameters:
- status()- Array of status changes to match for the bot
- filter()- Custom filter class for advanced logic
- priority()- Execution priority
@ChatJoinRequestHandler
Handles chat join request events for private groups or channels requiring approval.
Parameters:
- filter()- Custom filter class for advanced logic
- priority()- Execution priority
@PreCheckoutHandler
Handles pre-checkout query events for payment validation.
Parameters:
- payload()- Array of payment payload identifiers to match
- regex()- Regular expression pattern for payload matching
- filter()- Custom filter class for advanced logic
- priority()- Execution priority
@ShippingHandler
Handles shipping query events during checkout processes.
Parameters:
- payload()- Array of shipping query payload identifiers to match
- filter()- Custom filter class for advanced logic
- priority()- Execution priority
@PurchasedPaidMediaHandler
Handles purchased paid media events for content delivery.
Parameters:
- payload()- Array of media purchase payload identifiers to match
- filter()- Custom filter class for advanced logic
- priority()- Execution priority
Reaction and Engagement Annotations
@ReactionHandler
Handles message reaction events when users add or remove reactions.
Parameters:
- reaction()- Specific reaction emoji to match
- filter()- Custom filter class for advanced logic
- priority()- Execution priority
@ReactionCountHandler
Handles reaction count update events for monitoring engagement.
Parameters:
- filter()- Custom filter class for advanced logic
- priority()- Execution priority
Boost and Community Annotations
@ChatBoostHandler
Handles chat boost events for supergroup upgrades and premium features.
Parameters:
- filter()- Custom filter class for advanced logic
- priority()- Execution priority
@RemovedChatBoostHandler
Handles removed chat boost events for monitoring community support patterns.
Parameters:
- filter()- Custom filter class for advanced logic
- priority()- Execution priority
@PollHandler
Handles poll state update events for monitoring poll lifecycle and results.
Parameters:
- filter()- Custom filter class for advanced logic
- priority()- Execution priority
@PollAnswerHandler
Handles poll answer events for processing user responses and voting patterns.
Parameters:
- filter()- Custom filter class for advanced logic
- priority()- Execution priority
@BusinessConnectionHandler
Handles business connection events for monitoring account connections and authorization.
Parameters:
- filter()- Custom filter class for advanced logic
- priority()- Execution priority
Usage Examples
Basic Message Handler
@MessageHandler(commands = "start", chatType = ChatType.PRIVATE)
void handleStartCommand(BotContext context, Message message) {
    context.sendMessage(message.chat.id, "Welcome!").exec();
}
Advanced Filtering with Custom Logic
@MessageHandler(
    commands = {"help", "support"},
    chatType = ChatType.PRIVATE,
    filter = PremiumUserFilter.class,
    state = "awaiting_support"
)
void premiumSupport(BotContext context, Message message) {
    // Handle premium support requests
}
Multiple Handlers with Priorities
@MessageHandler(commands = "stats")
void showStats(BotContext context, Message message) {
    // handle stats command
}
@MessageHandler(priority = 1)
void logMessage(BotContext context, Message message) {
    // Log all messages except /stats command
}
Common Patterns
Repeatable Annotations
All handler annotations are @Repeatable, allowing multiple handlers on the same method:
@MessageHandler(commands = "start", type = ChatType.PRIVATE)
@MessageHandler(commands = "help")
void handleStartAndHelp(BotContext context, Message message) {
    context.sendMessage(message.chat.id, "Welcome to help!").exec();
}
Custom Filter Implementation
public class PremiumUserFilter implements CustomFilter {
    @Override
    public boolean check(Update update) {
        return update.message.from.is_premium != null;
    }
}
Finally, you must add the class you registered handler in by using addHandler method
Priority System
- Lower priority numbers execute before higher numbers
- Negative values are allowed and execute even earlier
- Default priority is 0
- Useful for controlling execution order when multiple handlers match the same update with the same content
The Critical Importance of Priority in Handler Execution
The Fundamental Problem: JVM Method Ordering
Java's getDeclaredMethods() returns methods in an unspecified, non-deterministic order that can vary across:
- Different JVM implementations (OpenJDK vs Oracle JDK)
- Different versions of the same JVM
- Different runs of the same application
- Different class loading scenarios
- Code changes and recompilations
This complete unpredictability makes handler execution order random without explicit priority control.
The Dangerous Reality
The Problematic Code
@MessageHandler(commands = "start")
void start(BotContext context, Message message) {
    context.sendMessage(message.chat.id, "Welcome message!").exec();
}
@MessageHandler(commands = "help") 
void help(BotContext context, Message message) {
    context.sendMessage(message.chat.id, "Detailed help content").exec();
}
@MessageHandler(regex = "(start|help)")
void regex(BotContext context, Message message) {
    context.sendMessage(message.chat.id, "Generic help response").exec();
}
What Could Happen
- Development environment: start→help→regex(as declared)
- Production environment: regex→start→help(JVM reordering)
- After hot reload: help→regex→start(complete reshuffle)
- Different server: regexexecutes first and claims all/startand/helpcommands
The catastrophic result:
The regex handler might execute first and handle both /start and /help commands, completely blocking the specific handlers from ever running!
Why Priority is the Only Solution
The Working Solution
@MessageHandler(commands = "start") // priority = 0 (default)
void start(BotContext context, Message message) {
    context.sendMessage(message.chat.id, "Welcome message!").exec();
}
@MessageHandler(commands = "help") // priority = 0 (default)
void help(BotContext context, Message message) {
    context.sendMessage(message.chat.id, "Detailed help content").exec();
}
@MessageHandler(regex = "(start|help)", priority = 1) // Explicit higher priority
void regex(BotContext context, Message message) {
    context.sendMessage(message.chat.id, "Generic help response").exec();
}
How Priority Guarantees Order
Execution is sorted by priority in ascending order:
- All priority = 0 handlers (startandhelp)
- All priority = 1 handlers (regex)
Within same priority (like start and help both at priority=0), the order is still unpredictable, but this is acceptable because:
- They handle different commands (/startvs/help)
- They are mutually exclusive (no overlap)
- No risk of one blocking the other
Key Characteristics:
- Lower numbers execute first (-100 before 0 before 1 before 100)
- Same priority order is unpredictable but safe for non-conflicting handlers
- Small differences work (priority=1 always executes after priority=0)
- Complete control over which handler "wins" for overlapping filters
The Non-Negotiable Rule
Always use explicit priorities when handlers might match the same update with the same content.
The only safe time to rely on default priority=0 is when:
- Handlers are completely mutually exclusive, AND
- There's no risk of one handler blocking another, AND
- You're willing to accept unpredictable order among them
For any potential overlap or conflict, explicit priority control is mandatory.