This commit is contained in:
Poolitzer 2022-09-04 09:15:35 +02:00 committed by GitHub
parent d0c1a957c9
commit dca596dd3d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 421 additions and 36 deletions

View file

@ -20,7 +20,7 @@ We have a vibrant community of developers helping each other in our `Telegram gr
:target: https://pypi.org/project/python-telegram-bot/ :target: https://pypi.org/project/python-telegram-bot/
:alt: Supported Python versions :alt: Supported Python versions
.. image:: https://img.shields.io/badge/Bot%20API-6.1-blue?logo=telegram .. image:: https://img.shields.io/badge/Bot%20API-6.2-blue?logo=telegram
:target: https://core.telegram.org/bots/api-changelog :target: https://core.telegram.org/bots/api-changelog
:alt: Supported Bot API versions :alt: Supported Bot API versions
@ -112,7 +112,7 @@ Installing both ``python-telegram-bot`` and ``python-telegram-bot-raw`` in conju
Telegram API support Telegram API support
==================== ====================
All types and methods of the Telegram Bot API **6.1** are supported. All types and methods of the Telegram Bot API **6.2** are supported.
========== ==========
Installing Installing

View file

@ -20,7 +20,7 @@ We have a vibrant community of developers helping each other in our `Telegram gr
:target: https://pypi.org/project/python-telegram-bot-raw/ :target: https://pypi.org/project/python-telegram-bot-raw/
:alt: Supported Python versions :alt: Supported Python versions
.. image:: https://img.shields.io/badge/Bot%20API-6.1-blue?logo=telegram .. image:: https://img.shields.io/badge/Bot%20API-6.2-blue?logo=telegram
:target: https://core.telegram.org/bots/api-changelog :target: https://core.telegram.org/bots/api-changelog
:alt: Supported Bot API versions :alt: Supported Bot API versions
@ -105,7 +105,7 @@ Installing both ``python-telegram-bot`` and ``python-telegram-bot-raw`` in conju
Telegram API support Telegram API support
==================== ====================
All types and methods of the Telegram Bot API **6.1** are supported. All types and methods of the Telegram Bot API **6.2** are supported.
========== ==========
Installing Installing

View file

@ -1,4 +1,6 @@
sphinx==3.5.4 sphinx==3.5.4
# sphinx breaks because it relies on an removed param from jinja2, so pinning to old version
Jinja2<3.1
sphinx-pypi-upload sphinx-pypi-upload
# When bumping this, make sure to rebuild the dark-mode CSS # When bumping this, make sure to rebuild the dark-mode CSS
# More instructions at source/_static/dark.css # More instructions at source/_static/dark.css

View file

@ -2429,8 +2429,8 @@ class Bot(TelegramObject):
if result.get('file_path') and not is_local_file( # type: ignore[union-attr] if result.get('file_path') and not is_local_file( # type: ignore[union-attr]
result['file_path'] # type: ignore[index] result['file_path'] # type: ignore[index]
): ):
result['file_path'] = '{}/{}'.format( # type: ignore[index] result['file_path'] = ( # type: ignore[index]
self.base_file_url, result['file_path'] # type: ignore[index] f"{self.base_file_url}/" f"{result['file_path']}" # type: ignore[index]
) )
return File.de_json(result, self) # type: ignore[return-value, arg-type] return File.de_json(result, self) # type: ignore[return-value, arg-type]
@ -4813,6 +4813,37 @@ class Bot(TelegramObject):
return StickerSet.de_json(result, self) # type: ignore[return-value, arg-type] return StickerSet.de_json(result, self) # type: ignore[return-value, arg-type]
@log
def get_custom_emoji_stickers(
self,
custom_emoji_ids: List[str],
*,
timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: JSONDict = None,
) -> List[Sticker]:
"""
Use this method to get information about emoji stickers by their identifiers.
.. versionadded:: 13.14
Args:
custom_emoji_ids (List[:obj:`str`]): List of custom emoji identifiers.
At most 200 custom emoji identifiers can be specified.
Keyword Args:
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:
List[:class:`telegram.Sticker`]
Raises:
:class:`telegram.error.TelegramError`
"""
data: JSONDict = {"custom_emoji_ids": custom_emoji_ids}
result = self._post("getCustomEmojiStickers", data, timeout=timeout, api_kwargs=api_kwargs)
return Sticker.de_list(result, self) # type: ignore[return-value, arg-type]
@log @log
def upload_sticker_file( def upload_sticker_file(
self, self,
@ -4871,6 +4902,7 @@ class Bot(TelegramObject):
tgs_sticker: FileInput = None, tgs_sticker: FileInput = None,
api_kwargs: JSONDict = None, api_kwargs: JSONDict = None,
webm_sticker: FileInput = None, webm_sticker: FileInput = None,
sticker_type: str = None,
) -> bool: ) -> bool:
""" """
Use this method to create new sticker set owned by a user. Use this method to create new sticker set owned by a user.
@ -4887,6 +4919,10 @@ class Bot(TelegramObject):
The png_sticker and tgs_sticker argument can be either a file_id, an URL or a file from The png_sticker and tgs_sticker argument can be either a file_id, an URL or a file from
disk ``open(filename, 'rb')`` disk ``open(filename, 'rb')``
.. versionchanged:: 13.14
The parameter ``contains_masks`` has been depreciated as of Bot API 6.2.
Use ``sticker_type`` instead.
Args: Args:
user_id (:obj:`int`): User identifier of created sticker set owner. user_id (:obj:`int`): User identifier of created sticker set owner.
name (:obj:`str`): Short name of sticker set, to be used in t.me/addstickers/ URLs name (:obj:`str`): Short name of sticker set, to be used in t.me/addstickers/ URLs
@ -4924,6 +4960,12 @@ class Bot(TelegramObject):
should be created. should be created.
mask_position (:class:`telegram.MaskPosition`, optional): Position where the mask mask_position (:class:`telegram.MaskPosition`, optional): Position where the mask
should be placed on faces. should be placed on faces.
sticker_type (:obj:`str`, optional): Type of stickers in the set, pass
:attr:`telegram.Sticker.REGULAR` or :attr:`telegram.Sticker.MASK`. Custom emoji
sticker sets can't be created via the Bot API at the moment. By default, a
regular sticker set is created.
.. versionadded:: 13.14
timeout (:obj:`int` | :obj:`float`, optional): If this value is specified, use it as 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 the read timeout from the server (instead of the one specified during
creation of the connection pool). creation of the connection pool).
@ -4951,7 +4993,8 @@ class Bot(TelegramObject):
# We need to_json() instead of to_dict() here, because we're sending a media # We need to_json() instead of to_dict() here, because we're sending a media
# message here, which isn't json dumped by utils.request # message here, which isn't json dumped by utils.request
data['mask_position'] = mask_position.to_json() data['mask_position'] = mask_position.to_json()
if sticker_type is not None:
data['sticker_type'] = sticker_type
result = self._post('createNewStickerSet', data, timeout=timeout, api_kwargs=api_kwargs) result = self._post('createNewStickerSet', data, timeout=timeout, api_kwargs=api_kwargs)
return result # type: ignore[return-value] return result # type: ignore[return-value]
@ -6206,6 +6249,8 @@ class Bot(TelegramObject):
"""Alias for :meth:`unpin_all_chat_messages`""" """Alias for :meth:`unpin_all_chat_messages`"""
getStickerSet = get_sticker_set getStickerSet = get_sticker_set
"""Alias for :meth:`get_sticker_set`""" """Alias for :meth:`get_sticker_set`"""
getCustomEmojiStickers = get_custom_emoji_stickers
"""Alias for :meth:`get_custom_emoji_stickers`"""
uploadStickerFile = upload_sticker_file uploadStickerFile = upload_sticker_file
"""Alias for :meth:`upload_sticker_file`""" """Alias for :meth:`upload_sticker_file`"""
createNewStickerSet = create_new_sticker_set createNewStickerSet = create_new_sticker_set

