Пример #1
0
 async def stats_raw_json(self, request: Request) -> Response:
     try:
         room_id = RoomID(request.match_info["room_id"])
     except KeyError:
         return Response(status=404, text="Room ID missing")
     return Response(status=200,
                     content_type="application/json",
                     text=json.dumps([
                         pong._asdict() for pong in self.iter_pongs(
                             room_id, **self._get_min_max_age(request, 0))
                     ]))
Пример #2
0
 async def get_json(self, request: web.Request) -> web.Response:
     try:
         room_id = RoomID(request.query["room_id"])
     except KeyError:
         return web.Response(
             status=400,
             content_type="application/json",
             text='{"error": "Room  ID query param missing"}')
     return web.Response(status=200,
                         content_type="application/json",
                         text=json.dumps(await self.get_codes(room_id)))
async def _get_portal_and_check_permission(evt: CommandEvent) -> Optional[po.Portal]:
    room_id = RoomID(evt.args[0]) if len(evt.args) > 0 else evt.room_id

    portal = po.Portal.get_by_mxid(room_id)
    if not portal:
        that_this = "This" if room_id == evt.room_id else "That"
        await evt.reply(f"{that_this} is not a portal room.")
        return None

    if not await user_has_power_level(portal.mxid, evt.az.intent, evt.sender, "unbridge"):
        await evt.reply("You do not have the permissions to unbridge that portal.")
        return None
    return portal
Пример #4
0
 async def get_widget(self, request: web.Request) -> web.Response:
     try:
         room_id = RoomID(request.query["room_id"])
     except KeyError:
         return web.Response(status=400,
                             content_type="text/plain",
                             text="Room ID query param missing")
     return web.Response(status=200,
                         content_type="text/html",
                         text=self.widget_tpl.render(
                             codes=await self.get_codes(room_id),
                             profiles=await self.get_profiles(room_id),
                             get_url=self._get_download_url))
Пример #5
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))
Пример #6
0
    async def process_hook_01(self, req: Request) -> None:
        if self.config["send_as_notice"]:
            msgtype = MessageType.NOTICE
        else:
            msgtype = MessageType.TEXT

        try:
            msg = None
            body = await req.json()

            if body["secret"] != self.config["webhook-secret"]:
                self.log.error("Failed to handle Gitea event: secret doesnt match.")
            else:
                event = req.headers["X-Gitea-Event"]
                if event == 'push':
                    commits = body["commits"]
                    commit_count = len(commits)
                    if commit_count > 0:
                        msg = (f"user '{body['sender']['login']}' pushed "
                               f"{commit_count} commit(s) to "
                               f"'{body['repository']['full_name']}' at '{URL(body['repository']['html_url']).host}'.")
                elif event == 'create':
                    msg = (f"user '{body['sender']['login']}' created a tag or branch in "
                           f"'{body['repository']['full_name']}' at '{URL(body['repository']['html_url']).host}'.")
                elif event == 'delete':
                    msg = (f"user '{body['sender']['login']}' deleted a tag or branch in "
                           f"'{body['repository']['full_name']}' at '{URL(body['repository']['html_url']).host}'.")
                elif event == 'issues':
                    msg = (f"user '{body['sender']['login']}' {body['action']} issue #{body['number']} in "
                           f"'{body['repository']['full_name']}' at '{URL(body['repository']['html_url']).host}'.")
                elif event == 'issue_comment':
                    msg = (f"user '{body['sender']['login']}' {body['action']} a comment on issue #{body['issue']['id']} in "
                           f"'{body['repository']['full_name']}' at '{URL(body['repository']['html_url']).host}'.")
                else:
                    self.log.error(f"unhandled hook: {event}")
                    self.log.error(await req.text())

                room_id = RoomID(req.query["room"])
                if msg:
                    event_id = await self.client.send_markdown(room_id, msg, allow_html=True, msgtype=msgtype)

        except Exception:
            self.log.error("Failed to handle Gitea event", exc_info=True)

        task = asyncio.current_task()
        if task:
            self.task_list.remove(task)
