[{"data":1,"prerenderedAt":1145},["ShallowReactive",2],{"blog-ru-aiogram-3-tutorial-2025":3},{"id":4,"title":5,"body":6,"date":1129,"description":1130,"extension":1131,"meta":1132,"navigation":196,"path":1133,"readTime":1134,"seo":1135,"slug":1136,"stem":1137,"tags":1138,"__hash__":1144},"blogRu\u002Fru\u002Fblog\u002Faiogram-3-tutorial-2025.md","Aiogram 3: туториал по созданию Telegram-бота на Python (2025)",{"type":7,"value":8,"toc":1117},"minimark",[9,13,18,21,68,75,79,104,119,122,130,136,154,158,267,274,278,289,358,386,389,393,400,480,487,491,550,606,613,617,620,659,833,847,851,899,902,906,909,989,1045,1048,1052,1055,1095,1110,1113],[10,11,12],"p",{},"Aiogram 3 — это асинхронный Python-фреймворк для Telegram-ботов. Я использовал его в 20+ продакшн-проектах. Вот всё, что нужно чтобы пройти путь от нуля до запущенного бота, не продираясь через устаревшие туториалы.",[14,15,17],"h2",{"id":16},"почему-именно-aiogram-3","Почему именно Aiogram 3",[10,19,20],{},"Есть несколько Python-библиотек для Telegram-ботов. Aiogram 3 выигрывает для продакшна потому что:",[22,23,24,32,38,56,62],"ul",{},[25,26,27,31],"li",{},[28,29,30],"strong",{},"Async-first"," — построен на asyncio, обрабатывает тысячи обновлений эффективно",[25,33,34,37],{},[28,35,36],{},"Система роутеров"," — разделяйте хендлеры по модулям чисто и аккуратно",[25,39,40,43,44,48,49,48,52,55],{},[28,41,42],{},"Magic filters"," — ",[45,46,47],"code",{},"F.text == \"привет\"",", ",[45,50,51],{},"F.photo",[45,53,54],{},"F.from_user.id == 123456"," — выразительно и читаемо",[25,57,58,61],{},[28,59,60],{},"FSM"," — встроенный конечный автомат для многошаговых диалогов",[25,63,64,67],{},[28,65,66],{},"Типобезопасность"," — полная типизация во всём фреймворке",[10,69,70,71,74],{},"Старый ",[45,72,73],{},"python-telegram-bot"," тоже работает, но архитектура Aiogram 3 чище для любого проекта сложнее игрушечного.",[14,76,78],{"id":77},"установка","Установка",[80,81,86],"pre",{"className":82,"code":83,"language":84,"meta":85,"style":85},"language-bash shiki shiki-themes github-light github-dark","pip install aiogram\n","bash","",[45,87,88],{"__ignoreMap":85},[89,90,93,97,101],"span",{"class":91,"line":92},"line",1,[89,94,96],{"class":95},"sScJk","pip",[89,98,100],{"class":99},"sZZnC"," install",[89,102,103],{"class":99}," aiogram\n",[10,105,106,107,114,115,118],{},"Получите токен бота у ",[108,109,113],"a",{"href":110,"rel":111},"https:\u002F\u002Ft.me\u002FBotFather",[112],"nofollow","@BotFather"," — отправьте ",[45,116,117],{},"\u002Fnewbot",", следуйте инструкциям.",[10,120,121],{},"Структура проекта, которую я использую:",[80,123,128],{"className":124,"code":126,"language":127},[125],"language-text","mybot\u002F\n├── bot.py          # точка входа\n├── config.py       # токен + настройки\n├── handlers\u002F\n│   ├── start.py\n│   └── orders.py\n├── keyboards\u002F\n│   └── kb.py\n└── states\u002F\n    └── states.py\n","text",[45,129,126],{"__ignoreMap":85},[10,131,132,135],{},[45,133,134],{},"config.py",":",[80,137,141],{"className":138,"code":139,"language":140,"meta":85,"style":85},"language-python shiki shiki-themes github-light github-dark","import os\nBOT_TOKEN = os.getenv(\"BOT_TOKEN\", \"your_token_here\")\n","python",[45,142,143,148],{"__ignoreMap":85},[89,144,145],{"class":91,"line":92},[89,146,147],{},"import os\n",[89,149,151],{"class":91,"line":150},2,[89,152,153],{},"BOT_TOKEN = os.getenv(\"BOT_TOKEN\", \"your_token_here\")\n",[14,155,157],{"id":156},"первый-бот-хендлер-start","Первый бот: хендлер \u002Fstart",[80,159,161],{"className":138,"code":160,"language":140,"meta":85,"style":85},"# bot.py\nimport asyncio\nfrom aiogram import Bot, Dispatcher\nfrom aiogram.filters import CommandStart\nfrom aiogram.types import Message\n\nbot = Bot(token=\"YOUR_TOKEN\")\ndp = Dispatcher()\n\n@dp.message(CommandStart())\nasync def cmd_start(message: Message):\n    await message.answer(f\"Привет, {message.from_user.first_name}!\")\n\nasync def main():\n    await dp.start_polling(bot)\n\nif __name__ == \"__main__\":\n    asyncio.run(main())\n",[45,162,163,168,173,179,185,191,198,204,210,215,221,227,233,238,244,250,255,261],{"__ignoreMap":85},[89,164,165],{"class":91,"line":92},[89,166,167],{},"# bot.py\n",[89,169,170],{"class":91,"line":150},[89,171,172],{},"import asyncio\n",[89,174,176],{"class":91,"line":175},3,[89,177,178],{},"from aiogram import Bot, Dispatcher\n",[89,180,182],{"class":91,"line":181},4,[89,183,184],{},"from aiogram.filters import CommandStart\n",[89,186,188],{"class":91,"line":187},5,[89,189,190],{},"from aiogram.types import Message\n",[89,192,194],{"class":91,"line":193},6,[89,195,197],{"emptyLinePlaceholder":196},true,"\n",[89,199,201],{"class":91,"line":200},7,[89,202,203],{},"bot = Bot(token=\"YOUR_TOKEN\")\n",[89,205,207],{"class":91,"line":206},8,[89,208,209],{},"dp = Dispatcher()\n",[89,211,213],{"class":91,"line":212},9,[89,214,197],{"emptyLinePlaceholder":196},[89,216,218],{"class":91,"line":217},10,[89,219,220],{},"@dp.message(CommandStart())\n",[89,222,224],{"class":91,"line":223},11,[89,225,226],{},"async def cmd_start(message: Message):\n",[89,228,230],{"class":91,"line":229},12,[89,231,232],{},"    await message.answer(f\"Привет, {message.from_user.first_name}!\")\n",[89,234,236],{"class":91,"line":235},13,[89,237,197],{"emptyLinePlaceholder":196},[89,239,241],{"class":91,"line":240},14,[89,242,243],{},"async def main():\n",[89,245,247],{"class":91,"line":246},15,[89,248,249],{},"    await dp.start_polling(bot)\n",[89,251,253],{"class":91,"line":252},16,[89,254,197],{"emptyLinePlaceholder":196},[89,256,258],{"class":91,"line":257},17,[89,259,260],{},"if __name__ == \"__main__\":\n",[89,262,264],{"class":91,"line":263},18,[89,265,266],{},"    asyncio.run(main())\n",[10,268,269,270,273],{},"Запустите, отправьте ",[45,271,272],{},"\u002Fstart"," своему боту. Это база.",[14,275,277],{"id":276},"роутеры-держим-код-в-порядке","Роутеры: держим код в порядке",[10,279,280,281,284,285,288],{},"В Aiogram 3 ",[45,282,283],{},"Router"," — ключевая абстракция. Вместо того чтобы регистрировать всё на ",[45,286,287],{},"Dispatcher",", создавайте роутеры на каждый модуль и подключайте их:",[80,290,292],{"className":138,"code":291,"language":140,"meta":85,"style":85},"# handlers\u002Fstart.py\nfrom aiogram import Router, F\nfrom aiogram.filters import CommandStart\nfrom aiogram.types import Message\n\nrouter = Router()\n\n@router.message(CommandStart())\nasync def cmd_start(message: Message):\n    await message.answer(\"Добро пожаловать!\")\n\n@router.message(F.text == \"помощь\")\nasync def help_text(message: Message):\n    await message.answer(\"Вот как работает этот бот...\")\n",[45,293,294,299,304,308,312,316,321,325,330,334,339,343,348,353],{"__ignoreMap":85},[89,295,296],{"class":91,"line":92},[89,297,298],{},"# handlers\u002Fstart.py\n",[89,300,301],{"class":91,"line":150},[89,302,303],{},"from aiogram import Router, F\n",[89,305,306],{"class":91,"line":175},[89,307,184],{},[89,309,310],{"class":91,"line":181},[89,311,190],{},[89,313,314],{"class":91,"line":187},[89,315,197],{"emptyLinePlaceholder":196},[89,317,318],{"class":91,"line":193},[89,319,320],{},"router = Router()\n",[89,322,323],{"class":91,"line":200},[89,324,197],{"emptyLinePlaceholder":196},[89,326,327],{"class":91,"line":206},[89,328,329],{},"@router.message(CommandStart())\n",[89,331,332],{"class":91,"line":212},[89,333,226],{},[89,335,336],{"class":91,"line":217},[89,337,338],{},"    await message.answer(\"Добро пожаловать!\")\n",[89,340,341],{"class":91,"line":223},[89,342,197],{"emptyLinePlaceholder":196},[89,344,345],{"class":91,"line":229},[89,346,347],{},"@router.message(F.text == \"помощь\")\n",[89,349,350],{"class":91,"line":235},[89,351,352],{},"async def help_text(message: Message):\n",[89,354,355],{"class":91,"line":240},[89,356,357],{},"    await message.answer(\"Вот как работает этот бот...\")\n",[80,359,361],{"className":138,"code":360,"language":140,"meta":85,"style":85},"# bot.py\nfrom handlers import start, orders\n\ndp.include_router(start.router)\ndp.include_router(orders.router)\n",[45,362,363,367,372,376,381],{"__ignoreMap":85},[89,364,365],{"class":91,"line":92},[89,366,167],{},[89,368,369],{"class":91,"line":150},[89,370,371],{},"from handlers import start, orders\n",[89,373,374],{"class":91,"line":175},[89,375,197],{"emptyLinePlaceholder":196},[89,377,378],{"class":91,"line":181},[89,379,380],{},"dp.include_router(start.router)\n",[89,382,383],{"class":91,"line":187},[89,384,385],{},"dp.include_router(orders.router)\n",[10,387,388],{},"Это сохраняет больших ботов поддерживаемыми. Каждый файл хендлеров остаётся сфокусированным.",[14,390,392],{"id":391},"magic-filters-f","Magic filters: F.",[10,394,395,396,399],{},"Объект ",[45,397,398],{},"F"," — то, где Aiogram 3 особенно хорош:",[80,401,403],{"className":138,"code":402,"language":140,"meta":85,"style":85},"from aiogram import F\n\n# Точное совпадение текста\n@router.message(F.text == \"Купить\")\n\n# Сообщения с фото\n@router.message(F.photo)\n\n# Конкретный пользователь\n@router.message(F.from_user.id == 123456789)\n\n# Комбинирование фильтров\n@router.message(F.text.startswith(\"заказ:\") & F.from_user.is_premium)\n\n# Отрицание\n@router.message(~F.text.in_({\"стоп\", \"отмена\"}))\n",[45,404,405,410,414,419,424,428,433,438,442,447,452,456,461,466,470,475],{"__ignoreMap":85},[89,406,407],{"class":91,"line":92},[89,408,409],{},"from aiogram import F\n",[89,411,412],{"class":91,"line":150},[89,413,197],{"emptyLinePlaceholder":196},[89,415,416],{"class":91,"line":175},[89,417,418],{},"# Точное совпадение текста\n",[89,420,421],{"class":91,"line":181},[89,422,423],{},"@router.message(F.text == \"Купить\")\n",[89,425,426],{"class":91,"line":187},[89,427,197],{"emptyLinePlaceholder":196},[89,429,430],{"class":91,"line":193},[89,431,432],{},"# Сообщения с фото\n",[89,434,435],{"class":91,"line":200},[89,436,437],{},"@router.message(F.photo)\n",[89,439,440],{"class":91,"line":206},[89,441,197],{"emptyLinePlaceholder":196},[89,443,444],{"class":91,"line":212},[89,445,446],{},"# Конкретный пользователь\n",[89,448,449],{"class":91,"line":217},[89,450,451],{},"@router.message(F.from_user.id == 123456789)\n",[89,453,454],{"class":91,"line":223},[89,455,197],{"emptyLinePlaceholder":196},[89,457,458],{"class":91,"line":229},[89,459,460],{},"# Комбинирование фильтров\n",[89,462,463],{"class":91,"line":235},[89,464,465],{},"@router.message(F.text.startswith(\"заказ:\") & F.from_user.is_premium)\n",[89,467,468],{"class":91,"line":240},[89,469,197],{"emptyLinePlaceholder":196},[89,471,472],{"class":91,"line":246},[89,473,474],{},"# Отрицание\n",[89,476,477],{"class":91,"line":252},[89,478,479],{},"@router.message(~F.text.in_({\"стоп\", \"отмена\"}))\n",[10,481,482,483,486],{},"Больше никаких ",[45,484,485],{},"if message.text == \"Купить\":"," внутри хендлеров. Фильтры объявлены декларативно — их легко читать и комбинировать.",[14,488,490],{"id":489},"инлайн-клавиатуры-и-колбэки","Инлайн-клавиатуры и колбэки",[80,492,494],{"className":138,"code":493,"language":140,"meta":85,"style":85},"# keyboards\u002Fkb.py\nfrom aiogram.utils.keyboard import InlineKeyboardBuilder\nfrom aiogram.types import InlineKeyboardMarkup\n\ndef main_kb() -> InlineKeyboardMarkup:\n    b = InlineKeyboardBuilder()\n    b.button(text=\"📦 Товары\", callback_data=\"products\")\n    b.button(text=\"📋 Мои заказы\", callback_data=\"my_orders\")\n    b.button(text=\"💬 Контакт\", url=\"https:\u002F\u002Ft.me\u002Fyourusername\")\n    b.adjust(2, 1)\n    return b.as_markup()\n",[45,495,496,501,506,511,515,520,525,530,535,540,545],{"__ignoreMap":85},[89,497,498],{"class":91,"line":92},[89,499,500],{},"# keyboards\u002Fkb.py\n",[89,502,503],{"class":91,"line":150},[89,504,505],{},"from aiogram.utils.keyboard import InlineKeyboardBuilder\n",[89,507,508],{"class":91,"line":175},[89,509,510],{},"from aiogram.types import InlineKeyboardMarkup\n",[89,512,513],{"class":91,"line":181},[89,514,197],{"emptyLinePlaceholder":196},[89,516,517],{"class":91,"line":187},[89,518,519],{},"def main_kb() -> InlineKeyboardMarkup:\n",[89,521,522],{"class":91,"line":193},[89,523,524],{},"    b = InlineKeyboardBuilder()\n",[89,526,527],{"class":91,"line":200},[89,528,529],{},"    b.button(text=\"📦 Товары\", callback_data=\"products\")\n",[89,531,532],{"class":91,"line":206},[89,533,534],{},"    b.button(text=\"📋 Мои заказы\", callback_data=\"my_orders\")\n",[89,536,537],{"class":91,"line":212},[89,538,539],{},"    b.button(text=\"💬 Контакт\", url=\"https:\u002F\u002Ft.me\u002Fyourusername\")\n",[89,541,542],{"class":91,"line":217},[89,543,544],{},"    b.adjust(2, 1)\n",[89,546,547],{"class":91,"line":223},[89,548,549],{},"    return b.as_markup()\n",[80,551,553],{"className":138,"code":552,"language":140,"meta":85,"style":85},"# handler\nfrom aiogram.types import CallbackQuery\n\n@router.message(CommandStart())\nasync def cmd_start(message: Message):\n    await message.answer(\"Выберите:\", reply_markup=main_kb())\n\n@router.callback_query(F.data == \"products\")\nasync def show_products(callback: CallbackQuery):\n    await callback.answer()\n    await callback.message.edit_text(\"Вот наши товары...\", reply_markup=products_kb())\n",[45,554,555,560,565,569,573,577,582,586,591,596,601],{"__ignoreMap":85},[89,556,557],{"class":91,"line":92},[89,558,559],{},"# handler\n",[89,561,562],{"class":91,"line":150},[89,563,564],{},"from aiogram.types import CallbackQuery\n",[89,566,567],{"class":91,"line":175},[89,568,197],{"emptyLinePlaceholder":196},[89,570,571],{"class":91,"line":181},[89,572,329],{},[89,574,575],{"class":91,"line":187},[89,576,226],{},[89,578,579],{"class":91,"line":193},[89,580,581],{},"    await message.answer(\"Выберите:\", reply_markup=main_kb())\n",[89,583,584],{"class":91,"line":200},[89,585,197],{"emptyLinePlaceholder":196},[89,587,588],{"class":91,"line":206},[89,589,590],{},"@router.callback_query(F.data == \"products\")\n",[89,592,593],{"class":91,"line":212},[89,594,595],{},"async def show_products(callback: CallbackQuery):\n",[89,597,598],{"class":91,"line":217},[89,599,600],{},"    await callback.answer()\n",[89,602,603],{"class":91,"line":223},[89,604,605],{},"    await callback.message.edit_text(\"Вот наши товары...\", reply_markup=products_kb())\n",[10,607,608,609,612],{},"Всегда вызывайте ",[45,610,611],{},"callback.answer()"," первой — без неё кнопка остаётся в состоянии загрузки 30 секунд.",[14,614,616],{"id":615},"fsm-многошаговые-диалоги","FSM: многошаговые диалоги",[10,618,619],{},"FSM (конечный автомат) — это то, как обрабатывать сценарии, которые длятся несколько сообщений: формы заказа, регистрация, опросы.",[80,621,623],{"className":138,"code":622,"language":140,"meta":85,"style":85},"# states\u002Fstates.py\nfrom aiogram.fsm.state import State, StatesGroup\n\nclass OrderForm(StatesGroup):\n    waiting_name = State()\n    waiting_phone = State()\n    waiting_description = State()\n",[45,624,625,630,635,639,644,649,654],{"__ignoreMap":85},[89,626,627],{"class":91,"line":92},[89,628,629],{},"# states\u002Fstates.py\n",[89,631,632],{"class":91,"line":150},[89,633,634],{},"from aiogram.fsm.state import State, StatesGroup\n",[89,636,637],{"class":91,"line":175},[89,638,197],{"emptyLinePlaceholder":196},[89,640,641],{"class":91,"line":181},[89,642,643],{},"class OrderForm(StatesGroup):\n",[89,645,646],{"class":91,"line":187},[89,647,648],{},"    waiting_name = State()\n",[89,650,651],{"class":91,"line":193},[89,652,653],{},"    waiting_phone = State()\n",[89,655,656],{"class":91,"line":200},[89,657,658],{},"    waiting_description = State()\n",[80,660,662],{"className":138,"code":661,"language":140,"meta":85,"style":85},"# handlers\u002Forders.py\nfrom aiogram.fsm.context import FSMContext\nfrom states.states import OrderForm\n\n@router.callback_query(F.data == \"new_order\")\nasync def start_order(callback: CallbackQuery, state: FSMContext):\n    await state.set_state(OrderForm.waiting_name)\n    await callback.answer()\n    await callback.message.answer(\"Как вас зовут?\")\n\n@router.message(OrderForm.waiting_name)\nasync def got_name(message: Message, state: FSMContext):\n    await state.update_data(name=message.text)\n    await state.set_state(OrderForm.waiting_phone)\n    await message.answer(\"Ваш номер телефона:\")\n\n@router.message(OrderForm.waiting_phone)\nasync def got_phone(message: Message, state: FSMContext):\n    await state.update_data(phone=message.text)\n    await state.set_state(OrderForm.waiting_description)\n    await message.answer(\"Опишите задачу:\")\n\n@router.message(OrderForm.waiting_description)\nasync def got_description(message: Message, state: FSMContext):\n    data = await state.get_data()\n    await state.clear()\n    await message.answer(\n        f\"✅ Заказ принят!\\n\"\n        f\"Имя: {data['name']}\\n\"\n        f\"Телефон: {data['phone']}\\n\"\n        f\"Задача: {message.text}\"\n    )\n",[45,663,664,669,674,679,683,688,693,698,702,707,711,716,721,726,731,736,740,745,750,756,762,768,773,779,785,791,797,803,809,815,821,827],{"__ignoreMap":85},[89,665,666],{"class":91,"line":92},[89,667,668],{},"# handlers\u002Forders.py\n",[89,670,671],{"class":91,"line":150},[89,672,673],{},"from aiogram.fsm.context import FSMContext\n",[89,675,676],{"class":91,"line":175},[89,677,678],{},"from states.states import OrderForm\n",[89,680,681],{"class":91,"line":181},[89,682,197],{"emptyLinePlaceholder":196},[89,684,685],{"class":91,"line":187},[89,686,687],{},"@router.callback_query(F.data == \"new_order\")\n",[89,689,690],{"class":91,"line":193},[89,691,692],{},"async def start_order(callback: CallbackQuery, state: FSMContext):\n",[89,694,695],{"class":91,"line":200},[89,696,697],{},"    await state.set_state(OrderForm.waiting_name)\n",[89,699,700],{"class":91,"line":206},[89,701,600],{},[89,703,704],{"class":91,"line":212},[89,705,706],{},"    await callback.message.answer(\"Как вас зовут?\")\n",[89,708,709],{"class":91,"line":217},[89,710,197],{"emptyLinePlaceholder":196},[89,712,713],{"class":91,"line":223},[89,714,715],{},"@router.message(OrderForm.waiting_name)\n",[89,717,718],{"class":91,"line":229},[89,719,720],{},"async def got_name(message: Message, state: FSMContext):\n",[89,722,723],{"class":91,"line":235},[89,724,725],{},"    await state.update_data(name=message.text)\n",[89,727,728],{"class":91,"line":240},[89,729,730],{},"    await state.set_state(OrderForm.waiting_phone)\n",[89,732,733],{"class":91,"line":246},[89,734,735],{},"    await message.answer(\"Ваш номер телефона:\")\n",[89,737,738],{"class":91,"line":252},[89,739,197],{"emptyLinePlaceholder":196},[89,741,742],{"class":91,"line":257},[89,743,744],{},"@router.message(OrderForm.waiting_phone)\n",[89,746,747],{"class":91,"line":263},[89,748,749],{},"async def got_phone(message: Message, state: FSMContext):\n",[89,751,753],{"class":91,"line":752},19,[89,754,755],{},"    await state.update_data(phone=message.text)\n",[89,757,759],{"class":91,"line":758},20,[89,760,761],{},"    await state.set_state(OrderForm.waiting_description)\n",[89,763,765],{"class":91,"line":764},21,[89,766,767],{},"    await message.answer(\"Опишите задачу:\")\n",[89,769,771],{"class":91,"line":770},22,[89,772,197],{"emptyLinePlaceholder":196},[89,774,776],{"class":91,"line":775},23,[89,777,778],{},"@router.message(OrderForm.waiting_description)\n",[89,780,782],{"class":91,"line":781},24,[89,783,784],{},"async def got_description(message: Message, state: FSMContext):\n",[89,786,788],{"class":91,"line":787},25,[89,789,790],{},"    data = await state.get_data()\n",[89,792,794],{"class":91,"line":793},26,[89,795,796],{},"    await state.clear()\n",[89,798,800],{"class":91,"line":799},27,[89,801,802],{},"    await message.answer(\n",[89,804,806],{"class":91,"line":805},28,[89,807,808],{},"        f\"✅ Заказ принят!\\n\"\n",[89,810,812],{"class":91,"line":811},29,[89,813,814],{},"        f\"Имя: {data['name']}\\n\"\n",[89,816,818],{"class":91,"line":817},30,[89,819,820],{},"        f\"Телефон: {data['phone']}\\n\"\n",[89,822,824],{"class":91,"line":823},31,[89,825,826],{},"        f\"Задача: {message.text}\"\n",[89,828,830],{"class":91,"line":829},32,[89,831,832],{},"    )\n",[10,834,835,838,839,842,843,846],{},[45,836,837],{},"FSMContext"," автоматически внедряется Aiogram. Состояние хранится между сообщениями. По умолчанию используется ",[45,840,841],{},"MemoryStorage"," (теряется при перезапуске) — для продакшна переключайтесь на ",[45,844,845],{},"RedisStorage",".",[14,848,850],{"id":849},"middleware-код-на-каждое-обновление","Middleware: код на каждое обновление",[80,852,854],{"className":138,"code":853,"language":140,"meta":85,"style":85},"from aiogram import BaseMiddleware\nfrom aiogram.types import TelegramObject\n\nclass LoggingMiddleware(BaseMiddleware):\n    async def __call__(self, handler, event: TelegramObject, data: dict):\n        print(f\"Обновление от пользователя {event.from_user.id}\")\n        return await handler(event, data)\n\ndp.update.middleware(LoggingMiddleware())\n",[45,855,856,861,866,870,875,880,885,890,894],{"__ignoreMap":85},[89,857,858],{"class":91,"line":92},[89,859,860],{},"from aiogram import BaseMiddleware\n",[89,862,863],{"class":91,"line":150},[89,864,865],{},"from aiogram.types import TelegramObject\n",[89,867,868],{"class":91,"line":175},[89,869,197],{"emptyLinePlaceholder":196},[89,871,872],{"class":91,"line":181},[89,873,874],{},"class LoggingMiddleware(BaseMiddleware):\n",[89,876,877],{"class":91,"line":187},[89,878,879],{},"    async def __call__(self, handler, event: TelegramObject, data: dict):\n",[89,881,882],{"class":91,"line":193},[89,883,884],{},"        print(f\"Обновление от пользователя {event.from_user.id}\")\n",[89,886,887],{"class":91,"line":200},[89,888,889],{},"        return await handler(event, data)\n",[89,891,892],{"class":91,"line":206},[89,893,197],{"emptyLinePlaceholder":196},[89,895,896],{"class":91,"line":212},[89,897,898],{},"dp.update.middleware(LoggingMiddleware())\n",[10,900,901],{},"Я использую middleware чтобы автоматом сохранять пользователей в базу при первом контакте — это выполняется до любого хендлера, никогда не забудешь добавить.",[14,903,905],{"id":904},"деплой-systemd-на-vps","Деплой: systemd на VPS",[10,907,908],{},"Простейшая продакшн-установка: Ubuntu VPS, systemd сервис. Docker не нужен для простых ботов.",[80,910,914],{"className":911,"code":912,"language":913,"meta":85,"style":85},"language-ini shiki shiki-themes github-light github-dark","# \u002Fetc\u002Fsystemd\u002Fsystem\u002Fmybot.service\n[Unit]\nDescription=My Telegram Bot\nAfter=network.target\n\n[Service]\nUser=ubuntu\nWorkingDirectory=\u002Fhome\u002Fubuntu\u002Fmybot\nEnvironment=\"BOT_TOKEN=your_token\"\nExecStart=\u002Fhome\u002Fubuntu\u002Fmybot\u002F.venv\u002Fbin\u002Fpython bot.py\nRestart=always\nRestartSec=5\n\n[Install]\nWantedBy=multi-user.target\n","ini",[45,915,916,921,926,931,936,940,945,950,955,960,965,970,975,979,984],{"__ignoreMap":85},[89,917,918],{"class":91,"line":92},[89,919,920],{},"# \u002Fetc\u002Fsystemd\u002Fsystem\u002Fmybot.service\n",[89,922,923],{"class":91,"line":150},[89,924,925],{},"[Unit]\n",[89,927,928],{"class":91,"line":175},[89,929,930],{},"Description=My Telegram Bot\n",[89,932,933],{"class":91,"line":181},[89,934,935],{},"After=network.target\n",[89,937,938],{"class":91,"line":187},[89,939,197],{"emptyLinePlaceholder":196},[89,941,942],{"class":91,"line":193},[89,943,944],{},"[Service]\n",[89,946,947],{"class":91,"line":200},[89,948,949],{},"User=ubuntu\n",[89,951,952],{"class":91,"line":206},[89,953,954],{},"WorkingDirectory=\u002Fhome\u002Fubuntu\u002Fmybot\n",[89,956,957],{"class":91,"line":212},[89,958,959],{},"Environment=\"BOT_TOKEN=your_token\"\n",[89,961,962],{"class":91,"line":217},[89,963,964],{},"ExecStart=\u002Fhome\u002Fubuntu\u002Fmybot\u002F.venv\u002Fbin\u002Fpython bot.py\n",[89,966,967],{"class":91,"line":223},[89,968,969],{},"Restart=always\n",[89,971,972],{"class":91,"line":229},[89,973,974],{},"RestartSec=5\n",[89,976,977],{"class":91,"line":235},[89,978,197],{"emptyLinePlaceholder":196},[89,980,981],{"class":91,"line":240},[89,982,983],{},"[Install]\n",[89,985,986],{"class":91,"line":246},[89,987,988],{},"WantedBy=multi-user.target\n",[80,990,992],{"className":82,"code":991,"language":84,"meta":85,"style":85},"sudo systemctl enable mybot\nsudo systemctl start mybot\nsudo systemctl status mybot\njournalctl -u mybot -f\n",[45,993,994,1008,1019,1030],{"__ignoreMap":85},[89,995,996,999,1002,1005],{"class":91,"line":92},[89,997,998],{"class":95},"sudo",[89,1000,1001],{"class":99}," systemctl",[89,1003,1004],{"class":99}," enable",[89,1006,1007],{"class":99}," mybot\n",[89,1009,1010,1012,1014,1017],{"class":91,"line":150},[89,1011,998],{"class":95},[89,1013,1001],{"class":99},[89,1015,1016],{"class":99}," start",[89,1018,1007],{"class":99},[89,1020,1021,1023,1025,1028],{"class":91,"line":175},[89,1022,998],{"class":95},[89,1024,1001],{"class":99},[89,1026,1027],{"class":99}," status",[89,1029,1007],{"class":99},[89,1031,1032,1035,1039,1042],{"class":91,"line":181},[89,1033,1034],{"class":95},"journalctl",[89,1036,1038],{"class":1037},"sj4cs"," -u",[89,1040,1041],{"class":99}," mybot",[89,1043,1044],{"class":1037}," -f\n",[10,1046,1047],{},"Бот теперь переживает перезагрузки VPS и автоматически перезапускается после падений.",[14,1049,1051],{"id":1050},"что-изучать-дальше","Что изучать дальше",[10,1053,1054],{},"Когда освоите базу:",[22,1056,1057,1063,1077,1083,1089],{},[25,1058,1059,1062],{},[28,1060,1061],{},"PostgreSQL + asyncpg"," — большинству ботов рано или поздно нужна база данных",[25,1064,1065,43,1068,48,1071,48,1074],{},[28,1066,1067],{},"Telegram Stars платежи",[45,1069,1070],{},"send_invoice()",[45,1072,1073],{},"pre_checkout_query",[45,1075,1076],{},"successful_payment",[25,1078,1079,1082],{},[28,1080,1081],{},"Webhooks"," — быстрее чем polling для высоконагруженных ботов, нужен HTTPS домен",[25,1084,1085,1088],{},[28,1086,1087],{},"Telegram Mini App"," — полноценный веб-интерфейс внутри Telegram",[25,1090,1091,1094],{},[28,1092,1093],{},"Система рассылок"," — отправка сообщений всем пользователям без превышения лимитов",[10,1096,1097,1098,1103,1104,1109],{},"Документация на ",[108,1099,1102],{"href":1100,"rel":1101},"https:\u002F\u002Fdocs.aiogram.dev",[112],"docs.aiogram.dev"," отличная. Telegram-чат Aiogram (",[108,1105,1108],{"href":1106,"rel":1107},"https:\u002F\u002Ft.me\u002Faiogram",[112],"@aiogram",") активный и полезный.",[10,1111,1112],{},"Сделайте реальный проект, запустите его, смотрите как работает. Только так реально учишься.",[1114,1115,1116],"style",{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}",{"title":85,"searchDepth":150,"depth":150,"links":1118},[1119,1120,1121,1122,1123,1124,1125,1126,1127,1128],{"id":16,"depth":150,"text":17},{"id":77,"depth":150,"text":78},{"id":156,"depth":150,"text":157},{"id":276,"depth":150,"text":277},{"id":391,"depth":150,"text":392},{"id":489,"depth":150,"text":490},{"id":615,"depth":150,"text":616},{"id":849,"depth":150,"text":850},{"id":904,"depth":150,"text":905},{"id":1050,"depth":150,"text":1051},"2025-04-10","Пошаговое руководство по созданию Telegram-бота на Aiogram 3 — хендлеры, FSM, инлайн-клавиатуры, деплой. Без воды.","md",{},"\u002Fru\u002Fblog\u002Faiogram-3-tutorial-2025","12 мин",{"title":5,"description":1130},"aiogram-3-tutorial-2025","ru\u002Fblog\u002Faiogram-3-tutorial-2025",[1139,1140,1141,1142,1143],"Aiogram","Python","Туториал","Telegram","Боты","XCKvK8lWKgGyFv3ab2Lb_tkRQYomxSmmfzUgJRcySX4",1783440106527]