async def say_from_matrix(self,
                              user_id,
                              room_id,
                              event_id,
                              body,
                              emote=False):

        namespace = self.config['appservice.namespace']
        if user_id.startswith(f"@{namespace}"):
            return

        self.log.debug(f"room ID = {room_id}")
        self.log.debug(f"user ID = {user_id}")

        room_name = None
        for k, v in self.rooms.items():
            if v.get("room_id") == room_id:
                room_name = k

        if room_name is None:
            return

        # room_name = list(v.get('name') for _, v in self.rooms.items() if v.get('room_id') == room_id)[0]

        room_data = self.rooms.get(room_name)

        channel = room_data.get('name')
        stored_room_id = room_data.get('room_id')
        enabled = room_data.get('enabled')

        if enabled is False:
            self.log.debug(f"room id: {stored_room_id} active: {enabled}")
            return

        user_name = self.appserv.intent.user(user_id=UserID(user_id)).localpart
        domain = self.appserv.intent.user(user_id=UserID(user_id)).domain

        self.log.debug(f"User Name = {user_name}")

        if user_name.startswith("_discord"):
            domain = "discord"
            user_name = user_name.lstrip("_discord_")

        elif user_name.startswith("freenode_"):
            domain = "frenode.org"
            user_name = user_name.lstrip("freenode_")

        if len(user_name) > 15:
            user_name = user_name[:15]

        # if emote is True:
        #     self.bot.say_ex(user_name, domain, channel, body)
        # else:
        self.bot.say_from(user_name, domain, channel, body)

        await self.appserv.intent.mark_read(room_id=room_id, event_id=event_id)
Example #2
0
    async def int_handle_event(self, evt: Event) -> None:
        if isinstance(evt, StateEvent) and evt.type == EventType.ROOM_MEMBER and self.e2ee:
            await self.e2ee.handle_member_event(evt)
        if self.filter_matrix_event(evt):
            return
        self.log.trace("Received event: %s", evt)
        start_time = time.time()

        if evt.type == EventType.ROOM_MEMBER:
            evt: StateEvent
            prev_content = evt.unsigned.prev_content or MemberStateEventContent()
            prev_membership = prev_content.membership if prev_content else Membership.JOIN
            if evt.content.membership == Membership.INVITE:
                await self.int_handle_invite(evt.room_id, UserID(evt.state_key), evt.sender,
                                             evt.event_id)
            elif evt.content.membership == Membership.LEAVE:
                if prev_membership == Membership.BAN:
                    await self.handle_unban(evt.room_id, UserID(evt.state_key), evt.sender,
                                            evt.content.reason, evt.event_id)
                elif prev_membership == Membership.INVITE:
                    if evt.sender == evt.state_key:
                        await self.handle_reject(evt.room_id, UserID(evt.state_key),
                                                 evt.content.reason, evt.event_id)
                    else:
                        await self.handle_disinvite(evt.room_id, UserID(evt.state_key), evt.sender,
                                                    evt.content.reason, evt.event_id)
                elif evt.sender == evt.state_key:
                    await self.handle_leave(evt.room_id, UserID(evt.state_key), evt.event_id)
                else:
                    await self.handle_kick(evt.room_id, UserID(evt.state_key), evt.sender,
                                           evt.content.reason, evt.event_id)
            elif evt.content.membership == Membership.BAN:
                await self.handle_ban(evt.room_id, UserID(evt.state_key), evt.sender,
                                      evt.content.reason, evt.event_id)
            elif evt.content.membership == Membership.JOIN:
                if prev_membership != Membership.JOIN:
                    await self.handle_join(evt.room_id, UserID(evt.state_key), evt.event_id)
                else:
                    await self.handle_member_info_change(evt.room_id, UserID(evt.state_key),
                                                         evt.content, prev_content, evt.event_id)
        elif evt.type in (EventType.ROOM_MESSAGE, EventType.STICKER):
            evt: MessageEvent
            if evt.type != EventType.ROOM_MESSAGE:
                evt.content.msgtype = MessageType(str(evt.type))
            await self.handle_message(evt.room_id, evt.sender, evt.content, evt.event_id)
        elif evt.type == EventType.ROOM_ENCRYPTED and self.e2ee:
            await self.handle_encrypted(evt)
        elif evt.type == EventType.ROOM_ENCRYPTION:
            await self.handle_encryption(evt)
        else:
            if evt.type.is_state and isinstance(evt, StateEvent):
                await self.handle_state_event(evt)
            else:
                await self.handle_event(evt)

        await self.log_event_handle_duration(evt, time.time() - start_time)
