From 9f4cd4948a3541c2f1d6d4923f9084b4ec087d01 Mon Sep 17 00:00:00 2001 From: nikita Date: Wed, 9 Sep 2026 18:18:07 +0300 Subject: [PATCH] Text filters update and stability - **kwargs are propagated to base classes in text filters from now on - `_callbacks.py` prints filter exceptions from now on --- src/mab/bot/_callbacks.py | 6 +++++- src/mab/filters/text.py | 36 +++++++++++++++++++++++------------- 2 files changed, 28 insertions(+), 14 deletions(-) diff --git a/src/mab/bot/_callbacks.py b/src/mab/bot/_callbacks.py index b190e39..8699587 100644 --- a/src/mab/bot/_callbacks.py +++ b/src/mab/bot/_callbacks.py @@ -51,7 +51,11 @@ class Callbacks: for callback_info in self._filters: if not isinstance(callback_info, self._FilterBasedCallback): continue - if not await callback_info.filter(room, event, self._client): + try: + if not await callback_info.filter(room, event, self._client): + continue + except: + self._logger.error(traceback.format_exc()) continue event_data = RoomEventData( room=room, diff --git a/src/mab/filters/text.py b/src/mab/filters/text.py index c88e161..e2862f2 100644 --- a/src/mab/filters/text.py +++ b/src/mab/filters/text.py @@ -14,9 +14,14 @@ class TextFilter(BaseEventFilter): If this filter matches, you can access `event.body` and it stores unformatted text of the message. + + 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. """ - def __init__(self): - super().__init__() + def __init__(self, *, ignore_filename_in_body: bool = True, **kwargs): + super().__init__(**kwargs) + self._ignore_filename_in_body = ignore_filename_in_body def __repr__(self) -> str: return "TextFilter()" @@ -28,6 +33,10 @@ class TextFilter(BaseEventFilter): return False if not event.body.strip(): # type: ignore return False + if self._ignore_filename_in_body: + content = event.source["content"] + if "filename" in content and content["filename"] == event.body: # type: ignore + return False return True class FormattedTextFilter(BaseEventFilter): @@ -40,8 +49,8 @@ class FormattedTextFilter(BaseEventFilter): If this filter matches, you can access `event.formatted_body` and it stores formatted text of the message. """ - def __init__(self): - super().__init__() + def __init__(self, **kwargs): + super().__init__(**kwargs) def __repr__(self): return "FormattedTextFilter()" @@ -68,8 +77,8 @@ class TextContainsFilter(TextFilter): is True. That means you must ensure that `needle` is lower case. The filter will never match otherwise. """ - def __init__(self, needle: str | list[str], *, any_case: bool = True): - super().__init__() + def __init__(self, needle: str | list[str], *, any_case: bool = True, **kwargs): + super().__init__(**kwargs) if type(needle) is str: needle = [needle] self._any_case = any_case @@ -100,8 +109,8 @@ class TextStartsWithFilter(TextFilter): is True. That means you must ensure that `needle` is lower case. The filter will never match otherwise. """ - def __init__(self, substring: str | list[str], *, any_case: bool = True): - super().__init__() + def __init__(self, substring: str | list[str], *, any_case: bool = True, **kwargs): + super().__init__(**kwargs) if type(substring) is str: substring = [substring] self._any_case = any_case @@ -132,8 +141,8 @@ class TextEndsWithFilter(TextFilter): is True. That means you must ensure that `needle` is lower case. The filter will never match otherwise. """ - def __init__(self, substring: str | list[str], *, any_case: bool = True): - super().__init__() + def __init__(self, substring: str | list[str], *, any_case: bool = True, **kwargs): + super().__init__(**kwargs) if type(substring) is str: substring = [substring] self._any_case = any_case @@ -172,8 +181,8 @@ class TextCommandFilter(TextFilter): `event.command_args: list[str]`. You may use this attribute in your callback for this event. """ - def __init__(self, verbs: str | list[str], min_args: int = 0, max_args: int | None = None, prefix: str = "!"): - super().__init__() + def __init__(self, verbs: str | list[str], min_args: int = 0, max_args: int | None = None, prefix: str = "!", **kwargs): + super().__init__(**kwargs) if type(verbs) is str: verbs = [verbs] self._verbs = verbs @@ -206,7 +215,8 @@ class TextRegexFilter(TextFilter): """ This filter returns True if the `event.body` passes the regex. """ - def __init__(self, regex: re.Pattern | str): + def __init__(self, regex: re.Pattern | str, **kwargs): + super().__init__(**kwargs) if isinstance(regex, str): regex = re.compile(regex) self._regex = regex