From 044b0824e68c4dacdaf26ff52a741ca1b5118c9b Mon Sep 17 00:00:00 2001 From: Rapptz Date: Sun, 9 Oct 2016 01:05:07 -0400 Subject: Begin working on the rewrite. --- discord/member.py | 130 +++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 113 insertions(+), 17 deletions(-) (limited to 'discord/member.py') diff --git a/discord/member.py b/discord/member.py index 50ad184a..4d0f86b9 100644 --- a/discord/member.py +++ b/discord/member.py @@ -28,9 +28,11 @@ from .user import User from .game import Game from .permissions import Permissions from . import utils -from .enums import Status, ChannelType +from .enums import Status, ChannelType, try_enum from .colour import Colour + import copy +import inspect class VoiceState: """Represents a Discord user's voice state. @@ -52,8 +54,8 @@ class VoiceState: is not currently in a voice channel. """ - __slots__ = [ 'session_id', 'deaf', 'mute', 'self_mute', - 'self_deaf', 'is_afk', 'voice_channel' ] + __slots__ = ( 'session_id', 'deaf', 'mute', 'self_mute', + 'self_deaf', 'is_afk', 'voice_channel' ) def __init__(self, **kwargs): self.session_id = kwargs.get('session_id') @@ -74,12 +76,57 @@ def flatten_voice_states(cls): setattr(cls, attr, property(getter)) return cls +def flatten_user(cls): + for attr, value in User.__dict__.items(): + # ignore private/special methods + if attr.startswith('_'): + continue + + # don't override what we already have + if attr in cls.__dict__: + continue + + # if it's a slotted attribute or a property, redirect it + # slotted members are implemented as member_descriptors in Type.__dict__ + if hasattr(value, '__get__'): + def getter(self, x=attr): + return getattr(self._user, x) + setattr(cls, attr, property(getter, doc='Equivalent to :attr:`User.%s`' % attr)) + else: + # probably a member function by now + def generate_function(x): + def general(self, *args, **kwargs): + return getattr(self._user, x)(*args, **kwargs) + + general.__name__ = x + return general + + func = generate_function(attr) + func.__doc__ = value.__doc__ + setattr(cls, attr, func) + + return cls + @flatten_voice_states -class Member(User): +@flatten_user +class Member: """Represents a Discord member to a :class:`Server`. - This is a subclass of :class:`User` that extends more functionality - that server members have such as roles and permissions. + This implements a lot of the functionality of :class:`User`. + + Supported Operations: + + +-----------+-----------------------------------------------+ + | Operation | Description | + +===========+===============================================+ + | x == y | Checks if two members are equal. | + +-----------+-----------------------------------------------+ + | x != y | Checks if two members are not equal. | + +-----------+-----------------------------------------------+ + | hash(x) | Return the member's hash. | + +-----------+-----------------------------------------------+ + | str(x) | Returns the member's name with discriminator. | + +-----------+-----------------------------------------------+ Attributes ---------- @@ -103,18 +150,31 @@ class Member(User): The server specific nickname of the user. """ - __slots__ = [ 'roles', 'joined_at', 'status', 'game', 'server', 'nick', 'voice' ] + __slots__ = ('roles', 'joined_at', 'status', 'game', 'server', 'nick', 'voice', '_user', '_state') - def __init__(self, **kwargs): - super().__init__(**kwargs.get('user')) - self.voice = VoiceState(**kwargs) - self.joined_at = utils.parse_time(kwargs.get('joined_at')) - self.roles = kwargs.get('roles', []) + def __init__(self, *, data, server, state): + self._state = state + self._user = state.try_insert_user(data['user']) + self.voice = VoiceState(**data) + self.joined_at = utils.parse_time(data.get('joined_at')) + self.roles = data.get('roles', []) self.status = Status.offline - game = kwargs.get('game', {}) + game = data.get('game', {}) self.game = Game(**game) if game else None - self.server = kwargs.get('server', None) - self.nick = kwargs.get('nick', None) + self.server = server + self.nick = data.get('nick', None) + + def __str__(self): + return self._user.__str__() + + def __eq__(self, other): + return isinstance(other, Member) and other._user.id == self._user.id and self.server.id == other.server.id + + def __ne__(self, other): + return not self.__eq__(other) + + def __hash__(self): + return hash(self._user.id) def _update_voice_state(self, **kwargs): self.voice.self_mute = kwargs.get('self_mute', False) @@ -146,6 +206,35 @@ class Member(User): ret.voice = copy.copy(self.voice) return ret + def _update(self, data, user): + self._user.name = user['username'] + self._user.discriminator = user['discriminator'] + self._user.avatar = user['avatar'] + self._user.bot = user.get('bot', False) + + # the nickname change is optional, + # if it isn't in the payload then it didn't change + if 'nick' in data: + self.nick = data['nick'] + + # update the roles + self.roles = [self.server.default_role] + for role in self.server.roles: + if role.id in data['roles']: + self.roles.append(role) + + # sort the roles by ID since they can be "randomised" + self.roles.sort(key=lambda r: int(r.id)) + + def _presence_update(self, data, user): + self.status = try_enum(Status, data['status']) + game = data.get('game', {}) + self.game = Game(**game) if game else None + u = self._user + u.name = user.get('username', u.name) + u.avatar = user.get('avatar', u.avatar) + u.discriminator = user.get('discriminator', u.discriminator) + @property def colour(self): """A property that returns a :class:`Colour` denoting the rendered colour @@ -173,13 +262,20 @@ class Member(User): @property def mention(self): + """Returns a string that mentions the member.""" if self.nick: return '<@!{}>'.format(self.id) return '<@{}>'.format(self.id) def mentioned_in(self, message): - mentioned = super().mentioned_in(message) - if mentioned: + """Checks if the member is mentioned in the specified message. + + Parameters + ----------- + message: :class:`Message` + The message to check if you're mentioned in. + """ + if self._user.mentioned_in(message): return True for role in message.role_mentions: -- cgit v1.2.3