def _parse(json_data): """Try and parse the JSON returned from Telegram and return an empty dictionary if there is any error. Args: url: urllib.urlopen object Returns: A JSON parsed as Python dict with results. """ decoded_s = json_data.decode('utf-8') try: data = json.loads(decoded_s) except ValueError: raise TelegramError('Invalid server response') if not data.get('ok') and data.get('description'): return data['description'] return data['result']
def show_backups(bot, update): try: bucket = get_env('S3_BUCKET_NAME') backup_path = get_env('S3_BACKUP_PATH') endpoint_url = get_env('ENDPOINT_URL') except NotConfigured as exception: raise TelegramError(str(exception)) def _process(stdout): backups = [] aws_ls = stdout.split('\n') for entry in aws_ls[:-1]: match = re.match('.+backup_(?P<date>[\dT-]{19}Z)_(?P<tag>.+)\.pg_dump$', entry) if match: data = match.groupdict() backups.append(f"{data['date']} - {data['tag']}") backups.reverse() message = '\n'.join(backups[:5]) return message run_and_reply(update, f'aws --endpoint-url={endpoint_url} s3 ls s3://{bucket}{backup_path}', process_func=_process)
def _start_webhook(self, listen, port, url_path, cert, key, bootstrap_retries, clean, webhook_url, allowed_updates): self.logger.debug('Updater thread started (webhook)') use_ssl = cert is not None and key is not None if not url_path.startswith('/'): url_path = '/{0}'.format(url_path) # Create Tornado app instance app = WebhookAppClass(url_path, self.bot, self.update_queue) # Form SSL Context # An SSLError is raised if the private key does not match with the certificate ssl_ctx = None if use_ssl: try: ssl_ctx = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH) ssl_ctx.load_cert_chain(cert, key) except ssl.SSLError: raise TelegramError('SSL Certificate invalid') # Create and start server if asyncio: asyncio.set_event_loop(asyncio.new_event_loop()) self.httpd = WebhookServer(port, app, ssl_ctx) if use_ssl: # DO NOT CHANGE: Only set webhook if SSL is handled by library if not webhook_url: webhook_url = self._gen_webhook_url(listen, port, url_path) self._bootstrap(max_retries=bootstrap_retries, clean=clean, webhook_url=webhook_url, cert=open(cert, 'rb'), allowed_updates=allowed_updates) elif clean: self.logger.warning("cleaning updates is not supported if " "SSL-termination happens elsewhere; skipping") self.httpd.serve_forever()
def _start_webhook(self, host, port, cert, key, listen): self.logger.info('Updater thread started') url_base = "https://%s:%d" % (host, port) url_path = "/%s" % self.bot.token # Remove webhook self.bot.setWebhook(webhook_url=None) # Set webhook self.bot.setWebhook(webhook_url=url_base + url_path, certificate=open(cert, 'rb')) # Start server self.httpd = WebhookServer((listen, port), WebhookHandler, self.update_queue, url_path) # Check SSL-Certificate with openssl, if possible try: DEVNULL = open(os.devnull, 'wb') exit_code = subprocess.call( ["openssl", "x509", "-text", "-noout", "-in", cert], stdout=DEVNULL, stderr=subprocess.STDOUT) except OSError: exit_code = 0 if exit_code is 0: try: self.httpd.socket = ssl.wrap_socket(self.httpd.socket, certfile=cert, keyfile=key, server_side=True) self.httpd.serve_forever(poll_interval=1) except ssl.SSLError as error: self.logger.error(str(error)) finally: self.logger.info('Updater thread stopped') else: raise TelegramError('SSL Certificate invalid')
def start(self): """ Thread target of thread 'dispatcher'. Runs in background and processes the update queue. """ if self.running: self.logger.warning('already running') return if self.__exception_event.is_set(): msg = 'reusing dispatcher after exception event is forbidden' self.logger.error(msg) raise TelegramError(msg) self._init_async_threads(uuid4(), self.workers) self.running = True self.logger.debug('Dispatcher started') while 1: try: # Pop update from update queue. update = self.update_queue.get(True, 1) except Empty: if self.__stop_event.is_set(): self.logger.debug('orderly stopping') break elif self.__exception_event.is_set(): self.logger.critical( 'stopping due to exception in another thread') break continue self.logger.debug('Processing Update: %s' % update) self.process_update(update) self.running = False self.logger.debug('Dispatcher thread stopped')
def __init__(self, data): self.data = data self.boundary = choose_boundary() for t in FILE_TYPES: if t in data: self.input_name = t self.input_file = data.pop(t) break else: raise TelegramError('Unknown inputfile type') if hasattr(self.input_file, 'read'): self.filename = None self.input_file_content = self.input_file.read() if 'filename' in data: self.filename = self.data.pop('filename') elif (hasattr(self.input_file, 'name') and not isinstance(self.input_file.name, int) and # py3 self.input_file.name != '<fdopen>'): # py2 # on py2.7, pylint fails to understand this properly # pylint: disable=E1101 self.filename = os.path.basename(self.input_file.name) try: self.mimetype = self.is_image(self.input_file_content) if not self.filename or '.' not in self.filename: self.filename = self.mimetype.replace('/', '.') except TelegramError: if self.filename: self.mimetype = mimetypes.guess_type( self.filename)[0] or DEFAULT_MIME_TYPE else: self.mimetype = DEFAULT_MIME_TYPE if sys.version_info < (3,): if isinstance(self.filename, unicode): # flake8: noqa pylint: disable=E0602 self.filename = self.filename.encode('utf-8', 'replace')
def test_error_handler_that_raises_errors(self, dp): """ Make sure that errors raised in error handlers don't break the main loop of the dispatcher """ handler_raise_error = MessageHandler(Filters.all, self.callback_raise_error) handler_increase_count = MessageHandler(Filters.all, self.callback_increase_count) error = TelegramError('Unauthorized.') dp.add_error_handler(self.error_handler_raise_error) # From errors caused by handlers dp.add_handler(handler_raise_error) dp.update_queue.put(self.message_update) sleep(.1) # From errors in the update_queue dp.remove_handler(handler_raise_error) dp.add_handler(handler_increase_count) dp.update_queue.put(error) dp.update_queue.put(self.message_update) sleep(.1) assert self.count == 1
def _parse(json_data): """Try and parse the JSON returned from Telegram. Returns: dict: A JSON parsed as Python dict with results - on error this dict will be empty. """ decoded_s = json_data.decode('utf-8') try: data = json.loads(decoded_s) except ValueError: raise TelegramError('Invalid server response') if not data.get('ok'): description = data.get('description') parameters = data.get('parameters') if parameters: migrate_to_chat_id = parameters.get('migrate_to_chat_id') if migrate_to_chat_id: raise ChatMigrated(migrate_to_chat_id) if description: return description return data['result']
def test_flow_stop_in_error_handler(self, dp, bot): passed = [] err = TelegramError('Telegram error') def start1(b, u): passed.append('start1') raise err def start2(b, u): passed.append('start2') def start3(b, u): passed.append('start3') def error(b, u, e): passed.append('error') passed.append(e) raise DispatcherHandlerStop update = Update(1, message=Message(1, None, None, None, text='/start', bot=bot)) # If a TelegramException was caught, an error handler should be called and no further # handlers from the same group should be called. dp.add_handler(CommandHandler('start', start1), 1) dp.add_handler(CommandHandler('start', start2), 1) dp.add_handler(CommandHandler('start', start3), 2) dp.add_error_handler(error) dp.process_update(update) assert passed == ['start1', 'error', err] assert passed[2] is err
class TestUpdater: message_count = 0 received = None attempts = 0 err_handler_called = Event() cb_handler_called = Event() offset = 0 test_flag = False def test_slot_behaviour(self, updater, mro_slots, recwarn): for at in updater.__slots__: at = f"_Updater{at}" if at.startswith( '__') and not at.endswith('__') else at assert getattr(updater, at, 'err') != 'err', f"got extra slot '{at}'" assert not updater.__dict__, f"got missing slot(s): {updater.__dict__}" assert len(mro_slots(updater)) == len(set( mro_slots(updater))), "duplicate slot" updater.custom, updater.running = 'should give warning', updater.running assert len(recwarn) == 1 and 'custom' in str( recwarn[0].message), recwarn.list class CustomUpdater(Updater): pass # Tests that setting custom attributes of Updater subclass doesn't raise warning a = CustomUpdater(updater.bot.token) a.my_custom = 'no error!' assert len(recwarn) == 1 updater.__setattr__('__test', 'mangled success') assert getattr(updater, '_Updater__test', 'e') == 'mangled success', "mangling failed" @pytest.fixture(autouse=True) def reset(self): self.message_count = 0 self.received = None self.attempts = 0 self.err_handler_called.clear() self.cb_handler_called.clear() self.test_flag = False def error_handler(self, bot, update, error): self.received = error.message self.err_handler_called.set() def callback(self, bot, update): self.received = update.message.text self.cb_handler_called.set() def test_warn_arbitrary_callback_data(self, bot, recwarn): Updater(bot=bot, arbitrary_callback_data=True) assert len(recwarn) == 1 assert 'Passing arbitrary_callback_data to an Updater' in str( recwarn[0].message) @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): def test(*args, **kwargs): raise error monkeypatch.setattr(updater.bot, 'get_updates', test) monkeypatch.setattr(updater.bot, 'set_webhook', lambda *args, **kwargs: True) updater.dispatcher.add_error_handler(self.error_handler) updater.start_polling(0.01) # 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() @pytest.mark.filterwarnings( 'ignore:.*:pytest.PytestUnhandledThreadExceptionWarning') def test_get_updates_bailout_err(self, monkeypatch, updater, caplog): error = InvalidToken() def test(*args, **kwargs): raise error with caplog.at_level(logging.DEBUG): monkeypatch.setattr(updater.bot, 'get_updates', test) monkeypatch.setattr(updater.bot, 'set_webhook', lambda *args, **kwargs: True) updater.dispatcher.add_error_handler(self.error_handler) updater.start_polling(0.01) assert self.err_handler_called.wait(1) is not True sleep(1) # NOTE: This test might hit a race condition and fail (though the 1 seconds delay above # 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. import pprint pprint.pprint([rec.getMessage() for rec in caplog.get_records('call')]) assert any(f'unhandled exception in Bot:{updater.bot.id}:updater' in rec.getMessage() for rec in caplog.get_records('call')) @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 monkeypatch.setattr(updater.bot, 'get_updates', test) monkeypatch.setattr(updater.bot, 'set_webhook', lambda *args, **kwargs: True) updater.dispatcher.add_error_handler(self.error_handler) updater.start_polling(0.01) # 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 @pytest.mark.parametrize('ext_bot', [True, False]) def test_webhook(self, monkeypatch, updater, ext_bot): # Testing with both ExtBot and Bot to make sure any logic in WebhookHandler # that depends on this distinction works if ext_bot and not isinstance(updater.bot, ExtBot): updater.bot = ExtBot(updater.bot.token) if not ext_bot and not type(updater.bot) is Bot: updater.bot = Bot(updater.bot.token) 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(0.2) try: # Now, we send an update to the server via urlopen update = Update( 1, message=Message(1, None, Chat(1, ''), from_user=User(1, '', False), text='Webhook'), ) self._send_webhook_msg(ip, port, update.to_json(), 'TOKEN') sleep(0.2) assert q.get(False) == update # 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 with pytest.raises(HTTPError) as excinfo: self._send_webhook_msg(ip, port, None, 'webookhandler.py', get_method=lambda: 'HEAD') assert excinfo.value.code == 404 # Test multiple shutdown() calls updater.httpd.shutdown() finally: updater.httpd.shutdown() sleep(0.2) assert not updater.httpd.is_running updater.stop() @pytest.mark.parametrize('invalid_data', [True, False]) def test_webhook_arbitrary_callback_data(self, monkeypatch, updater, invalid_data): """Here we only test one simple setup. telegram.ext.ExtBot.insert_callback_data is tested extensively in test_bot.py in conjunction with get_updates.""" updater.bot.arbitrary_callback_data = True try: 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(0.2) try: # Now, we send an update to the server via urlopen reply_markup = InlineKeyboardMarkup.from_button( InlineKeyboardButton(text='text', callback_data='callback_data')) if not invalid_data: reply_markup = updater.bot.callback_data_cache.process_keyboard( reply_markup) message = Message( 1, None, None, reply_markup=reply_markup, ) update = Update(1, message=message) self._send_webhook_msg(ip, port, update.to_json(), 'TOKEN') sleep(0.2) received_update = q.get(False) assert received_update == update button = received_update.message.reply_markup.inline_keyboard[ 0][0] if invalid_data: assert isinstance(button.callback_data, InvalidCallbackData) else: assert button.callback_data == 'callback_data' # Test multiple shutdown() calls updater.httpd.shutdown() finally: updater.httpd.shutdown() sleep(0.2) assert not updater.httpd.is_running updater.stop() finally: updater.bot.arbitrary_callback_data = False updater.bot.callback_data_cache.clear_callback_data() updater.bot.callback_data_cache.clear_callback_queries() def test_start_webhook_no_warning_or_error_logs(self, caplog, updater, monkeypatch): monkeypatch.setattr(updater.bot, 'set_webhook', lambda *args, **kwargs: True) monkeypatch.setattr(updater.bot, 'delete_webhook', lambda *args, **kwargs: True) # prevent api calls from @info decorator when updater.bot.id is used in thread names monkeypatch.setattr(updater.bot, '_bot', User(id=123, first_name='bot', is_bot=True)) monkeypatch.setattr(updater.bot, '_commands', []) ip = '127.0.0.1' port = randrange(1024, 49152) # Select random port with caplog.at_level(logging.WARNING): updater.start_webhook(ip, port) updater.stop() assert not caplog.records def test_webhook_ssl(self, monkeypatch, updater): monkeypatch.setattr(updater.bot, 'set_webhook', lambda *args, **kwargs: True) monkeypatch.setattr(updater.bot, 'delete_webhook', lambda *args, **kwargs: True) ip = '127.0.0.1' port = randrange(1024, 49152) # Select random port 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, drop_pending_updates=False, webhook_url=None, allowed_updates=None, ) except TelegramError: tg_err = True assert tg_err def test_webhook_no_ssl(self, monkeypatch, updater): 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, webhook_url=None) sleep(0.2) # Now, we send an update to the server via urlopen update = Update( 1, message=Message(1, None, Chat(1, ''), from_user=User(1, '', False), text='Webhook 2'), ) self._send_webhook_msg(ip, port, update.to_json()) sleep(0.2) assert q.get(False) == update updater.stop() def test_webhook_ssl_just_for_telegram(self, monkeypatch, updater): q = Queue() def set_webhook(**kwargs): self.test_flag.append(bool(kwargs.get('certificate'))) return True orig_wh_server_init = WebhookServer.__init__ def webhook_server_init(*args): self.test_flag = [args[-1] is None] orig_wh_server_init(*args) monkeypatch.setattr(updater.bot, 'set_webhook', set_webhook) monkeypatch.setattr(updater.bot, 'delete_webhook', lambda *args, **kwargs: True) monkeypatch.setattr('telegram.ext.Dispatcher.process_update', lambda _, u: q.put(u)) monkeypatch.setattr( 'telegram.ext.utils.webhookhandler.WebhookServer.__init__', webhook_server_init) ip = '127.0.0.1' port = randrange(1024, 49152) # Select random port updater.start_webhook(ip, port, webhook_url=None, cert='./tests/test_updater.py') sleep(0.2) # Now, we send an update to the server via urlopen update = Update( 1, message=Message(1, None, Chat(1, ''), from_user=User(1, '', False), text='Webhook 2'), ) self._send_webhook_msg(ip, port, update.to_json()) sleep(0.2) assert q.get(False) == update updater.stop() assert self.test_flag == [True, True] @pytest.mark.parametrize('pass_max_connections', [True, False]) def test_webhook_max_connections(self, monkeypatch, updater, pass_max_connections): q = Queue() max_connections = 42 def set_webhook(**kwargs): print(kwargs) self.test_flag = kwargs.get('max_connections') == ( max_connections if pass_max_connections else 40) return True monkeypatch.setattr(updater.bot, 'set_webhook', set_webhook) 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 if pass_max_connections: updater.start_webhook(ip, port, webhook_url=None, max_connections=max_connections) else: updater.start_webhook(ip, port, webhook_url=None) sleep(0.2) # Now, we send an update to the server via urlopen update = Update( 1, message=Message(1, None, Chat(1, ''), from_user=User(1, '', False), text='Webhook 2'), ) self._send_webhook_msg(ip, port, update.to_json()) sleep(0.2) assert q.get(False) == update updater.stop() assert self.test_flag is True @pytest.mark.parametrize(('error', ), argvalues=[(TelegramError(''), )], ids=('TelegramError', )) def test_bootstrap_retries_success(self, monkeypatch, updater, error): retries = 2 def attempt(*args, **kwargs): if self.attempts < retries: self.attempts += 1 raise error monkeypatch.setattr(updater.bot, 'set_webhook', attempt) updater.running = True updater._bootstrap(retries, False, 'path', None, bootstrap_interval=0) assert self.attempts == retries @pytest.mark.parametrize( ('error', 'attempts'), argvalues=[(TelegramError(''), 2), (Unauthorized(''), 1), (InvalidToken(), 1)], ids=('TelegramError', 'Unauthorized', 'InvalidToken'), ) def test_bootstrap_retries_error(self, monkeypatch, updater, error, attempts): retries = 1 def attempt(*args, **kwargs): self.attempts += 1 raise error monkeypatch.setattr(updater.bot, 'set_webhook', attempt) updater.running = True with pytest.raises(type(error)): updater._bootstrap(retries, False, 'path', None, bootstrap_interval=0) assert self.attempts == attempts @pytest.mark.parametrize('drop_pending_updates', (True, False)) def test_bootstrap_clean_updates(self, monkeypatch, updater, drop_pending_updates): # As dropping pending updates is done by passing `drop_pending_updates` to # set_webhook, we just check that we pass the correct value self.test_flag = False def delete_webhook(**kwargs): self.test_flag = kwargs.get( 'drop_pending_updates') == drop_pending_updates monkeypatch.setattr(updater.bot, 'delete_webhook', delete_webhook) updater.running = True updater._bootstrap( 1, drop_pending_updates=drop_pending_updates, webhook_url=None, allowed_updates=None, bootstrap_interval=0, ) assert self.test_flag is True def test_deprecation_warnings_start_webhook(self, recwarn, updater, monkeypatch): monkeypatch.setattr(updater.bot, 'set_webhook', lambda *args, **kwargs: True) monkeypatch.setattr(updater.bot, 'delete_webhook', lambda *args, **kwargs: True) # prevent api calls from @info decorator when updater.bot.id is used in thread names monkeypatch.setattr(updater.bot, '_bot', User(id=123, first_name='bot', is_bot=True)) monkeypatch.setattr(updater.bot, '_commands', []) ip = '127.0.0.1' port = randrange(1024, 49152) # Select random port updater.start_webhook(ip, port, clean=True, force_event_loop=False) updater.stop() for warning in recwarn: print(warning) try: # This is for flaky tests (there's an unclosed socket sometimes) recwarn.pop( ResourceWarning ) # internally iterates through recwarn.list and deletes it except AssertionError: pass assert len(recwarn) == 3 assert str(recwarn[0].message).startswith('Old Handler API') assert str(recwarn[1].message).startswith('The argument `clean` of') assert str(recwarn[2].message).startswith( 'The argument `force_event_loop` of') def test_clean_deprecation_warning_polling(self, recwarn, updater, monkeypatch): monkeypatch.setattr(updater.bot, 'set_webhook', lambda *args, **kwargs: True) monkeypatch.setattr(updater.bot, 'delete_webhook', lambda *args, **kwargs: True) # prevent api calls from @info decorator when updater.bot.id is used in thread names monkeypatch.setattr(updater.bot, '_bot', User(id=123, first_name='bot', is_bot=True)) monkeypatch.setattr(updater.bot, '_commands', []) updater.start_polling(clean=True) updater.stop() for msg in recwarn: print(msg) try: # This is for flaky tests (there's an unclosed socket sometimes) recwarn.pop( ResourceWarning ) # internally iterates through recwarn.list and deletes it except AssertionError: pass assert len(recwarn) == 2 assert str(recwarn[0].message).startswith('Old Handler API') assert str(recwarn[1].message).startswith('The argument `clean` of') def test_clean_drop_pending_mutually_exclusive(self, updater): with pytest.raises( TypeError, match='`clean` and `drop_pending_updates` are mutually'): updater.start_polling(clean=True, drop_pending_updates=False) with pytest.raises( TypeError, match='`clean` and `drop_pending_updates` are mutually'): updater.start_webhook(clean=True, drop_pending_updates=False) @flaky(3, 1) def test_webhook_invalid_posts(self, updater): ip = '127.0.0.1' port = randrange(1024, 49152) # select random port for travis thr = Thread(target=updater._start_webhook, args=(ip, port, '', None, None, 0, False, None, None)) thr.start() sleep(0.2) try: 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 with pytest.raises(HTTPError) as excinfo: self._send_webhook_msg(ip, port, 'dummy-payload', content_len=-2) assert excinfo.value.code == 500 # TODO: prevent urllib or the underlying from adding content-length # with pytest.raises(HTTPError) as excinfo: # self._send_webhook_msg(ip, port, 'dummy-payload', content_len=None) # assert excinfo.value.code == 411 with pytest.raises(HTTPError): self._send_webhook_msg(ip, port, 'dummy-payload', content_len='not-a-number') assert excinfo.value.code == 500 finally: updater.httpd.shutdown() thr.join() def _send_webhook_msg( self, ip, port, payload_str, url_path='', content_len=-1, content_type='application/json', get_method=None, ): headers = { 'content-type': content_type, } if not payload_str: content_len = None payload = None else: payload = bytes(payload_str, encoding='utf-8') if content_len == -1: content_len = len(payload) if content_len is not None: headers['content-length'] = str(content_len) url = f'http://{ip}:{port}/{url_path}' req = Request(url, data=payload, headers=headers) if get_method is not None: req.get_method = get_method return urlopen(req) def signal_sender(self, updater): sleep(0.2) while not updater.running: sleep(0.2) os.kill(os.getpid(), signal.SIGTERM) @signalskip def test_idle(self, updater, caplog): updater.start_polling(0.01) Thread(target=partial(self.signal_sender, updater=updater)).start() with caplog.at_level(logging.INFO): updater.idle() # There is a chance of a conflict when getting updates since there can be many tests # (bots) running simultaneously while testing in github actions. records = caplog.records.copy( ) # To avoid iterating and removing at same time for idx, log in enumerate(records): print(log) msg = log.getMessage() if msg.startswith('Error while getting Updates: Conflict'): caplog.records.pop(idx) # For stability if msg.startswith('No error handlers are registered'): caplog.records.pop(idx) assert len(caplog.records) == 2, caplog.records rec = caplog.records[-2] assert rec.getMessage().startswith(f'Received signal {signal.SIGTERM}') assert rec.levelname == 'INFO' rec = caplog.records[-1] assert rec.getMessage().startswith('Scheduler has been shut down') assert rec.levelname == 'INFO' # If we get this far, idle() ran through sleep(0.5) assert updater.running is False @signalskip def test_user_signal(self, updater): temp_var = {'a': 0} def user_signal_inc(signum, frame): temp_var['a'] = 1 updater.user_sig_handler = user_signal_inc updater.start_polling(0.01) Thread(target=partial(self.signal_sender, updater=updater)).start() updater.idle() # If we get this far, idle() ran through sleep(0.5) assert updater.running is False assert temp_var['a'] != 0 def test_create_bot(self): updater = Updater('123:abcd') assert updater.bot is not None def test_mutual_exclude_token_bot(self): bot = Bot('123:zyxw') with pytest.raises(ValueError): Updater(token='123:abcd', bot=bot) def test_no_token_or_bot_or_dispatcher(self): with pytest.raises(ValueError): Updater() def test_mutual_exclude_bot_private_key(self): bot = Bot('123:zyxw') with pytest.raises(ValueError): Updater(bot=bot, private_key=b'key') def test_mutual_exclude_bot_dispatcher(self, bot): dispatcher = Dispatcher(bot, None) bot = Bot('123:zyxw') with pytest.raises(ValueError): Updater(bot=bot, dispatcher=dispatcher) def test_mutual_exclude_persistence_dispatcher(self, bot): dispatcher = Dispatcher(bot, None) persistence = DictPersistence() with pytest.raises(ValueError): Updater(dispatcher=dispatcher, persistence=persistence) def test_mutual_exclude_workers_dispatcher(self, bot): dispatcher = Dispatcher(bot, None) with pytest.raises(ValueError): Updater(dispatcher=dispatcher, workers=8) def test_mutual_exclude_use_context_dispatcher(self, bot): dispatcher = Dispatcher(bot, None) use_context = not dispatcher.use_context with pytest.raises(ValueError): Updater(dispatcher=dispatcher, use_context=use_context) def test_mutual_exclude_custom_context_dispatcher(self): dispatcher = Dispatcher(None, None) with pytest.raises(ValueError): Updater(dispatcher=dispatcher, context_types=True) def test_defaults_warning(self, bot): with pytest.warns(TelegramDeprecationWarning, match='no effect when a Bot is passed'): Updater(bot=bot, defaults=Defaults())
def _valid_token(token): """a very basic validation on token""" left, sep, _right = token.partition(':') if (not sep) or (not left.isdigit()) or (len(left) < 3): raise TelegramError('Invalid token') return token
def errorRaisingHandlerTest(self, bot, update): raise TelegramError(update)
def creator(self): _id = helper.safe_list_get(self._data, "creator", None) if not _id or not isinstance(_id, int): raise TelegramError("Некорректный id желающего делать арт") return User.get(_id)
def increase_step(self, increase_step: int): raise TelegramError( "Increase step is being changed only via Firebase store")
def sendToAll(bot, message, list_of_chats, user_chat_id): timeout = 10 #Timeout in seconds, though this might be a good idea, don't think this bot will be hitting this any time soon # This is the bulk of the work in this bot. if Filters.forwarded(message): message_id = message.message_id from_chat = message.forward_from_chat.id for chat in list_of_chats: try: bot.forward_message(chat_id=chat, from_chat_id=from_chat, message_id=message_id, timeout=timeout) except TelegramError as te: logger.debug( "Unable to send message to admin in sendToAll: %s" % te) try: newMessage = bot.forward_message(chat_id=user_chat_id, from_chat_id=from_chat, message_id=message_id, timeout=timeout) # If the user has not responded. Message all of the admins. except TelegramError as te: logger.debug("Unable to send message to user in sendToAll: %s" % te) for chat in list_of_chats: try: #error checking in error checking because bot.sendMessage doesn't work for people who haven't messaged the bot. bot.sendMessage( chat_id=chat, text= "Unable to forward message to user, if this persists, resolve this thread, they may have stopped talking to the bot." ) except TelegramError: pass elif Filters.text(message): for chat in list_of_chats: try: bot.send_message(chat_id=chat, text=message.text, timeout=timeout) except TelegramError as te: logger.debug( "Unable to send message to admin in sendToAll: %s" % te) try: newMessage = bot.send_message(chat_id=user_chat_id, text=message.text, timeout=timeout) except TelegramError as te: logger.debug("Unable to send message to user in sendToAll: %s" % te) for chat in list_of_chats: try: bot.sendMessage( chat_id=chat, text= "Unable to send text message to user, if this persists, resolve this thread, they may have stopped talking to the bot." ) except TelegramError: pass elif Filters.audio(message): audio = message.audio.file_id for chat in list_of_chats: try: bot.send_audio(chat_id=chat, audio=audio, timeout=timeout) except TelegramError as te: logger.debug( "Unable to send message to admin in sendToAll: %s" % te) try: newMessage = bot.send_audio(chat_id=user_chat_id, audio=audio, timeout=timeout) except TelegramError as te: logger.debug("Unable to send message to user in sendToAll: %s" % te) for chat in list_of_chats: try: bot.sendMessage( chat_id=chat, text= "Unable to send audio message to user, if this persists, resolve this thread, they may have stopped talking to the bot." ) except TelegramError: pass elif Filters.document(message): document = message.document.file_id for chat in list_of_chats: try: bot.send_document(chat_id=chat, document=document, timeout=timeout) except TelegramError as te: logger.debug( "Unable to send message to admin in sendToAll: %s" % te) try: newMessage = bot.send_document(chat_id=user_chat_id, document=document, timeout=timeout) except TelegramError as te: logger.debug("Unable to send message to user in sendToAll: %s" % te) for chat in list_of_chats: try: bot.sendMessage( chat_id=chat, text= "Unable to send document to user, if this persists, resolve this thread, they may have stopped talking to the bot." ) except TelegramError: pass elif Filters.photo(message): photo = message.photo[0].file_id caption = "" if message.caption: caption = message.caption for chat in list_of_chats: try: bot.send_photo(chat_id=chat, photo=photo, caption=caption, timeout=timeout) except TelegramError as te: logger.debug( "Unable to send message to admin in sendToAll: %s" % te) try: newMessage = bot.send_photo(chat_id=user_chat_id, photo=photo, caption=caption, timeout=timeout) except TelegramError as te: logger.debug("Unable to send message to user in sendToAll: %s" % te) for chat in list_of_chats: try: bot.sendMessage( chat_id=chat, text= "Unable to send photo to user, if this persists, resolve this thread, they may have stopped talking to the bot." ) except TelegramError: pass elif Filters.sticker(message): sticker = message.sticker.file_id for chat in list_of_chats: try: bot.send_sticker(chat_id=chat, sticker=sticker, timeout=timeout) except TelegramError as te: logger.debug( "Unable to send messages to admin in SendToAll: %s" % te) try: newMessage = bot.send_sticker(chat_id=user_chat_id, sticker=sticker, timeout=timeout) except TelegramError as te: logger.debug("Unable to send message to user in sendToAll: %s" % te) for chat in list_of_chats: try: bot.sendMessage( chat_id=chat, text= "Unable to send sticker to user, if this persists, resolve this thread, they may have stopped talking to the bot." ) except TelegramError: pass elif Filters.voice(message): voice = message.voice.file_id for chat in list_of_chats: try: bot.send_voice(chat_id=chat, voice=voice, timeout=timeout) except TelegramError as te: logger.debug( "Unable to send message to admin in sendToAll: %s " % te) try: newMessage = bot.send_voice(chat_id=user_chat_id, voice=voice, timeout=timeout) except TelegramError as te: logger.debug("Unable to send message to user in sendToAll: %s" % te) for chat in list_of_chats: try: bot.sendMessage( chat_id=chat, text= "Unable to send voice message to user, if this persists, resolve this thread, they may have stopped talking to the bot." ) except TelegramError: pass elif Filters.video(message): video = message.video.file_id for chat in list_of_chats: try: bot.send_video(chat_id=chat, video=video, timeout=timeout) except TelegramError as te: logger.debug( "Unable to send message to admin in sendToAll: %s" % te) try: newMessage = bot.send_video(chat_id=user_chat_id, video=video, timeout=timeout) except TelegramError as te: logger.debug("Unable to send message to user in sendToAll: %s" % te) for chat in list_of_chats: try: bot.sendMessage( chat_id=chat, text= "Unable to send message to user, if this persists, resolve this thread, they may have stopped talking to the bot." ) except TelegramError: pass elif Filters.contact(message): phone_number = message.contact.phone_number first_name = message.contact.first_name last_name = message.contact.last_name for chat in list_of_chats: try: bot.send_contact(chat_id=chat, phone_number=phone_number, first_name=first_name, last_name=last_name, timeout=timeout) except TelegramError as te: logger.debug( "Unbable to send message to admin in sendToAll: %s" % te) try: newMessage = bot.send_contact(chat_id=user_chat_id, phone_number=phone_number, first_name=first_name, last_name=last_name, timeout=timeout) except TelegramError as te: logger.debug("Unable to send message to user in sendToAll: %s" % te) for chat in list_of_chats: try: bot.sendMessage( chat_id=chat, text= "Unable to send contact to user, if this persists, resolve this thread, they may have stopped talking to the bot." ) except TelegramError: pass elif Filters.location(message): lat = message.location.latitude lon = message.location.longitude for chat in list_of_chats: try: bot.send_location(chat_id=chat, longitude=lon, latitude=lat, timeout=timeout) except TelegramError as te: logger.debug( "Unable to send message to admin in sendToAll: %s" % te) try: newMessage = bot.send_location(chat_id=user_chat_id, longitude=lon, latitude=lat, timeout=timeout) except TelegramError as te: logger.debug("Unable to send message to user in sendToAll: %s" % te) for chat in list_of_chats: try: bot.sendMessage( chat_id=chat, text= "Unable to send location to user, if this persists, resolve this thread, they may have stopped talking to the bot." ) except TelegramError: pass else: logger.warning("Message %s not handled in SendToAll") raise TelegramError("No handler for forwarding.") MDB.active.update({'_id': user_chat_id}, {'$push': { 'log': newMessage.message_id }})
def test(*args, **kwargs): raise TelegramError('Test Error 2')
def ticket_description(self, ticket_description: str): raise TelegramError("Direct setter for ticket_description is denied")
def decorator(self, *args, **kwargs): if not self.__auth: raise TelegramError({'message': "API must be authenticated."}) return func(self, *args, **kwargs)
class TestErrors: def test_telegram_error(self): with pytest.raises(TelegramError, match="^test message$"): raise TelegramError("test message") with pytest.raises(TelegramError, match="^Test message$"): raise TelegramError("Error: test message") with pytest.raises(TelegramError, match="^Test message$"): raise TelegramError("[Error]: test message") with pytest.raises(TelegramError, match="^Test message$"): raise TelegramError("Bad Request: test message") def test_unauthorized(self): with pytest.raises(Unauthorized, match="test message"): raise Unauthorized("test message") with pytest.raises(Unauthorized, match="^Test message$"): raise Unauthorized("Error: test message") with pytest.raises(Unauthorized, match="^Test message$"): raise Unauthorized("[Error]: test message") with pytest.raises(Unauthorized, match="^Test message$"): raise Unauthorized("Bad Request: test message") def test_invalid_token(self): with pytest.raises(InvalidToken, match="Invalid token"): raise InvalidToken def test_network_error(self): with pytest.raises(NetworkError, match="test message"): raise NetworkError("test message") with pytest.raises(NetworkError, match="^Test message$"): raise NetworkError("Error: test message") with pytest.raises(NetworkError, match="^Test message$"): raise NetworkError("[Error]: test message") with pytest.raises(NetworkError, match="^Test message$"): raise NetworkError("Bad Request: test message") def test_bad_request(self): with pytest.raises(BadRequest, match="test message"): raise BadRequest("test message") with pytest.raises(BadRequest, match="^Test message$"): raise BadRequest("Error: test message") with pytest.raises(BadRequest, match="^Test message$"): raise BadRequest("[Error]: test message") with pytest.raises(BadRequest, match="^Test message$"): raise BadRequest("Bad Request: test message") def test_timed_out(self): with pytest.raises(TimedOut, match="^Timed out$"): raise TimedOut def test_chat_migrated(self): with pytest.raises(ChatMigrated, match="Group migrated to supergroup. New chat id: 1234"): raise ChatMigrated(1234) try: raise ChatMigrated(1234) except ChatMigrated as e: assert e.new_chat_id == 1234 def test_retry_after(self): with pytest.raises(RetryAfter, match="Flood control exceeded. Retry in 12.0 seconds"): raise RetryAfter(12) def test_conflict(self): with pytest.raises(Conflict, match='Something something.'): raise Conflict('Something something.') @pytest.mark.parametrize( "exception, attributes", [ (TelegramError("test message"), ["message"]), (Unauthorized("test message"), ["message"]), (InvalidToken(), ["message"]), (NetworkError("test message"), ["message"]), (BadRequest("test message"), ["message"]), (TimedOut(), ["message"]), (ChatMigrated(1234), ["message", "new_chat_id"]), (RetryAfter(12), ["message", "retry_after"]), (Conflict("test message"), ["message"]), (TelegramDecryptionError("test message"), ["message"]) ], ) def test_errors_pickling(self, exception, attributes): print(exception) pickled = pickle.dumps(exception) unpickled = pickle.loads(pickled) assert type(unpickled) is type(exception) assert str(unpickled) == str(exception) for attribute in attributes: assert getattr(unpickled, attribute) == getattr(exception, attribute) def test_pickling_test_coverage(self): """ This test is only here to make sure that new errors will override __reduce__ properly. Add the new error class to the below covered_subclasses dict, if it's covered in the above test_errors_pickling test. """ def make_assertion(cls): assert {sc for sc in cls.__subclasses__()} == covered_subclasses[cls] for subcls in cls.__subclasses__(): make_assertion(subcls) covered_subclasses = defaultdict(set) covered_subclasses.update({ TelegramError: {Unauthorized, InvalidToken, NetworkError, ChatMigrated, RetryAfter, Conflict, TelegramDecryptionError}, NetworkError: {BadRequest, TimedOut} }) make_assertion(TelegramError)
class TestUpdater(object): message_count = 0 received = None attempts = 0 err_handler_called = Event() cb_handler_called = Event() @pytest.fixture(autouse=True) def reset(self): self.message_count = 0 self.received = None self.attempts = 0 self.err_handler_called.clear() self.cb_handler_called.clear() def error_handler(self, bot, update, error): self.received = error.message self.err_handler_called.set() def callback(self, bot, update): self.received = update.message.text self.cb_handler_called.set() # TODO: test clean= argument of Updater._bootstrap @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): def test(*args, **kwargs): raise error monkeypatch.setattr(updater.bot, 'get_updates', test) monkeypatch.setattr(updater.bot, 'set_webhook', lambda *args, **kwargs: True) updater.dispatcher.add_error_handler(self.error_handler) updater.start_polling(0.01) # 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): monkeypatch.setattr(updater.bot, 'get_updates', test) monkeypatch.setattr(updater.bot, 'set_webhook', lambda *args, **kwargs: True) updater.dispatcher.add_error_handler(self.error_handler) updater.start_polling(0.01) assert self.err_handler_called.wait(1) is not True sleep(1) # NOTE: This test might hit a race condition and fail (though the 1 seconds delay above # 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. 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')) @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 monkeypatch.setattr(updater.bot, 'get_updates', test) monkeypatch.setattr(updater.bot, 'set_webhook', lambda *args, **kwargs: True) updater.dispatcher.add_error_handler(self.error_handler) updater.start_polling(0.01) # 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 def test_webhook(self, monkeypatch, updater): 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: # 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 # 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 with pytest.raises(HTTPError) as excinfo: self._send_webhook_msg(ip, port, None, 'webookhandler.py', get_method=lambda: 'HEAD') assert excinfo.value.code == 404 # Test multiple shutdown() calls updater.httpd.shutdown() finally: updater.httpd.shutdown() sleep(.2) assert not updater.httpd.is_running updater.stop() def test_webhook_ssl(self, monkeypatch, updater): monkeypatch.setattr(updater.bot, 'set_webhook', lambda *args, **kwargs: True) monkeypatch.setattr(updater.bot, 'delete_webhook', lambda *args, **kwargs: True) ip = '127.0.0.1' port = randrange(1024, 49152) # Select random port 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 def test_webhook_no_ssl(self, monkeypatch, updater): 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, webhook_url=None) 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 2')) self._send_webhook_msg(ip, port, update.to_json()) sleep(.2) assert q.get(False) == update updater.stop() def test_webhook_default_quote(self, monkeypatch, updater): updater._default_quote = True q = Queue() monkeypatch.setattr(updater.bot, 'set_webhook', lambda *args, **kwargs: True) monkeypatch.setattr(updater.bot, 'delete_webhook', lambda *args, **kwargs: True) monkeypatch.setattr('telegram.ext.Dispatcher.process_update', lambda _, u: q.put(u)) ip = '127.0.0.1' port = randrange(1024, 49152) # Select random port updater.start_webhook( ip, port, url_path='TOKEN') sleep(.2) # Now, we send an update to the server via urlopen update = Update(1, message=Message(1, User(1, '', False), None, Chat(1, ''), text='Webhook')) self._send_webhook_msg(ip, port, update.to_json(), 'TOKEN') sleep(.2) # assert q.get(False) == update assert q.get(False).message.default_quote is True updater.stop() @pytest.mark.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() @pytest.mark.parametrize(('error',), argvalues=[(TelegramError(''),)], ids=('TelegramError',)) def test_bootstrap_retries_success(self, monkeypatch, updater, error): retries = 2 def attempt(*args, **kwargs): if self.attempts < retries: self.attempts += 1 raise error monkeypatch.setattr(updater.bot, 'set_webhook', attempt) updater.running = True updater._bootstrap(retries, False, 'path', None, bootstrap_interval=0) assert self.attempts == retries @pytest.mark.parametrize(('error', 'attempts'), argvalues=[(TelegramError(''), 2), (Unauthorized(''), 1), (InvalidToken(), 1)], ids=('TelegramError', 'Unauthorized', 'InvalidToken')) def test_bootstrap_retries_error(self, monkeypatch, updater, error, attempts): retries = 1 def attempt(*args, **kwargs): self.attempts += 1 raise error monkeypatch.setattr(updater.bot, 'set_webhook', attempt) updater.running = True with pytest.raises(type(error)): updater._bootstrap(retries, False, 'path', None, bootstrap_interval=0) assert self.attempts == attempts @flaky(3, 1) def test_webhook_invalid_posts(self, updater): ip = '127.0.0.1' port = randrange(1024, 49152) # select random port for travis thr = Thread( target=updater._start_webhook, args=(ip, port, '', None, None, 0, False, None, None)) thr.start() sleep(.2) try: 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 with pytest.raises(HTTPError) as excinfo: self._send_webhook_msg(ip, port, 'dummy-payload', content_len=-2) assert excinfo.value.code == 500 # TODO: prevent urllib or the underlying from adding content-length # with pytest.raises(HTTPError) as excinfo: # self._send_webhook_msg(ip, port, 'dummy-payload', content_len=None) # assert excinfo.value.code == 411 with pytest.raises(HTTPError): self._send_webhook_msg(ip, port, 'dummy-payload', content_len='not-a-number') assert excinfo.value.code == 500 finally: updater.httpd.shutdown() thr.join() def _send_webhook_msg(self, ip, port, payload_str, url_path='', content_len=-1, content_type='application/json', get_method=None): headers = {'content-type': content_type, } if not payload_str: content_len = None payload = None else: payload = bytes(payload_str, encoding='utf-8') if content_len == -1: content_len = len(payload) if content_len is not None: headers['content-length'] = str(content_len) url = 'http://{ip}:{port}/{path}'.format(ip=ip, port=port, path=url_path) req = Request(url, data=payload, headers=headers) if get_method is not None: req.get_method = get_method return urlopen(req) def signal_sender(self, updater): sleep(0.2) while not updater.running: sleep(0.2) os.kill(os.getpid(), signal.SIGTERM) @signalskip def test_idle(self, updater, caplog): updater.start_polling(0.01) 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' # If we get this far, idle() ran through sleep(.5) assert updater.running is False @signalskip def test_user_signal(self, updater): temp_var = {'a': 0} def user_signal_inc(signum, frame): temp_var['a'] = 1 updater.user_sig_handler = user_signal_inc updater.start_polling(0.01) Thread(target=partial(self.signal_sender, updater=updater)).start() updater.idle() # If we get this far, idle() ran through sleep(.5) assert updater.running is False assert temp_var['a'] != 0 def test_create_bot(self): updater = Updater('123:abcd') assert updater.bot is not None def test_mutual_exclude_token_bot(self): bot = Bot('123:zyxw') with pytest.raises(ValueError): Updater(token='123:abcd', bot=bot) def test_no_token_or_bot_or_dispatcher(self): with pytest.raises(ValueError): Updater() def test_mutual_exclude_bot_private_key(self): bot = Bot('123:zyxw') with pytest.raises(ValueError): Updater(bot=bot, private_key=b'key') 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) persistence = BasePersistence() 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)
def mock(**_kw): raise TelegramError()
def callback(): raise TelegramError('Error')
def test(*args, **kwargs): raise TelegramError('test worked')
def attempt(_, *args, **kwargs): if self.attempts < retries: self.attempts += 1 raise TelegramError('')
class TestUpdater(object): message_count = 0 received = None attempts = 0 @pytest.fixture(autouse=True) def reset(self): self.message_count = 0 self.received = None self.attempts = 0 def error_handler(self, bot, update, error): self.received = error.message def callback(self, bot, update): self.received = update.message.text # TODO: test clean= argument def test_error_on_get_updates(self, monkeypatch, updater): def test(*args, **kwargs): raise TelegramError('Test Error 2') monkeypatch.setattr('telegram.Bot.get_updates', test) monkeypatch.setattr('telegram.Bot.set_webhook', lambda *args, **kwargs: True) updater.dispatcher.add_error_handler(self.error_handler) updater.start_polling(0.01) sleep(.1) assert self.received == 'Test Error 2' def test_webhook(self, monkeypatch, updater): q = Queue() monkeypatch.setattr('telegram.Bot.set_webhook', lambda *args, **kwargs: True) monkeypatch.setattr('telegram.Bot.delete_webhook', lambda *args, **kwargs: True) monkeypatch.setattr('telegram.ext.Dispatcher.process_update', lambda _, u: q.put(u)) ip = '127.0.0.1' port = randrange(1024, 49152) # Select random port for travis updater.start_webhook( ip, port, url_path='TOKEN', cert='./tests/test_updater.py', key='./tests/test_updater.py', ) sleep(.2) # SSL-Wrapping will fail, so we start the server without SSL thr = Thread(target=updater.httpd.serve_forever) thr.start() try: # 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 response = self._send_webhook_msg(ip, port, None, 'webookhandler.py') assert b'' == response.read() assert 200 == response.code response = self._send_webhook_msg(ip, port, None, 'webookhandler.py', get_method=lambda: 'HEAD') assert b'' == response.read() assert 200 == response.code # Test multiple shutdown() calls updater.httpd.shutdown() finally: updater.httpd.shutdown() thr.join() def test_webhook_no_ssl(self, monkeypatch, updater): q = Queue() monkeypatch.setattr('telegram.Bot.set_webhook', lambda *args, **kwargs: True) monkeypatch.setattr('telegram.Bot.delete_webhook', lambda *args, **kwargs: True) monkeypatch.setattr('telegram.ext.Dispatcher.process_update', lambda _, u: q.put(u)) ip = '127.0.0.1' port = randrange(1024, 49152) # Select random port for travis updater.start_webhook(ip, port, webhook_url=None) 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 2')) self._send_webhook_msg(ip, port, update.to_json()) sleep(.2) assert q.get(False) == update def test_bootstrap_retries_success(self, monkeypatch, updater): retries = 2 def attempt(_, *args, **kwargs): if self.attempts < retries: self.attempts += 1 raise TelegramError('') monkeypatch.setattr('telegram.Bot.set_webhook', attempt) updater._bootstrap(retries, False, 'path', None) assert self.attempts == retries @pytest.mark.parametrize( ('error', 'attempts'), argvalues=[(TelegramError(''), 2), (Unauthorized(''), 1), (InvalidToken(), 1)], ids=('TelegramError', 'Unauthorized', 'InvalidToken')) def test_bootstrap_retries_error(self, monkeypatch, updater, error, attempts): retries = 1 def attempt(_, *args, **kwargs): self.attempts += 1 raise error monkeypatch.setattr('telegram.Bot.set_webhook', attempt) with pytest.raises(type(error)): updater._bootstrap(retries, False, 'path', None) assert self.attempts == attempts def test_webhook_invalid_posts(self, updater): ip = '127.0.0.1' port = randrange(1024, 49152) # select random port for travis thr = Thread(target=updater._start_webhook, args=(ip, port, '', None, None, 0, False, None, None)) thr.start() sleep(.2) try: 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 with pytest.raises(HTTPError) as excinfo: self._send_webhook_msg(ip, port, 'dummy-payload', content_len=-2) assert excinfo.value.code == 403 # TODO: prevent urllib or the underlying from adding content-length # with pytest.raises(HTTPError) as excinfo: # self._send_webhook_msg(ip, port, 'dummy-payload', content_len=None) # assert excinfo.value.code == 411 with pytest.raises(HTTPError): self._send_webhook_msg(ip, port, 'dummy-payload', content_len='not-a-number') assert excinfo.value.code == 403 finally: updater.httpd.shutdown() thr.join() def _send_webhook_msg(self, ip, port, payload_str, url_path='', content_len=-1, content_type='application/json', get_method=None): headers = { 'content-type': content_type, } if not payload_str: content_len = None payload = None else: payload = bytes(payload_str, encoding='utf-8') if content_len == -1: content_len = len(payload) if content_len is not None: headers['content-length'] = str(content_len) url = 'http://{ip}:{port}/{path}'.format(ip=ip, port=port, path=url_path) req = Request(url, data=payload, headers=headers) if get_method is not None: req.get_method = get_method return urlopen(req) def signal_sender(self, updater): sleep(0.2) while not updater.running: sleep(0.2) os.kill(os.getpid(), signal.SIGTERM) @signalskip def test_idle(self, updater, caplog): updater.start_polling(0.01) 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' # If we get this far, idle() ran through sleep(.5) assert updater.running is False @signalskip def test_user_signal(self, updater): temp_var = {'a': 0} def user_signal_inc(signum, frame): temp_var['a'] = 1 updater.user_sig_handler = user_signal_inc updater.start_polling(0.01) Thread(target=partial(self.signal_sender, updater=updater)).start() updater.idle() # If we get this far, idle() ran through sleep(.5) assert updater.running is False assert temp_var['a'] != 0 def test_create_bot(self): updater = Updater('123:abcd') assert updater.bot is not None def test_mutual_exclude_token_bot(self): bot = Bot('123:zyxw') with pytest.raises(ValueError): Updater(token='123:abcd', bot=bot) def test_no_token_or_bot(self): with pytest.raises(ValueError): Updater()
def callback_raise_error(self, bot, update): raise TelegramError(update.message.text)
def ticket_name(self, ticket_name: str): raise TelegramError("Direct setter for ticket name is denied")
class TestUpdater: message_count = 0 received = None attempts = 0 err_handler_called = Event() cb_handler_called = Event() offset = 0 @pytest.fixture(autouse=True) def reset(self): self.message_count = 0 self.received = None self.attempts = 0 self.err_handler_called.clear() self.cb_handler_called.clear() def error_handler(self, bot, update, error): self.received = error.message self.err_handler_called.set() def callback(self, bot, update): self.received = update.message.text self.cb_handler_called.set() @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): def test(*args, **kwargs): raise error monkeypatch.setattr(updater.bot, 'get_updates', test) monkeypatch.setattr(updater.bot, 'set_webhook', lambda *args, **kwargs: True) updater.dispatcher.add_error_handler(self.error_handler) updater.start_polling(0.01) # 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): monkeypatch.setattr(updater.bot, 'get_updates', test) monkeypatch.setattr(updater.bot, 'set_webhook', lambda *args, **kwargs: True) updater.dispatcher.add_error_handler(self.error_handler) updater.start_polling(0.01) assert self.err_handler_called.wait(1) is not True sleep(1) # NOTE: This test might hit a race condition and fail (though the 1 seconds delay above # 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. 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')) @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 monkeypatch.setattr(updater.bot, 'get_updates', test) monkeypatch.setattr(updater.bot, 'set_webhook', lambda *args, **kwargs: True) updater.dispatcher.add_error_handler(self.error_handler) updater.start_polling(0.01) # 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 def test_webhook(self, monkeypatch, updater): 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: # Now, we send an update to the server via urlopen update = Update(1, message=Message(1, None, Chat(1, ''), from_user=User(1, '', False), text='Webhook')) self._send_webhook_msg(ip, port, update.to_json(), 'TOKEN') sleep(.2) assert q.get(False) == update # 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 with pytest.raises(HTTPError) as excinfo: self._send_webhook_msg(ip, port, None, 'webookhandler.py', get_method=lambda: 'HEAD') assert excinfo.value.code == 404 # Test multiple shutdown() calls updater.httpd.shutdown() finally: updater.httpd.shutdown() sleep(.2) assert not updater.httpd.is_running updater.stop() def test_start_webhook_no_warning_or_error_logs(self, caplog, updater, monkeypatch): monkeypatch.setattr(updater.bot, 'set_webhook', lambda *args, **kwargs: True) monkeypatch.setattr(updater.bot, 'delete_webhook', lambda *args, **kwargs: True) # prevent api calls from @info decorator when updater.bot.id is used in thread names monkeypatch.setattr(updater.bot, 'bot', User(id=123, first_name='bot', is_bot=True)) monkeypatch.setattr(updater.bot, '_commands', []) ip = '127.0.0.1' port = randrange(1024, 49152) # Select random port with caplog.at_level(logging.WARNING): updater.start_webhook(ip, port) updater.stop() assert not caplog.records @pytest.mark.skipif( os.name != 'nt' or sys.version_info < (3, 8), reason='Workaround only relevant on windows with py3.8+') def test_start_webhook_ensure_event_loop(self, updater, monkeypatch): def serve_forever(self, force_event_loop=False, ready=None): with self.server_lock: self.is_running = True self._ensure_event_loop(force_event_loop=force_event_loop) if ready is not None: ready.set() monkeypatch.setattr(WebhookServer, 'serve_forever', serve_forever) monkeypatch.setattr(updater.bot, 'set_webhook', lambda *args, **kwargs: True) monkeypatch.setattr(updater.bot, 'delete_webhook', lambda *args, **kwargs: True) ip = '127.0.0.1' port = randrange(1024, 49152) # Select random port with set_asyncio_event_loop(None): updater._start_webhook(ip, port, url_path='TOKEN', cert=None, key=None, bootstrap_retries=0, clean=False, webhook_url=None, allowed_updates=None) assert isinstance(asyncio.get_event_loop(), asyncio.SelectorEventLoop) @pytest.mark.skipif( os.name != 'nt' or sys.version_info < (3, 8), reason='Workaround only relevant on windows with py3.8+') def test_start_webhook_force_event_loop_false(self, updater, monkeypatch): monkeypatch.setattr(updater.bot, 'set_webhook', lambda *args, **kwargs: True) monkeypatch.setattr(updater.bot, 'delete_webhook', lambda *args, **kwargs: True) ip = '127.0.0.1' port = randrange(1024, 49152) # Select random port with set_asyncio_event_loop(asyncio.ProactorEventLoop()): with pytest.raises(TypeError, match='`ProactorEventLoop` is incompatible'): updater._start_webhook(ip, port, url_path='TOKEN', cert=None, key=None, bootstrap_retries=0, clean=False, webhook_url=None, allowed_updates=None) @pytest.mark.skipif( os.name != 'nt' or sys.version_info < (3, 8), reason='Workaround only relevant on windows with py3.8+') def test_start_webhook_force_event_loop_true(self, updater, monkeypatch): def serve_forever(self, force_event_loop=False, ready=None): with self.server_lock: self.is_running = True self._ensure_event_loop(force_event_loop=force_event_loop) if ready is not None: ready.set() monkeypatch.setattr(WebhookServer, 'serve_forever', serve_forever) monkeypatch.setattr(updater.bot, 'set_webhook', lambda *args, **kwargs: True) monkeypatch.setattr(updater.bot, 'delete_webhook', lambda *args, **kwargs: True) ip = '127.0.0.1' port = randrange(1024, 49152) # Select random port with set_asyncio_event_loop(asyncio.ProactorEventLoop()): updater._start_webhook(ip, port, url_path='TOKEN', cert=None, key=None, bootstrap_retries=0, clean=False, webhook_url=None, allowed_updates=None, force_event_loop=True) assert isinstance(asyncio.get_event_loop(), asyncio.ProactorEventLoop) def test_webhook_ssl(self, monkeypatch, updater): monkeypatch.setattr(updater.bot, 'set_webhook', lambda *args, **kwargs: True) monkeypatch.setattr(updater.bot, 'delete_webhook', lambda *args, **kwargs: True) ip = '127.0.0.1' port = randrange(1024, 49152) # Select random port 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 def test_webhook_no_ssl(self, monkeypatch, updater): 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, webhook_url=None) sleep(.2) # Now, we send an update to the server via urlopen update = Update(1, message=Message(1, None, Chat(1, ''), from_user=User(1, '', False), text='Webhook 2')) self._send_webhook_msg(ip, port, update.to_json()) sleep(.2) assert q.get(False) == update updater.stop() @pytest.mark.parametrize(('error', ), argvalues=[(TelegramError(''), )], ids=('TelegramError', )) def test_bootstrap_retries_success(self, monkeypatch, updater, error): retries = 2 def attempt(*args, **kwargs): if self.attempts < retries: self.attempts += 1 raise error monkeypatch.setattr(updater.bot, 'set_webhook', attempt) updater.running = True updater._bootstrap(retries, False, 'path', None, bootstrap_interval=0) assert self.attempts == retries @pytest.mark.parametrize( ('error', 'attempts'), argvalues=[(TelegramError(''), 2), (Unauthorized(''), 1), (InvalidToken(), 1)], ids=('TelegramError', 'Unauthorized', 'InvalidToken')) def test_bootstrap_retries_error(self, monkeypatch, updater, error, attempts): retries = 1 def attempt(*args, **kwargs): self.attempts += 1 raise error monkeypatch.setattr(updater.bot, 'set_webhook', attempt) updater.running = True with pytest.raises(type(error)): updater._bootstrap(retries, False, 'path', None, bootstrap_interval=0) assert self.attempts == attempts 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 @flaky(3, 1) def test_webhook_invalid_posts(self, updater): ip = '127.0.0.1' port = randrange(1024, 49152) # select random port for travis thr = Thread(target=updater._start_webhook, args=(ip, port, '', None, None, 0, False, None, None)) thr.start() sleep(.2) try: 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 with pytest.raises(HTTPError) as excinfo: self._send_webhook_msg(ip, port, 'dummy-payload', content_len=-2) assert excinfo.value.code == 500 # TODO: prevent urllib or the underlying from adding content-length # with pytest.raises(HTTPError) as excinfo: # self._send_webhook_msg(ip, port, 'dummy-payload', content_len=None) # assert excinfo.value.code == 411 with pytest.raises(HTTPError): self._send_webhook_msg(ip, port, 'dummy-payload', content_len='not-a-number') assert excinfo.value.code == 500 finally: updater.httpd.shutdown() thr.join() def _send_webhook_msg(self, ip, port, payload_str, url_path='', content_len=-1, content_type='application/json', get_method=None): headers = { 'content-type': content_type, } if not payload_str: content_len = None payload = None else: payload = bytes(payload_str, encoding='utf-8') if content_len == -1: content_len = len(payload) if content_len is not None: headers['content-length'] = str(content_len) url = 'http://{ip}:{port}/{path}'.format(ip=ip, port=port, path=url_path) req = Request(url, data=payload, headers=headers) if get_method is not None: req.get_method = get_method return urlopen(req) def signal_sender(self, updater): sleep(0.2) while not updater.running: sleep(0.2) os.kill(os.getpid(), signal.SIGTERM) @signalskip def test_idle(self, updater, caplog): updater.start_polling(0.01) Thread(target=partial(self.signal_sender, updater=updater)).start() with caplog.at_level(logging.INFO): updater.idle() rec = caplog.records[-2] assert rec.msg.startswith('Received signal {}'.format(signal.SIGTERM)) assert rec.levelname == 'INFO' rec = caplog.records[-1] assert rec.msg.startswith('Scheduler has been shut down') assert rec.levelname == 'INFO' # If we get this far, idle() ran through sleep(.5) assert updater.running is False @signalskip def test_user_signal(self, updater): temp_var = {'a': 0} def user_signal_inc(signum, frame): temp_var['a'] = 1 updater.user_sig_handler = user_signal_inc updater.start_polling(0.01) Thread(target=partial(self.signal_sender, updater=updater)).start() updater.idle() # If we get this far, idle() ran through sleep(.5) assert updater.running is False assert temp_var['a'] != 0 def test_create_bot(self): updater = Updater('123:abcd') assert updater.bot is not None def test_mutual_exclude_token_bot(self): bot = Bot('123:zyxw') with pytest.raises(ValueError): Updater(token='123:abcd', bot=bot) def test_no_token_or_bot_or_dispatcher(self): with pytest.raises(ValueError): Updater() def test_mutual_exclude_bot_private_key(self): bot = Bot('123:zyxw') with pytest.raises(ValueError): Updater(bot=bot, private_key=b'key') 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) persistence = DictPersistence() 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) def test_defaults_warning(self, bot): with pytest.warns(TelegramDeprecationWarning, match='no effect when a Bot is passed'): Updater(bot=bot, defaults=Defaults())
def callback_raise_error(self, bot, update): if isinstance(bot, Bot): raise TelegramError(update.message.text) raise TelegramError(bot.message.text)
def ticket_base_price(self, ticket_base_price: str): raise TelegramError("Direct setter for ticket_base_price is denied")