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/
: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
:alt: Supported Bot API versions
@ -112,7 +112,7 @@ Installing both ``python-telegram-bot`` and ``python-telegram-bot-raw`` in conju
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

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/
: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
:alt: Supported Bot API versions
@ -105,7 +105,7 @@ Installing both ``python-telegram-bot`` and ``python-telegram-bot-raw`` in conju
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

View file

@ -1,4 +1,6 @@
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
# When bumping this, make sure to rebuild the dark-mode 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]
result['file_path'] # type: ignore[index]
):
result['file_path'] = '{}/{}'.format( # type: ignore[index]
self.base_file_url, result['file_path'] # type: ignore[index]
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]
@ -4813,6 +4813,37 @@ class Bot(TelegramObject):
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
def upload_sticker_file(
self,
@ -4871,6 +4902,7 @@ class Bot(TelegramObject):
tgs_sticker: FileInput = None,
api_kwargs: JSONDict = None,
webm_sticker: FileInput = None,
sticker_type: str = None,
) -> bool:
"""
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
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:
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
@ -4924,6 +4960,12 @@ class Bot(TelegramObject):
should be created.
mask_position (:class:`telegram.MaskPosition`, optional): Position where the mask
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
the read timeout from the server (instead of the one specified during
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
# message here, which isn't json dumped by utils.request
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)
return result # type: ignore[return-value]
@ -6206,6 +6249,8 @@ class Bot(TelegramObject):
"""Alias for :meth:`unpin_all_chat_messages`"""
getStickerSet = 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
"""Alias for :meth:`upload_sticker_file`"""
createNewStickerSet = create_new_sticker_set

View file

@ -126,6 +126,11 @@ class Chat(TelegramObject):
:meth:`telegram.Bot.get_chat`.
.. 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.
Attributes:
@ -180,6 +185,11 @@ class Chat(TelegramObject):
:meth:`telegram.Bot.get_chat`.
.. 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__ = (
@ -207,6 +217,7 @@ class Chat(TelegramObject):
'has_private_forwards',
'join_to_send_messages',
'join_by_request',
'has_restricted_voice_and_video_messages',
'_id_attrs',
)
@ -249,6 +260,7 @@ class Chat(TelegramObject):
has_protected_content: bool = None,
join_to_send_messages: bool = None,
join_by_request: bool = None,
has_restricted_voice_and_video_messages: bool = None,
**_kwargs: Any,
):
# Required
@ -279,6 +291,7 @@ class Chat(TelegramObject):
self.location = location
self.join_to_send_messages = join_to_send_messages
self.join_by_request = join_by_request
self.has_restricted_voice_and_video_messages = has_restricted_voice_and_video_messages
self.bot = bot
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>`_.
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``.
.. versionadded:: 13.4
@ -144,6 +144,9 @@ Attributes:
MESSAGEENTITY_SPOILER (:obj:`str`): ``'spoiler'``
.. 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.
:class:`telegram.ParseMode`:
@ -160,6 +163,19 @@ Attributes:
POLL_QUIZ (:obj:`str`): ``'quiz'``
MAX_POLL_QUESTION_LENGTH (:obj:`int`): 300
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`:
@ -247,7 +263,7 @@ Attributes:
"""
from typing import List
BOT_API_VERSION: str = '6.1'
BOT_API_VERSION: str = '6.2'
MAX_MESSAGE_LENGTH: int = 4096
MAX_CAPTION_LENGTH: int = 1024
ANONYMOUS_ADMIN_ID: int = 1087968824
@ -325,6 +341,7 @@ MESSAGEENTITY_TEXT_MENTION: str = 'text_mention'
MESSAGEENTITY_UNDERLINE: str = 'underline'
MESSAGEENTITY_STRIKETHROUGH: str = 'strikethrough'
MESSAGEENTITY_SPOILER: str = 'spoiler'
MESSAGEENTITY_CUSTOM_EMOJI: str = 'custom_emoji'
MESSAGEENTITY_ALL_TYPES: List[str] = [
MESSAGEENTITY_MENTION,
MESSAGEENTITY_HASHTAG,
@ -342,6 +359,7 @@ MESSAGEENTITY_ALL_TYPES: List[str] = [
MESSAGEENTITY_UNDERLINE,
MESSAGEENTITY_STRIKETHROUGH,
MESSAGEENTITY_SPOILER,
MESSAGEENTITY_CUSTOM_EMOJI,
]
PARSEMODE_MARKDOWN: str = 'Markdown'
@ -353,6 +371,10 @@ POLL_QUIZ: str = 'quiz'
MAX_POLL_QUESTION_LENGTH: int = 300
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_EYES: str = 'eyes'
STICKER_MOUTH: str = 'mouth'

View file

@ -56,7 +56,7 @@ class TelegramError(Exception):
self.message = msg
def __str__(self) -> str:
return '%s' % self.message
return f'{self.message}'
def __reduce__(self) -> Tuple[type, Tuple[str]]:
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.
.. 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
format.
emoji (:obj:`str`, optional): Emoji associated with the sticker
@ -60,10 +65,14 @@ class Sticker(TelegramObject):
position where the mask should be placed.
file_size (:obj:`int`, optional): File size.
bot (:class:`telegram.Bot`, optional): The Bot to use for instance methods.
premium_animation (:class:`telegram.File`, optional): Premium animation for the sticker,
if the sticker is premium.
premium_animation (:class:`telegram.File`, optional): For premium regular stickers,
premium animation for the sticker.
.. 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.
Attributes:
@ -77,6 +86,11 @@ class Sticker(TelegramObject):
is_video (:obj:`bool`): :obj:`True`, if the sticker is a video sticker.
.. 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
format.
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
where the mask should be placed.
file_size (:obj:`int`): Optional. File size.
premium_animation (:class:`telegram.File`): Optional. Premium animation for the sticker,
if the sticker is premium.
premium_animation (:class:`telegram.File`): Optional. For premium regular stickers,
premium animation for the sticker.
.. 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.
"""
@ -106,6 +124,8 @@ class Sticker(TelegramObject):
'file_unique_id',
'emoji',
'premium_animation',
'type',
'custom_emoji_id',
'_id_attrs',
)
@ -117,6 +137,7 @@ class Sticker(TelegramObject):
height: int,
is_animated: bool,
is_video: bool,
type: str, # pylint: disable=redefined-builtin
thumb: PhotoSize = None,
emoji: str = None,
file_size: int = None,
@ -124,6 +145,7 @@ class Sticker(TelegramObject):
mask_position: 'MaskPosition' = None,
bot: 'Bot' = None,
premium_animation: 'File' = None,
custom_emoji_id: str = None,
**_kwargs: Any,
):
# Required
@ -133,6 +155,7 @@ class Sticker(TelegramObject):
self.height = int(height)
self.is_animated = is_animated
self.is_video = is_video
self.type = type
# Optionals
self.thumb = thumb
self.emoji = emoji
@ -141,6 +164,7 @@ class Sticker(TelegramObject):
self.mask_position = mask_position
self.bot = bot
self.premium_animation = premium_animation
self.custom_emoji_id = custom_emoji_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)
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):
"""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
passed correctly.
.. versionchanged:: 13.14:
The parameter ``contains_masks`` has been depreciated as of Bot API 6.2.
Use ``sticker_type`` instead.
Args:
name (:obj:`str`): Sticker set name.
title (:obj:`str`): Sticker set title.
@ -199,6 +243,11 @@ class StickerSet(TelegramObject):
.. versionadded:: 13.11
contains_masks (:obj:`bool`): :obj:`True`, if the sticker set contains masks.
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``,
``.TGS``, or ``.WEBM`` format.
@ -211,6 +260,9 @@ class StickerSet(TelegramObject):
.. versionadded:: 13.11
contains_masks (:obj:`bool`): :obj:`True`, if the sticker set contains masks.
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``,
``.TGS`` or ``.WEBM`` format.
@ -224,6 +276,7 @@ class StickerSet(TelegramObject):
'title',
'stickers',
'name',
'sticker_type',
'_id_attrs',
)
@ -236,6 +289,7 @@ class StickerSet(TelegramObject):
stickers: List[Sticker],
is_video: bool,
thumb: PhotoSize = None,
sticker_type: str = None,
**_kwargs: Any,
):
self.name = name
@ -246,6 +300,7 @@ class StickerSet(TelegramObject):
self.stickers = stickers
# Optionals
self.thumb = thumb
self.sticker_type = sticker_type
self._id_attrs = (self.name,)

