mirror of
				https://github.com/Rapptz/discord.py.git
				synced 2025-10-25 18:43:00 +00:00 
			
		
		
		
	Implement StageChannel and related methods
This commit is contained in:
		| @@ -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) | ||||||
|  |  | ||||||
|  |         if payload: | ||||||
|             await http.edit_member(guild_id, self.id, reason=reason, **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 | ||||||
| ~~~~~~~~~~~~~~~~~ | ~~~~~~~~~~~~~~~~~ | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user