Пример #7
0
 async def stats(self,
                 request: Request,
                 force_json: bool = False) -> Response:
     try:
         room_id = RoomID(request.match_info["room_id"])
     except KeyError:
         return Response(status=404, text="Room ID missing")
     data = self.get_room_data(room_id, **self._get_min_max_age(request))
     if "application/json" in request.headers.get("Accept",
                                                  "") or force_json:
         return Response(status=200,
                         content_type="application/json",
                         text=json.dumps(data))
     n = 1
     for ping in data["pings"].values():
         ping["n"] = n
         n += 1
     return Response(
         status=200,
         content_type="text/html",
         text=self.stats_tpl.render(**data,
                                    prettify_diff=self.prettify_diff))
    async def leave_matrix_room(self, room, clients):
        self.log.debug("leaving matrix room left from lobby")
        self.log.debug(room)
        for client in clients:
            self.log.debug(client)
            if client != "spring":
                self.log.debug(f"CLIENT {client}")

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

                matrix_id = f"@{namespace}_{client.lower()}:{domain}"
                self.log.debug(matrix_id)

                room_id = RoomID(self.rooms[room]["room_id"])
                self.log.debug(room_id)

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

                self.log.debug(user)
                await user.leave_room(room_id=room_id)

        self.log.debug("succes leaved matrix room left from lobby")
    async def sync_matrix_users(self) -> None:
        self.log.debug("Sync matrix users")

        bot_username = self.config["appservice.bot_username"]

        for room_name, room_data in self.rooms.items():
            spring_room = room_data.get('name')
            room_id = RoomID(room_data.get("room_id"))
            enabled = room_data.get("enabled")

            if enabled:
                self.log.debug(f"Room {spring_room} enabled")
                await self.appserv.intent.ensure_joined(room_id=room_id)
                members = await self.appserv.intent.get_room_members(room_id)

                for mxid in members:

                    self.log.debug(f"member {mxid}")

                    member = await self.appserv.intent.get_room_member_info(
                        room_id=room_id, user_id=mxid)

                    if mxid.startswith(f"@{bot_username}"):
                        self.log.debug(f"Ignore myself {mxid}")

                    elif mxid.startswith(
                            f"@{self.config['appservice.namespace']}_"):
                        self.log.debug(f"Ignoring local user {mxid}")
                    else:
                        self.log.debug(f"Loging user {mxid}")
                        await self.appserv.state_store.set_member(
                            room_id, mxid, member)
            else:
                self.log.debug(f"Room {spring_room} disabled")

        self.log.debug("Start bridging users")

        bridged_clients = list()
        for room_name, room_data in self.rooms.items():
            enabled = room_data.get("enabled")
            if enabled:
                room_id = RoomID(room_data.get("room_id"))
                room_users = await self.appserv.intent.get_room_members(room_id
                                                                        )
                for user in room_users:
                    bridged_clients.append(user)

        for member in list(set(bridged_clients)):
            self.log.debug(f"User {member}")
            localpart, domain = self.appserv.intent.parse_user_id(member)

            if localpart == "_discord_bot":
                continue

            try:
                displayname = await self.appserv.intent.get_displayname(
                    UserID(member))
            except Exception as nf:
                self.log.error(f"user {localpart} has no profile {nf}")
                displayname = localpart

            if localpart.startswith("_discord_"):
                localpart = localpart.lstrip("_discord_")
                domain = "discord"
            elif localpart.startswith("freenode_"):
                localpart = localpart.lstrip("freenode_")
                domain = "freenode.org"
            elif localpart.startswith("spring_"):
                localpart = localpart.lstrip("spring_")
                domain = "springlobby"

            if len(displayname) > 15:
                displayname = displayname[:15]
            if len(localpart) > 15:
                localpart = localpart[:15]
            if len(domain) > 15:
                domain = domain[:15]

            domain = domain.replace('-', '_')

            self.log.debug(
                f"Bridging user {member} for {domain} externalID {localpart} externalUsername {displayname}"
            )
            self.bot.bridged_client_from(domain, localpart.lower(),
                                         displayname)

        self.log.debug("Users bridged")
        self.log.debug("Join matrix users")

        for room_name, room_data in self.rooms.items():
            enabled = room_data.get("enabled")
            if enabled:
                room_id = RoomID(room_data.get("room_id"))
                room_users = await self.appserv.intent.get_room_members(room_id
                                                                        )

                for member in room_users:

                    self.log.debug(f"\tMember: {member}")

                    localpart, user_domain = self.appserv.intent.parse_user_id(
                        UserID(member))

                    self.log.debug(f"\t\tdetails: {localpart} {user_domain}")

                    if localpart == self.config["appservice.bot_username"]:
                        self.log.debug(f"Not bridging the local appservice")
                        continue
                    elif localpart == "_discord_bot":
                        self.log.debug(f"Not bridging the discord appservice")
                        continue

                    if localpart.startswith(
                            self.config["appservice.namespace"]):
                        self.log.debug(f"Ignoring local user {localpart}")
                        continue
                    elif localpart.startswith("_discord_"):
                        localpart = localpart.lstrip("_discord_")
                        user_domain = "discord"
                    elif localpart.startswith("freenode_"):
                        localpart = localpart.lstrip("freenode_")
                        user_domain = "freenode.org"
                    elif localpart.startswith("spring"):
                        localpart = localpart.lstrip("spring_")
                        user_domain = "springlobby"

                    try:
                        displayname = await self.appserv.intent.get_displayname(
                            UserID(member))
                    except Exception as nf:
                        self.log.error(f"user {localpart} has no profile {nf}")
                        displayname = localpart

                    if len(displayname) > 15:
                        displayname = displayname[:15]
                    if len(localpart) > 15:
                        localpart = localpart[:15]
                    if len(user_domain) > 15:
                        self.log.debug("user domain too long")
                        user_domain = user_domain[:15]

                    self.log.debug(f"user_name = {localpart}")
                    self.log.debug(f"display_name = {displayname}")
                    self.log.debug(f"domain = {user_domain}")

                    for _, room in self.rooms.items():
                        if room["room_id"] == room_id:
                            self.log.debug(
                                f"Join channel {room.get('name')}, user {localpart}, domain {user_domain}"
                            )
                            self.bot.join_from(room["name"], user_domain,
                                               localpart)