View file

@ -126,6 +126,11 @@ class Chat(TelegramObject):
:meth:`telegram.Bot.get_chat`. :meth:`telegram.Bot.get_chat`.
.. versionadded:: 13.13 .. versionadded:: 13.13
has_restricted_voice_and_video_messages (:obj:`bool`, optional): :obj:`True`, if the
privacy settings of the other party restrict sending voice and video note messages
in the private chat. Returned only in :meth:`telegram.Bot.get_chat`.
.. versionadded:: 13.14
**kwargs (:obj:`dict`): Arbitrary keyword arguments. **kwargs (:obj:`dict`): Arbitrary keyword arguments.
Attributes: Attributes:
@ -180,6 +185,11 @@ class Chat(TelegramObject):
:meth:`telegram.Bot.get_chat`. :meth:`telegram.Bot.get_chat`.
.. versionadded:: 13.13 .. versionadded:: 13.13
has_restricted_voice_and_video_messages (:obj:`bool`): Optional. :obj:`True`, if the
privacy settings of the other party restrict sending voice and video note messages
in the private chat. Returned only in :meth:`telegram.Bot.get_chat`.
.. versionadded:: 13.14
""" """
__slots__ = ( __slots__ = (
@ -207,6 +217,7 @@ class Chat(TelegramObject):
'has_private_forwards', 'has_private_forwards',
'join_to_send_messages', 'join_to_send_messages',
'join_by_request', 'join_by_request',
'has_restricted_voice_and_video_messages',
'_id_attrs', '_id_attrs',
) )
@ -249,6 +260,7 @@ class Chat(TelegramObject):
has_protected_content: bool = None, has_protected_content: bool = None,
join_to_send_messages: bool = None, join_to_send_messages: bool = None,
join_by_request: bool = None, join_by_request: bool = None,
has_restricted_voice_and_video_messages: bool = None,
**_kwargs: Any, **_kwargs: Any,
): ):
# Required # Required
@ -279,6 +291,7 @@ class Chat(TelegramObject):
self.location = location self.location = location
self.join_to_send_messages = join_to_send_messages self.join_to_send_messages = join_to_send_messages
self.join_by_request = join_by_request self.join_by_request = join_by_request
self.has_restricted_voice_and_video_messages = has_restricted_voice_and_video_messages
self.bot = bot self.bot = bot
self._id_attrs = (self.id,) self._id_attrs = (self.id,)

View file

