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` :class:`Permissions`
The resolved permissions. The resolved permissions.
""" """
return Permissions._dm_permissions()
base = Permissions.text()
base.read_messages = True
base.send_tts_messages = False
base.manage_messages = False
return base
def get_partial_message(self, message_id: int, /) -> PartialMessage: def get_partial_message(self, message_id: int, /) -> PartialMessage:
"""Creates a :class:`PartialMessage` from the message ID. """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. The resolved permissions for the user.
""" """
base = Permissions.text() base = Permissions._dm_permissions()
base.read_messages = True
base.send_tts_messages = False
base.manage_messages = False
base.mention_everyone = True base.mention_everyone = True
if obj.id == self.owner_id: 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.abc
import discord.utils 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 discord.context_managers import Typing
from .view import StringView from .view import StringView
@ -456,6 +456,76 @@ class Context(discord.abc.Messageable, Generic[BotT]):
# bot.user will never be None at this point. # 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 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 @property
def voice_client(self) -> Optional[VoiceProtocol]: def voice_client(self) -> Optional[VoiceProtocol]:
r"""Optional[:class:`.VoiceProtocol`]: A shortcut to :attr:`.Guild.voice_client`\, if applicable.""" 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)}") raise TypeError(f"Invalid permission(s): {', '.join(invalid)}")
def predicate(ctx: Context[BotT]) -> bool: def predicate(ctx: Context[BotT]) -> bool:
ch = ctx.channel permissions = ctx.permissions
permissions = ch.permissions_for(ctx.author) # type: ignore
missing = [perm for perm, value in perms.items() if getattr(permissions, perm) != value] 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)}") raise TypeError(f"Invalid permission(s): {', '.join(invalid)}")
def predicate(ctx: Context[BotT]) -> bool: def predicate(ctx: Context[BotT]) -> bool:
guild = ctx.guild permissions = ctx.bot_permissions
me = guild.me if guild is not None else ctx.bot.user
permissions = ctx.channel.permissions_for(me) # type: ignore
missing = [perm for perm, value in perms.items() if getattr(permissions, perm) != value] 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 p.read_message_history = False
return ~p.value 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 @classmethod
def all_channel(cls) -> Self: def all_channel(cls) -> Self:
"""A :class:`Permissions` with all channel-specific permissions set to """A :class:`Permissions` with all channel-specific permissions set to

Loading…
Cancel
Save