2015-11-06 00:23:34 +01:00
|
|
|
#!/usr/bin/env python
|
|
|
|
#
|
|
|
|
# A library that provides a Python interface to the Telegram Bot API
|
2020-02-02 22:08:54 +01:00
|
|
|
# Copyright (C) 2015-2020
|
2016-01-05 14:12:03 +01:00
|
|
|
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
2015-11-06 00:23:34 +01:00
|
|
|
#
|
|
|
|
# This program is free software: you can redistribute it and/or modify
|
2017-08-11 23:58:41 +02:00
|
|
|
# it under the terms of the GNU Lesser Public License as published by
|
2015-11-06 00:23:34 +01:00
|
|
|
# the Free Software Foundation, either version 3 of the License, or
|
|
|
|
# (at your option) any later version.
|
|
|
|
#
|
|
|
|
# This program is distributed in the hope that it will be useful,
|
|
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
2017-08-11 23:58:41 +02:00
|
|
|
# GNU Lesser Public License for more details.
|
2015-11-06 00:23:34 +01:00
|
|
|
#
|
2017-08-11 23:58:41 +02:00
|
|
|
# You should have received a copy of the GNU Lesser Public License
|
2015-11-06 00:23:34 +01:00
|
|
|
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
2018-01-09 16:54:07 +01:00
|
|
|
import logging
|
2017-08-11 23:58:41 +02:00
|
|
|
import os
|
2015-11-24 14:57:54 +01:00
|
|
|
import signal
|
2016-04-24 04:11:25 +02:00
|
|
|
import sys
|
2020-03-09 22:13:16 +01:00
|
|
|
import asyncio
|
2020-02-07 17:32:38 +01:00
|
|
|
from flaky import flaky
|
2018-01-09 16:54:07 +01:00
|
|
|
from functools import partial
|
2016-09-06 15:38:07 +02:00
|
|
|
from queue import Queue
|
2016-04-27 00:28:21 +02:00
|
|
|
from random import randrange
|
2018-03-02 22:11:16 +01:00
|
|
|
from threading import Thread, Event
|
2017-08-11 23:58:41 +02:00
|
|
|
from time import sleep
|
2015-11-06 00:23:34 +01:00
|
|
|
|
2020-06-15 18:20:51 +02:00
|
|
|
from urllib.request import Request, urlopen
|
|
|
|
from urllib.error import HTTPError
|
2015-11-06 00:23:34 +01:00
|
|
|
|
2017-08-11 23:58:41 +02:00
|
|
|
import pytest
|
2015-11-06 00:23:34 +01:00
|
|
|
|
2017-08-11 23:58:41 +02:00
|
|
|
from telegram import TelegramError, Message, User, Chat, Update, Bot
|
2018-03-02 22:11:16 +01:00
|
|
|
from telegram.error import Unauthorized, InvalidToken, TimedOut, RetryAfter
|
2020-05-01 20:27:34 +02:00
|
|
|
from telegram.ext import Updater, Dispatcher, DictPersistence
|
2015-11-06 00:23:34 +01:00
|
|
|
|
2017-08-11 23:58:41 +02:00
|
|
|
signalskip = pytest.mark.skipif(sys.platform == 'win32',
|
|
|
|
reason='Can\'t send signals without stopping '
|
|
|
|
'whole process on windows')
|
2016-09-06 15:38:07 +02:00
|
|
|
|
|
|
|
|
2020-03-09 22:13:16 +01:00
|
|
|
if sys.platform.startswith("win") and sys.version_info >= (3, 8):
|
|
|
|
"""set default asyncio policy to be compatible with tornado
|
|
|
|
Tornado 6 (at least) is not compatible with the default
|
|
|
|
asyncio implementation on Windows
|
|
|
|
Pick the older SelectorEventLoopPolicy on Windows
|
|
|
|
if the known-incompatible default policy is in use.
|
|
|
|
do this as early as possible to make it a low priority and overrideable
|
|
|
|
ref: https://github.com/tornadoweb/tornado/issues/2608
|
|
|
|
TODO: if/when tornado supports the defaults in asyncio,
|
|
|
|
remove and bump tornado requirement for py38
|
|
|
|
Copied from https://github.com/ipython/ipykernel/pull/456/
|
|
|
|
"""
|
|
|
|
try:
|
|
|
|
from asyncio import (
|
|
|
|
WindowsProactorEventLoopPolicy,
|
|
|
|
WindowsSelectorEventLoopPolicy,
|
|
|
|
)
|
|
|
|
except ImportError:
|
|
|
|
pass
|
|
|
|
# not affected
|
|
|
|
else:
|
|
|
|
if type(asyncio.get_event_loop_policy()) is WindowsProactorEventLoopPolicy:
|
|
|
|
# WindowsProactorEventLoopPolicy is not compatible with tornado 6
|
|
|
|
# fallback to the pre-3.8 default of Selector
|
|
|
|
asyncio.set_event_loop_policy(WindowsSelectorEventLoopPolicy())
|
|
|
|
|
|
|
|
|
2020-06-15 18:20:51 +02:00
|
|
|
class TestUpdater:
|
2017-08-11 23:58:41 +02:00
|
|
|
message_count = 0
|
|
|
|
received = None
|
|
|
|
attempts = 0
|
2018-03-02 22:11:16 +01:00
|
|
|
err_handler_called = Event()
|
|
|
|
cb_handler_called = Event()
|
2020-06-24 00:25:58 +02:00
|
|
|
offset = 0
|
2015-11-21 15:45:45 +01:00
|
|
|
|
2017-08-11 23:58:41 +02:00
|
|
|
@pytest.fixture(autouse=True)
|
2015-11-24 20:34:38 +01:00
|
|
|
def reset(self):
|
|
|
|
self.message_count = 0
|
2017-08-11 23:58:41 +02:00
|
|
|
self.received = None
|
|
|
|
self.attempts = 0
|
2018-03-02 22:11:16 +01:00
|
|
|
self.err_handler_called.clear()
|
|
|
|
self.cb_handler_called.clear()
|
2016-04-21 13:07:44 +02:00
|
|
|
|
2017-08-11 23:58:41 +02:00
|
|
|
def error_handler(self, bot, update, error):
|
|
|
|
self.received = error.message
|
2018-03-02 22:11:16 +01:00
|
|
|
self.err_handler_called.set()
|
2015-11-21 19:35:24 +01:00
|
|
|
|
2017-08-11 23:58:41 +02:00
|
|
|
def callback(self, bot, update):
|
|
|
|
self.received = update.message.text
|
2018-03-02 22:11:16 +01:00
|
|
|
self.cb_handler_called.set()
|
2016-09-06 15:38:07 +02:00
|
|
|
|
2018-03-02 22:11:16 +01:00
|
|
|
@pytest.mark.parametrize(('error',),
|
|
|
|
argvalues=[(TelegramError('Test Error 2'),),
|
|
|
|
(Unauthorized('Test Unauthorized'),)],
|
|
|
|
ids=('TelegramError', 'Unauthorized'))
|
|
|
|
def test_get_updates_normal_err(self, monkeypatch, updater, error):
|
2017-08-11 23:58:41 +02:00
|
|
|
def test(*args, **kwargs):
|
2018-03-02 22:11:16 +01:00
|
|
|
raise error
|
|
|
|
|
2020-02-06 11:22:56 +01:00
|
|
|
monkeypatch.setattr(updater.bot, 'get_updates', test)
|
|
|
|
monkeypatch.setattr(updater.bot, 'set_webhook', lambda *args, **kwargs: True)
|
2018-03-02 22:11:16 +01:00
|
|
|
updater.dispatcher.add_error_handler(self.error_handler)
|
|
|
|
updater.start_polling(0.01)
|
|
|
|
|
|
|
|
# Make sure that the error handler was called
|
|
|
|
self.err_handler_called.wait()
|
|
|
|
assert self.received == error.message
|
|
|
|
|
|
|
|
# Make sure that Updater polling thread keeps running
|
|
|
|
self.err_handler_called.clear()
|
|
|
|
self.err_handler_called.wait()
|
|
|
|
|
|
|
|
def test_get_updates_bailout_err(self, monkeypatch, updater, caplog):
|
|
|
|
error = InvalidToken()
|
|
|
|
|
|
|
|
def test(*args, **kwargs):
|
|
|
|
raise error
|
|
|
|
|
|
|
|
with caplog.at_level(logging.DEBUG):
|
2020-02-06 11:22:56 +01:00
|
|
|
monkeypatch.setattr(updater.bot, 'get_updates', test)
|
|
|
|
monkeypatch.setattr(updater.bot, 'set_webhook', lambda *args, **kwargs: True)
|
2018-03-02 22:11:16 +01:00
|
|
|
updater.dispatcher.add_error_handler(self.error_handler)
|
|
|
|
updater.start_polling(0.01)
|
2018-09-07 16:05:45 +02:00
|
|
|
assert self.err_handler_called.wait(1) is not True
|
2018-03-02 22:11:16 +01:00
|
|
|
|
2018-10-02 09:00:38 +02:00
|
|
|
sleep(1)
|
|
|
|
# NOTE: This test might hit a race condition and fail (though the 1 seconds delay above
|
2018-03-02 22:11:16 +01:00
|
|
|
# should work around it).
|
|
|
|
# NOTE: Checking Updater.running is problematic because it is not set to False when there's
|
|
|
|
# an unhandled exception.
|
|
|
|
# TODO: We should have a way to poll Updater status and decide if it's running or not.
|
2019-08-23 22:57:08 +02:00
|
|
|
import pprint
|
|
|
|
pprint.pprint([rec.getMessage() for rec in caplog.get_records('call')])
|
|
|
|
assert any('unhandled exception in Bot:{}:updater'.format(updater.bot.id) in
|
|
|
|
rec.getMessage() for rec in caplog.get_records('call'))
|
2018-03-02 22:11:16 +01:00
|
|
|
|
|
|
|
@pytest.mark.parametrize(('error',),
|
|
|
|
argvalues=[(RetryAfter(0.01),),
|
|
|
|
(TimedOut(),)],
|
|
|
|
ids=('RetryAfter', 'TimedOut'))
|
|
|
|
def test_get_updates_retries(self, monkeypatch, updater, error):
|
|
|
|
event = Event()
|
|
|
|
|
|
|
|
def test(*args, **kwargs):
|
|
|
|
event.set()
|
|
|
|
raise error
|
2016-08-06 14:47:45 +02:00
|
|
|
|
2020-02-06 11:22:56 +01:00
|
|
|
monkeypatch.setattr(updater.bot, 'get_updates', test)
|
|
|
|
monkeypatch.setattr(updater.bot, 'set_webhook', lambda *args, **kwargs: True)
|
2017-08-11 23:58:41 +02:00
|
|
|
updater.dispatcher.add_error_handler(self.error_handler)
|
|
|
|
updater.start_polling(0.01)
|
2018-03-02 22:11:16 +01:00
|
|
|
|
|
|
|
# Make sure that get_updates was called, but not the error handler
|
|
|
|
event.wait()
|
|
|
|
assert self.err_handler_called.wait(0.5) is not True
|
|
|
|
assert self.received != error.message
|
|
|
|
|
|
|
|
# Make sure that Updater polling thread keeps running
|
|
|
|
event.clear()
|
|
|
|
event.wait()
|
|
|
|
assert self.err_handler_called.wait(0.5) is not True
|
2016-08-06 14:47:45 +02:00
|
|
|
|
2017-08-11 23:58:41 +02:00
|
|
|
def test_webhook(self, monkeypatch, updater):
|
|
|
|
q = Queue()
|
2020-02-06 11:22:56 +01:00
|
|
|
monkeypatch.setattr(updater.bot, 'set_webhook', lambda *args, **kwargs: True)
|
|
|
|
monkeypatch.setattr(updater.bot, 'delete_webhook', lambda *args, **kwargs: True)
|
2017-08-11 23:58:41 +02:00
|
|
|
monkeypatch.setattr('telegram.ext.Dispatcher.process_update', lambda _, u: q.put(u))
|
2015-11-21 23:22:41 +01:00
|
|
|
|
2016-02-19 16:37:38 +01:00
|
|
|
ip = '127.0.0.1'
|
2020-02-23 22:04:56 +01:00
|
|
|
port = randrange(1024, 49152) # Select random port
|
2017-08-11 23:58:41 +02:00
|
|
|
updater.start_webhook(
|
2016-08-26 11:17:05 +02:00
|
|
|
ip,
|
|
|
|
port,
|
2018-09-08 22:25:48 +02:00
|
|
|
url_path='TOKEN')
|
2017-08-11 23:58:41 +02:00
|
|
|
sleep(.2)
|
|
|
|
try:
|
|
|
|
# Now, we send an update to the server via urlopen
|
2018-02-19 11:41:38 +01:00
|
|
|
update = Update(1, message=Message(1, User(1, '', False), None, Chat(1, ''),
|
|
|
|
text='Webhook'))
|
2017-08-11 23:58:41 +02:00
|
|
|
self._send_webhook_msg(ip, port, update.to_json(), 'TOKEN')
|
|
|
|
sleep(.2)
|
|
|
|
assert q.get(False) == update
|
2015-11-21 21:22:58 +01:00
|
|
|
|
2018-09-08 22:25:48 +02:00
|
|
|
# Returns 404 if path is incorrect
|
|
|
|
with pytest.raises(HTTPError) as excinfo:
|
|
|
|
self._send_webhook_msg(ip, port, None, 'webookhandler.py')
|
|
|
|
assert excinfo.value.code == 404
|
2015-11-24 19:35:02 +01:00
|
|
|
|
2018-09-08 22:25:48 +02:00
|
|
|
with pytest.raises(HTTPError) as excinfo:
|
|
|
|
self._send_webhook_msg(ip, port, None, 'webookhandler.py',
|
|
|
|
get_method=lambda: 'HEAD')
|
|
|
|
assert excinfo.value.code == 404
|
2015-11-24 19:35:02 +01:00
|
|
|
|
2017-08-11 23:58:41 +02:00
|
|
|
# Test multiple shutdown() calls
|
|
|
|
updater.httpd.shutdown()
|
|
|
|
finally:
|
|
|
|
updater.httpd.shutdown()
|
2018-09-08 22:25:48 +02:00
|
|
|
sleep(.2)
|
|
|
|
assert not updater.httpd.is_running
|
|
|
|
updater.stop()
|
|
|
|
|
|
|
|
def test_webhook_ssl(self, monkeypatch, updater):
|
2020-02-06 11:22:56 +01:00
|
|
|
monkeypatch.setattr(updater.bot, 'set_webhook', lambda *args, **kwargs: True)
|
|
|
|
monkeypatch.setattr(updater.bot, 'delete_webhook', lambda *args, **kwargs: True)
|
2018-09-08 22:25:48 +02:00
|
|
|
ip = '127.0.0.1'
|
2020-02-23 22:04:56 +01:00
|
|
|
port = randrange(1024, 49152) # Select random port
|
2018-09-08 22:25:48 +02:00
|
|
|
tg_err = False
|
|
|
|
try:
|
|
|
|
updater._start_webhook(
|
|
|
|
ip,
|
|
|
|
port,
|
|
|
|
url_path='TOKEN',
|
|
|
|
cert='./tests/test_updater.py',
|
|
|
|
key='./tests/test_updater.py',
|
|
|
|
bootstrap_retries=0,
|
|
|
|
clean=False,
|
|
|
|
webhook_url=None,
|
|
|
|
allowed_updates=None)
|
|
|
|
except TelegramError:
|
|
|
|
tg_err = True
|
|
|
|
assert tg_err
|
2015-11-24 19:35:02 +01:00
|
|
|
|
2017-08-11 23:58:41 +02:00
|
|
|
def test_webhook_no_ssl(self, monkeypatch, updater):
|
|
|
|
q = Queue()
|
2020-02-06 11:22:56 +01:00
|
|
|
monkeypatch.setattr(updater.bot, 'set_webhook', lambda *args, **kwargs: True)
|
|
|
|
monkeypatch.setattr(updater.bot, 'delete_webhook', lambda *args, **kwargs: True)
|
2017-08-11 23:58:41 +02:00
|
|
|
monkeypatch.setattr('telegram.ext.Dispatcher.process_update', lambda _, u: q.put(u))
|
2015-11-30 23:00:56 +01:00
|
|
|
|
2016-02-19 16:37:38 +01:00
|
|
|
ip = '127.0.0.1'
|
2020-02-23 22:04:56 +01:00
|
|
|
port = randrange(1024, 49152) # Select random port
|
2017-08-11 23:58:41 +02:00
|
|
|
updater.start_webhook(ip, port, webhook_url=None)
|
|
|
|
sleep(.2)
|
2015-11-30 23:00:56 +01:00
|
|
|
|
|
|
|
# Now, we send an update to the server via urlopen
|
2018-02-19 11:41:38 +01:00
|
|
|
update = Update(1, message=Message(1, User(1, '', False), None, Chat(1, ''),
|
|
|
|
text='Webhook 2'))
|
2016-02-19 16:37:38 +01:00
|
|
|
self._send_webhook_msg(ip, port, update.to_json())
|
2017-08-11 23:58:41 +02:00
|
|
|
sleep(.2)
|
|
|
|
assert q.get(False) == update
|
2018-09-08 22:25:48 +02:00
|
|
|
updater.stop()
|
2017-08-11 23:58:41 +02:00
|
|
|
|
2020-02-06 11:22:56 +01:00
|
|
|
def test_webhook_default_quote(self, monkeypatch, updater):
|
|
|
|
updater._default_quote = True
|
|
|
|
q = Queue()
|
|
|
|
monkeypatch.setattr(updater.bot, 'set_webhook', lambda *args, **kwargs: True)
|
|
|
|
monkeypatch.setattr(updater.bot, 'delete_webhook', lambda *args, **kwargs: True)
|
|
|
|
monkeypatch.setattr('telegram.ext.Dispatcher.process_update', lambda _, u: q.put(u))
|
|
|
|
|
|
|
|
ip = '127.0.0.1'
|
2020-02-23 22:04:56 +01:00
|
|
|
port = randrange(1024, 49152) # Select random port
|
2020-02-06 11:22:56 +01:00
|
|
|
updater.start_webhook(
|
|
|
|
ip,
|
|
|
|
port,
|
|
|
|
url_path='TOKEN')
|
|
|
|
sleep(.2)
|
|
|
|
|
|
|
|
# Now, we send an update to the server via urlopen
|
|
|
|
update = Update(1, message=Message(1, User(1, '', False), None, Chat(1, ''),
|
|
|
|
text='Webhook'))
|
|
|
|
self._send_webhook_msg(ip, port, update.to_json(), 'TOKEN')
|
|
|
|
sleep(.2)
|
|
|
|
# assert q.get(False) == update
|
|
|
|
assert q.get(False).message.default_quote is True
|
|
|
|
updater.stop()
|
|
|
|
|
2020-03-09 22:13:16 +01:00
|
|
|
@pytest.mark.skipif(not (sys.platform.startswith("win") and sys.version_info >= (3, 8)),
|
|
|
|
reason="only relevant on win with py>=3.8")
|
|
|
|
def test_webhook_tornado_win_py38_workaround(self, updater, monkeypatch):
|
|
|
|
updater._default_quote = True
|
|
|
|
q = Queue()
|
|
|
|
monkeypatch.setattr(updater.bot, 'set_webhook', lambda *args, **kwargs: True)
|
|
|
|
monkeypatch.setattr(updater.bot, 'delete_webhook', lambda *args, **kwargs: True)
|
|
|
|
monkeypatch.setattr('telegram.ext.Dispatcher.process_update', lambda _, u: q.put(u))
|
|
|
|
|
|
|
|
ip = '127.0.0.1'
|
|
|
|
port = randrange(1024, 49152) # Select random port
|
|
|
|
updater.start_webhook(
|
|
|
|
ip,
|
|
|
|
port,
|
|
|
|
url_path='TOKEN')
|
|
|
|
sleep(.2)
|
|
|
|
|
|
|
|
try:
|
|
|
|
from asyncio import (WindowsSelectorEventLoopPolicy)
|
|
|
|
except ImportError:
|
|
|
|
pass
|
|
|
|
# not affected
|
|
|
|
else:
|
|
|
|
assert isinstance(asyncio.get_event_loop_policy(), WindowsSelectorEventLoopPolicy)
|
|
|
|
|
|
|
|
updater.stop()
|
|
|
|
|
2018-03-02 22:11:16 +01:00
|
|
|
@pytest.mark.parametrize(('error',),
|
|
|
|
argvalues=[(TelegramError(''),)],
|
|
|
|
ids=('TelegramError',))
|
|
|
|
def test_bootstrap_retries_success(self, monkeypatch, updater, error):
|
2017-08-11 23:58:41 +02:00
|
|
|
retries = 2
|
|
|
|
|
2020-02-06 11:22:56 +01:00
|
|
|
def attempt(*args, **kwargs):
|
2017-08-11 23:58:41 +02:00
|
|
|
if self.attempts < retries:
|
|
|
|
self.attempts += 1
|
2018-03-02 22:11:16 +01:00
|
|
|
raise error
|
2017-08-11 23:58:41 +02:00
|
|
|
|
2020-02-06 11:22:56 +01:00
|
|
|
monkeypatch.setattr(updater.bot, 'set_webhook', attempt)
|
2017-08-11 23:58:41 +02:00
|
|
|
|
2018-03-02 22:11:16 +01:00
|
|
|
updater.running = True
|
|
|
|
updater._bootstrap(retries, False, 'path', None, bootstrap_interval=0)
|
2017-08-11 23:58:41 +02:00
|
|
|
assert self.attempts == retries
|
|
|
|
|
|
|
|
@pytest.mark.parametrize(('error', 'attempts'),
|
2018-02-19 11:41:38 +01:00
|
|
|
argvalues=[(TelegramError(''), 2),
|
|
|
|
(Unauthorized(''), 1),
|
|
|
|
(InvalidToken(), 1)],
|
2017-08-11 23:58:41 +02:00
|
|
|
ids=('TelegramError', 'Unauthorized', 'InvalidToken'))
|
|
|
|
def test_bootstrap_retries_error(self, monkeypatch, updater, error, attempts):
|
2016-03-14 00:36:01 +01:00
|
|
|
retries = 1
|
|
|
|
|
2020-02-06 11:22:56 +01:00
|
|
|
def attempt(*args, **kwargs):
|
2017-08-11 23:58:41 +02:00
|
|
|
self.attempts += 1
|
|
|
|
raise error
|
|
|
|
|
2020-02-06 11:22:56 +01:00
|
|
|
monkeypatch.setattr(updater.bot, 'set_webhook', attempt)
|
2016-03-14 00:36:01 +01:00
|
|
|
|
2018-03-02 22:11:16 +01:00
|
|
|
updater.running = True
|
2017-08-11 23:58:41 +02:00
|
|
|
with pytest.raises(type(error)):
|
2018-03-02 22:11:16 +01:00
|
|
|
updater._bootstrap(retries, False, 'path', None, bootstrap_interval=0)
|
2017-08-11 23:58:41 +02:00
|
|
|
assert self.attempts == attempts
|
2016-02-19 16:37:38 +01:00
|
|
|
|
2020-06-24 00:25:58 +02:00
|
|
|
def test_bootstrap_clean_updates(self, monkeypatch, updater):
|
|
|
|
clean = True
|
|
|
|
expected_id = 4
|
|
|
|
self.offset = 0
|
|
|
|
|
|
|
|
def get_updates(*args, **kwargs):
|
|
|
|
# we're hitting this func twice
|
|
|
|
# 1. no args, return list of updates
|
|
|
|
# 2. with 1 arg, int => if int == expected_id => test successful
|
|
|
|
|
|
|
|
# case 2
|
|
|
|
# 2nd call from bootstrap____clean
|
|
|
|
# we should be called with offset = 4
|
|
|
|
# save value passed in self.offset for assert down below
|
|
|
|
if len(args) > 0:
|
|
|
|
self.offset = int(args[0])
|
|
|
|
return []
|
|
|
|
|
|
|
|
class FakeUpdate():
|
|
|
|
def __init__(self, update_id):
|
|
|
|
self.update_id = update_id
|
|
|
|
|
|
|
|
# case 1
|
|
|
|
# return list of obj's
|
|
|
|
|
|
|
|
# build list of fake updates
|
|
|
|
# returns list of 4 objects with
|
|
|
|
# update_id's 0, 1, 2 and 3
|
|
|
|
return [FakeUpdate(i) for i in range(0, expected_id)]
|
|
|
|
|
|
|
|
monkeypatch.setattr(updater.bot, 'get_updates', get_updates)
|
|
|
|
|
|
|
|
updater.running = True
|
|
|
|
updater._bootstrap(1, clean, None, None, bootstrap_interval=0)
|
|
|
|
assert self.offset == expected_id
|
|
|
|
|
2020-02-07 17:32:38 +01:00
|
|
|
@flaky(3, 1)
|
2017-08-11 23:58:41 +02:00
|
|
|
def test_webhook_invalid_posts(self, updater):
|
2016-02-19 16:37:38 +01:00
|
|
|
ip = '127.0.0.1'
|
|
|
|
port = randrange(1024, 49152) # select random port for travis
|
2016-08-26 11:17:05 +02:00
|
|
|
thr = Thread(
|
2017-08-11 23:58:41 +02:00
|
|
|
target=updater._start_webhook,
|
2017-03-25 12:46:37 +01:00
|
|
|
args=(ip, port, '', None, None, 0, False, None, None))
|
2016-02-19 16:37:38 +01:00
|
|
|
thr.start()
|
2015-11-30 23:00:56 +01:00
|
|
|
|
2017-08-11 23:58:41 +02:00
|
|
|
sleep(.2)
|
2016-02-19 16:37:38 +01:00
|
|
|
|
|
|
|
try:
|
2017-08-11 23:58:41 +02:00
|
|
|
with pytest.raises(HTTPError) as excinfo:
|
|
|
|
self._send_webhook_msg(ip, port, '<root><bla>data</bla></root>',
|
|
|
|
content_type='application/xml')
|
|
|
|
assert excinfo.value.code == 403
|
2016-02-19 16:37:38 +01:00
|
|
|
|
2017-08-11 23:58:41 +02:00
|
|
|
with pytest.raises(HTTPError) as excinfo:
|
2016-05-15 02:52:35 +02:00
|
|
|
self._send_webhook_msg(ip, port, 'dummy-payload', content_len=-2)
|
2018-09-08 22:25:48 +02:00
|
|
|
assert excinfo.value.code == 500
|
2016-02-19 16:37:38 +01:00
|
|
|
|
|
|
|
# TODO: prevent urllib or the underlying from adding content-length
|
2017-08-11 23:58:41 +02:00
|
|
|
# with pytest.raises(HTTPError) as excinfo:
|
|
|
|
# self._send_webhook_msg(ip, port, 'dummy-payload', content_len=None)
|
|
|
|
# assert excinfo.value.code == 411
|
2016-02-19 16:37:38 +01:00
|
|
|
|
2018-02-19 11:41:38 +01:00
|
|
|
with pytest.raises(HTTPError):
|
2016-05-15 02:52:35 +02:00
|
|
|
self._send_webhook_msg(ip, port, 'dummy-payload', content_len='not-a-number')
|
2018-09-08 22:25:48 +02:00
|
|
|
assert excinfo.value.code == 500
|
2016-02-19 16:37:38 +01:00
|
|
|
|
|
|
|
finally:
|
2017-08-11 23:58:41 +02:00
|
|
|
updater.httpd.shutdown()
|
2016-02-19 16:37:38 +01:00
|
|
|
thr.join()
|
|
|
|
|
2016-04-26 23:23:15 +02:00
|
|
|
def _send_webhook_msg(self,
|
|
|
|
ip,
|
|
|
|
port,
|
|
|
|
payload_str,
|
|
|
|
url_path='',
|
|
|
|
content_len=-1,
|
|
|
|
content_type='application/json',
|
2016-02-19 16:37:38 +01:00
|
|
|
get_method=None):
|
2017-08-11 23:58:41 +02:00
|
|
|
headers = {'content-type': content_type, }
|
2015-11-30 23:00:56 +01:00
|
|
|
|
2016-02-19 16:37:38 +01:00
|
|
|
if not payload_str:
|
|
|
|
content_len = None
|
|
|
|
payload = None
|
|
|
|
else:
|
|
|
|
payload = bytes(payload_str, encoding='utf-8')
|
2015-11-30 23:00:56 +01:00
|
|
|
|
2016-02-19 16:37:38 +01:00
|
|
|
if content_len == -1:
|
|
|
|
content_len = len(payload)
|
|
|
|
|
|
|
|
if content_len is not None:
|
|
|
|
headers['content-length'] = str(content_len)
|
|
|
|
|
2016-05-15 02:52:35 +02:00
|
|
|
url = 'http://{ip}:{port}/{path}'.format(ip=ip, port=port, path=url_path)
|
2016-02-19 16:37:38 +01:00
|
|
|
|
|
|
|
req = Request(url, data=payload, headers=headers)
|
|
|
|
|
|
|
|
if get_method is not None:
|
|
|
|
req.get_method = get_method
|
|
|
|
|
|
|
|
return urlopen(req)
|
2015-11-30 23:00:56 +01:00
|
|
|
|
2018-01-09 16:54:07 +01:00
|
|
|
def signal_sender(self, updater):
|
2017-08-11 23:58:41 +02:00
|
|
|
sleep(0.2)
|
2018-01-09 16:54:07 +01:00
|
|
|
while not updater.running:
|
|
|
|
sleep(0.2)
|
|
|
|
|
2015-11-24 14:57:54 +01:00
|
|
|
os.kill(os.getpid(), signal.SIGTERM)
|
|
|
|
|
2017-08-11 23:58:41 +02:00
|
|
|
@signalskip
|
2018-01-09 16:54:07 +01:00
|
|
|
def test_idle(self, updater, caplog):
|
2017-08-11 23:58:41 +02:00
|
|
|
updater.start_polling(0.01)
|
2018-01-09 16:54:07 +01:00
|
|
|
Thread(target=partial(self.signal_sender, updater=updater)).start()
|
|
|
|
|
|
|
|
with caplog.at_level(logging.INFO):
|
|
|
|
updater.idle()
|
|
|
|
|
|
|
|
rec = caplog.records[-1]
|
|
|
|
assert rec.msg.startswith('Received signal {}'.format(signal.SIGTERM))
|
|
|
|
assert rec.levelname == 'INFO'
|
|
|
|
|
2015-11-24 14:57:54 +01:00
|
|
|
# If we get this far, idle() ran through
|
2017-08-11 23:58:41 +02:00
|
|
|
sleep(.5)
|
|
|
|
assert updater.running is False
|
2015-11-24 14:57:54 +01:00
|
|
|
|
2017-08-11 23:58:41 +02:00
|
|
|
@signalskip
|
|
|
|
def test_user_signal(self, updater):
|
|
|
|
temp_var = {'a': 0}
|
2017-03-26 14:36:13 +02:00
|
|
|
|
2017-08-11 23:58:41 +02:00
|
|
|
def user_signal_inc(signum, frame):
|
|
|
|
temp_var['a'] = 1
|
2017-03-26 14:36:13 +02:00
|
|
|
|
2017-08-11 23:58:41 +02:00
|
|
|
updater.user_sig_handler = user_signal_inc
|
|
|
|
updater.start_polling(0.01)
|
2018-01-09 16:54:07 +01:00
|
|
|
Thread(target=partial(self.signal_sender, updater=updater)).start()
|
2017-08-11 23:58:41 +02:00
|
|
|
updater.idle()
|
2017-03-26 14:36:13 +02:00
|
|
|
# If we get this far, idle() ran through
|
2017-08-11 23:58:41 +02:00
|
|
|
sleep(.5)
|
|
|
|
assert updater.running is False
|
|
|
|
assert temp_var['a'] != 0
|
2017-03-26 14:36:13 +02:00
|
|
|
|
2017-08-11 23:58:41 +02:00
|
|
|
def test_create_bot(self):
|
|
|
|
updater = Updater('123:abcd')
|
|
|
|
assert updater.bot is not None
|
2015-11-24 14:57:54 +01:00
|
|
|
|
2017-08-11 23:58:41 +02:00
|
|
|
def test_mutual_exclude_token_bot(self):
|
2015-12-26 18:32:36 +01:00
|
|
|
bot = Bot('123:zyxw')
|
2017-08-11 23:58:41 +02:00
|
|
|
with pytest.raises(ValueError):
|
|
|
|
Updater(token='123:abcd', bot=bot)
|
2016-04-24 04:11:25 +02:00
|
|
|
|
2020-01-26 20:59:47 +01:00
|
|
|
def test_no_token_or_bot_or_dispatcher(self):
|
2017-08-11 23:58:41 +02:00
|
|
|
with pytest.raises(ValueError):
|
|
|
|
Updater()
|
Bot API 4.0 (#1168)
Telegram Passport (#1174):
- Add full support for telegram passport.
- New types: PassportData, PassportFile, EncryptedPassportElement, EncryptedCredentials, PassportElementError, PassportElementErrorDataField, PassportElementErrorFrontSide, PassportElementErrorReverseSide, PassportElementErrorSelfie, PassportElementErrorFile and PassportElementErrorFiles.
- New bot method: set_passport_data_errors
- New filter: Filters.passport_data
- Field passport_data field on Message
- PassportData is automagically decrypted when you specify your private key when creating Updater or Bot.
- PassportFiles is also automagically decrypted as you download/retrieve them.
- See new passportbot.py example for details on how to use, or go to our telegram passport wiki page for more info
- NOTE: Passport decryption requires new dependency `cryptography`.
Inputfile rework (#1184):
- Change how Inputfile is handled internally
- This allows support for specifying the thumbnails of photos and videos using the thumb= argument in the different send_ methods.
- Also allows Bot.send_media_group to actually finally send more than one media.
- Add thumb to Audio, Video and Videonote
- Add Bot.edit_message_media together with InputMediaAnimation, InputMediaAudio, and inputMediaDocument.
Other Bot API 4.0 changes:
- Add forusquare_type to Venue, InlineQueryResultVenue, InputVenueMessageContent, and Bot.send_venue. (#1170)
- Add vCard support by adding vcard field to Contact, InlineQueryResultContact, InputContactMessageContent, and Bot.send_contact. (#1166)
- Support new message entities: CASHTAG and PHONE_NUMBER. (#1179)
- Cashtag seems to be things like $USD and $GBP, but it seems telegram doesn't currently send them to bots.
- Phone number also seems to have limited support for now
- Add Bot.send_animation, add width, height, and duration to Animation, and add Filters.animation. (#1172)
Co-authored-by: Jasmin Bom <jsmnbom@gmail.com>
Co-authored-by: code1mountain <32801117+code1mountain@users.noreply.github.com>
Co-authored-by: Eldinnie <pieter.schutz+github@gmail.com>
Co-authored-by: mathefreak1 <mathefreak@hi2.in>
2018-08-29 14:18:58 +02:00
|
|
|
|
|
|
|
def test_mutual_exclude_bot_private_key(self):
|
|
|
|
bot = Bot('123:zyxw')
|
|
|
|
with pytest.raises(ValueError):
|
|
|
|
Updater(bot=bot, private_key=b'key')
|
2020-01-26 20:59:47 +01:00
|
|
|
|
|
|
|
def test_mutual_exclude_bot_dispatcher(self):
|
|
|
|
dispatcher = Dispatcher(None, None)
|
|
|
|
bot = Bot('123:zyxw')
|
|
|
|
with pytest.raises(ValueError):
|
|
|
|
Updater(bot=bot, dispatcher=dispatcher)
|
|
|
|
|
|
|
|
def test_mutual_exclude_persistence_dispatcher(self):
|
|
|
|
dispatcher = Dispatcher(None, None)
|
2020-05-01 20:27:34 +02:00
|
|
|
persistence = DictPersistence()
|
2020-01-26 20:59:47 +01:00
|
|
|
with pytest.raises(ValueError):
|
|
|
|
Updater(dispatcher=dispatcher, persistence=persistence)
|
|
|
|
|
|
|
|
def test_mutual_exclude_workers_dispatcher(self):
|
|
|
|
dispatcher = Dispatcher(None, None)
|
|
|
|
with pytest.raises(ValueError):
|
|
|
|
Updater(dispatcher=dispatcher, workers=8)
|
|
|
|
|
|
|
|
def test_mutual_exclude_use_context_dispatcher(self):
|
|
|
|
dispatcher = Dispatcher(None, None)
|
|
|
|
use_context = not dispatcher.use_context
|
|
|
|
with pytest.raises(ValueError):
|
|
|
|
Updater(dispatcher=dispatcher, use_context=use_context)
|