@ -21,7 +21,7 @@ The following constants were extracted from the
`Telegram Bots API <https://core.telegram.org/bots/api>`_. `Telegram Bots API <https://core.telegram.org/bots/api>`_.
Attributes: Attributes:
BOT_API_VERSION (:obj:`str`): `6.1`. Telegram Bot API version supported by this BOT_API_VERSION (:obj:`str`): `6.2`. Telegram Bot API version supported by this
version of `python-telegram-bot`. Also available as ``telegram.bot_api_version``. version of `python-telegram-bot`. Also available as ``telegram.bot_api_version``.
.. versionadded:: 13.4 .. versionadded:: 13.4
@ -144,6 +144,9 @@ Attributes:
MESSAGEENTITY_SPOILER (:obj:`str`): ``'spoiler'`` MESSAGEENTITY_SPOILER (:obj:`str`): ``'spoiler'``
.. versionadded:: 13.10 .. versionadded:: 13.10
MESSAGEENTITY_CUSTOM_EMOJI (:obj:`str`): ``'custom_emoji'``
.. versionadded:: 13.14
MESSAGEENTITY_ALL_TYPES (List[:obj:`str`]): List of all the types of message entity. MESSAGEENTITY_ALL_TYPES (List[:obj:`str`]): List of all the types of message entity.
:class:`telegram.ParseMode`: :class:`telegram.ParseMode`:
@ -160,6 +163,19 @@ Attributes:
POLL_QUIZ (:obj:`str`): ``'quiz'`` POLL_QUIZ (:obj:`str`): ``'quiz'``
MAX_POLL_QUESTION_LENGTH (:obj:`int`): 300 MAX_POLL_QUESTION_LENGTH (:obj:`int`): 300
MAX_POLL_OPTION_LENGTH (:obj:`int`): 100 MAX_POLL_OPTION_LENGTH (:obj:`int`): 100
:class:`telegram.Sticker`:
Attributes:
STICKER_REGULAR (:obj:`str`)= ``'regular'``
.. versionadded:: 13.14
STICKER_MASK (:obj:`str`) = ``'mask'``
.. versionadded:: 13.14
STICKER_CUSTOM_EMOJI (:obj:`str`) = ``'custom_emoji'``
.. versionadded:: 13.14
:class:`telegram.MaskPosition`: :class:`telegram.MaskPosition`:
@ -247,7 +263,7 @@ Attributes:
""" """
from typing import List from typing import List
BOT_API_VERSION: str = '6.1' BOT_API_VERSION: str = '6.2'
MAX_MESSAGE_LENGTH: int = 4096 MAX_MESSAGE_LENGTH: int = 4096
MAX_CAPTION_LENGTH: int = 1024 MAX_CAPTION_LENGTH: int = 1024
ANONYMOUS_ADMIN_ID: int = 1087968824 ANONYMOUS_ADMIN_ID: int = 1087968824
@ -325,6 +341,7 @@ MESSAGEENTITY_TEXT_MENTION: str = 'text_mention'
MESSAGEENTITY_UNDERLINE: str = 'underline' MESSAGEENTITY_UNDERLINE: str = 'underline'
MESSAGEENTITY_STRIKETHROUGH: str = 'strikethrough' MESSAGEENTITY_STRIKETHROUGH: str = 'strikethrough'
MESSAGEENTITY_SPOILER: str = 'spoiler' MESSAGEENTITY_SPOILER: str = 'spoiler'
MESSAGEENTITY_CUSTOM_EMOJI: str = 'custom_emoji'
MESSAGEENTITY_ALL_TYPES: List[str] = [ MESSAGEENTITY_ALL_TYPES: List[str] = [
MESSAGEENTITY_MENTION, MESSAGEENTITY_MENTION,
MESSAGEENTITY_HASHTAG, MESSAGEENTITY_HASHTAG,
@ -342,6 +359,7 @@ MESSAGEENTITY_ALL_TYPES: List[str] = [
MESSAGEENTITY_UNDERLINE, MESSAGEENTITY_UNDERLINE,
MESSAGEENTITY_STRIKETHROUGH, MESSAGEENTITY_STRIKETHROUGH,
MESSAGEENTITY_SPOILER, MESSAGEENTITY_SPOILER,
MESSAGEENTITY_CUSTOM_EMOJI,
] ]
PARSEMODE_MARKDOWN: str = 'Markdown' PARSEMODE_MARKDOWN: str = 'Markdown'
@ -353,6 +371,10 @@ POLL_QUIZ: str = 'quiz'
MAX_POLL_QUESTION_LENGTH: int = 300 MAX_POLL_QUESTION_LENGTH: int = 300
MAX_POLL_OPTION_LENGTH: int = 100 MAX_POLL_OPTION_LENGTH: int = 100
STICKER_REGULAR: str = "regular"
STICKER_MASK: str = "mask"
STICKER_CUSTOM_EMOJI: str = "custom_emoji"
STICKER_FOREHEAD: str = 'forehead' STICKER_FOREHEAD: str = 'forehead'
STICKER_EYES: str = 'eyes' STICKER_EYES: str = 'eyes'
STICKER_MOUTH: str = 'mouth' STICKER_MOUTH: str = 'mouth'

View file

@ -56,7 +56,7 @@ class TelegramError(Exception):
self.message = msg self.message = msg
def __str__(self) -> str: def __str__(self) -> str:
return '%s' % self.message return f'{self.message}'
def __reduce__(self) -> Tuple[type, Tuple[str]]: def __reduce__(self) -> Tuple[type, Tuple[str]]:
return self.__class__, (self.message,) return self.__class__, (self.message,)

View file

@ -51,6 +51,11 @@ class Sticker(TelegramObject):
is_video (:obj:`bool`): :obj:`True`, if the sticker is a video sticker. is_video (:obj:`bool`): :obj:`True`, if the sticker is a video sticker.
.. versionadded:: 13.11 .. versionadded:: 13.11
type (:obj:`str`): Type of the sticker. Currently one of :attr:`REGULAR`,
:attr:`MASK`, :attr:`CUSTOM_EMOJI`. The type of the sticker is independent from its
format, which is determined by the fields :attr:`is_animated` and :attr:`is_video`.
.. versionadded:: 13.14
thumb (:class:`telegram.PhotoSize`, optional): Sticker thumbnail in the .WEBP or .JPG thumb (:class:`telegram.PhotoSize`, optional): Sticker thumbnail in the .WEBP or .JPG
format. format.
emoji (:obj:`str`, optional): Emoji associated with the sticker emoji (:obj:`str`, optional): Emoji associated with the sticker
@ -60,10 +65,14 @@ class Sticker(TelegramObject):
position where the mask should be placed. position where the mask should be placed.
file_size (:obj:`int`, optional): File size. file_size (:obj:`int`, optional): File size.
bot (:class:`telegram.Bot`, optional): The Bot to use for instance methods. bot (:class:`telegram.Bot`, optional): The Bot to use for instance methods.
premium_animation (:class:`telegram.File`, optional): Premium animation for the sticker, premium_animation (:class:`telegram.File`, optional): For premium regular stickers,
if the sticker is premium. premium animation for the sticker.
.. versionadded:: 13.13 .. versionadded:: 13.13
custom_emoji (:obj:`str`, optional): For custom emoji stickers, unique identifier of the
custom emoji.
.. versionadded:: 13.14
**kwargs (obj:`dict`): Arbitrary keyword arguments. **kwargs (obj:`dict`): Arbitrary keyword arguments.
Attributes: Attributes:
@ -77,6 +86,11 @@ class Sticker(TelegramObject):
is_video (:obj:`bool`): :obj:`True`, if the sticker is a video sticker. is_video (:obj:`bool`): :obj:`True`, if the sticker is a video sticker.
.. versionadded:: 13.11 .. versionadded:: 13.11
type (:obj:`str`): Type of the sticker. Currently one of :attr:`REGULAR`,
:attr:`MASK`, :attr:`CUSTOM_EMOJI`. The type of the sticker is independent from its
format, which is determined by the fields :attr:`is_animated` and :attr:`is_video`.
.. versionadded:: 13.14
thumb (:class:`telegram.PhotoSize`): Optional. Sticker thumbnail in the .webp or .jpg thumb (:class:`telegram.PhotoSize`): Optional. Sticker thumbnail in the .webp or .jpg
format. format.
emoji (:obj:`str`): Optional. Emoji associated with the sticker. emoji (:obj:`str`): Optional. Emoji associated with the sticker.
@ -84,10 +98,14 @@ class Sticker(TelegramObject):
mask_position (:class:`telegram.MaskPosition`): Optional. For mask stickers, the position mask_position (:class:`telegram.MaskPosition`): Optional. For mask stickers, the position
where the mask should be placed. where the mask should be placed.
file_size (:obj:`int`): Optional. File size. file_size (:obj:`int`): Optional. File size.
premium_animation (:class:`telegram.File`): Optional. Premium animation for the sticker, premium_animation (:class:`telegram.File`): Optional. For premium regular stickers,
if the sticker is premium. premium animation for the sticker.
.. versionadded:: 13.13 .. versionadded:: 13.13
custom_emoji (:obj:`str`): Optional. For custom emoji stickers, unique identifier of the
custom emoji.
.. versionadded:: 13.14
bot (:class:`telegram.Bot`): Optional. The Bot to use for instance methods. bot (:class:`telegram.Bot`): Optional. The Bot to use for instance methods.
""" """
@ -106,6 +124,8 @@ class Sticker(TelegramObject):
'file_unique_id', 'file_unique_id',
'emoji', 'emoji',
'premium_animation', 'premium_animation',
'type',
'custom_emoji_id',
'_id_attrs', '_id_attrs',
) )
@ -117,6 +137,7 @@ class Sticker(TelegramObject):
height: int, height: int,
is_animated: bool, is_animated: bool,
is_video: bool, is_video: bool,
type: str, # pylint: disable=redefined-builtin
thumb: PhotoSize = None, thumb: PhotoSize = None,
emoji: str = None, emoji: str = None,
file_size: int = None, file_size: int = None,
@ -124,6 +145,7 @@ class Sticker(TelegramObject):
mask_position: 'MaskPosition' = None, mask_position: 'MaskPosition' = None,
bot: 'Bot' = None, bot: 'Bot' = None,
premium_animation: 'File' = None, premium_animation: 'File' = None,
custom_emoji_id: str = None,
**_kwargs: Any, **_kwargs: Any,
): ):
# Required # Required
@ -133,6 +155,7 @@ class Sticker(TelegramObject):
self.height = int(height) self.height = int(height)
self.is_animated = is_animated self.is_animated = is_animated
self.is_video = is_video self.is_video = is_video
self.type = type
# Optionals # Optionals
self.thumb = thumb self.thumb = thumb
self.emoji = emoji self.emoji = emoji
@ -141,6 +164,7 @@ class Sticker(TelegramObject):
self.mask_position = mask_position self.mask_position = mask_position
self.bot = bot self.bot = bot
self.premium_animation = premium_animation self.premium_animation = premium_animation
self.custom_emoji_id = custom_emoji_id
self._id_attrs = (self.file_unique_id,) self._id_attrs = (self.file_unique_id,)
@ -178,6 +202,22 @@ class Sticker(TelegramObject):
""" """
return self.bot.get_file(file_id=self.file_id, timeout=timeout, api_kwargs=api_kwargs) return self.bot.get_file(file_id=self.file_id, timeout=timeout, api_kwargs=api_kwargs)
REGULAR: ClassVar[str] = constants.STICKER_REGULAR
""":const:`telegram.constants.STICKER_REGULAR`
.. versionadded:: 13.14
"""
MASK: ClassVar[str] = constants.STICKER_MASK
""":const:`telegram.constants.STICKER_MASK`
.. versionadded:: 13.14
"""
CUSTOM_EMOJI: ClassVar[str] = constants.STICKER_CUSTOM_EMOJI
""":const:`telegram.constants.STICKER_CUSTOM_EMOJI`
.. versionadded:: 13.14
"""
class StickerSet(TelegramObject): class StickerSet(TelegramObject):
"""This object represents a sticker set. """This object represents a sticker set.
@ -190,6 +230,10 @@ class StickerSet(TelegramObject):
arguments had to be changed. Use keyword arguments to make sure that the arguments are arguments had to be changed. Use keyword arguments to make sure that the arguments are
passed correctly. passed correctly.
.. versionchanged:: 13.14:
The parameter ``contains_masks`` has been depreciated as of Bot API 6.2.
Use ``sticker_type`` instead.
Args: Args:
name (:obj:`str`): Sticker set name. name (:obj:`str`): Sticker set name.
title (:obj:`str`): Sticker set title. title (:obj:`str`): Sticker set title.
@ -199,6 +243,11 @@ class StickerSet(TelegramObject):
.. versionadded:: 13.11 .. versionadded:: 13.11
contains_masks (:obj:`bool`): :obj:`True`, if the sticker set contains masks. contains_masks (:obj:`bool`): :obj:`True`, if the sticker set contains masks.
stickers (List[:class:`telegram.Sticker`]): List of all set stickers. stickers (List[:class:`telegram.Sticker`]): List of all set stickers.
sticker_type (:obj:`str`, optional): Type of stickers in the set, currently one of
:attr:`telegram.Sticker.REGULAR`, :attr:`telegram.Sticker.MASK`,
:attr:`telegram.Sticker.CUSTOM_EMOJI`.
.. versionadded:: 13.14
thumb (:class:`telegram.PhotoSize`, optional): Sticker set thumbnail in the ``.WEBP``, thumb (:class:`telegram.PhotoSize`, optional): Sticker set thumbnail in the ``.WEBP``,
``.TGS``, or ``.WEBM`` format. ``.TGS``, or ``.WEBM`` format.
@ -211,6 +260,9 @@ class StickerSet(TelegramObject):
.. versionadded:: 13.11 .. versionadded:: 13.11
contains_masks (:obj:`bool`): :obj:`True`, if the sticker set contains masks. contains_masks (:obj:`bool`): :obj:`True`, if the sticker set contains masks.
stickers (List[:class:`telegram.Sticker`]): List of all set stickers. stickers (List[:class:`telegram.Sticker`]): List of all set stickers.
sticker_type (:obj:`str`): Optional. Type of stickers in the set.
.. versionadded:: 13.14
thumb (:class:`telegram.PhotoSize`): Optional. Sticker set thumbnail in the ``.WEBP``, thumb (:class:`telegram.PhotoSize`): Optional. Sticker set thumbnail in the ``.WEBP``,
``.TGS`` or ``.WEBM`` format. ``.TGS`` or ``.WEBM`` format.
@ -224,6 +276,7 @@ class StickerSet(TelegramObject):
'title', 'title',
'stickers', 'stickers',
'name', 'name',
'sticker_type',
'_id_attrs', '_id_attrs',
) )
@ -236,6 +289,7 @@ class StickerSet(TelegramObject):
stickers: List[Sticker], stickers: List[Sticker],
is_video: bool, is_video: bool,
thumb: PhotoSize = None, thumb: PhotoSize = None,
sticker_type: str = None,
**_kwargs: Any, **_kwargs: Any,
): ):
self.name = name self.name = name
@ -246,6 +300,7 @@ class StickerSet(TelegramObject):
self.stickers = stickers self.stickers = stickers
# Optionals # Optionals
self.thumb = thumb self.thumb = thumb
self.sticker_type = sticker_type
self._id_attrs = (self.name,) self._id_attrs = (self.name,)

