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 .errors import InvalidAccessToken
|
||||
from .errors import InvalidAccessToken, CommandException
|
||||
from .error_helper import raise_unsuccessful_response
|
||||
|
||||
try:
|
||||
@ -257,6 +257,22 @@ class Client:
|
||||
"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:
|
||||
return await self._api_user_request_result(
|
||||
Chatlog,
|
||||
|
@ -122,7 +122,7 @@ class Portal(DBPortal, BasePortal):
|
||||
config: Config
|
||||
|
||||
_main_intent: IntentAPI | None
|
||||
_kt_sender: int | None
|
||||
_kt_sender: Long | None
|
||||
_create_room_lock: asyncio.Lock
|
||||
_send_locks: dict[int, asyncio.Lock]
|
||||
_noop_lock: FakeLock = FakeLock()
|
||||
@ -218,7 +218,7 @@ class Portal(DBPortal, BasePortal):
|
||||
if self.mxid:
|
||||
await DBMessage.delete_all_by_room(self.mxid)
|
||||
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.name_set = False
|
||||
self.avatar_set = False
|
||||
@ -230,7 +230,7 @@ class Portal(DBPortal, BasePortal):
|
||||
# region Properties
|
||||
|
||||
@property
|
||||
def _ktid_full(self) -> tuple[int, int]:
|
||||
def ktid_full(self) -> tuple[Long, Long]:
|
||||
return self.ktid, self.kt_receiver
|
||||
|
||||
@property
|
||||
@ -263,11 +263,7 @@ class Portal(DBPortal, BasePortal):
|
||||
@property
|
||||
def main_intent(self) -> IntentAPI:
|
||||
if not self._main_intent:
|
||||
raise ValueError(
|
||||
"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"
|
||||
)
|
||||
raise ValueError("Portal must be postinit()ed before main_intent can be used")
|
||||
return self._main_intent
|
||||
|
||||
async def get_dm_puppet(self) -> p.Puppet | None:
|
||||
@ -485,7 +481,7 @@ class Portal(DBPortal, BasePortal):
|
||||
self, source: u.User, participant: UserInfoUnion
|
||||
) -> bool:
|
||||
# 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)
|
||||
await puppet.update_info_from_participant(source, participant)
|
||||
changed = False
|
||||
@ -504,14 +500,6 @@ class Portal(DBPortal, BasePortal):
|
||||
if participants is None:
|
||||
self.log.debug("Called _update_participants with no participants, fetching them now...")
|
||||
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 = [
|
||||
self._update_participant(source, pcp) for pcp in participants
|
||||
]
|
||||
@ -1277,21 +1265,25 @@ class Portal(DBPortal, BasePortal):
|
||||
# region Database getters
|
||||
|
||||
async def postinit(self) -> None:
|
||||
self.by_ktid[self._ktid_full] = self
|
||||
self.by_ktid[self.ktid_full] = self
|
||||
if self.mxid:
|
||||
self.by_mxid[self.mxid] = self
|
||||
if not self.is_direct:
|
||||
self._main_intent = self.az.intent
|
||||
elif self.mxid:
|
||||
else:
|
||||
# TODO Save kt_sender in DB instead? Depends on if DM channels are shared...
|
||||
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}"
|
||||
if user.is_connected:
|
||||
await self._update_participants(user)
|
||||
if self.kt_type == KnownChannelType.MemoChat:
|
||||
self._kt_sender = user.ktid
|
||||
else:
|
||||
self.log.debug(f"Not setting _main_intent of new direct chat for disconnected user {user.ktid}")
|
||||
else:
|
||||
self.log.debug("Not setting _main_intent of new direct chat until after checking participant list")
|
||||
# NOTE This throws if the user isn't connected--good!
|
||||
# Nothing should init a portal for a disconnected user.
|
||||
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
|
||||
@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.bson import Long
|
||||
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.client.client_session import LoginDataItem, LoginResult
|
||||
from .kt.types.oauth import OAuthCredential
|
||||
@ -556,18 +556,28 @@ class User(DBUser, BaseUser):
|
||||
return await pu.Puppet.get_by_ktid(self.ktid)
|
||||
|
||||
async def get_portal_with(self, puppet: pu.Puppet, create: bool = True) -> po.Portal | None:
|
||||
# TODO
|
||||
return None
|
||||
"""
|
||||
if not self.ktid or not self.client:
|
||||
# TODO Make upstream request to return custom failure message
|
||||
if not self.ktid or not self.is_connected:
|
||||
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(
|
||||
ktid, kt_receiver=self.ktid, create=create, kt_type=kt_type
|
||||
)
|
||||
else:
|
||||
self.log.warning(f"Didn't find an existing DM channel with KakaoTalk user {puppet.ktid}, so not creating one")
|
||||
return None
|
||||
return await po.Portal.get_by_ktid(
|
||||
await self.client.get_dm_channel_id_for(puppet.ktid),
|
||||
kt_receiver=self.ktid,
|
||||
create=create,
|
||||
kt_type=KnownChannelType.DirectChat if puppet.ktid != self.ktid else KnownChannelType.MemoChat
|
||||
)
|
||||
"""
|
||||
|
||||
# region KakaoTalk event handling
|
||||
|
||||
|
@ -464,13 +464,41 @@ export default class PeerClient {
|
||||
|
||||
/**
|
||||
* @param {Object} req
|
||||
* @param {?string} req.mxid
|
||||
* @param {?OAuthCredential} req.oauth_credential
|
||||
* @param {string} req.mxid
|
||||
*/
|
||||
listFriends = async (req) => {
|
||||
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 {string} req.mxid
|
||||
@ -590,6 +618,8 @@ export default class PeerClient {
|
||||
get_participants: this.getParticipants,
|
||||
get_chats: this.getChats,
|
||||
list_friends: this.listFriends,
|
||||
get_friend_dm_id: req => this.getFriendProperty(req, "directChatId"),
|
||||
get_memo_ids: this.getMemoIds,
|
||||
send_message: this.sendMessage,
|
||||
send_media: this.sendMedia,
|
||||
}[req.command] || this.handleUnknownCommand
|
||||
|
Loading…
Reference in New Issue
Block a user