Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
3c0fd67
Get a very rough start on TelegramObject(api_kwargs)
Bibo-Joshi Sep 8, 2022
790c53e
More work on api_kwargs
Bibo-Joshi Sep 9, 2022
1cc0f2e
More work on api_kwargs
Bibo-Joshi Sep 9, 2022
54d999f
Only copy data if necessary
Bibo-Joshi Sep 9, 2022
702b008
pre-commit updates
Bibo-Joshi Sep 9, 2022
b4f7efe
add _apply_api_kwargs
Bibo-Joshi Sep 9, 2022
912aa0e
Get started on tests
Bibo-Joshi Sep 9, 2022
55a7c94
More tests
Bibo-Joshi Sep 10, 2022
674dd4c
Handle deprecated field in stickerset
Bibo-Joshi Sep 10, 2022
e8884e7
Finish on existing tests
Bibo-Joshi Sep 11, 2022
07a3001
include api_kwargs in to_dict
Bibo-Joshi Sep 11, 2022
0e0da1a
API kwargs on unpickling
Bibo-Joshi Sep 11, 2022
3789e59
deepsource
Bibo-Joshi Sep 11, 2022
16be53c
more deepsource
Bibo-Joshi Sep 11, 2022
f0f9a3f
fix another failing test
Bibo-Joshi Sep 11, 2022
f29da8d
Documentation of TelegramObject
Bibo-Joshi Sep 11, 2022
0789f21
More Documentation
Bibo-Joshi Sep 11, 2022
b317709
Merge branch 'master' into TO-overhaul
Bibo-Joshi Sep 25, 2022
21a75aa
Get startet on review
Bibo-Joshi Sep 25, 2022
ea3f13d
More review & dropping some unneeded `_parse_data` calls
Bibo-Joshi Sep 25, 2022
3ff5cae
more review
Bibo-Joshi Sep 25, 2022
17bd0da
Fix signature caching for subclasses
Bibo-Joshi Sep 25, 2022
d5ac429
doc fix
Bibo-Joshi Sep 25, 2022
1896b85
pre-commit
Bibo-Joshi Sep 25, 2022
059c9a0
coverage
Bibo-Joshi Sep 25, 2022
74dd8b6
update test_official
Bibo-Joshi Sep 25, 2022
8b8b386
Tone down on versionchanged
Bibo-Joshi Sep 25, 2022
0016e0b
Add tests for empty `api_kwargs`
Bibo-Joshi Sep 25, 2022
5c99ed2
bug fix
Bibo-Joshi Sep 25, 2022
5175cc7
More test adaptions
Bibo-Joshi Sep 25, 2022
3f81766
make `api_kwargs` a kw-only argument, pass to super in a few places a…
Bibo-Joshi Sep 29, 2022
cf6475b
tippity tappity, fixing tests
Bibo-Joshi Sep 29, 2022
7031807
Merge branch 'master' into TO-overhaul
Bibo-Joshi Oct 7, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions docs/substitutions/global.rst
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,9 @@
.. |thumbdocstringnopath| replace:: |thumbdocstringbase| |uploadinputnopath|

.. |editreplymarkup| replace:: It is currently only possible to edit messages without :attr:`telegram.Message.reply_markup` or with inline keyboards.

.. |toapikwargsbase| replace:: These arguments are also considered by :meth:`~telegram.TelegramObject.to_dict` and :meth:`~telegram.TelegramObject.to_json`, i.e. when passing objects to Telegram. Passing them to Telegram is however not guaranteed to work for all kinds of objects, e.g. this will fail for objects that can not directly be JSON serialized.

.. |toapikwargsarg| replace:: Arbitrary keyword arguments. Can be used to store data for which there are no dedicated attributes. |toapikwargsbase|

