diff --git a/telegram/chat.py b/telegram/chat.py index e049f0114a4..d9fbe6f761d 100644 --- a/telegram/chat.py +++ b/telegram/chat.py @@ -189,6 +189,24 @@ def __init__( self.bot = bot self._id_attrs = (self.id,) + @property + def full_name(self) -> Optional[str]: + """ + :obj:`str`: Convenience property. If :attr:`first_name` is not :obj:`None` gives, + :attr:`first_name` followed by (if available) :attr:`last_name`. + + Note: + :attr:`full_name` will always be :obj:`None`, if the chat is a (super)group or + channel. + + .. versionadded:: 13.2 + """ + if not self.first_name: + return None + if self.last_name: + return f'{self.first_name} {self.last_name}' + return self.first_name + @property def link(self) -> Optional[str]: """:obj:`str`: Convenience property. If the chat has a :attr:`username`, returns a t.me @@ -350,6 +368,80 @@ def unban_member( only_if_banned=only_if_banned, ) + def promote_member( + self, + user_id: Union[str, int], + can_change_info: bool = None, + can_post_messages: bool = None, + can_edit_messages: bool = None, + can_delete_messages: bool = None, + can_invite_users: bool = None, + can_restrict_members: bool = None, + can_pin_messages: bool = None, + can_promote_members: bool = None, + timeout: float = None, + api_kwargs: JSONDict = None, + is_anonymous: bool = None, + ) -> bool: + """Shortcut for:: + + bot.promote_chat_member(update.effective_chat.id, *args, **kwargs) + + For the documentation of the arguments, please see + :meth:`telegram.Bot.promote_chat_member`. + + .. versionadded:: 13.2 + + Returns: + :obj:`bool`: If the action was sent successfully. + + """ + return self.bot.promote_chat_member( + chat_id=self.id, + user_id=user_id, + can_change_info=can_change_info, + can_post_messages=can_post_messages, + can_edit_messages=can_edit_messages, + can_delete_messages=can_delete_messages, + can_invite_users=can_invite_users, + can_restrict_members=can_restrict_members, + can_pin_messages=can_pin_messages, + can_promote_members=can_promote_members, + timeout=timeout, + api_kwargs=api_kwargs, + is_anonymous=is_anonymous, + ) + + def restrict_member( + self, + user_id: Union[str, int], + permissions: ChatPermissions, + until_date: Union[int, datetime] = None, + timeout: float = None, + api_kwargs: JSONDict = None, + ) -> bool: + """Shortcut for:: + + bot.restrict_chat_member(update.effective_chat.id, *args, **kwargs) + + For the documentation of the arguments, please see + :meth:`telegram.Bot.restrict_chat_member`. + + .. versionadded:: 13.2 + + Returns: + :obj:`bool`: If the action was sent successfully. + + """ + return self.bot.restrict_chat_member( + chat_id=self.id, + user_id=user_id, + permissions=permissions, + until_date=until_date, + timeout=timeout, + api_kwargs=api_kwargs, + ) + def set_permissions( self, permissions: ChatPermissions, diff --git a/telegram/ext/conversationhandler.py b/telegram/ext/conversationhandler.py index fdcf8748b5a..2bee77406f9 100644 --- a/telegram/ext/conversationhandler.py +++ b/telegram/ext/conversationhandler.py @@ -136,6 +136,15 @@ class ConversationHandler(Handler[Update]): map_to_parent (Dict[:obj:`object`, :obj:`object`], optional): A :obj:`dict` that can be used to instruct a nested conversationhandler to transition into a mapped state on its parent conversationhandler in place of a specified nested state. + run_async (:obj:`bool`, optional): Pass :obj:`True` to *override* the + :attr:`Handler.run_async` setting of all handlers (in :attr:`entry_points`, + :attr:`states` and :attr:`fallbacks`). + + Note: + If set to :obj:`True`, you should not pass a handler instance, that needs to be + run synchronously in another context. + + .. versionadded:: 13.2 Raises: ValueError @@ -168,6 +177,10 @@ class ConversationHandler(Handler[Update]): map_to_parent (Dict[:obj:`object`, :obj:`object`]): Optional. A :obj:`dict` that can be used to instruct a nested conversationhandler to transition into a mapped state on its parent conversationhandler in place of a specified nested state. + run_async (:obj:`bool`): If :obj:`True`, will override the + :attr:`Handler.run_async` setting of all internal handlers on initialization. + + .. versionadded:: 13.2 """ @@ -192,8 +205,9 @@ def __init__( name: str = None, persistent: bool = False, map_to_parent: Dict[object, object] = None, + run_async: bool = False, ): - self.run_async = False + self.run_async = run_async self._entry_points = entry_points self._states = states @@ -229,7 +243,7 @@ def __init__( "since message IDs are not globally unique." ) - all_handlers = list() + all_handlers: List[Handler] = list() all_handlers.extend(entry_points) all_handlers.extend(fallbacks) @@ -263,6 +277,10 @@ def __init__( ) break + if self.run_async: + for handler in all_handlers: + handler.run_async = True + @property def entry_points(self) -> List[Handler]: return self._entry_points diff --git a/telegram/messageentity.py b/telegram/messageentity.py index c92a98e0f2d..04947cb9563 100644 --- a/telegram/messageentity.py +++ b/telegram/messageentity.py @@ -33,7 +33,7 @@ class MessageEntity(TelegramObject): usernames, URLs, etc. Objects of this class are comparable in terms of equality. Two objects of this class are - considered equal, if their :attr:`type`, :attr:`offset` and :attr`length` are equal. + considered equal, if their :attr:`type`, :attr:`offset` and :attr:`length` are equal. Args: type (:obj:`str`): Type of the entity. Can be mention (@username), hashtag, bot_command, diff --git a/telegram/user.py b/telegram/user.py index 404b30026fb..e0e6642978e 100644 --- a/telegram/user.py +++ b/telegram/user.py @@ -135,7 +135,7 @@ def full_name(self) -> str: available) :attr:`last_name`.""" if self.last_name: - return u'{} {}'.format(self.first_name, self.last_name) + return f'{self.first_name} {self.last_name}' return self.first_name @property diff --git a/telegram/utils/helpers.py b/telegram/utils/helpers.py index 8e9a82c7975..522a9940488 100644 --- a/telegram/utils/helpers.py +++ b/telegram/utils/helpers.py @@ -317,7 +317,7 @@ def mention_html(user_id: Union[int, str], name: str) -> str: Returns: :obj:`str`: The inline mention for the user as html. """ - return u'{}'.format(user_id, escape(name)) + return f'{escape(name)}' def mention_markdown(user_id: Union[int, str], name: str, version: int = 1) -> str: @@ -331,7 +331,7 @@ def mention_markdown(user_id: Union[int, str], name: str, version: int = 1) -> s Returns: :obj:`str`: The inline mention for the user as markdown. """ - return u'[{}](tg://user?id={})'.format(escape_markdown(name, version=version), user_id) + return f'[{escape_markdown(name, version=version)}](tg://user?id={user_id})' def effective_message_type(entity: Union['Message', 'Update']) -> Optional[str]: diff --git a/tests/test_chat.py b/tests/test_chat.py index f980eb1ee82..02453109203 100644 --- a/tests/test_chat.py +++ b/tests/test_chat.py @@ -112,6 +112,19 @@ def test_link(self, chat): chat.username = None assert chat.link is None + def test_full_name(self): + chat = Chat( + id=1, type=Chat.PRIVATE, first_name=u'first\u2022name', last_name=u'last\u2022name' + ) + assert chat.full_name == u'first\u2022name last\u2022name' + chat = Chat(id=1, type=Chat.PRIVATE, first_name=u'first\u2022name') + assert chat.full_name == u'first\u2022name' + chat = Chat( + id=1, + type=Chat.PRIVATE, + ) + assert chat.full_name is None + def test_send_action(self, monkeypatch, chat): send_chat_action = chat.bot.send_chat_action @@ -211,6 +224,44 @@ def make_assertion(*_, **kwargs): monkeypatch.setattr(chat.bot, 'unban_chat_member', make_assertion) assert chat.unban_member(user_id=42, only_if_banned=only_if_banned) + @pytest.mark.parametrize('is_anonymous', [True, False, None]) + def test_promote_member(self, monkeypatch, chat, is_anonymous): + promote_chat_member = chat.bot.promote_chat_member + + def make_assertion(*_, **kwargs): + chat_id = kwargs['chat_id'] == chat.id + user_id = kwargs['user_id'] == 42 + o_i_b = kwargs.get('is_anonymous', None) == is_anonymous + return ( + chat_id and user_id and o_i_b and check_shortcut_call(kwargs, promote_chat_member) + ) + + assert check_shortcut_signature( + Chat.promote_member, Bot.promote_chat_member, ['chat_id'], [] + ) + + monkeypatch.setattr(chat.bot, 'promote_chat_member', make_assertion) + assert chat.promote_member(user_id=42, is_anonymous=is_anonymous) + + def test_restrict_member(self, monkeypatch, chat): + restrict_chat_member = chat.bot.restrict_chat_member + permissions = ChatPermissions(True, False, True, False, True, False, True, False) + + def make_assertion(*_, **kwargs): + chat_id = kwargs['chat_id'] == chat.id + user_id = kwargs['user_id'] == 42 + o_i_b = kwargs.get('permissions', None) == permissions + return ( + chat_id and user_id and o_i_b and check_shortcut_call(kwargs, restrict_chat_member) + ) + + assert check_shortcut_signature( + Chat.restrict_member, Bot.restrict_chat_member, ['chat_id'], [] + ) + + monkeypatch.setattr(chat.bot, 'restrict_chat_member', make_assertion) + assert chat.restrict_member(user_id=42, permissions=permissions) + def test_set_permissions(self, monkeypatch, chat): set_chat_permissions = chat.bot.set_chat_permissions diff --git a/tests/test_conversationhandler.py b/tests/test_conversationhandler.py index 8436d2c491a..4bb36c644e5 100644 --- a/tests/test_conversationhandler.py +++ b/tests/test_conversationhandler.py @@ -1429,3 +1429,36 @@ def test_callback(u, c): assert self.current_state[user1.id] == self.STOPPING assert handler.conversations.get((0, user1.id)) is None assert not self.test_flag + + def test_conversation_handler_run_async_true(self, dp): + conv_handler = ConversationHandler( + entry_points=self.entry_points, + states=self.states, + fallbacks=self.fallbacks, + run_async=True, + ) + + all_handlers = conv_handler.entry_points + conv_handler.fallbacks + for state_handlers in conv_handler.states.values(): + all_handlers += state_handlers + + for handler in all_handlers: + assert handler.run_async + + def test_conversation_handler_run_async_false(self, dp): + conv_handler = ConversationHandler( + entry_points=[CommandHandler('start', self.start_end, run_async=True)], + states=self.states, + fallbacks=self.fallbacks, + run_async=False, + ) + + for handler in conv_handler.entry_points: + assert handler.run_async + + all_handlers = conv_handler.fallbacks + for state_handlers in conv_handler.states.values(): + all_handlers += state_handlers + + for handler in all_handlers: + assert not handler.run_async.value pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy