mirror of
https://github.com/Rapptz/discord.py.git
synced 2025-06-08 04:38:42 +00:00
Implement StageChannel and related methods
This commit is contained in:
parent
e8a9726ad2
commit
1b2688518e
@ -38,6 +38,7 @@ from .errors import ClientException, NoMoreItems, InvalidArgument
|
|||||||
__all__ = (
|
__all__ = (
|
||||||
'TextChannel',
|
'TextChannel',
|
||||||
'VoiceChannel',
|
'VoiceChannel',
|
||||||
|
'StageChannel',
|
||||||
'DMChannel',
|
'DMChannel',
|
||||||
'CategoryChannel',
|
'CategoryChannel',
|
||||||
'StoreChannel',
|
'StoreChannel',
|
||||||
@ -537,51 +538,7 @@ class TextChannel(discord.abc.Messageable, discord.abc.GuildChannel, Hashable):
|
|||||||
from .message import PartialMessage
|
from .message import PartialMessage
|
||||||
return PartialMessage(channel=self, id=message_id)
|
return PartialMessage(channel=self, id=message_id)
|
||||||
|
|
||||||
class VoiceChannel(discord.abc.Connectable, discord.abc.GuildChannel, Hashable):
|
class VocalGuildChannel(discord.abc.Connectable, discord.abc.GuildChannel, Hashable):
|
||||||
"""Represents a Discord guild voice channel.
|
|
||||||
|
|
||||||
.. container:: operations
|
|
||||||
|
|
||||||
.. describe:: x == y
|
|
||||||
|
|
||||||
Checks if two channels are equal.
|
|
||||||
|
|
||||||
.. describe:: x != y
|
|
||||||
|
|
||||||
Checks if two channels are not equal.
|
|
||||||
|
|
||||||
.. describe:: hash(x)
|
|
||||||
|
|
||||||
Returns the channel's hash.
|
|
||||||
|
|
||||||
.. describe:: str(x)
|
|
||||||
|
|
||||||
Returns the channel's name.
|
|
||||||
|
|
||||||
Attributes
|
|
||||||
-----------
|
|
||||||
name: :class:`str`
|
|
||||||
The channel name.
|
|
||||||
guild: :class:`Guild`
|
|
||||||
The guild the channel belongs to.
|
|
||||||
id: :class:`int`
|
|
||||||
The channel ID.
|
|
||||||
category_id: Optional[:class:`int`]
|
|
||||||
The category channel ID this channel belongs to, if applicable.
|
|
||||||
position: :class:`int`
|
|
||||||
The position in the channel list. This is a number that starts at 0. e.g. the
|
|
||||||
top channel is position 0.
|
|
||||||
bitrate: :class:`int`
|
|
||||||
The channel's preferred audio bitrate in bits per second.
|
|
||||||
user_limit: :class:`int`
|
|
||||||
The channel's limit for number of members that can be in a voice channel.
|
|
||||||
rtc_region: Optional[:class:`VoiceRegion`]
|
|
||||||
The region for the voice channel's voice communication.
|
|
||||||
A value of ``None`` indicates automatic voice region detection.
|
|
||||||
|
|
||||||
.. versionadded:: 1.7
|
|
||||||
"""
|
|
||||||
|
|
||||||
__slots__ = ('name', 'id', 'guild', 'bitrate', 'user_limit',
|
__slots__ = ('name', 'id', 'guild', 'bitrate', 'user_limit',
|
||||||
'_state', 'position', '_overwrites', 'category_id',
|
'_state', 'position', '_overwrites', 'category_id',
|
||||||
'rtc_region')
|
'rtc_region')
|
||||||
@ -591,29 +548,12 @@ class VoiceChannel(discord.abc.Connectable, discord.abc.GuildChannel, Hashable):
|
|||||||
self.id = int(data['id'])
|
self.id = int(data['id'])
|
||||||
self._update(guild, data)
|
self._update(guild, data)
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
attrs = [
|
|
||||||
('id', self.id),
|
|
||||||
('name', self.name),
|
|
||||||
('rtc_region', self.rtc_region),
|
|
||||||
('position', self.position),
|
|
||||||
('bitrate', self.bitrate),
|
|
||||||
('user_limit', self.user_limit),
|
|
||||||
('category_id', self.category_id)
|
|
||||||
]
|
|
||||||
return '<%s %s>' % (self.__class__.__name__, ' '.join('%s=%r' % t for t in attrs))
|
|
||||||
|
|
||||||
def _get_voice_client_key(self):
|
def _get_voice_client_key(self):
|
||||||
return self.guild.id, 'guild_id'
|
return self.guild.id, 'guild_id'
|
||||||
|
|
||||||
def _get_voice_state_pair(self):
|
def _get_voice_state_pair(self):
|
||||||
return self.guild.id, self.id
|
return self.guild.id, self.id
|
||||||
|
|
||||||
@property
|
|
||||||
def type(self):
|
|
||||||
""":class:`ChannelType`: The channel's Discord type."""
|
|
||||||
return ChannelType.voice
|
|
||||||
|
|
||||||
def _update(self, guild, data):
|
def _update(self, guild, data):
|
||||||
self.guild = guild
|
self.guild = guild
|
||||||
self.name = data['name']
|
self.name = data['name']
|
||||||
@ -671,6 +611,70 @@ class VoiceChannel(discord.abc.Connectable, discord.abc.GuildChannel, Hashable):
|
|||||||
base.value &= ~denied.value
|
base.value &= ~denied.value
|
||||||
return base
|
return base
|
||||||
|
|
||||||
|
class VoiceChannel(VocalGuildChannel):
|
||||||
|
"""Represents a Discord guild voice channel.
|
||||||
|
|
||||||
|
.. container:: operations
|
||||||
|
|
||||||
|
.. describe:: x == y
|
||||||
|
|
||||||
|
Checks if two channels are equal.
|
||||||
|
|
||||||
|
.. describe:: x != y
|
||||||
|
|
||||||
|
Checks if two channels are not equal.
|
||||||
|
|
||||||
|
.. describe:: hash(x)
|
||||||
|
|
||||||
|
Returns the channel's hash.
|
||||||
|
|
||||||
|
.. describe:: str(x)
|
||||||
|
|
||||||
|
Returns the channel's name.
|
||||||
|
|
||||||
|
Attributes
|
||||||
|
-----------
|
||||||
|
name: :class:`str`
|
||||||
|
The channel name.
|
||||||
|
guild: :class:`Guild`
|
||||||
|
The guild the channel belongs to.
|
||||||
|
id: :class:`int`
|
||||||
|
The channel ID.
|
||||||
|
category_id: Optional[:class:`int`]
|
||||||
|
The category channel ID this channel belongs to, if applicable.
|
||||||
|
position: :class:`int`
|
||||||
|
The position in the channel list. This is a number that starts at 0. e.g. the
|
||||||
|
top channel is position 0.
|
||||||
|
bitrate: :class:`int`
|
||||||
|
The channel's preferred audio bitrate in bits per second.
|
||||||
|
user_limit: :class:`int`
|
||||||
|
The channel's limit for number of members that can be in a voice channel.
|
||||||
|
rtc_region: Optional[:class:`VoiceRegion`]
|
||||||
|
The region for the voice channel's voice communication.
|
||||||
|
A value of ``None`` indicates automatic voice region detection.
|
||||||
|
|
||||||
|
.. versionadded:: 1.7
|
||||||
|
"""
|
||||||
|
|
||||||
|
__slots__ = ()
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
attrs = [
|
||||||
|
('id', self.id),
|
||||||
|
('name', self.name),
|
||||||
|
('rtc_region', self.rtc_region),
|
||||||
|
('position', self.position),
|
||||||
|
('bitrate', self.bitrate),
|
||||||
|
('user_limit', self.user_limit),
|
||||||
|
('category_id', self.category_id)
|
||||||
|
]
|
||||||
|
return '<%s %s>' % (self.__class__.__name__, ' '.join('%s=%r' % t for t in attrs))
|
||||||
|
|
||||||
|
@property
|
||||||
|
def type(self):
|
||||||
|
""":class:`ChannelType`: The channel's Discord type."""
|
||||||
|
return ChannelType.voice
|
||||||
|
|
||||||
@utils.copy_doc(discord.abc.GuildChannel.clone)
|
@utils.copy_doc(discord.abc.GuildChannel.clone)
|
||||||
async def clone(self, *, name=None, reason=None):
|
async def clone(self, *, name=None, reason=None):
|
||||||
return await self._clone_impl({
|
return await self._clone_impl({
|
||||||
@ -728,6 +732,130 @@ class VoiceChannel(discord.abc.Connectable, discord.abc.GuildChannel, Hashable):
|
|||||||
|
|
||||||
await self._edit(options, reason=reason)
|
await self._edit(options, reason=reason)
|
||||||
|
|
||||||
|
class StageChannel(VocalGuildChannel):
|
||||||
|
"""Represents a Discord guild stage channel.
|
||||||
|
|
||||||
|
.. versionadded:: 1.7
|
||||||
|
|
||||||
|
.. container:: operations
|
||||||
|
|
||||||
|
.. describe:: x == y
|
||||||
|
|
||||||
|
Checks if two channels are equal.
|
||||||
|
|
||||||
|
.. describe:: x != y
|
||||||
|
|
||||||
|
Checks if two channels are not equal.
|
||||||
|
|
||||||
|
.. describe:: hash(x)
|
||||||
|
|
||||||
|
Returns the channel's hash.
|
||||||
|
|
||||||
|
.. describe:: str(x)
|
||||||
|
|
||||||
|
Returns the channel's name.
|
||||||
|
|
||||||
|
Attributes
|
||||||
|
-----------
|
||||||
|
name: :class:`str`
|
||||||
|
The channel name.
|
||||||
|
guild: :class:`Guild`
|
||||||
|
The guild the channel belongs to.
|
||||||
|
id: :class:`int`
|
||||||
|
The channel ID.
|
||||||
|
topic: Optional[:class:`str`]
|
||||||
|
The channel's topic. ``None`` if it isn't set.
|
||||||
|
category_id: Optional[:class:`int`]
|
||||||
|
The category channel ID this channel belongs to, if applicable.
|
||||||
|
position: :class:`int`
|
||||||
|
The position in the channel list. This is a number that starts at 0. e.g. the
|
||||||
|
top channel is position 0.
|
||||||
|
bitrate: :class:`int`
|
||||||
|
The channel's preferred audio bitrate in bits per second.
|
||||||
|
user_limit: :class:`int`
|
||||||
|
The channel's limit for number of members that can be in a stage channel.
|
||||||
|
rtc_region: Optional[:class:`VoiceRegion`]
|
||||||
|
The region for the stage channel's voice communication.
|
||||||
|
A value of ``None`` indicates automatic voice region detection.
|
||||||
|
"""
|
||||||
|
__slots__ = ('topic',)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
attrs = [
|
||||||
|
('id', self.id),
|
||||||
|
('name', self.name),
|
||||||
|
('topic', self.topic),
|
||||||
|
('rtc_region', self.rtc_region),
|
||||||
|
('position', self.position),
|
||||||
|
('bitrate', self.bitrate),
|
||||||
|
('user_limit', self.user_limit),
|
||||||
|
('category_id', self.category_id)
|
||||||
|
]
|
||||||
|
return '<%s %s>' % (self.__class__.__name__, ' '.join('%s=%r' % t for t in attrs))
|
||||||
|
|
||||||
|
def _update(self, guild, data):
|
||||||
|
super()._update(guild, data)
|
||||||
|
self.topic = data.get('topic')
|
||||||
|
|
||||||
|
@property
|
||||||
|
def requesting_to_speak(self):
|
||||||
|
"""List[:class:`Member`]: A list of members who are requesting to speak in the stage channel."""
|
||||||
|
return [member for member in self.members if member.voice.requested_to_speak_at is not None]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def type(self):
|
||||||
|
""":class:`ChannelType`: The channel's Discord type."""
|
||||||
|
return ChannelType.stage_voice
|
||||||
|
|
||||||
|
@utils.copy_doc(discord.abc.GuildChannel.clone)
|
||||||
|
async def clone(self, *, name=None, reason=None):
|
||||||
|
return await self._clone_impl({
|
||||||
|
'topic': self.topic,
|
||||||
|
}, name=name, reason=reason)
|
||||||
|
|
||||||
|
async def edit(self, *, reason=None, **options):
|
||||||
|
"""|coro|
|
||||||
|
|
||||||
|
Edits the channel.
|
||||||
|
|
||||||
|
You must have the :attr:`~Permissions.manage_channels` permission to
|
||||||
|
use this.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
name: :class:`str`
|
||||||
|
The new channel's name.
|
||||||
|
topic: :class:`str`
|
||||||
|
The new channel's topic.
|
||||||
|
position: :class:`int`
|
||||||
|
The new channel's position.
|
||||||
|
sync_permissions: :class:`bool`
|
||||||
|
Whether to sync permissions with the channel's new or pre-existing
|
||||||
|
category. Defaults to ``False``.
|
||||||
|
category: Optional[:class:`CategoryChannel`]
|
||||||
|
The new category for this channel. Can be ``None`` to remove the
|
||||||
|
category.
|
||||||
|
reason: Optional[:class:`str`]
|
||||||
|
The reason for editing this channel. Shows up on the audit log.
|
||||||
|
overwrites: :class:`dict`
|
||||||
|
A :class:`dict` of target (either a role or a member) to
|
||||||
|
:class:`PermissionOverwrite` to apply to the channel.
|
||||||
|
rtc_region: Optional[:class:`VoiceRegion`]
|
||||||
|
The new region for the stage channel's voice communication.
|
||||||
|
A value of ``None`` indicates automatic voice region detection.
|
||||||
|
|
||||||
|
Raises
|
||||||
|
------
|
||||||
|
InvalidArgument
|
||||||
|
If the permission overwrite information is not in proper form.
|
||||||
|
Forbidden
|
||||||
|
You do not have permissions to edit the channel.
|
||||||
|
HTTPException
|
||||||
|
Editing the channel failed.
|
||||||
|
"""
|
||||||
|
|
||||||
|
await self._edit(options, reason=reason)
|
||||||
|
|
||||||
class CategoryChannel(discord.abc.GuildChannel, Hashable):
|
class CategoryChannel(discord.abc.GuildChannel, Hashable):
|
||||||
"""Represents a Discord channel category.
|
"""Represents a Discord channel category.
|
||||||
|
|
||||||
@ -874,6 +1002,18 @@ class CategoryChannel(discord.abc.GuildChannel, Hashable):
|
|||||||
ret.sort(key=lambda c: (c.position, c.id))
|
ret.sort(key=lambda c: (c.position, c.id))
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
@property
|
||||||
|
def stage_channels(self):
|
||||||
|
"""List[:class:`StageChannel`]: Returns the voice channels that are under this category.
|
||||||
|
|
||||||
|
.. versionadded:: 1.7
|
||||||
|
"""
|
||||||
|
ret = [c for c in self.guild.channels
|
||||||
|
if c.category_id == self.id
|
||||||
|
and isinstance(c, StageChannel)]
|
||||||
|
ret.sort(key=lambda c: (c.position, c.id))
|
||||||
|
return ret
|
||||||
|
|
||||||
async def create_text_channel(self, name, *, overwrites=None, reason=None, **options):
|
async def create_text_channel(self, name, *, overwrites=None, reason=None, **options):
|
||||||
"""|coro|
|
"""|coro|
|
||||||
|
|
||||||
@ -898,6 +1038,20 @@ class CategoryChannel(discord.abc.GuildChannel, Hashable):
|
|||||||
"""
|
"""
|
||||||
return await self.guild.create_voice_channel(name, overwrites=overwrites, category=self, reason=reason, **options)
|
return await self.guild.create_voice_channel(name, overwrites=overwrites, category=self, reason=reason, **options)
|
||||||
|
|
||||||
|
async def create_stage_channel(self, name, *, overwrites=None, reason=None, **options):
|
||||||
|
"""|coro|
|
||||||
|
|
||||||
|
A shortcut method to :meth:`Guild.create_stage_channel` to create a :class:`StageChannel` in the category.
|
||||||
|
|
||||||
|
.. versionadded:: 1.7
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
:class:`StageChannel`
|
||||||
|
The channel that was just created.
|
||||||
|
"""
|
||||||
|
return await self.guild.create_stage_channel(name, overwrites=overwrites, category=self, reason=reason, **options)
|
||||||
|
|
||||||
class StoreChannel(discord.abc.GuildChannel, Hashable):
|
class StoreChannel(discord.abc.GuildChannel, Hashable):
|
||||||
"""Represents a Discord guild store channel.
|
"""Represents a Discord guild store channel.
|
||||||
|
|
||||||
@ -1407,5 +1561,7 @@ def _channel_factory(channel_type):
|
|||||||
return TextChannel, value
|
return TextChannel, value
|
||||||
elif value is ChannelType.store:
|
elif value is ChannelType.store:
|
||||||
return StoreChannel, value
|
return StoreChannel, value
|
||||||
|
elif value is ChannelType.stage_voice:
|
||||||
|
return StageChannel, value
|
||||||
else:
|
else:
|
||||||
return None, value
|
return None, value
|
||||||
|
@ -158,6 +158,7 @@ class ChannelType(Enum):
|
|||||||
category = 4
|
category = 4
|
||||||
news = 5
|
news = 5
|
||||||
store = 6
|
store = 6
|
||||||
|
stage_voice = 13
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
@ -46,6 +46,7 @@ __all__ = (
|
|||||||
'ColourConverter',
|
'ColourConverter',
|
||||||
'ColorConverter',
|
'ColorConverter',
|
||||||
'VoiceChannelConverter',
|
'VoiceChannelConverter',
|
||||||
|
'StageChannelConverter',
|
||||||
'EmojiConverter',
|
'EmojiConverter',
|
||||||
'PartialEmojiConverter',
|
'PartialEmojiConverter',
|
||||||
'CategoryChannelConverter',
|
'CategoryChannelConverter',
|
||||||
@ -396,6 +397,46 @@ class VoiceChannelConverter(IDConverter):
|
|||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
class StageChannelConverter(IDConverter):
|
||||||
|
"""Converts to a :class:`~discord.StageChannel`.
|
||||||
|
|
||||||
|
.. versionadded:: 1.7
|
||||||
|
|
||||||
|
All lookups are via the local guild. If in a DM context, then the lookup
|
||||||
|
is done by the global cache.
|
||||||
|
|
||||||
|
The lookup strategy is as follows (in order):
|
||||||
|
|
||||||
|
1. Lookup by ID.
|
||||||
|
2. Lookup by mention.
|
||||||
|
3. Lookup by name
|
||||||
|
"""
|
||||||
|
async def convert(self, ctx, argument):
|
||||||
|
bot = ctx.bot
|
||||||
|
match = self._get_id_match(argument) or re.match(r'<#([0-9]+)>$', argument)
|
||||||
|
result = None
|
||||||
|
guild = ctx.guild
|
||||||
|
|
||||||
|
if match is None:
|
||||||
|
# not a mention
|
||||||
|
if guild:
|
||||||
|
result = discord.utils.get(guild.stage_channels, name=argument)
|
||||||
|
else:
|
||||||
|
def check(c):
|
||||||
|
return isinstance(c, discord.StageChannel) and c.name == argument
|
||||||
|
result = discord.utils.find(check, bot.get_all_channels())
|
||||||
|
else:
|
||||||
|
channel_id = int(match.group(1))
|
||||||
|
if guild:
|
||||||
|
result = guild.get_channel(channel_id)
|
||||||
|
else:
|
||||||
|
result = _get_from_guilds(bot, 'get_channel', channel_id)
|
||||||
|
|
||||||
|
if not isinstance(result, discord.StageChannel):
|
||||||
|
raise ChannelNotFound(argument)
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
class CategoryChannelConverter(IDConverter):
|
class CategoryChannelConverter(IDConverter):
|
||||||
"""Converts to a :class:`~discord.CategoryChannel`.
|
"""Converts to a :class:`~discord.CategoryChannel`.
|
||||||
|
|
||||||
|
@ -371,6 +371,18 @@ class Guild(Hashable):
|
|||||||
r.sort(key=lambda c: (c.position, c.id))
|
r.sort(key=lambda c: (c.position, c.id))
|
||||||
return r
|
return r
|
||||||
|
|
||||||
|
@property
|
||||||
|
def stage_channels(self):
|
||||||
|
"""List[:class:`StageChannel`]: A list of voice channels that belongs to this guild.
|
||||||
|
|
||||||
|
.. versionadded:: 1.7
|
||||||
|
|
||||||
|
This is sorted by the position and are in UI order from top to bottom.
|
||||||
|
"""
|
||||||
|
r = [ch for ch in self._channels.values() if isinstance(ch, StageChannel)]
|
||||||
|
r.sort(key=lambda c: (c.position, c.id))
|
||||||
|
return r
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def me(self):
|
def me(self):
|
||||||
""":class:`Member`: Similar to :attr:`Client.user` except an instance of :class:`Member`.
|
""":class:`Member`: Similar to :attr:`Client.user` except an instance of :class:`Member`.
|
||||||
@ -979,6 +991,38 @@ class Guild(Hashable):
|
|||||||
self._channels[channel.id] = channel
|
self._channels[channel.id] = channel
|
||||||
return channel
|
return channel
|
||||||
|
|
||||||
|
async def create_stage_channel(self, name, *, topic=None, category=None, overwrites=None, reason=None, position=None):
|
||||||
|
"""|coro|
|
||||||
|
|
||||||
|
This is similar to :meth:`create_text_channel` except makes a :class:`StageChannel` instead.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
The ``slowmode_delay`` and ``nsfw`` parameters are not supported in this function.
|
||||||
|
|
||||||
|
.. versionadded:: 1.7
|
||||||
|
|
||||||
|
Raises
|
||||||
|
------
|
||||||
|
Forbidden
|
||||||
|
You do not have the proper permissions to create this channel.
|
||||||
|
HTTPException
|
||||||
|
Creating the channel failed.
|
||||||
|
InvalidArgument
|
||||||
|
The permission overwrite information is not in proper form.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
:class:`StageChannel`
|
||||||
|
The channel that was just created.
|
||||||
|
"""
|
||||||
|
data = await self._create_channel(name, overwrites, ChannelType.stage_voice, category, reason=reason, position=position, topic=topic)
|
||||||
|
channel = StageChannel(state=self._state, guild=self, data=data)
|
||||||
|
|
||||||
|
# temporarily add to the cache
|
||||||
|
self._channels[channel.id] = channel
|
||||||
|
return channel
|
||||||
|
|
||||||
async def create_category(self, name, *, overwrites=None, reason=None, position=None):
|
async def create_category(self, name, *, overwrites=None, reason=None, position=None):
|
||||||
"""|coro|
|
"""|coro|
|
||||||
|
|
||||||
|
@ -574,6 +574,14 @@ class HTTPClient:
|
|||||||
}
|
}
|
||||||
return self.request(r, json=payload, reason=reason)
|
return self.request(r, json=payload, reason=reason)
|
||||||
|
|
||||||
|
def edit_my_voice_state(self, guild_id, 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, user_id, 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(self, guild_id, user_id, *, reason=None, **fields):
|
def edit_member(self, guild_id, user_id, *, reason=None, **fields):
|
||||||
r = Route('PATCH', '/guilds/{guild_id}/members/{user_id}', guild_id=guild_id, user_id=user_id)
|
r = Route('PATCH', '/guilds/{guild_id}/members/{user_id}', guild_id=guild_id, user_id=user_id)
|
||||||
return self.request(r, json=fields, reason=reason)
|
return self.request(r, json=fields, reason=reason)
|
||||||
|
@ -24,6 +24,7 @@ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|||||||
DEALINGS IN THE SOFTWARE.
|
DEALINGS IN THE SOFTWARE.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import datetime
|
||||||
import inspect
|
import inspect
|
||||||
import itertools
|
import itertools
|
||||||
import sys
|
import sys
|
||||||
@ -32,6 +33,7 @@ from operator import attrgetter
|
|||||||
import discord.abc
|
import discord.abc
|
||||||
|
|
||||||
from . import utils
|
from . import utils
|
||||||
|
from .errors import ClientException
|
||||||
from .user import BaseUser, User
|
from .user import BaseUser, User
|
||||||
from .activity import create_activity
|
from .activity import create_activity
|
||||||
from .permissions import Permissions
|
from .permissions import Permissions
|
||||||
@ -59,15 +61,32 @@ class VoiceState:
|
|||||||
|
|
||||||
self_video: :class:`bool`
|
self_video: :class:`bool`
|
||||||
Indicates if the user is currently broadcasting video.
|
Indicates if the user is currently broadcasting video.
|
||||||
|
suppress: :class:`bool`
|
||||||
|
Indicates if the user is suppressed from speaking.
|
||||||
|
|
||||||
|
Only applies to stage channels.
|
||||||
|
|
||||||
|
.. versionadded:: 1.7
|
||||||
|
|
||||||
|
requested_to_speak_at: Optional[:class:`datetime.datetime`]
|
||||||
|
A datetime object that specifies the date and time in UTC that the member
|
||||||
|
requested to speak. It will be ``None`` if they are not requesting to speak
|
||||||
|
anymore or have been accepted to speak.
|
||||||
|
|
||||||
|
Only applicable to stage channels.
|
||||||
|
|
||||||
|
.. versionadded:: 1.7
|
||||||
|
|
||||||
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[:class:`VoiceChannel`]
|
channel: Optional[Union[:class:`VoiceChannel`, :class:`StageChannel`]]
|
||||||
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.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
__slots__ = ('session_id', 'deaf', 'mute', 'self_mute',
|
__slots__ = ('session_id', 'deaf', 'mute', 'self_mute',
|
||||||
'self_stream', 'self_video', 'self_deaf', 'afk', 'channel')
|
'self_stream', 'self_video', 'self_deaf', 'afk', 'channel',
|
||||||
|
'requested_to_speak_at', 'suppress')
|
||||||
|
|
||||||
def __init__(self, *, data, channel=None):
|
def __init__(self, *, data, channel=None):
|
||||||
self.session_id = data.get('session_id')
|
self.session_id = data.get('session_id')
|
||||||
@ -81,10 +100,20 @@ class VoiceState:
|
|||||||
self.afk = data.get('suppress', False)
|
self.afk = data.get('suppress', False)
|
||||||
self.mute = data.get('mute', False)
|
self.mute = data.get('mute', False)
|
||||||
self.deaf = data.get('deaf', False)
|
self.deaf = data.get('deaf', False)
|
||||||
|
self.suppress = data.get('suppress', False)
|
||||||
|
self.requested_to_speak_at = utils.parse_time(data.get('request_to_speak_timestamp'))
|
||||||
self.channel = channel
|
self.channel = channel
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return '<VoiceState self_mute={0.self_mute} self_deaf={0.self_deaf} self_stream={0.self_stream} channel={0.channel!r}>'.format(self)
|
attrs = [
|
||||||
|
('self_mute', self.self_mute),
|
||||||
|
('self_deaf', self.self_deaf),
|
||||||
|
('self_stream', self.self_stream),
|
||||||
|
('suppress', self.suppress),
|
||||||
|
('requested_to_speak_at', self.requested_to_speak_at),
|
||||||
|
('channel', self.channel)
|
||||||
|
]
|
||||||
|
return '<%s %s>' % (self.__class__.__name__, ' '.join('%s=%r' % t for t in attrs))
|
||||||
|
|
||||||
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()):
|
||||||
@ -559,6 +588,11 @@ class Member(discord.abc.Messageable, _BaseUser):
|
|||||||
Indicates if the member should be guild muted or un-muted.
|
Indicates if the member should be guild muted or un-muted.
|
||||||
deafen: :class:`bool`
|
deafen: :class:`bool`
|
||||||
Indicates if the member should be guild deafened or un-deafened.
|
Indicates if the member should be guild deafened or un-deafened.
|
||||||
|
suppress: :class:`bool`
|
||||||
|
Indicates if the member should be suppressed in stage channels.
|
||||||
|
|
||||||
|
.. versionadded:: 1.7
|
||||||
|
|
||||||
roles: Optional[List[:class:`Role`]]
|
roles: Optional[List[:class:`Role`]]
|
||||||
The member's new list of roles. This *replaces* the roles.
|
The member's new list of roles. This *replaces* the roles.
|
||||||
voice_channel: Optional[:class:`VoiceChannel`]
|
voice_channel: Optional[:class:`VoiceChannel`]
|
||||||
@ -576,6 +610,7 @@ class Member(discord.abc.Messageable, _BaseUser):
|
|||||||
"""
|
"""
|
||||||
http = self._state.http
|
http = self._state.http
|
||||||
guild_id = self.guild.id
|
guild_id = self.guild.id
|
||||||
|
me = self._state.self_id == self.id
|
||||||
payload = {}
|
payload = {}
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@ -585,7 +620,7 @@ class Member(discord.abc.Messageable, _BaseUser):
|
|||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
nick = nick or ''
|
nick = nick or ''
|
||||||
if self._state.self_id == self.id:
|
if me:
|
||||||
await http.change_my_nickname(guild_id, nick, reason=reason)
|
await http.change_my_nickname(guild_id, nick, reason=reason)
|
||||||
else:
|
else:
|
||||||
payload['nick'] = nick
|
payload['nick'] = nick
|
||||||
@ -598,6 +633,23 @@ class Member(discord.abc.Messageable, _BaseUser):
|
|||||||
if mute is not None:
|
if mute is not None:
|
||||||
payload['mute'] = mute
|
payload['mute'] = mute
|
||||||
|
|
||||||
|
suppress = fields.get('suppress')
|
||||||
|
if suppress is not None:
|
||||||
|
voice_state_payload = {
|
||||||
|
'channel_id': self.voice.channel.id,
|
||||||
|
'suppress': suppress,
|
||||||
|
}
|
||||||
|
|
||||||
|
if suppress or self.bot:
|
||||||
|
voice_state_payload['request_to_speak_timestamp'] = None
|
||||||
|
|
||||||
|
if me:
|
||||||
|
await http.edit_my_voice_state(guild_id, voice_state_payload)
|
||||||
|
else:
|
||||||
|
if not suppress:
|
||||||
|
voice_state_payload['request_to_speak_timestamp'] = datetime.datetime.utcnow().isoformat()
|
||||||
|
await http.edit_voice_state(guild_id, self.id, voice_state_payload)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
vc = fields['voice_channel']
|
vc = fields['voice_channel']
|
||||||
except KeyError:
|
except KeyError:
|
||||||
@ -612,10 +664,43 @@ class Member(discord.abc.Messageable, _BaseUser):
|
|||||||
else:
|
else:
|
||||||
payload['roles'] = tuple(r.id for r in roles)
|
payload['roles'] = tuple(r.id for r in roles)
|
||||||
|
|
||||||
await http.edit_member(guild_id, self.id, reason=reason, **payload)
|
if payload:
|
||||||
|
await http.edit_member(guild_id, self.id, reason=reason, **payload)
|
||||||
|
|
||||||
# TODO: wait for WS event for modify-in-place behaviour
|
# TODO: wait for WS event for modify-in-place behaviour
|
||||||
|
|
||||||
|
async def request_to_speak(self):
|
||||||
|
"""|coro|
|
||||||
|
|
||||||
|
Request to speak in the connected channel.
|
||||||
|
|
||||||
|
Only applies to stage channels.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
Requesting members that are not the client is equivalent
|
||||||
|
to :attr:`.edit` providing ``suppress`` as ``False``.
|
||||||
|
|
||||||
|
.. versionadded:: 1.7
|
||||||
|
|
||||||
|
Raises
|
||||||
|
-------
|
||||||
|
Forbidden
|
||||||
|
You do not have the proper permissions to the action requested.
|
||||||
|
HTTPException
|
||||||
|
The operation failed.
|
||||||
|
"""
|
||||||
|
payload = {
|
||||||
|
'channel_id': self.voice.channel.id,
|
||||||
|
'request_to_speak_timestamp': datetime.datetime.utcnow().isoformat(),
|
||||||
|
}
|
||||||
|
|
||||||
|
if self._state.self_id != self.id:
|
||||||
|
payload['suppress'] = False
|
||||||
|
await self._state.http.edit_voice_state(self.guild.id, self.id, payload)
|
||||||
|
else:
|
||||||
|
await self._state.http.edit_my_voice_state(self.guild.id, payload)
|
||||||
|
|
||||||
async def move_to(self, channel, *, reason=None):
|
async def move_to(self, channel, *, reason=None):
|
||||||
"""|coro|
|
"""|coro|
|
||||||
|
|
||||||
|
@ -213,6 +213,15 @@ class Permissions(BaseFlags):
|
|||||||
"""
|
"""
|
||||||
return cls(1 << 32)
|
return cls(1 << 32)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def stage_moderator(cls):
|
||||||
|
"""A factory method that creates a :class:`Permissions` with all
|
||||||
|
"Stage Moderator" permissions from the official Discord UI set to ``True``.
|
||||||
|
|
||||||
|
.. versionadded:: 1.7
|
||||||
|
"""
|
||||||
|
return cls(0b100000001010000000000000000000000)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def advanced(cls):
|
def advanced(cls):
|
||||||
"""A factory method that creates a :class:`Permissions` with all
|
"""A factory method that creates a :class:`Permissions` with all
|
||||||
@ -222,7 +231,6 @@ class Permissions(BaseFlags):
|
|||||||
"""
|
"""
|
||||||
return cls(1 << 3)
|
return cls(1 << 3)
|
||||||
|
|
||||||
|
|
||||||
def update(self, **kwargs):
|
def update(self, **kwargs):
|
||||||
r"""Bulk updates this permission object.
|
r"""Bulk updates this permission object.
|
||||||
|
|
||||||
|
15
docs/api.rst
15
docs/api.rst
@ -1074,6 +1074,12 @@ of :class:`enum.Enum`.
|
|||||||
|
|
||||||
A guild store channel.
|
A guild store channel.
|
||||||
|
|
||||||
|
.. attribute:: stage_voice
|
||||||
|
|
||||||
|
A guild stage voice channel.
|
||||||
|
|
||||||
|
.. versionadded:: 1.7
|
||||||
|
|
||||||
.. class:: MessageType
|
.. class:: MessageType
|
||||||
|
|
||||||
Specifies the type of :class:`Message`. This is used to denote if a message
|
Specifies the type of :class:`Message`. This is used to denote if a message
|
||||||
@ -3038,6 +3044,15 @@ VoiceChannel
|
|||||||
:members:
|
:members:
|
||||||
:inherited-members:
|
:inherited-members:
|
||||||
|
|
||||||
|
StageChannel
|
||||||
|
~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
.. attributetable:: StageChannel
|
||||||
|
|
||||||
|
.. autoclass:: StageChannel()
|
||||||
|
:members:
|
||||||
|
:inherited-members:
|
||||||
|
|
||||||
CategoryChannel
|
CategoryChannel
|
||||||
~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user