Browse Source

[commands] Add Context.permissions and Context.bot_permissions

pull/8173/merge
zephyrkul 3 years ago
committed by GitHub
parent
commit
9fe19dcc69
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 12
      discord/channel.py
  2. 72
      discord/ext/commands/context.py
  3. 7
      discord/ext/commands/core.py
  4. 8
      discord/permissions.py

12
discord/channel.py

@ -2498,12 +2498,7 @@ class DMChannel(discord.abc.Messageable, discord.abc.PrivateChannel, Hashable):
:class:`Permissions`
The resolved permissions.
"""
base = Permissions.text()
base.read_messages = True
base.send_tts_messages = False
base.manage_messages = False
return base
return Permissions._dm_permissions()
def get_partial_message(self, message_id: int, /) -> PartialMessage:
"""Creates a :class:`PartialMessage` from the message ID.
@ -2671,10 +2666,7 @@ class GroupChannel(discord.abc.Messageable, discord.abc.PrivateChannel, Hashable
The resolved permissions for the user.
"""
base = Permissions.text()
base.read_messages = True
base.send_tts_messages = False
base.manage_messages = False
base = Permissions._dm_permissions()
base.mention_everyone = True
if obj.id == self.owner_id:

72
discord/ext/commands/context.py

@ -28,7 +28,7 @@ from typing import TYPE_CHECKING, Any, Dict, Generator, Generic, List, Optional,
import discord.abc
import discord.utils
from discord import Interaction, Message, Attachment, MessageType, User, PartialMessageable
from discord import Interaction, Message, Attachment, MessageType, User, PartialMessageable, Permissions, ChannelType, Thread
from discord.context_managers import Typing
from .view import StringView
@ -456,6 +456,76 @@ class Context(discord.abc.Messageable, Generic[BotT]):
# bot.user will never be None at this point.
return self.guild.me if self.guild is not None else self.bot.user # type: ignore
@discord.utils.cached_property
def permissions(self) -> Permissions:
""":class:`.Permissions`: Returns the resolved permissions for the invoking user in this channel.
Shorthand for :meth:`.abc.GuildChannel.permissions_for` or :attr:`.Interaction.permissions`.
.. versionadded:: 2.0
"""
if self.channel.type is ChannelType.private:
return Permissions._dm_permissions()
if not self.interaction:
# channel and author will always match relevant types here
return self.channel.permissions_for(self.author) # type: ignore
base = self.interaction.permissions
if self.channel.type in (ChannelType.voice, ChannelType.stage_voice):
if not base.connect:
# voice channels cannot be edited by people who can't connect to them
# It also implicitly denies all other voice perms
denied = Permissions.voice()
denied.update(manage_channels=True, manage_roles=True)
base.value &= ~denied.value
else:
# text channels do not have voice related permissions
denied = Permissions.voice()
base.value &= ~denied.value
return base
@discord.utils.cached_property
def bot_permissions(self) -> Permissions:
""":class:`.Permissions`: Returns the resolved permissions for the bot in this channel.
Shorthand for :meth:`.abc.GuildChannel.permissions_for` or :attr:`.Interaction.app_permissions`.
For interaction-based commands, this will reflect the effective permissions
for :class:`Context` calls, which may differ from calls through
other :class:`.abc.Messageable` endpoints, like :attr:`channel`.
Notably, sending messages, embedding links, and attaching files are always
permitted, while reading messages might not be.
.. versionadded:: 2.0
"""
channel = self.channel
if channel.type == ChannelType.private:
return Permissions._dm_permissions()
if not self.interaction:
# channel and me will always match relevant types here
return channel.permissions_for(self.me) # type: ignore
guild = channel.guild
base = self.interaction.app_permissions
if self.channel.type in (ChannelType.voice, ChannelType.stage_voice):
if not base.connect:
# voice channels cannot be edited by people who can't connect to them
# It also implicitly denies all other voice perms
denied = Permissions.voice()
denied.update(manage_channels=True, manage_roles=True)
base.value &= ~denied.value
else:
# text channels do not have voice related permissions
denied = Permissions.voice()
base.value &= ~denied.value
base.update(
embed_links=True,
attach_files=True,
send_tts_messages=False,
)
if isinstance(channel, Thread):
base.send_messages_in_threads = True
else:
base.send_messages = True
return base
@property
def voice_client(self) -> Optional[VoiceProtocol]:
r"""Optional[:class:`.VoiceProtocol`]: A shortcut to :attr:`.Guild.voice_client`\, if applicable."""

7
discord/ext/commands/core.py

@ -2159,8 +2159,7 @@ def has_permissions(**perms: bool) -> Check[Any]:
raise TypeError(f"Invalid permission(s): {', '.join(invalid)}")
def predicate(ctx: Context[BotT]) -> bool:
ch = ctx.channel
permissions = ch.permissions_for(ctx.author) # type: ignore
permissions = ctx.permissions
missing = [perm for perm, value in perms.items() if getattr(permissions, perm) != value]
@ -2185,9 +2184,7 @@ def bot_has_permissions(**perms: bool) -> Check[Any]:
raise TypeError(f"Invalid permission(s): {', '.join(invalid)}")
def predicate(ctx: Context[BotT]) -> bool:
guild = ctx.guild
me = guild.me if guild is not None else ctx.bot.user
permissions = ctx.channel.permissions_for(me) # type: ignore
permissions = ctx.bot_permissions
missing = [perm for perm, value in perms.items() if getattr(permissions, perm) != value]

8
discord/permissions.py

@ -186,6 +186,14 @@ class Permissions(BaseFlags):
p.read_message_history = False
return ~p.value
@classmethod
def _dm_permissions(cls) -> Self:
base = cls.text()
base.read_messages = True
base.send_tts_messages = False
base.manage_messages = False
return base
@classmethod
def all_channel(cls) -> Self:
"""A :class:`Permissions` with all channel-specific permissions set to

Loading…
Cancel
Save