Compare commits
3 Commits
d14e110525
...
v0.4.0
| Author | SHA1 | Date | |
|---|---|---|---|
| f58c8601d1 | |||
| 159a43ebe6 | |||
| 9f4cd4948a |
16
README.md
16
README.md
@@ -18,7 +18,7 @@ The package supports the following features:
|
|||||||
Use `pip` to install this package:
|
Use `pip` to install this package:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
python -m pip install git+https://git.tyukalov.su/nikita/mab@v0.3.0
|
python -m pip install git+https://git.tyukalov.su/nikita/mab@v0.4.0
|
||||||
```
|
```
|
||||||
|
|
||||||
You should specify package version you want to use, because `main` without tags
|
You should specify package version you want to use, because `main` without tags
|
||||||
@@ -27,18 +27,16 @@ contain unstable code.
|
|||||||
## Basic usage
|
## Basic usage
|
||||||
|
|
||||||
This is the most simple bot you can create. It would respond to any message
|
This is the most simple bot you can create. It would respond to any message
|
||||||
that starts with `!test`, `!hello` or `!hi`.
|
that contains `hello` and `hi` words.
|
||||||
|
|
||||||
```python
|
```python
|
||||||
import asyncio
|
import asyncio
|
||||||
|
from pathlib import Path
|
||||||
from mab import MatrixBot, MatrixBotConfig
|
from mab import MatrixBot, MatrixBotConfig
|
||||||
from mab import TextCommandFilter
|
from mab import TextContainsFilter, SenderIsBotFilter
|
||||||
from mab.types import RoomEventData
|
from mab.types import RoomEventData
|
||||||
|
|
||||||
async def on_valid_command(data: RoomEventData) -> None:
|
async def on_message(data: RoomEventData) -> None:
|
||||||
# do not respond to ourselves
|
|
||||||
if event.sender == data.bot.get_client().user_id:
|
|
||||||
return
|
|
||||||
text = f"Your message contains {len(data.event.body)} symbols"
|
text = f"Your message contains {len(data.event.body)} symbols"
|
||||||
await data.bot.send_text_to_room(data.room, text)
|
await data.bot.send_text_to_room(data.room, text)
|
||||||
|
|
||||||
@@ -51,8 +49,8 @@ async def main() -> None:
|
|||||||
)
|
)
|
||||||
bot = MatrixBot(matrix_bot_config)
|
bot = MatrixBot(matrix_bot_config)
|
||||||
bot.add_callback(
|
bot.add_callback(
|
||||||
TextCommandFilter(["test", "hello", "hi"]),
|
~SenderIsBotFilter() & TextContainsFilter(["test", "hello", "hi"]),
|
||||||
on_valid_command
|
on_message
|
||||||
)
|
)
|
||||||
await bot.start()
|
await bot.start()
|
||||||
# wait for Ctrl+C
|
# wait for Ctrl+C
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "mab"
|
name = "mab"
|
||||||
version = "0.3.0"
|
version = "0.4.0"
|
||||||
authors = [
|
authors = [
|
||||||
{ name = "Tyukalov Nikita", email = "nikita@tyukalov.su" }
|
{ name = "Tyukalov Nikita", email = "nikita@tyukalov.su" }
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -51,7 +51,13 @@ 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
|
||||||
if not await callback_info.filter(room, event, self._client):
|
try:
|
||||||
|
if not await callback_info.filter(room, event, self._client):
|
||||||
|
continue
|
||||||
|
except asyncio.CancelledError:
|
||||||
|
raise
|
||||||
|
except:
|
||||||
|
self._logger.error(traceback.format_exc())
|
||||||
continue
|
continue
|
||||||
event_data = RoomEventData(
|
event_data = RoomEventData(
|
||||||
room=room,
|
room=room,
|
||||||
|
|||||||
@@ -191,11 +191,13 @@ class ClientManager:
|
|||||||
raise RuntimeError("The bot was never started")
|
raise RuntimeError("The bot was never started")
|
||||||
self._background_task.cancel()
|
self._background_task.cancel()
|
||||||
try:
|
try:
|
||||||
asyncio.shield(self._background_task)
|
await asyncio.shield(self._background_task)
|
||||||
|
except asyncio.CancelledError:
|
||||||
|
pass
|
||||||
except:
|
except:
|
||||||
self._logger.error(traceback.format_exc())
|
self._logger.error(traceback.format_exc())
|
||||||
try:
|
try:
|
||||||
asyncio.shield(self._close_client())
|
await asyncio.shield(self._close_client())
|
||||||
except:
|
except:
|
||||||
self._logger.error(traceback.format_exc())
|
self._logger.error(traceback.format_exc())
|
||||||
self._background_task = None
|
self._background_task = None
|
||||||
|
|||||||
28
src/mab/filters/__init__.py
Normal file
28
src/mab/filters/__init__.py
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
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"
|
||||||
|
]
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
from abc import ABC, abstractmethod
|
from abc import ABC, abstractmethod
|
||||||
import logging
|
import logging
|
||||||
|
from typing import Any, Type
|
||||||
|
|
||||||
from nio import AsyncClient
|
from nio import AsyncClient
|
||||||
from nio import MatrixRoom, Event
|
from nio import MatrixRoom, Event
|
||||||
@@ -52,30 +53,38 @@ class BaseEventFilter(ABC):
|
|||||||
)
|
)
|
||||||
|
|
||||||
# PAYLOAD
|
# PAYLOAD
|
||||||
@abstractmethod
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
"""This method must be redefined in derived classes to improve
|
|
||||||
debugging experience.
|
|
||||||
"""
|
"""
|
||||||
pass
|
This method may be redefined in derived classes to improve debugging
|
||||||
|
experience.
|
||||||
|
"""
|
||||||
|
return str(self.__class__.__name__)
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
async def __call__(self, room: MatrixRoom, event: Event, client: AsyncClient) -> bool:
|
async def __call__(self, room: MatrixRoom, event: Event, client: AsyncClient) -> bool:
|
||||||
"""This abstract method must be redefined in derived classes so that
|
|
||||||
the filter operates according to its description. This method must
|
|
||||||
not raise exceptions. In case of exception it should log it using
|
|
||||||
`self._logger` and return False
|
|
||||||
|
|
||||||
Args:
|
|
||||||
room - room the event has happened in
|
|
||||||
event - the event to check againts this filter
|
|
||||||
client - the client
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
True if the event satisfies this filter
|
|
||||||
False if the event does not satisfy this filter
|
|
||||||
"""
|
"""
|
||||||
pass
|
This abstract method must be redefined in derived classes so that the
|
||||||
|
filter operates according to its description. This method must not raise
|
||||||
|
exceptions. In case of exception it should log it using `self._logger`
|
||||||
|
and return False
|
||||||
|
|
||||||
|
Args:
|
||||||
|
- room - room the event has happened in
|
||||||
|
- event - the event to check againts this filter
|
||||||
|
- client - the client
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
- True if the event satisfies this filter
|
||||||
|
- False if the event does not satisfy this filter
|
||||||
|
"""
|
||||||
|
return True
|
||||||
|
|
||||||
|
class EventTypeFilter(BaseEventFilter):
|
||||||
|
"""Event filter that checks if the event is an instance of some class"""
|
||||||
|
def __init__(self, type: Type):
|
||||||
|
self._type = type
|
||||||
|
|
||||||
|
async def __call__(self, room: MatrixRoom, event: Event, client: AsyncClient) -> bool:
|
||||||
|
return isinstance(event, self._type)
|
||||||
|
|
||||||
class CompoundEventFilter(BaseEventFilter):
|
class CompoundEventFilter(BaseEventFilter):
|
||||||
"""Event filter that consists of multiple filters"""
|
"""Event filter that consists of multiple filters"""
|
||||||
|
|||||||
87
src/mab/filters/message.py
Normal file
87
src/mab/filters/message.py
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
import traceback
|
||||||
|
from .base import BaseEventFilter, EventTypeFilter
|
||||||
|
|
||||||
|
from nio import AsyncClient
|
||||||
|
from nio import MatrixRoom, Event
|
||||||
|
|
||||||
|
from nio import RedactionEvent
|
||||||
|
|
||||||
|
class NewMessageFilter(BaseEventFilter):
|
||||||
|
"""
|
||||||
|
This filter returns True if the event is a new message. Most filters are
|
||||||
|
derived from this base class because it ignores events about edited
|
||||||
|
messages.
|
||||||
|
"""
|
||||||
|
def __init__(self, **kwargs):
|
||||||
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
|
async def __call__(self, room: MatrixRoom, event: Event, client: AsyncClient) -> bool:
|
||||||
|
if not await super().__call__(room, event, client):
|
||||||
|
return False
|
||||||
|
return "m.new_content" not in event.source["content"]
|
||||||
|
|
||||||
|
class EditedMessageFilter(BaseEventFilter):
|
||||||
|
"""
|
||||||
|
This filter returns True if the event is an edited message. You may use this
|
||||||
|
filter to create callbacks that are called if the message gets edited.
|
||||||
|
"""
|
||||||
|
def __init__(self, **kwargs):
|
||||||
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
|
async def __call__(self, room: MatrixRoom, event: Event, client: AsyncClient) -> bool:
|
||||||
|
if not await super().__call__(room, event, client):
|
||||||
|
return False
|
||||||
|
return "m.new_content" in event.source["content"]
|
||||||
|
|
||||||
|
class RedactedMessageFilter(EventTypeFilter):
|
||||||
|
"""
|
||||||
|
This filter returns True if the event is a RedactionEvent.
|
||||||
|
"""
|
||||||
|
def __init__(self, **kwargs):
|
||||||
|
super().__init__(RedactionEvent, **kwargs)
|
||||||
|
|
||||||
|
async def __call__(self, room: MatrixRoom, event: Event, client: AsyncClient) -> bool:
|
||||||
|
return await super().__call__(room, event, client)
|
||||||
|
|
||||||
|
class SenderIsFilter(BaseEventFilter):
|
||||||
|
"""
|
||||||
|
This filter returns True if `event.sender` is any of specified senders.
|
||||||
|
`event.sender` is converted to lower case if `any_case` is True (default).
|
||||||
|
Supplied sender list is NEVER converted to lower case, so it is your duty to
|
||||||
|
use lower case if `any_case` is True.
|
||||||
|
|
||||||
|
`senders` list is stored by reference so you can modify behavior of this
|
||||||
|
filter dynamically.
|
||||||
|
"""
|
||||||
|
def __init__(self, sender: list[str] | str, *, any_case: bool = True, **kwargs):
|
||||||
|
super().__init__(**kwargs)
|
||||||
|
if isinstance(sender, str):
|
||||||
|
sender = [sender]
|
||||||
|
self._sender = sender
|
||||||
|
self._any_case = any_case
|
||||||
|
|
||||||
|
async def __call__(self, room: MatrixRoom, event: Event, client: AsyncClient) -> bool:
|
||||||
|
if not await super().__call__(room, event, client):
|
||||||
|
return False
|
||||||
|
sender = event.sender.lower() if self._any_case else event.sender
|
||||||
|
for s in self._sender:
|
||||||
|
if sender == s:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
class SenderIsBotFilter(BaseEventFilter):
|
||||||
|
"""
|
||||||
|
This filter returns True if `event.sender` is the client that has received
|
||||||
|
the event. You may use this filter to set callbacks for messages sent by
|
||||||
|
other users by using the following syntax:
|
||||||
|
```py
|
||||||
|
~SenderIsBotFilter()
|
||||||
|
```
|
||||||
|
"""
|
||||||
|
def __init__(self, **kwargs):
|
||||||
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
|
async def __call__(self, room: MatrixRoom, event: Event, client: AsyncClient) -> bool:
|
||||||
|
if not await super().__call__(room, event, client):
|
||||||
|
return False
|
||||||
|
return client.user_id == event.sender
|
||||||
@@ -3,93 +3,6 @@ from .base import BaseEventFilter
|
|||||||
from nio import AsyncClient
|
from nio import AsyncClient
|
||||||
from nio import MatrixRoom, Event
|
from nio import MatrixRoom, Event
|
||||||
|
|
||||||
class RoomIdContainsFilter(BaseEventFilter):
|
|
||||||
"""
|
|
||||||
This filter returns True if the `room.room_id` contains `needle` (or
|
|
||||||
any of needles from the list). The check will be case insensetive if
|
|
||||||
`any_case` is True.
|
|
||||||
"""
|
|
||||||
def __init__(self, needle: str | list[str], *, any_case: bool = True):
|
|
||||||
super().__init__()
|
|
||||||
if type(needle) is str:
|
|
||||||
needle = [needle]
|
|
||||||
self._any_case = any_case
|
|
||||||
if self._any_case:
|
|
||||||
self._needle = [s.lower() for s in needle]
|
|
||||||
else:
|
|
||||||
self._needle = list(needle)
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return f"RoomIdContainsFilter({repr(self._needle)}, any_case={repr(self._any_case)})"
|
|
||||||
|
|
||||||
async def __call__(self, room: MatrixRoom, event: Event, client: AsyncClient) -> bool:
|
|
||||||
try:
|
|
||||||
room_id = room.room_id.lower() if self._any_case else room.room_id
|
|
||||||
for s in self._needle:
|
|
||||||
if s in room_id:
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
except:
|
|
||||||
return False
|
|
||||||
|
|
||||||
class RoomIdStartsWithFilter(BaseEventFilter):
|
|
||||||
"""
|
|
||||||
This filter returns True if the `room.room_id` starts with `substring`
|
|
||||||
(or any of substrings from the list). The check will be case insensetive
|
|
||||||
if `any_case` is True.
|
|
||||||
"""
|
|
||||||
def __init__(self, substring: str | list[str], *, any_case: bool = True):
|
|
||||||
super().__init__()
|
|
||||||
if type(substring) is str:
|
|
||||||
substring = [substring]
|
|
||||||
self._any_case = any_case
|
|
||||||
if self._any_case:
|
|
||||||
self._substring = [s.lower() for s in substring]
|
|
||||||
else:
|
|
||||||
self._substring = list(substring)
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return f"RoomIdStartsWithFilter({repr(self._substring)}, any_case={repr(self._any_case)})"
|
|
||||||
|
|
||||||
async def __call__(self, room: MatrixRoom, event: Event, client: AsyncClient) -> bool:
|
|
||||||
try:
|
|
||||||
room_id = room.room_id.lower() if self._any_case else room.room_id
|
|
||||||
for s in self._substring:
|
|
||||||
if room_id.startswith(s):
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
except:
|
|
||||||
return False
|
|
||||||
|
|
||||||
class RoomIdEndsWithFilter(BaseEventFilter):
|
|
||||||
"""
|
|
||||||
This filter returns True if the `room.room_id` ends with `substring` (or
|
|
||||||
any of substrings from the list). The check will be case insensetive
|
|
||||||
if `any_case` is True.
|
|
||||||
"""
|
|
||||||
def __init__(self, substring: str | list[str], *, any_case: bool = True):
|
|
||||||
super().__init__()
|
|
||||||
if type(substring) is str:
|
|
||||||
substring = [substring]
|
|
||||||
self._any_case = any_case
|
|
||||||
if self._any_case:
|
|
||||||
self._substring = [s.lower() for s in substring]
|
|
||||||
else:
|
|
||||||
self._substring = list(substring)
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return f"RoomIdEndsWithFilter({repr(self._substring)}, any_case={repr(self._any_case)})"
|
|
||||||
|
|
||||||
async def __call__(self, room: MatrixRoom, event: Event, client: AsyncClient) -> bool:
|
|
||||||
try:
|
|
||||||
room_id = room.room_id.lower() if self._any_case else room.room_id
|
|
||||||
for s in self._substring:
|
|
||||||
if room_id.endswith(s):
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
except:
|
|
||||||
return False
|
|
||||||
|
|
||||||
class RoomEncryptedFilter(BaseEventFilter):
|
class RoomEncryptedFilter(BaseEventFilter):
|
||||||
"""
|
"""
|
||||||
This filter returns True if the room is encrypted.
|
This filter returns True if the room is encrypted.
|
||||||
@@ -97,9 +10,6 @@ class RoomEncryptedFilter(BaseEventFilter):
|
|||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return f"RoomEncryptedFilter()"
|
|
||||||
|
|
||||||
async def __call__(self, room: MatrixRoom, event: Event, client: AsyncClient) -> bool:
|
async def __call__(self, room: MatrixRoom, event: Event, client: AsyncClient) -> bool:
|
||||||
try:
|
try:
|
||||||
return room.encrypted
|
return room.encrypted
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import re
|
import re
|
||||||
import traceback
|
import traceback
|
||||||
from .base import BaseEventFilter
|
from .message import NewMessageFilter
|
||||||
|
|
||||||
from nio import AsyncClient
|
from nio import AsyncClient
|
||||||
from nio import MatrixRoom, Event
|
from nio import MatrixRoom, Event
|
||||||
|
|
||||||
class TextFilter(BaseEventFilter):
|
class TextFilter(NewMessageFilter):
|
||||||
"""
|
"""
|
||||||
This filter returns True if all conditions are met:
|
This filter returns True if all conditions are met:
|
||||||
1. `event` has attribute `body`
|
1. `event` has attribute `body`
|
||||||
@@ -14,12 +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.
|
||||||
"""
|
|
||||||
def __init__(self):
|
|
||||||
super().__init__()
|
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
If `event.body` value equals to `event.source["content"]["filename"]` (if it
|
||||||
return "TextFilter()"
|
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, *, 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:
|
async def __call__(self, room: MatrixRoom, event: Event, client: AsyncClient) -> bool:
|
||||||
if not hasattr(event, "body"):
|
if not hasattr(event, "body"):
|
||||||
@@ -28,31 +30,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
|
||||||
return True
|
if self._ignore_filename_in_body:
|
||||||
|
content = event.source["content"]
|
||||||
class FormattedTextFilter(BaseEventFilter):
|
if "filename" in content and content["filename"] == event.body: # type: ignore
|
||||||
"""
|
return False
|
||||||
This filter returns True if all conditions are met:
|
|
||||||
1. `event` has attribute `formatted_body`
|
|
||||||
2. `event.formatted_body` is instance of `str`
|
|
||||||
3. `event.formatted_body.strip()` evaluates to True
|
|
||||||
|
|
||||||
If this filter matches, you can access `event.formatted_body` and it stores
|
|
||||||
formatted text of the message.
|
|
||||||
"""
|
|
||||||
def __init__(self):
|
|
||||||
super().__init__()
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return "FormattedTextFilter()"
|
|
||||||
|
|
||||||
async def __call__(self, room: MatrixRoom, event: Event, client: AsyncClient) -> bool:
|
|
||||||
if not hasattr(event, "formatted_body"):
|
|
||||||
return False
|
|
||||||
if not isinstance(event.formatted_body, str): # type: ignore
|
|
||||||
return False
|
|
||||||
if not event.formatted_body.strip(): # type: ignore
|
|
||||||
return False
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
class TextContainsFilter(TextFilter):
|
class TextContainsFilter(TextFilter):
|
||||||
@@ -68,16 +49,13 @@ 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
|
||||||
self._needle = needle
|
self._needle = needle
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
|
||||||
return f"TextContainsFilter({repr(self._needle)}, any_case={repr(self._any_case)})"
|
|
||||||
|
|
||||||
async def __call__(self, room: MatrixRoom, event: Event, client: AsyncClient) -> bool:
|
async def __call__(self, room: MatrixRoom, event: Event, client: AsyncClient) -> bool:
|
||||||
if not await super().__call__(room, event, client):
|
if not await super().__call__(room, event, client):
|
||||||
return False
|
return False
|
||||||
@@ -100,16 +78,13 @@ 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
|
||||||
self._substring = substring
|
self._substring = substring
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return f"TextStartsWithFilter({repr(self._substring)}, any_case={repr(self._any_case)})"
|
|
||||||
|
|
||||||
async def __call__(self, room: MatrixRoom, event: Event, client: AsyncClient) -> bool:
|
async def __call__(self, room: MatrixRoom, event: Event, client: AsyncClient) -> bool:
|
||||||
if not await super().__call__(room, event, client):
|
if not await super().__call__(room, event, client):
|
||||||
return False
|
return False
|
||||||
@@ -132,16 +107,13 @@ 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
|
||||||
self._substring = substring
|
self._substring = substring
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return f"TextEndsWithFilter({repr(self._substring)}, any_case={repr(self._any_case)})"
|
|
||||||
|
|
||||||
async def __call__(self, room: MatrixRoom, event: Event, client: AsyncClient) -> bool:
|
async def __call__(self, room: MatrixRoom, event: Event, client: AsyncClient) -> bool:
|
||||||
if not await super().__call__(room, event, client):
|
if not await super().__call__(room, event, client):
|
||||||
return False
|
return False
|
||||||
@@ -172,8 +144,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
|
||||||
@@ -181,9 +153,6 @@ class TextCommandFilter(TextFilter):
|
|||||||
self._max_args = max_args
|
self._max_args = max_args
|
||||||
self._prefix = prefix
|
self._prefix = prefix
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return f"TextCommandFilter({repr(self._verbs)}, {repr(self._min_args)}, {repr(self._max_args)}, {repr(self._prefix)})"
|
|
||||||
|
|
||||||
async def __call__(self, room: MatrixRoom, event: Event, client: AsyncClient) -> bool:
|
async def __call__(self, room: MatrixRoom, event: Event, client: AsyncClient) -> bool:
|
||||||
if not await super().__call__(room, event, client):
|
if not await super().__call__(room, event, client):
|
||||||
return False
|
return False
|
||||||
@@ -206,14 +175,12 @@ 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
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return f"TextRegexFilter({repr(self._regex)})"
|
|
||||||
|
|
||||||
async def __call__(self, room: MatrixRoom, event: Event, client: AsyncClient) -> bool:
|
async def __call__(self, room: MatrixRoom, event: Event, client: AsyncClient) -> bool:
|
||||||
if not await super().__call__(room, event, client):
|
if not await super().__call__(room, event, client):
|
||||||
return False
|
return False
|
||||||
|
|||||||
Reference in New Issue
Block a user