diff --git a/mautrix/bridge/commands/__init__.py b/mautrix/bridge/commands/__init__.py index 894a0e28..5b558f48 100644 --- a/mautrix/bridge/commands/__init__.py +++ b/mautrix/bridge/commands/__init__.py @@ -1,6 +1,7 @@ from .handler import ( SECTION_ADMIN, SECTION_AUTH, + SECTION_CONNECTION, SECTION_GENERAL, CommandEvent, CommandHandler, @@ -12,7 +13,15 @@ ) from .meta import cancel, help_cmd, unknown_command -from . import admin, clean_rooms, crypto, delete_portal, login_matrix, manhole # isort: skip +from . import ( # isort: skip + admin, + clean_rooms, + crypto, + delete_portal, + login_matrix, + manhole, + relay, +) __all__ = [ "HelpSection", @@ -25,4 +34,5 @@ "SECTION_GENERAL", "SECTION_ADMIN", "SECTION_AUTH", + "SECTION_CONNECTION", ] diff --git a/mautrix/bridge/commands/handler.py b/mautrix/bridge/commands/handler.py index 77f7a707..daf68768 100644 --- a/mautrix/bridge/commands/handler.py +++ b/mautrix/bridge/commands/handler.py @@ -29,6 +29,7 @@ SECTION_GENERAL = HelpSection("General", 0, "") SECTION_AUTH = HelpSection("Authentication", 10, "") SECTION_ADMIN = HelpSection("Administration", 50, "") +SECTION_CONNECTION = HelpSection("Connection management", 15, "") def ensure_trailing_newline(s: str) -> str: diff --git a/mautrix/bridge/commands/relay.py b/mautrix/bridge/commands/relay.py new file mode 100644 index 00000000..31aa8c3f --- /dev/null +++ b/mautrix/bridge/commands/relay.py @@ -0,0 +1,45 @@ +# Copyright (c) 2021 Tulir Asokan +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +from mautrix.types import EventID + +from .handler import SECTION_CONNECTION, CommandEvent, command_handler + + +@command_handler( + needs_auth=True, + management_only=False, + name="set-relay", + help_section=SECTION_CONNECTION, + help_text="Relay messages in this room through your account.", +) +async def set_relay(evt: CommandEvent) -> EventID: + if not evt.config["bridge.relay.enabled"]: + return await evt.reply("Relay mode is not enable in this instance of the bridge.") + elif not evt.is_portal: + return await evt.reply("This is not a portal room.") + await evt.portal.set_relay_user(evt.sender) + return await evt.reply( + "Messages from non-logged-in users in this room will now be bridged " + "through your account." + ) + + +@command_handler( + needs_auth=True, + management_only=False, + name="unset-relay", + help_section=SECTION_CONNECTION, + help_text="Stop relaying messages in this room.", +) +async def unset_relay(evt: CommandEvent) -> EventID: + if not evt.config["bridge.relay.enabled"]: + return await evt.reply("Relay mode is not enable in this instance of the bridge.") + elif not evt.is_portal: + return await evt.reply("This is not a portal room.") + elif not evt.portal.has_relay: + return await evt.reply("This room does not have a relay user set.") + await evt.portal.set_relay_user(None) + return await evt.reply("Messages from non-logged-in users will no longer be bridged.") diff --git a/mautrix/bridge/config.py b/mautrix/bridge/config.py index 88439e37..916886d4 100644 --- a/mautrix/bridge/config.py +++ b/mautrix/bridge/config.py @@ -64,7 +64,7 @@ def forbidden_defaults(self) -> list[ForbiddenDefault]: ) def do_update(self, helper: ConfigUpdateHelper) -> None: - copy = helper.copy + copy, copy_dict = helper.copy, helper.copy_dict copy("homeserver.address") copy("homeserver.domain") @@ -100,6 +100,8 @@ def do_update(self, helper: ConfigUpdateHelper) -> None: copy("bridge.management_room_text.welcome_unconnected") copy("bridge.management_room_text.additional_help") copy("bridge.management_room_multiple_messages") + copy("bridge.relay.enabled") + copy_dict("bridge.relay.message_formats") copy("manhole.enabled") copy("manhole.path") diff --git a/mautrix/bridge/portal.py b/mautrix/bridge/portal.py index 083d1311..f1384a87 100644 --- a/mautrix/bridge/portal.py +++ b/mautrix/bridge/portal.py @@ -5,7 +5,7 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. from __future__ import annotations -from typing import Any +from typing import Any, Optional, Tuple from abc import ABC, abstractmethod from collections import defaultdict import asyncio @@ -35,6 +35,7 @@ class BasePortal(ABC): encrypted: bool is_direct: bool backfill_lock: SimpleLock + _relay_user: Optional["br.BaseUser"] @abstractmethod async def save(self) -> None: @@ -46,6 +47,33 @@ async def handle_matrix_message( ) -> None: pass + @property + @abstractmethod + async def has_relay(self) -> bool: + pass + + @property + @abstractmethod + async def get_relay_user(self) -> Optional["br.BaseUser"]: + pass + + @abstractmethod + async def set_relay_user(self, user: Optional["br.BaseUser"]) -> None: + pass + + @abstractmethod + async def get_relay_sender( + self, sender: br.BaseUser, evt_identifier: str + ) -> Tuple[Optional["br.BaseUser"], bool]: + pass + + @abstractmethod + async def apply_msg_format(self, sender: br.BaseUser, content: MessageEventContent) -> None: + pass + + async def get_displayname(self, user: br.BaseUser) -> str: + return await self.main_intent.get_room_displayname(self.mxid, user.mxid) or user.mxid + async def check_dm_encryption(self) -> bool | None: try: evt = await self.main_intent.get_state_event(self.mxid, EventType.ROOM_ENCRYPTION)