View file

@ -407,6 +407,8 @@ class Message(TelegramObject):
to the message. to the message.
bot (:class:`telegram.Bot`): Optional. The Bot to use for instance methods. bot (:class:`telegram.Bot`): Optional. The Bot to use for instance methods.
.. |custom_emoji_formatting_note| replace:: Custom emoji entities will currently be ignored
by this function. Instead, the supplied replacement for the emoji will be used.
""" """
# fmt: on # fmt: on
@ -2752,6 +2754,9 @@ class Message(TelegramObject):
Use this if you want to retrieve the message text with the entities formatted as HTML in Use this if you want to retrieve the message text with the entities formatted as HTML in
the same way the original message was formatted. the same way the original message was formatted.
Note:
|custom_emoji_formatting_note|
.. versionchanged:: 13.10 .. versionchanged:: 13.10
Spoiler entities are now formatted as HTML. Spoiler entities are now formatted as HTML.
@ -2768,6 +2773,9 @@ class Message(TelegramObject):
Use this if you want to retrieve the message text with the entities formatted as HTML. Use this if you want to retrieve the message text with the entities formatted as HTML.
This also formats :attr:`telegram.MessageEntity.URL` as a hyperlink. This also formats :attr:`telegram.MessageEntity.URL` as a hyperlink.
Note:
|custom_emoji_formatting_note|
.. versionchanged:: 13.10 .. versionchanged:: 13.10
Spoiler entities are now formatted as HTML. Spoiler entities are now formatted as HTML.
@ -2785,6 +2793,9 @@ class Message(TelegramObject):
Use this if you want to retrieve the message caption with the caption entities formatted as Use this if you want to retrieve the message caption with the caption entities formatted as
HTML in the same way the original message was formatted. HTML in the same way the original message was formatted.
Note:
|custom_emoji_formatting_note|
.. versionchanged:: 13.10 .. versionchanged:: 13.10
Spoiler entities are now formatted as HTML. Spoiler entities are now formatted as HTML.
@ -2801,6 +2812,9 @@ class Message(TelegramObject):
Use this if you want to retrieve the message caption with the caption entities formatted as Use this if you want to retrieve the message caption with the caption entities formatted as
HTML. This also formats :attr:`telegram.MessageEntity.URL` as a hyperlink. HTML. This also formats :attr:`telegram.MessageEntity.URL` as a hyperlink.
Note:
|custom_emoji_formatting_note|
.. versionchanged:: 13.10 .. versionchanged:: 13.10
Spoiler entities are now formatted as HTML. Spoiler entities are now formatted as HTML.
@ -2983,8 +2997,10 @@ class Message(TelegramObject):
in the same way the original message was formatted. in the same way the original message was formatted.
Note: Note:
:attr:`telegram.ParseMode.MARKDOWN` is is a legacy mode, retained by Telegram for * :attr:`telegram.ParseMode.MARKDOWN` is a legacy mode, retained by Telegram for
backward compatibility. You should use :meth:`text_markdown_v2` instead. backward compatibility. You should use :meth:`text_markdown_v2` instead.
* |custom_emoji_formatting_note|
Returns: Returns:
:obj:`str`: Message text with entities formatted as Markdown. :obj:`str`: Message text with entities formatted as Markdown.
@ -3004,6 +3020,9 @@ class Message(TelegramObject):
Use this if you want to retrieve the message text with the entities formatted as Markdown Use this if you want to retrieve the message text with the entities formatted as Markdown
in the same way the original message was formatted. in the same way the original message was formatted.
Note:
|custom_emoji_formatting_note|
.. versionchanged:: 13.10 .. versionchanged:: 13.10
Spoiler entities are now formatted as Markdown V2. Spoiler entities are now formatted as Markdown V2.
@ -3021,8 +3040,10 @@ class Message(TelegramObject):
This also formats :attr:`telegram.MessageEntity.URL` as a hyperlink. This also formats :attr:`telegram.MessageEntity.URL` as a hyperlink.
Note: Note:
:attr:`telegram.ParseMode.MARKDOWN` is is a legacy mode, retained by Telegram for * :attr:`telegram.ParseMode.MARKDOWN` is a legacy mode, retained by Telegram for
backward compatibility. You should use :meth:`text_markdown_v2_urled` instead. backward compatibility. You should use :meth:`text_markdown_v2_urled` instead.
* |custom_emoji_formatting_note|
Returns: Returns:
:obj:`str`: Message text with entities formatted as Markdown. :obj:`str`: Message text with entities formatted as Markdown.
@ -3042,6 +3063,9 @@ class Message(TelegramObject):
Use this if you want to retrieve the message text with the entities formatted as Markdown. Use this if you want to retrieve the message text with the entities formatted as Markdown.
This also formats :attr:`telegram.MessageEntity.URL` as a hyperlink. This also formats :attr:`telegram.MessageEntity.URL` as a hyperlink.
Note:
|custom_emoji_formatting_note|
.. versionchanged:: 13.10 .. versionchanged:: 13.10
Spoiler entities are now formatted as Markdown V2. Spoiler entities are now formatted as Markdown V2.
@ -3059,8 +3083,10 @@ class Message(TelegramObject):
Markdown in the same way the original message was formatted. Markdown in the same way the original message was formatted.
Note: Note:
:attr:`telegram.ParseMode.MARKDOWN` is is a legacy mode, retained by Telegram for * :attr:`telegram.ParseMode.MARKDOWN` is a legacy mode, retained by Telegram for
backward compatibility. You should use :meth:`caption_markdown_v2` instead. backward compatibility. You should use :meth:`caption_markdown_v2` instead.
* |custom_emoji_formatting_note|
Returns: Returns:
:obj:`str`: Message caption with caption entities formatted as Markdown. :obj:`str`: Message caption with caption entities formatted as Markdown.
@ -3080,6 +3106,9 @@ class Message(TelegramObject):
Use this if you want to retrieve the message caption with the caption entities formatted as Use this if you want to retrieve the message caption with the caption entities formatted as
Markdown in the same way the original message was formatted. Markdown in the same way the original message was formatted.
Note:
|custom_emoji_formatting_note|
.. versionchanged:: 13.10 .. versionchanged:: 13.10
Spoiler entities are now formatted as Markdown V2. Spoiler entities are now formatted as Markdown V2.
@ -3099,8 +3128,10 @@ class Message(TelegramObject):
Markdown. This also formats :attr:`telegram.MessageEntity.URL` as a hyperlink. Markdown. This also formats :attr:`telegram.MessageEntity.URL` as a hyperlink.
Note: Note:
:attr:`telegram.ParseMode.MARKDOWN` is is a legacy mode, retained by Telegram for * :attr:`telegram.ParseMode.MARKDOWN` is a legacy mode, retained by Telegram for
backward compatibility. You should use :meth:`caption_markdown_v2_urled` instead. backward compatibility. You should use :meth:`caption_markdown_v2_urled` instead.
* |custom_emoji_formatting_note|
Returns: Returns:
:obj:`str`: Message caption with caption entities formatted as Markdown. :obj:`str`: Message caption with caption entities formatted as Markdown.
@ -3120,6 +3151,9 @@ class Message(TelegramObject):
Use this if you want to retrieve the message caption with the caption entities formatted as Use this if you want to retrieve the message caption with the caption entities formatted as
Markdown. This also formats :attr:`telegram.MessageEntity.URL` as a hyperlink. Markdown. This also formats :attr:`telegram.MessageEntity.URL` as a hyperlink.
Note:
|custom_emoji_formatting_note|
.. versionchanged:: 13.10 .. versionchanged:: 13.10
Spoiler entities are now formatted as Markdown V2. Spoiler entities are now formatted as Markdown V2.

