From 619bc50e5d665d811c48e4a18c2ab06f5eed0400 Mon Sep 17 00:00:00 2001 From: z03h <7235242+z03h@users.noreply.github.com> Date: Sun, 1 May 2022 15:59:57 -0700 Subject: [PATCH] Add application command permissions to audit log --- discord/app_commands/models.py | 27 +++++ discord/audit_logs.py | 112 ++++++++++++++++++- discord/enums.py | 192 +++++++++++++++++---------------- discord/guild.py | 39 ++++--- discord/integrations.py | 51 ++++++++- discord/types/audit_log.py | 12 +++ discord/types/command.py | 2 +- discord/types/integration.py | 1 + docs/api.rst | 43 +++++++- docs/interactions/api.rst | 8 ++ 10 files changed, 374 insertions(+), 113 deletions(-) diff --git a/discord/app_commands/models.py b/discord/app_commands/models.py index 687c0ddc7..fcac106fe 100644 --- a/discord/app_commands/models.py +++ b/discord/app_commands/models.py @@ -39,6 +39,7 @@ __all__ = ( 'AppCommandThread', 'Argument', 'Choice', + 'AllChannels', ) ChoiceT = TypeVar('ChoiceT', str, int, float, Union[str, int, float]) @@ -70,6 +71,32 @@ if TYPE_CHECKING: ApplicationCommandParent = Union['AppCommand', 'AppCommandGroup'] +class AllChannels: + """Represents all channels for application command permissions. + + .. versionadded:: 2.0 + + Attributes + ----------- + id: :class:`int` + The guilds id - 1. + guild: :class:`~discord.Guild` + The guild the application command permission is for. + """ + + __slots__ = ( + 'id', + 'guild', + ) + + def __init__(self, guild: Guild): + self.id = guild.id - 1 + self.guild = guild + + def __repr__(self): + return f'' + + class AppCommand(Hashable): """Represents a application command. diff --git a/discord/audit_logs.py b/discord/audit_logs.py index 5c4c5d2c1..b744deaf1 100644 --- a/discord/audit_logs.py +++ b/discord/audit_logs.py @@ -61,10 +61,13 @@ if TYPE_CHECKING: from .types.invite import Invite as InvitePayload from .types.role import Role as RolePayload from .types.snowflake import Snowflake + from .types.command import ApplicationCommandPermissions from .user import User from .stage_instance import StageInstance from .sticker import GuildSticker from .threads import Thread + from .integrations import PartialIntegration + from .app_commands import AppCommand TargetType = Union[ Guild, abc.GuildChannel, Member, User, Role, Invite, Emoji, StageInstance, GuildSticker, Thread, Object, None @@ -253,6 +256,24 @@ class AuditLogChanges: self.before: AuditLogDiff = AuditLogDiff() self.after: AuditLogDiff = AuditLogDiff() + if entry.action is enums.AuditLogAction.app_command_permission_update: + # special case entire process since each + # element in data is a different target + self.before.app_command_permissions = [] + self.after.app_command_permissions = [] + + for d in data: + + self._handle_app_command_permissions( + self.before, + self.after, + entry, + int(d['key']), + d.get('old_value'), # type: ignore # old value will be an ApplicationCommandPermissions if present + d.get('new_value'), # type: ignore # new value will be an ApplicationCommandPermissions if present + ) + return + for elem in data: attr = elem['key'] @@ -324,6 +345,52 @@ class AuditLogChanges: setattr(second, 'roles', data) + def _handle_app_command_permissions( + self, + before: AuditLogDiff, + after: AuditLogDiff, + entry: AuditLogEntry, + target_id: int, + old_value: Optional[ApplicationCommandPermissions], + new_value: Optional[ApplicationCommandPermissions], + ): + guild = entry.guild + + old_permission = new_permission = target = None + + if target_id == (guild.id - 1): + # avoid circular import + from .app_commands import AllChannels + + # all channels + target = AllChannels(guild) + else: + # get type and determine role, user or channel + _value = old_value or new_value + if _value is None: + return + permission_type = _value['type'] + if permission_type == 1: + # role + target = guild.get_role(target_id) + elif permission_type == 2: + # user + target = entry._get_member(target_id) + elif permission_type == 3: + # channel + target = guild.get_channel(target_id) + + if target is None: + target = Object(target_id) + + if old_value is not None: + old_permission = old_value['permission'] + before.app_command_permissions.append((target, old_permission)) + + if new_value is not None: + new_permission = new_value['permission'] + after.app_command_permissions.append((target, new_permission)) + class _AuditLogProxy: def __init__(self, **kwargs: Any) -> None: @@ -397,10 +464,20 @@ class AuditLogEntry(Hashable): which actions have this field filled out. """ - def __init__(self, *, users: Dict[int, User], data: AuditLogEntryPayload, guild: Guild): + def __init__( + self, + *, + users: Dict[int, User], + integrations: Dict[int, PartialIntegration], + app_commands: Dict[int, AppCommand], + data: AuditLogEntryPayload, + guild: Guild, + ): self._state: ConnectionState = guild._state self.guild: Guild = guild self._users: Dict[int, User] = users + self._integrations: Dict[int, PartialIntegration] = integrations + self._app_commands: Dict[int, AppCommand] = app_commands self._from_data(data) def _from_data(self, data: AuditLogEntryPayload) -> None: @@ -418,7 +495,7 @@ class AuditLogEntry(Hashable): _AuditLogProxyMemberDisconnect, _AuditLogProxyPinAction, _AuditLogProxyStageInstanceAction, - Member, User, None, + Member, User, None, PartialIntegration, Role, Object ] = None # fmt: on @@ -463,6 +540,9 @@ class AuditLogEntry(Hashable): self.extra = _AuditLogProxyStageInstanceAction( channel=self.guild.get_channel(channel_id) or Object(id=channel_id) ) + elif self.action.name.startswith('app_command'): + application_id = int(extra['application_id']) + self.extra = self._get_integration_by_app_id(application_id) or Object(application_id) # this key is not present when the above is present, typically. # It's a list of { new_value: a, old_value: b, key: c } @@ -481,6 +561,25 @@ class AuditLogEntry(Hashable): return self.guild.get_member(user_id) or self._users.get(user_id) + def _get_integration(self, integration_id: Optional[int]) -> Optional[PartialIntegration]: + if integration_id is None: + return None + + return self._integrations.get(integration_id) + + def _get_integration_by_app_id(self, application_id: Optional[int]) -> Optional[PartialIntegration]: + if application_id is None: + return None + + # get PartialIntegration by application id + return utils.get(self._integrations.values(), application_id=application_id) + + def _get_app_command(self, app_command_id: Optional[int]) -> Optional[AppCommand]: + if app_command_id is None: + return None + + return self._app_commands.get(app_command_id) + def __repr__(self) -> str: return f'' @@ -575,3 +674,12 @@ class AuditLogEntry(Hashable): def _convert_target_guild_scheduled_event(self, target_id: int) -> Union[ScheduledEvent, Object]: return self.guild.get_scheduled_event(target_id) or Object(id=target_id) + + def _convert_target_integration(self, target_id: int) -> Union[PartialIntegration, Object]: + return self._get_integration(target_id) or Object(target_id) + + def _convert_target_app_command(self, target_id: int) -> Union[AppCommand, Object]: + return self._get_app_command(target_id) or Object(target_id) + + def _convert_target_integration_or_app_command(self, target_id: int) -> Union[PartialIntegration, AppCommand, Object]: + return self._get_integration_by_app_id(target_id) or self._get_app_command(target_id) or Object(target_id) diff --git a/discord/enums.py b/discord/enums.py index d0b69c560..c56c92cc5 100644 --- a/discord/enums.py +++ b/discord/enums.py @@ -298,106 +298,108 @@ class AuditLogActionCategory(Enum): class AuditLogAction(Enum): # fmt: off - guild_update = 1 - channel_create = 10 - channel_update = 11 - channel_delete = 12 - overwrite_create = 13 - overwrite_update = 14 - overwrite_delete = 15 - kick = 20 - member_prune = 21 - ban = 22 - unban = 23 - member_update = 24 - member_role_update = 25 - member_move = 26 - member_disconnect = 27 - bot_add = 28 - role_create = 30 - role_update = 31 - role_delete = 32 - invite_create = 40 - invite_update = 41 - invite_delete = 42 - webhook_create = 50 - webhook_update = 51 - webhook_delete = 52 - emoji_create = 60 - emoji_update = 61 - emoji_delete = 62 - message_delete = 72 - message_bulk_delete = 73 - message_pin = 74 - message_unpin = 75 - integration_create = 80 - integration_update = 81 - integration_delete = 82 - stage_instance_create = 83 - stage_instance_update = 84 - stage_instance_delete = 85 - sticker_create = 90 - sticker_update = 91 - sticker_delete = 92 - scheduled_event_create = 100 - scheduled_event_update = 101 - scheduled_event_delete = 102 - thread_create = 110 - thread_update = 111 - thread_delete = 112 + guild_update = 1 + channel_create = 10 + channel_update = 11 + channel_delete = 12 + overwrite_create = 13 + overwrite_update = 14 + overwrite_delete = 15 + kick = 20 + member_prune = 21 + ban = 22 + unban = 23 + member_update = 24 + member_role_update = 25 + member_move = 26 + member_disconnect = 27 + bot_add = 28 + role_create = 30 + role_update = 31 + role_delete = 32 + invite_create = 40 + invite_update = 41 + invite_delete = 42 + webhook_create = 50 + webhook_update = 51 + webhook_delete = 52 + emoji_create = 60 + emoji_update = 61 + emoji_delete = 62 + message_delete = 72 + message_bulk_delete = 73 + message_pin = 74 + message_unpin = 75 + integration_create = 80 + integration_update = 81 + integration_delete = 82 + stage_instance_create = 83 + stage_instance_update = 84 + stage_instance_delete = 85 + sticker_create = 90 + sticker_update = 91 + sticker_delete = 92 + scheduled_event_create = 100 + scheduled_event_update = 101 + scheduled_event_delete = 102 + thread_create = 110 + thread_update = 111 + thread_delete = 112 + app_command_permission_update = 121 # fmt: on @property def category(self) -> Optional[AuditLogActionCategory]: # fmt: off lookup: Dict[AuditLogAction, Optional[AuditLogActionCategory]] = { - AuditLogAction.guild_update: AuditLogActionCategory.update, - AuditLogAction.channel_create: AuditLogActionCategory.create, - AuditLogAction.channel_update: AuditLogActionCategory.update, - AuditLogAction.channel_delete: AuditLogActionCategory.delete, - AuditLogAction.overwrite_create: AuditLogActionCategory.create, - AuditLogAction.overwrite_update: AuditLogActionCategory.update, - AuditLogAction.overwrite_delete: AuditLogActionCategory.delete, - AuditLogAction.kick: None, - AuditLogAction.member_prune: None, - AuditLogAction.ban: None, - AuditLogAction.unban: None, - AuditLogAction.member_update: AuditLogActionCategory.update, - AuditLogAction.member_role_update: AuditLogActionCategory.update, - AuditLogAction.member_move: None, - AuditLogAction.member_disconnect: None, - AuditLogAction.bot_add: None, - AuditLogAction.role_create: AuditLogActionCategory.create, - AuditLogAction.role_update: AuditLogActionCategory.update, - AuditLogAction.role_delete: AuditLogActionCategory.delete, - AuditLogAction.invite_create: AuditLogActionCategory.create, - AuditLogAction.invite_update: AuditLogActionCategory.update, - AuditLogAction.invite_delete: AuditLogActionCategory.delete, - AuditLogAction.webhook_create: AuditLogActionCategory.create, - AuditLogAction.webhook_update: AuditLogActionCategory.update, - AuditLogAction.webhook_delete: AuditLogActionCategory.delete, - AuditLogAction.emoji_create: AuditLogActionCategory.create, - AuditLogAction.emoji_update: AuditLogActionCategory.update, - AuditLogAction.emoji_delete: AuditLogActionCategory.delete, - AuditLogAction.message_delete: AuditLogActionCategory.delete, - AuditLogAction.message_bulk_delete: AuditLogActionCategory.delete, - AuditLogAction.message_pin: None, - AuditLogAction.message_unpin: None, - AuditLogAction.integration_create: AuditLogActionCategory.create, - AuditLogAction.integration_update: AuditLogActionCategory.update, - AuditLogAction.integration_delete: AuditLogActionCategory.delete, - AuditLogAction.stage_instance_create: AuditLogActionCategory.create, - AuditLogAction.stage_instance_update: AuditLogActionCategory.update, - AuditLogAction.stage_instance_delete: AuditLogActionCategory.delete, - AuditLogAction.sticker_create: AuditLogActionCategory.create, - AuditLogAction.sticker_update: AuditLogActionCategory.update, - AuditLogAction.sticker_delete: AuditLogActionCategory.delete, - AuditLogAction.scheduled_event_create: AuditLogActionCategory.create, - AuditLogAction.scheduled_event_update: AuditLogActionCategory.update, - AuditLogAction.scheduled_event_delete: AuditLogActionCategory.delete, - AuditLogAction.thread_create: AuditLogActionCategory.create, - AuditLogAction.thread_update: AuditLogActionCategory.update, - AuditLogAction.thread_delete: AuditLogActionCategory.delete, + AuditLogAction.guild_update: AuditLogActionCategory.update, + AuditLogAction.channel_create: AuditLogActionCategory.create, + AuditLogAction.channel_update: AuditLogActionCategory.update, + AuditLogAction.channel_delete: AuditLogActionCategory.delete, + AuditLogAction.overwrite_create: AuditLogActionCategory.create, + AuditLogAction.overwrite_update: AuditLogActionCategory.update, + AuditLogAction.overwrite_delete: AuditLogActionCategory.delete, + AuditLogAction.kick: None, + AuditLogAction.member_prune: None, + AuditLogAction.ban: None, + AuditLogAction.unban: None, + AuditLogAction.member_update: AuditLogActionCategory.update, + AuditLogAction.member_role_update: AuditLogActionCategory.update, + AuditLogAction.member_move: None, + AuditLogAction.member_disconnect: None, + AuditLogAction.bot_add: None, + AuditLogAction.role_create: AuditLogActionCategory.create, + AuditLogAction.role_update: AuditLogActionCategory.update, + AuditLogAction.role_delete: AuditLogActionCategory.delete, + AuditLogAction.invite_create: AuditLogActionCategory.create, + AuditLogAction.invite_update: AuditLogActionCategory.update, + AuditLogAction.invite_delete: AuditLogActionCategory.delete, + AuditLogAction.webhook_create: AuditLogActionCategory.create, + AuditLogAction.webhook_update: AuditLogActionCategory.update, + AuditLogAction.webhook_delete: AuditLogActionCategory.delete, + AuditLogAction.emoji_create: AuditLogActionCategory.create, + AuditLogAction.emoji_update: AuditLogActionCategory.update, + AuditLogAction.emoji_delete: AuditLogActionCategory.delete, + AuditLogAction.message_delete: AuditLogActionCategory.delete, + AuditLogAction.message_bulk_delete: AuditLogActionCategory.delete, + AuditLogAction.message_pin: None, + AuditLogAction.message_unpin: None, + AuditLogAction.integration_create: AuditLogActionCategory.create, + AuditLogAction.integration_update: AuditLogActionCategory.update, + AuditLogAction.integration_delete: AuditLogActionCategory.delete, + AuditLogAction.stage_instance_create: AuditLogActionCategory.create, + AuditLogAction.stage_instance_update: AuditLogActionCategory.update, + AuditLogAction.stage_instance_delete: AuditLogActionCategory.delete, + AuditLogAction.sticker_create: AuditLogActionCategory.create, + AuditLogAction.sticker_update: AuditLogActionCategory.update, + AuditLogAction.sticker_delete: AuditLogActionCategory.delete, + AuditLogAction.scheduled_event_create: AuditLogActionCategory.create, + AuditLogAction.scheduled_event_update: AuditLogActionCategory.update, + AuditLogAction.scheduled_event_delete: AuditLogActionCategory.delete, + AuditLogAction.thread_create: AuditLogActionCategory.create, + AuditLogAction.thread_delete: AuditLogActionCategory.delete, + AuditLogAction.thread_update: AuditLogActionCategory.update, + AuditLogAction.app_command_permission_update: AuditLogActionCategory.update, } # fmt: on return lookup[self] @@ -435,6 +437,8 @@ class AuditLogAction(Enum): return 'guild_scheduled_event' elif v < 113: return 'thread' + elif v < 122: + return 'integration_or_app_command' class UserFlags(Enum): diff --git a/discord/guild.py b/discord/guild.py index 5974a5b98..fd5890b31 100644 --- a/discord/guild.py +++ b/discord/guild.py @@ -78,7 +78,7 @@ from .invite import Invite from .widget import Widget from .asset import Asset from .flags import SystemChannelFlags -from .integrations import Integration, _integration_factory +from .integrations import Integration, PartialIntegration, _integration_factory from .scheduled_event import ScheduledEvent from .stage_instance import StageInstance from .threads import Thread, ThreadMember @@ -3346,11 +3346,11 @@ class Guild(Hashable): if data and entries: if limit is not None: - limit -= len(data) + limit -= len(entries) before = Object(id=int(entries[-1]['id'])) - return data.get('users', []), entries, before, limit + return data, entries, after, limit async def _after_strategy(retrieve, after, limit): after_id = after.id if after else None @@ -3362,11 +3362,11 @@ class Guild(Hashable): if data and entries: if limit is not None: - limit -= len(data) + limit -= len(entries) after = Object(id=int(entries[0]['id'])) - return data.get('users', []), entries, after, limit + return data, entries, after, limit if user is not MISSING: user_id = user.id @@ -3397,31 +3397,46 @@ class Guild(Hashable): if after and after != OLDEST_OBJECT: predicate = lambda m: int(m['id']) > after.id + # avoid circular import + from .app_commands import AppCommand + while True: retrieve = min(100 if limit is None else limit, 100) if retrieve < 1: return - raw_users, data, state, limit = await strategy(retrieve, state, limit) + data, raw_entries, state, limit = await strategy(retrieve, state, limit) # Terminate loop on next iteration; there's no data left after this - if len(data) < 100: + if len(raw_entries) < 100: limit = 0 if reverse: - data = reversed(data) + raw_entries = reversed(raw_entries) if predicate: - data = filter(predicate, data) + raw_entries = filter(predicate, raw_entries) - users = (User(data=raw_user, state=self._state) for raw_user in raw_users) + users = (User(data=raw_user, state=self._state) for raw_user in data.get('users', [])) user_map = {user.id: user for user in users} - for raw_entry in data: + integrations = (PartialIntegration(data=raw_i, guild=self) for raw_i in data.get('integrations', [])) + integration_map = {integration.id: integration for integration in integrations} + + app_commands = (AppCommand(data=raw_cmd, state=self._state) for raw_cmd in data.get('application_commands', [])) + app_command_map = {app_command.id: app_command for app_command in app_commands} + + for raw_entry in raw_entries: # Weird Discord quirk if raw_entry['action_type'] is None: continue - yield AuditLogEntry(data=raw_entry, users=user_map, guild=self) + yield AuditLogEntry( + data=raw_entry, + users=user_map, + integrations=integration_map, + app_commands=app_command_map, + guild=self, + ) async def widget(self) -> Widget: """|coro| diff --git a/discord/integrations.py b/discord/integrations.py index d3a2ec244..afc071e82 100644 --- a/discord/integrations.py +++ b/discord/integrations.py @@ -36,6 +36,7 @@ __all__ = ( 'Integration', 'StreamIntegration', 'BotIntegration', + 'PartialIntegration', ) if TYPE_CHECKING: @@ -49,6 +50,7 @@ if TYPE_CHECKING: BotIntegration as BotIntegrationPayload, IntegrationType, IntegrationApplication as IntegrationApplicationPayload, + PartialIntegration as PartialIntegrationPayload, ) @@ -115,7 +117,7 @@ class Integration: self._from_data(data) def __repr__(self) -> str: - return f"<{self.__class__.__name__} id={self.id} name={self.name!r}>" + return f'<{self.__class__.__name__} id={self.id} name={self.name!r}>' def _from_data(self, data: IntegrationPayload) -> None: self.id: int = int(data['id']) @@ -362,6 +364,53 @@ class BotIntegration(Integration): self.application: IntegrationApplication = IntegrationApplication(data=data['application'], state=self._state) +class PartialIntegration: + """Represents a partial guild integration. + + .. versionadded:: 2.0 + + Attributes + ----------- + id: :class:`int` + The integration ID. + name: :class:`str` + The integration name. + guild: :class:`Guild` + The guild of the integration. + type: :class:`str` + The integration type (i.e. Twitch). + account: :class:`IntegrationAccount` + The account linked to this integration. + application_id: Optional[:class:`int`] + The id of the application this integration belongs to. + """ + + __slots__ = ( + 'guild', + '_state', + 'id', + 'type', + 'name', + 'account', + 'application_id', + ) + + def __init__(self, *, data: PartialIntegrationPayload, guild: Guild): + self.guild: Guild = guild + self._state: ConnectionState = guild._state + self._from_data(data) + + def __repr__(self) -> str: + return f'<{self.__class__.__name__} id={self.id} name={self.name!r}>' + + def _from_data(self, data: PartialIntegrationPayload) -> None: + self.id: int = int(data['id']) + self.type: IntegrationType = data['type'] + self.name: str = data['name'] + self.account: IntegrationAccount = IntegrationAccount(data['account']) + self.application_id: Optional[int] = _get_as_snowflake(data, 'application_id') + + def _integration_factory(value: str) -> Tuple[Type[Integration], str]: if value == 'discord': return BotIntegration, value diff --git a/discord/types/audit_log.py b/discord/types/audit_log.py index c2997596b..48180c7ae 100644 --- a/discord/types/audit_log.py +++ b/discord/types/audit_log.py @@ -36,6 +36,7 @@ from .snowflake import Snowflake from .role import Role from .channel import ChannelType, PrivacyLevel, VideoQualityMode, PermissionOverwrite from .threads import Thread +from .command import ApplicationCommand, ApplicationCommandPermissions AuditLogEvent = Literal[ 1, @@ -85,6 +86,7 @@ AuditLogEvent = Literal[ 110, 111, 112, + 121, ] @@ -242,6 +244,12 @@ class _AuditLogChange_EntityType(TypedDict): old_value: EntityType +class _AuditLogChange_AppCommandPermissions(TypedDict): + key: str + new_value: ApplicationCommandPermissions + old_value: ApplicationCommandPermissions + + AuditLogChange = Union[ _AuditLogChange_Str, _AuditLogChange_AssetHash, @@ -260,6 +268,7 @@ AuditLogChange = Union[ _AuditLogChange_PrivacyLevel, _AuditLogChange_Status, _AuditLogChange_EntityType, + _AuditLogChange_AppCommandPermissions, ] @@ -272,6 +281,8 @@ class AuditEntryInfo(TypedDict): id: Snowflake type: Literal['0', '1'] role_name: str + application_id: Snowflake + guild_id: Snowflake class AuditLogEntry(TypedDict): @@ -291,3 +302,4 @@ class AuditLog(TypedDict): integrations: List[PartialIntegration] threads: List[Thread] guild_scheduled_events: List[GuildScheduledEvent] + application_commands: List[ApplicationCommand] diff --git a/discord/types/command.py b/discord/types/command.py index 2b732204a..8035ce0aa 100644 --- a/discord/types/command.py +++ b/discord/types/command.py @@ -192,7 +192,7 @@ ApplicationCommand = Union[ ] -ApplicationCommandPermissionType = Literal[1, 2] +ApplicationCommandPermissionType = Literal[1, 2, 3] class ApplicationCommandPermissions(TypedDict): diff --git a/discord/types/integration.py b/discord/types/integration.py index cf73b7a90..cf1197997 100644 --- a/discord/types/integration.py +++ b/discord/types/integration.py @@ -53,6 +53,7 @@ class PartialIntegration(TypedDict): name: str type: IntegrationType account: IntegrationAccount + application_id: Snowflake IntegrationType = Literal['twitch', 'youtube', 'discord'] diff --git a/docs/api.rst b/docs/api.rst index b9d6eb8cc..53a65e679 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -2239,7 +2239,8 @@ of :class:`enum.Enum`. A guild integration was created. When this is the action, the type of :attr:`~AuditLogEntry.target` is - the :class:`Object` with the integration ID of the integration which was created. + a :class:`PartialIntegration` or :class:`Object` with the + integration ID of the integration which was created. .. versionadded:: 1.3 @@ -2248,7 +2249,8 @@ of :class:`enum.Enum`. A guild integration was updated. When this is the action, the type of :attr:`~AuditLogEntry.target` is - the :class:`Object` with the integration ID of the integration which was updated. + a :class:`PartialIntegration` or :class:`Object` with the + integration ID of the integration which was updated. .. versionadded:: 1.3 @@ -2257,7 +2259,8 @@ of :class:`enum.Enum`. A guild integration was deleted. When this is the action, the type of :attr:`~AuditLogEntry.target` is - the :class:`Object` with the integration ID of the integration which was deleted. + a :class:`PartialIntegration` or :class:`Object` with the + integration ID of the integration which was deleted. .. versionadded:: 1.3 @@ -2465,6 +2468,27 @@ of :class:`enum.Enum`. .. versionadded:: 2.0 + .. attribute:: app_command_permission_update + + An application command or integrations application command permissions + were updated. + + When this is the action, the type of :attr:`~AuditLogEntry.target` is + a :class:`PartialIntegration` for an integrations general permissions, + :class:`~discord.app_commands.AppCommand` for a specific commands permissions, + or :class:`Object` with the ID of the command or integration which + was updated. + + When this is the action, the type of :attr:`~AuditLogEntry.extra` is + set to an :class:`PartialIntegration` or :class:`Object` with the ID of + application that command or integration belongs to. + + Possible attributes for :class:`AuditLogDiff`: + + - :attr:`~AuditLogDiff.app_command_permissions` + + .. versionadded:: 2.0 + .. class:: AuditLogActionCategory Represents the category that the :class:`AuditLogAction` belongs to. @@ -3477,6 +3501,16 @@ AuditLogDiff :type: :class:`Asset` + .. attribute:: app_command_permissions + + A list of application command permission tuples that represents a + target and a :class:`bool` for said target. + + The first element is the object being targeted, which can either + be a :class:`Member`, :class:`abc.GuildChannel`, + :class:`~discord.app_commands.AllChannels`, or :class:`Role`. + :type: List[Tuple[target, :class:`bool`]] + .. this is currently missing the following keys: reason and application_id I'm not sure how to about porting these @@ -3727,6 +3761,9 @@ Integration .. autoclass:: StreamIntegration() :members: +.. autoclass:: PartialIntegration() + :members: + Member ~~~~~~ diff --git a/docs/interactions/api.rst b/docs/interactions/api.rst index b1a97d62c..41200cbf8 100644 --- a/docs/interactions/api.rst +++ b/docs/interactions/api.rst @@ -129,6 +129,14 @@ Argument .. autoclass:: discord.app_commands.Argument() :members: +AllChannels +~~~~~~~~~~~~ + +.. attributetable:: discord.app_commands.AllChannels + +.. autoclass:: discord.app_commands.AllChannels() + :members: + Data Classes --------------