Make ClientUser separate from a regular User.
This removes Client.edit_profile in favour of ClientUser.edit.
This commit is contained in:
		| @@ -18,7 +18,7 @@ __copyright__ = 'Copyright 2015-2016 Rapptz' | ||||
| __version__ = '1.0.0a0' | ||||
|  | ||||
| from .client import Client, AppInfo, ChannelPermissions | ||||
| from .user import User | ||||
| from .user import User, ClientUser | ||||
| from .game import Game | ||||
| from .emoji import Emoji, PartialEmoji | ||||
| from .channel import * | ||||
|   | ||||
| @@ -99,7 +99,7 @@ class Client: | ||||
|  | ||||
|     Attributes | ||||
|     ----------- | ||||
|     user : Optional[:class:`User`] | ||||
|     user : Optional[:class:`ClientUser`] | ||||
|         Represents the connected client. None if not logged in. | ||||
|     voice_clients: List[:class:`VoiceClient`] | ||||
|         Represents a list of voice connections. To connect to voice use | ||||
| @@ -814,84 +814,6 @@ class Client: | ||||
|  | ||||
|         yield from self.ws.send_as_json(payload) | ||||
|  | ||||
|     @asyncio.coroutine | ||||
|     def edit_profile(self, password=None, **fields): | ||||
|         """|coro| | ||||
|  | ||||
|         Edits the current profile of the client. | ||||
|  | ||||
|         If a bot account is used then the password field is optional, | ||||
|         otherwise it is required. | ||||
|  | ||||
|         The :attr:`Client.user` object is not modified directly afterwards until the | ||||
|         corresponding WebSocket event is received. | ||||
|  | ||||
|         Note | ||||
|         ----- | ||||
|         To upload an avatar, a *bytes-like object* must be passed in that | ||||
|         represents the image being uploaded. If this is done through a file | ||||
|         then the file must be opened via ``open('some_filename', 'rb')`` and | ||||
|         the *bytes-like object* is given through the use of ``fp.read()``. | ||||
|  | ||||
|         The only image formats supported for uploading is JPEG and PNG. | ||||
|  | ||||
|         Parameters | ||||
|         ----------- | ||||
|         password : str | ||||
|             The current password for the client's account. Not used | ||||
|             for bot accounts. | ||||
|         new_password : str | ||||
|             The new password you wish to change to. | ||||
|         email : str | ||||
|             The new email you wish to change to. | ||||
|         username :str | ||||
|             The new username you wish to change to. | ||||
|         avatar : bytes | ||||
|             A *bytes-like object* representing the image to upload. | ||||
|             Could be ``None`` to denote no avatar. | ||||
|  | ||||
|         Raises | ||||
|         ------ | ||||
|         HTTPException | ||||
|             Editing your profile failed. | ||||
|         InvalidArgument | ||||
|             Wrong image format passed for ``avatar``. | ||||
|         ClientException | ||||
|             Password is required for non-bot accounts. | ||||
|         """ | ||||
|  | ||||
|         try: | ||||
|             avatar_bytes = fields['avatar'] | ||||
|         except KeyError: | ||||
|             avatar = self.user.avatar | ||||
|         else: | ||||
|             if avatar_bytes is not None: | ||||
|                 avatar = utils._bytes_to_base64_data(avatar_bytes) | ||||
|             else: | ||||
|                 avatar = None | ||||
|  | ||||
|         not_bot_account = not self.user.bot | ||||
|         if not_bot_account and password is None: | ||||
|             raise ClientException('Password is required for non-bot accounts.') | ||||
|  | ||||
|         args = { | ||||
|             'password': password, | ||||
|             'username': fields.get('username', self.user.name), | ||||
|             'avatar': avatar | ||||
|         } | ||||
|  | ||||
|         if not_bot_account: | ||||
|             args['email'] = fields.get('email', self.email) | ||||
|  | ||||
|             if 'new_password' in fields: | ||||
|                 args['new_password'] = fields['new_password'] | ||||
|  | ||||
|         data = yield from self.http.edit_profile(**args) | ||||
|         if not_bot_account: | ||||
|             self.email = data['email'] | ||||
|             if 'token' in data: | ||||
|                 self.http._token(data['token'], bot=False) | ||||
|  | ||||
|     @asyncio.coroutine | ||||
|     def change_presence(self, *, game=None, status=None, afk=False): | ||||
|         """|coro| | ||||
|   | ||||
| @@ -29,7 +29,7 @@ import asyncio | ||||
| import discord.abc | ||||
|  | ||||
| from . import utils | ||||
| from .user import User | ||||
| from .user import BaseUser | ||||
| from .game import Game | ||||
| from .permissions import Permissions | ||||
| from .enums import Status, ChannelType, try_enum | ||||
| @@ -74,7 +74,7 @@ class VoiceState: | ||||
|         return '<VoiceState self_mute={0.self_mute} self_deaf={0.self_deaf} channel={0.channel!r}>'.format(self) | ||||
|  | ||||
| def flatten_user(cls): | ||||
|     for attr, value in User.__dict__.items(): | ||||
|     for attr, value in BaseUser.__dict__.items(): | ||||
|         # ignore private/special methods | ||||
|         if attr.startswith('_'): | ||||
|             continue | ||||
|   | ||||
| @@ -25,7 +25,7 @@ DEALINGS IN THE SOFTWARE. | ||||
| """ | ||||
|  | ||||
| from .guild import Guild | ||||
| from .user import User | ||||
| from .user import User, ClientUser | ||||
| from .game import Game | ||||
| from .emoji import Emoji, PartialEmoji | ||||
| from .reaction import Reaction | ||||
| @@ -239,7 +239,7 @@ class ConnectionState: | ||||
|  | ||||
|     def parse_ready(self, data): | ||||
|         self._ready_state = ReadyState(launch=asyncio.Event(), guilds=[]) | ||||
|         self.user = self.store_user(data['user']) | ||||
|         self.user = ClientUser(state=self, data=data['user']) | ||||
|  | ||||
|         guilds = self._ready_state.guilds | ||||
|         for guild_data in data['guilds']: | ||||
| @@ -339,7 +339,7 @@ class ConnectionState: | ||||
|         self.dispatch('member_update', old_member, member) | ||||
|  | ||||
|     def parse_user_update(self, data): | ||||
|         self.user = User(state=self, data=data) | ||||
|         self.user = ClientUser(state=self, data=data) | ||||
|  | ||||
|     def parse_channel_delete(self, data): | ||||
|         guild =  self._get_guild(utils._get_as_snowflake(data, 'guild_id')) | ||||
|   | ||||
							
								
								
									
										275
									
								
								discord/user.py
									
									
									
									
									
								
							
							
						
						
									
										275
									
								
								discord/user.py
									
									
									
									
									
								
							| @@ -24,44 +24,15 @@ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | ||||
