diff --git a/revolt/channel.py b/revolt/channel.py index d27bdc1..71ea06e 100755 --- a/revolt/channel.py +++ b/revolt/channel.py @@ -7,7 +7,7 @@ from revolt.utils import Missing from .asset import Asset from .enums import ChannelType from .messageable import Messageable -from .permissions import ChannelPermissions +from .permissions import Permissions, PermissionsOverwrite from .utils import Missing if TYPE_CHECKING: @@ -21,7 +21,9 @@ if TYPE_CHECKING: from .types import SavedMessages as SavedMessagesPayload from .types import TextChannel as TextChannelPayload from .types import VoiceChannel as VoiceChannelPayload - from .user import User + from .types import GuildChannel as GuildChannelPayload + from .types import File as FilePayload + from .types import Overwrite as OverwritePayload __all__ = ("DMChannel", "GroupDMChannel", "SavedMessageChannel", "TextChannel", "VoiceChannel", "Channel") @@ -128,8 +130,7 @@ class GroupDMChannel(Channel, Messageable, EditableChannel): else: self.icon = None - perms = data.get("permissions", 0) - self.permissions = ChannelPermissions._from_value(perms) + self.permissions = Permissions(data.get("permissions", 0)) def _update(self, *, name: Optional[str] = None, recipients: Optional[list[str]] = None, description: Optional[str] = None): if name: @@ -141,17 +142,88 @@ class GroupDMChannel(Channel, Messageable, EditableChannel): if description: self.description = description - async def set_default_permissions(self, permissions: ChannelPermissions) -> None: + async def set_default_permissions(self, permissions: Permissions) -> None: """Sets the default permissions for a group. Parameters ----------- permissions: :class:`ChannelPermissions` The new default group permissions """ - await self.state.http.set_channel_default_permissions(self.id, permissions.value) + await self.state.http.set_group_channel_default_permissions(self.id, permissions.value) -class TextChannel(Channel, Messageable, EditableChannel): - __slots__ = ("name", "description", "last_message_id", "server_id", "default_permissions", "role_permissions", "icon") +class GuildChannel(Channel): + def __init__(self, data: GuildChannelPayload, state: State): + super().__init__(data, state) + + self.server_id = data["server"] + self.name = data["name"] + self.description: Optional[str] = data.get("description") + + self.default_permissions = PermissionsOverwrite._from_overwrite(data.get("default_permissions", {"a": 0, "d": 0})) + + permissions: dict[str, PermissionsOverwrite] = {} + + for role_name, overwrite_data in data.get("role_permissions", {}).items(): + overwrite = PermissionsOverwrite._from_overwrite(overwrite_data) + permissions[role_name] = overwrite + + self.permissions = permissions + if icon := data.get("icon"): + self.icon = Asset(icon, state) + else: + self.icon = None + + async def set_default_permissions(self, permissions: PermissionsOverwrite) -> None: + """Sets the default permissions for the channel. + Parameters + ----------- + permissions: :class:`ChannelPermissions` + The new default channel permissions + """ + allow, deny = permissions.to_pair() + await self.state.http.set_guild_channel_default_permissions(self.id, allow.value, deny.value) + + async def set_role_permissions(self, role: Role, permissions: PermissionsOverwrite) -> None: + """Sets the permissions for a role in the channel. + Parameters + ----------- + permissions: :class:`ChannelPermissions` + The new channel permissions + """ + allow, deny = permissions.to_pair() + + await self.state.http.set_guild_channel_role_permissions(self.id, role.id, allow.value, deny.value) + + def _update(self, *, name: Optional[str] = None, description: Optional[str] = None, icon: Optional[FilePayload] = None, nsfw: Optional[bool] = None, active: Optional[bool] = None, role_permissions: Optional[dict[str, OverwritePayload]] = None, default_permissions: Optional[OverwritePayload] = None): + if name is not None: + self.name = name + + if description is not None: + self.description = description + + if icon: + self.icon = Asset(icon, self.state) + + if nsfw is not None: + self.nsfw = nsfw + + if active is not None: + self.active = active + + if role_permissions is not None: + permissions = {} + + for role_name, overwrite_data in role_permissions.items(): + overwrite = PermissionsOverwrite._from_overwrite(overwrite_data) + permissions[role_name] = overwrite + + self.permissions = permissions + + if default_permissions is not None: + self.default_permissions = default_permissions + +class TextChannel(GuildChannel, Messageable, EditableChannel): + __slots__ = ("name", "description", "last_message_id", "server_id", "default_permissions", "icon", "overwrites") """A text channel @@ -175,26 +247,14 @@ class TextChannel(Channel, Messageable, EditableChannel): def __init__(self, data: TextChannelPayload, state: State): super().__init__(data, state) - self.server_id = data["server"] - self.name = data["name"] - self.description: Optional[str] = data.get("description") - last_message_id = data.get("last_message") self.last_message_id = last_message_id - self.default_permissions = ChannelPermissions._from_value(data.get("default_permissions", 0)) - self.role_permissions = {role_id: ChannelPermissions._from_value(perms) for role_id, perms in data.get("role_permissions", {}).items()} - - if icon := data.get("icon"): - self.icon = Asset(icon, state) - else: - self.icon = None - async def _get_channel_id(self) -> str: return self.id @property - def last_message(self) -> Optional[Message]: + def last_message(self) -> Message: """Gets the last message from the channel, shorthand for `client.get_message(channel.last_message_id)` Returns @@ -203,36 +263,11 @@ class TextChannel(Channel, Messageable, EditableChannel): """ if not self.last_message_id: - return + raise LookupError return self.state.get_message(self.last_message_id) - def _update(self, *, name: Optional[str] = None, description: Optional[str] = None): - if name: - self.name = name - - if description: - self.description = description - - async def set_default_permissions(self, permissions: ChannelPermissions) -> None: - """Sets the default permissions for a channel. - Parameters - ----------- - permissions: :class:`ChannelPermissions` - The new default channel permissions - """ - await self.state.http.set_channel_default_permissions(self.id, permissions.value) - - async def set_role_permissions(self, role: Role, permissions: ChannelPermissions) -> None: - """Sets the permissions for a role in a channel. - Parameters - ----------- - permissions: :class:`ChannelPermissions` - The new channel permissions - """ - await self.state.http.set_channel_role_permissions(self.id, role.id, permissions.value) - -class VoiceChannel(Channel, EditableChannel): +class VoiceChannel(GuildChannel, EditableChannel): """A voice channel Attributes @@ -252,49 +287,6 @@ class VoiceChannel(Channel, EditableChannel): description: Optional[:class:`str`] The description of the channel, if any """ - def __init__(self, data: VoiceChannelPayload, state: State): - super().__init__(data, state) - - self.server_id = data["server"] - self.name = data["name"] - self.description: Optional[str] = data.get("description") - - if perms := data.get("default_permissions"): - self.default_permissions = ChannelPermissions._from_value(perms) - else: - self.default_permissions = ChannelPermissions._from_value(0) - - self.role_permissions = {role_id: ChannelPermissions._from_value(perms) for role_id, perms in data.get("role_permissions", {}).items()} - - if icon := data.get("icon"): - self.icon = Asset(icon, state) - else: - self.icon = None - - def _update(self, *, name: Optional[str] = None, description: Optional[str] = None): - if name: - self.name = name - - if description: - self.description = description - - async def set_default_permissions(self, permissions: ChannelPermissions) -> None: - """Sets the default permissions for a voice channel. - Parameters - ----------- - permissions: :class:`ChannelPermissions` - The new default channel permissions - """ - await self.state.http.set_channel_default_permissions(self.id, permissions.value) - - async def set_role_permissions(self, role: Role, permissions: ChannelPermissions) -> None: - """Sets the permissions for a role in a voice channel - Parameters - ----------- - permissions: :class:`ChannelPermissions` - The new channel permissions - """ - await self.state.http.set_channel_role_permissions(self.id, role.id, permissions.value) def channel_factory(data: ChannelPayload, state: State) -> Union[DMChannel, GroupDMChannel, SavedMessageChannel, TextChannel, VoiceChannel]: if data["channel_type"] == "SavedMessages": diff --git a/revolt/flags.py b/revolt/flags.py index 1c79d94..b1f67ab 100755 --- a/revolt/flags.py +++ b/revolt/flags.py @@ -33,8 +33,14 @@ class flag_value: instance._set_flag(self.flag, value) class Flags: - def __init__(self, **kwargs: bool): - self.value = 0 + FLAG_NAMES: list[str] + + def __init_subclass__(cls) -> None: + flags = cls._flags() + cls.FLAG_NAMES = list(flags.keys()) + + def __init__(self, value: int = 0, **kwargs: bool): + self.value = value for k, v in kwargs.items(): setattr(self, k, v) @@ -92,6 +98,10 @@ class Flags: def __hash__(self) -> int: return hash(self.value) + @classmethod + def _flags(cls) -> dict[str, flag_value]: + return {name: value for name, value in cls.__dict__.items() if isinstance(value, flag_value)} + class UserBadges(Flags): """Contains all user badges""" diff --git a/revolt/http.py b/revolt/http.py index 8d9b80f..85d812a 100755 --- a/revolt/http.py +++ b/revolt/http.py @@ -293,14 +293,6 @@ class HttpClient: def close_channel(self, channel_id: str) -> Request[None]: return self.request("DELETE", f"/channels/{channel_id}") - def set_channel_role_permissions(self, channel_id: str, role_id: str, channel_permissions: int) -> Request[None]: - payload = {"permissions": channel_permissions} - return self.request("PUT", f"/channels/{channel_id}/permissions/{role_id}", json=payload) - - def set_channel_default_permissions(self, channel_id: str, channel_permissions: int) -> Request[None]: - payload = {"permissions": channel_permissions} - return self.request("PUT", f"/channels/{channel_id}/permissions/default", json=payload) - def fetch_server(self, server_id: str) -> Request[Server]: return self.request("GET", f"/servers/{server_id}") @@ -345,30 +337,10 @@ class HttpClient: def unban_member(self, server_id: str, member_id: str) -> Request[None]: return self.request("DELETE", f"/servers/{server_id}/bans/{member_id}") - + def fetch_bans(self, server_id: str) -> Request[ServerBans]: return self.request("GET", f"/servers/{server_id}/bans") - def set_role_permissions(self, server_id: str, role_id: str, server_permissions: int, channel_permissions: int) -> Request[None]: - payload = { - "permissions": { - "server": server_permissions, - "channel": channel_permissions - } - } - - return self.request("PUT", f"/servers/{server_id}/permissions/{role_id}", json=payload, nonce=False) - - def set_default_permissions(self, server_id: str, server_permissions: int, channel_permissions: int) -> Request[None]: - payload = { - "permissions": { - "server": server_permissions, - "channel": channel_permissions - } - } - - return self.request("PUT", f"/servers/{server_id}/permissions/default", json=payload, nonce=False) - def create_role(self, server_id: str, name: str) -> Request[Role]: return self.request("POST", f"/servers/{server_id}/roles", json={"name": name}, nonce=False) @@ -412,5 +384,19 @@ class HttpClient: if not values.get("status", Missing): del values["status"] - print(remove, values) return await self.request("PATCH", "/users/@me", json=values) + + def set_guild_channel_default_permissions(self, channel_id: str, allow: int, deny: int) -> Request: + return self.request("PUT", f"/channels/{channel_id}/permissions/default", json={"permissions": {"allow": allow, "deny": deny}}) + + def set_guild_channel_role_permissions(self, channel_id: str, role_id: str, allow: int, deny: int) -> Request: + return self.request("PUT", f"/channels/{channel_id}/permissions/{role_id}", json={"permissions": {"allow": allow, "deny": deny}}) + + def set_group_channel_default_permissions(self, channel_id: str, value: int): + return self.request("PUT", f"/channels/{channel_id}/permissions/default", json={"permissions": value}) + + def set_server_role_permissions(self, server_id: str, role_id: str, allow: int, deny: int): + return self.request("PUT", f"/server/{server_id}/permissions/{role_id}", json={"permissions": {"allow": allow, "deny": deny}}) + + def set_server_default_permissions(self, server_id: str, value: int): + return self.request("PUT", f"/server/{server_id}/permissions/default", json={"permissions": value}) diff --git a/revolt/permissions.py b/revolt/permissions.py index 8c009fc..aa11401 100755 --- a/revolt/permissions.py +++ b/revolt/permissions.py @@ -1,152 +1,199 @@ from __future__ import annotations +from typing import TYPE_CHECKING, Any, Callable, Optional, TypeAlias +from typing_extensions import Self + +from revolt.types.permissions import Overwrite from .flags import Flags, flag_value -__all__ = ( - "ChannelPermissions", - "ServerPermissions" -) - -# Channel permissions -# -# View = 0b00000000000000000000000000000001 // 1 -# SendMessage = 0b00000000000000000000000000000010 // 2 -# ManageMessages = 0b00000000000000000000000000000100 // 4 -# ManageChannel = 0b00000000000000000000000000001000 // 8 -# VoiceCall = 0b00000000000000000000000000010000 // 16 -# InviteOthers = 0b00000000000000000000000000100000 // 32 -# EmbedLinks = 0b00000000000000000000000001000000 // 64 -# UploadFiles = 0b00000000000000000000000010000000 // 128 - - -# Server permissions -# -# View = 0b00000000000000000000000000000001 // 1 -# ManageRoles = 0b00000000000000000000000000000010 // 2 -# ManageChannels = 0b00000000000000000000000000000100 // 4 -# ManageServer = 0b00000000000000000000000000001000 // 8 -# KickMembers = 0b00000000000000000000000000010000 // 16 -# BanMembers = 0b00000000000000000000000000100000 // 32 - -# ChangeNickname = 0b00000000000000000001000000000000 // 4096 -# ManageNicknames = 0b00000000000000000010000000000000 // 8192 -# ChangeAvatar = 0b00000000000000000100000000000000 // 16382 -# RemoveAvatars = 0b00000000000000001000000000000000 // 32768 - -class ChannelPermissions(Flags): - """Represents the channel permissions for a role as seen in channel settings.""" - - @classmethod - def none(cls) -> ChannelPermissions: - return cls._from_value(0) - - @classmethod - def all(cls) -> ChannelPermissions: - return cls._from_value(0b11111111) - - @classmethod - def view(cls) -> ChannelPermissions: - return cls._from_value(0b1) - - @classmethod - def send_message(cls) -> ChannelPermissions: - return cls._from_value(0b11) - - @classmethod - def manage_channel(cls) -> ChannelPermissions: - return cls._from_value(0b1001) - - @classmethod - def voice_call(cls) -> ChannelPermissions: - return cls._from_value(0b10001) - - @classmethod - def invite_others(cls) -> ChannelPermissions: - return cls._from_value(0b100001) - - @classmethod - def embed_links(cls) -> ChannelPermissions: - return cls._from_value(0b1000001) - - @classmethod - def upload_files(cls) -> ChannelPermissions: - return cls._from_value(0b10000001) - +class Permissions(Flags): @flag_value - def can_view() -> int: + def manage_channel() -> int: return 1 << 0 - @flag_value - def can_send_message() -> int: - return 1 << 1 - - @flag_value - def can_manage_channel() -> int: - return 1 << 3 - - @flag_value - def can_voice_call() -> int: - return 1 << 4 - - @flag_value - def can_invite_others() -> int: - return 1 << 5 - - @flag_value - def can_embed_links() -> int: - return 1 << 6 - - @flag_value - def can_upload_files() -> int: - return 1 << 7 - -class ServerPermissions(Flags): - """Represents the server permissions for a role as seen in server settings.""" - - @classmethod - def none(cls) -> ServerPermissions: - return cls._from_value(0) - - @classmethod - def all(cls) -> ServerPermissions: - return cls._from_value(0b1111000000111111) - - @flag_value - def view_server() -> int: - return 1 << 0 - - @flag_value - def manage_roles() -> int: - return 1 << 1 - - @flag_value - def manage_channels() -> int: - return 1 << 2 - @flag_value def manage_server() -> int: + return 1 << 1 + + @flag_value + def manage_permissions() -> int: + return 1 << 2 + + @flag_value + def manage_role() -> int: return 1 << 3 @flag_value def kick_members() -> int: - return 1 << 4 + return 1 << 6 @flag_value def ban_members() -> int: - return 1 << 5 + return 1 << 7 @flag_value - def change_nicknames() -> int: - return 1 << 12 + def timeout_members() -> int: + return 1 << 8 + + @flag_value + def asign_roles() -> int: + return 1 << 9 + + @flag_value + def change_nickname() -> int: + return 1 << 10 @flag_value def manage_nicknames() -> int: - return 1 << 13 + return 1 << 11 @flag_value - def change_avatar() -> int: - return 1 << 14 + def change_avatars() -> int: + return 1 << 12 @flag_value def remove_avatars() -> int: - return 1 << 15 + return 1 << 13 + + @flag_value + def view_channel() -> int: + return 1 << 20 + + @flag_value + def read_message_history() -> int: + return 1 << 21 + + @flag_value + def send_messages() -> int: + return 1 << 22 + + @flag_value + def manage_messages() -> int: + return 1 << 23 + + @flag_value + def manage_webhooks() -> int: + return 1 << 24 + + @flag_value + def invite_others() -> int: + return 1 << 25 + + @flag_value + def send_embeds() -> int: + return 1 << 26 + + @flag_value + def upload_files() -> int: + return 1 << 27 + + @flag_value + def masquerade() -> int: + return 1 << 28 + + @flag_value + def connect() -> int: + return 1 << 30 + + @flag_value + def speak() -> int: + return 1 << 31 + + @flag_value + def video() -> int: + return 1 << 32 + + @flag_value + def mute_members() -> int: + return 1 << 33 + + @flag_value + def deafen_members() -> int: + return 1 << 34 + + @flag_value + def move_members() -> int: + return 1 << 35 + + @classmethod + def all(cls) -> Self: + return cls(0x000F_FFFF_FFFF_FFFF) + + @classmethod + def default_view_only(cls) -> Self: + return cls(view_channel=True, read_message_history=True) + + @classmethod + def default(cls) -> Self: + return cls.default_view_only() | cls(send_messages=True, invite_others=True, send_embeds=True, upload_files=True, connect=True, speak=True) + +class PermissionsOverwrite: + def __init__(self, allow: Permissions, deny: Permissions): + self._allow = allow + self._deny = deny + + for perm in Permissions.FLAG_NAMES: + if getattr(allow, perm): + value = True + elif getattr(deny, perm): + value = False + else: + value = None + + super().__setattr__(perm, value) + + def __setattr__(self, key: str, value: Any): + if key in Permissions.FLAG_NAMES: + if key is True: + setattr(self._allow, key, True) + super().__setattr__(key, True) + + elif key is False: + setattr(self._deny, key, True) + super().__setattr__(key, False) + + else: + setattr(self._allow, key, False) + setattr(self._deny, key, False) + super().__setattr__(key, None) + else: + super().__setattr__(key, value) + + if TYPE_CHECKING: + manage_channel: Optional[bool] + manage_server: Optional[bool] + manage_permissions: Optional[bool] + manage_role: Optional[bool] + kick_members: Optional[bool] + ban_members: Optional[bool] + timeout_members: Optional[bool] + asign_roles: Optional[bool] + change_nickname: Optional[bool] + manage_nicknames: Optional[bool] + change_avatars: Optional[bool] + remove_avatars: Optional[bool] + view_channel: Optional[bool] + read_message_history: Optional[bool] + send_messages: Optional[bool] + manage_messages: Optional[bool] + manage_webhooks: Optional[bool] + invite_others: Optional[bool] + send_embeds: Optional[bool] + upload_files: Optional[bool] + masquerade: Optional[bool] + connect: Optional[bool] + speak: Optional[bool] + video: Optional[bool] + mute_members: Optional[bool] + deafen_members: Optional[bool] + move_members: Optional[bool] + + def to_pair(self) -> tuple[Permissions, Permissions]: + return self._allow, self._deny + + @classmethod + def _from_overwrite(cls, overwrite: Overwrite) -> Self: + allow = Permissions(overwrite["a"]) + deny = Permissions(overwrite["d"]) + + return cls(allow, deny) diff --git a/revolt/role.py b/revolt/role.py index d9ecaeb..054fdf1 100755 --- a/revolt/role.py +++ b/revolt/role.py @@ -2,7 +2,7 @@ from __future__ import annotations from typing import TYPE_CHECKING, Optional -from .permissions import ChannelPermissions, ServerPermissions +from .permissions import Permissions, PermissionsOverwrite from .utils import Missing if TYPE_CHECKING: @@ -35,7 +35,7 @@ class Role: channel_permissions: :class:`ChannelPermissions` The channel permissions for the role """ - __slots__ = ("id", "name", "colour", "hoist", "rank", "state", "server", "server_permissions", "channel_permissions") + __slots__ = ("id", "name", "colour", "hoist", "rank", "state", "server", "permissions") def __init__(self, data: RolePayload, role_id: str, server: Server, state: State): self.state = state @@ -45,14 +45,13 @@ class Role: self.hoist = False self.rank = 0 self.server = server - self.server_permissions = ServerPermissions._from_value(data["permissions"][0]) - self.channel_permissions = ChannelPermissions._from_value(data["permissions"][1]) + self.permissions = PermissionsOverwrite._from_overwrite(data.get("permissions", {"a": 0, "d": 0})) @property def color(self): return self.colour - async def set_permissions(self, *, server_permissions: Optional[ServerPermissions] = None, channel_permissions: Optional[ChannelPermissions] = None) -> None: + async def set_permissions_overwrite(self, *, permissions: PermissionsOverwrite) -> None: """Sets the permissions for a role in a server. Parameters ----------- @@ -61,14 +60,8 @@ class Role: channel_permissions: Optional[:class:`ChannelPermissions`] The new channel permissions for the role """ - - if not server_permissions and not channel_permissions: - return - - server_value = (server_permissions or self.server_permissions).value - channel_value = (channel_permissions or self.channel_permissions).value - - await self.state.http.set_role_permissions(self.server.id, self.id, server_value, channel_value) + allow, deny = permissions.to_pair() + await self.state.http.set_server_role_permissions(self.server.id, self.id, allow.value, deny.value) def _update(self, *, name: Optional[str] = None, colour: Optional[str] = None, hoist: Optional[bool] = None, rank: Optional[int] = None): if name: diff --git a/revolt/server.py b/revolt/server.py index ab8c2bb..52ac33e 100755 --- a/revolt/server.py +++ b/revolt/server.py @@ -6,7 +6,7 @@ from .asset import Asset from .category import Category from .channel import Channel, VoiceChannel from .invite import Invite -from .permissions import ChannelPermissions, ServerPermissions +from .permissions import Permissions from .role import Role if TYPE_CHECKING: @@ -16,7 +16,6 @@ if TYPE_CHECKING: from .types import Ban from .types import Category as CategoryPayload from .types import File as FilePayload - from .types import Permission as PermissionPayload from .types import Server as ServerPayload from .types import SystemMessagesConfig @@ -88,20 +87,21 @@ class Server: The servers icon banner: Optional[:class:`Asset`] The servers banner + default_permissions: :class:`Permissions` + The permissions for the default role """ - __slots__ = ("state", "id", "name", "owner_id", "default_server_permissions", "default_channel_permissions", "_members", "_roles", "_channels", "description", "icon", "banner", "nsfw", "system_messages", "_categories") + __slots__ = ("state", "id", "name", "owner_id", "default_permissions", "_members", "_roles", "_channels", "description", "icon", "banner", "nsfw", "system_messages", "_categories") def __init__(self, data: ServerPayload, state: State): self.state = state self.id = data["_id"] self.name = data["name"] self.owner_id = data["owner"] - self.default_server_permissions = ServerPermissions._from_value(data["default_permissions"][0]) - self.default_channel_permissions = ChannelPermissions._from_value(data["default_permissions"][1]) self.description = data.get("description") or None self.nsfw = data.get("nsfw", False) self.system_messages = SystemMessages(data.get("system_messages", cast("SystemMessagesConfig", {})), state) self._categories = {data["id"]: Category(data, state) for data in data.get("categories", [])} + self.default_permissions = Permissions(data["default_permissions"]) if icon := data.get("icon"): self.icon = Asset(icon, state) @@ -118,7 +118,7 @@ class Server: self._channels: dict[str, Channel] = {channel_id: state.get_channel(channel_id) for channel_id in data.get("channels", [])} - def _update(self, *, owner: Optional[str] = None, name: Optional[str] = None, description: Optional[str] = None, icon: Optional[FilePayload] = None, banner: Optional[FilePayload] = None, default_permissions: Optional[PermissionPayload] = None, nsfw: Optional[bool] = None, system_messages: Optional[SystemMessagesConfig] = None, categories: Optional[list[CategoryPayload]] = None): + def _update(self, *, owner: Optional[str] = None, name: Optional[str] = None, description: Optional[str] = None, icon: Optional[FilePayload] = None, banner: Optional[FilePayload] = None, default_permissions: Optional[int] = None, nsfw: Optional[bool] = None, system_messages: Optional[SystemMessagesConfig] = None, categories: Optional[list[CategoryPayload]] = None): if owner: self.owner_id = owner if name: @@ -129,9 +129,8 @@ class Server: self.icon = Asset(icon, self.state) if banner: self.banner = Asset(banner, self.state) - if default_permissions: - self.default_server_permissions = ServerPermissions._from_value(default_permissions[0]) - self.default_channel_permissions = ChannelPermissions._from_value(default_permissions[1]) + if default_permissions is not None: + self.default_permissions = Permissions(default_permissions) if nsfw is not None: self.nsfw = nsfw if system_messages is not None: @@ -224,7 +223,7 @@ class Server: """:class:`Member` The owner of the server""" return self.get_member(self.owner_id) - async def set_default_permissions(self, *, server_permissions: Optional[ServerPermissions] = None, channel_permissions: Optional[ChannelPermissions] = None) -> None: + async def set_default_permissions(self, permissions: Permissions) -> None: """Sets the default server permissions. Parameters ----------- @@ -233,10 +232,8 @@ class Server: channel_permissions: Optional[:class:`ChannelPermissions`] the new default channel permissions """ - server_value = (server_permissions or self.default_server_permissions).value - channel_value = (channel_permissions or self.default_channel_permissions).value - await self.state.http.set_default_permissions(self.id, server_value, channel_value) + await self.state.http.set_server_default_permissions(self.id, permissions.value) async def leave_server(self): """Leaves or deletes the server""" @@ -348,12 +345,26 @@ class Server: return Role(payload, name, self, self.state) class ServerBan: + """Represents a server ban + + Attributes + ----------- + reason: Optional[:class:str`] + The reason the user was banned + server: :class:`Server` + The server the user was banned in + user_id: :class:`str` + The id of the user who was banned + """ + + __slots__ = ("reason", "server", "user_id", "state") + def __init__(self, ban: Ban, state: State): self.reason = ban.get("reason") self.server = state.get_server(ban["_id"]["server"]) - self.user = state.get_user(ban["_id"]["user"]) + self.user_id = ban["_id"]["user"] self.state = state async def unban(self): """Unbans the user""" - await self.state.http.unban_member(self.server.id, self.user.id) + await self.state.http.unban_member(self.server.id, self.user_id) diff --git a/revolt/types/__init__.py b/revolt/types/__init__.py index 4ce0865..6bedb41 100755 --- a/revolt/types/__init__.py +++ b/revolt/types/__init__.py @@ -7,6 +7,7 @@ from .http import * from .invite import * from .member import * from .message import * +from .permissions import Overwrite from .role import * from .server import * from .user import * diff --git a/revolt/types/channel.py b/revolt/types/channel.py index 8ea520c..54c813f 100755 --- a/revolt/types/channel.py +++ b/revolt/types/channel.py @@ -1,13 +1,13 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Literal, TypedDict, Union +from typing import TYPE_CHECKING, Literal, Text, TypedDict, Union from typing_extensions import NotRequired if TYPE_CHECKING: from .file import File from .message import Message - + from .permissions import Overwrite __all__ = ( "SavedMessages", @@ -15,6 +15,7 @@ __all__ = ( "GroupDMChannel", "TextChannel", "VoiceChannel", + "GuildChannel", "Channel", ) @@ -47,8 +48,8 @@ class TextChannel(BaseChannel): description: str channel_type: Literal["TextChannel"] icon: NotRequired[File] - default_permissions: NotRequired[int] - role_permissions: NotRequired[dict[str, int]] + default_permissions: NotRequired[Overwrite] + role_permissions: NotRequired[dict[str, Overwrite]] last_message: NotRequired[str] class VoiceChannel(BaseChannel): @@ -57,7 +58,8 @@ class VoiceChannel(BaseChannel): description: str channel_type: Literal["VoiceChannel"] icon: NotRequired[File] - default_permissions: NotRequired[int] - role_permissions: NotRequired[dict[str, int]] + default_permissions: NotRequired[Overwrite] + role_permissions: NotRequired[dict[str, Overwrite]] +GuildChannel = Union[TextChannel, VoiceChannel] Channel = Union[SavedMessages, DMChannel, GroupDMChannel, TextChannel, VoiceChannel] diff --git a/revolt/types/gateway.py b/revolt/types/gateway.py index b3c40d8..5461d3e 100755 --- a/revolt/types/gateway.py +++ b/revolt/types/gateway.py @@ -2,6 +2,8 @@ from __future__ import annotations from typing import TYPE_CHECKING, Literal, TypedDict, Union +from revolt.types.permissions import Overwrite + from .channel import (Channel, DMChannel, GroupDMChannel, SavedMessages, TextChannel, VoiceChannel) from .file import File @@ -11,7 +13,6 @@ from .user import Status if TYPE_CHECKING: from .category import Category from .member import Member, MemberID - from .role import Permission from .server import Server, SystemMessagesConfig from .user import User @@ -87,9 +88,18 @@ class ChannelCreateEventPayload_DMChannel(BasePayload, DMChannel): ChannelCreateEventPayload = Union[ChannelCreateEventPayload_Group, ChannelCreateEventPayload_Group, ChannelCreateEventPayload_TextChannel, ChannelCreateEventPayload_VoiceChannel, ChannelCreateEventPayload_DMChannel] +class ChannelUpdateEventPayloadData(TypedDict, total=False): + name: str + description: str + icon: File + nsfw: bool + active: bool + role_permissions: dict[str, Overwrite] + default_permissions: Overwrite + class ChannelUpdateEventPayload(BasePayload): id: str - data: ... + data: ChannelUpdateEventPayloadData clear: Literal["Icon", "Description"] class ChannelDeleteEventPayload(BasePayload): @@ -107,7 +117,7 @@ class ServerUpdateEventPayloadData(TypedDict, total=False): description: str icon: File banner: File - default_permissions: Permission + default_permissions: int nsfw: bool system_messages: SystemMessagesConfig categories: list[Category] diff --git a/revolt/types/permissions.py b/revolt/types/permissions.py new file mode 100755 index 0000000..9007888 --- /dev/null +++ b/revolt/types/permissions.py @@ -0,0 +1,7 @@ +from __future__ import annotations + +from typing import TypedDict + +class Overwrite(TypedDict): + a: int + d: int diff --git a/revolt/types/role.py b/revolt/types/role.py index 28d4915..922f360 100755 --- a/revolt/types/role.py +++ b/revolt/types/role.py @@ -1,14 +1,18 @@ from __future__ import annotations -from typing import TypedDict +from typing import TYPE_CHECKING, TypedDict +from typing_extensions import NotRequired + +if TYPE_CHECKING: + from .permissions import Overwrite __all__ = ( - "Permission", "Role", ) -Permission = tuple[int, int] - class Role(TypedDict): name: str - permissions: Permission + permissions: Overwrite + colour: NotRequired[str] + hoist: NotRequired[bool] + rank: int diff --git a/revolt/types/server.py b/revolt/types/server.py index 115e779..b3afd46 100755 --- a/revolt/types/server.py +++ b/revolt/types/server.py @@ -8,7 +8,7 @@ if TYPE_CHECKING: from .category import Category from .channel import Channel from .file import File - from .role import Permission, Role + from .role import Role __all__ = ( "Server", @@ -30,7 +30,7 @@ class Server(TypedDict): owner: str name: str channels: list[str] - default_permissions: Permission + default_permissions: int nonce: NotRequired[str] description: NotRequired[str] categories: NotRequired[list[Category]]