diff --git a/ROADMAP.md b/ROADMAP.md index 5505e66..c4358c9 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -25,8 +25,8 @@ * [ ] Ban[4] * [ ] Unban[4] * [ ] Room metadata changes - * [x] Name[1] - * [x] Topic[1] + * [x] Name + * [x] Topic * [ ] Avatar * [ ] Per-room user nick * KakaoTalk → Matrix diff --git a/matrix_appservice_kakaotalk/portal.py b/matrix_appservice_kakaotalk/portal.py index e7be31f..e569152 100644 --- a/matrix_appservice_kakaotalk/portal.py +++ b/matrix_appservice_kakaotalk/portal.py @@ -1219,9 +1219,6 @@ class Portal(DBPortal, BasePortal): # 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 - if not self.is_open: - self.log.info(f"Not bridging f{handler.action_name} change of portal for non-open channel") - return try: effective_sender, _ = await self.get_relay_sender(sender, f"{handler.action_name} {evt.event_id}") if effective_sender: @@ -1338,6 +1335,9 @@ class Portal(DBPortal, BasePortal): ) -> None: if content.topic == prev_content.topic: return + if not self.is_open: + self.log.info(f"Not bridging topic change of portal for non-open channel") + return if not (sender and sender.is_connected): raise Exception( "Only users connected to KakaoTalk can set the description of a KakaoTalk channel" diff --git a/node/src/client.js b/node/src/client.js index b2ff192..622c51a 100644 --- a/node/src/client.js +++ b/node/src/client.js @@ -366,20 +366,39 @@ class UserClient { * @param {ChannelProps} channelProps */ async getChannel(channelProps) { - let channel = this.#talkClient.channelList.get(channelProps.id) - if (channel) { - return channel - } else { - const channelList = getChannelListForType( - this.#talkClient.channelList, - channelProps.type - ) - const res = await channelList.addChannel({ channelId: channelProps.id }) - if (!res.success) { - throw new Error(`Unable to add ${channelProps.type} channel ${channelProps.id}`) - } - return res.result + const talkChannel = this.#talkClient.channelList.get(channelProps.id) + if (talkChannel) { + return talkChannel } + + const channelList = getChannelListForType( + this.#talkClient.channelList, + channelProps.type + ) + const res = await channelList.addChannel({ channelId: channelProps.id }) + if (!res.success) { + this.error(`Unable to add ${channelProps.type} channel ${channelProps.id}`) + throw res + } + return res.result + } + + /** + * @param {Long} channelId + */ + async getNormalChannel(channelId) { + const channelList = this.#talkClient.channelList.normal + const talkChannel = channelList.get(channelId) + if (talkChannel) { + return talkChannel + } + + const res = await channelList.addChannel({ channelId: channelId }) + if (!res.success) { + this.error(`Unable to add normal channel ${channelProps.id}`) + throw res + } + return res.result } /** @@ -600,14 +619,32 @@ export default class PeerClient { const userClient = this.#getUser(mxid) const talkChannel = await userClient.getChannel(channelProps) if (permNeeded) { - const permActual = talkChannel.getUserInfo({ userId: userClient.userId }).perm - if (permNeeded.indexOf(permActual) == -1) { - throw new PermError(permNeeded, permActual, action) - } + await this.#requireChannelPerm(talkChannel, permNeeded, action) } return talkChannel } + /** + * @param {TalkOpenChannel} talkChannel + * @param {OpenChannelUserPerm[]} permNeeded Throw if the user's permission level matches none of the values in this list. + * @param {string} action The action requiring permission + * @throws {PermError} if the user does not have the specified permission level. + */ + async #requireChannelPerm(talkChannel, permNeeded, action) { + const permActual = talkChannel.getUserInfo({ userId: talkChannel.clientUser.userId }).perm + if (permNeeded.indexOf(permActual) == -1) { + throw new PermError(permNeeded, permActual, action) + } + } + + /** + * @param {string} mxid + * @param {Long} channelId + */ + async #getUserNormalChannel(mxid, channelId) { + return await this.#getUser(mxid).getNormalChannel(channelId) + } + /** * @param {object} req * @param {string} req.mxid @@ -1104,13 +1141,12 @@ export default class PeerClient { * @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) + if (!isChannelTypeOpen(req.channel_props.type)) { + const talkChannel = await this.#getUserNormalChannel(req.mxid, req.channel_props.id) + return await talkChannel.setTitleMeta(req.name) + } else { + return await this.#setOpenChannelProperty(req.mxid, req.channel_props, "linkName", req.name) + } } /** @@ -1120,13 +1156,33 @@ export default class PeerClient { * @param {string} req.description */ setChannelDescription = async (req) => { - const talkChannel = await this.#getUserChannel( - req.mxid, - req.channel_props, - [OpenChannelUserPerm.OWNER], - "change channel description" + return await this.#setOpenChannelProperty(req.mxid, req.channel_props, "description", req.description) + } + + /** + * @param {string} mxid + * @param {ChannelProps} channelProps + * @param {string} propertyName + * @param {any} propertyValue + */ + async #setOpenChannelProperty(mxid, channelProps, propertyName, propertyValue) { + if (isChannelTypeOpen(channelProps)) { + throw ProtocolError(`Cannot set ${propertyName} of non-open channel ${channelProps.id} (type = ${channelProps.type})`) + } + + const userClient = this.#getUser(mxid) + /** @type {TalkOpenChannel} */ + const talkChannel = await userClient.getChannel(channelProps) + this.#requireChannelPerm(talkChannel, [OpenChannelUserPerm.OWNER], `change channel ${propertyName}`) + + const linkRes = await talkChannel.getLatestOpenLink() + if (!linkRes.success) throw linkRes + + const link = linkRes.result + link[propertyName] = propertyValue + return await userClient.talkClient.channelList.open.updateOpenLink( + { linkId: link.linkId }, link ) - return await talkChannel.setNoticeMeta(req.description) } /*