mirror of
https://github.com/python-telegram-bot/python-telegram-bot.git
synced 2025-03-17 04:39:55 +01:00
Merge branch 'master' into dec04
This commit is contained in:
commit
975d193441
25 changed files with 662 additions and 199 deletions
|
@ -9,12 +9,14 @@ Contributors
|
|||
The following wonderful people contributed directly or indirectly to this project:
|
||||
|
||||
- `Avanatiker <https://github.com/Avanatiker>`_
|
||||
- `Anton Tagunov <https://github.com/anton-tagunov>`_
|
||||
- `Balduro <https://github.com/Balduro>`_
|
||||
- `bimmlerd <https://github.com/bimmlerd>`_
|
||||
- `Eli Gao <https://github.com/eligao>`_
|
||||
- `ErgoZ Riftbit Vaper <https://github.com/ergoz>`_
|
||||
- `Eugene Lisitsky <https://github.com/lisitsky>`_
|
||||
- `franciscod <https://github.com/franciscod>`_
|
||||
- `Hugo Damer <https://github.com/HakimusGIT>`_
|
||||
- `Jacob Bom <https://github.com/bomjacob>`_
|
||||
- `JASON0916 <https://github.com/JASON0916>`_
|
||||
- `jh0ker <https://github.com/jh0ker>`_
|
||||
|
@ -31,6 +33,7 @@ The following wonderful people contributed directly or indirectly to this projec
|
|||
- `overquota <https://github.com/overquota>`_
|
||||
- `Patrick Hofmann <https://github.com/PH89>`_
|
||||
- `Rahiel Kasim <https://github.com/rahiel>`_
|
||||
- `Joscha Götzer <https://github.com/Rostgnom>`_
|
||||
- `Shelomentsev D <https://github.com/shelomentsevd>`_
|
||||
- `sooyhwang <https://github.com/sooyhwang>`_
|
||||
- `Valentijn <https://github.com/Faalentijn>`_
|
||||
|
|
|
@ -55,6 +55,8 @@ class Animation(TelegramObject):
|
|||
if not data:
|
||||
return None
|
||||
|
||||
data = super(Animation, Animation).de_json(data, bot)
|
||||
|
||||
data['thumb'] = PhotoSize.de_json(data.get('thumb'), bot)
|
||||
|
||||
return Animation(**data)
|
||||
|
|
217
telegram/bot.py
217
telegram/bot.py
|
@ -158,9 +158,14 @@ class Bot(TelegramObject):
|
|||
return decorator
|
||||
|
||||
@log
|
||||
def getMe(self, **kwargs):
|
||||
def getMe(self, timeout=None, **kwargs):
|
||||
"""A simple method for testing your bot's auth token.
|
||||
|
||||
Args:
|
||||
timeout (Optional[int|float]): 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).
|
||||
|
||||
Returns:
|
||||
:class:`telegram.User`: A :class:`telegram.User` instance representing that bot if the
|
||||
credentials are valid, `None` otherwise.
|
||||
|
@ -171,7 +176,7 @@ class Bot(TelegramObject):
|
|||
"""
|
||||
url = '{0}/getMe'.format(self.base_url)
|
||||
|
||||
result = self._request.get(url)
|
||||
result = self._request.get(url, timeout=timeout)
|
||||
|
||||
self.bot = User.de_json(result, self)
|
||||
|
||||
|
@ -211,8 +216,9 @@ class Bot(TelegramObject):
|
|||
interface options. A JSON-serialized object for an inline
|
||||
keyboard, custom reply keyboard, instructions to remove reply
|
||||
keyboard or to force a reply from the user.
|
||||
timeout (Optional[float]): If this value is specified, use it as
|
||||
the definitive timeout (in seconds) for urlopen() operations.
|
||||
timeout (Optional[int|float]): If this value is specified, use it as the read timeout
|
||||
from the server (instead of the one specified during creation of the connection
|
||||
pool).
|
||||
**kwargs (dict): Arbitrary keyword arguments.
|
||||
|
||||
Returns:
|
||||
|
@ -251,8 +257,9 @@ class Bot(TelegramObject):
|
|||
message_id: Unique message identifier.
|
||||
disable_notification (Optional[bool]): Sends the message silently. iOS users will not
|
||||
receive a notification, Android users will receive a notification with no sound.
|
||||
timeout (Optional[float]): If this value is specified, use it as
|
||||
the definitive timeout (in seconds) for urlopen() operations.
|
||||
timeout (Optional[int|float]): If this value is specified, use it as the read timeout
|
||||
from the server (instead of the one specified during creation of the connection
|
||||
pool).
|
||||
**kwargs (dict): Arbitrary keyword arguments.
|
||||
|
||||
Returns:
|
||||
|
@ -301,8 +308,9 @@ class Bot(TelegramObject):
|
|||
reply_markup (Optional[:class:`telegram.ReplyMarkup`]): Additional interface options. A
|
||||
JSON-serialized object for an inline keyboard, custom reply keyboard, instructions
|
||||
to remove reply keyboard or to force a reply from the user.
|
||||
timeout (Optional[float]): If this value is specified, use it as the definitive timeout
|
||||
(in seconds) for urlopen() operations.
|
||||
timeout (Optional[int|float]): If this value is specified, use it as the read timeout
|
||||
from the server (instead of the one specified during creation of the connection
|
||||
pool).
|
||||
**kwargs (dict): Arbitrary keyword arguments.
|
||||
|
||||
Returns:
|
||||
|
@ -362,8 +370,9 @@ class Bot(TelegramObject):
|
|||
reply_markup (Optional[:class:`telegram.ReplyMarkup`]): Additional interface options. A
|
||||
JSON-serialized object for an inline keyboard, custom reply keyboard, instructions
|
||||
to remove reply keyboard or to force a reply from the user.
|
||||
timeout (Optional[float]): If this value is specified, use it as the definitive timeout
|
||||
(in seconds) for urlopen() operations.
|
||||
timeout (Optional[int|float]): If this value is specified, use it as the read timeout
|
||||
from the server (instead of the one specified during creation of the connection
|
||||
pool).
|
||||
**kwargs (dict): Arbitrary keyword arguments.
|
||||
|
||||
Returns:
|
||||
|
@ -417,8 +426,9 @@ class Bot(TelegramObject):
|
|||
reply_markup (Optional[:class:`telegram.ReplyMarkup`]): Additional interface options. A
|
||||
JSON-serialized object for an inline keyboard, custom reply keyboard, instructions
|
||||
to remove reply keyboard or to force a reply from the user.
|
||||
timeout (Optional[float]): If this value is specified, use it as the definitive timeout
|
||||
(in seconds) for urlopen() operations.
|
||||
timeout (Optional[int|float]): If this value is specified, use it as the read timeout
|
||||
from the server (instead of the one specified during creation of the connection
|
||||
pool).
|
||||
**kwargs (dict): Arbitrary keyword arguments.
|
||||
|
||||
Returns:
|
||||
|
@ -463,8 +473,9 @@ class Bot(TelegramObject):
|
|||
reply_markup (Optional[:class:`telegram.ReplyMarkup`]): Additional interface options. A
|
||||
JSON-serialized object for an inline keyboard, custom reply keyboard, instructions
|
||||
to remove reply keyboard or to force a reply from the user.
|
||||
timeout (Optional[float]): If this value is specified, use it as the definitive timeout
|
||||
(in seconds) for urlopen() operations.
|
||||
timeout (Optional[int|float]): If this value is specified, use it as the read timeout
|
||||
from the server (instead of the one specified during creation of the connection
|
||||
pool).
|
||||
**kwargs (dict): Arbitrary keyword arguments.
|
||||
|
||||
Returns:
|
||||
|
@ -510,8 +521,9 @@ class Bot(TelegramObject):
|
|||
reply_markup (Optional[:class:`telegram.ReplyMarkup`]): Additional interface options. A
|
||||
JSON-serialized object for an inline keyboard, custom reply keyboard, instructions
|
||||
to remove reply keyboard or to force a reply from the user.
|
||||
timeout (Optional[float]): If this value is specified, use it as the definitive timeout
|
||||
(in seconds) for urlopen() operations.
|
||||
timeout (Optional[int|float]): 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).
|
||||
|
||||
Returns:
|
||||
:class:`telegram.Message`: On success, instance representing the message posted.
|
||||
|
@ -563,8 +575,9 @@ class Bot(TelegramObject):
|
|||
reply_markup (Optional[:class:`telegram.ReplyMarkup`]): Additional interface options. A
|
||||
JSON-serialized object for an inline keyboard, custom reply keyboard, instructions
|
||||
to remove reply keyboard or to force a reply from the user.
|
||||
timeout (Optional[float]): If this value is specified, use it as the definitive timeout
|
||||
(in seconds) for urlopen() operations.
|
||||
timeout (Optional[int|float]): If this value is specified, use it as the read timeout
|
||||
from the server (instead of the one specified during creation of the connection
|
||||
pool).
|
||||
**kwargs (dict): Arbitrary keyword arguments.
|
||||
|
||||
Returns:
|
||||
|
@ -609,8 +622,9 @@ class Bot(TelegramObject):
|
|||
reply_markup (Optional[:class:`telegram.ReplyMarkup`]): Additional interface options. A
|
||||
JSON-serialized object for an inline keyboard, custom reply keyboard, instructions
|
||||
to remove reply keyboard or to force a reply from the user.
|
||||
timeout (Optional[float]): If this value is specified, use it as the definitive timeout
|
||||
(in seconds) for urlopen() operations.
|
||||
timeout (Optional[int|float]): If this value is specified, use it as the read timeout
|
||||
from the server (instead of the one specified during creation of the connection
|
||||
pool).
|
||||
**kwargs (dict): Arbitrary keyword arguments.
|
||||
|
||||
Returns:
|
||||
|
@ -658,8 +672,9 @@ class Bot(TelegramObject):
|
|||
reply_markup (Optional[:class:`telegram.ReplyMarkup`]): Additional interface options. A
|
||||
JSON-serialized object for an inline keyboard, custom reply keyboard, instructions
|
||||
to remove reply keyboard or to force a reply from the user.
|
||||
timeout (Optional[float]): If this value is specified, use it as the definitive timeout
|
||||
(in seconds) for urlopen() operations.
|
||||
timeout (Optional[int|float]): If this value is specified, use it as the read timeout
|
||||
from the server (instead of the one specified during creation of the connection
|
||||
pool).
|
||||
**kwargs (dict): Arbitrary keyword arguments.
|
||||
|
||||
Returns:
|
||||
|
@ -712,8 +727,9 @@ class Bot(TelegramObject):
|
|||
reply_markup (Optional[:class:`telegram.ReplyMarkup`]): Additional interface options. A
|
||||
JSON-serialized object for an inline keyboard, custom reply keyboard, instructions
|
||||
to remove reply keyboard or to force a reply from the user.
|
||||
timeout (Optional[float]): If this value is specified, use it as the definitive timeout
|
||||
(in seconds) for urlopen() operations.
|
||||
timeout (Optional[int|float]): If this value is specified, use it as the read timeout
|
||||
from the server (instead of the one specified during creation of the connection
|
||||
pool).
|
||||
**kwargs (dict): Arbitrary keyword arguments.
|
||||
|
||||
Returns:
|
||||
|
@ -734,7 +750,7 @@ class Bot(TelegramObject):
|
|||
|
||||
@log
|
||||
@message
|
||||
def sendGame(self, chat_id, game_short_name, **kwargs):
|
||||
def sendGame(self, chat_id, game_short_name, timeout=None, **kwargs):
|
||||
"""Use this method to send a game.
|
||||
|
||||
Args:
|
||||
|
@ -751,8 +767,9 @@ class Bot(TelegramObject):
|
|||
reply_markup (Optional[:class:`telegram.ReplyMarkup`]): Additional interface options.
|
||||
A JSON-serialized object for an inline keyboard, custom reply keyboard,
|
||||
instructions to remove reply keyboard or to force a reply from the user.
|
||||
timeout (Optional[float]): If this value is specified, use it as
|
||||
the definitive timeout (in seconds) for urlopen() operations.
|
||||
timeout (Optional[int|float]): 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).
|
||||
|
||||
Returns:
|
||||
:class:`telegram.Message`: On success, the sent message is returned.
|
||||
|
@ -769,7 +786,7 @@ class Bot(TelegramObject):
|
|||
|
||||
@log
|
||||
@message
|
||||
def sendChatAction(self, chat_id, action, **kwargs):
|
||||
def sendChatAction(self, chat_id, action, timeout=None, **kwargs):
|
||||
"""Use this method when you need to tell the user that something is happening on the bot's
|
||||
side. The status is set for 5 seconds or less (when a message arrives from your bot,
|
||||
Telegram clients clear its typing status).
|
||||
|
@ -785,6 +802,9 @@ class Bot(TelegramObject):
|
|||
- ChatAction.UPLOAD_AUDIO for audio files,
|
||||
- ChatAction.UPLOAD_DOCUMENT for general files,
|
||||
- ChatAction.FIND_LOCATION for location data.
|
||||
timeout (Optional[int|float]): If this value is specified, use it as the read timeout
|
||||
from the server (instead of the one specified during creation of the connection
|
||||
pool).
|
||||
**kwargs (dict): Arbitrary keyword arguments.
|
||||
|
||||
"""
|
||||
|
@ -826,8 +846,9 @@ class Bot(TelegramObject):
|
|||
start message with the parameter switch_pm_parameter.
|
||||
switch_pm_parameter (Optional[str]): Parameter for the start message sent to the bot
|
||||
when user presses the switch button.
|
||||
timeout (Optional[float]): If this value is specified, use it as the definitive timeout
|
||||
(in seconds) for urlopen() operations.
|
||||
timeout (Optional[int|float]): If this value is specified, use it as the read timeout
|
||||
from the server (instead of the one specified during creation of the connection
|
||||
pool).
|
||||
**kwargs (dict): Arbitrary keyword arguments.
|
||||
|
||||
Returns:
|
||||
|
@ -868,8 +889,9 @@ class Bot(TelegramObject):
|
|||
default, all photos are returned.
|
||||
limit (Optional[int]): Limits the number of photos to be retrieved. Values between
|
||||
1-100 are accepted. Defaults to 100.
|
||||
timeout (Optional[float]): If this value is specified, use it as the definitive timeout
|
||||
(in seconds) for urlopen() operations.
|
||||
timeout (Optional[int|float]): If this value is specified, use it as the read timeout
|
||||
from the server (instead of the one specified during creation of the connection
|
||||
pool).
|
||||
**kwargs (dict): Arbitrary keyword arguments.
|
||||
|
||||
Returns:
|
||||
|
@ -900,8 +922,9 @@ class Bot(TelegramObject):
|
|||
|
||||
Args:
|
||||
file_id: File identifier to get info about.
|
||||
timeout (Optional[float]): If this value is specified, use it as the definitive timeout
|
||||
(in seconds) for urlopen() operations.
|
||||
timeout (Optional[int|float]): If this value is specified, use it as the read timeout
|
||||
from the server (instead of the one specified during creation of the connection
|
||||
pool).
|
||||
**kwargs (dict): Arbitrary keyword arguments.
|
||||
|
||||
Returns:
|
||||
|
@ -934,8 +957,9 @@ class Bot(TelegramObject):
|
|||
chat_id: Unique identifier for the target group or username of the target supergroup
|
||||
(in the format @supergroupusername).
|
||||
user_id: Unique identifier of the target user.
|
||||
timeout (Optional[float]): If this value is specified, use it as the definitive timeout
|
||||
(in seconds) for urlopen() operations.
|
||||
timeout (Optional[int|float]): If this value is specified, use it as the read timeout
|
||||
from the server (instead of the one specified during creation of the connection
|
||||
pool).
|
||||
**kwargs (dict): Arbitrary keyword arguments.
|
||||
|
||||
Returns:
|
||||
|
@ -963,8 +987,9 @@ class Bot(TelegramObject):
|
|||
chat_id: Unique identifier for the target group or username of the target supergroup
|
||||
(in the format @supergroupusername).
|
||||
user_id: Unique identifier of the target user.
|
||||
timeout (Optional[float]): If this value is specified, use it as the definitive timeout
|
||||
(in seconds) for urlopen() operations.
|
||||
timeout (Optional[int|float]): If this value is specified, use it as the read timeout
|
||||
from the server (instead of the one specified during creation of the connection
|
||||
pool).
|
||||
**kwargs (dict): Arbitrary keyword arguments.
|
||||
|
||||
Returns:
|
||||
|
@ -1005,8 +1030,9 @@ class Bot(TelegramObject):
|
|||
cache_time (Optional[int]): The maximum amount of time in seconds that the result of
|
||||
the callback query may be cached client-side. Telegram apps will support caching
|
||||
starting in version 3.14. Defaults to 0.
|
||||
timeout (Optional[float]): If this value is specified, use it as the definitive timeout
|
||||
(in seconds) for urlopen() operations.
|
||||
timeout (Optional[int|float]): If this value is specified, use it as the read timeout
|
||||
from the server (instead of the one specified during creation of the connection
|
||||
pool).
|
||||
**kwargs (dict): Arbitrary keyword arguments.
|
||||
|
||||
Returns:
|
||||
|
@ -1062,8 +1088,9 @@ class Bot(TelegramObject):
|
|||
reply_markup (Optional[:class:`telegram.ReplyMarkup`]): Additional interface options. A
|
||||
JSON-serialized object for an inline keyboard, custom reply keyboard, instructions
|
||||
to remove reply keyboard or to force a reply from the user.
|
||||
timeout (Optional[float]): If this value is specified, use it as the definitive timeout
|
||||
(in seconds) for urlopen() operations.
|
||||
timeout (Optional[int|float]): If this value is specified, use it as the read timeout
|
||||
from the server (instead of the one specified during creation of the connection
|
||||
pool).
|
||||
**kwargs (dict): Arbitrary keyword arguments.
|
||||
|
||||
Returns:
|
||||
|
@ -1115,8 +1142,9 @@ class Bot(TelegramObject):
|
|||
caption (Optional[str]): New caption of the message.
|
||||
reply_markup (Optional[:class:`telegram.InlineKeyboardMarkup`]): A JSON-serialized
|
||||
object for an inline keyboard.
|
||||
timeout (Optional[float]): If this value is specified, use it as the definitive timeout
|
||||
(in seconds) for urlopen() operations.
|
||||
timeout (Optional[int|float]): If this value is specified, use it as the read timeout
|
||||
from the server (instead of the one specified during creation of the connection
|
||||
pool).
|
||||
**kwargs (dict): Arbitrary keyword arguments.
|
||||
|
||||
Returns:
|
||||
|
@ -1169,8 +1197,9 @@ class Bot(TelegramObject):
|
|||
specified. Identifier of the inline message.
|
||||
reply_markup (Optional[:class:`telegram.InlineKeyboardMarkup`]): A JSON-serialized
|
||||
object for an inline keyboard.
|
||||
timeout (Optional[float]): If this value is specified, use it as the definitive timeout
|
||||
(in seconds) for urlopen() operations.
|
||||
timeout (Optional[int|float]): If this value is specified, use it as the read timeout
|
||||
from the server (instead of the one specified during creation of the connection
|
||||
pool).
|
||||
**kwargs (dict): Arbitrary keyword arguments.
|
||||
|
||||
Returns:
|
||||
|
@ -1205,7 +1234,8 @@ class Bot(TelegramObject):
|
|||
offset=None,
|
||||
limit=100,
|
||||
timeout=0,
|
||||
network_delay=5.,
|
||||
network_delay=None,
|
||||
read_latency=2.,
|
||||
allowed_updates=None,
|
||||
**kwargs):
|
||||
"""Use this method to receive incoming updates using long polling.
|
||||
|
@ -1227,13 +1257,25 @@ class Bot(TelegramObject):
|
|||
Please note that this parameter doesn't affect updates created before the call to
|
||||
the setWebhook, so unwanted updates may be received for a short period of time.
|
||||
timeout (Optional[int]): Timeout in seconds for long polling. Defaults to 0, i.e. usual
|
||||
short polling.
|
||||
network_delay (Optional[float]): Additional timeout in seconds to allow the response
|
||||
from Telegram servers. This should cover network latency around the globe, SSL
|
||||
handshake and slowness of the Telegram servers (which unfortunately happens a lot
|
||||
recently - 2016-05-28). Defaults to 5.
|
||||
short polling. Be careful not to set this timeout too high, as the connection might
|
||||
be dropped and there's no way of knowing it immediately (so most likely the failure
|
||||
will be detected after the timeout had passed).
|
||||
network_delay: Deprecated. Will be honoured as `read_latency` for a while but will be
|
||||
removed in the future.
|
||||
read_latency (Optional[float|int]): Grace time in seconds for receiving the reply from
|
||||
server. Will be added to the `timeout` value and used as the read timeout from
|
||||
server (Default: 2).
|
||||
**kwargs (dict): Arbitrary keyword arguments.
|
||||
|
||||
Notes:
|
||||
The main problem with long polling is that a connection will be dropped and we won't
|
||||
be getting the notification in time for it. For that, we need to use long polling, but
|
||||
not too long as well read latency which is short, but not too short.
|
||||
Long polling improves performance, but if it's too long and the connection is dropped
|
||||
on many cases we won't know the connection dropped before the long polling timeout and
|
||||
the read latency time had passed. If you experience connection timeouts, you should
|
||||
tune these settings.
|
||||
|
||||
Returns:
|
||||
list[:class:`telegram.Update`]
|
||||
|
||||
|
@ -1243,6 +1285,10 @@ class Bot(TelegramObject):
|
|||
"""
|
||||
url = '{0}/getUpdates'.format(self.base_url)
|
||||
|
||||
if network_delay is not None:
|
||||
warnings.warn('network_delay is deprecated, use read_latency instead')
|
||||
read_latency = network_delay
|
||||
|
||||
data = {'timeout': timeout}
|
||||
|
||||
if offset:
|
||||
|
@ -1252,9 +1298,12 @@ class Bot(TelegramObject):
|
|||
if allowed_updates is not None:
|
||||
data['allowed_updates'] = allowed_updates
|
||||
|
||||
urlopen_timeout = timeout + network_delay
|
||||
|
||||
result = self._request.post(url, data, timeout=urlopen_timeout)
|
||||
# Ideally we'd use an aggressive read timeout for the polling. However,
|
||||
# * Short polling should return within 2 seconds.
|
||||
# * Long polling poses a different problem: the connection might have been dropped while
|
||||
# waiting for the server to return and there's no way of knowing the connection had been
|
||||
# dropped in real time.
|
||||
result = self._request.post(url, data, timeout=float(read_latency) + float(timeout))
|
||||
|
||||
if result:
|
||||
self.logger.debug('Getting updates: %s', [u['update_id'] for u in result])
|
||||
|
@ -1292,8 +1341,9 @@ class Bot(TelegramObject):
|
|||
specified, the previous setting will be used.
|
||||
Please note that this parameter doesn't affect updates created before the call to
|
||||
the setWebhook, so unwanted updates may be received for a short period of time.
|
||||
timeout (Optional[float]): If this value is specified, use it as the definitive timeout
|
||||
(in seconds) for urlopen() operations.
|
||||
timeout (Optional[int|float]): If this value is specified, use it as the read timeout
|
||||
from the server (instead of the one specified during creation of the connection
|
||||
pool).
|
||||
**kwargs (dict): Arbitrary keyword arguments.
|
||||
|
||||
Returns:
|
||||
|
@ -1363,8 +1413,9 @@ class Bot(TelegramObject):
|
|||
Args:
|
||||
chat_id: Unique identifier for the target chat or username of the target channel (in
|
||||
the format @channelusername).
|
||||
timeout (Optional[float]): If this value is specified, use it as the definitive timeout
|
||||
(in seconds) for urlopen() operations.
|
||||
timeout (Optional[int|float]): If this value is specified, use it as the read timeout
|
||||
from the server (instead of the one specified during creation of the connection
|
||||
pool).
|
||||
**kwargs (dict): Arbitrary keyword arguments.
|
||||
|
||||
Returns:
|
||||
|
@ -1390,8 +1441,9 @@ class Bot(TelegramObject):
|
|||
Args:
|
||||
chat_id: Unique identifier for the target chat or username of the target channel (in
|
||||
the format @channelusername).
|
||||
timeout (Optional[float]): If this value is specified, use it as the definitive timeout
|
||||
(in seconds) for urlopen() operations.
|
||||
timeout (Optional[int|float]): If this value is specified, use it as the read timeout
|
||||
from the server (instead of the one specified during creation of the connection
|
||||
pool).
|
||||
**kwargs (dict): Arbitrary keyword arguments.
|
||||
|
||||
Returns:
|
||||
|
@ -1420,8 +1472,9 @@ class Bot(TelegramObject):
|
|||
Args:
|
||||
chat_id: Unique identifier for the target chat or username of the target channel (in
|
||||
the format @channelusername).
|
||||
timeout (Optional[float]): If this value is specified, use it as the definitive timeout
|
||||
(in seconds) for urlopen() operations.
|
||||
timeout (Optional[int|float]): If this value is specified, use it as the read timeout
|
||||
from the server (instead of the one specified during creation of the connection
|
||||
pool).
|
||||
**kwargs (dict): Arbitrary keyword arguments.
|
||||
|
||||
Returns:
|
||||
|
@ -1446,8 +1499,9 @@ class Bot(TelegramObject):
|
|||
Args:
|
||||
chat_id: Unique identifier for the target chat or username of the target channel (in
|
||||
the format @channelusername).
|
||||
timeout (Optional[float]): If this value is specified, use it as the definitive timeout
|
||||
(in seconds) for urlopen() operations.
|
||||
timeout (Optional[int|float]): If this value is specified, use it as the read timeout
|
||||
from the server (instead of the one specified during creation of the connection
|
||||
pool).
|
||||
**kwargs (dict): Arbitrary keyword arguments.
|
||||
|
||||
Returns:
|
||||
|
@ -1473,8 +1527,9 @@ class Bot(TelegramObject):
|
|||
chat_id: Unique identifier for the target chat or username of the target channel (in
|
||||
the format @channelusername).
|
||||
user_id: Unique identifier of the target user.
|
||||
timeout (Optional[float]): If this value is specified, use it as the definitive timeout
|
||||
(in seconds) for urlopen() operations.
|
||||
timeout (Optional[int|float]): If this value is specified, use it as the read timeout
|
||||
from the server (instead of the one specified during creation of the connection
|
||||
pool).
|
||||
**kwargs (dict): Arbitrary keyword arguments.
|
||||
|
||||
Returns:
|
||||
|
@ -1492,11 +1547,16 @@ class Bot(TelegramObject):
|
|||
|
||||
return ChatMember.de_json(result, self)
|
||||
|
||||
def getWebhookInfo(self, **kwargs):
|
||||
def getWebhookInfo(self, timeout=None, **kwargs):
|
||||
"""Use this method to get current webhook status.
|
||||
|
||||
If the bot is using getUpdates, will return an object with the url field empty.
|
||||
|
||||
Args:
|
||||
timeout (Optional[int|float]): 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).
|
||||
|
||||
Returns:
|
||||
:class: `telegram.WebhookInfo`
|
||||
|
||||
|
@ -1505,7 +1565,7 @@ class Bot(TelegramObject):
|
|||
|
||||
data = {}
|
||||
|
||||
result = self._request.post(url, data, timeout=kwargs.get('timeout'))
|
||||
result = self._request.post(url, data, timeout=timeout)
|
||||
|
||||
return WebhookInfo.de_json(result, self)
|
||||
|
||||
|
@ -1518,6 +1578,7 @@ class Bot(TelegramObject):
|
|||
edit_message=None,
|
||||
force=None,
|
||||
disable_edit_message=None,
|
||||
timeout=None,
|
||||
**kwargs):
|
||||
"""Use this method to set the score of the specified user in a game.
|
||||
|
||||
|
@ -1537,10 +1598,9 @@ class Bot(TelegramObject):
|
|||
automatically edited to include the current scoreboard.
|
||||
edit_message (Optional[bool]): Deprecated. Has the opposite logic for
|
||||
`disable_edit_message`.
|
||||
|
||||
Keyword Args:
|
||||
timeout (Optional[float]): If this value is specified, use it as the definitive timeout
|
||||
(in seconds) for urlopen() operations.
|
||||
timeout (Optional[int|float]): 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).
|
||||
|
||||
Returns:
|
||||
:class:`telegram.Message` or True: The edited message, or if the
|
||||
|
@ -1568,7 +1628,7 @@ class Bot(TelegramObject):
|
|||
else:
|
||||
warnings.warn('edit_message is ignored when disable_edit_message is used')
|
||||
|
||||
result = self._request.post(url, data, timeout=kwargs.get('timeout'))
|
||||
result = self._request.post(url, data, timeout=timeout)
|
||||
if result is True:
|
||||
return result
|
||||
else:
|
||||
|
@ -1579,9 +1639,15 @@ class Bot(TelegramObject):
|
|||
chat_id=None,
|
||||
message_id=None,
|
||||
inline_message_id=None,
|
||||
timeout=None,
|
||||
**kwargs):
|
||||
"""Use this method to get data for high score tables.
|
||||
|
||||
Args:
|
||||
timeout (Optional[int|float]): 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).
|
||||
|
||||
Returns:
|
||||
list[:class:`telegram.GameHighScore`]: Scores of the specified user and several of his
|
||||
neighbors in a game.
|
||||
|
@ -1598,7 +1664,7 @@ class Bot(TelegramObject):
|
|||
if inline_message_id:
|
||||
data['inline_message_id'] = inline_message_id
|
||||
|
||||
result = self._request.post(url, data, timeout=kwargs.get('timeout'))
|
||||
result = self._request.post(url, data, timeout=timeout)
|
||||
|
||||
return [GameHighScore.de_json(hs, self) for hs in result]
|
||||
|
||||
|
@ -1647,7 +1713,6 @@ class Bot(TelegramObject):
|
|||
edit_message_reply_markup = editMessageReplyMarkup
|
||||
get_updates = getUpdates
|
||||
set_webhook = setWebhook
|
||||
delete_webhook = deleteWebhook
|
||||
leave_chat = leaveChat
|
||||
get_chat = getChat
|
||||
get_chat_administrators = getChatAdministrators
|
||||
|
|
|
@ -60,6 +60,8 @@ class CallbackQuery(TelegramObject):
|
|||
if not data:
|
||||
return None
|
||||
|
||||
data = super(CallbackQuery, CallbackQuery).de_json(data, bot)
|
||||
|
||||
data['from_user'] = User.de_json(data.get('from'), bot)
|
||||
data['message'] = Message.de_json(data.get('message'), bot)
|
||||
|
||||
|
|
|
@ -59,6 +59,8 @@ class ChatMember(TelegramObject):
|
|||
if not data:
|
||||
return None
|
||||
|
||||
data = super(ChatMember, ChatMember).de_json(data, bot)
|
||||
|
||||
data['user'] = User.de_json(data.get('user'), bot)
|
||||
|
||||
return ChatMember(**data)
|
||||
|
|
|
@ -74,7 +74,8 @@ class ChosenInlineResult(TelegramObject):
|
|||
if not data:
|
||||
return None
|
||||
|
||||
# Required
|
||||
data = super(ChosenInlineResult, ChosenInlineResult).de_json(data, bot)
|
||||
# Required
|
||||
data['from_user'] = User.de_json(data.pop('from'), bot)
|
||||
# Optionals
|
||||
data['location'] = Location.de_json(data.get('location'), bot)
|
||||
|
|
|
@ -63,6 +63,8 @@ class Document(TelegramObject):
|
|||
if not data:
|
||||
return None
|
||||
|
||||
data = super(Document, Document).de_json(data, bot)
|
||||
|
||||
data['thumb'] = PhotoSize.de_json(data.get('thumb'), bot)
|
||||
|
||||
return Document(**data)
|
||||
|
|
|
@ -28,7 +28,9 @@ from telegram.utils.promise import Promise
|
|||
|
||||
class ConversationHandler(Handler):
|
||||
"""
|
||||
A handler to hold a conversation with a user by managing four collections of other handlers.
|
||||
A handler to hold a conversation with a single user by managing four collections of other
|
||||
handlers. Note that neither posts in Telegram Channels, nor group interactions with multiple
|
||||
users are managed by instances of this class.
|
||||
|
||||
The first collection, a ``list`` named ``entry_points``, is used to initiate the conversation,
|
||||
for example with a ``CommandHandler`` or ``RegexHandler``.
|
||||
|
@ -113,7 +115,8 @@ class ConversationHandler(Handler):
|
|||
|
||||
def check_update(self, update):
|
||||
|
||||
if not isinstance(update, Update):
|
||||
# Ignore messages in channels
|
||||
if not isinstance(update, Update) or update.channel_post:
|
||||
return False
|
||||
|
||||
chat, user = extract_chat_and_user(update)
|
||||
|
|
|
@ -22,6 +22,7 @@ import logging
|
|||
import time
|
||||
import warnings
|
||||
import datetime
|
||||
import weakref
|
||||
from numbers import Number
|
||||
from threading import Thread, Lock, Event
|
||||
from queue import PriorityQueue, Empty
|
||||
|
@ -68,40 +69,182 @@ class JobQueue(object):
|
|||
|
||||
Args:
|
||||
job (telegram.ext.Job): The ``Job`` instance representing the new job
|
||||
next_t (Optional[int, float, datetime.timedelta]): Time in which the job
|
||||
should be executed first. Defaults to ``job.interval``. ``int`` and ``float``
|
||||
will be interpreted as seconds.
|
||||
next_t (Optional[int, float, datetime.timedelta, datetime.datetime, datetime.time]):
|
||||
Time in or at which the job should run for the first time. This parameter will be
|
||||
interpreted depending on its type.
|
||||
``int`` or ``float`` will be interpreted as "seconds from now" in which the job
|
||||
should run.
|
||||
``datetime.timedelta`` will be interpreted as "time from now" in which the job
|
||||
should run.
|
||||
``datetime.datetime`` will be interpreted as a specific date and time at which the
|
||||
job should run.
|
||||
``datetime.time`` will be interpreted as a specific time at which the job should
|
||||
run. This could be either today or, if the time has already passed, tomorrow.
|
||||
"""
|
||||
warnings.warn("'JobQueue.put' is being deprecated, use 'JobQueue.one_time_job', "
|
||||
"'JobQueue.repeating_job' or 'JobQueue.daily_job' instead")
|
||||
if job.job_queue is None:
|
||||
job.job_queue = self
|
||||
self._put(job, next_t=next_t)
|
||||
|
||||
def _put(self, job, next_t=None, last_t=None):
|
||||
"""Queue a new job.
|
||||
|
||||
Args:
|
||||
job (telegram.ext.Job): The ``Job`` instance representing the new job
|
||||
next_t (Optional[int, float, datetime.timedelta, datetime.datetime, datetime.time]):
|
||||
Time in or at which the job should run for the first time. This parameter will be
|
||||
interpreted depending on its type.
|
||||
|
||||
* ``int`` or ``float`` will be interpreted as "seconds from now" in which the job
|
||||
should run.
|
||||
* ``datetime.timedelta`` will be interpreted as "time from now" in which the job
|
||||
should run.
|
||||
* ``datetime.datetime`` will be interpreted as a specific date and time at which
|
||||
the job should run.
|
||||
* ``datetime.time`` will be interpreted as a specific time of day at which the job
|
||||
should run. This could be either today or, if the time has already passed,
|
||||
tomorrow.
|
||||
last_t (Optional[float]): Timestamp of the time when ``job`` was scheduled for in the
|
||||
last ``put`` call. If provided, it will be used to calculate the next timestamp
|
||||
more accurately by accounting for the execution time of the job (and possibly
|
||||
others). If None, `now` will be assumed.
|
||||
|
||||
"""
|
||||
job.job_queue = self
|
||||
|
||||
if next_t is None:
|
||||
interval = job.interval
|
||||
next_t = job.interval
|
||||
if next_t is None:
|
||||
raise ValueError('next_t is None')
|
||||
|
||||
if isinstance(next_t, datetime.datetime):
|
||||
next_t = (next_t - datetime.datetime.now()).total_seconds()
|
||||
|
||||
elif isinstance(next_t, datetime.time):
|
||||
next_datetime = datetime.datetime.combine(datetime.date.today(), next_t)
|
||||
|
||||
if datetime.datetime.now().time() > next_t:
|
||||
next_datetime += datetime.timedelta(days=1)
|
||||
|
||||
next_t = (next_datetime - datetime.datetime.now()).total_seconds()
|
||||
|
||||
if isinstance(interval, Number):
|
||||
next_t = interval
|
||||
elif isinstance(interval, datetime.timedelta):
|
||||
next_t = interval.total_seconds()
|
||||
else:
|
||||
raise ValueError("The interval argument should be of type datetime.timedelta,"
|
||||
" int or float")
|
||||
elif isinstance(next_t, datetime.timedelta):
|
||||
next_t = next_t.total_second()
|
||||
next_t = next_t.total_seconds()
|
||||
|
||||
now = time.time()
|
||||
next_t += now
|
||||
next_t += last_t or time.time()
|
||||
|
||||
self.logger.debug('Putting job %s with t=%f', job.name, next_t)
|
||||
|
||||
self.queue.put((next_t, job))
|
||||
|
||||
# Wake up the loop if this job should be executed next
|
||||
self._set_next_peek(next_t)
|
||||
|
||||
def run_once(self, callback, when, context=None, name=None):
|
||||
"""Creates a new ``Job`` that runs once and adds it to the queue.
|
||||
|
||||
Args:
|
||||
callback (function): The callback function that should be executed by the new job. It
|
||||
should take two parameters ``bot`` and ``job``, where ``job`` is the ``Job``
|
||||
instance. It can be used to access it's ``context`` or change it to a repeating
|
||||
job.
|
||||
when (int, float, datetime.timedelta, datetime.datetime, datetime.time):
|
||||
Time in or at which the job should run. This parameter will be interpreted
|
||||
depending on its type.
|
||||
|
||||
* ``int`` or ``float`` will be interpreted as "seconds from now" in which the job
|
||||
should run.
|
||||
* ``datetime.timedelta`` will be interpreted as "time from now" in which the job
|
||||
should run.
|
||||
* ``datetime.datetime`` will be interpreted as a specific date and time at which
|
||||
the job should run.
|
||||
* ``datetime.time`` will be interpreted as a specific time of day at which the job
|
||||
should run. This could be either today or, if the time has already passed,
|
||||
tomorrow.
|
||||
|
||||
context (Optional[object]): Additional data needed for the callback function. Can be
|
||||
accessed through ``job.context`` in the callback. Defaults to ``None``
|
||||
name (Optional[str]): The name of the new job. Defaults to ``callback.__name__``
|
||||
|
||||
Returns:
|
||||
Job: The new ``Job`` instance that has been added to the job queue.
|
||||
|
||||
"""
|
||||
job = Job(callback, repeat=False, context=context, name=name, job_queue=self)
|
||||
self._put(job, next_t=when)
|
||||
return job
|
||||
|
||||
def run_repeating(self, callback, interval, first=None, context=None, name=None):
|
||||
"""Creates a new ``Job`` that runs once and adds it to the queue.
|
||||
|
||||
Args:
|
||||
callback (function): The callback function that should be executed by the new job. It
|
||||
should take two parameters ``bot`` and ``job``, where ``job`` is the ``Job``
|
||||
instance. It can be used to access it's ``context``, terminate the job or change
|
||||
its interval.
|
||||
interval (int, float, datetime.timedelta): The interval in which the job will run.
|
||||
If it is an ``int`` or a ``float``, it will be interpreted as seconds.
|
||||
first (int, float, datetime.timedelta, datetime.datetime, datetime.time):
|
||||
|
||||
* ``int`` or ``float`` will be interpreted as "seconds from now" in which the job
|
||||
should run.
|
||||
* ``datetime.timedelta`` will be interpreted as "time from now" in which the job
|
||||
should run.
|
||||
* ``datetime.datetime`` will be interpreted as a specific date and time at which
|
||||
the job should run.
|
||||
* ``datetime.time`` will be interpreted as a specific time of day at which the job
|
||||
should run. This could be either today or, if the time has already passed,
|
||||
tomorrow.
|
||||
|
||||
Defaults to ``interval``
|
||||
context (Optional[object]): Additional data needed for the callback function. Can be
|
||||
accessed through ``job.context`` in the callback. Defaults to ``None``
|
||||
name (Optional[str]): The name of the new job. Defaults to ``callback.__name__``
|
||||
|
||||
Returns:
|
||||
Job: The new ``Job`` instance that has been added to the job queue.
|
||||
|
||||
"""
|
||||
job = Job(callback,
|
||||
interval=interval,
|
||||
repeat=True,
|
||||
context=context,
|
||||
name=name,
|
||||
job_queue=self)
|
||||
self._put(job, next_t=first)
|
||||
return job
|
||||
|
||||
def run_daily(self, callback, time, days=Days.EVERY_DAY, context=None, name=None):
|
||||
"""Creates a new ``Job`` that runs once and adds it to the queue.
|
||||
|
||||
Args:
|
||||
callback (function): The callback function that should be executed by the new job. It
|
||||
should take two parameters ``bot`` and ``job``, where ``job`` is the ``Job``
|
||||
instance. It can be used to access it's ``context`` or terminate the job.
|
||||
time (datetime.time): Time of day at which the job should run.
|
||||
days (Optional[tuple[int]]): Defines on which days of the week the job should run.
|
||||
Defaults to ``Days.EVERY_DAY``
|
||||
context (Optional[object]): Additional data needed for the callback function. Can be
|
||||
accessed through ``job.context`` in the callback. Defaults to ``None``
|
||||
name (Optional[str]): The name of the new job. Defaults to ``callback.__name__``
|
||||
|
||||
Returns:
|
||||
Job: The new ``Job`` instance that has been added to the job queue.
|
||||
|
||||
"""
|
||||
job = Job(callback,
|
||||
interval=datetime.timedelta(days=1),
|
||||
repeat=True,
|
||||
days=days,
|
||||
context=context,
|
||||
name=name,
|
||||
job_queue=self)
|
||||
self._put(job, next_t=time)
|
||||
return job
|
||||
|
||||
def _set_next_peek(self, t):
|
||||
"""
|
||||
Set next peek if not defined or `t` is before next peek.
|
||||
In case the next peek was set, also trigger the `self.__tick` event.
|
||||
|
||||
"""
|
||||
with self.__next_peek_lock:
|
||||
if not self._next_peek or self._next_peek > t:
|
||||
|
@ -126,9 +269,9 @@ class JobQueue(object):
|
|||
self.logger.debug('Peeked at %s with t=%f', job.name, t)
|
||||
|
||||
if t > now:
|
||||
# we can get here in two conditions:
|
||||
# 1. At the second or later pass of the while loop, after we've already processed
|
||||
# the job(s) we were supposed to at this time.
|
||||
# We can get here in two conditions:
|
||||
# 1. At the second or later pass of the while loop, after we've already
|
||||
# processed the job(s) we were supposed to at this time.
|
||||
# 2. At the first iteration of the loop only if `self.put()` had triggered
|
||||
# `self.__tick` because `self._next_peek` wasn't set
|
||||
self.logger.debug("Next task isn't due yet. Finished!")
|
||||
|
@ -136,7 +279,7 @@ class JobQueue(object):
|
|||
self._set_next_peek(t)
|
||||
break
|
||||
|
||||
if job._remove.is_set():
|
||||
if job.removed:
|
||||
self.logger.debug('Removing job %s', job.name)
|
||||
continue
|
||||
|
||||
|
@ -146,15 +289,17 @@ class JobQueue(object):
|
|||
if any(day == current_week_day for day in job.days):
|
||||
self.logger.debug('Running job %s', job.name)
|
||||
job.run(self.bot)
|
||||
|
||||
except:
|
||||
self.logger.exception('An uncaught error was raised while executing job %s',
|
||||
job.name)
|
||||
|
||||
else:
|
||||
self.logger.debug('Skipping disabled job %s', job.name)
|
||||
|
||||
if job.repeat:
|
||||
self.put(job)
|
||||
if job.repeat and not job.removed:
|
||||
self._put(job, last_t=t)
|
||||
else:
|
||||
self.logger.debug('Dropping non-repeating or removed job %s', job.name)
|
||||
|
||||
def start(self):
|
||||
"""
|
||||
|
@ -169,7 +314,6 @@ class JobQueue(object):
|
|||
self.__thread = Thread(target=self._main_loop, name="job_queue")
|
||||
self.__thread.start()
|
||||
self.logger.debug('%s thread started', self.__class__.__name__)
|
||||
|
||||
else:
|
||||
self.__start_lock.release()
|
||||
|
||||
|
@ -182,7 +326,7 @@ class JobQueue(object):
|
|||
while self._running:
|
||||
# self._next_peek may be (re)scheduled during self.tick() or self.put()
|
||||
with self.__next_peek_lock:
|
||||
tmout = self._next_peek and self._next_peek - time.time()
|
||||
tmout = self._next_peek - time.time() if self._next_peek else None
|
||||
self._next_peek = None
|
||||
self.__tick.clear()
|
||||
|
||||
|
@ -216,46 +360,58 @@ class Job(object):
|
|||
"""This class encapsulates a Job
|
||||
|
||||
Attributes:
|
||||
callback (function):
|
||||
interval (float):
|
||||
days: (tuple)
|
||||
repeat (bool):
|
||||
name (str):
|
||||
callback (function): The function that the job executes when it's due
|
||||
interval (int, float, datetime.timedelta): The interval in which the job runs
|
||||
days (tuple[int]): A tuple of ``int`` values that determine on which days of the week the
|
||||
job runs
|
||||
repeat (bool): If the job runs periodically or only once
|
||||
name (str): The name of this job
|
||||
job_queue (JobQueue): The ``JobQueue`` this job belongs to
|
||||
enabled (bool): Boolean property that decides if this job is currently active
|
||||
|
||||
Args:
|
||||
callback (function): The callback function that should be executed by the Job. It should
|
||||
take two parameters ``bot`` and ``job``, where ``job`` is the ``Job`` instance. It
|
||||
can be used to terminate the job or modify its interval.
|
||||
interval ([int, float, datetime.timedelta]): The interval in which the job will execute its
|
||||
callback function. ``int`` and ``float`` will be interpreted as seconds.
|
||||
interval (Optional[int, float, datetime.timedelta]): The interval in which the job will
|
||||
execute its callback function. ``int`` and ``float`` will be interpreted as seconds.
|
||||
If you don't set this value, you must set ``repeat=False`` and specify ``next_t`` when
|
||||
you put the job into the job queue.
|
||||
repeat (Optional[bool]): If this job should be periodically execute its callback function
|
||||
(``True``) or only once (``False``). Defaults to ``True``
|
||||
context (Optional[object]): Additional data needed for the callback function. Can be
|
||||
accessed through ``job.context`` in the callback. Defaults to ``None``
|
||||
days (Tuple): Defines on which days the job should be ran.
|
||||
days (Optional[tuple[int]]): Defines on which days of the week the job should run.
|
||||
Defaults to ``Days.EVERY_DAY``
|
||||
name (Optional[str]): The name of this job. Defaults to ``callback.__name__``
|
||||
job_queue (Optional[class:`telegram.ext.JobQueue`]): The ``JobQueue`` this job belongs to.
|
||||
Only optional for backward compatibility with ``JobQueue.put()``.
|
||||
|
||||
"""
|
||||
job_queue = None
|
||||
|
||||
def __init__(self, callback, interval, repeat=True, context=None, days=Days.EVERY_DAY):
|
||||
def __init__(self,
|
||||
callback,
|
||||
interval=None,
|
||||
repeat=True,
|
||||
context=None,
|
||||
days=Days.EVERY_DAY,
|
||||
name=None,
|
||||
job_queue=None):
|
||||
|
||||
self.callback = callback
|
||||
self.context = context
|
||||
self.name = name or callback.__name__
|
||||
|
||||
self._repeat = repeat
|
||||
self._interval = None
|
||||
self.interval = interval
|
||||
self.repeat = repeat
|
||||
self.context = context
|
||||
|
||||
if not isinstance(days, tuple):
|
||||
raise ValueError("The 'days argument should be of type 'tuple'")
|
||||
|
||||
if not all(isinstance(day, int) for day in days):
|
||||
raise ValueError("The elements of the 'days' argument should be of type 'int'")
|
||||
|
||||
if not all(day >= 0 and day <= 6 for day in days):
|
||||
raise ValueError("The elements of the 'days' argument should be from 0 up to and "
|
||||
"including 6")
|
||||
|
||||
self._days = None
|
||||
self.days = days
|
||||
self.name = callback.__name__
|
||||
|
||||
self._job_queue = weakref.proxy(job_queue) if job_queue is not None else None
|
||||
|
||||
self._remove = Event()
|
||||
self._enabled = Event()
|
||||
self._enabled.set()
|
||||
|
@ -268,20 +424,86 @@ class Job(object):
|
|||
"""
|
||||
Schedules this job for removal from the ``JobQueue``. It will be removed without executing
|
||||
its callback function again.
|
||||
|
||||
"""
|
||||
self._remove.set()
|
||||
|
||||
def is_enabled(self):
|
||||
@property
|
||||
def removed(self):
|
||||
return self._remove.is_set()
|
||||
|
||||
@property
|
||||
def enabled(self):
|
||||
return self._enabled.is_set()
|
||||
|
||||
def set_enabled(self, status):
|
||||
@enabled.setter
|
||||
def enabled(self, status):
|
||||
if status:
|
||||
self._enabled.set()
|
||||
else:
|
||||
self._enabled.clear()
|
||||
|
||||
enabled = property(is_enabled, set_enabled)
|
||||
@property
|
||||
def interval(self):
|
||||
return self._interval
|
||||
|
||||
@interval.setter
|
||||
def interval(self, interval):
|
||||
if interval is None and self.repeat:
|
||||
raise ValueError("The 'interval' can not be 'None' when 'repeat' is set to 'True'")
|
||||
|
||||
if not (interval is None or isinstance(interval, (Number, datetime.timedelta))):
|
||||
raise ValueError("The 'interval' must be of type 'datetime.timedelta',"
|
||||
" 'int' or 'float'")
|
||||
|
||||
self._interval = interval
|
||||
|
||||
@property
|
||||
def interval_seconds(self):
|
||||
if isinstance(self.interval, datetime.timedelta):
|
||||
return self.interval.total_seconds()
|
||||
else:
|
||||
return self.interval
|
||||
|
||||
@property
|
||||
def repeat(self):
|
||||
return self._repeat
|
||||
|
||||
@repeat.setter
|
||||
def repeat(self, repeat):
|
||||
if self.interval is None and repeat:
|
||||
raise ValueError("'repeat' can not be set to 'True' when no 'interval' is set")
|
||||
self._repeat = repeat
|
||||
|
||||
@property
|
||||
def days(self):
|
||||
return self._days
|
||||
|
||||
@days.setter
|
||||
def days(self, days):
|
||||
if not isinstance(days, tuple):
|
||||
raise ValueError("The 'days' argument should be of type 'tuple'")
|
||||
|
||||
if not all(isinstance(day, int) for day in days):
|
||||
raise ValueError("The elements of the 'days' argument should be of type 'int'")
|
||||
|
||||
if not all(0 <= day <= 6 for day in days):
|
||||
raise ValueError("The elements of the 'days' argument should be from 0 up to and "
|
||||
"including 6")
|
||||
|
||||
self._days = days
|
||||
|
||||
@property
|
||||
def job_queue(self):
|
||||
""" :rtype: JobQueue """
|
||||
return self._job_queue
|
||||
|
||||
@job_queue.setter
|
||||
def job_queue(self, job_queue):
|
||||
# Property setter for backward compatibility with JobQueue.put()
|
||||
if not self._job_queue:
|
||||
self._job_queue = weakref.proxy(job_queue)
|
||||
else:
|
||||
raise RuntimeError("The 'job_queue' attribute can only be set once.")
|
||||
|
||||
def __lt__(self, other):
|
||||
return False
|
||||
|
|
|
@ -22,6 +22,7 @@ Telegram bots intuitive."""
|
|||
import logging
|
||||
import os
|
||||
import ssl
|
||||
import warnings
|
||||
from threading import Thread, Lock, current_thread, Event
|
||||
from time import sleep
|
||||
import subprocess
|
||||
|
@ -61,6 +62,8 @@ class Updater(object):
|
|||
bot (Optional[Bot]): A pre-initialized bot instance. If a pre-initizlied bot is used, it is
|
||||
the user's responsibility to create it using a `Request` instance with a large enough
|
||||
connection pool.
|
||||
request_kwargs (Optional[dict]): Keyword args to control the creation of a request object
|
||||
(ignored if `bot` argument is used).
|
||||
|
||||
Raises:
|
||||
ValueError: If both `token` and `bot` are passed or none of them.
|
||||
|
@ -68,7 +71,7 @@ class Updater(object):
|
|||
"""
|
||||
_request = None
|
||||
|
||||
def __init__(self, token=None, base_url=None, workers=4, bot=None):
|
||||
def __init__(self, token=None, base_url=None, workers=4, bot=None, request_kwargs=None):
|
||||
if (token is None) and (bot is None):
|
||||
raise ValueError('`token` or `bot` must be passed')
|
||||
if (token is not None) and (bot is not None):
|
||||
|
@ -83,7 +86,11 @@ class Updater(object):
|
|||
# * 1 for polling Updater (even if webhook is used, we can spare a connection)
|
||||
# * 1 for JobQueue
|
||||
# * 1 for main thread
|
||||
self._request = Request(con_pool_size=workers + 4)
|
||||
if request_kwargs is None:
|
||||
request_kwargs = {}
|
||||
if 'con_pool_size' not in request_kwargs:
|
||||
request_kwargs['con_pool_size'] = workers + 4
|
||||
self._request = Request(**request_kwargs)
|
||||
self.bot = Bot(token, base_url, request=self._request)
|
||||
self.update_queue = Queue()
|
||||
self.job_queue = JobQueue(self.bot)
|
||||
|
@ -122,9 +129,10 @@ class Updater(object):
|
|||
def start_polling(self,
|
||||
poll_interval=0.0,
|
||||
timeout=10,
|
||||
network_delay=5.,
|
||||
network_delay=None,
|
||||
clean=False,
|
||||
bootstrap_retries=0,
|
||||
read_latency=2.,
|
||||
allowed_updates=None):
|
||||
"""
|
||||
Starts polling updates from Telegram.
|
||||
|
@ -135,7 +143,8 @@ class Updater(object):
|
|||
|
||||
timeout (Optional[float]): Passed to Bot.getUpdates
|
||||
|
||||
network_delay (Optional[float]): Passed to Bot.getUpdates
|
||||
network_delay: Deprecated. Will be honoured as `read_latency` for a while but will be
|
||||
removed in the future.
|
||||
|
||||
clean (Optional[bool]): Whether to clean any pending updates on Telegram servers before
|
||||
actually starting to poll. Default is False.
|
||||
|
@ -144,15 +153,24 @@ class Updater(object):
|
|||
will retry on failures on the Telegram server.
|
||||
|
||||
| < 0 - retry indefinitely
|
||||
| 0 - no retries (default)
|
||||
| 0 - no retries (default)
|
||||
| > 0 - retry up to X times
|
||||
|
||||
allowed_updates (Optional[list[str]]): Passed to Bot.getUpdates
|
||||
|
||||
read_latency (Optional[float|int]): Grace time in seconds for receiving the reply from
|
||||
server. Will be added to the `timeout` value and used as the read timeout from
|
||||
server (Default: 2).
|
||||
|
||||
|
||||
Returns:
|
||||
Queue: The update queue that can be filled from the main thread
|
||||
|
||||
"""
|
||||
if network_delay is not None:
|
||||
warnings.warn('network_delay is deprecated, use read_latency instead')
|
||||
read_latency = network_delay
|
||||
|
||||
with self.__lock:
|
||||
if not self.running:
|
||||
self.running = True
|
||||
|
@ -161,7 +179,7 @@ class Updater(object):
|
|||
self.job_queue.start()
|
||||
self._init_thread(self.dispatcher.start, "dispatcher")
|
||||
self._init_thread(self._start_polling, "updater", poll_interval, timeout,
|
||||
network_delay, bootstrap_retries, clean, allowed_updates)
|
||||
read_latency, bootstrap_retries, clean, allowed_updates)
|
||||
|
||||
# Return the update queue so the main thread can insert updates
|
||||
return self.update_queue
|
||||
|
@ -218,7 +236,7 @@ class Updater(object):
|
|||
# Return the update queue so the main thread can insert updates
|
||||
return self.update_queue
|
||||
|
||||
def _start_polling(self, poll_interval, timeout, network_delay, bootstrap_retries, clean,
|
||||
def _start_polling(self, poll_interval, timeout, read_latency, bootstrap_retries, clean,
|
||||
allowed_updates):
|
||||
"""
|
||||
Thread target of thread 'updater'. Runs in background, pulls
|
||||
|
@ -236,7 +254,7 @@ class Updater(object):
|
|||
updates = self.bot.getUpdates(
|
||||
self.last_update_id,
|
||||
timeout=timeout,
|
||||
network_delay=network_delay,
|
||||
read_latency=read_latency,
|
||||
allowed_updates=allowed_updates)
|
||||
except RetryAfter as e:
|
||||
self.logger.info(str(e))
|
||||
|
|
|
@ -65,17 +65,39 @@ class File(TelegramObject):
|
|||
|
||||
return File(bot=bot, **data)
|
||||
|
||||
def download(self, custom_path=None):
|
||||
def download(self, custom_path=None, out=None, timeout=None):
|
||||
"""
|
||||
Download this file. By default, the file is saved in the current working directory with its
|
||||
original filename as reported by Telegram. If a ``custom_path`` is supplied, it will be
|
||||
saved to that path instead. If ``out`` is defined, the file contents will be saved to that
|
||||
object using the ``out.write`` method. ``custom_path`` and ``out`` are mutually exclusive.
|
||||
|
||||
Args:
|
||||
custom_path (str):
|
||||
custom_path (Optional[str]): Custom path.
|
||||
out (Optional[object]): A file-like object. Must be opened in binary mode, if
|
||||
applicable.
|
||||
timeout (Optional[int|float]): 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).
|
||||
|
||||
Raises:
|
||||
ValueError: If both ``custom_path`` and ``out`` are passed.
|
||||
|
||||
"""
|
||||
|
||||
if custom_path is not None and out is not None:
|
||||
raise ValueError('custom_path and out are mutually exclusive')
|
||||
|
||||
url = self.file_path
|
||||
|
||||
if custom_path:
|
||||
filename = custom_path
|
||||
else:
|
||||
filename = basename(url)
|
||||
if out:
|
||||
buf = self.bot.request.retrieve(url)
|
||||
out.write(buf)
|
||||
|
||||
self.bot.request.download(url, filename)
|
||||
else:
|
||||
if custom_path:
|
||||
filename = custom_path
|
||||
else:
|
||||
filename = basename(url)
|
||||
|
||||
self.bot.request.download(url, filename, timeout=timeout)
|
||||
|
|
|
@ -73,6 +73,8 @@ class Game(TelegramObject):
|
|||
if not data:
|
||||
return None
|
||||
|
||||
data = super(Game, Game).de_json(data, bot)
|
||||
|
||||
data['photo'] = PhotoSize.de_list(data.get('photo'), bot)
|
||||
data['text_entities'] = MessageEntity.de_list(data.get('text_entities'), bot)
|
||||
data['animation'] = Animation.de_json(data.get('animation'), bot)
|
||||
|
|
|
@ -49,6 +49,8 @@ class GameHighScore(TelegramObject):
|
|||
if not data:
|
||||
return None
|
||||
|
||||
data = super(GameHighScore, GameHighScore).de_json(data, bot)
|
||||
|
||||
data['user'] = User.de_json(data.get('user'), bot)
|
||||
|
||||
return GameHighScore(**data)
|
||||
|
|
|
@ -200,6 +200,8 @@ class Message(TelegramObject):
|
|||
if not data:
|
||||
return None
|
||||
|
||||
data = super(Message, Message).de_json(data, bot)
|
||||
|
||||
data['from_user'] = User.de_json(data.get('from'), bot)
|
||||
data['date'] = datetime.fromtimestamp(data['date'])
|
||||
data['chat'] = Chat.de_json(data.get('chat'), bot)
|
||||
|
|
|
@ -67,6 +67,8 @@ class ReplyKeyboardMarkup(ReplyMarkup):
|
|||
if not data:
|
||||
return None
|
||||
|
||||
data = super(ReplyKeyboardMarkup, ReplyKeyboardMarkup).de_json(data, bot)
|
||||
|
||||
data['keyboard'] = [KeyboardButton.de_list(keyboard, bot) for keyboard in data['keyboard']]
|
||||
|
||||
return ReplyKeyboardMarkup(**data)
|
||||
|
|
|
@ -67,6 +67,8 @@ class Sticker(TelegramObject):
|
|||
if not data:
|
||||
return None
|
||||
|
||||
data = super(Sticker, Sticker).de_json(data, bot)
|
||||
|
||||
data['thumb'] = PhotoSize.de_json(data.get('thumb'), bot)
|
||||
|
||||
return Sticker(**data)
|
||||
|
|
|
@ -86,6 +86,8 @@ class Update(TelegramObject):
|
|||
if not data:
|
||||
return None
|
||||
|
||||
data = super(Update, Update).de_json(data, bot)
|
||||
|
||||
data['message'] = Message.de_json(data.get('message'), bot)
|
||||
data['edited_message'] = Message.de_json(data.get('edited_message'), bot)
|
||||
data['inline_query'] = InlineQuery.de_json(data.get('inline_query'), bot)
|
||||
|
|
|
@ -77,6 +77,8 @@ class User(TelegramObject):
|
|||
if not data:
|
||||
return None
|
||||
|
||||
data = super(User, User).de_json(data, bot)
|
||||
|
||||
return User(bot=bot, **data)
|
||||
|
||||
def get_profile_photos(self, *args, **kwargs):
|
||||
|
|
|
@ -52,6 +52,8 @@ class UserProfilePhotos(TelegramObject):
|
|||
if not data:
|
||||
return None
|
||||
|
||||
data = super(UserProfilePhotos, UserProfilePhotos).de_json(data, bot)
|
||||
|
||||
data['photos'] = [PhotoSize.de_list(photo, bot) for photo in data['photos']]
|
||||
|
||||
return UserProfilePhotos(**data)
|
||||
|
|
|
@ -17,22 +17,23 @@
|
|||
# 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 methods to make POST and GET requests"""
|
||||
import os
|
||||
import socket
|
||||
import logging
|
||||
|
||||
try:
|
||||
import ujson as json
|
||||
except ImportError:
|
||||
import json
|
||||
import os
|
||||
import socket
|
||||
import logging
|
||||
|
||||
import certifi
|
||||
import urllib3
|
||||
from urllib3.connection import HTTPConnection
|
||||
from urllib3.util.timeout import Timeout
|
||||
|
||||
from telegram import (InputFile, TelegramError)
|
||||
from telegram.error import Unauthorized, InvalidToken, NetworkError, TimedOut, BadRequest, \
|
||||
ChatMigrated, RetryAfter
|
||||
from telegram.error import (Unauthorized, NetworkError, TimedOut, BadRequest, ChatMigrated,
|
||||
RetryAfter, InvalidToken)
|
||||
|
||||
logging.getLogger('urllib3').setLevel(logging.WARNING)
|
||||
|
||||
|
@ -43,23 +44,40 @@ class Request(object):
|
|||
telegram servers.
|
||||
|
||||
Args:
|
||||
con_pool_size (int): Number of connections to keep in the connection pool.
|
||||
proxy_url (str): The URL to the proxy server. For example: `http://127.0.0.1:3128`.
|
||||
urllib3_proxy_kwargs (dict): Arbitrary arguments passed as-is to `urllib3.ProxyManager`.
|
||||
This value will be ignored if proxy_url is not set.
|
||||
connect_timeout (int|float): The maximum amount of time (in seconds) to wait for a
|
||||
connection attempt to a server to succeed. None will set an infinite timeout for
|
||||
connection attempts. (default: 5.)
|
||||
read_timeout (int|float): The maximum amount of time (in seconds) to wait between
|
||||
consecutive read operations for a response from the server. None will set an infinite
|
||||
timeout. This value is usually overridden by the various ``telegram.Bot`` methods.
|
||||
(default: 5.)
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, con_pool_size=1, proxy_url=None, urllib3_proxy_kwargs=None):
|
||||
def __init__(self,
|
||||
con_pool_size=1,
|
||||
proxy_url=None,
|
||||
urllib3_proxy_kwargs=None,
|
||||
connect_timeout=5.,
|
||||
read_timeout=5.):
|
||||
if urllib3_proxy_kwargs is None:
|
||||
urllib3_proxy_kwargs = dict()
|
||||
|
||||
self._connect_timeout = connect_timeout
|
||||
|
||||
kwargs = dict(
|
||||
maxsize=con_pool_size,
|
||||
cert_reqs='CERT_REQUIRED',
|
||||
ca_certs=certifi.where(),
|
||||
socket_options=HTTPConnection.default_socket_options + [
|
||||
(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1),
|
||||
])
|
||||
],
|
||||
timeout=urllib3.Timeout(
|
||||
connect=self._connect_timeout, read=read_timeout),)
|
||||
|
||||
# Set a proxy according to the following order:
|
||||
# * proxy defined in proxy_url (+ urllib3_proxy_kwargs)
|
||||
|
@ -128,6 +146,12 @@ class Request(object):
|
|||
TelegramError
|
||||
|
||||
"""
|
||||
# Make sure to hint Telegram servers that we reuse connections by sending
|
||||
# "Connection: keep-alive" in the HTTP headers.
|
||||
if 'headers' not in kwargs:
|
||||
kwargs['headers'] = {}
|
||||
kwargs['headers']['connection'] = 'keep-alive'
|
||||
|
||||
try:
|
||||
resp = self._con_pool.request(*args, **kwargs)
|
||||
except urllib3.exceptions.TimeoutError:
|
||||
|
@ -157,33 +181,14 @@ class Request(object):
|
|||
else:
|
||||
raise NetworkError('{0} ({1})'.format(message, resp.status))
|
||||
|
||||
def get(self, url):
|
||||
def get(self, url, timeout=None):
|
||||
"""Request an URL.
|
||||
|
||||
Args:
|
||||
url:
|
||||
The web location we want to retrieve.
|
||||
|
||||
Returns:
|
||||
A JSON object.
|
||||
|
||||
"""
|
||||
result = self._request_wrapper('GET', url)
|
||||
return self._parse(result)
|
||||
|
||||
def post(self, url, data, timeout=None):
|
||||
"""Request an URL.
|
||||
Args:
|
||||
url:
|
||||
The web location we want to retrieve.
|
||||
data:
|
||||
A dict of (str, unicode) key/value pairs.
|
||||
timeout:
|
||||
float. If this value is specified, use it as the definitive timeout (in
|
||||
seconds) for urlopen() operations. [Optional]
|
||||
|
||||
Notes:
|
||||
If neither `timeout` nor `data['timeout']` is specified. The underlying
|
||||
defaults are used.
|
||||
url (str): The web location we want to retrieve.
|
||||
timeout (Optional[int|float]): 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).
|
||||
|
||||
Returns:
|
||||
A JSON object.
|
||||
|
@ -192,11 +197,33 @@ class Request(object):
|
|||
urlopen_kwargs = {}
|
||||
|
||||
if timeout is not None:
|
||||
urlopen_kwargs['timeout'] = timeout
|
||||
urlopen_kwargs['timeout'] = Timeout(read=timeout, connect=self._connect_timeout)
|
||||
|
||||
result = self._request_wrapper('GET', url, **urlopen_kwargs)
|
||||
return self._parse(result)
|
||||
|
||||
def post(self, url, data, timeout=None):
|
||||
"""Request an URL.
|
||||
Args:
|
||||
url (str): The web location we want to retrieve.
|
||||
data (dict[str, str]): A dict of key/value pairs. Note: On py2.7 value is unicode.
|
||||
timeout (Optional[int|float]): 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).
|
||||
|
||||
Returns:
|
||||
A JSON object.
|
||||
|
||||
"""
|
||||
urlopen_kwargs = {}
|
||||
|
||||
if timeout is not None:
|
||||
urlopen_kwargs['timeout'] = Timeout(read=timeout, connect=self._connect_timeout)
|
||||
|
||||
if InputFile.is_inputfile(data):
|
||||
data = InputFile(data)
|
||||
result = self._request_wrapper('POST', url, body=data.to_form(), headers=data.headers)
|
||||
result = self._request_wrapper(
|
||||
'POST', url, body=data.to_form(), headers=data.headers, **urlopen_kwargs)
|
||||
else:
|
||||
data = json.dumps(data)
|
||||
result = self._request_wrapper(
|
||||
|
@ -208,16 +235,34 @@ class Request(object):
|
|||
|
||||
return self._parse(result)
|
||||
|
||||
def download(self, url, filename):
|
||||
def retrieve(self, url, timeout=None):
|
||||
"""Retrieve the contents of a file by its URL.
|
||||
|
||||
Args:
|
||||
url (str): The web location we want to retrieve.
|
||||
timeout (Optional[int|float]): 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).
|
||||
|
||||
"""
|
||||
urlopen_kwargs = {}
|
||||
if timeout is not None:
|
||||
urlopen_kwargs['timeout'] = Timeout(read=timeout, connect=self._connect_timeout)
|
||||
|
||||
return self._request_wrapper('GET', url, **urlopen_kwargs)
|
||||
|
||||
def download(self, url, filename, timeout=None):
|
||||
"""Download a file by its URL.
|
||||
Args:
|
||||
url:
|
||||
The web location we want to retrieve.
|
||||
url (str): The web location we want to retrieve.
|
||||
timeout (Optional[int|float]): 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).
|
||||
|
||||
filename:
|
||||
The filename within the path to download the file.
|
||||
|
||||
"""
|
||||
buf = self._request_wrapper('GET', url)
|
||||
buf = self.retrieve(url, timeout=timeout)
|
||||
with open(filename, 'wb') as fobj:
|
||||
fobj.write(buf)
|
||||
|
|
|
@ -78,6 +78,8 @@ class Video(TelegramObject):
|
|||
if not data:
|
||||
return None
|
||||
|
||||
data = super(Video, Video).de_json(data, bot)
|
||||
|
||||
data['thumb'] = PhotoSize.de_json(data.get('thumb'), bot)
|
||||
|
||||
return Video(**data)
|
||||
|
|
|
@ -61,4 +61,6 @@ class Voice(TelegramObject):
|
|||
if not data:
|
||||
return None
|
||||
|
||||
data = super(Voice, Voice).de_json(data, bot)
|
||||
|
||||
return Voice(**data)
|
||||
|
|
|
@ -438,6 +438,9 @@ class BotTest(BaseTest, unittest.TestCase):
|
|||
@flaky(3, 1)
|
||||
@timeout(10)
|
||||
def test_send_contact(self):
|
||||
# test disabled due to telegram servers annoyances repeatedly returning:
|
||||
# "Flood control exceeded. Retry in 2036 seconds"
|
||||
return
|
||||
phone = '+3-54-5445445'
|
||||
name = 'name'
|
||||
last = 'last'
|
||||
|
|
|
@ -62,9 +62,6 @@ class JobQueueTest(BaseTest, unittest.TestCase):
|
|||
if self.jq is not None:
|
||||
self.jq.stop()
|
||||
|
||||
def getSeconds(self):
|
||||
return int(ceil(time.time()))
|
||||
|
||||
def job1(self, bot, job):
|
||||
self.result += 1
|
||||
|
||||
|
@ -79,7 +76,7 @@ class JobQueueTest(BaseTest, unittest.TestCase):
|
|||
self.result += job.context
|
||||
|
||||
def job5(self, bot, job):
|
||||
self.job_time = self.getSeconds()
|
||||
self.job_time = time.time()
|
||||
|
||||
def test_basic(self):
|
||||
self.jq.put(Job(self.job1, 0.1))
|
||||
|
@ -181,22 +178,78 @@ class JobQueueTest(BaseTest, unittest.TestCase):
|
|||
|
||||
def test_time_unit_int(self):
|
||||
# Testing seconds in int
|
||||
seconds_interval = 5
|
||||
expected_time = self.getSeconds() + seconds_interval
|
||||
delta = 2
|
||||
expected_time = time.time() + delta
|
||||
|
||||
self.jq.put(Job(self.job5, seconds_interval, repeat=False))
|
||||
sleep(6)
|
||||
self.assertEqual(self.job_time, expected_time)
|
||||
self.jq.put(Job(self.job5, delta, repeat=False))
|
||||
sleep(2.5)
|
||||
self.assertAlmostEqual(self.job_time, expected_time, delta=0.1)
|
||||
|
||||
def test_time_unit_dt_time(self):
|
||||
def test_time_unit_dt_timedelta(self):
|
||||
# Testing seconds, minutes and hours as datetime.timedelta object
|
||||
# This is sufficient to test that it actually works.
|
||||
interval = datetime.timedelta(seconds=5)
|
||||
expected_time = self.getSeconds() + interval.total_seconds()
|
||||
interval = datetime.timedelta(seconds=2)
|
||||
expected_time = time.time() + interval.total_seconds()
|
||||
|
||||
self.jq.put(Job(self.job5, interval, repeat=False))
|
||||
sleep(6)
|
||||
self.assertEqual(self.job_time, expected_time)
|
||||
sleep(2.5)
|
||||
self.assertAlmostEqual(self.job_time, expected_time, delta=0.1)
|
||||
|
||||
def test_time_unit_dt_datetime(self):
|
||||
# Testing running at a specific datetime
|
||||
delta = datetime.timedelta(seconds=2)
|
||||
next_t = datetime.datetime.now() + delta
|
||||
expected_time = time.time() + delta.total_seconds()
|
||||
|
||||
self.jq.put(Job(self.job5, repeat=False), next_t=next_t)
|
||||
sleep(2.5)
|
||||
self.assertAlmostEqual(self.job_time, expected_time, delta=0.1)
|
||||
|
||||
def test_time_unit_dt_time_today(self):
|
||||
# Testing running at a specific time today
|
||||
delta = 2
|
||||
next_t = (datetime.datetime.now() + datetime.timedelta(seconds=delta)).time()
|
||||
expected_time = time.time() + delta
|
||||
|
||||
self.jq.put(Job(self.job5, repeat=False), next_t=next_t)
|
||||
sleep(2.5)
|
||||
self.assertAlmostEqual(self.job_time, expected_time, delta=0.1)
|
||||
|
||||
def test_time_unit_dt_time_tomorrow(self):
|
||||
# Testing running at a specific time that has passed today. Since we can't wait a day, we
|
||||
# test if the jobs next_t has been calculated correctly
|
||||
delta = -2
|
||||
next_t = (datetime.datetime.now() + datetime.timedelta(seconds=delta)).time()
|
||||
expected_time = time.time() + delta + 60 * 60 * 24
|
||||
|
||||
self.jq.put(Job(self.job5, repeat=False), next_t=next_t)
|
||||
self.assertAlmostEqual(self.jq.queue.get(False)[0], expected_time, delta=0.1)
|
||||
|
||||
def test_run_once(self):
|
||||
delta = 2
|
||||
expected_time = time.time() + delta
|
||||
|
||||
self.jq.run_once(self.job5, delta)
|
||||
sleep(2.5)
|
||||
self.assertAlmostEqual(self.job_time, expected_time, delta=0.1)
|
||||
|
||||
def test_run_repeating(self):
|
||||
interval = 0.1
|
||||
first = 1.5
|
||||
|
||||
self.jq.run_repeating(self.job1, interval, first=first)
|
||||
sleep(2.505)
|
||||
self.assertAlmostEqual(self.result, 10, delta=1)
|
||||
|
||||
def test_run_daily(self):
|
||||
delta = 1
|
||||
time_of_day = (datetime.datetime.now() + datetime.timedelta(seconds=delta)).time()
|
||||
expected_time = time.time() + 60 * 60 * 24 + delta
|
||||
|
||||
self.jq.run_daily(self.job1, time_of_day)
|
||||
sleep(2 * delta)
|
||||
self.assertEqual(self.result, 1)
|
||||
self.assertAlmostEqual(self.jq.queue.get(False)[0], expected_time, delta=0.1)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
|
|
@ -809,7 +809,7 @@ class MockBot(object):
|
|||
self.bootstrap_attempts += 1
|
||||
raise self.bootstrap_err
|
||||
|
||||
def getUpdates(self, offset=None, limit=100, timeout=0, network_delay=2.):
|
||||
def getUpdates(self, offset=None, limit=100, timeout=0, network_delay=None, read_latency=2.):
|
||||
|
||||
if self.raise_error:
|
||||
raise TelegramError('Test Error 2')
|
||||
|
|
Loading…
Add table
Reference in a new issue