diff --git a/ROADMAP.md b/ROADMAP.md index 9eec13b..3331f15 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -73,10 +73,7 @@ * [x] At startup * [x] When added to chat * [x] When receiving message - * [ ] Private chat creation by inviting Matrix puppet of KakaoTalk user to new room - * [x] For existing recently-updated KakaoTalk channels - * [ ] For existing long-idled KakaoTalk channels - * [ ] For new KakaoTalk channels + * [x] Direct chat creation by inviting Matrix puppet of KakaoTalk user to new room * [x] Option to use own Matrix account for messages sent from other KakaoTalk clients * [ ] KakaoTalk friends list management * [x] List friends diff --git a/matrix_appservice_kakaotalk/kt/client/client.py b/matrix_appservice_kakaotalk/kt/client/client.py index 2a45fbb..b4379da 100644 --- a/matrix_appservice_kakaotalk/kt/client/client.py +++ b/matrix_appservice_kakaotalk/kt/client/client.py @@ -505,6 +505,13 @@ class Client: photo_url=photo_url, ) + def create_direct_chat(self, ktid: Long) -> Awaitable[Long]: + return self._api_user_request_result( + Long, + "create_direct_chat", + user_id=ktid.serialize(), + ) + def leave_channel( self, channel_props: ChannelProps, diff --git a/matrix_appservice_kakaotalk/matrix.py b/matrix_appservice_kakaotalk/matrix.py index 8d6757c..62b4437 100644 --- a/matrix_appservice_kakaotalk/matrix.py +++ b/matrix_appservice_kakaotalk/matrix.py @@ -30,7 +30,7 @@ from mautrix.types import ( UserID, ) -from . import portal as po, user as u +from . import portal as po, puppet as pu, user as u from .db import Message as DBMessage if TYPE_CHECKING: @@ -63,6 +63,19 @@ class MatrixHandler(BaseMatrixHandler): room_id, "This room has been marked as your KakaoTalk bridge notice room." ) + async def handle_puppet_dm_invite( + self, room_id: RoomID, puppet: pu.Puppet, invited_by: u.User, evt: StateEvent + ) -> None: + # TODO Make upstream request to return custom failure message, + # instead of having to reimplement the entire function + portal = await invited_by.get_portal_with(puppet) + if portal: + await portal.accept_matrix_dm(room_id, invited_by, puppet) + else: + await puppet.default_mxid_intent.leave_room( + room_id, reason="Unable to create a DM for this KakaoTalk user" + ) + async def handle_invite( self, room_id: RoomID, user_id: UserID, invited_by: u.User, event_id: EventID ) -> None: diff --git a/matrix_appservice_kakaotalk/user.py b/matrix_appservice_kakaotalk/user.py index 77f27cc..c6dd586 100644 --- a/matrix_appservice_kakaotalk/user.py +++ b/matrix_appservice_kakaotalk/user.py @@ -685,14 +685,18 @@ class User(DBUser, BaseUser): else: ktid = memo_ids[0] if len(memo_ids) > 1: - self.log.info("Found multiple memo chats, so using the first one as a fallback") + self.log.warning("Found multiple memo chats, so using the first one as a fallback") if ktid: - return await po.Portal.get_by_ktid( - ktid, kt_receiver=self.ktid, create=create, kt_type=kt_type - ) + self.log.debug(f"Found existing direct chat with KakaoTalk user {puppet.ktid}") else: - self.log.warning(f"Didn't find an existing DM channel with KakaoTalk user {puppet.ktid}, so not creating one") - return None + self.log.debug(f"Didn't find an existing direct chat with KakaoTalk user {puppet.ktid}, so will create one") + try: + ktid = await self.client.create_direct_chat(puppet.ktid) + except: + self.log.exception(f"Failed to create direct chat with {puppet.ktid}") + return await po.Portal.get_by_ktid( + ktid, kt_receiver=self.ktid, create=create, kt_type=kt_type + ) if ktid else None # region KakaoTalk event handling diff --git a/node/src/client.js b/node/src/client.js index f053b02..defd0ab 100644 --- a/node/src/client.js +++ b/node/src/client.js @@ -959,6 +959,7 @@ export default class PeerClient { /** @type Long[] */ const channelIds = [] const channelList = this.#getUser(req.mxid).talkClient.channelList + // TODO channelList.all() doesn't really return *all* channels... for (const channel of channelList.all()) { if (channel.info.type == "MemoChat") { channelIds.push(channel.channelId) @@ -1128,6 +1129,20 @@ export default class PeerClient { }) } + /** + * @param {Object} req + * @param {string} req.mxid + * @param {Long} req.user_id + */ + createDirectChat = async (req) => { + const channelList = this.#getUser(req.mxid).talkClient.channelList.normal + const res = await channelList.createChannel({ + userList: [{ userId: req.user_id }], + }) + if (!res.success) return res + return makeCommandResult(res.result.channelId) + } + /** * @param {Object} req * @param {string} req.mxid @@ -1236,6 +1251,7 @@ export default class PeerClient { set_channel_name: this.setChannelName, set_channel_description: this.setChannelDescription, set_channel_photo: this.setChannelPhoto, + create_direct_chat: this.createDirectChat, leave_channel: this.leaveChannel, }[req.command] || this.handleUnknownCommand }