diff --git a/discord/audit_logs.py b/discord/audit_logs.py index 10a69877f..6e634fbd4 100644 --- a/discord/audit_logs.py +++ b/discord/audit_logs.py @@ -53,7 +53,11 @@ if TYPE_CHECKING: AuditLogChange as AuditLogChangePayload, AuditLogEntry as AuditLogEntryPayload, ) - from .types.channel import PermissionOverwrite as PermissionOverwritePayload + from .types.channel import ( + PartialChannel as PartialChannelPayload, + PermissionOverwrite as PermissionOverwritePayload, + ) + from .types.invite import Invite as InvitePayload from .types.role import Role as RolePayload from .types.snowflake import Snowflake from .user import User @@ -131,13 +135,13 @@ def _transform_icon(entry: AuditLogEntry, data: Optional[str]) -> Optional[Asset if entry.action is enums.AuditLogAction.guild_update: return Asset._from_guild_icon(entry._state, entry.guild.id, data) else: - return Asset._from_icon(entry._state, entry._target_id, data, path='role') + return Asset._from_icon(entry._state, entry._target_id, data, path='role') # type: ignore - target_id won't be None in this case def _transform_avatar(entry: AuditLogEntry, data: Optional[str]) -> Optional[Asset]: if data is None: return None - return Asset._from_avatar(entry._state, entry._target_id, data) # type: ignore + return Asset._from_avatar(entry._state, entry._target_id, data) # type: ignore - target_id won't be None in this case def _guild_hash_transformer(path: str) -> Callable[[AuditLogEntry, Optional[str]], Optional[Asset]]: @@ -237,10 +241,10 @@ class AuditLogChanges: # special cases for role add/remove if attr == '$add': - self._handle_role(self.before, self.after, entry, elem['new_value']) # type: ignore + self._handle_role(self.before, self.after, entry, elem['new_value']) # type: ignore - new_value is a list of roles in this case continue elif attr == '$remove': - self._handle_role(self.after, self.before, entry, elem['new_value']) # type: ignore + self._handle_role(self.after, self.before, entry, elem['new_value']) # type: ignore - new_value is a list of roles in this case continue try: @@ -289,7 +293,7 @@ class AuditLogChanges: setattr(first, 'roles', []) data = [] - g: Guild = entry.guild # type: ignore + g: Guild = entry.guild for e in elem: role_id = int(e['id']) @@ -297,33 +301,39 @@ class AuditLogChanges: if role is None: role = Object(id=role_id) - role.name = e['name'] # type: ignore + role.name = e['name'] # type: ignore - Object doesn't usually have name data.append(role) setattr(second, 'roles', data) -class _AuditLogProxyMemberPrune: +class _AuditLogProxy: + def __init__(self, **kwargs: Any) -> None: + for k, v in kwargs.items(): + setattr(self, k, v) + + +class _AuditLogProxyMemberPrune(_AuditLogProxy): delete_member_days: int members_removed: int -class _AuditLogProxyMemberMoveOrMessageDelete: +class _AuditLogProxyMemberMoveOrMessageDelete(_AuditLogProxy): channel: abc.GuildChannel count: int -class _AuditLogProxyMemberDisconnect: +class _AuditLogProxyMemberDisconnect(_AuditLogProxy): count: int -class _AuditLogProxyPinAction: +class _AuditLogProxyPinAction(_AuditLogProxy): channel: abc.GuildChannel message_id: int -class _AuditLogProxyStageInstanceAction: +class _AuditLogProxyStageInstanceAction(_AuditLogProxy): channel: abc.GuildChannel @@ -382,51 +392,48 @@ class AuditLogEntry(Hashable): # this key is technically not usually present self.reason = data.get('reason') - self.extra = data.get('options') + extra = data.get('options') - if isinstance(self.action, enums.AuditLogAction) and self.extra: + if isinstance(self.action, enums.AuditLogAction) and extra: if self.action is enums.AuditLogAction.member_prune: # member prune has two keys with useful information - self.extra: _AuditLogProxyMemberPrune = type( - '_AuditLogProxy', (), {k: int(v) for k, v in self.extra.items()} - )() + self.extra = _AuditLogProxyMemberPrune( + delete_member_days=int(extra['delete_member_days']), + members_removed=int(extra['members_removed']), + ) elif self.action is enums.AuditLogAction.member_move or self.action is enums.AuditLogAction.message_delete: - channel_id = int(self.extra['channel_id']) - elems = { - 'count': int(self.extra['count']), - 'channel': self.guild.get_channel(channel_id) or Object(id=channel_id), - } - self.extra: _AuditLogProxyMemberMoveOrMessageDelete = type('_AuditLogProxy', (), elems)() + channel_id = int(extra['channel_id']) + self.extra = _AuditLogProxyMemberMoveOrMessageDelete( + count=int(extra['count']), + channel=self.guild.get_channel(channel_id) or Object(id=channel_id), + ) elif self.action is enums.AuditLogAction.member_disconnect: # The member disconnect action has a dict with some information - elems = { - 'count': int(self.extra['count']), - } - self.extra: _AuditLogProxyMemberDisconnect = type('_AuditLogProxy', (), elems)() + self.extra = _AuditLogProxyMemberDisconnect(count=int(extra['count'])) elif self.action.name.endswith('pin'): # the pin actions have a dict with some information - channel_id = int(self.extra['channel_id']) - elems = { - 'channel': self.guild.get_channel(channel_id) or Object(id=channel_id), - 'message_id': int(self.extra['message_id']), - } - self.extra: _AuditLogProxyPinAction = type('_AuditLogProxy', (), elems)() + channel_id = int(extra['channel_id']) + self.extra = _AuditLogProxyPinAction( + channel=self.guild.get_channel(channel_id) or Object(id=channel_id), + message_id=int(extra['message_id']), + ) elif self.action.name.startswith('overwrite_'): # the overwrite_ actions have a dict with some information - instance_id = int(self.extra['id']) - the_type = self.extra.get('type') + instance_id = int(extra['id']) + the_type = extra.get('type') if the_type == '1': self.extra = self._get_member(instance_id) elif the_type == '0': role = self.guild.get_role(instance_id) if role is None: role = Object(id=instance_id) - role.name = self.extra.get('role_name') # type: ignore - self.extra: Role = role + role.name = self.extra.get('role_name') # type: ignore - Object doesn't usually have name + self.extra = role elif self.action.name.startswith('stage_instance'): - channel_id = int(self.extra['channel_id']) - elems = {'channel': self.guild.get_channel(channel_id) or Object(id=channel_id)} - self.extra: _AuditLogProxyStageInstanceAction = type('_AuditLogProxy', (), elems)() + channel_id = int(extra['channel_id']) + self.extra = _AuditLogProxyStageInstanceAction( + channel=self.guild.get_channel(channel_id) or Object(id=channel_id) + ) # fmt: off self.extra: Union[ @@ -436,7 +443,7 @@ class AuditLogEntry(Hashable): _AuditLogProxyPinAction, _AuditLogProxyStageInstanceAction, Member, User, None, - Role, + Role, Object ] # fmt: on @@ -447,7 +454,8 @@ class AuditLogEntry(Hashable): # into meaningful data when requested self._changes = data.get('changes', []) - self.user = self._get_member(utils._get_as_snowflake(data, 'user_id')) # type: ignore + user_id = utils._get_as_snowflake(data, 'user_id') + self.user = user_id and self._get_member(user_id) self._target_id = utils._get_as_snowflake(data, 'target_id') def _get_member(self, user_id: int) -> Union[Member, User, None]: @@ -463,6 +471,9 @@ class AuditLogEntry(Hashable): @utils.cached_property def target(self) -> TargetType: + if self._target_id is None or self.action.target_type is None: + return None + try: converter = getattr(self, '_convert_target_' + self.action.target_type) except AttributeError: @@ -471,7 +482,7 @@ class AuditLogEntry(Hashable): return converter(self._target_id) @utils.cached_property - def category(self) -> enums.AuditLogActionCategory: + def category(self) -> Optional[enums.AuditLogActionCategory]: """Optional[:class:`AuditLogActionCategory`]: The category of the action, if applicable.""" return self.action.category @@ -509,15 +520,16 @@ class AuditLogEntry(Hashable): # so figure out which change has the full invite data changeset = self.before if self.action is enums.AuditLogAction.invite_delete else self.after - fake_payload = { + fake_payload: InvitePayload = { 'max_age': changeset.max_age, 'max_uses': changeset.max_uses, 'code': changeset.code, 'temporary': changeset.temporary, 'uses': changeset.uses, + 'channel': None, # type: ignore - the channel is passed to the Invite constructor directly } - obj = Invite(state=self._state, data=fake_payload, guild=self.guild, channel=changeset.channel) # type: ignore + obj = Invite(state=self._state, data=fake_payload, guild=self.guild, channel=changeset.channel) try: obj.inviter = changeset.inviter except AttributeError: