diff --git a/docs/source/telegram.ext.defaults.rst b/docs/source/telegram.ext.defaults.rst new file mode 100644 index 00000000000..d08a6ed5c12 --- /dev/null +++ b/docs/source/telegram.ext.defaults.rst @@ -0,0 +1,6 @@ +telegram.ext.Defaults +===================== + +.. autoclass:: telegram.ext.Defaults + :members: + :show-inheritance: diff --git a/docs/source/telegram.ext.rst b/docs/source/telegram.ext.rst index 02e27376818..42da96e8c5f 100644 --- a/docs/source/telegram.ext.rst +++ b/docs/source/telegram.ext.rst @@ -12,6 +12,7 @@ telegram.ext package telegram.ext.messagequeue telegram.ext.delayqueue telegram.ext.callbackcontext + telegram.ext.defaults Handlers -------- diff --git a/requirements.txt b/requirements.txt index c004d5fc790..6f926578e47 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,3 +2,4 @@ future>=0.16.0 certifi tornado>=5.1 cryptography +decorator>=4.4.0 diff --git a/telegram/bot.py b/telegram/bot.py index 0ff51397fe0..a75771a87e1 100644 --- a/telegram/bot.py +++ b/telegram/bot.py @@ -21,6 +21,8 @@ """This module contains an object that represents a Telegram Bot.""" import functools +import inspect +from decorator import decorate try: import ujson as json @@ -39,7 +41,7 @@ PhotoSize, Audio, Document, Sticker, Video, Animation, Voice, VideoNote, Location, Venue, Contact, InputFile, Poll) from telegram.error import InvalidToken, TelegramError -from telegram.utils.helpers import to_timestamp +from telegram.utils.helpers import to_timestamp, DEFAULT_NONE from telegram.utils.request import Request logging.getLogger(__name__).addHandler(logging.NullHandler()) @@ -57,18 +59,17 @@ def decorator(self, *args, **kwargs): return decorator -def log(func): +def log(func, *args, **kwargs): logger = logging.getLogger(func.__module__) - @functools.wraps(func) def decorator(self, *args, **kwargs): logger.debug('Entering: %s', func.__name__) - result = func(self, *args, **kwargs) + result = func(*args, **kwargs) logger.debug(result) logger.debug('Exiting: %s', func.__name__) return result - return decorator + return decorate(func, decorator) class Bot(TelegramObject): @@ -82,13 +83,55 @@ class Bot(TelegramObject): :obj:`telegram.utils.request.Request`. private_key (:obj:`bytes`, optional): Private key for decryption of telegram passport data. private_key_password (:obj:`bytes`, optional): Password for above private key. + defaults (:class:`telegram.ext.Defaults`, optional): An object containing default values to + be used if not set explicitly in the bot methods. """ - def __init__(self, token, base_url=None, base_file_url=None, request=None, private_key=None, - private_key_password=None): + def __new__(cls, *args, **kwargs): + # Get default values from kwargs + defaults = kwargs.get('defaults') + + # Make an instance of the class + instance = super(Bot, cls).__new__(cls) + + if not defaults: + return instance + + # For each method ... + for method_name, method in inspect.getmembers(instance, predicate=inspect.ismethod): + # ... get kwargs + argspec = inspect.getargspec(method) + kwarg_names = argspec.args[-len(argspec.defaults or []):] + # ... check if Defaults has a attribute that matches the kwarg name + needs_default = [ + kwarg_name for kwarg_name in kwarg_names if hasattr(defaults, kwarg_name) + ] + # ... make a dict of kwarg name and the default value + default_kwargs = { + kwarg_name: getattr(defaults, kwarg_name) for kwarg_name in needs_default if ( + getattr(defaults, kwarg_name) is not DEFAULT_NONE + ) + } + # ... apply the defaults using a partial + if default_kwargs: + setattr(instance, method_name, functools.partial(method, **default_kwargs)) + + return instance + + def __init__(self, + token, + base_url=None, + base_file_url=None, + request=None, + private_key=None, + private_key_password=None, + defaults=None): self.token = self._validate_token(token) + # Gather default + self.defaults = defaults + if base_url is None: base_url = 'https://api.telegram.org/bot' @@ -120,11 +163,20 @@ def _message(self, url, data, reply_to_message_id=None, disable_notification=Non else: data['reply_markup'] = reply_markup + if data.get('media') and (data['media'].parse_mode == DEFAULT_NONE): + if self.defaults: + data['media'].parse_mode = self.defaults.parse_mode + else: + data['media'].parse_mode = None + result = self._request.post(url, data, timeout=timeout) if result is True: return result + if self.defaults: + result['default_quote'] = self.defaults.quote + return Message.de_json(result, self) @property @@ -978,6 +1030,13 @@ def send_media_group(self, data = {'chat_id': chat_id, 'media': media} + for m in data['media']: + if m.parse_mode == DEFAULT_NONE: + if self.defaults: + m.parse_mode = self.defaults.parse_mode + else: + m.parse_mode = None + if reply_to_message_id: data['reply_to_message_id'] = reply_to_message_id if disable_notification: @@ -985,6 +1044,10 @@ def send_media_group(self, result = self._request.post(url, data, timeout=timeout) + if self.defaults: + for res in result: + res['default_quote'] = self.defaults.quote + return [Message.de_json(res, self) for res in result] @log @@ -1447,6 +1510,24 @@ def answer_inline_query(self, """ url = '{0}/answerInlineQuery'.format(self.base_url) + for res in results: + if res._has_parse_mode and res.parse_mode == DEFAULT_NONE: + res.parse_mode = self.defaults.parse_mode + if res._has_input_message_content and res.input_message_content: + if (res.input_message_content._has_parse_mode + and res.input_message_content.parse_mode == DEFAULT_NONE): + if self.defaults: + res.input_message_content.parse_mode = self.defaults.parse_mode + else: + res.input_message_content.parse_mode = None + if (res.input_message_content._has_disable_web_page_preview + and res.input_message_content.disable_web_page_preview == DEFAULT_NONE): + if self.defaults: + res.input_message_content.disable_web_page_preview = \ + self.defaults.disable_web_page_preview + else: + res.input_message_content.disable_web_page_preview = None + results = [res.to_dict() for res in results] data = {'inline_query_id': inline_query_id, 'results': results} @@ -1987,6 +2068,10 @@ def get_updates(self, else: self.logger.debug('No new updates found.') + if self.defaults: + for u in result: + u['default_quote'] = self.defaults.quote + return [Update.de_json(u, self) for u in result] @log @@ -2167,6 +2252,9 @@ def get_chat(self, chat_id, timeout=None, **kwargs): result = self._request.post(url, data, timeout=timeout) + if self.defaults: + result['default_quote'] = self.defaults.quote + return Chat.de_json(result, self) @log diff --git a/telegram/callbackquery.py b/telegram/callbackquery.py index 2aeb97b37d3..0077e59b518 100644 --- a/telegram/callbackquery.py +++ b/telegram/callbackquery.py @@ -101,7 +101,10 @@ def de_json(cls, data, bot): data = super(CallbackQuery, cls).de_json(data, bot) data['from_user'] = User.de_json(data.get('from'), bot) - data['message'] = Message.de_json(data.get('message'), bot) + message = data.get('message') + if message: + message['default_quote'] = data.get('default_quote') + data['message'] = Message.de_json(message, bot) return cls(bot=bot, **data) diff --git a/telegram/chat.py b/telegram/chat.py index f582de477d8..68e6243799e 100644 --- a/telegram/chat.py +++ b/telegram/chat.py @@ -135,7 +135,10 @@ def de_json(cls, data, bot): data['photo'] = ChatPhoto.de_json(data.get('photo'), bot) from telegram import Message - data['pinned_message'] = Message.de_json(data.get('pinned_message'), bot) + pinned_message = data.get('pinned_message') + if pinned_message: + pinned_message['default_quote'] = data.get('default_quote') + data['pinned_message'] = Message.de_json(pinned_message, bot) data['permissions'] = ChatPermissions.de_json(data.get('permissions'), bot) return cls(bot=bot, **data) diff --git a/telegram/ext/__init__.py b/telegram/ext/__init__.py index 565883c6d46..7528cb3d11f 100644 --- a/telegram/ext/__init__.py +++ b/telegram/ext/__init__.py @@ -41,6 +41,7 @@ from .shippingqueryhandler import ShippingQueryHandler from .messagequeue import MessageQueue from .messagequeue import DelayQueue +from .defaults import Defaults __all__ = ('Dispatcher', 'JobQueue', 'Job', 'Updater', 'CallbackQueryHandler', 'ChosenInlineResultHandler', 'CommandHandler', 'Handler', 'InlineQueryHandler', @@ -48,4 +49,4 @@ 'StringRegexHandler', 'TypeHandler', 'ConversationHandler', 'PreCheckoutQueryHandler', 'ShippingQueryHandler', 'MessageQueue', 'DelayQueue', 'DispatcherHandlerStop', 'run_async', 'CallbackContext', 'BasePersistence', - 'PicklePersistence', 'DictPersistence', 'PrefixHandler') + 'PicklePersistence', 'DictPersistence', 'PrefixHandler', 'Defaults') diff --git a/telegram/ext/defaults.py b/telegram/ext/defaults.py new file mode 100644 index 00000000000..918ad6d1d4b --- /dev/null +++ b/telegram/ext/defaults.py @@ -0,0 +1,127 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2020 +# Leandro Toledo de Souza +# +# 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/]. +"""This module contains the class Defaults, which allows to pass default values to Updater.""" + +from telegram.utils.helpers import DEFAULT_NONE + + +class Defaults: + """Convenience Class to gather all parameters with a (user defined) default value + + Attributes: + parse_mode (:obj:`str`): Optional. Send Markdown or HTML, if you want Telegram apps to show + bold, italic, fixed-width toxt or URLs in your bot's message. + disable_notification (:obj:`bool`): Optional. Sends the message silently. Users will + receive a notification with no sound. + disable_web_page_preview (:obj:`bool`): Optional. Disables link previews for links in this + message. + 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). + quote (:obj:`bool`): Optional. If set to ``True``, the reply is sent as an actual reply to + the 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. + + Parameters: + parse_mode (:obj:`str`, optional): Send Markdown or HTML, if you want Telegram apps to show + bold, italic, fixed-width toxt or URLs in your bot's message. + disable_notification (:obj:`bool`, optional): Sends the message silently. Users will + receive a notification with no sound. + disable_web_page_preview (:obj:`bool`, optional): Disables link previews for links in this + message. + 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). + quote (:obj:`bool`, opitonal): If set to ``True``, the reply is sent as an actual reply to + the 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. + """ + def __init__(self, + parse_mode=None, + disable_notification=None, + disable_web_page_preview=None, + # Timeout needs special treatment, since the bot methods have two different + # default values for timeout (None and 20s) + timeout=DEFAULT_NONE, + quote=None): + self._parse_mode = parse_mode + self._disable_notification = disable_notification + self._disable_web_page_preview = disable_web_page_preview + self._timeout = timeout + self._quote = quote + + @property + def parse_mode(self): + return self._parse_mode + + @parse_mode.setter + def parse_mode(self, value): + raise AttributeError("You can not assign a new value to defaults after because it would " + "not have any effect.") + + @property + def disable_notification(self): + return self._disable_notification + + @disable_notification.setter + def disable_notification(self, value): + raise AttributeError("You can not assign a new value to defaults after because it would " + "not have any effect.") + + @property + def disable_web_page_preview(self): + return self._disable_web_page_preview + + @disable_web_page_preview.setter + def disable_web_page_preview(self, value): + raise AttributeError("You can not assign a new value to defaults after because it would " + "not have any effect.") + + @property + def timeout(self): + return self._timeout + + @timeout.setter + def timeout(self, value): + raise AttributeError("You can not assign a new value to defaults after because it would " + "not have any effect.") + + @property + def quote(self): + return self._quote + + @quote.setter + def quote(self, value): + raise AttributeError("You can not assign a new value to defaults after because it would " + "not have any effect.") + + def __hash__(self): + return hash((self._parse_mode, + self._disable_notification, + self._disable_web_page_preview, + self._timeout, + self._quote)) + + def __eq__(self, other): + if isinstance(other, Defaults): + return self.__dict__ == other.__dict__ + return False + + def __ne__(self, other): + return not self == other diff --git a/telegram/ext/updater.py b/telegram/ext/updater.py index bc643982093..cd151e49ec7 100644 --- a/telegram/ext/updater.py +++ b/telegram/ext/updater.py @@ -87,6 +87,8 @@ class Updater(object): persistence (:class:`telegram.ext.BasePersistence`, optional): The persistence class to store data that should be persistent over restarts (ignored if `dispatcher` argument is used). + defaults (:class:`telegram.ext.Defaults`, optional): An object containing default values to + be used if not set explicitly in the bot methods. Note: You must supply either a :attr:`bot` or a :attr:`token` argument. @@ -108,6 +110,7 @@ def __init__(self, user_sig_handler=None, request_kwargs=None, persistence=None, + defaults=None, use_context=False, dispatcher=None, base_file_url=None): @@ -157,7 +160,8 @@ def __init__(self, base_file_url=base_file_url, request=self._request, private_key=private_key, - private_key_password=private_key_password) + private_key_password=private_key_password, + defaults=defaults) self.update_queue = Queue() self.job_queue = JobQueue() self.__exception_event = Event() @@ -192,6 +196,9 @@ def __init__(self, self.__lock = Lock() self.__threads = [] + # Just for passing to WebhookAppClass + self._default_quote = defaults.quote if defaults else None + def _init_thread(self, target, name, *args, **kwargs): thr = Thread(target=self._thread_wrapper, name="Bot:{}:{}".format(self.bot.id, name), @@ -409,7 +416,8 @@ def _start_webhook(self, listen, port, url_path, cert, key, bootstrap_retries, c url_path = '/{0}'.format(url_path) # Create Tornado app instance - app = WebhookAppClass(url_path, self.bot, self.update_queue) + app = WebhookAppClass(url_path, self.bot, self.update_queue, + default_quote=self._default_quote) # Form SSL Context # An SSLError is raised if the private key does not match with the certificate diff --git a/telegram/files/inputmedia.py b/telegram/files/inputmedia.py index fcfc710853e..39131f61322 100644 --- a/telegram/files/inputmedia.py +++ b/telegram/files/inputmedia.py @@ -19,6 +19,7 @@ """Base class for Telegram InputMedia Objects.""" from telegram import TelegramObject, InputFile, PhotoSize, Animation, Video, Audio, Document +from telegram.utils.helpers import DEFAULT_NONE class InputMedia(TelegramObject): @@ -77,7 +78,13 @@ class InputMediaAnimation(InputMedia): arguments. """ - def __init__(self, media, thumb=None, caption=None, parse_mode=None, width=None, height=None, + def __init__(self, + media, + thumb=None, + caption=None, + parse_mode=DEFAULT_NONE, + width=None, + height=None, duration=None): self.type = 'animation' @@ -98,8 +105,7 @@ def __init__(self, media, thumb=None, caption=None, parse_mode=None, width=None, if caption: self.caption = caption - if parse_mode: - self.parse_mode = parse_mode + self.parse_mode = parse_mode if width: self.width = width if height: @@ -133,7 +139,7 @@ class InputMediaPhoto(InputMedia): in :class:`telegram.ParseMode` for the available modes. """ - def __init__(self, media, caption=None, parse_mode=None): + def __init__(self, media, caption=None, parse_mode=DEFAULT_NONE): self.type = 'photo' if isinstance(media, PhotoSize): @@ -145,8 +151,7 @@ def __init__(self, media, caption=None, parse_mode=None): if caption: self.caption = caption - if parse_mode: - self.parse_mode = parse_mode + self.parse_mode = parse_mode class InputMediaVideo(InputMedia): @@ -198,7 +203,7 @@ class InputMediaVideo(InputMedia): """ def __init__(self, media, caption=None, width=None, height=None, duration=None, - supports_streaming=None, parse_mode=None, thumb=None): + supports_streaming=None, parse_mode=DEFAULT_NONE, thumb=None): self.type = 'video' if isinstance(media, Video): @@ -218,8 +223,7 @@ def __init__(self, media, caption=None, width=None, height=None, duration=None, if caption: self.caption = caption - if parse_mode: - self.parse_mode = parse_mode + self.parse_mode = parse_mode if width: self.width = width if height: @@ -276,7 +280,7 @@ class InputMediaAudio(InputMedia): optional arguments. """ - def __init__(self, media, thumb=None, caption=None, parse_mode=None, + def __init__(self, media, thumb=None, caption=None, parse_mode=DEFAULT_NONE, duration=None, performer=None, title=None): self.type = 'audio' @@ -297,8 +301,7 @@ def __init__(self, media, thumb=None, caption=None, parse_mode=None, if caption: self.caption = caption - if parse_mode: - self.parse_mode = parse_mode + self.parse_mode = parse_mode if duration: self.duration = duration if performer: @@ -340,7 +343,7 @@ class InputMediaDocument(InputMedia): is passed as a string or file_id. """ - def __init__(self, media, thumb=None, caption=None, parse_mode=None): + def __init__(self, media, thumb=None, caption=None, parse_mode=DEFAULT_NONE): self.type = 'document' if isinstance(media, Document): @@ -357,5 +360,4 @@ def __init__(self, media, thumb=None, caption=None, parse_mode=None): if caption: self.caption = caption - if parse_mode: - self.parse_mode = parse_mode + self.parse_mode = parse_mode diff --git a/telegram/inline/inlinequeryresult.py b/telegram/inline/inlinequeryresult.py index e68ba7f4eff..6073dd8af93 100644 --- a/telegram/inline/inlinequeryresult.py +++ b/telegram/inline/inlinequeryresult.py @@ -41,3 +41,11 @@ def __init__(self, type, id, **kwargs): self.id = str(id) self._id_attrs = (self.id,) + + @property + def _has_parse_mode(self): + return hasattr(self, 'parse_mode') + + @property + def _has_input_message_content(self): + return hasattr(self, 'input_message_content') diff --git a/telegram/inline/inlinequeryresultaudio.py b/telegram/inline/inlinequeryresultaudio.py index fefa6e4d9a8..40963d2d3dc 100644 --- a/telegram/inline/inlinequeryresultaudio.py +++ b/telegram/inline/inlinequeryresultaudio.py @@ -19,6 +19,7 @@ """This module contains the classes that represent Telegram InlineQueryResultAudio.""" from telegram import InlineQueryResult +from telegram.utils.helpers import DEFAULT_NONE class InlineQueryResultAudio(InlineQueryResult): @@ -70,7 +71,7 @@ def __init__(self, caption=None, reply_markup=None, input_message_content=None, - parse_mode=None, + parse_mode=DEFAULT_NONE, **kwargs): # Required diff --git a/telegram/inline/inlinequeryresultcachedaudio.py b/telegram/inline/inlinequeryresultcachedaudio.py index 2e8a6daa8e4..5c415df5d26 100644 --- a/telegram/inline/inlinequeryresultcachedaudio.py +++ b/telegram/inline/inlinequeryresultcachedaudio.py @@ -19,6 +19,7 @@ """This module contains the classes that represent Telegram InlineQueryResultCachedAudio.""" from telegram import InlineQueryResult +from telegram.utils.helpers import DEFAULT_NONE class InlineQueryResultCachedAudio(InlineQueryResult): @@ -61,7 +62,7 @@ def __init__(self, caption=None, reply_markup=None, input_message_content=None, - parse_mode=None, + parse_mode=DEFAULT_NONE, **kwargs): # Required super(InlineQueryResultCachedAudio, self).__init__('audio', id) diff --git a/telegram/inline/inlinequeryresultcacheddocument.py b/telegram/inline/inlinequeryresultcacheddocument.py index 2436e1e0887..ef28bb39ce5 100644 --- a/telegram/inline/inlinequeryresultcacheddocument.py +++ b/telegram/inline/inlinequeryresultcacheddocument.py @@ -19,6 +19,7 @@ """This module contains the classes that represent Telegram InlineQueryResultCachedDocument.""" from telegram import InlineQueryResult +from telegram.utils.helpers import DEFAULT_NONE class InlineQueryResultCachedDocument(InlineQueryResult): @@ -67,7 +68,7 @@ def __init__(self, caption=None, reply_markup=None, input_message_content=None, - parse_mode=None, + parse_mode=DEFAULT_NONE, **kwargs): # Required super(InlineQueryResultCachedDocument, self).__init__('document', id) diff --git a/telegram/inline/inlinequeryresultcachedgif.py b/telegram/inline/inlinequeryresultcachedgif.py index 6f98c74a85f..1493148fb86 100644 --- a/telegram/inline/inlinequeryresultcachedgif.py +++ b/telegram/inline/inlinequeryresultcachedgif.py @@ -19,6 +19,7 @@ """This module contains the classes that represent Telegram InlineQueryResultCachedGif.""" from telegram import InlineQueryResult +from telegram.utils.helpers import DEFAULT_NONE class InlineQueryResultCachedGif(InlineQueryResult): @@ -65,7 +66,7 @@ def __init__(self, caption=None, reply_markup=None, input_message_content=None, - parse_mode=None, + parse_mode=DEFAULT_NONE, **kwargs): # Required super(InlineQueryResultCachedGif, self).__init__('gif', id) diff --git a/telegram/inline/inlinequeryresultcachedmpeg4gif.py b/telegram/inline/inlinequeryresultcachedmpeg4gif.py index e6d209c379f..4d95f5a6922 100644 --- a/telegram/inline/inlinequeryresultcachedmpeg4gif.py +++ b/telegram/inline/inlinequeryresultcachedmpeg4gif.py @@ -19,6 +19,7 @@ """This module contains the classes that represent Telegram InlineQueryResultMpeg4Gif.""" from telegram import InlineQueryResult +from telegram.utils.helpers import DEFAULT_NONE class InlineQueryResultCachedMpeg4Gif(InlineQueryResult): @@ -65,7 +66,7 @@ def __init__(self, caption=None, reply_markup=None, input_message_content=None, - parse_mode=None, + parse_mode=DEFAULT_NONE, **kwargs): # Required super(InlineQueryResultCachedMpeg4Gif, self).__init__('mpeg4_gif', id) diff --git a/telegram/inline/inlinequeryresultcachedphoto.py b/telegram/inline/inlinequeryresultcachedphoto.py index bab29fb5009..d97121f2b98 100644 --- a/telegram/inline/inlinequeryresultcachedphoto.py +++ b/telegram/inline/inlinequeryresultcachedphoto.py @@ -19,6 +19,7 @@ """This module contains the classes that represent Telegram InlineQueryResultPhoto""" from telegram import InlineQueryResult +from telegram.utils.helpers import DEFAULT_NONE class InlineQueryResultCachedPhoto(InlineQueryResult): @@ -68,7 +69,7 @@ def __init__(self, caption=None, reply_markup=None, input_message_content=None, - parse_mode=None, + parse_mode=DEFAULT_NONE, **kwargs): # Required super(InlineQueryResultCachedPhoto, self).__init__('photo', id) diff --git a/telegram/inline/inlinequeryresultcachedvideo.py b/telegram/inline/inlinequeryresultcachedvideo.py index 8ef8d08d084..d4970ac5113 100644 --- a/telegram/inline/inlinequeryresultcachedvideo.py +++ b/telegram/inline/inlinequeryresultcachedvideo.py @@ -19,6 +19,7 @@ """This module contains the classes that represent Telegram InlineQueryResultCachedVideo.""" from telegram import InlineQueryResult +from telegram.utils.helpers import DEFAULT_NONE class InlineQueryResultCachedVideo(InlineQueryResult): @@ -68,7 +69,7 @@ def __init__(self, caption=None, reply_markup=None, input_message_content=None, - parse_mode=None, + parse_mode=DEFAULT_NONE, **kwargs): # Required super(InlineQueryResultCachedVideo, self).__init__('video', id) diff --git a/telegram/inline/inlinequeryresultcachedvoice.py b/telegram/inline/inlinequeryresultcachedvoice.py index b3d6b65fc92..8df5f97cadc 100644 --- a/telegram/inline/inlinequeryresultcachedvoice.py +++ b/telegram/inline/inlinequeryresultcachedvoice.py @@ -19,6 +19,7 @@ """This module contains the classes that represent Telegram InlineQueryResultCachedVoice.""" from telegram import InlineQueryResult +from telegram.utils.helpers import DEFAULT_NONE class InlineQueryResultCachedVoice(InlineQueryResult): @@ -64,7 +65,7 @@ def __init__(self, caption=None, reply_markup=None, input_message_content=None, - parse_mode=None, + parse_mode=DEFAULT_NONE, **kwargs): # Required super(InlineQueryResultCachedVoice, self).__init__('voice', id) diff --git a/telegram/inline/inlinequeryresultdocument.py b/telegram/inline/inlinequeryresultdocument.py index 7d0ddd2a66f..215b9617285 100644 --- a/telegram/inline/inlinequeryresultdocument.py +++ b/telegram/inline/inlinequeryresultdocument.py @@ -19,6 +19,7 @@ """This module contains the classes that represent Telegram InlineQueryResultDocument""" from telegram import InlineQueryResult +from telegram.utils.helpers import DEFAULT_NONE class InlineQueryResultDocument(InlineQueryResult): @@ -82,7 +83,7 @@ def __init__(self, thumb_url=None, thumb_width=None, thumb_height=None, - parse_mode=None, + parse_mode=DEFAULT_NONE, **kwargs): # Required super(InlineQueryResultDocument, self).__init__('document', id) diff --git a/telegram/inline/inlinequeryresultgif.py b/telegram/inline/inlinequeryresultgif.py index 830ea628d07..48a50fa1329 100644 --- a/telegram/inline/inlinequeryresultgif.py +++ b/telegram/inline/inlinequeryresultgif.py @@ -19,6 +19,7 @@ """This module contains the classes that represent Telegram InlineQueryResultGif.""" from telegram import InlineQueryResult +from telegram.utils.helpers import DEFAULT_NONE class InlineQueryResultGif(InlineQueryResult): @@ -76,7 +77,7 @@ def __init__(self, reply_markup=None, input_message_content=None, gif_duration=None, - parse_mode=None, + parse_mode=DEFAULT_NONE, **kwargs): # Required diff --git a/telegram/inline/inlinequeryresultmpeg4gif.py b/telegram/inline/inlinequeryresultmpeg4gif.py index ebeb5b4dbea..1c06e03be5d 100644 --- a/telegram/inline/inlinequeryresultmpeg4gif.py +++ b/telegram/inline/inlinequeryresultmpeg4gif.py @@ -19,6 +19,7 @@ """This module contains the classes that represent Telegram InlineQueryResultMpeg4Gif.""" from telegram import InlineQueryResult +from telegram.utils.helpers import DEFAULT_NONE class InlineQueryResultMpeg4Gif(InlineQueryResult): @@ -77,7 +78,7 @@ def __init__(self, reply_markup=None, input_message_content=None, mpeg4_duration=None, - parse_mode=None, + parse_mode=DEFAULT_NONE, **kwargs): # Required diff --git a/telegram/inline/inlinequeryresultphoto.py b/telegram/inline/inlinequeryresultphoto.py index 46c0be76890..698661d8c24 100644 --- a/telegram/inline/inlinequeryresultphoto.py +++ b/telegram/inline/inlinequeryresultphoto.py @@ -19,6 +19,7 @@ """This module contains the classes that represent Telegram InlineQueryResultPhoto.""" from telegram import InlineQueryResult +from telegram.utils.helpers import DEFAULT_NONE class InlineQueryResultPhoto(InlineQueryResult): @@ -78,7 +79,7 @@ def __init__(self, caption=None, reply_markup=None, input_message_content=None, - parse_mode=None, + parse_mode=DEFAULT_NONE, **kwargs): # Required super(InlineQueryResultPhoto, self).__init__('photo', id) diff --git a/telegram/inline/inlinequeryresultvideo.py b/telegram/inline/inlinequeryresultvideo.py index 0b83548dd52..f802d020655 100644 --- a/telegram/inline/inlinequeryresultvideo.py +++ b/telegram/inline/inlinequeryresultvideo.py @@ -19,6 +19,7 @@ """This module contains the classes that represent Telegram InlineQueryResultVideo.""" from telegram import InlineQueryResult +from telegram.utils.helpers import DEFAULT_NONE class InlineQueryResultVideo(InlineQueryResult): @@ -83,7 +84,7 @@ def __init__(self, description=None, reply_markup=None, input_message_content=None, - parse_mode=None, + parse_mode=DEFAULT_NONE, **kwargs): # Required diff --git a/telegram/inline/inlinequeryresultvoice.py b/telegram/inline/inlinequeryresultvoice.py index f12079f35b1..3111904d5ef 100644 --- a/telegram/inline/inlinequeryresultvoice.py +++ b/telegram/inline/inlinequeryresultvoice.py @@ -19,6 +19,7 @@ """This module contains the classes that represent Telegram InlineQueryResultVoice.""" from telegram import InlineQueryResult +from telegram.utils.helpers import DEFAULT_NONE class InlineQueryResultVoice(InlineQueryResult): @@ -68,7 +69,7 @@ def __init__(self, caption=None, reply_markup=None, input_message_content=None, - parse_mode=None, + parse_mode=DEFAULT_NONE, **kwargs): # Required diff --git a/telegram/inline/inputmessagecontent.py b/telegram/inline/inputmessagecontent.py index 8f45a0a0cb8..d045306e509 100644 --- a/telegram/inline/inputmessagecontent.py +++ b/telegram/inline/inputmessagecontent.py @@ -29,4 +29,10 @@ class InputMessageContent(TelegramObject): :class:`telegram.InputVenueMessageContent` for more details. """ - pass + @property + def _has_parse_mode(self): + return hasattr(self, 'parse_mode') + + @property + def _has_disable_web_page_preview(self): + return hasattr(self, 'disable_web_page_preview') diff --git a/telegram/inline/inputtextmessagecontent.py b/telegram/inline/inputtextmessagecontent.py index 9b726bfc978..8948d2c77cc 100644 --- a/telegram/inline/inputtextmessagecontent.py +++ b/telegram/inline/inputtextmessagecontent.py @@ -19,6 +19,7 @@ """This module contains the classes that represent Telegram InputTextMessageContent.""" from telegram import InputMessageContent +from telegram.utils.helpers import DEFAULT_NONE class InputTextMessageContent(InputMessageContent): @@ -43,7 +44,11 @@ class InputTextMessageContent(InputMessageContent): """ - def __init__(self, message_text, parse_mode=None, disable_web_page_preview=None, **kwargs): + def __init__(self, + message_text, + parse_mode=DEFAULT_NONE, + disable_web_page_preview=DEFAULT_NONE, + **kwargs): # Required self.message_text = message_text # Optionals diff --git a/telegram/message.py b/telegram/message.py index 11a3af8a39d..c6fe8f2d075 100644 --- a/telegram/message.py +++ b/telegram/message.py @@ -109,6 +109,8 @@ class Message(TelegramObject): reply_markup (:class:`telegram.InlineKeyboardMarkup`): Optional. Inline keyboard attached to the message. bot (:class:`telegram.Bot`): Optional. The Bot to use for instance methods. + default_quote (:obj:`bool`): Optional. Default setting for the `quote` parameter of the + :attr:`reply_text` and friends. Args: message_id (:obj:`int`): Unique message identifier inside this chat. @@ -214,6 +216,8 @@ class Message(TelegramObject): information about the poll. reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): Inline keyboard attached to the message. login_url buttons are represented as ordinary url buttons. + default_quote (:obj:`bool`, optional): Default setting for the `quote` parameter of the + :attr:`reply_text` and friends. """ @@ -277,6 +281,7 @@ def __init__(self, forward_sender_name=None, reply_markup=None, bot=None, + default_quote=None, **kwargs): # Required self.message_id = int(message_id) @@ -328,6 +333,7 @@ def __init__(self, self.poll = poll self.reply_markup = reply_markup self.bot = bot + self.default_quote = default_quote self._id_attrs = (self.message_id,) @@ -358,13 +364,22 @@ def de_json(cls, data, bot): data['from_user'] = User.de_json(data.get('from'), bot) data['date'] = from_timestamp(data['date']) - data['chat'] = Chat.de_json(data.get('chat'), bot) + chat = data.get('chat') + if chat: + chat['default_quote'] = data.get('default_quote') + data['chat'] = Chat.de_json(chat, bot) data['entities'] = MessageEntity.de_list(data.get('entities'), bot) data['caption_entities'] = MessageEntity.de_list(data.get('caption_entities'), bot) data['forward_from'] = User.de_json(data.get('forward_from'), bot) - data['forward_from_chat'] = Chat.de_json(data.get('forward_from_chat'), bot) + forward_from_chat = data.get('forward_from_chat') + if forward_from_chat: + forward_from_chat['default_quote'] = data.get('default_quote') + data['forward_from_chat'] = Chat.de_json(forward_from_chat, bot) data['forward_date'] = from_timestamp(data.get('forward_date')) - data['reply_to_message'] = Message.de_json(data.get('reply_to_message'), bot) + reply_to_message = data.get('reply_to_message') + if reply_to_message: + reply_to_message['default_quote'] = data.get('default_quote') + data['reply_to_message'] = Message.de_json(reply_to_message, bot) data['edit_date'] = from_timestamp(data.get('edit_date')) data['audio'] = Audio.de_json(data.get('audio'), bot) data['document'] = Document.de_json(data.get('document'), bot) @@ -381,7 +396,10 @@ def de_json(cls, data, bot): data['new_chat_members'] = User.de_list(data.get('new_chat_members'), bot) data['left_chat_member'] = User.de_json(data.get('left_chat_member'), bot) data['new_chat_photo'] = PhotoSize.de_list(data.get('new_chat_photo'), bot) - data['pinned_message'] = Message.de_json(data.get('pinned_message'), bot) + pinned_message = data.get('pinned_message') + if pinned_message: + pinned_message['default_quote'] = data.get('default_quote') + data['pinned_message'] = Message.de_json(pinned_message, bot) data['invoice'] = Invoice.de_json(data.get('invoice'), bot) data['successful_payment'] = SuccessfulPayment.de_json(data.get('successful_payment'), bot) data['passport_data'] = PassportData.de_json(data.get('passport_data'), bot) @@ -464,7 +482,8 @@ def _quote(self, kwargs): del kwargs['quote'] else: - if self.chat.type != Chat.PRIVATE: + if ((self.default_quote is None and self.chat.type != Chat.PRIVATE) + or self.default_quote): kwargs['reply_to_message_id'] = self.message_id def reply_text(self, *args, **kwargs): diff --git a/telegram/update.py b/telegram/update.py index 35b6d3a94a5..d930a8ab1d0 100644 --- a/telegram/update.py +++ b/telegram/update.py @@ -211,16 +211,31 @@ def de_json(cls, data, bot): data = super(Update, cls).de_json(data, bot) - data['message'] = Message.de_json(data.get('message'), bot) - data['edited_message'] = Message.de_json(data.get('edited_message'), bot) + message = data.get('message') + if message: + message['default_quote'] = data.get('default_quote') + data['message'] = Message.de_json(message, bot) + edited_message = data.get('edited_message') + if edited_message: + edited_message['default_quote'] = data.get('default_quote') + data['edited_message'] = Message.de_json(edited_message, bot) data['inline_query'] = InlineQuery.de_json(data.get('inline_query'), bot) data['chosen_inline_result'] = ChosenInlineResult.de_json( data.get('chosen_inline_result'), bot) - data['callback_query'] = CallbackQuery.de_json(data.get('callback_query'), bot) + callback_query = data.get('callback_query') + if callback_query: + callback_query['default_quote'] = data.get('default_quote') + data['callback_query'] = CallbackQuery.de_json(callback_query, bot) data['shipping_query'] = ShippingQuery.de_json(data.get('shipping_query'), bot) data['pre_checkout_query'] = PreCheckoutQuery.de_json(data.get('pre_checkout_query'), bot) - data['channel_post'] = Message.de_json(data.get('channel_post'), bot) - data['edited_channel_post'] = Message.de_json(data.get('edited_channel_post'), bot) + channel_post = data.get('channel_post') + if channel_post: + channel_post['default_quote'] = data.get('default_quote') + data['channel_post'] = Message.de_json(channel_post, bot) + edited_channel_post = data.get('edited_channel_post') + if edited_channel_post: + edited_channel_post['default_quote'] = data.get('default_quote') + data['edited_channel_post'] = Message.de_json(edited_channel_post, bot) data['poll'] = Poll.de_json(data.get('poll'), bot) return cls(**data) diff --git a/telegram/utils/helpers.py b/telegram/utils/helpers.py index 8a078618091..4a527c8a6af 100644 --- a/telegram/utils/helpers.py +++ b/telegram/utils/helpers.py @@ -360,3 +360,57 @@ def decode_user_chat_data_from_json(data): pass tmp[user][key] = value return tmp + + +class DefaultValue: + """Wrapper for immutable default arguments that allows to check, if the default value was set + explicitly. Usage:: + + DefaultOne = DefaultValue(1) + def f(arg=DefaultOne): + if arg is DefaultOne: + print('`arg` is the default') + arg = arg.value + else: + print('`arg` was set explicitly') + print('`arg` = ' + str(arg)) + + This yields:: + + >>> f() + `arg` is the default + `arg` = 1 + >>> f(1) + `arg` was set explicitly + `arg` = 1 + >>> f(2) + `arg` was set explicitly + `arg` = 2 + + Also allows to evaluate truthiness:: + + default = DefaultValue(value) + if default: + ... + + is equivalent to:: + + default = DefaultValue(value) + if value: + ... + + Attributes: + value (:obj:`obj`): The value of the default argument + + Args: + value (:obj:`obj`): The value of the default argument + """ + def __init__(self, value=None): + self.value = value + + def __bool__(self): + return bool(self.value) + + +DEFAULT_NONE = DefaultValue(None) +""":class:`DefaultValue`: Default `None`""" diff --git a/telegram/utils/webhookhandler.py b/telegram/utils/webhookhandler.py index 5678146c5c7..32db6ae0fd4 100644 --- a/telegram/utils/webhookhandler.py +++ b/telegram/utils/webhookhandler.py @@ -71,8 +71,9 @@ def handle_error(self, request, client_address): class WebhookAppClass(tornado.web.Application): - def __init__(self, webhook_path, bot, update_queue): - self.shared_objects = {"bot": bot, "update_queue": update_queue} + def __init__(self, webhook_path, bot, update_queue, default_quote=None): + self.shared_objects = {"bot": bot, "update_queue": update_queue, + "default_quote": default_quote} handlers = [ (r"{0}/?".format(webhook_path), WebhookHandler, self.shared_objects) @@ -91,9 +92,10 @@ def __init__(self, application, request, **kwargs): super(WebhookHandler, self).__init__(application, request, **kwargs) self.logger = logging.getLogger(__name__) - def initialize(self, bot, update_queue): + def initialize(self, bot, update_queue, default_quote=None): self.bot = bot self.update_queue = update_queue + self._default_quote = default_quote def set_default_headers(self): self.set_header("Content-Type", 'application/json; charset="utf-8"') @@ -105,6 +107,7 @@ def post(self): data = json.loads(json_string) self.set_status(200) self.logger.debug('Webhook received data: ' + json_string) + data['default_quote'] = self._default_quote update = Update.de_json(data, self.bot) self.logger.debug('Received Update with ID %d on Webhook' % update.update_id) self.update_queue.put(update) diff --git a/tests/conftest.py b/tests/conftest.py index 7e9826dc4cf..7d7afe444d0 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -30,7 +30,7 @@ from telegram import (Bot, Message, User, Chat, MessageEntity, Update, InlineQuery, CallbackQuery, ShippingQuery, PreCheckoutQuery, ChosenInlineResult) -from telegram.ext import Dispatcher, JobQueue, Updater, BaseFilter +from telegram.ext import Dispatcher, JobQueue, Updater, BaseFilter, Defaults from telegram.utils.helpers import _UtcOffsetTimezone from tests.bots import get_bot @@ -59,6 +59,21 @@ def bot(bot_info): return make_bot(bot_info) +DEFAULT_BOTS = {} +@pytest.fixture(scope='function') +def default_bot(request, bot_info): + param = request.param if hasattr(request, 'param') else {} + + defaults = Defaults(**param) + default_bot = DEFAULT_BOTS.get(defaults) + if default_bot: + return default_bot + else: + default_bot = make_bot(bot_info, **{'defaults': defaults}) + DEFAULT_BOTS[defaults] = default_bot + return default_bot + + @pytest.fixture(scope='session') def chat_id(bot_info): return bot_info['chat_id'] @@ -159,8 +174,8 @@ def pytest_configure(config): # TODO: Write so good code that we don't need to ignore ResourceWarnings anymore -def make_bot(bot_info): - return Bot(bot_info['token'], private_key=PRIVATE_KEY) +def make_bot(bot_info, **kwargs): + return Bot(bot_info['token'], private_key=PRIVATE_KEY, **kwargs) CMD_PATTERN = re.compile(r'/[\da-z_]{1,32}(?:@\w{1,32})?') diff --git a/tests/test_animation.py b/tests/test_animation.py index b2229b021f4..c659dbf9bd7 100644 --- a/tests/test_animation.py +++ b/tests/test_animation.py @@ -22,6 +22,7 @@ from flaky import flaky from telegram import PhotoSize, Animation, Voice, TelegramError +from telegram.utils.helpers import escape_markdown @pytest.fixture(scope='function') @@ -107,6 +108,39 @@ def test_send_animation_url_file(self, bot, chat_id, animation): assert message.animation.mime_type == animation.mime_type assert message.animation.file_size == animation.file_size + @flaky(3, 1) + @pytest.mark.timeout(10) + @pytest.mark.parametrize('default_bot', [{'parse_mode': 'Markdown'}], indirect=True) + def test_send_animation_default_parse_mode_1(self, default_bot, chat_id, animation_file): + test_string = 'Italic Bold Code' + test_markdown_string = '_Italic_ *Bold* `Code`' + + message = default_bot.send_animation(chat_id, animation_file, caption=test_markdown_string) + assert message.caption_markdown == test_markdown_string + assert message.caption == test_string + + @flaky(3, 1) + @pytest.mark.timeout(10) + @pytest.mark.parametrize('default_bot', [{'parse_mode': 'Markdown'}], indirect=True) + def test_send_animation_default_parse_mode_2(self, default_bot, chat_id, animation_file): + test_markdown_string = '_Italic_ *Bold* `Code`' + + message = default_bot.send_animation(chat_id, animation_file, caption=test_markdown_string, + parse_mode=None) + assert message.caption == test_markdown_string + assert message.caption_markdown == escape_markdown(test_markdown_string) + + @flaky(3, 1) + @pytest.mark.timeout(10) + @pytest.mark.parametrize('default_bot', [{'parse_mode': 'Markdown'}], indirect=True) + def test_send_animation_default_parse_mode_3(self, default_bot, chat_id, animation_file): + test_markdown_string = '_Italic_ *Bold* `Code`' + + message = default_bot.send_animation(chat_id, animation_file, caption=test_markdown_string, + parse_mode='HTML') + assert message.caption == test_markdown_string + assert message.caption_markdown == escape_markdown(test_markdown_string) + @flaky(3, 1) @pytest.mark.timeout(10) @pytest.mark.skip(reason='Doesnt work without API 4.5') diff --git a/tests/test_audio.py b/tests/test_audio.py index a15de6f9694..b9c66ef8496 100644 --- a/tests/test_audio.py +++ b/tests/test_audio.py @@ -22,6 +22,7 @@ from flaky import flaky from telegram import Audio, TelegramError, Voice +from telegram.utils.helpers import escape_markdown @pytest.fixture(scope='function') @@ -133,6 +134,39 @@ def test(_, url, data, **kwargs): message = bot.send_audio(audio=audio, chat_id=chat_id) assert message + @flaky(3, 1) + @pytest.mark.timeout(10) + @pytest.mark.parametrize('default_bot', [{'parse_mode': 'Markdown'}], indirect=True) + def test_send_audio_default_parse_mode_1(self, default_bot, chat_id, audio_file, thumb_file): + test_string = 'Italic Bold Code' + test_markdown_string = '_Italic_ *Bold* `Code`' + + message = default_bot.send_audio(chat_id, audio_file, caption=test_markdown_string) + assert message.caption_markdown == test_markdown_string + assert message.caption == test_string + + @flaky(3, 1) + @pytest.mark.timeout(10) + @pytest.mark.parametrize('default_bot', [{'parse_mode': 'Markdown'}], indirect=True) + def test_send_audio_default_parse_mode_2(self, default_bot, chat_id, audio_file, thumb_file): + test_markdown_string = '_Italic_ *Bold* `Code`' + + message = default_bot.send_audio(chat_id, audio_file, caption=test_markdown_string, + parse_mode=None) + assert message.caption == test_markdown_string + assert message.caption_markdown == escape_markdown(test_markdown_string) + + @flaky(3, 1) + @pytest.mark.timeout(10) + @pytest.mark.parametrize('default_bot', [{'parse_mode': 'Markdown'}], indirect=True) + def test_send_audio_default_parse_mode_3(self, default_bot, chat_id, audio_file, thumb_file): + test_markdown_string = '_Italic_ *Bold* `Code`' + + message = default_bot.send_audio(chat_id, audio_file, caption=test_markdown_string, + parse_mode='HTML') + assert message.caption == test_markdown_string + 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, diff --git a/tests/test_bot.py b/tests/test_bot.py index 70bb4663f7c..1bf5756ed21 100644 --- a/tests/test_bot.py +++ b/tests/test_bot.py @@ -30,7 +30,7 @@ InlineKeyboardButton, InlineQueryResultArticle, InputTextMessageContent, ShippingOption, LabeledPrice, ChatPermissions, Poll) from telegram.error import BadRequest, InvalidToken, NetworkError, RetryAfter -from telegram.utils.helpers import from_timestamp +from telegram.utils.helpers import from_timestamp, escape_markdown BASE_TIME = time.time() HIGHSCORE_DELTA = 1450000000 @@ -309,6 +309,34 @@ def test_edit_message_text(self, bot, message): assert message.text == 'new_text' + @flaky(3, 1) + @pytest.mark.timeout(10) + @pytest.mark.parametrize('default_bot', [{'parse_mode': 'Markdown'}], indirect=True) + def test_edit_message_text_default_parse_mode(self, default_bot, message): + test_string = 'Italic Bold Code' + test_markdown_string = '_Italic_ *Bold* `Code`' + + message = default_bot.edit_message_text(text=test_markdown_string, chat_id=message.chat_id, + message_id=message.message_id, + disable_web_page_preview=True) + assert message.text_markdown == test_markdown_string + assert message.text == test_string + + message = default_bot.edit_message_text(text=test_markdown_string, chat_id=message.chat_id, + message_id=message.message_id, parse_mode=None, + disable_web_page_preview=True) + assert message.text == test_markdown_string + assert message.text_markdown == escape_markdown(test_markdown_string) + + message = default_bot.edit_message_text(text=test_markdown_string, chat_id=message.chat_id, + message_id=message.message_id, + disable_web_page_preview=True) + message = default_bot.edit_message_text(text=test_markdown_string, chat_id=message.chat_id, + message_id=message.message_id, parse_mode='HTML', + disable_web_page_preview=True) + assert message.text == test_markdown_string + assert message.text_markdown == escape_markdown(test_markdown_string) + @pytest.mark.skip(reason='need reference to an inline message') def test_edit_message_text_inline(self): pass @@ -323,6 +351,36 @@ def test_edit_message_caption(self, bot, media_message): # edit_message_media is tested in test_inputmedia + @flaky(3, 1) + @pytest.mark.timeout(10) + @pytest.mark.parametrize('default_bot', [{'parse_mode': 'Markdown'}], indirect=True) + def test_edit_message_caption_default_parse_mode(self, default_bot, media_message): + test_string = 'Italic Bold Code' + test_markdown_string = '_Italic_ *Bold* `Code`' + + message = default_bot.edit_message_caption(caption=test_markdown_string, + chat_id=media_message.chat_id, + message_id=media_message.message_id) + assert message.caption_markdown == test_markdown_string + assert message.caption == test_string + + message = default_bot.edit_message_caption(caption=test_markdown_string, + chat_id=media_message.chat_id, + message_id=media_message.message_id, + parse_mode=None) + assert message.caption == test_markdown_string + assert message.caption_markdown == escape_markdown(test_markdown_string) + + message = default_bot.edit_message_caption(caption=test_markdown_string, + chat_id=media_message.chat_id, + message_id=media_message.message_id) + message = default_bot.edit_message_caption(caption=test_markdown_string, + chat_id=media_message.chat_id, + message_id=media_message.message_id, + parse_mode='HTML') + assert message.caption == test_markdown_string + assert message.caption_markdown == escape_markdown(test_markdown_string) + @flaky(3, 1) @pytest.mark.timeout(10) def test_edit_message_caption_with_parse_mode(self, bot, media_message): @@ -409,6 +467,20 @@ def test_get_chat(self, bot, super_group_id): assert chat.title == '>>> telegram.Bot(test)' assert chat.id == int(super_group_id) + # TODO: Add bot to group to test there too + @flaky(3, 1) + @pytest.mark.timeout(10) + @pytest.mark.parametrize('default_bot', [{'quote': True}], indirect=True) + def test_get_chat_default_quote(self, default_bot, super_group_id): + message = default_bot.send_message(super_group_id, text="test_get_chat_default_quote") + assert default_bot.pin_chat_message(chat_id=super_group_id, message_id=message.message_id, + disable_notification=True) + + chat = default_bot.get_chat(super_group_id) + assert chat.pinned_message.default_quote is True + + assert default_bot.unpinChatMessage(super_group_id) + @flaky(3, 1) @pytest.mark.timeout(10) def test_get_chat_administrators(self, bot, channel_id): @@ -724,3 +796,29 @@ def request_wrapper(*args, **kwargs): # Test file uploading with pytest.raises(OkException): bot.send_photo(chat_id, open('tests/data/telegram.jpg', 'rb')) + + @flaky(3, 1) + @pytest.mark.timeout(10) + @pytest.mark.parametrize('default_bot', [{'parse_mode': 'Markdown'}], indirect=True) + def test_send_message_default_parse_mode(self, default_bot, chat_id): + test_string = 'Italic Bold Code' + test_markdown_string = '_Italic_ *Bold* `Code`' + + message = default_bot.send_message(chat_id, test_markdown_string) + assert message.text_markdown == test_markdown_string + assert message.text == test_string + + message = default_bot.send_message(chat_id, test_markdown_string, parse_mode=None) + assert message.text == test_markdown_string + assert message.text_markdown == escape_markdown(test_markdown_string) + + message = default_bot.send_message(chat_id, test_markdown_string, parse_mode='HTML') + assert message.text == test_markdown_string + assert message.text_markdown == escape_markdown(test_markdown_string) + + @flaky(3, 1) + @pytest.mark.timeout(10) + @pytest.mark.parametrize('default_bot', [{'quote': True}], indirect=True) + def test_send_message_default_quote(self, default_bot, chat_id): + message = default_bot.send_message(chat_id, 'test') + assert message.default_quote is True diff --git a/tests/test_callbackquery.py b/tests/test_callbackquery.py index a7f3228fe00..909b4348133 100644 --- a/tests/test_callbackquery.py +++ b/tests/test_callbackquery.py @@ -53,13 +53,15 @@ def test_de_json(self, bot): 'message': self.message.to_dict(), 'data': self.data, 'inline_message_id': self.inline_message_id, - 'game_short_name': self.game_short_name} + 'game_short_name': self.game_short_name, + 'default_quote': True} callback_query = CallbackQuery.de_json(json_dict, bot) assert callback_query.id == self.id assert callback_query.from_user == self.from_user assert callback_query.chat_instance == self.chat_instance assert callback_query.message == self.message + assert callback_query.message.default_quote is True assert callback_query.data == self.data assert callback_query.inline_message_id == self.inline_message_id assert callback_query.game_short_name == self.game_short_name @@ -80,15 +82,15 @@ def test_to_dict(self, callback_query): def test_answer(self, monkeypatch, callback_query): def test(*args, **kwargs): - return args[1] == callback_query.id + return args[0] == callback_query.id - monkeypatch.setattr('telegram.Bot.answerCallbackQuery', test) + monkeypatch.setattr(callback_query.bot, 'answerCallbackQuery', test) # TODO: PEP8 assert callback_query.answer() def test_edit_message_text(self, monkeypatch, callback_query): def test(*args, **kwargs): - text = args[1] == 'test' + text = args[0] == 'test' try: id = kwargs['inline_message_id'] == callback_query.inline_message_id return id and text @@ -97,7 +99,7 @@ def test(*args, **kwargs): message_id = kwargs['message_id'] == callback_query.message.message_id return chat_id and message_id and text - monkeypatch.setattr('telegram.Bot.edit_message_text', test) + monkeypatch.setattr(callback_query.bot, 'edit_message_text', test) assert callback_query.edit_message_text(text='test') assert callback_query.edit_message_text('test') @@ -112,7 +114,7 @@ def test(*args, **kwargs): message = kwargs['message_id'] == callback_query.message.message_id return id and message and caption - monkeypatch.setattr('telegram.Bot.edit_message_caption', test) + monkeypatch.setattr(callback_query.bot, 'edit_message_caption', test) assert callback_query.edit_message_caption(caption='new caption') assert callback_query.edit_message_caption('new caption') @@ -127,7 +129,7 @@ def test(*args, **kwargs): message = kwargs['message_id'] == callback_query.message.message_id return id and message and reply_markup - monkeypatch.setattr('telegram.Bot.edit_message_reply_markup', test) + monkeypatch.setattr(callback_query.bot, 'edit_message_reply_markup', test) assert callback_query.edit_message_reply_markup(reply_markup=[['1', '2']]) assert callback_query.edit_message_reply_markup([['1', '2']]) diff --git a/tests/test_chat.py b/tests/test_chat.py index 800e58c1d1b..f2431ab2d6c 100644 --- a/tests/test_chat.py +++ b/tests/test_chat.py @@ -20,7 +20,7 @@ import pytest from telegram import Chat, ChatAction, ChatPermissions -from telegram import User +from telegram import User, Message @pytest.fixture(scope='class') @@ -68,6 +68,22 @@ def test_de_json(self, bot): assert chat.can_set_sticker_set == self.can_set_sticker_set assert chat.permissions == self.permissions + def test_de_json_default_quote(self, bot): + json_dict = { + 'id': self.id, + 'type': self.type, + 'pinned_message': Message( + message_id=123, + from_user=None, + date=None, + chat=None + ).to_dict(), + 'default_quote': True + } + chat = Chat.de_json(json_dict, bot) + + assert chat.pinned_message.default_quote is True + def test_to_dict(self, chat): chat_dict = chat.to_dict() @@ -86,139 +102,139 @@ def test_link(self, chat): def test_send_action(self, monkeypatch, chat): def test(*args, **kwargs): - id = args[1] == chat.id + id = args[0] == chat.id action = kwargs['action'] == ChatAction.TYPING return id and action - monkeypatch.setattr('telegram.Bot.send_chat_action', test) + monkeypatch.setattr(chat.bot, 'send_chat_action', test) assert chat.send_action(action=ChatAction.TYPING) def test_leave(self, monkeypatch, chat): def test(*args, **kwargs): - return args[1] == chat.id + return args[0] == chat.id - monkeypatch.setattr('telegram.Bot.leave_chat', test) + monkeypatch.setattr(chat.bot, 'leave_chat', test) assert chat.leave() def test_get_administrators(self, monkeypatch, chat): def test(*args, **kwargs): - return args[1] == chat.id + return args[0] == chat.id - monkeypatch.setattr('telegram.Bot.get_chat_administrators', test) + monkeypatch.setattr(chat.bot, 'get_chat_administrators', test) assert chat.get_administrators() def test_get_members_count(self, monkeypatch, chat): def test(*args, **kwargs): - return args[1] == chat.id + return args[0] == chat.id - monkeypatch.setattr('telegram.Bot.get_chat_members_count', test) + monkeypatch.setattr(chat.bot, 'get_chat_members_count', test) assert chat.get_members_count() def test_get_member(self, monkeypatch, chat): def test(*args, **kwargs): - chat_id = args[1] == chat.id - user_id = args[2] == 42 + chat_id = args[0] == chat.id + user_id = args[1] == 42 return chat_id and user_id - monkeypatch.setattr('telegram.Bot.get_chat_member', test) + monkeypatch.setattr(chat.bot, 'get_chat_member', test) assert chat.get_member(42) def test_kick_member(self, monkeypatch, chat): def test(*args, **kwargs): - chat_id = args[1] == chat.id - user_id = args[2] == 42 + chat_id = args[0] == chat.id + user_id = args[1] == 42 until = kwargs['until_date'] == 43 return chat_id and user_id and until - monkeypatch.setattr('telegram.Bot.kick_chat_member', test) + monkeypatch.setattr(chat.bot, 'kick_chat_member', test) assert chat.kick_member(42, until_date=43) def test_unban_member(self, monkeypatch, chat): def test(*args, **kwargs): - chat_id = args[1] == chat.id - user_id = args[2] == 42 + chat_id = args[0] == chat.id + user_id = args[1] == 42 return chat_id and user_id - monkeypatch.setattr('telegram.Bot.unban_chat_member', test) + monkeypatch.setattr(chat.bot, 'unban_chat_member', test) assert chat.unban_member(42) def test_set_permissions(self, monkeypatch, chat): def test(*args, **kwargs): - chat_id = args[1] == chat.id - permissions = args[2] == self.permissions + chat_id = args[0] == chat.id + permissions = args[1] == self.permissions return chat_id and permissions - monkeypatch.setattr('telegram.Bot.set_chat_permissions', test) + monkeypatch.setattr(chat.bot, 'set_chat_permissions', test) assert chat.set_permissions(self.permissions) def test_instance_method_send_message(self, monkeypatch, chat): def test(*args, **kwargs): - return args[1] == chat.id and args[2] == 'test' + return args[0] == chat.id and args[1] == 'test' - monkeypatch.setattr('telegram.Bot.send_message', test) + monkeypatch.setattr(chat.bot, 'send_message', test) assert chat.send_message('test') def test_instance_method_send_photo(self, monkeypatch, chat): def test(*args, **kwargs): - return args[1] == chat.id and args[2] == 'test_photo' + return args[0] == chat.id and args[1] == 'test_photo' - monkeypatch.setattr('telegram.Bot.send_photo', test) + monkeypatch.setattr(chat.bot, 'send_photo', test) assert chat.send_photo('test_photo') def test_instance_method_send_audio(self, monkeypatch, chat): def test(*args, **kwargs): - return args[1] == chat.id and args[2] == 'test_audio' + return args[0] == chat.id and args[1] == 'test_audio' - monkeypatch.setattr('telegram.Bot.send_audio', test) + monkeypatch.setattr(chat.bot, 'send_audio', test) assert chat.send_audio('test_audio') def test_instance_method_send_document(self, monkeypatch, chat): def test(*args, **kwargs): - return args[1] == chat.id and args[2] == 'test_document' + return args[0] == chat.id and args[1] == 'test_document' - monkeypatch.setattr('telegram.Bot.send_document', test) + monkeypatch.setattr(chat.bot, 'send_document', test) assert chat.send_document('test_document') def test_instance_method_send_sticker(self, monkeypatch, chat): def test(*args, **kwargs): - return args[1] == chat.id and args[2] == 'test_sticker' + return args[0] == chat.id and args[1] == 'test_sticker' - monkeypatch.setattr('telegram.Bot.send_sticker', test) + monkeypatch.setattr(chat.bot, 'send_sticker', test) assert chat.send_sticker('test_sticker') def test_instance_method_send_video(self, monkeypatch, chat): def test(*args, **kwargs): - return args[1] == chat.id and args[2] == 'test_video' + return args[0] == chat.id and args[1] == 'test_video' - monkeypatch.setattr('telegram.Bot.send_video', test) + monkeypatch.setattr(chat.bot, 'send_video', test) assert chat.send_video('test_video') def test_instance_method_send_video_note(self, monkeypatch, chat): def test(*args, **kwargs): - return args[1] == chat.id and args[2] == 'test_video_note' + return args[0] == chat.id and args[1] == 'test_video_note' - monkeypatch.setattr('telegram.Bot.send_video_note', test) + monkeypatch.setattr(chat.bot, 'send_video_note', test) assert chat.send_video_note('test_video_note') def test_instance_method_send_voice(self, monkeypatch, chat): def test(*args, **kwargs): - return args[1] == chat.id and args[2] == 'test_voice' + return args[0] == chat.id and args[1] == 'test_voice' - monkeypatch.setattr('telegram.Bot.send_voice', test) + monkeypatch.setattr(chat.bot, 'send_voice', test) assert chat.send_voice('test_voice') def test_instance_method_send_animation(self, monkeypatch, chat): def test(*args, **kwargs): - return args[1] == chat.id and args[2] == 'test_animation' + return args[0] == chat.id and args[1] == 'test_animation' - monkeypatch.setattr('telegram.Bot.send_animation', test) + monkeypatch.setattr(chat.bot, 'send_animation', test) assert chat.send_animation('test_animation') def test_instance_method_send_poll(self, monkeypatch, chat): def test(*args, **kwargs): - return args[1] == chat.id and args[2] == 'test_poll' + return args[0] == chat.id and args[1] == 'test_poll' - monkeypatch.setattr('telegram.Bot.send_poll', test) + monkeypatch.setattr(chat.bot, 'send_poll', test) assert chat.send_poll('test_poll') def test_equality(self): diff --git a/tests/test_defaults.py b/tests/test_defaults.py new file mode 100644 index 00000000000..1f98ad46e2b --- /dev/null +++ b/tests/test_defaults.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2020 +# Leandro Toledo de Souza +# +# 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 pytest + +from telegram.ext import Defaults +from telegram import User + + +class TestDefault(object): + def test_data_assignment(self, cdp): + defaults = Defaults() + + with pytest.raises(AttributeError): + defaults.parse_mode = True + with pytest.raises(AttributeError): + defaults.disable_notification = True + with pytest.raises(AttributeError): + defaults.disable_web_page_preview = True + with pytest.raises(AttributeError): + defaults.timeout = True + with pytest.raises(AttributeError): + defaults.quote = True + + def test_equality(self): + a = Defaults(parse_mode='HTML', quote=True) + b = Defaults(parse_mode='HTML', quote=True) + c = Defaults(parse_mode='HTML', quote=False) + d = Defaults(parse_mode='HTML', timeout=50) + e = User(123, 'test_user', False) + + 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) diff --git a/tests/test_document.py b/tests/test_document.py index 8c4bc324b68..c39b3dde627 100644 --- a/tests/test_document.py +++ b/tests/test_document.py @@ -22,6 +22,7 @@ from flaky import flaky from telegram import Document, PhotoSize, TelegramError, Voice +from telegram.utils.helpers import escape_markdown @pytest.fixture(scope='function') @@ -124,6 +125,39 @@ def test(_, url, data, **kwargs): assert message + @flaky(3, 1) + @pytest.mark.timeout(10) + @pytest.mark.parametrize('default_bot', [{'parse_mode': 'Markdown'}], indirect=True) + def test_send_document_default_parse_mode_1(self, default_bot, chat_id, document): + test_string = 'Italic Bold Code' + test_markdown_string = '_Italic_ *Bold* `Code`' + + message = default_bot.send_document(chat_id, document, caption=test_markdown_string) + assert message.caption_markdown == test_markdown_string + assert message.caption == test_string + + @flaky(3, 1) + @pytest.mark.timeout(10) + @pytest.mark.parametrize('default_bot', [{'parse_mode': 'Markdown'}], indirect=True) + def test_send_document_default_parse_mode_2(self, default_bot, chat_id, document): + test_markdown_string = '_Italic_ *Bold* `Code`' + + message = default_bot.send_document(chat_id, document, caption=test_markdown_string, + parse_mode=None) + assert message.caption == test_markdown_string + assert message.caption_markdown == escape_markdown(test_markdown_string) + + @flaky(3, 1) + @pytest.mark.timeout(10) + @pytest.mark.parametrize('default_bot', [{'parse_mode': 'Markdown'}], indirect=True) + def test_send_document_default_parse_mode_3(self, default_bot, chat_id, document): + test_markdown_string = '_Italic_ *Bold* `Code`' + + message = default_bot.send_document(chat_id, document, caption=test_markdown_string, + parse_mode='HTML') + assert message.caption == test_markdown_string + 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(), diff --git a/tests/test_inlinequery.py b/tests/test_inlinequery.py index 7edf4fd18cc..509b110c25d 100644 --- a/tests/test_inlinequery.py +++ b/tests/test_inlinequery.py @@ -63,9 +63,9 @@ def test_to_dict(self, inline_query): def test_answer(self, monkeypatch, inline_query): def test(*args, **kwargs): - return args[1] == inline_query.id + return args[0] == inline_query.id - monkeypatch.setattr('telegram.Bot.answer_inline_query', test) + monkeypatch.setattr(inline_query.bot, 'answer_inline_query', test) assert inline_query.answer() def test_equality(self): diff --git a/tests/test_inputmedia.py b/tests/test_inputmedia.py index 82338b102fe..bce3cf95c82 100644 --- a/tests/test_inputmedia.py +++ b/tests/test_inputmedia.py @@ -329,6 +329,13 @@ def test_send_media_group_new_files(self, bot, chat_id, video_file, photo_file, assert all([isinstance(mes, Message) for mes in messages]) assert all([mes.media_group_id == messages[0].media_group_id for mes in messages]) + @flaky(3, 1) + @pytest.mark.timeout(10) + @pytest.mark.parametrize('default_bot', [{'quote': True}], indirect=True) + def test_send_media_group_default_quote(self, default_bot, chat_id, media_group): + messages = default_bot.send_media_group(chat_id, media_group) + assert all([mes.default_quote is True for mes in messages]) + @flaky(3, 1) @pytest.mark.timeout(10) def test_edit_message_media(self, bot, chat_id, media_group): diff --git a/tests/test_message.py b/tests/test_message.py index e66d55ee267..2258d462572 100644 --- a/tests/test_message.py +++ b/tests/test_message.py @@ -94,7 +94,8 @@ def message(bot): {'text': 'a text message', 'reply_markup': {'inline_keyboard': [[{ 'text': 'start', 'url': 'http://google.com'}, { 'text': 'next', 'callback_data': 'abcd'}], - [{'text': 'Cancel', 'callback_data': 'Cancel'}]]}} + [{'text': 'Cancel', 'callback_data': 'Cancel'}]]}}, + {'quote': True} ], ids=['forwarded_user', 'forwarded_channel', 'reply', 'edited', 'text', 'caption_entities', 'audio', 'document', 'animation', 'game', 'photo', @@ -103,7 +104,8 @@ def message(bot): 'group_created', 'supergroup_created', 'channel_created', 'migrated_to', 'migrated_from', 'pinned', 'invoice', 'successful_payment', 'connected_website', 'forward_signature', 'author_signature', - 'photo_from_media_group', 'passport_data', 'poll', 'reply_markup']) + 'photo_from_media_group', 'passport_data', 'poll', 'reply_markup', + 'default_quote']) def message_params(bot, request): return Message(message_id=TestMessage.id, from_user=TestMessage.from_user, @@ -343,15 +345,15 @@ def test_effective_attachment(self, message_params): def test_reply_text(self, monkeypatch, message): def test(*args, **kwargs): - id = args[1] == message.chat_id - text = args[2] == 'test' + id = args[0] == message.chat_id + text = args[1] == 'test' if kwargs.get('reply_to_message_id'): reply = kwargs['reply_to_message_id'] == message.message_id else: reply = True return id and text and reply - monkeypatch.setattr('telegram.Bot.send_message', test) + monkeypatch.setattr(message.bot, 'send_message', test) assert message.reply_text('test') assert message.reply_text('test', quote=True) assert message.reply_text('test', reply_to_message_id=message.message_id, quote=True) @@ -362,8 +364,8 @@ def test_reply_markdown(self, monkeypatch, message): 'http://google.com') def test(*args, **kwargs): - cid = args[1] == message.chat_id - markdown_text = args[2] == test_md_string + cid = args[0] == message.chat_id + markdown_text = args[1] == test_md_string markdown_enabled = kwargs['parse_mode'] == ParseMode.MARKDOWN if kwargs.get('reply_to_message_id'): reply = kwargs['reply_to_message_id'] == message.message_id @@ -374,7 +376,7 @@ def test(*args, **kwargs): text_markdown = self.test_message.text_markdown assert text_markdown == test_md_string - monkeypatch.setattr('telegram.Bot.send_message', test) + monkeypatch.setattr(message.bot, 'send_message', test) assert message.reply_markdown(self.test_message.text_markdown) assert message.reply_markdown(self.test_message.text_markdown, quote=True) assert message.reply_markdown(self.test_message.text_markdown, @@ -388,8 +390,8 @@ def test_reply_html(self, monkeypatch, message): '
pre
. http://google.com') def test(*args, **kwargs): - cid = args[1] == message.chat_id - html_text = args[2] == test_html_string + cid = args[0] == message.chat_id + html_text = args[1] == test_html_string html_enabled = kwargs['parse_mode'] == ParseMode.HTML if kwargs.get('reply_to_message_id'): reply = kwargs['reply_to_message_id'] == message.message_id @@ -400,7 +402,7 @@ def test(*args, **kwargs): text_html = self.test_message.text_html assert text_html == test_html_string - monkeypatch.setattr('telegram.Bot.send_message', test) + 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, @@ -409,7 +411,7 @@ def test(*args, **kwargs): def test_reply_media_group(self, monkeypatch, message): def test(*args, **kwargs): - id = args[1] == message.chat_id + id = args[0] == message.chat_id media = kwargs['media'] == 'reply_media_group' if kwargs.get('reply_to_message_id'): reply = kwargs['reply_to_message_id'] == message.message_id @@ -417,13 +419,13 @@ def test(*args, **kwargs): reply = True return id and media and reply - monkeypatch.setattr('telegram.Bot.send_media_group', test) + monkeypatch.setattr(message.bot, 'send_media_group', test) assert message.reply_media_group(media='reply_media_group') assert message.reply_media_group(media='reply_media_group', quote=True) def test_reply_photo(self, monkeypatch, message): def test(*args, **kwargs): - id = args[1] == message.chat_id + id = args[0] == message.chat_id photo = kwargs['photo'] == 'test_photo' if kwargs.get('reply_to_message_id'): reply = kwargs['reply_to_message_id'] == message.message_id @@ -431,13 +433,13 @@ def test(*args, **kwargs): reply = True return id and photo and reply - monkeypatch.setattr('telegram.Bot.send_photo', test) + monkeypatch.setattr(message.bot, 'send_photo', test) assert message.reply_photo(photo='test_photo') assert message.reply_photo(photo='test_photo', quote=True) def test_reply_audio(self, monkeypatch, message): def test(*args, **kwargs): - id = args[1] == message.chat_id + id = args[0] == message.chat_id audio = kwargs['audio'] == 'test_audio' if kwargs.get('reply_to_message_id'): reply = kwargs['reply_to_message_id'] == message.message_id @@ -445,13 +447,13 @@ def test(*args, **kwargs): reply = True return id and audio and reply - monkeypatch.setattr('telegram.Bot.send_audio', test) + monkeypatch.setattr(message.bot, 'send_audio', test) assert message.reply_audio(audio='test_audio') assert message.reply_audio(audio='test_audio', quote=True) def test_reply_document(self, monkeypatch, message): def test(*args, **kwargs): - id = args[1] == message.chat_id + id = args[0] == message.chat_id document = kwargs['document'] == 'test_document' if kwargs.get('reply_to_message_id'): reply = kwargs['reply_to_message_id'] == message.message_id @@ -459,13 +461,13 @@ def test(*args, **kwargs): reply = True return id and document and reply - monkeypatch.setattr('telegram.Bot.send_document', test) + monkeypatch.setattr(message.bot, 'send_document', test) assert message.reply_document(document='test_document') assert message.reply_document(document='test_document', quote=True) def test_reply_animation(self, monkeypatch, message): def test(*args, **kwargs): - id = args[1] == message.chat_id + id = args[0] == message.chat_id animation = kwargs['animation'] == 'test_animation' if kwargs.get('reply_to_message_id'): reply = kwargs['reply_to_message_id'] == message.message_id @@ -473,13 +475,13 @@ def test(*args, **kwargs): reply = True return id and animation and reply - monkeypatch.setattr('telegram.Bot.send_animation', test) + monkeypatch.setattr(message.bot, 'send_animation', test) assert message.reply_animation(animation='test_animation') assert message.reply_animation(animation='test_animation', quote=True) def test_reply_sticker(self, monkeypatch, message): def test(*args, **kwargs): - id = args[1] == message.chat_id + id = args[0] == message.chat_id sticker = kwargs['sticker'] == 'test_sticker' if kwargs.get('reply_to_message_id'): reply = kwargs['reply_to_message_id'] == message.message_id @@ -487,13 +489,13 @@ def test(*args, **kwargs): reply = True return id and sticker and reply - monkeypatch.setattr('telegram.Bot.send_sticker', test) + monkeypatch.setattr(message.bot, 'send_sticker', test) assert message.reply_sticker(sticker='test_sticker') assert message.reply_sticker(sticker='test_sticker', quote=True) def test_reply_video(self, monkeypatch, message): def test(*args, **kwargs): - id = args[1] == message.chat_id + id = args[0] == message.chat_id video = kwargs['video'] == 'test_video' if kwargs.get('reply_to_message_id'): reply = kwargs['reply_to_message_id'] == message.message_id @@ -501,13 +503,13 @@ def test(*args, **kwargs): reply = True return id and video and reply - monkeypatch.setattr('telegram.Bot.send_video', test) + monkeypatch.setattr(message.bot, 'send_video', test) assert message.reply_video(video='test_video') assert message.reply_video(video='test_video', quote=True) def test_reply_video_note(self, monkeypatch, message): def test(*args, **kwargs): - id = args[1] == message.chat_id + id = args[0] == message.chat_id video_note = kwargs['video_note'] == 'test_video_note' if kwargs.get('reply_to_message_id'): reply = kwargs['reply_to_message_id'] == message.message_id @@ -515,13 +517,13 @@ def test(*args, **kwargs): reply = True return id and video_note and reply - monkeypatch.setattr('telegram.Bot.send_video_note', test) + monkeypatch.setattr(message.bot, 'send_video_note', test) assert message.reply_video_note(video_note='test_video_note') assert message.reply_video_note(video_note='test_video_note', quote=True) def test_reply_voice(self, monkeypatch, message): def test(*args, **kwargs): - id = args[1] == message.chat_id + id = args[0] == message.chat_id voice = kwargs['voice'] == 'test_voice' if kwargs.get('reply_to_message_id'): reply = kwargs['reply_to_message_id'] == message.message_id @@ -529,13 +531,13 @@ def test(*args, **kwargs): reply = True return id and voice and reply - monkeypatch.setattr('telegram.Bot.send_voice', test) + monkeypatch.setattr(message.bot, 'send_voice', test) assert message.reply_voice(voice='test_voice') assert message.reply_voice(voice='test_voice', quote=True) def test_reply_location(self, monkeypatch, message): def test(*args, **kwargs): - id = args[1] == message.chat_id + id = args[0] == message.chat_id location = kwargs['location'] == 'test_location' if kwargs.get('reply_to_message_id'): reply = kwargs['reply_to_message_id'] == message.message_id @@ -543,13 +545,13 @@ def test(*args, **kwargs): reply = True return id and location and reply - monkeypatch.setattr('telegram.Bot.send_location', test) + monkeypatch.setattr(message.bot, 'send_location', test) assert message.reply_location(location='test_location') assert message.reply_location(location='test_location', quote=True) def test_reply_venue(self, monkeypatch, message): def test(*args, **kwargs): - id = args[1] == message.chat_id + id = args[0] == message.chat_id venue = kwargs['venue'] == 'test_venue' if kwargs.get('reply_to_message_id'): reply = kwargs['reply_to_message_id'] == message.message_id @@ -557,13 +559,13 @@ def test(*args, **kwargs): reply = True return id and venue and reply - monkeypatch.setattr('telegram.Bot.send_venue', test) + monkeypatch.setattr(message.bot, 'send_venue', test) assert message.reply_venue(venue='test_venue') assert message.reply_venue(venue='test_venue', quote=True) def test_reply_contact(self, monkeypatch, message): def test(*args, **kwargs): - id = args[1] == message.chat_id + id = args[0] == message.chat_id contact = kwargs['contact'] == 'test_contact' if kwargs.get('reply_to_message_id'): reply = kwargs['reply_to_message_id'] == message.message_id @@ -571,13 +573,13 @@ def test(*args, **kwargs): reply = True return id and contact and reply - monkeypatch.setattr('telegram.Bot.send_contact', test) + monkeypatch.setattr(message.bot, 'send_contact', test) assert message.reply_contact(contact='test_contact') assert message.reply_contact(contact='test_contact', quote=True) def test_reply_poll(self, monkeypatch, message): def test(*args, **kwargs): - id = args[1] == message.chat_id + id = args[0] == message.chat_id contact = kwargs['contact'] == 'test_poll' if kwargs.get('reply_to_message_id'): reply = kwargs['reply_to_message_id'] == message.message_id @@ -585,7 +587,7 @@ def test(*args, **kwargs): reply = True return id and contact and reply - monkeypatch.setattr('telegram.Bot.send_poll', test) + monkeypatch.setattr(message.bot, 'send_poll', test) assert message.reply_poll(contact='test_poll') assert message.reply_poll(contact='test_poll', quote=True) @@ -600,7 +602,7 @@ def test(*args, **kwargs): notification = True return chat_id and from_chat and message_id and notification - monkeypatch.setattr('telegram.Bot.forward_message', test) + monkeypatch.setattr(message.bot, 'forward_message', test) assert message.forward(123456) assert message.forward(123456, disable_notification=True) assert not message.forward(635241) @@ -612,7 +614,7 @@ def test(*args, **kwargs): text = kwargs['text'] == 'test' return chat_id and message_id and text - monkeypatch.setattr('telegram.Bot.edit_message_text', test) + monkeypatch.setattr(message.bot, 'edit_message_text', test) assert message.edit_text(text='test') def test_edit_caption(self, monkeypatch, message): @@ -622,7 +624,7 @@ def test(*args, **kwargs): caption = kwargs['caption'] == 'new caption' return chat_id and message_id and caption - monkeypatch.setattr('telegram.Bot.edit_message_caption', test) + monkeypatch.setattr(message.bot, 'edit_message_caption', test) assert message.edit_caption(caption='new caption') def test_edit_media(self, monkeypatch, message): @@ -632,7 +634,7 @@ def test(*args, **kwargs): media = kwargs['media'] == 'my_media' return chat_id and message_id and media - monkeypatch.setattr('telegram.Bot.edit_message_media', test) + monkeypatch.setattr(message.bot, 'edit_message_media', test) assert message.edit_media('my_media') def test_edit_reply_markup(self, monkeypatch, message): @@ -642,7 +644,7 @@ def test(*args, **kwargs): reply_markup = kwargs['reply_markup'] == [['1', '2']] return chat_id and message_id and reply_markup - monkeypatch.setattr('telegram.Bot.edit_message_reply_markup', test) + monkeypatch.setattr(message.bot, 'edit_message_reply_markup', test) assert message.edit_reply_markup(reply_markup=[['1', '2']]) def test_delete(self, monkeypatch, message): @@ -651,9 +653,30 @@ def test(*args, **kwargs): message_id = kwargs['message_id'] == message.message_id return chat_id and message_id - monkeypatch.setattr('telegram.Bot.delete_message', test) + monkeypatch.setattr(message.bot, 'delete_message', test) assert message.delete() + def test_default_quote(self, message): + kwargs = {} + + message.default_quote = False + message._quote(kwargs) + assert 'reply_to_message_id' not in kwargs + + message.default_quote = True + message._quote(kwargs) + assert 'reply_to_message_id' in kwargs + + kwargs = {} + message.default_quote = None + message.chat.type = Chat.PRIVATE + message._quote(kwargs) + assert 'reply_to_message_id' not in kwargs + + message.chat.type = Chat.GROUP + message._quote(kwargs) + assert 'reply_to_message_id' in kwargs + def test_equality(self): id = 1 a = Message(id, self.from_user, self.date, self.chat) diff --git a/tests/test_passport.py b/tests/test_passport.py index 0b682bcb652..ccb58852530 100644 --- a/tests/test_passport.py +++ b/tests/test_passport.py @@ -296,9 +296,9 @@ def test_mocked_download_passport_file(self, passport_data, monkeypatch): selfie = passport_data.decrypted_data[1].selfie def get_file(*args, **kwargs): - return File(args[1]) + return File(args[0]) - monkeypatch.setattr('telegram.Bot.get_file', get_file) + monkeypatch.setattr(passport_data.bot, 'get_file', get_file) file = selfie.get_file() assert file.file_id == selfie.file_id assert file._credentials.file_hash == self.driver_license_selfie_credentials_file_hash diff --git a/tests/test_photo.py b/tests/test_photo.py index 1bf7daa400f..bb9dae2008d 100644 --- a/tests/test_photo.py +++ b/tests/test_photo.py @@ -23,6 +23,7 @@ from flaky import flaky from telegram import Sticker, TelegramError, PhotoSize, InputFile +from telegram.utils.helpers import escape_markdown @pytest.fixture(scope='function') @@ -139,6 +140,39 @@ def test_send_photo_parse_mode_html(self, bot, chat_id, photo_file, thumb, photo assert message.caption == TestPhoto.caption.replace('', '').replace('', '') assert len(message.caption_entities) == 1 + @flaky(3, 1) + @pytest.mark.timeout(10) + @pytest.mark.parametrize('default_bot', [{'parse_mode': 'Markdown'}], indirect=True) + def test_send_photo_default_parse_mode_1(self, default_bot, chat_id, photo_file, thumb, photo): + test_string = 'Italic Bold Code' + test_markdown_string = '_Italic_ *Bold* `Code`' + + message = default_bot.send_photo(chat_id, photo_file, caption=test_markdown_string) + assert message.caption_markdown == test_markdown_string + assert message.caption == test_string + + @flaky(3, 1) + @pytest.mark.timeout(10) + @pytest.mark.parametrize('default_bot', [{'parse_mode': 'Markdown'}], indirect=True) + def test_send_photo_default_parse_mode_2(self, default_bot, chat_id, photo_file, thumb, photo): + test_markdown_string = '_Italic_ *Bold* `Code`' + + message = default_bot.send_photo(chat_id, photo_file, caption=test_markdown_string, + parse_mode=None) + assert message.caption == test_markdown_string + assert message.caption_markdown == escape_markdown(test_markdown_string) + + @flaky(3, 1) + @pytest.mark.timeout(10) + @pytest.mark.parametrize('default_bot', [{'parse_mode': 'Markdown'}], indirect=True) + def test_send_photo_default_parse_mode_3(self, default_bot, chat_id, photo_file, thumb, photo): + test_markdown_string = '_Italic_ *Bold* `Code`' + + message = default_bot.send_photo(chat_id, photo_file, caption=test_markdown_string, + parse_mode='HTML') + assert message.caption == test_markdown_string + assert message.caption_markdown == escape_markdown(test_markdown_string) + @flaky(3, 1) @pytest.mark.timeout(10) @pytest.mark.skip(reason='Doesnt work without API 4.5') diff --git a/tests/test_precheckoutquery.py b/tests/test_precheckoutquery.py index cb261d1c9e3..3618e985d76 100644 --- a/tests/test_precheckoutquery.py +++ b/tests/test_precheckoutquery.py @@ -77,9 +77,9 @@ def test_to_dict(self, pre_checkout_query): def test_answer(self, monkeypatch, pre_checkout_query): def test(*args, **kwargs): - return args[1] == pre_checkout_query.id + return args[0] == pre_checkout_query.id - monkeypatch.setattr('telegram.Bot.answer_pre_checkout_query', test) + monkeypatch.setattr(pre_checkout_query.bot, 'answer_pre_checkout_query', test) assert pre_checkout_query.answer() def test_equality(self): diff --git a/tests/test_shippingquery.py b/tests/test_shippingquery.py index e5eb1976fd1..2cfd69f42eb 100644 --- a/tests/test_shippingquery.py +++ b/tests/test_shippingquery.py @@ -63,9 +63,9 @@ def test_to_dict(self, shipping_query): def test_answer(self, monkeypatch, shipping_query): def test(*args, **kwargs): - return args[1] == shipping_query.id + return args[0] == shipping_query.id - monkeypatch.setattr('telegram.Bot.answer_shipping_query', test) + monkeypatch.setattr(shipping_query.bot, 'answer_shipping_query', test) assert shipping_query.answer() def test_equality(self): diff --git a/tests/test_update.py b/tests/test_update.py index ce74e85fe41..fd2cec51c60 100644 --- a/tests/test_update.py +++ b/tests/test_update.py @@ -75,6 +75,14 @@ def test_update_de_json_empty(self, bot): assert update is None + def test_de_json_default_quote(self, bot): + json_dict = {'update_id': TestUpdate.update_id} + json_dict['message'] = message.to_dict() + json_dict['default_quote'] = True + update = Update.de_json(json_dict, bot) + + assert update.message.default_quote is True + def test_to_dict(self, update): update_dict = update.to_dict() diff --git a/tests/test_updater.py b/tests/test_updater.py index 424de3c8c31..2b300988565 100644 --- a/tests/test_updater.py +++ b/tests/test_updater.py @@ -79,8 +79,8 @@ def test_get_updates_normal_err(self, monkeypatch, updater, error): def test(*args, **kwargs): raise error - monkeypatch.setattr('telegram.Bot.get_updates', test) - monkeypatch.setattr('telegram.Bot.set_webhook', lambda *args, **kwargs: True) + monkeypatch.setattr(updater.bot, 'get_updates', test) + monkeypatch.setattr(updater.bot, 'set_webhook', lambda *args, **kwargs: True) updater.dispatcher.add_error_handler(self.error_handler) updater.start_polling(0.01) @@ -99,8 +99,8 @@ def test(*args, **kwargs): raise error with caplog.at_level(logging.DEBUG): - monkeypatch.setattr('telegram.Bot.get_updates', test) - monkeypatch.setattr('telegram.Bot.set_webhook', lambda *args, **kwargs: True) + monkeypatch.setattr(updater.bot, 'get_updates', test) + monkeypatch.setattr(updater.bot, 'set_webhook', lambda *args, **kwargs: True) updater.dispatcher.add_error_handler(self.error_handler) updater.start_polling(0.01) assert self.err_handler_called.wait(1) is not True @@ -127,8 +127,8 @@ def test(*args, **kwargs): event.set() raise error - monkeypatch.setattr('telegram.Bot.get_updates', test) - monkeypatch.setattr('telegram.Bot.set_webhook', lambda *args, **kwargs: True) + monkeypatch.setattr(updater.bot, 'get_updates', test) + monkeypatch.setattr(updater.bot, 'set_webhook', lambda *args, **kwargs: True) updater.dispatcher.add_error_handler(self.error_handler) updater.start_polling(0.01) @@ -144,8 +144,8 @@ def test(*args, **kwargs): def test_webhook(self, monkeypatch, updater): q = Queue() - monkeypatch.setattr('telegram.Bot.set_webhook', lambda *args, **kwargs: True) - monkeypatch.setattr('telegram.Bot.delete_webhook', lambda *args, **kwargs: True) + monkeypatch.setattr(updater.bot, 'set_webhook', lambda *args, **kwargs: True) + monkeypatch.setattr(updater.bot, 'delete_webhook', lambda *args, **kwargs: True) monkeypatch.setattr('telegram.ext.Dispatcher.process_update', lambda _, u: q.put(u)) ip = '127.0.0.1' @@ -182,8 +182,8 @@ def test_webhook(self, monkeypatch, updater): updater.stop() def test_webhook_ssl(self, monkeypatch, updater): - monkeypatch.setattr('telegram.Bot.set_webhook', lambda *args, **kwargs: True) - monkeypatch.setattr('telegram.Bot.delete_webhook', lambda *args, **kwargs: True) + monkeypatch.setattr(updater.bot, 'set_webhook', lambda *args, **kwargs: True) + monkeypatch.setattr(updater.bot, 'delete_webhook', lambda *args, **kwargs: True) ip = '127.0.0.1' port = randrange(1024, 49152) # Select random port for travis tg_err = False @@ -204,8 +204,8 @@ def test_webhook_ssl(self, monkeypatch, updater): def test_webhook_no_ssl(self, monkeypatch, updater): q = Queue() - monkeypatch.setattr('telegram.Bot.set_webhook', lambda *args, **kwargs: True) - monkeypatch.setattr('telegram.Bot.delete_webhook', lambda *args, **kwargs: True) + monkeypatch.setattr(updater.bot, 'set_webhook', lambda *args, **kwargs: True) + monkeypatch.setattr(updater.bot, 'delete_webhook', lambda *args, **kwargs: True) monkeypatch.setattr('telegram.ext.Dispatcher.process_update', lambda _, u: q.put(u)) ip = '127.0.0.1' @@ -221,18 +221,42 @@ def test_webhook_no_ssl(self, monkeypatch, updater): assert q.get(False) == update updater.stop() + def test_webhook_default_quote(self, monkeypatch, updater): + updater._default_quote = True + q = Queue() + monkeypatch.setattr(updater.bot, 'set_webhook', lambda *args, **kwargs: True) + monkeypatch.setattr(updater.bot, 'delete_webhook', lambda *args, **kwargs: True) + monkeypatch.setattr('telegram.ext.Dispatcher.process_update', lambda _, u: q.put(u)) + + ip = '127.0.0.1' + port = randrange(1024, 49152) # Select random port for travis + updater.start_webhook( + ip, + port, + url_path='TOKEN') + sleep(.2) + + # Now, we send an update to the server via urlopen + update = Update(1, message=Message(1, User(1, '', False), None, Chat(1, ''), + text='Webhook')) + self._send_webhook_msg(ip, port, update.to_json(), 'TOKEN') + sleep(.2) + # assert q.get(False) == update + assert q.get(False).message.default_quote is True + updater.stop() + @pytest.mark.parametrize(('error',), argvalues=[(TelegramError(''),)], ids=('TelegramError',)) def test_bootstrap_retries_success(self, monkeypatch, updater, error): retries = 2 - def attempt(_, *args, **kwargs): + def attempt(*args, **kwargs): if self.attempts < retries: self.attempts += 1 raise error - monkeypatch.setattr('telegram.Bot.set_webhook', attempt) + monkeypatch.setattr(updater.bot, 'set_webhook', attempt) updater.running = True updater._bootstrap(retries, False, 'path', None, bootstrap_interval=0) @@ -246,11 +270,11 @@ def attempt(_, *args, **kwargs): def test_bootstrap_retries_error(self, monkeypatch, updater, error, attempts): retries = 1 - def attempt(_, *args, **kwargs): + def attempt(*args, **kwargs): self.attempts += 1 raise error - monkeypatch.setattr('telegram.Bot.set_webhook', attempt) + monkeypatch.setattr(updater.bot, 'set_webhook', attempt) updater.running = True with pytest.raises(type(error)): diff --git a/tests/test_user.py b/tests/test_user.py index 64db43c1394..aa308cb5839 100644 --- a/tests/test_user.py +++ b/tests/test_user.py @@ -103,73 +103,73 @@ def test_link(self, user): assert user.link is None def test_get_profile_photos(self, monkeypatch, user): - def test(_, *args, **kwargs): + def test(*args, **kwargs): return args[0] == user.id - monkeypatch.setattr('telegram.Bot.get_user_profile_photos', test) + monkeypatch.setattr(user.bot, 'get_user_profile_photos', test) assert user.get_profile_photos() def test_instance_method_send_message(self, monkeypatch, user): def test(*args, **kwargs): - return args[1] == user.id and args[2] == 'test' + return args[0] == user.id and args[1] == 'test' - monkeypatch.setattr('telegram.Bot.send_message', test) + monkeypatch.setattr(user.bot, 'send_message', test) assert user.send_message('test') def test_instance_method_send_photo(self, monkeypatch, user): def test(*args, **kwargs): - return args[1] == user.id and args[2] == 'test_photo' + return args[0] == user.id and args[1] == 'test_photo' - monkeypatch.setattr('telegram.Bot.send_photo', test) + monkeypatch.setattr(user.bot, 'send_photo', test) assert user.send_photo('test_photo') def test_instance_method_send_audio(self, monkeypatch, user): def test(*args, **kwargs): - return args[1] == user.id and args[2] == 'test_audio' + return args[0] == user.id and args[1] == 'test_audio' - monkeypatch.setattr('telegram.Bot.send_audio', test) + monkeypatch.setattr(user.bot, 'send_audio', test) assert user.send_audio('test_audio') def test_instance_method_send_document(self, monkeypatch, user): def test(*args, **kwargs): - return args[1] == user.id and args[2] == 'test_document' + return args[0] == user.id and args[1] == 'test_document' - monkeypatch.setattr('telegram.Bot.send_document', test) + monkeypatch.setattr(user.bot, 'send_document', test) assert user.send_document('test_document') def test_instance_method_send_sticker(self, monkeypatch, user): def test(*args, **kwargs): - return args[1] == user.id and args[2] == 'test_sticker' + return args[0] == user.id and args[1] == 'test_sticker' - monkeypatch.setattr('telegram.Bot.send_sticker', test) + monkeypatch.setattr(user.bot, 'send_sticker', test) assert user.send_sticker('test_sticker') def test_instance_method_send_video(self, monkeypatch, user): def test(*args, **kwargs): - return args[1] == user.id and args[2] == 'test_video' + return args[0] == user.id and args[1] == 'test_video' - monkeypatch.setattr('telegram.Bot.send_video', test) + monkeypatch.setattr(user.bot, 'send_video', test) assert user.send_video('test_video') def test_instance_method_send_video_note(self, monkeypatch, user): def test(*args, **kwargs): - return args[1] == user.id and args[2] == 'test_video_note' + return args[0] == user.id and args[1] == 'test_video_note' - monkeypatch.setattr('telegram.Bot.send_video_note', test) + monkeypatch.setattr(user.bot, 'send_video_note', test) assert user.send_video_note('test_video_note') def test_instance_method_send_voice(self, monkeypatch, user): def test(*args, **kwargs): - return args[1] == user.id and args[2] == 'test_voice' + return args[0] == user.id and args[1] == 'test_voice' - monkeypatch.setattr('telegram.Bot.send_voice', test) + monkeypatch.setattr(user.bot, 'send_voice', test) assert user.send_voice('test_voice') def test_instance_method_send_animation(self, monkeypatch, user): def test(*args, **kwargs): - return args[1] == user.id and args[2] == 'test_animation' + return args[0] == user.id and args[1] == 'test_animation' - monkeypatch.setattr('telegram.Bot.send_animation', test) + monkeypatch.setattr(user.bot, 'send_animation', test) assert user.send_animation('test_animation') def test_mention_html(self, user): diff --git a/tests/test_video.py b/tests/test_video.py index a29726f2fb3..d144aacd678 100644 --- a/tests/test_video.py +++ b/tests/test_video.py @@ -22,6 +22,7 @@ from flaky import flaky from telegram import Video, TelegramError, Voice, PhotoSize +from telegram.utils.helpers import escape_markdown @pytest.fixture(scope='function') @@ -142,6 +143,39 @@ def test(_, url, data, **kwargs): message = bot.send_video(chat_id, video=video) assert message + @flaky(3, 1) + @pytest.mark.timeout(10) + @pytest.mark.parametrize('default_bot', [{'parse_mode': 'Markdown'}], indirect=True) + def test_send_video_default_parse_mode_1(self, default_bot, chat_id, video): + test_string = 'Italic Bold Code' + test_markdown_string = '_Italic_ *Bold* `Code`' + + message = default_bot.send_video(chat_id, video, caption=test_markdown_string) + assert message.caption_markdown == test_markdown_string + assert message.caption == test_string + + @flaky(3, 1) + @pytest.mark.timeout(10) + @pytest.mark.parametrize('default_bot', [{'parse_mode': 'Markdown'}], indirect=True) + def test_send_video_default_parse_mode_2(self, default_bot, chat_id, video): + test_markdown_string = '_Italic_ *Bold* `Code`' + + message = default_bot.send_video(chat_id, video, caption=test_markdown_string, + parse_mode=None) + assert message.caption == test_markdown_string + assert message.caption_markdown == escape_markdown(test_markdown_string) + + @flaky(3, 1) + @pytest.mark.timeout(10) + @pytest.mark.parametrize('default_bot', [{'parse_mode': 'Markdown'}], indirect=True) + def test_send_video_default_parse_mode_3(self, default_bot, chat_id, video): + test_markdown_string = '_Italic_ *Bold* `Code`' + + message = default_bot.send_video(chat_id, video, caption=test_markdown_string, + parse_mode='HTML') + assert message.caption == test_markdown_string + assert message.caption_markdown == escape_markdown(test_markdown_string) + def test_de_json(self, bot): json_dict = { 'file_id': 'not a file id', diff --git a/tests/test_voice.py b/tests/test_voice.py index 27412aeef80..33856120450 100644 --- a/tests/test_voice.py +++ b/tests/test_voice.py @@ -22,6 +22,7 @@ from flaky import flaky from telegram import Audio, Voice, TelegramError +from telegram.utils.helpers import escape_markdown @pytest.fixture(scope='function') @@ -112,6 +113,39 @@ def test(_, url, data, **kwargs): message = bot.send_voice(chat_id, voice=voice) assert message + @flaky(3, 1) + @pytest.mark.timeout(10) + @pytest.mark.parametrize('default_bot', [{'parse_mode': 'Markdown'}], indirect=True) + def test_send_voice_default_parse_mode_1(self, default_bot, chat_id, voice): + test_string = 'Italic Bold Code' + test_markdown_string = '_Italic_ *Bold* `Code`' + + message = default_bot.send_voice(chat_id, voice, caption=test_markdown_string) + assert message.caption_markdown == test_markdown_string + assert message.caption == test_string + + @flaky(3, 1) + @pytest.mark.timeout(10) + @pytest.mark.parametrize('default_bot', [{'parse_mode': 'Markdown'}], indirect=True) + def test_send_voice_default_parse_mode_2(self, default_bot, chat_id, voice): + test_markdown_string = '_Italic_ *Bold* `Code`' + + message = default_bot.send_voice(chat_id, voice, caption=test_markdown_string, + parse_mode=None) + assert message.caption == test_markdown_string + assert message.caption_markdown == escape_markdown(test_markdown_string) + + @flaky(3, 1) + @pytest.mark.timeout(10) + @pytest.mark.parametrize('default_bot', [{'parse_mode': 'Markdown'}], indirect=True) + def test_send_voice_default_parse_mode_3(self, default_bot, chat_id, voice): + test_markdown_string = '_Italic_ *Bold* `Code`' + + message = default_bot.send_voice(chat_id, voice, caption=test_markdown_string, + parse_mode='HTML') + assert message.caption == test_markdown_string + assert message.caption_markdown == escape_markdown(test_markdown_string) + def test_de_json(self, bot): json_dict = { 'file_id': 'not a file id',