Skip to content
5 changes: 5 additions & 0 deletions changes/unreleased/5186.VngJMeztfXKDXAhcqTxJjK.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
other = "Centralize `de_json` into `TelegramObject`"
[[pull_requests]]
uid = "5186"
author_uids = ["harshil21"]
closes_threads = []
51 changes: 14 additions & 37 deletions src/telegram/_botcommandscope.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,13 @@
# pylint: disable=redefined-builtin
"""This module contains objects representing Telegram bot command scopes."""

from typing import TYPE_CHECKING, Final
from typing import ClassVar, Final

from telegram import constants
from telegram._telegramobject import TelegramObject
from telegram._utils import enum
from telegram._utils.types import JSONDict

if TYPE_CHECKING:
from telegram import Bot


class BotCommandScope(TelegramObject):
"""Base class for objects that represent the scope to which bot commands are applied.
Expand Down Expand Up @@ -62,6 +59,19 @@ class BotCommandScope(TelegramObject):

__slots__ = ("type",)

__DE_JSON_DISPATCH__: ClassVar[tuple[str, dict[str, str]] | None] = (
"type",
{
"default": "BotCommandScopeDefault",
"all_private_chats": "BotCommandScopeAllPrivateChats",
"all_group_chats": "BotCommandScopeAllGroupChats",
"all_chat_administrators": "BotCommandScopeAllChatAdministrators",
"chat": "BotCommandScopeChat",
"chat_administrators": "BotCommandScopeChatAdministrators",
"chat_member": "BotCommandScopeChatMember",
},
)

DEFAULT: Final[str] = constants.BotCommandScopeType.DEFAULT
""":const:`telegram.constants.BotCommandScopeType.DEFAULT`"""
ALL_PRIVATE_CHATS: Final[str] = constants.BotCommandScopeType.ALL_PRIVATE_CHATS
Expand All @@ -84,39 +94,6 @@ def __init__(self, type: str, *, api_kwargs: JSONDict | None = None):

self._freeze()

@classmethod
def de_json(cls, data: JSONDict, bot: "Bot | None" = None) -> "BotCommandScope":
"""Converts JSON data to the appropriate :class:`BotCommandScope` object, i.e. takes
care of selecting the correct subclass.

Args:
data (dict[:obj:`str`, ...]): The JSON data.
bot (:class:`telegram.Bot`, optional): The bot associated with this object. Defaults to
:obj:`None`, in which case shortcut methods will not be available.

.. versionchanged:: 21.4
:paramref:`bot` is now optional and defaults to :obj:`None`

Returns:
The Telegram object.

"""
data = cls._parse_data(data)

_class_mapping: dict[str, type[BotCommandScope]] = {
cls.DEFAULT: BotCommandScopeDefault,
cls.ALL_PRIVATE_CHATS: BotCommandScopeAllPrivateChats,
cls.ALL_GROUP_CHATS: BotCommandScopeAllGroupChats,
cls.ALL_CHAT_ADMINISTRATORS: BotCommandScopeAllChatAdministrators,
cls.CHAT: BotCommandScopeChat,
cls.CHAT_ADMINISTRATORS: BotCommandScopeChatAdministrators,
cls.CHAT_MEMBER: BotCommandScopeChatMember,
}

if cls is BotCommandScope and data.get("type") in _class_mapping:
return _class_mapping[data.pop("type")].de_json(data=data, bot=bot)
return super().de_json(data=data, bot=bot)


class BotCommandScopeDefault(BotCommandScope):
"""Represents the default scope of bot commands. Default commands are used if no commands with
Expand Down
61 changes: 2 additions & 59 deletions src/telegram/_business.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,24 +25,19 @@
from zoneinfo import ZoneInfo

from telegram._chat import Chat
from telegram._files.location import Location
from telegram._files.sticker import Sticker
from telegram._telegramobject import TelegramObject
from telegram._user import User
from telegram._utils.argumentparsing import (
de_json_optional,
de_list_optional,
parse_sequence_arg,
)
from telegram._utils.datetime import (
extract_tzinfo_from_defaults,
from_timestamp,
get_zone_info,
)
from telegram._utils.types import JSONDict

if TYPE_CHECKING:
from telegram import Bot
from telegram._files.location import Location
from telegram._user import User


