Add default values (#1490)

* added parse_mode parameter in Updater and in Bot class to set a default parse mode for bot

* DefaultValue

* Add default parse_mode everywhere

* Renome to default_parse_mode

* Test def parse_mode for send_*, edit_message_*

* Remove duplicate code in Input* classes

* Improve handling of def_p_m for Bot methods

* Remove unneeded deletion of kwargs

* Make @log preserve signature, add bots with defaults to tests

* Fix Codacy

* Fix Travis Error

* Add default_disable_notification

* Add default_disable_web_page_preview

* Add default_disable_timeout

* add default_disable_web_page_preview for InputTextMessageContent

* add default_quote

* Try fixing test_pin_and_unpin

* Just run 3.5 tests for paranoia

* add tests for Defaults

* Revert "Just run 3.5 tests for paranoia"

This reverts commit 1baa91a3a1.

* Tidy up parameters, move Defaults to ext

* Stage new files, because im an idiot

* remove debug prints

* change equality checks for DEFAULT_NONE

* Some last changes

* fix S&R error so that tests actually run

Co-authored-by: Ak4zh <agwl.akash@gmail.com>
Co-authored-by: Eldinnie <Eldinnie@users.noreply.github.com>
This commit is contained in:
Bibo-Joshi 2020-02-06 11:22:56 +01:00 committed by GitHub
parent bacabbe767
commit 960c7a0c70
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
51 changed files with 1015 additions and 195 deletions

View file

@ -0,0 +1,6 @@
telegram.ext.Defaults
=====================
.. autoclass:: telegram.ext.Defaults
:members:
:show-inheritance:

View file

@ -12,6 +12,7 @@ telegram.ext package
telegram.ext.messagequeue
telegram.ext.delayqueue
telegram.ext.callbackcontext
telegram.ext.defaults
Handlers
--------

View file

@ -2,3 +2,4 @@ future>=0.16.0
certifi
tornado>=5.1
cryptography
decorator>=4.4.0

View file

@ -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 @@ from telegram import (User, Message, Update, Chat, ChatMember, UserProfilePhotos
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 info(func):
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 @@ class Bot(TelegramObject):
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 @@ class Bot(TelegramObject):
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 @@ class Bot(TelegramObject):
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 @@ class Bot(TelegramObject):
"""
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 @@ class Bot(TelegramObject):
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 @@ class Bot(TelegramObject):
result = self._request.post(url, data, timeout=timeout)
if self.defaults:
result['default_quote'] = self.defaults.quote
return Chat.de_json(result, self)
@log

View file

@ -101,7 +101,10 @@ class CallbackQuery(TelegramObject):
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)

View file

@ -135,7 +135,10 @@ class Chat(TelegramObject):
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)

View file

@ -41,6 +41,7 @@ from .precheckoutqueryhandler import PreCheckoutQueryHandler
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 @@ __all__ = ('Dispatcher', 'JobQueue', 'Job', 'Updater', 'CallbackQueryHandler',
'StringRegexHandler', 'TypeHandler', 'ConversationHandler',
'PreCheckoutQueryHandler', 'ShippingQueryHandler', 'MessageQueue', 'DelayQueue',
'DispatcherHandlerStop', 'run_async', 'CallbackContext', 'BasePersistence',
'PicklePersistence', 'DictPersistence', 'PrefixHandler')
'PicklePersistence', 'DictPersistence', 'PrefixHandler', 'Defaults')

127
telegram/ext/defaults.py Normal file
View file

@ -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 <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser Public License for more details.
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""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

View file

@ -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 @@ class Updater(object):
user_sig_handler=None,
request_kwargs=None,
persistence=None,
defaults=None,
use_context=False,
dispatcher=None,
base_file_url=None):
@ -157,7 +160,8 @@ class Updater(object):
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 @@ class Updater(object):
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 @@ class Updater(object):
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

View file

@ -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 @@ class InputMediaAnimation(InputMedia):
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 @@ class InputMediaPhoto(InputMedia):
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 @@ class InputMediaVideo(InputMedia):
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 @@ class InputMediaAudio(InputMedia):
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 @@ class InputMediaDocument(InputMedia):
if caption:
self.caption = caption
if parse_mode:
self.parse_mode = parse_mode
self.parse_mode = parse_mode

View file

@ -41,3 +41,11 @@ class InlineQueryResult(TelegramObject):
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')

View file

@ -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 @@ class InlineQueryResultAudio(InlineQueryResult):
caption=None,
reply_markup=None,
input_message_content=None,
parse_mode=None,
parse_mode=DEFAULT_NONE,
**kwargs):
# Required

View file

@ -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 @@ class InlineQueryResultCachedAudio(InlineQueryResult):
caption=None,
reply_markup=None,
input_message_content=None,
parse_mode=None,
parse_mode=DEFAULT_NONE,
**kwargs):
# Required
super(InlineQueryResultCachedAudio, self).__init__('audio', id)

View file

@ -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 @@ class InlineQueryResultCachedDocument(InlineQueryResult):
caption=None,
reply_markup=None,
input_message_content=None,
parse_mode=None,
parse_mode=DEFAULT_NONE,
**kwargs):
# Required
super(InlineQueryResultCachedDocument, self).__init__('document', id)

View file

@ -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 @@ class InlineQueryResultCachedGif(InlineQueryResult):
caption=None,
reply_markup=None,
input_message_content=None,
parse_mode=None,
parse_mode=DEFAULT_NONE,
**kwargs):
# Required
super(InlineQueryResultCachedGif, self).__init__('gif', id)

View file

@ -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 @@ class InlineQueryResultCachedMpeg4Gif(InlineQueryResult):
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)

View file

@ -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 @@ class InlineQueryResultCachedPhoto(InlineQueryResult):
caption=None,
reply_markup=None,
input_message_content=None,
parse_mode=None,
parse_mode=DEFAULT_NONE,
**kwargs):
# Required
super(InlineQueryResultCachedPhoto, self).__init__('photo', id)

View file

@ -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 @@ class InlineQueryResultCachedVideo(InlineQueryResult):
caption=None,
reply_markup=None,
input_message_content=None,
parse_mode=None,
parse_mode=DEFAULT_NONE,
**kwargs):
# Required
super(InlineQueryResultCachedVideo, self).__init__('video', id)

View file

@ -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 @@ class InlineQueryResultCachedVoice(InlineQueryResult):
caption=None,
reply_markup=None,
input_message_content=None,
parse_mode=None,
parse_mode=DEFAULT_NONE,
**kwargs):
# Required
super(InlineQueryResultCachedVoice, self).__init__('voice', id)

View file

@ -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 @@ class InlineQueryResultDocument(InlineQueryResult):
thumb_url=None,
thumb_width=None,
thumb_height=None,
parse_mode=None,
parse_mode=DEFAULT_NONE,
**kwargs):
# Required
super(InlineQueryResultDocument, self).__init__('document', id)

View file

@ -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 @@ class InlineQueryResultGif(InlineQueryResult):
reply_markup=None,
input_message_content=None,
gif_duration=None,
parse_mode=None,
parse_mode=DEFAULT_NONE,
**kwargs):
# Required

View file

@ -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 @@ class InlineQueryResultMpeg4Gif(InlineQueryResult):
reply_markup=None,
input_message_content=None,
mpeg4_duration=None,
parse_mode=None,
parse_mode=DEFAULT_NONE,
**kwargs):
# Required

View file

@ -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 @@ class InlineQueryResultPhoto(InlineQueryResult):
caption=None,
reply_markup=None,
input_message_content=None,
parse_mode=None,
parse_mode=DEFAULT_NONE,
**kwargs):
# Required
super(InlineQueryResultPhoto, self).__init__('photo', id)

View file

@ -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 @@ class InlineQueryResultVideo(InlineQueryResult):
description=None,
reply_markup=None,
input_message_content=None,
parse_mode=None,
parse_mode=DEFAULT_NONE,
**kwargs):
# Required

View file

@ -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 @@ class InlineQueryResultVoice(InlineQueryResult):
caption=None,
reply_markup=None,
input_message_content=None,
parse_mode=None,
parse_mode=DEFAULT_NONE,
**kwargs):
# Required

View file

@ -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')

View file

@ -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

View file

@ -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 @@ class Message(TelegramObject):
forward_sender_name=None,
reply_markup=None,
bot=None,
default_quote=None,
**kwargs):
# Required
self.message_id = int(message_id)
@ -328,6 +333,7 @@ class Message(TelegramObject):
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 @@ class Message(TelegramObject):
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 @@ class Message(TelegramObject):
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 @@ class Message(TelegramObject):
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):

View file

@ -211,16 +211,31 @@ class Update(TelegramObject):
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)

View file

@ -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`"""

View file

@ -71,8 +71,9 @@ class WebhookServer(object):
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 @@ class WebhookHandler(tornado.web.RequestHandler):
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 @@ class WebhookHandler(tornado.web.RequestHandler):
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)

View file

@ -30,7 +30,7 @@ import pytest
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})?')

