Skip to content
2 changes: 1 addition & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ make the development of bots easy and straightforward. These classes are contain
Telegram API support
====================

As of **3. Oct 2016**, all types and methods of the Telegram Bot API are supported.
As of **4. Dec 2016**, all types and methods of the Telegram Bot API are supported.

==========
Installing
Expand Down
83 changes: 77 additions & 6 deletions telegram/bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -1236,6 +1236,7 @@ def getUpdates(self,
timeout=0,
network_delay=None,
read_latency=2.,
allowed_updates=None,
**kwargs):
"""Use this method to receive incoming updates using long polling.

Expand All @@ -1247,6 +1248,14 @@ def getUpdates(self,
higher than its update_id.
limit (Optional[int]): Limits the number of updates to be retrieved. Values between
1-100 are accepted. Defaults to 100.
allowed_updates (Optional[list[str]]): List the types of updates you want your bot to
receive. For example, specify
``["message", "edited_channel_post", "callback_query"]`` to only receive updates of
these types. See ``telegram.Update`` for a complete list of available update types.
Specify an empty list to receive all updates regardless of type (default). If not
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[int]): Timeout in seconds for long polling. Defaults to 0, i.e. usual
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
Expand Down Expand Up @@ -1286,6 +1295,8 @@ def getUpdates(self,
data['offset'] = offset
if limit:
data['limit'] = limit
if allowed_updates is not None:
data['allowed_updates'] = allowed_updates

# Ideally we'd use an aggressive read timeout for the polling. However,
# * Short polling should return within 2 seconds.
Expand All @@ -1302,15 +1313,34 @@ def getUpdates(self,
return [Update.de_json(u, self) for u in result]

@log
def setWebhook(self, webhook_url=None, certificate=None, timeout=None, **kwargs):
def setWebhook(self,
url=None,
certificate=None,
timeout=None,
max_connections=40,
allowed_updates=None,
**kwargs):
"""Use this method to specify a url and receive incoming updates via an outgoing webhook.
Whenever there is an update for the bot, we will send an HTTPS POST request to the
specified url, containing a JSON-serialized Update. In case of an unsuccessful request, we
will give up after a reasonable amount of attempts.

Args:
webhook_url: HTTPS url to send updates to. Use an empty string to remove webhook
integration.
url: HTTPS url to send updates to. Use an empty string to remove webhook integration.
certificate (file): Upload your public key certificate so that the root certificate in
use can be checked.
max_connections (Optional[int]): Maximum allowed number of simultaneous HTTPS
connections to the webhook for update delivery, 1-100. Defaults to 40. Use lower
values to limit the load on your bot's server, and higher values to increase your
bot's throughput.
allowed_updates (Optional[list[str]]): List the types of updates you want your bot to
receive. For example, specify
``["message", "edited_channel_post", "callback_query"]`` to only receive updates of
these types. See ``telegram.Update`` for a complete list of available update types.
Specify an empty list to receive all updates regardless of type (default). If not
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[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).
Expand All @@ -1323,14 +1353,54 @@ def setWebhook(self, webhook_url=None, certificate=None, timeout=None, **kwargs)
:class:`telegram.TelegramError`

"""
url = '{0}/setWebhook'.format(self.base_url)
url_ = '{0}/setWebhook'.format(self.base_url)

# Backwards-compatibility: 'url' used to be named 'webhook_url'
if 'webhook_url' in kwargs:
warnings.warn("The 'webhook_url' parameter has been renamed to 'url' in accordance "
"with the API")

if url is not None:
raise ValueError("The parameters 'url' and 'webhook_url' are mutually exclusive")

url = kwargs['webhook_url']
del kwargs['webhook_url']

data = {}

if webhook_url is not None:
data['url'] = webhook_url
if url is not None:
data['url'] = url
if certificate:
data['certificate'] = certificate
if max_connections is not None:
data['max_connections'] = max_connections
if allowed_updates is not None:
data['allowed_updates'] = allowed_updates

result = self._request.post(url_, data, timeout=timeout)

return result

@log
def deleteWebhook(self, timeout=None, **kwargs):
"""Use this method to remove webhook integration if you decide to switch back to
getUpdates. Returns True on success. Requires no parameters.

Args:
timeout (Optional[float]): If this value is specified, use it as the definitive timeout
(in seconds) for urlopen() operations.
**kwargs (dict): Arbitrary keyword arguments.

Returns:
bool: On success, `True` is returned.

Raises:
:class:`telegram.TelegramError`

"""
url = '{0}/deleteWebhook'.format(self.base_url)

data = {}

result = self._request.post(url, data, timeout=timeout)

Expand Down Expand Up @@ -1643,6 +1713,7 @@ def __reduce__(self):
edit_message_reply_markup = editMessageReplyMarkup
get_updates = getUpdates
set_webhook = setWebhook
delete_webhook = deleteWebhook
leave_chat = leaveChat
get_chat = getChat
get_chat_administrators = getChatAdministrators
Expand Down
19 changes: 13 additions & 6 deletions telegram/ext/updater.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,8 @@ def start_polling(self,
network_delay=None,
clean=False,
bootstrap_retries=0,
read_latency=2.):
read_latency=2.,
allowed_updates=None):
"""
Starts polling updates from Telegram.