class BusinessBotRights(TelegramObject):
Expand Down Expand Up @@ -266,20 +261,6 @@ def __init__(

self._freeze()

@classmethod
def de_json(cls, data: JSONDict, bot: "Bot | None" = None) -> "BusinessConnection":
"""See :meth:`telegram.TelegramObject.de_json`."""
data = cls._parse_data(data)

# Get the local timezone from the bot if it has defaults
loc_tzinfo = extract_tzinfo_from_defaults(bot)

data["date"] = from_timestamp(data.get("date"), tzinfo=loc_tzinfo)
data["user"] = de_json_optional(data.get("user"), User, bot)
data["rights"] = de_json_optional(data.get("rights"), BusinessBotRights, bot)

return super().de_json(data=data, bot=bot)


class BusinessMessagesDeleted(TelegramObject):
"""
Expand Down Expand Up @@ -333,15 +314,6 @@ def __init__(

self._freeze()

@classmethod
def de_json(cls, data: JSONDict, bot: "Bot | None" = None) -> "BusinessMessagesDeleted":
"""See :meth:`telegram.TelegramObject.de_json`."""
data = cls._parse_data(data)

data["chat"] = de_json_optional(data.get("chat"), Chat, bot)

return super().de_json(data=data, bot=bot)


class BusinessIntro(TelegramObject):
"""
Expand Down Expand Up @@ -387,15 +359,6 @@ def __init__(

self._freeze()

@classmethod
def de_json(cls, data: JSONDict, bot: "Bot | None" = None) -> "BusinessIntro":
"""See :meth:`telegram.TelegramObject.de_json`."""
data = cls._parse_data(data)

data["sticker"] = de_json_optional(data.get("sticker"), Sticker, bot)

return super().de_json(data=data, bot=bot)


class BusinessLocation(TelegramObject):
"""
Expand Down Expand Up @@ -436,15 +399,6 @@ def __init__(

self._freeze()

@classmethod
def de_json(cls, data: JSONDict, bot: "Bot | None" = None) -> "BusinessLocation":
"""See :meth:`telegram.TelegramObject.de_json`."""
data = cls._parse_data(data)

data["location"] = de_json_optional(data.get("location"), Location, bot)

return super().de_json(data=data, bot=bot)


class BusinessOpeningHoursInterval(TelegramObject):
"""
Expand Down Expand Up @@ -678,14 +632,3 @@ def is_open(self, datetime: dtm.datetime) -> bool:
return True

return False

@classmethod
def de_json(cls, data: JSONDict, bot: "Bot | None" = None) -> "BusinessOpeningHours":
"""See :meth:`telegram.TelegramObject.de_json`."""
data = cls._parse_data(data)

data["opening_hours"] = de_list_optional(
data.get("opening_hours"), BusinessOpeningHoursInterval, bot
)

return super().de_json(data=data, bot=bot)
12 changes: 0 additions & 12 deletions src/telegram/_callbackquery.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,11 @@
from telegram._message import MaybeInaccessibleMessage, Message
from telegram._telegramobject import TelegramObject
from telegram._user import User
from telegram._utils.argumentparsing import de_json_optional
from telegram._utils.defaultvalue import DEFAULT_NONE
from telegram._utils.types import JSONDict, ODVInput, TimePeriod

if TYPE_CHECKING:
from telegram import (
Bot,
GameHighScore,
InlineKeyboardMarkup,
InputMedia,
Expand Down Expand Up @@ -153,16 +151,6 @@ def __init__(

self._freeze()

@classmethod
def de_json(cls, data: JSONDict, bot: "Bot | None" = None) -> "CallbackQuery":
"""See :meth:`telegram.TelegramObject.de_json`."""
data = cls._parse_data(data)

data["from_user"] = de_json_optional(data.pop("from", None), User, bot)
data["message"] = de_json_optional(data.get("message"), Message, bot)

return super().de_json(data=data, bot=bot)

async def answer(
self,
text: str | None = None,
Expand Down
74 changes: 21 additions & 53 deletions src/telegram/_chatbackground.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,15 @@
"""This module contains objects related to chat backgrounds."""

from collections.abc import Sequence
from typing import TYPE_CHECKING, Final
from typing import ClassVar, Final

from telegram import constants
from telegram._files.document import Document
from telegram._telegramobject import TelegramObject
from telegram._utils import enum
from telegram._utils.argumentparsing import de_json_optional, parse_sequence_arg
from telegram._utils.argumentparsing import parse_sequence_arg
from telegram._utils.types import JSONDict

if TYPE_CHECKING:
from telegram import Bot


class BackgroundFill(TelegramObject):
"""Base class for Telegram BackgroundFill Objects. It can be one of:
Expand All @@ -57,6 +54,15 @@ class BackgroundFill(TelegramObject):

__slots__ = ("type",)

__DE_JSON_DISPATCH__: ClassVar[tuple[str, dict[str, str]] | None] = (
"type",
{
"solid": "BackgroundFillSolid",
"gradient": "BackgroundFillGradient",
"freeform_gradient": "BackgroundFillFreeformGradient",
},
)

SOLID: Final[constants.BackgroundFillType] = constants.BackgroundFillType.SOLID
""":const:`telegram.constants.BackgroundFillType.SOLID`"""
GRADIENT: Final[constants.BackgroundFillType] = constants.BackgroundFillType.GRADIENT
Expand All @@ -79,22 +85,6 @@ def __init__(
self._id_attrs = (self.type,)
self._freeze()

@classmethod
def de_json(cls, data: JSONDict, bot: "Bot | None" = None) -> "BackgroundFill":
"""See :meth:`telegram.TelegramObject.de_json`."""
data = cls._parse_data(data)

_class_mapping: dict[str, type[BackgroundFill]] = {
cls.SOLID: BackgroundFillSolid,
cls.GRADIENT: BackgroundFillGradient,
cls.FREEFORM_GRADIENT: BackgroundFillFreeformGradient,
}

if cls is BackgroundFill and data.get("type") in _class_mapping:
return _class_mapping[data.pop("type")].de_json(data=data, bot=bot)

return super().de_json(data=data, bot=bot)


class BackgroundFillSolid(BackgroundFill):
"""
Expand Down Expand Up @@ -243,6 +233,16 @@ class BackgroundType(TelegramObject):

__slots__ = ("type",)

__DE_JSON_DISPATCH__: ClassVar[tuple[str, dict[str, str]] | None] = (
"type",
{
"fill": "BackgroundTypeFill",
"wallpaper": "BackgroundTypeWallpaper",
"pattern": "BackgroundTypePattern",
"chat_theme": "BackgroundTypeChatTheme",
},
)

FILL: Final[constants.BackgroundTypeType] = constants.BackgroundTypeType.FILL
""":const:`telegram.constants.BackgroundTypeType.FILL`"""
WALLPAPER: Final[constants.BackgroundTypeType] = constants.BackgroundTypeType.WALLPAPER
Expand All @@ -265,29 +265,6 @@ def __init__(
self._id_attrs = (self.type,)
self._freeze()

@classmethod
def de_json(cls, data: JSONDict, bot: "Bot | None" = None) -> "BackgroundType":
"""See :meth:`telegram.TelegramObject.de_json`."""
data = cls._parse_data(data)

_class_mapping: dict[str, type[BackgroundType]] = {
cls.FILL: BackgroundTypeFill,
cls.WALLPAPER: BackgroundTypeWallpaper,
cls.PATTERN: BackgroundTypePattern,
cls.CHAT_THEME: BackgroundTypeChatTheme,
}

if cls is BackgroundType and data.get("type") in _class_mapping:
return _class_mapping[data.pop("type")].de_json(data=data, bot=bot)

if "fill" in data:
data["fill"] = de_json_optional(data.get("fill"), BackgroundFill, bot)

if "document" in data:
data["document"] = de_json_optional(data.get("document"), Document, bot)

return super().de_json(data=data, bot=bot)


class BackgroundTypeFill(BackgroundType):
"""
Expand Down Expand Up @@ -522,12 +499,3 @@ def __init__(

self._id_attrs = (self.type,)
self._freeze()

@classmethod
def de_json(cls, data: JSONDict, bot: "Bot | None" = None) -> "ChatBackground":
"""See :meth:`telegram.TelegramObject.de_json`."""
data = cls._parse_data(data)

data["type"] = de_json_optional(data.get("type"), BackgroundType, bot)

return super().de_json(data=data, bot=bot)
Loading
Loading