Skip to content

feat: соответствие swagger 0.0.30#145

Open
love-apples wants to merge 4 commits into
mainfrom
upd-swagger-0-0-30-c
Open

feat: соответствие swagger 0.0.30#145
love-apples wants to merge 4 commits into
mainfrom
upd-swagger-0-0-30-c

Conversation

@love-apples
Copy link
Copy Markdown
Owner

closes #141, #140

@codecov
Copy link
Copy Markdown

codecov Bot commented May 5, 2026

Codecov Report

❌ Patch coverage is 98.41270% with 1 line in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
maxapi/methods/send_action.py 0.00% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

Comment thread maxapi/enums/text_style.py
@love-apples
Copy link
Copy Markdown
Owner Author

Неееет копилот
Я не закончил, там траблов оч много)))

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

Этот PR приводит библиотеку в соответствие со Swagger 0.0.30 и закрывает связанные проблемы вокруг callback-ответов (редактирование сообщения через MessageCallback, работа инлайн‑клавиатур в примерах), а также синхронизирует модели/enum’ы с актуальными полями API.

Changes:

  • Добавлена поддержка передачи attachments в MessageCallback.edit()/answer() и обновлена сериализация вложений в SendCallback.
  • Обновлены enum’ы/модели под Swagger (права админов, TextStyle/markup, поля вложений image/video/contact/share).
  • Удалены утилиты/тесты для генерации ссылок на сообщения (message_link) и упрощена модель Message.url.

Reviewed changes

Copilot reviewed 28 out of 28 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
tests/test_utils/test_message_link.py Удалены тесты утилит message_link (функциональность выведена из проекта).
tests/test_upload_file.py Адаптация тестов к новому способу мокинга session.post (await вместо async context manager).
tests/test_swagger_alignment.py Добавлены проверки совместимости моделей/enum’ов с актуальным Swagger.
tests/test_send_callback.py Новый тест на сериализацию inline keyboard как attachment в /answers.
tests/test_messagecallback_none.py Добавлены тесты на attachments в MessageCallback.edit().
tests/test_message_url.py Удалены тесты свойства Message.url (раньше включало генерацию ссылки).
tests/test_highlevel_shortcuts.py Обновлены high-level shortcut тесты под новые аргументы attachments в callback-операциях.
tests/test_formatting.py Переименование BLOCKQUOTEQUOTE в тестах форматирования.
tests/test_coverage_gaps.py Актуализация моков upload’а под await session.post(...).
tests/test_bot.py Удалены тесты, завязанные на строковые значения send_action.
maxapi/utils/message_link.py Удалена реализация message_link утилит.
maxapi/utils/formatting.py Добавлен стиль Highlighted для <mark> / ^^..^^.
maxapi/utils/init.py Убраны реэкспорты message_link из maxapi.utils.
maxapi/types/users.py ChatAdmin расширен полем alias для совместимости со Swagger.
maxapi/types/updates/message_callback.py MessageCallback.edit/answer расширены поддержкой attachments.
maxapi/types/message.py Обновлена обработка стилей разметки; url теперь обычное поле (без генерации).
maxapi/types/attachments/video.py Video/thumbnail сделаны более совместимыми со Swagger (nullable поля, default type).
maxapi/types/attachments/image.py Обновлена форма photos в request payload и добавлен PhotoToken.
maxapi/types/attachments/attachment.py Ослаблены ограничения на поля некоторых payload’ов и добавлен AttachmentPayload alias.
maxapi/types/init.py Экспортирован PhotoToken.
maxapi/methods/send_callback.py Ручная сериализация message.attachments с обработкой InputMedia/Upload.
maxapi/methods/send_action.py Упрощён ввод action (теперь типизирован как SenderAction).
maxapi/methods/edit_chat.py Уточнена валидация взаимоисключающих полей icon и обработка notify=False.
maxapi/enums/text_style.py BLOCKQUOTE заменён на QUOTE, добавлен/актуализирован HIGHLIGHTED.
maxapi/enums/chat_permission.py Добавлены новые значения прав админов под Swagger.
maxapi/connection/base.py Изменён способ получения response при upload (await post вместо async with).
maxapi/bot.py send_action теперь принимает SenderAction (без str в сигнатуре).
examples/03_keyboard_bot.py Исправлен пример: использование event.edit(...) и корректная очистка клавиатуры.

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