.. |toapikwargsattr| replace:: Optional. Arbitrary keyword arguments. Used to store data for which there are no dedicated attributes. |toapikwargsbase|
1 change: 1 addition & 0 deletions telegram/_bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,7 @@ def __init__(
private_key_password: bytes = None,
local_mode: bool = False,
):
super().__init__(api_kwargs=None)
if not token:
raise InvalidToken("You must pass the token you received from https://t.me/Botfather!")
self._token = token
Expand Down
5 changes: 3 additions & 2 deletions telegram/_botcommand.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains an object that represents a Telegram Bot Command."""
from typing import Any

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


class BotCommand(TelegramObject):
Expand All @@ -42,7 +42,8 @@ class BotCommand(TelegramObject):

__slots__ = ("description", "command")

def __init__(self, command: str, description: str, **_kwargs: Any):
def __init__(self, command: str, description: str, *, api_kwargs: JSONDict = None):
super().__init__(api_kwargs=api_kwargs)
self.command = command
self.description = description

Expand Down
44 changes: 20 additions & 24 deletions telegram/_botcommandscope.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
# along with this program. If not, see [http://www.gnu.org/licenses/].
# pylint: disable=redefined-builtin
"""This module contains objects representing Telegram bot command scopes."""
from typing import TYPE_CHECKING, Any, ClassVar, Dict, Optional, Type, Union
from typing import TYPE_CHECKING, ClassVar, Dict, Optional, Type, Union

from telegram import constants
from telegram._telegramobject import TelegramObject
Expand Down Expand Up @@ -75,7 +75,8 @@ class BotCommandScope(TelegramObject):
CHAT_MEMBER: ClassVar[str] = constants.BotCommandScopeType.CHAT_MEMBER
""":const:`telegram.constants.BotCommandScopeType.CHAT_MEMBER`"""

def __init__(self, type: str, **_kwargs: Any):
def __init__(self, type: str, *, api_kwargs: JSONDict = None):
super().__init__(api_kwargs=api_kwargs)
self.type = type
self._id_attrs = (self.type,)

Expand Down Expand Up @@ -107,9 +108,9 @@ def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["BotCommandSc
cls.CHAT_MEMBER: BotCommandScopeChatMember,
}

if cls is BotCommandScope:
return _class_mapping.get(data["type"], cls)(**data, bot=bot)
return cls(**data)
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):
Expand All @@ -119,15 +120,14 @@ class BotCommandScopeDefault(BotCommandScope):
.. _`narrower scope`: https://core.telegram.org/bots/api#determining-list-of-commands

.. versionadded:: 13.7

Attributes:
type (:obj:`str`): Scope type :tg-const:`telegram.BotCommandScope.DEFAULT`.
"""

__slots__ = ()

def __init__(self, **_kwargs: Any):
super().__init__(type=BotCommandScope.DEFAULT)
def __init__(self, *, api_kwargs: JSONDict = None):
super().__init__(type=BotCommandScope.DEFAULT, api_kwargs=api_kwargs)


class BotCommandScopeAllPrivateChats(BotCommandScope):
Expand All @@ -141,38 +141,36 @@ class BotCommandScopeAllPrivateChats(BotCommandScope):

__slots__ = ()

def __init__(self, **_kwargs: Any):
super().__init__(type=BotCommandScope.ALL_PRIVATE_CHATS)
def __init__(self, *, api_kwargs: JSONDict = None):
super().__init__(type=BotCommandScope.ALL_PRIVATE_CHATS, api_kwargs=api_kwargs)


class BotCommandScopeAllGroupChats(BotCommandScope):
"""Represents the scope of bot commands, covering all group and supergroup chats.

.. versionadded:: 13.7

Attributes:
type (:obj:`str`): Scope type :tg-const:`telegram.BotCommandScope.ALL_GROUP_CHATS`.
"""

__slots__ = ()

def __init__(self, **_kwargs: Any):
super().__init__(type=BotCommandScope.ALL_GROUP_CHATS)
def __init__(self, *, api_kwargs: JSONDict = None):
super().__init__(type=BotCommandScope.ALL_GROUP_CHATS, api_kwargs=api_kwargs)


class BotCommandScopeAllChatAdministrators(BotCommandScope):
"""Represents the scope of bot commands, covering all group and supergroup chat administrators.

.. versionadded:: 13.7

Attributes:
type (:obj:`str`): Scope type :tg-const:`telegram.BotCommandScope.ALL_CHAT_ADMINISTRATORS`.
"""

__slots__ = ()

def __init__(self, **_kwargs: Any):
super().__init__(type=BotCommandScope.ALL_CHAT_ADMINISTRATORS)
def __init__(self, *, api_kwargs: JSONDict = None):
super().__init__(type=BotCommandScope.ALL_CHAT_ADMINISTRATORS, api_kwargs=api_kwargs)


class BotCommandScopeChat(BotCommandScope):
Expand All @@ -186,7 +184,6 @@ class BotCommandScopeChat(BotCommandScope):
Args:
chat_id (:obj:`str` | :obj:`int`): Unique identifier for the target chat or username of the
target supergroup (in the format ``@supergroupusername``)

Attributes:
type (:obj:`str`): Scope type :tg-const:`telegram.BotCommandScope.CHAT`.
chat_id (:obj:`str` | :obj:`int`): Unique identifier for the target chat or username of the
Expand All @@ -195,8 +192,8 @@ class BotCommandScopeChat(BotCommandScope):

__slots__ = ("chat_id",)

