aiogram-renderer
О проекте
Библиотека создана для того чтобы ее можно было использовать вместе с aiogram, а не вместо aiogram. Реализованы основные текстовые и клавиатурные виджеты, чтобы ускорить и упростить создание telegram ботов.
Поддержка разработчика
Функции
aiogram-renderer включает в себя:
- 100% асинхронность
- гибкую структуру, позволяющую без проблем использовать библиотеку совместно с aiogram
- поддержку как Reply так и Inline клавиатур
- удобный виджет Progress для управления загрузками
- режимы бота, доступные из разных окон
- работу с файлами
- организованную логику для управления FSM данными окон
- окна-уведомления, которые можно отправлять без использования данных в FSM
- управление видимостью виджетов через параметр show_on
- динамические наборы кнопок доступные из разных окон
Установка
Для установки, выполните команду
pip install aiogram-renderer
Начало
Для подключения библиотеки к проекту надо выполнить функцию configure_renderer, в ней вам нужно передать окна, которые будут использоваться в проекте и настроить при необходимости режимы.
async def main():
logging.basicConfig(level=logging.INFO)
await configure_renderer(
dp=dp,
# Подключаем окна
windows=[window],
# Задаем режимы бота (первый активный по умолчанию)
modes=[
BotMode(
name="mode1",
values=["off 🟥⬜️ Режим 1", "on ⬜️🟩 Режим 1"],
alert_window=alert,
),
BotMode(
name="mode2",
values=["off 🟥⬜️ Режим 2", "on ⬜️🟩 Режим 2"],
alert_window=alert,
has_custom_handler=False
)
]
)
await bot.delete_webhook(drop_pending_updates=True)
await dp.start_polling(bot)
Типы окон
Для работы можно использовать 2 типа окон:
- Window - окно с состоянием State, данные хранятся в памяти FSM. Рендерятся окна с установкой стейта автоматически. Мапятся по state, подробнее в разделе.
window = Window(
Text("Тест окна"),
Panel(
Button(text="Кнопка", data="button"),
Button(text="Тоже кнопка", data="button_too"),
Mode(name="mode1"),
width=2
),
state=MainSteps.step1
)
- Alert - окно-уведомление, его данные не хранятся в FSM, соответственно на alert есть ограничения по использованию некоторых виджетов (Mode, DynamicPanel)
alert = Alert(
Area(
Bold("Тест окна", end_count=3),
"все хорошо",
sep_count=2
)
Отправка
Для отправки сообщений из собранных окон с виджетами, используйте функции render из встроенного аргумента renderer с mode=RenderMode.ANSWER и др. или более быстрые answer, edit, delete_and_send, reply. Вы можете использовать chat_id, message_id или event по желанию, по умолчанию renderer подхватывает event в хендлере.
@dp.message(F.text=="/start")
async def start(message: Message, renderer: Renderer):
message, window = await renderer.render(window=window, chat_id=message.chat_id, message_id=message.message_id, mode=RenderMode.EDIT)
@dp.message(F.text=="use1")
async def use1(message: Message, renderer: Renderer):
# Использование хендлера с Renderer
message, window = await renderer.delete_and_send(window=window)
@dp.message(F.text=="use2")
async def use2(message: Message):
# Использование встроенных методов окна для генерации исключельно клавиатуры или текста
text = await window.gen_text(data={"username": message.from_user.username})
await message.answer(text=text)
@dp.message(F.text=="use3")
async def use3(message: Message):
# Использование хендлера без Renderer
await message.answer(text="Все ок!")
Хранение данных
Данные собранные через библиотеку хранятся в fsm для каждого пользователя в следующем формате:
{
"__modes__": {
"mode1": ["off 🟥⬜️ Режим 1", "on ⬜️🟩 Режим 1"],
"mode2": ["off 🟥⬜️ Режим 2", "on ⬜️🟩 Режим 2"]
},
"__dpanels__": {
"test_dp": {
"page": 2,
"text": ["1","2","3"],
"data": ["d1","d2","d3"]
}
},
"__windows__": {
"MainSteps.step1": {
"username": "Коля",
"visible": true
},
"MainSteps.step2": {
"username": "Маша",
"surname": "Сечинова",
"visible": false
}
}
}
Все окна хранятся в порядке открытия и могут перезаписываться если указывать новые параметры data при их рендере:
@dp.message(F.text=="/start")
async def start(message: Message, renderer: Renderer):
await renderer.delete_and_send(window=window, data={"username": message.from_user.username})
window = Window(
Text("Привет {username}, Тест окна"),
Panel(
Button(text="Кнопка", data="button{username}"),
Button(text="Тоже кнопка", data="button_too"),
Mode(name="mode1"),
width=2
),
state=MainSteps.step1
)
Видимость виджетов
Для управления отображением виджетами используйте параметр show_on, значение берется из параметра data функции render и в дальнейшем хранится в памяти, учитывайте это и обновляйте параметр при необходимости.
window = Window(
Text("Привет {username}, Тест окна"),
Panel(
Button(text="Кнопка", data="button{username}", show_on="is_admin"),
Button(text="Тоже кнопка", data="button_too", show_on="is_admin"),
Mode(name="mode1"),
width=2
),
state=MainSteps.step1
)
@dp.message(F.text=="/start")
async def start(message: Message, renderer: Renderer):
await renderer.delete_and_send(window=window, data={"is_admin": True})
Режимы
Частенько требовалась кнопка которая бы обновляла и хранила свое активное состояние в разных сообщениях. Режимы позволяют реализовать это, укажите их в configure_renderer и потом передавайте в окна через Mode, ReplyMode виджеты. У каждого режима есть alert_window, это нужно чтобы работал системный хендлер ReplyMode, также вы можете указать has_custom_handler и настроить свою хендлер для обработки апдейта режима.
window = Window(
Text("Привет {username}, Тест окна"),
ReplyPanel(
ReplyButton(text="Кнопка", data="button{username}"),
ReplyButton(text="Тоже кнопка", data="button_too", show_on="is_admin"),
ReplyMode(name="mode2"),
width=2
),
state=MainSteps.step1
)
async def main():
logging.basicConfig(level=logging.INFO)
await configure_renderer(
dp=dp,
# Подключаем окна
windows=[window],
# Задаем режимы бота (первый активный по умолчанию)
modes=[
BotMode(
name="mode1",
values=["off 🟥⬜️ Режим 1", "on ⬜️🟩 Режим 1"],
),
BotMode(
name="mode2",
values=["Админ", "Юзер", "Продавец"],
alert_window=alert,
has_custom_handler=True
)
]
)
await bot.delete_webhook(drop_pending_updates=True)
await dp.start_polling(bot)
@dp.message(IsMode("mode2"))
async def update_mode(message: Message, renderer: Renderer):
# Получаем режим с помощью функции get_mode_by_value (также есть get_mode_by_name)
mode = await renderer.bot_modes.get_mode_by_value(value=message.text)
# Обновляем режим
await renderer.bot_modes.update_mode(mode=mode.name)
# Обновляем окно
await renderer.edit(window=MenuStates.step1)
Прогресс бар
Для управления полосой загрузки используйте виджет Progress и функцию update_progress, задайте нужный интервал обновления.
window = Window(
Text("Привет {username}, Тест окна", end_count=2),
Progress(name="test_pr")
Panel(
Button(text="Кнопка", data="button{username}"),
Button(text="Тоже кнопка", data="button_too", show_on="is_admin"),
Mode(name="mode1"),
width=2
),
state=MainSteps.step1
)
@dp.message(F.text=="/start")
async def start(message: Message, renderer: Renderer):
data = {"username": message,from_user.username, "test_pr": 0}
# Сначала будет установлен процент 0
pr_message, window = await renderer.answer(window=MenuStates.step1, data=data)
for i in range(99):
await renderer.update_progress(window=MenuStates.step1, event=pr_message, interval=0.3,
name="test_pr", percent=i, data=data)
await sleep(0.3)
Динамические наборы
Для отображения кнопок со своими данными используется виджет DynamicPanel. Нужно указать text, data и первую активную страницу, данные сохранятся в FSM и будут доступны для всех окон.
window = Window(
Text("Привет {username}, Тест окна", end_count=2),
DynamicPanel(name="dpanel", hide_number_pages=True),
state=MainSteps.step1
)
@dp.message(F.text=="/start")
async def start(message: Message, renderer: Renderer):
data = {
"dpanel": {
"page": 2,
"text": ["1", "2", "3", "4", "5"],
"data": ["d1", "d2", "d3", "d4", "d5"]
},
}
await renderer.answer(window=MenuStates.step1, data=data)
@dp.callback_query(F.data.startswith("d"))
async def dpanel_opt(callback: CallbackQuery, renderer: Renderer):
# Тут ваши действия для обработки кнопок из DynamicPanel
pass
Файлы
Для работы с файлами реализовано 2 виджета, первый это виджет File и его дочерние Photo, Video, Audio. В нем мы указываем путь к файлу и его название.
window = Window(
Text("Привет {username}, Тест окна", end_count=2),
Audio(file_name="audio.mp3", path="audio.mp3"),
state=MainSteps.step1
)
Второй это FileBytes, он используется для передачи файла в байтах, данные не хранятся в памяти и отображаются только тогда когда вы передаете параметр file_bytes в функции render.
window = Window(
Text("Привет {username}, Тест окна", end_count=2),
AudioBytes(file_name="audio.mp3", bytes_name="bytes_a"),
state=MainSteps.step1
)
@dp.message(F.text=="/start")
async def start(message: Message, renderer: Renderer):
async with aiofiles.open(file="audio.mp3", mode="rb") as f:
await renderer.answer(window=MenuStates.step1, file_bytes={"bytes_a": await f.read()})
Медиа группа имеет специфичную логику работы, так что пока она в разработке, нужно понять как ее правильно добавить в экосистему библиотеки.
Будущее проекта
В документации указаны не все виджеты, с остальными предлагаю ознакомиться самостоятельно 😉 По мере поддержки или при необходимости в работе буду добавлять новые виджеты и совершенствовать библиотеку, кому интересно. Так в планах MediaGroup, ReplyDynamicPanel, поддержка виджетов в Alert, виджет Calendar и поддержка доп. полей для кнопок, которые задаются в aiogram. Также возможно сделаю виджет DynamicMediaGroup. Все по мере возможностей, спасибо что прочитали.
Metadata
Release files for aiogram-renderer 1.3.8
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| aiogram_renderer-1.3.8.tar.gz | 99.7 kB | Without provenance | Details for aiogram_renderer-1.3.8.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| aiogram_renderer-1.3.8-py3-none-any.whl | Python 3 | none | any | Details for aiogram_renderer-1.3.8-py3-none-any.whl |
Total release size: 209.3 kB
Release files / aiogram_renderer-1.3.8.tar.gz
| Download URL | aiogram_renderer-1.3.8.tar.gz |
|---|---|
| Size | 99.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
f9ec2aba0ac91fb3264ed4e3d3f42e2c5e3760202d9f2848f4e8a9ad762b36b5
|
|
BLAKE2b-256 checksum How to use checksums |
b886bf54300524f6856c67308ec8c4b4ad32b95d37e0079b5e36fd631c7850c8
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.1.0 CPython/3.11.7
|
Release files / aiogram_renderer-1.3.8-py3-none-any.whl
| Download URL | aiogram_renderer-1.3.8-py3-none-any.whl |
|---|---|
| Size | 109.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
0618d7c07dc66635c12dd2e3cb65cba7ef29588168184a962b79f62791400e82
|
|
BLAKE2b-256 checksum How to use checksums |
03d34b0e79ed2142b4f9898e0afa7aa32c479500fdf416abe7016007a286b84d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.1.0 CPython/3.11.7
|
Security for aiogram-renderer 1.3.8
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.