Comment thread maxapi/connection/base.py Outdated
Comment on lines +235 to +238
session = bot.session
if session is not None and not session.closed:
async with session.post(url=url, data=form) as response:
return await response.text()
response = await session.post(url=url, data=form)
return await response.text()
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.

Дельное замечание

Comment thread maxapi/connection/base.py Outdated
Comment on lines 240 to 244
async with ClientSession(
timeout=bot.default_connection.timeout
) as temp_session:
response = await temp_session.post(url=url, data=form)
return await response.text()
Comment thread maxapi/connection/base.py Outdated
Comment on lines +286 to +289
session = bot.session
if session is not None and not session.closed:
async with session.post(url=url, data=form) as response:
return await response.text()
response = await session.post(url=url, data=form)
return await response.text()
Comment thread maxapi/connection/base.py Outdated
Comment on lines 291 to 295
async with ClientSession(
timeout=bot.default_connection.timeout
) as temp_session:
response = await temp_session.post(url=url, data=form)
return await response.text()
) from e

self.bot = bot
self.chat_id = chat_id
Copy link
Copy Markdown
Collaborator

@Olegt0rr Olegt0rr May 6, 2026

Choose a reason for hiding this comment

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

Замечание дельное, а предложение хреновое. Нужно убедиться, что SenderAction это StrEnum и тогда можно убрать использование .value – тогда проблема уйдёт.

Comment on lines +45 to +48
text: str | None = None
attachments: list[Attachments] | None = Field(default_factory=list) # type: ignore
attachments: list[Attachment | AttachmentPayload | Attachments] | None = (
Field(default_factory=list)
)
self,
notification: str | None = None,
new_text: str | None = None,
attachments: Sequence[Attachment] | None = None,
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

Copilot reviewed 22 out of 22 changed files in this pull request and generated 7 comments.

Comments suppressed due to low confidence (4)

maxapi/types/updates/message_callback.py:42

  • attachments: list[AttachmentInput] | None = Field(default_factory=list) declares the field as list | None but the default factory produces [], never None. Either drop | None from the annotation, or set default=None (and adapt downstream code) — keeping both is misleading to consumers and to type checkers (the field will never be None by default).
    attachments: list[AttachmentInput] | None = Field(default_factory=list)

maxapi/methods/edit_chat.py:84

  • The new check sum(value is not None for value in dump.values()) != 1 implicitly assumes the icon model has exactly one populated discriminator field, but PhotoAttachmentRequestPayload now contains 3 fields (url, token, photos). The previous logic None not in counter or not counter[None] == 2 was tied to a 2-field model and is now both wrong (off by one against the new shape) and brittle. The new logic is correct for the current shape but the docstring above (в модели должно быть ровно 2 поля с None) still describes the old 2-None invariant — please update the docstring to reflect "ровно одно непустое поле" so the constraint stays clear if more icon fields are added later.
        if self.icon:
            dump = self.icon.model_dump()

            if sum(value is not None for value in dump.values()) != 1:
                raise MaxIconParamsException(
                    "Все атрибуты модели Icon являются взаимоисключающими | "
                    "https://dev.max.ru/docs-api/methods/PATCH/chats/-chatId-"
                )

