Use More/LessSettings instead of profile on login; add whoami command
This commit is contained in:
parent
370865c2c1
commit
9a33f3dcf2
|
@ -14,6 +14,7 @@
|
||||||
# You should have received a copy of the GNU Affero General Public License
|
# 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/>.
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
from mautrix.bridge.commands import HelpSection, command_handler
|
from mautrix.bridge.commands import HelpSection, command_handler
|
||||||
|
from mautrix.types import SerializerError
|
||||||
|
|
||||||
from .typehint import CommandEvent
|
from .typehint import CommandEvent
|
||||||
|
|
||||||
|
@ -53,24 +54,36 @@ async def disconnect(evt: CommandEvent) -> None:
|
||||||
needs_auth=True,
|
needs_auth=True,
|
||||||
management_only=True,
|
management_only=True,
|
||||||
help_section=SECTION_CONNECTION,
|
help_section=SECTION_CONNECTION,
|
||||||
help_text="Check if you're logged into KakaoTalk & connected to chats",
|
help_text="Check if you're logged into KakaoTalk and retrieve your account information",
|
||||||
)
|
)
|
||||||
async def ping(evt: CommandEvent) -> None:
|
async def whoami(evt: CommandEvent) -> None:
|
||||||
if not await evt.sender.is_logged_in():
|
|
||||||
await evt.reply("You're not logged into KakaoTalk")
|
|
||||||
return
|
|
||||||
await evt.mark_read()
|
await evt.mark_read()
|
||||||
try:
|
try:
|
||||||
own_info = await evt.sender.get_own_info()
|
own_info = await evt.sender.get_own_info()
|
||||||
await evt.reply(
|
await evt.reply(
|
||||||
f"You're logged in as {own_info.nickname} (user ID {evt.sender.ktid})."
|
f"You're logged in as `{own_info.more.uuid}` (nickname: {own_info.more.nickName}, user ID: {evt.sender.ktid})."
|
||||||
"\n\n"
|
)
|
||||||
f"You are {'connected to' if evt.sender.is_connected else '**disconnected** from'} KakaoTalk chats.\n\n"
|
except SerializerError:
|
||||||
|
evt.sender.log.exception("Failed to deserialize settings struct")
|
||||||
|
await evt.reply(
|
||||||
|
f"You're logged in, but the bridge is unable to retrieve your profile information (user ID: {evt.sender.ktid})."
|
||||||
)
|
)
|
||||||
except CommandException as e:
|
except CommandException as e:
|
||||||
await evt.reply(f"Error from KakaoTalk: {e}")
|
await evt.reply(f"Error from KakaoTalk: {e}")
|
||||||
|
|
||||||
|
|
||||||
|
@command_handler(
|
||||||
|
needs_auth=True,
|
||||||
|
management_only=True,
|
||||||
|
help_section=SECTION_CONNECTION,
|
||||||
|
help_text="Check if you're connected to KakaoTalk chats",
|
||||||
|
)
|
||||||
|
async def ping(evt: CommandEvent) -> None:
|
||||||
|
await evt.reply(
|
||||||
|
f"You are {'connected to' if evt.sender.is_connected else '**disconnected** from'} KakaoTalk chats."
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@command_handler(
|
@command_handler(
|
||||||
needs_auth=True,
|
needs_auth=True,
|
||||||
management_only=True,
|
management_only=True,
|
||||||
|
|
|
@ -32,6 +32,7 @@ from aiohttp import ClientSession
|
||||||
from aiohttp.client import _RequestContextManager
|
from aiohttp.client import _RequestContextManager
|
||||||
from yarl import URL
|
from yarl import URL
|
||||||
|
|
||||||
|
from mautrix.types import SerializerError
|
||||||
from mautrix.util.logging import TraceLogger
|
from mautrix.util.logging import TraceLogger
|
||||||
|
|
||||||
from ...config import Config
|
from ...config import Config
|
||||||
|
@ -55,7 +56,12 @@ from ..types.request import (
|
||||||
CommandResultDoneValue
|
CommandResultDoneValue
|
||||||
)
|
)
|
||||||
|
|
||||||
from .types import PortalChannelInfo, UserInfoUnion, ChannelProps
|
from .types import (
|
||||||
|
ChannelProps,
|
||||||
|
PortalChannelInfo,
|
||||||
|
SettingsStruct,
|
||||||
|
UserInfoUnion,
|
||||||
|
)
|
||||||
|
|
||||||
from .errors import InvalidAccessToken, CommandException
|
from .errors import InvalidAccessToken, CommandException
|
||||||
from .error_helper import raise_unsuccessful_response
|
from .error_helper import raise_unsuccessful_response
|
||||||
|
@ -197,17 +203,21 @@ class Client:
|
||||||
|
|
||||||
# region post-token commands
|
# region post-token commands
|
||||||
|
|
||||||
async def start(self) -> ProfileStruct:
|
async def start(self) -> SettingsStruct | None:
|
||||||
"""
|
"""
|
||||||
Initialize user-specific bridging & state by providing a token obtained from a prior login.
|
Initialize user-specific bridging & state by providing a token obtained from a prior login.
|
||||||
Receive the user's profile info in response.
|
Receive the user's profile info in response.
|
||||||
"""
|
"""
|
||||||
profile_req_struct = await self._api_user_request_result(ProfileReqStruct, "start")
|
try:
|
||||||
|
settings_struct = await self._api_user_request_result(SettingsStruct, "start")
|
||||||
|
except SerializerError:
|
||||||
|
self.log.exception("Unable to deserialize settings struct, but starting client anyways")
|
||||||
|
settings_struct = None
|
||||||
if not self._rpc_disconnection_task:
|
if not self._rpc_disconnection_task:
|
||||||
self._rpc_disconnection_task = asyncio.create_task(self._rpc_disconnection_handler())
|
self._rpc_disconnection_task = asyncio.create_task(self._rpc_disconnection_handler())
|
||||||
else:
|
else:
|
||||||
self.log.warning("Called \"start\" on an already-started client")
|
self.log.warning("Called \"start\" on an already-started client")
|
||||||
return profile_req_struct.profile
|
return settings_struct
|
||||||
|
|
||||||
async def stop(self) -> None:
|
async def stop(self) -> None:
|
||||||
"""Immediately stop bridging this user."""
|
"""Immediately stop bridging this user."""
|
||||||
|
@ -246,6 +256,9 @@ class Client:
|
||||||
await self._rpc_client.request("disconnect", mxid=self.user.mxid)
|
await self._rpc_client.request("disconnect", mxid=self.user.mxid)
|
||||||
await self._on_disconnect(None)
|
await self._on_disconnect(None)
|
||||||
|
|
||||||
|
async def get_settings(self) -> SettingsStruct:
|
||||||
|
return await self._api_user_request_result(SettingsStruct, "get_settings")
|
||||||
|
|
||||||
async def get_own_profile(self) -> ProfileStruct:
|
async def get_own_profile(self) -> ProfileStruct:
|
||||||
profile_req_struct = await self._api_user_request_result(ProfileReqStruct, "get_own_profile")
|
profile_req_struct = await self._api_user_request_result(ProfileReqStruct, "get_own_profile")
|
||||||
return profile_req_struct.profile
|
return profile_req_struct.profile
|
||||||
|
|
|
@ -26,6 +26,7 @@ from mautrix.types import (
|
||||||
MessageType,
|
MessageType,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
from ..types.api.struct import MoreSettingsStruct, LessSettingsStruct
|
||||||
from ..types.bson import Long
|
from ..types.bson import Long
|
||||||
from ..types.channel.channel_info import NormalChannelInfo
|
from ..types.channel.channel_info import NormalChannelInfo
|
||||||
from ..types.channel.channel_type import ChannelType
|
from ..types.channel.channel_type import ChannelType
|
||||||
|
@ -34,6 +35,12 @@ from ..types.openlink.open_channel_info import OpenChannelInfo
|
||||||
from ..types.user.channel_user_info import NormalChannelUserInfo, OpenChannelUserInfo
|
from ..types.user.channel_user_info import NormalChannelUserInfo, OpenChannelUserInfo
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class SettingsStruct(SerializableAttrs):
|
||||||
|
more: MoreSettingsStruct
|
||||||
|
less: LessSettingsStruct
|
||||||
|
|
||||||
|
|
||||||
ChannelInfoUnion = NewType("ChannelInfoUnion", Union[NormalChannelInfo, OpenChannelInfo])
|
ChannelInfoUnion = NewType("ChannelInfoUnion", Union[NormalChannelInfo, OpenChannelInfo])
|
||||||
|
|
||||||
@deserializer(ChannelInfoUnion)
|
@deserializer(ChannelInfoUnion)
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
# You should have received a copy of the GNU Affero General Public License
|
# 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/>.
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
##from .login import *
|
##from .login import *
|
||||||
##from .account import *
|
from .account import *
|
||||||
from .profile import *
|
from .profile import *
|
||||||
from .friends import *
|
from .friends import *
|
||||||
##from .openlink import *
|
##from .openlink import *
|
||||||
|
|
|
@ -0,0 +1,141 @@
|
||||||
|
# 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 typing import Optional, Union
|
||||||
|
|
||||||
|
from attr import dataclass
|
||||||
|
|
||||||
|
from mautrix.types import SerializableAttrs
|
||||||
|
|
||||||
|
from ...bson import Long
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class OpenChatSettingsStruct(SerializableAttrs):
|
||||||
|
chatMemberMaxJoin: int
|
||||||
|
chatRoomMaxJoin: int
|
||||||
|
createLinkLimit: 10;
|
||||||
|
createCardLinkLimit: 3;
|
||||||
|
numOfStaffLimit: 5;
|
||||||
|
rewritable: bool
|
||||||
|
handoverEnabled: bool
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(kw_only=True)
|
||||||
|
class MoreSettingsStruct(SerializableAttrs):
|
||||||
|
since: int
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class ClientConf(SerializableAttrs):
|
||||||
|
osVersion: str
|
||||||
|
clientConf: ClientConf
|
||||||
|
|
||||||
|
available: int
|
||||||
|
available2: int
|
||||||
|
friendsPollingInterval: Optional[int] = None # NOTE Made optional
|
||||||
|
settingsPollingInterval: Optional[int] = None # NOTE Made optional
|
||||||
|
profilePollingInterval: Optional[int] = None # NOTE Made optional
|
||||||
|
moreListPollingInterval: Optional[int] = None # NOTE Made optional
|
||||||
|
morePayPollingInterval: Optional[int] = None # NOTE Made optional
|
||||||
|
daumMediaPollingInterval: Optional[int] = None # NOTE Made optional
|
||||||
|
lessSettingsPollingInterval: Optional[int] = None # NOTE Made optional
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class MoreApps(SerializableAttrs):
|
||||||
|
recommend: Optional[list[str]] = None # NOTE From unknown[]
|
||||||
|
all: Optional[list[str]] = None # NOTE From unknown[]
|
||||||
|
moreApps: MoreApps
|
||||||
|
|
||||||
|
shortcuts: Optional[dict[str, int]] = None # NOTE Made optional
|
||||||
|
seasonProfileRev: int
|
||||||
|
seasonNoticeRev: int
|
||||||
|
serviceUserId: Union[Long, int]
|
||||||
|
accountId: int
|
||||||
|
accountDisplayId: str
|
||||||
|
hashedAccountId: str
|
||||||
|
pstnNumber: str
|
||||||
|
formattedPstnNumber: str
|
||||||
|
nsnNumber: str
|
||||||
|
formattedNsnNumber: str
|
||||||
|
contactNameSync: int
|
||||||
|
allowMigration: bool
|
||||||
|
emailStatus: int
|
||||||
|
emailAddress: str
|
||||||
|
emailVerified: bool
|
||||||
|
uuid: str
|
||||||
|
uuidSearchable: bool
|
||||||
|
nickName: str
|
||||||
|
openChat: OpenChatSettingsStruct
|
||||||
|
profileImageUrl: str
|
||||||
|
fullProfileImageUrl: str
|
||||||
|
originalProfileImageUrl: str
|
||||||
|
statusMessage: str
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(kw_only=True)
|
||||||
|
class LessSettingsStruct(SerializableAttrs):
|
||||||
|
kakaoAutoLoginDomain: list[str]
|
||||||
|
daumSsoDomain: list[str]
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class GoogleMapsApi(SerializableAttrs):
|
||||||
|
key: str
|
||||||
|
signature: str
|
||||||
|
googleMapsApi: GoogleMapsApi
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class ChatReportLimit(SerializableAttrs):
|
||||||
|
chat: int
|
||||||
|
open_chat: int
|
||||||
|
plus_chat: int
|
||||||
|
chat_report_limit: ChatReportLimit
|
||||||
|
|
||||||
|
externalApiList: str # NOTE From unknown
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class BirthdayFriends(SerializableAttrs):
|
||||||
|
landing_url: str
|
||||||
|
birthday_friends: BirthdayFriends
|
||||||
|
|
||||||
|
messageDeleteTime: Optional[int] = None # NOTE Made optional
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class VoiceTalk(SerializableAttrs):
|
||||||
|
groupCallMaxParticipants: int
|
||||||
|
voiceTalk: VoiceTalk
|
||||||
|
|
||||||
|
profileActions: bool
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class PostExpirationSetting(SerializableAttrs):
|
||||||
|
flagOn: bool
|
||||||
|
newPostTerm: int
|
||||||
|
postExpirationSetting: PostExpirationSetting
|
||||||
|
|
||||||
|
kakaoAlertIds: list[int]
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class LoginTokenStruct(SerializableAttrs):
|
||||||
|
token: str
|
||||||
|
expires: int
|
||||||
|
|
||||||
|
|
||||||
|
___all___ = [
|
||||||
|
"OpenChatSettingsStruct",
|
||||||
|
"MoreSettingsStruct",
|
||||||
|
"LessSettingsStruct",
|
||||||
|
"LoginTokenStruct",
|
||||||
|
]
|
|
@ -25,6 +25,7 @@ from mautrix.types import (
|
||||||
JSON,
|
JSON,
|
||||||
MessageType,
|
MessageType,
|
||||||
RoomID,
|
RoomID,
|
||||||
|
SerializerError,
|
||||||
TextMessageEventContent,
|
TextMessageEventContent,
|
||||||
UserID,
|
UserID,
|
||||||
)
|
)
|
||||||
|
@ -38,7 +39,7 @@ from .db import User as DBUser
|
||||||
|
|
||||||
from .kt.client import Client
|
from .kt.client import Client
|
||||||
from .kt.client.errors import AuthenticationRequired, ResponseError
|
from .kt.client.errors import AuthenticationRequired, ResponseError
|
||||||
from .kt.types.api.struct.profile import ProfileStruct
|
from .kt.client.types import SettingsStruct
|
||||||
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, KnownChannelType
|
from .kt.types.channel.channel_type import ChannelType, KnownChannelType
|
||||||
|
@ -96,7 +97,7 @@ class User(DBUser, BaseUser):
|
||||||
_db_instance: DBUser | None
|
_db_instance: DBUser | None
|
||||||
_sync_lock: SimpleLock
|
_sync_lock: SimpleLock
|
||||||
_is_rpc_reconnecting: bool
|
_is_rpc_reconnecting: bool
|
||||||
_logged_in_info: ProfileStruct | None
|
_logged_in_info: SettingsStruct | None
|
||||||
_logged_in_info_time: float
|
_logged_in_info_time: float
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
|
@ -253,9 +254,9 @@ class User(DBUser, BaseUser):
|
||||||
self.log.warning(f"UUID mismatch: expected {self.uuid}, got {oauth_credential.deviceUUID}")
|
self.log.warning(f"UUID mismatch: expected {self.uuid}, got {oauth_credential.deviceUUID}")
|
||||||
self.uuid = oauth_credential.deviceUUID
|
self.uuid = oauth_credential.deviceUUID
|
||||||
|
|
||||||
async def get_own_info(self) -> ProfileStruct:
|
async def get_own_info(self) -> SettingsStruct:
|
||||||
if not self._logged_in_info or self._logged_in_info_time + 60 * 60 < time.monotonic():
|
if not self._logged_in_info or self._logged_in_info_time + 60 * 60 < time.monotonic():
|
||||||
self._logged_in_info = await self.client.get_own_profile()
|
self._logged_in_info = await self.client.get_settings()
|
||||||
self._logged_in_info_time = time.monotonic()
|
self._logged_in_info_time = time.monotonic()
|
||||||
return self._logged_in_info
|
return self._logged_in_info
|
||||||
|
|
||||||
|
@ -272,7 +273,7 @@ class User(DBUser, BaseUser):
|
||||||
return False
|
return False
|
||||||
client = Client(self, log=self.log.getChild("ktclient"))
|
client = Client(self, log=self.log.getChild("ktclient"))
|
||||||
user_info = await client.start()
|
user_info = await client.start()
|
||||||
# NOTE On failure, client.start throws instead of returning False
|
# NOTE On failure, client.start throws instead of returning something falsy
|
||||||
self.log.info("Loaded session successfully")
|
self.log.info("Loaded session successfully")
|
||||||
self.client = client
|
self.client = client
|
||||||
self._logged_in_info = user_info
|
self._logged_in_info = user_info
|
||||||
|
@ -303,6 +304,12 @@ class User(DBUser, BaseUser):
|
||||||
if self._is_logged_in is None or _override:
|
if self._is_logged_in is None or _override:
|
||||||
try:
|
try:
|
||||||
self._is_logged_in = bool(await self.get_own_info())
|
self._is_logged_in = bool(await self.get_own_info())
|
||||||
|
except SerializerError:
|
||||||
|
self.log.exception(
|
||||||
|
"Unable to deserialize settings struct, "
|
||||||
|
"but didn't get auth error, so treating user as logged in"
|
||||||
|
)
|
||||||
|
self._is_logged_in = True
|
||||||
except Exception:
|
except Exception:
|
||||||
self.log.exception("Exception checking login status")
|
self.log.exception("Exception checking login status")
|
||||||
self._is_logged_in = False
|
self._is_logged_in = False
|
||||||
|
|
|
@ -460,8 +460,7 @@ export default class PeerClient {
|
||||||
*/
|
*/
|
||||||
userStart = async (req) => {
|
userStart = async (req) => {
|
||||||
const userClient = this.#tryGetUser(req.mxid) || await UserClient.create(req.mxid, req.oauth_credential, this)
|
const userClient = this.#tryGetUser(req.mxid) || await UserClient.create(req.mxid, req.oauth_credential, this)
|
||||||
// TODO Should call requestMore/LessSettings instead
|
const res = await this.#getSettings(userClient.serviceClient)
|
||||||
const res = await userClient.serviceClient.requestMyProfile()
|
|
||||||
if (res.success) {
|
if (res.success) {
|
||||||
this.userClients.set(req.mxid, userClient)
|
this.userClients.set(req.mxid, userClient)
|
||||||
}
|
}
|
||||||
|
@ -496,7 +495,31 @@ export default class PeerClient {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {Object} req
|
* @param {Object} req
|
||||||
* @param {?string} req.mxid
|
* @param {string} req.mxid
|
||||||
|
*/
|
||||||
|
getSettings = async (req) => {
|
||||||
|
return await this.#getSettings(this.#getUser(req.mxid).serviceClient)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {ServiceApiClient} serviceClient
|
||||||
|
*/
|
||||||
|
#getSettings = async (serviceClient) => {
|
||||||
|
const moreRes = await serviceClient.requestMoreSettings()
|
||||||
|
if (!moreRes.success) return moreRes
|
||||||
|
|
||||||
|
const lessRes = await serviceClient.requestLessSettings()
|
||||||
|
if (!lessRes.success) return lessRes
|
||||||
|
|
||||||
|
return makeCommandResult({
|
||||||
|
more: moreRes.result,
|
||||||
|
less: lessRes.result,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Object} req
|
||||||
|
* @param {string} req.mxid
|
||||||
*/
|
*/
|
||||||
getOwnProfile = async (req) => {
|
getOwnProfile = async (req) => {
|
||||||
return await this.#getUser(req.mxid).serviceClient.requestMyProfile()
|
return await this.#getUser(req.mxid).serviceClient.requestMyProfile()
|
||||||
|
@ -504,7 +527,15 @@ export default class PeerClient {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {Object} req
|
* @param {Object} req
|
||||||
* @param {?string} req.mxid
|
* @param {string} req.mxid
|
||||||
|
*/
|
||||||
|
getOwnProfile = async (req) => {
|
||||||
|
return await this.#getUser(req.mxid).serviceClient.requestMyProfile()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Object} req
|
||||||
|
* @param {string} req.mxid
|
||||||
* @param {Long} req.user_id
|
* @param {Long} req.user_id
|
||||||
*/
|
*/
|
||||||
getProfile = async (req) => {
|
getProfile = async (req) => {
|
||||||
|
@ -522,7 +553,7 @@ export default class PeerClient {
|
||||||
const res = await talkChannel.updateAll()
|
const res = await talkChannel.updateAll()
|
||||||
if (!res.success) return res
|
if (!res.success) return res
|
||||||
|
|
||||||
return this.#makeCommandResult({
|
return makeCommandResult({
|
||||||
name: talkChannel.getDisplayName(),
|
name: talkChannel.getDisplayName(),
|
||||||
participants: Array.from(talkChannel.getAllUserInfo()),
|
participants: Array.from(talkChannel.getAllUserInfo()),
|
||||||
// TODO Image
|
// TODO Image
|
||||||
|
@ -574,7 +605,7 @@ export default class PeerClient {
|
||||||
const res = await this.#getUser(req.mxid).serviceClient.findFriendById(req.friend_id)
|
const res = await this.#getUser(req.mxid).serviceClient.findFriendById(req.friend_id)
|
||||||
if (!res.success) return res
|
if (!res.success) return res
|
||||||
|
|
||||||
return this.#makeCommandResult(res.result.friend[propertyName])
|
return makeCommandResult(res.result.friend[propertyName])
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -662,14 +693,6 @@ export default class PeerClient {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#makeCommandResult(result) {
|
|
||||||
return {
|
|
||||||
success: true,
|
|
||||||
status: 0,
|
|
||||||
result: result
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
handleUnknownCommand = () => {
|
handleUnknownCommand = () => {
|
||||||
throw new Error("Unknown command")
|
throw new Error("Unknown command")
|
||||||
}
|
}
|
||||||
|
@ -735,6 +758,7 @@ export default class PeerClient {
|
||||||
stop: this.userStop,
|
stop: this.userStop,
|
||||||
connect: this.handleConnect,
|
connect: this.handleConnect,
|
||||||
disconnect: this.handleDisconnect,
|
disconnect: this.handleDisconnect,
|
||||||
|
get_settings: this.getSettings,
|
||||||
get_own_profile: this.getOwnProfile,
|
get_own_profile: this.getOwnProfile,
|
||||||
get_profile: this.getProfile,
|
get_profile: this.getProfile,
|
||||||
get_portal_channel_info: this.getPortalChannelInfo,
|
get_portal_channel_info: this.getPortalChannelInfo,
|
||||||
|
@ -790,6 +814,17 @@ export default class PeerClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {object} result
|
||||||
|
*/
|
||||||
|
function makeCommandResult(result) {
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
status: 0,
|
||||||
|
result: result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {TalkChannelList} channelList
|
* @param {TalkChannelList} channelList
|
||||||
* @param {ChannelType} channelType
|
* @param {ChannelType} channelType
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
[Unit]
|
||||||
|
Description=Node backend for matrix-appservice-kakaotalk
|
||||||
|
After=multi-user.target network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
; User=matrix-appservice-kakaotalk
|
||||||
|
; Group=matrix-appservice-kakaotalk
|
||||||
|
Type=notify
|
||||||
|
NotifyAccess=all
|
||||||
|
WorkingDirectory=/opt/matrix-appservice-kakaotalk/node
|
||||||
|
ConfigurationDirectory=matrix-appservice-kakaotalk
|
||||||
|
RuntimeDirectory=matrix-appservice-kakaotalk
|
||||||
|
ExecStart=/usr/bin/env node src/main.js --config ${CONFIGURATION_DIRECTORY}/config.json
|
||||||
|
Restart=on-failure
|
||||||
|
RestartSec=3
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
|
@ -0,0 +1,16 @@
|
||||||
|
[Unit]
|
||||||
|
Description=matrix-appservice-kakaotalk bridge
|
||||||
|
After=multi-user.target network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
; User=matrix-appservice-kakaotalk
|
||||||
|
; Group=matrix-appservice-kakaotalk
|
||||||
|
WorkingDirectory=/opt/matrix-appservice-kakaotalk
|
||||||
|
ConfigurationDirectory=matrix-appservice-kakaotalk
|
||||||
|
RuntimeDirectory=matrix-appservice-kakaotalk
|
||||||
|
ExecStart=/opt/matrix-appservice-kakaotalk/.venv/bin/python -m matrix_appservice_kakaotalk -c ${CONFIGURATION_DIRECTORY}/config.yaml
|
||||||
|
Restart=on-failure
|
||||||
|
RestartSec=3
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
Loading…
Reference in New Issue