From b22e7b49037c568e5d4558cd9bb4ef7b828642ee Mon Sep 17 00:00:00 2001 From: nikita Date: Sun, 27 Sep 2026 16:47:08 +0300 Subject: [PATCH] Added room operations (not tested) --- README.md | 2 +- pyproject.toml | 2 +- src/mab/bot/_client_room_operations.py | 147 +++++++++++++++++++++++++ src/mab/bot/bot.py | 15 ++- 4 files changed, 163 insertions(+), 3 deletions(-) create mode 100644 src/mab/bot/_client_room_operations.py diff --git a/README.md b/README.md index ba555f7..06b185a 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ install the latest version of the library: ```bash apt install libmagic1-dev libolm-dev -python -m pip install git+https://git.tyukalov.su/nikita/mab@v0.6.0 +python -m pip install git+https://git.tyukalov.su/nikita/mab@v0.6.1 ``` `libmagic1-dev` is needed for automatic file MIME type detection, `libolm-dev` diff --git a/pyproject.toml b/pyproject.toml index b44f806..d3fbc43 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "mab" -version = "0.6.0" +version = "0.6.1" authors = [ { name = "Tyukalov Nikita", email = "nikita@tyukalov.su" } ] diff --git a/src/mab/bot/_client_room_operations.py b/src/mab/bot/_client_room_operations.py new file mode 100644 index 0000000..8b08a32 --- /dev/null +++ b/src/mab/bot/_client_room_operations.py @@ -0,0 +1,147 @@ +import logging +from nio import ( + AsyncClient, + Event, + MatrixRoom, + MatrixUser, + RoomMember, + + RoomGetEventResponse, + RoomLeaveResponse, + JoinResponse, + RoomInviteResponse, + RoomKickResponse, + RoomBanResponse, + RoomUnbanResponse, + JoinedMembersResponse +) +from ..utils import MatrixBotConfig + +class ClientRoomOperations: + """This class works with rooms.""" + + @staticmethod + def _room_id(room: MatrixRoom | str) -> str: + """Convert room to its ID if needed""" + return room.room_id if isinstance(room, MatrixRoom) else room + + @staticmethod + def _user_id(user: MatrixUser | str) -> str: + """Convert user to its ID if needed""" + return user.user_id if isinstance(user, MatrixUser) else user + + def __init__(self): + self._logger = logging.getLogger("ClientRoomOperations") + self._config: MatrixBotConfig | None = None + self._client: AsyncClient | None = None + + async def setup(self, + config: MatrixBotConfig, + client: AsyncClient) -> None: + """Setup room operations. + + Args: + - config - config to use + - client - client to use + """ + self._config = config + self._client = client + + async def event(self, room: MatrixRoom | str, event_id: str) -> Event: + """Get raw event information from matrix server (no cache used yet). + + Args: + - room - room where the event happened + - event_id - ID of the event + + Returns: + - `nio.Event` on success + - raises an exception on matrix error + """ + if self._config is None or self._client is None: + raise RuntimeError("ClientRoom is not set up") + res = await self._client.room_get_event( + self._room_id(room), + event_id + ) + if isinstance(res, RoomGetEventResponse): + return res.event + else: + raise RuntimeError(res) + + async def leave(self, room: MatrixRoom | str) -> None: + """Leave the room (or reject the invite).""" + if self._config is None or self._client is None: + raise RuntimeError("ClientRoom is not set up") + res = await self._client.room_leave(self._room_id(room)) + if not isinstance(res, RoomLeaveResponse): + raise RuntimeError(res) + + async def join(self, room: MatrixRoom | str) -> None: + """Join the room.""" + if self._config is None or self._client is None: + raise RuntimeError("ClientRoom is not set up") + res = await self._client.join( + self._room_id(room) + ) + if not isinstance(res, JoinResponse): + raise RuntimeError(res) + + async def invite(self, room: MatrixRoom | str, user: MatrixUser | str) -> None: + """Invite the user to the room.""" + if self._config is None or self._client is None: + raise RuntimeError("ClientRoom is not set up") + res = await self._client.room_invite( + self._room_id(room), + self._user_id(user) + ) + if not isinstance(res, RoomInviteResponse): + raise RuntimeError(res) + + async def kick(self, room: MatrixRoom | str, user: MatrixUser | str, reason: str | None = None) -> None: + """Kick the user from the room. The user will be able to join the room + again. + """ + if self._config is None or self._client is None: + raise RuntimeError("ClientRoom is not set up") + res = await self._client.room_kick( + self._room_id(room), + self._user_id(user), + reason + ) + if not isinstance(res, RoomKickResponse): + raise RuntimeError(res) + + async def ban(self, room: MatrixRoom | str, user: MatrixUser | str, reason: str | None = None) -> None: + """Ban the user from the room. The user will be unable to join until he + is unbanned. + """ + if self._config is None or self._client is None: + raise RuntimeError("ClientRoom is not set up") + res = await self._client.room_ban( + self._room_id(room), + self._user_id(user), + reason + ) + if not isinstance(res, RoomBanResponse): + raise RuntimeError(res) + + async def unban(self, room: MatrixRoom | str, user: MatrixUser | str) -> None: + """Unban the user from the room.""" + if self._config is None or self._client is None: + raise RuntimeError("ClientRoom is not set up") + res = await self._client.room_unban( + self._room_id(room), + self._user_id(user) + ) + if not isinstance(res, RoomUnbanResponse): + raise RuntimeError(res) + + async def members(self, room: MatrixRoom | str) -> list[RoomMember]: + """Get list of room members.""" + if self._config is None or self._client is None: + raise RuntimeError("ClientRoom is not set up") + res = await self._client.joined_members(self._room_id(room)) + if not isinstance(res, JoinedMembersResponse): + raise RuntimeError(res) + return res.members \ No newline at end of file diff --git a/src/mab/bot/bot.py b/src/mab/bot/bot.py index 5fb086d..dd353f2 100644 --- a/src/mab/bot/bot.py +++ b/src/mab/bot/bot.py @@ -12,6 +12,7 @@ from ..context import EventContext from ._validation import Validator from ._storage import Storage from ._client_auth import ClientAuth +from ._client_room_operations import ClientRoomOperations from ._client_downloader import ClientDownloader from ._client_manager import ClientManager from ._client_uploader import ClientUploader @@ -35,6 +36,7 @@ class MatrixBot: self._client_manager = ClientManager(self._client_auth, self._storage) self._client_uploader = ClientUploader(self._storage) self._client_downloader = ClientDownloader() + self._client_room_operations = ClientRoomOperations() self._client_sender = ClientSender() self._callbacks = Callbacks(self._storage, self) # validate the config and save it @@ -86,6 +88,10 @@ class MatrixBot: self._config, self._client_manager.get_client() ) + await self._client_room_operations.setup( + self._config, + self._client_manager.get_client() + ) await self._client_sender.setup( self._config, self._client_manager.get_client(), @@ -138,4 +144,11 @@ class MatrixBot: @property def download(self) -> ClientDownloader: """Get ClientDownloader that you should use to download files.""" - return self._client_downloader \ No newline at end of file + return self._client_downloader + + @property + def rooms(self) -> ClientRoomOperations: + """Get ClientRoomOperations that you should use to do room-related + actions. + """ + return self._client_room_operations \ No newline at end of file