From 32dd415fb86e822a977fbdf2e4b78f945715eb57 Mon Sep 17 00:00:00 2001 From: zeshuaro Date: Sat, 14 Sep 2019 05:07:56 +1000 Subject: [PATCH] Add instance methods to Animation and ChatPhoto (#1489) get_file, get_small_file, get_big_file --- .gitignore | 1 + AUTHORS.rst | 1 + telegram/bot.py | 3 +- telegram/files/animation.py | 26 ++++++++++++- telegram/files/chatphoto.py | 44 ++++++++++++++++++++-- tests/test_animation.py | 75 +++++++++++++++++++++++++++++++++---- 6 files changed, 138 insertions(+), 12 deletions(-) diff --git a/.gitignore b/.gitignore index 0337785f3..a98e967bc 100644 --- a/.gitignore +++ b/.gitignore @@ -69,6 +69,7 @@ target/ *.sublime* # unitests files +game.gif telegram.mp3 telegram.mp4 telegram2.mp4 diff --git a/AUTHORS.rst b/AUTHORS.rst index e3da77ad9..9feb05969 100644 --- a/AUTHORS.rst +++ b/AUTHORS.rst @@ -79,5 +79,6 @@ The following wonderful people contributed directly or indirectly to this projec - `Vorobjev Simon `_ - `Wagner Macedo `_ - `wjt `_ +- `zeshuaro `_ Please add yourself here alphabetically when you submit your first pull request. diff --git a/telegram/bot.py b/telegram/bot.py index 2e1f6e5ea..a60991e07 100644 --- a/telegram/bot.py +++ b/telegram/bot.py @@ -1514,7 +1514,8 @@ class Bot(TelegramObject): calling get_file again. Args: - file_id (:obj:`str` | :class:`telegram.Audio` | :class:`telegram.Document` | \ + file_id (:obj:`str` | :class:`telegram.Animation` | :class:`telegram.Audio` | \ + :class:`telegram.ChatPhoto` | :class:`telegram.Document` | \ :class:`telegram.PhotoSize` | :class:`telegram.Sticker` | \ :class:`telegram.Video` | :class:`telegram.VideoNote` | \ :class:`telegram.Voice`): diff --git a/telegram/files/animation.py b/telegram/files/animation.py index 4ff659402..6a672d8f2 100644 --- a/telegram/files/animation.py +++ b/telegram/files/animation.py @@ -34,6 +34,7 @@ class Animation(TelegramObject): file_name (:obj:`str`): Optional. Original animation filename 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 file identifier. @@ -44,6 +45,8 @@ class Animation(TelegramObject): file_name (:obj:`str`, optional): Original animation filename 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. + **kwargs (:obj:`dict`): Arbitrary keyword arguments. """ @@ -56,16 +59,19 @@ class Animation(TelegramObject): file_name=None, mime_type=None, file_size=None, + bot=None, **kwargs): # Required self.file_id = str(file_id) self.width = int(width) self.height = int(height) self.duration = duration + # Optionals self.thumb = thumb self.file_name = file_name self.mime_type = mime_type self.file_size = file_size + self.bot = bot self._id_attrs = (self.file_id,) @@ -78,4 +84,22 @@ class Animation(TelegramObject): data['thumb'] = PhotoSize.de_json(data.get('thumb'), bot) - return cls(**data) + return cls(bot=bot, **data) + + def get_file(self, timeout=None, **kwargs): + """Convenience wrapper over :attr:`telegram.Bot.get_file` + + Args: + timeout (:obj:`int` | :obj:`float`, optional): If this value is specified, use it as + the read timeout from the server (instead of the one specified during creation of + the connection pool). + **kwargs (:obj:`dict`): Arbitrary keyword arguments. + + Returns: + :class:`telegram.File` + + Raises: + :class:`telegram.TelegramError` + + """ + return self.bot.get_file(self.file_id, timeout=timeout, **kwargs) diff --git a/telegram/files/chatphoto.py b/telegram/files/chatphoto.py index 3e17c1b87..9bf1bac16 100644 --- a/telegram/files/chatphoto.py +++ b/telegram/files/chatphoto.py @@ -17,9 +17,6 @@ # You should have received a copy of the GNU Lesser Public License # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram ChatPhoto.""" - -# TODO: add direct download shortcuts. - from telegram import TelegramObject @@ -43,6 +40,9 @@ class ChatPhoto(TelegramObject): def __init__(self, small_file_id, big_file_id, bot=None, **kwargs): self.small_file_id = small_file_id self.big_file_id = big_file_id + self.bot = bot + + self._id_attrs = (self.small_file_id, self.big_file_id) @classmethod def de_json(cls, data, bot): @@ -50,3 +50,41 @@ class ChatPhoto(TelegramObject): return None return cls(bot=bot, **data) + + def get_small_file(self, timeout=None, **kwargs): + """Convenience wrapper over :attr:`telegram.Bot.get_file` for getting the + small (160x160) chat photo + + Args: + timeout (:obj:`int` | :obj:`float`, optional): If this value is specified, use it as + the read timeout from the server (instead of the one specified during creation of + the connection pool). + **kwargs (:obj:`dict`): Arbitrary keyword arguments. + + Returns: + :class:`telegram.File` + + Raises: + :class:`telegram.TelegramError` + + """ + return self.bot.get_file(self.small_file_id, timeout=timeout, **kwargs) + + def get_big_file(self, timeout=None, **kwargs): + """Convenience wrapper over :attr:`telegram.Bot.get_file` for getting the + big (640x640) chat photo + + Args: + timeout (:obj:`int` | :obj:`float`, optional): If this value is specified, use it as + the read timeout from the server (instead of the one specified during creation of + the connection pool). + **kwargs (:obj:`dict`): Arbitrary keyword arguments. + + Returns: + :class:`telegram.File` + + Raises: + :class:`telegram.TelegramError` + + """ + return self.bot.get_file(self.big_file_id, timeout=timeout, **kwargs) diff --git a/tests/test_animation.py b/tests/test_animation.py index d0a5374db..ae694fde5 100644 --- a/tests/test_animation.py +++ b/tests/test_animation.py @@ -17,10 +17,11 @@ # 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 PhotoSize, Animation, Voice +from telegram import PhotoSize, Animation, Voice, TelegramError @pytest.fixture(scope='function') @@ -42,6 +43,9 @@ class TestAnimation(object): width = 320 height = 180 duration = 1 + # animation_file_url = 'https://python-telegram-bot.org/static/testfiles/game.gif' + # Shortened link, the above one is cached with the wrong duration. + animation_file_url = 'http://bit.ly/2L18jua' file_name = 'game.gif.mp4' mime_type = 'video/mp4' file_size = 4127 @@ -72,20 +76,52 @@ class TestAnimation(object): assert message.animation.file_name == animation.file_name assert message.animation.mime_type == animation.mime_type assert message.animation.file_size == animation.file_size - assert message.animation.thumb.width == 320 - assert message.animation.thumb.height == 180 + assert message.animation.thumb.width == self.width + assert message.animation.thumb.height == self.height @flaky(3, 1) - def test_resend(self, bot, chat_id, animation): - message = bot.send_animation(chat_id, animation.file_id) + @pytest.mark.timeout(10) + def test_get_and_download(self, bot, animation): + new_file = bot.get_file(animation.file_id) + + assert new_file.file_size == self.file_size + assert new_file.file_id == animation.file_id + assert new_file.file_path.startswith('https://') + + new_file.download('game.gif') + + assert os.path.isfile('game.gif') + + @flaky(3, 1) + @pytest.mark.timeout(10) + def test_send_animation_url_file(self, bot, chat_id, animation): + message = bot.send_animation(chat_id=chat_id, animation=self.animation_file_url, + caption=self.caption) + + assert message.caption == self.caption assert isinstance(message.animation, Animation) assert isinstance(message.animation.file_id, str) - assert message.animation.file_id != '' - assert message.animation.file_name == animation.file_name + assert message.animation.file_id is not None + assert message.animation.duration == animation.duration assert message.animation.mime_type == animation.mime_type assert message.animation.file_size == animation.file_size + @flaky(3, 1) + @pytest.mark.timeout(10) + def test_resend(self, bot, chat_id, animation): + message = bot.send_animation(chat_id, animation.file_id) + + assert message.animation == animation + + def test_send_with_animation(self, monkeypatch, bot, chat_id, animation): + def test(_, url, data, **kwargs): + return data['animation'] == animation.file_id + + monkeypatch.setattr('telegram.utils.request.Request.post', test) + message = bot.send_animation(animation=animation, chat_id=chat_id) + assert message + def test_de_json(self, bot, animation): json_dict = { 'file_id': self.animation_file_id, @@ -117,6 +153,31 @@ class TestAnimation(object): assert animation_dict['mime_type'] == animation.mime_type assert animation_dict['file_size'] == animation.file_size + @flaky(3, 1) + @pytest.mark.timeout(10) + def test_error_send_empty_file(self, bot, chat_id): + animation_file = open(os.devnull, 'rb') + + with pytest.raises(TelegramError): + bot.send_animation(chat_id=chat_id, animation=animation_file) + + @flaky(3, 1) + @pytest.mark.timeout(10) + def test_error_send_empty_file_id(self, bot, chat_id): + with pytest.raises(TelegramError): + bot.send_animation(chat_id=chat_id, animation='') + + def test_error_send_without_required_args(self, bot, chat_id): + with pytest.raises(TypeError): + bot.send_animation(chat_id=chat_id) + + def test_get_file_instance_method(self, monkeypatch, animation): + def test(*args, **kwargs): + return args[1] == animation.file_id + + monkeypatch.setattr('telegram.Bot.get_file', test) + 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)