Example #3
0
 def verify_token(self, token: str) -> UserID:
     token = verify_token(self.secret_key, token)
     if token:
         if token.get("expiry", 0) < int(time.time()):
             raise InvalidTokenError("Access token has expired")
         return UserID(token.get("mxid"))
     raise InvalidTokenError("Access token is invalid")
Example #4
0
    def __init__(self,
                 server: str,
                 domain: str,
                 as_token: str,
                 hs_token: str,
                 bot_localpart: str,
                 id: str,
                 loop: Optional[asyncio.AbstractEventLoop] = None,
                 log: Optional[Union[logging.Logger, str]] = None,
                 verify_ssl: bool = True,
                 tls_cert: Optional[str] = None,
                 tls_key: Optional[str] = None,
                 query_user: QueryFunc = None,
                 query_alias: QueryFunc = None,
                 real_user_content_key: Optional[
                     str] = "net.maunium.appservice.puppet",
                 state_store: ASStateStore = None,
                 aiohttp_params: Dict = None,
                 ephemeral_events: bool = False) -> None:
        super().__init__(ephemeral_events=ephemeral_events)
        self.server = server
        self.domain = domain
        self.id = id
        self.verify_ssl = verify_ssl
        self.tls_cert = tls_cert
        self.tls_key = tls_key
        self.as_token = as_token
        self.hs_token = hs_token
        self.bot_mxid = UserID(f"@{bot_localpart}:{domain}")
        self.real_user_content_key: str = real_user_content_key
        if not state_store:
            file = state_store if isinstance(state_store,
                                             str) else "mx-state.json"
            self.state_store = FileASStateStore(path=file, binary=False)
        elif isinstance(state_store, ASStateStore):
            self.state_store = state_store
        else:
            raise ValueError(f"Unsupported state store {type(state_store)}")

        self._http_session = None
        self._intent = None

        self.loop = loop or asyncio.get_event_loop()
        self.log = (logging.getLogger(log) if isinstance(log, str) else log
                    or logging.getLogger("mautrix_appservice"))

        self.query_user = query_user or self.query_user
        self.query_alias = query_alias or self.query_alias
        self.live = True
        self.ready = False

        self.app = web.Application(loop=self.loop,
                                   **aiohttp_params if aiohttp_params else {})
        self.app.router.add_route("GET", "/_matrix/mau/live",
                                  self._liveness_probe)
        self.app.router.add_route("GET", "/_matrix/mau/ready",
                                  self._readiness_probe)
        self.register_routes(self.app)

        self.matrix_event_handler(self.state_store.update_state)
Example #5
0
    async def int_handle_invite(self, evt: StateEvent) -> None:
        self.log.debug(f"{evt.sender} invited {evt.state_key} to {evt.room_id}")
        inviter = await self.bridge.get_user(evt.sender)
        if inviter is None:
            self.log.exception(f"Failed to find user with Matrix ID {evt.sender}")
            return
        elif evt.state_key == self.az.bot_mxid:
            await self.accept_bot_invite(evt.room_id, inviter)
            return

        puppet = await self.bridge.get_puppet(UserID(evt.state_key))
        if puppet:
            await self.handle_puppet_invite(evt.room_id, puppet, inviter, evt)
            return

        await self.handle_invite(evt.room_id, UserID(evt.state_key), inviter, evt)
 async def handle_matrix_upgrade(self, sender: UserID, new_room: RoomID,
                                 event_id: EventID) -> None:
     _, server = self.main_intent.parse_user_id(sender)
     old_room = self.mxid
     self.migrate_and_save_matrix(new_room)
     await self.main_intent.join_room(new_room, servers=[server])
     entity: Optional[TypeInputPeer] = None
     user: Optional[AbstractUser] = None
     if self.bot and self.has_bot:
         user = self.bot
         entity = await self.get_input_entity(self.bot)
     if not entity:
         user_mxids = await self.main_intent.get_room_members(self.mxid)
         for user_str in user_mxids:
             user_id = UserID(user_str)
             if user_id == self.az.bot_mxid:
                 continue
             user = u.User.get_by_mxid(user_id, create=False)
             if user and user.tgid:
                 entity = await self.get_input_entity(user)
                 if entity:
                     break
     if not entity:
         self.log.error("Failed to fully migrate to upgraded Matrix room: "
                        "no Telegram user found.")
         return
     await self.update_matrix_room(user,
                                   entity,
                                   direct=self.peer_type == "user")
     self.log.info(f"{sender} upgraded room from {old_room} to {self.mxid}")
     await self._send_delivery_receipt(event_id, room_id=old_room)
