mirror of
https://github.com/Rapptz/discord.py.git
synced 2025-06-07 04:17:16 +00:00
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'
|
__version__ = '1.0.0a0'
|
||||||
|
|
||||||
from .client import Client, AppInfo, ChannelPermissions
|
from .client import Client, AppInfo, ChannelPermissions
|
||||||
from .user import User
|
from .user import User, ClientUser
|
||||||
from .game import Game
|
from .game import Game
|
||||||
from .emoji import Emoji, PartialEmoji
|
from .emoji import Emoji, PartialEmoji
|
||||||
from .channel import *
|
from .channel import *
|
||||||
|
@ -99,7 +99,7 @@ class Client:
|
|||||||
|
|
||||||
Attributes
|
Attributes
|
||||||
-----------
|
-----------
|
||||||
user : Optional[:class:`User`]
|
user : Optional[:class:`ClientUser`]
|
||||||
Represents the connected client. None if not logged in.
|
Represents the connected client. None if not logged in.
|
||||||
voice_clients: List[:class:`VoiceClient`]
|
voice_clients: List[:class:`VoiceClient`]
|
||||||
Represents a list of voice connections. To connect to voice use
|
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)
|
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
|
@asyncio.coroutine
|
||||||
def change_presence(self, *, game=None, status=None, afk=False):
|
def change_presence(self, *, game=None, status=None, afk=False):
|
||||||
"""|coro|
|
"""|coro|
|
||||||
|
@ -29,7 +29,7 @@ import asyncio
|
|||||||
import discord.abc
|
import discord.abc
|
||||||
|
|
||||||
from . import utils
|
from . import utils
|
||||||
from .user import User
|
from .user import BaseUser
|
||||||
from .game import Game
|
from .game import Game
|
||||||
from .permissions import Permissions
|
from .permissions import Permissions
|
||||||
from .enums import Status, ChannelType, try_enum
|
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)
|
return '<VoiceState self_mute={0.self_mute} self_deaf={0.self_deaf} channel={0.channel!r}>'.format(self)
|
||||||
|
|
||||||
def flatten_user(cls):
|
def flatten_user(cls):
|
||||||
for attr, value in User.__dict__.items():
|
for attr, value in BaseUser.__dict__.items():
|
||||||
# ignore private/special methods
|
# ignore private/special methods
|
||||||
if attr.startswith('_'):
|
if attr.startswith('_'):
|
||||||
continue
|
continue
|
||||||
|
@ -25,7 +25,7 @@ DEALINGS IN THE SOFTWARE.
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
from .guild import Guild
|
from .guild import Guild
|
||||||
from .user import User
|
from .user import User, ClientUser
|
||||||
from .game import Game
|
from .game import Game
|
||||||
from .emoji import Emoji, PartialEmoji
|
from .emoji import Emoji, PartialEmoji
|
||||||
from .reaction import Reaction
|
from .reaction import Reaction
|
||||||
@ -239,7 +239,7 @@ class ConnectionState:
|
|||||||
|
|
||||||
def parse_ready(self, data):
|
def parse_ready(self, data):
|
||||||
self._ready_state = ReadyState(launch=asyncio.Event(), guilds=[])
|
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
|
guilds = self._ready_state.guilds
|
||||||
for guild_data in data['guilds']:
|
for guild_data in data['guilds']:
|
||||||
@ -339,7 +339,7 @@ class ConnectionState:
|
|||||||
self.dispatch('member_update', old_member, member)
|
self.dispatch('member_update', old_member, member)
|
||||||
|
|
||||||
def parse_user_update(self, data):
|
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):
|
def parse_channel_delete(self, data):
|
||||||
guild = self._get_guild(utils._get_as_snowflake(data, 'guild_id'))
|
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.
|
DEALINGS IN THE SOFTWARE.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from .utils import snowflake_time
|
from .utils import snowflake_time, _bytes_to_base64_data
|
||||||
from .enums import DefaultAvatar
|
from .enums import DefaultAvatar
|
||||||
|
from .errors import ClientException
|
||||||
|
|
||||||
import discord.abc
|
import discord.abc
|
||||||
import asyncio
|
import asyncio
|
||||||
|
|
||||||
class User(discord.abc.Messageable):
|
class BaseUser:
|
||||||
"""Represents a Discord user.
|
__slots__ = ('name', 'id', 'discriminator', 'avatar', 'bot', '_state')
|
||||||
|
|
||||||
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__')
|
|
||||||
|
|
||||||
def __init__(self, *, state, data):
|
def __init__(self, *, state, data):
|
||||||
self._state = state
|
self._state = state
|
||||||
@ -75,7 +46,7 @@ class User(discord.abc.Messageable):
|
|||||||
return '{0.name}#{0.discriminator}'.format(self)
|
return '{0.name}#{0.discriminator}'.format(self)
|
||||||
|
|
||||||
def __eq__(self, other):
|
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):
|
def __ne__(self, other):
|
||||||
return not self.__eq__(other)
|
return not self.__eq__(other)
|
||||||
@ -83,38 +54,6 @@ class User(discord.abc.Messageable):
|
|||||||
def __hash__(self):
|
def __hash__(self):
|
||||||
return self.id >> 22
|
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
|
@property
|
||||||
def avatar_url(self):
|
def avatar_url(self):
|
||||||
"""Returns a friendly URL version of the avatar the user has.
|
"""Returns a friendly URL version of the avatar the user has.
|
||||||
@ -191,7 +130,207 @@ class User(discord.abc.Messageable):
|
|||||||
if message.mention_everyone:
|
if message.mention_everyone:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
if self in message.mentions:
|
for user in message.mentions:
|
||||||
return True
|
if user.id == self.id:
|
||||||
|
return True
|
||||||
|
|
||||||
return False
|
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
|
.. autoclass:: Object
|
||||||
:members:
|
:members:
|
||||||
|
|
||||||
|
ClientUser
|
||||||
|
~~~~~~~~~~~~
|
||||||
|
|
||||||
|
.. autoclass:: ClientUser
|
||||||
|
:members:
|
||||||
|
:inherited-members:
|
||||||
|
|
||||||
User
|
User
|
||||||
~~~~~
|
~~~~~
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user