Skip to content

feat: 10 примеров ботов для быстрого старта и миграции с Telegram#97

Merged
love-apples merged 8 commits intolove-apples:mainfrom
bish-x:feat/examples-bots
Apr 28, 2026
Merged

feat: 10 примеров ботов для быстрого старта и миграции с Telegram#97
love-apples merged 8 commits intolove-apples:mainfrom
bish-x:feat/examples-bots

Conversation

@bish-x
Copy link
Copy Markdown
Contributor

@bish-x bish-x commented Apr 8, 2026

Описание

Добавлены 10 готовых к запуску примеров ботов в директории examples/, покрывающих основные сценарии разработки на maxapi. Каждый пример — самодостаточный .py файл с подробными комментариями на русском языке и указанием аналога из экосистемы Telegram (aiogram / python-telegram-bot).

Примеры прошли 5 раундов ревью: code review, security audit, architecture review, проверка API-сигнатур по исходникам библиотеки.

Что добавлено

10 примеров ботов (examples/)

Базовые

Пример Строк Что покрывает
01_echo_bot.py 82 Bot, Dispatcher, CommandStart, BotStarted, SenderAction, polling
02_formatting_bot.py 172 Bold, Italic, Code, Link, UserMention, Text, as_html(), TextFormat
03_keyboard_bot.py 153 InlineKeyboardBuilder, CallbackButton, LinkButton, RequestContactButton, event.answer(), навигация «Назад»

Средний уровень

Пример Строк Что покрывает
04_fsm_bot.py 196 StatesGroup, State, BaseContext, set_state(), update_data(), clear(), валидация ввода, /cancel
05_media_bot.py 182 InputMedia, InputMediaBuffer, upload_media(), forward(), обработка вложений по типу
06_admin_bot.py 227 pin_message(), delete_message(), edit_message(), get_chat_by_id(), user_added/removed

Продвинутый

Пример Строк Что покрывает
07_router_bot.py 206 Router, include_routers(), BaseFilter, роутер-уровневые middleware и фильтры
08_middleware_bot.py 267 LoggingMiddleware, ThrottleMiddleware, AuthMiddleware, ErrorHandlingMiddleware, outer_middleware()
09_webhook_bot.py 217 FastAPIMaxWebhook, subscribe_webhook(), secret, кастомные роуты, lifespan
10_callback_payload_bot.py 287 CallbackPayload с prefix, pack(), filter(), каталог товаров с навигацией

Документация (examples/README.md)

Подробный гайд на ~360 строк:

  • Что такое MAX Messenger — вводный абзац + ссылки для новичков
  • Подготовка окружения — venv, установка, получение токена, настройка .env
  • Описание каждого примера — что демонстрирует, что изучите, аналог из Telegram
  • Структура примера — шаблон для создания своего бота
  • FAQ — 8 частых вопросов (InvalidToken, answer vs send_message, BotStarted, логирование, деплой)
  • Таблица миграции с Telegram (aiogram) — 11 ключевых отличий

Обновление README.md

Добавлена секция «Примеры ботов» с таблицами по уровням сложности (базовый / средний / продвинутый) и краткой таблицей миграции maxapi ↔ aiogram.

Настройка линтера (pyproject.toml)