Example #7
0
async def login(evt: CommandEvent) -> EventID:
    override_sender = False
    if len(evt.args) > 0 and evt.sender.is_admin:
        evt.sender = await u.User.get_by_mxid(UserID(evt.args[0])).ensure_started()
        override_sender = True
    if await evt.sender.is_logged_in():
        return await evt.reply(f"You are already logged in as {evt.sender.human_tg_id}.")

    allow_matrix_login = evt.config["bridge.allow_matrix_login"]
    if allow_matrix_login and not override_sender:
        evt.sender.command_status = {
            "next": enter_phone_or_token,
            "action": "Login",
        }

    nb = "**N.B. Logging in grants the bridge full access to your Telegram account.**"
    if evt.config["appservice.public.enabled"]:
        prefix = evt.config["appservice.public.external"]
        url = f"{prefix}/login?token={evt.public_website.make_token(evt.sender.mxid, '/login')}"
        if override_sender:
            return await evt.reply(f"[Click here to log in]({url}) as "
                                   f"[{evt.sender.mxid}](https://matrix.to/#/{evt.sender.mxid}).")
        elif allow_matrix_login:
            return await evt.reply(f"[Click here to log in]({url}). Alternatively, send your phone"
                                   f" number (or bot auth token) here to log in.\n\n{nb}")
        return await evt.reply(f"[Click here to log in]({url}).\n\n{nb}")
    elif allow_matrix_login:
        if override_sender:
            return await evt.reply(
                "This bridge instance does not allow you to log in outside of Matrix. "
                "Logging in as another user inside Matrix is not currently possible.")
        return await evt.reply("Please send your phone number (or bot auth token) here to start "
                               f"the login process.\n\n{nb}")
    return await evt.reply("This bridge instance has been configured to not allow logging in.")
Example #8
0
 async def cleanup_room(cls,
                        intent: IntentAPI,
                        room_id: RoomID,
                        message: str,
                        puppets_only: bool = False) -> None:
     try:
         members = await intent.get_room_members(room_id)
     except MatrixRequestError:
         members = []
     for user in members:
         puppet = p.Puppet.get_by_mxid(UserID(user), create=False)
         if user != intent.mxid and (not puppets_only or puppet):
             try:
                 if puppet:
                     await puppet.default_mxid_intent.leave_room(room_id)
                 else:
                     await intent.kick_user(room_id, user, message)
             except (MatrixRequestError, IntentError):
                 pass
     try:
         await intent.leave_room(room_id)
     except (MatrixRequestError, IntentError):
         cls.log.warning(
             f"Failed to leave room {room_id} when cleaning up room",
             exc_info=True)
Example #9
0
 async def _login_with_shared_secret(cls, mxid: UserID) -> Optional[str]:
     _, server = cls.az.intent.parse_user_id(mxid)
     try:
         secret = cls.login_shared_secret_map[server]
     except KeyError:
         return None
     try:
         base_url = cls.homeserver_url_map[server]
     except KeyError:
         if server == cls.az.domain:
             base_url = cls.az.intent.api.base_url
         else:
             return None
     password = hmac.new(secret, mxid.encode("utf-8"),
                         hashlib.sha512).hexdigest()
     url = base_url / str(Path.login)
     resp = await cls.az.http_session.post(
         url,
         data=json.dumps({
             "type": str(LoginType.PASSWORD),
             "initial_device_display_name": cls.login_device_name,
             "device_id": cls.login_device_name,
             "identifier": {
                 "type": "m.id.user",
                 "user": mxid,
             },
             "password": password
         }),
         headers={"Content-Type": "application/json"})
     data = await resp.json()
     return data["access_token"]