Expand All @@ -155,6 +156,8 @@ def start_polling(self,
| 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).
Expand All @@ -176,7 +179,7 @@ def start_polling(self,
self.job_queue.start()
self._init_thread(self.dispatcher.start, "dispatcher")
self._init_thread(self._start_polling, "updater", poll_interval, timeout,
read_latency, bootstrap_retries, clean)
read_latency, bootstrap_retries, clean, allowed_updates)

# Return the update queue so the main thread can insert updates
return self.update_queue
Expand Down Expand Up @@ -233,7 +236,8 @@ def start_webhook(self,
# Return the update queue so the main thread can insert updates
return self.update_queue

def _start_polling(self, poll_interval, timeout, read_latency, 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
updates from Telegram and inserts them in the update queue of the
Expand All @@ -248,7 +252,10 @@ def _start_polling(self, poll_interval, timeout, read_latency, bootstrap_retries
while self.running:
try:
updates = self.bot.getUpdates(
self.last_update_id, timeout=timeout, read_latency=read_latency)
self.last_update_id,
timeout=timeout,
read_latency=read_latency,
allowed_updates=allowed_updates)
except RetryAfter as e:
self.logger.info(str(e))
cur_interval = 0.5 + e.retry_after
Expand Down Expand Up @@ -346,11 +353,11 @@ def _bootstrap(self, max_retries, clean, webhook_url, cert=None):
try:
if clean:
# Disable webhook for cleaning
self.bot.setWebhook(webhook_url='')
self.bot.deleteWebhook()
self._clean_updates()
sleep(1)

self.bot.setWebhook(webhook_url=webhook_url, certificate=cert)
self.bot.setWebhook(url=webhook_url, certificate=cert)
except (Unauthorized, InvalidToken):
raise
except TelegramError:
Expand Down
16 changes: 13 additions & 3 deletions telegram/webhookinfo.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,23 @@ class WebhookInfo(TelegramObject):

"""

def __init__(self, url, has_custom_certificate, pending_update_count, **kwargs):
def __init__(self,
url,
has_custom_certificate=None,
pending_update_count=None,
last_error_date=None,
last_error_message=None,
max_connections=None,
allowed_updates=None,
**kwargs):
# Required
self.url = url
self.has_custom_certificate = has_custom_certificate
self.pending_update_count = pending_update_count
self.last_error_date = kwargs.get('last_error_date', '')
self.last_error_message = kwargs.get('last_error_message', '')
self.last_error_date = last_error_date
self.last_error_message = last_error_message
self.max_connections = max_connections
self.allowed_updates = allowed_updates

@staticmethod
def de_json(data, bot):
Expand Down
22 changes: 18 additions & 4 deletions tests/test_bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ def test_sendMessage_no_web_page_preview(self):
@flaky(3, 1)
@timeout(10)
def testGetUpdates(self):
self._bot.delete_webhook() # make sure there is no webhook set if webhook tests failed
updates = self._bot.getUpdates(timeout=1)

if updates:
Expand Down Expand Up @@ -291,7 +292,7 @@ def testGetChatMember(self):

@flaky(3, 1)
@timeout(10)
def test_forward_channel_messgae(self):
def test_forward_channel_message(self):
text = 'test forward message'
msg = self._bot.sendMessage(self._channel_id, text)
self.assertEqual(text, msg.text)
Expand All @@ -301,12 +302,25 @@ def test_forward_channel_messgae(self):

@flaky(3, 1)
@timeout(10)
def test_get_webhook_info(self):
def test_set_webhook_get_webhook_info(self):
url = 'https://python-telegram-bot.org/test/webhook'
self._bot.set_webhook(url)
max_connections = 7
allowed_updates = ['message']
self._bot.set_webhook(url, max_connections=7, allowed_updates=['message'])
info = self._bot.getWebhookInfo()
self._bot.set_webhook('')
self._bot.delete_webhook()
self.assertEqual(url, info.url)
self.assertEqual(max_connections, info.max_connections)
self.assertListEqual(allowed_updates, info.allowed_updates)

@flaky(3, 1)
@timeout(10)
def test_delete_webhook(self):
url = 'https://python-telegram-bot.org/test/webhook'
self._bot.set_webhook(url)
self._bot.delete_webhook()
info = self._bot.getWebhookInfo()
self.assertEqual(info.url, '')

@flaky(3, 1)
@timeout(10)
Expand Down
18 changes: 16 additions & 2 deletions tests/test_updater.py
Original file line number Diff line number Diff line change
Expand Up @@ -801,15 +801,29 @@ def mockUpdate(self, text):

return update

def setWebhook(self, webhook_url=None, certificate=None):
def setWebhook(self, url=None, certificate=None):
if self.bootstrap_retries is None:
return

if self.bootstrap_attempts < self.bootstrap_retries:
self.bootstrap_attempts += 1
raise self.bootstrap_err

def getUpdates(self, offset=None, limit=100, timeout=0, network_delay=None, read_latency=2.):
def deleteWebhook(self):
if self.bootstrap_retries is None:
return

if self.bootstrap_attempts < self.bootstrap_retries:
self.bootstrap_attempts += 1
raise self.bootstrap_err

def getUpdates(self,
offset=None,
limit=100,
timeout=0,
network_delay=None,
read_latency=2.,
allowed_updates=None):

if self.raise_error:
raise TelegramError('Test Error 2')
Expand Down