Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
79 changes: 63 additions & 16 deletions telegram/bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -3628,18 +3628,18 @@ def pin_chat_message(
api_kwargs: JSONDict = None,
) -> bool:
"""
Use this method to pin a message in a group, a supergroup, or a channel.
The bot must be an administrator in the chat for this to work and must have the
can_pin_messages admin right in the supergroup or can_edit_messages’ admin right
in the channel.
Use this method to add a message to the list of pinned messages in a chat. If the
chat is not a private chat, the bot must be an administrator in the chat for this to work
and must have the ``can_pin_messages`` admin right in a supergroup or ``can_edit_messages``
admin right in a channel.

Args:
chat_id (:obj:`int` | :obj:`str`): Unique identifier for the target chat or username
of the target channel (in the format @channelusername).
message_id (:obj:`int`): Identifier of a message to pin.
disable_notification (:obj:`bool`, optional): Pass :obj:`True`, if it is not necessary
to send a notification to all group members about the new pinned message.
Notifications are always disabled in channels.
to send a notification to all chat members about the new pinned message.
Notifications are always disabled in channels and private chats.
timeout (:obj:`int` | :obj:`float`, optional): If this value is specified, use it as
the read timeout from the server (instead of the one specified during creation of
the connection pool).
Expand All @@ -3658,23 +3658,29 @@ def pin_chat_message(
if disable_notification is not None:
data['disable_notification'] = disable_notification

result = self._post('pinChatMessage', data, timeout=timeout, api_kwargs=api_kwargs)

return result # type: ignore[return-value]
return self._post( # type: ignore[return-value]
'pinChatMessage', data, timeout=timeout, api_kwargs=api_kwargs
)

@log
def unpin_chat_message(
self, chat_id: Union[str, int], timeout: float = None, api_kwargs: JSONDict = None
self,
chat_id: Union[str, int],
timeout: float = None,
api_kwargs: JSONDict = None,
message_id: Union[str, int] = None,
) -> bool:
"""
Use this method to unpin a message in a group, a supergroup, or a channel.
The bot must be an administrator in the chat for this to work and must have the
``can_pin_messages`` admin right in the supergroup or ``can_edit_messages`` admin right
in the channel.
Use this method to remove a message from the list of pinned messages in a chat. If the
chat is not a private chat, the bot must be an administrator in the chat for this to work
and must have the ``can_pin_messages`` admin right in a supergroup or ``can_edit_messages``
admin right in a channel.

