import logging import typing log = logging.getLogger('aiogram.Middleware') class MiddlewareManager: """ Middlewares manager. Works only with dispatcher. """ def __init__(self, dispatcher): """ Init :param dispatcher: instance of Dispatcher """ self.dispatcher = dispatcher self.bot = dispatcher.bot self.storage = dispatcher.storage self.applications = [] @property def loop(self): return self.dispatcher.loop def setup(self, middleware): """ Setup middleware :param middleware: :return: """ if not isinstance(middleware, BaseMiddleware): raise TypeError(f"`middleware` must be an instance of BaseMiddleware, not {type(middleware)}") if middleware.is_configured(): raise ValueError('That middleware is already used!') self.applications.append(middleware) middleware.setup(self) log.debug(f"Loaded middleware '{middleware.__class__.__name__}'") return middleware async def trigger(self, action: str, args: typing.Iterable): """ Call action to middlewares with args lilt. :param action: :param args: :return: """ for app in self.applications: await app.trigger(action, args) class BaseMiddleware: """ Base class for middleware. All methods on the middle always must be coroutines and name starts with "on_" like "on_process_message". """ def __init__(self): self._configured = False self._manager = None @property def manager(self) -> MiddlewareManager: """ Instance of MiddlewareManager """ if self._manager is None: raise RuntimeError('Middleware is not configured!') return self._manager def setup(self, manager): """ Mark middleware as configured :param manager: :return: """ self._manager = manager self._configured = True def is_configured(self) -> bool: """ Check middleware is configured :return: """ return self._configured async def trigger(self, action, args): """ Trigger action. :param action: :param args: :return: """ handler_name = f"on_{action}" handler = getattr(self, handler_name, None) if not handler: return None await handler(*args) class LifetimeControllerMiddleware(BaseMiddleware): # TODO: Rename class skip_patterns = None _skip_actions = None @property def skip_actions(self): if self._skip_actions is None: self._skip_actions = [] if self.skip_patterns: for item in self.skip_patterns: self._skip_actions.extend([ f"pre_process_{item}", f"process_{item}", f"post_process_{item}", ]) return self._skip_actions async def pre_process(self, obj, data, *args): pass async def post_process(self, obj, data, *args): pass async def trigger(self, action, args): if action in self.skip_actions: return False obj, *args, data = args if action.startswith('pre_process_'): await self.pre_process(obj, data, *args) elif action.startswith('post_process_'): await self.post_process(obj, data, *args) else: return False return True