Router
Hierarchical routers with 25+ Pyrogram event decorators, sub-router nesting, and deferred handler registration.
Basic Usage
from pyrogram import filters
from pyrogram_patch.router import Router
router = Router()
@router.on_message(filters.command("start"))
async def start(client, message):
await message.reply("Hello!")
@router.on_callback_query(filters.regex("^menu_"))
async def handle_menu(client, callback_query):
await callback_query.answer("Selected!")Convenience Decorators
Shorthands for common patterns — no need to import filters:
on_command()
# Matches /start — equivalent to on_message(filters.command("start"))
@router.on_command("start")
async def start(client, message):
await message.reply("Hello!")
# Custom prefix
@router.on_command("help", prefixes="!")
async def help_cmd(client, message):
...on_callback()
# Matches exact callback_data == "profile"
@router.on_callback("profile")
async def show_profile(client, query):
await query.answer("Opening profile...")on_callback_data() v0.5.0
Register a callback query handler with regex capture group injection. Named groups ((?P<name>…)) are extracted from query.data and injected as keyword arguments — similar to FastAPI path parameters.
Named groups
@router.on_callback_data(r"page:(?P<num>\d+)")
async def paginate(client, query, num):
# num is always a string — cast as needed
page = int(num)
await query.answer(f"Page {page}")Positional groups
@router.on_callback_data(r"item:(\d+):(buy|sell)")
async def trade(client, query, group_1, group_2):
item_id = int(group_1) # "123"
action = group_2 # "buy" or "sell"
await query.answer(f"{action} item {item_id}")Tip: For complex structured data, consider CallbackData which handles type coercion and validation automatically.
Sub-Routers
Split handlers across multiple routers for modular bot architecture:
# admin_router.py
admin_router = Router()
@admin_router.on_message(filters.command("ban"))
async def ban_user(client, message):
...
# main.py
main_router = Router()
main_router.include_router(admin_router)
app = KurigramClient("my_bot", ...)
app.include_router(main_router)Context Manager v0.5.0
Use async with for automatic cleanup — handlers are unregistered when the block exits:
async with Router() as router:
@router.on_message()
async def handler(client, message):
...
router.set_client(app)
# ... do work ...
# handlers automatically unregistered hereAvailable Decorators
on_messageon_callback_queryon_inline_queryon_chosen_inline_resulton_edited_messageon_deleted_messageson_chat_member_updatedon_chat_join_requeston_raw_updateon_disconnecton_user_statuson_pollon_storyon_bot_business_connecton_bot_business_messageon_edited_bot_business_messageon_deleted_bot_business_messageson_message_reaction_updatedon_message_reaction_count_updatedon_chat_boost_updatedon_removed_chat_booston_pre_checkout_queryon_shipping_queryon_paid_media_purchasedPlus convenience shorthands: on_command(), on_callback(), on_callback_data()
Handler DI
Handlers receive dependencies via their parameter names. Add a patch_helper: PatchHelperparameter to get FSM and data access:
from pyrogram_patch.patch_helper import PatchHelper
@router.on_message(filters.command("profile"))
async def profile(client, message, patch_helper: PatchHelper):
state = await patch_helper.get_state()
data = await patch_helper.get_data()
await message.reply(f"State: {state}, Data: {data}")Router API
| Property / Method | Description |
|---|---|
.client | Current attached Pyrogram client (or None) |
.is_registered | True if handlers are registered with a client |
.handler_count | Number of registered handlers |
.pending_count | Handlers awaiting registration |
set_client(app) | Attach to a Pyrogram client and register all handlers |
include_router(r) | Add a sub-router |
unregister_handlers() | Remove all handlers from the client |
clear_handlers() | Remove all handlers and sub-routers entirely |
get_stats() | Returns registration statistics dict |