diff --git a/discord/audit_logs.py b/discord/audit_logs.py index b50f496af1..ee8a4c47e9 100644 --- a/discord/audit_logs.py +++ b/discord/audit_logs.py @@ -238,6 +238,7 @@ class AuditLogChanges: "location_type", _enum_transformer(enums.ScheduledEventLocationType), ), + "command_id": ("command_id", _transform_snowflake), } def __init__( diff --git a/discord/bot.py b/discord/bot.py index e9ec44f777..97f0b78640 100644 --- a/discord/bot.py +++ b/discord/bot.py @@ -256,7 +256,8 @@ def _check_command(cmd: ApplicationCommand, match: Dict) -> bool: else: as_dict = cmd.to_dict() to_check = { - "default_permission": None, + "dm_permission": None, + "default_member_permissions": None, "name": None, "description": None, "name_localizations": None, @@ -572,7 +573,6 @@ async def sync_commands( register_guild_commands: bool = True, check_guilds: Optional[List[int]] = [], delete_exiting: bool = True, - _register_permissions: bool = True, # TODO: Remove for perms v2 ) -> None: """|coro| @@ -645,12 +645,6 @@ async def sync_commands( guild_commands, guild_id=guild_id, method=method, force=force, delete_existing=delete_exiting ) - # TODO: 2.1: Remove this and favor permissions v2 - # Global Command Permissions - - if not _register_permissions: - return - global_permissions: List = [] for i in registered_commands: @@ -664,12 +658,7 @@ async def sync_commands( cmd.id = i["id"] self._application_commands[cmd.id] = cmd - # Permissions (Roles will be converted to IDs just before Upsert for Global Commands) - global_permissions.append({"id": i["id"], "permissions": cmd.permissions}) - for guild_id, commands in registered_guild_commands.items(): - guild_permissions: List = [] - for i in commands: cmd = find( lambda cmd: cmd.name == i["name"] @@ -684,105 +673,6 @@ async def sync_commands( cmd.id = i["id"] self._application_commands[cmd.id] = cmd - # Permissions - permissions = [ - perm.to_dict() - for perm in cmd.permissions - if perm.guild_id is None - or (perm.guild_id == guild_id and cmd.guild_ids is not None and perm.guild_id in cmd.guild_ids) - ] - guild_permissions.append({"id": i["id"], "permissions": permissions}) - - for global_command in global_permissions: - permissions = [ - perm.to_dict() - for perm in global_command["permissions"] - if perm.guild_id is None - or (perm.guild_id == guild_id and cmd.guild_ids is not None and perm.guild_id in cmd.guild_ids) - ] - guild_permissions.append({"id": global_command["id"], "permissions": permissions}) - - # Collect & Upsert Permissions for Each Guild - # Command Permissions for this Guild - guild_cmd_perms: List = [] - - # Loop through Commands Permissions available for this Guild - for item in guild_permissions: - new_cmd_perm = {"id": item["id"], "permissions": []} - - # Replace Role / Owner Names with IDs - for permission in item["permissions"]: - if isinstance(permission["id"], str): - # Replace Role Names - if permission["type"] == 1: - role = get( - self._bot.get_guild(guild_id).roles, - name=permission["id"], - ) - - # If not missing - if role is not None: - new_cmd_perm["permissions"].append( - { - "id": role.id, - "type": 1, - "permission": permission["permission"], - } - ) - else: - raise RuntimeError( - "No Role ID found in Guild ({guild_id}) for Role ({role})".format( - guild_id=guild_id, role=permission["id"] - ) - ) - # Add owner IDs - elif permission["type"] == 2 and permission["id"] == "owner": - app = await self.application_info() # type: ignore - if app.team: - for m in app.team.members: - new_cmd_perm["permissions"].append( - { - "id": m.id, - "type": 2, - "permission": permission["permission"], - } - ) - else: - new_cmd_perm["permissions"].append( - { - "id": app.owner.id, - "type": 2, - "permission": permission["permission"], - } - ) - # Add the rest - else: - new_cmd_perm["permissions"].append(permission) - - # Make sure we don't have over 10 overwrites - if len(new_cmd_perm["permissions"]) > 10: - raise RuntimeError( - "Command '{name}' has more than 10 permission overrides in guild ({guild_id}).".format( - name=self._application_commands[new_cmd_perm["id"]].name, - guild_id=guild_id, - ) - ) - # Append to guild_cmd_perms - guild_cmd_perms.append(new_cmd_perm) - - # Upsert - try: - await self._bot.http.bulk_upsert_command_permissions(self._bot.user.id, guild_id, guild_cmd_perms) - except Forbidden: - raise RuntimeError( - f"Failed to add command permissions to guild {guild_id}", - file=sys.stderr, - ) - except HTTPException: - _log.warning( - "Command Permissions V2 not yet implemented, permissions will not be set for your commands." - ) - async def process_application_commands(self, interaction: Interaction, auto_sync: bool = None) -> None: """|coro| diff --git a/discord/commands/core.py b/discord/commands/core.py index 14065bed9a..5059ce8aeb 100644 --- a/discord/commands/core.py +++ b/discord/commands/core.py @@ -67,7 +67,6 @@ from ..utils import async_all, find, get_or_fetch, utcnow from .context import ApplicationContext, AutocompleteContext from .options import Option, OptionChoice -from .permissions import CommandPermission __all__ = ( "_BaseCommand", @@ -88,6 +87,7 @@ from typing_extensions import Concatenate, ParamSpec from ..cog import Cog + from .. import Permissions T = TypeVar("T") CogT = TypeVar("CogT", bound="Cog") @@ -201,6 +201,12 @@ def __init__(self, func: Callable, **kwargs) -> None: self.guild_ids: Optional[List[int]] = kwargs.get("guild_ids", None) self.parent = kwargs.get("parent") + # Permissions + self.default_member_permissions: Optional["Permissions"] = getattr( + func, "__default_member_permissions__", kwargs.get("default_member_permissions", None) + ) + self.guild_only: Optional[bool] = getattr(func, "__guild_only__", kwargs.get("guild_only", None)) + def __repr__(self) -> str: return f"" @@ -573,16 +579,11 @@ class SlashCommand(ApplicationCommand): parent: Optional[:class:`SlashCommandGroup`] The parent group that this command belongs to. ``None`` if there isn't one. - default_permission: :class:`bool` - Whether the command is enabled by default when it is added to a guild. - permissions: List[:class:`CommandPermission`] - The permissions for this command. - - .. note:: - - If this is not empty then default_permissions will be set to False. - - cog: Optional[:class:`.Cog`] + guild_only: :class:`bool` + Whether the command should only be usable inside a guild. + default_member_permissions: :class:`~discord.Permissions` + The default permissions a member needs to be able to run the command. + cog: Optional[:class:`Cog`] The cog that this command belongs to. ``None`` if there isn't one. checks: List[Callable[[:class:`.ApplicationContext`], :class:`bool`]] A list of predicates that verifies if the command could be executed @@ -647,15 +648,11 @@ def __init__(self, func: Callable, *args, **kwargs) -> None: self._before_invoke = None self._after_invoke = None - # Permissions - self.default_permission = kwargs.get("default_permission", True) - self.permissions: List[CommandPermission] = getattr(func, "__app_cmd_perms__", []) + kwargs.get( - "permissions", [] - ) - if self.permissions and self.default_permission: - self.default_permission = False - - def _check_required_params(self, params): + def _parse_options(self, params) -> List[Option]: + if list(params.items())[0][0] == "self": + temp = list(params.items()) + temp.pop(0) + params = dict(temp) params = iter(params.items()) required_params = ["self", "context"] if self.attached_to_group or self.cog else ["context"] for p in required_params: @@ -760,7 +757,6 @@ def to_dict(self) -> Dict: "name": self.name, "description": self.description, "options": [o.to_dict() for o in self.options], - "default_permission": self.default_permission, } if self.name_localizations is not None: as_dict["name_localizations"] = self.name_localizations @@ -769,6 +765,12 @@ def to_dict(self) -> Dict: if self.is_subcommand: as_dict["type"] = SlashCommandOptionType.sub_command.value + if self.guild_only is not None: + as_dict["guild_only"] = self.guild_only + + if self.default_member_permissions is not None: + as_dict["default_member_permissions"] = self.default_member_permissions.value + return as_dict async def _invoke(self, ctx: ApplicationContext) -> None: @@ -916,6 +918,10 @@ class SlashCommandGroup(ApplicationCommand): parent: Optional[:class:`SlashCommandGroup`] The parent group that this group belongs to. ``None`` if there isn't one. + guild_only: :class:`bool` + Whether the command should only be usable inside a guild. + default_member_permissions: :class:`~discord.Permissions` + The default permissions a member needs to be able to run the command. subcommands: List[Union[:class:`SlashCommand`, :class:`SlashCommandGroup`]] The list of all subcommands under this group. cog: Optional[:class:`.Cog`] @@ -978,10 +984,9 @@ def __init__( self.id = None # Permissions - self.default_permission = kwargs.get("default_permission", True) - self.permissions: List[CommandPermission] = kwargs.get("permissions", []) - if self.permissions and self.default_permission: - self.default_permission = False + self.default_member_permissions: Optional["Permissions"] = kwargs.get("default_member_permissions", None) + self.guild_only: Optional[bool] = kwargs.get("guild_only", None) + self.name_localizations: Optional[Dict[str, str]] = kwargs.get("name_localizations", None) self.description_localizations: Optional[Dict[str, str]] = kwargs.get("description_localizations", None) @@ -994,7 +999,6 @@ def to_dict(self) -> Dict: "name": self.name, "description": self.description, "options": [c.to_dict() for c in self.subcommands], - "default_permission": self.default_permission, } if self.name_localizations is not None: as_dict["name_localizations"] = self.name_localizations @@ -1004,6 +1008,12 @@ def to_dict(self) -> Dict: if self.parent is not None: as_dict["type"] = self.input_type.value + if self.guild_only is not None: + as_dict["guild_only"] = self.guild_only + + if self.default_member_permissions is not None: + as_dict["default_member_permissions"] = self.default_member_permissions.value + return as_dict def command(self, **kwargs) -> Callable[[Callable], SlashCommand]: @@ -1182,15 +1192,11 @@ class ContextMenuCommand(ApplicationCommand): The coroutine that is executed when the command is called. guild_ids: Optional[List[:class:`int`]] The ids of the guilds where this command will be registered. - default_permission: :class:`bool` - Whether the command is enabled by default when it is added to a guild. - permissions: List[:class:`.CommandPermission`] - The permissions for this command. - - .. note:: - If this is not empty then default_permissions will be set to ``False``. - - cog: Optional[:class:`.Cog`] + guild_only: :class:`bool` + Whether the command should only be usable inside a guild. + default_member_permissions: :class:`~discord.Permissions` + The default permissions a member needs to be able to run the command. + cog: Optional[:class:`Cog`] The cog that this command belongs to. ``None`` if there isn't one. checks: List[Callable[[:class:`.ApplicationContext`], :class:`bool`]] A list of predicates that verifies if the command could be executed @@ -1235,13 +1241,6 @@ def __init__(self, func: Callable, *args, **kwargs) -> None: self.validate_parameters() - self.default_permission = kwargs.get("default_permission", True) - self.permissions: List[CommandPermission] = getattr(func, "__app_cmd_perms__", []) + kwargs.get( - "permissions", [] - ) - if self.permissions and self.default_permission: - self.default_permission = False - # Context Menu commands can't have parents self.parent = None @@ -1282,9 +1281,14 @@ def to_dict(self) -> Dict[str, Union[str, int]]: "name": self.name, "description": self.description, "type": self.type, - "default_permission": self.default_permission, } + if self.guild_only is not None: + as_dict["guild_only"] = self.guild_only + + if self.default_member_permissions is not None: + as_dict["default_member_permissions"] = self.default_member_permissions.value + if self.name_localizations is not None: as_dict["name_localizations"] = self.name_localizations @@ -1618,42 +1622,42 @@ def validate_chat_input_name(name: Any, locale: Optional[str] = None): # Must meet the regex ^[-_\w\d\u0901-\u097D\u0E00-\u0E7F]{1,32}$ if locale is not None and locale not in valid_locales: raise ValidationError( - f"Locale '{locale}' is not a valid locale, " - f"see {docs}/reference#locales for list of supported locales." + f"Locale '{locale}' is not a valid locale, " f"see {docs}/reference#locales for list of supported locales." ) error = None - if not isinstance(name, str): + if not isinstance(name, str) or not re.match(r"^[\w-]{1,32}$", name): error = TypeError(f"Command names and options must be of type str. Received \"{name}\"") elif not re.match(r"^[-_\w\d\u0901-\u097D\u0E00-\u0E7F]{1,32}$", name): error = ValidationError( r"Command names and options must follow the regex \"^[-_\w\d\u0901-\u097D\u0E00-\u0E7F]{1,32}$\". For more information, see " f"{docs}/interactions/application-commands#application-command-object-application-command-naming. " - f"Received \"{name}\"" + f'Received "{name}"' ) elif not 1 <= len(name) <= 32: - error = ValidationError(f"Command names and options must be 1-32 characters long. Received \"{name}\"") + error = ValidationError(f'Command names and options must be 1-32 characters long. Received "{name}"') elif not name.lower() == name: # Can't use islower() as it fails if none of the chars can be lower. See #512. - error = ValidationError(f"Command names and options must be lowercase. Received \"{name}\"") + error = ValidationError(f'Command names and options must be lowercase. Received "{name}"') if error: if locale: - error.args = (error.args[0]+f" in locale {locale}",) + error.args = (f"{error.args[0]} in locale {locale}",) raise error def validate_chat_input_description(description: Any, locale: Optional[str] = None): if locale is not None and locale not in valid_locales: raise ValidationError( - f"Locale '{locale}' is not a valid locale, " - f"see {docs}/reference#locales for list of supported locales." + f"Locale '{locale}' is not a valid locale, " f"see {docs}/reference#locales for list of supported locales." ) error = None if not isinstance(description, str): - error = TypeError(f"Command and option description must be of type str. Received \"{description}\"") + error = TypeError(f'Command and option description must be of type str. Received "{description}"') elif not 1 <= len(description) <= 100: - error = ValidationError(f"Command and option description must be 1-100 characters long. Received \"{description}\"") + error = ValidationError( + f'Command and option description must be 1-100 characters long. Received "{description}"' + ) if error: if locale: - error.args = (error.args[0]+f" in locale {locale}",) + error.args = (f"{error.args[0]} in locale {locale}",) raise error diff --git a/discord/commands/permissions.py b/discord/commands/permissions.py index c8a6c3480d..49f3a08901 100644 --- a/discord/commands/permissions.py +++ b/discord/commands/permissions.py @@ -24,228 +24,83 @@ """ from typing import Callable, Dict, Union +from ..permissions import Permissions +from .core import ApplicationCommand __all__ = ( - "CommandPermission", - "has_role", - "has_any_role", - "is_user", - "is_owner", - "permission", + "default_permissions", + "guild_only", ) -class CommandPermission: - """The class used in the application command decorators - to hash permission data into a dictionary using the - :meth:`~to_dict` method to be sent to the discord API later on. - - .. versionadded:: 2.0 - - Attributes - ----------- - id: Union[:class:`int`, :class:`str`] - A string or integer that represents or helps get - the id of the user or role that the permission is tied to. - type: :class:`int` - An integer representing the type of the permission. - permission: :class:`bool` - A boolean representing the permission's value. - guild_id: :class:`int` - The integer which represents the id of the guild that the - permission may be tied to. - """ - - def __init__( - self, - id: Union[int, str], - type: int, - permission: bool = True, - guild_id: int = None, - ): - self.id = id - self.type = type - self.permission = permission - self.guild_id = guild_id - - def to_dict(self) -> Dict[str, Union[int, bool]]: - return {"id": self.id, "type": self.type, "permission": self.permission} +def default_permissions(**perms: bool) -> Callable: + """A decorator that limits the usage of a slash command to members with certain + permissions. + The permissions passed in must be exactly like the properties shown under + :class:`.discord.Permissions`. -def permission( - role_id: int = None, - user_id: int = None, - permission: bool = True, - guild_id: int = None, -): - """The method used to specify application command permissions - for specific users or roles using their id. - - This method is meant to be used as a decorator. - - .. versionadded:: 2.0 + .. note:: + These permissions can be updated by server administrators per-guild. As such, these are only "defaults", as the + name suggests. If you want to make sure that a user **always** has the specified permissions regardless, you + should use an internal check such as :func:`~.ext.commands.has_permissions`. Parameters - ----------- - role_id: :class:`int` - An integer which represents the id of the role that the - permission may be tied to. - user_id: :class:`int` - An integer which represents the id of the user that the - permission may be tied to. - permission: :class:`bool` - A boolean representing the permission's value. - guild_id: :class:`int` - The integer which represents the id of the guild that the - permission may be tied to. - """ - - def decorator(func: Callable): - if not role_id is None: - app_cmd_perm = CommandPermission(role_id, 1, permission, guild_id) - elif not user_id is None: - app_cmd_perm = CommandPermission(user_id, 2, permission, guild_id) - else: - raise ValueError("role_id or user_id must be specified!") - - # Create __app_cmd_perms__ - if not hasattr(func, "__app_cmd_perms__"): - func.__app_cmd_perms__ = [] - - # Append - func.__app_cmd_perms__.append(app_cmd_perm) - - return func + ------------ + perms + An argument list of permissions to check for. - return decorator + Example + --------- + .. code-block:: python3 -def has_role(item: Union[int, str], guild_id: int = None): - """The method used to specify application command role restrictions. + from discord import has_permissions - This method is meant to be used as a decorator. + @bot.slash_command() + @has_permissions(manage_messages=True) + async def test(ctx): + await ctx.respond('You can manage messages.') - .. versionadded:: 2.0 - - Parameters - ----------- - item: Union[:class:`int`, :class:`str`] - An integer or string that represent the id or name of the role - that the permission is tied to. - guild_id: :class:`int` - The integer which represents the id of the guild that the - permission may be tied to. """ - def decorator(func: Callable): - # Create __app_cmd_perms__ - if not hasattr(func, "__app_cmd_perms__"): - func.__app_cmd_perms__ = [] - - # Permissions (Will Convert ID later in register_commands if needed) - app_cmd_perm = CommandPermission(item, 1, True, guild_id) # {"id": item, "type": 1, "permission": True} - - # Append - func.__app_cmd_perms__.append(app_cmd_perm) - - return func - - return decorator + invalid = set(perms) - set(Permissions.VALID_FLAGS) + if invalid: + raise TypeError(f"Invalid permission(s): {', '.join(invalid)}") + def inner(command: Callable): + if isinstance(command, ApplicationCommand): + command.default_member_permissions = Permissions(**perms) + else: + command.__default_member_permissions__ = Permissions(**perms) + return command -def has_any_role(*items: Union[int, str], guild_id: int = None): - """The method used to specify multiple application command role restrictions, - The application command runs if the invoker has **any** of the specified roles. - - This method is meant to be used as a decorator. - - .. versionadded:: 2.0 - - Parameters - ----------- - *items: Union[:class:`int`, :class:`str`] - The integers or strings that represent the ids or names of the roles - that the permission is tied to. - guild_id: :class:`int` - The integer which represents the id of the guild that the - permission may be tied to. - """ - - def decorator(func: Callable): - # Create __app_cmd_perms__ - if not hasattr(func, "__app_cmd_perms__"): - func.__app_cmd_perms__ = [] - - # Permissions (Will Convert ID later in register_commands if needed) - for item in items: - app_cmd_perm = CommandPermission(item, 1, True, guild_id) # {"id": item, "type": 1, "permission": True} - - # Append - func.__app_cmd_perms__.append(app_cmd_perm) - - return func - - return decorator - - -def is_user(user: int, guild_id: int = None): - """The method used to specify application command user restrictions. - - This method is meant to be used as a decorator. - - .. versionadded:: 2.0 - - Parameters - ----------- - user: :class:`int` - An integer that represent the id of the user that the permission is tied to. - guild_id: :class:`int` - The integer which represents the id of the guild that the - permission may be tied to. - """ - - def decorator(func: Callable): - # Create __app_cmd_perms__ - if not hasattr(func, "__app_cmd_perms__"): - func.__app_cmd_perms__ = [] - - # Permissions (Will Convert ID later in register_commands if needed) - app_cmd_perm = CommandPermission(user, 2, True, guild_id) # {"id": user, "type": 2, "permission": True} - - # Append - func.__app_cmd_perms__.append(app_cmd_perm) + return inner - return func - return decorator +def guild_only() -> Callable: + """A decorator that limits the usage of a slash command to guild contexts. + The command won't be able to be used in private message channels. + Example + --------- -def is_owner(guild_id: int = None): - """The method used to limit application commands exclusively - to the owner of the bot. + .. code-block:: python3 - This method is meant to be used as a decorator. + from discord import guild_only - .. versionadded:: 2.0 + @bot.slash_command() + @guild_only() + async def test(ctx): + await ctx.respond('You\'re in a guild.') - Parameters - ----------- - guild_id: :class:`int` - The integer which represents the id of the guild that the - permission may be tied to. """ - def decorator(func: Callable): - # Create __app_cmd_perms__ - if not hasattr(func, "__app_cmd_perms__"): - func.__app_cmd_perms__ = [] - - # Permissions (Will Convert ID later in register_commands if needed) - app_cmd_perm = CommandPermission("owner", 2, True, guild_id) # {"id": "owner", "type": 2, "permission": True} - - # Append - func.__app_cmd_perms__.append(app_cmd_perm) - - return func + def inner(command: Callable): + if isinstance(command, ApplicationCommand): + command.guild_only = True + else: + command.__guild_only__ = True + return command - return decorator + return inner diff --git a/discord/enums.py b/discord/enums.py index 7a9230de11..9066b77d7a 100644 --- a/discord/enums.py +++ b/discord/enums.py @@ -380,6 +380,7 @@ class AuditLogAction(Enum): thread_create = 110 thread_update = 111 thread_delete = 112 + application_command_permission_update = 121 @property def category(self) -> Optional[AuditLogActionCategory]: @@ -431,6 +432,7 @@ def category(self) -> Optional[AuditLogActionCategory]: AuditLogAction.thread_create: AuditLogActionCategory.create, AuditLogAction.thread_update: AuditLogActionCategory.update, AuditLogAction.thread_delete: AuditLogActionCategory.delete, + AuditLogAction.application_command_permission_update: AuditLogActionCategory.update, } return lookup[self] @@ -467,6 +469,8 @@ def target_type(self) -> Optional[str]: return "scheduled_event" elif v < 113: return "thread" + elif v < 121: + return "application_command_permission" class UserFlags(Enum): diff --git a/discord/http.py b/discord/http.py index 5ce4222e22..8d8dae8697 100644 --- a/discord/http.py +++ b/discord/http.py @@ -2239,20 +2239,35 @@ def bulk_upsert_guild_commands( ) return self.request(r, json=payload) - def bulk_upsert_command_permissions( + # Application commands (permissions) + + def get_command_permissions( self, application_id: Snowflake, guild_id: Snowflake, - payload: List[interactions.EditApplicationCommand], - ) -> Response[List[interactions.ApplicationCommand]]: + command_id: Snowflake, + ) -> Response[interactions.GuildApplicationCommandPermissions]: r = Route( - "PUT", + "GET", + "/applications/{application_id}/guilds/{guild_id}/commands/{command_id}/permissions", + application_id=application_id, + guild_id=guild_id, + ) + return self.request(r) + + def get_guild_command_permissions( + self, + application_id: Snowflake, + guild_id: Snowflake, + ) -> Response[List[interactions.GuildApplicationCommandPermissions]]: + r = Route( + "GET", "/applications/{application_id}/guilds/{guild_id}/commands/permissions", application_id=application_id, guild_id=guild_id, ) - return self.request(r, json=payload) - + return self.request(r) + # Interaction responses def _edit_webhook_helper( diff --git a/discord/state.py b/discord/state.py index f115f0193f..0226351e73 100644 --- a/discord/state.py +++ b/discord/state.py @@ -613,6 +613,10 @@ def parse_ready(self, data) -> None: def parse_resumed(self, data) -> None: self.dispatch("resumed") + def parse_application_command_permissions_update(self, data) -> None: + # unsure what the implementation would be like + pass + def parse_message_create(self, data) -> None: channel, _ = self._get_guild_channel(data) # channel would be the correct type here diff --git a/discord/types/interactions.py b/discord/types/interactions.py index 8ce26f4a76..56cb9c1f5e 100644 --- a/discord/types/interactions.py +++ b/discord/types/interactions.py @@ -85,7 +85,7 @@ class ApplicationCommandOptionChoice(_ApplicationCommandOptionChoiceOptional): value: Union[str, int] -ApplicationCommandPermissionType = Literal[1, 2] +ApplicationCommandPermissionType = Literal[1, 2, 3] class ApplicationCommandPermissions(TypedDict): diff --git a/docs/api.rst b/docs/api.rst index 7be1558187..7c6648a8b3 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -96,6 +96,18 @@ AutoShardedBot Application Commands --------------------- + +Command Permission Decorators +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +.. autofunction:: discord.commands.default_permissions + :decorator: + +.. autofunction:: discord.commands.guild_only + :decorator: + + ApplicationCommand ~~~~~~~~~~~~~~~~~~~ @@ -104,10 +116,10 @@ ApplicationCommand .. autoclass:: ApplicationCommand :members: -.. autofunction:: discord.commands.core.application_command +.. autofunction:: discord.commands.application_command :decorator: -.. autofunction:: discord.commands.core.command +.. autofunction:: discord.commands.command :decorator: SlashCommand @@ -118,7 +130,7 @@ SlashCommand .. autoclass:: SlashCommand :members: -.. autofunction:: discord.commands.core.slash_command +.. autofunction:: discord.commands.slash_command :decorator: SlashCommandGroup @@ -137,7 +149,7 @@ Option .. autoclass:: Option :members: -.. autofunction:: discord.commands.core.Option +.. autofunction:: discord.commands.Option :decorator: OptionChoice @@ -156,7 +168,7 @@ UserCommand .. autoclass:: UserCommand :members: -.. autofunction:: discord.commands.core.user_command +.. autofunction:: discord.commands.user_command :decorator: MessageCommand @@ -167,7 +179,7 @@ MessageCommand .. autoclass:: MessageCommand :members: -.. autofunction:: discord.commands.core.message_command +.. autofunction:: discord.commands.message_command :decorator: ApplicationContext @@ -186,29 +198,6 @@ AutocompleteContext .. autoclass:: AutocompleteContext :members: -CommandPermission -~~~~~~~~~~~~~~~~~ - -.. attributetable:: CommandPermission - -.. autoclass:: CommandPermission - :members: - -.. autofunction:: discord.commands.permissions.permission - :decorator: - -.. autofunction:: discord.commands.permissions.has_role - :decorator: - -.. autofunction:: discord.commands.permissions.has_any_role - :decorator: - -.. autofunction:: discord.commands.permissions.is_user - :decorator: - -.. autofunction:: discord.commands.permissions.is_owner - :decorator: - Cogs ----- @@ -2865,6 +2854,21 @@ of :class:`enum.Enum`. - :attr:`~AuditLogDiff.invitable` .. versionadded:: 2.0 + + .. attribute:: application_command_permission_update + + An application command's permissions were updated. + + When this is the action, the type of :attr:`~AuditLogEntry.target` is + an :class:`Object` with the ID of the command that + had it's permissions edited. + + Possible attributes for :class:`AuditLogDiff`: + + - :attr:`~AuditLogDiff.command_id` + + .. versionadded:: 2.0 + .. class:: AuditLogActionCategory @@ -3927,6 +3931,12 @@ AuditLogDiff Non-moderators can now add other non-moderators to this thread. :type: :class:`bool` + + .. attribute:: command_id + + This command's permissions were updated. + + :type: :class:`int` .. this is currently missing the following keys: reason and application_id I'm not sure how to about porting these