View file

@ -40,7 +40,10 @@ class MessageEntity(TelegramObject):
bot_command, url, email, phone_number, bold (bold text), italic (italic text), bot_command, url, email, phone_number, bold (bold text), italic (italic text),
strikethrough, spoiler (spoiler message), code (monowidth string), pre strikethrough, spoiler (spoiler message), code (monowidth string), pre
(monowidth block), text_link (for clickable text URLs), text_mention (monowidth block), text_link (for clickable text URLs), text_mention
(for users without usernames). (for users without usernames), custom_emoji (for inline custom emoji stickers).
.. versionadded:: 13.14
added inline custom emoji
offset (:obj:`int`): Offset in UTF-16 code units to the start of the entity. offset (:obj:`int`): Offset in UTF-16 code units to the start of the entity.
length (:obj:`int`): Length of the entity in UTF-16 code units. length (:obj:`int`): Length of the entity in UTF-16 code units.
url (:obj:`str`, optional): For :attr:`TEXT_LINK` only, url that will be opened after url (:obj:`str`, optional): For :attr:`TEXT_LINK` only, url that will be opened after
@ -49,6 +52,11 @@ class MessageEntity(TelegramObject):
user. user.
language (:obj:`str`, optional): For :attr:`PRE` only, the programming language of language (:obj:`str`, optional): For :attr:`PRE` only, the programming language of
the entity text. the entity text.
custom_emoji_id (:obj:`str`, optional): For :attr:`CUSTOM_EMOJI` only, unique identifier
of the custom emoji. Use :meth:`telegram.Bot.get_custom_emoji_stickers` to get full
information about the sticker.
.. versionadded:: 13.14
Attributes: Attributes:
type (:obj:`str`): Type of the entity. type (:obj:`str`): Type of the entity.
@ -57,10 +65,22 @@ class MessageEntity(TelegramObject):
url (:obj:`str`): Optional. Url that will be opened after user taps on the text. url (:obj:`str`): Optional. Url that will be opened after user taps on the text.
user (:class:`telegram.User`): Optional. The mentioned user. user (:class:`telegram.User`): Optional. The mentioned user.
language (:obj:`str`): Optional. Programming language of the entity text. language (:obj:`str`): Optional. Programming language of the entity text.
custom_emoji_id (:obj:`str`): Optional. Unique identifier of the custom emoji.
.. versionadded:: 13.14
""" """
__slots__ = ('length', 'url', 'user', 'type', 'language', 'offset', '_id_attrs') __slots__ = (
'length',
'url',
'user',
'type',
'language',
'offset',
'custom_emoji_id',
'_id_attrs',
)
def __init__( def __init__(
self, self,
@ -70,6 +90,7 @@ class MessageEntity(TelegramObject):
url: str = None, url: str = None,
user: User = None, user: User = None,
language: str = None, language: str = None,
custom_emoji_id: str = None,
**_kwargs: Any, **_kwargs: Any,
): ):
# Required # Required
@ -80,6 +101,7 @@ class MessageEntity(TelegramObject):
self.url = url self.url = url
self.user = user self.user = user
self.language = language self.language = language
self.custom_emoji_id = custom_emoji_id
self._id_attrs = (self.type, self.offset, self.length) self._id_attrs = (self.type, self.offset, self.length)
@ -130,6 +152,11 @@ class MessageEntity(TelegramObject):
.. versionadded:: 13.10 .. versionadded:: 13.10
""" """
CUSTOM_EMOJI: ClassVar[str] = constants.MESSAGEENTITY_CUSTOM_EMOJI
""":const:`telegram.constants.MESSAGEENTITY_CUSTOM_EMOJI`
.. versionadded:: 13.14
"""
ALL_TYPES: ClassVar[List[str]] = constants.MESSAGEENTITY_ALL_TYPES ALL_TYPES: ClassVar[List[str]] = constants.MESSAGEENTITY_ALL_TYPES
""":const:`telegram.constants.MESSAGEENTITY_ALL_TYPES`\n """:const:`telegram.constants.MESSAGEENTITY_ALL_TYPES`\n
List of all the types""" List of all the types"""