Example #10
0
    async def test_handle_delegated_handler(
            self, command_processor: CommandProcessor,
            boolean2: Tuple[bool, bool], mocker: MockFixture) -> None:
        mocker.patch('mautrix_telegram.user.config', self.config)
        mocker.patch('mautrix.bridge.commands.handler.command_handlers', {
            "help": AsyncMock(),
            "unknown-command": AsyncMock()
        })

        sender = u.User(UserID("@sender:example.org"))
        sender.command_status = {"foo": AsyncMock(), "next": AsyncMock()}

        result = await command_processor.handle(
            room_id=RoomID("#mock_room:example.org"),
            event_id=EventID("$H45H:example.org"),
            sender=sender,  # u.User
            command="foo",
            args=[],
            is_management=boolean2[0],
            is_portal=boolean2[1])

        assert result is None
        command_handlers = mautrix.bridge.commands.handler.command_handlers
        command_handlers["help"].mock.assert_not_called()  # type: ignore
        command_handlers["unknown-command"].mock.assert_not_called(
        )  # type: ignore
        sender.command_status["foo"].mock.assert_not_called()  # type: ignore
        sender.command_status["next"].mock.assert_called_once()  # type: ignore
Example #11
0
async def set_ghost_avatar(evt: CommandEvent) -> Optional[EventID]:
    try:
        mxc_uri = ContentURI(evt.args[0])
    except (KeyError, IndexError):
        return await evt.reply(
            "**Usage:** `$cmdprefix+sp set-avatar <mxc_uri> [mxid]`")
    if not mxc_uri.startswith("mxc://"):
        return await evt.reply("The URI has to start with mxc://.")
    if len(evt.args) > 1:
        # TODO support parsing mention pills instead of requiring a plaintext mxid
        puppet = await evt.processor.bridge.get_puppet(UserID(evt.args[1]))
        if puppet is None:
            return await evt.reply("The given mxid was not a valid ghost user."
                                   )
        intent = puppet.intent
    elif evt.is_portal:
        intent = evt.portal.main_intent
        if intent == evt.az.intent:
            return await evt.reply(
                "No mxid given and the main intent is not a ghost user.")
    else:
        return await evt.reply("No mxid given and not in a portal.")
    try:
        return await intent.set_avatar_url(mxc_uri)
    except (MatrixRequestError, IntentError):
        evt.log.exception("Failed to set avatar.")
        return await evt.reply(
            "Failed to set avatar (see logs for more details).")
Example #12
0
    async def handle_command(self, message: Message) -> None:
        def reply(reply_text: str) -> Awaitable[Message]:
            return self.client.send_message(message.chat_id, reply_text, reply_to=message.id)

        text = message.message

        if self.match_command(text, "start"):
            pcm = config["bridge.relaybot.private_chat.message"]
            if pcm:
                await reply(pcm)
            return
        elif self.match_command(text, "id"):
            await self.handle_command_id(message, reply)
            return
        elif message.is_private:
            return

        portal = po.Portal.get_by_entity(message.to_id)

        is_portal_cmd = self.match_command(text, "portal")
        is_invite_cmd = self.match_command(text, "invite")
        if is_portal_cmd or is_invite_cmd:
            if not await self.check_can_use_commands(message, reply):
                return
            if is_portal_cmd:
                await self.handle_command_portal(portal, reply)
            elif is_invite_cmd:
                try:
                    mxid = text[text.index(" ") + 1:]
                except ValueError:
                    mxid = ""
                await self.handle_command_invite(portal, reply, mxid_input=UserID(mxid))
Example #13
0
async def set_ghost_display_name(evt: CommandEvent) -> Optional[EventID]:
    if len(evt.args) > 1:
        # This allows whitespaces in the name
        puppet = await evt.processor.bridge.get_puppet(
            UserID(evt.args[len(evt.args) - 1]))
        if puppet is None:
            return await evt.reply(
                "The given mxid was not a valid ghost user. "
                "If the display name has whitespaces mxid is required")
        intent = puppet.intent
        displayname = " ".join(evt.args[:-1])
    elif evt.is_portal:
        intent = evt.portal.main_intent
        if intent == evt.az.intent:
            return await evt.reply(
                "No mxid given and the main intent is not a ghost user.")
        displayname = evt.args[0]
    else:
        return await evt.reply("No mxid given and not in a portal.")
    try:
        return await intent.set_displayname(displayname)
    except (MatrixRequestError, IntentError):
        evt.log.exception("Failed to set display name.")
        return await evt.reply(
            "Failed to set display name (see logs for more details).")
