### Running a Guest Bot with uv
Source: https://github.com/mastergroosha/aiogram-3-guide/blob/master/book_src/guest-bots.md
This example shows how to add aiogram as a dependency and run a simple guest bot example using `uv`. Ensure aiogram version 3.28.0 or higher is installed.
```Bash
uv add "aiogram>=3.28.0"
BOT_TOKEN=1234567890:AaBbCcDdEeFfGrOoShAHhIiJjKkLlMmNnOo uv run simple_example.py
```
--------------------------------
### Install and Run Advanced Bot
Source: https://github.com/mastergroosha/aiogram-3-guide/blob/master/book_src/guest-bots.md
Installs necessary libraries and runs the bot using uv. Ensure settings.toml is configured.
```bash
uv add structlog pydantic-settings openai
uv run -m bot
```
--------------------------------
### TOML Configuration Example
Source: https://github.com/mastergroosha/aiogram-3-guide/blob/master/book_src/quickstart.md
This snippet demonstrates how to define bot configuration, such as the token, in a TOML file. TOML is preferred for its readability and lack of indentation issues.
```toml
[bot]\ntoken = "1234567890:AaBbCcDdEeFfGrOoShAHhIiJjKkLlMmNnOo"
```
--------------------------------
### Basic Bot Entrypoint with Dispatcher
Source: https://github.com/mastergroosha/aiogram-3-guide/blob/master/book_src/routers.md
Sets up the main asynchronous function to initialize the bot and dispatcher, and starts polling for updates. Ensure to replace 'TOKEN' with your actual bot token.
```python
import asyncio
from aiogram import Bot, Dispatcher
# Запуск бота
async def main():
bot = Bot(token="TOKEN")
dp = Dispatcher()
# Запускаем бота и пропускаем все накопленные входящие
# Да, этот метод можно вызвать даже если у вас поллинг
await bot.delete_webhook(drop_pending_updates=True)
await dp.start_polling(bot)
if __name__ == "__main__":
asyncio.run(main())
```
--------------------------------
### Basic Router with Message Handler
Source: https://github.com/mastergroosha/aiogram-3-guide/blob/master/book_src/quickstart.md
This snippet shows how to set up a basic router and a handler that responds to any incoming message. It's a starting point for handling general user input.
```Python
from aiogram import Router
from aiogram.types import Message
router = Router(name="start")
@router.message()
async def any_message(
message: Message,
) -> None:
await message.answer("Я тебя не понимаю")
```
--------------------------------
### Example TOML Configuration
Source: https://github.com/mastergroosha/aiogram-3-guide/blob/master/book_src/quickstart.md
This TOML file defines bot token and logging settings. Ensure your actual settings.toml file contains your real bot token.
```toml
[bot]
token = "1234567890:AaBbCcDdEeFfGrOoShAHhIiJjKkLlMmNnOo"
[logs]
project_name = "hello_world"
show_datetime = true
datetime_format = "%Y-%m-%d %H:%M:%S"
show_debug_logs = false
time_in_utc = false
use_colors_in_console = true
renderer = "console" # "console" or "json"
allow_third_party_logs = true
```
--------------------------------
### Get Routers for Bot Configuration
Source: https://github.com/mastergroosha/aiogram-3-guide/blob/master/book_src/blog/posts/project_threads_llm.md
Returns a list of configured routers for the bot. This is a basic setup function.
```python
def get_routers() -> list[Router]:
return [
general_topic_router,
in_topic_router,
]
```
--------------------------------
### Environment Variable Configuration Example
Source: https://github.com/mastergroosha/aiogram-3-guide/blob/master/book_src/quickstart.md
This snippet shows how to set the bot token using environment variables as an alternative to a TOML file. This provides flexibility for deployment and CI/CD pipelines.
```bash
BOT_TOKEN="1234567890:AaBbCcDdEeFfGrOoShAHhIiJjKkLlMmNnOo"
```
--------------------------------
### Router with CommandStart and General Message Handlers
Source: https://github.com/mastergroosha/aiogram-3-guide/blob/master/book_src/quickstart.md
This snippet demonstrates a more advanced router setup with two handlers. The first handler specifically catches the /start command using CommandStart filter, while the second handles all other messages. This illustrates prioritized handler execution.
```Python
from aiogram import Router
from aiogram.filters import CommandStart
from aiogram.types import Message
router = Router(name="start")
@router.message(CommandStart())
async def cmd_start(
message: Message,
) -> None:
await message.answer("Привет!")
@router.message()
async def any_message(
message: Message,
) -> None:
await message.answer("Я тебя не понимаю")
```
--------------------------------
### Callback Data Factory Example
Source: https://github.com/mastergroosha/aiogram-3-guide/blob/master/book_src/buttons.md
Demonstrates the creation of a CallbackData factory for structured callback data. This factory defines a prefix and fields for actions and values, simplifying data parsing.
```python
from aiogram.filters.callback_data import CallbackData
class NumbersCallbackFactory(CallbackData, prefix="fabnum"):
action: str
value: int | None = None
```
--------------------------------
### Basic Bot Entrypoint (bot.py)
Source: https://github.com/mastergroosha/aiogram-3-guide/blob/master/book_src/filters-and-middlewares.md
This is the main entry point for the aiogram bot application. It initializes the Bot and Dispatcher, sets up webhook deletion, and starts polling.
```python
import asyncio
from aiogram import Bot, Dispatcher
async def main():
bot = Bot(token="TOKEN")
dp = Dispatcher()
# Запускаем бота и пропускаем все накопленные входящие
# Да, этот метод можно вызвать даже если у вас поллинг
await bot.delete_webhook(drop_pending_updates=True)
await dp.start_polling(bot)
if __name__ == "__main__":
asyncio.run(main())
```
--------------------------------
### Weekend Callback Middleware Example
Source: https://github.com/mastergroosha/aiogram-3-guide/blob/master/book_src/filters-and-middlewares.md
This example implements a middleware that prevents callback queries from being processed on weekends. It checks the current day of the week and either proceeds with the handler or responds with an alert.
```python
import asyncio
import logging
import sys
from datetime import datetime
from typing import Any, Callable, Dict, Awaitable
from aiogram import Bot, Dispatcher, Router, BaseMiddleware, F
from aiogram.filters import Command
from aiogram.types import Message, CallbackQuery, TelegramObject
from aiogram.utils.keyboard import InlineKeyboardBuilder
router = Router()
# Это будет outer-мидлварь на любые колбэки
class WeekendCallbackMiddleware(BaseMiddleware):
def is_weekend(self) -> bool:
# 5 - суббота, 6 - воскресенье
return datetime.utcnow().weekday() in (5, 6)
async def __call__(
self,
handler: Callable[[TelegramObject, Dict[str, Any]], Awaitable[Any]],
event: TelegramObject,
data: Dict[str, Any]
) -> Any:
# Можно подстраховаться и игнорировать мидлварь,
# если она установлена по ошибке НЕ на колбэки
if not isinstance(event, CallbackQuery):
# тут как-нибудь залогировать
return await handler(event, data)
# Если сегодня не суббота и не воскресенье,
# то продолжаем обработку.
if not self.is_weekend():
return await handler(event, data)
# В противном случае отвечаем на колбэк самостоятельно
# и прекращаем дальнейшую обработку
await event.answer(
"Какая работа? Завод остановлен до понедельника!",
show_alert=True
)
return
@router.message(Command("checkin"))
async def cmd_checkin(message: Message):
builder = InlineKeyboardBuilder()
builder.button(text="Я на работе!", callback_data="checkin")
await message.answer(
text="Нажимайте эту кнопку только по будним дням!",
reply_markup=builder.as_markup()
)
@router.callback_query(F.data == "checkin")
async def callback_checkin(callback: CallbackQuery):
# Тут много сложного кода
await callback.answer(
text="Спасибо, что подтвердили своё присутствие!",
show_alert=True
)
async def main() -> None:
bot = Bot('1234567890:AaBbCcDdEeFfGrOoShAHhIiJjKkLlMmNnOo')
dp = Dispatcher()
dp.callback_query.outer_middleware(WeekendCallbackMiddleware())
dp.include_router(router)
await dp.start_polling(bot)
if __name__ == "__main__":
logging.basicConfig(level=logging.INFO, stream=sys.stdout)
asyncio.run(main())
```
--------------------------------
### Main Bot Initialization and Client Setup
Source: https://github.com/mastergroosha/aiogram-3-guide/blob/master/book_src/blog/posts/project_threads_llm.md
Initialize the Aiogram bot, dispatcher, HTTP client, and LLM client. The HTTP and LLM clients are added to the dispatcher's workflow data. Includes shutdown hook for closing the HTTP client.
```python
import asyncio
import httpx
import structlog
from aiogram import Bot, Dispatcher
from aiogram.fsm.storage.memory import MemoryStorage
from structlog.typing import FilteringBoundLogger
from .config_reader import Settings
from .handlers import get_routers
from .logs import get_structlog_config
from .llm import LLMClient
logger: FilteringBoundLogger = structlog.get_logger()
async def shutdown(dispatcher: Dispatcher) -> None:
await dispatcher["http_client"].aclose()
async def main():
settings = Settings()
structlog.configure(**get_structlog_config(settings.logs))
bot = Bot(token=settings.bot.token.get_secret_value())
dp = Dispatcher(storage=MemoryStorage())
dp.include_routers(*get_routers())
dp.shutdown.register(shutdown)
http_client = httpx.AsyncClient(timeout=None)
llm_client = LLMClient(http_client, settings.llm.url)
dp.workflow_data.update(
http_client=http_client,
llm_client=llm_client,
)
await logger.ainfo("Starting bot...")
await dp.start_polling(bot)
asyncio.run(main())
```
--------------------------------
### Basic Router Setup for Group Events
Source: https://github.com/mastergroosha/aiogram-3-guide/blob/master/book_src/special-updates.md
Sets up a router for handling events within a specific group chat. It includes a comment suggesting a custom filter for checking administrator status.
```python
from aiogram import Router, F
from aiogram.filters.command import Command
from aiogram.types import Message
router = Router()
# Вообще говоря, можно на роутер навесить кастомный фильтр
# с проверкой, лежит ли айди вызывающего во множестве admins.
# Тогда все хэндлеры в роутере автоматически будут вызываться
# только для людей из admins, это сократит код и избавит от лишнего if
```
--------------------------------
### Handle Deep Link for Adding Data
Source: https://github.com/mastergroosha/aiogram-3-guide/blob/master/book_src/inline-mode.md
Register a handler for `CommandStart` with a specific deep link parameter (`add`). This handler will be triggered when a user clicks the `switch_pm_parameter` from an inline query, allowing them to directly start the data addition process.
```python
# Учтите, что хэндлер на просто /start должен идти ПОЗЖЕ
@router.message(Command(commands=["start"]))
async def cmd_start(message: Message, state: FSMContext):
...
```
--------------------------------
### Telegram Bot Command Handler with Streaming
Source: https://github.com/mastergroosha/aiogram-3-guide/blob/master/book_src/blog/posts/project_threads_llm.md
Handles the /start command, initializing messages for an LLM and setting up a random draft ID for Telegram's streaming API. This is the initial setup before sending streamed messages.
```python
from random import randint
import structlog
from aiogram import Bot, Router
from aiogram.exceptions import TelegramRetryAfter
from aiogram.filters import CommandStart
from aiogram.types import Message
from structlog.typing import FilteringBoundLogger
from ..llm import LLMClient
router = Router()
logger: FilteringBoundLogger = structlog.get_logger()
@router.message(CommandStart())
async def cmd_start(
message: Message,
llm_client: LLMClient,
bot: Bot,
):
messages = [
{
"role": "system",
"content": "Ты — умный ассистент, помогаешь пользователям с разными задачами."
},
{
"role": "user",
"content": "Ты кто?"
}
]
# Начальное состояние текста
text = ""
# Случайный идентификатор черновика
draft_id = randint(1, 100_000_000)
```
--------------------------------
### Start Handler for New Chat Creation
Source: https://github.com/mastergroosha/aiogram-3-guide/blob/master/book_src/blog/posts/project_threads_llm.md
This handler is triggered by the /start command and prompts the user to create a new chat. It's the entry point for initiating a new thread-based conversation.
```python
import structlog
from aiogram import Router
from aiogram.filters import CommandStart
from aiogram.types import Message
from structlog.typing import FilteringBoundLogger
router = Router()
logger: FilteringBoundLogger = structlog.get_logger()
@router.message(CommandStart())
async def cmd_start(
message: Message,
):
await message.answer("Добро пожаловать! Нажмите /new для создания нового чата")
```
--------------------------------
### Advanced Message Formatting Example
Source: https://github.com/mastergroosha/aiogram-3-guide/blob/master/book_src/messages.md
Demonstrates complex message construction using various formatting utilities like lists, marked sections, key-value pairs, and hashtags. This approach is useful for creating structured and informative messages.
```python
from aiogram.filters import Command
from aiogram.utils.formatting import (
Bold, as_list, as_marked_section, as_key_value, HashTag
)
@dp.message(Command("advanced_example"))
async def cmd_advanced_example(message: Message):
content = as_list(
as_marked_section(
Bold("Success:"),
"Test 1",
"Test 3",
"Test 4",
marker="✅ ",
),
as_marked_section(
Bold("Failed:"),
"Test 2",
marker="❌ ",
),
as_marked_section(
Bold("Summary:"),
as_key_value("Total", 4),
as_key_value("Success", 3),
as_key_value("Failed", 1),
marker=" ",
),
HashTag("#test"),
sep="\n\n",
)
await message.answer(**content.as_kwargs())
```
--------------------------------
### Main Bot File with Router Registration and Admin Loading
Source: https://github.com/mastergroosha/aiogram-3-guide/blob/master/book_src/special-updates.md
This is the main bot file that sets up the dispatcher, registers routers, and loads the administrator list upon startup. It uses aiogram's default properties for HTML parsing and starts the polling process.
```python
import asyncio
import logging
from aiogram import Bot, Dispatcher
from aiogram.client.default import DefaultBotProperties
from aiogram.enums import ParseMode
from config_reader import config
from handlers import in_pm, bot_in_group, admin_changes_in_group, events_in_group
async def main():
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s - %(levelname)s - %(name)s - %(message)s",
)
dp = Dispatcher()
bot = Bot(
config.bot_token.get_secret_value(),
default=DefaultBotProperties(
parse_mode=ParseMode.HTML
)
)
dp.include_routers(
in_pm.router, events_in_group.router,
bot_in_group.router, admin_changes_in_group.router
)
# Подгрузка списка админов
admins = await bot.get_chat_administrators(config.main_chat_id)
admin_ids = {admin.user.id for admin in admins}
await dp.start_polling(bot, admins=admin_ids)
if __name__ == '__main__':
asyncio.run(main())
```
--------------------------------
### Guest Message Handler
Source: https://github.com/mastergroosha/aiogram-3-guide/blob/master/book_src/guest-bots.md
Handles incoming user messages in guest mode. It prepares the system prompt by injecting message context and then calls the LLM to get a response, handling potential errors.
```python
@router.guest_message(F.text)
async def guest_message(
message: Message,
llm_client: AsyncOpenAI,
llm_model: str,
system_prompt: str,
) -> None:
# Проверка, является ли вызывающее сообщение ответом на какое-то другое.
if (replied_message := message.reply_to_message) is None:
# Это пойдет в системный промпт
replied_message = "(none provided)"
else:
replied_message = (
replied_message.text
or f"(some mediafile, contents unknown, "
f"but there is a caption: {replied_message.caption})")
# Считаем, что не умеем "читать" медиафайлы
or "(some mediafile, contents unknown)"
)
# Подготовка системного промта
prompt = (
system_prompt
.replace("{{replied_message}}", replied_message)
.replace("{{current_message}}", message.text) # noqa
.replace("{{date_today}}", datetime.now().strftime("%d.%m.%Y"))
)
response_text = await get_llm_response(
client=llm_client,
prompt=prompt,
model=llm_model,
)
parse_mode = None
# Бывает, что ответа от модели нет. В таком случае, пусть будет заглушка.
if response_text is None:
response_text = "К сожалению, не удалось получить ответ от модели."
parse_mode = ParseMode.HTML
# Отвечаем на исходный запрос
await message.answer_guest_query(
result=InlineQueryResultArticle(
id="1",
title=".",
input_message_content=InputTextMessageContent(
message_text=response_text,
parse_mode=parse_mode,
),
)
)
```
--------------------------------
### Handle Bot Added as Admin or Member in Group/Supergroup
Source: https://github.com/mastergroosha/aiogram-3-guide/blob/master/book_src/special-updates.md
This example demonstrates setting up a router and handlers to react to a bot being added to a group or supergroup, distinguishing between administrator and member roles. It filters for group and supergroup chat types.
```python
from aiogram import F, Router
from aiogram.filters.chat_member_updated import \
ChatMemberUpdatedFilter, IS_NOT_MEMBER, MEMBER, ADMINISTRATOR
from aiogram.types import ChatMemberUpdated
router = Router()
router.my_chat_member.filter(F.chat.type.in_({"group", "supergroup"}))
chats_variants = {
"group": "группу",
"supergroup": "супергруппу"
}
# Не удалось воспроизвести случай добавления бота как Restricted,
```
--------------------------------
### Include Routers for General Topic Handling
Source: https://github.com/mastergroosha/aiogram-3-guide/blob/master/book_src/blog/posts/project_threads_llm.md
This code snippet shows how to initialize and include routers for handling messages within the general topic. It filters messages to only include those not in a specific thread and then includes the start, new chat creation, and general topic handlers.
```python
from aiogram import Router, F
from . import (
chat_in_general_topic,
new_chat_creation,
start,
)
general_topic_router = Router()
general_topic_router.message.filter(F.message_thread_id.is_(None))
general_topic_router.include_routers(
start.router,
new_chat_creation.router,
chat_in_general_topic.router,
)
in_topic_router = Router()
```
--------------------------------
### Create URL Inline Buttons
Source: https://github.com/mastergroosha/aiogram-3-guide/blob/master/book_src/buttons.md
Demonstrates how to create inline buttons that link to external URLs or Telegram user profiles using InlineKeyboardBuilder. It includes examples for GitHub, the official Telegram channel, and a specific user ID, with a check for user privacy settings.
```Python
# новый импорт
from aiogram.utils.keyboard import InlineKeyboardBuilder
@dp.message(Command("inline_url"))
async def cmd_inline_url(message: types.Message, bot: Bot):
builder = InlineKeyboardBuilder()
builder.row(types.InlineKeyboardButton(
text="GitHub", url="https://github.com")
)
builder.row(types.InlineKeyboardButton(
text="Оф. канал Telegram",
url="tg://resolve?domain=telegram")
)
# Чтобы иметь возможность показать ID-кнопку,
# У юзера должен быть False флаг has_private_forwards
user_id = 1234567890
chat_info = await bot.get_chat(user_id)
if not chat_info.has_private_forwards:
builder.row(types.InlineKeyboardButton(
text="Какой-то пользователь",
url=f"tg://user?id={user_id}")
)
await message.answer(
'Выберите ссылку',
reply_markup=builder.as_markup(),
)
```
--------------------------------
### Initialize Bot and Dispatcher in bot/__main__.py
Source: https://github.com/mastergroosha/aiogram-3-guide/blob/master/book_src/quickstart.md
This script sets up the bot, dispatcher, and configures logging. It includes reading settings, creating the Bot and Dispatcher instances, and including the routers.
```python
import asyncio
import structlog
from structlog.typing import FilteringBoundLogger
from aiogram import Bot, Dispatcher
from bot.config import Settings
from bot.handlers import get_routers
from bot.logging_config import get_structlog_config
logger: FilteringBoundLogger = structlog.get_logger()
async def main() -> None:
# Чтение конфигурации (toml-файл или env vars – не важно)
settings = Settings()
# Конфигурирование логгера
structlog.configure(**get_structlog_config(settings.logs))
# Создание объекта бота. Обязательный аргумент token – читаем токен
# из конфигурации. Поскольку токен помечен как SecretStr, то необходимо
# дополнительно вызывать get_secret_value().
bot = Bot(
token=settings.bot.token.get_secret_value(),
)
# Создание объекта диспетчера и привязка роутеров
dp = Dispatcher()
# Небольшой лайфхак: include_routers() принимает на вход
# произвольное количество аргументов
# get_routers() возвращает список: [A, B, C,...]
# и этот список будет передан как набор аргументов:
# include_routers(A, B, C,...)
dp.include_routers(*get_routers())
# Запуск бота в режиме поллинга
await logger.ainfo("Starting polling...")
try:
await dp.start_polling(bot)
finally:
await logger.ainfo("Bot stopped")
asyncio.run(main())
```
--------------------------------
### Basic Message and Callback Query Handlers
Source: https://github.com/mastergroosha/aiogram-3-guide/blob/master/book_src/filters-and-middlewares.md
This snippet demonstrates a basic message handler for the /start command and a callback query handler for a button press. It shows how to create inline keyboards and respond to user interactions.
```python
import asyncio
import logging
import sys
from aiogram.filters import CommandStart, F
from aiogram.types import Message, CallbackQuery
from aiogram.utils.keyboard import InlineKeyboardBuilder
from aiogram import Bot, Dispatcher
# Assume maintenance_router and regular_router are defined elsewhere
# For demonstration purposes, let's define dummy routers
class DummyRouter:
def message(self, *args, **kwargs):
def decorator(handler):
return handler
return decorator
def callback_query(self, *args, **kwargs):
def decorator(handler):
return handler
return decorator
def include_routers(self, *routers):
pass
maintenance_router = DummyRouter()
regular_router = DummyRouter()
@regular_router.message(CommandStart())
async def cmd_start(message: Message):
builder = InlineKeyboardBuilder()
builder.button(text="Нажми меня", callback_data="anything")
await message.answer(
text="Какой-то текст с кнопкой",
reply_markup=builder.as_markup()
)
@regular_router.callback_query(F.data == "anything")
async def callback_anything(callback: CallbackQuery):
await callback.answer(
text="Это какое-то обычное действие",
show_alert=True
)
async def main() -> None:
bot = Bot('1234567890:AaBbCcDdEeFfGrOoShAHhIiJjKkLlMmNnOo')
# В реальной жизни значение maintenance_mode
# будет взято из стороннего источника (например, конфиг или через API)
# Помните, что т.к. bool тип является иммутабельным,
# его смена в рантайме ни на что не повлияет
dp = Dispatcher(maintenance_mode=True)
# Maintenance-роутер должен быть первый
dp.include_routers(maintenance_router, regular_router)
await dp.start_polling(bot)
if __name__ == "__main__":
logging.basicConfig(level=logging.INFO, stream=sys.stdout)
asyncio.run(main())
```
--------------------------------
### Set 'long_operation' flag for a handler
Source: https://github.com/mastergroosha/aiogram-3-guide/blob/master/book_src/filters-and-middlewares.md
Example of how to apply a flag to a handler using the dispatcher. This flag can be read by middlewares.
```python
@dp.message(<тут ваши фильтры>, flags={"long_operation": "upload_video_note"})
```
--------------------------------
### OpenRouter Client and Prompt Initialization
Source: https://github.com/mastergroosha/aiogram-3-guide/blob/master/book_src/guest-bots.md
Initializes the OpenRouter client and loads the system prompt from a file during bot startup. These are then passed to the Dispatcher.
```python
async def main() -> None:
settings = Settings()
...
openrouter_client = AsyncOpenAI(
base_url=settings.llm.base_url,
api_key=settings.llm.api_key.get_secret_value(),
)
with open("bot/system_prompt.txt", "r", encoding="utf-8") as f:
system_prompt: str = f.read()
dp = Dispatcher(
llm_client=openrouter_client,
llm_model=settings.llm.model_name,
system_prompt=system_prompt,
)
...
```
--------------------------------
### Payment Successful Localization
Source: https://github.com/mastergroosha/aiogram-3-guide/blob/master/book_src/payments.md
Example of a Fluent localization string for a successful payment message. It includes placeholders for dynamic data like the transaction ID.
```fluent
payment-successful =
Огромное спасибо!
Ваш айди транзакции:
{$id}
Сохраните его, если вдруг сделать рефанд в будущем 😢
```
--------------------------------
### Get Largest Photo Size using as_()
Source: https://github.com/mastergroosha/aiogram-3-guide/blob/master/book_src/filters-and-middlewares.md
Utilize the as_() method with MagicFilter to extract and pass the largest photo object directly to the handler.
```python
from aiogram.types import Message, PhotoSize
from aiogram import F
@router.message(F.photo[-1].as_("largest_photo"))
async def forward_from_channel_handler(message: Message, largest_photo: PhotoSize) -> None:
print(largest_photo.width, largest_photo.height)
```
--------------------------------
### Start Polling with Specific Allowed Updates
Source: https://github.com/mastergroosha/aiogram-3-guide/blob/master/book_src/special-updates.md
Initiate bot polling with a defined list of updates to receive. If `allowed_updates` is not specified, aiogram will automatically collect them from all routers.
```python
async def main():
dp = Dispatcher()
bot = Bot("токен")
await dp.start_polling(
bot,
allowed_updates=["message", "inline_query", "chat_member"]
)
```
--------------------------------
### Handle Deep Links with /start Command
Source: https://github.com/mastergroosha/aiogram-3-guide/blob/master/book_src/messages.md
Use CommandStart with deep_link=True to process parameters passed via t.me/bot?start=xxx links. The magic parameter can filter based on arguments, and regexp can extract specific patterns.
```python
import re
from aiogram import F
from aiogram.types import Message
from aiogram.filters import Command, CommandObject, CommandStart
@dp.message(Command("help"))
@dp.message(CommandStart(
deep_link=True, magic=F.args == "help"
))
async def cmd_start_help(message: Message):
await message.answer("Это сообщение со справкой")
@dp.message(CommandStart(
deep_link=True,
magic=F.args.regexp(re.compile(r'book_(\d+)'))
))
async def cmd_start_book(
message: Message,
command: CommandObject
):
book_number = command.args.split("_")[1]
await message.answer(f"Sending book №{book_number}")
```
--------------------------------
### Implement /start and /cancel Commands in Aiogram
Source: https://github.com/mastergroosha/aiogram-3-guide/blob/master/book_src/fsm.md
Handles the /start command to display a welcome message and remove the reply keyboard. Implements /cancel command handlers for scenarios with and without an active state, clearing data or the entire state and removing the reply keyboard.
```python
from aiogram import F, Router
from aiogram.filters import Command
from aiogram.filters import StateFilter
from aiogram.fsm.context import FSMContext
from aiogram.fsm.state import default_state
from aiogram.types import Message, ReplyKeyboardRemove
router = Router()
@router.message(Command(commands=["start"]))
async def cmd_start(message: Message, state: FSMContext):
await state.clear()
await message.answer(
text="Выберите, что хотите заказать: "
"блюда (/food) или напитки (/drinks).",
reply_markup=ReplyKeyboardRemove()
)
# Нетрудно догадаться, что следующие два хэндлера можно
# спокойно объединить в один, но для полноты картины оставим так
# default_state - это то же самое, что и StateFilter(None)
@router.message(StateFilter(None), Command(commands=["cancel"]))
@router.message(default_state, F.text.lower() == "отмена")
async def cmd_cancel_no_state(message: Message, state: FSMContext):
# Стейт сбрасывать не нужно, удалим только данные
await state.set_data({})
await message.answer(
text="Нечего отменять",
reply_markup=ReplyKeyboardRemove()
)
@router.message(Command(commands=["cancel"]))
@router.message(F.text.lower() == "отмена")
async def cmd_cancel(message: Message, state: FSMContext):
await state.clear()
await message.answer(
text="Действие отменено",
reply_markup=ReplyKeyboardRemove()
)
```
--------------------------------
### Aiogram Bot Entrypoint Configuration
Source: https://github.com/mastergroosha/aiogram-3-guide/blob/master/book_src/fsm.md
Sets up the main bot entry point, including logging configuration, initializing the Dispatcher with MemoryStorage, and registering routers for common commands and food ordering.
```python
import asyncio
import logging
from aiogram import Bot, Dispatcher
from aiogram.fsm.storage.memory import MemoryStorage
# файл config_reader.py можно взять из репозитория
# пример — в первой главе
from config_reader import config
from handlers import common, ordering_food
async def main():
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s - %(levelname)s - %(name)s - %(message)s",
)
# Если не указать storage, то по умолчанию всё равно будет MemoryStorage
# Но явное лучше неявного =]
dp = Dispatcher(storage=MemoryStorage())
bot = Bot(config.bot_token.get_secret_value())
dp.include_router(common.router)
dp.include_router(ordering_food.router)
# сюда импортируйте ваш собственный роутер для напитков
await dp.start_polling(bot)
if __name__ == '__main__':
asyncio.run(main())
```
--------------------------------
### Get LLM Response Function
Source: https://github.com/mastergroosha/aiogram-3-guide/blob/master/book_src/guest-bots.md
Asynchronously fetches a response from an LLM provider like OpenRouter. Ensure streaming is disabled for guest bots and handle cases where no completion is returned.
```python
async def get_llm_response(
client: AsyncOpenAI,
prompt: str,
model: str,
) -> str | None:
completion = await client.chat.completions.create(
model=model,
messages=[
{
"role": "system",
"content": prompt,
},
],
stream=False,
extra_body={"reasoning": {"enabled": True}},
)
if not completion.choices:
return None
return completion.choices[0].message.content
```
--------------------------------
### LLM Handler for /start Command
Source: https://github.com/mastergroosha/aiogram-3-guide/blob/master/book_src/blog/posts/project_threads_llm.md
Implement a message handler for the /start command that interacts with the LLM client. It sends a system prompt and a user question to the LLM and answers with the response.
```python
from aiogram import Router
from aiogram.filters import CommandStart
from aiogram.types import Message
from ..llm import LLMClient
router = Router()
@router.message(CommandStart())
async def cmd_start(
message: Message,
llm_client: LLMClient,
):
messages = [
{
"role": "system",
"content": "Ты — умный ассистент, помогаешь пользователям с разными задачами."
},
{
"role": "user",
"content": "Ты кто?"
}
]
response_text: str = await llm_client.generate_response(
messages=messages,
temperature=0.7,
)
await message.answer(response_text)
```
--------------------------------
### Define Handlers for /start and Yes/No Answers
Source: https://github.com/mastergroosha/aiogram-3-guide/blob/master/book_src/routers.md
This module defines a router and handlers for the /start command, which displays a question with Yes/No buttons, and for the 'Да' and 'Нет' answers, providing appropriate responses and removing the keyboard.
```python
from aiogram import Router, F
from aiogram.filters import Command
from aiogram.types import Message, ReplyKeyboardRemove
from keyboards.for_questions import get_yes_no_kb
router = Router()
@router.message(Command("start"))
async def cmd_start(message: Message):
await message.answer(
"Вы довольны своей работой?",
reply_markup=get_yes_no_kb()
)
@router.message(F.text.lower() == "да")
async def answer_yes(message: Message):
await message.answer(
"Это здорово!",
reply_markup=ReplyKeyboardRemove()
)
@router.message(F.text.lower() == "нет")
async def answer_no(message: Message):
await message.answer(
"Жаль...",
reply_markup=ReplyKeyboardRemove()
)
```
--------------------------------
### Applying Slowpoke Middleware to Routers
Source: https://github.com/mastergroosha/aiogram-3-guide/blob/master/book_src/filters-and-middlewares.md
Apply the custom middleware to specific routers with different configurations. This example shows how to attach the 'SlowpokeMiddleware' with varying delay times to two different routers.
```Python
from aiogram import Router
from <...> import SlowpokeMiddleware
# Где-то в другом месте
router1 = Router()
router2 = Router()
router1.message.middleware(SlowpokeMiddleware(sleep_sec=5))
router2.message.middleware(SlowpokeMiddleware(sleep_sec=10))
```
--------------------------------
### Команда /start в Telegram-боте
Source: https://github.com/mastergroosha/aiogram-3-guide/blob/master/book_src/payments.md
Обработчик команды /start для отображения приветственного сообщения пользователю. Использует локализацию через FluentLocalization и устанавливает parse_mode в None для корректного отображения подстрок.
```fluent
cmd-start =
Здравствуйте! Спасибо, что решили воспользоваться ботом.
Доступны следующие команды:
• /donate_1: подарить 1 звезду.
• /donate_25: подарить 25 звёзд.
• /donate_50: подарить 50 звёзд.
• /donate <число>: подарить <число> звёзд.
• /paysupport: помощь с покупками.
• /refund: возврат платежа (рефанд).
```
```python
@router.message(CommandStart())
async def cmd_start(
message: Message,
l10n: FluentLocalization,
):
await message.answer(
l10n.format_value("cmd-start"),
parse_mode=None,
)
```
--------------------------------
### Dynamic Handler Registration
Source: https://github.com/mastergroosha/aiogram-3-guide/blob/master/book_src/quickstart.md
Register handlers dynamically using the router's register() method when filter parameters are dynamic. This approach is used when filter parameters are not known until the bot starts.
```Python
from aiogram import Router
from aiogram.filters import CommandStart
from aiogram.types import Message
router = Router(name="start")
async def cmd_start(
message: Message,
) -> None:
await message.answer("Привет!")
async def any_message(
message: Message,
) -> None:
await message.answer("Я тебя не понимаю")
```
--------------------------------
### Handle Initial State Transition with Command
Source: https://github.com/mastergroosha/aiogram-3-guide/blob/master/book_src/fsm.md
This handler reacts to the /food command when no state is set for the user. It prompts the user to choose a food item and sets the state to OrderFood.choosing_food_name.
```python
from aiogram.filters import Command, StateFilter
from aiogram.fsm.context import FSMContext
@router.message(StateFilter(None), Command("food"))
async def cmd_food(message: Message, state: FSMContext):
await message.answer(
text="Выберите блюдо:",
reply_markup=make_row_keyboard(available_food_names)
)
# Устанавливаем пользователю состояние "выбирает название"
await state.set_state(OrderFood.choosing_food_name)
```
--------------------------------
### Basic Text Message Handler with Conditional Logic
Source: https://github.com/mastergroosha/aiogram-3-guide/blob/master/book_src/filters-and-middlewares.md
This handler processes any text message and responds differently based on the user's ID. It's an initial example before introducing more specific filters.
```python
from random import choice
@router.message(F.text)
async def my_text_handler(message: Message):
phrases = [
"Привет! Отлично выглядишь :)",
"Хэллоу, сегодня будет отличный день!",
"Здравствуй)) улыбнись :)"
]
if message.from_user.id in (111, 777):
await message.answer(choice(phrases))
else:
await message.answer("Я с тобой не разговариваю!")
```
--------------------------------
### Create and Send a Reply Keyboard with ReplyKeyboardBuilder
Source: https://github.com/mastergroosha/aiogram-3-guide/blob/master/book_src/buttons.md
This snippet shows how to initialize ReplyKeyboardBuilder, add multiple buttons, adjust their layout, and send the keyboard to the user. It's useful for creating custom sets of buttons that users can tap to send predefined messages.
```Python
from aiogram.utils.keyboard import ReplyKeyboardBuilder
from aiogram import types
from aiogram.filters import Command
# Assuming 'dp' is your Dispatcher instance
# dp = Dispatcher()
@dp.message(Command("reply_builder"))
async def reply_builder(message: types.Message):
builder = ReplyKeyboardBuilder()
for i in range(1, 17):
builder.add(types.KeyboardButton(text=str(i)))
builder.adjust(4)
await message.answer(
"Выберите число:",
reply_markup=builder.as_markup(resize_keyboard=True),
)
```
--------------------------------
### Callback Query Handler for Number Actions
Source: https://github.com/mastergroosha/aiogram-3-guide/blob/master/book_src/buttons.md
Processes callback queries starting with 'num_'. It increments, decrements, or finishes the number selection based on the callback data, updating the message text accordingly.
```python
@dp.callback_query(F.data.startswith("num_"))
async def callbacks_num(callback: types.CallbackQuery):
user_value = user_data.get(callback.from_user.id, 0)
action = callback.data.split("_")[1]
if action == "incr":
user_data[callback.from_user.id] = user_value+1
await update_num_text(callback.message, user_value+1)
elif action == "decr":
user_data[callback.from_user.id] = user_value-1
await update_num_text(callback.message, user_value-1)
elif action == "finish":
await callback.message.edit_text(f"Итого: {user_value}")
await callback.answer()
```
--------------------------------
### Handle New Chat Creation Command
Source: https://github.com/mastergroosha/aiogram-3-guide/blob/master/book_src/blog/posts/project_threads_llm.md
Handles the '/new' command to create a new forum topic, set up chat storage, and prompt the user to choose a persona. It correctly manages FSM context within the new topic.
```python
import structlog
from aiogram import Router, Bot
from aiogram.enums.topic_icon_color import TopicIconColor
from aiogram.filters import Command
from aiogram.fsm.context import FSMContext
from aiogram.fsm.storage.base import StorageKey
from aiogram.types import Message
from structlog.typing import FilteringBoundLogger
from bot.keyboards import make_prompt_keyboard
from bot.states import NewChatFlow
from bot.storage import memory_chat_storage
router = Router()
logger: FilteringBoundLogger = structlog.get_logger()
@router.message(Command("new"))
async def cmd_start(
message: Message,
bot: Bot,
state: FSMContext,
):
try:
new_topic = await bot.create_forum_topic(
chat_id=message.chat.id,
name="Новый чат",
icon_color=TopicIconColor.YELLOW,
)
except: # noqa
await logger.aexception("Failed to create new topic")
await message.answer("Что-то пошло не так. Пожалуйста, попробуйте ещё раз.")
return
new_topic_id = new_topic.message_thread_id
await memory_chat_storage.create(
user_id=message.from_user.id,
thread_id=new_topic_id,
)
# Важный момент: исходное сообщение пользователя (/new)
# принадлежит топику General, но стейт надо поменять уже внутри
# только что созданного топика. Поэтому отдельно создаём
# ключ FSM и по нему меняем стейт юзеру.
key = StorageKey(
bot_id=bot.id,
chat_id=message.chat.id,
user_id=message.from_user.id,
thread_id=new_topic_id,
)
fsm = FSMContext(
storage=state.storage,
key=key,
)
await bot.send_message(
chat_id=message.chat.id,
text="Пожалуйста, выберите стиль для этого чата одной из кнопок ниже.",
message_thread_id=new_topic_id,
reply_markup=make_prompt_keyboard(),
)
await fsm.set_state(NewChatFlow.choosing_persona)
```
--------------------------------
### Send Hidden Link with aiogram
Source: https://github.com/mastergroosha/aiogram-3-guide/blob/master/book_src/messages.md
This example shows how to use the `hide_link` utility from `aiogram.utils.markdown` to embed a URL in a message that will generate a link preview without being directly visible in the text. This is useful for attaching media with long captions.
```python
from aiogram.types import Message
from aiogram.utils.markdown import hide_link
from aiogram import Command
# Assuming 'dp' is your Dispatcher instance
@dp.message(Command("hidden_link"))
async def cmd_hidden_link(message: Message):
await message.answer(
f"{hide_link('https://telegra.ph/file/562a512448876923e28c3.png')}"
f"Документация Telegram: *существует*\n"
f"Пользователи: *не читают документацию*\n"
f"Груша:"
)
```
--------------------------------
### Configure CallbackAnswerMiddleware with custom settings
Source: https://github.com/mastergroosha/aiogram-3-guide/blob/master/book_src/buttons.md
Customize the default behavior of the CallbackAnswerMiddleware by specifying parameters like `pre`, `text`, and `show_alert` during its initialization.
```python
dp.callback_query.middleware(
CallbackAnswerMiddleware(
pre=True, text="Готово!", show_alert=True
)
)
```
--------------------------------
### Build and Send Media Group (Album)
Source: https://github.com/mastergroosha/aiogram-3-guide/blob/master/book_src/messages.md
Construct a media group (album) with an optional general caption and individual media items. Media can be added from local files, URLs, or file IDs. Remember to call `.build()` on the `MediaGroupBuilder` before sending.
```python
from aiogram.filters import Command
from aiogram.types import FSInputFile, Message
from aiogram.utils.media_group import MediaGroupBuilder
@dp.message(Command("album"))
async def cmd_album(message: Message):
album_builder = MediaGroupBuilder(
caption="Общая подпись для будущего альбома"
)
album_builder.add(
type="photo",
media=FSInputFile("image_from_pc.jpg")
# caption="Подпись к конкретному медиа"
)
# Если мы сразу знаем тип, то вместо общего add
# можно сразу вызывать add_<тип>
album_builder.add_photo(
# Для ссылок или file_id достаточно сразу указать значение
media="https://picsum.photos/seed/groosha/400/300"
)
album_builder.add_photo(
media="<ваш file_id>"
)
await message.answer_media_group(
# Не забудьте вызвать build()
media=album_builder.build()
)
```
--------------------------------
### Create Paysupport Command Redirecting to Refund
Source: https://github.com/mastergroosha/aiogram-3-guide/blob/master/book_src/payments.md
A simple command handler for `/paysupport` that redirects users to the `/refund` command by providing a predefined message. This is useful for offering a more user-friendly entry point for refund requests.
```python
```python
@router.message(Command("paysupport"))
async def cmd_paysupport(
message: Message,
l10n: FluentLocalization
):
await message.answer(l10n.format_value("cmd-paysupport"))
```
```