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 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.""" | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user