Добавлены исключения ruff для examples/**.py — правила стиля, специфичные для примеров (INP001, PTH, PLC0415 и др.), чтобы код примеров оставался чистым и понятным.

.gitignore

Убрана строка examples/ из .gitignore (ранее директория игнорировалась).

Качество кода

Каждый пример проверен на:

  • Синтаксис — все файлы парсятся (ast.parse)
  • Линтерruff check examples/ → 0 ошибок
  • Форматruff format examples/ --check → 0 изменений
  • Корректность API — каждый импорт, метод и сигнатура сверены с исходниками maxapi
  • Безопасность — нет хардкоженных токенов, паролей, email, IP, личных данных
  • Production-паттерны — проверка None в body/sender, event.answer() для callbacks, error handling

Тестирование

480 passed, 14 skipped (интеграционные без токена)
ruff check — All checks passed
ruff format — 10 files already formatted

Существующие тесты библиотеки проходят без изменений.

Для кого это

  • Разработчики, мигрирующие с Telegram (aiogram / python-telegram-bot)
  • Новички, впервые создающие бота для MAX
  • Опытные разработчики, ищущие reference-реализации паттернов (FSM, middleware, webhook, payloads)

@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 9, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Добавляет в репозиторий набор самодостаточных примеров ботов и сопутствующую документацию, чтобы упростить быстрый старт и миграцию с Telegram-экосистемы на maxapi.

Changes:

  • Добавлены 10 Python-скриптов примеров ботов в examples/ (polling/webhook, middleware, router, FSM, media, callback payloads).
  • Добавлен подробный гайд examples/README.md и секция про примеры в корневом README.md.
  • Обновлены настройки Ruff (per-file ignores) и .gitignore для публикации examples/.

Reviewed changes

Copilot reviewed 13 out of 15 changed files in this pull request and generated 21 comments.

Show a summary per file
File Description
README.md Добавлена секция со ссылками на примеры и мини-таблица миграции.
pyproject.toml Добавлены Ruff per-file-ignores для examples/**.py.
examples/README.md Новый подробный гайд по запуску примеров, FAQ и таблица миграции.
examples/01_echo_bot.py Пример эхо-бота (polling).
examples/02_formatting_bot.py Пример форматирования (HTML/Markdown).
examples/03_keyboard_bot.py Пример inline-клавиатур и callback-обработки.
examples/04_fsm_bot.py Пример FSM/контекста состояний.
examples/05_media_bot.py Пример отправки/загрузки медиа и обработки вложений.
examples/06_admin_bot.py Пример админ-команд (pin/delete/edit/info/members) и событий чата.
examples/07_router_bot.py Пример роутеров, фильтров и middleware на уровне роутера.
examples/08_middleware_bot.py Пример middleware-цепочки (logging/throttle/auth/errors).
examples/09_webhook_bot.py Пример webhook-интеграции с FastAPI/uvicorn.
examples/10_callback_payload_bot.py Пример типизированных callback payloads и навигации.
examples/.DS_Store Добавлен бинарный артефакт macOS Finder.
.gitignore Убрано игнорирование examples/.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread examples/README.md
Comment on lines +18 to +29
```bash
python -m venv .venv
source .venv/bin/activate # Linux/macOS
# .venv\Scripts\activate # Windows

pip install maxapi
```

Для примера с webhook (09):
```bash
pip install maxapi[fastapi]
```
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Инструкция предлагает pip install maxapi, но все примеры импортируют python-dotenv и вызывают load_dotenv(). Сейчас python-dotenv находится только в dev-зависимостях, поэтому запуск примеров у пользователя приведёт к ModuleNotFoundError. Либо сделайте импорт dotenv опциональным в примерах, либо добавьте python-dotenv в зависимости/extra и обновите инструкцию установки.

Copilot uses AI. Check for mistakes.
Comment thread examples/README.md Outdated
Логгеры библиотеки: `bot`, `dispatcher`, `connection`.

**Как перейти с polling на webhook?**
Смотрите пример [`09_webhook_bot.py`](09_webhook_bot.py). Не забудьте удалить подписки polling через `await bot.delete_webhook()` если они были.
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Фраза про «удалить подписки polling через await bot.delete_webhook()» некорректна: delete_webhook() относится к webhook, а не к polling. Лучше переформулировать как «перед переходом на polling удалите webhook» или «если был webhook — выполните delete_webhook()».

Suggested change
Смотрите пример [`09_webhook_bot.py`](09_webhook_bot.py). Не забудьте удалить подписки polling через `await bot.delete_webhook()` если они были.
Смотрите пример [`09_webhook_bot.py`](09_webhook_bot.py). Если ранее был настроен webhook, удалите его через `await bot.delete_webhook()` перед переходом на polling.

Copilot uses AI. Check for mistakes.
Comment thread examples/01_echo_bot.py Outdated
Comment on lines +24 to +31
from dotenv import load_dotenv
from maxapi import Bot, Dispatcher, F
from maxapi.enums.sender_action import SenderAction
from maxapi.filters.command import CommandStart
from maxapi.types.updates.bot_started import BotStarted
from maxapi.types.updates.message_created import MessageCreated

load_dotenv()
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

from dotenv import load_dotenv делает пример зависимым от python-dotenv, который не устанавливается при pip install maxapi (см. pyproject.toml). Чтобы примеры запускались «из коробки», сделайте импорт/вызов load_dotenv() опциональным (try/except ImportError) или обновите зависимости/инструкцию установки.

Suggested change
from dotenv import load_dotenv
from maxapi import Bot, Dispatcher, F
from maxapi.enums.sender_action import SenderAction
from maxapi.filters.command import CommandStart
from maxapi.types.updates.bot_started import BotStarted
from maxapi.types.updates.message_created import MessageCreated
load_dotenv()
try:
from dotenv import load_dotenv
except ImportError:
load_dotenv = None
from maxapi import Bot, Dispatcher, F
from maxapi.enums.sender_action import SenderAction
from maxapi.filters.command import CommandStart
from maxapi.types.updates.bot_started import BotStarted
from maxapi.types.updates.message_created import MessageCreated
if load_dotenv is not None:
load_dotenv()

Copilot uses AI. Check for mistakes.
Comment thread examples/02_formatting_bot.py Outdated
Comment on lines +28 to +49
from dotenv import load_dotenv
from maxapi import Bot, Dispatcher
from maxapi.enums.parse_mode import TextFormat
from maxapi.filters.command import Command, CommandStart
from maxapi.types.updates.message_created import MessageCreated

# Импорты билдеров форматирования
from maxapi.utils.formatting import (
Bold,
Code,
Heading,
Italic,
Link,
Strikethrough,
Text,
Underline,
UserMention,
)

load_dotenv()
logging.basicConfig(level=logging.INFO)

Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

from dotenv import load_dotenv делает пример зависимым от python-dotenv, который не устанавливается при pip install maxapi. Лучше сделать импорт/вызов load_dotenv() опциональным или явно указать установку python-dotenv в документации.

Copilot uses AI. Check for mistakes.
Comment thread examples/03_keyboard_bot.py Outdated
Comment on lines +26 to +43
from dotenv import load_dotenv
from maxapi import Bot, Dispatcher
from maxapi.filters.command import CommandStart
from maxapi.types.attachments.buttons.callback_button import CallbackButton
from maxapi.types.attachments.buttons.link_button import LinkButton
from maxapi.types.attachments.buttons.request_contact import (
RequestContactButton,
)
from maxapi.types.attachments.buttons.request_geo_location_button import (
RequestGeoLocationButton,
)
from maxapi.types.updates.message_callback import MessageCallback
from maxapi.types.updates.message_created import MessageCreated
from maxapi.utils.inline_keyboard import InlineKeyboardBuilder

load_dotenv()
logging.basicConfig(level=logging.INFO)

Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

from dotenv import load_dotenv делает пример зависимым от python-dotenv, который не устанавливается при pip install maxapi. Лучше сделать импорт/вызов load_dotenv() опциональным или явно указать установку python-dotenv в документации.

Copilot uses AI. Check for mistakes.
Comment thread examples/09_webhook_bot.py Outdated
Comment on lines +57 to +94
WEBHOOK_URL: str = os.getenv("WEBHOOK_URL", "https://example.com/webhook")
WEBHOOK_SECRET: str | None = os.getenv("WEBHOOK_SECRET") or None
WEBHOOK_HOST: str = os.getenv(
"WEBHOOK_HOST", "0.0.0.0"
) # В production используйте 127.0.0.1 за reverse proxy
WEBHOOK_PORT: int = int(os.getenv("WEBHOOK_PORT", "8080"))
WEBHOOK_PATH: str = os.getenv("WEBHOOK_PATH", "/webhook")

bot = Bot()
dp = Dispatcher()

# ---------------------------------------------------------------------------
# Хендлеры
# ---------------------------------------------------------------------------


@dp.on_started()
async def on_dp_started() -> None:
"""Вызывается один раз при старте диспетчера.

Регистрируем webhook в MAX-платформе, чтобы она слала обновления
на наш URL.
"""
log.info("Диспетчер запущен, подписываемся на webhook: %s", WEBHOOK_URL)
try:
await bot.subscribe_webhook(
url=WEBHOOK_URL,
update_types=[
UpdateType.MESSAGE_CREATED,
UpdateType.MESSAGE_CALLBACK,
UpdateType.BOT_STARTED,
],
secret=WEBHOOK_SECRET,
)
log.info("Подписка на webhook зарегистрирована успешно.")
except Exception as exc:
log.error("Не удалось зарегистрировать webhook: %s", exc)

Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WEBHOOK_URL берётся с дефолтом https://example.com/webhook, после чего subscribe_webhook() вызывается автоматически в on_started. Если пользователь забудет выставить переменную окружения, пример будет пытаться зарегистрировать заведомо неверный URL. Лучше сделать WEBHOOK_URL обязательной (без дефолта) и/или при отсутствии значения пропускать подписку с понятным сообщением и завершением процесса.

Copilot uses AI. Check for mistakes.
Comment thread examples/10_callback_payload_bot.py Outdated
Comment on lines +23 to +36
from dotenv import load_dotenv
from maxapi import Bot, Dispatcher, F
from maxapi.enums.sender_action import SenderAction
from maxapi.filters.callback_payload import CallbackPayload
from maxapi.filters.command import CommandStart
from maxapi.types.attachments.buttons.callback_button import CallbackButton
from maxapi.types.updates.bot_started import BotStarted
from maxapi.types.updates.message_callback import MessageCallback
from maxapi.types.updates.message_created import MessageCreated
from maxapi.utils.inline_keyboard import InlineKeyboardBuilder

load_dotenv()
logging.basicConfig(level=logging.INFO)

Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

from dotenv import load_dotenv делает пример зависимым от python-dotenv, который не устанавливается при pip install maxapi. Лучше сделать импорт/вызов load_dotenv() опциональным или явно указать установку python-dotenv в документации.

Copilot uses AI. Check for mistakes.
Comment on lines +135 to +139
# Кнопка «Купить» (для демонстрации — просто уведомление)
buy_payload = DetailPayload(
item_id=item_id, category_id=category_id
).pack()
builder.row(CallbackButton(text="Купить", payload=buy_payload))
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Название DetailPayload и его докстринг («Подробнее») не соответствуют фактическому использованию: этим payload обрабатывается нажатие кнопки «Купить». Чтобы пример был понятнее, лучше выделить отдельный payload (например, BuyPayload) или переименовать текущий класс/описание под реальное действие.

Suggested change
# Кнопка «Купить» (для демонстрации — просто уведомление)
buy_payload = DetailPayload(
item_id=item_id, category_id=category_id
).pack()
builder.row(CallbackButton(text="Купить", payload=buy_payload))
# Кнопка «Подробнее» использует payload детального просмотра
buy_payload = DetailPayload(
item_id=item_id, category_id=category_id
).pack()
builder.row(CallbackButton(text="Подробнее", payload=buy_payload))

Copilot uses AI. Check for mistakes.
Comment thread pyproject.toml
Comment on lines 157 to +174
[tool.ruff.lint.per-file-ignores]
"tests/**.py" = [
"ARG",
"E402",
"PL",
"S",
"SLF",
]
"examples/**.py" = [
"INP001",
"PTH",
"ASYNC240",
"PLC0415",
"S",
"T20",
"FBT",
"SIM105",
]
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Перечень per-file-ignores добавлен для examples/**.py, но сами примеры сейчас требуют python-dotenv (см. импорты from dotenv import load_dotenv). При этом python-dotenv не входит в основную зависимость пакета. Чтобы примеры действительно работали при обычной установке библиотеки, стоит либо добавить python-dotenv в dependencies/optional extra, либо сделать dotenv опциональным в самих примерах.

Copilot uses AI. Check for mistakes.
Comment thread README.md Outdated
Comment on lines +169 to +174
В директории [`examples/`](examples/) находятся готовые к запуску примеры ботов, покрывающие основные сценарии использования библиотеки. Каждый пример — самодостаточный `.py` файл с подробными комментариями на русском языке.

Запуск любого примера:
```bash
MAX_BOT_TOKEN=ваш_токен python examples/01_echo_bot.py
```
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

В README предлагается запускать примеры напрямую после установки, но сами примеры импортируют python-dotenv и вызывают load_dotenv(). При pip install maxapi эта зависимость не устанавливается, поэтому примеры могут не запуститься. Либо сделайте dotenv опциональным в примерах, либо уточните в README необходимость pip install python-dotenv/соответствующего extra.

Copilot uses AI. Check for mistakes.
@bish-x
Copy link
Copy Markdown
Contributor Author

bish-x commented Apr 14, 2026

@love-apples поправил все 4 замечания в 5754e19:

  • P1 (03_keyboard_bot.py, 04_fsm_bot.py) — заменил attachments=None на attachments=[]. Message.edit() при None подставляет существующие вложения назад, пустой список — единственный способ удалить клавиатуру. Добавил комментарий, чтобы будущие читатели не наступили на те же грабли.
  • P1 (05_media_bot.py, /photo и /upload) — добавил get_sample_image_path(): если рядом со скриптом есть sample.jpg — берём его, иначе создаём временный PNG 1×1. Пример теперь запускается из коробки, как и обещано в README.
  • P2 (05_media_bot.py) — перешёл с имён-строк PhotoAttachment/VideoAttachment/… на isinstance по реальным классам Image/Video/Audio/File/Sticker. Заодно добавил ветку для стикеров.
  • P3 (examples/README.md)bot.get_members_chat()bot.get_chat_members().

Параллельно в #96 починил тест, который упал из-за мерджа main в ветку (лог в _resolve_from_user перестал включать текст исключения MaxConnection — вернул %s для exc).

bish-x and others added 7 commits April 22, 2026 19:25
Добавлена директория examples/ с 10 готовыми к запуску примерами ботов,
покрывающими основные сценарии разработки и миграции с Telegram.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Made python-dotenv optional (try/except ImportError) in all 10 examples
- Added note about optional python-dotenv to README.md and examples/README.md
- Added event.message is None guard in 03_keyboard_bot.py and 04_fsm_bot.py
- Added chat_id is None early return in 05_media_bot.py cmd_photo
- Fixed AuthMiddleware in 08_middleware_bot.py to extract user_id from callbacks
- Fixed docstring in 09_webhook_bot.py (webhook.run() -> uvicorn.Server)
- Made WEBHOOK_URL required (os.environ["WEBHOOK_URL"]) in 09_webhook_bot.py
- Renamed DetailPayload to BuyPayload in 10_callback_payload_bot.py
- Fixed incorrect phrase in examples/README.md (polling -> webhook subscriptions)
- Removed examples/.DS_Store and added .DS_Store to .gitignore

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- F401: добавлен вызов load_dotenv() (был только импорт)
- E501: комментарий перенесён на отдельную строку
- Добавлен guard `if chat_id is None: return` в cmd_buffer, cmd_upload и on_attachment
- Исправлена формулировка про webhook/polling в FAQ
- Добавлено упоминание python-dotenv как опциональной зависимости

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
P1: В 03_keyboard_bot.py и 04_fsm_bot.py передавался attachments=None
    для удаления клавиатуры, но Message.edit() при None подставляет
    существующие вложения обратно. Теперь используем пустой список — это
    единственный способ реально удалить клавиатуру.

P1: В 05_media_bot.py команды /photo и /upload падали в «файл не найден»,
    потому что sample.jpg не лежит в репозитории. Добавлен fallback:
    если sample.jpg рядом нет — создаём временный PNG 1×1 пиксель, так
    пример запускается «из коробки» (см. обещание в README.md).

P2: В 05_media_bot.py определение типа вложения шло по несуществующим
    именам (PhotoAttachment/VideoAttachment/…). Реальные классы в
    maxapi — Image, Video, Audio, File, Sticker. Перешёл на isinstance
    проверки по реальным классам — это заодно защищает от опечаток.

P3: В examples/README.md опечатка bot.get_members_chat() исправлена на
    bot.get_chat_members().
…ела получения токена

- Откат добавленных 55 строк в корневом README.md (секция
  «Примеры ботов» дублировала содержимое examples/README.md).
- В examples/README.md удалён раздел «2. Получение токена»
  (избыточная инструкция по установке MAX + создание бота через
  MasterBot — выходит за рамки примеров библиотеки).
- Перенумерованы оставшиеся подразделы 3→2, 4→3.
- Обновлена якорная ссылка в FAQ на love-apples#2-настройка-токена.
@bish-x bish-x force-pushed the feat/examples-bots branch from 5754e19 to f7dfd1b Compare April 22, 2026 12:27
@bish-x
Copy link
Copy Markdown
Contributor Author

bish-x commented Apr 22, 2026

Сузил scope:

  • откатил правки корневого README.md (секция «Примеры ботов» дублировала examples/README.md);
  • в examples/README.md убрал раздел «2. Получение токена» (инструкция по установке MAX + создание бота через MasterBot — не по теме примеров), оставшиеся подразделы перенумеровал;
  • попутно ребейзнул на main (rebaseable-флаг был false).

По коду примеров ничего не трогал.

Olegt0rr

This comment was marked as outdated.

@Olegt0rr Olegt0rr dismissed their stale review April 22, 2026 22:16

Не актуально, не обновил страницу

Comment thread examples/01_echo_bot.py Outdated
Comment on lines +24 to +29
try:
from dotenv import load_dotenv

load_dotenv()
except ImportError:
pass
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Не очень хорошая практика

  1. Для глушения в 0 есть suppress.
  2. Если load не предусмотрен, то зачем писать? Если предусмотрен - лучше падать.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Поправил по п. 1 — заменил try/except: pass на contextlib.suppress(ImportError) во всех 10 примерах (5d8ac1b). Заодно убрал SIM105 из examples/**.py per-file-ignores в pyproject.toml — после правки заглушка не нужна.

По п. 2: dotenv-блок осознанно сохранил как опциональный QoL-хук для разработчика (помечено комментарием над with). python-dotenv — в [dependency-groups] dev, в публичный контракт не входит, поэтому жёсткий fail (как в webhook/fastapi.py) тут уместен меньше: пример должен запускаться и без неё через MAX_BOT_TOKEN=… python …. Если такая трактовка не ок — уберу блок целиком отдельным коммитом, скажи.

Заодно вернул [tool.setuptools.package-data] maxapi = ["py.typed"] — случайно стёрлось при rebase'е.

По замечанию @Olegt0rr (love-apples#97 r3127205121): try/except: pass — анти-
паттерн (ruff SIM105). Заменено на contextlib.suppress(ImportError)
во всех 10 примерах. dotenv-блок осознанно сохранён как опциональный
QoL-хук для разработчика (помечено комментарием), python-dotenv в
[dependency-groups] dev.

- 10 examples/0*.py: try/except → with contextlib.suppress(ImportError)
- pyproject.toml: убран SIM105 из examples/**.py per-file-ignores
  (после правки уже не нужен)
- pyproject.toml: восстановлено [tool.setuptools.package-data]
  maxapi = ["py.typed"] (артефакт rebase'а — случайно стёрто)
- 08_middleware_bot.py: тот же SIM105-фикс на пре-существующий
  LoggingMiddleware (try/except Exception → contextlib.suppress)
@love-apples love-apples merged commit 6f4280f into love-apples:main Apr 28, 2026
13 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants