diff --git a/aiogram/client/session/aiohttp.py b/aiogram/client/session/aiohttp.py index c15faf7a..b4c791e8 100644 --- a/aiogram/client/session/aiohttp.py +++ b/aiogram/client/session/aiohttp.py @@ -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 diff --git a/aiogram/client/session/middlewares/base.py b/aiogram/client/session/middlewares/base.py index 90dcd09f..52e955a1 100644 --- a/aiogram/client/session/middlewares/base.py +++ b/aiogram/client/session/middlewares/base.py @@ -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], ], ] diff --git a/aiogram/client/session/middlewares/manager.py b/aiogram/client/session/middlewares/manager.py index 0e76801e..93d8d6c3 100644 --- a/aiogram/client/session/middlewares/manager.py +++ b/aiogram/client/session/middlewares/manager.py @@ -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 diff --git a/aiogram/client/session/middlewares/request_logging.py b/aiogram/client/session/middlewares/request_logging.py index af7b9d6e..cec0d810 100644 --- a/aiogram/client/session/middlewares/request_logging.py +++ b/aiogram/client/session/middlewares/request_logging.py @@ -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( diff --git a/aiogram/filters/callback_data.py b/aiogram/filters/callback_data.py index 05d7783d..96c8f3af 100644 --- a/aiogram/filters/callback_data.py +++ b/aiogram/filters/callback_data.py @@ -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) diff --git a/aiogram/methods/answer_inline_query.py b/aiogram/methods/answer_inline_query.py index 7b6b6d88..6f189b9b 100644 --- a/aiogram/methods/answer_inline_query.py +++ b/aiogram/methods/answer_inline_query.py @@ -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 `_ 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 diff --git a/aiogram/methods/base.py b/aiogram/methods/base.py index 33045bf0..6ae8e67b 100644 --- a/aiogram/methods/base.py +++ b/aiogram/methods/base.py @@ -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 diff --git a/aiogram/methods/send_message.py b/aiogram/methods/send_message.py index 9b7dae7b..263871b0 100644 --- a/aiogram/methods/send_message.py +++ b/aiogram/methods/send_message.py @@ -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, diff --git a/aiogram/types/__init__.py b/aiogram/types/__init__.py index 4808f7fe..a669cbe8 100644 --- a/aiogram/types/__init__.py +++ b/aiogram/types/__init__.py @@ -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 diff --git a/aiogram/types/base.py b/aiogram/types/base.py index 707e328c..a9d3eb1a 100644 --- a/aiogram/types/base.py +++ b/aiogram/types/base.py @@ -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 diff --git a/aiogram/types/bot_command_scope_all_chat_administrators.py b/aiogram/types/bot_command_scope_all_chat_administrators.py index e9f6a969..9d033a2a 100644 --- a/aiogram/types/bot_command_scope_all_chat_administrators.py +++ b/aiogram/types/bot_command_scope_all_chat_administrators.py @@ -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*""" diff --git a/aiogram/types/bot_command_scope_all_group_chats.py b/aiogram/types/bot_command_scope_all_group_chats.py index f9675ad6..479e38b6 100644 --- a/aiogram/types/bot_command_scope_all_group_chats.py +++ b/aiogram/types/bot_command_scope_all_group_chats.py @@ -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*""" diff --git a/aiogram/types/bot_command_scope_all_private_chats.py b/aiogram/types/bot_command_scope_all_private_chats.py index f13e2866..6399e5ab 100644 --- a/aiogram/types/bot_command_scope_all_private_chats.py +++ b/aiogram/types/bot_command_scope_all_private_chats.py @@ -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*""" diff --git a/aiogram/types/bot_command_scope_chat.py b/aiogram/types/bot_command_scope_chat.py index d96bc6f4..6fa2677b 100644 --- a/aiogram/types/bot_command_scope_chat.py +++ b/aiogram/types/bot_command_scope_chat.py @@ -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`)""" diff --git a/aiogram/types/bot_command_scope_chat_administrators.py b/aiogram/types/bot_command_scope_chat_administrators.py index 824dc5a1..09873f2a 100644 --- a/aiogram/types/bot_command_scope_chat_administrators.py +++ b/aiogram/types/bot_command_scope_chat_administrators.py @@ -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`)""" diff --git a/aiogram/types/bot_command_scope_chat_member.py b/aiogram/types/bot_command_scope_chat_member.py index e9fb0dda..b76dd883 100644 --- a/aiogram/types/bot_command_scope_chat_member.py +++ b/aiogram/types/bot_command_scope_chat_member.py @@ -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`)""" diff --git a/aiogram/types/bot_command_scope_default.py b/aiogram/types/bot_command_scope_default.py index 79825631..449665c9 100644 --- a/aiogram/types/bot_command_scope_default.py +++ b/aiogram/types/bot_command_scope_default.py @@ -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*""" diff --git a/aiogram/types/chat_member_administrator.py b/aiogram/types/chat_member_administrator.py index 94fc76c3..33aa999d 100644 --- a/aiogram/types/chat_member_administrator.py +++ b/aiogram/types/chat_member_administrator.py @@ -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""" diff --git a/aiogram/types/chat_member_banned.py b/aiogram/types/chat_member_banned.py index 85c07f51..038bbef1 100644 --- a/aiogram/types/chat_member_banned.py +++ b/aiogram/types/chat_member_banned.py @@ -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""" diff --git a/aiogram/types/chat_member_left.py b/aiogram/types/chat_member_left.py index 6d7968c1..2abe1f23 100644 --- a/aiogram/types/chat_member_left.py +++ b/aiogram/types/chat_member_left.py @@ -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""" diff --git a/aiogram/types/chat_member_member.py b/aiogram/types/chat_member_member.py index 303a7d9d..1f9f032b 100644 --- a/aiogram/types/chat_member_member.py +++ b/aiogram/types/chat_member_member.py @@ -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""" diff --git a/aiogram/types/chat_member_owner.py b/aiogram/types/chat_member_owner.py index e7c64fc1..605c9a8b 100644 --- a/aiogram/types/chat_member_owner.py +++ b/aiogram/types/chat_member_owner.py @@ -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""" diff --git a/aiogram/types/chat_member_restricted.py b/aiogram/types/chat_member_restricted.py index b6f4f556..963bf123 100644 --- a/aiogram/types/chat_member_restricted.py +++ b/aiogram/types/chat_member_restricted.py @@ -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""" diff --git a/aiogram/types/chat_permissions.py b/aiogram/types/chat_permissions.py index 762fe208..0ff4e7c4 100644 --- a/aiogram/types/chat_permissions.py +++ b/aiogram/types/chat_permissions.py @@ -2,7 +2,7 @@ from __future__ import annotations from typing import Optional -from .base import MutableTelegramObject +from .base import MutableTelegramObject, TelegramObject class ChatPermissions(MutableTelegramObject): diff --git a/aiogram/types/error_event.py b/aiogram/types/error_event.py index 1667d9e9..212bee56 100644 --- a/aiogram/types/error_event.py +++ b/aiogram/types/error_event.py @@ -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 diff --git a/aiogram/types/force_reply.py b/aiogram/types/force_reply.py index c27af31e..ae6428e6 100644 --- a/aiogram/types/force_reply.py +++ b/aiogram/types/force_reply.py @@ -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""" diff --git a/aiogram/types/inline_keyboard_button.py b/aiogram/types/inline_keyboard_button.py index 977fae8d..afe3be79 100644 --- a/aiogram/types/inline_keyboard_button.py +++ b/aiogram/types/inline_keyboard_button.py @@ -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 diff --git a/aiogram/types/inline_keyboard_markup.py b/aiogram/types/inline_keyboard_markup.py index 473e292c..ff0387dd 100644 --- a/aiogram/types/inline_keyboard_markup.py +++ b/aiogram/types/inline_keyboard_markup.py @@ -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 diff --git a/aiogram/types/inline_query_result.py b/aiogram/types/inline_query_result.py index b615aa2b..b53e4aab 100644 --- a/aiogram/types/inline_query_result.py +++ b/aiogram/types/inline_query_result.py @@ -1,6 +1,6 @@ from __future__ import annotations -from .base import MutableTelegramObject +from .base import MutableTelegramObject, TelegramObject class InlineQueryResult(MutableTelegramObject): diff --git a/aiogram/types/inline_query_result_article.py b/aiogram/types/inline_query_result_article.py index dd65b595..8b65d35b 100644 --- a/aiogram/types/inline_query_result_article.py +++ b/aiogram/types/inline_query_result_article.py @@ -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""" diff --git a/aiogram/types/inline_query_result_audio.py b/aiogram/types/inline_query_result_audio.py index 3b830fc8..fa275c05 100644 --- a/aiogram/types/inline_query_result_audio.py +++ b/aiogram/types/inline_query_result_audio.py @@ -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""" diff --git a/aiogram/types/inline_query_result_cached_audio.py b/aiogram/types/inline_query_result_cached_audio.py index 8358f723..1f163947 100644 --- a/aiogram/types/inline_query_result_cached_audio.py +++ b/aiogram/types/inline_query_result_cached_audio.py @@ -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""" diff --git a/aiogram/types/inline_query_result_cached_document.py b/aiogram/types/inline_query_result_cached_document.py index 5513971f..cd9a2682 100644 --- a/aiogram/types/inline_query_result_cached_document.py +++ b/aiogram/types/inline_query_result_cached_document.py @@ -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""" diff --git a/aiogram/types/inline_query_result_cached_gif.py b/aiogram/types/inline_query_result_cached_gif.py index 7aa5c535..299e9c13 100644 --- a/aiogram/types/inline_query_result_cached_gif.py +++ b/aiogram/types/inline_query_result_cached_gif.py @@ -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""" diff --git a/aiogram/types/inline_query_result_cached_mpeg4_gif.py b/aiogram/types/inline_query_result_cached_mpeg4_gif.py index 539de025..e4d09b70 100644 --- a/aiogram/types/inline_query_result_cached_mpeg4_gif.py +++ b/aiogram/types/inline_query_result_cached_mpeg4_gif.py @@ -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""" diff --git a/aiogram/types/inline_query_result_cached_photo.py b/aiogram/types/inline_query_result_cached_photo.py index fc892826..1f0d294a 100644 --- a/aiogram/types/inline_query_result_cached_photo.py +++ b/aiogram/types/inline_query_result_cached_photo.py @@ -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""" diff --git a/aiogram/types/inline_query_result_cached_sticker.py b/aiogram/types/inline_query_result_cached_sticker.py index 3d75c29c..6d27d10e 100644 --- a/aiogram/types/inline_query_result_cached_sticker.py +++ b/aiogram/types/inline_query_result_cached_sticker.py @@ -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""" diff --git a/aiogram/types/inline_query_result_cached_video.py b/aiogram/types/inline_query_result_cached_video.py index acda3962..2f94ddc3 100644 --- a/aiogram/types/inline_query_result_cached_video.py +++ b/aiogram/types/inline_query_result_cached_video.py @@ -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""" diff --git a/aiogram/types/inline_query_result_cached_voice.py b/aiogram/types/inline_query_result_cached_voice.py index 3f03b2fe..0b5d3d46 100644 --- a/aiogram/types/inline_query_result_cached_voice.py +++ b/aiogram/types/inline_query_result_cached_voice.py @@ -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""" diff --git a/aiogram/types/inline_query_result_contact.py b/aiogram/types/inline_query_result_contact.py index 0f88bb9f..fb2edf83 100644 --- a/aiogram/types/inline_query_result_contact.py +++ b/aiogram/types/inline_query_result_contact.py @@ -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""" diff --git a/aiogram/types/inline_query_result_document.py b/aiogram/types/inline_query_result_document.py index 7698e4c6..12edb742 100644 --- a/aiogram/types/inline_query_result_document.py +++ b/aiogram/types/inline_query_result_document.py @@ -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""" diff --git a/aiogram/types/inline_query_result_game.py b/aiogram/types/inline_query_result_game.py index e4d92a61..58fee484 100644 --- a/aiogram/types/inline_query_result_game.py +++ b/aiogram/types/inline_query_result_game.py @@ -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""" diff --git a/aiogram/types/inline_query_result_gif.py b/aiogram/types/inline_query_result_gif.py index b38cfa1a..9136b3a9 100644 --- a/aiogram/types/inline_query_result_gif.py +++ b/aiogram/types/inline_query_result_gif.py @@ -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""" diff --git a/aiogram/types/inline_query_result_location.py b/aiogram/types/inline_query_result_location.py index aa1f6d88..6841434b 100644 --- a/aiogram/types/inline_query_result_location.py +++ b/aiogram/types/inline_query_result_location.py @@ -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""" diff --git a/aiogram/types/inline_query_result_mpeg4_gif.py b/aiogram/types/inline_query_result_mpeg4_gif.py index 06c73620..682b1dfa 100644 --- a/aiogram/types/inline_query_result_mpeg4_gif.py +++ b/aiogram/types/inline_query_result_mpeg4_gif.py @@ -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""" diff --git a/aiogram/types/inline_query_result_photo.py b/aiogram/types/inline_query_result_photo.py index 6ed85276..38533f85 100644 --- a/aiogram/types/inline_query_result_photo.py +++ b/aiogram/types/inline_query_result_photo.py @@ -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""" diff --git a/aiogram/types/inline_query_result_venue.py b/aiogram/types/inline_query_result_venue.py index 0c92a008..2972b435 100644 --- a/aiogram/types/inline_query_result_venue.py +++ b/aiogram/types/inline_query_result_venue.py @@ -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""" diff --git a/aiogram/types/inline_query_result_video.py b/aiogram/types/inline_query_result_video.py index ba14d066..8326f748 100644 --- a/aiogram/types/inline_query_result_video.py +++ b/aiogram/types/inline_query_result_video.py @@ -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""" diff --git a/aiogram/types/inline_query_result_voice.py b/aiogram/types/inline_query_result_voice.py index 7de1f6cf..eef9eef3 100644 --- a/aiogram/types/inline_query_result_voice.py +++ b/aiogram/types/inline_query_result_voice.py @@ -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""" diff --git a/aiogram/types/input_media.py b/aiogram/types/input_media.py index f7523c9e..90fe5ac3 100644 --- a/aiogram/types/input_media.py +++ b/aiogram/types/input_media.py @@ -1,6 +1,6 @@ from __future__ import annotations -from .base import MutableTelegramObject +from .base import MutableTelegramObject, TelegramObject class InputMedia(MutableTelegramObject): diff --git a/aiogram/types/input_media_animation.py b/aiogram/types/input_media_animation.py index eabc4b5e..dfd6f118 100644 --- a/aiogram/types/input_media_animation.py +++ b/aiogram/types/input_media_animation.py @@ -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://' to upload a new one using multipart/form-data under name. :ref:`More information on Sending Files » `""" diff --git a/aiogram/types/input_media_audio.py b/aiogram/types/input_media_audio.py index 8de62a6f..b43b99c7 100644 --- a/aiogram/types/input_media_audio.py +++ b/aiogram/types/input_media_audio.py @@ -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://' to upload a new one using multipart/form-data under name. :ref:`More information on Sending Files » `""" diff --git a/aiogram/types/input_media_document.py b/aiogram/types/input_media_document.py index 9ef4d52f..704e098d 100644 --- a/aiogram/types/input_media_document.py +++ b/aiogram/types/input_media_document.py @@ -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://' to upload a new one using multipart/form-data under name. :ref:`More information on Sending Files » `""" diff --git a/aiogram/types/input_media_photo.py b/aiogram/types/input_media_photo.py index 0f9500e8..ab04991d 100644 --- a/aiogram/types/input_media_photo.py +++ b/aiogram/types/input_media_photo.py @@ -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://' to upload a new one using multipart/form-data under name. :ref:`More information on Sending Files » `""" diff --git a/aiogram/types/input_media_video.py b/aiogram/types/input_media_video.py index 89d4df91..1a2b5cce 100644 --- a/aiogram/types/input_media_video.py +++ b/aiogram/types/input_media_video.py @@ -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://' to upload a new one using multipart/form-data under name. :ref:`More information on Sending Files » `""" diff --git a/aiogram/types/input_message_content.py b/aiogram/types/input_message_content.py index 858524a5..c331d449 100644 --- a/aiogram/types/input_message_content.py +++ b/aiogram/types/input_message_content.py @@ -1,6 +1,6 @@ from __future__ import annotations -from .base import MutableTelegramObject +from .base import MutableTelegramObject, TelegramObject class InputMessageContent(MutableTelegramObject): diff --git a/aiogram/types/keyboard_button.py b/aiogram/types/keyboard_button.py index 08d4ce37..6afa0f54 100644 --- a/aiogram/types/keyboard_button.py +++ b/aiogram/types/keyboard_button.py @@ -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 diff --git a/aiogram/types/keyboard_button_poll_type.py b/aiogram/types/keyboard_button_poll_type.py index 2747adfb..c0540ebc 100644 --- a/aiogram/types/keyboard_button_poll_type.py +++ b/aiogram/types/keyboard_button_poll_type.py @@ -2,7 +2,7 @@ from __future__ import annotations from typing import Optional -from .base import MutableTelegramObject +from .base import MutableTelegramObject, TelegramObject class KeyboardButtonPollType(MutableTelegramObject): diff --git a/aiogram/types/labeled_price.py b/aiogram/types/labeled_price.py index 3dcffb89..9d4c85c9 100644 --- a/aiogram/types/labeled_price.py +++ b/aiogram/types/labeled_price.py @@ -1,6 +1,6 @@ from __future__ import annotations -from .base import MutableTelegramObject +from .base import MutableTelegramObject, TelegramObject class LabeledPrice(MutableTelegramObject): diff --git a/aiogram/types/menu_button.py b/aiogram/types/menu_button.py index b37c2390..db3bd059 100644 --- a/aiogram/types/menu_button.py +++ b/aiogram/types/menu_button.py @@ -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 diff --git a/aiogram/types/menu_button_commands.py b/aiogram/types/menu_button_commands.py index 62a9061c..099f9d5a 100644 --- a/aiogram/types/menu_button_commands.py +++ b/aiogram/types/menu_button_commands.py @@ -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*""" diff --git a/aiogram/types/menu_button_default.py b/aiogram/types/menu_button_default.py index dc754ec0..2bb26731 100644 --- a/aiogram/types/menu_button_default.py +++ b/aiogram/types/menu_button_default.py @@ -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*""" diff --git a/aiogram/types/menu_button_web_app.py b/aiogram/types/menu_button_web_app.py index f77ed2ea..3fa8857e 100644 --- a/aiogram/types/menu_button_web_app.py +++ b/aiogram/types/menu_button_web_app.py @@ -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""" diff --git a/aiogram/types/message.py b/aiogram/types/message.py index a057ef97..6327f2b3 100644 --- a/aiogram/types/message.py +++ b/aiogram/types/message.py @@ -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 ( diff --git a/aiogram/types/message_entity.py b/aiogram/types/message_entity.py index 39773a66..161844e0 100644 --- a/aiogram/types/message_entity.py +++ b/aiogram/types/message_entity.py @@ -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 diff --git a/aiogram/types/passport_element_error.py b/aiogram/types/passport_element_error.py index 36a1db86..8fcff6c5 100644 --- a/aiogram/types/passport_element_error.py +++ b/aiogram/types/passport_element_error.py @@ -1,6 +1,6 @@ from __future__ import annotations -from .base import MutableTelegramObject +from .base import MutableTelegramObject, TelegramObject class PassportElementError(MutableTelegramObject): diff --git a/aiogram/types/passport_element_error_data_field.py b/aiogram/types/passport_element_error_data_field.py index f5b0b67b..ace16328 100644 --- a/aiogram/types/passport_element_error_data_field.py +++ b/aiogram/types/passport_element_error_data_field.py @@ -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'""" diff --git a/aiogram/types/passport_element_error_file.py b/aiogram/types/passport_element_error_file.py index 217deaaa..72196b68 100644 --- a/aiogram/types/passport_element_error_file.py +++ b/aiogram/types/passport_element_error_file.py @@ -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'""" diff --git a/aiogram/types/passport_element_error_files.py b/aiogram/types/passport_element_error_files.py index 6f42d693..5add9547 100644 --- a/aiogram/types/passport_element_error_files.py +++ b/aiogram/types/passport_element_error_files.py @@ -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'""" diff --git a/aiogram/types/passport_element_error_front_side.py b/aiogram/types/passport_element_error_front_side.py index 98e60acd..55eecb10 100644 --- a/aiogram/types/passport_element_error_front_side.py +++ b/aiogram/types/passport_element_error_front_side.py @@ -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'""" diff --git a/aiogram/types/passport_element_error_reverse_side.py b/aiogram/types/passport_element_error_reverse_side.py index 0c6073ba..69bc20b8 100644 --- a/aiogram/types/passport_element_error_reverse_side.py +++ b/aiogram/types/passport_element_error_reverse_side.py @@ -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'""" diff --git a/aiogram/types/passport_element_error_selfie.py b/aiogram/types/passport_element_error_selfie.py index 4a3b2fe1..08b0a313 100644 --- a/aiogram/types/passport_element_error_selfie.py +++ b/aiogram/types/passport_element_error_selfie.py @@ -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'""" diff --git a/aiogram/types/passport_element_error_translation_file.py b/aiogram/types/passport_element_error_translation_file.py index d4106453..b981c691 100644 --- a/aiogram/types/passport_element_error_translation_file.py +++ b/aiogram/types/passport_element_error_translation_file.py @@ -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'""" diff --git a/aiogram/types/passport_element_error_translation_files.py b/aiogram/types/passport_element_error_translation_files.py index df0db64f..3fa62191 100644 --- a/aiogram/types/passport_element_error_translation_files.py +++ b/aiogram/types/passport_element_error_translation_files.py @@ -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'""" diff --git a/aiogram/types/passport_element_error_unspecified.py b/aiogram/types/passport_element_error_unspecified.py index 39d0c417..2df05d45 100644 --- a/aiogram/types/passport_element_error_unspecified.py +++ b/aiogram/types/passport_element_error_unspecified.py @@ -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""" diff --git a/aiogram/types/reply_keyboard_markup.py b/aiogram/types/reply_keyboard_markup.py index da2f6214..0c65c531 100644 --- a/aiogram/types/reply_keyboard_markup.py +++ b/aiogram/types/reply_keyboard_markup.py @@ -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 diff --git a/aiogram/types/reply_keyboard_remove.py b/aiogram/types/reply_keyboard_remove.py index 1e2c2da9..0260ab6c 100644 --- a/aiogram/types/reply_keyboard_remove.py +++ b/aiogram/types/reply_keyboard_remove.py @@ -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.""" diff --git a/aiogram/types/update.py b/aiogram/types/update.py index 9f9eb413..481e7566 100644 --- a/aiogram/types/update.py +++ b/aiogram/types/update.py @@ -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 diff --git a/aiogram/types/user.py b/aiogram/types/user.py index 3b71af19..11828dee 100644 --- a/aiogram/types/user.py +++ b/aiogram/types/user.py @@ -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 diff --git a/pyproject.toml b/pyproject.toml index 8c926c6d..57f70196 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -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] diff --git a/tests/test_api/test_methods/test_send_message.py b/tests/test_api/test_methods/test_send_message.py index 0210d73f..1458ed4e 100644 --- a/tests/test_api/test_methods/test_send_message.py +++ b/tests/test_api/test_methods/test_send_message.py @@ -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) diff --git a/tests/test_filters/test_chat_member_updated.py b/tests/test_filters/test_chat_member_updated.py index f3fdce66..8592792c 100644 --- a/tests/test_filters/test_chat_member_updated.py +++ b/tests/test_filters/test_chat_member_updated.py @@ -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(), )