mirror of
https://github.com/python-telegram-bot/python-telegram-bot.git
synced 2024-12-23 15:00:40 +01:00
Merge branch 'master' into restructure-internal
This commit is contained in:
commit
1e4248136c
8 changed files with 143 additions and 134 deletions
|
@ -2,6 +2,14 @@
|
|||
Changes
|
||||
=======
|
||||
|
||||
**2017-06-18**
|
||||
|
||||
*Released 6.1.0*
|
||||
|
||||
- Fully support Bot API 3.0
|
||||
- Add more fine-grained filters for status updates
|
||||
- Bug fixes and other improvements
|
||||
|
||||
**2017-05-29**
|
||||
|
||||
*Released 6.0.3*
|
||||
|
|
|
@ -84,13 +84,7 @@ make the development of bots easy and straightforward. These classes are contain
|
|||
Telegram API support
|
||||
====================
|
||||
|
||||
As of **21. May 2017**, all types and methods of the Telegram Bot API 2.3.1 are supported. Additionally, the ``deleteMessage`` API function and the field ``User.language_code`` are supported.
|
||||
|
||||
Also, version 6.1 beta 0 is available, offering full but experimental Bot API 3.0 coverage:
|
||||
|
||||
.. code:: shell
|
||||
|
||||
$ pip install python-telegram-bot==6.1b0
|
||||
As of **18. June 2017**, all types and methods of the Telegram Bot API 3.0 are supported.
|
||||
|
||||
==========
|
||||
Installing
|
||||
|
|
29
appveyor.yml
Normal file
29
appveyor.yml
Normal file
|
@ -0,0 +1,29 @@
|
|||
environment:
|
||||
|
||||
matrix:
|
||||
# For Python versions available on Appveyor, see
|
||||
# http://www.appveyor.com/docs/installed-software#python
|
||||
# The list here is complete (excluding Python 2.6, which
|
||||
# isn't covered by this document) at the time of writing.
|
||||
|
||||
- PYTHON: "C:\\Python27"
|
||||
- PYTHON: "C:\\Python33"
|
||||
- PYTHON: "C:\\Python34"
|
||||
- PYTHON: "C:\\Python35"
|
||||
- PYTHON: "C:\\Python36"
|
||||
|
||||
install:
|
||||
# We need wheel installed to build wheels
|
||||
- "git submodule update --init --recursive"
|
||||
- "%PYTHON%\\python.exe -m pip install wheel"
|
||||
- "%PYTHON%\\python.exe -m pip install -r requirements.txt"
|
||||
- "%PYTHON%\\python.exe -m pip install -r requirements-dev.txt"
|
||||
|
||||
build: off
|
||||
|
||||
test_script:
|
||||
- "%python%\\Scripts\\nosetests -v --with-flaky --no-flaky-report tests"
|
||||
|
||||
after_test:
|
||||
# This step builds your wheels.
|
||||
- "%PYTHON%\\python.exe setup.py bdist_wheel"
|
|
@ -58,9 +58,9 @@ author = u'Leandro Toledo'
|
|||
# built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
version = '6.0' # telegram.__version__[:3]
|
||||
version = '6.1' # telegram.__version__[:3]
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = '6.0.3' # telegram.__version__
|
||||
release = '6.1.0' # telegram.__version__
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
|
|
205
telegram/bot.py
205
telegram/bot.py
|
@ -31,10 +31,45 @@ from telegram.utils.request import Request
|
|||
logging.getLogger(__name__).addHandler(logging.NullHandler())
|
||||
|
||||
|
||||
def info(func):
|
||||
@functools.wraps(func)
|
||||
def decorator(self, *args, **kwargs):
|
||||
if not self.bot:
|
||||
self.get_me()
|
||||
|
||||
result = func(self, *args, **kwargs)
|
||||
return result
|
||||
|
||||
return decorator
|
||||
|
||||
|
||||
def log(func):
|
||||
logger = logging.getLogger(func.__module__)
|
||||
|
||||
@functools.wraps(func)
|
||||
def decorator(self, *args, **kwargs):
|
||||
logger.debug('Entering: %s', func.__name__)
|
||||
result = func(self, *args, **kwargs)
|
||||
logger.debug(result)
|
||||
logger.debug('Exiting: %s', func.__name__)
|
||||
return result
|
||||
|
||||
return decorator
|
||||
|
||||
|
||||
def message(func):
|
||||
@functools.wraps(func)
|
||||
def decorator(self, *args, **kwargs):
|
||||
url, data = func(self, *args, **kwargs)
|
||||
return self._message_wrapper(url, data, *args, **kwargs)
|
||||
|
||||
return decorator
|
||||
|
||||
|
||||
class Bot(TelegramObject):
|
||||
"""This object represents a Telegram Bot.
|
||||
|
||||
Attributes:
|
||||
Properties:
|
||||
id (int): Unique identifier for this bot.
|
||||
first_name (str): Bot's first name.
|
||||
last_name (str): Bot's last name.
|
||||
|
@ -80,18 +115,6 @@ class Bot(TelegramObject):
|
|||
|
||||
return token
|
||||
|
||||
def info(func):
|
||||
|
||||
@functools.wraps(func)
|
||||
def decorator(self, *args, **kwargs):
|
||||
if not self.bot:
|
||||
self.get_me()
|
||||
|
||||
result = func(self, *args, **kwargs)
|
||||
return result
|
||||
|
||||
return decorator
|
||||
|
||||
@property
|
||||
@info
|
||||
def id(self):
|
||||
|
@ -116,19 +139,6 @@ class Bot(TelegramObject):
|
|||
def name(self):
|
||||
return '@{0}'.format(self.username)
|
||||
|
||||
def log(func):
|
||||
logger = logging.getLogger(func.__module__)
|
||||
|
||||
@functools.wraps(func)
|
||||
def decorator(self, *args, **kwargs):
|
||||
logger.debug('Entering: %s', func.__name__)
|
||||
result = func(self, *args, **kwargs)
|
||||
logger.debug(result)
|
||||
logger.debug('Exiting: %s', func.__name__)
|
||||
return result
|
||||
|
||||
return decorator
|
||||
|
||||
def _message_wrapper(self, url, data, *args, **kwargs):
|
||||
if kwargs.get('reply_to_message_id'):
|
||||
data['reply_to_message_id'] = kwargs.get('reply_to_message_id')
|
||||
|
@ -150,15 +160,6 @@ class Bot(TelegramObject):
|
|||
|
||||
return Message.de_json(result, self)
|
||||
|
||||
def message(func):
|
||||
|
||||
@functools.wraps(func)
|
||||
def decorator(self, *args, **kwargs):
|
||||
url, data = func(self, *args, **kwargs)
|
||||
return Bot._message_wrapper(self, url, data, *args, **kwargs)
|
||||
|
||||
return decorator
|
||||
|
||||
@log
|
||||
def get_me(self, timeout=None, **kwargs):
|
||||
"""A simple method for testing your bot's auth token.
|
||||
|
@ -242,8 +243,7 @@ class Bot(TelegramObject):
|
|||
return url, data
|
||||
|
||||
@log
|
||||
@message
|
||||
def delete_message(self, chat_id, message_id):
|
||||
def delete_message(self, chat_id, message_id, timeout=None, **kwargs):
|
||||
"""Use this method to delete a message. A message can only be deleted if it was sent less
|
||||
than 48 hours ago. Any such recently sent outgoing message may be deleted. Additionally,
|
||||
if the bot is an administrator in a group chat, it can delete any message. If the bot is
|
||||
|
@ -257,6 +257,10 @@ class Bot(TelegramObject):
|
|||
username of the target channel (in the format
|
||||
@channelusername).
|
||||
message_id (int): Unique message identifier.
|
||||
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:
|
||||
bool: On success, `True` is returned.
|
||||
|
@ -269,7 +273,9 @@ class Bot(TelegramObject):
|
|||
|
||||
data = {'chat_id': chat_id, 'message_id': message_id}
|
||||
|
||||
return url, data
|
||||
result = self._request.post(url, data, timeout=timeout)
|
||||
|
||||
return result
|
||||
|
||||
@log
|
||||
@message
|
||||
|
@ -315,6 +321,7 @@ class Bot(TelegramObject):
|
|||
return url, data
|
||||
|
||||
@log
|
||||
@message
|
||||
def send_photo(self,
|
||||
chat_id,
|
||||
photo,
|
||||
|
@ -356,19 +363,10 @@ class Bot(TelegramObject):
|
|||
if caption:
|
||||
data['caption'] = caption
|
||||
|
||||
return self._message_wrapper(
|
||||
url,
|
||||
data,
|
||||
chat_id=chat_id,
|
||||
photo=photo,
|
||||
caption=caption,
|
||||
disable_notification=disable_notification,
|
||||
reply_to_message_id=reply_to_message_id,
|
||||
reply_markup=reply_markup,
|
||||
timeout=timeout,
|
||||
**kwargs)
|
||||
return url, data
|
||||
|
||||
@log
|
||||
@message
|
||||
def send_audio(self,
|
||||
chat_id,
|
||||
audio,
|
||||
|
@ -431,22 +429,10 @@ class Bot(TelegramObject):
|
|||
if caption:
|
||||
data['caption'] = caption
|
||||
|
||||
return self._message_wrapper(
|
||||
url,
|
||||
data,
|
||||
chat_id=chat_id,
|
||||
audio=audio,
|
||||
duration=duration,
|
||||
performer=performer,
|
||||
title=title,
|
||||
caption=caption,
|
||||
disable_notification=disable_notification,
|
||||
reply_to_message_id=reply_to_message_id,
|
||||
reply_markup=reply_markup,
|
||||
timeout=timeout,
|
||||
**kwargs)
|
||||
return url, data
|
||||
|
||||
@log
|
||||
@message
|
||||
def send_document(self,
|
||||
chat_id,
|
||||
document,
|
||||
|
@ -493,18 +479,7 @@ class Bot(TelegramObject):
|
|||
if caption:
|
||||
data['caption'] = caption
|
||||
|
||||
return self._message_wrapper(
|
||||
url,
|
||||
data,
|
||||
chat_id=chat_id,
|
||||
document=document,
|
||||
filename=filename,
|
||||
caption=caption,
|
||||
disable_notification=disable_notification,
|
||||
reply_to_message_id=reply_to_message_id,
|
||||
reply_markup=reply_markup,
|
||||
timeout=timeout,
|
||||
**kwargs)
|
||||
return url, data
|
||||
|
||||
@log
|
||||
@message
|
||||
|
@ -549,6 +524,7 @@ class Bot(TelegramObject):
|
|||
return url, data
|
||||
|
||||
@log
|
||||
@message
|
||||
def send_video(self,
|
||||
chat_id,
|
||||
video,
|
||||
|
@ -595,20 +571,10 @@ class Bot(TelegramObject):
|
|||
if caption:
|
||||
data['caption'] = caption
|
||||
|
||||
return self._message_wrapper(
|
||||
url,
|
||||
data,
|
||||
chat_id=chat_id,
|
||||
video=video,
|
||||
duration=duration,
|
||||
caption=caption,
|
||||
disable_notification=disable_notification,
|
||||
reply_to_message_id=reply_to_message_id,
|
||||
reply_markup=reply_markup,
|
||||
timeout=timeout,
|
||||
**kwargs)
|
||||
return url, data
|
||||
|
||||
@log
|
||||
@message
|
||||
def send_voice(self,
|
||||
chat_id,
|
||||
voice,
|
||||
|
@ -658,20 +624,10 @@ class Bot(TelegramObject):
|
|||
if caption:
|
||||
data['caption'] = caption
|
||||
|
||||
return self._message_wrapper(
|
||||
url,
|
||||
data,
|
||||
chat_id=chat_id,
|
||||
voice=voice,
|
||||
duration=duration,
|
||||
caption=caption,
|
||||
disable_notification=disable_notification,
|
||||
reply_to_message_id=reply_to_message_id,
|
||||
reply_markup=reply_markup,
|
||||
timeout=timeout,
|
||||
**kwargs)
|
||||
return url, data
|
||||
|
||||
@log
|
||||
@message
|
||||
def send_video_note(self,
|
||||
chat_id,
|
||||
video_note,
|
||||
|
@ -718,18 +674,7 @@ class Bot(TelegramObject):
|
|||
if length is not None:
|
||||
data['length'] = length
|
||||
|
||||
return self._message_wrapper(
|
||||
url,
|
||||
data,
|
||||
chat_id=chat_id,
|
||||
video_note=video_note,
|
||||
duration=duration,
|
||||
length=length,
|
||||
disable_notification=disable_notification,
|
||||
reply_to_message_id=reply_to_message_id,
|
||||
reply_markup=reply_markup,
|
||||
timeout=timeout,
|
||||
**kwargs)
|
||||
return url, data
|
||||
|
||||
@log
|
||||
@message
|
||||
|
@ -898,8 +843,6 @@ class Bot(TelegramObject):
|
|||
channel (in the format @channelusername).
|
||||
game_short_name (str): Short name of the game, serves as the unique identifier for the
|
||||
game.
|
||||
|
||||
Keyword Args:
|
||||
disable_notification (Optional[bool]): Sends the message silently. iOS users will not
|
||||
receive a notification, Android users will receive a notification with no sound.
|
||||
reply_to_message_id (Optional[int]): If the message is a reply,
|
||||
|
@ -910,6 +853,7 @@ class Bot(TelegramObject):
|
|||
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:
|
||||
:class:`telegram.Message`: On success, the sent message is returned.
|
||||
|
@ -925,7 +869,6 @@ class Bot(TelegramObject):
|
|||
return url, data
|
||||
|
||||
@log
|
||||
@message
|
||||
def send_chat_action(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,
|
||||
|
@ -952,7 +895,9 @@ class Bot(TelegramObject):
|
|||
|
||||
data = {'chat_id': chat_id, 'action': action}
|
||||
|
||||
return url, data
|
||||
result = self._request.post(url, data, timeout=timeout)
|
||||
|
||||
return result
|
||||
|
||||
@log
|
||||
def answer_inline_query(self,
|
||||
|
@ -1698,6 +1643,7 @@ class Bot(TelegramObject):
|
|||
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:
|
||||
:class: `telegram.WebhookInfo`
|
||||
|
@ -1711,6 +1657,8 @@ class Bot(TelegramObject):
|
|||
|
||||
return WebhookInfo.de_json(result, self)
|
||||
|
||||
@log
|
||||
@message
|
||||
def set_game_score(self,
|
||||
user_id,
|
||||
score,
|
||||
|
@ -1743,6 +1691,7 @@ class Bot(TelegramObject):
|
|||
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:
|
||||
:class:`telegram.Message` or True: The edited message, or if the
|
||||
|
@ -1770,12 +1719,9 @@ class Bot(TelegramObject):
|
|||
else:
|
||||
warnings.warn('edit_message is ignored when disable_edit_message is used')
|
||||
|
||||
result = self._request.post(url, data, timeout=timeout)
|
||||
if result is True:
|
||||
return result
|
||||
else:
|
||||
return Message.de_json(result, self)
|
||||
return url, data
|
||||
|
||||
@log
|
||||
def get_game_high_scores(self,
|
||||
user_id,
|
||||
chat_id=None,
|
||||
|
@ -1797,6 +1743,7 @@ class Bot(TelegramObject):
|
|||
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:
|
||||
list[:class:`telegram.GameHighScore`]: Scores of the specified user and several of his
|
||||
|
@ -1927,6 +1874,7 @@ class Bot(TelegramObject):
|
|||
|
||||
return url, data
|
||||
|
||||
@log
|
||||
def answer_shipping_query(self,
|
||||
shipping_query_id,
|
||||
ok,
|
||||
|
@ -1950,6 +1898,9 @@ class Bot(TelegramObject):
|
|||
form that explains why it is impossible to complete the order (e.g. "Sorry,
|
||||
delivery to your desired address is unavailable'). Telegram will display this
|
||||
message to the user.
|
||||
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:
|
||||
|
@ -1960,12 +1911,14 @@ class Bot(TelegramObject):
|
|||
|
||||
"""
|
||||
|
||||
if ok is True and (shipping_options is None or error_message is not None):
|
||||
ok = bool(ok)
|
||||
|
||||
if ok and (shipping_options is None or error_message is not None):
|
||||
raise TelegramError(
|
||||
'answerShippingQuery: If ok is True, shipping_options '
|
||||
'should not be empty and there should not be error_message')
|
||||
|
||||
if ok is False and (shipping_options is not None or error_message is None):
|
||||
if not ok and (shipping_options is not None or error_message is None):
|
||||
raise TelegramError(
|
||||
'answerShippingQuery: If ok is False, error_message '
|
||||
'should not be empty and there should not be shipping_options')
|
||||
|
@ -1974,7 +1927,7 @@ class Bot(TelegramObject):
|
|||
|
||||
data = {'shipping_query_id': shipping_query_id, 'ok': ok}
|
||||
|
||||
if ok is True:
|
||||
if ok:
|
||||
data['shipping_options'] = [option.to_dict() for option in shipping_options]
|
||||
if error_message is not None:
|
||||
data['error_message'] = error_message
|
||||
|
@ -1983,6 +1936,7 @@ class Bot(TelegramObject):
|
|||
|
||||
return result
|
||||
|
||||
@log
|
||||
def answer_pre_checkout_query(self, pre_checkout_query_id, ok,
|
||||
error_message=None, timeout=None, **kwargs):
|
||||
"""
|
||||
|
@ -1999,6 +1953,9 @@ class Bot(TelegramObject):
|
|||
"Sorry, somebody just bought the last of our amazing black T-shirts while you were
|
||||
busy filling out your payment details. Please choose a different color or
|
||||
garment!"). Telegram will display this message to the user.
|
||||
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:
|
||||
|
@ -2009,6 +1966,8 @@ class Bot(TelegramObject):
|
|||
|
||||
"""
|
||||
|
||||
ok = bool(ok)
|
||||
|
||||
if not (ok ^ (error_message is not None)):
|
||||
raise TelegramError(
|
||||
'answerPreCheckoutQuery: If ok is True, there should '
|
||||
|
|
|
@ -224,7 +224,7 @@ class Request(object):
|
|||
"""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.
|
||||
data (dict[str, str|int]): 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).
|
||||
|
|
|
@ -17,4 +17,4 @@
|
|||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
|
||||
__version__ = '6.0.3'
|
||||
__version__ = '6.1.0'
|
||||
|
|
|
@ -31,6 +31,7 @@ from flaky import flaky
|
|||
sys.path.append('.')
|
||||
|
||||
import telegram
|
||||
from telegram.utils.request import Request
|
||||
from telegram.error import BadRequest
|
||||
from tests.base import BaseTest, timeout
|
||||
|
||||
|
@ -467,6 +468,24 @@ class BotTest(BaseTest, unittest.TestCase):
|
|||
self.assertEqual(name, message.contact.first_name)
|
||||
self.assertEqual(last, message.contact.last_name)
|
||||
|
||||
def test_timeout_propagation(self):
|
||||
class OkException(Exception):
|
||||
pass
|
||||
|
||||
class MockRequest(Request):
|
||||
def post(self, url, data, timeout=None):
|
||||
raise OkException(timeout)
|
||||
|
||||
_request = self._bot._request
|
||||
self._bot._request = MockRequest()
|
||||
|
||||
timeout = 500
|
||||
|
||||
with self.assertRaises(OkException) as ok:
|
||||
self._bot.send_photo(self._chat_id, open('tests/data/telegram.jpg'), timeout=timeout)
|
||||
self.assertEqual(ok.exception.args[0], timeout)
|
||||
|
||||
self._bot._request = _request
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
|
Loading…
Reference in a new issue