Aiogram extention aiogram-dialog
Aiogram-dialog — это фреймворк графического интерфейса для телеграмм-бота. Он вдохновлен идеями Android SDK и React.js.
Versions
На момент написания статьи последней дев-версией был 2.0b17. Как мигрировать тут.
Пример кода смотри тут.
Version status
- v1.x stable release, supports aiogram v2.x, bugfix only
- v2.x beta, future release, supports aiogram v3.x
Основные идеи
- Раздельное извлечение данных и рендеринг сообщений
- Объединененные отрисовки кнопок и обработки кликов
- Улучшенная маршрутизация состояний
- Виджеты
Основным строительным блоком интерфейса является Window
. Каждое окно представляет собой сообщение, отправляемое пользователю, и обработку реакции пользователя на это сообщение.
Каждое окно состоит из виджетов и функций обратного вызова. Виджеты могут отображать текст сообщения и клавиатуру. Обратные вызовы используются для получения необходимых данных или обработки пользовательского ввода.
Окна объединены в Dialog
. Это позволяет переключаться между окнами, создавая различные сценарии общения с пользователем.
В более сложных случаях можно создать более одного диалога. Можно запускать новые диалоги, не закрывая предыдущий, и автоматически возвращаться обратно, когда новый диалог закрывается. Можно передавать данные между диалогами, одновременно сохраняя их состояние изолированным.
Quicl start
pip install aiogram_dialog
Создайте [aiogram] группу состояний для вашего диалога:
from aiogram.dispatcher.filters.state import StatesGroup, State
class MySG(StatesGroup):
main = State()
Создайте хотя бы одно окно с кнопками или текстом:
from aiogram.dispatcher.filters.state import StatesGroup, State
from aiogram_dialog import Window
from aiogram_dialog.widgets.kbd import Button
from aiogram_dialog.widgets.text import Const
class MySG(StatesGroup):
main = State()
main_window = Window(
Const("Hello, unknown person"), # just a constant text
Button(Const("Useless button"), id="nothing"), # button with text and id
state=MySG.main, # state is used to identify window between dialogs
)
Создайте диалог с вашими окнами:
from aiogram_dialog import Dialog
dialog = Dialog(main_window)
Предположим, что вы создали своего бота с диспетчером и хранилищем состояний, как обычно. Важно, чтобы у вас было хранилище, потому что aiogram_dialog использует FSMContext aiogram для хранения своего состояния
from aiogram import Bot, Dispatcher, executor
from aiogram.contrib.fsm_storage.memory import MemoryStorage
storage = MemoryStorage()
bot = Bot(token='BOT TOKEN HERE')
dp = Dispatcher(bot, storage=storage)
Чтобы начать использовать ваш диалог, вам необходимо его зарегистрировать. Также библиотека нуждается в некоторых дополнительных регистрациях для своих внутренних процессов. Для этого мы создадим DialogRegistry и используем его для регистрации нашего диалога.
from aiogram_dialog import DialogRegistry
registry = DialogRegistry(dp) # this is required to use `aiogram_dialog`
registry.register(dialog) # register a dialog
На данный момент мы все настроили. Но диалог не запускается сам. Мы создадим простой обработчик команд, чтобы справиться с этим. Для запуска диалога нам нужен DialogManager
, который автоматически внедряется библиотекой. Также обратите внимание на reset_stack
аргумент. Библиотека может запускать несколько диалогов, расположенных друг над другом. В настоящее время нам не нужна эта функция, поэтому мы будем сбрасывать стек при каждом запуске:
from aiogram.types import Message
from aiogram_dialog import DialogManager, StartMode
@dp.message_handler(commands=["start"])
async def start(m: Message, dialog_manager: DialogManager):
# Important: always set `mode=StartMode.RESET_STACK` you don't want to stack dialogs
await dialog_manager.start(MySG.main, mode=StartMode.RESET_STACK)
Последний шаг, вам нужно запустить бота как обычно:
from aiogram import executor
if __name__ == '__main__':
executor.start_polling(dp, skip_updates=True)
Полный пример (совместим с [aiogram] 2.0):
from aiogram import Bot, Dispatcher, executor
from aiogram.contrib.fsm_storage.memory import MemoryStorage
from aiogram.dispatcher.filters.state import StatesGroup, State
from aiogram.types import Message
from aiogram_dialog import Window, Dialog, DialogRegistry, DialogManager, StartMode
from aiogram_dialog.widgets.kbd import Button
from aiogram_dialog.widgets.text import Const
storage = MemoryStorage()
bot = Bot(token='BOT TOKEN HERE')
dp = Dispatcher(bot, storage=storage)
registry = DialogRegistry(dp)
class MySG(StatesGroup):
main = State()
main_window = Window(
Const("Hello, unknown person"),
Button(Const("Useless button"), id="nothing"),
state=MySG.main,
)
dialog = Dialog(main_window)
registry.register(dialog)
@dp.message_handler(commands=["start"])
async def start(m: Message, dialog_manager: DialogManager):
await dialog_manager.start(MySG.main, mode=StartMode.RESET_STACK)
if __name__ == '__main__':
executor.start_polling(dp, skip_updates=True)
Виджеты и рендеринг
Как передаются данные
Некоторые виджеты содержат фиксированный текст, другие могут отображать динамическое содержимое. Для загрузки данных в окнах и диалогах есть getter
атрибут. detter
может быть как функцией, возвращающей данные, так и статическим словарем или списком таких объектов.
from aiogram.dispatcher.filters.state import StatesGroup, State
from aiogram_dialog import Window, Dialog
from aiogram_dialog.widgets.kbd import Button
from aiogram_dialog.widgets.text import Const, Format
class MySG(StatesGroup):
main = State()
async def get_data(**kwargs):
return {
"name": "Tishka17",
}
dialog = Dialog(
Window(
Format("Hello, {name}!"),
Button(Const("Useless button"), id="nothing"),
state=MySG.main,
getter=get_data, # here we set our data getter
)
)
Часть объектов доступно без геттера, а через атрибут хендлера:
dialog_data
- содержимое соответствующего поля из текущего контекста. Обычно он используется для хранения данных между несколькими вызовами и окнами в одном диалоговом окне.start_data
- данные, передаваемые при запуске текущего диалога. Также доступны с помощьюcurrent_context
middleware_data
- данные передаются от промежуточного программного обеспечения к обработчику. Еще можно такdialog_manager.data
event
- текущее событие обработки, вызвавшее обновление окна. Будьте осторожны, используя его, потому что разные типы событий могут привести к обновлению одного и того же окна.
Типы виджетов
В настоящее время существует 3 вида виджетов: тексты, клавиатуры и медиа
- Texts, используются для отображения текста в любом месте диалога. Это может быть текст сообщения, заголовок кнопки и так далее.
- Keyboards представляют собой части
InlineKeyboard
[aiogram] - Media представляет мультимедийный аттачмент к сообщению
Также есть 2 основных типа:
Whenable
могут быть скрыты или показаны в зависимости от данных или некоторых условий. В настоящее время все виджеты доступны.Actionable
любой виджет с действием (в настоящее время только любой тип клавиатуры). Он имеет id и может быть найден по этому идентификатору. Рекомендуется, чтобы все виджеты с состоянием (например, флаги) имели уникальный идентификатор в диалоговом окне. Кнопки с разным поведением также должны иметь разные идентификаторы.
Тексты
Const
- возвращает текст без промежуточных значенийFormat
- форматирует текст с помощьюformat
. При использовании в окне данные извлекаются через getter.Multi
- несколько текстов, соединенных разделителемCase
- показывает один из текстов по условиюProgress
- показывает индикатор выполненияJinja
— представляет собой HTML, отображаемый с использованием шаблона jinja2.
Клавиатуры
Каждая клавиатура имеет одну или несколько встроенных кнопок. Текст на кнопке отображается с помощью текстового виджета
Button
— одна встроенная кнопка. Предоставленный пользователем on_click метод вызывается при нажатии.Url
— одна встроенная кнопка с URLGroup
- любая группа клавиатур друг над другом или микс из кнопокScrollingGroup
— то же, что иGroup
, но с возможностью прокрутки страниц кнопками.ListGroup
- группа виджетов применяемая многократно для каждого элемента в спискеRow
- упрощенный вариант группы. Все кнопки расположены в один ряд.Column
— еще одна упрощенная версия группы. Все кнопки размещены в одном столбце по одной в строке.Checkbox
— кнопка с двумя состояниямиSelect
- динамическая группа кнопок, предназначенная для использования при выборе.Radio
- переключение между несколькими элементами. Подобно select, но сохраняет выбранный элемент и отображает его по-другому.Multiselect
- выбор нескольких элементов. Подобно select/radio, но сохраняет все выбранные элементы и отображает их по-разному.Calendar
— имитирует календарь в виде клавиатуры.SwitchTo
- переключает окно в диалоге, используя предоставленное состояниеNext/Back
- переключает состояние вперед или назадStart
- запускает новый диалог без параметровCancel
- закрывает текущий диалог без результата. Отображается базовый диалог
Transitions
Разговаривая с пользователем, вам нужно будет переключаться между различными состояниями чата. Это можно сделать с помощью четырех типов переходов:
- Переключение состояния внутри диалога. При этом вы просто покажете другое окно.
- Запук диалога в том же стеке. В этом случае диалог будет добавлен в стек задач с пустым контекстом диалога, а соответствующее окно будет показано вместо ранее видимого.
- Начать диалог в новом стеке. В этом случае диалог будет отображаться в новом сообщении и вести себя независимо от текущего.
- Закрыть диалог. Диалог будет удален из стека, его данные стерты. Основной диалог будет снова показан.
Стек задач
Для работы с несколькими открытыми диалогами в aiogram_dialog есть стек диалогов. Это позволяет открывать диалоги друг над другом («складывать»), поэтому виден только один из них.
Каждый раз, когда вы запускаете диалог, новая задача добавляется поверх стека и создается новый контекст диалога.
Каждый раз, когда вы закрываете диалог, задача и контекст диалога удаляются.
Вы можете запустить один и тот же диалог несколько раз, и несколько контекстов (обозначенных intent_id) будут добавлены в стек с сохранением порядка. Поэтому вы должны быть осторожны, перезапуская диалоги: не забудьте очистить стек, иначе он съест всю вашу память.
Начиная с версии 1.0 вы можете создавать новые стеки, но всегда существует один по умолчанию.
Самое простое, что вы можете сделать, чтобы изменить макет пользовательского интерфейса, — это переключить состояние диалога. Это не влияет на стек задач и просто рисует другое окно. Контекст диалога остается прежним, поэтому все ваши данные по-прежнему доступны.
Есть несколько способов сделать это:
dialog.switch_to
метод. Передайте другое состояние, и окно будет переключеноdialog.next
метод. Он переключится на следующее окно в том же порядке, в котором они были переданы при создании диалога. Невозможно вызвать, когда последнее окно активноdialog.back
метод. Переключиться на противоположное направление (на предыдущее). Невозможно вызвать, когда активно первое окно
Пример
from aiogram.dispatcher.filters.state import StatesGroup, State
from aiogram.types import CallbackQuery
from aiogram_dialog import Dialog, DialogManager, Window
from aiogram_dialog.widgets.kbd import Button, Row
from aiogram_dialog.widgets.text import Const
class DialogSG(StatesGroup):
first = State()
second = State()
third = State()
async def to_second(c: CallbackQuery, button: Button, manager: DialogManager):
await manager.dialog().switch_to(DialogSG.second)
async def go_back(c: CallbackQuery, button: Button, manager: DialogManager):
await manager.dialog().back()
async def go_next(c: CallbackQuery, button: Button, manager: DialogManager):
await manager.dialog().next()
dialog = Dialog(
Window(
Const("First"),
Button(Const("To second"), id="sec", on_click=to_second),
state=DialogSG.first,
),
Window(
Const("Second"),
Row(
Button(Const("Back"), id="back2", on_click=go_back),
Button(Const("Next"), id="next2", on_click=go_next),
),
state=DialogSG.second,
),
Window(
Const("Third"),
Button(Const("Back"), id="back3", on_click=go_back),
state=DialogSG.third,
)
)
Для упрощения у нас есть специальные типы кнопок. Каждый из них может содержать пользовательский текст, если это необходимо:
SwitchTo
- switch_to при нажатии. Состояние предоставляется через атрибут конструктораNext
- next при кликеBack
- back при клике
Несколько полезных инстурментов контроля реализации диалгов
Смотри еще:
- документация
- github
- [aiogram]
- [telegram-bots]
- [python-telegram-bot] библиотека
- [aiohttp]
- [asyncio]