Example #14
0
    async def handle_command(self, message: Message) -> None:
        def reply(reply_text: str) -> Awaitable[Message]:
            return self.client.send_message(message.to_id,
                                            reply_text,
                                            reply_to=message.id)

        text = message.message

        if self.match_command(text, "id"):
            await self.handle_command_id(message, reply)
            return

        portal = po.Portal.get_by_entity(message.to_id)

        if self.match_command(text, "portal"):
            if not await self.check_can_use_commands(message, reply):
                return
            await self.handle_command_portal(portal, reply)
        elif self.match_command(text, "invite"):
            if not await self.check_can_use_commands(message, reply):
                return
            try:
                mxid = text[text.index(" ") + 1:]
            except ValueError:
                mxid = ""
            await self.handle_command_invite(portal,
                                             reply,
                                             mxid_input=UserID(mxid))
Example #15
0
 async def update_state(self, evt: StateEvent) -> None:
     if evt.type == EventType.ROOM_POWER_LEVELS:
         await self.set_power_levels(evt.room_id, evt.content)
     elif evt.type == EventType.ROOM_MEMBER:
         await self.set_member(evt.room_id, UserID(evt.state_key), evt.content)
     elif evt.type == EventType.ROOM_ENCRYPTION:
         await self.set_encryption_info(evt.room_id, evt.content)
Example #16
0
    async def handle_event(self, evt: Event) -> None:
        if self.filter_matrix_event(evt):
            return
        self.log.debug("Received event: %s", evt)

        if evt.type == EventType.ROOM_MEMBER:
            evt: StateEvent
            if evt.content.membership == Membership.INVITE:
                await self.handle_invite(evt.room_id, UserID(evt.state_key),
                                         evt.sender)
        elif evt.type in (EventType.ROOM_MESSAGE, EventType.STICKER):
            evt: MessageEvent
            if evt.type != EventType.ROOM_MESSAGE:
                evt.content.msgtype = MessageType(str(evt.type))
            await self.handle_message(evt.room_id, evt.sender, evt.content,
                                      evt.event_id)
        elif evt.type == EventType.ROOM_REDACTION:
            evt: RedactionEvent
            await self.handle_redaction(evt.room_id, evt.sender, evt.redacts)
        elif evt.type == EventType.PRESENCE:
            await self.handle_presence(evt)
        elif evt.type == EventType.TYPING:
            await self.handle_typing(evt)
        elif evt.type == EventType.RECEIPT:
            await self.handle_receipt(evt)
Example #17
0
    def test_reply_with_cmdprefix(self, command_processor: CommandProcessor,
                                  mocker: MockFixture) -> None:
        mocker.patch("mautrix_telegram.user.config", self.config)

        evt = CommandEvent(
            processor=command_processor,
            room_id=RoomID("#mock_room:example.org"),
            event_id=EventID("$H45H:example.org"),
            sender=u.User(UserID("@sender:example.org")),
            command="help",
            args=[],
            is_management=False,
            is_portal=False,
        )

        mock_az = command_processor.az

        evt.reply("$cmdprefix+sp ....$cmdprefix+sp...$cmdprefix $cmdprefix",
                  allow_html=False,
                  render_markdown=False)

        mock_az.intent.send_notice.assert_called_with(
            RoomID("#mock_room:example.org"),
            "tg ....tg+sp...tg tg",
            html=None,
        )
