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:
		@@ -25,6 +25,10 @@ DEALINGS IN THE SOFTWARE.
 | 
			
		||||
 | 
			
		||||
from copy import deepcopy
 | 
			
		||||
from . import utils
 | 
			
		||||
from .permissions import Permissions
 | 
			
		||||
from collections import namedtuple
 | 
			
		||||
 | 
			
		||||
MemberOverwrite = namedtuple('MemberOverwrite', ['id', 'allow', 'deny'])
 | 
			
		||||
 | 
			
		||||
class Channel(object):
 | 
			
		||||
    """Represents a Discord server channel.
 | 
			
		||||
@@ -75,7 +79,13 @@ class Channel(object):
 | 
			
		||||
        self.position = kwargs.get('position')
 | 
			
		||||
        self.type = kwargs.get('type')
 | 
			
		||||
        self.changed_roles = []
 | 
			
		||||
        self._user_permissions = []
 | 
			
		||||
        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
 | 
			
		||||
            role = utils.find(lambda r: r.id == overridden['id'], self.server.roles)
 | 
			
		||||
            if role is None:
 | 
			
		||||
@@ -84,22 +94,77 @@ class Channel(object):
 | 
			
		||||
            denied = overridden.get('deny', 0)
 | 
			
		||||
            allowed = overridden.get('allow', 0)
 | 
			
		||||
            override = deepcopy(role)
 | 
			
		||||
 | 
			
		||||
            # 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
 | 
			
		||||
            override.permissions.handle_overwrite(allowed, denied)
 | 
			
		||||
            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):
 | 
			
		||||
    """Represents a Discord private channel.
 | 
			
		||||
 | 
			
		||||
@@ -121,3 +186,27 @@ class PrivateChannel(object):
 | 
			
		||||
        self.id = id
 | 
			
		||||
        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
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user