View file

@ -2072,8 +2072,8 @@ class TestBot:
assert bot.unpin_all_chat_messages(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, # 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 # set_sticker_position_in_set, delete_sticker_from_set and get_custom_emoji_stickers
# test_sticker module. # are tested in the test_sticker module.
def test_timeout_propagation_explicit(self, monkeypatch, bot, chat_id): def test_timeout_propagation_explicit(self, monkeypatch, bot, chat_id):

View file

@ -45,6 +45,7 @@ def chat(bot):
has_protected_content=True, has_protected_content=True,
join_to_send_messages=True, join_to_send_messages=True,
join_by_request=True, join_by_request=True,
has_restricted_voice_and_video_messages=True,
) )
@ -70,6 +71,7 @@ class TestChat:
has_private_forwards = True has_private_forwards = True
join_to_send_messages = True join_to_send_messages = True
join_by_request = True join_by_request = True
has_restricted_voice_and_video_messages = True
def test_slot_behaviour(self, chat, recwarn, mro_slots): def test_slot_behaviour(self, chat, recwarn, mro_slots):
for attr in chat.__slots__: for attr in chat.__slots__:
@ -98,6 +100,9 @@ class TestChat:
'location': self.location.to_dict(), 'location': self.location.to_dict(),
'join_to_send_messages': self.join_to_send_messages, 'join_to_send_messages': self.join_to_send_messages,
'join_by_request': self.join_by_request, 'join_by_request': self.join_by_request,
'has_restricted_voice_and_video_messages': (
self.has_restricted_voice_and_video_messages
),
} }
chat = Chat.de_json(json_dict, bot) chat = Chat.de_json(json_dict, bot)
@ -119,6 +124,10 @@ class TestChat:
assert chat.location.address == self.location.address assert chat.location.address == self.location.address
assert chat.join_to_send_messages == self.join_to_send_messages assert chat.join_to_send_messages == self.join_to_send_messages
assert chat.join_by_request == self.join_by_request assert chat.join_by_request == self.join_by_request
assert (
chat.has_restricted_voice_and_video_messages
== self.has_restricted_voice_and_video_messages
)
def test_to_dict(self, chat): def test_to_dict(self, chat):
chat_dict = chat.to_dict() chat_dict = chat.to_dict()
@ -139,6 +148,10 @@ class TestChat:
assert chat_dict['location'] == chat.location.to_dict() assert chat_dict['location'] == chat.location.to_dict()
assert chat_dict["join_to_send_messages"] == chat.join_to_send_messages assert chat_dict["join_to_send_messages"] == chat.join_to_send_messages
assert chat_dict["join_by_request"] == chat.join_by_request assert chat_dict["join_by_request"] == chat.join_by_request
assert (
chat_dict["has_restricted_voice_and_video_messages"]
== chat.has_restricted_voice_and_video_messages
)
def test_link(self, chat): def test_link(self, chat):
assert chat.link == f'https://t.me/{chat.username}' assert chat.link == f'https://t.me/{chat.username}'

View file

@ -272,7 +272,7 @@ class TestHelpers:
test_message.text = None test_message.text = None
test_message = build_test_message( test_message = build_test_message(
sticker=Sticker('sticker_id', 'unique_id', 50, 50, False, False) sticker=Sticker('sticker_id', 'unique_id', 50, 50, False, False, Sticker.REGULAR)
) )
assert helpers.effective_message_type(test_message) == 'sticker' assert helpers.effective_message_type(test_message) == 'sticker'
test_message.sticker = None test_message.sticker = None

View file

@ -110,7 +110,7 @@ def message(bot):
) )
}, },
{'photo': [PhotoSize('photo_id', 'unique_id', 50, 50)], 'caption': 'photo_file'}, {'photo': [PhotoSize('photo_id', 'unique_id', 50, 50)], 'caption': 'photo_file'},
{'sticker': Sticker('sticker_id', 'unique_id', 50, 50, True, False)}, {'sticker': Sticker('sticker_id', 'unique_id', 50, 50, True, False, Sticker.REGULAR)},
{'video': Video('video_id', 'unique_id', 12, 12, 12), 'caption': 'video_file'}, {'video': Video('video_id', 'unique_id', 12, 12, 12), 'caption': 'video_file'},
{'voice': Voice('voice_id', 'unique_id', 5)}, {'voice': Voice('voice_id', 'unique_id', 5)},
{'video_note': VideoNote('video_note_id', 'unique_id', 20, 12)}, {'video_note': VideoNote('video_note_id', 'unique_id', 20, 12)},
@ -522,6 +522,36 @@ class TestMessage:
) )
assert expected == message.text_markdown assert expected == message.text_markdown
@pytest.mark.parametrize(
"type_",
argvalues=[
"text_html",
"text_html_urled",
"text_markdown",
"text_markdown_urled",
"text_markdown_v2",
"text_markdown_v2_urled",
],
)
def test_text_custom_emoji(self, type_):
text = "Look a custom emoji: 😎"
expected = "Look a custom emoji: 😎"
emoji_entity = MessageEntity(
type=MessageEntity.CUSTOM_EMOJI,
offset=21,
length=2,
custom_emoji_id="5472409228461217725",
)
message = Message(
1,
from_user=self.from_user,
date=self.date,
chat=self.chat,
text=text,
entities=[emoji_entity],
)
assert expected == message[type_]
def test_caption_html_simple(self): def test_caption_html_simple(self):
test_html_string = ( test_html_string = (
'<u>Test</u> for &lt;<b>bold</b>, <i>ita_lic</i>, ' '<u>Test</u> for &lt;<b>bold</b>, <i>ita_lic</i>, '
@ -631,6 +661,36 @@ class TestMessage:
) )
assert expected == message.caption_markdown assert expected == message.caption_markdown
@pytest.mark.parametrize(
"type_",
argvalues=[
"caption_html",
"caption_html_urled",
"caption_markdown",
"caption_markdown_urled",
"caption_markdown_v2",
"caption_markdown_v2_urled",
],
)
def test_caption_custom_emoji(self, type_):
caption = "Look a custom emoji: 😎"
expected = "Look a custom emoji: 😎"
emoji_entity = MessageEntity(
type=MessageEntity.CUSTOM_EMOJI,
offset=21,
length=2,
custom_emoji_id="5472409228461217725",
)
message = Message(
1,
from_user=self.from_user,
date=self.date,
chat=self.chat,
caption=caption,
caption_entities=[emoji_entity],
)
assert expected == message[type_]
def test_parse_entities_url_emoji(self): def test_parse_entities_url_emoji(self):
url = b'http://github.com/?unicode=\\u2713\\U0001f469'.decode('unicode-escape') url = b'http://github.com/?unicode=\\u2713\\U0001f469'.decode('unicode-escape')
text = 'some url' text = 'some url'

