Improve things
This commit is contained in:
+2
-1
@@ -119,9 +119,10 @@ bridge:
|
||||
# their Telegram account at startup.
|
||||
startup_sync: true
|
||||
# Number of most recently active dialogs to check when syncing chats.
|
||||
# Dialogs include groups and private chats, but only groups are synced.
|
||||
# Set to 0 to remove limit.
|
||||
sync_dialog_limit: 30
|
||||
# Whether or not to sync and create portals for direct chats at startup.
|
||||
sync_direct_chats: false
|
||||
# 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
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
from typing import Tuple, Optional, AsyncIterable, Union, Dict, TYPE_CHECKING
|
||||
from typing import Tuple, Optional, Union, Dict, TYPE_CHECKING
|
||||
from abc import ABC, abstractmethod
|
||||
import asyncio
|
||||
import logging
|
||||
@@ -23,12 +23,12 @@ import time
|
||||
from telethon.sessions import Session
|
||||
from telethon.tl.patched import MessageService, Message
|
||||
from telethon.tl.types import (
|
||||
Channel, ChannelForbidden, Chat, ChatForbidden, MessageActionChannelMigrateFrom, PeerUser,
|
||||
TypeUpdate, UpdateChannelPinnedMessage, UpdateChatPinnedMessage, UpdateChatParticipantAdmin,
|
||||
UpdateChatParticipants, UpdateChatUserTyping, UpdateDeleteChannelMessages, UpdateNewMessage,
|
||||
UpdateDeleteMessages, UpdateEditChannelMessage, UpdateEditMessage, UpdateNewChannelMessage,
|
||||
UpdateReadHistoryOutbox, UpdateShortChatMessage, UpdateShortMessage, UpdateUserName,
|
||||
UpdateUserPhoto, UpdateUserStatus, UpdateUserTyping, User, UserStatusOffline, UserStatusOnline)
|
||||
Channel, Chat, MessageActionChannelMigrateFrom, PeerUser, TypeUpdate, UpdateChatPinnedMessage,
|
||||
UpdateChannelPinnedMessage, UpdateChatParticipantAdmin, UpdateChatParticipants,
|
||||
UpdateChatUserTyping, UpdateDeleteChannelMessages, UpdateNewMessage, UpdateDeleteMessages,
|
||||
UpdateEditChannelMessage, UpdateEditMessage, UpdateNewChannelMessage, UpdateReadHistoryOutbox,
|
||||
UpdateShortChatMessage, UpdateShortMessage, UpdateUserName, UpdateUserPhoto, UpdateUserStatus,
|
||||
UpdateUserTyping, User, UserStatusOffline, UserStatusOnline)
|
||||
|
||||
from mautrix.types import UserID, PresenceState
|
||||
from mautrix.errors import MatrixError
|
||||
@@ -189,13 +189,6 @@ class AbstractUser(ABC):
|
||||
if UPDATE_TIME:
|
||||
UPDATE_TIME.labels(update_type=type(update).__name__).observe(time.time() - start_time)
|
||||
|
||||
def get_dialogs(self, limit: int = None) -> AsyncIterable[Union[User, Chat, Channel]]:
|
||||
return (dialog.entity async for dialog in
|
||||
self.client.iter_dialogs(limit=limit, ignore_migrated=True, archived=False)
|
||||
if isinstance(dialog.entity, (ChatForbidden, ChannelForbidden))
|
||||
or (isinstance(dialog.entity, Chat)
|
||||
and (dialog.entity.deactivated or dialog.entity.left)))
|
||||
|
||||
@property
|
||||
@abstractmethod
|
||||
def name(self) -> str:
|
||||
|
||||
@@ -85,6 +85,7 @@ class Config(BaseBridgeConfig):
|
||||
copy("bridge.skip_deleted_members")
|
||||
copy("bridge.startup_sync")
|
||||
copy("bridge.sync_dialog_limit")
|
||||
copy("bridge.sync_direct_chats")
|
||||
copy("bridge.max_telegram_delete")
|
||||
copy("bridge.sync_matrix_state")
|
||||
copy("bridge.allow_matrix_login")
|
||||
|
||||
@@ -115,7 +115,7 @@ class BasePortal(ABC):
|
||||
self._db_instance = db_instance
|
||||
self._main_intent = None
|
||||
self.deleted = False
|
||||
self.log = self.base_log.getChild(self.tgid_log) if self.tgid else self.base_log
|
||||
self.log = self.base_log.getChild(self.tgid_log if self.tgid else self.mxid)
|
||||
|
||||
self.dedup = PortalDedup(self)
|
||||
self.send_lock = PortalSendLock()
|
||||
@@ -155,7 +155,7 @@ class BasePortal(ABC):
|
||||
if not self._main_intent:
|
||||
direct = self.peer_type == "user"
|
||||
puppet = p.Puppet.get(self.tgid) if direct else None
|
||||
self._main_intent = puppet.intent if direct else self.az.intent
|
||||
self._main_intent = puppet.intent_for(self) if direct else self.az.intent
|
||||
return self._main_intent
|
||||
|
||||
@property
|
||||
@@ -272,7 +272,7 @@ class BasePortal(ABC):
|
||||
if user != intent.mxid and (not puppets_only or puppet):
|
||||
try:
|
||||
if puppet:
|
||||
await puppet.intent.leave_room(room_id)
|
||||
await puppet.default_mxid_intent.leave_room(room_id)
|
||||
else:
|
||||
await intent.kick_user(room_id, user, message)
|
||||
except (MatrixRequestError, IntentError):
|
||||
|
||||
@@ -100,7 +100,7 @@ class PortalMetadata(BasePortal, ABC):
|
||||
self.tgid = new_id
|
||||
self.tg_receiver = new_id
|
||||
self.by_tgid[self.tgid_full] = self
|
||||
self.log = self.base_log.getChild(str(self.tgid))
|
||||
self.log = self.base_log.getChild(self.tgid_log)
|
||||
self.log.info(f"Telegram chat upgraded from {old_id}")
|
||||
|
||||
async def set_telegram_username(self, source: 'u.User', username: str) -> None:
|
||||
@@ -145,7 +145,7 @@ class PortalMetadata(BasePortal, ABC):
|
||||
self.by_tgid[self.tgid_full] = self
|
||||
await self.update_info(source, entity)
|
||||
self.db_instance.insert()
|
||||
self.log = self.base_log.getChild(str(self.tgid))
|
||||
self.log = self.base_log.getChild(self.tgid_log)
|
||||
|
||||
if self.bot and self.bot.tgid in invites:
|
||||
self.bot.add_chat(self.tgid, self.peer_type)
|
||||
@@ -192,6 +192,17 @@ class PortalMetadata(BasePortal, ABC):
|
||||
levels: PowerLevelStateEventContent = None,
|
||||
users: List[User] = None,
|
||||
participants: List[TypeParticipant] = None) -> None:
|
||||
try:
|
||||
await self._update_matrix_room(user, entity, direct, puppet, levels, users,
|
||||
participants)
|
||||
except Exception:
|
||||
self.log.exception("Fatal error updating Matrix room")
|
||||
|
||||
async def _update_matrix_room(self, user: 'AbstractUser', entity: Union[TypeChat, User],
|
||||
direct: bool, puppet: p.Puppet = None,
|
||||
levels: PowerLevelStateEventContent = None,
|
||||
users: List[User] = None,
|
||||
participants: List[TypeParticipant] = None) -> None:
|
||||
if not direct:
|
||||
await self.update_info(user, entity)
|
||||
if not users or not participants:
|
||||
@@ -202,7 +213,7 @@ class PortalMetadata(BasePortal, ABC):
|
||||
if not puppet:
|
||||
puppet = p.Puppet.get(self.tgid)
|
||||
await puppet.update_info(user, entity)
|
||||
await puppet.intent.join_room(self.mxid)
|
||||
await puppet.intent_for(self).join_room(self.mxid)
|
||||
if self.sync_matrix_state:
|
||||
await self.sync_matrix_members()
|
||||
|
||||
@@ -221,7 +232,10 @@ class PortalMetadata(BasePortal, ABC):
|
||||
await self.invite_to_matrix(invites or [])
|
||||
return self.mxid
|
||||
async with self._room_create_lock:
|
||||
return await self._create_matrix_room(user, entity, invites)
|
||||
try:
|
||||
return await self._create_matrix_room(user, entity, invites)
|
||||
except Exception:
|
||||
self.log.exception("Fatal error creating Matrix room")
|
||||
|
||||
async def _create_matrix_room(self, user: 'AbstractUser', entity: TypeChat, invites: InviteList
|
||||
) -> Optional[RoomID]:
|
||||
@@ -235,7 +249,7 @@ class PortalMetadata(BasePortal, ABC):
|
||||
|
||||
if not entity:
|
||||
entity = await self.get_entity(user)
|
||||
self.log.debug("Fetched data: %s", entity)
|
||||
self.log.debug(f"Fetched data: {entity}")
|
||||
|
||||
self.log.debug("Creating room")
|
||||
|
||||
@@ -244,8 +258,12 @@ class PortalMetadata(BasePortal, ABC):
|
||||
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
|
||||
self._main_intent = puppet.intent if direct else self.az.intent
|
||||
self._main_intent = puppet.intent_for(self) if direct else self.az.intent
|
||||
|
||||
if self.peer_type == "channel":
|
||||
self.megagroup = entity.megagroup
|
||||
@@ -280,7 +298,8 @@ class PortalMetadata(BasePortal, ABC):
|
||||
|
||||
room_id = await self.main_intent.create_room(alias_localpart=alias, preset=preset,
|
||||
is_direct=direct, invitees=invites or [],
|
||||
name=self.title, initial_state=initial_state)
|
||||
name=self.title, topic=self.about,
|
||||
initial_state=initial_state)
|
||||
if not room_id:
|
||||
raise Exception(f"Failed to create room")
|
||||
|
||||
@@ -420,7 +439,7 @@ class PortalMetadata(BasePortal, ABC):
|
||||
if entity.bot:
|
||||
self._add_bot_chat(entity)
|
||||
allowed_tgids.add(entity.id)
|
||||
await puppet.intent.ensure_joined(self.mxid)
|
||||
await puppet.intent_for(self).ensure_joined(self.mxid)
|
||||
await puppet.update_info(source, entity)
|
||||
|
||||
user = u.User.get_by_tgid(TelegramID(entity.id))
|
||||
@@ -461,7 +480,7 @@ class PortalMetadata(BasePortal, ABC):
|
||||
if source:
|
||||
entity: User = await source.client.get_entity(PeerUser(user_id))
|
||||
await puppet.update_info(source, entity)
|
||||
await puppet.intent.join_room(self.mxid)
|
||||
await puppet.intent_for(self).join_room(self.mxid)
|
||||
|
||||
user = u.User.get_by_tgid(user_id)
|
||||
if user:
|
||||
@@ -476,16 +495,16 @@ class PortalMetadata(BasePortal, ABC):
|
||||
else "Left Telegram chat")
|
||||
if sender.tgid != puppet.tgid:
|
||||
try:
|
||||
await sender.intent.kick_user(self.mxid, puppet.mxid)
|
||||
await sender.intent_for(self).kick_user(self.mxid, puppet.mxid)
|
||||
except MForbidden:
|
||||
await self.main_intent.kick_user(self.mxid, puppet.mxid, kick_message)
|
||||
else:
|
||||
await puppet.intent.leave_room(self.mxid)
|
||||
await puppet.intent_for(self).leave_room(self.mxid)
|
||||
if user:
|
||||
user.unregister_portal(self)
|
||||
if sender.tgid != puppet.tgid:
|
||||
try:
|
||||
await sender.intent.kick_user(self.mxid, puppet.mxid)
|
||||
await sender.intent_for(self).kick_user(self.mxid, puppet.mxid)
|
||||
return
|
||||
except MForbidden:
|
||||
pass
|
||||
|
||||
@@ -73,7 +73,7 @@ class PortalTelegram(BasePortal, ABC):
|
||||
|
||||
async def handle_telegram_typing(self, user: p.Puppet,
|
||||
_: Union[UpdateUserTyping, UpdateChatUserTyping]) -> None:
|
||||
await user.intent.set_typing(self.mxid, is_typing=True)
|
||||
await user.intent_for(self).set_typing(self.mxid, is_typing=True)
|
||||
|
||||
def _get_external_url(self, evt: Message) -> Optional[str]:
|
||||
if self.peer_type == "channel" and self.username is not None:
|
||||
@@ -361,7 +361,7 @@ class PortalTelegram(BasePortal, ABC):
|
||||
f"{editing_msg.mxid}\">Edit</a>: "
|
||||
f"{content.formatted_body or escape_html(content.body)}")
|
||||
|
||||
intent = sender.intent if sender else self.main_intent
|
||||
intent = sender.intent_for(self) if sender else self.main_intent
|
||||
await intent.set_typing(self.mxid, is_typing=False)
|
||||
event_id = await intent.send_message(self.mxid, content)
|
||||
|
||||
@@ -409,7 +409,7 @@ class PortalTelegram(BasePortal, ABC):
|
||||
MessageMediaGame, MessageMediaPoll, MessageMediaUnsupported)
|
||||
media = evt.media if hasattr(evt, "media") and isinstance(evt.media,
|
||||
allowed_media) else None
|
||||
intent = sender.intent if sender else self.main_intent
|
||||
intent = sender.intent_for(self) if sender else self.main_intent
|
||||
if not media and evt.message:
|
||||
is_bot = sender.is_bot if sender else False
|
||||
event_id = await self.handle_telegram_text(source, intent, is_bot, evt)
|
||||
@@ -489,7 +489,7 @@ class PortalTelegram(BasePortal, ABC):
|
||||
elif isinstance(action, MessageActionChatMigrateTo):
|
||||
self.peer_type = "channel"
|
||||
self._migrate_and_save_telegram(TelegramID(action.channel_id))
|
||||
await sender.intent.send_emote(self.mxid, "upgraded this group to a supergroup.")
|
||||
await sender.intent_for(self).send_emote(self.mxid, "upgraded this group to a supergroup.")
|
||||
elif isinstance(action, MessageActionPinMessage):
|
||||
await self.receive_telegram_pin_sender(sender)
|
||||
elif isinstance(action, MessageActionGameScore):
|
||||
@@ -515,7 +515,7 @@ class PortalTelegram(BasePortal, ABC):
|
||||
await self.update_telegram_pin()
|
||||
|
||||
async def update_telegram_pin(self) -> None:
|
||||
intent = (self._temp_pinned_message_sender.intent
|
||||
intent = (self._temp_pinned_message_sender.intent_for(self)
|
||||
if self._temp_pinned_message_sender else self.main_intent)
|
||||
msg_id = self._temp_pinned_message_id
|
||||
self._temp_pinned_message_id = None
|
||||
|
||||
@@ -28,7 +28,7 @@ from mautrix.types import UserID
|
||||
|
||||
from .types import TelegramID
|
||||
from .db import Puppet as DBPuppet
|
||||
from . import util
|
||||
from . import util, portal as p
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .matrix import MatrixHandler
|
||||
@@ -135,6 +135,11 @@ class Puppet(CustomPuppetMixin):
|
||||
) -> Awaitable[Union[TypeInputPeer, TypeInputUser]]:
|
||||
return user.client.get_input_entity(self.peer)
|
||||
|
||||
def intent_for(self, portal: 'p.Portal') -> IntentAPI:
|
||||
if portal.tgid == self.tgid:
|
||||
return self.default_mxid_intent
|
||||
return self.intent
|
||||
|
||||
# region DB conversion
|
||||
|
||||
@property
|
||||
|
||||
@@ -13,14 +13,15 @@
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
from typing import (Awaitable, Dict, List, Iterable, Match, NewType, Optional, Tuple, Any,
|
||||
TYPE_CHECKING)
|
||||
from typing import (Awaitable, Dict, List, Iterable, Match, NewType, Optional, Tuple, Any, Union,
|
||||
AsyncIterable, TYPE_CHECKING)
|
||||
import logging
|
||||
import asyncio
|
||||
import re
|
||||
|
||||
from telethon.tl.types import (TypeUpdate, UpdateNewMessage, UpdateNewChannelMessage, PeerUser,
|
||||
UpdateShortChatMessage, UpdateShortMessage, User as TLUser)
|
||||
UpdateShortChatMessage, UpdateShortMessage, User as TLUser,
|
||||
ChannelForbidden, ChatForbidden, Chat, Channel)
|
||||
from telethon.tl.types.contacts import ContactsNotModified
|
||||
from telethon.tl.functions.contacts import GetContactsRequest, SearchRequest
|
||||
from telethon.tl.functions.account import UpdateStatusRequest
|
||||
@@ -310,7 +311,17 @@ class User(AbstractUser):
|
||||
if self.is_bot:
|
||||
return
|
||||
creators = []
|
||||
async for entity in self.get_dialogs(limit=config["bridge.sync_dialog_limit"] or None):
|
||||
limit = config["bridge.sync_dialog_limit"] or None
|
||||
self.log.debug(f"Syncing dialogs (limit={limit}, synchronous_create={synchronous_create})")
|
||||
async for dialog in self.client.iter_dialogs(limit=limit, ignore_migrated=True,
|
||||
archived=False):
|
||||
entity = dialog.entity
|
||||
if isinstance(entity, Chat) and (entity.deactivated or entity.left):
|
||||
self.log.warn(f"Ignoring deactivated or left chat {entity} while syncing")
|
||||
continue
|
||||
elif isinstance(entity, TLUser) and not config["bridge.sync_direct_chats"]:
|
||||
continue
|
||||
self.log.info(f"Syncing {type(entity)}")
|
||||
portal = po.Portal.get_by_entity(entity)
|
||||
self.portals[portal.tgid_full] = portal
|
||||
creators.append(
|
||||
@@ -318,6 +329,7 @@ class User(AbstractUser):
|
||||
synchronous=synchronous_create))
|
||||
self.save(portals=True)
|
||||
await asyncio.gather(*creators, loop=self.loop)
|
||||
self.log.debug("Dialog syncing complete")
|
||||
|
||||
def register_portal(self, portal: po.Portal) -> None:
|
||||
try:
|
||||
|
||||
@@ -319,7 +319,7 @@ class ProvisioningAPI(AuthAPI):
|
||||
return web.json_response([{
|
||||
"id": get_peer_id(chat),
|
||||
"title": chat.title,
|
||||
} async for chat in user.get_dialogs()])
|
||||
} async for chat in user.client.get_dialogs(ignore_migrated=True, archived=False)])
|
||||
else:
|
||||
return web.json_response([{
|
||||
"id": get_peer_id(chat.peer),
|
||||
|
||||
Reference in New Issue
Block a user