Args:
chat_id (:obj:`int` | :obj:`str`): Unique identifier for the target chat or username
of the target channel (in the format @channelusername).
message_id (:obj:`int`, optional): Identifier of a message to unpin. If not specified,
the most recent pinned message (by sending date) will be unpinned.
timeout (:obj:`int` | :obj:`float`, optional): If this value is specified, use it as
the read timeout from the server (instead of the one specified during creation of
the connection pool).
Expand All @@ -3690,9 +3696,48 @@ def unpin_chat_message(
"""
data: JSONDict = {'chat_id': chat_id}

result = self._post('unpinChatMessage', data, timeout=timeout, api_kwargs=api_kwargs)
if message_id is not None:
data['message_id'] = message_id

return self._post( # type: ignore[return-value]
'unpinChatMessage', data, timeout=timeout, api_kwargs=api_kwargs
)

return result # type: ignore[return-value]
@log
def unpin_all_chat_messages(
self,
chat_id: Union[str, int],
timeout: float = None,
api_kwargs: JSONDict = None,
) -> bool:
"""
Use this method to clear the list of pinned messages in a chat. If the
chat is not a private chat, the bot must be an administrator in the chat for this
to work and must have the ``can_pin_messages`` admin right in a supergroup or
``can_edit_messages`` admin right in a channel.

Args:
chat_id (:obj:`int` | :obj:`str`): Unique identifier for the target chat or username
of the target channel (in the format @channelusername).
timeout (:obj:`int` | :obj:`float`, optional): If this value is specified, use it as
the read timeout from the server (instead of the one specified during creation of
the connection pool).
api_kwargs (:obj:`dict`, optional): Arbitrary keyword arguments to be passed to the
Telegram API.

Returns:
:obj:`bool`: On success, :obj:`True` is returned.

Raises:
:class:`telegram.TelegramError`

"""

data: JSONDict = {'chat_id': chat_id}

return self._post( # type: ignore[return-value]
'unpinAllChatMessages', data, timeout=timeout, api_kwargs=api_kwargs
)

@log
def get_sticker_set(
Expand Down Expand Up @@ -4483,6 +4528,8 @@ def to_dict(self) -> JSONDict:
"""Alias for :attr:`pin_chat_message`"""
unpinChatMessage = unpin_chat_message
"""Alias for :attr:`unpin_chat_message`"""
unpinAllChatMessages = unpin_all_chat_messages
"""Alias for :attr:`unpin_all_chat_messages`"""
getStickerSet = get_sticker_set
"""Alias for :attr:`get_sticker_set`"""
uploadStickerFile = upload_sticker_file
Expand Down
28 changes: 28 additions & 0 deletions telegram/callbackquery.py
Original file line number Diff line number Diff line change
Expand Up @@ -325,3 +325,31 @@ def delete_message(self, *args: Any, **kwargs: Any) -> Union[Message, bool]:

"""
return self.message.delete(*args, **kwargs)

def pin_message(self, *args: Any, **kwargs: Any) -> bool:
"""Shortcut for::

bot.pin_chat_message(chat_id=message.chat_id,
message_id=message.message_id,
*args,
**kwargs)

Returns:
:obj:`bool`: On success, :obj:`True` is returned.

"""
return self.message.pin(*args, **kwargs)

def unpin_message(self, *args: Any, **kwargs: Any) -> bool:
"""Shortcut for::

bot.unpin_chat_message(chat_id=message.chat_id,
message_id=message.message_id,
*args,
**kwargs)

Returns:
:obj:`bool`: On success, :obj:`True` is returned.

"""
return self.message.unpin(*args, **kwargs)
47 changes: 43 additions & 4 deletions telegram/chat.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,8 @@ class Chat(TelegramObject):
photo (:class:`telegram.ChatPhoto`): Optional. Chat photo.
description (:obj:`str`): Optional. Description, for groups, supergroups and channel chats.
invite_link (:obj:`str`): Optional. Chat invite link, for supergroups and channel chats.
pinned_message (:class:`telegram.Message`): Optional. Pinned message, for supergroups.
Returned only in :meth:`telegram.Bot.get_chat`.
pinned_message (:class:`telegram.Message`): Optional. The most recent pinned message
(by sending date). Returned only in :meth:`telegram.Bot.get_chat`.
permissions (:class:`telegram.ChatPermissions`): Optional. Default chat member permissions,
for groups and supergroups. Returned only in :meth:`telegram.Bot.get_chat`.
slow_mode_delay (:obj:`int`): Optional. For supergroups, the minimum allowed delay between
Expand Down Expand Up @@ -77,8 +77,8 @@ class Chat(TelegramObject):
chats. Each administrator in a chat generates their own invite links, so the bot must
first generate the link using ``export_chat_invite_link()``. Returned only
in :meth:`telegram.Bot.get_chat`.
pinned_message (:class:`telegram.Message`, optional): Pinned message, for groups,
supergroups and channels. Returned only in :meth:`telegram.Bot.get_chat`.
pinned_message (:class:`telegram.Message`, optional): The most recent pinned message
(by sending date). Returned only in :meth:`telegram.Bot.get_chat`.
permissions (:class:`telegram.ChatPermissions`): Optional. Default chat member permissions,
for groups and supergroups. Returned only in :meth:`telegram.Bot.get_chat`.
slow_mode_delay (:obj:`int`, optional): For supergroups, the minimum allowed delay between
Expand Down Expand Up @@ -277,6 +277,45 @@ def set_administrator_custom_title(self, *args: Any, **kwargs: Any) -> bool:
"""
return self.bot.set_chat_administrator_custom_title(self.id, *args, **kwargs)

def pin_message(self, *args: Any, **kwargs: Any) -> bool:
"""Shortcut for::

bot.pin_chat_message(chat_id=update.effective_chat.id,
*args,
**kwargs)

Returns:
:obj:`bool`: On success, :obj:`True` is returned.

"""
return self.bot.pin_chat_message(self.id, *args, **kwargs)

def unpin_message(self, *args: Any, **kwargs: Any) -> bool:
"""Shortcut for::

bot.unpin_chat_message(chat_id=update.effective_chat.id,
*args,
**kwargs)

Returns:
:obj:`bool`: On success, :obj:`True` is returned.

"""
return self.bot.unpin_chat_message(self.id, *args, **kwargs)

def unpin_all_messages(self, *args: Any, **kwargs: Any) -> bool:
"""Shortcut for::

bot.unpin_all_chat_messages(chat_id=update.effective_chat.id,
*args,
**kwargs)

Returns:
:obj:`bool`: On success, :obj:`True` is returned.

"""
return self.bot.unpin_all_chat_messages(chat_id=self.id, *args, **kwargs)

def send_message(self, *args: Any, **kwargs: Any) -> 'Message':
"""Shortcut for::

Expand Down
18 changes: 17 additions & 1 deletion telegram/message.py
Original file line number Diff line number Diff line change
Expand Up @@ -1136,13 +1136,29 @@ def pin(self, *args: Any, **kwargs: Any) -> bool:
**kwargs)

Returns:
:obj:`True`: On success.
:obj:`bool`: On success, :obj:`True` is returned.