View file

@ -407,6 +407,8 @@ class Message(TelegramObject):
to the message.
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
@ -2752,6 +2754,9 @@ class Message(TelegramObject):
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.
Note:
|custom_emoji_formatting_note|
.. versionchanged:: 13.10
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.
This also formats :attr:`telegram.MessageEntity.URL` as a hyperlink.
Note:
|custom_emoji_formatting_note|
.. versionchanged:: 13.10
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
HTML in the same way the original message was formatted.
Note:
|custom_emoji_formatting_note|
.. versionchanged:: 13.10
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
HTML. This also formats :attr:`telegram.MessageEntity.URL` as a hyperlink.
Note:
|custom_emoji_formatting_note|
.. versionchanged:: 13.10
Spoiler entities are now formatted as HTML.
@ -2983,8 +2997,10 @@ class Message(TelegramObject):
in the same way the original message was formatted.
Note:
:attr:`telegram.ParseMode.MARKDOWN` is is a legacy mode, retained by Telegram for
backward compatibility. You should use :meth:`text_markdown_v2` instead.
* :attr:`telegram.ParseMode.MARKDOWN` is a legacy mode, retained by Telegram for
backward compatibility. You should use :meth:`text_markdown_v2` instead.
* |custom_emoji_formatting_note|
Returns:
: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
in the same way the original message was formatted.
Note:
|custom_emoji_formatting_note|
.. versionchanged:: 13.10
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.
Note:
:attr:`telegram.ParseMode.MARKDOWN` is is a legacy mode, retained by Telegram for
backward compatibility. You should use :meth:`text_markdown_v2_urled` instead.
* :attr:`telegram.ParseMode.MARKDOWN` is a legacy mode, retained by Telegram for
backward compatibility. You should use :meth:`text_markdown_v2_urled` instead.
* |custom_emoji_formatting_note|
Returns:
: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.
This also formats :attr:`telegram.MessageEntity.URL` as a hyperlink.
Note:
|custom_emoji_formatting_note|
.. versionchanged:: 13.10
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.
Note:
:attr:`telegram.ParseMode.MARKDOWN` is is a legacy mode, retained by Telegram for
backward compatibility. You should use :meth:`caption_markdown_v2` instead.
* :attr:`telegram.ParseMode.MARKDOWN` is a legacy mode, retained by Telegram for
backward compatibility. You should use :meth:`caption_markdown_v2` instead.
* |custom_emoji_formatting_note|
Returns:
: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
Markdown in the same way the original message was formatted.
Note:
|custom_emoji_formatting_note|
.. versionchanged:: 13.10
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.
Note:
:attr:`telegram.ParseMode.MARKDOWN` is is a legacy mode, retained by Telegram for
backward compatibility. You should use :meth:`caption_markdown_v2_urled` instead.
* :attr:`telegram.ParseMode.MARKDOWN` is a legacy mode, retained by Telegram for
backward compatibility. You should use :meth:`caption_markdown_v2_urled` instead.
* |custom_emoji_formatting_note|
Returns:
: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
Markdown. This also formats :attr:`telegram.MessageEntity.URL` as a hyperlink.
Note:
|custom_emoji_formatting_note|
.. versionchanged:: 13.10
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),
strikethrough, spoiler (spoiler message), code (monowidth string), pre
(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.
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
@ -49,6 +52,11 @@ class MessageEntity(TelegramObject):
user.
language (:obj:`str`, optional): For :attr:`PRE` only, the programming language of
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:
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.
user (:class:`telegram.User`): Optional. The mentioned user.
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__(
self,
@ -70,6 +90,7 @@ class MessageEntity(TelegramObject):
url: str = None,
user: User = None,
language: str = None,
custom_emoji_id: str = None,
**_kwargs: Any,
):
# Required
@ -80,6 +101,7 @@ class MessageEntity(TelegramObject):
self.url = url
self.user = user
self.language = language
self.custom_emoji_id = custom_emoji_id
self._id_attrs = (self.type, self.offset, self.length)
@ -130,6 +152,11 @@ class MessageEntity(TelegramObject):
.. 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
""":const:`telegram.constants.MESSAGEENTITY_ALL_TYPES`\n
List of all the types"""

View file

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

View file

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

View file

@ -272,7 +272,7 @@ class TestHelpers:
test_message.text = None
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'
test_message.sticker = None

View file

@ -110,7 +110,7 @@ def message(bot):
)
},
{'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'},
{'voice': Voice('voice_id', 'unique_id', 5)},
{'video_note': VideoNote('video_note_id', 'unique_id', 20, 12)},
@ -522,6 +522,36 @@ class TestMessage:
)
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):
test_html_string = (
'<u>Test</u> for &lt;<b>bold</b>, <i>ita_lic</i>, '
@ -631,6 +661,36 @@ class TestMessage:
)
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):
url = b'http://github.com/?unicode=\\u2713\\U0001f469'.decode('unicode-escape')
text = 'some url'

