diff --git a/examples/asynchronous_telebot/continue_handling.py b/examples/asynchronous_telebot/continue_handling.py new file mode 100644 index 0000000..4781cc4 --- /dev/null +++ b/examples/asynchronous_telebot/continue_handling.py @@ -0,0 +1,27 @@ +from telebot.async_telebot import AsyncTeleBot +from telebot.asyncio_handler_backends import ContinueHandling + + +bot = AsyncTeleBot('TOKEN') + +@bot.message_handler(commands=['start']) +async def start(message): + await bot.send_message(message.chat.id, 'Hello World!') + return ContinueHandling() + +@bot.message_handler(commands=['start']) +async def start2(message): + """ + This handler comes after the first one, but it will never be called. + But you can call it by returning ContinueHandling() in the first handler. + + If you return ContinueHandling() in the first handler, the next + registered handler with appropriate filters will be called. + """ + await bot.send_message(message.chat.id, 'Hello World2!') + +import asyncio +asyncio.run(bot.polling()) # just a reminder that infinity polling +# wraps polling into try/except block just as sync version, +# but you can use any of them because neither of them stops if you +# pass non_stop=True diff --git a/examples/continue_handling.py b/examples/continue_handling.py new file mode 100644 index 0000000..68e26f1 --- /dev/null +++ b/examples/continue_handling.py @@ -0,0 +1,23 @@ +from telebot import TeleBot +from telebot.handler_backends import ContinueHandling + + +bot = TeleBot('TOKEN') + +@bot.message_handler(commands=['start']) +def start(message): + bot.send_message(message.chat.id, 'Hello World!') + return ContinueHandling() + +@bot.message_handler(commands=['start']) +def start2(message): + """ + This handler comes after the first one, but it will never be called. + But you can call it by returning ContinueHandling() in the first handler. + + If you return ContinueHandling() in the first handler, the next + registered handler with appropriate filters will be called. + """ + bot.send_message(message.chat.id, 'Hello World2!') + +bot.infinity_polling() diff --git a/telebot/__init__.py b/telebot/__init__.py index bf808ec..5b697b0 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -38,7 +38,10 @@ logger.addHandler(console_output_handler) logger.setLevel(logging.ERROR) from telebot import apihelper, util, types -from telebot.handler_backends import HandlerBackend, MemoryHandlerBackend, FileHandlerBackend, BaseMiddleware, CancelUpdate, SkipHandler, State +from telebot.handler_backends import ( + HandlerBackend, MemoryHandlerBackend, FileHandlerBackend, BaseMiddleware, + CancelUpdate, SkipHandler, State, ContinueHandling +) from telebot.custom_filters import SimpleCustomFilter, AdvancedCustomFilter @@ -6078,80 +6081,84 @@ class TeleBot: for handler in handlers: if self._test_message_handler(handler, message): if handler.get('pass_bot', False): - handler['function'](message, bot = self) + result = handler['function'](message, bot=self) else: - handler['function'](message) + result = handler['function'](message) + if not isinstance(result, ContinueHandling): + break + return + + data = {} + params =[] + handler_error = None + skip_handlers = False + + if middlewares: + for middleware in middlewares: + if middleware.update_sensitive: + if hasattr(middleware, f'pre_process_{update_type}'): + result = getattr(middleware, f'pre_process_{update_type}')(message, data) + else: + logger.error('Middleware {} does not have pre_process_{} method. pre_process function execution was skipped.'.format(middleware.__class__.__name__, update_type)) + result = None + else: + result = middleware.pre_process(message, data) + # We will break this loop if CancelUpdate is returned + # Also, we will not run other middlewares + if isinstance(result, CancelUpdate): + return + elif isinstance(result, SkipHandler): + skip_handlers = True + + if handlers and not skip_handlers: + try: + for handler in handlers: + process_handler = self._test_message_handler(handler, message) + if not process_handler: continue + for i in inspect.signature(handler['function']).parameters: + params.append(i) + result = None + if len(params) == 1: + result = handler['function'](message) + elif "data" in params: + if len(params) == 2: + result = handler['function'](message, data) + elif len(params) == 3: + result = handler['function'](message, data=data, bot=self) + else: + logger.error("It is not allowed to pass data and values inside data to the handler. Check your handler: {}".format(handler['function'])) + return + else: + data_copy = data.copy() + for key in list(data_copy): + # remove data from data_copy if handler does not accept it + if key not in params: + del data_copy[key] + if handler.get('pass_bot'): + data_copy["bot"] = self + if len(data_copy) > len(params) - 1: # remove the message parameter + logger.error("You are passing more parameters than the handler needs. Check your handler: {}".format(handler['function'])) + return + result = handler["function"](message, **data_copy) + if not isinstance(result, ContinueHandling): break - else: - data = {} - params =[] - handler_error = None - skip_handlers = False + except Exception as e: + handler_error = e + if self.exception_handler: + self.exception_handler.handle(e) + else: + logger.error(str(e)) + logger.debug("Exception traceback:\n%s", traceback.format_exc()) - if middlewares: - for middleware in middlewares: - if middleware.update_sensitive: - if hasattr(middleware, f'pre_process_{update_type}'): - result = getattr(middleware, f'pre_process_{update_type}')(message, data) - else: - logger.error('Middleware {} does not have pre_process_{} method. pre_process function execution was skipped.'.format(middleware.__class__.__name__, update_type)) - result = None + if middlewares: + for middleware in middlewares: + if middleware.update_sensitive: + if hasattr(middleware, f'post_process_{update_type}'): + getattr(middleware, f'post_process_{update_type}')(message, data, handler_error) else: - result = middleware.pre_process(message, data) - # We will break this loop if CancelUpdate is returned - # Also, we will not run other middlewares - if isinstance(result, CancelUpdate): - return - elif isinstance(result, SkipHandler): - skip_handlers = True - - if handlers and not(skip_handlers): - try: - for handler in handlers: - process_handler = self._test_message_handler(handler, message) - if not process_handler: continue - for i in inspect.signature(handler['function']).parameters: - params.append(i) - if len(params) == 1: - handler['function'](message) - elif "data" in params: - if len(params) == 2: - handler['function'](message, data) - elif len(params) == 3: - handler['function'](message, data=data, bot=self) - else: - logger.error("It is not allowed to pass data and values inside data to the handler. Check your handler: {}".format(handler['function'])) - return - else: - data_copy = data.copy() - for key in list(data_copy): - # remove data from data_copy if handler does not accept it - if key not in params: - del data_copy[key] - if handler.get('pass_bot'): - data_copy["bot"] = self - if len(data_copy) > len(params) - 1: # remove the message parameter - logger.error("You are passing more parameters than the handler needs. Check your handler: {}".format(handler['function'])) - return - handler["function"](message, **data_copy) - break - except Exception as e: - handler_error = e - if self.exception_handler: - self.exception_handler.handle(e) - else: - logger.error(str(e)) - logger.debug("Exception traceback:\n%s", traceback.format_exc()) - - if middlewares: - for middleware in middlewares: - if middleware.update_sensitive: - if hasattr(middleware, f'post_process_{update_type}'): - getattr(middleware, f'post_process_{update_type}')(message, data, handler_error) - else: - logger.error("Middleware: {} does not have post_process_{} method. Post process function was not executed.".format(middleware.__class__.__name__, update_type)) - else: - middleware.post_process(message, data, handler_error) + logger.error("Middleware: {} does not have post_process_{} method. Post process function was not executed.".format(middleware.__class__.__name__, update_type)) + else: + middleware.post_process(message, data, handler_error) def _notify_command_handlers(self, handlers, new_messages, update_type): """ diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 6a37ff8..3a9e142 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -14,7 +14,7 @@ import telebot.types # storages from telebot.asyncio_storage import StateMemoryStorage, StatePickleStorage, StateStorageBase -from telebot.asyncio_handler_backends import BaseMiddleware, CancelUpdate, SkipHandler, State +from telebot.asyncio_handler_backends import BaseMiddleware, CancelUpdate, SkipHandler, State, ContinueHandling from inspect import signature @@ -497,16 +497,14 @@ class AsyncTeleBot: if not process_update: continue for i in signature(handler['function']).parameters: params.append(i) + result = None if len(params) == 1: - await handler['function'](message) - break + result = await handler['function'](message) elif "data" in params: if len(params) == 2: - await handler['function'](message, data) - break + result = await handler['function'](message, data) elif len(params) == 3: - await handler['function'](message, data=data, bot=self) - break + result = await handler['function'](message, data=data, bot=self) else: logger.error("It is not allowed to pass data and values inside data to the handler. Check your handler: {}".format(handler['function'])) return @@ -521,7 +519,8 @@ class AsyncTeleBot: if len(data_copy) > len(params) - 1: # remove the message parameter logger.error("You are passing more data than the handler needs. Check your handler: {}".format(handler['function'])) return - await handler["function"](message, **data_copy) + result = await handler["function"](message, **data_copy) + if not isinstance(result, ContinueHandling): break except Exception as e: if self.exception_handler: diff --git a/telebot/asyncio_handler_backends.py b/telebot/asyncio_handler_backends.py index 34cc19a..96e13ee 100644 --- a/telebot/asyncio_handler_backends.py +++ b/telebot/asyncio_handler_backends.py @@ -3,8 +3,6 @@ File with all middleware classes, states. """ - - class BaseMiddleware: """ Base class for middleware. @@ -96,6 +94,7 @@ class SkipHandler: def __init__(self) -> None: pass + class CancelUpdate: """ Class for canceling updates. @@ -106,4 +105,27 @@ class CancelUpdate: """ def __init__(self) -> None: - pass \ No newline at end of file + pass + + +class ContinueHandling: + """ + Class for continue updates in handlers. + Just return instance of this class + in handlers to continue process. + + .. code-block:: python3 + :caption: Example of using ContinueHandling + + @bot.message_handler(commands=['start']) + async def start(message): + await bot.send_message(message.chat.id, 'Hello World!') + return ContinueHandling() + + @bot.message_handler(commands=['start']) + async def start2(message): + await bot.send_message(message.chat.id, 'Hello World2!') + + """ + def __init__(self) -> None: + pass diff --git a/telebot/handler_backends.py b/telebot/handler_backends.py index 42c5804..be2714c 100644 --- a/telebot/handler_backends.py +++ b/telebot/handler_backends.py @@ -174,7 +174,6 @@ class State: def __str__(self) -> str: return self.name - class StatesGroup: """ @@ -192,9 +191,6 @@ class StatesGroup: value.name = ':'.join((cls.__name__, name)) value.group = cls - - - class BaseMiddleware: """ @@ -253,10 +249,10 @@ class SkipHandler: Update will go to post_process, but will skip execution of handler. """ - def __init__(self) -> None: pass + class CancelUpdate: """ Class for canceling updates. @@ -265,6 +261,28 @@ class CancelUpdate: Update will skip handler and execution of post_process in middlewares. """ - def __init__(self) -> None: - pass \ No newline at end of file + pass + + +class ContinueHandling: + """ + Class for continue updates in handlers. + Just return instance of this class + in handlers to continue process. + + .. code-block:: python3 + :caption: Example of using ContinueHandling + + @bot.message_handler(commands=['start']) + def start(message): + bot.send_message(message.chat.id, 'Hello World!') + return ContinueHandling() + + @bot.message_handler(commands=['start']) + def start2(message): + bot.send_message(message.chat.id, 'Hello World2!') + + """ + def __init__(self) -> None: + pass