Inbound permissions & outbound power levels
Note that these only apply to OpenChannels
This commit is contained in:
parent
a9c7bfe046
commit
ecb04fc2f5
|
@ -22,7 +22,7 @@
|
||||||
* [x] Message redactions<sup>[1]</sup>
|
* [x] Message redactions<sup>[1]</sup>
|
||||||
* [ ] Message reactions
|
* [ ] Message reactions
|
||||||
* [x] Read receipts
|
* [x] Read receipts
|
||||||
* [ ] Power level
|
* [x] Power level
|
||||||
* [ ] Membership actions
|
* [ ] Membership actions
|
||||||
* [ ] Invite
|
* [ ] Invite
|
||||||
* [ ] Kick
|
* [ ] Kick
|
||||||
|
@ -52,7 +52,7 @@
|
||||||
* [ ] Read receipts
|
* [ ] Read receipts
|
||||||
* [ ] On backfill
|
* [ ] On backfill
|
||||||
* [x] On live event
|
* [x] On live event
|
||||||
* [ ] Admin status
|
* [x] Admin status
|
||||||
* [x] Membership actions
|
* [x] Membership actions
|
||||||
* [x] Add member
|
* [x] Add member
|
||||||
* [x] Remove member
|
* [x] Remove member
|
||||||
|
|
|
@ -46,6 +46,7 @@ from ..types.chat import Chatlog, KnownChatType
|
||||||
from ..types.chat.attachment import MentionStruct, ReplyAttachment
|
from ..types.chat.attachment import MentionStruct, ReplyAttachment
|
||||||
from ..types.client.client_session import LoginResult
|
from ..types.client.client_session import LoginResult
|
||||||
from ..types.oauth import OAuthCredential, OAuthInfo
|
from ..types.oauth import OAuthCredential, OAuthInfo
|
||||||
|
from ..types.openlink.open_link_type import OpenChannelUserPerm
|
||||||
from ..types.openlink.open_link_user_info import OpenLinkChannelUserInfo
|
from ..types.openlink.open_link_user_info import OpenLinkChannelUserInfo
|
||||||
from ..types.packet.chat.kickout import KnownKickoutType, KickoutRes
|
from ..types.packet.chat.kickout import KnownKickoutType, KickoutRes
|
||||||
from ..types.request import (
|
from ..types.request import (
|
||||||
|
@ -384,6 +385,19 @@ class Client:
|
||||||
read_until_chat_id=read_until_chat_id.serialize(),
|
read_until_chat_id=read_until_chat_id.serialize(),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
async def send_perm(
|
||||||
|
self,
|
||||||
|
channel_props: ChannelProps,
|
||||||
|
user_id: Long,
|
||||||
|
perm: OpenChannelUserPerm,
|
||||||
|
) -> None:
|
||||||
|
return await self._api_user_request_void(
|
||||||
|
"send_perm",
|
||||||
|
channel_props=channel_props.serialize(),
|
||||||
|
user_id=user_id.serialize(),
|
||||||
|
perm=perm,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
# TODO Combine these into one
|
# TODO Combine these into one
|
||||||
|
|
||||||
|
@ -445,6 +459,14 @@ class Client:
|
||||||
OpenLinkChannelUserInfo.deserialize(data["info"]),
|
OpenLinkChannelUserInfo.deserialize(data["info"]),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
async def _on_perm_changed(self, data: dict[str, JSON]) -> None:
|
||||||
|
await self.user.on_perm_changed(
|
||||||
|
Long.deserialize(data["userId"]),
|
||||||
|
OpenChannelUserPerm(data["perm"]),
|
||||||
|
Long.deserialize(data["channelId"]),
|
||||||
|
str(data["channelType"]),
|
||||||
|
)
|
||||||
|
|
||||||
async def _on_channel_join(self, data: dict[str, JSON]) -> None:
|
async def _on_channel_join(self, data: dict[str, JSON]) -> None:
|
||||||
await self.user.on_channel_join(
|
await self.user.on_channel_join(
|
||||||
ChannelInfo.deserialize(data["channelInfo"]),
|
ChannelInfo.deserialize(data["channelInfo"]),
|
||||||
|
@ -504,6 +526,7 @@ class Client:
|
||||||
self._add_event_handler("chat_deleted", self._on_chat_deleted)
|
self._add_event_handler("chat_deleted", self._on_chat_deleted)
|
||||||
self._add_event_handler("chat_read", self._on_chat_read)
|
self._add_event_handler("chat_read", self._on_chat_read)
|
||||||
self._add_event_handler("profile_changed", self._on_profile_changed)
|
self._add_event_handler("profile_changed", self._on_profile_changed)
|
||||||
|
self._add_event_handler("perm_changed", self._on_perm_changed)
|
||||||
self._add_event_handler("channel_join", self._on_channel_join)
|
self._add_event_handler("channel_join", self._on_channel_join)
|
||||||
self._add_event_handler("channel_left", self._on_channel_left)
|
self._add_event_handler("channel_left", self._on_channel_left)
|
||||||
self._add_event_handler("channel_kicked", self._on_channel_kicked)
|
self._add_event_handler("channel_kicked", self._on_channel_kicked)
|
||||||
|
|
|
@ -32,6 +32,7 @@ from ..types.channel.channel_info import NormalChannelInfo
|
||||||
from ..types.channel.channel_type import ChannelType
|
from ..types.channel.channel_type import ChannelType
|
||||||
from ..types.chat import KnownChatType
|
from ..types.chat import KnownChatType
|
||||||
from ..types.openlink.open_channel_info import OpenChannelInfo
|
from ..types.openlink.open_channel_info import OpenChannelInfo
|
||||||
|
from ..types.openlink.open_link_type import OpenChannelUserPerm
|
||||||
from ..types.user.channel_user_info import NormalChannelUserInfo, OpenChannelUserInfo
|
from ..types.user.channel_user_info import NormalChannelUserInfo, OpenChannelUserInfo
|
||||||
|
|
||||||
|
|
||||||
|
@ -91,3 +92,25 @@ TO_MSGTYPE_MAP: dict[MessageType, KnownChatType] = {
|
||||||
|
|
||||||
# https://stackoverflow.com/a/483833
|
# https://stackoverflow.com/a/483833
|
||||||
FROM_MSGTYPE_MAP: dict[KnownChatType, MessageType] = {v: k for k, v in TO_MSGTYPE_MAP.items()}
|
FROM_MSGTYPE_MAP: dict[KnownChatType, MessageType] = {v: k for k, v in TO_MSGTYPE_MAP.items()}
|
||||||
|
|
||||||
|
|
||||||
|
# TODO Consider allowing custom power/perm mappings
|
||||||
|
|
||||||
|
FROM_PERM_MAP: dict[OpenChannelUserPerm, int] = {
|
||||||
|
OpenChannelUserPerm.OWNER: 100,
|
||||||
|
OpenChannelUserPerm.MANAGER: 50,
|
||||||
|
# TODO Decide on an appropriate value for this
|
||||||
|
#OpenChannelUserPerm.BOT: 101,
|
||||||
|
# NOTE Intentionally skipping OpenChannelUserPerm.NONE
|
||||||
|
}
|
||||||
|
|
||||||
|
# NOTE Using a class to make it look like a dict
|
||||||
|
class TO_PERM_MAP:
|
||||||
|
@staticmethod
|
||||||
|
def get(key: int, default: Optional[OpenChannelUserPerm] = None) -> OpenChannelUserPerm:
|
||||||
|
if key >= 100:
|
||||||
|
return OpenChannelUserPerm.OWNER
|
||||||
|
elif key >= 50:
|
||||||
|
return OpenChannelUserPerm.MANAGER
|
||||||
|
else:
|
||||||
|
return default or OpenChannelUserPerm.NONE
|
||||||
|
|
|
@ -26,10 +26,15 @@ from mautrix.types import (
|
||||||
RedactionEvent,
|
RedactionEvent,
|
||||||
RoomID,
|
RoomID,
|
||||||
SingleReceiptEventContent,
|
SingleReceiptEventContent,
|
||||||
|
PowerLevelStateEventContent,
|
||||||
|
StateEvent,
|
||||||
UserID,
|
UserID,
|
||||||
)
|
)
|
||||||
|
|
||||||
from . import portal as po, user as u
|
from .kt.client.errors import CommandException
|
||||||
|
from .kt.client.types import TO_PERM_MAP
|
||||||
|
|
||||||
|
from . import portal as po, puppet as pu, user as u
|
||||||
from .db import Message as DBMessage
|
from .db import Message as DBMessage
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
|
@ -135,7 +140,7 @@ class MatrixHandler(BaseMatrixHandler):
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
user = await u.User.get_by_mxid(user_id)
|
user = await u.User.get_by_mxid(user_id)
|
||||||
if not user:
|
if not user or not user.is_connected:
|
||||||
return
|
return
|
||||||
|
|
||||||
portal = await po.Portal.get_by_mxid(room_id)
|
portal = await po.Portal.get_by_mxid(room_id)
|
||||||
|
@ -160,6 +165,39 @@ class MatrixHandler(BaseMatrixHandler):
|
||||||
if message:
|
if message:
|
||||||
await user.client.mark_read(portal.channel_props, message.ktid)
|
await user.client.mark_read(portal.channel_props, message.ktid)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
async def handle_power_level(
|
||||||
|
cls,
|
||||||
|
room_id: RoomID,
|
||||||
|
user_id: UserID,
|
||||||
|
prev_content: PowerLevelStateEventContent,
|
||||||
|
content: PowerLevelStateEventContent,
|
||||||
|
) -> None:
|
||||||
|
user = await u.User.get_by_mxid(user_id)
|
||||||
|
if not user or not user.is_connected:
|
||||||
|
return
|
||||||
|
|
||||||
|
portal = await po.Portal.get_by_mxid(room_id)
|
||||||
|
if not portal:
|
||||||
|
return
|
||||||
|
|
||||||
|
for target_mxid, power_level in content.users.items():
|
||||||
|
if power_level == prev_content.get_user_level(target_mxid):
|
||||||
|
continue
|
||||||
|
puppet = await pu.Puppet.get_by_mxid(target_mxid)
|
||||||
|
if puppet:
|
||||||
|
perm = TO_PERM_MAP.get(power_level)
|
||||||
|
try:
|
||||||
|
await user.client.send_perm(portal.channel_props, puppet.ktid, perm)
|
||||||
|
except CommandException:
|
||||||
|
cls.log.exception(
|
||||||
|
"Failed to handle power level change (%d->%d) for puppet user %s, so changing it back",
|
||||||
|
prev_content.get_user_level(target_mxid),
|
||||||
|
power_level,
|
||||||
|
target_mxid,
|
||||||
|
)
|
||||||
|
await portal.main_intent.set_power_levels(room_id, prev_content)
|
||||||
|
|
||||||
async def handle_ephemeral_event(
|
async def handle_ephemeral_event(
|
||||||
self, evt: ReceiptEvent | Event
|
self, evt: ReceiptEvent | Event
|
||||||
) -> None:
|
) -> None:
|
||||||
|
@ -175,3 +213,7 @@ class MatrixHandler(BaseMatrixHandler):
|
||||||
evt: ReactionEvent
|
evt: ReactionEvent
|
||||||
await self.handle_reaction(evt.room_id, evt.sender, evt.event_id, evt.content)
|
await self.handle_reaction(evt.room_id, evt.sender, evt.event_id, evt.content)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
async def handle_state_event(self, evt: StateEvent) -> None:
|
||||||
|
if evt.type == EventType.ROOM_POWER_LEVELS:
|
||||||
|
await self.handle_power_level(evt.room_id, evt.sender, evt.prev_content, evt.content)
|
||||||
|
|
|
@ -48,6 +48,7 @@ from mautrix.types import (
|
||||||
Membership,
|
Membership,
|
||||||
MessageEventContent,
|
MessageEventContent,
|
||||||
MessageType,
|
MessageType,
|
||||||
|
PowerLevelStateEventContent,
|
||||||
RelationType,
|
RelationType,
|
||||||
RoomID,
|
RoomID,
|
||||||
TextMessageEventContent,
|
TextMessageEventContent,
|
||||||
|
@ -80,12 +81,15 @@ from .kt.types.chat.attachment import (
|
||||||
ReplyAttachment,
|
ReplyAttachment,
|
||||||
VideoAttachment,
|
VideoAttachment,
|
||||||
)
|
)
|
||||||
|
from .kt.types.user.channel_user_info import OpenChannelUserInfo
|
||||||
|
from .kt.types.openlink.open_link_type import OpenChannelUserPerm
|
||||||
|
|
||||||
from .kt.client.types import (
|
from .kt.client.types import (
|
||||||
UserInfoUnion,
|
UserInfoUnion,
|
||||||
PortalChannelInfo,
|
PortalChannelInfo,
|
||||||
ChannelProps,
|
ChannelProps,
|
||||||
TO_MSGTYPE_MAP,
|
TO_MSGTYPE_MAP,
|
||||||
|
FROM_PERM_MAP,
|
||||||
)
|
)
|
||||||
from .kt.client.errors import CommandException
|
from .kt.client.errors import CommandException
|
||||||
|
|
||||||
|
@ -234,6 +238,10 @@ class Portal(DBPortal, BasePortal):
|
||||||
def is_direct(self) -> bool:
|
def is_direct(self) -> bool:
|
||||||
return KnownChannelType.is_direct(self.kt_type)
|
return KnownChannelType.is_direct(self.kt_type)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_open(self) -> bool:
|
||||||
|
return KnownChannelType.is_open(self.kt_type)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def kt_sender(self) -> int | None:
|
def kt_sender(self) -> int | None:
|
||||||
if self.is_direct:
|
if self.is_direct:
|
||||||
|
@ -317,8 +325,48 @@ class Portal(DBPortal, BasePortal):
|
||||||
if changed or force_save:
|
if changed or force_save:
|
||||||
await self.update_bridge_info()
|
await self.update_bridge_info()
|
||||||
await self.save()
|
await self.save()
|
||||||
|
if self.mxid and self.is_open:
|
||||||
|
user_power_levels = await self._get_mapped_participant_power_levels(info.participants, skip_default=False)
|
||||||
|
await self.set_power_levels(user_power_levels)
|
||||||
return info
|
return info
|
||||||
|
|
||||||
|
async def _get_mapped_participant_power_levels(self, participants: list[UserInfoUnion], skip_default: bool) -> dict[UserID, int]:
|
||||||
|
user_power_levels: dict[UserID, int] = {}
|
||||||
|
default_value = None if skip_default else 0
|
||||||
|
for participant in participants:
|
||||||
|
if not isinstance(participant, OpenChannelUserInfo):
|
||||||
|
self.log.warning(f"Info object for participant {participant.userId} of open channel is not an OpenChannelUserInfo")
|
||||||
|
continue
|
||||||
|
power_level = FROM_PERM_MAP.get(participant.perm, default_value)
|
||||||
|
if power_level is None:
|
||||||
|
continue
|
||||||
|
await self.update_mapped_ktid_power_levels(user_power_levels, participant.userId, power_level)
|
||||||
|
return user_power_levels
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
async def update_mapped_ktid_power_levels(user_power_levels: dict[UserID, int], ktid: int, power_level: int) -> None:
|
||||||
|
user = await u.User.get_by_ktid(ktid)
|
||||||
|
if user:
|
||||||
|
user_power_levels[user.mxid] = power_level
|
||||||
|
puppet = await p.Puppet.get_by_ktid(ktid)
|
||||||
|
if puppet:
|
||||||
|
user_power_levels[puppet.mxid] = power_level
|
||||||
|
|
||||||
|
async def set_power_levels(self, user_power_levels: dict[UserID, int]) -> None:
|
||||||
|
if self.mxid and user_power_levels:
|
||||||
|
changed = False
|
||||||
|
power_levels = await self.main_intent.get_power_levels(self.mxid)
|
||||||
|
for user, power_level in user_power_levels.items():
|
||||||
|
changed = power_levels.ensure_user_level(user, power_level) or changed
|
||||||
|
if changed:
|
||||||
|
await self.main_intent.set_power_levels(self.mxid, power_levels)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
async def get_mapped_ktid_power_levels(ktid: int, power_level: int) -> dict[UserID, int]:
|
||||||
|
user_power_levels: dict[UserID, int] = {}
|
||||||
|
await Portal.update_mapped_ktid_power_levels(user_power_levels, ktid, power_level)
|
||||||
|
return user_power_levels
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
async def _reupload_kakaotalk_file(
|
async def _reupload_kakaotalk_file(
|
||||||
cls,
|
cls,
|
||||||
|
@ -632,7 +680,8 @@ class Portal(DBPortal, BasePortal):
|
||||||
"content": self.bridge_info,
|
"content": self.bridge_info,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
if KnownChannelType.is_open(info.channel_info.type):
|
|
||||||
|
if self.is_open:
|
||||||
initial_state.extend((
|
initial_state.extend((
|
||||||
{
|
{
|
||||||
"type": str(EventType.ROOM_JOIN_RULES),
|
"type": str(EventType.ROOM_JOIN_RULES),
|
||||||
|
@ -643,6 +692,15 @@ class Portal(DBPortal, BasePortal):
|
||||||
"content": {"guest_access": "forbidden"},
|
"content": {"guest_access": "forbidden"},
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
|
user_power_levels = await self._get_mapped_participant_power_levels(info.participants, skip_default=True)
|
||||||
|
user_power_levels[self.main_intent.mxid] = 1 + FROM_PERM_MAP.get(OpenChannelUserPerm.OWNER)
|
||||||
|
initial_state.append(
|
||||||
|
{
|
||||||
|
"type": str(EventType.ROOM_POWER_LEVELS),
|
||||||
|
"content": PowerLevelStateEventContent(users=user_power_levels).serialize()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
invites = []
|
invites = []
|
||||||
if self.config["bridge.encryption.default"] and self.matrix.e2ee:
|
if self.config["bridge.encryption.default"] and self.matrix.e2ee:
|
||||||
self.encrypted = True
|
self.encrypted = True
|
||||||
|
|
|
@ -39,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.client.types import SettingsStruct
|
from .kt.client.types import SettingsStruct, FROM_PERM_MAP
|
||||||
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
|
||||||
|
@ -47,6 +47,7 @@ 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
|
||||||
from .kt.types.openlink.open_channel_info import OpenChannelData, OpenChannelInfo
|
from .kt.types.openlink.open_channel_info import OpenChannelData, OpenChannelInfo
|
||||||
|
from .kt.types.openlink.open_link_type import OpenChannelUserPerm
|
||||||
from .kt.types.openlink.open_link_user_info import OpenLinkChannelUserInfo
|
from .kt.types.openlink.open_link_user_info import OpenLinkChannelUserInfo
|
||||||
from .kt.types.packet.chat.kickout import KnownKickoutType, KickoutRes
|
from .kt.types.packet.chat.kickout import KnownKickoutType, KickoutRes
|
||||||
|
|
||||||
|
@ -55,6 +56,7 @@ METRIC_CHAT = Summary("bridge_on_chat", "calls to on_chat")
|
||||||
METRIC_CHAT_DELETED = Summary("bridge_on_chat_deleted", "calls to on_chat_deleted")
|
METRIC_CHAT_DELETED = Summary("bridge_on_chat_deleted", "calls to on_chat_deleted")
|
||||||
METRIC_CHAT_READ = Summary("bridge_on_chat_read", "calls to on_chat_read")
|
METRIC_CHAT_READ = Summary("bridge_on_chat_read", "calls to on_chat_read")
|
||||||
METRIC_PROFILE_CHANGE = Summary("bridge_on_profile_changed", "calls to on_profile_changed")
|
METRIC_PROFILE_CHANGE = Summary("bridge_on_profile_changed", "calls to on_profile_changed")
|
||||||
|
METRIC_PERM_CHANGE = Summary("bridge_on_perm_changed", "calls to on_perm_changed")
|
||||||
METRIC_CHANNEL_JOIN = Summary("bridge_on_channel_join", "calls to on_channel_join")
|
METRIC_CHANNEL_JOIN = Summary("bridge_on_channel_join", "calls to on_channel_join")
|
||||||
METRIC_CHANNEL_LEFT = Summary("bridge_on_channel_left", "calls to on_channel_left")
|
METRIC_CHANNEL_LEFT = Summary("bridge_on_channel_left", "calls to on_channel_left")
|
||||||
METRIC_CHANNEL_KICKED = Summary("bridge_on_channel_kicked", "calls to on_channel_kicked")
|
METRIC_CHANNEL_KICKED = Summary("bridge_on_channel_kicked", "calls to on_channel_kicked")
|
||||||
|
@ -745,6 +747,25 @@ class User(DBUser, BaseUser):
|
||||||
if puppet:
|
if puppet:
|
||||||
await puppet.update_info_from_participant(self, info)
|
await puppet.update_info_from_participant(self, info)
|
||||||
|
|
||||||
|
@async_time(METRIC_PERM_CHANGE)
|
||||||
|
async def on_perm_changed(
|
||||||
|
self,
|
||||||
|
user_id: Long,
|
||||||
|
perm: OpenChannelUserPerm,
|
||||||
|
channel_id: Long,
|
||||||
|
channel_type: ChannelType,
|
||||||
|
) -> None:
|
||||||
|
portal = await po.Portal.get_by_ktid(
|
||||||
|
channel_id,
|
||||||
|
kt_receiver=self.ktid,
|
||||||
|
kt_type=channel_type,
|
||||||
|
create=False,
|
||||||
|
)
|
||||||
|
if portal and portal.mxid:
|
||||||
|
power_level = FROM_PERM_MAP.get(perm, 0)
|
||||||
|
user_power_levels = await po.Portal.get_mapped_ktid_power_levels(user_id, power_level)
|
||||||
|
await portal.set_power_levels(user_power_levels)
|
||||||
|
|
||||||
@async_time(METRIC_CHANNEL_JOIN)
|
@async_time(METRIC_CHANNEL_JOIN)
|
||||||
def on_channel_join(self, channel_info: ChannelInfo) -> Awaitable[None]:
|
def on_channel_join(self, channel_info: ChannelInfo) -> Awaitable[None]:
|
||||||
return self._sync_channel(channel_info)
|
return self._sync_channel(channel_info)
|
||||||
|
|
|
@ -27,6 +27,7 @@ import {
|
||||||
/** @typedef {import("node-kakao").ChannelType} ChannelType */
|
/** @typedef {import("node-kakao").ChannelType} ChannelType */
|
||||||
/** @typedef {import("node-kakao").ReplyAttachment} ReplyAttachment */
|
/** @typedef {import("node-kakao").ReplyAttachment} ReplyAttachment */
|
||||||
/** @typedef {import("node-kakao").MentionStruct} MentionStruct */
|
/** @typedef {import("node-kakao").MentionStruct} MentionStruct */
|
||||||
|
/** @typedef {import("node-kakao").OpenChannelUserPerm} OpenChannelUserPerm */
|
||||||
/** @typedef {import("node-kakao/dist/talk").TalkChannelList} TalkChannelList */
|
/** @typedef {import("node-kakao/dist/talk").TalkChannelList} TalkChannelList */
|
||||||
|
|
||||||
import chat from "node-kakao/chat"
|
import chat from "node-kakao/chat"
|
||||||
|
@ -135,6 +136,18 @@ class UserClient {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
this.#talkClient.on("perm_changed", (channel, lastInfo, user) => {
|
||||||
|
// TODO Fix the type hint on lastInfo and user: they should each be a OpenChannelUserInfo, not just a ChannelUserInfo
|
||||||
|
this.log(`Perms of user ${user.userId} in channel ${channel.channelId} changed from ${lastInfo.perm} to ${user.perm}`)
|
||||||
|
return this.write("perm_changed", {
|
||||||
|
is_sequential: true,
|
||||||
|
userId: user.userId,
|
||||||
|
perm: user.perm,
|
||||||
|
channelId: channel.channelId,
|
||||||
|
channelType: channel.info.type,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
this.#talkClient.on("channel_join", channel => {
|
this.#talkClient.on("channel_join", channel => {
|
||||||
this.log(`Joined channel ${channel.channelId}`)
|
this.log(`Joined channel ${channel.channelId}`)
|
||||||
return this.write("channel_join", {
|
return this.write("channel_join", {
|
||||||
|
@ -695,6 +708,21 @@ export default class PeerClient {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Object} req
|
||||||
|
* @param {string} req.mxid
|
||||||
|
* @param {ChannelProps} req.channel_props
|
||||||
|
* @param {Long} req.user_id
|
||||||
|
* @param {OpenChannelUserPerm} req.perm
|
||||||
|
*/
|
||||||
|
sendPerm = async (req) => {
|
||||||
|
if (!isChannelTypeOpen(req.channel_props.type)) {
|
||||||
|
throw Error("Can't send perm on non-open channel")
|
||||||
|
}
|
||||||
|
const talkChannel = await this.#getUserChannel(req.mxid, req.channel_props)
|
||||||
|
return await talkChannel.setUserPerm({ userId: req.user_id }, req.perm)
|
||||||
|
}
|
||||||
|
|
||||||
handleUnknownCommand = () => {
|
handleUnknownCommand = () => {
|
||||||
throw new Error("Unknown command")
|
throw new Error("Unknown command")
|
||||||
}
|
}
|
||||||
|
@ -773,6 +801,7 @@ export default class PeerClient {
|
||||||
send_media: this.sendMedia,
|
send_media: this.sendMedia,
|
||||||
delete_chat: this.deleteChat,
|
delete_chat: this.deleteChat,
|
||||||
mark_read: this.markRead,
|
mark_read: this.markRead,
|
||||||
|
send_perm: this.sendPerm,
|
||||||
}[req.command] || this.handleUnknownCommand
|
}[req.command] || this.handleUnknownCommand
|
||||||
}
|
}
|
||||||
const resp = { id: req.id }
|
const resp = { id: req.id }
|
||||||
|
|
Loading…
Reference in New Issue