β¨οΈ Keyboard FactoryΒΆ
Raito comes with two decorators for building keyboards declaratively:
@keyboard.staticβ for simple static layouts@keyboard.dynamicβ for flexible builder-style keyboards
No need to manually manage builders or markup objects β just write declarative logic.
@keyboard.staticΒΆ
Use this when you want to declare layout via return value.
from raito import rt
@rt.keyboard.static(inline=False)
def start_markup():
return [
["π Throw a ball"],
[["π FAQ"], ["π Leaderboard"]],
]
Each item is either:
A
strortupleβ becomes a single buttonA list of such items β becomes a row
Tip
Returns a ReplyKeyboardMarkup or InlineKeyboardMarkup depending on inline= param.
@keyboard.dynamicΒΆ
Use this when you need to build keyboards programmatically.
from aiogram.utils.keyboard import InlineKeyboardBuilder
from raito import rt
@rt.keyboard.dynamic(inline=True)
def faq_markup(builder: InlineKeyboardBuilder, tos_url: str, privacy_url: str):
builder.button(text="Terms of Service", url=tos_url)
builder.button(text="Privacy", url=privacy_url)
Tip
You get the builder as the first argument. Everything else is up to you.
ParametersΒΆ
Both decorators accept optional arguments:
inline=Trueβ whether to build inline or reply keyboardadjust=Trueβ whether to auto-adjust layout usingbuilder.adjust()repeat=Trueβ if True,adjust(*sizes)will repeat the pattern*sizesβ pattern for button row sizes (e.g.,2, 2, 1)
@rt.keyboard.dynamic(2, 2, 1, inline=False, repeat=False)
def markup(builder: ReplyKeyboardBuilder):
...
ExamplesΒΆ
Static inline keyboard:ΒΆ
from raito import rt
@rt.keyboard.static()
def info_markup():
return [
[("π¬ Support", "support")],
[("π Privacy", "privacy"), ("π TOS", "terms_of_use")]
]
@router.message(...)
async def handler(message: Message):
await message.answer("Buttons:", reply_markup=info_markup())
Static reply keyboard:ΒΆ
from raito import rt
@rt.keyboard.static(inline=False)
def info_markup():
return [
["π¬ Support"],
[["π Privacy"], ["π TOS"]]
]
@router.message(...)
async def handler(message: Message):
await message.answer("Buttons:", reply_markup=info_markup())
Dynamic inline keyboard:ΒΆ
from aiogram.utils.keyboard import InlineKeyboardBuilder
from raito import rt
@rt.keyboard.dynamic(1, 2)
def info_markup(builder: InlineKeyboardBuilder, privacy_url: str, tos_url: str):
builder.button(text="π¬ Support", callback_data="support")
builder.button(text="π Privacy", url=privacy_url)
builder.button(text="π TOS", url=tos_url)
@router.message(...)
async def handler(message: Message):
await message.answer("Buttons:", reply_markup=info_markup(
privacy_url="https://example.com/privacy",
tos_url="https://example.com/tos",
))
Dynamic reply keyboard:ΒΆ
from aiogram.utils.keyboard import ReplyKeyboardBuilder
from raito import rt
@rt.keyboard.dynamic(1, 2, inline=False)
def info_markup(builder: ReplyKeyboardBuilder):
builder.button(text="π¬ Support")
builder.button(text="π Privacy")
builder.button(text="π TOS")
@router.message(...)
async def handler(message: Message):
await message.answer("Buttons:", reply_markup=info_markup())
Custom adjust:ΒΆ
from aiogram.utils.keyboard import InlineKeyboardBuilder
from raito import rt
@rt.keyboard.dynamic(adjust=False)
def admin_markup(builder: InlineKeyboardBuilder, show_balance_management: bool = False):
adjust = []
builder.button(text="π€ Users", callback_data="users")
adjust.append(1)
if show_balance_management:
builder.button(text="π€ Withdraw", callback_data="withdraw")
builder.button(text="π₯ Deposit", callback_data="deposit")
adjust.append(2)
builder.adjust(*adjust)
@router.message(...)
async def handler(message: Message):
await message.answer("Buttons:", reply_markup=admin_markup(True))