View file

@ -22,6 +22,7 @@ import pytest
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 @@ class TestAnimation(object):
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')

View file

@ -22,6 +22,7 @@ import pytest
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 @@ class TestAudio(object):
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,

View file

@ -30,7 +30,7 @@ from telegram import (Bot, Update, ChatAction, TelegramError, User, InlineKeyboa
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 @@ class TestBot(object):
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 @@ class TestBot(object):
# 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 @@ class TestBot(object):
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 @@ class TestBot(object):
# 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

View file

@ -53,13 +53,15 @@ class TestCallbackQuery(object):
'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 @@ class TestCallbackQuery(object):
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 @@ class TestCallbackQuery(object):
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 @@ class TestCallbackQuery(object):
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 @@ class TestCallbackQuery(object):
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']])

View file

@ -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 @@ class TestChat(object):
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 @@ class TestChat(object):
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):

59
tests/test_defaults.py Normal file
View file

@ -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 <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser Public License for more details.
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
import 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)

View file

@ -22,6 +22,7 @@ import pytest
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 @@ class TestDocument(object):
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(),

View file

@ -63,9 +63,9 @@ class TestInlineQuery(object):
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):

View file

@ -329,6 +329,13 @@ class TestSendMediaGroup(object):
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):

View file

@ -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 @@ class TestMessage(object):
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 @@ class TestMessage(object):
'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 @@ class TestMessage(object):
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 @@ class TestMessage(object):
'<pre>pre</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 @@ class TestMessage(object):
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 @@ class TestMessage(object):
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 @@ class TestMessage(object):
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 @@ class TestMessage(object):
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 @@ class TestMessage(object):
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 @@ class TestMessage(object):
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 @@ class TestMessage(object):
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 @@ class TestMessage(object):
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 @@ class TestMessage(object):
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 @@ class TestMessage(object):
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 @@ class TestMessage(object):
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 @@ class TestMessage(object):
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 @@ class TestMessage(object):
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 @@ class TestMessage(object):
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 @@ class TestMessage(object):
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 @@ class TestMessage(object):
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 @@ class TestMessage(object):
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 @@ class TestMessage(object):
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 @@ class TestMessage(object):
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 @@ class TestMessage(object):
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 @@ class TestMessage(object):
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)

View file

@ -296,9 +296,9 @@ class TestPassport(object):
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

View file

@ -23,6 +23,7 @@ import pytest
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 @@ class TestPhoto(object):
assert message.caption == TestPhoto.caption.replace('<b>', '').replace('</b>', '')
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')

View file

@ -77,9 +77,9 @@ class TestPreCheckoutQuery(object):
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):

View file

@ -63,9 +63,9 @@ class TestShippingQuery(object):
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):

View file

@ -75,6 +75,14 @@ class TestUpdate(object):
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()

View file

@ -79,8 +79,8 @@ class TestUpdater(object):
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 @@ class TestUpdater(object):
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 @@ class TestUpdater(object):
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 @@ class TestUpdater(object):
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 @@ class TestUpdater(object):
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 @@ class TestUpdater(object):
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 @@ class TestUpdater(object):
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 @@ class TestUpdater(object):
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)):

View file

@ -103,73 +103,73 @@ class TestUser(object):
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):

View file

@ -22,6 +22,7 @@ import pytest
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 @@ class TestVideo(object):
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',

View file

@ -22,6 +22,7 @@ import pytest
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 @@ class TestVoice(object):
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',