diff --git a/example-config.yaml b/example-config.yaml index fc20633a..ea03b25f 100644 --- a/example-config.yaml +++ b/example-config.yaml @@ -144,11 +144,15 @@ bridge: leave: "$displayname left the room." name_change: "$prev_displayname changed their name to $displayname" + # Filter rooms that can/can't be bridged. Can also be managed using the `filter` and + # `filter-mode` management commands. + # + # Filters do not affect direct chats. + # An empty blacklist will essentially disable the filter. filter: # Filter mode to use. Either "blacklist" or "whitelist". - # If the mode is "blacklist", the listed chats will never be bridged. An empty blacklist disables the filter. + # If the mode is "blacklist", the listed chats will never be bridged. # If the mode is "whitelist", only the listed chats can be bridged. - # Direct chats are not affected. mode: blacklist # The list of group/channel IDs to filter. list: [] @@ -159,6 +163,7 @@ bridge: # Permissions for using the bridge. # Permitted values: # relaybot - Only use the bridge via the relaybot, no access to commands. + # user - Relaybot level + access to commands to create bridges (no puppeting) # full - Full access to use the bridge via relaybot or logging in with Telegram account. # admin - Full access to use the bridge and some extra administration commands. # Permitted keys: @@ -167,8 +172,8 @@ bridge: # mxid - Specific user permissions: "*": "relaybot" + "public.example.com": "user" "example.com": "full" - "public.example.com": "full" "@admin:example.com": "admin" # Options related to the message relay Telegram bot. diff --git a/mautrix_telegram/abstract_user.py b/mautrix_telegram/abstract_user.py index 4a2b4451..74c2ee08 100644 --- a/mautrix_telegram/abstract_user.py +++ b/mautrix_telegram/abstract_user.py @@ -36,7 +36,10 @@ class AbstractUser: az = None def __init__(self): + self.puppet_whitelisted = False self.whitelisted = False + self.relaybot_whitelisted = False + self.is_admin = False self.client = None self.tgid = None self.mxid = None @@ -93,7 +96,7 @@ class AbstractUser: return self.client and await self.client.is_user_authorized() async def has_full_access(self, allow_bot=False): - return self.whitelisted and (not self.is_bot or allow_bot) and await self.is_logged_in() + return self.puppet_whitelisted and (not self.is_bot or allow_bot) and await self.is_logged_in() async def start(self, delete_unless_authenticated=False): if not self.client: @@ -103,7 +106,7 @@ class AbstractUser: return self async def ensure_started(self, even_if_no_session=False): - if not self.whitelisted: + if not self.puppet_whitelisted: return self self.log.debug("ensure_started(%s, connected=%s, even_if_no_session=%s, session_count=%s)", self.mxid, self.connected, even_if_no_session, diff --git a/mautrix_telegram/bot.py b/mautrix_telegram/bot.py index 575ea841..c05a62aa 100644 --- a/mautrix_telegram/bot.py +++ b/mautrix_telegram/bot.py @@ -39,7 +39,9 @@ class Bot(AbstractUser): def __init__(self, token: str): super().__init__() self.token = token + self.puppet_whitelisted = True self.whitelisted = True + self.relaybot_whitelisted = True self.username = None self.is_relaybot = True self.is_bot = True diff --git a/mautrix_telegram/commands/auth.py b/mautrix_telegram/commands/auth.py index 1256ca6b..77a711ea 100644 --- a/mautrix_telegram/commands/auth.py +++ b/mautrix_telegram/commands/auth.py @@ -32,7 +32,7 @@ async def ping(evt): return await evt.reply("You're not logged in.") -@command_handler() +@command_handler(needs_auth=False, needs_puppeting=False) async def ping_bot(evt): if not evt.tgbot: return await evt.reply("Telegram message relay bot not configured.") diff --git a/mautrix_telegram/commands/handler.py b/mautrix_telegram/commands/handler.py index bca3b55b..dbfbfcad 100644 --- a/mautrix_telegram/commands/handler.py +++ b/mautrix_telegram/commands/handler.py @@ -24,7 +24,8 @@ from ..util import format_duration command_handlers = {} -def command_handler(needs_auth=True, management_only=False, needs_admin=False, name=None): +def command_handler(needs_auth=True, management_only=False, needs_puppeting=True, + needs_admin=False, name=None): def decorator(func): async def wrapper(evt): if management_only and not evt.is_management: @@ -32,8 +33,10 @@ def command_handler(needs_auth=True, management_only=False, needs_admin=False, n "you may only run it in management rooms.") elif needs_auth and not await evt.sender.is_logged_in(): return await evt.reply("This command requires you to be logged in.") + elif needs_puppeting and not evt.sender.puppet_whitelisted: + return await evt.reply("This command requires puppeting privileges.") elif needs_admin and not evt.sender.is_admin: - return await evt.reply("This is command requires administrator privileges.") + return await evt.reply("This command requires administrator privileges.") return await func(evt) command_handlers[name or func.__name__.replace("_", "-")] = wrapper diff --git a/mautrix_telegram/commands/meta.py b/mautrix_telegram/commands/meta.py index f2b52b60..fc4ef4be 100644 --- a/mautrix_telegram/commands/meta.py +++ b/mautrix_telegram/commands/meta.py @@ -17,7 +17,7 @@ from . import command_handler -@command_handler(needs_auth=False) +@command_handler(needs_auth=False, needs_puppeting=False) def cancel(evt): if evt.sender.command_status: action = evt.sender.command_status["action"] @@ -27,12 +27,12 @@ def cancel(evt): return evt.reply("No ongoing command.") -@command_handler(needs_auth=False) +@command_handler(needs_auth=False, needs_puppeting=False) def unknown_command(evt): return evt.reply("Unknown command. Try `$cmdprefix+sp help` for help.") -@command_handler(needs_auth=False) +@command_handler(needs_auth=False, needs_puppeting=False) def help(evt): if evt.is_management: management_status = ("This is a management room: prefixing commands " @@ -44,7 +44,24 @@ def help(evt): else: management_status = ("**This is not a management room**: you must " "prefix commands with `$cmdprefix`.\n") - help = """\n + help = None + if not evt.sender.puppet_whitelisted: + help = """\n +#### Generic bridge commands +**help** - Show this help message. +**cancel** - Cancel an ongoing action (such as login). +**ping-bot** - Get info of the message relay Telegram bot. +**invite-link** - Get a Telegram invite link to the current chat. +**delete-portal** - Remove all users from the current portal room and forget the portal. + Only works for group chats; to delete a private chat portal, simply + leave the room. +**unbridge** - Remove puppets from the current portal room and forget the portal. +**bridge** [_id_] - Bridge the current Matrix room to the Telegram chat with the given + ID. The ID must be the prefixed version that you get with the `/id` + command of the Telegram-side bot. + +""" + help = help or """\n #### Generic bridge commands **help** - Show this help message. **cancel** - Cancel an ongoing action (such as login). diff --git a/mautrix_telegram/commands/portal.py b/mautrix_telegram/commands/portal.py index 780c9a29..8b9eee00 100644 --- a/mautrix_telegram/commands/portal.py +++ b/mautrix_telegram/commands/portal.py @@ -103,7 +103,7 @@ def _get_portal_murder_function(action, room_id, function, command, completed_me } -@command_handler(needs_auth=False) +@command_handler(needs_auth=False, needs_puppeting=False) async def delete_portal(evt: CommandEvent): portal, ok = await _get_portal_and_check_permission(evt, "delete_portal") if not ok: diff --git a/mautrix_telegram/config.py b/mautrix_telegram/config.py index c803a8d0..09476659 100644 --- a/mautrix_telegram/config.py +++ b/mautrix_telegram/config.py @@ -224,9 +224,10 @@ class Config(DictWithRecursion): def _get_permissions(self, key): level = self["bridge.permissions"].get(key, "") admin = level == "admin" - whitelisted = level == "full" or admin - relaybot = level == "relaybot" or whitelisted - return relaybot, whitelisted, admin + puppeting = level == "full" or admin + user = level == "user" or puppeting + relaybot = level == "relaybot" or user + return relaybot, user, puppeting, admin def get_permissions(self, mxid): permissions = self["bridge.permissions"] or {} diff --git a/mautrix_telegram/public/__init__.py b/mautrix_telegram/public/__init__.py index 2dfd4548..6a463f2e 100644 --- a/mautrix_telegram/public/__init__.py +++ b/mautrix_telegram/public/__init__.py @@ -50,7 +50,7 @@ class PublicBridgeWebsite: return self.render_login( mxid=request.rel_url.query["mxid"] if "mxid" in request.rel_url.query else None, state=state) - elif not user.whitelisted: + elif not user.puppet_whitelisted: return self.render_login(mxid=user.mxid, error="You are not whitelisted.", status=403) await user.ensure_started() if not await user.is_logged_in(): @@ -160,7 +160,7 @@ class PublicBridgeWebsite: return self.render_login(error="Please enter your Matrix ID.", status=400) user = await User.get_by_mxid(data["mxid"]).ensure_started() - if not user.whitelisted: + if not user.puppet_whitelisted: return self.render_login(mxid=user.mxid, error="You are not whitelisted.", status=403) elif await user.is_logged_in(): return self.render_login(mxid=user.mxid, username=user.username) diff --git a/mautrix_telegram/user.py b/mautrix_telegram/user.py index 8eb7cc57..1a72fd0a 100644 --- a/mautrix_telegram/user.py +++ b/mautrix_telegram/user.py @@ -53,6 +53,7 @@ class User(AbstractUser): (self.relaybot_whitelisted, self.whitelisted, + self.puppet_whitelisted, self.is_admin) = config.get_permissions(self.mxid) self.by_mxid[mxid] = self