Описание
bot.get_chats() (и любой парсинг ответа GET /chats через модель Chats) падает с ValidationError, если хотя бы в одном чате закреплено пересланное сообщение.
Для пересланного закрепа API MAX возвращает pinned_message.link с type: "forward" и без поля message (поле message присутствует только у reply-линков, у forward его в payload нет). Однако в модели LinkedMessage поле message объявлено обязательным:
# maxapi/types/message.py
class LinkedMessage(BaseModel):
type: MessageLinkType
sender: User | None = None
chat_id: int | None = None
message: MessageBody # <-- required, падает на type="forward"
Так как Chats валидирует всю страницу атомарно (chats: list[Chat]), один «отравленный» чат рушит весь ответ get_chats() — список чатов становится пустым у бота, даже если он админ во всех чатах. На проде это привело к тому, что у пользователей пропал список их чатов.
Точный текст ошибки
1 validation error for Chats
chats.0.pinned_message.link.message
Field required [type=missing, input_value={'type': 'forward', 'chat_id': ...}, input_type=dict]
For further information visit https://errors.pydantic.dev/2.x/v/missing
Минимальный repro
from maxapi.types.chats import Chats
payload = {
"chats": [
{
"chat_id": 70000000000001,
"type": "chat",
"status": "active",
"title": "Test chat",
"last_event_time": 1700000000000,
"participants_count": 3,
"is_public": False,
"pinned_message": {
"recipient": {"chat_id": 70000000000001, "chat_type": "chat"},
"timestamp": 1700000000000,
"link": {
"type": "forward",
"chat_id": 70000000000002
# поля "message" нет — так приходит для пересланного закрепа
},
"body": {"mid": "mid1", "seq": 1, "text": "forwarded"}
}
}
],
"marker": None
}
Chats.model_validate(payload) # -> ValidationError: ...link.message Field required
Затронутые версии
Воспроизводится на 0.9.16 и на актуальной 1.1.0 (проверено в чистом venv, Python 3.12, pydantic 2.x). В обеих LinkedMessage.message остаётся обязательным.
Предлагаемое исправление
Сделать message необязательным, по аналогии с уже исправленным sender (#11):
class LinkedMessage(BaseModel):
type: MessageLinkType
sender: User | None = None
chat_id: int | None = None
message: MessageBody | None = None # forward-линк приходит без message
Места, использующие self.link.message.mid (например Message.edit()), стоит защитить проверкой на None.
См. также #11 — там аналогичная проблема с link.sender.
Описание
bot.get_chats()(и любой парсинг ответаGET /chatsчерез модельChats) падает сValidationError, если хотя бы в одном чате закреплено пересланное сообщение.Для пересланного закрепа API MAX возвращает
pinned_message.linkсtype: "forward"и без поляmessage(полеmessageприсутствует только уreply-линков, уforwardего в payload нет). Однако в моделиLinkedMessageполеmessageобъявлено обязательным:Так как
Chatsвалидирует всю страницу атомарно (chats: list[Chat]), один «отравленный» чат рушит весь ответget_chats()— список чатов становится пустым у бота, даже если он админ во всех чатах. На проде это привело к тому, что у пользователей пропал список их чатов.Точный текст ошибки
Минимальный repro
Затронутые версии
Воспроизводится на 0.9.16 и на актуальной 1.1.0 (проверено в чистом venv, Python 3.12, pydantic 2.x). В обеих
LinkedMessage.messageостаётся обязательным.Предлагаемое исправление
Сделать
messageнеобязательным, по аналогии с уже исправленнымsender(#11):Места, использующие
self.link.message.mid(напримерMessage.edit()), стоит защитить проверкой наNone.См. также #11 — там аналогичная проблема с
link.sender.