6 Commits

Author SHA1 Message Date
Zomatree 99b974f96e bump version 2022-06-23 02:10:19 +01:00
Zomatree 357fd7b071 update docs 2022-05-31 01:57:22 +01:00
Zomatree 1f411c12bf fix CI issues 2022-05-31 01:47:07 +01:00
Zomatree 25f3b4e4fc inital updated permissions system 2022-05-31 01:44:41 +01:00
Zomatree 6871754a27 remove trailing whitespace 2022-04-25 13:42:37 +01:00
Zomatree 0fdca7eb7d isort 2022-04-25 13:40:39 +01:00
36 changed files with 445 additions and 366 deletions
+6 -6
View File
@@ -130,15 +130,15 @@ Messageable
.. autoclass:: Messageable
:members:
ChannelPermissions
~~~~~~~~~~~~~~~~~~~
Permissions
~~~~~~~~~~~~
.. autoclass:: ChannelPermissions
.. autoclass:: Permissions
:members:
ServerPermissions
~~~~~~~~~~~~~~~~~~
.. autoclass:: ServerPermissions
PermissionsOverwrite
~~~~~~~~~~~~~~~~~~~~~
.. autoclass:: PermissionsOverwrite
:members:
Role
+1
View File
@@ -17,6 +17,7 @@ import sphinx_nameko_theme
sys.path.insert(0, os.path.abspath('..'))
import revolt
# -- Project information -----------------------------------------------------
+1 -1
View File
@@ -1,6 +1,6 @@
[tool.poetry]
name = "revolt.py"
version = "0.1.7"
version = "0.1.8"
description = "Python wrapper for the revolt.chat API"
authors = ["Zomatee <me@zomatree.live>"]
license = "MIT"
+1 -1
View File
@@ -17,4 +17,4 @@ from .role import *
from .server import *
from .user import *
__version__ = (0, 1, 7)
__version__ = (0, 1, 8)
+4 -4
View File
@@ -17,7 +17,7 @@ __all__ = ("Asset", "PartialAsset")
class Asset:
"""Represents a file on revolt
Attributes
-----------
id: :class:`str`
@@ -40,7 +40,7 @@ class Asset:
The assets url
"""
__slots__ = ("state", "id", "tag", "size", "filename", "content_type", "width", "height", "type", "url")
def __init__(self, data: FilePayload, state: State):
self.state = state
@@ -48,7 +48,7 @@ class Asset:
self.tag = data['tag']
self.size = data['size']
self.filename = data['filename']
metadata = data['metadata']
if metadata["type"] == "Image" or metadata["type"] == "Video": # cannot use `in` because type narrowing will not happen
@@ -70,7 +70,7 @@ class Asset:
async def save(self, fp: IOBase):
"""Reads the files content and saves it to a file
Parameters
-----------
fp: IOBase
+85 -93
View File
@@ -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")
@@ -43,7 +45,7 @@ class EditableChannel:
class Channel:
"""Base class for all channels
Attributes
-----------
id: :class:`str`
@@ -64,7 +66,7 @@ class Channel:
async def _get_channel_id(self) -> str:
return self.id
def _update(self):
def _update(self, **_):
pass
async def delete(self):
@@ -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":
+8 -8
View File
@@ -12,8 +12,8 @@ from .http import HttpClient
from .invite import Invite
from .message import Message
from .state import State
from .websocket import WebsocketHandler
from .utils import Missing
from .websocket import WebsocketHandler
try:
import ujson as json
@@ -99,12 +99,12 @@ class Client:
def get_user(self, id: str) -> User:
"""Gets a user from the cache
Parameters
-----------
id: :class:`str`
The id of the user
Returns
--------
:class:`User`
@@ -114,12 +114,12 @@ class Client:
def get_channel(self, id: str) -> Channel:
"""Gets a channel from the cache
Parameters
-----------
id: :class:`str`
The id of the channel
Returns
--------
:class:`Channel`
@@ -129,12 +129,12 @@ class Client:
def get_server(self, id: str) -> Server:
"""Gets a server from the cache
Parameters
-----------
id: :class:`str`
The id of the server
Returns
--------
:class:`Server`
@@ -144,7 +144,7 @@ class Client:
async def wait_for(self, event: str, *, check: Optional[Callable[..., bool]] = None, timeout: Optional[float] = None) -> Any:
"""Waits for an event
Parameters
-----------
event: :class:`str`
+4 -4
View File
@@ -2,17 +2,17 @@ from __future__ import annotations
from typing import TYPE_CHECKING, Optional, Union
from .enums import EmbedType
from .asset import Asset
from .enums import EmbedType
if TYPE_CHECKING:
from .state import State
from .types import Embed as EmbedPayload
from .types import SendableEmbed as SendableEmbedPayload
from .types import WebsiteEmbed as WebsiteEmbedPayload
from .types import ImageEmbed as ImageEmbedPayload
from .types import TextEmbed as TextEmbedPayload
from .types import NoneEmbed as NoneEmbedPayload
from .types import SendableEmbed as SendableEmbedPayload
from .types import TextEmbed as TextEmbedPayload
from .types import WebsiteEmbed as WebsiteEmbedPayload
__all__ = ("Embed", "WebsiteEmbed", "ImageEmbed", "TextEmbed", "NoneEmbed", "to_embed", "SendableEmbed")
+1 -1
View File
@@ -1,9 +1,9 @@
from .checks import *
from .client import *
from .cog import *
from .command import *
from .context import *
from .converters import *
from .errors import *
from .group import *
from .help import *
from .cog import *
+6 -4
View File
@@ -1,21 +1,23 @@
from __future__ import annotations
import traceback
import sys
from typing import Any, Union, Protocol, runtime_checkable, Optional, TYPE_CHECKING
from typing_extensions import Self
import traceback
from importlib import import_module
from typing import (TYPE_CHECKING, Any, Optional, Protocol, Union,
runtime_checkable)
from typing_extensions import Self
import revolt
if TYPE_CHECKING:
from .help import HelpCommand
from .cog import Cog
from .command import Command
from .context import Context
from .errors import CheckError, CommandNotFound, MissingSetup
from .view import StringView
from .cog import Cog
__all__ = (
"CommandsMeta",
+1 -1
View File
@@ -1,6 +1,6 @@
from __future__ import annotations
from distutils import command
from distutils import command
from typing import TYPE_CHECKING, Any, Optional
from .command import Command
+2 -2
View File
@@ -13,10 +13,10 @@ from .errors import InvalidLiteralArgument, UnionConverterError
from .utils import evaluate_parameters
if TYPE_CHECKING:
from .context import Context
from .checks import Check
from .group import Group
from .cog import Cog
from .context import Context
from .group import Group
__all__ = (
"Command",
+1 -1
View File
@@ -1,6 +1,6 @@
from __future__ import annotations
from typing import TYPE_CHECKING, Optional, Callable, Coroutine, Any
from typing import TYPE_CHECKING, Any, Callable, Coroutine, Optional
from .command import Command
+8 -6
View File
@@ -1,19 +1,21 @@
from __future__ import annotations
from abc import ABC, abstractmethod
from typing import TYPE_CHECKING, TypedDict, Union, Optional
from typing_extensions import NotRequired
from itertools import groupby
from typing import TYPE_CHECKING, Optional, TypedDict, Union
from typing_extensions import NotRequired
from .utils import evaluate_parameters
from .command import Command, command
from .client import CommandsClient
from .command import Command, command
from .context import Context
from .group import Group
from .utils import evaluate_parameters
if TYPE_CHECKING:
from revolt import File, Message, Messageable, MessageReply, SendableEmbed
from .cog import Cog
from revolt import SendableEmbed, File, MessageReply, Message, Messageable
__all__ = ("MessagePayload", "HelpCommand", "DefaultHelpCommand", "help_command_impl")
+1 -1
View File
@@ -1,5 +1,5 @@
from inspect import Parameter
from typing import Iterable, Any
from typing import Any, Iterable
__all__ = ("evaluate_parameters",)
+4 -4
View File
@@ -6,7 +6,7 @@ __all__ = ("File",)
class File:
"""Respresents a file about to be uploaded to revolt
Parameters
-----------
file: Union[str, bytes]
@@ -17,7 +17,7 @@ class File:
Determines if the file will be a spoiler, this prefexes the filename with `SPOILER_`
"""
__slots__ = ("f", "spoiler", "filename")
def __init__(self, file: Union[str, bytes], *, filename: Optional[str] = None, spoiler: bool = False):
if isinstance(file, str):
self.f = open(file, "rb")
@@ -26,10 +26,10 @@ class File:
if filename is None and isinstance(file, str):
filename = self.f.name
self.spoiler = spoiler or (filename and filename.startswith("SPOILER_"))
if self.spoiler and (filename and not filename.startswith("SPOILER_")):
filename = f"SPOILER_{filename}"
self.filename = filename
self.filename = filename
+12 -2
View File
@@ -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"""
+22 -34
View File
@@ -25,14 +25,16 @@ if TYPE_CHECKING:
from .types import Autumn as AutumnPayload
from .types import Channel, DMChannel
from .types import Embed as EmbedPayload
from .types import GetServerMembers, Invite
from .types import GetServerMembers, GroupDMChannel, Invite
from .types import Masquerade as MasqueradePayload
from .types import Member
from .types import Message as MessagePayload
from .types import (MessageReplyPayload, MessageWithUserData,
PartialInvite, Role, Server, ServerBans, TextChannel)
from .types import User as UserPayload
from .types import UserProfile, VoiceChannel, GroupDMChannel, Member
PartialInvite, Role)
from .types import SendableEmbed as SendableEmbedPayload
from .types import Server, ServerBans, TextChannel
from .types import User as UserPayload
from .types import UserProfile, VoiceChannel
__all__ = ("HttpClient",)
@@ -291,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}")
@@ -343,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)
@@ -410,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})
+2 -2
View File
@@ -19,7 +19,7 @@ def flattern_user(member: Member, user: User):
class Member(User):
"""Represents a member of a server, subclasses :class:`User`
Attributes
-----------
nickname: Optional[:class:`str`]
@@ -32,7 +32,7 @@ class Member(User):
The member's guild avatar if any
"""
__slots__ = ("_state", "nickname", "roles", "server", "guild_avatar")
def __init__(self, data: MemberPayload, server: Server, state: State):
user = state.get_user(data["_id"]["user"])
flattern_user(self, user)
+2 -2
View File
@@ -5,14 +5,14 @@ from typing import TYPE_CHECKING, NamedTuple, Optional
from .asset import Asset, PartialAsset
from .channel import Messageable
from .embed import to_embed, SendableEmbed
from .embed import SendableEmbed, to_embed
if TYPE_CHECKING:
from .state import State
from .types import Embed as EmbedPayload
from .types import Masquerade as MasqueradePayload
from .types import Message as MessagePayload
from .types import MessageReplyPayload
from .types import Embed as EmbedPayload
__all__ = (
+173 -125
View File
@@ -1,152 +1,200 @@
from __future__ import annotations
from typing import TYPE_CHECKING, Any, Optional
from typing_extensions import Self
from .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)
__all__ = ("Permissions", "PermissionsOverwrite")
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)
+6 -13
View File
@@ -2,8 +2,8 @@ from __future__ import annotations
from typing import TYPE_CHECKING, Optional
from .permissions import Permissions, PermissionsOverwrite
from .utils import Missing
from .permissions import ChannelPermissions, ServerPermissions
if TYPE_CHECKING:
from .server import Server
@@ -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:
+34 -22
View File
@@ -6,18 +6,18 @@ 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:
from .channel import TextChannel
from .member import Member
from .state import State
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, Ban
from .types import SystemMessagesConfig
__all__ = ("Server", "SystemMessages", "ServerBan")
@@ -87,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)
@@ -117,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:
@@ -128,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:
@@ -160,12 +160,12 @@ class Server:
def get_role(self, role_id: str) -> Role:
"""Gets a role from the cache
Parameters
-----------
id: :class:`str`
The id of the role
Returns
--------
:class:`Role`
@@ -175,12 +175,12 @@ class Server:
def get_member(self, member_id: str) -> Member:
"""Gets a member from the cache
Parameters
-----------
id: :class:`str`
The id of the member
Returns
--------
:class:`Member`
@@ -190,12 +190,12 @@ class Server:
def get_channel(self, channel_id: str) -> Channel:
"""Gets a channel from the cache
Parameters
-----------
id: :class:`str`
The id of the channel
Returns
--------
:class:`Channel`
@@ -223,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
-----------
@@ -232,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"""
@@ -347,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)
+1
View File
@@ -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 *
+9 -6
View File
@@ -1,12 +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",
@@ -14,6 +15,7 @@ __all__ = (
"GroupDMChannel",
"TextChannel",
"VoiceChannel",
"GuildChannel",
"Channel",
)
@@ -46,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):
@@ -56,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]
+1
View File
@@ -1,6 +1,7 @@
from __future__ import annotations
from typing import TYPE_CHECKING, Literal, TypedDict, Union
from typing_extensions import NotRequired
if TYPE_CHECKING:
+17 -8
View File
@@ -2,19 +2,19 @@ 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
from .message import Message
from .user import Status
from .file import File
if TYPE_CHECKING:
from .member import Member, MemberID
from .server import Server
from .user import User
from .role import Permission
from .category import Category
from .server import SystemMessagesConfig
from .member import Member, MemberID
from .server import Server, SystemMessagesConfig
from .user import User
__all__ = (
"BasePayload",
@@ -88,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):
@@ -108,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]
+1
View File
@@ -1,6 +1,7 @@
from __future__ import annotations
from typing import TYPE_CHECKING, TypedDict
from typing_extensions import NotRequired
if TYPE_CHECKING:
+1
View File
@@ -1,6 +1,7 @@
from __future__ import annotations
from typing import TYPE_CHECKING, TypedDict, Union
from typing_extensions import NotRequired
if TYPE_CHECKING:
+7
View File
@@ -0,0 +1,7 @@
from __future__ import annotations
from typing import TypedDict
class Overwrite(TypedDict):
a: int
d: int
+9 -5
View File
@@ -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
+3 -2
View File
@@ -1,13 +1,14 @@
from __future__ import annotations
from typing import TYPE_CHECKING, TypedDict
from typing_extensions import NotRequired
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",
@@ -29,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]]
+1
View File
@@ -1,6 +1,7 @@
from __future__ import annotations
from typing import TYPE_CHECKING, Literal, TypedDict
from typing_extensions import NotRequired
if TYPE_CHECKING:
+1 -1
View File
@@ -34,7 +34,7 @@ class UserProfile(NamedTuple):
class User(Messageable):
"""Represents a user
Attributes
-----------
id: :class:`str`
+3 -2
View File
@@ -1,9 +1,10 @@
import inspect
from contextlib import asynccontextmanager
from operator import attrgetter
from typing import Any, Callable, Coroutine, Iterable, TypeVar, Union
from contextlib import asynccontextmanager
from typing_extensions import ParamSpec
from aiohttp import ClientSession
from typing_extensions import ParamSpec
__all__ = ("Missing", "copy_doc", "maybe_coroutine", "get", "client_session")
+6 -5
View File
@@ -3,10 +3,14 @@ from __future__ import annotations
import asyncio
import logging
from copy import copy
from traceback import print_exception
from typing import TYPE_CHECKING, Callable, cast
from traceback import print_exception;
from .channel import GroupDMChannel, TextChannel, VoiceChannel
from .enums import RelationshipType
from .types import (ChannelCreateEventPayload, ChannelDeleteEventPayload,
ChannelDeleteTypingEventPayload,
ChannelStartTypingEventPayload, ChannelUpdateEventPayload)
from .types import Message as MessagePayload
from .types import (MessageDeleteEventPayload, MessageUpdateEventPayload,
ServerDeleteEventPayload, ServerMemberJoinEventPayload,
@@ -14,11 +18,8 @@ from .types import (MessageDeleteEventPayload, MessageUpdateEventPayload,
ServerMemberUpdateEventPayload,
ServerRoleDeleteEventPayload, ServerRoleUpdateEventPayload,
ServerUpdateEventPayload, UserRelationshipEventPayload,
UserUpdateEventPayload, ChannelCreateEventPayload, ChannelDeleteEventPayload,
ChannelDeleteTypingEventPayload,
ChannelStartTypingEventPayload, ChannelUpdateEventPayload)
UserUpdateEventPayload)
from .user import Status, UserProfile
from .channel import TextChannel, GroupDMChannel, VoiceChannel
try:
import ujson as json