Example #18
0
    async def check_token(self, request: web.Request) -> 'u.User':
        try:
            token = request.headers["Authorization"]
            token = token[len("Bearer "):]
        except KeyError:
            raise web.HTTPBadRequest(
                text='{"error": "Missing Authorization header"}',
                headers=self._headers)
        except IndexError:
            raise web.HTTPBadRequest(
                text='{"error": "Malformed Authorization header"}',
                headers=self._headers)
        if token != self.shared_secret:
            raise web.HTTPForbidden(text='{"error": "Invalid token"}',
                                    headers=self._headers)
        try:
            user_id = request.query["user_id"]
        except KeyError:
            raise web.HTTPBadRequest(
                text='{"error": "Missing user_id query param"}',
                headers=self._headers)

        if not self.bridge.signal.is_connected:
            await self.bridge.signal.wait_for_connected()

        return await u.User.get_by_mxid(UserID(user_id))
Example #19
0
 def verify_token(self,
                  token: str,
                  endpoint: str = "/login") -> UserID | None:
     token = verify_token(self.secret_key, token)
     if token and (token.get("expiry", 0) > int(time.time())
                   and token.get("endpoint", None) == endpoint):
         return UserID(token.get("mxid", None))
     return None
Example #20
0
 def get_mxid_from_id(cls, address: Address) -> UserID:
     if address.uuid:
         identifier = str(address.uuid).lower()
     elif address.number:
         identifier = f"phone_{address.number.lstrip('+')}"
     else:
         raise ValueError("Empty address")
     return UserID(cls.mxid_template.format_full(identifier))
Example #21
0
async def login_qr(evt: CommandEvent) -> EventID:
    login_as = evt.sender
    if len(evt.args) > 0 and evt.sender.is_admin:
        login_as = await u.User.get_by_mxid(UserID(evt.args[0]))
    if not qrcode or not QRLogin:
        return await evt.reply("This bridge instance does not support logging in with a QR code.")
    if await login_as.is_logged_in():
        return await evt.reply(f"You are already logged in as {login_as.human_tg_id}.")

    await login_as.ensure_started(even_if_no_session=True)
    qr_login = QRLogin(login_as.client, ignored_ids=[])
    qr_event_id: EventID | None = None

    async def upload_qr() -> None:
        nonlocal qr_event_id
        buffer = io.BytesIO()
        image = qrcode.make(qr_login.url)
        size = image.pixel_size
        image.save(buffer, "PNG")
        qr = buffer.getvalue()
        mxc = await evt.az.intent.upload_media(qr, "image/png", "login-qr.png", len(qr))
        content = MediaMessageEventContent(
            body=qr_login.url,
            url=mxc,
            msgtype=MessageType.IMAGE,
            info=ImageInfo(mimetype="image/png", size=len(qr), width=size, height=size),
        )
        if qr_event_id:
            content.set_edit(qr_event_id)
            await evt.az.intent.send_message(evt.room_id, content)
        else:
            content.set_reply(evt.event_id)
            qr_event_id = await evt.az.intent.send_message(evt.room_id, content)

    retries = 4
    while retries > 0:
        await qr_login.recreate()
        await upload_qr()
        try:
            user = await qr_login.wait()
            break
        except asyncio.TimeoutError:
            retries -= 1
        except SessionPasswordNeededError:
            evt.sender.command_status = {
                "next": enter_password,
                "login_as": login_as if login_as != evt.sender else None,
                "action": "Login (password entry)",
            }
            return await evt.reply(
                "Your account has two-factor authentication. Please send your password here."
            )
    else:
        timeout = TextMessageEventContent(body="Login timed out", msgtype=MessageType.TEXT)
        timeout.set_edit(qr_event_id)
        return await evt.az.intent.send_message(evt.room_id, timeout)

    return await _finish_sign_in(evt, user, login_as=login_as)