| DEALINGS IN THE SOFTWARE. | ||||
| """ | ||||
|  | ||||
| from .utils import snowflake_time | ||||
| from .utils import snowflake_time, _bytes_to_base64_data | ||||
| from .enums import DefaultAvatar | ||||
| from .errors import ClientException | ||||
|  | ||||
| import discord.abc | ||||
| import asyncio | ||||
|  | ||||
| class User(discord.abc.Messageable): | ||||
|     """Represents a Discord user. | ||||
|  | ||||
|     Supported Operations: | ||||
|  | ||||
|     +-----------+---------------------------------------------+ | ||||
|     | Operation |                 Description                 | | ||||
|     +===========+=============================================+ | ||||
|     | x == y    | Checks if two users are equal.              | | ||||
|     +-----------+---------------------------------------------+ | ||||
|     | x != y    | Checks if two users are not equal.          | | ||||
|     +-----------+---------------------------------------------+ | ||||
|     | hash(x)   | Return the user's hash.                     | | ||||
|     +-----------+---------------------------------------------+ | ||||
|     | str(x)    | Returns the user's name with discriminator. | | ||||
|     +-----------+---------------------------------------------+ | ||||
|  | ||||
|     Attributes | ||||
|     ----------- | ||||
|     name: str | ||||
|         The user's username. | ||||
|     id: int | ||||
|         The user's unique ID. | ||||
|     discriminator: str | ||||
|         The user's discriminator. This is given when the username has conflicts. | ||||
|     avatar: str | ||||
|         The avatar hash the user has. Could be None. | ||||
|     bot: bool | ||||
|         Specifies if the user is a bot account. | ||||
|     """ | ||||
|  | ||||
|     __slots__ = ('name', 'id', 'discriminator', 'avatar', 'bot', '_state', '__weakref__') | ||||
| class BaseUser: | ||||
|     __slots__ = ('name', 'id', 'discriminator', 'avatar', 'bot', '_state') | ||||
|  | ||||
|     def __init__(self, *, state, data): | ||||
|         self._state = state | ||||
| @@ -75,7 +46,7 @@ class User(discord.abc.Messageable): | ||||
|         return '{0.name}#{0.discriminator}'.format(self) | ||||
|  | ||||
|     def __eq__(self, other): | ||||
|         return isinstance(other, User) and other.id == self.id | ||||
|         return isinstance(other, BaseUser) and other.id == self.id | ||||
|  | ||||
|     def __ne__(self, other): | ||||
|         return not self.__eq__(other) | ||||
| @@ -83,38 +54,6 @@ class User(discord.abc.Messageable): | ||||
|     def __hash__(self): | ||||
|         return self.id >> 22 | ||||
|  | ||||
|     def __repr__(self): | ||||
|         return '<User id={0.id} name={0.name!r} discriminator={0.discriminator!r} bot={0.bot}>'.format(self) | ||||
|  | ||||
|     @asyncio.coroutine | ||||
|     def _get_channel(self): | ||||
|         ch = yield from self.create_dm() | ||||
|         return ch | ||||
|  | ||||
|     @property | ||||
|     def dm_channel(self): | ||||
|         """Returns the :class:`DMChannel` associated with this user if it exists. | ||||
|  | ||||
|         If this returns ``None``, you can create a DM channel by calling the | ||||
|         :meth:`create_dm` coroutine function. | ||||
|         """ | ||||
|         return self._state._get_private_channel_by_user(self.id) | ||||
|  | ||||
|     @asyncio.coroutine | ||||
|     def create_dm(self): | ||||
|         """Creates a :class:`DMChannel` with this user. | ||||
|  | ||||
|         This should be rarely called, as this is done transparently for most | ||||
|         people. | ||||
|         """ | ||||
|         found = self.dm_channel | ||||
|         if found is not None: | ||||
|             return found | ||||
|  | ||||
|         state = self._state | ||||
|         data = yield from state.http.start_private_message(self.id) | ||||
|         return state.add_dm_channel(data) | ||||
|  | ||||
|     @property | ||||
|     def avatar_url(self): | ||||
|         """Returns a friendly URL version of the avatar the user has. | ||||
| @@ -191,7 +130,207 @@ class User(discord.abc.Messageable): | ||||
|         if message.mention_everyone: | ||||
|             return True | ||||
|  | ||||
|         if self in message.mentions: | ||||
|             return True | ||||
|         for user in message.mentions: | ||||
|             if user.id == self.id: | ||||
|                 return True | ||||
|  | ||||
|         return False | ||||
|  | ||||
| class ClientUser(BaseUser): | ||||
|     """Represents your Discord user. | ||||
|  | ||||
|     Supported Operations: | ||||
|  | ||||
|     +-----------+---------------------------------------------+ | ||||
|     | Operation |                 Description                 | | ||||
|     +===========+=============================================+ | ||||
|     | x == y    | Checks if two users are equal.              | | ||||
|     +-----------+---------------------------------------------+ | ||||
|     | x != y    | Checks if two users are not equal.          | | ||||
|     +-----------+---------------------------------------------+ | ||||
|     | hash(x)   | Return the user's hash.                     | | ||||
|     +-----------+---------------------------------------------+ | ||||
|     | str(x)    | Returns the user's name with discriminator. | | ||||
|     +-----------+---------------------------------------------+ | ||||
|  | ||||
|     Attributes | ||||
|     ----------- | ||||
|     name: str | ||||
|         The user's username. | ||||
|     id: int | ||||
|         The user's unique ID. | ||||
|     discriminator: str | ||||
|         The user's discriminator. This is given when the username has conflicts. | ||||
|     avatar: str | ||||
|         The avatar hash the user has. Could be None. | ||||
|     bot: bool | ||||
|         Specifies if the user is a bot account. | ||||
|     verified: bool | ||||
|         Specifies if the user is a verified account. | ||||
|     email: Optional[str] | ||||
|         The email the user used when registering. | ||||
|     mfa_enabled: bool | ||||
|         Specifies if the user has MFA turned on and working. | ||||
|     """ | ||||
|     __slots__ = ('email', 'verified', 'mfa_enabled') | ||||
|  | ||||
|     def __init__(self, *, state, data): | ||||
|         super().__init__(state=state, data=data) | ||||
|         self.verified = data.get('verified', False) | ||||
|         self.email = data.get('email') | ||||
|         self.mfa_enabled = data.get('mfa_enabled', False) | ||||
|  | ||||
|     def __repr__(self): | ||||
|         return '<ClientUser id={0.id} name={0.name!r} discriminator={0.discriminator!r}' \ | ||||
|                ' bot={0.bot} verified={0.verified} mfa_enabled={0.mfa_enabled}>'.format(self) | ||||
|  | ||||
|  | ||||
|     @asyncio.coroutine | ||||
|     def edit(self, **fields): | ||||
|         """|coro| | ||||
|  | ||||
|         Edits the current profile of the client. | ||||
|  | ||||
|         If a bot account is used then a password field is optional, | ||||
|         otherwise it is required. | ||||
|  | ||||
|         Note | ||||
|         ----- | ||||
|         To upload an avatar, a *bytes-like object* must be passed in that | ||||
|         represents the image being uploaded. If this is done through a file | ||||
|         then the file must be opened via ``open('some_filename', 'rb')`` and | ||||
|         the *bytes-like object* is given through the use of ``fp.read()``. | ||||
|  | ||||
|         The only image formats supported for uploading is JPEG and PNG. | ||||
|  | ||||
|         Parameters | ||||
|         ----------- | ||||
|         password : str | ||||
|             The current password for the client's account. | ||||
|             Only applicable to user accounts. | ||||
|         new_password: str | ||||
|             The new password you wish to change to. | ||||
|             Only applicable to user accounts. | ||||
|         email: str | ||||
|             The new email you wish to change to. | ||||
|             Only applicable to user accounts. | ||||
|         username :str | ||||
|             The new username you wish to change to. | ||||
|         avatar: bytes | ||||
|             A *bytes-like object* representing the image to upload. | ||||
|             Could be ``None`` to denote no avatar. | ||||
|  | ||||
|         Raises | ||||
|         ------ | ||||
|         HTTPException | ||||
|             Editing your profile failed. | ||||
|         InvalidArgument | ||||
|             Wrong image format passed for ``avatar``. | ||||
|         ClientException | ||||
|             Password is required for non-bot accounts. | ||||
|         """ | ||||
|  | ||||
|         try: | ||||
|             avatar_bytes = fields['avatar'] | ||||
|         except KeyError: | ||||
|             avatar = self.avatar | ||||
|         else: | ||||
|             if avatar_bytes is not None: | ||||
|                 avatar = _bytes_to_base64_data(avatar_bytes) | ||||
|             else: | ||||
|                 avatar = None | ||||
|  | ||||
|         not_bot_account = not self.bot | ||||
|         password = fields.get('password') | ||||
|         if not_bot_account and password is None: | ||||
|             raise ClientException('Password is required for non-bot accounts.') | ||||
|  | ||||
|         args = { | ||||
|             'password': password, | ||||
|             'username': fields.get('username', self.name), | ||||
|             'avatar': avatar | ||||
|         } | ||||
|  | ||||
|         if not_bot_account: | ||||
|             args['email'] = fields.get('email', self.email) | ||||
|  | ||||
|             if 'new_password' in fields: | ||||
|                 args['new_password'] = fields['new_password'] | ||||
|  | ||||
|         http = self._state.http | ||||
|  | ||||
|         data = yield from http.edit_profile(**args) | ||||
|         if not_bot_account: | ||||
|             self.email = data['email'] | ||||
|             try: | ||||
|                 http._token(data['token'], bot=False) | ||||
|             except KeyError: | ||||
|                 pass | ||||
|  | ||||
|         # manually update data by calling __init__ explicitly. | ||||
|         self.__init__(state=self._state, data=data) | ||||
|  | ||||
| class User(BaseUser, discord.abc.Messageable): | ||||
|     """Represents a Discord user. | ||||
|  | ||||
|     Supported Operations: | ||||
|  | ||||
|     +-----------+---------------------------------------------+ | ||||
|     | Operation |                 Description                 | | ||||
|     +===========+=============================================+ | ||||
|     | x == y    | Checks if two users are equal.              | | ||||
|     +-----------+---------------------------------------------+ | ||||
|     | x != y    | Checks if two users are not equal.          | | ||||
|     +-----------+---------------------------------------------+ | ||||
|     | hash(x)   | Return the user's hash.                     | | ||||
|     +-----------+---------------------------------------------+ | ||||
|     | str(x)    | Returns the user's name with discriminator. | | ||||
|     +-----------+---------------------------------------------+ | ||||
|  | ||||
|     Attributes | ||||
|     ----------- | ||||
|     name: str | ||||
|         The user's username. | ||||
|     id: int | ||||
|         The user's unique ID. | ||||
|     discriminator: str | ||||
|         The user's discriminator. This is given when the username has conflicts. | ||||
|     avatar: str | ||||
|         The avatar hash the user has. Could be None. | ||||
|     bot: bool | ||||
|         Specifies if the user is a bot account. | ||||
|     """ | ||||
|  | ||||
|     __slots__ = ('__weakref__') | ||||
|  | ||||
|     def __repr__(self): | ||||
|         return '<User id={0.id} name={0.name!r} discriminator={0.discriminator!r} bot={0.bot}>'.format(self) | ||||
|  | ||||
|     @asyncio.coroutine | ||||
|     def _get_channel(self): | ||||
|         ch = yield from self.create_dm() | ||||
|         return ch | ||||
|  | ||||
|     @property | ||||
|     def dm_channel(self): | ||||
|         """Returns the :class:`DMChannel` associated with this user if it exists. | ||||
|  | ||||
|         If this returns ``None``, you can create a DM channel by calling the | ||||
|         :meth:`create_dm` coroutine function. | ||||
|         """ | ||||
|         return self._state._get_private_channel_by_user(self.id) | ||||
|  | ||||
|     @asyncio.coroutine | ||||
|     def create_dm(self): | ||||
|         """Creates a :class:`DMChannel` with this user. | ||||
|  | ||||
|         This should be rarely called, as this is done transparently for most | ||||
|         people. | ||||
|         """ | ||||
|         found = self.dm_channel | ||||
|         if found is not None: | ||||
|             return found | ||||
|  | ||||
|         state = self._state | ||||
|         data = yield from state.http.start_private_message(self.id) | ||||
|         return state.add_dm_channel(data) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user