def __init__(self, chat_id: Union[str, int], **_kwargs: Any):
super().__init__(type=BotCommandScope.CHAT)
def __init__(self, chat_id: Union[str, int], *, api_kwargs: JSONDict = None):
super().__init__(type=BotCommandScope.CHAT, api_kwargs=api_kwargs)
self.chat_id = (
chat_id if isinstance(chat_id, str) and chat_id.startswith("@") else int(chat_id)
)
Expand All @@ -215,7 +212,6 @@ class BotCommandScopeChatAdministrators(BotCommandScope):
Args:
chat_id (:obj:`str` | :obj:`int`): Unique identifier for the target chat or username of the
target supergroup (in the format ``@supergroupusername``)

Attributes:
type (:obj:`str`): Scope type :tg-const:`telegram.BotCommandScope.CHAT_ADMINISTRATORS`.
chat_id (:obj:`str` | :obj:`int`): Unique identifier for the target chat or username of the
Expand All @@ -224,8 +220,8 @@ class BotCommandScopeChatAdministrators(BotCommandScope):

__slots__ = ("chat_id",)

def __init__(self, chat_id: Union[str, int], **_kwargs: Any):
super().__init__(type=BotCommandScope.CHAT_ADMINISTRATORS)
def __init__(self, chat_id: Union[str, int], *, api_kwargs: JSONDict = None):
super().__init__(type=BotCommandScope.CHAT_ADMINISTRATORS, api_kwargs=api_kwargs)
self.chat_id = (
chat_id if isinstance(chat_id, str) and chat_id.startswith("@") else int(chat_id)
)
Expand Down Expand Up @@ -255,8 +251,8 @@ class BotCommandScopeChatMember(BotCommandScope):

__slots__ = ("chat_id", "user_id")

def __init__(self, chat_id: Union[str, int], user_id: int, **_kwargs: Any):
super().__init__(type=BotCommandScope.CHAT_MEMBER)
def __init__(self, chat_id: Union[str, int], user_id: int, *, api_kwargs: JSONDict = None):
super().__init__(type=BotCommandScope.CHAT_MEMBER, api_kwargs=api_kwargs)
self.chat_id = (
chat_id if isinstance(chat_id, str) and chat_id.startswith("@") else int(chat_id)
)
Expand Down
18 changes: 8 additions & 10 deletions telegram/_callbackquery.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
# along with this program. If not, see [http://www.gnu.org/licenses/].
# pylint: disable=redefined-builtin
"""This module contains an object that represents a Telegram CallbackQuery"""
from typing import TYPE_CHECKING, Any, ClassVar, List, Optional, Tuple, Union
from typing import TYPE_CHECKING, ClassVar, List, Optional, Tuple, Union

from telegram import constants
from telegram._files.location import Location
Expand Down Expand Up @@ -79,7 +79,6 @@ class CallbackQuery(TelegramObject):
inline mode, that originated the query.
game_short_name (:obj:`str`, optional): Short name of a Game to be returned, serves as
the unique identifier for the game
bot (:class:`telegram.Bot`, optional): The Bot to use for instance methods.

Attributes:
id (:obj:`str`): Unique identifier for this query.
Expand All @@ -96,7 +95,7 @@ class CallbackQuery(TelegramObject):
inline_message_id (:obj:`str`): Optional. Identifier of the message sent via the bot in
inline mode, that originated the query.
game_short_name (:obj:`str`): Optional. Short name of a Game to be returned.
bot (:class:`telegram.Bot`, optional): The Bot to use for instance methods.


"""

Expand All @@ -112,16 +111,17 @@ class CallbackQuery(TelegramObject):

def __init__(
self,
id: str, # pylint: disable=invalid-name
id: str,
from_user: User,
chat_instance: str,
message: Message = None,
data: str = None,
inline_message_id: str = None,
game_short_name: str = None,
bot: "Bot" = None,
**_kwargs: Any,
*,
api_kwargs: JSONDict = None,
):
super().__init__(api_kwargs=api_kwargs)
# Required
self.id = id # pylint: disable=invalid-name
self.from_user = from_user
Expand All @@ -132,8 +132,6 @@ def __init__(
self.inline_message_id = inline_message_id
self.game_short_name = game_short_name

self.set_bot(bot)

self._id_attrs = (self.id,)

@classmethod
Expand All @@ -144,10 +142,10 @@ def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["CallbackQuer
if not data:
return None

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

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

async def answer(
self,
Expand Down
33 changes: 20 additions & 13 deletions telegram/_chat.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains an object that represents a Telegram Chat."""
from datetime import datetime
from typing import TYPE_CHECKING, Any, ClassVar, List, Optional, Tuple, Union
from typing import TYPE_CHECKING, ClassVar, List, Optional, Tuple, Union

