Browse Source

Migrate member

pull/10109/head
dolfies 4 years ago
parent
commit
8566630665
  1. 65
      discord/http.py
  2. 88
      discord/member.py

65
discord/http.py

@ -854,17 +854,25 @@ class HTTPClient:
def edit_profile(self, payload: Dict[str, Any]) -> Response[user.User]: def edit_profile(self, payload: Dict[str, Any]) -> Response[user.User]:
return self.request(Route('PATCH', '/users/@me'), json=payload) return self.request(Route('PATCH', '/users/@me'), json=payload)
def edit_my_voice_state(self, guild_id: Snowflake, payload: Dict[str, Any]) -> Response[None]: # TODO: remove payload
r = Route('PATCH', '/guilds/{guild_id}/voice-states/@me', guild_id=guild_id)
return self.request(r, json=payload)
def edit_voice_state(self, guild_id: Snowflake, user_id: Snowflake, payload: Dict[str, Any]) -> Response[None]: # TODO: remove payload
r = Route('PATCH', '/guilds/{guild_id}/voice-states/{user_id}', guild_id=guild_id, user_id=user_id)
return self.request(r, json=payload)
def edit_me( def edit_me(
self, self,
guild_id: Snowflake, guild_id: Snowflake,
nickname: Optional[str] = MISSING,
avatar: Optional[bytes] = MISSING,
*, *,
nick: Optional[str] = MISSING,
avatar: Optional[bytes] = MISSING,
reason: Optional[str] = None, reason: Optional[str] = None,
) -> Response[member.MemberWithUser]: ) -> Response[member.MemberWithUser]:
payload = {} payload = {}
if nickname is not MISSING: if nick is not MISSING:
payload['nick'] = nickname payload['nick'] = nick
if avatar is not MISSING: if avatar is not MISSING:
r = Route('PATCH', '/guilds/{guild_id}/members/@me', guild_id=guild_id) r = Route('PATCH', '/guilds/{guild_id}/members/@me', guild_id=guild_id)
payload['avatar'] = avatar payload['avatar'] = avatar
@ -876,29 +884,6 @@ class HTTPClient:
return self.request(r, json=payload, reason=reason) return self.request(r, json=payload, reason=reason)
def change_nickname(
self,
guild_id: Snowflake,
user_id: Snowflake,
nickname: str,
*,
reason: Optional[str] = None,
) -> Response[member.MemberWithUser]:
r = Route('PATCH', '/guilds/{guild_id}/members/{user_id}', guild_id=guild_id, user_id=user_id)
payload = {
'nick': nickname,
}
return self.request(r, json=payload, reason=reason)
def edit_my_voice_state(self, guild_id: Snowflake, payload: Dict[str, Any]) -> Response[None]: # TODO: remove payload
r = Route('PATCH', '/guilds/{guild_id}/voice-states/@me', guild_id=guild_id)
return self.request(r, json=payload)
def edit_voice_state(self, guild_id: Snowflake, user_id: Snowflake, payload: Dict[str, Any]) -> Response[None]: # TODO: remove payload
r = Route('PATCH', '/guilds/{guild_id}/voice-states/{user_id}', guild_id=guild_id, user_id=user_id)
return self.request(r, json=payload)
def edit_member( def edit_member(
self, self,
guild_id: Snowflake, guild_id: Snowflake,
@ -995,6 +980,7 @@ class HTTPClient:
*, *,
name: str, name: str,
auto_archive_duration: threads.ThreadArchiveDuration, auto_archive_duration: threads.ThreadArchiveDuration,
location: str = MISSING,
reason: Optional[str] = None, reason: Optional[str] = None,
) -> Response[threads.Thread]: ) -> Response[threads.Thread]:
route = Route( route = Route(
@ -1002,10 +988,13 @@ class HTTPClient:
) )
payload = { payload = {
'auto_archive_duration': auto_archive_duration, 'auto_archive_duration': auto_archive_duration,
'location': choice(('Message', 'Reply Chain Nudge')),
'name': name, 'name': name,
'type': 11, 'type': 11,
} }
if location is MISSING:
payload['location'] = choice(('Message', 'Reply Chain Nudge'))
else:
payload['location'] = location
return self.request(route, json=payload, reason=reason) return self.request(route, json=payload, reason=reason)
@ -1056,7 +1045,7 @@ class HTTPClient:
params = { params = {
'location': 'Context Menu' 'location': 'Context Menu'
} }
return self.request(r, params=params) return self.request(r, params=params)
def get_public_archived_threads( def get_public_archived_threads(
@ -1301,16 +1290,24 @@ class HTTPClient:
def get_sticker(self, sticker_id: Snowflake) -> Response[sticker.Sticker]: def get_sticker(self, sticker_id: Snowflake) -> Response[sticker.Sticker]:
return self.request(Route('GET', '/stickers/{sticker_id}', sticker_id=sticker_id)) return self.request(Route('GET', '/stickers/{sticker_id}', sticker_id=sticker_id))
def list_premium_sticker_packs(self) -> Response[sticker.ListPremiumStickerPacks]: def list_premium_sticker_packs(
return self.request(Route('GET', '/sticker-packs')) self, country: str = 'US', locale: str = 'en-US', payment_source_id: int = MISSING
) -> Response[sticker.ListPremiumStickerPacks]:
params = {
'country_code': country,
'locale': locale,
}
if payment_source_id is not MISSING:
params['payment_source_id'] = payment_source_id
return self.request(Route('GET', '/sticker-packs'), params=params)
def get_all_guild_stickers(self, guild_id: Snowflake) -> Response[List[sticker.GuildSticker]]: def get_all_guild_stickers(self, guild_id: Snowflake) -> Response[List[sticker.GuildSticker]]:
return self.request(Route('GET', '/guilds/{guild_id}/stickers', guild_id=guild_id)) return self.request(Route('GET', '/guilds/{guild_id}/stickers', guild_id=guild_id))
def get_guild_sticker(self, guild_id: Snowflake, sticker_id: Snowflake) -> Response[sticker.GuildSticker]: def get_guild_sticker(self, guild_id: Snowflake, sticker_id: Snowflake) -> Response[sticker.GuildSticker]:
return self.request( r = Route('GET', '/guilds/{guild_id}/stickers/{sticker_id}', guild_id=guild_id, sticker_id=sticker_id)
Route('GET', '/guilds/{guild_id}/stickers/{sticker_id}', guild_id=guild_id, sticker_id=sticker_id) return self.request(r)
)
def create_guild_sticker( def create_guild_sticker(
self, guild_id: Snowflake, payload: sticker.CreateGuildSticker, file: File, reason: str self, guild_id: Snowflake, payload: sticker.CreateGuildSticker, file: File, reason: str

88
discord/member.py

@ -39,7 +39,7 @@ from .utils import MISSING
from .user import BaseUser, User, _UserTag from .user import BaseUser, User, _UserTag
from .activity import create_activity, ActivityTypes from .activity import create_activity, ActivityTypes
from .permissions import Permissions from .permissions import Permissions
from .enums import Status, try_enum from .enums import RelationshipAction, Status, try_enum
from .colour import Colour from .colour import Colour
from .object import Object from .object import Object
@ -60,7 +60,7 @@ if TYPE_CHECKING:
UserWithMember as UserWithMemberPayload, UserWithMember as UserWithMemberPayload,
) )
from .types.user import User as UserPayload from .types.user import User as UserPayload
from .abc import Snowflake from .abc import Connectable, Snowflake
from .state import ConnectionState from .state import ConnectionState
from .message import Message from .message import Message
from .role import Role from .role import Role
@ -76,8 +76,12 @@ class VoiceState:
------------ ------------
deaf: :class:`bool` deaf: :class:`bool`
Indicates if the user is currently deafened by the guild. Indicates if the user is currently deafened by the guild.
Doesn't apply to private channels.
mute: :class:`bool` mute: :class:`bool`
Indicates if the user is currently muted by the guild. Indicates if the user is currently muted by the guild.
Doesn't apply to private channels.
self_mute: :class:`bool` self_mute: :class:`bool`
Indicates if the user is currently muted by their own accord. Indicates if the user is currently muted by their own accord.
self_deaf: :class:`bool` self_deaf: :class:`bool`
@ -107,7 +111,7 @@ class VoiceState:
afk: :class:`bool` afk: :class:`bool`
Indicates if the user is currently in the AFK channel in the guild. Indicates if the user is currently in the AFK channel in the guild.
channel: Optional[Union[:class:`VoiceChannel`, :class:`StageChannel`]] channel: Optional[Union[:class:`VoiceChannel`, :class:`StageChannel`, :class:`DMChannel`, :class:`GroupChannel`]]
The voice channel that the user is currently connected to. ``None`` if the user The voice channel that the user is currently connected to. ``None`` if the user
is not currently in a voice channel. is not currently in a voice channel.
""" """
@ -140,7 +144,7 @@ class VoiceState:
self.deaf: bool = data.get('deaf', False) self.deaf: bool = data.get('deaf', False)
self.suppress: bool = data.get('suppress', False) self.suppress: bool = data.get('suppress', False)
self.requested_to_speak_at: Optional[datetime.datetime] = utils.parse_time(data.get('request_to_speak_timestamp')) self.requested_to_speak_at: Optional[datetime.datetime] = utils.parse_time(data.get('request_to_speak_timestamp'))
self.channel: Optional[VocalGuildChannel] = channel self.channel: Optional[Connectable] = channel
def __repr__(self) -> str: def __repr__(self) -> str:
attrs = [ attrs = [
@ -157,26 +161,26 @@ class VoiceState:
def flatten_user(cls): def flatten_user(cls):
for attr, value in itertools.chain(BaseUser.__dict__.items(), User.__dict__.items()): for attr, value in itertools.chain(BaseUser.__dict__.items(), User.__dict__.items()):
# ignore private/special methods # Ignore private/special methods (or not)
if attr.startswith('_'): # if attr.startswith('_'):
continue # continue
# don't override what we already have # Don't override what we already have
if attr in cls.__dict__: if attr in cls.__dict__:
continue continue
# if it's a slotted attribute or a property, redirect it # If it's a slotted attribute or a property, redirect it
# slotted members are implemented as member_descriptors in Type.__dict__ # Slotted members are implemented as member_descriptors in Type.__dict__
if not hasattr(value, '__annotations__'): if not hasattr(value, '__annotations__'):
getter = attrgetter('_user.' + attr) getter = attrgetter('_user.' + attr)
setattr(cls, attr, property(getter, doc=f'Equivalent to :attr:`User.{attr}`')) setattr(cls, attr, property(getter, doc=f'Equivalent to :attr:`User.{attr}`'))
else: else:
# Technically, this can also use attrgetter # Technically, this can also use attrgetter,
# However I'm not sure how I feel about "functions" returning properties # however I'm not sure how I feel about "functions" returning properties
# It probably breaks something in Sphinx. # It probably breaks something in Sphinx
# probably a member function by now # Probably a member function by now
def generate_function(x): def generate_function(x):
# We want sphinx to properly show coroutine functions as coroutines # We want Sphinx to properly show coroutine functions as coroutines
if inspect.iscoroutinefunction(value): if inspect.iscoroutinefunction(value):
async def general(self, *args, **kwargs): # type: ignore async def general(self, *args, **kwargs): # type: ignore
@ -201,7 +205,7 @@ M = TypeVar('M', bound='Member')
@flatten_user @flatten_user
class Member(discord.abc.Messageable, _UserTag): class Member(discord.abc.Messageable, discord.abc.connectable, _UserTag):
"""Represents a Discord member to a :class:`Guild`. """Represents a Discord member to a :class:`Guild`.
This implements a lot of the functionality of :class:`User`. This implements a lot of the functionality of :class:`User`.
@ -359,10 +363,6 @@ class Member(discord.abc.Messageable, _UserTag):
self._user = member._user self._user = member._user
return self return self
async def _get_channel(self):
ch = await self.create_dm()
return ch
def _update(self, data: MemberPayload) -> None: def _update(self, data: MemberPayload) -> None:
# the nickname change is optional, # the nickname change is optional,
# if it isn't in the payload then it didn't change # if it isn't in the payload then it didn't change
@ -389,7 +389,7 @@ class Member(discord.abc.Messageable, _UserTag):
if len(user) > 1: if len(user) > 1:
return self._update_inner_user(user) return self._update_inner_user(user)
return None return
def _update_inner_user(self, user: UserPayload) -> Optional[Tuple[User, User]]: def _update_inner_user(self, user: UserPayload) -> Optional[Tuple[User, User]]:
u = self._user u = self._user
@ -448,11 +448,9 @@ class Member(discord.abc.Messageable, _UserTag):
There is an alias for this named :attr:`color`. There is an alias for this named :attr:`color`.
""" """
roles = self.roles[1:] # remove @everyone roles = self.roles[1:] # Remove @everyone
# highest order of the colour is the one that gets rendered. # Highest role with a colour is the one that's rendered
# if the highest is the default colour then the next one with a colour
# is chosen instead
for role in reversed(roles): for role in reversed(roles):
if role.colour.value: if role.colour.value:
return role.colour return role.colour
@ -644,6 +642,7 @@ class Member(discord.abc.Messageable, _UserTag):
roles: List[discord.abc.Snowflake] = MISSING, roles: List[discord.abc.Snowflake] = MISSING,
voice_channel: Optional[VocalGuildChannel] = MISSING, voice_channel: Optional[VocalGuildChannel] = MISSING,
reason: Optional[str] = None, reason: Optional[str] = None,
avatar: Optional[bytes] = MISSING,
) -> Optional[Member]: ) -> Optional[Member]:
"""|coro| """|coro|
@ -667,6 +666,13 @@ class Member(discord.abc.Messageable, _UserTag):
All parameters are optional. All parameters are optional.
.. note::
To upload an avatar, a :term:`py: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 :term:`py:bytes-like object` is given through the use of ``fp.read()``.
.. versionchanged:: 1.1 .. versionchanged:: 1.1
Can now pass ``None`` to ``voice_channel`` to kick a member from voice. Can now pass ``None`` to ``voice_channel`` to kick a member from voice.
@ -691,6 +697,9 @@ class Member(discord.abc.Messageable, _UserTag):
voice_channel: Optional[:class:`VoiceChannel`] voice_channel: Optional[:class:`VoiceChannel`]
The voice channel to move the member to. The voice channel to move the member to.
Pass ``None`` to kick them from voice. Pass ``None`` to kick them from voice.
avatar: Optional[:class:`bytes`]
The member's new guild avatar. Pass ``None`` to remove the avatar.
You can only change your own guild avatar.
reason: Optional[:class:`str`] reason: Optional[:class:`str`]
The reason for editing this member. Shows up on the audit log. The reason for editing this member. Shows up on the audit log.
@ -713,11 +722,14 @@ class Member(discord.abc.Messageable, _UserTag):
payload: Dict[str, Any] = {} payload: Dict[str, Any] = {}
if nick is not MISSING: if nick is not MISSING:
nick = nick or '' payload['nick'] = nick
if me:
await http.change_my_nickname(guild_id, nick, reason=reason) if avatar is not MISSING:
else: payload['avatar'] = utils._bytes_to_base64_data(avatar)
payload['nick'] = nick
if me and payload:
data = await http.edit_me(**payload)
payload = {}
if deafen is not MISSING: if deafen is not MISSING:
payload['deaf'] = deafen payload['deaf'] = deafen
@ -749,6 +761,8 @@ class Member(discord.abc.Messageable, _UserTag):
if payload: if payload:
data = await http.edit_member(guild_id, self.id, reason=reason, **payload) data = await http.edit_member(guild_id, self.id, reason=reason, **payload)
if data:
return Member(data=data, guild=self.guild, state=self._state) return Member(data=data, guild=self.guild, state=self._state)
async def request_to_speak(self) -> None: async def request_to_speak(self) -> None:
@ -906,3 +920,17 @@ class Member(discord.abc.Messageable, _UserTag):
The role or ``None`` if not found in the member's roles. The role or ``None`` if not found in the member's roles.
""" """
return self.guild.get_role(role_id) if self._roles.has(role_id) else None return self.guild.get_role(role_id) if self._roles.has(role_id) else None
async def send_friend_request(self) -> None: # TODO: check if the req returns a relationship obj
"""|coro|
Sends the member a friend request.
Raises
-------
Forbidden
Not allowed to send a friend request to the member.
HTTPException
Sending the friend request failed.
"""
await self._state.http.add_relationship(self._user.id, action=RelationshipAction.send_friend_request)

Loading…
Cancel
Save