Skip to main content

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

Project description

🔦 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
  • 💬 FSM Toolkit — interactive confirmations, questionnaires, and mockable message flow
  • 🚀 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)

⌨️ 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

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

raito-1.3.6.tar.gz (49.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

raito-1.3.6-py3-none-any.whl (85.2 kB view details)

Uploaded Python 3

File details

Details for the file raito-1.3.6.tar.gz.

File metadata

  • Download URL: raito-1.3.6.tar.gz
  • Upload date:
  • Size: 49.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.11

File hashes

Hashes for raito-1.3.6.tar.gz
Algorithm Hash digest
SHA256 bd82fe276393d404f69492f7200471aa0d9701f49789f58d58370a9cc0bb0600
MD5 8aca338d8a68df25ea63863675e18026
BLAKE2b-256 e0fd1d5a93ca3194ac03565c2ddd25d1346d394d5136c0966be9cfe4e3001f3a

See more details on using hashes here.

File details

Details for the file raito-1.3.6-py3-none-any.whl.

File metadata

  • Download URL: raito-1.3.6-py3-none-any.whl
  • Upload date:
  • Size: 85.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.11

File hashes

Hashes for raito-1.3.6-py3-none-any.whl
Algorithm Hash digest
SHA256 4922a55fb68b3e56604aa33b06f93d0ef8f2c8b5c166d6c589e99f5bec9e1991
MD5 d358055d613a7bb4a18171249b27027b
BLAKE2b-256 2db2b92cfd7373463b8f8185c1ebc565d1cf0fd273c22093dfee59d93b3dd7e7

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page