From d8088932740a06e9f84ee68ca2a17ac7c4276bad Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Mon, 26 Oct 2020 19:56:20 +0200 Subject: [PATCH] Move clean-rooms command to mautrix-python --- mautrix_telegram/commands/__init__.py | 2 +- mautrix_telegram/commands/clean_rooms.py | 195 ----------------------- mautrix_telegram/portal/base.py | 47 ++---- requirements.txt | 2 +- 4 files changed, 13 insertions(+), 233 deletions(-) delete mode 100644 mautrix_telegram/commands/clean_rooms.py diff --git a/mautrix_telegram/commands/__init__.py b/mautrix_telegram/commands/__init__.py index cbdd778c..ebdf60f3 100644 --- a/mautrix_telegram/commands/__init__.py +++ b/mautrix_telegram/commands/__init__.py @@ -1,7 +1,7 @@ from .handler import (command_handler, CommandHandler, CommandProcessor, CommandEvent, SECTION_AUTH, SECTION_CREATING_PORTALS, SECTION_PORTAL_MANAGEMENT, SECTION_MISC, SECTION_ADMIN) -from . import portal, telegram, clean_rooms, matrix_auth, manhole +from . import portal, telegram, matrix_auth, manhole __all__ = ["command_handler", "CommandHandler", "CommandProcessor", "CommandEvent", "SECTION_AUTH", "SECTION_MISC", "SECTION_ADMIN", "SECTION_CREATING_PORTALS", diff --git a/mautrix_telegram/commands/clean_rooms.py b/mautrix_telegram/commands/clean_rooms.py deleted file mode 100644 index 57d711eb..00000000 --- a/mautrix_telegram/commands/clean_rooms.py +++ /dev/null @@ -1,195 +0,0 @@ -# mautrix-telegram - A Matrix-Telegram puppeting bridge -# Copyright (C) 2019 Tulir Asokan -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -from typing import List, NamedTuple, Tuple, Union - -from mautrix.appservice import IntentAPI -from mautrix.errors import MatrixRequestError -from mautrix.types import RoomID, UserID, EventID, EventType - -from . import command_handler, CommandEvent, SECTION_ADMIN -from .. import puppet as pu, portal as po - -ManagementRoom = NamedTuple('ManagementRoom', room_id=RoomID, user_id=UserID) - - -async def _find_rooms(intent: IntentAPI) -> Tuple[List[ManagementRoom], List[RoomID], List[RoomID], - List['po.Portal'], List['po.Portal']]: - management_rooms: List[ManagementRoom] = [] - unidentified_rooms: List[RoomID] = [] - tombstoned_rooms: List[RoomID] = [] - portals: List[po.Portal] = [] - empty_portals: List[po.Portal] = [] - - rooms = await intent.get_joined_rooms() - for room_id in rooms: - portal = po.Portal.get_by_mxid(room_id) - if not portal: - try: - tombstone = await intent.get_state_event(room_id, EventType.ROOM_TOMBSTONE) - if tombstone and tombstone.replacement_room: - tombstoned_rooms.append(room_id) - continue - except MatrixRequestError: - pass - try: - members = await intent.get_room_members(room_id) - except MatrixRequestError: - members = [] - if len(members) == 2: - other_member = members[0] if members[0] != intent.mxid else members[1] - if pu.Puppet.get_id_from_mxid(other_member): - unidentified_rooms.append(room_id) - else: - management_rooms.append(ManagementRoom(room_id, other_member)) - else: - unidentified_rooms.append(room_id) - else: - members = await portal.get_authenticated_matrix_users() - if len(members) == 0: - empty_portals.append(portal) - else: - portals.append(portal) - - return management_rooms, unidentified_rooms, tombstoned_rooms, portals, empty_portals - - -@command_handler(needs_admin=True, needs_auth=False, management_only=True, name="clean-rooms", - help_section=SECTION_ADMIN, - help_text="Clean up unused portal/management rooms.") -async def clean_rooms(evt: CommandEvent) -> EventID: - (management_rooms, unidentified_rooms, tombstoned_rooms, - portals, empty_portals) = await _find_rooms(evt.az.intent) - - reply = ["#### Management rooms (M)"] - reply += ([f"{n+1}. [M{n+1}](https://matrix.to/#/{room}) (with {other_member}" - for n, (room, other_member) in enumerate(management_rooms)] - or ["No management rooms found."]) - reply.append("#### Active portal rooms (A)") - reply += ([f"{n+1}. [A{n+1}](https://matrix.to/#/{portal.mxid}) " - f"(to Telegram chat \"{portal.title}\")" - for n, portal in enumerate(portals)] - or ["No active portal rooms found."]) - reply.append("#### Unidentified rooms (U)") - reply += ([f"{n+1}. [U{n+1}](https://matrix.to/#/{room})" - for n, room in enumerate(unidentified_rooms)] - or ["No unidentified rooms found."]) - reply.append("#### Tombstoned rooms (T)") - reply += ([f"{n+1}. [T{n+1}](https://matrix.to/#/{room})" - for n, room in enumerate(tombstoned_rooms)] - or ["No tombstoned rooms found."]) - reply.append("#### Inactive portal rooms (I)") - reply += ([f"{n}. [I{n}](https://matrix.to/#/{portal.mxid}) " - f"(to Telegram chat \"{portal.title}\")" - for n, portal in enumerate(empty_portals)] - or ["No inactive portal rooms found."]) - - reply += ["#### Usage", - ("To clean the recommended set of rooms (unidentified & inactive portals), " - "type `$cmdprefix+sp clean-recommended`"), - "", - ("To clean other groups of rooms, type `$cmdprefix+sp clean-groups ` " - "where `letters` are the first letters of the group names (M, A, U, I, T)"), - "", - ("To clean specific rooms, type `$cmdprefix+sp clean-range ` " - "where `range` is the range (e.g. `5-21`) prefixed with the first letter of" - "the group name. (e.g. `I2-6`)"), - "", - ("Please note that you will have to re-run `$cmdprefix+sp clean-rooms` " - "between each use of the commands above.")] - - evt.sender.command_status = { - "next": lambda clean_evt: set_rooms_to_clean(clean_evt, management_rooms, - unidentified_rooms, tombstoned_rooms, portals, - empty_portals), - "action": "Room cleaning", - } - - return await evt.reply("\n".join(reply)) - - -async def set_rooms_to_clean(evt, management_rooms: List[ManagementRoom], - unidentified_rooms: List[RoomID], tombstoned_rooms: List[RoomID], - portals: List["po.Portal"], empty_portals: List["po.Portal"]) -> None: - command = evt.args[0] - rooms_to_clean: List[Union[po.Portal, RoomID]] = [] - if command == "clean-recommended": - rooms_to_clean += empty_portals - rooms_to_clean += unidentified_rooms - elif command == "clean-groups": - if len(evt.args) < 2: - return await evt.reply("**Usage:** `$cmdprefix+sp clean-groups [M][A][U][I]") - groups_to_clean = evt.args[1].upper() - if "M" in groups_to_clean: - rooms_to_clean += [room_id for (room_id, user_id) in management_rooms] - if "A" in groups_to_clean: - rooms_to_clean += portals - if "U" in groups_to_clean: - rooms_to_clean += unidentified_rooms - if "I" in groups_to_clean: - rooms_to_clean += empty_portals - if "T" in groups_to_clean: - rooms_to_clean += tombstoned_rooms - elif command == "clean-range": - try: - clean_range = evt.args[1] - group, clean_range = clean_range[0], clean_range[1:] - start, end = clean_range.split("-") - start, end = int(start), int(end) - if group == "M": - group = [room_id for (room_id, user_id) in management_rooms] - elif group == "A": - group = portals - elif group == "U": - group = unidentified_rooms - elif group == "I": - group = empty_portals - elif group == "T": - group = tombstoned_rooms - else: - raise ValueError("Unknown group") - rooms_to_clean = group[start - 1:end] - except (KeyError, ValueError): - return await evt.reply( - "**Usage:** `$cmdprefix+sp clean-groups <_M|A|U|I_>") - else: - return await evt.reply(f"Unknown room cleaning action `{command}`. " - "Use `$cmdprefix+sp cancel` to cancel room " - "cleaning.") - - evt.sender.command_status = { - "next": lambda confirm: execute_room_cleanup(confirm, rooms_to_clean), - "action": "Room cleaning", - } - await evt.reply(f"To confirm cleaning up {len(rooms_to_clean)} rooms, type " - "`$cmdprefix+sp confirm-clean`.") - - -async def execute_room_cleanup(evt, rooms_to_clean: List[Union[po.Portal, RoomID]]) -> None: - if len(evt.args) > 0 and evt.args[0] == "confirm-clean": - await evt.reply(f"Cleaning {len(rooms_to_clean)} rooms. " - "This might take a while.") - cleaned = 0 - for room in rooms_to_clean: - if isinstance(room, po.Portal): - await room.cleanup_and_delete() - cleaned += 1 - else: - await po.Portal.cleanup_room(evt.az.intent, room, "Room deleted") - cleaned += 1 - evt.sender.command_status = None - await evt.reply(f"{cleaned} rooms cleaned up successfully.") - else: - await evt.reply("Room cleaning cancelled.") diff --git a/mautrix_telegram/portal/base.py b/mautrix_telegram/portal/base.py index 737cea56..00c837de 100644 --- a/mautrix_telegram/portal/base.py +++ b/mautrix_telegram/portal/base.py @@ -155,6 +155,10 @@ class BasePortal(MautrixBasePortal, ABC): return str(self.tgid) return f"{self.tg_receiver}<->{self.tgid}" + @property + def name(self) -> str: + return self.title + @property def alias(self) -> Optional[RoomAlias]: if not self.username: @@ -272,46 +276,22 @@ class BasePortal(MautrixBasePortal, ABC): # endregion # region Matrix room cleanup - async def get_authenticated_matrix_users(self) -> List['u.User']: + async def get_authenticated_matrix_users(self) -> List[UserID]: try: members = await self.main_intent.get_room_members(self.mxid) except MatrixRequestError: return [] - authenticated: List[u.User] = [] + authenticated: List[UserID] = [] has_bot = self.has_bot - for member_str in members: - member = UserID(member_str) - if p.Puppet.get_id_from_mxid(member) or member == self.main_intent.mxid: + for member in members: + if p.Puppet.get_id_from_mxid(member) or member == self.az.bot_mxid: continue user = await u.User.get_by_mxid(member).ensure_started() authenticated_through_bot = has_bot and user.relaybot_whitelisted if authenticated_through_bot or await user.has_full_access(allow_bot=True): - authenticated.append(user) + authenticated.append(user.mxid) return authenticated - @classmethod - async def cleanup_room(cls, intent: IntentAPI, room_id: RoomID, message: str, - puppets_only: bool = False) -> None: - # TODO use the cleanup_room from BasePortal instead of this - try: - members = await intent.get_room_members(room_id) - except MatrixRequestError: - members = [] - for user in members: - puppet = await 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) - async def cleanup_portal(self, message: str, puppets_only: bool = False) -> None: if self.username: try: @@ -319,13 +299,6 @@ class BasePortal(MautrixBasePortal, ABC): except (MatrixRequestError, IntentError): self.log.warning("Failed to remove alias when cleaning up room", exc_info=True) await self.cleanup_room(self.main_intent, self.mxid, message, puppets_only) - - async def unbridge(self) -> None: - await self.cleanup_portal("Room unbridged", puppets_only=True) - self.delete() - - async def cleanup_and_delete(self) -> None: - await self.cleanup_portal("Portal deleted") self.delete() # endregion @@ -351,6 +324,7 @@ class BasePortal(MautrixBasePortal, ABC): encrypted=self.encrypted) def delete(self) -> None: + # TODO the superclass delete method is async, this should be too try: del self.by_tgid[self.tgid_full] except KeyError: @@ -544,6 +518,7 @@ def init(context: Context) -> None: global config BasePortal.az, config, BasePortal.loop, BasePortal.bot = context.core BasePortal.matrix = context.mx + BasePortal.bridge = context.bridge BasePortal.max_initial_member_sync = config["bridge.max_initial_member_sync"] BasePortal.sync_channel_members = config["bridge.sync_channel_members"] BasePortal.sync_matrix_state = config["bridge.sync_matrix_state"] diff --git a/requirements.txt b/requirements.txt index 9b0d4fa6..a62145b9 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,6 +5,6 @@ python-magic>=0.4,<0.5 commonmark>=0.8,<0.10 aiohttp>=3,<3.7 yarl<1.6 -mautrix==0.8.0rc1 +mautrix==0.8.0rc2 telethon>=1.17,<1.18 telethon-session-sqlalchemy>=0.2.14,<0.3