* Allow for nested MessageEntities in Message._parse_markdown/html, adjust tests

* remove testing relict

* Use MessageEntitys new equality check (#1465)

* Remove unused variable

* Update to custom_title feature and slow_mode_delay option

Changes:

 - custom_title for ChatMember
 - new method setChatAdministratorCustomTitle for Bot
 - new slow_mode_delay for Chat

Update due to new API future `custom_title` from API 4.5 (https://core.telegram.org/bots/api#december-31-2019)

* Minor typo fix

* Comply with Flake8

* Add new MessageEntities and MarkdownV2

* Added file_unique_id attrs from API 4.5 and updated tests for it

* Fixed test and checked using flake8

* Fixed ChatPhoto documentation

* Fix Flake8

* Add setChatAdminCstmTitle to Bot

* Rename MDV2 methods

* Change files id attrs to unique id

* correct id_attrs for chat_photo

* Revert "temporarily skip tests failing b/c missing api 4.5 (#1738)"

This reverts commit 7cde6ca268.

* Fix text_markdown_v2 for monospace and text_links

* closing remarks from pieter

* Minor fix in escape_markdown, improve tests for it

* Fix offset bug in Message._parse_*

* Add test_chatphoto.py

* remove debug print from test_message.py

* try making codecov happy

* Update readme

* all hail codecov

* Improve Link handling for MarkdownV1 and adjust tests. Closes #1654

* Dont use beginning of pre-entity as language in _parse_markdown

* Remove debug print

* Dummy commit to try fix codecov

Co-authored-by: Hoi Dmytro <dmytro.hoi@gmail.com>
Co-authored-by: Dmytro Hoi <code@dmytrohoi.com>
Co-authored-by: poolitzer <25934244+poolitzer@users.noreply.github.com>
This commit is contained in:
Bibo-Joshi 2020-03-28 16:37:26 +01:00 committed by GitHub
parent e86ae25a62
commit 8d2c7af1f3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
45 changed files with 1223 additions and 265 deletions

View file

@ -93,7 +93,7 @@ make the development of bots easy and straightforward. These classes are contain
Telegram API support
====================
All types and methods of the Telegram Bot API **4.1** are supported.
All types and methods of the Telegram Bot API **4.5** are supported.
==========
Installing

View file

@ -2941,6 +2941,44 @@ class Bot(TelegramObject):
return result
@log
def set_chat_administrator_custom_title(self,
chat_id,
user_id,
custom_title,
timeout=None,
**kwargs):
"""
Use this method to set a custom title for administrators promoted by the bot in a
supergroup. The bot must be an administrator for this to work. Returns True on success.
Args:
chat_id (:obj:`int` | :obj:`str`): Unique identifier for the target chat or username of
the target supergroup (in the format `@supergroupusername`).
user_id (:obj:`int`): Unique identifier of the target administrator.
custom_title (:obj:`str`) New custom title for the administrator. It must be a string
with len 0-16 characters, emoji are not allowed.
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).
**kwargs (:obj:`dict`): Arbitrary keyword arguments
Returns:
:obj:`bool`: Returns True on success.
Raises:
:class:`telegram.TelegramError`
"""
url = '{0}/setChatAdministratorCustomTitle'.format(self.base_url)
data = {'chat_id': chat_id, 'user_id': user_id, 'custom_title': custom_title}
data.update(kwargs)
result = self._request.post(url, data, timeout=timeout)
return result
@log
def export_chat_invite_link(self, chat_id, timeout=None, **kwargs):
"""
@ -3655,6 +3693,8 @@ class Bot(TelegramObject):
"""Alias for :attr:`promote_chat_member`"""
setChatPermissions = set_chat_permissions
"""Alias for :attr:`set_chat_permissions`"""
setChatAdministratorCustomTitle = set_chat_administrator_custom_title
"""Alias for :attr:`set_chat_administrator_custom_title`"""
exportChatInviteLink = export_chat_invite_link
"""Alias for :attr:`export_chat_invite_link`"""
setChatPhoto = set_chat_photo

View file

@ -40,6 +40,8 @@ class Chat(TelegramObject):
Returned only in get_chat.
permissions (:class:`telegram.ChatPermission`): Optional. Default chat member permissions,
for groups and supergroups. Returned only in getChat.
slow_mode_delay (:obj:`int`): Optional. For supergroups, the minimum allowed delay between
consecutive messages sent by each unpriviledged user. Returned only in getChat.
sticker_set_name (:obj:`str`): Optional. For supergroups, name of Group sticker set.
can_set_sticker_set (:obj:`bool`): Optional. ``True``, if the bot can change group the
sticker set.
@ -65,6 +67,8 @@ class Chat(TelegramObject):
Returned only in get_chat.
permissions (:class:`telegram.ChatPermission`): Optional. Default chat member permissions,
for groups and supergroups. Returned only in getChat.
slow_mode_delay (:obj:`int`, optional): For supergroups, the minimum allowed delay between
consecutive messages sent by each unpriviledged user. Returned only in getChat.
bot (:class:`telegram.Bot`, optional): The Bot to use for instance methods.
sticker_set_name (:obj:`str`, optional): For supergroups, name of Group sticker set.
Returned only in get_chat.
@ -98,6 +102,7 @@ class Chat(TelegramObject):
permissions=None,
sticker_set_name=None,
can_set_sticker_set=None,
slow_mode_delay=None,
**kwargs):
# Required
self.id = int(id)
@ -114,6 +119,7 @@ class Chat(TelegramObject):
self.invite_link = invite_link
self.pinned_message = pinned_message
self.permissions = permissions
self.slow_mode_delay = slow_mode_delay
self.sticker_set_name = sticker_set_name
self.can_set_sticker_set = can_set_sticker_set
@ -240,6 +246,17 @@ class Chat(TelegramObject):
"""
return self.bot.set_chat_permissions(self.id, *args, **kwargs)
def set_administrator_custom_title(self, *args, **kwargs):
"""Shortcut for::
bot.set_chat_administrator_custom_title(update.message.chat.id, *args, **kwargs)
Returns:
:obj:`bool`: If the action was sent successfully.
"""
return self.bot.set_chat_administrator_custom_title(self.id, *args, **kwargs)
def send_message(self, *args, **kwargs):
"""Shortcut for::

View file

@ -28,6 +28,7 @@ class ChatMember(TelegramObject):
Attributes:
user (:class:`telegram.User`): Information about the user.
status (:obj:`str`): The member's status in the chat.
custom_title (:obj:`str`): Optional. Custom title for owner and administrators.
until_date (:class:`datetime.datetime`): Optional. Date when restrictions will be lifted
for this user.
can_be_edited (:obj:`bool`): Optional. If the bot is allowed to edit administrator
@ -62,6 +63,8 @@ class ChatMember(TelegramObject):
user (:class:`telegram.User`): Information about the user.
status (:obj:`str`): The member's status in the chat. Can be 'creator', 'administrator',
'member', 'restricted', 'left' or 'kicked'.
custom_title (:obj:`str`, optional): Owner and administrators only.
Custom title for this user.
until_date (:class:`datetime.datetime`, optional): Restricted and kicked only. Date when
restrictions will be lifted for this user.
can_be_edited (:obj:`bool`, optional): Administrators only. True, if the bot is allowed to
@ -118,10 +121,11 @@ class ChatMember(TelegramObject):
can_restrict_members=None, can_pin_messages=None,
can_promote_members=None, can_send_messages=None,
can_send_media_messages=None, can_send_polls=None, can_send_other_messages=None,
can_add_web_page_previews=None, is_member=None, **kwargs):
can_add_web_page_previews=None, is_member=None, custom_title=None, **kwargs):
# Required
self.user = user
self.status = status
self.custom_title = custom_title
self.until_date = until_date
self.can_be_edited = can_be_edited
self.can_change_info = can_change_info

View file

@ -25,7 +25,10 @@ class Animation(TelegramObject):
"""This object represents an animation file to be displayed in the message containing a game.
Attributes:
file_id (:obj:`str`): Unique file identifier.
file_id (:obj:`str`): File identifier.
file_unique_id (:obj:`str`): Unique identifier for this file, which
is supposed to be the same over time and for different bots.
Can't be used to download or reuse the file.
width (:obj:`int`): Video width as defined by sender.
height (:obj:`int`): Video height as defined by sender.
duration (:obj:`int`): Duration of the video in seconds as defined by sender.
@ -37,7 +40,10 @@ class Animation(TelegramObject):
bot (:class:`telegram.Bot`): Optional. The Bot to use for instance methods.
Args:
file_id (:obj:`str`): Unique file identifier.
file_id (:obj:`str`): Identifier for this file, which can be used to download
or reuse the file.
file_unique_id (:obj:`str`): Unique and the same over time and
for different bots file identifier.
width (:obj:`int`): Video width as defined by sender.
height (:obj:`int`): Video height as defined by sender.
duration (:obj:`int`): Duration of the video in seconds as defined by sender.
@ -52,6 +58,7 @@ class Animation(TelegramObject):
def __init__(self,
file_id,
file_unique_id,
width,
height,
duration,
@ -63,6 +70,7 @@ class Animation(TelegramObject):
**kwargs):
# Required
self.file_id = str(file_id)
self.file_unique_id = str(file_unique_id)
self.width = int(width)
self.height = int(height)
self.duration = duration
@ -73,7 +81,7 @@ class Animation(TelegramObject):
self.file_size = file_size
self.bot = bot
self._id_attrs = (self.file_id,)
self._id_attrs = (self.file_unique_id,)
@classmethod
def de_json(cls, data, bot):

View file

@ -26,6 +26,9 @@ class Audio(TelegramObject):
Attributes:
file_id (:obj:`str`): Unique identifier for this file.
file_unique_id (:obj:`str`): Unique identifier for this file, which
is supposed to be the same over time and for different bots.
Can't be used to download or reuse the file.
duration (:obj:`int`): Duration of the audio in seconds.
performer (:obj:`str`): Optional. Performer of the audio as defined by sender or by audio
tags.
@ -37,7 +40,10 @@ class Audio(TelegramObject):
bot (:class:`telegram.Bot`): Optional. The Bot to use for instance methods.
Args:
file_id (:obj:`str`): Unique identifier for this file.
file_id (:obj:`str`): Identifier for this file, which can be used to download
or reuse the file.
file_unique_id (:obj:`str`): Unique and the same over time and
for different bots file identifier.
duration (:obj:`int`): Duration of the audio in seconds as defined by sender.
performer (:obj:`str`, optional): Performer of the audio as defined by sender or by audio
tags.
@ -53,6 +59,7 @@ class Audio(TelegramObject):
def __init__(self,
file_id,
file_unique_id,
duration,
performer=None,
title=None,
@ -63,6 +70,7 @@ class Audio(TelegramObject):
**kwargs):
# Required
self.file_id = str(file_id)
self.file_unique_id = str(file_unique_id)
self.duration = int(duration)
# Optionals
self.performer = performer
@ -72,7 +80,7 @@ class Audio(TelegramObject):
self.thumb = thumb
self.bot = bot
self._id_attrs = (self.file_id,)
self._id_attrs = (self.file_unique_id,)
@classmethod
def de_json(cls, data, bot):

View file

@ -25,24 +25,48 @@ class ChatPhoto(TelegramObject):
Attributes:
small_file_id (:obj:`str`): File identifier of small (160x160) chat photo.
This file_id can be used only for photo download and only for as long
as the photo is not changed.
small_file_unique_id (:obj:`str`): Unique file identifier of small (160x160) chat photo,
which is supposed to be the same over time and for different bots.
Can't be used to download or reuse the file.
big_file_id (:obj:`str`): File identifier of big (640x640) chat photo.
This file_id can be used only for photo download and only for as long as
the photo is not changed.
big_file_unique_id (:obj:`str`): Unique file identifier of big (640x640) chat photo,
which is supposed to be the same over time and for different bots.
Can't be used to download or reuse the file.
Args:
small_file_id (:obj:`str`): File identifier of small (160x160) chat photo. This file_id can
be used only for photo download and only for as long as the photo is not changed.
big_file_id (:obj:`str`): File identifier of big (640x640) chat photo. This file_id can be
used only for photo download and only for as long as the photo is not changed.
small_file_id (:obj:`str`): Unique file identifier of small (160x160) chat photo. This
file_id can be used only for photo download and only for as long
as the photo is not changed.
small_file_unique_id (:obj:`str`): Unique file identifier of small (160x160) chat photo,
which is supposed to be the same over time and for different bots.
Can't be used to download or reuse the file.
big_file_id (:obj:`str`): Unique file identifier of big (640x640) chat photo. This file_id
can be used only for photo download and only for as long as the photo is not changed.
big_file_unique_id (:obj:`str`): Unique file identifier of big (640x640) chat photo,
which is supposed to be the same over time and for different bots.
Can't be used to download or reuse the file.
bot (:class:`telegram.Bot`, optional): The Bot to use for instance methods
**kwargs (:obj:`dict`): Arbitrary keyword arguments.
"""
def __init__(self, small_file_id, big_file_id, bot=None, **kwargs):
def __init__(self,
small_file_id,
small_file_unique_id,
big_file_id,
big_file_unique_id,
bot=None, **kwargs):
self.small_file_id = small_file_id
self.small_file_unique_id = small_file_unique_id
self.big_file_id = big_file_id
self.big_file_unique_id = big_file_unique_id
self.bot = bot
self._id_attrs = (self.small_file_id, self.big_file_id)
self._id_attrs = (self.small_file_unique_id, self.big_file_unique_id,)
@classmethod
def de_json(cls, data, bot):

View file

@ -26,6 +26,9 @@ class Document(TelegramObject):
Attributes:
file_id (:obj:`str`): Unique file identifier.
file_unique_id (:obj:`str`): Unique identifier for this file, which
is supposed to be the same over time and for different bots.
Can't be used to download or reuse the file.
thumb (:class:`telegram.PhotoSize`): Optional. Document thumbnail.
file_name (:obj:`str`): Original filename.
mime_type (:obj:`str`): Optional. MIME type of the file.
@ -33,7 +36,10 @@ class Document(TelegramObject):
bot (:class:`telegram.Bot`): Optional. The Bot to use for instance methods.
Args:
file_id (:obj:`str`): Unique file identifier
file_id (:obj:`str`): Identifier for this file, which can be used to download
or reuse the file.
file_unique_id (:obj:`str`): Unique and the same over time and
for different bots file identifier.
thumb (:class:`telegram.PhotoSize`, optional): Document thumbnail as defined by sender.
file_name (:obj:`str`, optional): Original filename as defined by sender.
mime_type (:obj:`str`, optional): MIME type of the file as defined by sender.
@ -46,6 +52,7 @@ class Document(TelegramObject):
def __init__(self,
file_id,
file_unique_id,
thumb=None,
file_name=None,
mime_type=None,
@ -54,6 +61,7 @@ class Document(TelegramObject):
**kwargs):
# Required
self.file_id = str(file_id)
self.file_unique_id = str(file_unique_id)
# Optionals
self.thumb = thumb
self.file_name = file_name
@ -61,7 +69,7 @@ class Document(TelegramObject):
self.file_size = file_size
self.bot = bot
self._id_attrs = (self.file_id,)
self._id_attrs = (self.file_unique_id,)
@classmethod
def de_json(cls, data, bot):

View file

@ -38,11 +38,17 @@ class File(TelegramObject):
Attributes:
file_id (:obj:`str`): Unique identifier for this file.
file_unique_id (:obj:`str`): Unique identifier for this file, which
is supposed to be the same over time and for different bots.
Can't be used to download or reuse the file.
file_size (:obj:`str`): Optional. File size.
file_path (:obj:`str`): Optional. File path. Use :attr:`download` to get the file.
Args:
file_id (:obj:`str`): Unique identifier for this file.
file_id (:obj:`str`): Identifier for this file, which can be used to download
or reuse the file.
file_unique_id (:obj:`str`): Unique and the same over time and
for different bots file identifier.
file_size (:obj:`int`, optional): Optional. File size, if known.
file_path (:obj:`str`, optional): File path. Use :attr:`download` to get the file.
bot (:obj:`telegram.Bot`, optional): Bot to use with shortcut method.
@ -54,18 +60,23 @@ class File(TelegramObject):
"""
def __init__(self, file_id, bot=None, file_size=None, file_path=None, **kwargs):
def __init__(self,
file_id,
file_unique_id,
bot=None,
file_size=None,
file_path=None,
**kwargs):
# Required
self.file_id = str(file_id)
self.file_unique_id = str(file_unique_id)
# Optionals
self.file_size = file_size
self.file_path = file_path
self.bot = bot
self._credentials = None
self._id_attrs = (self.file_id,)
self._id_attrs = (self.file_unique_id,)
@classmethod
def de_json(cls, data, bot):

View file

@ -26,13 +26,19 @@ class PhotoSize(TelegramObject):
Attributes:
file_id (:obj:`str`): Unique identifier for this file.
file_unique_id (:obj:`str`): Unique identifier for this file, which
is supposed to be the same over time and for different bots.
Can't be used to download or reuse the file.
width (:obj:`int`): Photo width.
height (:obj:`int`): Photo height.
file_size (:obj:`int`): Optional. File size.
bot (:class:`telegram.Bot`): Optional. The Bot to use for instance methods.
Args:
file_id (:obj:`str`): Unique identifier for this file.
file_id (:obj:`str`): Identifier for this file, which can be used to download
or reuse the file.
file_unique_id (:obj:`str`): Unique and the same over time and
for different bots file identifier.
width (:obj:`int`): Photo width.
height (:obj:`int`): Photo height.
file_size (:obj:`int`, optional): File size.
@ -41,16 +47,24 @@ class PhotoSize(TelegramObject):
"""
def __init__(self, file_id, width, height, file_size=None, bot=None, **kwargs):
def __init__(self,
file_id,
file_unique_id,
width,
height,
file_size=None,
bot=None,
**kwargs):
# Required
self.file_id = str(file_id)
self.file_unique_id = str(file_unique_id)
self.width = int(width)
self.height = int(height)
# Optionals
self.file_size = file_size
self.bot = bot
self._id_attrs = (self.file_id,)
self._id_attrs = (self.file_unique_id,)
@classmethod
def de_json(cls, data, bot):

View file

@ -26,6 +26,9 @@ class Sticker(TelegramObject):
Attributes:
file_id (:obj:`str`): Unique identifier for this file.
file_unique_id (:obj:`str`): Unique identifier for this file, which
is supposed to be the same over time and for different bots.
Can't be used to download or reuse the file.
width (:obj:`int`): Sticker width.
height (:obj:`int`): Sticker height.
is_animated (:obj:`bool`): True, if the sticker is animated.
@ -39,7 +42,10 @@ class Sticker(TelegramObject):
bot (:class:`telegram.Bot`): Optional. The Bot to use for instance methods.
Args:
file_id (:obj:`str`): Unique identifier for this file.
file_id (:obj:`str`): Identifier for this file, which can be used to download
or reuse the file.
file_unique_id (:obj:`str`): Unique and the same over time and
for different bots file identifier.
width (:obj:`int`): Sticker width.
height (:obj:`int`): Sticker height.
is_animated (:obj:`bool`): True, if the sticker is animated.
@ -58,6 +64,7 @@ class Sticker(TelegramObject):
def __init__(self,
file_id,
file_unique_id,
width,
height,
is_animated,
@ -70,6 +77,7 @@ class Sticker(TelegramObject):
**kwargs):
# Required
self.file_id = str(file_id)
self.file_unique_id = str(file_unique_id)
self.width = int(width)
self.height = int(height)
self.is_animated = is_animated
@ -81,7 +89,7 @@ class Sticker(TelegramObject):
self.mask_position = mask_position
self.bot = bot
self._id_attrs = (self.file_id,)
self._id_attrs = (self.file_unique_id,)
@classmethod
def de_json(cls, data, bot):

View file

@ -26,6 +26,9 @@ class Video(TelegramObject):
Attributes:
file_id (:obj:`str`): Unique identifier for this file.
file_unique_id (:obj:`str`): Unique identifier for this file, which
is supposed to be the same over time and for different bots.
Can't be used to download or reuse the file.
width (:obj:`int`): Video width as defined by sender.
height (:obj:`int`): Video height as defined by sender.
duration (:obj:`int`): Duration of the video in seconds as defined by sender.
@ -35,7 +38,10 @@ class Video(TelegramObject):
bot (:class:`telegram.Bot`): Optional. The Bot to use for instance methods.
Args:
file_id (:obj:`str`): Unique identifier for this file.
file_id (:obj:`str`): Identifier for this file, which can be used to download
or reuse the file.
file_unique_id (:obj:`str`): Unique and the same over time and
for different bots file identifier.
width (:obj:`int`): Video width as defined by sender.
height (:obj:`int`): Video height as defined by sender.
duration (:obj:`int`): Duration of the video in seconds as defined by sender.
@ -49,6 +55,7 @@ class Video(TelegramObject):
def __init__(self,
file_id,
file_unique_id,
width,
height,
duration,
@ -59,6 +66,7 @@ class Video(TelegramObject):
**kwargs):
# Required
self.file_id = str(file_id)
self.file_unique_id = str(file_unique_id)
self.width = int(width)
self.height = int(height)
self.duration = int(duration)
@ -68,7 +76,7 @@ class Video(TelegramObject):
self.file_size = file_size
self.bot = bot
self._id_attrs = (self.file_id,)
self._id_attrs = (self.file_unique_id,)
@classmethod
def de_json(cls, data, bot):

View file

@ -26,6 +26,9 @@ class VideoNote(TelegramObject):
Attributes:
file_id (:obj:`str`): Unique identifier for this file.
file_unique_id (:obj:`str`): Unique identifier for this file, which
is supposed to be the same over time and for different bots.
Can't be used to download or reuse the file.
length (:obj:`int`): Video width and height as defined by sender.
duration (:obj:`int`): Duration of the video in seconds as defined by sender.
thumb (:class:`telegram.PhotoSize`): Optional. Video thumbnail.
@ -33,7 +36,10 @@ class VideoNote(TelegramObject):
bot (:class:`telegram.Bot`): Optional. The Bot to use for instance methods.
Args:
file_id (:obj:`str`): Unique identifier for this file.
file_id (:obj:`str`): Identifier for this file, which can be used to download
or reuse the file.
file_unique_id (:obj:`str`): Unique and the same over time and
for different bots file identifier.
length (:obj:`int`): Video width and height as defined by sender.
duration (:obj:`int`): Duration of the video in seconds as defined by sender.
thumb (:class:`telegram.PhotoSize`, optional): Video thumbnail.
@ -43,9 +49,18 @@ class VideoNote(TelegramObject):
"""
def __init__(self, file_id, length, duration, thumb=None, file_size=None, bot=None, **kwargs):
def __init__(self,
file_id,
file_unique_id,
length,
duration,
thumb=None,
file_size=None,
bot=None,
**kwargs):
# Required
self.file_id = str(file_id)
self.file_unique_id = str(file_unique_id)
self.length = int(length)
self.duration = int(duration)
# Optionals
@ -53,7 +68,7 @@ class VideoNote(TelegramObject):
self.file_size = file_size
self.bot = bot
self._id_attrs = (self.file_id,)
self._id_attrs = (self.file_unique_id,)
@classmethod
def de_json(cls, data, bot):

View file

@ -26,13 +26,19 @@ class Voice(TelegramObject):
Attributes:
file_id (:obj:`str`): Unique identifier for this file.
file_unique_id (:obj:`str`): Unique identifier for this file, which
is supposed to be the same over time and for different bots.
Can't be used to download or reuse the file.
duration (:obj:`int`): Duration of the audio in seconds as defined by sender.
mime_type (:obj:`str`): Optional. MIME type of the file as defined by sender.
file_size (:obj:`int`): Optional. File size.
bot (:class:`telegram.Bot`): Optional. The Bot to use for instance methods.
Args:
file_id (:obj:`str`): Unique identifier for this file.
file_id (:obj:`str`): Identifier for this file, which can be used to download
or reuse the file.
file_unique_id (:obj:`str`): Unique and the same over time and
for different bots file identifier.
duration (:obj:`int`, optional): Duration of the audio in seconds as defined by sender.
mime_type (:obj:`str`, optional): MIME type of the file as defined by sender.
file_size (:obj:`int`, optional): File size.
@ -41,16 +47,24 @@ class Voice(TelegramObject):
"""
def __init__(self, file_id, duration, mime_type=None, file_size=None, bot=None, **kwargs):
def __init__(self,
file_id,
file_unique_id,
duration,
mime_type=None,
file_size=None,
bot=None,
**kwargs):
# Required
self.file_id = str(file_id)
self.file_unique_id = str(file_unique_id)
self.duration = int(duration)
# Optionals
self.mime_type = mime_type
self.file_size = file_size
self.bot = bot
self._id_attrs = (self.file_id,)
self._id_attrs = (self.file_unique_id,)
@classmethod
def de_json(cls, data, bot):

View file

@ -510,7 +510,7 @@ class Message(TelegramObject):
bot.send_message(update.message.chat_id, parse_mode=ParseMode.MARKDOWN, *args,
**kwargs)
Sends a message with markdown formatting.
Sends a message with markdown version 1 formatting.
Keyword Args:
quote (:obj:`bool`, optional): If set to ``True``, the message is sent as an actual
@ -528,6 +528,30 @@ class Message(TelegramObject):
return self.bot.send_message(self.chat_id, *args, **kwargs)
def reply_markdown_v2(self, *args, **kwargs):
"""Shortcut for::
bot.send_message(update.message.chat_id, parse_mode=ParseMode.MARKDOWN_V2, *args,
**kwargs)
Sends a message with markdown version 2 formatting.
Keyword Args:
quote (:obj:`bool`, optional): If set to ``True``, the message is sent as an actual
reply to this message. If ``reply_to_message_id`` is passed in ``kwargs``, this
parameter will be ignored. Default: ``True`` in group chats and ``False`` in
private chats.
Returns:
:class:`telegram.Message`: On success, instance representing the message posted.
"""
kwargs['parse_mode'] = ParseMode.MARKDOWN_V2
self._quote(kwargs)
return self.bot.send_message(self.chat_id, *args, **kwargs)
def reply_html(self, *args, **kwargs):
"""Shortcut for::
@ -1012,7 +1036,7 @@ class Message(TelegramObject):
}
@staticmethod
def _parse_html(message_text, entities, urled=False):
def _parse_html(message_text, entities, urled=False, offset=0):
if message_text is None:
return None
@ -1022,38 +1046,74 @@ class Message(TelegramObject):
html_text = ''
last_offset = 0
for entity, text in sorted(entities.items(), key=(lambda item: item[0].offset)):
text = escape(text)
sorted_entities = sorted(entities.items(), key=(lambda item: item[0].offset))
parsed_entities = []
if entity.type == MessageEntity.TEXT_LINK:
insert = '<a href="{}">{}</a>'.format(entity.url, text)
elif entity.type == MessageEntity.TEXT_MENTION and entity.user:
insert = '<a href="tg://user?id={}">{}</a>'.format(entity.user.id, text)
elif entity.type == MessageEntity.URL and urled:
insert = '<a href="{0}">{0}</a>'.format(text)
elif entity.type == MessageEntity.BOLD:
insert = '<b>' + text + '</b>'
elif entity.type == MessageEntity.ITALIC:
insert = '<i>' + text + '</i>'
elif entity.type == MessageEntity.CODE:
insert = '<code>' + text + '</code>'
elif entity.type == MessageEntity.PRE:
insert = '<pre>' + text + '</pre>'
else:
insert = text
for (entity, text) in sorted_entities:
if entity not in parsed_entities:
nested_entities = {
e: t
for (e, t) in sorted_entities if e.offset >= entity.offset
and e.offset + e.length <= entity.offset + entity.length
and e != entity
}
parsed_entities.extend([e for e in nested_entities.keys()])
text = escape(text)
if nested_entities:
text = Message._parse_html(text, nested_entities,
urled=urled, offset=entity.offset)
if entity.type == MessageEntity.TEXT_LINK:
insert = '<a href="{}">{}</a>'.format(entity.url, text)
elif entity.type == MessageEntity.TEXT_MENTION and entity.user:
insert = '<a href="tg://user?id={}">{}</a>'.format(entity.user.id, text)
elif entity.type == MessageEntity.URL and urled:
insert = '<a href="{0}">{0}</a>'.format(text)
elif entity.type == MessageEntity.BOLD:
insert = '<b>' + text + '</b>'
elif entity.type == MessageEntity.ITALIC:
insert = '<i>' + text + '</i>'
elif entity.type == MessageEntity.CODE:
insert = '<code>' + text + '</code>'
elif entity.type == MessageEntity.PRE:
insert = '<pre>' + text + '</pre>'
elif entity.type == MessageEntity.UNDERLINE:
insert = '<u>' + text + '</u>'
elif entity.type == MessageEntity.STRIKETHROUGH:
insert = '<s>' + text + '</s>'
else:
insert = text
if offset == 0:
if sys.maxunicode == 0xffff:
html_text += escape(message_text[last_offset:entity.offset
- offset]) + insert
else:
html_text += escape(message_text[last_offset * 2:(entity.offset
- offset) * 2]
.decode('utf-16-le')) + insert
else:
if sys.maxunicode == 0xffff:
html_text += message_text[last_offset:entity.offset - offset] + insert
else:
html_text += message_text[last_offset * 2:(entity.offset
- offset) * 2].decode('utf-16-le') + insert
last_offset = entity.offset - offset + entity.length
if offset == 0:
if sys.maxunicode == 0xffff:
html_text += escape(message_text[last_offset:entity.offset]) + insert
html_text += escape(message_text[last_offset:])
else:
html_text += escape(message_text[last_offset * 2:entity.offset * 2]
.decode('utf-16-le')) + insert
last_offset = entity.offset + entity.length
if sys.maxunicode == 0xffff:
html_text += escape(message_text[last_offset:])
html_text += escape(message_text[last_offset * 2:].decode('utf-16-le'))
else:
html_text += escape(message_text[last_offset * 2:].decode('utf-16-le'))
if sys.maxunicode == 0xffff:
html_text += message_text[last_offset:]
else:
html_text += message_text[last_offset * 2:].decode('utf-16-le')
return html_text
@property
@ -1111,7 +1171,9 @@ class Message(TelegramObject):
return self._parse_html(self.caption, self.parse_caption_entities(), urled=True)
@staticmethod
def _parse_markdown(message_text, entities, urled=False):
def _parse_markdown(message_text, entities, urled=False, version=1, offset=0):
version = int(version)
if message_text is None:
return None
@ -1121,42 +1183,114 @@ class Message(TelegramObject):
markdown_text = ''
last_offset = 0
for entity, text in sorted(entities.items(), key=(lambda item: item[0].offset)):
text = escape_markdown(text)
sorted_entities = sorted(entities.items(), key=(lambda item: item[0].offset))
parsed_entities = []
if entity.type == MessageEntity.TEXT_LINK:
insert = '[{}]({})'.format(text, entity.url)
elif entity.type == MessageEntity.TEXT_MENTION and entity.user:
insert = '[{}](tg://user?id={})'.format(text, entity.user.id)
elif entity.type == MessageEntity.URL and urled:
insert = '[{0}]({0})'.format(text)
elif entity.type == MessageEntity.BOLD:
insert = '*' + text + '*'
elif entity.type == MessageEntity.ITALIC:
insert = '_' + text + '_'
elif entity.type == MessageEntity.CODE:
insert = '`' + text + '`'
elif entity.type == MessageEntity.PRE:
insert = '```' + text + '```'
else:
insert = text
for (entity, text) in sorted_entities:
if entity not in parsed_entities:
nested_entities = {
e: t
for (e, t) in sorted_entities if e.offset >= entity.offset
and e.offset + e.length <= entity.offset + entity.length
and e != entity
}
parsed_entities.extend([e for e in nested_entities.keys()])
orig_text = text
text = escape_markdown(text, version=version)
if nested_entities:
if version < 2:
raise ValueError('Nested entities are not supported for Markdown '
'version 1')
text = Message._parse_markdown(text, nested_entities,
urled=urled, offset=entity.offset,
version=version)
if entity.type == MessageEntity.TEXT_LINK:
if version == 1:
url = entity.url
else:
# Links need special escaping. Also can't have entities nested within
url = escape_markdown(entity.url, version=version,
entity_type=MessageEntity.TEXT_LINK)
insert = '[{}]({})'.format(text, url)
elif entity.type == MessageEntity.TEXT_MENTION and entity.user:
insert = '[{}](tg://user?id={})'.format(text, entity.user.id)
elif entity.type == MessageEntity.URL and urled:
if version == 1:
link = orig_text
else:
link = text
insert = '[{}]({})'.format(link, orig_text)
elif entity.type == MessageEntity.BOLD:
insert = '*' + text + '*'
elif entity.type == MessageEntity.ITALIC:
insert = '_' + text + '_'
elif entity.type == MessageEntity.CODE:
# Monospace needs special escaping. Also can't have entities nested within
insert = '`' + escape_markdown(orig_text, version=version,
entity_type=MessageEntity.CODE) + '`'
elif entity.type == MessageEntity.PRE:
# Monospace needs special escaping. Also can't have entities nested within
code = escape_markdown(orig_text, version=version,
entity_type=MessageEntity.PRE)
if code.startswith('\\'):
prefix = '```'
else:
prefix = '```\n'
insert = prefix + code + '```'
elif entity.type == MessageEntity.UNDERLINE:
if version == 1:
raise ValueError('Underline entities are not supported for Markdown '
'version 1')
insert = '__' + text + '__'
elif entity.type == MessageEntity.STRIKETHROUGH:
if version == 1:
raise ValueError('Strikethrough entities are not supported for Markdown '
'version 1')
insert = '~' + text + '~'
else:
insert = text
if offset == 0:
if sys.maxunicode == 0xffff:
markdown_text += escape_markdown(message_text[last_offset:entity.offset
- offset],
version=version) + insert
else:
markdown_text += escape_markdown(message_text[last_offset * 2:
(entity.offset - offset) * 2]
.decode('utf-16-le'),
version=version) + insert
else:
if sys.maxunicode == 0xffff:
markdown_text += message_text[last_offset:entity.offset - offset] + insert
else:
markdown_text += message_text[last_offset * 2:(entity.offset
- offset) * 2].decode('utf-16-le') + insert
last_offset = entity.offset - offset + entity.length
if offset == 0:
if sys.maxunicode == 0xffff:
markdown_text += escape_markdown(message_text[last_offset:entity.offset]) + insert
markdown_text += escape_markdown(message_text[last_offset:], version=version)
else:
markdown_text += escape_markdown(message_text[last_offset * 2:entity.offset * 2]
.decode('utf-16-le')) + insert
last_offset = entity.offset + entity.length
if sys.maxunicode == 0xffff:
markdown_text += escape_markdown(message_text[last_offset:])
markdown_text += escape_markdown(message_text[last_offset * 2:]
.decode('utf-16-le'), version=version)
else:
markdown_text += escape_markdown(message_text[last_offset * 2:].decode('utf-16-le'))
if sys.maxunicode == 0xffff:
markdown_text += message_text[last_offset:]
else:
markdown_text += message_text[last_offset * 2:].decode('utf-16-le')
return markdown_text
@property
def text_markdown(self):
"""Creates an Markdown-formatted string from the markup entities found in the message.
"""Creates an Markdown-formatted string from the markup entities found in the message
using :class:`telegram.ParseMode.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.
@ -1167,9 +1301,24 @@ class Message(TelegramObject):
"""
return self._parse_markdown(self.text, self.parse_entities(), urled=False)
@property
def text_markdown_v2(self):
"""Creates an Markdown-formatted string from the markup entities found in the message
using :class:`telegram.ParseMode.MARKDOWN_V2`.
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.
Returns:
:obj:`str`: Message text with entities formatted as Markdown.
"""
return self._parse_markdown(self.text, self.parse_entities(), urled=False, version=2)
@property
def text_markdown_urled(self):
"""Creates an Markdown-formatted string from the markup entities found in the message.
"""Creates an Markdown-formatted string from the markup entities found in the message
using :class:`telegram.ParseMode.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.
@ -1180,10 +1329,24 @@ class Message(TelegramObject):
"""
return self._parse_markdown(self.text, self.parse_entities(), urled=True)
@property
def text_markdown_v2_urled(self):
"""Creates an Markdown-formatted string from the markup entities found in the message
using :class:`telegram.ParseMode.MARKDOWN_V2`.
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.
Returns:
:obj:`str`: Message text with entities formatted as Markdown.
"""
return self._parse_markdown(self.text, self.parse_entities(), urled=True, version=2)
@property
def caption_markdown(self):
"""Creates an Markdown-formatted string from the markup entities found in the message's
caption.
caption using :class:`telegram.ParseMode.MARKDOWN`.
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.
@ -1194,10 +1357,25 @@ class Message(TelegramObject):
"""
return self._parse_markdown(self.caption, self.parse_caption_entities(), urled=False)
@property
def caption_markdown_v2(self):
"""Creates an Markdown-formatted string from the markup entities found in the message's
caption using :class:`telegram.ParseMode.MARKDOWN_V2`.
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.
Returns:
:obj:`str`: Message caption with caption entities formatted as Markdown.
"""
return self._parse_markdown(self.caption, self.parse_caption_entities(),
urled=False, version=2)
@property
def caption_markdown_urled(self):
"""Creates an Markdown-formatted string from the markup entities found in the message's
caption.
caption using :class:`telegram.ParseMode.MARKDOWN`.
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.
@ -1207,3 +1385,18 @@ class Message(TelegramObject):
"""
return self._parse_markdown(self.caption, self.parse_caption_entities(), urled=True)
@property
def caption_markdown_v2_urled(self):
"""Creates an Markdown-formatted string from the markup entities found in the message's
caption using :class:`telegram.ParseMode.MARKDOWN_V2`.
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.
Returns:
:obj:`str`: Message caption with caption entities formatted as Markdown.
"""
return self._parse_markdown(self.caption, self.parse_caption_entities(),
urled=True, version=2)

View file

@ -105,8 +105,12 @@ class MessageEntity(TelegramObject):
""":obj:`str`: 'text_link'"""
TEXT_MENTION = 'text_mention'
""":obj:`str`: 'text_mention'"""
UNDERLINE = 'underline'
""":obj:`str`: 'underline'"""
STRIKETHROUGH = 'strikethrough'
""":obj:`str`: 'strikethrough'"""
ALL_TYPES = [
MENTION, HASHTAG, CASHTAG, PHONE_NUMBER, BOT_COMMAND, URL,
EMAIL, BOLD, ITALIC, CODE, PRE, TEXT_LINK, TEXT_MENTION
EMAIL, BOLD, ITALIC, CODE, PRE, TEXT_LINK, TEXT_MENTION, UNDERLINE, STRIKETHROUGH
]
"""List[:obj:`str`]: List of all the types."""

View file

@ -25,5 +25,7 @@ class ParseMode(object):
MARKDOWN = 'Markdown'
""":obj:`str`: 'Markdown'"""
MARKDOWN_V2 = 'MarkdownV2'
""":obj:`str`: 'MarkdownV2'"""
HTML = 'HTML'
""":obj:`str`: 'HTML'"""

View file

@ -28,12 +28,18 @@ class PassportFile(TelegramObject):
Attributes:
file_id (:obj:`str`): Unique identifier for this file.
file_unique_id (:obj:`str`): Unique identifier for this file, which
is supposed to be the same over time and for different bots.
Can't be used to download or reuse the file.
file_size (:obj:`int`): File size.
file_date (:obj:`int`): Unix time when the file was uploaded.
bot (:class:`telegram.Bot`): Optional. The Bot to use for instance methods.
Args:
file_id (:obj:`str`): Unique identifier for this file.
file_id (:obj:`str`): Identifier for this file, which can be used to download
or reuse the file.
file_unique_id (:obj:`str`): Unique and the same over time and
for different bots file identifier.
file_size (:obj:`int`): File size.
file_date (:obj:`int`): Unix time when the file was uploaded.
bot (:class:`telegram.Bot`, optional): The Bot to use for instance methods.
@ -41,16 +47,24 @@ class PassportFile(TelegramObject):
"""
def __init__(self, file_id, file_date, file_size=None, bot=None, credentials=None, **kwargs):
def __init__(self,
file_id,
file_unique_id,
file_date,
file_size=None,
bot=None,
credentials=None,
**kwargs):
# Required
self.file_id = file_id
self.file_unique_id = file_unique_id
self.file_size = file_size
self.file_date = file_date
# Optionals
self.bot = bot
self._credentials = credentials
self._id_attrs = (self.file_id,)
self._id_attrs = (self.file_unique_id,)
@classmethod
def de_json(cls, data, bot):

View file

@ -131,13 +131,26 @@ class User(TelegramObject):
name (:obj:`str`): The name used as a link for the user. Defaults to :attr:`full_name`.
Returns:
:obj:`str`: The inline mention for the user as markdown.
:obj:`str`: The inline mention for the user as markdown (version 1).
"""
if name:
return util_mention_markdown(self.id, name)
return util_mention_markdown(self.id, self.full_name)
def mention_markdown_v2(self, name=None):
"""
Args:
name (:obj:`str`): The name used as a link for the user. Defaults to :attr:`full_name`.
Returns:
:obj:`str`: The inline mention for the user as markdown (version 2).
"""
if name:
return util_mention_markdown(self.id, name, version=2)
return util_mention_markdown(self.id, self.full_name, version=2)
def mention_html(self, name=None):
"""
Args:

View file

@ -44,9 +44,31 @@ def get_signal_name(signum):
return _signames[signum]
def escape_markdown(text):
"""Helper function to escape telegram markup symbols."""
escape_chars = '\*_`\['
def escape_markdown(text, version=1, entity_type=None):
"""
Helper function to escape telegram markup symbols.
Args:
text (:obj:`str`): The text.
version (:obj:`int` | :obj:`str`): Use to specify the version of telegrams Markdown.
Either ``1`` or ``2``. Defaults to ``1``.
entity_type (:obj:`str`, optional): For the entity types ``PRE``, ``CODE`` and the link
part of ``TEXT_LINKS``, only certain characters need to be escaped in ``MarkdownV2``.
See the official API documentation for details. Only valid in combination with
``version=2``, will be ignored else.
"""
if int(version) == 1:
escape_chars = '\*_`\['
elif int(version) == 2:
if entity_type == 'pre' or entity_type == 'code':
escape_chars = '`\\\\'
elif entity_type == 'text_link':
escape_chars = ')\\\\'
else:
escape_chars = '_*\[\]()~`>\#\+\-=|{}\.!'
else:
raise ValueError('Markdown version musst be either 1 or 2!')
return re.sub(r'([%s])' % escape_chars, r'\\\1', text)
@ -207,17 +229,19 @@ def mention_html(user_id, name):
return u'<a href="tg://user?id={}">{}</a>'.format(user_id, escape(name))
def mention_markdown(user_id, name):
def mention_markdown(user_id, name, version=1):
"""
Args:
user_id (:obj:`int`) The user's id which you want to mention.
name (:obj:`str`) The name the mention is showing.
version (:obj:`int` | :obj:`str`): Use to specify the version of telegrams Markdown.
Either ``1`` or ``2``. Defaults to ``1``
Returns:
:obj:`str`: The inline mention for the user as markdown.
"""
if isinstance(user_id, int):
return u'[{}](tg://user?id={})'.format(escape_markdown(name), user_id)
return u'[{}](tg://user?id={})'.format(escape_markdown(name, version=version), user_id)
def effective_message_type(entity):

View file

@ -41,6 +41,7 @@ def animation(bot, chat_id):
class TestAnimation(object):
animation_file_id = 'CgADAQADngIAAuyVeEez0xRovKi9VAI'
animation_file_unique_id = 'adc3145fd2e84d95b64d68eaa22aa33e'
width = 320
height = 180
duration = 1
@ -55,7 +56,9 @@ class TestAnimation(object):
def test_creation(self, animation):
assert isinstance(animation, Animation)
assert isinstance(animation.file_id, str)
assert isinstance(animation.file_unique_id, str)
assert animation.file_id != ''
assert animation.file_unique_id != ''
def test_expected_values(self, animation):
assert animation.file_size == self.file_size
@ -73,7 +76,9 @@ class TestAnimation(object):
assert isinstance(message.animation, Animation)
assert isinstance(message.animation.file_id, str)
assert isinstance(message.animation.file_unique_id, str)
assert message.animation.file_id != ''
assert message.animation.file_unique_id != ''
assert message.animation.file_name == animation.file_name
assert message.animation.mime_type == animation.mime_type
assert message.animation.file_size == animation.file_size
@ -103,8 +108,12 @@ class TestAnimation(object):
assert isinstance(message.animation, Animation)
assert isinstance(message.animation.file_id, str)
assert message.animation.file_id is not None
assert isinstance(message.animation.file_unique_id, str)
assert message.animation.file_id != ''
assert message.animation.file_unique_id != ''
assert message.animation.duration == animation.duration
assert message.animation.file_name == animation.file_name
assert message.animation.mime_type == animation.mime_type
assert message.animation.file_size == animation.file_size
@ -143,7 +152,6 @@ class TestAnimation(object):
@flaky(3, 1)
@pytest.mark.timeout(10)
@pytest.mark.skip(reason='Doesnt work without API 4.5')
def test_resend(self, bot, chat_id, animation):
message = bot.send_animation(chat_id, animation.file_id)
@ -160,6 +168,7 @@ class TestAnimation(object):
def test_de_json(self, bot, animation):
json_dict = {
'file_id': self.animation_file_id,
'file_unique_id': self.animation_file_unique_id,
'width': self.width,
'height': self.height,
'duration': self.duration,
@ -170,6 +179,7 @@ class TestAnimation(object):
}
animation = Animation.de_json(json_dict, bot)
assert animation.file_id == self.animation_file_id
assert animation.file_unique_id == self.animation_file_unique_id
assert animation.thumb == animation.thumb
assert animation.file_name == self.file_name
assert animation.mime_type == self.mime_type
@ -180,6 +190,7 @@ class TestAnimation(object):
assert isinstance(animation_dict, dict)
assert animation_dict['file_id'] == animation.file_id
assert animation_dict['file_unique_id'] == animation.file_unique_id
assert animation_dict['width'] == animation.width
assert animation_dict['height'] == animation.height
assert animation_dict['duration'] == animation.duration
@ -214,10 +225,12 @@ class TestAnimation(object):
assert animation.get_file()
def test_equality(self):
a = Animation(self.animation_file_id, self.height, self.width, self.duration)
b = Animation(self.animation_file_id, self.height, self.width, self.duration)
d = Animation('', 0, 0, 0)
e = Voice(self.animation_file_id, 0)
a = Animation(self.animation_file_id, self.animation_file_unique_id,
self.height, self.width, self.duration)
b = Animation('', self.animation_file_unique_id,
self.height, self.width, self.duration)
d = Animation('', '', 0, 0, 0)
e = Voice(self.animation_file_id, self.animation_file_unique_id, 0)
assert a == b
assert hash(a) == hash(b)

View file

@ -52,12 +52,16 @@ class TestAudio(object):
thumb_file_size = 1427
thumb_width = 50
thumb_height = 50
audio_file_id = '5a3128a4d2a04750b5b58397f3b5e812'
audio_file_unique_id = 'adc3145fd2e84d95b64d68eaa22aa33e'
def test_creation(self, audio):
# Make sure file has been uploaded.
assert isinstance(audio, Audio)
assert isinstance(audio.file_id, str)
assert isinstance(audio.file_unique_id, str)
assert audio.file_id != ''
assert audio.file_unique_id != ''
def test_expected_values(self, audio):
assert audio.duration == self.duration
@ -81,6 +85,8 @@ class TestAudio(object):
assert isinstance(message.audio, Audio)
assert isinstance(message.audio.file_id, str)
assert isinstance(message.audio.file_unique_id, str)
assert message.audio.file_unique_id is not None
assert message.audio.file_id is not None
assert message.audio.duration == self.duration
assert message.audio.performer == self.performer
@ -98,6 +104,7 @@ class TestAudio(object):
assert new_file.file_size == self.file_size
assert new_file.file_id == audio.file_id
assert new_file.file_unique_id == audio.file_unique_id
assert new_file.file_path.startswith('https://')
new_file.download('telegram.mp3')
@ -113,6 +120,8 @@ class TestAudio(object):
assert isinstance(message.audio, Audio)
assert isinstance(message.audio.file_id, str)
assert isinstance(message.audio.file_unique_id, str)
assert message.audio.file_unique_id is not None
assert message.audio.file_id is not None
assert message.audio.duration == audio.duration
assert message.audio.mime_type == audio.mime_type
@ -120,7 +129,6 @@ class TestAudio(object):
@flaky(3, 1)
@pytest.mark.timeout(10)
@pytest.mark.skip(reason='Doesnt work without API 4.5')
def test_resend(self, bot, chat_id, audio):
message = bot.send_audio(chat_id=chat_id, audio=audio.file_id)
@ -168,17 +176,21 @@ class TestAudio(object):
assert message.caption_markdown == escape_markdown(test_markdown_string)
def test_de_json(self, bot, audio):
json_dict = {'file_id': 'not a file id',
'duration': self.duration,
'performer': self.performer,
'title': self.title,
'caption': self.caption,
'mime_type': self.mime_type,
'file_size': self.file_size,
'thumb': audio.thumb.to_dict()}
json_dict = {
'file_id': self.audio_file_id,
'file_unique_id': self.audio_file_unique_id,
'duration': self.duration,
'performer': self.performer,
'title': self.title,
'caption': self.caption,
'mime_type': self.mime_type,
'file_size': self.file_size,
'thumb': audio.thumb.to_dict()
}
json_audio = Audio.de_json(json_dict, bot)
assert json_audio.file_id == 'not a file id'
assert json_audio.file_id == self.audio_file_id
assert json_audio.file_unique_id == self.audio_file_unique_id
assert json_audio.duration == self.duration
assert json_audio.performer == self.performer
assert json_audio.title == self.title
@ -191,6 +203,7 @@ class TestAudio(object):
assert isinstance(audio_dict, dict)
assert audio_dict['file_id'] == audio.file_id
assert audio_dict['file_unique_id'] == audio.file_unique_id
assert audio_dict['duration'] == audio.duration
assert audio_dict['mime_type'] == audio.mime_type
assert audio_dict['file_size'] == audio.file_size
@ -221,11 +234,11 @@ class TestAudio(object):
assert audio.get_file()
def test_equality(self, audio):
a = Audio(audio.file_id, audio.duration)
b = Audio(audio.file_id, audio.duration)
c = Audio(audio.file_id, 0)
d = Audio('', audio.duration)
e = Voice(audio.file_id, audio.duration)
a = Audio(audio.file_id, audio.file_unique_id, audio.duration)
b = Audio('', audio.file_unique_id, audio.duration)
c = Audio(audio.file_id, audio.file_unique_id, 0)
d = Audio('', '', audio.duration)
e = Voice(audio.file_id, audio.file_unique_id, audio.duration)
assert a == b
assert hash(a) == hash(b)

View file

@ -349,6 +349,16 @@ class TestBot(object):
assert bot.set_chat_permissions(2, chat_permissions)
def test_set_chat_administrator_custom_title(self, monkeypatch, bot):
def test(_, url, data, *args, **kwargs):
chat_id = data['chat_id'] == 2
user_id = data['user_id'] == 32
custom_title = data['custom_title'] == 'custom_title'
return chat_id and user_id and custom_title
monkeypatch.setattr('telegram.utils.request.Request.post', test)
assert bot.set_chat_administrator_custom_title(2, 32, 'custom_title')
# TODO: Needs improvement. Need an incoming callbackquery to test
def test_answer_callback_query(self, monkeypatch, bot):
# For now just test that our internals pass the correct data

View file

@ -138,7 +138,7 @@ class TestCallbackQuery(object):
b = CallbackQuery(self.id_, self.from_user, 'chat')
c = CallbackQuery(self.id_, None, '')
d = CallbackQuery('', None, 'chat')
e = Audio(self.id_, 1)
e = Audio(self.id_, 'unique_id', 1)
assert a == b
assert hash(a) == hash(b)

View file

@ -29,7 +29,8 @@ def chat(bot):
all_members_are_administrators=TestChat.all_members_are_administrators,
bot=bot, sticker_set_name=TestChat.sticker_set_name,
can_set_sticker_set=TestChat.can_set_sticker_set,
permissions=TestChat.permissions)
permissions=TestChat.permissions,
slow_mode_delay=TestChat.slow_mode_delay)
class TestChat(object):
@ -45,6 +46,7 @@ class TestChat(object):
can_change_info=False,
can_invite_users=True,
)
slow_mode_delay = 30
def test_de_json(self, bot):
json_dict = {
@ -55,7 +57,8 @@ class TestChat(object):
'all_members_are_administrators': self.all_members_are_administrators,
'sticker_set_name': self.sticker_set_name,
'can_set_sticker_set': self.can_set_sticker_set,
'permissions': self.permissions.to_dict()
'permissions': self.permissions.to_dict(),
'slow_mode_delay': self.slow_mode_delay
}
chat = Chat.de_json(json_dict, bot)
@ -67,6 +70,7 @@ class TestChat(object):
assert chat.sticker_set_name == self.sticker_set_name
assert chat.can_set_sticker_set == self.can_set_sticker_set
assert chat.permissions == self.permissions
assert chat.slow_mode_delay == self.slow_mode_delay
def test_de_json_default_quote(self, bot):
json_dict = {
@ -94,6 +98,7 @@ class TestChat(object):
assert chat_dict['username'] == chat.username
assert chat_dict['all_members_are_administrators'] == chat.all_members_are_administrators
assert chat_dict['permissions'] == chat.permissions.to_dict()
assert chat_dict['slow_mode_delay'] == chat.slow_mode_delay
def test_link(self, chat):
assert chat.link == 'https://t.me/{}'.format(chat.username)
@ -167,6 +172,16 @@ class TestChat(object):
monkeypatch.setattr(chat.bot, 'set_chat_permissions', test)
assert chat.set_permissions(self.permissions)
def test_set_administrator_custom_title(self, monkeypatch, chat):
def test(*args, **kwargs):
chat_id = args[1] == chat.id
user_id = args[2] == 42
custom_title = args[3] == 'custom_title'
return chat_id and user_id and custom_title
monkeypatch.setattr('telegram.Bot.set_chat_administrator_custom_title', test)
assert chat.set_administrator_custom_title(42, 'custom_title')
def test_instance_method_send_message(self, monkeypatch, chat):
def test(*args, **kwargs):
return args[0] == chat.id and args[1] == 'test'

View file

@ -47,8 +47,11 @@ class TestChatMember(object):
def test_de_json_all_args(self, bot, user):
time = datetime.datetime.utcnow()
custom_title = 'custom_title'
json_dict = {'user': user.to_dict(),
'status': self.status,
'custom_title': custom_title,
'until_date': to_timestamp(time),
'can_be_edited': False,
'can_change_info': True,
@ -69,6 +72,7 @@ class TestChatMember(object):
assert chat_member.user == user
assert chat_member.status == self.status
assert chat_member.custom_title == custom_title
assert chat_member.can_be_edited is False
assert chat_member.can_change_info is True
assert chat_member.can_post_messages is False

155
tests/test_chatphoto.py Normal file
View file

@ -0,0 +1,155 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2020
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser Public License for more details.
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
import os
import pytest
from flaky import flaky
from telegram import ChatPhoto, Voice, TelegramError
@pytest.fixture(scope='function')
def chatphoto_file():
f = open('tests/data/telegram.jpg', 'rb')
yield f
f.close()
@pytest.fixture(scope='function')
def chat_photo(bot, super_group_id):
return bot.get_chat(super_group_id, timeout=50).photo
class TestChatPhoto(object):
chatphoto_small_file_id = 'smallCgADAQADngIAAuyVeEez0xRovKi9VAI'
chatphoto_big_file_id = 'bigCgADAQADngIAAuyVeEez0xRovKi9VAI'
chatphoto_small_file_unique_id = 'smalladc3145fd2e84d95b64d68eaa22aa33e'
chatphoto_big_file_unique_id = 'bigadc3145fd2e84d95b64d68eaa22aa33e'
chatphoto_file_url = 'https://python-telegram-bot.org/static/testfiles/telegram.jpg'
@flaky(3, 1)
@pytest.mark.timeout(10)
def test_send_all_args(self, bot, super_group_id, chatphoto_file, chat_photo, thumb_file):
assert bot.set_chat_photo(super_group_id, chatphoto_file)
@flaky(3, 1)
@pytest.mark.timeout(10)
def test_get_and_download(self, bot, chat_photo):
new_file = bot.get_file(chat_photo.small_file_id)
assert new_file.file_id == chat_photo.small_file_id
assert new_file.file_path.startswith('https://')
new_file.download('telegram.jpg')
assert os.path.isfile('telegram.jpg')
new_file = bot.get_file(chat_photo.big_file_id)
assert new_file.file_id == chat_photo.big_file_id
assert new_file.file_path.startswith('https://')
new_file.download('telegram.jpg')
assert os.path.isfile('telegram.jpg')
def test_send_with_chat_photo(self, monkeypatch, bot, super_group_id, chat_photo):
def test(_, url, data, **kwargs):
return data['photo'] == chat_photo
monkeypatch.setattr('telegram.utils.request.Request.post', test)
message = bot.set_chat_photo(photo=chat_photo, chat_id=super_group_id)
assert message
def test_de_json(self, bot, chat_photo):
json_dict = {
'small_file_id': self.chatphoto_small_file_id,
'big_file_id': self.chatphoto_big_file_id,
'small_file_unique_id': self.chatphoto_small_file_unique_id,
'big_file_unique_id': self.chatphoto_big_file_unique_id,
}
chat_photo = ChatPhoto.de_json(json_dict, bot)
assert chat_photo.small_file_id == self.chatphoto_small_file_id
assert chat_photo.big_file_id == self.chatphoto_big_file_id
assert chat_photo.small_file_unique_id == self.chatphoto_small_file_unique_id
assert chat_photo.big_file_unique_id == self.chatphoto_big_file_unique_id
def test_to_dict(self, chat_photo):
chat_photo_dict = chat_photo.to_dict()
assert isinstance(chat_photo_dict, dict)
assert chat_photo_dict['small_file_id'] == chat_photo.small_file_id
assert chat_photo_dict['big_file_id'] == chat_photo.big_file_id
assert chat_photo_dict['small_file_unique_id'] == chat_photo.small_file_unique_id
assert chat_photo_dict['big_file_unique_id'] == chat_photo.big_file_unique_id
@flaky(3, 1)
@pytest.mark.timeout(10)
def test_error_send_empty_file(self, bot, super_group_id):
chatphoto_file = open(os.devnull, 'rb')
with pytest.raises(TelegramError):
bot.set_chat_photo(chat_id=super_group_id, photo=chatphoto_file)
@flaky(3, 1)
@pytest.mark.timeout(10)
def test_error_send_empty_file_id(self, bot, super_group_id):
with pytest.raises(TelegramError):
bot.set_chat_photo(chat_id=super_group_id, photo='')
def test_error_send_without_required_args(self, bot, super_group_id):
with pytest.raises(TypeError):
bot.set_chat_photo(chat_id=super_group_id)
def test_get_small_file_instance_method(self, monkeypatch, chat_photo):
def test(*args, **kwargs):
return args[1] == chat_photo.small_file_id
monkeypatch.setattr('telegram.Bot.get_file', test)
assert chat_photo.get_small_file()
def test_get_big_file_instance_method(self, monkeypatch, chat_photo):
def test(*args, **kwargs):
return args[1] == chat_photo.big_file_id
monkeypatch.setattr('telegram.Bot.get_file', test)
assert chat_photo.get_big_file()
def test_equality(self):
a = ChatPhoto(self.chatphoto_small_file_id, self.chatphoto_big_file_id,
self.chatphoto_small_file_unique_id, self.chatphoto_big_file_unique_id)
b = ChatPhoto(self.chatphoto_small_file_id, self.chatphoto_big_file_id,
self.chatphoto_small_file_unique_id, self.chatphoto_big_file_unique_id)
c = ChatPhoto('', '', self.chatphoto_small_file_unique_id,
self.chatphoto_big_file_unique_id)
d = ChatPhoto('', '', 0, 0)
e = Voice(self.chatphoto_small_file_id, self.chatphoto_small_file_unique_id, 0)
assert a == b
assert hash(a) == hash(b)
assert a is not b
assert a != c
assert hash(a) != hash(c)
assert a != d
assert hash(a) != hash(d)
assert a != e
assert hash(a) != hash(e)

View file

@ -74,7 +74,7 @@ class TestChosenInlineResult(object):
b = ChosenInlineResult(self.result_id, user, 'Query', '')
c = ChosenInlineResult(self.result_id, user, '', '')
d = ChosenInlineResult('', user, 'Query', '')
e = Voice(self.result_id, 0)
e = Voice(self.result_id, 'unique_id', 0)
assert a == b
assert hash(a) == hash(b)

View file

@ -80,7 +80,7 @@ class TestContact(object):
b = Contact(self.phone_number, self.first_name)
c = Contact(self.phone_number, '')
d = Contact('', self.first_name)
e = Voice('', 0)
e = Voice('', 'unique_id', 0)
assert a == b
assert hash(a) == hash(b)

View file

@ -47,11 +47,15 @@ class TestDocument(object):
thumb_file_size = 8090
thumb_width = 300
thumb_height = 300
document_file_id = '5a3128a4d2a04750b5b58397f3b5e812'
document_file_unique_id = 'adc3145fd2e84d95b64d68eaa22aa33e'
def test_creation(self, document):
assert isinstance(document, Document)
assert isinstance(document.file_id, str)
assert isinstance(document.file_unique_id, str)
assert document.file_id != ''
assert document.file_unique_id != ''
def test_expected_values(self, document):
assert document.file_size == self.file_size
@ -71,6 +75,8 @@ class TestDocument(object):
assert isinstance(message.document, Document)
assert isinstance(message.document.file_id, str)
assert message.document.file_id != ''
assert isinstance(message.document.file_unique_id, str)
assert message.document.file_unique_id != ''
assert isinstance(message.document.thumb, PhotoSize)
assert message.document.file_name == 'telegram_custom.png'
assert message.document.mime_type == document.mime_type
@ -86,6 +92,7 @@ class TestDocument(object):
assert new_file.file_size == document.file_size
assert new_file.file_id == document.file_id
assert new_file.file_unique_id == document.file_unique_id
assert new_file.file_path.startswith('https://')
new_file.download('telegram.png')
@ -102,6 +109,8 @@ class TestDocument(object):
assert isinstance(document, Document)
assert isinstance(document.file_id, str)
assert document.file_id != ''
assert isinstance(message.document.file_unique_id, str)
assert message.document.file_unique_id != ''
assert isinstance(document.thumb, PhotoSize)
assert document.file_name == 'telegram.gif'
assert document.mime_type == 'image/gif'
@ -109,7 +118,6 @@ class TestDocument(object):
@flaky(3, 1)
@pytest.mark.timeout(10)
@pytest.mark.skip(reason='Doesnt work without API 4.5')
def test_send_resend(self, bot, chat_id, document):
message = bot.send_document(chat_id=chat_id, document=document.file_id)
@ -159,15 +167,18 @@ class TestDocument(object):
assert message.caption_markdown == escape_markdown(test_markdown_string)
def test_de_json(self, bot, document):
json_dict = {'file_id': 'not a file id',
'thumb': document.thumb.to_dict(),
'file_name': self.file_name,
'mime_type': self.mime_type,
'file_size': self.file_size
}
json_dict = {
'file_id': self.document_file_id,
'file_unique_id': self.document_file_unique_id,
'thumb': document.thumb.to_dict(),
'file_name': self.file_name,
'mime_type': self.mime_type,
'file_size': self.file_size
}
test_document = Document.de_json(json_dict, bot)
assert test_document.file_id == 'not a file id'
assert test_document.file_id == self.document_file_id
assert test_document.file_unique_id == self.document_file_unique_id
assert test_document.thumb == document.thumb
assert test_document.file_name == self.file_name
assert test_document.mime_type == self.mime_type
@ -178,6 +189,7 @@ class TestDocument(object):
assert isinstance(document_dict, dict)
assert document_dict['file_id'] == document.file_id
assert document_dict['file_unique_id'] == document.file_unique_id
assert document_dict['file_name'] == document.file_name
assert document_dict['mime_type'] == document.mime_type
assert document_dict['file_size'] == document.file_size
@ -207,10 +219,10 @@ class TestDocument(object):
assert document.get_file()
def test_equality(self, document):
a = Document(document.file_id)
b = Document(document.file_id)
d = Document('')
e = Voice(document.file_id, 0)
a = Document(document.file_id, document.file_unique_id)
b = Document('', document.file_unique_id)
d = Document('', '')
e = Voice(document.file_id, document.file_unique_id, 0)
assert a == b
assert hash(a) == hash(b)

View file

@ -29,6 +29,7 @@ from telegram import File, TelegramError, Voice
@pytest.fixture(scope='class')
def file(bot):
return File(TestFile.file_id,
TestFile.file_unique_id,
file_path=TestFile.file_path,
file_size=TestFile.file_size,
bot=bot)
@ -36,6 +37,7 @@ def file(bot):
class TestFile(object):
file_id = 'NOTVALIDDOESNOTMATTER'
file_unique_id = 'adc3145fd2e84d95b64d68eaa22aa33e'
file_path = (
u'https://api.org/file/bot133505823:AAHZFMHno3mzVLErU5b5jJvaeG--qUyLyG0/document/file_3')
file_size = 28232
@ -44,12 +46,14 @@ class TestFile(object):
def test_de_json(self, bot):
json_dict = {
'file_id': self.file_id,
'file_unique_id': self.file_unique_id,
'file_path': self.file_path,
'file_size': self.file_size
}
new_file = File.de_json(json_dict, bot)
assert new_file.file_id == self.file_id
assert new_file.file_unique_id == self.file_unique_id
assert new_file.file_path == self.file_path
assert new_file.file_size == self.file_size
@ -58,6 +62,7 @@ class TestFile(object):
assert isinstance(file_dict, dict)
assert file_dict['file_id'] == file.file_id
assert file_dict['file_unique_id'] == file.file_unique_id
assert file_dict['file_path'] == file.file_path
assert file_dict['file_size'] == file.file_size
@ -142,11 +147,11 @@ class TestFile(object):
assert buf2[:len(buf)] == buf
def test_equality(self, bot):
a = File(self.file_id, bot)
b = File(self.file_id, bot)
c = File(self.file_id, None)
d = File('', bot)
e = Voice(self.file_id, 0)
a = File(self.file_id, self.file_unique_id, bot)
b = File('', self.file_unique_id, bot)
c = File(self.file_id, self.file_unique_id, None)
d = File('', '', bot)
e = Voice(self.file_id, self.file_unique_id, 0)
assert a == b
assert hash(a) == hash(b)

View file

@ -300,7 +300,7 @@ class TestFilters(object):
assert Filters.document(update)
def test_filters_document_type(self, update):
update.message.document = Document("file_id",
update.message.document = Document("file_id", 'unique_id',
mime_type="application/vnd.android.package-archive")
assert Filters.document.apk(update)
assert Filters.document.application(update)

View file

@ -35,11 +35,11 @@ def game():
class TestGame(object):
title = 'Python-telegram-bot Test Game'
description = 'description'
photo = [PhotoSize('Blah', 640, 360, file_size=0)]
photo = [PhotoSize('Blah', 'ElseBlah', 640, 360, file_size=0)]
text = (b'\\U0001f469\\u200d\\U0001f469\\u200d\\U0001f467'
b'\\u200d\\U0001f467\\U0001f431http://google.com').decode('unicode-escape')
text_entities = [MessageEntity(13, 17, MessageEntity.URL)]
animation = Animation('blah', 320, 180, 1)
animation = Animation('blah', 'unique_id', 320, 180, 1)
def test_de_json_required(self, bot):
json_dict = {

View file

@ -24,6 +24,7 @@ import pytest
from telegram import Sticker
from telegram import Update
from telegram import User
from telegram import MessageEntity
from telegram.message import Message
from telegram.utils import helpers
from telegram.utils.helpers import _UtcOffsetTimezone, _datetime_to_float_timestamp
@ -46,6 +47,34 @@ class TestHelpers(object):
assert expected_str == helpers.escape_markdown(test_str)
def test_escape_markdown_v2(self):
test_str = 'a_b*c[d]e (fg) h~I`>JK#L+MN -O=|p{qr}s.t! u'
expected_str = 'a\_b\*c\[d\]e \(fg\) h\~I\`\>JK\#L\+MN \-O\=\|p\{qr\}s\.t\! u'
assert expected_str == helpers.escape_markdown(test_str, version=2)
def test_escape_markdown_v2_monospaced(self):
test_str = 'mono/pre: `abc` \int (`\some \`stuff)'
expected_str = 'mono/pre: \`abc\` \\\\int (\`\\\\some \\\\\`stuff)'
assert expected_str == helpers.escape_markdown(test_str, version=2,
entity_type=MessageEntity.PRE)
assert expected_str == helpers.escape_markdown(test_str, version=2,
entity_type=MessageEntity.CODE)
def test_escape_markdown_v2_text_link(self):
test_str = 'https://url.containing/funny)cha)\\ra\)cter\s'
expected_str = 'https://url.containing/funny\)cha\)\\\\ra\\\\\)cter\\\\s'
assert expected_str == helpers.escape_markdown(test_str, version=2,
entity_type=MessageEntity.TEXT_LINK)
def test_markdown_invalid_version(self):
with pytest.raises(ValueError):
helpers.escape_markdown('abc', version=-1)
def test_to_float_timestamp_absolute_naive(self):
"""Conversion from timezone-naive datetime to timestamp.
Naive datetimes should be assumed to be in UTC.
@ -161,7 +190,8 @@ class TestHelpers(object):
assert helpers.effective_message_type(test_message) == 'text'
test_message.text = None
test_message = build_test_message(sticker=Sticker('sticker_id', 50, 50, False))
test_message = build_test_message(sticker=Sticker('sticker_id', 'unique_id',
50, 50, False))
assert helpers.effective_message_type(test_message) == 'sticker'
test_message.sticker = None
@ -188,3 +218,8 @@ class TestHelpers(object):
expected = '[the name](tg://user?id=1)'
assert expected == helpers.mention_markdown(1, 'the name')
def test_mention_markdown_2(self):
expected = r'[the\_name](tg://user?id=1)'
assert expected == helpers.mention_markdown(1, 'the_name')

View file

@ -47,21 +47,21 @@ def message(bot):
{'caption': 'A message caption',
'caption_entities': [MessageEntity('bold', 1, 1),
MessageEntity('text_link', 4, 3)]},
{'audio': Audio('audio_id', 12),
{'audio': Audio('audio_id', 'unique_id', 12),
'caption': 'audio_file'},
{'document': Document('document_id'),
{'document': Document('document_id', 'unique_id'),
'caption': 'document_file'},
{'animation': Animation('animation_id', 30, 30, 1),
{'animation': Animation('animation_id', 'unique_id', 30, 30, 1),
'caption': 'animation_file'},
{'game': Game('my_game', 'just my game',
[PhotoSize('game_photo_id', 30, 30), ])},
{'photo': [PhotoSize('photo_id', 50, 50)],
[PhotoSize('game_photo_id', 'unique_id', 30, 30), ])},
{'photo': [PhotoSize('photo_id', 'unique_id', 50, 50)],
'caption': 'photo_file'},
{'sticker': Sticker('sticker_id', 50, 50, True)},
{'video': Video('video_id', 12, 12, 12),
{'sticker': Sticker('sticker_id', 'unique_id', 50, 50, True)},
{'video': Video('video_id', 'unique_id', 12, 12, 12),
'caption': 'video_file'},
{'voice': Voice('voice_id', 5)},
{'video_note': VideoNote('video_note_id', 20, 12)},
{'voice': Voice('voice_id', 'unique_id', 5)},
{'video_note': VideoNote('video_note_id', 'unique_id', 20, 12)},
{'new_chat_members': [User(55, 'new_user', False)]},
{'contact': Contact('phone_numner', 'contact_name')},
{'location': Location(-23.691288, 46.788279)},
@ -69,7 +69,7 @@ def message(bot):
'some place', 'right here')},
{'left_chat_member': User(33, 'kicked', False)},
{'new_chat_title': 'new title'},
{'new_chat_photo': [PhotoSize('photo_id', 50, 50)]},
{'new_chat_photo': [PhotoSize('photo_id', 'unique_id', 50, 50)]},
{'delete_chat_photo': True},
{'group_chat_created': True},
{'supergroup_chat_created': True},
@ -84,7 +84,7 @@ def message(bot):
{'connected_website': 'http://example.com/'},
{'forward_signature': 'some_forward_sign'},
{'author_signature': 'some_author_sign'},
{'photo': [PhotoSize('photo_id', 50, 50)],
{'photo': [PhotoSize('photo_id', 'unique_id', 50, 50)],
'caption': 'photo_file',
'media_group_id': 1234443322222},
{'passport_data': PassportData.de_json(RAW_PASSPORT_DATA, None)},
@ -119,14 +119,31 @@ class TestMessage(object):
date = datetime.utcnow()
chat = Chat(3, 'private')
test_entities = [{'length': 4, 'offset': 10, 'type': 'bold'},
{'length': 7, 'offset': 16, 'type': 'italic'},
{'length': 3, 'offset': 16, 'type': 'italic'},
{'length': 3, 'offset': 20, 'type': 'italic'},
{'length': 4, 'offset': 25, 'type': 'code'},
{'length': 5, 'offset': 31, 'type': 'text_link', 'url': 'http://github.com/'},
{'length': 5, 'offset': 31, 'type': 'text_link',
'url': 'http://github.com/ab_'},
{'length': 12, 'offset': 38, 'type': 'text_mention',
'user': User(123456789, 'mentioned user', False)},
{'length': 3, 'offset': 55, 'type': 'pre'},
{'length': 17, 'offset': 60, 'type': 'url'}]
test_text = 'Test for <bold, ita_lic, code, links, text-mention and pre. http://google.com'
{'length': 21, 'offset': 60, 'type': 'url'}]
test_text = 'Test for <bold, ita_lic, code, links, text-mention and pre. http://google.com/ab_'
test_entities_v2 = [{'length': 4, 'offset': 0, 'type': 'underline'},
{'length': 4, 'offset': 10, 'type': 'bold'},
{'length': 7, 'offset': 16, 'type': 'italic'},
{'length': 6, 'offset': 25, 'type': 'code'},
{'length': 5, 'offset': 33, 'type': 'text_link',
'url': 'http://github.com/abc\)def'},
{'length': 12, 'offset': 40, 'type': 'text_mention',
'user': User(123456789, 'mentioned user', False)},
{'length': 5, 'offset': 57, 'type': 'pre'},
{'length': 17, 'offset': 64, 'type': 'url'},
{'length': 36, 'offset': 86, 'type': 'italic'},
{'length': 24, 'offset': 91, 'type': 'bold'},
{'length': 4, 'offset': 101, 'type': 'strikethrough'}]
test_text_v2 = ('Test for <bold, ita_lic, \`code, links, text-mention and `\pre. '
'http://google.com and bold nested in strk nested in italic.')
test_message = Message(message_id=1,
from_user=None,
date=None,
@ -135,6 +152,16 @@ class TestMessage(object):
entities=[MessageEntity(**e) for e in test_entities],
caption=test_text,
caption_entities=[MessageEntity(**e) for e in test_entities])
test_message_v2 = Message(message_id=1,
from_user=None,
date=None,
chat=None,
text=test_text_v2,
entities=[MessageEntity(**e) for e in test_entities_v2],
caption=test_text_v2,
caption_entities=[
MessageEntity(**e) for e in test_entities_v2
])
def test_all_posibilities_de_json_and_to_dict(self, bot, message_params):
new = Message.de_json(message_params.to_dict(), bot)
@ -182,11 +209,12 @@ class TestMessage(object):
assert message.parse_caption_entities() == {entity: 'http://google.com', entity_2: 'h'}
def test_text_html_simple(self):
test_html_string = ('Test for &lt;<b>bold</b>, <i>ita_lic</i>, <code>code</code>, '
'<a href="http://github.com/">links</a>, '
test_html_string = ('<u>Test</u> for &lt;<b>bold</b>, <i>ita_lic</i>, <code>\`code</code>,'
' <a href="http://github.com/abc\)def">links</a>, '
'<a href="tg://user?id=123456789">text-mention</a> and '
'<pre>pre</pre>. http://google.com')
text_html = self.test_message.text_html
'<pre>`\pre</pre>. http://google.com '
'and <i>bold <b>nested in <s>strk</s> nested in</b> italic</i>.')
text_html = self.test_message_v2.text_html
assert text_html == test_html_string
def test_text_html_empty(self, message):
@ -195,32 +223,68 @@ class TestMessage(object):
assert message.text_html is None
def test_text_html_urled(self):
test_html_string = ('Test for &lt;<b>bold</b>, <i>ita_lic</i>, <code>code</code>, '
'<a href="http://github.com/">links</a>, '
test_html_string = ('<u>Test</u> for &lt;<b>bold</b>, <i>ita_lic</i>, <code>\`code</code>,'
' <a href="http://github.com/abc\)def">links</a>, '
'<a href="tg://user?id=123456789">text-mention</a> and '
'<pre>pre</pre>. <a href="http://google.com">http://google.com</a>')
text_html = self.test_message.text_html_urled
'<pre>`\pre</pre>. <a href="http://google.com">http://google.com</a> '
'and <i>bold <b>nested in <s>strk</s> nested in</b> italic</i>.')
text_html = self.test_message_v2.text_html_urled
assert text_html == test_html_string
def test_text_markdown_simple(self):
test_md_string = (r'Test for <*bold*, _ita\_lic_, `code`, [links](http://github.com/), '
'[text-mention](tg://user?id=123456789) and ```pre```. '
'http://google.com')
test_md_string = ('Test for <*bold*, _ita_\__lic_, `code`, [links](http://github.com/ab_),'
' [text-mention](tg://user?id=123456789) and ```\npre```. '
'http://google.com/ab\_')
text_markdown = self.test_message.text_markdown
assert text_markdown == test_md_string
def test_text_markdown_v2_simple(self):
test_md_string = (r'__Test__ for <*bold*, _ita\_lic_, `\\\`code`, '
'[links](http://github.com/abc\\\\\)def), '
'[text\-mention](tg://user?id=123456789) and ```\`\\\\pre```\. '
'http://google\.com and _bold *nested in ~strk~ nested in* italic_\.')
text_markdown = self.test_message_v2.text_markdown_v2
assert text_markdown == test_md_string
def test_text_markdown_new_in_v2(self, message):
message.text = 'test'
message.entities = [MessageEntity(MessageEntity.BOLD, offset=0, length=4),
MessageEntity(MessageEntity.ITALIC, offset=0, length=4)]
with pytest.raises(ValueError):
assert message.text_markdown
message.entities = [MessageEntity(MessageEntity.UNDERLINE, offset=0, length=4)]
with pytest.raises(ValueError):
message.text_markdown
message.entities = [MessageEntity(MessageEntity.STRIKETHROUGH, offset=0, length=4)]
with pytest.raises(ValueError):
message.text_markdown
message.entities = []
def test_text_markdown_empty(self, message):
message.text = None
message.caption = "test"
assert message.text_markdown is None
assert message.text_markdown_v2 is None
def test_text_markdown_urled(self):
test_md_string = (r'Test for <*bold*, _ita\_lic_, `code`, [links](http://github.com/), '
'[text-mention](tg://user?id=123456789) and ```pre```. '
'[http://google.com](http://google.com)')
test_md_string = ('Test for <*bold*, _ita_\__lic_, `code`, [links](http://github.com/ab_),'
' [text-mention](tg://user?id=123456789) and ```\npre```. '
'[http://google.com/ab_](http://google.com/ab_)')
text_markdown = self.test_message.text_markdown_urled
assert text_markdown == test_md_string
def test_text_markdown_v2_urled(self):
test_md_string = (r'__Test__ for <*bold*, _ita\_lic_, `\\\`code`, '
'[links](http://github.com/abc\\\\\)def), '
'[text\-mention](tg://user?id=123456789) and ```\`\\\\pre```\. '
'[http://google\.com](http://google.com) and _bold *nested in ~strk~ '
'nested in* italic_\.')
text_markdown = self.test_message_v2.text_markdown_v2_urled
assert text_markdown == test_md_string
def test_text_html_emoji(self):
text = b'\\U0001f469\\u200d\\U0001f469\\u200d ABC'.decode('unicode-escape')
expected = b'\\U0001f469\\u200d\\U0001f469\\u200d <b>ABC</b>'.decode('unicode-escape')
@ -238,11 +302,12 @@ class TestMessage(object):
assert expected == message.text_markdown
def test_caption_html_simple(self):
test_html_string = ('Test for &lt;<b>bold</b>, <i>ita_lic</i>, <code>code</code>, '
'<a href="http://github.com/">links</a>, '
test_html_string = ('<u>Test</u> for &lt;<b>bold</b>, <i>ita_lic</i>, <code>\`code</code>,'
' <a href="http://github.com/abc\)def">links</a>, '
'<a href="tg://user?id=123456789">text-mention</a> and '
'<pre>pre</pre>. http://google.com')
caption_html = self.test_message.caption_html
'<pre>`\pre</pre>. http://google.com '
'and <i>bold <b>nested in <s>strk</s> nested in</b> italic</i>.')
caption_html = self.test_message_v2.caption_html
assert caption_html == test_html_string
def test_caption_html_empty(self, message):
@ -251,32 +316,51 @@ class TestMessage(object):
assert message.caption_html is None
def test_caption_html_urled(self):
test_html_string = ('Test for &lt;<b>bold</b>, <i>ita_lic</i>, <code>code</code>, '
'<a href="http://github.com/">links</a>, '
test_html_string = ('<u>Test</u> for &lt;<b>bold</b>, <i>ita_lic</i>, <code>\`code</code>,'
' <a href="http://github.com/abc\)def">links</a>, '
'<a href="tg://user?id=123456789">text-mention</a> and '
'<pre>pre</pre>. <a href="http://google.com">http://google.com</a>')
caption_html = self.test_message.caption_html_urled
'<pre>`\pre</pre>. <a href="http://google.com">http://google.com</a> '
'and <i>bold <b>nested in <s>strk</s> nested in</b> italic</i>.')
caption_html = self.test_message_v2.caption_html_urled
assert caption_html == test_html_string
def test_caption_markdown_simple(self):
test_md_string = (r'Test for <*bold*, _ita\_lic_, `code`, [links](http://github.com/), '
'[text-mention](tg://user?id=123456789) and ```pre```. '
'http://google.com')
test_md_string = ('Test for <*bold*, _ita_\__lic_, `code`, [links](http://github.com/ab_),'
' [text-mention](tg://user?id=123456789) and ```\npre```. '
'http://google.com/ab\_')
caption_markdown = self.test_message.caption_markdown
assert caption_markdown == test_md_string
def test_caption_markdown_v2_simple(self):
test_md_string = (r'__Test__ for <*bold*, _ita\_lic_, `\\\`code`, '
'[links](http://github.com/abc\\\\\\)def), '
'[text\-mention](tg://user?id=123456789) and ```\`\\\\pre```\. '
'http://google\.com and _bold *nested in ~strk~ nested in* italic_\.')
caption_markdown = self.test_message_v2.caption_markdown_v2
assert caption_markdown == test_md_string
def test_caption_markdown_empty(self, message):
message.text = "test"
message.caption = None
assert message.caption_markdown is None
assert message.caption_markdown_v2 is None
def test_caption_markdown_urled(self):
test_md_string = (r'Test for <*bold*, _ita\_lic_, `code`, [links](http://github.com/), '
'[text-mention](tg://user?id=123456789) and ```pre```. '
'[http://google.com](http://google.com)')
test_md_string = ('Test for <*bold*, _ita_\__lic_, `code`, [links](http://github.com/ab_),'
' [text-mention](tg://user?id=123456789) and ```\npre```. '
'[http://google.com/ab_](http://google.com/ab_)')
caption_markdown = self.test_message.caption_markdown_urled
assert caption_markdown == test_md_string
def test_caption_markdown_v2_urled(self):
test_md_string = (r'__Test__ for <*bold*, _ita\_lic_, `\\\`code`, '
'[links](http://github.com/abc\\\\\\)def), '
'[text\-mention](tg://user?id=123456789) and ```\`\\\\pre```\. '
'[http://google\.com](http://google.com) and _bold *nested in ~strk~ '
'nested in* italic_\.')
caption_markdown = self.test_message_v2.caption_markdown_v2_urled
assert caption_markdown == test_md_string
def test_caption_html_emoji(self):
caption = b'\\U0001f469\\u200d\\U0001f469\\u200d ABC'.decode('unicode-escape')
expected = b'\\U0001f469\\u200d\\U0001f469\\u200d <b>ABC</b>'.decode('unicode-escape')
@ -359,9 +443,9 @@ class TestMessage(object):
assert message.reply_text('test', reply_to_message_id=message.message_id, quote=True)
def test_reply_markdown(self, monkeypatch, message):
test_md_string = (r'Test for <*bold*, _ita\_lic_, `code`, [links](http://github.com/), '
'[text-mention](tg://user?id=123456789) and ```pre```. '
'http://google.com')
test_md_string = ('Test for <*bold*, _ita_\__lic_, `code`, [links](http://github.com/ab_),'
' [text-mention](tg://user?id=123456789) and ```\npre```. '
'http://google.com/ab\_')
def test(*args, **kwargs):
cid = args[0] == message.chat_id
@ -383,11 +467,38 @@ class TestMessage(object):
reply_to_message_id=message.message_id,
quote=True)
def test_reply_markdown_v2(self, monkeypatch, message):
test_md_string = (r'__Test__ for <*bold*, _ita\_lic_, `\\\`code`, '
'[links](http://github.com/abc\\\\\)def), '
'[text\-mention](tg://user?id=123456789) and ```\`\\\\pre```\. '
'http://google\.com and _bold *nested in ~strk~ nested in* italic_\.')
def test(*args, **kwargs):
cid = args[0] == message.chat_id
markdown_text = args[1] == test_md_string
markdown_enabled = kwargs['parse_mode'] == ParseMode.MARKDOWN_V2
if kwargs.get('reply_to_message_id'):
reply = kwargs['reply_to_message_id'] == message.message_id
else:
reply = True
return all([cid, markdown_text, reply, markdown_enabled])
text_markdown = self.test_message_v2.text_markdown_v2
assert text_markdown == test_md_string
monkeypatch.setattr(message.bot, 'send_message', test)
assert message.reply_markdown_v2(self.test_message_v2.text_markdown_v2)
assert message.reply_markdown_v2(self.test_message_v2.text_markdown_v2, quote=True)
assert message.reply_markdown_v2(self.test_message_v2.text_markdown_v2,
reply_to_message_id=message.message_id,
quote=True)
def test_reply_html(self, monkeypatch, message):
test_html_string = ('Test for &lt;<b>bold</b>, <i>ita_lic</i>, <code>code</code>, '
'<a href="http://github.com/">links</a>, '
test_html_string = ('<u>Test</u> for &lt;<b>bold</b>, <i>ita_lic</i>, <code>\`code</code>,'
' <a href="http://github.com/abc\)def">links</a>, '
'<a href="tg://user?id=123456789">text-mention</a> and '
'<pre>pre</pre>. http://google.com')
'<pre>`\pre</pre>. http://google.com '
'and <i>bold <b>nested in <s>strk</s> nested in</b> italic</i>.')
def test(*args, **kwargs):
cid = args[0] == message.chat_id
@ -399,13 +510,13 @@ class TestMessage(object):
reply = True
return all([cid, html_text, reply, html_enabled])
text_html = self.test_message.text_html
text_html = self.test_message_v2.text_html
assert text_html == test_html_string
monkeypatch.setattr(message.bot, 'send_message', test)
assert message.reply_html(self.test_message.text_html)
assert message.reply_html(self.test_message.text_html, quote=True)
assert message.reply_html(self.test_message.text_html,
assert message.reply_html(self.test_message_v2.text_html)
assert message.reply_html(self.test_message_v2.text_html, quote=True)
assert message.reply_html(self.test_message_v2.text_html,
reply_to_message_id=message.message_id,
quote=True)

View file

@ -53,26 +53,35 @@ RAW_PASSPORT_DATA = {'credentials': {'hash': 'qB4hz2LMcXYhglwz6EvXMMyI3PURisWLXl
'type': 'personal_details'
}, {
'reverse_side': {'file_date': 1534074942,
'file_id': 'DgADBAADNQQAAtoagFPf4wwmFZdmyQI'},
'file_id': 'DgADBAADNQQAAtoagFPf4wwmFZdmyQI',
'file_unique_id': 'adc3145fd2e84d95b64d68eaa22aa33e'},
'translation': [{'file_size': 28640, 'file_date': 1535630933,
'file_id': 'DgADBAADswMAAisqQVAmooP-kVgLgAI'},
'file_id': 'DgADBAADswMAAisqQVAmooP-kVgLgAI',
'file_unique_id': '52a90d53d6064bb58feb582acdc3a324'},
{'file_size': 28672, 'file_date': 1535630933,
'file_id': 'DgADBAAD1QMAAnrpQFBMZsT3HysjwwI'}],
'file_id': 'DgADBAAD1QMAAnrpQFBMZsT3HysjwwI',
'file_unique_id': '7285f864d168441ba1f7d02146250432'}],
'front_side': {'file_size': 28624, 'file_date': 1534074942,
'file_id': 'DgADBAADxwMAApnQgVPK2-ckL2eXVAI'},
'file_id': 'DgADBAADxwMAApnQgVPK2-ckL2eXVAI',
'file_unique_id': 'd9d52a700cbb4a189a80104aa5978133'},
'type': 'driver_license',
'selfie': {'file_size': 28592, 'file_date': 1534074942,
'file_id': 'DgADBAADEQQAAkopgFNr6oi-wISRtAI'},
'file_id': 'DgADBAADEQQAAkopgFNr6oi-wISRtAI',
'file_unique_id': 'd4e390cca57b4da5a65322b304762a12'},
'data': 'eJUOFuY53QKmGqmBgVWlLBAQCUQJ79n405SX6M5aGFIIodOPQqnLYvMNqTwTrXGDlW+mVLZcbu+y8luLVO8WsJB/0SB7q5WaXn/IMt1G9lz5G/KMLIZG/x9zlnimsaQLg7u8srG6L4KZzv+xkbbHjZdETrxU8j0N/DoS4HvLMRSJAgeFUrY6v2YW9vSRg+fSxIqQy1jR2VKpzAT8OhOz7A=='
}, {
'translation': [{'file_size': 28480, 'file_date': 1535630939,
'file_id': 'DgADBAADyQUAAqyqQVC_eoX_KwNjJwI'},
'file_id': 'DgADBAADyQUAAqyqQVC_eoX_KwNjJwI',
'file_unique_id': '38b2877b443542cbaf520c6e36a33ac4'},
{'file_size': 28528, 'file_date': 1535630939,
'file_id': 'DgADBAADsQQAAubTQVDRO_FN3lOwWwI'}],
'file_id': 'DgADBAADsQQAAubTQVDRO_FN3lOwWwI',
'file_unique_id': 'f008ca48c44b4a47895ddbcd2f76741e'}],
'files': [{'file_size': 28640, 'file_date': 1534074988,
'file_id': 'DgADBAADLAMAAhwfgVMyfGa5Nr0LvAI'},
'file_id': 'DgADBAADLAMAAhwfgVMyfGa5Nr0LvAI',
'file_unique_id': 'b170748794834644baaa3ec57ee4ce7a'},
{'file_size': 28480, 'file_date': 1534074988,
'file_id': 'DgADBAADaQQAAsFxgVNVfLZuT-_3ZQI'}],
'file_id': 'DgADBAADaQQAAsFxgVNVfLZuT-_3ZQI',
'file_unique_id': '19a12ae34dca424b85e0308f706cee75'}],
'type': 'utility_bill'
}, {
'data': 'j9SksVkSj128DBtZA+3aNjSFNirzv+R97guZaMgae4Gi0oDVNAF7twPR7j9VSmPedfJrEwL3O889Ei+a5F1xyLLyEI/qEBljvL70GFIhYGitS0JmNabHPHSZrjOl8b4s/0Z0Px2GpLO5siusTLQonimdUvu4UPjKquYISmlKEKhtmGATy+h+JDjNCYuOkhakeNw0Rk0BHgj0C3fCb7WZNQSyVb+2GTu6caR6eXf/AFwFp0TV3sRz3h0WIVPW8bna',
@ -138,14 +147,23 @@ def passport_data(bot):
class TestPassport(object):
driver_license_selfie_file_id = 'DgADBAADEQQAAkopgFNr6oi-wISRtAI'
driver_license_selfie_file_unique_id = 'd4e390cca57b4da5a65322b304762a12'
driver_license_front_side_file_id = 'DgADBAADxwMAApnQgVPK2-ckL2eXVAI'
driver_license_front_side_file_unique_id = 'd9d52a700cbb4a189a80104aa5978133'
driver_license_reverse_side_file_id = 'DgADBAADNQQAAtoagFPf4wwmFZdmyQI'
driver_license_reverse_side_file_unique_id = 'adc3145fd2e84d95b64d68eaa22aa33e'
driver_license_translation_1_file_id = 'DgADBAADswMAAisqQVAmooP-kVgLgAI'
driver_license_translation_1_file_unique_id = '52a90d53d6064bb58feb582acdc3a324'
driver_license_translation_2_file_id = 'DgADBAAD1QMAAnrpQFBMZsT3HysjwwI'
driver_license_translation_2_file_unique_id = '7285f864d168441ba1f7d02146250432'
utility_bill_1_file_id = 'DgADBAADLAMAAhwfgVMyfGa5Nr0LvAI'
utility_bill_1_file_unique_id = 'b170748794834644baaa3ec57ee4ce7a'
utility_bill_2_file_id = 'DgADBAADaQQAAsFxgVNVfLZuT-_3ZQI'
utility_bill_2_file_unique_id = '19a12ae34dca424b85e0308f706cee75'
utility_bill_translation_1_file_id = 'DgADBAADyQUAAqyqQVC_eoX_KwNjJwI'
utility_bill_translation_1_file_unique_id = '38b2877b443542cbaf520c6e36a33ac4'
utility_bill_translation_2_file_id = 'DgADBAADsQQAAubTQVDRO_FN3lOwWwI'
utility_bill_translation_2_file_unique_id = 'f008ca48c44b4a47895ddbcd2f76741e'
driver_license_selfie_credentials_file_hash = 'Cila/qLXSBH7DpZFbb5bRZIRxeFW2uv/ulL0u0JNsYI='
driver_license_selfie_credentials_secret = 'tivdId6RNYNsvXYPppdzrbxOBuBOr9wXRPDcCvnXU7E='
@ -162,24 +180,40 @@ class TestPassport(object):
assert driver_license.data == RAW_PASSPORT_DATA['data'][1]['data']
assert isinstance(driver_license.selfie, PassportFile)
assert driver_license.selfie.file_id == self.driver_license_selfie_file_id
assert driver_license.selfie.file_unique_id == self.driver_license_selfie_file_unique_id
assert isinstance(driver_license.front_side, PassportFile)
assert driver_license.front_side.file_id == self.driver_license_front_side_file_id
assert driver_license.front_side.file_unique_id == self.driver_license_front_side_file_unique_id
assert isinstance(driver_license.reverse_side, PassportFile)
assert driver_license.reverse_side.file_id == self.driver_license_reverse_side_file_id
assert driver_license.reverse_side.file_unique_id == self.driver_license_reverse_side_file_unique_id
assert isinstance(driver_license.translation[0], PassportFile)
assert driver_license.translation[0].file_id == self.driver_license_translation_1_file_id
assert driver_license.translation[0].file_unique_id == self.driver_license_translation_1_file_unique_id
assert isinstance(driver_license.translation[1], PassportFile)
assert driver_license.translation[1].file_id == self.driver_license_translation_2_file_id
assert driver_license.translation[1].file_unique_id == self.driver_license_translation_2_file_unique_id
assert utility_bill.type == 'utility_bill'
assert isinstance(utility_bill.files[0], PassportFile)
assert utility_bill.files[0].file_id == self.utility_bill_1_file_id
assert utility_bill.files[0].file_unique_id == self.utility_bill_1_file_unique_id
assert isinstance(utility_bill.files[1], PassportFile)
assert utility_bill.files[1].file_id == self.utility_bill_2_file_id
assert utility_bill.files[1].file_unique_id == self.utility_bill_2_file_unique_id
assert isinstance(utility_bill.translation[0], PassportFile)
assert utility_bill.translation[0].file_id == self.utility_bill_translation_1_file_id
assert utility_bill.translation[0].file_unique_id == self.utility_bill_translation_1_file_unique_id
assert isinstance(utility_bill.translation[1], PassportFile)
assert utility_bill.translation[1].file_id == self.utility_bill_translation_2_file_id
assert utility_bill.translation[1].file_unique_id == self.utility_bill_translation_2_file_unique_id
assert address.type == 'address'
assert address.data == RAW_PASSPORT_DATA['data'][3]['data']
@ -208,10 +242,15 @@ class TestPassport(object):
'document_no': 'DOCUMENT_NO'}
assert isinstance(driver_license.selfie, PassportFile)
assert driver_license.selfie.file_id == self.driver_license_selfie_file_id
assert driver_license.selfie.file_unique_id == self.driver_license_selfie_file_unique_id
assert isinstance(driver_license.front_side, PassportFile)
assert driver_license.front_side.file_id == self.driver_license_front_side_file_id
assert driver_license.front_side.file_unique_id == self.driver_license_front_side_file_unique_id
assert isinstance(driver_license.reverse_side, PassportFile)
assert driver_license.reverse_side.file_id == self.driver_license_reverse_side_file_id
assert driver_license.reverse_side.file_unique_id == self.driver_license_reverse_side_file_unique_id
assert address.type == 'address'
assert address.data.to_dict() == {'city': 'CITY', 'street_line2': 'STREET_LINE2',
@ -221,8 +260,11 @@ class TestPassport(object):
assert utility_bill.type == 'utility_bill'
assert isinstance(utility_bill.files[0], PassportFile)
assert utility_bill.files[0].file_id == self.utility_bill_1_file_id
assert utility_bill.files[0].file_unique_id == self.utility_bill_1_file_unique_id
assert isinstance(utility_bill.files[1], PassportFile)
assert utility_bill.files[1].file_id == self.utility_bill_2_file_id
assert utility_bill.files[1].file_unique_id == self.utility_bill_2_file_unique_id
assert email.type == 'email'
assert email.email == 'fb3e3i47zt@dispostable.com'
@ -295,12 +337,14 @@ class TestPassport(object):
# TODO: Actually download a passport file in a test
selfie = passport_data.decrypted_data[1].selfie
# NOTE: file_unique_id is not used in the get_file method, so it is passed directly
def get_file(*args, **kwargs):
return File(args[0])
return File(args[0], selfie.file_unique_id)
monkeypatch.setattr(passport_data.bot, 'get_file', get_file)
file = selfie.get_file()
assert file.file_id == selfie.file_id
assert file.file_unique_id == selfie.file_unique_id
assert file._credentials.file_hash == self.driver_license_selfie_credentials_file_hash
assert file._credentials.secret == self.driver_license_selfie_credentials_secret

View file

@ -25,17 +25,20 @@ from telegram import PassportFile, PassportElementError
@pytest.fixture(scope='class')
def passport_file():
return PassportFile(file_id=TestPassportFile.file_id,
file_unique_id=TestPassportFile.file_unique_id,
file_size=TestPassportFile.file_size,
file_date=TestPassportFile.file_date)
class TestPassportFile(object):
file_id = 'data'
file_unique_id = 'adc3145fd2e84d95b64d68eaa22aa33e'
file_size = 50
file_date = 1532879128
def test_expected_values(self, passport_file):
assert passport_file.file_id == self.file_id
assert passport_file.file_unique_id == self.file_unique_id
assert passport_file.file_size == self.file_size
assert passport_file.file_date == self.file_date
@ -45,16 +48,18 @@ class TestPassportFile(object):
assert isinstance(passport_file_dict, dict)
assert (passport_file_dict['file_id']
== passport_file.file_id)
assert (passport_file_dict['file_unique_id']
== passport_file.file_unique_id)
assert (passport_file_dict['file_size']
== passport_file.file_size)
assert (passport_file_dict['file_date']
== passport_file.file_date)
def test_equality(self):
a = PassportFile(self.file_id, self.file_size, self.file_date)
b = PassportFile(self.file_id, self.file_size, self.file_date)
c = PassportFile(self.file_id, '', '')
d = PassportFile('', self.file_size, self.file_date)
a = PassportFile(self.file_id, self.file_unique_id, self.file_size, self.file_date)
b = PassportFile('', self.file_unique_id, self.file_size, self.file_date)
c = PassportFile(self.file_id, self.file_unique_id, '', '')
d = PassportFile('', '', self.file_size, self.file_date)
e = PassportElementError('source', 'type', 'message')
assert a == b

View file

@ -60,11 +60,15 @@ class TestPhoto(object):
# Make sure file has been uploaded.
assert isinstance(photo, PhotoSize)
assert isinstance(photo.file_id, str)
assert isinstance(photo.file_unique_id, str)
assert photo.file_id != ''
assert photo.file_unique_id != ''
assert isinstance(thumb, PhotoSize)
assert isinstance(thumb.file_id, str)
assert isinstance(thumb.file_unique_id, str)
assert thumb.file_id != ''
assert thumb.file_unique_id != ''
def test_expected_values(self, photo, thumb):
assert photo.width == self.width
@ -82,14 +86,18 @@ class TestPhoto(object):
assert isinstance(message.photo[0], PhotoSize)
assert isinstance(message.photo[0].file_id, str)
assert isinstance(message.photo[0].file_unique_id, str)
assert message.photo[0].file_id != ''
assert message.photo[0].file_unique_id != ''
assert message.photo[0].width == thumb.width
assert message.photo[0].height == thumb.height
assert message.photo[0].file_size == thumb.file_size
assert isinstance(message.photo[1], PhotoSize)
assert isinstance(message.photo[1].file_id, str)
assert isinstance(message.photo[1].file_unique_id, str)
assert message.photo[1].file_id != ''
assert message.photo[1].file_unique_id != ''
assert message.photo[1].width == photo.width
assert message.photo[1].height == photo.height
assert message.photo[1].file_size == photo.file_size
@ -103,14 +111,18 @@ class TestPhoto(object):
parse_mode='Markdown')
assert isinstance(message.photo[0], PhotoSize)
assert isinstance(message.photo[0].file_id, str)
assert isinstance(message.photo[0].file_unique_id, str)
assert message.photo[0].file_id != ''
assert message.photo[0].file_unique_id != ''
assert message.photo[0].width == thumb.width
assert message.photo[0].height == thumb.height
assert message.photo[0].file_size == thumb.file_size
assert isinstance(message.photo[1], PhotoSize)
assert isinstance(message.photo[1].file_id, str)
assert isinstance(message.photo[1].file_unique_id, str)
assert message.photo[1].file_id != ''
assert message.photo[1].file_unique_id != ''
assert message.photo[1].width == photo.width
assert message.photo[1].height == photo.height
assert message.photo[1].file_size == photo.file_size
@ -125,14 +137,18 @@ class TestPhoto(object):
parse_mode='HTML')
assert isinstance(message.photo[0], PhotoSize)
assert isinstance(message.photo[0].file_id, str)
assert isinstance(message.photo[0].file_unique_id, str)
assert message.photo[0].file_id != ''
assert message.photo[0].file_unique_id != ''
assert message.photo[0].width == thumb.width
assert message.photo[0].height == thumb.height
assert message.photo[0].file_size == thumb.file_size
assert isinstance(message.photo[1], PhotoSize)
assert isinstance(message.photo[1].file_id, str)
assert isinstance(message.photo[1].file_unique_id, str)
assert message.photo[1].file_id != ''
assert message.photo[1].file_unique_id != ''
assert message.photo[1].width == photo.width
assert message.photo[1].height == photo.height
assert message.photo[1].file_size == photo.file_size
@ -175,12 +191,11 @@ class TestPhoto(object):
@flaky(3, 1)
@pytest.mark.timeout(10)
@pytest.mark.skip(reason='Doesnt work without API 4.5')
def test_get_and_download(self, bot, photo):
new_file = bot.getFile(photo.file_id)
assert new_file.file_size == photo.file_size
assert new_file.file_id == photo.file_id
assert new_file.file_unique_id == photo.file_unique_id
assert new_file.file_path.startswith('https://') is True
new_file.download('telegram.jpg')
@ -194,14 +209,18 @@ class TestPhoto(object):
assert isinstance(message.photo[0], PhotoSize)
assert isinstance(message.photo[0].file_id, str)
assert isinstance(message.photo[0].file_unique_id, str)
assert message.photo[0].file_id != ''
assert message.photo[0].file_unique_id != ''
assert message.photo[0].width == thumb.width
assert message.photo[0].height == thumb.height
assert message.photo[0].file_size == thumb.file_size
assert isinstance(message.photo[1], PhotoSize)
assert isinstance(message.photo[1].file_id, str)
assert isinstance(message.photo[1].file_unique_id, str)
assert message.photo[1].file_id != ''
assert message.photo[1].file_unique_id != ''
assert message.photo[1].width == photo.width
assert message.photo[1].height == photo.height
assert message.photo[1].file_size == photo.file_size
@ -216,7 +235,9 @@ class TestPhoto(object):
assert isinstance(photo, PhotoSize)
assert isinstance(photo.file_id, str)
assert isinstance(photo.file_unique_id, str)
assert photo.file_id != ''
assert photo.file_unique_id != ''
@flaky(3, 1)
@pytest.mark.timeout(10)
@ -228,7 +249,9 @@ class TestPhoto(object):
assert isinstance(photo, PhotoSize)
assert isinstance(photo.file_id, str)
assert isinstance(photo.file_unique_id, str)
assert photo.file_id != ''
assert photo.file_unique_id != ''
@flaky(3, 1)
@pytest.mark.timeout(10)
@ -243,7 +266,9 @@ class TestPhoto(object):
assert isinstance(photo, PhotoSize)
assert isinstance(photo.file_id, str)
assert isinstance(photo.file_unique_id, str)
assert photo.file_id != ''
assert photo.file_unique_id != ''
@flaky(3, 1)
@pytest.mark.timeout(10)
@ -266,7 +291,9 @@ class TestPhoto(object):
message = bot.send_photo(chat_id, photo=raw_bytes)
photo = message.photo[-1]
assert isinstance(photo.file_id, str)
assert isinstance(photo.file_unique_id, str)
assert photo.file_id != ''
assert photo.file_unique_id != ''
assert isinstance(photo, PhotoSize)
assert photo.width == 1280
assert photo.height == 720
@ -289,14 +316,18 @@ class TestPhoto(object):
assert isinstance(message.photo[0], PhotoSize)
assert isinstance(message.photo[0].file_id, str)
assert isinstance(message.photo[0].file_unique_id, str)
assert message.photo[0].file_id != ''
assert message.photo[0].file_unique_id != ''
assert message.photo[0].width == thumb.width
assert message.photo[0].height == thumb.height
assert message.photo[0].file_size == thumb.file_size
assert isinstance(message.photo[1], PhotoSize)
assert isinstance(message.photo[1].file_id, str)
assert isinstance(message.photo[1].file_unique_id, str)
assert message.photo[1].file_id != ''
assert message.photo[1].file_unique_id != ''
assert message.photo[1].width == photo.width
assert message.photo[1].height == photo.height
assert message.photo[1].file_size == photo.file_size
@ -304,6 +335,7 @@ class TestPhoto(object):
def test_de_json(self, bot, photo):
json_dict = {
'file_id': photo.file_id,
'file_unique_id': photo.file_unique_id,
'width': self.width,
'height': self.height,
'file_size': self.file_size
@ -311,6 +343,7 @@ class TestPhoto(object):
json_photo = PhotoSize.de_json(json_dict, bot)
assert json_photo.file_id == photo.file_id
assert json_photo.file_unique_id == photo.file_unique_id
assert json_photo.width == self.width
assert json_photo.height == self.height
assert json_photo.file_size == self.file_size
@ -320,6 +353,7 @@ class TestPhoto(object):
assert isinstance(photo_dict, dict)
assert photo_dict['file_id'] == photo.file_id
assert photo_dict['file_unique_id'] == photo.file_unique_id
assert photo_dict['width'] == photo.width
assert photo_dict['height'] == photo.height
assert photo_dict['file_size'] == photo.file_size
@ -348,11 +382,11 @@ class TestPhoto(object):
assert photo.get_file()
def test_equality(self, photo):
a = PhotoSize(photo.file_id, self.width, self.height)
b = PhotoSize(photo.file_id, self.width, self.height)
c = PhotoSize(photo.file_id, 0, 0)
d = PhotoSize('', self.width, self.height)
e = Sticker(photo.file_id, self.width, self.height, False)
a = PhotoSize(photo.file_id, 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)
d = PhotoSize('', '', self.width, self.height)
e = Sticker(photo.file_id, photo.file_unique_id, self.width, self.height, False)
assert a == b
assert hash(a) == hash(b)

View file

@ -55,7 +55,7 @@ class TestShippingOption(object):
b = ShippingOption(self.id_, self.title, self.prices)
c = ShippingOption(self.id_, '', [])
d = ShippingOption(0, self.title, self.prices)
e = Voice(self.id_, 0)
e = Voice(self.id_, 'someid', 0)
assert a == b
assert hash(a) == hash(b)

View file

@ -55,14 +55,21 @@ class TestSticker(object):
thumb_height = 320
thumb_file_size = 21472
sticker_file_id = '5a3128a4d2a04750b5b58397f3b5e812'
sticker_file_unique_id = 'adc3145fd2e84d95b64d68eaa22aa33e'
def test_creation(self, sticker):
# Make sure file has been uploaded.
assert isinstance(sticker, Sticker)
assert isinstance(sticker.file_id, str)
assert isinstance(sticker.file_unique_id, str)
assert sticker.file_id != ''
assert sticker.file_unique_id != ''
assert isinstance(sticker.thumb, PhotoSize)
assert isinstance(sticker.thumb.file_id, str)
assert isinstance(sticker.thumb.file_unique_id, str)
assert sticker.thumb.file_id != ''
assert sticker.thumb.file_unique_id != ''
def test_expected_values(self, sticker):
assert sticker.width == self.width
@ -80,7 +87,9 @@ class TestSticker(object):
assert isinstance(message.sticker, Sticker)
assert isinstance(message.sticker.file_id, str)
assert isinstance(message.sticker.file_unique_id, str)
assert message.sticker.file_id != ''
assert message.sticker.file_unique_id != ''
assert message.sticker.width == sticker.width
assert message.sticker.height == sticker.height
assert message.sticker.is_animated == sticker.is_animated
@ -88,7 +97,9 @@ class TestSticker(object):
assert isinstance(message.sticker.thumb, PhotoSize)
assert isinstance(message.sticker.thumb.file_id, str)
assert isinstance(message.sticker.thumb.file_unique_id, str)
assert message.sticker.thumb.file_id != ''
assert message.sticker.thumb.file_unique_id != ''
assert message.sticker.thumb.width == sticker.thumb.width
assert message.sticker.thumb.height == sticker.thumb.height
assert message.sticker.thumb.file_size == sticker.thumb.file_size
@ -100,6 +111,7 @@ class TestSticker(object):
assert new_file.file_size == sticker.file_size
assert new_file.file_id == sticker.file_id
assert new_file.file_unique_id == sticker.file_unique_id
assert new_file.file_path.startswith('https://')
new_file.download('telegram.webp')
@ -108,7 +120,6 @@ class TestSticker(object):
@flaky(3, 1)
@pytest.mark.timeout(10)
@pytest.mark.skip(reason='Doesnt work without API 4.5')
def test_resend(self, bot, chat_id, sticker):
message = bot.send_sticker(chat_id=chat_id, sticker=sticker.file_id)
@ -133,7 +144,9 @@ class TestSticker(object):
assert isinstance(message.sticker, Sticker)
assert isinstance(message.sticker.file_id, str)
assert isinstance(message.sticker.file_unique_id, str)
assert message.sticker.file_id != ''
assert message.sticker.file_unique_id != ''
assert message.sticker.width == sticker.width
assert message.sticker.height == sticker.height
assert message.sticker.is_animated == sticker.is_animated
@ -141,14 +154,17 @@ class TestSticker(object):
assert isinstance(message.sticker.thumb, PhotoSize)
assert isinstance(message.sticker.thumb.file_id, str)
assert isinstance(message.sticker.thumb.file_unique_id, str)
assert message.sticker.thumb.file_id != ''
assert message.sticker.thumb.file_unique_id != ''
assert message.sticker.thumb.width == sticker.thumb.width
assert message.sticker.thumb.height == sticker.thumb.height
assert message.sticker.thumb.file_size == sticker.thumb.file_size
def test_de_json(self, bot, sticker):
json_dict = {
'file_id': 'not a file id',
'file_id': self.sticker_file_id,
'file_unique_id': self.sticker_file_unique_id,
'width': self.width,
'height': self.height,
'is_animated': self.is_animated,
@ -158,7 +174,8 @@ class TestSticker(object):
}
json_sticker = Sticker.de_json(json_dict, bot)
assert json_sticker.file_id == 'not a file id'
assert json_sticker.file_id == self.sticker_file_id
assert json_sticker.file_unique_id == self.sticker_file_unique_id
assert json_sticker.width == self.width
assert json_sticker.height == self.height
assert json_sticker.is_animated == self.is_animated
@ -179,6 +196,7 @@ class TestSticker(object):
assert isinstance(sticker_dict, dict)
assert sticker_dict['file_id'] == sticker.file_id
assert sticker_dict['file_unique_id'] == sticker.file_unique_id
assert sticker_dict['width'] == sticker.width
assert sticker_dict['height'] == sticker.height
assert sticker_dict['is_animated'] == sticker.is_animated
@ -202,11 +220,14 @@ class TestSticker(object):
bot.send_sticker(chat_id)
def test_equality(self, sticker):
a = Sticker(sticker.file_id, self.width, self.height, self.is_animated)
b = Sticker(sticker.file_id, self.width, self.height, self.is_animated)
c = Sticker(sticker.file_id, 0, 0, False)
d = Sticker('', self.width, self.height, self.is_animated)
e = PhotoSize(sticker.file_id, self.width, self.height, self.is_animated)
a = Sticker(sticker.file_id, sticker.file_unique_id, self.width,
self.height, self.is_animated)
b = Sticker('', sticker.file_unique_id, self.width,
self.height, self.is_animated)
c = Sticker(sticker.file_id, sticker.file_unique_id, 0, 0, False)
d = Sticker('', '', self.width, self.height, self.is_animated)
e = PhotoSize(sticker.file_id, sticker.file_unique_id, self.width,
self.height, self.is_animated)
assert a == b
assert hash(a) == hash(b)
@ -234,7 +255,7 @@ class TestStickerSet(object):
title = 'Test stickers'
is_animated = True
contains_masks = False
stickers = [Sticker('file_id', 512, 512, True)]
stickers = [Sticker('file_id', 'file_un_id', 512, 512, True)]
name = 'NOTAREALNAME'
def test_de_json(self, bot):
@ -298,7 +319,7 @@ class TestStickerSet(object):
b = StickerSet(self.name, self.title, self.is_animated, self.contains_masks, self.stickers)
c = StickerSet(self.name, None, None, None, None)
d = StickerSet('blah', self.title, self.is_animated, self.contains_masks, self.stickers)
e = Audio(self.name, 0, None, None)
e = Audio(self.name, '', 0, None, None)
assert a == b
assert hash(a) == hash(b)

View file

@ -19,6 +19,7 @@
import pytest
from telegram import User, Update
from telegram.utils.helpers import escape_markdown
@pytest.fixture(scope='function')
@ -188,6 +189,18 @@ class TestUser(object):
user.id)
assert user.mention_markdown(user.username) == expected.format(user.username, user.id)
def test_mention_markdown_v2(self, user):
user.first_name = 'first{name'
user.last_name = 'last_name'
expected = u'[{}](tg://user?id={})'
assert user.mention_markdown_v2() == expected.format(escape_markdown(user.full_name,
version=2), user.id)
assert user.mention_markdown_v2('the{name>\u2022') == expected.format('the\{name\>\u2022',
user.id)
assert user.mention_markdown_v2(user.username) == expected.format(user.username, user.id)
def test_equality(self):
a = User(self.id_, self.first_name, self.is_bot, self.last_name)
b = User(self.id_, self.first_name, self.is_bot, self.last_name)

View file

@ -23,12 +23,12 @@ class TestUserProfilePhotos(object):
total_count = 2
photos = [
[
PhotoSize('file_id1', 512, 512),
PhotoSize('file_id2', 512, 512)
PhotoSize('file_id1', 'file_un_id1', 512, 512),
PhotoSize('file_id2', 'file_un_id1', 512, 512)
],
[
PhotoSize('file_id3', 512, 512),
PhotoSize('file_id4', 512, 512)
PhotoSize('file_id3', 'file_un_id3', 512, 512),
PhotoSize('file_id4', 'file_un_id4', 512, 512)
]
]

View file

@ -53,15 +53,22 @@ class TestVideo(object):
caption = u'<b>VideoTest</b> - *Caption*'
video_file_url = 'https://python-telegram-bot.org/static/testfiles/telegram.mp4'
video_file_id = '5a3128a4d2a04750b5b58397f3b5e812'
video_file_unique_id = 'adc3145fd2e84d95b64d68eaa22aa33e'
def test_creation(self, video):
# Make sure file has been uploaded.
assert isinstance(video, Video)
assert isinstance(video.file_id, str)
assert isinstance(video.file_unique_id, str)
assert video.file_id != ''
assert video.file_unique_id != ''
assert isinstance(video.thumb, PhotoSize)
assert isinstance(video.thumb.file_id, str)
assert isinstance(video.thumb.file_unique_id, str)
assert video.thumb.file_id != ''
assert video.thumb.file_unique_id != ''
def test_expected_values(self, video):
assert video.width == self.width
@ -80,7 +87,9 @@ class TestVideo(object):
assert isinstance(message.video, Video)
assert isinstance(message.video.file_id, str)
assert isinstance(message.video.file_unique_id, str)
assert message.video.file_id != ''
assert message.video.file_unique_id != ''
assert message.video.width == video.width
assert message.video.height == video.height
assert message.video.duration == video.duration
@ -99,6 +108,7 @@ class TestVideo(object):
assert new_file.file_size == self.file_size
assert new_file.file_id == video.file_id
assert new_file.file_unique_id == video.file_unique_id
assert new_file.file_path.startswith('https://')
new_file.download('telegram.mp4')
@ -112,7 +122,9 @@ class TestVideo(object):
assert isinstance(message.video, Video)
assert isinstance(message.video.file_id, str)
assert isinstance(message.video.file_unique_id, str)
assert message.video.file_id != ''
assert message.video.file_unique_id != ''
assert message.video.width == video.width
assert message.video.height == video.height
assert message.video.duration == video.duration
@ -120,7 +132,9 @@ class TestVideo(object):
assert isinstance(message.video.thumb, PhotoSize)
assert isinstance(message.video.thumb.file_id, str)
assert isinstance(message.video.thumb.file_unique_id, str)
assert message.video.thumb.file_id != ''
assert message.video.thumb.file_unique_id != ''
assert message.video.thumb.width == 51 # This seems odd that it's not self.thumb_width
assert message.video.thumb.height == 90 # Ditto
assert message.video.thumb.file_size == 645 # same
@ -129,7 +143,6 @@ class TestVideo(object):
@flaky(3, 1)
@pytest.mark.timeout(10)
@pytest.mark.skip(reason='Doesnt work without API 4.5')
def test_resend(self, bot, chat_id, video):
message = bot.send_video(chat_id, video.file_id)
@ -178,7 +191,8 @@ class TestVideo(object):
def test_de_json(self, bot):
json_dict = {
'file_id': 'not a file id',
'file_id': self.video_file_id,
'file_unique_id': self.video_file_unique_id,
'width': self.width,
'height': self.height,
'duration': self.duration,
@ -187,7 +201,8 @@ class TestVideo(object):
}
json_video = Video.de_json(json_dict, bot)
assert json_video.file_id == 'not a file id'
assert json_video.file_id == self.video_file_id
assert json_video.file_unique_id == self.video_file_unique_id
assert json_video.width == self.width
assert json_video.height == self.height
assert json_video.duration == self.duration
@ -199,6 +214,7 @@ class TestVideo(object):
assert isinstance(video_dict, dict)
assert video_dict['file_id'] == video.file_id
assert video_dict['file_unique_id'] == video.file_unique_id
assert video_dict['width'] == video.width
assert video_dict['height'] == video.height
assert video_dict['duration'] == video.duration
@ -229,11 +245,11 @@ class TestVideo(object):
assert video.get_file()
def test_equality(self, video):
a = Video(video.file_id, self.width, self.height, self.duration)
b = Video(video.file_id, self.width, self.height, self.duration)
c = Video(video.file_id, 0, 0, 0)
d = Video('', self.width, self.height, self.duration)
e = Voice(video.file_id, self.duration)
a = Video(video.file_id, video.file_unique_id, self.width, self.height, self.duration)
b = Video('', video.file_unique_id, self.width, self.height, self.duration)
c = Video(video.file_id, video.file_unique_id, 0, 0, 0)
d = Video('', '', self.width, self.height, self.duration)
e = Voice(video.file_id, video.file_unique_id, self.duration)
assert a == b
assert hash(a) == hash(b)

View file

@ -47,16 +47,22 @@ class TestVideoNote(object):
thumb_file_size = 11547
caption = u'VideoNoteTest - Caption'
videonote_file_id = '5a3128a4d2a04750b5b58397f3b5e812'
videonote_file_unique_id = 'adc3145fd2e84d95b64d68eaa22aa33e'
def test_creation(self, video_note):
# Make sure file has been uploaded.
assert isinstance(video_note, VideoNote)
assert isinstance(video_note.file_id, str)
assert isinstance(video_note.file_unique_id, str)
assert video_note.file_id != ''
assert video_note.file_unique_id != ''
assert isinstance(video_note.thumb, PhotoSize)
assert isinstance(video_note.thumb.file_id, str)
assert isinstance(video_note.thumb.file_unique_id, str)
assert video_note.thumb.file_id != ''
assert video_note.thumb.file_unique_id != ''
def test_expected_values(self, video_note):
assert video_note.length == self.length
@ -72,7 +78,9 @@ class TestVideoNote(object):
assert isinstance(message.video_note, VideoNote)
assert isinstance(message.video_note.file_id, str)
assert isinstance(message.video_note.file_unique_id, str)
assert message.video_note.file_id != ''
assert message.video_note.file_unique_id != ''
assert message.video_note.length == video_note.length
assert message.video_note.duration == video_note.duration
assert message.video_note.file_size == video_note.file_size
@ -88,6 +96,7 @@ class TestVideoNote(object):
assert new_file.file_size == self.file_size
assert new_file.file_id == video_note.file_id
assert new_file.file_unique_id == video_note.file_unique_id
assert new_file.file_path.startswith('https://')
new_file.download('telegram2.mp4')
@ -96,7 +105,6 @@ class TestVideoNote(object):
@flaky(3, 1)
@pytest.mark.timeout(10)
@pytest.mark.skip(reason='Doesnt work without API 4.5')
def test_resend(self, bot, chat_id, video_note):
message = bot.send_video_note(chat_id, video_note.file_id)
@ -112,14 +120,16 @@ class TestVideoNote(object):
def test_de_json(self, bot):
json_dict = {
'file_id': 'not a file id',
'file_id': self.videonote_file_id,
'file_unique_id': self.videonote_file_unique_id,
'length': self.length,
'duration': self.duration,
'file_size': self.file_size
}
json_video_note = VideoNote.de_json(json_dict, bot)
assert json_video_note.file_id == 'not a file id'
assert json_video_note.file_id == self.videonote_file_id
assert json_video_note.file_unique_id == self.videonote_file_unique_id
assert json_video_note.length == self.length
assert json_video_note.duration == self.duration
assert json_video_note.file_size == self.file_size
@ -129,6 +139,7 @@ class TestVideoNote(object):
assert isinstance(video_note_dict, dict)
assert video_note_dict['file_id'] == video_note.file_id
assert video_note_dict['file_unique_id'] == video_note.file_unique_id
assert video_note_dict['length'] == video_note.length
assert video_note_dict['duration'] == video_note.duration
assert video_note_dict['file_size'] == video_note.file_size
@ -157,11 +168,11 @@ class TestVideoNote(object):
assert video_note.get_file()
def test_equality(self, video_note):
a = VideoNote(video_note.file_id, self.length, self.duration)
b = VideoNote(video_note.file_id, self.length, self.duration)
c = VideoNote(video_note.file_id, 0, 0)
d = VideoNote('', self.length, self.duration)
e = Voice(video_note.file_id, self.duration)
a = VideoNote(video_note.file_id, video_note.file_unique_id, self.length, self.duration)
b = VideoNote('', video_note.file_unique_id, self.length, self.duration)
c = VideoNote(video_note.file_id, video_note.file_unique_id, 0, 0)
d = VideoNote('', '', self.length, self.duration)
e = Voice(video_note.file_id, video_note.file_unique_id, self.duration)
assert a == b
assert hash(a) == hash(b)

View file

@ -46,11 +46,16 @@ class TestVoice(object):
caption = u'Test *voice*'
voice_file_url = 'https://python-telegram-bot.org/static/testfiles/telegram.ogg'
voice_file_id = '5a3128a4d2a04750b5b58397f3b5e812'
voice_file_unique_id = 'adc3145fd2e84d95b64d68eaa22aa33e'
def test_creation(self, voice):
# Make sure file has been uploaded.
assert isinstance(voice, Voice)
assert isinstance(voice.file_id, str)
assert isinstance(voice.file_unique_id, str)
assert voice.file_id != ''
assert voice.file_unique_id != ''
def test_expected_values(self, voice):
assert voice.duration == self.duration
@ -66,7 +71,9 @@ class TestVoice(object):
assert isinstance(message.voice, Voice)
assert isinstance(message.voice.file_id, str)
assert isinstance(message.voice.file_unique_id, str)
assert message.voice.file_id != ''
assert message.voice.file_unique_id != ''
assert message.voice.duration == voice.duration
assert message.voice.mime_type == voice.mime_type
assert message.voice.file_size == voice.file_size
@ -79,6 +86,7 @@ class TestVoice(object):
assert new_file.file_size == voice.file_size
assert new_file.file_id == voice.file_id
assert new_file.file_unique_id == voice.file_unique_id
assert new_file.file_path.startswith('https://')
new_file.download('telegram.ogg')
@ -92,14 +100,15 @@ class TestVoice(object):
assert isinstance(message.voice, Voice)
assert isinstance(message.voice.file_id, str)
assert isinstance(message.voice.file_unique_id, str)
assert message.voice.file_id != ''
assert message.voice.file_unique_id != ''
assert message.voice.duration == voice.duration
assert message.voice.mime_type == voice.mime_type
assert message.voice.file_size == voice.file_size
@flaky(3, 1)
@pytest.mark.timeout(10)
@pytest.mark.skip(reason='Doesnt work without API 4.5')
def test_resend(self, bot, chat_id, voice):
message = bot.sendVoice(chat_id, voice.file_id)
@ -148,7 +157,8 @@ class TestVoice(object):
def test_de_json(self, bot):
json_dict = {
'file_id': 'not a file id',
'file_id': self.voice_file_id,
'file_unique_id': self.voice_file_unique_id,
'duration': self.duration,
'caption': self.caption,
'mime_type': self.mime_type,
@ -156,7 +166,8 @@ class TestVoice(object):
}
json_voice = Voice.de_json(json_dict, bot)
assert json_voice.file_id == 'not a file id'
assert json_voice.file_id == self.voice_file_id
assert json_voice.file_unique_id == self.voice_file_unique_id
assert json_voice.duration == self.duration
assert json_voice.mime_type == self.mime_type
assert json_voice.file_size == self.file_size
@ -166,6 +177,7 @@ class TestVoice(object):
assert isinstance(voice_dict, dict)
assert voice_dict['file_id'] == voice.file_id
assert voice_dict['file_unique_id'] == voice.file_unique_id
assert voice_dict['duration'] == voice.duration
assert voice_dict['mime_type'] == voice.mime_type
assert voice_dict['file_size'] == voice.file_size
@ -194,11 +206,11 @@ class TestVoice(object):
assert voice.get_file()
def test_equality(self, voice):
a = Voice(voice.file_id, self.duration)
b = Voice(voice.file_id, self.duration)
c = Voice(voice.file_id, 0)
d = Voice('', self.duration)
e = Audio(voice.file_id, self.duration)
a = Voice(voice.file_id, voice.file_unique_id, self.duration)
b = Voice('', voice.file_unique_id, self.duration)
c = Voice(voice.file_id, voice.file_unique_id, 0)
d = Voice('', '', self.duration)
e = Audio(voice.file_id, voice.file_unique_id, self.duration)
assert a == b
assert hash(a) == hash(b)