Make ClientUser separate from a regular User.
This removes Client.edit_profile in favour of ClientUser.edit.
This commit is contained in:
parent
4b6b5bd35e
commit
fa384f2114
@ -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)
|
||||
|
@ -645,6 +645,13 @@ Object
|
||||
.. autoclass:: Object
|
||||
:members:
|
||||
|
||||
ClientUser
|
||||
~~~~~~~~~~~~
|
||||
|
||||
.. autoclass:: ClientUser
|
||||
:members:
|
||||
:inherited-members:
|
||||
|
||||
User
|
||||
~~~~~
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user