"""
return self.bot.pin_chat_message(
chat_id=self.chat_id, message_id=self.message_id, *args, **kwargs
)

def unpin(self, *args: Any, **kwargs: Any) -> bool:
"""Shortcut for::

bot.unpin_chat_message(chat_id=message.chat_id,
message_id=message.message_id,
*args,
**kwargs)

Returns:
:obj:`bool`: On success, :obj:`True` is returned.

"""
return self.bot.unpin_chat_message(
chat_id=self.chat_id, message_id=self.message_id, *args, **kwargs
)

def parse_entity(self, entity: MessageEntity) -> str:
"""Returns the text from a given :class:`telegram.MessageEntity`.

Expand Down
39 changes: 39 additions & 0 deletions telegram/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,45 @@ def mention_html(self, name: str = None) -> str:
return util_mention_html(self.id, name)
return util_mention_html(self.id, self.full_name)

def pin_message(self, *args: Any, **kwargs: Any) -> bool:
"""Shortcut for::

bot.pin_chat_message(chat_id=update.effective_user.id,
*args,
**kwargs)

Returns:
:obj:`bool`: On success, :obj:`True` is returned.

"""
return self.bot.pin_chat_message(self.id, *args, **kwargs)

def unpin_message(self, *args: Any, **kwargs: Any) -> bool:
"""Shortcut for::

bot.unpin_chat_message(chat_id=update.effective_user.id,
*args,
**kwargs)

Returns:
:obj:`bool`: On success, :obj:`True` is returned.

"""
return self.bot.unpin_chat_message(self.id, *args, **kwargs)

def unpin_all_messages(self, *args: Any, **kwargs: Any) -> bool:
"""Shortcut for::

bot.unpin_all_chat_messages(chat_id=update.effective_user.id,
*args,
**kwargs)

Returns:
:obj:`bool`: On success, :obj:`True` is returned.

"""
return self.bot.unpin_all_chat_messages(self.id, *args, **kwargs)

def send_message(self, *args: Any, **kwargs: Any) -> 'Message':
"""Shortcut for::

Expand Down
21 changes: 17 additions & 4 deletions tests/test_bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -1263,15 +1263,28 @@ def test_set_chat_description(self, bot, channel_id):
@flaky(3, 1)
@pytest.mark.timeout(10)
def test_pin_and_unpin_message(self, bot, super_group_id):
message = bot.send_message(super_group_id, text="test_pin_message")
message1 = bot.send_message(super_group_id, text="test_pin_message_1")
message2 = bot.send_message(super_group_id, text="test_pin_message_2")
message3 = bot.send_message(super_group_id, text="test_pin_message_3")

assert bot.pin_chat_message(
chat_id=super_group_id, message_id=message.message_id, disable_notification=True
chat_id=super_group_id, message_id=message1.message_id, disable_notification=True
)

bot.pin_chat_message(
chat_id=super_group_id, message_id=message2.message_id, disable_notification=True
)
bot.pin_chat_message(
chat_id=super_group_id, message_id=message3.message_id, disable_notification=True
)

chat = bot.get_chat(super_group_id)
assert chat.pinned_message == message
assert chat.pinned_message == message3

assert bot.unpin_chat_message(super_group_id, message_id=message2.message_id)
assert bot.unpin_chat_message(super_group_id)

assert bot.unpinChatMessage(super_group_id)
assert bot.unpin_all_chat_messages(super_group_id)

# get_sticker_set, upload_sticker_file, create_new_sticker_set, add_sticker_to_set,
# set_sticker_position_in_set and delete_sticker_from_set are tested in the
Expand Down
28 changes: 28 additions & 0 deletions tests/test_callbackquery.py
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,34 @@ def make_assertion(*args, **kwargs):
monkeypatch.setattr(callback_query.bot, 'delete_message', make_assertion)
assert callback_query.delete_message()

def test_pin_message(self, monkeypatch, callback_query):
if callback_query.inline_message_id:
pytest.skip("Can't pin inline messages")

def make_assertion(*args, **kwargs):
_id = callback_query.message.chat_id
try:
return kwargs['chat_id'] == _id
except KeyError:
return args[0] == _id

monkeypatch.setattr(callback_query.bot, 'pin_chat_message', make_assertion)
assert callback_query.pin_message()

def test_unpin_message(self, monkeypatch, callback_query):
if callback_query.inline_message_id:
pytest.skip("Can't unpin inline messages")

def make_assertion(*args, **kwargs):
_id = callback_query.message.chat_id
try:
return kwargs['chat_id'] == _id
except KeyError:
return args[0] == _id

monkeypatch.setattr(callback_query.bot, 'unpin_chat_message', make_assertion)
assert callback_query.unpin_message()

def test_equality(self):
a = CallbackQuery(self.id_, self.from_user, 'chat')
b = CallbackQuery(self.id_, self.from_user, 'chat')
Expand Down
Loading