Even even even more migrations to mautrix-python

This commit is contained in:
Tulir Asokan
2019-08-05 22:10:43 +03:00
parent d6a2e7a9f7
commit 30563f3648
7 changed files with 183 additions and 109 deletions
+6 -3
View File
@@ -20,6 +20,7 @@ import logging
import platform
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,
@@ -29,7 +30,7 @@ from telethon.tl.types import (
UpdateReadHistoryOutbox, UpdateShortChatMessage, UpdateShortMessage, UpdateUserName,
UpdateUserPhoto, UpdateUserStatus, UpdateUserTyping, User, UserStatusOffline, UserStatusOnline)
from mautrix.types import UserID
from mautrix.types import UserID, PresenceState
from mautrix.errors import MatrixError
from mautrix.appservice import AppService
from alchemysession import AlchemySessionContainer
@@ -135,6 +136,8 @@ class AbstractUser(ABC):
sysversion = config["telegram.device_info.system_version"]
appversion = config["telegram.device_info.app_version"]
assert isinstance(self.session, Session)
self.client = MautrixTelegramClient(
session=self.session,
@@ -335,9 +338,9 @@ class AbstractUser(ABC):
async def update_status(self, update: UpdateUserStatus) -> None:
puppet = pu.Puppet.get(TelegramID(update.user_id))
if isinstance(update.status, UserStatusOnline):
await puppet.default_mxid_intent.set_presence("online")
await puppet.default_mxid_intent.set_presence(PresenceState.ONLINE)
elif isinstance(update.status, UserStatusOffline):
await puppet.default_mxid_intent.set_presence("offline")
await puppet.default_mxid_intent.set_presence(PresenceState.OFFLINE)
else:
self.log.warning("Unexpected user status update: %s", update)
return
+7 -4
View File
@@ -1,7 +1,7 @@
from .base import BasePortal, init as init_base
from .portal_matrix import PortalMatrix
from .portal_metadata import PortalMetadata
from .portal_telegram import PortalTelegram
from .portal_matrix import PortalMatrix, init as init_matrix
from .portal_metadata import PortalMetadata, init as init_metadata
from .portal_telegram import PortalTelegram, init as init_telegram
from .deduplication import init as init_dedup
from ..context import Context
@@ -13,6 +13,9 @@ class Portal(BasePortal, PortalMatrix, PortalTelegram, PortalMetadata):
def init(context: Context) -> None:
init_base(context)
init_dedup(context)
init_metadata(context)
init_telegram(context)
init_matrix(context)
__all__ = ["Portal", "BasePortal", "init"]
__all__ = ["Portal", "init"]
+2 -2
View File
@@ -1,7 +1,7 @@
from typing import Union
from .base import BasePortal, init as init_base
from .base import BasePortal
from .portal_matrix import PortalMatrix
from .portal_metadata import PortalMetadata
from .portal_telegram import PortalTelegram
Portal = Union[BasePortal, PortalMatrix, PortalTelegram, PortalMetadata]
Portal = Union[BasePortal, PortalMatrix, PortalMetadata, PortalTelegram]
+57 -8
View File
@@ -14,23 +14,24 @@
# 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, Optional, Pattern, Tuple, Union, Any, TYPE_CHECKING
from abc import ABC
from abc import ABC, abstractmethod
import asyncio
import logging
import json
import re
from telethon.tl.functions.messages import ExportChatInviteRequest
from telethon.tl.patched import Message, MessageService
from telethon.tl.types import (Channel, ChannelFull, Chat, ChatFull, ChatInviteEmpty, InputChannel,
InputPeerChannel, InputPeerChat, InputPeerUser, InputUser,
PeerChannel, PeerChat, PeerUser, TypeChat, TypeInputPeer, TypePeer,
TypeUser, TypeUserFull, User, UserFull, TypeInputChannel,
Photo, Document, TypePhotoSize, PhotoSize, InputPhotoFileLocation)
TypeUser, TypeUserFull, User, UserFull, TypeInputChannel, Photo,
Document, TypePhotoSize, PhotoSize, InputPhotoFileLocation,
TypeChatParticipant, TypeChannelParticipant, PhotoEmpty, ChatPhoto,
ChatPhotoEmpty)
from mautrix.errors import MatrixRequestError, IntentError
from mautrix.appservice import AppService, IntentAPI
from mautrix.types import RoomID, UserID, EventType
from mautrix.types import RoomID, RoomAlias, UserID, EventType, PowerLevelStateEventContent
from ..types import TelegramID
from ..context import Context
@@ -45,9 +46,11 @@ if TYPE_CHECKING:
from ..config import Config
from . import Portal
config: Optional['Config'] = None
TypeParticipant = Union[TypeChatParticipant, TypeChannelParticipant]
TypeChatPhoto = Union[ChatPhoto, ChatPhotoEmpty, Photo, PhotoEmpty]
InviteList = Union[UserID, List[UserID]]
TypeMessage = Union[Message, MessageService]
config: Optional['Config'] = None
class BasePortal(ABC):
@@ -86,6 +89,8 @@ class BasePortal(ABC):
deleted: bool
log: logging.Logger
alias: Optional[RoomAlias]
dedup: PortalDedup
send_lock: PortalSendLock
@@ -169,7 +174,7 @@ class BasePortal(ABC):
local = util.recursive_get(self.local_config, key)
if local is not None:
return local
return self.config[f"bridge.{key}"]
return config[f"bridge.{key}"]
@staticmethod
def _get_largest_photo_size(photo: Union[Photo, Document]
@@ -412,6 +417,50 @@ class BasePortal(ABC):
type_name if create else None)
# endregion
# region Abstract methods (cross-called in matrix/metadata/telegram classes)
@abstractmethod
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:
pass
@abstractmethod
async def create_matrix_room(self, user: 'AbstractUser', entity: TypeChat = None,
invites: InviteList = None, update_if_exists: bool = True,
synchronous: bool = False) -> Optional[str]:
pass
@abstractmethod
async def _add_telegram_user(self, user_id: TelegramID, source: Optional['AbstractUser'] = None
) -> None:
pass
@abstractmethod
async def _delete_telegram_user(self, user_id: TelegramID, sender: p.Puppet) -> None:
pass
@abstractmethod
async def _update_title(self, title: str, save: bool = False) -> bool:
pass
@abstractmethod
async def _update_avatar(self, user: 'AbstractUser', photo: Union[TypeChatPhoto],
save: bool = False) -> bool:
pass
@abstractmethod
def _migrate_and_save_telegram(self, new_id: TelegramID) -> None:
pass
@abstractmethod
def handle_matrix_power_levels(self, sender: 'u.User', new_levels: PowerLevelStateEventContent,
old_levels: PowerLevelStateEventContent) -> Awaitable[None]:
pass
# endregion
def init(context: Context) -> None:
+17 -12
View File
@@ -16,6 +16,7 @@
from typing import Awaitable, Dict, List, Optional, Tuple, Union, Any, TYPE_CHECKING
from html import escape as escape_html
from string import Template
from abc import ABC
import mimetypes
import magic
@@ -38,22 +39,26 @@ from telethon.tl.types import (
from mautrix.types import (EventID, RoomID, UserID, ContentURI, MessageType,
TextMessageEventContent, Format)
from mautrix.bridge import BasePortal as AbstractPortal
from mautrix.bridge import BasePortal as MautrixBasePortal
from ..types import TelegramID
from ..db import Message as DBMessage
from ..util import sane_mimetypes
from ..context import Context
from .. import puppet as p, user as u, formatter, util
from .base import BasePortal
if TYPE_CHECKING:
from ..abstract_user import AbstractUser
from ..tgclient import MautrixTelegramClient
from ..config import Config
TypeMessage = Union[Message, MessageService]
config: Optional['Config'] = None
class PortalMatrix(BasePortal, AbstractPortal):
class PortalMatrix(BasePortal, MautrixBasePortal, ABC):
@staticmethod
def _get_file_meta(body: str, mime: str) -> str:
try:
@@ -104,9 +109,7 @@ class PortalMatrix(BasePortal, AbstractPortal):
prev_displayname=prev_displayname)
async def get_displayname(self, user: 'u.User') -> str:
# FIXME this doesn't seem to support per-room names or use cache in mautrix 0.4
return (await self.main_intent.get_displayname(self.mxid, user.mxid)
or user.mxid)
return await self.main_intent.get_room_displayname(self.mxid, user.mxid) or user.mxid
def set_typing(self, user: 'u.User', typing: bool = True,
action: type = SendMessageTypingAction) -> Awaitable[bool]:
@@ -462,7 +465,7 @@ class PortalMatrix(BasePortal, AbstractPortal):
file = await self.main_intent.download_media(url)
mime = magic.from_buffer(file, mime=True)
ext = sane_mimetypes.guess_extension(mime)
uploaded = await sender.client.upload_file(file, file_name=f"avatar{ext}", use_cache=False)
uploaded = await sender.client.upload_file(file, file_name=f"avatar{ext}")
photo = InputChatUploadedPhoto(file=uploaded)
if self.peer_type == "chat":
@@ -485,8 +488,8 @@ class PortalMatrix(BasePortal, AbstractPortal):
old_room = self.mxid
self.migrate_and_save_matrix(new_room)
await self.main_intent.join_room(new_room)
entity = None # type: TypeInputPeer
user = None # type: AbstractUser
entity: Optional[TypeInputPeer] = None
user: Optional[AbstractUser] = None
if self.bot and self.has_bot:
user = self.bot
entity = await self.get_input_entity(self.bot)
@@ -505,10 +508,7 @@ class PortalMatrix(BasePortal, AbstractPortal):
self.log.error(
"Failed to fully migrate to upgraded Matrix room: no Telegram user found.")
return
users, participants = await self._get_users(self.bot, entity)
await self.sync_telegram_users(user, users)
levels = await self.main_intent.get_power_levels(self.mxid)
await self.update_telegram_participants(participants, levels)
await self.update_matrix_room(user, entity, direct=self.peer_type == "user")
self.log.info(f"Upgraded room from {old_room} to {self.mxid}")
def migrate_and_save_matrix(self, new_id: RoomID) -> None:
@@ -519,3 +519,8 @@ class PortalMatrix(BasePortal, AbstractPortal):
self.mxid = new_id
self.db_instance.update(mxid=self.mxid)
self.by_mxid[self.mxid] = self
def init(context: Context) -> None:
global config
config = context.config
+75 -71
View File
@@ -14,6 +14,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 List, Optional, Tuple, Union, TYPE_CHECKING
from abc import ABC
import asyncio
from telethon.tl.functions.messages import (AddChatUserRequest, CreateChatRequest,
@@ -22,28 +23,29 @@ from telethon.tl.functions.channels import (CreateChannelRequest, GetParticipant
InviteToChannelRequest, UpdateUsernameRequest)
from telethon.errors import ChatAdminRequiredError
from telethon.tl.types import (
Channel, ChatBannedRights, Document, ChannelParticipantsRecent, ChannelParticipantsSearch,
ChatPhoto, PhotoEmpty, InputChannel, InputPhotoFileLocation, InputUser, ChatPhotoEmpty,
PeerUser, Photo, TypeChannelParticipant, TypeChat, TypeChatParticipant, TypeInputPeer,
TypePhotoSize, TypeUser, PhotoSize, User, InputPeerPhotoFileLocation)
Channel, ChatBannedRights, ChannelParticipantsRecent, ChannelParticipantsSearch, ChatPhoto,
PhotoEmpty, InputChannel, InputUser, ChatPhotoEmpty, PeerUser, Photo, TypeChat, TypeInputPeer,
TypeUser, User, InputPeerPhotoFileLocation, ChatParticipantAdmin, ChannelParticipantAdmin,
ChatParticipantCreator, ChannelParticipantCreator)
from mautrix.errors import MForbidden
from mautrix.types import (RoomID, UserID, RoomCreatePreset, ContentURI, EventType, Membership,
from mautrix.types import (RoomID, UserID, RoomCreatePreset, EventType, Membership, Member,
PowerLevelStateEventContent, RoomAlias)
from ..types import TelegramID
from ..context import Context
from .. import puppet as p, user as u, util
from .base import BasePortal
from .base import BasePortal, InviteList, TypeParticipant, TypeChatPhoto
if TYPE_CHECKING:
from ..abstract_user import AbstractUser
from ..config import Config
from . import Portal
InviteList = Union[UserID, List[UserID]]
TypeParticipant = Union[TypeChatParticipant, TypeChannelParticipant]
config: Optional['Config'] = None
class PortalMetadata(BasePortal):
class PortalMetadata(BasePortal, ABC):
_room_create_lock: asyncio.Lock
def __init__(self, *args, **kwargs) -> None:
@@ -52,7 +54,7 @@ class PortalMetadata(BasePortal):
# region Matrix -> Telegram
async def _get_telegram_users_in_matrix_room(self) -> List[TelegramID]:
async def _get_telegram_users_in_matrix_room(self) -> List[Union[InputUser, PeerUser]]:
user_tgids = set()
user_mxids = await self.main_intent.get_room_members(self.mxid, (Membership.JOIN,
Membership.INVITE))
@@ -66,7 +68,7 @@ class PortalMetadata(BasePortal):
puppet_id = p.Puppet.get_id_from_mxid(user)
if puppet_id:
user_tgids.add(puppet_id)
return list(user_tgids)
return [PeerUser(user_id) for user_id in user_tgids]
async def upgrade_telegram_chat(self, source: 'u.User') -> None:
if self.peer_type != "chat":
@@ -81,10 +83,10 @@ class PortalMetadata(BasePortal):
if not entity:
raise ValueError("Upgrade may have failed: output channel not found.")
self.peer_type = "channel"
self.migrate_and_save_telegram(TelegramID(entity.id))
self._migrate_and_save_telegram(TelegramID(entity.id))
await self.update_info(source, entity)
def migrate_and_save_telegram(self, new_id: TelegramID) -> None:
def _migrate_and_save_telegram(self, new_id: TelegramID) -> None:
try:
del self.by_tgid[self.tgid_full]
except KeyError:
@@ -107,7 +109,7 @@ class PortalMetadata(BasePortal):
raise ValueError("Only channels and supergroups have usernames.")
await source.client(
UpdateUsernameRequest(await self.get_input_entity(source), username))
if await self.update_username(username):
if await self._update_username(username):
self.save()
async def create_telegram_chat(self, source: 'u.User', supergroup: bool = False) -> None:
@@ -153,7 +155,7 @@ class PortalMetadata(BasePortal):
if levels.get_user_level(self.main_intent.mxid) == 100:
levels = self._get_base_power_levels(levels, entity)
await self.main_intent.set_power_levels(self.mxid, levels)
await self.handle_matrix_power_levels(source, levels["users"], {})
await self.handle_matrix_power_levels(source, levels, PowerLevelStateEventContent())
async def invite_telegram(self, source: 'u.User',
puppet: Union[p.Puppet, 'AbstractUser']) -> None:
@@ -195,7 +197,7 @@ class PortalMetadata(BasePortal):
await self.update_info(user, entity)
if not users or not participants:
users, participants = await self._get_users(user, entity)
await self.sync_telegram_users(user, users)
await self._sync_telegram_users(user, users)
await self.update_telegram_participants(participants, levels)
else:
if not puppet:
@@ -382,7 +384,7 @@ class PortalMetadata(BasePortal):
return changed
async def update_telegram_participants(self, participants: List[TypeParticipant],
levels: dict = None) -> None:
levels: PowerLevelStateEventContent = None) -> None:
if not levels:
levels = await self.main_intent.get_power_levels(self.mxid)
if self._participants_to_power_levels(participants, levels):
@@ -400,7 +402,7 @@ class PortalMetadata(BasePortal):
return None
return self.alias_template.format(groupname=username)
def add_bot_chat(self, bot: User) -> None:
def _add_bot_chat(self, bot: User) -> None:
if self.bot and bot.id == self.bot.tgid:
self.bot.add_chat(self.tgid, self.peer_type)
return
@@ -409,7 +411,7 @@ class PortalMetadata(BasePortal):
if user and user.is_bot:
user.register_portal(self)
async def sync_telegram_users(self, source: 'AbstractUser', users: List[User]) -> None:
async def _sync_telegram_users(self, source: 'AbstractUser', users: List[User]) -> None:
allowed_tgids = set()
skip_deleted = config["bridge.skip_deleted_members"]
for entity in users:
@@ -417,7 +419,7 @@ class PortalMetadata(BasePortal):
continue
puppet = p.Puppet.get(TelegramID(entity.id))
if entity.bot:
self.add_bot_chat(entity)
self._add_bot_chat(entity)
allowed_tgids.add(entity.id)
await puppet.intent.ensure_joined(self.mxid)
await puppet.update_info(source, entity)
@@ -454,8 +456,8 @@ class PortalMetadata(BasePortal):
"You had left this Telegram chat.")
continue
async def add_telegram_user(self, user_id: TelegramID, source: Optional['AbstractUser'] = None
) -> None:
async def _add_telegram_user(self, user_id: TelegramID, source: Optional['AbstractUser'] = None
) -> None:
puppet = p.Puppet.get(user_id)
if source:
entity: User = await source.client.get_entity(PeerUser(user_id))
@@ -467,7 +469,7 @@ class PortalMetadata(BasePortal):
user.register_portal(self)
await self.invite_to_matrix(user.mxid)
async def delete_telegram_user(self, user_id: TelegramID, sender: p.Puppet) -> None:
async def _delete_telegram_user(self, user_id: TelegramID, sender: p.Puppet) -> None:
puppet = p.Puppet.get(user_id)
user = u.User.get_by_tgid(user_id)
kick_message = (f"Kicked by {sender.displayname}"
@@ -492,72 +494,68 @@ class PortalMetadata(BasePortal):
async def update_info(self, user: 'AbstractUser', entity: TypeChat = None) -> None:
if self.peer_type == "user":
self.log.warning(f"Called update_info() for direct chat portal")
self.log.warning("Called update_info() for direct chat portal")
return
self.log.debug(f"Updating info")
self.log.debug("Updating info")
if not entity:
entity = await self.get_entity(user)
self.log.debug("Fetched data: %s", entity)
self.log.debug(f"Fetched data: {entity}")
changed = False
if self.peer_type == "channel":
changed = await self.update_username(entity.username) or changed
changed = await self._update_username(entity.username) or changed
# TODO update about text
# changed = self.update_about(entity.about) or changed
changed = await self.update_title(entity.title) or changed
changed = await self._update_title(entity.title) or changed
if isinstance(entity.photo, ChatPhoto):
changed = await self.update_avatar(user, entity.photo) or changed
changed = await self._update_avatar(user, entity.photo) or changed
if changed:
self.save()
async def update_username(self, username: str, save: bool = False) -> bool:
if self.username != username:
if self.username:
await self.main_intent.remove_room_alias(self._get_alias_localpart())
self.username = username or None
if self.username:
await self.main_intent.add_room_alias(self.mxid, self._get_alias_localpart())
if Portal.public_portals:
await self.main_intent.set_join_rule(self.mxid, "public")
else:
await self.main_intent.set_join_rule(self.mxid, "invite")
async def _update_username(self, username: str, save: bool = False) -> bool:
if self.username == username:
return False
if save:
self.save()
return True
return False
if self.username:
await self.main_intent.remove_room_alias(self._get_alias_localpart())
self.username = username or None
if self.username:
await self.main_intent.add_room_alias(self.mxid, self._get_alias_localpart())
if Portal.public_portals:
await self.main_intent.set_join_rule(self.mxid, "public")
else:
await self.main_intent.set_join_rule(self.mxid, "invite")
async def update_about(self, about: str, save: bool = False) -> bool:
if self.about != about:
self.about = about
await self.main_intent.set_room_topic(self.mxid, self.about)
if save:
self.save()
return True
return False
async def update_title(self, title: str, save: bool = False) -> bool:
if self.title != title:
self.title = title
await self.main_intent.set_room_name(self.mxid, self.title)
if save:
self.save()
return True
return False
async def remove_avatar(self, _: 'AbstractUser', save: bool = False) -> None:
await self.main_intent.set_room_avatar(self.mxid, None)
self.photo_id = None
if save:
self.save()
return True
async def update_avatar(self, user: 'AbstractUser',
photo: Union[ChatPhoto, ChatPhotoEmpty, Photo, PhotoEmpty],
save: bool = False) -> bool:
async def _update_about(self, about: str, save: bool = False) -> bool:
if self.about == about:
return False
self.about = about
await self.main_intent.set_room_topic(self.mxid, self.about)
if save:
self.save()
return True
async def _update_title(self, title: str, save: bool = False) -> bool:
if self.title == title:
return False
self.title = title
await self.main_intent.set_room_name(self.mxid, self.title)
if save:
self.save()
return True
async def _update_avatar(self, user: 'AbstractUser', photo: TypeChatPhoto, save: bool = False
) -> bool:
if isinstance(photo, ChatPhoto):
loc = InputPeerPhotoFileLocation(
peer=await self.get_input_entity(user),
@@ -576,7 +574,7 @@ class PortalMetadata(BasePortal):
raise ValueError(f"Unknown photo type {type(photo)}")
if self.photo_id != photo_id:
if not photo_id:
await self.main_intent.set_room_avatar(self.mxid, ContentURI(""))
await self.main_intent.set_room_avatar(self.mxid, None)
self.photo_id = ""
if save:
self.save()
@@ -593,6 +591,7 @@ class PortalMetadata(BasePortal):
async def _get_users(self, user: 'AbstractUser',
entity: Union[TypeInputPeer, InputUser, TypeChat, TypeUser, InputChannel]
) -> Tuple[List[TypeUser], List[TypeParticipant]]:
# TODO replace with client.get_participants
if self.peer_type == "chat":
chat = await user.client(GetFullChatRequest(chat_id=self.tgid))
return chat.users, chat.full_chat.participants.participants
@@ -610,8 +609,8 @@ class PortalMetadata(BasePortal):
entity, ChannelParticipantsRecent(), offset=0, limit=limit, hash=0))
return response.users, response.participants
elif limit > 200 or limit == -1:
users = [] # type: List[TypeUser]
participants = [] # type: List[TypeParticipant]
users: List[TypeUser] = []
participants: List[TypeParticipant] = []
offset = 0
remaining_quota = limit if limit > 0 else 1000000
query = (ChannelParticipantsSearch("") if limit == -1
@@ -635,3 +634,8 @@ class PortalMetadata(BasePortal):
return [], []
# endregion
def init(context: Context) -> None:
global config
config = context.config
+19 -9
View File
@@ -15,6 +15,7 @@
# along with this program. If not, see <https://www.gnu.org/licenses/>.
from typing import Awaitable, Dict, List, Optional, Tuple, Union, TYPE_CHECKING
from html import escape as escape_html
from abc import ABC
import random
import mimetypes
import codecs
@@ -33,7 +34,7 @@ from telethon.tl.types import (
MessageMediaDocument, MessageMediaGeo, MessageMediaPhoto, MessageMediaUnsupported,
MessageMediaGame, PeerUser, PhotoCachedSize, TypeChannelParticipant, TypeChatParticipant,
TypeDocumentAttribute, TypeMessageAction, TypePhotoSize, PhotoSize, UpdateChatUserTyping,
UpdateUserTyping, MessageEntityPre)
UpdateUserTyping, MessageEntityPre, ChatPhotoEmpty)
from mautrix.appservice import IntentAPI
from mautrix.types import EventID, UserID, ImageInfo, ThumbnailInfo
@@ -41,17 +42,21 @@ from mautrix.types import EventID, UserID, ImageInfo, ThumbnailInfo
from ..types import TelegramID
from ..db import Message as DBMessage, TelegramFile as DBTelegramFile
from ..util import sane_mimetypes
from ..context import Context
from .. import puppet as p, user as u, formatter, util
from .base import BasePortal
if TYPE_CHECKING:
from ..abstract_user import AbstractUser
from ..config import Config
InviteList = Union[UserID, List[UserID]]
TypeParticipant = Union[TypeChatParticipant, TypeChannelParticipant]
config: Optional['Config'] = None
class PortalTelegram(BasePortal):
class PortalTelegram(BasePortal, ABC):
_temp_pinned_message_id: Optional[TelegramID]
_temp_pinned_message_id_space: Optional[TelegramID]
_temp_pinned_message_sender: Optional['p.Puppet']
@@ -509,21 +514,21 @@ class PortalTelegram(BasePortal):
if should_ignore or not self.mxid:
return
if isinstance(action, MessageActionChatEditTitle):
await self.update_title(action.title, save=True)
await self._update_title(action.title, save=True)
elif isinstance(action, MessageActionChatEditPhoto):
await self.update_avatar(source, action.photo, save=True)
await self._update_avatar(source, action.photo, save=True)
elif isinstance(action, MessageActionChatDeletePhoto):
await self.remove_avatar(source, save=True)
await self._update_avatar(source, ChatPhotoEmpty(), save=True)
elif isinstance(action, MessageActionChatAddUser):
for user_id in action.users:
await self.add_telegram_user(TelegramID(user_id), source)
await self._add_telegram_user(TelegramID(user_id), source)
elif isinstance(action, MessageActionChatJoinedByLink):
await self.add_telegram_user(sender.id, source)
await self._add_telegram_user(sender.id, source)
elif isinstance(action, MessageActionChatDeleteUser):
await self.delete_telegram_user(TelegramID(action.user_id), sender)
await self._delete_telegram_user(TelegramID(action.user_id), sender)
elif isinstance(action, MessageActionChatMigrateTo):
self.peer_type = "channel"
self.migrate_and_save_telegram(TelegramID(action.channel_id))
self._migrate_and_save_telegram(TelegramID(action.channel_id))
await sender.intent.send_emote(self.mxid, "upgraded this group to a supergroup.")
elif isinstance(action, MessageActionPinMessage):
await self.receive_telegram_pin_sender(sender)
@@ -577,3 +582,8 @@ class PortalTelegram(BasePortal):
levels["events"]["m.room.name"] = level
levels["events"]["m.room.avatar"] = level
await self.main_intent.set_power_levels(self.mxid, levels)
def init(context: Context) -> None:
global config
config = context.config