maxapi/types/updates/message_callback.py:249

  • MessageCallback.answer exposes the new attachments parameter but its docstring Args: section is not updated to mention it (only notification, new_text, link, notify, format, raise_if_not_exists are listed). Per the project's Google-style docstring convention used throughout this module, all public arguments should be documented.
    async def answer(
        self,
        notification: str | None = None,
        new_text: str | None = None,
        attachments: Sequence[AttachmentInput] | None = None,
        link: NewMessageLink | None = None,
        format: ParseMode | None = None,
        *,
        notify: bool = True,
        raise_if_not_exists: bool = True,
    ) -> SendedCallback:
        """
        Отправляет ответ на callback с возможностью изменить текст
        и параметры уведомления.

        Args:
            notification: Текст уведомления.
            new_text: Новый текст сообщения.
            link: Связь с другим сообщением.
            notify: Отправлять ли уведомление.
            format: Режим разбора текста.
            raise_if_not_exists: Выдавать ошибку при отсутствии сообщения,
                если пытаются изменить его содержимое (new_text/link/format).

        Returns:
            SendedCallback: Результат вызова send_callback бота.
        """

maxapi/types/updates/message_callback.py:144

  • MessageCallback.edit accepts the new attachments argument but its docstring is just a one-liner without an Args: section. The new argument has non-trivial semantics worth documenting: None means "preserve existing attachments from the original body", [] clears them, and any non-empty list replaces them.
    async def edit(
        self,
        text: str | None = None,
        attachments: Sequence[AttachmentInput] | None = None,
        link: NewMessageLink | None = None,
        format: ParseMode | None = None,
        *,
        notification: str | None = None,
        notify: bool = True,
        raise_if_not_exists: bool = True,
    ) -> SendedCallback:
        """Изменить сообщение, связанное с callback."""

        message = self.message
        original_body = None if message is None else message.body

        if original_body is None:
            if raise_if_not_exists and (
                text is not None or link is not None or format is not None
            ):
                raise ValueError(
                    "Невозможно изменить сообщение: "
                    "исходное сообщение отсутствует"
                )

            return await self.ack(notification=notification)

        bot = self._ensure_bot()
        resolved_attachments: Sequence[AttachmentInput]
        if attachments is None:
            resolved_attachments = original_body.attachments or []
        else:
            resolved_attachments = attachments

Comment thread maxapi/types/attachments/attachment.py Outdated
"""

vcf_info: str = "" # для корректного определения
vcf_info: str | None = ""
Comment on lines 69 to 80
@@ -75,14 +75,15 @@ class ContactAttachmentPayload(BaseModel):
max_info: Дополнительная информация о пользователе.
"""

vcf_info: str = "" # для корректного определения
vcf_info: str | None = ""
hash: str | None = None
max_info: User | None = None
Comment on lines +39 to +42
text: str | None = None
attachments: list[Attachments] | None = Field(default_factory=list) # type: ignore
model_config = ConfigDict(arbitrary_types_allowed=True)

attachments: list[AttachmentInput] | None = Field(default_factory=list)
Comment thread maxapi/methods/send_callback.py Outdated
Comment on lines +69 to +90
message_json["attachments"] = []

if self.message.attachments:
for att in self.message.attachments:
if isinstance(att, (InputMedia, InputMediaBuffer)):
input_media = await process_input_media(
base_connection=self,
bot=bot,
att=att,
)
message_json["attachments"].append(
input_media.model_dump()
)
elif isinstance(att, Attachment) and isinstance(
att.payload, AttachmentUpload
):
message_json["attachments"].append(
att.payload.model_dump()
)
else:
message_json["attachments"].append(att.model_dump())

Comment thread maxapi/methods/send_callback.py Outdated
att.payload, AttachmentUpload
):
message_json["attachments"].append(
att.payload.model_dump()
Comment thread maxapi/methods/send_callback.py Outdated

if self.message.attachments:
for att in self.message.attachments:
if isinstance(att, (InputMedia, InputMediaBuffer)):
Comment on lines 154 to +161
# Убираем клавиатуру, оставляем текст.
# Важно: attachments=None в Message.edit() трактуется как
# «не менять» и сохраняет существующие вложения. Чтобы
# действительно удалить клавиатуру — передаём пустой список.
await event.message.edit(
# Для callback-ответа пустой список attachments действительно
# очищает клавиатуру.
await event.edit(
text="Клавиатура убрана. Напишите /start для повтора.",
attachments=[],
)
return
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.

attachments в MessageCallback.edit

4 participants