From 1fe4434e16fdbd451e8d16bd4421f60c5a649f92 Mon Sep 17 00:00:00 2001 From: nikita Date: Sat, 12 Sep 2026 20:14:28 +0300 Subject: [PATCH] Filters refactoring and small improvement - Removed `filters/__init__.py` (filters are to be imported from `mab` directly) - Renamed `Text` filters to `Body` filters - Fixed `base.py` filters not propagating **kwargs to base classes - Added `__init__` for BaseEventFilter so that keyword argument that remain unused by its children get logged using `logging.critical` --- src/mab/__init__.py | 30 +++++++++++++++++++--------- src/mab/filters/__init__.py | 28 -------------------------- src/mab/filters/base.py | 11 +++++++--- src/mab/filters/{text.py => body.py} | 17 ++++++++++------ 4 files changed, 40 insertions(+), 46 deletions(-) delete mode 100644 src/mab/filters/__init__.py rename src/mab/filters/{text.py => body.py} (94%) diff --git a/src/mab/__init__.py b/src/mab/__init__.py index f0fc5f8..c120ece 100644 --- a/src/mab/__init__.py +++ b/src/mab/__init__.py @@ -1,12 +1,13 @@ from . import bot from . import types -from .types import MatrixBotConfig +from .types import MatrixBotConfig, RoomEventData, MessageType from .bot import MatrixBot from .filters.base import * -from .filters.text import * +from .filters.message import * +from .filters.body import * __all__ = [ # module names @@ -15,18 +16,29 @@ __all__ = [ # .types "MatrixBotConfig", + "RoomEventData", + "MessageType", # .bot "MatrixBot", # .filters.base "BaseEventFilter", + "EventTypeFilter", - # .filters.text - "TextFilter", - "FormattedTextFilter", - "TextContainsFilter", - "TextStartsWithFilter", - "TextEndsWithFilter", - "TextCommandFilter", + # .filters.body + "BodyExistsFilter", + "BodyContainsFilter", + "BodyStartsWithFilter", + "BodyEndsWithFilter", + "BodyCommandFilter", + "BodyRegexFilter", + + # .filters.message + "MessageTypeFilter", + "NewMessageFilter", + "EditedMessageFilter", + "RedactedMessageFilter", + "SenderIsFilter", + "SenderIsBotFilter", ] \ No newline at end of file diff --git a/src/mab/filters/__init__.py b/src/mab/filters/__init__.py deleted file mode 100644 index 55cb0eb..0000000 --- a/src/mab/filters/__init__.py +++ /dev/null @@ -1,28 +0,0 @@ -from .base import * -from .message import * -from .room import * -from .text import * - -__all__ = [ - # base.py - "BaseEventFilter", - "EventTypeFilter", - - # message.py - "NewMessageFilter", - "EditedMessageFilter", - "RedactedMessageFilter", - "SenderIsFilter", - "SenderIsBotFilter", - - # room.py - "RoomEncryptedFilter", - - # text.py - "TextFilter", - "TextContainsFilter", - "TextStartsWithFilter", - "TextEndsWithFilter", - "TextCommandFilter", - "TextRegexFilter" -] \ No newline at end of file diff --git a/src/mab/filters/base.py b/src/mab/filters/base.py index 5ebde04..5d59ab3 100644 --- a/src/mab/filters/base.py +++ b/src/mab/filters/base.py @@ -9,6 +9,10 @@ class BaseEventFilter(ABC): """Base class for all message filters""" _logger = logging.Logger("EventFilter") + def __init__(self, **kwargs): + for a in kwargs: + self._logger.critical(f"Unknown keyword argument for some filter is used: '{a}'={repr(kwargs[a])}") + # AND def __and__(self, other): if not isinstance(other, BaseEventFilter): @@ -80,7 +84,8 @@ class BaseEventFilter(ABC): class EventTypeFilter(BaseEventFilter): """Event filter that checks if the event is an instance of some class""" - def __init__(self, type: Type): + def __init__(self, type: Type, **kwargs): + super().__init__(**kwargs) self._type = type async def __call__(self, room: MatrixRoom, event: Event, client: AsyncClient) -> bool: @@ -113,8 +118,8 @@ class CompoundEventFilter(BaseEventFilter): CompoundEventFilter.OPERATOR_INVERT: [1], }[op] - def __init__(self, operator: str, arguments: list[BaseEventFilter]): - super().__init__() + def __init__(self, operator: str, arguments: list[BaseEventFilter], **kwargs): + super().__init__(**kwargs) if not self._is_operator_valid(operator): raise RuntimeError(f"Invalid operator `{operator}`") if not self._is_elements_count_valid(operator, len(arguments)): diff --git a/src/mab/filters/text.py b/src/mab/filters/body.py similarity index 94% rename from src/mab/filters/text.py rename to src/mab/filters/body.py index afb4f5f..e71514c 100644 --- a/src/mab/filters/text.py +++ b/src/mab/filters/body.py @@ -5,7 +5,7 @@ from .message import NewMessageFilter from nio import AsyncClient from nio import MatrixRoom, Event -class TextFilter(NewMessageFilter): +class BodyExistsFilter(NewMessageFilter): """ This filter returns True if all conditions are met: 1. `event` has attribute `body` @@ -18,12 +18,17 @@ class TextFilter(NewMessageFilter): If `event.body` value equals to `event.source["content"]["filename"]` (if it is present, of course) then this filter will not match it by default. You may disable `ignore_filename_in_body` to disable this feature. + + This filter will match any message that has `body` in it, including images, + videos, files, etc. """ def __init__(self, *, ignore_filename_in_body: bool = True, **kwargs): super().__init__(**kwargs) self._ignore_filename_in_body = ignore_filename_in_body async def __call__(self, room: MatrixRoom, event: Event, client: AsyncClient) -> bool: + if not await super().__call__(room, event, client): + return False if not hasattr(event, "body"): return False if not isinstance(event.body, str): # type: ignore @@ -36,7 +41,7 @@ class TextFilter(NewMessageFilter): return False return True -class TextContainsFilter(TextFilter): +class BodyContainsFilter(BodyExistsFilter): """ This filter returns True if `event.body` contains `needle` substring (or any of neddle from the list). `event.body` will be converted to lower case if @@ -65,7 +70,7 @@ class TextContainsFilter(TextFilter): return True return False -class TextStartsWithFilter(TextFilter): +class BodyStartsWithFilter(BodyExistsFilter): """ This filter returns True if `event.body` starts with `substring` (or any of substrings from the list). The check will be case insensetive if `any_case` @@ -94,7 +99,7 @@ class TextStartsWithFilter(TextFilter): return True return False -class TextEndsWithFilter(TextFilter): +class BodyEndsWithFilter(BodyExistsFilter): """ This filter returns True if `event.body` ends with `substring` (or any of substrings from the list). The check will be case insensetive if `any_case` @@ -123,7 +128,7 @@ class TextEndsWithFilter(TextFilter): return True return False -class TextCommandFilter(TextFilter): +class BodyCommandFilter(BodyExistsFilter): """ This filter returns True if all conditions are met: 1. `event.body` contains at least `min_args + 1` words after split() @@ -171,7 +176,7 @@ class TextCommandFilter(TextFilter): return True return False -class TextRegexFilter(TextFilter): +class BodyRegexFilter(BodyExistsFilter): """ This filter returns True if the `event.body` passes the regex. """