Outbound joins, and manage OpenLink URLs
This commit is contained in:
parent
0e8a5e5888
commit
a07f719495
|
@ -20,7 +20,7 @@
|
||||||
* [x] Power level<sup>[1]</sup>
|
* [x] Power level<sup>[1]</sup>
|
||||||
* [ ] Membership actions
|
* [ ] Membership actions
|
||||||
* [ ] Invite
|
* [ ] Invite
|
||||||
* [ ] Join
|
* [x] Join
|
||||||
* [x] Leave<sup>[3]</sup>
|
* [x] Leave<sup>[3]</sup>
|
||||||
* [ ] Ban<sup>[4]</sup>
|
* [ ] Ban<sup>[4]</sup>
|
||||||
* [ ] Unban<sup>[4]</sup>
|
* [ ] Unban<sup>[4]</sup>
|
||||||
|
@ -81,6 +81,9 @@
|
||||||
* [ ] Public search
|
* [ ] Public search
|
||||||
* [ ] Max number of participants
|
* [ ] Max number of participants
|
||||||
* [ ] Chatroom code
|
* [ ] Chatroom code
|
||||||
|
* [x] Display Open Chat public URL
|
||||||
|
* [x] Join Open Chat via public URL
|
||||||
|
* [ ] Join passcode-protected Open Chat
|
||||||
* [x] Option to use own Matrix account for messages sent from other KakaoTalk clients
|
* [x] Option to use own Matrix account for messages sent from other KakaoTalk clients
|
||||||
* [ ] KakaoTalk friends list management
|
* [ ] KakaoTalk friends list management
|
||||||
* [x] List friends
|
* [x] List friends
|
||||||
|
|
|
@ -301,11 +301,46 @@ async def _on_friend_edited(evt: CommandEvent, friend_struct: FriendStruct | Non
|
||||||
await puppet.update_info_from_friend(evt.sender, friend_struct)
|
await puppet.update_info_from_friend(evt.sender, friend_struct)
|
||||||
|
|
||||||
|
|
||||||
|
@command_handler(
|
||||||
|
management_only=False,
|
||||||
|
help_section=SECTION_CHANNELS,
|
||||||
|
help_text="If the current KakaoTalk channel is an Open Chat, display its URL",
|
||||||
|
)
|
||||||
|
async def get_url(evt: CommandEvent) -> None:
|
||||||
|
if not evt.is_portal:
|
||||||
|
await evt.reply("This command may only be used in a KakaoTalk channel portal room")
|
||||||
|
return
|
||||||
|
await evt.reply(
|
||||||
|
evt.portal.full_link_url or "This channel has no URL."
|
||||||
|
if evt.portal.is_open else "This channel is not an Open Chat."
|
||||||
|
)
|
||||||
|
|
||||||
|
@command_handler(
|
||||||
|
needs_auth=True,
|
||||||
|
management_only=True,
|
||||||
|
help_section=SECTION_CHANNELS,
|
||||||
|
help_text="Join a KakaoTalk Open Chat",
|
||||||
|
help_args="<_URL_>",
|
||||||
|
)
|
||||||
|
async def join(evt: CommandEvent) -> None:
|
||||||
|
if len(evt.args) != 1:
|
||||||
|
await evt.reply(f"**Usage:** `$cmdprefix+sp {evt.command} <URL>`")
|
||||||
|
return
|
||||||
|
if not evt.sender.is_connected:
|
||||||
|
await evt.reply("You are not connected to KakaoTalk chats")
|
||||||
|
return
|
||||||
|
await evt.mark_read()
|
||||||
|
try:
|
||||||
|
await evt.sender.join_channel(evt.args[0])
|
||||||
|
except CommandException as e:
|
||||||
|
await evt.reply(f"Error from KakaoTalk: {e}")
|
||||||
|
|
||||||
|
|
||||||
@command_handler(
|
@command_handler(
|
||||||
needs_auth=True,
|
needs_auth=True,
|
||||||
management_only=False,
|
management_only=False,
|
||||||
help_section=SECTION_CHANNELS,
|
help_section=SECTION_CHANNELS,
|
||||||
help_text="Leave this KakaoTalk channel",
|
help_text="Leave the current KakaoTalk channel",
|
||||||
)
|
)
|
||||||
async def leave(evt: CommandEvent) -> None:
|
async def leave(evt: CommandEvent) -> None:
|
||||||
if not evt.sender.is_connected:
|
if not evt.sender.is_connected:
|
||||||
|
|
|
@ -45,6 +45,8 @@ class Portal:
|
||||||
name_set: bool
|
name_set: bool
|
||||||
topic_set: bool
|
topic_set: bool
|
||||||
avatar_set: bool
|
avatar_set: bool
|
||||||
|
link_id: Long | None = field(converter=to_optional_long)
|
||||||
|
link_url: str | None
|
||||||
fully_read_kt_chat: Long | None = field(converter=to_optional_long)
|
fully_read_kt_chat: Long | None = field(converter=to_optional_long)
|
||||||
relay_user_id: UserID | None
|
relay_user_id: UserID | None
|
||||||
|
|
||||||
|
@ -58,7 +60,7 @@ class Portal:
|
||||||
|
|
||||||
_columns = (
|
_columns = (
|
||||||
"ktid, kt_receiver, kt_type, mxid, name, description, photo_id, avatar_url, encrypted, "
|
"ktid, kt_receiver, kt_type, mxid, name, description, photo_id, avatar_url, encrypted, "
|
||||||
"name_set, avatar_set, fully_read_kt_chat, relay_user_id"
|
"name_set, avatar_set, link_id, link_url, fully_read_kt_chat, relay_user_id"
|
||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@ -99,6 +101,8 @@ class Portal:
|
||||||
self.encrypted,
|
self.encrypted,
|
||||||
self.name_set,
|
self.name_set,
|
||||||
self.avatar_set,
|
self.avatar_set,
|
||||||
|
self.link_id,
|
||||||
|
self.link_url,
|
||||||
self.fully_read_kt_chat,
|
self.fully_read_kt_chat,
|
||||||
self.relay_user_id,
|
self.relay_user_id,
|
||||||
)
|
)
|
||||||
|
|
|
@ -21,3 +21,4 @@ from . import v01_initial_revision
|
||||||
from . import v02_channel_meta
|
from . import v02_channel_meta
|
||||||
from . import v03_user_connection
|
from . import v03_user_connection
|
||||||
from . import v04_read_receipt_sync
|
from . import v04_read_receipt_sync
|
||||||
|
from . import v05_open_link
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
# matrix-appservice-kakaotalk - A Matrix-KakaoTalk puppeting bridge.
|
||||||
|
# Copyright (C) 2022 Tulir Asokan, Andrew Ferrazzutti
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU Affero General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU Affero General Public License for more details.
|
||||||
|
#
|
||||||
|
# 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 mautrix.util.async_db import Connection, Scheme
|
||||||
|
|
||||||
|
from . import upgrade_table
|
||||||
|
|
||||||
|
|
||||||
|
@upgrade_table.register(description="Track OpenChannel public link IDs and URLs")
|
||||||
|
async def upgrade_v5(conn: Connection) -> None:
|
||||||
|
await conn.execute("ALTER TABLE portal ADD COLUMN link_id BIGINT")
|
||||||
|
await conn.execute("ALTER TABLE portal ADD COLUMN link_url TEXT")
|
|
@ -19,7 +19,6 @@ Currently a wrapper around a Node backend, but
|
||||||
the abstraction used here should be compatible
|
the abstraction used here should be compatible
|
||||||
with any other potential backend.
|
with any other potential backend.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from typing import TYPE_CHECKING, cast, Awaitable, Type, Optional, Union
|
from typing import TYPE_CHECKING, cast, Awaitable, Type, Optional, Union
|
||||||
|
@ -518,6 +517,26 @@ class Client:
|
||||||
user_id=ktid.serialize(),
|
user_id=ktid.serialize(),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def join_channel_by_url(self, url: str) -> Awaitable[Long]:
|
||||||
|
return self._api_user_request_result(
|
||||||
|
Long,
|
||||||
|
"join_channel_by_url",
|
||||||
|
url=url,
|
||||||
|
)
|
||||||
|
|
||||||
|
async def join_channel(
|
||||||
|
self,
|
||||||
|
channel_id: Long,
|
||||||
|
link_id: Long,
|
||||||
|
) -> None:
|
||||||
|
joined_id = await self._api_user_request_result(
|
||||||
|
Long,
|
||||||
|
"join_channel",
|
||||||
|
channel_id=channel_id.serialize(),
|
||||||
|
link_id=link_id.serialize(),
|
||||||
|
)
|
||||||
|
assert channel_id == joined_id, f"Mismatch of joined channel ID: expected {channel_id}, got {joined_id}"
|
||||||
|
|
||||||
def leave_channel(
|
def leave_channel(
|
||||||
self,
|
self,
|
||||||
channel_props: ChannelProps,
|
channel_props: ChannelProps,
|
||||||
|
|
|
@ -81,6 +81,8 @@ class PortalChannelInfo(SerializableAttrs):
|
||||||
name: str
|
name: str
|
||||||
description: Optional[str] = None
|
description: Optional[str] = None
|
||||||
photoURL: Optional[str] = None
|
photoURL: Optional[str] = None
|
||||||
|
linkId: Optional[Long] = None
|
||||||
|
linkURL: Optional[str] = None
|
||||||
participantInfo: Optional[PortalChannelParticipantInfo] = None # May set to None to skip participant update
|
participantInfo: Optional[PortalChannelParticipantInfo] = None # May set to None to skip participant update
|
||||||
channel_info: Optional[ChannelInfoUnion] = None # Should be set manually by caller
|
channel_info: Optional[ChannelInfoUnion] = None # Should be set manually by caller
|
||||||
|
|
||||||
|
@ -89,6 +91,7 @@ class PortalChannelInfo(SerializableAttrs):
|
||||||
class ChannelProps(SerializableAttrs):
|
class ChannelProps(SerializableAttrs):
|
||||||
id: Long
|
id: Long
|
||||||
type: ChannelType
|
type: ChannelType
|
||||||
|
link_id: Optional[Long]
|
||||||
|
|
||||||
|
|
||||||
# TODO Add non-media types, like polls & maps
|
# TODO Add non-media types, like polls & maps
|
||||||
|
|
|
@ -28,7 +28,7 @@ class KnownChannelType(str, Enum):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def is_direct(cls, value: Union["KnownChannelType", str]) -> bool:
|
def is_direct(cls, value: Union["KnownChannelType", str]) -> bool:
|
||||||
return value in [cls.DirectChat, cls.MemoChat]
|
return value in [cls.DirectChat, cls.MemoChat, cls.OD]
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def is_open(cls, value: Union["KnownChannelType", str]) -> bool:
|
def is_open(cls, value: Union["KnownChannelType", str]) -> bool:
|
||||||
|
|
|
@ -105,8 +105,7 @@ class MatrixHandler(BaseMatrixHandler):
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
self.log.debug(f"{user.mxid} joined {room_id}")
|
await portal.handle_matrix_join(user)
|
||||||
# await portal.join_matrix(user, event_id)
|
|
||||||
|
|
||||||
async def handle_leave(self, room_id: RoomID, user_id: UserID, event_id: EventID) -> None:
|
async def handle_leave(self, room_id: RoomID, user_id: UserID, event_id: EventID) -> None:
|
||||||
portal = await po.Portal.get_by_mxid(room_id)
|
portal = await po.Portal.get_by_mxid(room_id)
|
||||||
|
|
|
@ -166,6 +166,8 @@ class Portal(DBPortal, BasePortal):
|
||||||
_CHAT_TYPE_HANDLER_MAP: dict[ChatType, Callable[..., ACallable[list[EventID]]]]
|
_CHAT_TYPE_HANDLER_MAP: dict[ChatType, Callable[..., ACallable[list[EventID]]]]
|
||||||
_STATE_EVENT_HANDLER_MAP: dict[EventType, StateEventHandler]
|
_STATE_EVENT_HANDLER_MAP: dict[EventType, StateEventHandler]
|
||||||
|
|
||||||
|
OPEN_LINK_URL_PREFIX = "https://open.kakao.com/o/"
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
ktid: Long,
|
ktid: Long,
|
||||||
|
@ -180,6 +182,8 @@ class Portal(DBPortal, BasePortal):
|
||||||
name_set: bool = False,
|
name_set: bool = False,
|
||||||
topic_set: bool = False,
|
topic_set: bool = False,
|
||||||
avatar_set: bool = False,
|
avatar_set: bool = False,
|
||||||
|
link_id: Long | None = None,
|
||||||
|
link_url: str | None = None,
|
||||||
fully_read_kt_chat: Long | None = None,
|
fully_read_kt_chat: Long | None = None,
|
||||||
relay_user_id: UserID | None = None,
|
relay_user_id: UserID | None = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
|
@ -196,6 +200,8 @@ class Portal(DBPortal, BasePortal):
|
||||||
name_set,
|
name_set,
|
||||||
topic_set,
|
topic_set,
|
||||||
avatar_set,
|
avatar_set,
|
||||||
|
link_id,
|
||||||
|
link_url,
|
||||||
fully_read_kt_chat,
|
fully_read_kt_chat,
|
||||||
relay_user_id,
|
relay_user_id,
|
||||||
)
|
)
|
||||||
|
@ -315,9 +321,14 @@ class Portal(DBPortal, BasePortal):
|
||||||
def channel_props(self) -> ChannelProps:
|
def channel_props(self) -> ChannelProps:
|
||||||
return ChannelProps(
|
return ChannelProps(
|
||||||
id=self.ktid,
|
id=self.ktid,
|
||||||
type=self.kt_type
|
type=self.kt_type,
|
||||||
|
link_id=self.link_id,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def full_link_url(self) -> str:
|
||||||
|
return self.OPEN_LINK_URL_PREFIX + self.link_url if self.link_url else ""
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def main_intent(self) -> IntentAPI:
|
def main_intent(self) -> IntentAPI:
|
||||||
if not self._main_intent:
|
if not self._main_intent:
|
||||||
|
@ -378,6 +389,7 @@ class Portal(DBPortal, BasePortal):
|
||||||
self._update_name(info.name),
|
self._update_name(info.name),
|
||||||
self._update_description(info.description),
|
self._update_description(info.description),
|
||||||
self._update_photo(source, info.photoURL),
|
self._update_photo(source, info.photoURL),
|
||||||
|
self._update_open_link(info.linkId, info.linkURL),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
if info.participantInfo:
|
if info.participantInfo:
|
||||||
|
@ -550,6 +562,24 @@ class Portal(DBPortal, BasePortal):
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
async def _update_open_link(self, link_id: Long | None, link_url: str | None) -> bool:
|
||||||
|
changed = False
|
||||||
|
if self.link_id != link_id:
|
||||||
|
self.log.trace(f"Updating OpenLink ID {self.link_id} -> {link_id}")
|
||||||
|
self.link_id = link_id
|
||||||
|
changed = True
|
||||||
|
if self.link_url != link_url:
|
||||||
|
if link_url:
|
||||||
|
if not link_url.startswith(self.OPEN_LINK_URL_PREFIX):
|
||||||
|
self.log.error(f"Unexpected prefix for OpenLink URL {link_url}")
|
||||||
|
link_url = None
|
||||||
|
else:
|
||||||
|
link_url = link_url.removeprefix(self.OPEN_LINK_URL_PREFIX)
|
||||||
|
self.log.trace(f"Updating OpenLink URL {self.link_url} -> {link_url}")
|
||||||
|
self.link_url = link_url
|
||||||
|
changed = True
|
||||||
|
return changed
|
||||||
|
|
||||||
async def _update_photo(self, source: u.User, photo_id: str | None) -> bool:
|
async def _update_photo(self, source: u.User, photo_id: str | None) -> bool:
|
||||||
if self.is_direct and not self.encrypted:
|
if self.is_direct and not self.encrypted:
|
||||||
return False
|
return False
|
||||||
|
@ -1375,6 +1405,13 @@ class Portal(DBPortal, BasePortal):
|
||||||
async def _revert_matrix_room_avatar(self, prev_content: RoomAvatarStateEventContent) -> None:
|
async def _revert_matrix_room_avatar(self, prev_content: RoomAvatarStateEventContent) -> None:
|
||||||
await self.main_intent.set_room_avatar(self.mxid, prev_content.url)
|
await self.main_intent.set_room_avatar(self.mxid, prev_content.url)
|
||||||
|
|
||||||
|
async def handle_matrix_join(self, user: u.User) -> None:
|
||||||
|
if self.link_id:
|
||||||
|
try:
|
||||||
|
await user.client.join_channel(self.ktid, self.link_id)
|
||||||
|
except Exception as e:
|
||||||
|
await self.main_intent.kick_user(self.mxid, user.mxid, str(e))
|
||||||
|
|
||||||
async def handle_matrix_leave(self, user: u.User) -> None:
|
async def handle_matrix_leave(self, user: u.User) -> None:
|
||||||
if self.is_direct:
|
if self.is_direct:
|
||||||
self.log.info(f"{user.mxid} left private chat portal with {self.ktid}")
|
self.log.info(f"{user.mxid} left private chat portal with {self.ktid}")
|
||||||
|
|
|
@ -707,6 +707,10 @@ class User(DBUser, BaseUser):
|
||||||
|
|
||||||
# region Matrix->KakaoTalk commands
|
# region Matrix->KakaoTalk commands
|
||||||
|
|
||||||
|
async def join_channel(self, url: str) -> None:
|
||||||
|
await self.client.join_channel_by_url(url)
|
||||||
|
# TODO Get channel ID(s) and sync
|
||||||
|
|
||||||
async def leave_channel(self, portal: po.Portal) -> None:
|
async def leave_channel(self, portal: po.Portal) -> None:
|
||||||
await self.client.leave_channel(portal.channel_props)
|
await self.client.leave_channel(portal.channel_props)
|
||||||
await self.on_channel_left(portal.ktid, portal.kt_type)
|
await self.on_channel_left(portal.ktid, portal.kt_type)
|
||||||
|
|
|
@ -30,6 +30,7 @@ import { ReadStreamUtil } from "node-kakao/stream"
|
||||||
/** @typedef {import("node-kakao").MentionStruct} MentionStruct */
|
/** @typedef {import("node-kakao").MentionStruct} MentionStruct */
|
||||||
/** @typedef {import("node-kakao").TalkNormalChannel} TalkNormalChannel */
|
/** @typedef {import("node-kakao").TalkNormalChannel} TalkNormalChannel */
|
||||||
/** @typedef {import("node-kakao").TalkOpenChannel} TalkOpenChannel */
|
/** @typedef {import("node-kakao").TalkOpenChannel} TalkOpenChannel */
|
||||||
|
/** @typedef {import("node-kakao").OpenLink} OpenLink */
|
||||||
/** @typedef {import("node-kakao/dist/talk").TalkChannelList} TalkChannelList */
|
/** @typedef {import("node-kakao/dist/talk").TalkChannelList} TalkChannelList */
|
||||||
// TODO Remove once/if some helper type hints are upstreamed
|
// TODO Remove once/if some helper type hints are upstreamed
|
||||||
/** @typedef {import("node-kakao").OpenChannelUserInfo} OpenChannelUserInfo */
|
/** @typedef {import("node-kakao").OpenChannelUserInfo} OpenChannelUserInfo */
|
||||||
|
@ -46,6 +47,7 @@ import { emitLines, promisify } from "./util.js"
|
||||||
* @typedef {object} ChannelProps
|
* @typedef {object} ChannelProps
|
||||||
* @property {Long} id
|
* @property {Long} id
|
||||||
* @property {ChannelType} type
|
* @property {ChannelType} type
|
||||||
|
* @property {?Long} link_id
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
@ -297,8 +299,10 @@ class UserClient {
|
||||||
this.write("channel_meta_change", {
|
this.write("channel_meta_change", {
|
||||||
info: {
|
info: {
|
||||||
name: data.ol?.ln,
|
name: data.ol?.ln,
|
||||||
description: data.ol?.desc || null,
|
description: data.ol?.desc,
|
||||||
photoURL: data.ol?.liu || null,
|
photoURL: data.ol?.liu || null,
|
||||||
|
linkId: data.ol?.linkId,
|
||||||
|
linkURL: data.ol?.linkURL,
|
||||||
},
|
},
|
||||||
channelId: channel.channelId,
|
channelId: channel.channelId,
|
||||||
channelType: channel.info.type,
|
channelType: channel.info.type,
|
||||||
|
@ -375,7 +379,11 @@ class UserClient {
|
||||||
this.#talkClient.channelList,
|
this.#talkClient.channelList,
|
||||||
channelProps.type
|
channelProps.type
|
||||||
)
|
)
|
||||||
const res = await channelList.addChannel({ channelId: channelProps.id })
|
let res = await channelList.addChannel({ channelId: channelProps.id })
|
||||||
|
if (!res.success && channelProps.link_id) {
|
||||||
|
res = await this.#talkClient.channelList.open.joinChannel({ linkId: channelProps.link_id }, {})
|
||||||
|
}
|
||||||
|
|
||||||
if (!res.success) {
|
if (!res.success) {
|
||||||
this.error(`Unable to add ${channelProps.type} channel ${channelProps.id}`)
|
this.error(`Unable to add ${channelProps.type} channel ${channelProps.id}`)
|
||||||
throw res
|
throw res
|
||||||
|
@ -765,11 +773,16 @@ export default class PeerClient {
|
||||||
const res = await talkChannel.updateAll()
|
const res = await talkChannel.updateAll()
|
||||||
if (!res.success) return res
|
if (!res.success) return res
|
||||||
|
|
||||||
|
/** @type {?OpenLink} */
|
||||||
|
const openLink = talkChannel.info.openLink
|
||||||
|
|
||||||
return makeCommandResult({
|
return makeCommandResult({
|
||||||
name: talkChannel.getDisplayName(),
|
name: talkChannel.getDisplayName(),
|
||||||
description: talkChannel.info.openLink?.description,
|
description: openLink?.description,
|
||||||
// TODO Find out why linkCoverURL is blank, despite having updated the channel!
|
// TODO Find out why linkCoverURL is blank, despite having updated the channel!
|
||||||
photoURL: talkChannel.info.openLink?.linkCoverURL || null,
|
photoURL: openLink?.linkCoverURL || null,
|
||||||
|
linkId: openLink?.linkId,
|
||||||
|
linkURL: openLink?.linkURL,
|
||||||
participantInfo: {
|
participantInfo: {
|
||||||
// TODO Get members from chatON?
|
// TODO Get members from chatON?
|
||||||
participants: Array.from(talkChannel.getAllUserInfo()),
|
participants: Array.from(talkChannel.getAllUserInfo()),
|
||||||
|
@ -1254,6 +1267,60 @@ export default class PeerClient {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {object} req
|
||||||
|
* @param {string} req.mxid
|
||||||
|
* @param {string} req.url
|
||||||
|
*/
|
||||||
|
joinChannelByURL = async (req) => {
|
||||||
|
const channelList = this.#getUser(req.mxid).talkClient.channelList.open
|
||||||
|
|
||||||
|
const inviteRes = await channelList.getJoinInfo(req.url)
|
||||||
|
if (!inviteRes.success) throw inviteRes
|
||||||
|
|
||||||
|
const channelIds = channelList.getLinkChannelList(inviteRes.result.openLink.linkId)
|
||||||
|
if (channelIds.length == 0) {
|
||||||
|
throw new ProtocolError(`No channel found for OpenLink URL ${req.url}`)
|
||||||
|
}
|
||||||
|
if (channelIds.length > 1) {
|
||||||
|
this.log(`Multiple channels found for OpenLink URL ${req.url}: ${channelIds.join(", ")}`)
|
||||||
|
}
|
||||||
|
for (const channelId of channelList.getLinkChannelList(inviteRes.result.openLink.linkId)) {
|
||||||
|
if (channelList.get(channelId)) {
|
||||||
|
this.log(`Already joined channel ${channelId}`)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
const joinRes = await channelList.joinChannel(inviteRes.result.openLink, {})
|
||||||
|
if (!joinRes.success) {
|
||||||
|
this.error(`Failed to join channel ${channelId} via ${inviteRes.result.openLink.linkId}`)
|
||||||
|
} else {
|
||||||
|
this.log(`Joined channel ${channelId} via ${inviteRes.result.openLink.linkId}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// TODO Consider returning ID of each joined channel
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {object} req
|
||||||
|
* @param {string} req.mxid
|
||||||
|
* @param {Long} req.channel_id
|
||||||
|
* @param {Long} req.link_id
|
||||||
|
*/
|
||||||
|
joinChannel = async (req) => {
|
||||||
|
const channelList = this.#getUser(req.mxid).talkClient.channelList.open
|
||||||
|
let talkChannel = channelList.get(req.channel_id)
|
||||||
|
if (talkChannel) {
|
||||||
|
this.log(`Already joined channel ${channelId}`)
|
||||||
|
} else {
|
||||||
|
const joinRes = await channelList.joinChannel({ linkId: req.link_id }, {})
|
||||||
|
if (!joinRes.success) return joinRes
|
||||||
|
|
||||||
|
this.log(`Joined channel ${channelId} via ${req.link_id}`)
|
||||||
|
talkChannel = joinRes.result
|
||||||
|
}
|
||||||
|
return makeCommandResult(talkChannel.channelId)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {object} req
|
* @param {object} req
|
||||||
* @param {string} req.mxid
|
* @param {string} req.mxid
|
||||||
|
@ -1363,6 +1430,8 @@ export default class PeerClient {
|
||||||
set_channel_description: this.setChannelDescription,
|
set_channel_description: this.setChannelDescription,
|
||||||
//set_channel_photo: this.setChannelPhoto,
|
//set_channel_photo: this.setChannelPhoto,
|
||||||
create_direct_chat: this.createDirectChat,
|
create_direct_chat: this.createDirectChat,
|
||||||
|
join_channel_by_url: this.joinChannelByURL,
|
||||||
|
join_channel: this.joinChannel,
|
||||||
leave_channel: this.leaveChannel,
|
leave_channel: this.leaveChannel,
|
||||||
}[req.command] || this.handleUnknownCommand
|
}[req.command] || this.handleUnknownCommand
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue