Try outbound room title & description, and work on outbound room avatar
But they fail with -203 (invalid body)
This commit is contained in:
parent
abf3114203
commit
770b0e447b
|
@ -398,6 +398,39 @@ class Client:
|
|||
perm=perm,
|
||||
)
|
||||
|
||||
async def set_channel_name(
|
||||
self,
|
||||
channel_props: ChannelProps,
|
||||
name: str,
|
||||
) -> None:
|
||||
return await self._api_user_request_void(
|
||||
"set_channel_name",
|
||||
channel_props=channel_props.serialize(),
|
||||
name=name,
|
||||
)
|
||||
|
||||
async def set_channel_description(
|
||||
self,
|
||||
channel_props: ChannelProps,
|
||||
description: str,
|
||||
) -> None:
|
||||
return await self._api_user_request_void(
|
||||
"set_channel_description",
|
||||
channel_props=channel_props.serialize(),
|
||||
description=description,
|
||||
)
|
||||
|
||||
async def set_channel_photo(
|
||||
self,
|
||||
channel_props: ChannelProps,
|
||||
photo_url: str,
|
||||
) -> None:
|
||||
return await self._api_user_request_void(
|
||||
"set_channel_photo",
|
||||
channel_props=channel_props.serialize(),
|
||||
photo_url=photo_url,
|
||||
)
|
||||
|
||||
|
||||
# TODO Combine these into one
|
||||
|
||||
|
|
|
@ -26,7 +26,6 @@ from mautrix.types import (
|
|||
RedactionEvent,
|
||||
RoomID,
|
||||
SingleReceiptEventContent,
|
||||
PowerLevelStateEventContent,
|
||||
StateEvent,
|
||||
UserID,
|
||||
)
|
||||
|
@ -162,25 +161,6 @@ class MatrixHandler(BaseMatrixHandler):
|
|||
if message:
|
||||
await user.client.mark_read(portal.channel_props, message.ktid)
|
||||
|
||||
@classmethod
|
||||
async def handle_power_level(
|
||||
cls,
|
||||
room_id: RoomID,
|
||||
user_id: UserID,
|
||||
prev_content: PowerLevelStateEventContent,
|
||||
content: PowerLevelStateEventContent,
|
||||
event_id: EventID,
|
||||
) -> None:
|
||||
user = await u.User.get_by_mxid(user_id)
|
||||
if not user:
|
||||
return
|
||||
|
||||
portal = await po.Portal.get_by_mxid(room_id)
|
||||
if not portal:
|
||||
return
|
||||
|
||||
await portal.handle_matrix_power_level(user, prev_content, content, event_id)
|
||||
|
||||
async def handle_ephemeral_event(
|
||||
self, evt: ReceiptEvent | Event
|
||||
) -> None:
|
||||
|
@ -198,5 +178,13 @@ class MatrixHandler(BaseMatrixHandler):
|
|||
"""
|
||||
|
||||
async def handle_state_event(self, evt: StateEvent) -> None:
|
||||
if evt.type == EventType.ROOM_POWER_LEVELS:
|
||||
await self.handle_power_level(evt.room_id, evt.sender, evt.prev_content, evt.content, evt.event_id)
|
||||
if po.Portal.supports_state_event(evt.type):
|
||||
user = await u.User.get_by_mxid(evt.sender)
|
||||
if not user:
|
||||
return
|
||||
|
||||
portal = await po.Portal.get_by_mxid(evt.room_id)
|
||||
if not portal:
|
||||
return
|
||||
|
||||
await portal.handle_matrix_state_event(user, evt)
|
||||
|
|
|
@ -20,6 +20,8 @@ from typing import (
|
|||
Any,
|
||||
AsyncGenerator,
|
||||
Awaitable,
|
||||
Callable,
|
||||
NamedTuple,
|
||||
Pattern,
|
||||
cast,
|
||||
)
|
||||
|
@ -49,8 +51,13 @@ from mautrix.types import (
|
|||
MessageEventContent,
|
||||
MessageType,
|
||||
PowerLevelStateEventContent,
|
||||
RoomAvatarStateEventContent,
|
||||
RoomNameStateEventContent,
|
||||
RoomTopicStateEventContent,
|
||||
RelationType,
|
||||
RoomID,
|
||||
StateEvent,
|
||||
StateEventContent,
|
||||
TextMessageEventContent,
|
||||
UserID,
|
||||
VideoInfo,
|
||||
|
@ -118,6 +125,14 @@ class FakeLock:
|
|||
pass
|
||||
|
||||
|
||||
class StateEventHandler(NamedTuple):
|
||||
# TODO Can this use Generic to force the two StateEventContent parameters to be of the same type?
|
||||
# Or, just have a single StateEvent parameter
|
||||
apply: Callable[[u.User, StateEventContent, StateEventContent], Awaitable[None]]
|
||||
revert: Callable[[StateEventContent], Awaitable[None]]
|
||||
action_name: str
|
||||
|
||||
|
||||
StateBridge = EventType.find("m.bridge", EventType.Class.STATE)
|
||||
StateHalfShotBridge = EventType.find("uk.half-shot.bridge", EventType.Class.STATE)
|
||||
|
||||
|
@ -199,8 +214,7 @@ class Portal(DBPortal, BasePortal):
|
|||
NotificationDisabler.puppet_cls = p.Puppet
|
||||
NotificationDisabler.config_enabled = cls.config["bridge.backfill.disable_notifications"]
|
||||
|
||||
# TODO More
|
||||
cls._chat_type_handler_map = {
|
||||
cls._CHAT_TYPE_HANDLER_MAP = {
|
||||
KnownChatType.FEED: cls._handle_kakaotalk_feed,
|
||||
KnownChatType.TEXT: cls._handle_kakaotalk_text,
|
||||
KnownChatType.REPLY: cls._handle_kakaotalk_reply,
|
||||
|
@ -212,6 +226,33 @@ class Portal(DBPortal, BasePortal):
|
|||
16385: cls._handle_kakaotalk_deleted,
|
||||
}
|
||||
|
||||
cls._STATE_EVENT_HANDLER_MAP: dict[EventType, StateEventHandler] = {
|
||||
EventType.ROOM_POWER_LEVELS: StateEventHandler(
|
||||
cls._handle_matrix_power_levels,
|
||||
cls._revert_matrix_power_levels,
|
||||
"power level"
|
||||
),
|
||||
EventType.ROOM_NAME: StateEventHandler(
|
||||
cls._handle_matrix_room_name,
|
||||
cls._revert_matrix_room_name,
|
||||
"room name"
|
||||
),
|
||||
EventType.ROOM_TOPIC: StateEventHandler(
|
||||
cls._handle_matrix_room_topic,
|
||||
cls._revert_matrix_room_topic,
|
||||
"room topic"
|
||||
),
|
||||
EventType.ROOM_AVATAR: StateEventHandler(
|
||||
cls._handle_matrix_room_avatar,
|
||||
cls._revert_matrix_room_avatar,
|
||||
"room avatar"
|
||||
),
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def supports_state_event(cls, evt_type: EventType) -> bool:
|
||||
return evt_type in cls._STATE_EVENT_HANDLER_MAP
|
||||
|
||||
# region DB conversion
|
||||
|
||||
async def delete(self) -> None:
|
||||
|
@ -1044,51 +1085,50 @@ class Portal(DBPortal, BasePortal):
|
|||
pass
|
||||
"""
|
||||
|
||||
async def handle_matrix_power_level(
|
||||
self,
|
||||
sender: u.User,
|
||||
prev_content: PowerLevelStateEventContent,
|
||||
content: PowerLevelStateEventContent,
|
||||
event_id: EventID,
|
||||
) -> None:
|
||||
async def handle_matrix_state_event(self, sender: u.User, evt: StateEvent) -> None:
|
||||
try:
|
||||
await self._handle_matrix_power_level(sender, prev_content, content, event_id)
|
||||
handler: StateEventHandler = self._STATE_EVENT_HANDLER_MAP[evt.type]
|
||||
except KeyError:
|
||||
# Misses should be guarded by supports_state_event, but handle this just in case
|
||||
self.log.error(f"Skipping Matrix state event {evt.event_id} of unsupported type {evt.type}")
|
||||
return
|
||||
try:
|
||||
effective_sender, _ = await self.get_relay_sender(sender, f"{handler.action_name} {evt.event_id}")
|
||||
await handler.apply(self, effective_sender, evt.prev_content, evt.content)
|
||||
except Exception as e:
|
||||
self.log.error(
|
||||
f"Failed to handle Matrix power level {event_id}: {e}",
|
||||
f"Failed to handle Matrix {handler.action_name} {evt.event_id}: {e}",
|
||||
exc_info=not isinstance(e, NotImplementedError),
|
||||
)
|
||||
sender.send_remote_checkpoint(
|
||||
self._status_from_exception(e),
|
||||
event_id,
|
||||
evt.event_id,
|
||||
self.mxid,
|
||||
EventType.ROOM_POWER_LEVELS,
|
||||
evt.type,
|
||||
error=e,
|
||||
)
|
||||
if not isinstance(e, NotImplementedError):
|
||||
await self._send_bridge_error(
|
||||
f"{e}. Reverting the power level change...",
|
||||
thing="power level change"
|
||||
)
|
||||
change = f"{handler.action_name} change"
|
||||
await self._send_bridge_error(
|
||||
f"{e}. Reverting the {change}...",
|
||||
thing=change
|
||||
)
|
||||
# NOTE Redacting instead doesn't work
|
||||
await self.main_intent.set_power_levels(self.mxid, prev_content)
|
||||
await handler.revert(self, evt.prev_content)
|
||||
else:
|
||||
await self._send_delivery_receipt(event_id)
|
||||
await self._send_delivery_receipt(evt.event_id)
|
||||
sender.send_remote_checkpoint(
|
||||
MessageSendCheckpointStatus.SUCCESS,
|
||||
event_id,
|
||||
evt.event_id,
|
||||
self.mxid,
|
||||
EventType.ROOM_POWER_LEVELS,
|
||||
evt.type,
|
||||
)
|
||||
|
||||
async def _handle_matrix_power_level(
|
||||
async def _handle_matrix_power_levels(
|
||||
self,
|
||||
sender: u.User,
|
||||
prev_content: PowerLevelStateEventContent,
|
||||
content: PowerLevelStateEventContent,
|
||||
event_id: EventID,
|
||||
) -> None:
|
||||
sender, _ = await self.get_relay_sender(sender, f"power level {event_id}")
|
||||
for target_mxid, power_level in content.users.items():
|
||||
if power_level == prev_content.get_user_level(target_mxid):
|
||||
continue
|
||||
|
@ -1102,6 +1142,74 @@ class Portal(DBPortal, BasePortal):
|
|||
"Only users connected to KakaoTalk can set power levels of KakaoTalk users"
|
||||
)
|
||||
|
||||
async def _revert_matrix_power_levels(self, prev_content: PowerLevelStateEventContent) -> None:
|
||||
await self.main_intent.set_power_levels(self.mxid, prev_content)
|
||||
|
||||
async def _handle_matrix_room_name(
|
||||
self,
|
||||
sender: u.User,
|
||||
prev_content: RoomNameStateEventContent,
|
||||
content: RoomNameStateEventContent,
|
||||
) -> None:
|
||||
if content.name == prev_content.name:
|
||||
return
|
||||
if not (sender and sender.is_connected):
|
||||
raise Exception(
|
||||
"Only users connected to KakaoTalk can set the name of a KakaoTalk channel"
|
||||
)
|
||||
await sender.client.set_channel_name(self.channel_props, content.name)
|
||||
self.name = content.name
|
||||
self.name_set = True
|
||||
await self.save()
|
||||
|
||||
async def _revert_matrix_room_name(self, prev_content: RoomNameStateEventContent) -> None:
|
||||
await self.main_intent.set_room_name(self.mxid, prev_content.name)
|
||||
|
||||
async def _handle_matrix_room_topic(
|
||||
self,
|
||||
sender: u.User,
|
||||
prev_content: RoomTopicStateEventContent,
|
||||
content: RoomTopicStateEventContent,
|
||||
) -> None:
|
||||
if content.topic == prev_content.topic:
|
||||
return
|
||||
if not (sender and sender.is_connected):
|
||||
raise Exception(
|
||||
"Only users connected to KakaoTalk can set the description of a KakaoTalk channel"
|
||||
)
|
||||
await sender.client.set_channel_description(self.channel_props, content.topic)
|
||||
self.description = content.topic
|
||||
self.topic_set = True
|
||||
await self.save()
|
||||
|
||||
async def _revert_matrix_room_topic(self, prev_content: RoomTopicStateEventContent) -> None:
|
||||
await self.main_intent.set_room_topic(self.mxid, prev_content.topic)
|
||||
|
||||
async def _handle_matrix_room_avatar(
|
||||
self,
|
||||
sender: u.User,
|
||||
prev_content: RoomAvatarStateEventContent,
|
||||
content: RoomAvatarStateEventContent,
|
||||
) -> None:
|
||||
if content.url == prev_content.url:
|
||||
return
|
||||
if not (sender and sender.is_connected):
|
||||
raise Exception(
|
||||
"Only users connected to KakaoTalk can set the photo of a KakaoTalk channel"
|
||||
)
|
||||
raise NotImplementedError("Changing the room avatar is not supported by the KakaoTalk bridge.")
|
||||
""" TODO
|
||||
photo_url = str(self.main_intent.api.get_download_url(content.url))
|
||||
await sender.client.set_channel_photo(self.channel_props, photo_url)
|
||||
self.photo_id = photo_url
|
||||
self.avatar_url = content.url
|
||||
self.avatar_set = True
|
||||
await self.save()
|
||||
"""
|
||||
|
||||
async def _revert_matrix_room_avatar(self, prev_content: RoomAvatarStateEventContent) -> None:
|
||||
await self.main_intent.set_room_avatar(self.mxid, prev_content.url)
|
||||
|
||||
async def handle_matrix_leave(self, user: u.User) -> None:
|
||||
if self.is_direct:
|
||||
self.log.info(f"{user.mxid} left private chat portal with {self.ktid}")
|
||||
|
@ -1204,7 +1312,7 @@ class Portal(DBPortal, BasePortal):
|
|||
await intent.ensure_joined(self.mxid)
|
||||
self._backfill_leave.add(intent)
|
||||
|
||||
handler = self._chat_type_handler_map.get(chat.type, Portal._handle_kakaotalk_unsupported)
|
||||
handler = self._CHAT_TYPE_HANDLER_MAP.get(chat.type, Portal._handle_kakaotalk_unsupported)
|
||||
event_ids = [
|
||||
event_id for event_id in
|
||||
await handler(
|
||||
|
|
|
@ -519,7 +519,7 @@ export default class PeerClient {
|
|||
const talkChannel = await userClient.getChannel(channelProps)
|
||||
if (permNeeded) {
|
||||
const permActual = talkChannel.getUserInfo({ userId: userClient.userId }).perm
|
||||
if (!(permActual in permNeeded)) {
|
||||
if (permNeeded.indexOf(permActual) == -1) {
|
||||
throw new PermError(permNeeded, permActual, action)
|
||||
}
|
||||
}
|
||||
|
@ -807,6 +807,57 @@ export default class PeerClient {
|
|||
return await talkChannel.setUserPerm({ userId: req.user_id }, req.perm)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Object} req
|
||||
* @param {string} req.mxid
|
||||
* @param {ChannelProps} req.channel_props
|
||||
* @param {string} req.name
|
||||
*/
|
||||
setChannelName = async (req) => {
|
||||
const talkChannel = await this.#getUserChannel(
|
||||
req.mxid,
|
||||
req.channel_props,
|
||||
[OpenChannelUserPerm.OWNER],
|
||||
"change channel name"
|
||||
)
|
||||
return await talkChannel.setTitleMeta(req.name)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Object} req
|
||||
* @param {string} req.mxid
|
||||
* @param {ChannelProps} req.channel_props
|
||||
* @param {string} req.description
|
||||
*/
|
||||
setChannelDescription = async (req) => {
|
||||
const talkChannel = await this.#getUserChannel(
|
||||
req.mxid,
|
||||
req.channel_props,
|
||||
[OpenChannelUserPerm.OWNER],
|
||||
"change channel description"
|
||||
)
|
||||
return await talkChannel.setNoticeMeta(req.description)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Object} req
|
||||
* @param {string} req.mxid
|
||||
* @param {ChannelProps} req.channel_props
|
||||
* @param {string} req.photo_url
|
||||
*/
|
||||
setChannelPhoto = async (req) => {
|
||||
const talkChannel = await this.#getUserChannel(
|
||||
req.mxid,
|
||||
req.channel_props,
|
||||
[OpenChannelUserPerm.OWNER],
|
||||
"change channel photo"
|
||||
)
|
||||
return await talkChannel.setProfileMeta({
|
||||
imageUrl: req.photo_url,
|
||||
fullImageUrl: req.photo_url,
|
||||
})
|
||||
}
|
||||
|
||||
handleUnknownCommand = () => {
|
||||
throw new Error("Unknown command")
|
||||
}
|
||||
|
@ -886,6 +937,9 @@ export default class PeerClient {
|
|||
delete_chat: this.deleteChat,
|
||||
mark_read: this.markRead,
|
||||
send_perm: this.sendPerm,
|
||||
set_channel_name: this.setChannelName,
|
||||
set_channel_description: this.setChannelDescription,
|
||||
set_channel_photo: this.setChannelPhoto,
|
||||
}[req.command] || this.handleUnknownCommand
|
||||
}
|
||||
const resp = { id: req.id }
|
||||
|
|
Loading…
Reference in New Issue