mirror of
https://github.com/aiogram/aiogram.git
synced 2026-04-08 16:37:47 +00:00
Handle empty external reply story in updates (#1587)
This commit is contained in:
parent
1708980ceb
commit
58993e0e5e
4 changed files with 84 additions and 1 deletions
1
CHANGES/1587.bugfix.rst
Normal file
1
CHANGES/1587.bugfix.rst
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
Fixed deserialization for malformed Bot API updates where :code:`external_reply.story` is an empty object, treating it as missing data instead of crashing polling.
|
||||||
|
|
@ -2,6 +2,8 @@ from __future__ import annotations
|
||||||
|
|
||||||
from typing import TYPE_CHECKING, Any
|
from typing import TYPE_CHECKING, Any
|
||||||
|
|
||||||
|
from pydantic import model_validator
|
||||||
|
|
||||||
from .base import TelegramObject
|
from .base import TelegramObject
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
|
|
@ -37,6 +39,18 @@ class ExternalReplyInfo(TelegramObject):
|
||||||
Source: https://core.telegram.org/bots/api#externalreplyinfo
|
Source: https://core.telegram.org/bots/api#externalreplyinfo
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@model_validator(mode="before")
|
||||||
|
@classmethod
|
||||||
|
def handle_empty_story(cls, values: Any) -> Any:
|
||||||
|
if not isinstance(values, dict):
|
||||||
|
return values
|
||||||
|
|
||||||
|
if values.get("story") == {}:
|
||||||
|
values = values.copy()
|
||||||
|
values["story"] = None
|
||||||
|
|
||||||
|
return values
|
||||||
|
|
||||||
origin: MessageOriginUnion
|
origin: MessageOriginUnion
|
||||||
"""Origin of the message replied to by the given message"""
|
"""Origin of the message replied to by the given message"""
|
||||||
chat: Chat | None = None
|
chat: Chat | None = None
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ from aiogram.exceptions import (
|
||||||
TelegramServerError,
|
TelegramServerError,
|
||||||
TelegramUnauthorizedError,
|
TelegramUnauthorizedError,
|
||||||
)
|
)
|
||||||
from aiogram.methods import DeleteMessage, GetMe, TelegramMethod
|
from aiogram.methods import DeleteMessage, GetMe, GetUpdates, TelegramMethod
|
||||||
from aiogram.types import UNSET_PARSE_MODE, LinkPreviewOptions, User
|
from aiogram.types import UNSET_PARSE_MODE, LinkPreviewOptions, User
|
||||||
from aiogram.types.base import UNSET_DISABLE_WEB_PAGE_PREVIEW, UNSET_PROTECT_CONTENT
|
from aiogram.types.base import UNSET_DISABLE_WEB_PAGE_PREVIEW, UNSET_PROTECT_CONTENT
|
||||||
from tests.mocked_bot import MockedBot
|
from tests.mocked_bot import MockedBot
|
||||||
|
|
@ -227,6 +227,52 @@ class TestBaseSession:
|
||||||
content='{"ok": "test"}',
|
content='{"ok": "test"}',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def test_check_response_get_updates_with_empty_external_reply_story(self):
|
||||||
|
session = CustomSession()
|
||||||
|
bot = MockedBot()
|
||||||
|
method = GetUpdates()
|
||||||
|
|
||||||
|
response = session.check_response(
|
||||||
|
bot=bot,
|
||||||
|
method=method,
|
||||||
|
status_code=200,
|
||||||
|
content=json.dumps(
|
||||||
|
{
|
||||||
|
"ok": True,
|
||||||
|
"result": [
|
||||||
|
{
|
||||||
|
"update_id": 1,
|
||||||
|
"message": {
|
||||||
|
"message_id": 42,
|
||||||
|
"date": int(datetime.datetime.now().timestamp()),
|
||||||
|
"chat": {"id": 42, "type": "private"},
|
||||||
|
"from": {"id": 42, "is_bot": False, "first_name": "Test"},
|
||||||
|
"text": "test",
|
||||||
|
"external_reply": {
|
||||||
|
"origin": {
|
||||||
|
"type": "user",
|
||||||
|
"sender_user": {
|
||||||
|
"id": 43,
|
||||||
|
"is_bot": False,
|
||||||
|
"first_name": "Sender",
|
||||||
|
},
|
||||||
|
"date": int(datetime.datetime.now().timestamp()),
|
||||||
|
},
|
||||||
|
"story": {},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
],
|
||||||
|
}
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
assert len(response.result) == 1
|
||||||
|
update = response.result[0]
|
||||||
|
assert update.message is not None
|
||||||
|
assert update.message.external_reply is not None
|
||||||
|
assert update.message.external_reply.story is None
|
||||||
|
|
||||||
async def test_make_request(self):
|
async def test_make_request(self):
|
||||||
session = CustomSession()
|
session = CustomSession()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1016,6 +1016,28 @@ class TestAllMessageTypesTested:
|
||||||
|
|
||||||
|
|
||||||
class TestMessage:
|
class TestMessage:
|
||||||
|
def test_model_validate_external_reply_with_empty_story(self):
|
||||||
|
message = Message.model_validate(
|
||||||
|
{
|
||||||
|
"message_id": 42,
|
||||||
|
"date": int(datetime.datetime.now().timestamp()),
|
||||||
|
"chat": {"id": 42, "type": "private"},
|
||||||
|
"from": {"id": 42, "is_bot": False, "first_name": "Test"},
|
||||||
|
"text": "test",
|
||||||
|
"external_reply": {
|
||||||
|
"origin": {
|
||||||
|
"type": "user",
|
||||||
|
"sender_user": {"id": 43, "is_bot": False, "first_name": "Sender"},
|
||||||
|
"date": int(datetime.datetime.now().timestamp()),
|
||||||
|
},
|
||||||
|
"story": {},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
assert message.external_reply is not None
|
||||||
|
assert message.external_reply.story is None
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"message,content_type",
|
"message,content_type",
|
||||||
MESSAGES_AND_CONTENT_TYPES,
|
MESSAGES_AND_CONTENT_TYPES,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue