Update pydantic, fix errors and warnings (all?)

This commit is contained in:
Alex Root Junior 2023-07-02 01:06:17 +03:00
parent 066d16b522
commit 51aeceddad
No known key found for this signature in database
GPG key ID: 074C1D455EBEA4AC
82 changed files with 279 additions and 246 deletions

View file

@ -141,7 +141,7 @@ class AiohttpSession(BaseSession):
def build_form_data(self, bot: Bot, method: TelegramMethod[TelegramType]) -> FormData:
form = FormData(quote_fields=False)
files: Dict[str, InputFile] = {}
for key, value in method.dict().items():
for key, value in method.model_dump(warnings=False).items():
value = self.prepare_value(value, bot=bot, files=files)
if not value:
continue

View file

@ -9,14 +9,12 @@ from aiogram.methods.base import TelegramType
if TYPE_CHECKING:
from ...bot import Bot
NextRequestMiddlewareType = Callable[
["Bot", TelegramMethod[TelegramType]], Awaitable[Response[TelegramType]]
]
NextRequestMiddlewareType = Callable[["Bot", TelegramMethod], Awaitable[Response]]
RequestMiddlewareType = Union[
"BaseRequestMiddleware",
Callable[
[NextRequestMiddlewareType[TelegramType], "Bot", TelegramMethod[TelegramType]],
Awaitable[Response[TelegramType]],
[NextRequestMiddlewareType, "Bot", TelegramMethod],
Awaitable[Response],
],
]

View file

@ -25,44 +25,39 @@ if TYPE_CHECKING:
from aiogram import Bot
class RequestMiddlewareManager(Sequence[RequestMiddlewareType[TelegramObject]]):
class RequestMiddlewareManager(Sequence[RequestMiddlewareType]):
def __init__(self) -> None:
self._middlewares: List[RequestMiddlewareType[TelegramObject]] = []
self._middlewares: List[RequestMiddlewareType] = []
def register(
self,
middleware: RequestMiddlewareType[TelegramObject],
) -> RequestMiddlewareType[TelegramObject]:
middleware: RequestMiddlewareType,
) -> RequestMiddlewareType:
self._middlewares.append(middleware)
return middleware
def unregister(self, middleware: RequestMiddlewareType[TelegramObject]) -> None:
def unregister(self, middleware: RequestMiddlewareType) -> None:
self._middlewares.remove(middleware)
def __call__(
self,
middleware: Optional[RequestMiddlewareType[TelegramObject]] = None,
) -> Union[
Callable[[RequestMiddlewareType[TelegramObject]], RequestMiddlewareType[TelegramObject]],
RequestMiddlewareType[TelegramObject],
]:
middleware: Optional[RequestMiddlewareType] = None,
) -> Union[Callable[[RequestMiddlewareType], RequestMiddlewareType], RequestMiddlewareType,]:
if middleware is None:
return self.register
return self.register(middleware)
@overload
def __getitem__(self, item: int) -> RequestMiddlewareType[TelegramObject]:
def __getitem__(self, item: int) -> RequestMiddlewareType:
pass
@overload
def __getitem__(self, item: slice) -> Sequence[RequestMiddlewareType[TelegramObject]]:
def __getitem__(self, item: slice) -> Sequence[RequestMiddlewareType]:
pass
def __getitem__(
self, item: Union[int, slice]
) -> Union[
RequestMiddlewareType[TelegramObject], Sequence[RequestMiddlewareType[TelegramObject]]
]:
) -> Union[RequestMiddlewareType, Sequence[RequestMiddlewareType]]:
return self._middlewares[item]
def __len__(self) -> int:
@ -70,9 +65,9 @@ class RequestMiddlewareManager(Sequence[RequestMiddlewareType[TelegramObject]]):
def wrap_middlewares(
self,
callback: Callable[[Bot, TelegramMethod[TelegramType]], Awaitable[Response[TelegramType]]],
callback: Callable[[Bot, TelegramMethod], Awaitable[Response]],
**kwargs: Any,
) -> NextRequestMiddlewareType[TelegramType]:
) -> NextRequestMiddlewareType:
middleware = partial(callback, **kwargs)
for m in reversed(self._middlewares):
middleware = partial(m, middleware) # type: ignore

View file