View file

@ -102,6 +102,8 @@ def check_method(h4):
ignored |= {'current_offset'} # Added for ease of use ignored |= {'current_offset'} # Added for ease of use
elif name == 'promoteChatMember': elif name == 'promoteChatMember':
ignored |= {'can_manage_voice_chats'} # for backwards compatibility ignored |= {'can_manage_voice_chats'} # for backwards compatibility
elif name == 'createNewStickerSet':
ignored |= {'contains_masks'} # for backwards compatibility
assert (sig.parameters.keys() ^ checked) - ignored == set() assert (sig.parameters.keys() ^ checked) - ignored == set()
@ -195,7 +197,8 @@ def check_object(h4):
'voice_chat_scheduled', 'voice_chat_scheduled',
'voice_chat_started', 'voice_chat_started',
} }
elif name == 'StickerSet':
ignored |= {'contains_masks'} # for backwards compatibility
assert (sig.parameters.keys() ^ checked) - ignored == set() assert (sig.parameters.keys() ^ checked) - ignored == set()

View file

@ -473,7 +473,15 @@ class TestPhoto:
b = PhotoSize('', photo.file_unique_id, self.width, self.height) b = PhotoSize('', photo.file_unique_id, self.width, self.height)
c = PhotoSize(photo.file_id, photo.file_unique_id, 0, 0) c = PhotoSize(photo.file_id, photo.file_unique_id, 0, 0)
d = PhotoSize('', '', self.width, self.height) d = PhotoSize('', '', self.width, self.height)
e = Sticker(photo.file_id, photo.file_unique_id, self.width, self.height, False, False) e = Sticker(
photo.file_id,
photo.file_unique_id,
self.width,
self.height,
False,
False,
Sticker.REGULAR,
)
assert a == b assert a == b
assert hash(a) == hash(b) assert hash(a) == hash(b)

View file

@ -83,6 +83,8 @@ class TestSticker:
thumb_width = 319 thumb_width = 319
thumb_height = 320 thumb_height = 320
thumb_file_size = 21472 thumb_file_size = 21472
type = Sticker.REGULAR
custom_emoji_id = "ThisIsSuchACustomEmojiID"
sticker_file_id = '5a3128a4d2a04750b5b58397f3b5e812' sticker_file_id = '5a3128a4d2a04750b5b58397f3b5e812'
sticker_file_unique_id = 'adc3145fd2e84d95b64d68eaa22aa33e' sticker_file_unique_id = 'adc3145fd2e84d95b64d68eaa22aa33e'
@ -119,6 +121,7 @@ class TestSticker:
assert sticker.thumb.width == self.thumb_width assert sticker.thumb.width == self.thumb_width
assert sticker.thumb.height == self.thumb_height assert sticker.thumb.height == self.thumb_height
assert sticker.thumb.file_size == self.thumb_file_size assert sticker.thumb.file_size == self.thumb_file_size
assert sticker.type == self.type
# we need to be a premium TG user to send a premium sticker, so the below is not tested # we need to be a premium TG user to send a premium sticker, so the below is not tested
# assert sticker.premium_animation == self.premium_animation # assert sticker.premium_animation == self.premium_animation
@ -150,6 +153,7 @@ class TestSticker:
assert message.sticker.thumb.height == sticker.thumb.height assert message.sticker.thumb.height == sticker.thumb.height
assert message.sticker.thumb.file_size == sticker.thumb.file_size assert message.sticker.thumb.file_size == sticker.thumb.file_size
assert message.has_protected_content assert message.has_protected_content
assert message.sticker.type == sticker.type
@flaky(3, 1) @flaky(3, 1)
def test_get_and_download(self, bot, sticker): def test_get_and_download(self, bot, sticker):
@ -192,6 +196,7 @@ class TestSticker:
assert message.sticker.is_animated == sticker.is_animated assert message.sticker.is_animated == sticker.is_animated
assert message.sticker.is_video == sticker.is_video assert message.sticker.is_video == sticker.is_video
assert message.sticker.file_size == sticker.file_size assert message.sticker.file_size == sticker.file_size
assert message.sticker.type == sticker.type
assert isinstance(message.sticker.thumb, PhotoSize) assert isinstance(message.sticker.thumb, PhotoSize)
assert isinstance(message.sticker.thumb.file_id, str) assert isinstance(message.sticker.thumb.file_id, str)
@ -214,6 +219,8 @@ class TestSticker:
'emoji': self.emoji, 'emoji': self.emoji,
'file_size': self.file_size, 'file_size': self.file_size,
'premium_animation': self.premium_animation.to_dict(), 'premium_animation': self.premium_animation.to_dict(),
'type': self.type,
'custom_emoji_id': self.custom_emoji_id,
} }
json_sticker = Sticker.de_json(json_dict, bot) json_sticker = Sticker.de_json(json_dict, bot)
@ -227,6 +234,8 @@ class TestSticker:
assert json_sticker.file_size == self.file_size assert json_sticker.file_size == self.file_size
assert json_sticker.thumb == sticker.thumb assert json_sticker.thumb == sticker.thumb
assert json_sticker.premium_animation == self.premium_animation assert json_sticker.premium_animation == self.premium_animation
assert json_sticker.type == self.type
assert json_sticker.custom_emoji_id == self.custom_emoji_id
def test_send_with_sticker(self, monkeypatch, bot, chat_id, sticker): def test_send_with_sticker(self, monkeypatch, bot, chat_id, sticker):
def test(url, data, **kwargs): def test(url, data, **kwargs):
@ -297,6 +306,7 @@ class TestSticker:
assert sticker_dict['is_video'] == sticker.is_video assert sticker_dict['is_video'] == sticker.is_video
assert sticker_dict['file_size'] == sticker.file_size assert sticker_dict['file_size'] == sticker.file_size
assert sticker_dict['thumb'] == sticker.thumb.to_dict() assert sticker_dict['thumb'] == sticker.thumb.to_dict()
assert sticker_dict["type"] == sticker.type
@flaky(3, 1) @flaky(3, 1)
def test_error_send_empty_file(self, bot, chat_id): def test_error_send_empty_file(self, bot, chat_id):
@ -330,6 +340,16 @@ class TestSticker:
} }
assert premium_sticker.premium_animation.to_dict() == premium_sticker_dict assert premium_sticker.premium_animation.to_dict() == premium_sticker_dict
@flaky(3, 1)
def test_custom_emoji(self, bot):
# testing custom emoji stickers is as much of an annoyance as the premium animation, see
# in test_premium_animation
custom_emoji_set = bot.get_sticker_set("PTBStaticEmojiTestPack")
# the first one to appear here is a sticker with unique file id of AQADjBsAAkKD0Uty
# this could change in the future ofc.
custom_emoji_sticker = custom_emoji_set.stickers[0]
assert custom_emoji_sticker.custom_emoji_id == "6046140249875156202"
def test_equality(self, sticker): def test_equality(self, sticker):
a = Sticker( a = Sticker(
sticker.file_id, sticker.file_id,
@ -338,12 +358,35 @@ class TestSticker:
self.height, self.height,
self.is_animated, self.is_animated,
self.is_video, self.is_video,
self.type,
) )
b = Sticker( b = Sticker(
'', sticker.file_unique_id, self.width, self.height, self.is_animated, self.is_video "",
sticker.file_unique_id,
self.width,
self.height,
self.is_animated,
self.is_video,
self.type,
)
c = Sticker(
sticker.file_id,
sticker.file_unique_id,
0,
0,
False,
True,
self.type,
)
d = Sticker(
"",
"",
self.width,
self.height,
self.is_animated,
self.is_video,
self.type,
) )
c = Sticker(sticker.file_id, sticker.file_unique_id, 0, 0, False, True)
d = Sticker('', '', self.width, self.height, self.is_animated, self.is_video)
e = PhotoSize( e = PhotoSize(
sticker.file_id, sticker.file_unique_id, self.width, self.height, self.is_animated sticker.file_id, sticker.file_unique_id, self.width, self.height, self.is_animated
) )
@ -416,8 +459,9 @@ class TestStickerSet:
is_animated = True is_animated = True
is_video = True is_video = True
contains_masks = False contains_masks = False
stickers = [Sticker('file_id', 'file_un_id', 512, 512, True, True)] stickers = [Sticker('file_id', 'file_un_id', 512, 512, True, True, Sticker.REGULAR)]
name = 'NOTAREALNAME' name = 'NOTAREALNAME'
sticker_type = Sticker.REGULAR
def test_de_json(self, bot, sticker): def test_de_json(self, bot, sticker):
name = f'test_by_{bot.username}' name = f'test_by_{bot.username}'
@ -429,6 +473,7 @@ class TestStickerSet:
'contains_masks': self.contains_masks, 'contains_masks': self.contains_masks,
'stickers': [x.to_dict() for x in self.stickers], 'stickers': [x.to_dict() for x in self.stickers],
'thumb': sticker.thumb.to_dict(), 'thumb': sticker.thumb.to_dict(),
'sticker_type': self.sticker_type,
} }
sticker_set = StickerSet.de_json(json_dict, bot) sticker_set = StickerSet.de_json(json_dict, bot)
@ -439,6 +484,7 @@ class TestStickerSet:
assert sticker_set.contains_masks == self.contains_masks assert sticker_set.contains_masks == self.contains_masks
assert sticker_set.stickers == self.stickers assert sticker_set.stickers == self.stickers
assert sticker_set.thumb == sticker.thumb assert sticker_set.thumb == sticker.thumb
assert sticker_set.sticker_type == self.sticker_type
def test_create_sticker_set( def test_create_sticker_set(
self, bot, chat_id, sticker_file, animated_sticker_file, video_sticker_file self, bot, chat_id, sticker_file, animated_sticker_file, video_sticker_file
@ -526,6 +572,8 @@ class TestStickerSet:
assert sticker_set_dict['is_video'] == sticker_set.is_video assert sticker_set_dict['is_video'] == sticker_set.is_video
assert sticker_set_dict['contains_masks'] == sticker_set.contains_masks assert sticker_set_dict['contains_masks'] == sticker_set.contains_masks
assert sticker_set_dict['stickers'][0] == sticker_set.stickers[0].to_dict() assert sticker_set_dict['stickers'][0] == sticker_set.stickers[0].to_dict()
assert sticker_set_dict["thumb"] == sticker_set.thumb.to_dict()
assert sticker_set_dict["sticker_type"] == sticker_set.sticker_type
@flaky(3, 1) @flaky(3, 1)
def test_bot_methods_2_png(self, bot, sticker_set): def test_bot_methods_2_png(self, bot, sticker_set):
@ -626,6 +674,32 @@ class TestStickerSet:
assert test_flag assert test_flag
monkeypatch.delattr(bot, '_post') monkeypatch.delattr(bot, '_post')
def test_create_new_sticker_all_params(self, monkeypatch, bot, chat_id, mask_position):
def make_assertion(_, data, *args, **kwargs):
assert data["user_id"] == chat_id
assert data["name"] == "name"
assert data["title"] == "title"
assert data["emojis"] == "emoji"
assert data["mask_position"] == mask_position.to_json()
assert data["png_sticker"] == "wow.png"
assert data["tgs_sticker"] == "wow.tgs"
assert data["webm_sticker"] == "wow.webm"
assert data["sticker_type"] == Sticker.MASK
monkeypatch.setattr(bot, "_post", make_assertion)
bot.create_new_sticker_set(
chat_id,
"name",
"title",
"emoji",
mask_position=mask_position,
png_sticker="wow.png",
tgs_sticker="wow.tgs",
webm_sticker="wow.webm",
sticker_type=Sticker.MASK,
)
monkeypatch.delattr(bot, "_post")
def test_add_sticker_to_set_local_files(self, monkeypatch, bot, chat_id): def test_add_sticker_to_set_local_files(self, monkeypatch, bot, chat_id):
# For just test that the correct paths are passed as we have no local bot API set up # For just test that the correct paths are passed as we have no local bot API set up
test_flag = False test_flag = False
@ -675,6 +749,8 @@ class TestStickerSet:
self.contains_masks, self.contains_masks,
self.stickers, self.stickers,
self.is_video, self.is_video,
None,
self.sticker_type,
) )
b = StickerSet( b = StickerSet(
self.name, self.name,
@ -684,9 +760,16 @@ class TestStickerSet:
self.stickers, self.stickers,
self.is_video, self.is_video,
) )
c = StickerSet(self.name, None, None, None, None, None) c = StickerSet(self.name, None, None, None, None, None, None, Sticker.CUSTOM_EMOJI)
d = StickerSet( d = StickerSet(
'blah', self.title, self.is_animated, self.contains_masks, self.stickers, self.is_video 'blah',
self.title,
self.is_animated,
self.contains_masks,
self.stickers,
self.is_video,
None,
self.sticker_type,
) )
e = Audio(self.name, '', 0, None, None) e = Audio(self.name, '', 0, None, None)
@ -762,3 +845,23 @@ class TestMaskPosition:
assert a != e assert a != e
assert hash(a) != hash(e) assert hash(a) != hash(e)
class TestGetCustomEmojiSticker:
def test_custom_emoji_sticker(self, bot):
# we use the same ID as in test_custom_emoji
emoji_sticker_list = bot.get_custom_emoji_stickers(["6046140249875156202"])
assert emoji_sticker_list[0].emoji == "😎"
assert emoji_sticker_list[0].height == 100
assert emoji_sticker_list[0].width == 100
assert not emoji_sticker_list[0].is_animated
assert not emoji_sticker_list[0].is_video
assert emoji_sticker_list[0].set_name == "PTBStaticEmojiTestPack"
assert emoji_sticker_list[0].type == Sticker.CUSTOM_EMOJI
assert emoji_sticker_list[0].custom_emoji_id == "6046140249875156202"
assert emoji_sticker_list[0].thumb.width == 100
assert emoji_sticker_list[0].thumb.height == 100
assert emoji_sticker_list[0].thumb.file_size == 3614
assert emoji_sticker_list[0].thumb.file_unique_id == "AQAD6gwAAoY06FNy"
assert emoji_sticker_list[0].file_size == 3678
assert emoji_sticker_list[0].file_unique_id == "AgAD6gwAAoY06FM"