From 2c47cdfac66791ae124c540277e6d01f4d52eabf Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Sun, 29 Apr 2018 13:50:52 +0300 Subject: [PATCH] Add option to limit number of members in startup sync. Fixes #115 --- example-config.yaml | 7 +++- mautrix_telegram/config.py | 1 + mautrix_telegram/portal.py | 72 ++++++++++++++++++++++++-------------- 3 files changed, 52 insertions(+), 28 deletions(-) diff --git a/example-config.yaml b/example-config.yaml index 6f22062f..0edd70d9 100644 --- a/example-config.yaml +++ b/example-config.yaml @@ -84,7 +84,12 @@ bridge: # Highlight changed/added parts in edits. Requires lxml. highlight_edits: false # Whether or not Matrix bot messages (type m.notice) should be bridged. - bridge_notices: false + bridge_notices: true + # Maximum number of members to sync per portal when starting up. Other members will be + # synced when they send messages. The maximum is 10000, after which the Telegram server + # will not send any more members. + # Defaults to no local limit (-> limited to 10000 by server) + max_initial_member_sync: -1 # The maximum number of simultaneous Telegram deletions to handle. # A large number of simultaneous redactions could put strain on your homeserver. max_telegram_delete: 10 diff --git a/mautrix_telegram/config.py b/mautrix_telegram/config.py index e3c60b04..157751bd 100644 --- a/mautrix_telegram/config.py +++ b/mautrix_telegram/config.py @@ -171,6 +171,7 @@ class Config(DictWithRecursion): copy("bridge.edits_as_replies") copy("bridge.highlight_edits") copy("bridge.bridge_notices") + copy("bridge.max_initial_member_sync") copy("bridge.max_telegram_delete") copy("bridge.allow_matrix_login") copy("bridge.inline_images") diff --git a/mautrix_telegram/portal.py b/mautrix_telegram/portal.py index d264b22d..3e39d804 100644 --- a/mautrix_telegram/portal.py +++ b/mautrix_telegram/portal.py @@ -331,22 +331,27 @@ class Portal: await puppet.intent.ensure_joined(self.mxid) await puppet.update_info(source, entity) - joined_mxids = await self.main_intent.get_room_members(self.mxid) - for user in joined_mxids: - if user == self.az.bot_mxid: - continue - puppet_id = p.Puppet.get_id_from_mxid(user) - if puppet_id and puppet_id not in allowed_tgids: - if self.bot and puppet_id == self.bot.tgid: - self.bot.remove_chat(self.tgid) - await self.main_intent.kick(self.mxid, user, - "User had left this Telegram chat.") - continue - mx_user = u.User.get_by_mxid(user, create=False) - if mx_user and not self.has_bot and mx_user.tgid not in allowed_tgids: - await self.main_intent.kick(self.mxid, mx_user.mxid, - "You had left this Telegram chat.") - continue + # We can't trust the member list if any of the following cases is true: + # * There are close to 10 000 users, because Telegram might not be sending all members. + # * The member sync count is limited, because then we might ignore some members. + # * It's a channel, because non-admins don't have access to the member list. + if len(allowed_tgids) < 9900 and config["bridge.max_initial_member_sync"] == -1: + joined_mxids = await self.main_intent.get_room_members(self.mxid) + for user in joined_mxids: + if user == self.az.bot_mxid: + continue + puppet_id = p.Puppet.get_id_from_mxid(user) + if puppet_id and puppet_id not in allowed_tgids: + if self.bot and puppet_id == self.bot.tgid: + self.bot.remove_chat(self.tgid) + await self.main_intent.kick(self.mxid, user, + "User had left this Telegram chat.") + continue + mx_user = u.User.get_by_mxid(user, create=False) + if mx_user and not self.has_bot and mx_user.tgid not in allowed_tgids: + await self.main_intent.kick(self.mxid, mx_user.mxid, + "You had left this Telegram chat.") + continue async def add_telegram_user(self, user_id, source=None): puppet = p.Puppet.get(user_id) @@ -462,19 +467,32 @@ class Portal: chat = await user.client(GetFullChatRequest(chat_id=self.tgid)) return chat.users, chat.full_chat.participants.participants elif self.peer_type == "channel": + limit = config["bridge.max_initial_member_sync"] + if limit == 0: + return [], [] + try: - users, participants = [], [] - offset = 0 - while True: + if 0 < limit <= 200: response = await user.client(GetParticipantsRequest( - entity, ChannelParticipantsSearch(""), offset=offset, limit=100, hash=0 - )) - if not response.users: - break - participants += response.participants - users += response.users - offset += len(response.participants) - return users, participants + entity, ChannelParticipantsRecent(), offset=0, limit=limit, hash=0)) + return response.users, response.participants + elif limit > 200 or limit == -1: + users, participants = [], [] + offset = 0 + remaining_quota = limit if limit > 0 else 1000000 + query = ChannelParticipantsSearch("") if limit == -1 else ChannelParticipantsRecent() + while True: + if remaining_quota <= 0: + break + response = await user.client(GetParticipantsRequest( + entity, query, offset=offset, limit=min(remaining_quota, 100), hash=0)) + if not response.users: + break + participants += response.participants + users += response.users + offset += len(response.participants) + remaining_quota -= len(response.participants) + return users, participants except ChatAdminRequiredError: return [], [] elif self.peer_type == "user":