from telegram import constants
from telegram._chatlocation import ChatLocation
Expand Down Expand Up @@ -73,6 +73,11 @@ class Chat(TelegramObject):
``api_kwargs``. Use a named argument for those,
and notice that some positional arguments changed position as a result.

.. versionchanged:: 20.0
Removed the attribute ``all_members_are_administrators``. As long as Telegram provides
this field for backwards compatibility, it is available through
:attr:`~telegram.TelegramObject.api_kwargs`.

Args:
id (:obj:`int`): Unique identifier for this chat. This number may be greater than 32 bits
and some programming languages may have difficulty/silent defects in interpreting it.
Expand Down Expand Up @@ -114,7 +119,7 @@ class Chat(TelegramObject):
be forwarded to other chats. Returned only in :meth:`telegram.Bot.get_chat`.

.. versionadded:: 13.9
bot (:class:`telegram.Bot`, optional): The Bot to use for instance methods.

sticker_set_name (:obj:`str`, optional): For supergroups, name of group sticker set.
Returned only in :meth:`telegram.Bot.get_chat`.
can_set_sticker_set (:obj:`bool`, optional): :obj:`True`, if the bot can change group the
Expand All @@ -139,9 +144,6 @@ class Chat(TelegramObject):
in the private chat. Returned only in :meth:`telegram.Bot.get_chat`.

.. versionadded:: 20.0

**kwargs (:obj:`dict`): Arbitrary keyword arguments.

Attributes:
id (:obj:`int`): Unique identifier for this chat.
type (:obj:`str`): Type of chat.
Expand Down Expand Up @@ -220,7 +222,6 @@ class Chat(TelegramObject):
"title",
"photo",
"linked_chat_id",
"all_members_are_administrators",
"message_auto_delete_time",
"has_protected_content",
"has_private_forwards",
Expand All @@ -245,13 +246,12 @@ class Chat(TelegramObject):

def __init__(
self,
id: int, # pylint: disable=invalid-name
id: int,
type: str,
title: str = None,
username: str = None,
first_name: str = None,
last_name: str = None,
bot: "Bot" = None,
photo: ChatPhoto = None,
description: str = None,
invite_link: str = None,
Expand All @@ -269,8 +269,10 @@ def __init__(
join_to_send_messages: bool = None,
join_by_request: bool = None,
has_restricted_voice_and_video_messages: bool = None,
**_kwargs: Any,
*,
api_kwargs: JSONDict = None,
):
super().__init__(api_kwargs=api_kwargs)
# Required
self.id = id # pylint: disable=invalid-name
self.type = enum.get_member(constants.ChatType, type, type)
Expand All @@ -279,8 +281,6 @@ def __init__(
self.username = username
self.first_name = first_name
self.last_name = last_name
# TODO: Remove (also from tests), when Telegram drops this completely
self.all_members_are_administrators = _kwargs.get("all_members_are_administrators")
self.photo = photo
self.bio = bio
self.has_private_forwards = has_private_forwards
Expand All @@ -301,7 +301,6 @@ def __init__(
self.join_by_request = join_by_request
self.has_restricted_voice_and_video_messages = has_restricted_voice_and_video_messages

self.set_bot(bot)
self._id_attrs = (self.id,)

@property
Expand Down Expand Up @@ -346,7 +345,15 @@ def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["Chat"]:
data["permissions"] = ChatPermissions.de_json(data.get("permissions"), bot)
data["location"] = ChatLocation.de_json(data.get("location"), bot)

return cls(bot=bot, **data)
api_kwargs = {}
# This is a deprecated field that TG still returns for backwards compatibility
# Let's filter it out to speed up the de-json process
if "all_members_are_administrators" in data:
api_kwargs["all_members_are_administrators"] = data.pop(
"all_members_are_administrators"
)

return super()._de_json(data=data, bot=bot, api_kwargs=api_kwargs)

async def leave(
self,
Expand Down
7 changes: 4 additions & 3 deletions telegram/_chatadministratorrights.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,8 @@
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains the class which represents a Telegram ChatAdministratorRights."""

from typing import Any

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


class ChatAdministratorRights(TelegramObject):
Expand Down Expand Up @@ -119,8 +118,10 @@ def __init__(
can_post_messages: bool = None,
can_edit_messages: bool = None,
can_pin_messages: bool = None,
**_kwargs: Any,
*,
api_kwargs: JSONDict = None,
) -> None:
super().__init__(api_kwargs=api_kwargs)
# Required
self.is_anonymous = is_anonymous
self.can_manage_chat = can_manage_chat
Expand Down
Loading