{
  "$type": "site.standard.document",
  "bskyPostRef": {
    "cid": "bafyreibsqe7nli24rr3o4xvz4e3fo5nqonhgtvfhaoqvn5qarhiabvpo3i",
    "uri": "at://did:plc:25rdn5elo5izoxrmtis34zuk/app.bsky.feed.post/3mplhonxcrby2"
  },
  "coverImage": {
    "$type": "blob",
    "ref": {
      "$link": "bafkreifozmaufhcdrnaehruzcd4wr4tsjjxbwvuixilrjoyizzldsj2a6i"
    },
    "mimeType": "image/webp",
    "size": 66178
  },
  "path": "/castanderness/5-telegram-bot-patterns-every-python-developer-should-know-3iip",
  "publishedAt": "2026-07-01T11:43:12.000Z",
  "site": "https://dev.to",
  "tags": [
    "python",
    "telegram",
    "patterns",
    "tutorial",
    "@dp.message",
    "@wraps"
  ],
  "textContent": "#  5 Telegram Bot Patterns Every Python Developer Should Know\n\nAfter building 50+ bots, these patterns solve 90% of problems.\n\n##  1. FSM for Multi-Step Flows\n\n\n    from aiogram.fsm.state import State, StatesGroup\n\n    class OrderFlow(StatesGroup):\n        product = State()\n        quantity = State()\n        address = State()\n\n    @dp.message(Command('order'))\n    async def start_order(message: Message, state: FSMContext):\n        await state.set_state(OrderFlow.product)\n        await message.answer('What product?')\n\n    @dp.message(OrderFlow.product)\n    async def get_product(message: Message, state: FSMContext):\n        await state.update_data(product=message.text)\n        await state.set_state(OrderFlow.quantity)\n        await message.answer('How many?')\n\n\n##  2. Throttle Decorator\n\n\n    from functools import wraps\n    import time\n\n    _last_call = {}\n\n    def throttle(rate_limit: int = 3):\n        def decorator(func):\n            @wraps(func)\n            async def wrapper(message: Message, *args, **kwargs):\n                uid = message.from_user.id\n                now = time.time()\n                if now - _last_call.get(uid, 0) < rate_limit:\n                    await message.answer('Please wait...')\n                    return\n                _last_call[uid] = now\n                return await func(message, *args, **kwargs)\n            return wrapper\n        return decorator\n\n\n##  3. Paginated Results\n\n\n    def paginate(items: list, page: int, per_page: int = 5):\n        start = page * per_page\n        return items[start:start + per_page], len(items) // per_page\n\n    def build_page_kb(page: int, total: int) -> InlineKeyboardMarkup:\n        btns = []\n        if page > 0:\n            btns.append(InlineKeyboardButton('Prev', callback_data=f'page:{page-1}'))\n        if page < total:\n            btns.append(InlineKeyboardButton('Next', callback_data=f'page:{page+1}'))\n        return InlineKeyboardMarkup(inline_keyboard=[btns])\n\n\n##  4. Admin Middleware\n\n\n    from aiogram import BaseMiddleware\n\n    class AdminMiddleware(BaseMiddleware):\n        def __init__(self, admin_ids: list[int]):\n            self.admin_ids = admin_ids\n\n        async def __call__(self, handler, event, data):\n            user = data['event_from_user']\n            if user.id not in self.admin_ids:\n                await event.answer('Admin only')\n                return\n            return await handler(event, data)\n\n    dp.message.middleware(AdminMiddleware([ADMIN_ID]))\n\n\n##  5. Retry on Network Errors\n\n\n    async def safe_send(bot: Bot, chat_id: int, text: str, retries: int = 3):\n        for attempt in range(retries):\n            try:\n                return await bot.send_message(chat_id, text)\n            except TelegramRetryAfter as e:\n                await asyncio.sleep(e.retry_after)\n            except TelegramForbiddenError:\n                db.mark_inactive(chat_id)\n                return None\n            except Exception:\n                if attempt == retries - 1:\n                    raise\n                await asyncio.sleep(2 ** attempt)\n\n\nNeed a production-ready bot with these patterns? Find me on Upwork.\n\nFrom $49 | 2-3 days | Full source code included",
  "title": "5 Telegram Bot Patterns Every Python Developer Should Know"
}