Что такое dispatcher aiogram
Перейти к содержимому

Что такое dispatcher aiogram

  • автор:

Зачем нужен Dispather в Aiogram?

Для чего нужен Dispatcher, если отправка ведётся так: await bot.send_sticker(message.chat.id, stick) ?

А также, для чего открывать файл в двоичном режиме?

  • Вопрос задан более двух лет назад
  • 4863 просмотра

Комментировать
Решения вопроса 1

Bl4ckm45k

Не стоит на столько углубляться на данном этапе изучения библиотеки.
Если коротко Dispatcher принимает все апдейты и обрабатывает их.
Можете отправлять сообщения как вам удобно, хоть await mybot.bot.send_sticker хоть await bot.send_sticker лишь бы импортировали все правильно

Роутеры, многофайловость и структура бота¶

В этой главе мы познакомимся с новой фичей aiogram 3.x — роутерами, научимся разбивать наш код на отдельные компоненты, а также сформируем базовую структуру бота, которая пригодится в следующих главах и вообще по жизни.

Точка входа в приложение¶

Театр начинается с вешалки, а бот начинается с точки входа. Пусть это будет файл bot.py . В нём мы определим асинхронную функцию main() , в которой создадим необходимые объекты и запустим поллинг. Какие объекты являются необходимыми? Во-первых, разумеется, бот. Их может быть несколько, но об этом как-нибудь в другой раз. Во-вторых, диспетчер. Он занимается приёмом событий от Telegram и раскидыванием их по хэндлерам через фильтры и мидлвари.

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()) 

Но чтобы обрабатывать сообщения, этого недостаточно, нужны ещё хэндлеры. Мы хотим их расположить в других файлах, чтобы не устраивать портянки на несколько тысяч строк. В предыдущих главах все наши хэндлеры прицеплялись к диспетчеру, но сейчас он внутри функции и мы точно не хотим делать его глобальным объектом.
Что же делать? И тут на помощь приходят.

Роутеры¶

Обратимся к официальной документации aiogram 3.x и посмотрим на следующее изображение:

Несколько роутеров

  1. Диспетчер — корневой роутер.
  2. Хэндлеры цепляются к роутерам.
  3. Роутеры могут быть вложенными, но между ними только однонаправленная связь.
  4. Порядок включения (и, соответственно, проверки) роутеров явно определён.

На следующем изображении виден порядок поиска апдейтом нужного хэндлера для выполнения:

порядок поиска апдейтом нужного хэндлера

Напишем простенького бота с двумя фичами:

  1. Если боту отправили /start , он должен прислать вопрос и две кнопки с текстами «Да» и «Нет».
  2. Если боту прислали любой другой текст, стикер или гифку, он должен ответить названием типа сообщения.

Начнём с клавиатуры: создадим рядом с файлом bot.py каталог keyboards , а внутри него файл for_questions.py и напишем функцию для получения простой клавиатуры с кнопками «Да» и «Нет» в один ряд:

keyboards/for_questions.py

from aiogram.types import ReplyKeyboardMarkup from aiogram.utils.keyboard import ReplyKeyboardBuilder def get_yes_no_kb() -> ReplyKeyboardMarkup: kb = ReplyKeyboardBuilder() kb.button(text="Да") kb.button(text="Нет") kb.adjust(2) return kb.as_markup(resize_keyboard=True) 

Ничего сложного, тем более, что мы клавиатуры подробно разбирали ранее. Теперь рядом с файлом bot.py создадим другой каталог handlers , а внутри него файл questions.py .

handlers/questions.py

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() # [1]  @router.message(Command("start")) # [2] 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() ) 

Обратим внимание на пункты [1] и [2]. Во-первых, мы в файле создали свой собственный роутер уровня модуля, и далее будем цеплять его к корневому роутеру (диспетчеру). Во-вторых, хэндлеры «отпочковываются» уже от локального роутера.

Аналогичным образом сделаем второй файл с хэндлерами different_types.py , где просто будем выводить тип сообщения:

handlers/different_types.py

from aiogram import Router, F from aiogram.types import Message router = Router() @router.message(F.text) async def message_with_text(message: Message): await message.answer("Это текстовое сообщение!") @router.message(F.sticker) async def message_with_sticker(message: Message): await message.answer("Это стикер!") @router.message(F.animation) async def message_with_gif(message: Message): await message.answer("Это GIF!") 

Наконец, вернёмся к нашему bot.py , импортируем файлы с роутерами и хэндлерами, и подключим их к диспетчеру:

