Add private_chat_portal_meta option and fix bugs
* The new option is implicitly enabled when encryption is default * Private chat metadata is now updated after creating the room too * The puppet metadata is updated before creating the room, to make sure their name is available locally
This commit is contained in:
@@ -123,6 +123,7 @@ class Config(BaseBridgeConfig):
|
|||||||
copy("bridge.animated_sticker.args")
|
copy("bridge.animated_sticker.args")
|
||||||
copy("bridge.encryption.allow")
|
copy("bridge.encryption.allow")
|
||||||
copy("bridge.encryption.default")
|
copy("bridge.encryption.default")
|
||||||
|
copy("bridge.private_chat_portal_meta")
|
||||||
|
|
||||||
copy("bridge.initial_power_level_overrides.group")
|
copy("bridge.initial_power_level_overrides.group")
|
||||||
copy("bridge.initial_power_level_overrides.user")
|
copy("bridge.initial_power_level_overrides.user")
|
||||||
|
|||||||
@@ -207,6 +207,9 @@ bridge:
|
|||||||
# Default to encryption, force-enable encryption in all portals the bridge creates
|
# Default to encryption, force-enable encryption in all portals the bridge creates
|
||||||
# This will cause the bridge bot to be in private chats for the encryption to work properly.
|
# This will cause the bridge bot to be in private chats for the encryption to work properly.
|
||||||
default: false
|
default: false
|
||||||
|
# Whether or not to explicitly set the avatar and room name for private
|
||||||
|
# chat portal rooms. This will be implicitly enabled if encryption.default is true.
|
||||||
|
private_chat_portal_meta: false
|
||||||
|
|
||||||
# Overrides for base power levels.
|
# Overrides for base power levels.
|
||||||
initial_power_level_overrides:
|
initial_power_level_overrides:
|
||||||
|
|||||||
@@ -69,6 +69,7 @@ class BasePortal(ABC):
|
|||||||
sync_channel_members: bool = True
|
sync_channel_members: bool = True
|
||||||
sync_matrix_state: bool = True
|
sync_matrix_state: bool = True
|
||||||
public_portals: bool = False
|
public_portals: bool = False
|
||||||
|
private_chat_portal_meta: bool = False
|
||||||
|
|
||||||
alias_template: SimpleTemplate[str]
|
alias_template: SimpleTemplate[str]
|
||||||
hs_domain: str
|
hs_domain: str
|
||||||
@@ -518,6 +519,7 @@ def init(context: Context) -> None:
|
|||||||
BasePortal.sync_channel_members = config["bridge.sync_channel_members"]
|
BasePortal.sync_channel_members = config["bridge.sync_channel_members"]
|
||||||
BasePortal.sync_matrix_state = config["bridge.sync_matrix_state"]
|
BasePortal.sync_matrix_state = config["bridge.sync_matrix_state"]
|
||||||
BasePortal.public_portals = config["bridge.public_portals"]
|
BasePortal.public_portals = config["bridge.public_portals"]
|
||||||
|
BasePortal.private_chat_portal_meta = config["bridge.private_chat_portal_meta"]
|
||||||
BasePortal.filter_mode = config["bridge.filter.mode"]
|
BasePortal.filter_mode = config["bridge.filter.mode"]
|
||||||
BasePortal.filter_list = config["bridge.filter.list"]
|
BasePortal.filter_list = config["bridge.filter.list"]
|
||||||
BasePortal.hs_domain = config["homeserver.domain"]
|
BasePortal.hs_domain = config["homeserver.domain"]
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
#
|
#
|
||||||
# You should have received a copy of the GNU Affero General Public License
|
# 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/>.
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
from typing import List, Optional, Tuple, Union, Callable, TYPE_CHECKING
|
from typing import List, Optional, Tuple, Union, Callable, Awaitable, TYPE_CHECKING
|
||||||
from abc import ABC
|
from abc import ABC
|
||||||
import asyncio
|
import asyncio
|
||||||
|
|
||||||
@@ -26,7 +26,7 @@ from telethon.tl.types import (
|
|||||||
Channel, ChatBannedRights, ChannelParticipantsRecent, ChannelParticipantsSearch, ChatPhoto,
|
Channel, ChatBannedRights, ChannelParticipantsRecent, ChannelParticipantsSearch, ChatPhoto,
|
||||||
PhotoEmpty, InputChannel, InputUser, ChatPhotoEmpty, PeerUser, Photo, TypeChat, TypeInputPeer,
|
PhotoEmpty, InputChannel, InputUser, ChatPhotoEmpty, PeerUser, Photo, TypeChat, TypeInputPeer,
|
||||||
TypeUser, User, InputPeerPhotoFileLocation, ChatParticipantAdmin, ChannelParticipantAdmin,
|
TypeUser, User, InputPeerPhotoFileLocation, ChatParticipantAdmin, ChannelParticipantAdmin,
|
||||||
ChatParticipantCreator, ChannelParticipantCreator)
|
ChatParticipantCreator, ChannelParticipantCreator, UserProfilePhoto, UserProfilePhotoEmpty)
|
||||||
|
|
||||||
from mautrix.errors import MForbidden
|
from mautrix.errors import MForbidden
|
||||||
from mautrix.types import (RoomID, UserID, RoomCreatePreset, EventType, Membership, Member,
|
from mautrix.types import (RoomID, UserID, RoomCreatePreset, EventType, Membership, Member,
|
||||||
@@ -218,10 +218,17 @@ class PortalMetadata(BasePortal, ABC):
|
|||||||
puppet = p.Puppet.get(self.tgid)
|
puppet = p.Puppet.get(self.tgid)
|
||||||
await puppet.update_info(user, entity)
|
await puppet.update_info(user, entity)
|
||||||
await puppet.intent_for(self).join_room(self.mxid)
|
await puppet.intent_for(self).join_room(self.mxid)
|
||||||
|
if self.encrypted or self.private_chat_portal_meta:
|
||||||
|
# The bridge bot needs to join for e2ee, but that messes up the default name
|
||||||
|
# generation. If/when canonical DMs happen, this might not be necessary anymore.
|
||||||
|
changed = await self._update_title(puppet.displayname)
|
||||||
|
changed = await self._update_avatar(user, entity.photo) or changed
|
||||||
|
if changed:
|
||||||
|
self.save()
|
||||||
if self.sync_matrix_state:
|
if self.sync_matrix_state:
|
||||||
await self.sync_matrix_members()
|
await self.sync_matrix_members()
|
||||||
|
|
||||||
async def create_matrix_room(self, user: 'AbstractUser', entity: TypeChat = None,
|
async def create_matrix_room(self, user: 'AbstractUser', entity: Union[TypeChat, User] = None,
|
||||||
invites: InviteList = None, update_if_exists: bool = True,
|
invites: InviteList = None, update_if_exists: bool = True,
|
||||||
synchronous: bool = False) -> Optional[str]:
|
synchronous: bool = False) -> Optional[str]:
|
||||||
if self.mxid:
|
if self.mxid:
|
||||||
@@ -245,8 +252,8 @@ class PortalMetadata(BasePortal, ABC):
|
|||||||
except Exception:
|
except Exception:
|
||||||
self.log.exception("Fatal error creating Matrix room")
|
self.log.exception("Fatal error creating Matrix room")
|
||||||
|
|
||||||
async def _create_matrix_room(self, user: 'AbstractUser', entity: TypeChat, invites: InviteList
|
async def _create_matrix_room(self, user: 'AbstractUser', entity: Union[TypeChat, User],
|
||||||
) -> Optional[RoomID]:
|
invites: InviteList) -> Optional[RoomID]:
|
||||||
direct = self.peer_type == "user"
|
direct = self.peer_type == "user"
|
||||||
|
|
||||||
if invites is None:
|
if invites is None:
|
||||||
@@ -274,6 +281,8 @@ class PortalMetadata(BasePortal, ABC):
|
|||||||
self.about = "Your Telegram cloud storage chat"
|
self.about = "Your Telegram cloud storage chat"
|
||||||
|
|
||||||
puppet = p.Puppet.get(self.tgid) if direct else None
|
puppet = p.Puppet.get(self.tgid) if direct else None
|
||||||
|
if puppet:
|
||||||
|
await puppet.update_info(user, entity)
|
||||||
self._main_intent = puppet.intent_for(self) if direct else self.az.intent
|
self._main_intent = puppet.intent_for(self) if direct else self.az.intent
|
||||||
|
|
||||||
if self.peer_type == "channel":
|
if self.peer_type == "channel":
|
||||||
@@ -340,9 +349,8 @@ class PortalMetadata(BasePortal, ABC):
|
|||||||
})
|
})
|
||||||
if direct:
|
if direct:
|
||||||
invites.append(self.az.bot_mxid)
|
invites.append(self.az.bot_mxid)
|
||||||
# The bridge bot needs to join for e2ee, but that messes up the default name
|
if direct and (self.encrypted or self.private_chat_portal_meta):
|
||||||
# generation. If/when canonical DMs happen, this might not be necessary anymore.
|
self.title = puppet.displayname
|
||||||
self.title = puppet.displayname
|
|
||||||
if config["appservice.community_id"]:
|
if config["appservice.community_id"]:
|
||||||
initial_state.append({
|
initial_state.append({
|
||||||
"type": "m.room.related_groups",
|
"type": "m.room.related_groups",
|
||||||
@@ -587,12 +595,12 @@ class PortalMetadata(BasePortal, ABC):
|
|||||||
self.log.warning("Called update_info() for direct chat portal")
|
self.log.warning("Called update_info() for direct chat portal")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
changed = False
|
||||||
self.log.debug("Updating info")
|
self.log.debug("Updating info")
|
||||||
try:
|
try:
|
||||||
if not entity:
|
if not entity:
|
||||||
entity = await self.get_entity(user)
|
entity = await self.get_entity(user)
|
||||||
self.log.debug(f"Fetched data: {entity}")
|
self.log.debug(f"Fetched data: {entity}")
|
||||||
changed = False
|
|
||||||
|
|
||||||
if self.peer_type == "channel":
|
if self.peer_type == "channel":
|
||||||
changed = self.megagroup != entity.megagroup or changed
|
changed = self.megagroup != entity.megagroup or changed
|
||||||
@@ -631,7 +639,7 @@ class PortalMetadata(BasePortal, ABC):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
async def _try_use_intent(self, sender: Optional['p.Puppet'],
|
async def _try_use_intent(self, sender: Optional['p.Puppet'],
|
||||||
action: Callable[[IntentAPI], None]) -> None:
|
action: Callable[[IntentAPI], Awaitable[None]]) -> None:
|
||||||
if sender:
|
if sender:
|
||||||
try:
|
try:
|
||||||
await action(sender.intent_for(self))
|
await action(sender.intent_for(self))
|
||||||
@@ -666,18 +674,19 @@ class PortalMetadata(BasePortal, ABC):
|
|||||||
|
|
||||||
async def _update_avatar(self, user: 'AbstractUser', photo: TypeChatPhoto,
|
async def _update_avatar(self, user: 'AbstractUser', photo: TypeChatPhoto,
|
||||||
sender: Optional['p.Puppet'] = None, save: bool = False) -> bool:
|
sender: Optional['p.Puppet'] = None, save: bool = False) -> bool:
|
||||||
if isinstance(photo, ChatPhoto):
|
if isinstance(photo, (ChatPhoto, UserProfilePhoto)):
|
||||||
loc = InputPeerPhotoFileLocation(
|
loc = InputPeerPhotoFileLocation(
|
||||||
peer=await self.get_input_entity(user),
|
peer=await self.get_input_entity(user),
|
||||||
local_id=photo.photo_big.local_id,
|
local_id=photo.photo_big.local_id,
|
||||||
volume_id=photo.photo_big.volume_id,
|
volume_id=photo.photo_big.volume_id,
|
||||||
big=True
|
big=True
|
||||||
)
|
)
|
||||||
photo_id = f"{loc.volume_id}-{loc.local_id}"
|
photo_id = (f"{loc.volume_id}-{loc.local_id}" if isinstance(photo, ChatPhoto)
|
||||||
|
else photo.photo_id)
|
||||||
elif isinstance(photo, Photo):
|
elif isinstance(photo, Photo):
|
||||||
loc, largest = self._get_largest_photo_size(photo)
|
loc, largest = self._get_largest_photo_size(photo)
|
||||||
photo_id = f"{largest.location.volume_id}-{largest.location.local_id}"
|
photo_id = f"{largest.location.volume_id}-{largest.location.local_id}"
|
||||||
elif isinstance(photo, (ChatPhotoEmpty, PhotoEmpty)):
|
elif isinstance(photo, (UserProfilePhotoEmpty, ChatPhotoEmpty, PhotoEmpty)):
|
||||||
photo_id = ""
|
photo_id = ""
|
||||||
loc = None
|
loc = None
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -242,8 +242,7 @@ class Puppet(CustomPuppetMixin):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
changed = await self.update_displayname(source, info) or changed
|
changed = await self.update_displayname(source, info) or changed
|
||||||
if isinstance(info.photo, UserProfilePhoto):
|
changed = await self.update_avatar(source, info.photo) or changed
|
||||||
changed = await self.update_avatar(source, info.photo) or changed
|
|
||||||
except Exception:
|
except Exception:
|
||||||
self.log.exception(f"Failed to update info from source {source.tgid}")
|
self.log.exception(f"Failed to update info from source {source.tgid}")
|
||||||
|
|
||||||
@@ -294,10 +293,13 @@ class Puppet(CustomPuppetMixin):
|
|||||||
if self.disable_updates:
|
if self.disable_updates:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if isinstance(photo, UserProfilePhotoEmpty):
|
if photo is None or isinstance(photo, UserProfilePhotoEmpty):
|
||||||
photo_id = ""
|
photo_id = ""
|
||||||
else:
|
elif isinstance(photo, UserProfilePhoto):
|
||||||
photo_id = str(photo.photo_id)
|
photo_id = str(photo.photo_id)
|
||||||
|
else:
|
||||||
|
self.log.warning(f"Unknown user profile photo type: {type(photo)}")
|
||||||
|
return False
|
||||||
if self.photo_id != photo_id:
|
if self.photo_id != photo_id:
|
||||||
if not photo_id:
|
if not photo_id:
|
||||||
self.photo_id = ""
|
self.photo_id = ""
|
||||||
|
|||||||
Reference in New Issue
Block a user