@ -24,9 +24,9 @@ class RequestLogging(BaseRequestMiddleware):
async def __call__(
self,
make_request: NextRequestMiddlewareType[TelegramType],
make_request: NextRequestMiddlewareType,
bot: "Bot",
method: TelegramMethod[TelegramType],
method: TelegramMethod,
) -> Response[TelegramType]:
if type(method) not in self.ignore_methods:
loggers.middlewares.info(

View file

@ -110,7 +110,7 @@ class CallbackData(BaseModel):
:return: instance of CallbackData
"""
prefix, *parts = value.split(cls.__separator__)
names = cls.__fields__.keys()
names = cls.model_fields.keys()
if len(parts) != len(names):
raise TypeError(
f"Callback data {cls.__name__!r} takes {len(names)} arguments "
@ -120,8 +120,8 @@ class CallbackData(BaseModel):
raise ValueError(f"Bad prefix ({prefix!r} != {cls.__prefix__!r})")
payload = {}
for k, v in zip(names, parts): # type: str, Optional[str]
if field := cls.__fields__.get(k):
if v == "" and not field.required:
if field := cls.model_fields.get(k):
if v == "" and not field.is_required():
v = None
payload[k] = v
return cls(**payload)

View file

@ -32,12 +32,12 @@ class AnswerInlineQuery(TelegramMethod[bool]):
"""Pass the offset that a client should send in the next query with the same text to receive more results. Pass an empty string if there are no more results or if you don't support pagination. Offset length can't exceed 64 bytes."""
button: Optional[InlineQueryResultsButton] = None
"""A JSON-serialized object describing a button to be shown above inline query results"""
switch_pm_parameter: Optional[str] = Field(None, deprecated=True)
switch_pm_parameter: Optional[str] = Field(None, json_schema_extra={"deprecated": True})
"""`Deep-linking <https://core.telegram.org/bots/features#deep-linking>`_ parameter for the /start message sent to the bot when user presses the switch button. 1-64 characters, only :code:`A-Z`, :code:`a-z`, :code:`0-9`, :code:`_` and :code:`-` are allowed.
.. deprecated:: API:6.7
https://core.telegram.org/bots/api-changelog#april-21-2023"""
switch_pm_text: Optional[str] = Field(None, deprecated=True)
switch_pm_text: Optional[str] = Field(None, json_schema_extra={"deprecated": True})
"""If passed, clients will display a button with specified text that switches the user to a private chat with the bot and sends the bot a start message with the parameter *switch_pm_parameter*
.. deprecated:: API:6.7

View file

@ -3,8 +3,8 @@ from __future__ import annotations
from abc import ABC, abstractmethod
from typing import TYPE_CHECKING, Any, Dict, Generator, Generic, Optional, TypeVar
from pydantic import BaseConfig, BaseModel, Extra, root_validator
from pydantic.generics import GenericModel
from pydantic import BaseConfig, BaseModel, ConfigDict, Extra, root_validator
from pydantic.functional_validators import model_validator
from ..types import InputFile, ResponseParameters
from ..types.base import UNSET_TYPE
@ -16,16 +16,15 @@ TelegramType = TypeVar("TelegramType", bound=Any)
class Request(BaseModel):
model_config = ConfigDict(arbitrary_types_allowed=True)
method: str
data: Dict[str, Optional[Any]]
files: Optional[Dict[str, InputFile]]
class Config(BaseConfig):
arbitrary_types_allowed = True
class Response(GenericModel, Generic[TelegramType]):
class Response(BaseModel, Generic[TelegramType]):
ok: bool
result: Optional[TelegramType] = None
description: Optional[str] = None
@ -33,16 +32,14 @@ class Response(GenericModel, Generic[TelegramType]):
parameters: Optional[ResponseParameters] = None
class TelegramMethod(ABC, BaseModel, Generic[TelegramType]):
class Config(BaseConfig):
# use_enum_values = True
extra = Extra.allow
allow_population_by_field_name = True
arbitrary_types_allowed = True
orm_mode = True
smart_union = True # https://github.com/aiogram/aiogram/issues/901
class TelegramMethod(BaseModel, Generic[TelegramType], ABC):
model_config = ConfigDict(
extra="allow",
populate_by_name=True,
arbitrary_types_allowed=True,
)
@root_validator(pre=True)
@model_validator(mode="before")
def remove_unset(cls, values: Dict[str, Any]) -> Dict[str, Any]:
"""
Remove UNSET before fields validation.
@ -64,12 +61,6 @@ class TelegramMethod(ABC, BaseModel, Generic[TelegramType]):
def __api_method__(self) -> str:
pass
def dict(self, **kwargs: Any) -> Any:
# override dict of pydantic.BaseModel to overcome exporting request_timeout field
exclude = kwargs.pop("exclude", set())
return super().dict(exclude=exclude, **kwargs)
def build_response(self, data: Dict[str, Any]) -> Response[TelegramType]:
# noinspection PyTypeChecker
return Response[self.__returning__](**data) # type: ignore

View file

@ -2,6 +2,8 @@ from __future__ import annotations
from typing import TYPE_CHECKING, List, Optional, Union
from pydantic import Field
from ..types import (
UNSET_PARSE_MODE,
ForceReply,

View file

@ -1,4 +1,4 @@
from typing import Optional
from typing import List, Literal, Optional, Union
from .animation import Animation
from .audio import Audio
@ -324,11 +324,16 @@ __all__ = (
# Load typing forward refs for every TelegramObject
for _entity_name in __all__:
_entity = globals()[_entity_name]
if not hasattr(_entity, "update_forward_refs"):
if not hasattr(_entity, "model_rebuild"):
continue
_entity.update_forward_refs(
**{k: v for k, v in globals().items() if k in __all__},
**{"Optional": Optional},
_entity.model_rebuild(
_types_namespace={
"List": List,
"Optional": Optional,
"Union": Union,
"Literal": Literal,
**{k: v for k, v in globals().items() if k in __all__},
}
)
del _entity

View file

@ -1,26 +1,26 @@
import datetime
from typing import Any
from unittest.mock import sentinel
from pydantic import BaseModel, Extra
from pydantic import BaseModel, ConfigDict
from aiogram.utils.mixins import ContextInstanceMixin
class TelegramObject(ContextInstanceMixin["TelegramObject"], BaseModel):
class Config:
use_enum_values = True
orm_mode = True
extra = Extra.allow
validate_assignment = True
allow_mutation = False
allow_population_by_field_name = True
json_encoders = {datetime.datetime: lambda dt: int(dt.timestamp())}
model_config = ConfigDict(
use_enum_values=True,
extra="allow",
validate_assignment=True,
frozen=True,
populate_by_name=True,
arbitrary_types_allowed=True,
)
class MutableTelegramObject(TelegramObject):
class Config:
allow_mutation = True
model_config = ConfigDict(
frozen=False,
)
# special sentinel object which used in situation when None might be a useful value

View file

@ -1,5 +1,7 @@
from __future__ import annotations
from typing import Literal
from pydantic import Field
from ..enums import BotCommandScopeType
@ -13,5 +15,7 @@ class BotCommandScopeAllChatAdministrators(BotCommandScope):
Source: https://core.telegram.org/bots/api#botcommandscopeallchatadministrators
"""
type: str = Field(BotCommandScopeType.ALL_CHAT_ADMINISTRATORS, const=True)
type: Literal[
BotCommandScopeType.ALL_CHAT_ADMINISTRATORS
] = BotCommandScopeType.ALL_CHAT_ADMINISTRATORS
"""Scope type, must be *all_chat_administrators*"""

View file

@ -1,5 +1,7 @@
from __future__ import annotations
from typing import Literal
from pydantic import Field
from ..enums import BotCommandScopeType
@ -13,5 +15,5 @@ class BotCommandScopeAllGroupChats(BotCommandScope):
Source: https://core.telegram.org/bots/api#botcommandscopeallgroupchats
"""
type: str = Field(BotCommandScopeType.ALL_GROUP_CHATS, const=True)
type: Literal[BotCommandScopeType.ALL_GROUP_CHATS] = BotCommandScopeType.ALL_GROUP_CHATS
"""Scope type, must be *all_group_chats*"""

View file

@ -1,5 +1,7 @@
from __future__ import annotations
from typing import Literal
from pydantic import Field
from ..enums import BotCommandScopeType
@ -13,5 +15,5 @@ class BotCommandScopeAllPrivateChats(BotCommandScope):
Source: https://core.telegram.org/bots/api#botcommandscopeallprivatechats
"""
type: str = Field(BotCommandScopeType.ALL_PRIVATE_CHATS, const=True)
type: Literal[BotCommandScopeType.ALL_PRIVATE_CHATS] = BotCommandScopeType.ALL_PRIVATE_CHATS
"""Scope type, must be *all_private_chats*"""

View file

@ -1,6 +1,6 @@
from __future__ import annotations
from typing import Union
from typing import Literal, Union
from pydantic import Field
@ -15,7 +15,7 @@ class BotCommandScopeChat(BotCommandScope):
Source: https://core.telegram.org/bots/api#botcommandscopechat
"""
type: str = Field(BotCommandScopeType.CHAT, const=True)
type: Literal[BotCommandScopeType.CHAT] = BotCommandScopeType.CHAT
"""Scope type, must be *chat*"""
chat_id: Union[int, str]
"""Unique identifier for the target chat or username of the target supergroup (in the format :code:`@supergroupusername`)"""

View file

@ -1,6 +1,6 @@
from __future__ import annotations
from typing import Union
from typing import Literal, Union
from pydantic import Field
@ -15,7 +15,9 @@ class BotCommandScopeChatAdministrators(BotCommandScope):
Source: https://core.telegram.org/bots/api#botcommandscopechatadministrators
"""
type: str = Field(BotCommandScopeType.CHAT_ADMINISTRATORS, const=True)
type: Literal[
BotCommandScopeType.CHAT_ADMINISTRATORS
] = BotCommandScopeType.CHAT_ADMINISTRATORS
"""Scope type, must be *chat_administrators*"""
chat_id: Union[int, str]
"""Unique identifier for the target chat or username of the target supergroup (in the format :code:`@supergroupusername`)"""

View file

@ -1,6 +1,6 @@
from __future__ import annotations
from typing import Union
from typing import Literal, Union
from pydantic import Field
@ -15,7 +15,7 @@ class BotCommandScopeChatMember(BotCommandScope):
Source: https://core.telegram.org/bots/api#botcommandscopechatmember
"""
type: str = Field(BotCommandScopeType.CHAT_MEMBER, const=True)
type: Literal[BotCommandScopeType.CHAT_MEMBER] = BotCommandScopeType.CHAT_MEMBER
"""Scope type, must be *chat_member*"""
chat_id: Union[int, str]
"""Unique identifier for the target chat or username of the target supergroup (in the format :code:`@supergroupusername`)"""

View file

@ -1,6 +1,6 @@
from __future__ import annotations
from pydantic import Field
from typing import Literal
from ..enums import BotCommandScopeType
from .bot_command_scope import BotCommandScope
@ -13,5 +13,5 @@ class BotCommandScopeDefault(BotCommandScope):
Source: https://core.telegram.org/bots/api#botcommandscopedefault
"""
type: str = Field(BotCommandScopeType.DEFAULT, const=True)
type: Literal[BotCommandScopeType.DEFAULT] = BotCommandScopeType.DEFAULT
"""Scope type, must be *default*"""

View file

@ -1,6 +1,6 @@
from __future__ import annotations
from typing import TYPE_CHECKING, Optional
from typing import TYPE_CHECKING, Literal, Optional
from pydantic import Field
@ -18,7 +18,7 @@ class ChatMemberAdministrator(ChatMember):
Source: https://core.telegram.org/bots/api#chatmemberadministrator
"""
status: str = Field(ChatMemberStatus.ADMINISTRATOR, const=True)
status: Literal[ChatMemberStatus.ADMINISTRATOR] = ChatMemberStatus.ADMINISTRATOR
"""The member's status in the chat, always 'administrator'"""
user: User
"""Information about the user"""

View file

@ -1,7 +1,7 @@
from __future__ import annotations
import datetime
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, Literal
from pydantic import Field
@ -19,7 +19,7 @@ class ChatMemberBanned(ChatMember):
Source: https://core.telegram.org/bots/api#chatmemberbanned
"""
status: str = Field(ChatMemberStatus.KICKED, const=True)
status: Literal[ChatMemberStatus.KICKED] = ChatMemberStatus.KICKED
"""The member's status in the chat, always 'kicked'"""
user: User
"""Information about the user"""

View file

@ -1,6 +1,6 @@
from __future__ import annotations
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, Literal
from pydantic import Field
@ -18,7 +18,7 @@ class ChatMemberLeft(ChatMember):
Source: https://core.telegram.org/bots/api#chatmemberleft
"""
status: str = Field(ChatMemberStatus.LEFT, const=True)
status: Literal[ChatMemberStatus.LEFT] = ChatMemberStatus.LEFT
"""The member's status in the chat, always 'left'"""
user: User
"""Information about the user"""

View file

@ -1,6 +1,6 @@
from __future__ import annotations
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, Literal
from pydantic import Field
@ -18,7 +18,7 @@ class ChatMemberMember(ChatMember):
Source: https://core.telegram.org/bots/api#chatmembermember
"""
status: str = Field(ChatMemberStatus.MEMBER, const=True)
status: Literal[ChatMemberStatus.MEMBER] = ChatMemberStatus.MEMBER
"""The member's status in the chat, always 'member'"""
user: User
"""Information about the user"""

View file

@ -1,6 +1,6 @@
from __future__ import annotations
from typing import TYPE_CHECKING, Optional
from typing import TYPE_CHECKING, Literal, Optional
from pydantic import Field
@ -18,7 +18,7 @@ class ChatMemberOwner(ChatMember):
Source: https://core.telegram.org/bots/api#chatmemberowner
"""
status: str = Field(ChatMemberStatus.CREATOR, const=True)
status: Literal[ChatMemberStatus.CREATOR] = ChatMemberStatus.CREATOR
"""The member's status in the chat, always 'creator'"""
user: User
"""Information about the user"""

View file

@ -1,7 +1,7 @@
from __future__ import annotations
import datetime
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, Literal
from pydantic import Field
@ -19,7 +19,7 @@ class ChatMemberRestricted(ChatMember):
Source: https://core.telegram.org/bots/api#chatmemberrestricted
"""
status: str = Field(ChatMemberStatus.RESTRICTED, const=True)
status: Literal[ChatMemberStatus.RESTRICTED] = ChatMemberStatus.RESTRICTED
"""The member's status in the chat, always 'restricted'"""
user: User
"""Information about the user"""

View file

@ -2,7 +2,7 @@ from __future__ import annotations
from typing import Optional
from .base import MutableTelegramObject
from .base import MutableTelegramObject, TelegramObject
class ChatPermissions(MutableTelegramObject):

View file

@ -2,18 +2,13 @@ from __future__ import annotations
from typing import TYPE_CHECKING
from aiogram.types.base import MutableTelegramObject
from aiogram.types.base import TelegramObject
if TYPE_CHECKING:
from .update import Update
class _ErrorEvent(MutableTelegramObject):
class Config:
arbitrary_types_allowed = True
class ErrorEvent(_ErrorEvent):
class ErrorEvent(TelegramObject):
"""
Internal event, should be used to receive errors while processing Updates from Telegram

View file

@ -1,10 +1,10 @@
from __future__ import annotations
from typing import Optional
from typing import Literal, Optional
from pydantic import Field
from .base import MutableTelegramObject
from .base import MutableTelegramObject, TelegramObject
class ForceReply(MutableTelegramObject):
@ -21,7 +21,7 @@ class ForceReply(MutableTelegramObject):
Source: https://core.telegram.org/bots/api#forcereply
"""
force_reply: bool = Field(True, const=True)
force_reply: Literal[True] = True
"""Shows reply interface to the user, as if they manually selected the bot's message and tapped 'Reply'"""
input_field_placeholder: Optional[str] = None
"""*Optional*. The placeholder to be shown in the input field when the reply is active; 1-64 characters"""

View file

@ -2,7 +2,7 @@ from __future__ import annotations
from typing import TYPE_CHECKING, Optional
from .base import MutableTelegramObject
from .base import MutableTelegramObject, TelegramObject
if TYPE_CHECKING:
from .callback_game import CallbackGame

View file

@ -2,7 +2,7 @@ from __future__ import annotations
from typing import TYPE_CHECKING, List
from .base import MutableTelegramObject
from .base import MutableTelegramObject, TelegramObject
if TYPE_CHECKING:
from .inline_keyboard_button import InlineKeyboardButton

View file

@ -1,6 +1,6 @@
from __future__ import annotations
from .base import MutableTelegramObject
from .base import MutableTelegramObject, TelegramObject
class InlineQueryResult(MutableTelegramObject):

View file

@ -1,6 +1,6 @@
from __future__ import annotations
from typing import TYPE_CHECKING, Optional
from typing import TYPE_CHECKING, Literal, Optional
from pydantic import Field
@ -19,7 +19,7 @@ class InlineQueryResultArticle(InlineQueryResult):
Source: https://core.telegram.org/bots/api#inlinequeryresultarticle
"""
type: str = Field(InlineQueryResultType.ARTICLE, const=True)
type: Literal[InlineQueryResultType.ARTICLE] = InlineQueryResultType.ARTICLE
"""Type of the result, must be *article*"""
id: str
"""Unique identifier for this result, 1-64 Bytes"""

View file

@ -1,6 +1,6 @@
from __future__ import annotations
from typing import TYPE_CHECKING, List, Optional
from typing import TYPE_CHECKING, List, Literal, Optional
from pydantic import Field
@ -22,7 +22,7 @@ class InlineQueryResultAudio(InlineQueryResult):
Source: https://core.telegram.org/bots/api#inlinequeryresultaudio
"""
type: str = Field(InlineQueryResultType.AUDIO, const=True)
type: Literal[InlineQueryResultType.AUDIO] = InlineQueryResultType.AUDIO
"""Type of the result, must be *audio*"""
id: str
"""Unique identifier for this result, 1-64 bytes"""

View file

@ -1,6 +1,6 @@
from __future__ import annotations
from typing import TYPE_CHECKING, List, Optional
from typing import TYPE_CHECKING, List, Literal, Optional
from pydantic import Field
@ -22,7 +22,7 @@ class InlineQueryResultCachedAudio(InlineQueryResult):
Source: https://core.telegram.org/bots/api#inlinequeryresultcachedaudio
"""
type: str = Field(InlineQueryResultType.AUDIO, const=True)
type: Literal[InlineQueryResultType.AUDIO] = InlineQueryResultType.AUDIO
"""Type of the result, must be *audio*"""
id: str
"""Unique identifier for this result, 1-64 bytes"""

View file

@ -1,6 +1,6 @@
from __future__ import annotations
from typing import TYPE_CHECKING, List, Optional
from typing import TYPE_CHECKING, List, Literal, Optional
from pydantic import Field
@ -22,7 +22,7 @@ class InlineQueryResultCachedDocument(InlineQueryResult):
Source: https://core.telegram.org/bots/api#inlinequeryresultcacheddocument
"""
type: str = Field(InlineQueryResultType.DOCUMENT, const=True)
type: Literal[InlineQueryResultType.DOCUMENT] = InlineQueryResultType.DOCUMENT
"""Type of the result, must be *document*"""
id: str
"""Unique identifier for this result, 1-64 bytes"""

View file

@ -1,6 +1,6 @@
from __future__ import annotations
from typing import TYPE_CHECKING, List, Optional
from typing import TYPE_CHECKING, List, Literal, Optional
from pydantic import Field
@ -21,7 +21,7 @@ class InlineQueryResultCachedGif(InlineQueryResult):
Source: https://core.telegram.org/bots/api#inlinequeryresultcachedgif
"""
type: str = Field(InlineQueryResultType.GIF, const=True)
type: Literal[InlineQueryResultType.GIF] = InlineQueryResultType.GIF
"""Type of the result, must be *gif*"""
id: str
"""Unique identifier for this result, 1-64 bytes"""

View file

@ -1,6 +1,6 @@
from __future__ import annotations
from typing import TYPE_CHECKING, List, Optional
from typing import TYPE_CHECKING, List, Literal, Optional
from pydantic import Field
@ -21,7 +21,7 @@ class InlineQueryResultCachedMpeg4Gif(InlineQueryResult):
Source: https://core.telegram.org/bots/api#inlinequeryresultcachedmpeg4gif
"""
type: str = Field(InlineQueryResultType.MPEG4_GIF, const=True)
type: Literal[InlineQueryResultType.MPEG4_GIF] = InlineQueryResultType.MPEG4_GIF
"""Type of the result, must be *mpeg4_gif*"""
id: str
"""Unique identifier for this result, 1-64 bytes"""

View file

@ -1,6 +1,6 @@
from __future__ import annotations
from typing import TYPE_CHECKING, List, Optional
from typing import TYPE_CHECKING, List, Literal, Optional
from pydantic import Field
@ -21,7 +21,7 @@ class InlineQueryResultCachedPhoto(InlineQueryResult):
Source: https://core.telegram.org/bots/api#inlinequeryresultcachedphoto
"""
type: str = Field(InlineQueryResultType.PHOTO, const=True)
type: Literal[InlineQueryResultType.PHOTO] = InlineQueryResultType.PHOTO
"""Type of the result, must be *photo*"""
id: str
"""Unique identifier for this result, 1-64 bytes"""

View file

@ -1,6 +1,6 @@
from __future__ import annotations
from typing import TYPE_CHECKING, Optional
from typing import TYPE_CHECKING, Literal, Optional
from pydantic import Field
@ -20,7 +20,7 @@ class InlineQueryResultCachedSticker(InlineQueryResult):
Source: https://core.telegram.org/bots/api#inlinequeryresultcachedsticker
"""
type: str = Field(InlineQueryResultType.STICKER, const=True)
type: Literal[InlineQueryResultType.STICKER] = InlineQueryResultType.STICKER
"""Type of the result, must be *sticker*"""
id: str
"""Unique identifier for this result, 1-64 bytes"""

View file

@ -1,6 +1,6 @@
from __future__ import annotations
from typing import TYPE_CHECKING, List, Optional
from typing import TYPE_CHECKING, List, Literal, Optional
from pydantic import Field
@ -21,7 +21,7 @@ class InlineQueryResultCachedVideo(InlineQueryResult):
Source: https://core.telegram.org/bots/api#inlinequeryresultcachedvideo
"""
type: str = Field(InlineQueryResultType.VIDEO, const=True)
type: Literal[InlineQueryResultType.VIDEO] = InlineQueryResultType.VIDEO
"""Type of the result, must be *video*"""
id: str
"""Unique identifier for this result, 1-64 bytes"""

View file

@ -1,6 +1,6 @@
from __future__ import annotations
from typing import TYPE_CHECKING, List, Optional
from typing import TYPE_CHECKING, List, Literal, Optional
from pydantic import Field
@ -22,7 +22,7 @@ class InlineQueryResultCachedVoice(InlineQueryResult):
Source: https://core.telegram.org/bots/api#inlinequeryresultcachedvoice
"""
type: str = Field(InlineQueryResultType.VOICE, const=True)
type: Literal[InlineQueryResultType.VOICE] = InlineQueryResultType.VOICE
"""Type of the result, must be *voice*"""
id: str
"""Unique identifier for this result, 1-64 bytes"""

View file

@ -1,6 +1,6 @@
from __future__ import annotations
from typing import TYPE_CHECKING, Optional
from typing import TYPE_CHECKING, Literal, Optional
from pydantic import Field
@ -20,7 +20,7 @@ class InlineQueryResultContact(InlineQueryResult):
Source: https://core.telegram.org/bots/api#inlinequeryresultcontact
"""
type: str = Field(InlineQueryResultType.CONTACT, const=True)
type: Literal[InlineQueryResultType.CONTACT] = InlineQueryResultType.CONTACT
"""Type of the result, must be *contact*"""
id: str
"""Unique identifier for this result, 1-64 Bytes"""

View file

@ -1,6 +1,6 @@
from __future__ import annotations
from typing import TYPE_CHECKING, List, Optional
from typing import TYPE_CHECKING, List, Literal, Optional
from pydantic import Field
@ -22,7 +22,7 @@ class InlineQueryResultDocument(InlineQueryResult):
Source: https://core.telegram.org/bots/api#inlinequeryresultdocument
"""
type: str = Field(InlineQueryResultType.DOCUMENT, const=True)
type: Literal[InlineQueryResultType.DOCUMENT] = InlineQueryResultType.DOCUMENT
"""Type of the result, must be *document*"""
id: str
"""Unique identifier for this result, 1-64 bytes"""

View file

@ -1,6 +1,6 @@
from __future__ import annotations
from typing import TYPE_CHECKING, Optional
from typing import TYPE_CHECKING, Literal, Optional
from pydantic import Field
@ -19,7 +19,7 @@ class InlineQueryResultGame(InlineQueryResult):
Source: https://core.telegram.org/bots/api#inlinequeryresultgame
"""
type: str = Field(InlineQueryResultType.GAME, const=True)
type: Literal[InlineQueryResultType.GAME] = InlineQueryResultType.GAME
"""Type of the result, must be *game*"""
id: str
"""Unique identifier for this result, 1-64 bytes"""

View file

@ -1,6 +1,6 @@
from __future__ import annotations
from typing import TYPE_CHECKING, List, Optional
from typing import TYPE_CHECKING, List, Literal, Optional
from pydantic import Field
@ -21,7 +21,7 @@ class InlineQueryResultGif(InlineQueryResult):
Source: https://core.telegram.org/bots/api#inlinequeryresultgif
"""
type: str = Field(InlineQueryResultType.GIF, const=True)
type: Literal[InlineQueryResultType.GIF] = InlineQueryResultType.GIF
"""Type of the result, must be *gif*"""
id: str
"""Unique identifier for this result, 1-64 bytes"""

View file

@ -1,6 +1,6 @@
from __future__ import annotations
from typing import TYPE_CHECKING, Optional
from typing import TYPE_CHECKING, Literal, Optional
from pydantic import Field
@ -20,7 +20,7 @@ class InlineQueryResultLocation(InlineQueryResult):
Source: https://core.telegram.org/bots/api#inlinequeryresultlocation
"""
type: str = Field(InlineQueryResultType.LOCATION, const=True)
type: Literal[InlineQueryResultType.LOCATION] = InlineQueryResultType.LOCATION
"""Type of the result, must be *location*"""
id: str
"""Unique identifier for this result, 1-64 Bytes"""

View file

@ -1,6 +1,6 @@
from __future__ import annotations
from typing import TYPE_CHECKING, List, Optional
from typing import TYPE_CHECKING, List, Literal, Optional
from pydantic import Field
@ -21,7 +21,7 @@ class InlineQueryResultMpeg4Gif(InlineQueryResult):
Source: https://core.telegram.org/bots/api#inlinequeryresultmpeg4gif
"""
type: str = Field(InlineQueryResultType.MPEG4_GIF, const=True)
type: Literal[InlineQueryResultType.MPEG4_GIF] = InlineQueryResultType.MPEG4_GIF
"""Type of the result, must be *mpeg4_gif*"""
id: str
"""Unique identifier for this result, 1-64 bytes"""

View file

@ -1,6 +1,6 @@
from __future__ import annotations
from typing import TYPE_CHECKING, List, Optional
from typing import TYPE_CHECKING, List, Literal, Optional
from pydantic import Field
@ -21,7 +21,7 @@ class InlineQueryResultPhoto(InlineQueryResult):
Source: https://core.telegram.org/bots/api#inlinequeryresultphoto
"""
type: str = Field(InlineQueryResultType.PHOTO, const=True)
type: Literal[InlineQueryResultType.PHOTO] = InlineQueryResultType.PHOTO
"""Type of the result, must be *photo*"""
id: str
"""Unique identifier for this result, 1-64 bytes"""

View file

@ -1,6 +1,6 @@
from __future__ import annotations
from typing import TYPE_CHECKING, Optional
from typing import TYPE_CHECKING, Literal, Optional
from pydantic import Field
@ -20,7 +20,7 @@ class InlineQueryResultVenue(InlineQueryResult):
Source: https://core.telegram.org/bots/api#inlinequeryresultvenue
"""
type: str = Field(InlineQueryResultType.VENUE, const=True)
type: Literal[InlineQueryResultType.VENUE] = InlineQueryResultType.VENUE
"""Type of the result, must be *venue*"""
id: str
"""Unique identifier for this result, 1-64 Bytes"""

View file

@ -1,6 +1,6 @@
from __future__ import annotations
from typing import TYPE_CHECKING, List, Optional
from typing import TYPE_CHECKING, List, Literal, Optional
from pydantic import Field
@ -23,7 +23,7 @@ class InlineQueryResultVideo(InlineQueryResult):
Source: https://core.telegram.org/bots/api#inlinequeryresultvideo
"""
type: str = Field(InlineQueryResultType.VIDEO, const=True)
type: Literal[InlineQueryResultType.VIDEO] = InlineQueryResultType.VIDEO
"""Type of the result, must be *video*"""
id: str
"""Unique identifier for this result, 1-64 bytes"""

View file

@ -1,6 +1,6 @@
from __future__ import annotations
from typing import TYPE_CHECKING, List, Optional
from typing import TYPE_CHECKING, List, Literal, Optional
from pydantic import Field
@ -22,7 +22,7 @@ class InlineQueryResultVoice(InlineQueryResult):
Source: https://core.telegram.org/bots/api#inlinequeryresultvoice
"""
type: str = Field(InlineQueryResultType.VOICE, const=True)
type: Literal[InlineQueryResultType.VOICE] = InlineQueryResultType.VOICE
"""Type of the result, must be *voice*"""
id: str
"""Unique identifier for this result, 1-64 bytes"""

View file

@ -1,6 +1,6 @@
from __future__ import annotations
from .base import MutableTelegramObject
from .base import MutableTelegramObject, TelegramObject
class InputMedia(MutableTelegramObject):

View file

@ -1,6 +1,6 @@
from __future__ import annotations
from typing import TYPE_CHECKING, List, Optional, Union
from typing import TYPE_CHECKING, List, Literal, Optional, Union
from pydantic import Field
@ -20,7 +20,7 @@ class InputMediaAnimation(InputMedia):
Source: https://core.telegram.org/bots/api#inputmediaanimation
"""
type: str = Field(InputMediaType.ANIMATION, const=True)
type: Literal[InputMediaType.ANIMATION] = InputMediaType.ANIMATION
"""Type of the result, must be *animation*"""
media: Union[str, InputFile]
"""File to send. Pass a file_id to send a file that exists on the Telegram servers (recommended), pass an HTTP URL for Telegram to get a file from the Internet, or pass 'attach://<file_attach_name>' to upload a new one using multipart/form-data under <file_attach_name> name. :ref:`More information on Sending Files » <sending-files>`"""

View file

@ -1,6 +1,6 @@
from __future__ import annotations
from typing import TYPE_CHECKING, List, Optional, Union
from typing import TYPE_CHECKING, List, Literal, Optional, Union
from pydantic import Field
@ -20,7 +20,7 @@ class InputMediaAudio(InputMedia):
Source: https://core.telegram.org/bots/api#inputmediaaudio
"""
type: str = Field(InputMediaType.AUDIO, const=True)
type: Literal[InputMediaType.AUDIO] = InputMediaType.AUDIO
"""Type of the result, must be *audio*"""
media: Union[str, InputFile]
"""File to send. Pass a file_id to send a file that exists on the Telegram servers (recommended), pass an HTTP URL for Telegram to get a file from the Internet, or pass 'attach://<file_attach_name>' to upload a new one using multipart/form-data under <file_attach_name> name. :ref:`More information on Sending Files » <sending-files>`"""

View file

@ -1,6 +1,6 @@
from __future__ import annotations
from typing import TYPE_CHECKING, List, Optional, Union
from typing import TYPE_CHECKING, List, Literal, Optional, Union
from pydantic import Field
@ -20,7 +20,7 @@ class InputMediaDocument(InputMedia):
Source: https://core.telegram.org/bots/api#inputmediadocument
"""
type: str = Field(InputMediaType.DOCUMENT, const=True)
type: Literal[InputMediaType.DOCUMENT] = InputMediaType.DOCUMENT
"""Type of the result, must be *document*"""
media: Union[str, InputFile]
"""File to send. Pass a file_id to send a file that exists on the Telegram servers (recommended), pass an HTTP URL for Telegram to get a file from the Internet, or pass 'attach://<file_attach_name>' to upload a new one using multipart/form-data under <file_attach_name> name. :ref:`More information on Sending Files » <sending-files>`"""

View file

@ -1,6 +1,6 @@
from __future__ import annotations
from typing import TYPE_CHECKING, List, Optional, Union
from typing import TYPE_CHECKING, List, Literal, Optional, Union
from pydantic import Field
@ -20,7 +20,7 @@ class InputMediaPhoto(InputMedia):
Source: https://core.telegram.org/bots/api#inputmediaphoto
"""
type: str = Field(InputMediaType.PHOTO, const=True)
type: Literal[InputMediaType.PHOTO] = InputMediaType.PHOTO
"""Type of the result, must be *photo*"""
media: Union[str, InputFile]
"""File to send. Pass a file_id to send a file that exists on the Telegram servers (recommended), pass an HTTP URL for Telegram to get a file from the Internet, or pass 'attach://<file_attach_name>' to upload a new one using multipart/form-data under <file_attach_name> name. :ref:`More information on Sending Files » <sending-files>`"""

View file

@ -1,6 +1,6 @@
from __future__ import annotations
from typing import TYPE_CHECKING, List, Optional, Union
from typing import TYPE_CHECKING, List, Literal, Optional, Union
from pydantic import Field
@ -20,7 +20,7 @@ class InputMediaVideo(InputMedia):
Source: https://core.telegram.org/bots/api#inputmediavideo
"""
type: str = Field(InputMediaType.VIDEO, const=True)
type: Literal[InputMediaType.VIDEO] = InputMediaType.VIDEO
"""Type of the result, must be *video*"""
media: Union[str, InputFile]
"""File to send. Pass a file_id to send a file that exists on the Telegram servers (recommended), pass an HTTP URL for Telegram to get a file from the Internet, or pass 'attach://<file_attach_name>' to upload a new one using multipart/form-data under <file_attach_name> name. :ref:`More information on Sending Files » <sending-files>`"""

View file

@ -1,6 +1,6 @@
from __future__ import annotations
from .base import MutableTelegramObject
from .base import MutableTelegramObject, TelegramObject
class InputMessageContent(MutableTelegramObject):

View file

@ -2,7 +2,7 @@ from __future__ import annotations
from typing import TYPE_CHECKING, Optional
from .base import MutableTelegramObject
from .base import MutableTelegramObject, TelegramObject
if TYPE_CHECKING:
from .keyboard_button_poll_type import KeyboardButtonPollType

View file

@ -2,7 +2,7 @@ from __future__ import annotations
from typing import Optional
from .base import MutableTelegramObject
from .base import MutableTelegramObject, TelegramObject
class KeyboardButtonPollType(MutableTelegramObject):

View file

@ -1,6 +1,6 @@
from __future__ import annotations
from .base import MutableTelegramObject
from .base import MutableTelegramObject, TelegramObject
class LabeledPrice(MutableTelegramObject):

View file

@ -2,7 +2,7 @@ from __future__ import annotations
from typing import TYPE_CHECKING, Optional
from .base import MutableTelegramObject
from .base import MutableTelegramObject, TelegramObject
if TYPE_CHECKING:
from .web_app_info import WebAppInfo

View file

@ -1,5 +1,7 @@
from __future__ import annotations
from typing import Literal
from pydantic import Field
from ..enums import MenuButtonType
@ -13,5 +15,5 @@ class MenuButtonCommands(MenuButton):
Source: https://core.telegram.org/bots/api#menubuttoncommands
"""
type: str = Field(MenuButtonType.COMMANDS, const=True)
type: Literal[MenuButtonType.COMMANDS] = MenuButtonType.COMMANDS
"""Type of the button, must be *commands*"""

View file

@ -1,5 +1,7 @@
from __future__ import annotations
from typing import Literal
from pydantic import Field
from ..enums import MenuButtonType
@ -13,5 +15,5 @@ class MenuButtonDefault(MenuButton):
Source: https://core.telegram.org/bots/api#menubuttondefault
"""
type: str = Field(MenuButtonType.DEFAULT, const=True)
type: Literal[MenuButtonType.DEFAULT] = MenuButtonType.DEFAULT
"""Type of the button, must be *default*"""

View file

@ -1,6 +1,6 @@
from __future__ import annotations
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, Literal
from pydantic import Field
@ -18,7 +18,7 @@ class MenuButtonWebApp(MenuButton):
Source: https://core.telegram.org/bots/api#menubuttonwebapp
"""
type: str = Field(MenuButtonType.WEB_APP, const=True)
type: Literal[MenuButtonType.WEB_APP] = MenuButtonType.WEB_APP
"""Type of the button, must be *web_app*"""
text: str
"""Text on the button"""

View file

@ -10,14 +10,13 @@ from aiogram.utils.text_decorations import (
html_decoration,
markdown_decoration,
)
from ..enums import ContentType
from .base import (
UNSET_DISABLE_WEB_PAGE_PREVIEW,
UNSET_PARSE_MODE,
UNSET_PROTECT_CONTENT,
TelegramObject,
)
from ..enums import ContentType
if TYPE_CHECKING:
from ..methods import (

View file

@ -3,7 +3,7 @@ from __future__ import annotations
from typing import TYPE_CHECKING, Optional
from ..utils.text_decorations import add_surrogates, remove_surrogates
from .base import MutableTelegramObject
from .base import MutableTelegramObject, TelegramObject
if TYPE_CHECKING:
from .user import User

View file

@ -1,6 +1,6 @@
from __future__ import annotations
from .base import MutableTelegramObject
from .base import MutableTelegramObject, TelegramObject
class PassportElementError(MutableTelegramObject):

View file

@ -1,5 +1,7 @@
from __future__ import annotations
from typing import Literal
from pydantic import Field
from .passport_element_error import PassportElementError
@ -12,7 +14,7 @@ class PassportElementErrorDataField(PassportElementError):
Source: https://core.telegram.org/bots/api#passportelementerrordatafield
"""
source: str = Field("data", const=True)
source: Literal["data"] = "data"
"""Error source, must be *data*"""
type: str
"""The section of the user's Telegram Passport which has the error, one of 'personal_details', 'passport', 'driver_license', 'identity_card', 'internal_passport', 'address'"""

View file

@ -1,5 +1,7 @@
from __future__ import annotations
from typing import Literal
from pydantic import Field
from .passport_element_error import PassportElementError
@ -12,7 +14,7 @@ class PassportElementErrorFile(PassportElementError):
Source: https://core.telegram.org/bots/api#passportelementerrorfile
"""
source: str = Field("file", const=True)
source: Literal["file"] = "file"
"""Error source, must be *file*"""
type: str
"""The section of the user's Telegram Passport which has the issue, one of 'utility_bill', 'bank_statement', 'rental_agreement', 'passport_registration', 'temporary_registration'"""

View file

@ -1,6 +1,6 @@
from __future__ import annotations
from typing import List
from typing import List, Literal
from pydantic import Field
@ -14,7 +14,7 @@ class PassportElementErrorFiles(PassportElementError):
Source: https://core.telegram.org/bots/api#passportelementerrorfiles
"""
source: str = Field("files", const=True)
source: Literal["files"] = "files"
"""Error source, must be *files*"""
type: str
"""The section of the user's Telegram Passport which has the issue, one of 'utility_bill', 'bank_statement', 'rental_agreement', 'passport_registration', 'temporary_registration'"""

View file

@ -1,5 +1,7 @@
from __future__ import annotations
from typing import Literal
from pydantic import Field
from .passport_element_error import PassportElementError
@ -12,7 +14,7 @@ class PassportElementErrorFrontSide(PassportElementError):
Source: https://core.telegram.org/bots/api#passportelementerrorfrontside
"""
source: str = Field("front_side", const=True)
source: Literal["front_side"] = "front_side"
"""Error source, must be *front_side*"""
type: str
"""The section of the user's Telegram Passport which has the issue, one of 'passport', 'driver_license', 'identity_card', 'internal_passport'"""

View file

@ -1,5 +1,7 @@
from __future__ import annotations
from typing import Literal
from pydantic import Field
from .passport_element_error import PassportElementError
@ -12,7 +14,7 @@ class PassportElementErrorReverseSide(PassportElementError):
Source: https://core.telegram.org/bots/api#passportelementerrorreverseside
"""
source: str = Field("reverse_side", const=True)
source: Literal["reverse_side"] = "reverse_side"
"""Error source, must be *reverse_side*"""
type: str
"""The section of the user's Telegram Passport which has the issue, one of 'driver_license', 'identity_card'"""

View file

@ -1,5 +1,7 @@
from __future__ import annotations
from typing import Literal
from pydantic import Field
from .passport_element_error import PassportElementError
@ -12,7 +14,7 @@ class PassportElementErrorSelfie(PassportElementError):
Source: https://core.telegram.org/bots/api#passportelementerrorselfie
"""
source: str = Field("selfie", const=True)
source: Literal["selfie"] = "selfie"
"""Error source, must be *selfie*"""
type: str
"""The section of the user's Telegram Passport which has the issue, one of 'passport', 'driver_license', 'identity_card', 'internal_passport'"""

View file

@ -1,5 +1,7 @@
from __future__ import annotations
from typing import Literal
from pydantic import Field
from .passport_element_error import PassportElementError
@ -12,7 +14,7 @@ class PassportElementErrorTranslationFile(PassportElementError):
Source: https://core.telegram.org/bots/api#passportelementerrortranslationfile
"""
source: str = Field("translation_file", const=True)
source: Literal["translation_file"] = "translation_file"
"""Error source, must be *translation_file*"""
type: str
"""Type of element of the user's Telegram Passport which has the issue, one of 'passport', 'driver_license', 'identity_card', 'internal_passport', 'utility_bill', 'bank_statement', 'rental_agreement', 'passport_registration', 'temporary_registration'"""

View file

@ -1,6 +1,6 @@
from __future__ import annotations
from typing import List
from typing import List, Literal
from pydantic import Field
@ -14,7 +14,7 @@ class PassportElementErrorTranslationFiles(PassportElementError):
Source: https://core.telegram.org/bots/api#passportelementerrortranslationfiles
"""
source: str = Field("translation_files", const=True)
source: Literal["translation_files"] = "translation_files"
"""Error source, must be *translation_files*"""
type: str
"""Type of element of the user's Telegram Passport which has the issue, one of 'passport', 'driver_license', 'identity_card', 'internal_passport', 'utility_bill', 'bank_statement', 'rental_agreement', 'passport_registration', 'temporary_registration'"""

View file

@ -1,5 +1,7 @@
from __future__ import annotations
from typing import Literal
from pydantic import Field
from .passport_element_error import PassportElementError
@ -12,7 +14,7 @@ class PassportElementErrorUnspecified(PassportElementError):
Source: https://core.telegram.org/bots/api#passportelementerrorunspecified
"""
source: str = Field("unspecified", const=True)
source: Literal["unspecified"] = "unspecified"
"""Error source, must be *unspecified*"""
type: str
"""Type of element of the user's Telegram Passport which has the issue"""

View file

@ -2,7 +2,7 @@ from __future__ import annotations
from typing import TYPE_CHECKING, List, Optional
from .base import MutableTelegramObject
from .base import MutableTelegramObject, TelegramObject
if TYPE_CHECKING:
from .keyboard_button import KeyboardButton

View file

@ -1,8 +1,6 @@
from __future__ import annotations
from typing import Optional
from pydantic import Field
from typing import Literal, Optional
from .base import MutableTelegramObject
@ -14,7 +12,7 @@ class ReplyKeyboardRemove(MutableTelegramObject):
Source: https://core.telegram.org/bots/api#replykeyboardremove
"""
remove_keyboard: bool = Field(True, const=True)
remove_keyboard: Literal[True] = True
"""Requests clients to remove the custom keyboard (user will not be able to summon this keyboard; if you want to hide the keyboard from sight but keep it accessible, use *one_time_keyboard* in :class:`aiogram.types.reply_keyboard_markup.ReplyKeyboardMarkup`)"""
selective: Optional[bool] = None
"""*Optional*. Use this parameter if you want to remove the keyboard for specific users only. Targets: 1) users that are @mentioned in the *text* of the :class:`aiogram.types.message.Message` object; 2) if the bot's message is a reply (has *reply_to_message_id*), sender of the original message."""

View file

@ -2,8 +2,8 @@ from __future__ import annotations
from typing import TYPE_CHECKING, Optional, cast
from ..utils.mypy_hacks import lru_cache
from .base import TelegramObject
from ..utils.mypy_hacks import lru_cache
if TYPE_CHECKING:
from .callback_query import CallbackQuery

View file

@ -2,9 +2,9 @@ from __future__ import annotations
from typing import TYPE_CHECKING, Any, Optional
from .base import TelegramObject
from ..utils import markdown
from ..utils.link import create_tg_link
from .base import TelegramObject
if TYPE_CHECKING:
from ..methods import GetUserProfilePhotos

View file

@ -42,9 +42,9 @@ classifiers = [
dependencies = [
"magic-filter~=1.0.9",
"aiohttp~=3.8.4",
"pydantic~=1.10.7",
"pydantic~=2.0.0",
"aiofiles~=23.1.0",
"certifi>=2022.9.24",
"certifi>=2023.5.7",
]
dynamic = ["version"]
@ -65,7 +65,7 @@ i18n = [
"Babel~=2.12.1",
]
test = [
"pytest~=7.3.1",
"pytest~=7.4.0",
"pytest-html~=3.2.0",
"pytest-asyncio~=0.21.0",
"pytest-lazy-fixture~=0.6.3",
@ -94,13 +94,13 @@ docs = [
dev = [
"black~=23.3.0",
"isort~=5.11",
"ruff~=0.0.262",
"mypy~=1.2.0",
"ruff~=0.0.275",
"mypy~=1.4.1",
"toml~=0.10.2",
"pre-commit~=3.2.2",
"towncrier~=22.12.0",
"pre-commit~=3.3.3",
"towncrier~=23.6.0",
"packaging~=23.0",
"typing-extensions~=4.5.0",
"typing-extensions~=4.7.0",
]
[project.urls]

View file

@ -1,7 +1,7 @@
import datetime
from aiogram.methods import Request, SendMessage
from aiogram.types import Chat, ForceReply, Message
from aiogram.types import Chat, ForceReply, Message, ReplyKeyboardRemove
from tests.mocked_bot import MockedBot
@ -24,5 +24,11 @@ class TestSendMessage:
async def test_force_reply(self):
# https://github.com/aiogram/aiogram/issues/901
print("::::", SendMessage.__pydantic_core_schema__)
method = SendMessage(text="test", chat_id=42, reply_markup=ForceReply())
assert isinstance(method.reply_markup, ForceReply)
async def test_reply_keyboard_remove(self):
# https://github.com/aiogram/aiogram/issues/901
method = SendMessage(text="test", chat_id=42, reply_markup=ReplyKeyboardRemove())
assert isinstance(method.reply_markup, ReplyKeyboardRemove)

View file

@ -12,7 +12,16 @@ from aiogram.filters.chat_member_updated import (
_MemberStatusMarker,
_MemberStatusTransition,
)
from aiogram.types import Chat, ChatMember, ChatMemberUpdated, User
from aiogram.types import (
Chat,
ChatMember,
ChatMemberAdministrator,
ChatMemberLeft,
ChatMemberMember,
ChatMemberRestricted,
ChatMemberUpdated,
User,
)
class TestMemberStatusMarker:
@ -267,84 +276,91 @@ class TestMemberStatusTransition:
class TestChatMemberUpdatedStatusFilter:
USER = User(id=42, first_name="Test", is_bot=False)
PARAMS = {
"user": USER,
"until_date": datetime.now(),
"is_anonymous": True,
"custom_title": "title",
"can_be_edited": True,
"can_manage_chat": True,
"can_delete_messages": True,
"can_manage_video_chats": True,
"can_restrict_members": True,
"can_promote_members": True,
"can_change_info": True,
"can_invite_users": True,
"can_post_messages": True,
"can_edit_messages": True,
"can_pin_messages": True,
"can_manage_topics": True,
"can_send_messages": True,
"can_send_audios": True,
"can_send_documents": True,
"can_send_photos": True,
"can_send_videos": True,
"can_send_video_notes": True,
"can_send_voice_notes": True,
"can_send_polls": True,
"can_send_other_messages": True,
"can_add_web_page_previews": True,
}
@pytest.mark.parametrize(
"transition,old,new,result",
[
[JOIN_TRANSITION, ChatMember(status="left"), ChatMember(status="member"), True],
[
JOIN_TRANSITION,
ChatMember(status="restricted", is_member=True),
ChatMember(status="member"),
False,
],
[
JOIN_TRANSITION,
ChatMember(status="restricted", is_member=False),
ChatMember(status="member"),
ChatMemberLeft(status="left", **PARAMS),
ChatMemberMember(status="member", **PARAMS),
True,
],
[
JOIN_TRANSITION,
ChatMember(status="member"),
ChatMember(status="restricted", is_member=False),
ChatMemberRestricted(status="restricted", is_member=True, **PARAMS),
ChatMemberMember(status="member", **PARAMS),
False,
],
[
JOIN_TRANSITION,
ChatMemberRestricted(status="restricted", is_member=False, **PARAMS),
ChatMemberMember(status="member", **PARAMS),
True,
],
[
JOIN_TRANSITION,
ChatMemberMember(status="member", **PARAMS),
ChatMemberRestricted(status="restricted", is_member=False, **PARAMS),
False,
],
[
LEAVE_TRANSITION,
ChatMember(status="member"),
ChatMember(status="restricted", is_member=False),
ChatMemberMember(status="member", **PARAMS),
ChatMemberRestricted(status="restricted", is_member=False, **PARAMS),
True,
],
[
ADMINISTRATOR,
ChatMember(status="member"),
ChatMember(status="administrator"),
ChatMemberMember(status="member", **PARAMS),
ChatMemberAdministrator(status="administrator", **PARAMS),
True,
],
[
IS_MEMBER,
ChatMember(status="restricted", is_member=False),
ChatMember(status="member"),
ChatMemberRestricted(status="restricted", is_member=False, **PARAMS),
ChatMemberMember(status="member", **PARAMS),
True,
],
],
)
async def test_call(self, transition, old, new, result):
updated_filter = ChatMemberUpdatedFilter(member_status_changed=transition)
user = User(id=42, first_name="Test", is_bot=False)
update = {
"user": user,
"until_date": datetime.now(),
"is_anonymous": True,
"custom_title": True,
"can_be_edited": True,
"can_manage_chat": True,
"can_delete_messages": True,
"can_manage_video_chats": True,
"can_restrict_members": True,
"can_promote_members": True,
"can_change_info": True,
"can_invite_users": True,
"can_post_messages": True,
"can_edit_messages": True,
"can_pin_messages": True,
"can_manage_topics": True,
"can_send_messages": True,
"can_send_audios": True,
"can_send_documents": True,
"can_send_photos": True,
"can_send_videos": True,
"can_send_video_notes": True,
"can_send_voice_notes": True,
"can_send_polls": True,
"can_send_other_messages": True,
"can_add_web_page_previews": True,
}
event = ChatMemberUpdated(
chat=Chat(id=42, type="test"),
from_user=user,
old_chat_member=old.copy(update=update),
new_chat_member=new.copy(update=update),
from_user=self.USER,
old_chat_member=old,
new_chat_member=new,
date=datetime.now(),
)