Example #22
0
    async def test_permission_granted(
        self,
        is_management: bool,
        puppet_whitelisted: bool,
        matrix_puppet_whitelisted: bool,
        is_admin: bool,
        is_logged_in: bool,
        command_processor: CommandProcessor,
        boolean: bool,
        mocker: MockFixture,
    ) -> None:
        mocker.patch("mautrix_telegram.user.config", self.config)

        command = "testcmd"

        mock_handler = Mock()

        command_handler = CommandHandler(
            handler=mock_handler,
            needs_auth=False,
            needs_puppeting=False,
            needs_matrix_puppeting=False,
            needs_admin=False,
            management_only=False,
            name=command,
            help_text="No real command",
            help_args="mock mockmock",
            help_section=HelpSection("Mock Section", 42, ""),
        )

        sender = u.User(UserID("@sender:example.org"))
        sender.puppet_whitelisted = puppet_whitelisted
        sender.matrix_puppet_whitelisted = matrix_puppet_whitelisted
        sender.is_admin = is_admin
        mocker.patch.object(u.User, 'is_logged_in', return_value=is_logged_in)

        event = CommandEvent(
            processor=command_processor,
            room_id=RoomID("#mock_room:example.org"),
            event_id=EventID("$H45H:example.org"),
            sender=sender,
            command=command,
            args=[],
            content=Mock(),
            is_management=is_management,
            is_portal=boolean,
        )

        assert not await command_handler.get_permission_error(event)
        assert command_handler.has_permission(
            HelpCacheKey(is_management=is_management,
                         puppet_whitelisted=puppet_whitelisted,
                         matrix_puppet_whitelisted=matrix_puppet_whitelisted,
                         is_admin=is_admin,
                         is_logged_in=is_logged_in,
                         is_portal=boolean))
    def get_permissions(self, mxid: UserID) -> Tuple[bool, bool, str]:
        permissions = self["bridge.permissions"] or {}
        if mxid in permissions:
            return self._get_permissions(mxid)

        homeserver = mxid[mxid.index(":") + 1:]
        if homeserver in permissions:
            return self._get_permissions(homeserver)

        return self._get_permissions("*")
Example #24
0
    async def handle_event(self, event: Event) -> None:

        self.log.debug("Handle event")

        domain = self.config['homeserver.domain']
        namespace = self.config['appservice.namespace']

        event_type: str = event.get("type", "m.unknown")
        room_id: Optional[RoomID] = event.get("room_id", None)
        event_id: Optional[EventID] = event.get("event_id", None)
        sender: Optional[UserID] = event.get("sender", None)
        content: Dict = event.get("content", {})

        self.log.debug(f"Event {event}")

        self.log.debug(f"Event type: {event.type}")
        self.log.debug(f"Event room_id: {room_id}")
        self.log.debug(f"Event sender: {sender}")
        self.log.debug(f"Event content: {content}")

        if event.type == EventType.ROOM_MEMBER:
            event: StateEvent
            prev_content = event.unsigned.prev_content or MemberStateEventContent(
            )
            prev_membership = prev_content.membership if prev_content else Membership.JOIN

            if event.content.membership == Membership.LEAVE:
                if event.sender == event.state_key:
                    await self.sl.matrix_user_left(UserID(event.state_key),
                                                   event.room_id,
                                                   event.event_id)
            elif event.content.membership == Membership.JOIN:
                if prev_membership != Membership.JOIN:
                    await self.sl.matrix_user_joined(UserID(event.state_key),
                                                     event.room_id,
                                                     event.event_id)

        elif event.type in (EventType.ROOM_MESSAGE, EventType.STICKER):
            event: MessageEvent
            if event.type != EventType.ROOM_MESSAGE:
                event.content.msgtype = MessageType(str(event.type))
            await self.handle_message(event.room_id, event.sender,
                                      event.content, event.event_id)
Example #25
0
    async def get_portal_by_mxid(self, request: web.Request) -> web.Response:
        err = self.check_authorization(request)
        if err is not None:
            return err

        mxid = request.match_info["mxid"]
        portal = Portal.get_by_mxid(mxid)
        if not portal:
            return self.get_error_response(404, "portal_not_found",
                                           "Portal with given Matrix ID not found.")
        return await self._get_portal_response(UserID(request.query.get("user_id", "")), portal)
    async def saidex(self, user, room, message):

        domain = self.config['homeserver.domain']
        namespace = self.config['appservice.namespace']

        matrix_id = f"@{namespace}_{user.lower()}:{domain}"

        room_id = self.rooms[room]["room_id"]

        user = self.appserv.intent.user(UserID(matrix_id))

        await user.send_emote(room_id, message)
