Add Channel.permissions_for and PrivateChannel.permissions_for.
These functions handle permission resolution for a specific member. Aids with #18.
This commit is contained in:
parent
2813652995
commit
61f62c1468
@ -25,6 +25,10 @@ DEALINGS IN THE SOFTWARE.
|
|||||||
|
|
||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
from . import utils
|
from . import utils
|
||||||
|
from .permissions import Permissions
|
||||||
|
from collections import namedtuple
|
||||||
|
|
||||||
|
MemberOverwrite = namedtuple('MemberOverwrite', ['id', 'allow', 'deny'])
|
||||||
|
|
||||||
class Channel(object):
|
class Channel(object):
|
||||||
"""Represents a Discord server channel.
|
"""Represents a Discord server channel.
|
||||||
@ -75,7 +79,13 @@ class Channel(object):
|
|||||||
self.position = kwargs.get('position')
|
self.position = kwargs.get('position')
|
||||||
self.type = kwargs.get('type')
|
self.type = kwargs.get('type')
|
||||||
self.changed_roles = []
|
self.changed_roles = []
|
||||||
|
self._user_permissions = []
|
||||||
for overridden in kwargs.get('permission_overwrites', []):
|
for overridden in kwargs.get('permission_overwrites', []):
|
||||||
|
if overridden.get('type') == 'member':
|
||||||
|
del overridden['type']
|
||||||
|
self._user_permissions.append(MemberOverwrite(**overridden))
|
||||||
|
continue
|
||||||
|
|
||||||
# this is pretty inefficient due to the deep nested loops unfortunately
|
# this is pretty inefficient due to the deep nested loops unfortunately
|
||||||
role = utils.find(lambda r: r.id == overridden['id'], self.server.roles)
|
role = utils.find(lambda r: r.id == overridden['id'], self.server.roles)
|
||||||
if role is None:
|
if role is None:
|
||||||
@ -84,22 +94,77 @@ class Channel(object):
|
|||||||
denied = overridden.get('deny', 0)
|
denied = overridden.get('deny', 0)
|
||||||
allowed = overridden.get('allow', 0)
|
allowed = overridden.get('allow', 0)
|
||||||
override = deepcopy(role)
|
override = deepcopy(role)
|
||||||
|
override.permissions.handle_overwrite(allowed, denied)
|
||||||
# Basically this is what's happening here.
|
|
||||||
# We have an original bit array, e.g. 1010
|
|
||||||
# Then we have another bit array that is 'denied', e.g. 1111
|
|
||||||
# And then we have the last one which is 'allowed', e.g. 0101
|
|
||||||
# We want original OP denied to end up resulting in
|
|
||||||
# whatever is in denied to be set to 0.
|
|
||||||
# So 1010 OP 1111 -> 0000
|
|
||||||
# Then we take this value and look at the allowed values.
|
|
||||||
# And whatever is allowed is set to 1.
|
|
||||||
# So 0000 OP2 0101 -> 0101
|
|
||||||
# The OP is (base ^ denied) & ~denied.
|
|
||||||
# The OP2 is base | allowed.
|
|
||||||
override.permissions.value = ((override.permissions.value ^ denied) & ~denied) | allowed
|
|
||||||
self.changed_roles.append(override)
|
self.changed_roles.append(override)
|
||||||
|
|
||||||
|
def is_default_channel(self):
|
||||||
|
"""Checks if this is the default channel for the :class:`Server` it belongs to."""
|
||||||
|
return self.server.id == self.id
|
||||||
|
|
||||||
|
def permissions_for(self, member):
|
||||||
|
"""Handles permission resolution for the current :class:`Member`.
|
||||||
|
|
||||||
|
This function takes into consideration the following cases:
|
||||||
|
|
||||||
|
- Server owner
|
||||||
|
- Server roles
|
||||||
|
- Channel overrides
|
||||||
|
- Member overrides
|
||||||
|
- Whether the channel is the default channel.
|
||||||
|
|
||||||
|
:param member: The :class:`Member` to resolve permissions for.
|
||||||
|
:return: The resolved :class:`Permissions` for the :class:`Member`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# The current cases can be explained as:
|
||||||
|
# Server owner get all permissions -- no questions asked. Otherwise...
|
||||||
|
# The @everyone role gets the first application.
|
||||||
|
# After that, the applied roles that the user has in the channel
|
||||||
|
# (or otherwise) are then OR'd together.
|
||||||
|
# After the role permissions are resolved, the member permissions
|
||||||
|
# have to take into effect.
|
||||||
|
# After all that is done.. you have to do the following:
|
||||||
|
|
||||||
|
# If manage permissions is True, then all permissions are set to
|
||||||
|
# True. If the channel is the default channel then everyone gets
|
||||||
|
# read permissions regardless.
|
||||||
|
|
||||||
|
# The operation first takes into consideration the denied
|
||||||
|
# and then the allowed.
|
||||||
|
|
||||||
|
if member.id == self.server.owner.id:
|
||||||
|
return Permissions.ALL
|
||||||
|
|
||||||
|
base = self.server.get_default_role().permissions
|
||||||
|
|
||||||
|
# Apply server roles that the member has.
|
||||||
|
for role in member.roles:
|
||||||
|
denied = ~role.permissions.value
|
||||||
|
base.handle_overwrite(allow=role.permissions.value, deny=denied)
|
||||||
|
|
||||||
|
# Server-wide Manage Roles -> True for everything
|
||||||
|
if base.can_manage_roles:
|
||||||
|
base = Permissions.ALL
|
||||||
|
|
||||||
|
# Apply channel specific permission overwrites
|
||||||
|
for role in self.changed_roles:
|
||||||
|
denied = ~role.permissions.value
|
||||||
|
base.handle_overwrite(allow=role.permissions.value, deny=denied)
|
||||||
|
|
||||||
|
# Apply member specific permission overwrites
|
||||||
|
for overwrite in self._user_permissions:
|
||||||
|
if overwrite.id == member.id:
|
||||||
|
base.handle_overwrite(allow=overwrite.allow, deny=overwrite.deny)
|
||||||
|
|
||||||
|
if base.can_manage_roles:
|
||||||
|
# This point is essentially Channel-specific Manage Roles.
|
||||||
|
base.value |= Permissions.ALL_CHANNEL.value
|
||||||
|
|
||||||
|
if self.is_default_channel():
|
||||||
|
base.can_read_messages = True
|
||||||
|
|
||||||
|
return base
|
||||||
|
|
||||||
class PrivateChannel(object):
|
class PrivateChannel(object):
|
||||||
"""Represents a Discord private channel.
|
"""Represents a Discord private channel.
|
||||||
|
|
||||||
@ -121,3 +186,27 @@ class PrivateChannel(object):
|
|||||||
self.id = id
|
self.id = id
|
||||||
self.is_private = True
|
self.is_private = True
|
||||||
|
|
||||||
|
def permissions_for(user):
|
||||||
|
"""Handles permission resolution for a :class:`User`.
|
||||||
|
|
||||||
|
This function is there for compatibility with :class:`Channel`.
|
||||||
|
|
||||||
|
Actual private messages do not really have the concept of permissions.
|
||||||
|
|
||||||
|
This returns all the Text related permissions set to true except:
|
||||||
|
|
||||||
|
- can_send_tts_messages: You cannot send TTS messages in a PM.
|
||||||
|
- can_manage_messages: You cannot delete others messages in a PM.
|
||||||
|
- can_mention_everyone: There is no one to mention in a PM.
|
||||||
|
|
||||||
|
:param user: The :class:`User` to check permissions for.
|
||||||
|
:return: A :class:`Permission` with the resolved permission value.
|
||||||
|
"""
|
||||||
|
|
||||||
|
base = Permissions.TEXT
|
||||||
|
base.can_send_tts_messages = False
|
||||||
|
base.can_manage_messages = False
|
||||||
|
base.can_mention_everyone = False
|
||||||
|
return base
|
||||||
|
|
||||||
|
|
||||||
|
@ -24,6 +24,16 @@ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|||||||
DEALINGS IN THE SOFTWARE.
|
DEALINGS IN THE SOFTWARE.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
def create_permission_masks(cls):
|
||||||
|
cls.NONE = cls(0)
|
||||||
|
cls.ALL = cls(0b00000011111100111111110000111111)
|
||||||
|
cls.ALL_CHANNEL = cls(0b00000011111100111111110000011001)
|
||||||
|
cls.GENERAL = cls(0b00000000000000000000000000111111)
|
||||||
|
cls.TEXT = cls(0b00000000000000111111110000000000)
|
||||||
|
cls.VOICE = cls(0b00000011111100000000000000000000)
|
||||||
|
return cls
|
||||||
|
|
||||||
|
@create_permission_masks
|
||||||
class Permissions(object):
|
class Permissions(object):
|
||||||
"""Wraps up the Discord permission value.
|
"""Wraps up the Discord permission value.
|
||||||
|
|
||||||
@ -53,6 +63,21 @@ class Permissions(object):
|
|||||||
else:
|
else:
|
||||||
raise TypeError('Value to set for Permissions must be a bool.')
|
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
|
||||||
|
# Then we have another bit array that is 'denied', e.g. 1111
|
||||||
|
# And then we have the last one which is 'allowed', e.g. 0101
|
||||||
|
# We want original OP denied to end up resulting in
|
||||||
|
# whatever is in denied to be set to 0.
|
||||||
|
# So 1010 OP 1111 -> 0000
|
||||||
|
# Then we take this value and look at the allowed values.
|
||||||
|
# And whatever is allowed is set to 1.
|
||||||
|
# So 0000 OP2 0101 -> 0101
|
||||||
|
# The OP is (base ^ denied) & ~denied.
|
||||||
|
# The OP2 is base | allowed.
|
||||||
|
self.value = ((self.value ^ deny) & ~deny) | allow
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def can_create_instant_invite(self):
|
def can_create_instant_invite(self):
|
||||||
"""Returns True if the user can create instant invites."""
|
"""Returns True if the user can create instant invites."""
|
||||||
|
Loading…
x
Reference in New Issue
Block a user