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)) ]))
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
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))
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))
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)
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)
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