Skip to main content

🔦 Raito

REPL, hot-reload, keyboards, pagination, and internal dev tools — all in one. That's Raito.

GitHub License GitHub Repo stars PyPI - Version
uv Ruff PyPI - Python Version GitHub Actions Workflow Status

Highlights

  • 🔥 Hot Reload — automatic router loading and file watching for instant development cycles
  • 🎭 Role System — pre-configured roles (owner, support, tester, etc) and selector UI
  • 📚 Pagination — easy pagination over text and media using inline buttons
  • 🎬 Scenes — multi-step dialogs that release request-scoped dependencies after each message
  • 🚀 CLI Generator — $ raito init creates a ready-to-use bot template in seconds
  • ⌨️ Keyboard Factory — static and dynamic generation
  • 🛠️ Command Registration — automatic setup of bot commands with descriptions for each
  • 🖼️ Album Support — groups media albums and passes them to handlers
  • 🛡️ Rate Limiting — apply global or per-command throttling via decorators or middleware
  • 💾 Database Storages — optional JSON & SQL support
  • 🧪 REPL — execute async Python in context (_msg, _user, _raito)
  • 🔍 Params Parser — extracts and validates command arguments
  • ✏️ Logging Formatter — beautiful, readable logs out of the box
  • 📊 Metrics — inspect memory usage, uptime, and caching stats

Installation

pip install -U raito

Quick Start

import asyncio

from aiogram import Bot, Dispatcher
from raito import Raito


async def main() -> None:
    bot = Bot(token="TOKEN")
    dispatcher = Dispatcher()
    raito = Raito(dispatcher, "src/handlers")

    await raito.setup()
    await dispatcher.start_polling(bot)


if __name__ == "__main__":
    asyncio.run(main())

Why Raito?

Raito speeds up your bot development by removing the boring parts — no more boilerplate, no more manual restarts, no more duplicated code across projects.
Everything that used to slow you down is already solved.

Showcases

📦 Handling Commands

You can control access to commands using @rt.roles

The @rt.description decorator adds a description to each command — they will show up in the slash menu automatically.

For commands like /ban 1234, use @rt.params to extract and validate the arguments.

Limit command usage with @rt.limiter and control the rate by mode.

@router.message(filters.Command("ban"), OWNER | ADMINISTRATOR | MODERATOR)
@rt.description("Ban a user")
@rt.limiter(300, mode="chat")
@rt.params(user_id=int)
async def ban(message: types.Message, user_id: int, bot: Bot):
    await bot.ban_chat_member(chat_id=message.chat.id, user_id=user_id)
    await message.answer(text="✅ User banned successfully!")

🔥 Hot-Reload & Router Management

Whenever you change a file with handlers, Raito automatically reloads it without restarting the bot.

You can also manage your routers manually using the .rt load, .rt unload, .rt reload, or .rt routers commands in the bot.

https://github.com/user-attachments/assets/c7ecfb7e-b709-4f92-9de3-efc4982cc926


🎭 Roles

Use built-in roles to set different access levels for team members.

Roles


📚 Pagination

The simplest, most native and most effective pagination. Unlike many other libraries, it does not use internal storage.
It is very user-friendly and fully customizable.

@router.message(filters.Command("pagination"))
async def pagination(message: Message, raito: Raito, bot: Bot):
    if not message.from_user:
        return

    await raito.paginate(
        "button_list",
        chat_id=message.chat.id,
        bot=bot,
        from_user=message.from_user,
        limit=5,
    )


# mock data
BUTTONS = [
    InlineKeyboardButton(text=f"Button #{i}", callback_data=f"button:{i}") for i in range(10000)
]


@rt.on_pagination(router, "button_list")
async def on_pagination(query: CallbackQuery, paginator: InlinePaginator, offset: int, limit: int):
    content = BUTTONS[offset : offset + limit]
    await paginator.answer(text="Here is your buttons:", buttons=content)

🎬 Scenes

