Implement logging out from the bridge
But there is no API for logging out of KakaoTalk
This commit is contained in:
parent
db41292be7
commit
bb3d7057b3
|
@ -24,7 +24,7 @@ from mautrix.util.signed_token import sign_token
|
||||||
from ..kt.client import Client as KakaoTalkClient
|
from ..kt.client import Client as KakaoTalkClient
|
||||||
from ..kt.client.errors import DeviceVerificationRequired, IncorrectPasscode, IncorrectPassword, CommandException
|
from ..kt.client.errors import DeviceVerificationRequired, IncorrectPasscode, IncorrectPassword, CommandException
|
||||||
|
|
||||||
#from .. import puppet as pu
|
from .. import puppet as pu
|
||||||
from .typehint import CommandEvent
|
from .typehint import CommandEvent
|
||||||
|
|
||||||
SECTION_AUTH = HelpSection("Authentication", 10, "")
|
SECTION_AUTH = HelpSection("Authentication", 10, "")
|
||||||
|
@ -53,7 +53,7 @@ try_again_or_cancel = "Try again, or say `$cmdprefix+sp cancel` to give up."
|
||||||
help_args="[_email_]",
|
help_args="[_email_]",
|
||||||
)
|
)
|
||||||
async def login(evt: CommandEvent) -> None:
|
async def login(evt: CommandEvent) -> None:
|
||||||
if evt.sender.client:
|
if await evt.sender.is_logged_in():
|
||||||
await evt.reply("You're already logged in")
|
await evt.reply("You're already logged in")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -156,10 +156,43 @@ async def _handle_login_failure(evt: CommandEvent, e: Exception) -> None:
|
||||||
await evt.reply(f"{message}: {e}")
|
await evt.reply(f"{message}: {e}")
|
||||||
|
|
||||||
|
|
||||||
@command_handler(needs_auth=True, help_section=SECTION_AUTH, help_text="Log out of KakaoTalk")
|
@command_handler(
|
||||||
|
needs_auth=True,
|
||||||
|
help_section=SECTION_AUTH,
|
||||||
|
help_text="Log out of KakaoTalk (and optionally change your virtual device ID for next login)",
|
||||||
|
help_args="[--reset-device]",
|
||||||
|
)
|
||||||
async def logout(evt: CommandEvent) -> None:
|
async def logout(evt: CommandEvent) -> None:
|
||||||
#puppet = await pu.Puppet.get_by_ktid(evt.sender.ktid)
|
if len(evt.args) >= 1:
|
||||||
await evt.sender.logout()
|
if evt.args[0] == "--reset-device":
|
||||||
#if puppet.is_real_user:
|
reset_device = True
|
||||||
# await puppet.switch_mxid(None, None)
|
else:
|
||||||
await evt.reply("Successfully logged out")
|
await evt.reply("**Usage:** `$cmdprefix+sp logout [--reset-device]`")
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
reset_device = False
|
||||||
|
|
||||||
|
puppet = await pu.Puppet.get_by_ktid(evt.sender.ktid)
|
||||||
|
await evt.sender.logout(reset_device=reset_device)
|
||||||
|
if puppet.is_real_user:
|
||||||
|
await puppet.switch_mxid(None, None)
|
||||||
|
|
||||||
|
message = "Successfully logged out"
|
||||||
|
if reset_device:
|
||||||
|
message += (
|
||||||
|
", and your next login will use a different device ID.\n\n"
|
||||||
|
"The old device must be manually de-registered from the KakaoTalk app."
|
||||||
|
)
|
||||||
|
await evt.reply(message)
|
||||||
|
|
||||||
|
|
||||||
|
@command_handler(needs_auth=False, help_section=SECTION_AUTH, help_text="Change your virtual device ID for next login")
|
||||||
|
async def reset_device(evt: CommandEvent) -> None:
|
||||||
|
if await evt.sender.is_logged_in():
|
||||||
|
await evt.reply("This command requires you to be logged out.")
|
||||||
|
else:
|
||||||
|
await evt.sender.logout(reset_device=True)
|
||||||
|
await evt.reply(
|
||||||
|
"Your next login will use a different device ID.\n\n"
|
||||||
|
"The old device must be manually de-registered from the KakaoTalk app."
|
||||||
|
)
|
||||||
|
|
|
@ -172,7 +172,7 @@ class User(DBUser, BaseUser):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def has_state(self) -> bool:
|
def has_state(self) -> bool:
|
||||||
return self.uuid and self.ktid and self.access_token and self.refresh_token
|
return bool(self.uuid and self.ktid and self.access_token and self.refresh_token)
|
||||||
|
|
||||||
# region Database getters
|
# region Database getters
|
||||||
|
|
||||||
|
@ -233,10 +233,13 @@ class User(DBUser, BaseUser):
|
||||||
|
|
||||||
async def get_uuid(self, force: bool = False) -> str:
|
async def get_uuid(self, force: bool = False) -> str:
|
||||||
if self.uuid is None or force:
|
if self.uuid is None or force:
|
||||||
self.uuid = await Client.generate_uuid(await self.get_all_uuids())
|
self.uuid = await self._generate_uuid()
|
||||||
await self.save()
|
await self.save()
|
||||||
return self.uuid
|
return self.uuid
|
||||||
|
|
||||||
|
async def _generate_uuid(self) -> str:
|
||||||
|
return await Client.generate_uuid(await self.get_all_uuids())
|
||||||
|
|
||||||
# endregion
|
# endregion
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -369,8 +372,7 @@ class User(DBUser, BaseUser):
|
||||||
finally:
|
finally:
|
||||||
self._is_refreshing = False
|
self._is_refreshing = False
|
||||||
|
|
||||||
async def logout(self, remove_ktid: bool = True) -> bool:
|
async def logout(self, *, remove_ktid: bool = True, reset_device: bool = False) -> bool:
|
||||||
# TODO Remove tokens too?
|
|
||||||
ok = True
|
ok = True
|
||||||
self.stop_listen()
|
self.stop_listen()
|
||||||
if self.has_state:
|
if self.has_state:
|
||||||
|
@ -385,12 +387,15 @@ class User(DBUser, BaseUser):
|
||||||
await self.client.stop()
|
await self.client.stop()
|
||||||
self.client = None
|
self.client = None
|
||||||
|
|
||||||
if remove_ktid:
|
if self.ktid and remove_ktid:
|
||||||
if self.ktid:
|
|
||||||
#await UserPortal.delete_all(self.ktid)
|
#await UserPortal.delete_all(self.ktid)
|
||||||
del self.by_ktid[self.ktid]
|
del self.by_ktid[self.ktid]
|
||||||
self.ktid = None
|
self.ktid = None
|
||||||
self.uuid = None
|
|
||||||
|
if reset_device:
|
||||||
|
self.uuid = await self._generate_uuid()
|
||||||
|
self.access_token = None
|
||||||
|
self.refresh_token = None
|
||||||
|
|
||||||
await self.save()
|
await self.save()
|
||||||
return ok
|
return ok
|
||||||
|
@ -583,7 +588,7 @@ class User(DBUser, BaseUser):
|
||||||
self.log.info("TODO: stop_listen")
|
self.log.info("TODO: stop_listen")
|
||||||
|
|
||||||
async def on_logged_in(self, oauth_credential: OAuthCredential) -> None:
|
async def on_logged_in(self, oauth_credential: OAuthCredential) -> None:
|
||||||
self.log.debug(f"Successfully logged in as {oauth_credential.uuid}")
|
self.log.debug(f"Successfully logged in as {oauth_credential.userId}")
|
||||||
self.oauth_credential = oauth_credential
|
self.oauth_credential = oauth_credential
|
||||||
self.client = Client(self, log=self.log.getChild("ktclient"))
|
self.client = Client(self, log=self.log.getChild("ktclient"))
|
||||||
await self.save()
|
await self.save()
|
||||||
|
|
|
@ -28,6 +28,7 @@ import {
|
||||||
|
|
||||||
|
|
||||||
class UserClient {
|
class UserClient {
|
||||||
|
static #initializing = false
|
||||||
|
|
||||||
#talkClient = new TalkClient()
|
#talkClient = new TalkClient()
|
||||||
get talkClient() { return this.#talkClient }
|
get talkClient() { return this.#talkClient }
|
||||||
|
@ -37,16 +38,28 @@ class UserClient {
|
||||||
get serviceClient() { return this.#serviceClient }
|
get serviceClient() { return this.#serviceClient }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} mxid The ID of the associated Matrix user
|
* DO NOT CONSTRUCT DIRECTLY. Callers should use {@link UserClient#create} instead.
|
||||||
* @param {OAuthCredential} credential The tokens that API calls may use
|
* @param {string} mxid
|
||||||
|
* @param {OAuthCredential} credential
|
||||||
*/
|
*/
|
||||||
constructor(mxid, credential) {
|
constructor(mxid, credential) {
|
||||||
|
if (!UserClient.#initializing) {
|
||||||
|
throw new Error("Private constructor")
|
||||||
|
}
|
||||||
|
UserClient.#initializing = false
|
||||||
|
|
||||||
this.mxid = mxid
|
this.mxid = mxid
|
||||||
this.credential = credential
|
this.credential = credential
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} mxid The ID of the associated Matrix user
|
||||||
|
* @param {OAuthCredential} credential The tokens that API calls may use
|
||||||
|
*/
|
||||||
static async create(mxid, credential) {
|
static async create(mxid, credential) {
|
||||||
|
this.#initializing = true
|
||||||
const userClient = new UserClient(mxid, credential)
|
const userClient = new UserClient(mxid, credential)
|
||||||
|
|
||||||
userClient.#serviceClient = await ServiceApiClient.create(this.credential)
|
userClient.#serviceClient = await ServiceApiClient.create(this.credential)
|
||||||
return userClient
|
return userClient
|
||||||
}
|
}
|
||||||
|
@ -194,9 +207,9 @@ export default class PeerClient {
|
||||||
/**
|
/**
|
||||||
* Checked lookup of a UserClient for a given mxid.
|
* Checked lookup of a UserClient for a given mxid.
|
||||||
* @param {string} mxid
|
* @param {string} mxid
|
||||||
|
* @returns {UserClient}
|
||||||
*/
|
*/
|
||||||
#getUser(mxid) {
|
#getUser(mxid) {
|
||||||
/** @type {UserClient} */
|
|
||||||
const userClient = this.userClients.get(mxid)
|
const userClient = this.userClients.get(mxid)
|
||||||
if (userClient === undefined) {
|
if (userClient === undefined) {
|
||||||
throw new Error(`Could not find user ${mxid}`)
|
throw new Error(`Could not find user ${mxid}`)
|
||||||
|
@ -204,6 +217,17 @@ export default class PeerClient {
|
||||||
return userClient
|
return userClient
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the service client for the specified user ID, or create
|
||||||
|
* and return a new service client if no user ID is provided.
|
||||||
|
* @param {string} mxid
|
||||||
|
* @param {OAuthCredential} oauth_credential
|
||||||
|
*/
|
||||||
|
async #getServiceClient(mxid, oauth_credential) {
|
||||||
|
return this.userClients.get(mxid)?.serviceClient ||
|
||||||
|
await ServiceApiClient.create(oauth_credential)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {Object} req
|
* @param {Object} req
|
||||||
* @param {OAuthCredential} req.oauth_credential
|
* @param {OAuthCredential} req.oauth_credential
|
||||||
|
@ -270,9 +294,7 @@ export default class PeerClient {
|
||||||
* @param {OAuthCredential} req.oauth_credential
|
* @param {OAuthCredential} req.oauth_credential
|
||||||
*/
|
*/
|
||||||
getOwnProfile = async (req) => {
|
getOwnProfile = async (req) => {
|
||||||
const serviceClient =
|
const serviceClient = await this.#getServiceClient(req.mxid, req.oauth_credential)
|
||||||
this.userClients.get(req.mxid)?.serviceClient ||
|
|
||||||
await ServiceApiClient.create(req.oauth_credential)
|
|
||||||
return await serviceClient.requestMyProfile()
|
return await serviceClient.requestMyProfile()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -283,10 +305,8 @@ export default class PeerClient {
|
||||||
* @param {Long} req.user_id
|
* @param {Long} req.user_id
|
||||||
*/
|
*/
|
||||||
getProfile = async (req) => {
|
getProfile = async (req) => {
|
||||||
const serviceClient =
|
const serviceClient = await this.#getServiceClient(req.mxid, req.oauth_credential)
|
||||||
this.userClients.get(mxid)?.serviceClient ||
|
return await serviceClient.requestProfile(req.user_id)
|
||||||
await ServiceApiClient.create(req.oauth_credential)
|
|
||||||
return await serviceClient.requestProfile(user_id)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -310,6 +330,16 @@ export default class PeerClient {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Object} req
|
||||||
|
* @param {string} req.mxid
|
||||||
|
*/
|
||||||
|
handleStop = async (req) => {
|
||||||
|
this.#getUser(req.mxid).close()
|
||||||
|
this.userClients.delete(req.mxid)
|
||||||
|
return this.#voidCommandResult
|
||||||
|
}
|
||||||
|
|
||||||
#makeCommandResult(result) {
|
#makeCommandResult(result) {
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
|
@ -318,12 +348,9 @@ export default class PeerClient {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
#voidCommandResult = {
|
||||||
* @param {Object} req
|
success: true,
|
||||||
* @param {string} req.mxid
|
status: 0,
|
||||||
*/
|
|
||||||
handleStop = async (req) => {
|
|
||||||
this.#getUser(req.mxid).close()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleUnknownCommand = () => {
|
handleUnknownCommand = () => {
|
||||||
|
|
Loading…
Reference in New Issue