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