Multi-step dialogs where every step is an ordinary handler. Request-scoped dependencies — like an SQLAlchemy AsyncSession — are released between messages instead of held while the user thinks.
Steps are native aiogram states carrying a typed draft in FSM storage. Register them under the router's own event names, drive the flow with scene.next() / scene.finish(), and get inline buttons (@mute.on_callback_query) for free.

class MuteData(SceneData):
    username: str | None = None


class MuteStates(StatesGroup):
    username = State()
    duration = State()


mute = router.scene(MuteStates, data=MuteData)


@mute.on_message.enter(filters.Command("mute"))
async def start(message: Message, scene: Scene[MuteData]):
    await message.answer("Enter username:")
    await scene.next()


@mute.on_message(MuteStates.username, F.text)
async def username(message: Message, scene: Scene[MuteData]):
    scene.data.username = message.text
    await message.answer("Enter duration in minutes:")
    await scene.next()


@mute.on_message(MuteStates.duration, F.text)
async def duration(message: Message, scene: Scene[MuteData], session: AsyncSession):
    await mute_user(session, scene.data.username, int(message.text or 0))
    await message.answer("✅ User muted")
    await scene.finish()

⌨️ Keyboards

Sometimes you want quick layouts. Sometimes — full control. You get both.

Static (layout-based)
@rt.keyboard.static(inline=True)
def information():
    return [
        ("📄 Terms of Service", "tos"),
        [("ℹ️ About", "about"), ("⚙️ Website", "web")],
    ]
Dynamic (builder-based)
@rt.keyboard.dynamic(1, 2, adjust=True, inline=False)
def start_menu(builder: ReplyKeyboardBuilder, app_url: str):
    builder.button(text="📱 Open App", web_app=WebAppInfo(url=app_url))
    builder.button(text="💬 Support")
    builder.button(text="📢 Channel")

🍃 Lifespan

Define startup and shutdown logic in one place.

@rt.lifespan(router)
async def lifespan(bot: Bot):
    user = await bot.get_me()
    rt.debug("🚀 Bot [%s] is starting...", user.full_name)

    yield

    rt.debug("👋🏻 Bye!")

Contributing

Have an idea, found a bug, or want to improve something?
Contributions are welcome! Feel free to open an issue or submit a pull request.

Security

If you discover a security vulnerability, please report it responsibly.
You can open a private GitHub issue or contact the maintainer directly.

There’s no bounty program — this is a solo open source project.
Use in production at your own risk.

For full details, check out the Security Policy.

Questions?

Open an issue or start a discussion in the GitHub Discussions tab.
You can also ping @Aidenable for feedback or ideas.

Alt

GO TOP

Release files for raito 1.5.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for raito 1.5.0
File Size Uploaded
raito-1.5.0.tar.gz 58.0 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for raito 1.5.0
File Interpreter ABI Platform
raito-1.5.0-py3-none-any.whl Python 3 none any Details

Total release size: 157.2 kB

Release files / raito-1.5.0.tar.gz

Download URL raito-1.5.0.tar.gz
Size 58.0 kB
Tags Source
SHA-256 checksum
How to use checksums
67da13cc57128720f74896344353ec3934d497cf5dc4840df9fb8303a2652c27
BLAKE2b-256 checksum
How to use checksums
ae394b75687fec5fd5aa6d92a1b999578f9f77d840c6a0231862f98bffd674d4
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.12.13

Release files / raito-1.5.0-py3-none-any.whl

Download URL raito-1.5.0-py3-none-any.whl
Size 99.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
282f232e9b711143b591c02d217e31c19b4e207c52d31d156ef1f1da50f4bb8b
BLAKE2b-256 checksum
How to use checksums
1084e118767018c013cd5d783f9c719ba4f475cee918472547511800929c068b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.12.13

Release history Release notifications | RSS feed

1.5.1

2 release files

This release

1.5.0 This release

2 release files

1.4.0

2 release files

1.3.7

2 release files

1.3.6

2 release files

1.3.5

2 release files

1.3.4

2 release files

1.3.3

2 release files

1.3.2

2 release files

1.3.1

2 release files

1.3.0

2 release files

1.2.2

2 release files

1.2.1

2 release files

1.2.0

2 release files

1.1.0

2 release files

1.0.0

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page