2 Commits

Author SHA1 Message Date
159a43ebe6 Fixed _callbacks.py did not reraise CancelledError 2026-09-09 18:19:52 +03:00
9f4cd4948a 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
2026-09-09 18:18:07 +03:00
2 changed files with 30 additions and 14 deletions

View File

@@ -51,8 +51,14 @@ class Callbacks:
for callback_info in self._filters: for callback_info in self._filters:
if not isinstance(callback_info, self._FilterBasedCallback): if not isinstance(callback_info, self._FilterBasedCallback):
continue continue
try:
if not await callback_info.filter(room, event, self._client): if not await callback_info.filter(room, event, self._client):
continue continue
except asyncio.CancelledError:
raise
except:
self._logger.error(traceback.format_exc())
continue
event_data = RoomEventData( event_data = RoomEventData(
room=room, room=room,
event=event, event=event,

View File

@@ -14,9 +14,14 @@ class TextFilter(BaseEventFilter):
If this filter matches, you can access `event.body` and it stores If this filter matches, you can access `event.body` and it stores
unformatted text of the message. 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): def __init__(self, *, ignore_filename_in_body: bool = True, **kwargs):
super().__init__() super().__init__(**kwargs)
self._ignore_filename_in_body = ignore_filename_in_body
def __repr__(self) -> str: def __repr__(self) -> str:
return "TextFilter()" return "TextFilter()"
@@ -28,6 +33,10 @@ class TextFilter(BaseEventFilter):
return False return False
if not event.body.strip(): # type: ignore if not event.body.strip(): # type: ignore
return False 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 return True
class FormattedTextFilter(BaseEventFilter): class FormattedTextFilter(BaseEventFilter):
@@ -40,8 +49,8 @@ class FormattedTextFilter(BaseEventFilter):
If this filter matches, you can access `event.formatted_body` and it stores If this filter matches, you can access `event.formatted_body` and it stores
formatted text of the message. formatted text of the message.
""" """
def __init__(self): def __init__(self, **kwargs):
super().__init__() super().__init__(**kwargs)
def __repr__(self): def __repr__(self):
return "FormattedTextFilter()" return "FormattedTextFilter()"
@@ -68,8 +77,8 @@ class TextContainsFilter(TextFilter):
is True. That means you must ensure that `needle` is lower case. The filter is True. That means you must ensure that `needle` is lower case. The filter
will never match otherwise. will never match otherwise.
""" """
def __init__(self, needle: str | list[str], *, any_case: bool = True): def __init__(self, needle: str | list[str], *, any_case: bool = True, **kwargs):
super().__init__() super().__init__(**kwargs)
if type(needle) is str: if type(needle) is str:
needle = [needle] needle = [needle]
self._any_case = any_case 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 is True. That means you must ensure that `needle` is lower case. The filter
will never match otherwise. will never match otherwise.
""" """
def __init__(self, substring: str | list[str], *, any_case: bool = True): def __init__(self, substring: str | list[str], *, any_case: bool = True, **kwargs):
super().__init__() super().__init__(**kwargs)
if type(substring) is str: if type(substring) is str:
substring = [substring] substring = [substring]
self._any_case = any_case 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 is True. That means you must ensure that `needle` is lower case. The filter
will never match otherwise. will never match otherwise.
""" """
def __init__(self, substring: str | list[str], *, any_case: bool = True): def __init__(self, substring: str | list[str], *, any_case: bool = True, **kwargs):
super().__init__() super().__init__(**kwargs)
if type(substring) is str: if type(substring) is str:
substring = [substring] substring = [substring]
self._any_case = any_case 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 `event.command_args: list[str]`. You may use this attribute in your callback
for this event. for this event.
""" """
def __init__(self, verbs: str | list[str], min_args: int = 0, max_args: int | None = None, prefix: str = "!"): def __init__(self, verbs: str | list[str], min_args: int = 0, max_args: int | None = None, prefix: str = "!", **kwargs):
super().__init__() super().__init__(**kwargs)
if type(verbs) is str: if type(verbs) is str:
verbs = [verbs] verbs = [verbs]
self._verbs = verbs self._verbs = verbs
@@ -206,7 +215,8 @@ class TextRegexFilter(TextFilter):
""" """
This filter returns True if the `event.body` passes the regex. 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): if isinstance(regex, str):
regex = re.compile(regex) regex = re.compile(regex)
self._regex = regex self._regex = regex