mirror of
https://github.com/Rapptz/discord.py.git
synced 2025-05-14 09:50:03 +00:00
Redesign permissions to allow aliases to be used.
This adds manage_permissions, view_channel, and use_external_emojis aliases to Permissions. It should be noted that to prevent breaking changes and for sake of usefulness, aliases are not included in the `__iter__` for either Permissions or PermissionOverwrite. Fixes #2496
This commit is contained in:
parent
92a3c1b583
commit
2770137bd6
@ -24,7 +24,27 @@ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
"""
|
||||
|
||||
class Permissions:
|
||||
from .flags import BaseFlags, flag_value, fill_with_flags
|
||||
|
||||
__all__ = (
|
||||
'Permissions',
|
||||
'PermissionOverwrite',
|
||||
)
|
||||
|
||||
# A permission alias works like a regular flag but is marked
|
||||
# So the PermissionOverwrite knows to work with it
|
||||
class permission_alias(flag_value):
|
||||
pass
|
||||
|
||||
def make_permission_alias(alias):
|
||||
def decorator(func):
|
||||
ret = permission_alias(func)
|
||||
ret.alias = alias
|
||||
return ret
|
||||
return decorator
|
||||
|
||||
@fill_with_flags()
|
||||
class Permissions(BaseFlags):
|
||||
"""Wraps up the Discord permission value.
|
||||
|
||||
The properties provided are two way. You can set and retrieve individual
|
||||
@ -58,6 +78,7 @@ class Permissions:
|
||||
|
||||
Returns an iterator of ``(perm, value)`` pairs. This allows it
|
||||
to be, for example, constructed as a dict or a list of pairs.
|
||||
Note that aliases are not shown.
|
||||
|
||||
Attributes
|
||||
-----------
|
||||
@ -67,34 +88,17 @@ class Permissions:
|
||||
permissions via the properties rather than using this raw value.
|
||||
"""
|
||||
|
||||
__slots__ = ('value',)
|
||||
def __init__(self, permissions=0):
|
||||
__slots__ = ()
|
||||
|
||||
def __init__(self, permissions=0, **kwargs):
|
||||
if not isinstance(permissions, int):
|
||||
raise TypeError('Expected int parameter, received %s instead.' % permissions.__class__.__name__)
|
||||
|
||||
self.value = permissions
|
||||
|
||||
def __eq__(self, other):
|
||||
return isinstance(other, Permissions) and self.value == other.value
|
||||
|
||||
def __ne__(self, other):
|
||||
return not self.__eq__(other)
|
||||
|
||||
def __hash__(self):
|
||||
return hash(self.value)
|
||||
|
||||
def __repr__(self):
|
||||
return '<Permissions value=%s>' % self.value
|
||||
|
||||
def _perm_iterator(self):
|
||||
for attr in dir(self):
|
||||
# check if it's a property, because if so it's a permission
|
||||
is_property = isinstance(getattr(self.__class__, attr), property)
|
||||
if is_property:
|
||||
yield (attr, getattr(self, attr))
|
||||
|
||||
def __iter__(self):
|
||||
return self._perm_iterator()
|
||||
for key, value in kwargs.items():
|
||||
if key not in self.VALID_FLAGS:
|
||||
raise TypeError('%r is not a valid permission name.' % key)
|
||||
setattr(self, key, value)
|
||||
|
||||
def is_subset(self, other):
|
||||
"""Returns ``True`` if self has the same or fewer permissions as other."""
|
||||
@ -123,6 +127,14 @@ class Permissions:
|
||||
__lt__ = is_strict_subset
|
||||
__gt__ = is_strict_superset
|
||||
|
||||
def __iter__(self):
|
||||
for name, value in self.__class__.__dict__.items():
|
||||
if isinstance(value, permission_alias):
|
||||
continue
|
||||
|
||||
if isinstance(value, flag_value):
|
||||
yield (name, self._has_flag(value.flag))
|
||||
|
||||
@classmethod
|
||||
def none(cls):
|
||||
"""A factory method that creates a :class:`Permissions` with all
|
||||
@ -182,25 +194,9 @@ class Permissions:
|
||||
A list of key/value pairs to bulk update permissions with.
|
||||
"""
|
||||
for key, value in kwargs.items():
|
||||
try:
|
||||
is_property = isinstance(getattr(self.__class__, key), property)
|
||||
except AttributeError:
|
||||
continue
|
||||
|
||||
if is_property:
|
||||
if key in self.VALID_FLAGS:
|
||||
setattr(self, key, value)
|
||||
|
||||
def _bit(self, index):
|
||||
return bool((self.value >> index) & 1)
|
||||
|
||||
def _set(self, index, value):
|
||||
if value is True:
|
||||
self.value |= (1 << index)
|
||||
elif value is False:
|
||||
self.value &= ~(1 << index)
|
||||
else:
|
||||
raise TypeError('Value to set for Permissions must be a bool.')
|
||||
|
||||
def handle_overwrite(self, allow, deny):
|
||||
# Basically this is what's happening here.
|
||||
# We have an original bit array, e.g. 1010
|
||||
@ -216,129 +212,85 @@ class Permissions:
|
||||
# The OP2 is base | allowed.
|
||||
self.value = (self.value & ~deny) | allow
|
||||
|
||||
@property
|
||||
@flag_value
|
||||
def create_instant_invite(self):
|
||||
""":class:`bool`: Returns ``True`` if the user can create instant invites."""
|
||||
return self._bit(0)
|
||||
return 1 << 0
|
||||
|
||||
@create_instant_invite.setter
|
||||
def create_instant_invite(self, value):
|
||||
self._set(0, value)
|
||||
|
||||
@property
|
||||
@flag_value
|
||||
def kick_members(self):
|
||||
""":class:`bool`: Returns ``True`` if the user can kick users from the guild."""
|
||||
return self._bit(1)
|
||||
return 1 << 1
|
||||
|
||||
@kick_members.setter
|
||||
def kick_members(self, value):
|
||||
self._set(1, value)
|
||||
|
||||
@property
|
||||
@flag_value
|
||||
def ban_members(self):
|
||||
""":class:`bool`: Returns ``True`` if a user can ban users from the guild."""
|
||||
return self._bit(2)
|
||||
return 1 << 2
|
||||
|
||||
@ban_members.setter
|
||||
def ban_members(self, value):
|
||||
self._set(2, value)
|
||||
|
||||
@property
|
||||
@flag_value
|
||||
def administrator(self):
|
||||
""":class:`bool`: Returns ``True`` if a user is an administrator. This role overrides all other permissions.
|
||||
|
||||
This also bypasses all channel-specific overrides.
|
||||
"""
|
||||
return self._bit(3)
|
||||
return 1 << 3
|
||||
|
||||
@administrator.setter
|
||||
def administrator(self, value):
|
||||
self._set(3, value)
|
||||
|
||||
@property
|
||||
@flag_value
|
||||
def manage_channels(self):
|
||||
""":class:`bool`: Returns ``True`` if a user can edit, delete, or create channels in the guild.
|
||||
|
||||
This also corresponds to the "Manage Channel" channel-specific override."""
|
||||
return self._bit(4)
|
||||
return 1 << 4
|
||||
|
||||
@manage_channels.setter
|
||||
def manage_channels(self, value):
|
||||
self._set(4, value)
|
||||
|
||||
@property
|
||||
@flag_value
|
||||
def manage_guild(self):
|
||||
""":class:`bool`: Returns ``True`` if a user can edit guild properties."""
|
||||
return self._bit(5)
|
||||
return 1 << 5
|
||||
|
||||
@manage_guild.setter
|
||||
def manage_guild(self, value):
|
||||
self._set(5, value)
|
||||
|
||||
@property
|
||||
@flag_value
|
||||
def add_reactions(self):
|
||||
""":class:`bool`: Returns ``True`` if a user can add reactions to messages."""
|
||||
return self._bit(6)
|
||||
return 1 << 6
|
||||
|
||||
@add_reactions.setter
|
||||
def add_reactions(self, value):
|
||||
self._set(6, value)
|
||||
|
||||
@property
|
||||
@flag_value
|
||||
def view_audit_log(self):
|
||||
""":class:`bool`: Returns ``True`` if a user can view the guild's audit log."""
|
||||
return self._bit(7)
|
||||
return 1 << 7
|
||||
|
||||
@view_audit_log.setter
|
||||
def view_audit_log(self, value):
|
||||
self._set(7, value)
|
||||
|
||||
@property
|
||||
@flag_value
|
||||
def priority_speaker(self):
|
||||
""":class:`bool`: Returns ``True`` if a user can be more easily heard while talking."""
|
||||
return self._bit(8)
|
||||
return 1 << 8
|
||||
|
||||
@priority_speaker.setter
|
||||
def priority_speaker(self, value):
|
||||
self._set(8, value)
|
||||
|
||||
@property
|
||||
@flag_value
|
||||
def stream(self):
|
||||
""":class:`bool`: Returns ``True`` if a user can stream in a voice channel."""
|
||||
return self._bit(9)
|
||||
return 1 << 9
|
||||
|
||||
@stream.setter
|
||||
def stream(self, value):
|
||||
self._set(9, value)
|
||||
|
||||
@property
|
||||
@flag_value
|
||||
def read_messages(self):
|
||||
""":class:`bool`: Returns ``True`` if a user can read messages from all or specific text channels."""
|
||||
return self._bit(10)
|
||||
return 1 << 10
|
||||
|
||||
@read_messages.setter
|
||||
def read_messages(self, value):
|
||||
self._set(10, value)
|
||||
@make_permission_alias('read_messages')
|
||||
def view_channel(self):
|
||||
""":class:`bool`: An alias for :attr:`read_messages`.
|
||||
|
||||
@property
|
||||
.. versionadded:: 1.3
|
||||
"""
|
||||
return 1 << 10
|
||||
|
||||
@flag_value
|
||||
def send_messages(self):
|
||||
""":class:`bool`: Returns ``True`` if a user can send messages from all or specific text channels."""
|
||||
return self._bit(11)
|
||||
return 1 << 11
|
||||
|
||||
@send_messages.setter
|
||||
def send_messages(self, value):
|
||||
self._set(11, value)
|
||||
|
||||
@property
|
||||
@flag_value
|
||||
def send_tts_messages(self):
|
||||
""":class:`bool`: Returns ``True`` if a user can send TTS messages from all or specific text channels."""
|
||||
return self._bit(12)
|
||||
return 1 << 12
|
||||
|
||||
@send_tts_messages.setter
|
||||
def send_tts_messages(self, value):
|
||||
self._set(12, value)
|
||||
|
||||
@property
|
||||
@flag_value
|
||||
def manage_messages(self):
|
||||
""":class:`bool`: Returns ``True`` if a user can delete or pin messages in a text channel.
|
||||
|
||||
@ -346,189 +298,143 @@ class Permissions:
|
||||
|
||||
Note that there are currently no ways to edit other people's messages.
|
||||
"""
|
||||
return self._bit(13)
|
||||
return 1 << 13
|
||||
|
||||
@manage_messages.setter
|
||||
def manage_messages(self, value):
|
||||
self._set(13, value)
|
||||
|
||||
@property
|
||||
@flag_value
|
||||
def embed_links(self):
|
||||
""":class:`bool`: Returns ``True`` if a user's messages will automatically be embedded by Discord."""
|
||||
return self._bit(14)
|
||||
return 1 << 14
|
||||
|
||||
@embed_links.setter
|
||||
def embed_links(self, value):
|
||||
self._set(14, value)
|
||||
|
||||
@property
|
||||
@flag_value
|
||||
def attach_files(self):
|
||||
""":class:`bool`: Returns ``True`` if a user can send files in their messages."""
|
||||
return self._bit(15)
|
||||
return 1 << 15
|
||||
|
||||
@attach_files.setter
|
||||
def attach_files(self, value):
|
||||
self._set(15, value)
|
||||
|
||||
@property
|
||||
@flag_value
|
||||
def read_message_history(self):
|
||||
""":class:`bool`: Returns ``True`` if a user can read a text channel's previous messages."""
|
||||
return self._bit(16)
|
||||
return 1 << 16
|
||||
|
||||
@read_message_history.setter
|
||||
def read_message_history(self, value):
|
||||
self._set(16, value)
|
||||
|
||||
@property
|
||||
@flag_value
|
||||
def mention_everyone(self):
|
||||
""":class:`bool`: Returns ``True`` if a user's @everyone or @here will mention everyone in the text channel."""
|
||||
return self._bit(17)
|
||||
return 1 << 17
|
||||
|
||||
@mention_everyone.setter
|
||||
def mention_everyone(self, value):
|
||||
self._set(17, value)
|
||||
|
||||
@property
|
||||
@flag_value
|
||||
def external_emojis(self):
|
||||
""":class:`bool`: Returns ``True`` if a user can use emojis from other guilds."""
|
||||
return self._bit(18)
|
||||
return 1 << 18
|
||||
|
||||
@external_emojis.setter
|
||||
def external_emojis(self, value):
|
||||
self._set(18, value)
|
||||
@make_permission_alias('external_emojis')
|
||||
def use_external_emojis(self):
|
||||
""":class:`bool`: An alias for :attr:`external_emojis`.
|
||||
|
||||
@property
|
||||
.. versionadded:: 1.3
|
||||
"""
|
||||
return 1 << 18
|
||||
|
||||
@flag_value
|
||||
def view_guild_insights(self):
|
||||
""":class:`bool`: Returns ``True`` if a user can view the guild's insights.
|
||||
|
||||
|
||||
.. versionadded:: 1.3.0
|
||||
"""
|
||||
return self._bit(19)
|
||||
return 1 << 19
|
||||
|
||||
@view_guild_insights.setter
|
||||
def view_guild_insights(self, value):
|
||||
self._set(19, value)
|
||||
|
||||
@property
|
||||
@flag_value
|
||||
def connect(self):
|
||||
""":class:`bool`: Returns ``True`` if a user can connect to a voice channel."""
|
||||
return self._bit(20)
|
||||
return 1 << 20
|
||||
|
||||
@connect.setter
|
||||
def connect(self, value):
|
||||
self._set(20, value)
|
||||
|
||||
@property
|
||||
@flag_value
|
||||
def speak(self):
|
||||
""":class:`bool`: Returns ``True`` if a user can speak in a voice channel."""
|
||||
return self._bit(21)
|
||||
return 1 << 21
|
||||
|
||||
@speak.setter
|
||||
def speak(self, value):
|
||||
self._set(21, value)
|
||||
|
||||
@property
|
||||
@flag_value
|
||||
def mute_members(self):
|
||||
""":class:`bool`: Returns ``True`` if a user can mute other users."""
|
||||
return self._bit(22)
|
||||
return 1 << 22
|
||||
|
||||
@mute_members.setter
|
||||
def mute_members(self, value):
|
||||
self._set(22, value)
|
||||
|
||||
@property
|
||||
@flag_value
|
||||
def deafen_members(self):
|
||||
""":class:`bool`: Returns ``True`` if a user can deafen other users."""
|
||||
return self._bit(23)
|
||||
return 1 << 23
|
||||
|
||||
@deafen_members.setter
|
||||
def deafen_members(self, value):
|
||||
self._set(23, value)
|
||||
|
||||
@property
|
||||
@flag_value
|
||||
def move_members(self):
|
||||
""":class:`bool`: Returns ``True`` if a user can move users between other voice channels."""
|
||||
return self._bit(24)
|
||||
return 1 << 24
|
||||
|
||||
@move_members.setter
|
||||
def move_members(self, value):
|
||||
self._set(24, value)
|
||||
|
||||
@property
|
||||
@flag_value
|
||||
def use_voice_activation(self):
|
||||
""":class:`bool`: Returns ``True`` if a user can use voice activation in voice channels."""
|
||||
return self._bit(25)
|
||||
return 1 << 25
|
||||
|
||||
@use_voice_activation.setter
|
||||
def use_voice_activation(self, value):
|
||||
self._set(25, value)
|
||||
|
||||
@property
|
||||
@flag_value
|
||||
def change_nickname(self):
|
||||
""":class:`bool`: Returns ``True`` if a user can change their nickname in the guild."""
|
||||
return self._bit(26)
|
||||
return 1 << 26
|
||||
|
||||
@change_nickname.setter
|
||||
def change_nickname(self, value):
|
||||
self._set(26, value)
|
||||
|
||||
@property
|
||||
@flag_value
|
||||
def manage_nicknames(self):
|
||||
""":class:`bool`: Returns ``True`` if a user can change other user's nickname in the guild."""
|
||||
return self._bit(27)
|
||||
return 1 << 27
|
||||
|
||||
@manage_nicknames.setter
|
||||
def manage_nicknames(self, value):
|
||||
self._set(27, value)
|
||||
|
||||
@property
|
||||
@flag_value
|
||||
def manage_roles(self):
|
||||
""":class:`bool`: Returns ``True`` if a user can create or edit roles less than their role's position.
|
||||
|
||||
This also corresponds to the "Manage Permissions" channel-specific override.
|
||||
"""
|
||||
return self._bit(28)
|
||||
return 1 << 28
|
||||
|
||||
@manage_roles.setter
|
||||
def manage_roles(self, value):
|
||||
self._set(28, value)
|
||||
@make_permission_alias('manage_roles')
|
||||
def manage_permissions(self):
|
||||
""":class:`bool`: An alias for :attr:`manage_roles`.
|
||||
|
||||
@property
|
||||
.. versionadded:: 1.3
|
||||
"""
|
||||
return 1 << 28
|
||||
|
||||
@flag_value
|
||||
def manage_webhooks(self):
|
||||
""":class:`bool`: Returns ``True`` if a user can create, edit, or delete webhooks."""
|
||||
return self._bit(29)
|
||||
return 1 << 29
|
||||
|
||||
@manage_webhooks.setter
|
||||
def manage_webhooks(self, value):
|
||||
self._set(29, value)
|
||||
|
||||
@property
|
||||
@flag_value
|
||||
def manage_emojis(self):
|
||||
""":class:`bool`: Returns ``True`` if a user can create, edit, or delete emojis."""
|
||||
return self._bit(30)
|
||||
|
||||
@manage_emojis.setter
|
||||
def manage_emojis(self, value):
|
||||
self._set(30, value)
|
||||
return 1 << 30
|
||||
|
||||
# 1 unused
|
||||
|
||||
# after these 32 bits, there's 21 more unused ones technically
|
||||
|
||||
def augment_from_permissions(cls):
|
||||
cls.VALID_NAMES = {name for name in dir(Permissions) if isinstance(getattr(Permissions, name), property)}
|
||||
cls.VALID_NAMES = set(Permissions.VALID_FLAGS)
|
||||
aliases = set()
|
||||
|
||||
# make descriptors for all the valid names and aliases
|
||||
for name, value in Permissions.__dict__.items():
|
||||
if isinstance(value, permission_alias):
|
||||
key = value.alias
|
||||
aliases.add(name)
|
||||
elif isinstance(value, flag_value):
|
||||
key = name
|
||||
else:
|
||||
continue
|
||||
|
||||
# make descriptors for all the valid names
|
||||
for name in cls.VALID_NAMES:
|
||||
# god bless Python
|
||||
def getter(self, x=name):
|
||||
def getter(self, x=key):
|
||||
return self._values.get(x)
|
||||
def setter(self, value, x=name):
|
||||
def setter(self, value, x=key):
|
||||
self._set(x, value)
|
||||
|
||||
prop = property(getter, setter)
|
||||
setattr(cls, name, prop)
|
||||
|
||||
cls.PURE_FLAGS = cls.VALID_NAMES - aliases
|
||||
return cls
|
||||
|
||||
@augment_from_permissions
|
||||
@ -544,20 +450,19 @@ class PermissionOverwrite:
|
||||
The values supported by this are the same as :class:`Permissions`
|
||||
with the added possibility of it being set to ``None``.
|
||||
|
||||
Supported operations:
|
||||
.. container:: operations
|
||||
|
||||
+-----------+------------------------------------------+
|
||||
| Operation | Description |
|
||||
+===========+==========================================+
|
||||
| x == y | Checks if two overwrites are equal. |
|
||||
+-----------+------------------------------------------+
|
||||
| x != y | Checks if two overwrites are not equal. |
|
||||
+-----------+------------------------------------------+
|
||||
| iter(x) | Returns an iterator of (perm, value) |
|
||||
| | pairs. This allows this class to be used |
|
||||
| | as an iterable in e.g. set/list/dict |
|
||||
| | constructions. |
|
||||
+-----------+------------------------------------------+
|
||||
.. describe:: x == y
|
||||
|
||||
Checks if two overwrites are equal.
|
||||
.. describe:: x != y
|
||||
|
||||
Checks if two overwrites are not equal.
|
||||
.. describe:: iter(x)
|
||||
|
||||
Returns an iterator of ``(perm, value)`` pairs. This allows it
|
||||
to be, for example, constructed as a dict or a list of pairs.
|
||||
Note that aliases are not shown.
|
||||
|
||||
Parameters
|
||||
-----------
|
||||
@ -643,5 +548,5 @@ class PermissionOverwrite:
|
||||
setattr(self, key, value)
|
||||
|
||||
def __iter__(self):
|
||||
for key in self.VALID_NAMES:
|
||||
for key in self.PURE_FLAGS:
|
||||
yield key, self._values.get(key)
|
||||
|
Loading…
x
Reference in New Issue
Block a user