Example #27
0
    def test_reply(self, command_processor: CommandProcessor,
                   mocker: MockFixture) -> None:
        mocker.patch("mautrix_telegram.user.config", self.config)

        evt = CommandEvent(
            processor=command_processor,
            room_id=RoomID("#mock_room:example.org"),
            event_id=EventID("$H45H:example.org"),
            sender=u.User(UserID("@sender:example.org")),
            command="help",
            args=[],
            content=Mock(),
            is_management=True,
            is_portal=False,
        )

        mock_az = command_processor.az

        message = "**This** <i>was</i><br/><strong>all</strong>fun*!"

        # html, no markdown
        evt.reply(message, allow_html=True, render_markdown=False)
        mock_az.intent.send_notice.assert_called_with(
            RoomID("#mock_room:example.org"),
            "**This** <i>was</i><br/><strong>all</strong>fun*!",
            html="**This** <i>was</i><br/><strong>all</strong>fun*!\n",
        )

        # html, markdown (default)
        evt.reply(message, allow_html=True, render_markdown=True)
        mock_az.intent.send_notice.assert_called_with(
            RoomID("#mock_room:example.org"),
            "**This** <i>was</i><br/><strong>all</strong>fun*!",
            html=("<p><strong>This</strong> <i>was</i><br/>"
                  "<strong>all</strong>fun*!</p>\n"),
        )

        # no html, no markdown
        evt.reply(message, allow_html=False, render_markdown=False)
        mock_az.intent.send_notice.assert_called_with(
            RoomID("#mock_room:example.org"),
            "**This** <i>was</i><br/><strong>all</strong>fun*!",
            html=None,
        )

        # no html, markdown
        evt.reply(message, allow_html=False, render_markdown=True)
        mock_az.intent.send_notice.assert_called_with(
            RoomID("#mock_room:example.org"),
            "**This** <i>was</i><br/><strong>all</strong>fun*!",
            html="<p><strong>This</strong> &lt;i&gt;was&lt;/i&gt;&lt;br/&gt;"
            "&lt;strong&gt;all&lt;/strong&gt;fun*!</p>\n")
    async def join_matrix_room(self, room, clients):
        self.log.debug("joining matrix room join from lobby")
        self.log.debug(room)

        room_id = self.rooms[room]["room_id"]
        for client in clients:
            if client != "appservice":
                domain = self.config['homeserver.domain']
                namespace = self.config['appservice.namespace']
                matrix_id = f"@{namespace}_{client.lower()}:{domain}"
                user = self.appserv.intent.user(UserID(matrix_id))

                await user.join_room_by_id(room_id=room_id)
Example #29
0
    async def test_permissions_denied(
        self,
        needs_auth: bool,
        needs_puppeting: bool,
        needs_matrix_puppeting: bool,
        needs_admin: bool,
        management_only: bool,
        command_processor: CommandProcessor,
        boolean: bool,
        mocker: MockFixture,
    ) -> None:
        mocker.patch("mautrix_telegram.user.config", self.config)

        command = "testcmd"

        mock_handler = Mock()

        command_handler = CommandHandler(
            handler=mock_handler,
            needs_auth=needs_auth,
            needs_puppeting=needs_puppeting,
            needs_matrix_puppeting=needs_matrix_puppeting,
            needs_admin=needs_admin,
            management_only=management_only,
            name=command,
            help_text="No real command",
            help_args="mock mockmock",
            help_section=HelpSection("Mock Section", 42, ""),
        )

        sender = u.User(UserID("@sender:example.org"))
        sender.puppet_whitelisted = False
        sender.matrix_puppet_whitelisted = False
        sender.is_admin = False

        event = CommandEvent(
            processor=command_processor,
            room_id=RoomID("#mock_room:example.org"),
            event_id=EventID("$H45H:example.org"),
            sender=sender,
            command=command,
            args=[],
            content=Mock(),
            is_management=False,
            is_portal=boolean,
        )

        assert await command_handler.get_permission_error(event)
        assert not command_handler.has_permission(
            HelpCacheKey(False, False, False, False, False, False))
Example #30
0
 async def get_room_members(
     self,
     room_id: RoomID,
     allowed_memberships: Tuple[Membership, ...] = (Membership.JOIN, )
 ) -> List[UserID]:
     if len(allowed_memberships
            ) == 1 and allowed_memberships[0] == Membership.JOIN:
         memberships = await self.get_joined_members(room_id)
         return list(memberships.keys())
     member_events = await self.get_members(room_id)
     return [
         UserID(evt.state_key) for evt in member_events
         if evt.content.membership in allowed_memberships
     ]