View file

@ -102,6 +102,8 @@ def check_method(h4):
ignored |= {'current_offset'} # Added for ease of use
elif name == 'promoteChatMember':
ignored |= {'can_manage_voice_chats'} # for backwards compatibility
elif name == 'createNewStickerSet':
ignored |= {'contains_masks'} # for backwards compatibility
assert (sig.parameters.keys() ^ checked) - ignored == set()
@ -195,7 +197,8 @@ def check_object(h4):
'voice_chat_scheduled',
'voice_chat_started',
}
elif name == 'StickerSet':
ignored |= {'contains_masks'} # for backwards compatibility
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)
c = PhotoSize(photo.file_id, photo.file_unique_id, 0, 0)
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 hash(a) == hash(b)

View file

@ -83,6 +83,8 @@ class TestSticker:
thumb_width = 319
thumb_height = 320
thumb_file_size = 21472
type = Sticker.REGULAR
custom_emoji_id = "ThisIsSuchACustomEmojiID"
sticker_file_id = '5a3128a4d2a04750b5b58397f3b5e812'
sticker_file_unique_id = 'adc3145fd2e84d95b64d68eaa22aa33e'
@ -119,6 +121,7 @@ class TestSticker:
assert sticker.thumb.width == self.thumb_width
assert sticker.thumb.height == self.thumb_height
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
# 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.file_size == sticker.thumb.file_size
assert message.has_protected_content
assert message.sticker.type == sticker.type
@flaky(3, 1)
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_video == sticker.is_video
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.file_id, str)
@ -214,6 +219,8 @@ class TestSticker:
'emoji': self.emoji,
'file_size': self.file_size,
'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)
@ -227,6 +234,8 @@ class TestSticker:
assert json_sticker.file_size == self.file_size
assert json_sticker.thumb == sticker.thumb
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(url, data, **kwargs):
@ -297,6 +306,7 @@ class TestSticker:
assert sticker_dict['is_video'] == sticker.is_video
assert sticker_dict['file_size'] == sticker.file_size
assert sticker_dict['thumb'] == sticker.thumb.to_dict()
assert sticker_dict["type"] == sticker.type
@flaky(3, 1)
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
@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):
a = Sticker(
sticker.file_id,
@ -338,12 +358,35 @@ class TestSticker:
self.height,
self.is_animated,
self.is_video,
self.type,
)
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(
sticker.file_id, sticker.file_unique_id, self.width, self.height, self.is_animated
)
@ -416,8 +459,9 @@ class TestStickerSet:
is_animated = True
is_video = True
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'
sticker_type = Sticker.REGULAR
def test_de_json(self, bot, sticker):
name = f'test_by_{bot.username}'
@ -429,6 +473,7 @@ class TestStickerSet:
'contains_masks': self.contains_masks,
'stickers': [x.to_dict() for x in self.stickers],
'thumb': sticker.thumb.to_dict(),
'sticker_type': self.sticker_type,
}
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.stickers == self.stickers
assert sticker_set.thumb == sticker.thumb
assert sticker_set.sticker_type == self.sticker_type
def test_create_sticker_set(
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['contains_masks'] == sticker_set.contains_masks
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)
def test_bot_methods_2_png(self, bot, sticker_set):
@ -626,6 +674,32 @@ class TestStickerSet:
assert test_flag
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):
# For just test that the correct paths are passed as we have no local bot API set up
test_flag = False
@ -675,6 +749,8 @@ class TestStickerSet:
self.contains_masks,
self.stickers,
self.is_video,
None,
self.sticker_type,
)
b = StickerSet(
self.name,
@ -684,9 +760,16 @@ class TestStickerSet:
self.stickers,
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(
'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)
@ -762,3 +845,23 @@ class TestMaskPosition:
assert a != 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"