Пример #10
0
    async def _create_matrix_room(self, user: '******',
                                  entity: Union[TypeChat, User],
                                  invites: InviteList) -> Optional[RoomID]:
        direct = self.peer_type == "user"

        if invites is None:
            invites = []

        if self.mxid:
            return self.mxid

        if not self.allow_bridging:
            return None

        if not entity:
            entity = await self.get_entity(user)
            self.log.trace("Fetched data: %s", entity)

        self.log.debug("Creating room")

        try:
            self.title = entity.title
        except AttributeError:
            self.title = None

        if direct and self.tgid == user.tgid:
            self.title = "Telegram Saved Messages"
            self.about = "Your Telegram cloud storage chat"

        puppet = p.Puppet.get(self.tgid) if direct else None
        if puppet:
            await puppet.update_info(user, entity)
        self._main_intent = puppet.intent_for(
            self) if direct else self.az.intent

        if self.peer_type == "channel":
            self.megagroup = entity.megagroup

        if self.peer_type == "channel" and entity.username:
            preset = RoomCreatePreset.PUBLIC
            self.username = entity.username
            alias = self.alias_localpart
        else:
            preset = RoomCreatePreset.PRIVATE
            # TODO invite link alias?
            alias = None

        if alias:
            # TODO? properly handle existing room aliases
            await self.main_intent.remove_room_alias(alias)

        power_levels = self._get_base_power_levels(entity=entity)
        users = participants = None
        if not direct:
            users, participants = await self._get_users(user, entity)
            if self.has_bot:
                extra_invites = config["bridge.relaybot.group_chat_invite"]
                invites += extra_invites
                for invite in extra_invites:
                    power_levels.users.setdefault(invite, 100)
            self._participants_to_power_levels(participants, power_levels)
        elif self.bot and self.tg_receiver == self.bot.tgid:
            invites = config["bridge.relaybot.private_chat.invite"]
            for invite in invites:
                power_levels.users.setdefault(invite, 100)
            self.title = puppet.displayname
        bridge_info = {
            "bridgebot": self.az.bot_mxid,
            "creator": self.main_intent.mxid,
            "protocol": {
                "id": "telegram",
                "displayname": "Telegram",
                "avatar_url": config["appservice.bot_avatar"],
            },
            "channel": {
                "id": self.tgid
            }
        }
        initial_state = [
            {
                "type": EventType.ROOM_POWER_LEVELS.serialize(),
                "content": power_levels.serialize(),
            },
            {
                "type": "m.bridge",
                "state_key": f"net.maunium.telegram://telegram/{self.tgid}",
                "content": bridge_info
            },
            {
                # TODO remove this once https://github.com/matrix-org/matrix-doc/pull/2346 is in spec
                "type": "uk.half-shot.bridge",
                "state_key": f"net.maunium.telegram://telegram/{self.tgid}",
                "content": bridge_info
            }
        ]
        if config["bridge.encryption.default"] and self.matrix.e2ee:
            self.encrypted = True
            initial_state.append({
                "type": "m.room.encryption",
                "content": {
                    "algorithm": "m.megolm.v1.aes-sha2"
                },
            })
            if direct:
                invites.append(self.az.bot_mxid)
        if direct and (self.encrypted or self.private_chat_portal_meta):
            self.title = puppet.displayname
        if config["appservice.community_id"]:
            initial_state.append({
                "type": "m.room.related_groups",
                "content": {
                    "groups": [config["appservice.community_id"]]
                },
            })
        creation_content = {}
        if not config["bridge.federate_rooms"]:
            creation_content["m.federate"] = False

        room_id = await self.main_intent.create_room(
            alias_localpart=alias,
            preset=preset,
            is_direct=direct,
            invitees=invites or [],
            name=self.title,
            topic=self.about,
            initial_state=initial_state,
            creation_content=creation_content)
        if not room_id:
            raise Exception(f"Failed to create room")

        if self.encrypted and self.matrix.e2ee:
            members = [self.main_intent.mxid]
            if direct:
                try:
                    await self.az.intent.join_room_by_id(room_id)
                    members += [self.az.intent.mxid]
                except Exception:
                    self.log.warning(
                        f"Failed to add bridge bot to new private chat {room_id}"
                    )
            await self.matrix.e2ee.add_room(room_id,
                                            members=members,
                                            encrypted=True)

        self.mxid = RoomID(room_id)
        self.by_mxid[self.mxid] = self
        self.save()
        self.az.state_store.set_power_levels(self.mxid, power_levels)
        user.register_portal(self)
        asyncio.ensure_future(self.update_matrix_room(
            user,
            entity,
            direct,
            puppet,
            levels=power_levels,
            users=users,
            participants=participants),
                              loop=self.loop)

        return self.mxid