import asyncio from aiogram import Bot, Dispatcher from handlers import questions, different_types  # Запуск бота async def main(): bot = Bot(token="TOKEN") dp = Dispatcher() dp.include_routers(questions.router, different_types.router)  # Альтернативный вариант регистрации роутеров по одному на строку # dp.include_router(questions.router) # dp.include_router(different_types.router) # Запускаем бота и пропускаем все накопленные входящие # Да, этот метод можно вызвать даже если у вас поллинг await bot.delete_webhook(drop_pending_updates=True) await dp.start_polling(bot) if __name__ == "__main__": asyncio.run(main()) 

Мы просто импортируем файлы из каталога handlers/ и подключаем роутеры из этих файлов к диспетчеру. И здесь снова важен порядок импортов! Если мы поменяем местами регистрацию роутеров, то на команду /start бот будет отвечать фразой «Это текстовое сообщение!», поскольку функция message_with_text() первой успешно пройдёт все фильтры. Но о самих фильтрах мы поговорим чуть позже, а пока рассмотрим ещё один вопрос.

Итог¶

У нас получилось аккуратно разделить бота по разным файлам, не нарушая его работу. Примерное дерево файлов и каталогов получилось следующим (здесь сознательно пропущены некоторые несущественные для примера файлы):

├── bot.py ├── handlers │ ├── different_types.py │ └── questions.py ├── keyboards │ └── for_questions.py 

В дальнейшем мы будем придерживаться такой структуры, плюс добавятся новые каталоги для фильтров, мидлварей, файлов для работы с базами данных и т.д.

Что такое dispatcher aiogram

Скачай курс
в приложении

Перейти в приложение
Открыть мобильную версию сайта

© 2013 — 2024. Stepik

Наши условия использования и конфиденциальности

Get it on Google Play

Public user contributions licensed under cc-wiki license with attribution required

Dispatcher#

__init__ ( * , storage : BaseStorage | None = None , fsm_strategy : FSMStrategy = FSMStrategy.USER_IN_CHAT , events_isolation : BaseEventIsolation | None = None , disable_fsm : bool = False , name : str | None = None , ** kwargs : Any ) → None [source] #

  • storage – Storage for FSM
  • fsm_strategy – FSM strategy
  • events_isolation – Events isolation
  • disable_fsm – Disable FSM, note that if you disable FSM then you should not use storage and events isolation
  • kwargs – Other arguments, will be passed as keyword arguments to handlers

Main entry point for incoming updates with automatic Dict->Update serializer

Main entry point for incoming updates Response of this method can be used as Webhook response

  • bot
  • update

Run many bots with polling

  • bots – Bot instances (one or more)
  • polling_timeout – Long-polling wait time
  • handle_as_tasks – Run task for each event and no wait result
  • backoff_config – backoff-retry config
  • allowed_updates – List of the update types you want your bot to receive
  • handle_signals – handle signals (SIGINT/SIGTERM)
  • close_bot_session – close bot sessions on shutdown
  • kwargs – contextual data

async start_polling ( * bots : Bot , polling_timeout : int = 10 , handle_as_tasks : bool = True , backoff_config : BackoffConfig = BackoffConfig(min_delay=1.0, max_delay=5.0, factor=1.3, jitter=0.1) , allowed_updates : List [ str ] | _SentinelObject | None = sentinel.UNSET , handle_signals : bool = True , close_bot_session : bool = True , ** kwargs : Any ) → None [source] #

  • bots – Bot instances (one or more)
  • polling_timeout – Long-polling wait time
  • handle_as_tasks – Run task for each event and no wait result
  • backoff_config – backoff-retry config
  • allowed_updates – List of the update types you want your bot to receive By default, all used update types are enabled (resolved from handlers)
  • handle_signals – handle signals (SIGINT/SIGTERM)
  • close_bot_session – close bot sessions on shutdown
  • kwargs – contextual data

async stop_polling ( ) → None [source] #

Execute this method if you want to stop polling programmatically

Simple usage#

dp = Dispatcher() @dp.message() async def message_handler(message: types.Message) -> None: await SendMessage(chat_id=message.from_user.id, text=message.text) 
dp = Dispatcher() router1 = Router() dp.include_router(router1) 

Handling updates#

All updates can be propagated to the dispatcher by Dispatcher.feed_update(bot=. update=. ) method:

bot = Bot(. ) dp = Dispathcher() . result = await dp.feed_update(bot=bot, update=incoming_update) 

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *