Allow syncing (existing) direct chat channel by inviting puppet to DM
This commit is contained in:
parent
3900e666ff
commit
a9633118c5
|
@ -53,7 +53,7 @@ from ..types.request import (
|
||||||
|
|
||||||
from .types import PortalChannelInfo, UserInfoUnion, ChannelProps
|
from .types import PortalChannelInfo, UserInfoUnion, ChannelProps
|
||||||
|
|
||||||
from .errors import InvalidAccessToken
|
from .errors import InvalidAccessToken, CommandException
|
||||||
from .error_helper import raise_unsuccessful_response
|
from .error_helper import raise_unsuccessful_response
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -257,6 +257,22 @@ class Client:
|
||||||
"list_friends",
|
"list_friends",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
async def get_friend_dm_id(self, friend_id: Long) -> Long | None:
|
||||||
|
try:
|
||||||
|
return await self._api_user_request_result(
|
||||||
|
Long,
|
||||||
|
"get_friend_dm_id",
|
||||||
|
friend_id=friend_id.serialize(),
|
||||||
|
)
|
||||||
|
except CommandException:
|
||||||
|
self.log.exception(f"Could not find friend with ID {friend_id}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
async def get_memo_ids(self) -> list[Long]:
|
||||||
|
return ResultListType(Long).deserialize(
|
||||||
|
await self._rpc_client.request("get_memo_ids", mxid=self.user.mxid)
|
||||||
|
)
|
||||||
|
|
||||||
async def send_message(self, channel_props: ChannelProps, text: str) -> Chatlog:
|
async def send_message(self, channel_props: ChannelProps, text: str) -> Chatlog:
|
||||||
return await self._api_user_request_result(
|
return await self._api_user_request_result(
|
||||||
Chatlog,
|
Chatlog,
|
||||||
|
|
|
@ -122,7 +122,7 @@ class Portal(DBPortal, BasePortal):
|
||||||
config: Config
|
config: Config
|
||||||
|
|
||||||
_main_intent: IntentAPI | None
|
_main_intent: IntentAPI | None
|
||||||
_kt_sender: int | None
|
_kt_sender: Long | None
|
||||||
_create_room_lock: asyncio.Lock
|
_create_room_lock: asyncio.Lock
|
||||||
_send_locks: dict[int, asyncio.Lock]
|
_send_locks: dict[int, asyncio.Lock]
|
||||||
_noop_lock: FakeLock = FakeLock()
|
_noop_lock: FakeLock = FakeLock()
|
||||||
|
@ -218,7 +218,7 @@ class Portal(DBPortal, BasePortal):
|
||||||
if self.mxid:
|
if self.mxid:
|
||||||
await DBMessage.delete_all_by_room(self.mxid)
|
await DBMessage.delete_all_by_room(self.mxid)
|
||||||
self.by_mxid.pop(self.mxid, None)
|
self.by_mxid.pop(self.mxid, None)
|
||||||
self.by_ktid.pop(self._ktid_full, None)
|
self.by_ktid.pop(self.ktid_full, None)
|
||||||
self.mxid = None
|
self.mxid = None
|
||||||
self.name_set = False
|
self.name_set = False
|
||||||
self.avatar_set = False
|
self.avatar_set = False
|
||||||
|
@ -230,7 +230,7 @@ class Portal(DBPortal, BasePortal):
|
||||||
# region Properties
|
# region Properties
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def _ktid_full(self) -> tuple[int, int]:
|
def ktid_full(self) -> tuple[Long, Long]:
|
||||||
return self.ktid, self.kt_receiver
|
return self.ktid, self.kt_receiver
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -263,11 +263,7 @@ class Portal(DBPortal, BasePortal):
|
||||||
@property
|
@property
|
||||||
def main_intent(self) -> IntentAPI:
|
def main_intent(self) -> IntentAPI:
|
||||||
if not self._main_intent:
|
if not self._main_intent:
|
||||||
raise ValueError(
|
raise ValueError("Portal must be postinit()ed before main_intent can be used")
|
||||||
"Portal must be postinit()ed before main_intent can be used"
|
|
||||||
if not self.is_direct else
|
|
||||||
"Direct chat portal must call postinit and _update_participants before main_intent can be used"
|
|
||||||
)
|
|
||||||
return self._main_intent
|
return self._main_intent
|
||||||
|
|
||||||
async def get_dm_puppet(self) -> p.Puppet | None:
|
async def get_dm_puppet(self) -> p.Puppet | None:
|
||||||
|
@ -485,7 +481,7 @@ class Portal(DBPortal, BasePortal):
|
||||||
self, source: u.User, participant: UserInfoUnion
|
self, source: u.User, participant: UserInfoUnion
|
||||||
) -> bool:
|
) -> bool:
|
||||||
# TODO nick map?
|
# TODO nick map?
|
||||||
self.log.trace("Syncing participant %s", participant.id)
|
self.log.trace(f"Syncing participant {participant.userId}")
|
||||||
puppet = await p.Puppet.get_by_ktid(participant.userId)
|
puppet = await p.Puppet.get_by_ktid(participant.userId)
|
||||||
await puppet.update_info_from_participant(source, participant)
|
await puppet.update_info_from_participant(source, participant)
|
||||||
changed = False
|
changed = False
|
||||||
|
@ -504,14 +500,6 @@ class Portal(DBPortal, BasePortal):
|
||||||
if participants is None:
|
if participants is None:
|
||||||
self.log.debug("Called _update_participants with no participants, fetching them now...")
|
self.log.debug("Called _update_participants with no participants, fetching them now...")
|
||||||
participants = await source.client.get_participants(self.channel_props)
|
participants = await source.client.get_participants(self.channel_props)
|
||||||
if not self._main_intent:
|
|
||||||
assert self.is_direct, "_main_intent for non-direct chat portal should have been set already"
|
|
||||||
self._kt_sender = participants[
|
|
||||||
0 if self.kt_type == KnownChannelType.MemoChat or participants[0].userId != source.ktid else 1
|
|
||||||
].userId
|
|
||||||
self._main_intent = (await self.get_dm_puppet()).default_mxid_intent
|
|
||||||
else:
|
|
||||||
self._kt_sender = (await p.Puppet.get_by_mxid(self._main_intent.mxid)).ktid if self.is_direct else None
|
|
||||||
sync_tasks = [
|
sync_tasks = [
|
||||||
self._update_participant(source, pcp) for pcp in participants
|
self._update_participant(source, pcp) for pcp in participants
|
||||||
]
|
]
|
||||||
|
@ -1277,21 +1265,25 @@ class Portal(DBPortal, BasePortal):
|
||||||
# region Database getters
|
# region Database getters
|
||||||
|
|
||||||
async def postinit(self) -> None:
|
async def postinit(self) -> None:
|
||||||
self.by_ktid[self._ktid_full] = self
|
self.by_ktid[self.ktid_full] = self
|
||||||
if self.mxid:
|
if self.mxid:
|
||||||
self.by_mxid[self.mxid] = self
|
self.by_mxid[self.mxid] = self
|
||||||
if not self.is_direct:
|
if not self.is_direct:
|
||||||
self._main_intent = self.az.intent
|
self._main_intent = self.az.intent
|
||||||
elif self.mxid:
|
else:
|
||||||
# TODO Save kt_sender in DB instead? Depends on if DM channels are shared...
|
# TODO Save kt_sender in DB instead? Depends on if DM channels are shared...
|
||||||
user = await u.User.get_by_ktid(self.kt_receiver)
|
user = await u.User.get_by_ktid(self.kt_receiver)
|
||||||
assert user, f"Found no user for this portal's receiver of {self.kt_receiver}"
|
assert user, f"Found no user for this portal's receiver of {self.kt_receiver}"
|
||||||
if user.is_connected:
|
if self.kt_type == KnownChannelType.MemoChat:
|
||||||
await self._update_participants(user)
|
self._kt_sender = user.ktid
|
||||||
else:
|
else:
|
||||||
self.log.debug(f"Not setting _main_intent of new direct chat for disconnected user {user.ktid}")
|
# NOTE This throws if the user isn't connected--good!
|
||||||
else:
|
# Nothing should init a portal for a disconnected user.
|
||||||
self.log.debug("Not setting _main_intent of new direct chat until after checking participant list")
|
participants = await user.client.get_participants(self.channel_props)
|
||||||
|
self._kt_sender = participants[
|
||||||
|
0 if participants[0].userId != user.ktid else 1
|
||||||
|
].userId
|
||||||
|
self._main_intent = (await p.Puppet.get_by_ktid(self._kt_sender)).default_mxid_intent
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@async_getter_lock
|
@async_getter_lock
|
||||||
|
|
|
@ -40,7 +40,7 @@ from .kt.client.errors import AuthenticationRequired, ResponseError
|
||||||
from .kt.types.api.struct.profile import ProfileStruct
|
from .kt.types.api.struct.profile import ProfileStruct
|
||||||
from .kt.types.bson import Long
|
from .kt.types.bson import Long
|
||||||
from .kt.types.channel.channel_info import ChannelInfo, NormalChannelInfo, NormalChannelData
|
from .kt.types.channel.channel_info import ChannelInfo, NormalChannelInfo, NormalChannelData
|
||||||
from .kt.types.channel.channel_type import ChannelType
|
from .kt.types.channel.channel_type import ChannelType, KnownChannelType
|
||||||
from .kt.types.chat.chat import Chatlog
|
from .kt.types.chat.chat import Chatlog
|
||||||
from .kt.types.client.client_session import LoginDataItem, LoginResult
|
from .kt.types.client.client_session import LoginDataItem, LoginResult
|
||||||
from .kt.types.oauth import OAuthCredential
|
from .kt.types.oauth import OAuthCredential
|
||||||
|
@ -556,18 +556,28 @@ class User(DBUser, BaseUser):
|
||||||
return await pu.Puppet.get_by_ktid(self.ktid)
|
return await pu.Puppet.get_by_ktid(self.ktid)
|
||||||
|
|
||||||
async def get_portal_with(self, puppet: pu.Puppet, create: bool = True) -> po.Portal | None:
|
async def get_portal_with(self, puppet: pu.Puppet, create: bool = True) -> po.Portal | None:
|
||||||
# TODO
|
# TODO Make upstream request to return custom failure message
|
||||||
return None
|
if not self.ktid or not self.is_connected:
|
||||||
"""
|
|
||||||
if not self.ktid or not self.client:
|
|
||||||
return None
|
return None
|
||||||
|
if puppet.ktid != self.ktid:
|
||||||
|
kt_type = KnownChannelType.DirectChat
|
||||||
|
ktid = await self.client.get_friend_dm_id(puppet.ktid)
|
||||||
|
else:
|
||||||
|
kt_type = KnownChannelType.MemoChat
|
||||||
|
memo_ids = await self.client.get_memo_ids()
|
||||||
|
if not memo_ids:
|
||||||
|
ktid = Long(0)
|
||||||
|
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")
|
||||||
|
if ktid:
|
||||||
return await po.Portal.get_by_ktid(
|
return await po.Portal.get_by_ktid(
|
||||||
await self.client.get_dm_channel_id_for(puppet.ktid),
|
ktid, kt_receiver=self.ktid, create=create, kt_type=kt_type
|
||||||
kt_receiver=self.ktid,
|
|
||||||
create=create,
|
|
||||||
kt_type=KnownChannelType.DirectChat if puppet.ktid != self.ktid else KnownChannelType.MemoChat
|
|
||||||
)
|
)
|
||||||
"""
|
else:
|
||||||
|
self.log.warning(f"Didn't find an existing DM channel with KakaoTalk user {puppet.ktid}, so not creating one")
|
||||||
|
return None
|
||||||
|
|
||||||
# region KakaoTalk event handling
|
# region KakaoTalk event handling
|
||||||
|
|
||||||
|
|
|
@ -464,13 +464,41 @@ export default class PeerClient {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {Object} req
|
* @param {Object} req
|
||||||
* @param {?string} req.mxid
|
* @param {string} req.mxid
|
||||||
* @param {?OAuthCredential} req.oauth_credential
|
|
||||||
*/
|
*/
|
||||||
listFriends = async (req) => {
|
listFriends = async (req) => {
|
||||||
return await this.#getUser(req.mxid).serviceClient.requestFriendList()
|
return await this.#getUser(req.mxid).serviceClient.requestFriendList()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Object} req
|
||||||
|
* @param {string} req.mxid The user whose friend is being looked up.
|
||||||
|
* @param {string} req.friend_id The friend to search for.
|
||||||
|
* @param {string} propertyName The property to retrieve from the specified friend.
|
||||||
|
*/
|
||||||
|
getFriendProperty = async (req, propertyName) => {
|
||||||
|
const res = await this.#getUser(req.mxid).serviceClient.findFriendById(req.friend_id)
|
||||||
|
if (!res.success) return res
|
||||||
|
|
||||||
|
return this.#makeCommandResult(res.result.friend[propertyName])
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Object} req
|
||||||
|
* @param {string} req.mxid
|
||||||
|
*/
|
||||||
|
getMemoIds = (req) => {
|
||||||
|
/** @type Long[] */
|
||||||
|
const channelIds = []
|
||||||
|
const channelList = this.#getUser(req.mxid).talkClient.channelList
|
||||||
|
for (const channel of channelList.all()) {
|
||||||
|
if (channel.info.type == "MemoChat") {
|
||||||
|
channelIds.push(channel.channelId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return channelIds
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {Object} req
|
* @param {Object} req
|
||||||
* @param {string} req.mxid
|
* @param {string} req.mxid
|
||||||
|
@ -590,6 +618,8 @@ export default class PeerClient {
|
||||||
get_participants: this.getParticipants,
|
get_participants: this.getParticipants,
|
||||||
get_chats: this.getChats,
|
get_chats: this.getChats,
|
||||||
list_friends: this.listFriends,
|
list_friends: this.listFriends,
|
||||||
|
get_friend_dm_id: req => this.getFriendProperty(req, "directChatId"),
|
||||||
|
get_memo_ids: this.getMemoIds,
|
||||||
send_message: this.sendMessage,
|
send_message: this.sendMessage,
|
||||||
send_media: this.sendMedia,
|
send_media: this.sendMedia,
|
||||||
}[req.command] || this.handleUnknownCommand
|
}[req.command] || this.handleUnknownCommand
|
||||||
|
|
Loading…
Reference in New Issue