diff --git a/matrix_puppeteer_line/example-config.yaml b/matrix_puppeteer_line/example-config.yaml index a253f68..3c922e2 100644 --- a/matrix_puppeteer_line/example-config.yaml +++ b/matrix_puppeteer_line/example-config.yaml @@ -79,7 +79,8 @@ bridge: # Set 0 to disable automatic syncing. initial_conversation_sync: 10 # Whether or not the LINE users of logged in Matrix users should be - # invited to private chats when the user sends a message from another client. + # invited to rooms when the user sends a message from another client. + # NOTE: This setting is forced to "false" when custom puppets are enabled. invite_own_puppet_to_pm: false # Shared secret for https://github.com/devture/matrix-synapse-shared-secret-auth # diff --git a/matrix_puppeteer_line/portal.py b/matrix_puppeteer_line/portal.py index 5ab0568..6bd2871 100644 --- a/matrix_puppeteer_line/portal.py +++ b/matrix_puppeteer_line/portal.py @@ -29,7 +29,7 @@ from mautrix.types import (EventID, MessageEventContent, RoomID, EventType, Mess TextMessageEventContent, MediaMessageEventContent, Membership, Format, ContentURI, EncryptedFile, ImageInfo, RelatesTo, RelationType) -from mautrix.errors import MatrixError +from mautrix.errors import IntentError, MatrixError from mautrix.util.simple_lock import SimpleLock from .db import Portal as DBPortal, Message as DBMessage, ReceiptReaction as DBReceiptReaction, Media as DBMedia @@ -91,6 +91,12 @@ class Portal(DBPortal, BasePortal): def is_room(self) -> bool: return self.chat_id[0] == "r" + @property + def needs_bridgebot(self) -> bool: + # TODO Ask Tulir why e2b needs the bridgebot to be in the room + # Reminder that the bridgebot's intent is used for non-DM rooms + return not self.is_direct or (self.encrypted and self.matrix.e2ee) + @property def main_intent(self) -> IntentAPI: if not self._main_intent: @@ -110,6 +116,7 @@ class Portal(DBPortal, BasePortal): NotificationDisabler.config_enabled = cls.config["bridge.backfill.disable_notifications"] async def _send_delivery_receipt(self, event_id: EventID) -> None: + # TODO Also send receipt from own puppet, if it's in the room if event_id and self.config["bridge.delivery_receipts"]: try: await self.az.intent.mark_read(self.mxid, event_id) @@ -187,7 +194,15 @@ class Portal(DBPortal, BasePortal): intent = sender.intent if sender else self.az.intent if self.is_direct and (sender is None or sender.mid == source.mid and not sender.is_real_user): if self.invite_own_puppet_to_pm and invite: - await self.main_intent.invite_user(self.mxid, intent.mxid) + try: + await intent.ensure_joined(self.mxid) + except IntentError as e: + if self.main_intent != self.az.intent: + await self.main_intent.invite_user(self.mxid, intent.mxid) + await intent.ensure_joined(self.mxid) + else: + self.log.warning(f"Unable to invite own puppet to {self.mxid}: {e}") + intent = None elif await self.az.state_store.get_membership(self.mxid, intent.mxid) != Membership.JOIN: self.log.warning(f"Ignoring own {mid} in private chat because own puppet is not in" @@ -427,9 +442,16 @@ class Portal(DBPortal, BasePortal): return self._last_participant_update = current_members + # TODO When supporting multiple bridge users, do this per user + forbid_own_puppets = \ + not self.invite_own_puppet_to_pm or \ + (await u.User.get_by_mxid(self.config["bridge.user"], False)).intent is not None + # Make sure puppets who should be here are here for participant in participants: puppet = await p.Puppet.get_by_mid(participant.id) + if forbid_own_puppets and p.Puppet.is_mid_for_own_puppet(participant.id): + continue await puppet.intent.ensure_joined(self.mxid) print(current_members) @@ -437,12 +459,18 @@ class Portal(DBPortal, BasePortal): # Kick puppets who shouldn't be here for user_id in await self.main_intent.get_room_members(self.mxid): if user_id == self.az.bot_mxid: + if forbid_own_puppets and not self.needs_bridgebot: + await self.az.intent.leave_room(self.mxid) continue + mid = p.Puppet.get_id_from_mxid(user_id) if mid and mid not in current_members: print(mid) await self.main_intent.kick_user(self.mxid, user_id, reason="User had left this chat") + elif forbid_own_puppets and p.Puppet.is_mid_for_own_puppet(mid): + await self.main_intent.kick_user(self.mxid, user_id, + reason="Kicking own puppet") async def backfill(self, source: 'u.User') -> None: try: @@ -594,7 +622,7 @@ class Portal(DBPortal, BasePortal): if not self.mxid: raise Exception("Failed to create room: no mxid returned") - if self.encrypted and self.matrix.e2ee and self.is_direct: + if self.needs_bridgebot: try: await self.az.intent.ensure_joined(self.mxid) except Exception: diff --git a/matrix_puppeteer_line/puppet.py b/matrix_puppeteer_line/puppet.py index 24a3460..97c7976 100644 --- a/matrix_puppeteer_line/puppet.py +++ b/matrix_puppeteer_line/puppet.py @@ -148,6 +148,11 @@ class Puppet(DBPuppet, BasePuppet): return None + # TODO When supporting multiple bridge users, this should return the user whose puppet this is + @classmethod + def is_mid_for_own_puppet(cls, mid) -> bool: + return mid.startswith("_OWN_") if mid else False + @classmethod async def get_by_custom_mxid(cls, mxid: UserID) -> Optional['u.User']: if mxid == cls.config["bridge.user"]: