mirror of
https://github.com/Rapptz/discord.py.git
synced 2025-06-07 12:18:59 +00:00
Implement audit logs.
This commit is contained in:
parent
3f45db5c3b
commit
c54a6a927d
@ -42,6 +42,7 @@ from .embeds import Embed
|
|||||||
from .shard import AutoShardedClient
|
from .shard import AutoShardedClient
|
||||||
from .player import *
|
from .player import *
|
||||||
from .voice_client import VoiceClient
|
from .voice_client import VoiceClient
|
||||||
|
from .audit_logs import AuditLogChanges, AuditLogEntry, AuditLogDiff
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
319
discord/audit_logs.py
Normal file
319
discord/audit_logs.py
Normal file
@ -0,0 +1,319 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
"""
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2015-2017 Rapptz
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
copy of this software and associated documentation files (the "Software"),
|
||||||
|
to deal in the Software without restriction, including without limitation
|
||||||
|
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||||
|
and/or sell copies of the Software, and to permit persons to whom the
|
||||||
|
Software is furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||||
|
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
DEALINGS IN THE SOFTWARE.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from . import utils, enums
|
||||||
|
from .object import Object
|
||||||
|
from .permissions import PermissionOverwrite, Permissions
|
||||||
|
from .colour import Colour
|
||||||
|
from .invite import Invite
|
||||||
|
|
||||||
|
def _transform_verification_level(entry, data):
|
||||||
|
return enums.try_enum(enums.VerificationLevel, data)
|
||||||
|
|
||||||
|
def _transform_explicit_content_filter(entry, data):
|
||||||
|
return enums.try_enum(enums.ContentFilter, data)
|
||||||
|
|
||||||
|
def _transform_permissions(entry, data):
|
||||||
|
return Permissions(data)
|
||||||
|
|
||||||
|
def _transform_color(entry, data):
|
||||||
|
return Colour(data)
|
||||||
|
|
||||||
|
def _transform_snowflake(entry, data):
|
||||||
|
return int(data)
|
||||||
|
|
||||||
|
def _transform_channel(entry, data):
|
||||||
|
if data is None:
|
||||||
|
return None
|
||||||
|
channel = entry.guild.get_channel(int(data)) or Object(id=data)
|
||||||
|
return channel
|
||||||
|
|
||||||
|
def _transform_owner_id(entry, data):
|
||||||
|
if data is None:
|
||||||
|
return None
|
||||||
|
return entry._get_member(int(data))
|
||||||
|
|
||||||
|
def _transform_inviter_id(entry, data):
|
||||||
|
if data is None:
|
||||||
|
return None
|
||||||
|
return entry._get_member(int(data))
|
||||||
|
|
||||||
|
def _transform_overwrites(entry, data):
|
||||||
|
overwrites = []
|
||||||
|
for elem in data:
|
||||||
|
allow = Permissions(elem['allow'])
|
||||||
|
deny = Permissions(elem['deny'])
|
||||||
|
ow = PermissionOverwrite.from_pair(allow, deny)
|
||||||
|
|
||||||
|
ow_type = elem['type']
|
||||||
|
ow_id = int(elem['id'])
|
||||||
|
if ow_type == 'role':
|
||||||
|
target = utils.find(lambda r: r.id == ow_id, entry.guild.roles)
|
||||||
|
else:
|
||||||
|
target = entry._get_member(ow_id)
|
||||||
|
|
||||||
|
if target is None:
|
||||||
|
target = Object(id=ow_id)
|
||||||
|
|
||||||
|
overwrites.append((target, ow))
|
||||||
|
|
||||||
|
return overwrites
|
||||||
|
|
||||||
|
class AuditLogDiff:
|
||||||
|
def __len__(self):
|
||||||
|
return len(self.__dict__)
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
return self.__dict__.items()
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return '<AuditLogDiff attrs={0!r}>'.format(tuple(self.__dict__))
|
||||||
|
|
||||||
|
class AuditLogChanges:
|
||||||
|
TRANSFORMERS = {
|
||||||
|
'verification_level': (None, _transform_verification_level),
|
||||||
|
'explicit_content_filter': (None, _transform_explicit_content_filter),
|
||||||
|
'allow': (None, _transform_permissions),
|
||||||
|
'deny': (None, _transform_permissions),
|
||||||
|
'permissions': (None, _transform_permissions),
|
||||||
|
'id': (None, _transform_snowflake),
|
||||||
|
'color': ('colour', _transform_color),
|
||||||
|
'owner_id': ('owner', _transform_owner_id),
|
||||||
|
'inviter_id': ('inviter', _transform_inviter_id),
|
||||||
|
'channel_id': ('channel', _transform_channel),
|
||||||
|
'afk_channel_id': ('afk_channel', _transform_channel),
|
||||||
|
'widget_channel_id': ('widget_channel', _transform_channel),
|
||||||
|
'permission_overwrites': ('overwrites', _transform_overwrites),
|
||||||
|
'splash_hash': ('splash', None),
|
||||||
|
'icon_hash': ('icon', None),
|
||||||
|
'avatar_hash': ('avatar', None),
|
||||||
|
}
|
||||||
|
|
||||||
|
def __init__(self, entry, data):
|
||||||
|
self.before = AuditLogDiff()
|
||||||
|
self.after = AuditLogDiff()
|
||||||
|
|
||||||
|
for elem in data:
|
||||||
|
attr = elem['key']
|
||||||
|
|
||||||
|
# special cases for role add/remove
|
||||||
|
if attr == '$add':
|
||||||
|
self._handle_role(self.before, self.after, entry, elem)
|
||||||
|
continue
|
||||||
|
elif attr == '$remove':
|
||||||
|
self._handle_role(self.after, self.before, entry, elem)
|
||||||
|
continue
|
||||||
|
|
||||||
|
transformer = self.TRANSFORMERS.get(attr)
|
||||||
|
if transformer:
|
||||||
|
attr, transformer = transformer
|
||||||
|
|
||||||
|
try:
|
||||||
|
before = elem['old_value']
|
||||||
|
except KeyError:
|
||||||
|
before = None
|
||||||
|
else:
|
||||||
|
if transformer:
|
||||||
|
before = transformer(entry, before)
|
||||||
|
|
||||||
|
setattr(self.before, attr, before)
|
||||||
|
|
||||||
|
try:
|
||||||
|
after = elem['new_value']
|
||||||
|
except KeyError:
|
||||||
|
after = None
|
||||||
|
else:
|
||||||
|
if transformer:
|
||||||
|
after = transformer(entry, after)
|
||||||
|
|
||||||
|
setattr(self.after, attr, after)
|
||||||
|
|
||||||
|
# add an alias
|
||||||
|
if hasattr(self.after, 'colour'):
|
||||||
|
self.after.color = self.after.colour
|
||||||
|
self.before.color = self.before.colour
|
||||||
|
|
||||||
|
def _handle_role(self, first, second, entry, elem):
|
||||||
|
setattr(first, 'role', None)
|
||||||
|
|
||||||
|
# TODO: partial data?
|
||||||
|
role_id = int(elem['id'])
|
||||||
|
role = utils.find(lambda r: r.id == role_id, entry.guild.roles)
|
||||||
|
|
||||||
|
if role is None:
|
||||||
|
role = discord.Object(id=role_id)
|
||||||
|
role.name = elem['name']
|
||||||
|
|
||||||
|
setattr(second, 'role', role)
|
||||||
|
|
||||||
|
class AuditLogEntry:
|
||||||
|
"""Represents an Audit Log entry.
|
||||||
|
|
||||||
|
You retrieve these via :meth:`Guild.audit_log`.
|
||||||
|
|
||||||
|
Attributes
|
||||||
|
-----------
|
||||||
|
action: :class:`AuditLogAction`
|
||||||
|
The action that was done.
|
||||||
|
user: :class:`abc.User`
|
||||||
|
The user who initiated this action. Usually a :class:`Member`\, unless gone
|
||||||
|
then it's a :class:`User`.
|
||||||
|
id: int
|
||||||
|
The entry ID.
|
||||||
|
target: Any
|
||||||
|
The target that got changed. The exact type of this depends on
|
||||||
|
the action being done.
|
||||||
|
reason: Optional[str]
|
||||||
|
The reason this action was done.
|
||||||
|
extra: Any
|
||||||
|
Extra information that this entry has that might be useful.
|
||||||
|
For most actions, this is ``None``. However in some cases it
|
||||||
|
contains extra information. See :class:`AuditLogAction` for
|
||||||
|
which actions have this field filled out.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, *, users, data, guild):
|
||||||
|
self._state = guild._state
|
||||||
|
self.guild = guild
|
||||||
|
self._users = users
|
||||||
|
self._from_data(data)
|
||||||
|
|
||||||
|
def _from_data(self, data):
|
||||||
|
self.action = enums.AuditLogAction(data['action_type'])
|
||||||
|
self.id = int(data['id'])
|
||||||
|
|
||||||
|
# this key is technically not usually present
|
||||||
|
self.reason = data.get('reason')
|
||||||
|
self.extra = data.get('options')
|
||||||
|
|
||||||
|
if self.extra:
|
||||||
|
if self.action is enums.AuditLogAction.member_prune:
|
||||||
|
# member prune has two keys with useful information
|
||||||
|
self.extra = type('_AuditLogProxy', (), {k: int(v) for k, v in self.extra.items()})()
|
||||||
|
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')
|
||||||
|
if the_type == 'member':
|
||||||
|
self.extra = self._get_member(instance_id)
|
||||||
|
else:
|
||||||
|
role = utils.find(lambda r: r.id == instance_id, self.guild.roles)
|
||||||
|
if role is None:
|
||||||
|
role = Object(id=instance_id)
|
||||||
|
role.name = self.extra.get('role_name')
|
||||||
|
self.extra = role
|
||||||
|
|
||||||
|
# this key is not present when the above is present, typically.
|
||||||
|
# It's a list of { new_value: a, old_value: b, key: c }
|
||||||
|
# where new_value and old_value are not guaranteed to be there depending
|
||||||
|
# on the action type, so let's just fetch it for now and only turn it
|
||||||
|
# into meaningful data when requested
|
||||||
|
self._changes = data.get('changes', [])
|
||||||
|
|
||||||
|
self.user = self._get_member(int(data['user_id']))
|
||||||
|
self._target_id = utils._get_as_snowflake(data, 'target_id')
|
||||||
|
|
||||||
|
def _get_member(self, user_id):
|
||||||
|
return self.guild.get_member(user_id) or self._users.get(user_id)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return '<AuditLogEntry id={0.id} action={0.action} user={0.user!r}>'.format(self)
|
||||||
|
|
||||||
|
@utils.cached_property
|
||||||
|
def created_at(self):
|
||||||
|
"""Returns the entry's creation time in UTC."""
|
||||||
|
return utils.snowflake_time(self.id)
|
||||||
|
|
||||||
|
@utils.cached_property
|
||||||
|
def target(self):
|
||||||
|
try:
|
||||||
|
converter = getattr(self, '_convert_target_' + self.action.target_type)
|
||||||
|
except AttributeError:
|
||||||
|
return Object(id=self._target_id)
|
||||||
|
else:
|
||||||
|
return converter(self._target_id)
|
||||||
|
|
||||||
|
@utils.cached_property
|
||||||
|
def category(self):
|
||||||
|
"""Optional[:class:`AuditLogActionCategory`]: The category of the action, if applicable."""
|
||||||
|
return self.action.category
|
||||||
|
|
||||||
|
@utils.cached_property
|
||||||
|
def changes(self):
|
||||||
|
""":class:`AuditLogChanges`: The list of changes this entry has."""
|
||||||
|
obj = AuditLogChanges(self, self._changes)
|
||||||
|
del self._changes
|
||||||
|
return obj
|
||||||
|
|
||||||
|
@utils.cached_property
|
||||||
|
def before(self):
|
||||||
|
""":class:`AuditLogDiff`: The target's prior state."""
|
||||||
|
return self.changes.before
|
||||||
|
|
||||||
|
@utils.cached_property
|
||||||
|
def after(self):
|
||||||
|
""":class:`AuditLogDiff`: The target's subsequent state."""
|
||||||
|
return self.changes.after
|
||||||
|
|
||||||
|
def _convert_target_guild(self, target_id):
|
||||||
|
return self.guild
|
||||||
|
|
||||||
|
def _convert_target_channel(self, target_id):
|
||||||
|
ch = self.guild.get_channel(target_id)
|
||||||
|
if ch is None:
|
||||||
|
return Object(id=target_id)
|
||||||
|
return ch
|
||||||
|
|
||||||
|
def _convert_target_user(self, target_id):
|
||||||
|
return self._get_member(target_id)
|
||||||
|
|
||||||
|
def _convert_target_role(self, target_id):
|
||||||
|
role = utils.find(lambda r: r.id == target_id, self.guild.roles)
|
||||||
|
if role is None:
|
||||||
|
return Object(id=target_id)
|
||||||
|
return role
|
||||||
|
|
||||||
|
def _convert_target_invite(self, target_id):
|
||||||
|
# invites have target_id set to null
|
||||||
|
# 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 = {
|
||||||
|
'max_age': changeset.max_age,
|
||||||
|
'max_uses': changeset.max_uses,
|
||||||
|
'code': changeset.code,
|
||||||
|
'temporary': changeset.temporary,
|
||||||
|
'channel': changeset.channel,
|
||||||
|
'uses': changeset.uses,
|
||||||
|
'guild': self.guild,
|
||||||
|
}
|
||||||
|
|
||||||
|
obj = Invite(state=self._state, data=fake_payload)
|
||||||
|
obj.inviter = changeset.inviter
|
||||||
|
return obj
|
||||||
|
|
||||||
|
def _convert_target_emoji(self, target_id):
|
||||||
|
return self._state.get_emoji(target_id) or Object(id=target_id)
|
@ -27,7 +27,8 @@ DEALINGS IN THE SOFTWARE.
|
|||||||
from enum import Enum
|
from enum import Enum
|
||||||
|
|
||||||
__all__ = ['ChannelType', 'MessageType', 'GuildRegion', 'VerificationLevel',
|
__all__ = ['ChannelType', 'MessageType', 'GuildRegion', 'VerificationLevel',
|
||||||
'ContentFilter', 'Status', 'DefaultAvatar', 'RelationshipType' ]
|
'ContentFilter', 'Status', 'DefaultAvatar', 'RelationshipType',
|
||||||
|
'AuditLogAction', 'AuditLogActionCategory', ]
|
||||||
|
|
||||||
class ChannelType(Enum):
|
class ChannelType(Enum):
|
||||||
text = 0
|
text = 0
|
||||||
@ -114,6 +115,90 @@ class RelationshipType(Enum):
|
|||||||
incoming_request = 3
|
incoming_request = 3
|
||||||
outgoing_request = 4
|
outgoing_request = 4
|
||||||
|
|
||||||
|
class AuditLogActionCategory(Enum):
|
||||||
|
create = 1
|
||||||
|
delete = 2
|
||||||
|
update = 3
|
||||||
|
|
||||||
|
class AuditLogAction(Enum):
|
||||||
|
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
|
||||||
|
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
|
||||||
|
|
||||||
|
@property
|
||||||
|
def category(self):
|
||||||
|
lookup = {
|
||||||
|
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.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,
|
||||||
|
}
|
||||||
|
return lookup[self]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def target_type(self):
|
||||||
|
v = self.value
|
||||||
|
if v == -1:
|
||||||
|
return 'all'
|
||||||
|
elif v < 10:
|
||||||
|
return 'guild'
|
||||||
|
elif v < 20:
|
||||||
|
return 'channel'
|
||||||
|
elif v < 30:
|
||||||
|
return 'user'
|
||||||
|
elif v < 40:
|
||||||
|
return 'role'
|
||||||
|
elif v < 50:
|
||||||
|
return 'invite'
|
||||||
|
elif v < 60:
|
||||||
|
return 'webhook'
|
||||||
|
elif v < 70:
|
||||||
|
return 'emoji'
|
||||||
|
|
||||||
|
|
||||||
def try_enum(cls, val):
|
def try_enum(cls, val):
|
||||||
"""A function that tries to turn the value into enum ``cls``.
|
"""A function that tries to turn the value into enum ``cls``.
|
||||||
|
|
||||||
|
@ -41,6 +41,7 @@ from .enums import GuildRegion, Status, ChannelType, try_enum, VerificationLevel
|
|||||||
from .mixins import Hashable
|
from .mixins import Hashable
|
||||||
from .user import User
|
from .user import User
|
||||||
from .invite import Invite
|
from .invite import Invite
|
||||||
|
from .iterators import AuditLogIterator
|
||||||
|
|
||||||
BanEntry = namedtuple('BanEntry', 'reason user')
|
BanEntry = namedtuple('BanEntry', 'reason user')
|
||||||
|
|
||||||
@ -921,7 +922,7 @@ class Guild(Hashable):
|
|||||||
return role
|
return role
|
||||||
|
|
||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
def kick(self, user):
|
def kick(self, user, *, reason=None):
|
||||||
"""|coro|
|
"""|coro|
|
||||||
|
|
||||||
Kicks a user from the guild.
|
Kicks a user from the guild.
|
||||||
@ -935,6 +936,8 @@ class Guild(Hashable):
|
|||||||
-----------
|
-----------
|
||||||
user: :class:`abc.Snowflake`
|
user: :class:`abc.Snowflake`
|
||||||
The user to kick from their guild.
|
The user to kick from their guild.
|
||||||
|
reason: Optional[str]
|
||||||
|
The reason the user got kicked.
|
||||||
|
|
||||||
Raises
|
Raises
|
||||||
-------
|
-------
|
||||||
@ -943,10 +946,10 @@ class Guild(Hashable):
|
|||||||
HTTPException
|
HTTPException
|
||||||
Kicking failed.
|
Kicking failed.
|
||||||
"""
|
"""
|
||||||
yield from self._state.http.kick(user.id, self.id)
|
yield from self._state.http.kick(user.id, self.id, reason=reason)
|
||||||
|
|
||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
def ban(self, user, *, delete_message_days=1):
|
def ban(self, user, *, reason=None, delete_message_days=1):
|
||||||
"""|coro|
|
"""|coro|
|
||||||
|
|
||||||
Bans a user from the guild.
|
Bans a user from the guild.
|
||||||
@ -963,6 +966,8 @@ class Guild(Hashable):
|
|||||||
delete_message_days: int
|
delete_message_days: int
|
||||||
The number of days worth of messages to delete from the user
|
The number of days worth of messages to delete from the user
|
||||||
in the guild. The minimum is 0 and the maximum is 7.
|
in the guild. The minimum is 0 and the maximum is 7.
|
||||||
|
reason: Optional[str]
|
||||||
|
The reason the user got banned.
|
||||||
|
|
||||||
Raises
|
Raises
|
||||||
-------
|
-------
|
||||||
@ -971,7 +976,7 @@ class Guild(Hashable):
|
|||||||
HTTPException
|
HTTPException
|
||||||
Banning failed.
|
Banning failed.
|
||||||
"""
|
"""
|
||||||
yield from self._state.http.ban(user.id, self.id, delete_message_days)
|
yield from self._state.http.ban(user.id, self.id, delete_message_days, reason=reason)
|
||||||
|
|
||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
def unban(self, user):
|
def unban(self, user):
|
||||||
@ -1017,3 +1022,60 @@ class Guild(Hashable):
|
|||||||
if state.is_bot:
|
if state.is_bot:
|
||||||
raise ClientException('Must not be a bot account to ack messages.')
|
raise ClientException('Must not be a bot account to ack messages.')
|
||||||
return state.http.ack_guild(self.id)
|
return state.http.ack_guild(self.id)
|
||||||
|
|
||||||
|
def audit_logs(self, *, limit=100, before=None, after=None, reverse=None, user=None, action=None):
|
||||||
|
"""Return an :class:`AsyncIterator` that enables receiving the guild's audit logs.
|
||||||
|
|
||||||
|
You must have :attr:`Permissions.view_audit_log` permission to use this.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
-----------
|
||||||
|
limit: Optional[int]
|
||||||
|
The number of entries to retrieve. If ``None`` retrieve all entries.
|
||||||
|
before: Union[:class:`abc.Snowflake`, datetime]
|
||||||
|
Retrieve entries before this date or entry.
|
||||||
|
If a date is provided it must be a timezone-naive datetime representing UTC time.
|
||||||
|
after: Union[:class:`abc.Snowflake`, datetime]
|
||||||
|
Retrieve entries after this date or entry.
|
||||||
|
If a date is provided it must be a timezone-naive datetime representing UTC time.
|
||||||
|
reverse: bool
|
||||||
|
If set to true, return entries in oldest->newest order. If unspecified,
|
||||||
|
this defaults to ``False`` for most cases. However if passing in a
|
||||||
|
``after`` parameter then this is set to ``True``. This avoids getting entries
|
||||||
|
out of order in the ``after`` case.
|
||||||
|
user: :class:`abc.Snowflake`
|
||||||
|
The moderator to filter entries from.
|
||||||
|
action: :class:`AuditLogAction`
|
||||||
|
The action to filter with.
|
||||||
|
|
||||||
|
Yields
|
||||||
|
--------
|
||||||
|
:class:`AuditLogEntry`
|
||||||
|
The audit log entry.
|
||||||
|
|
||||||
|
Examples
|
||||||
|
----------
|
||||||
|
|
||||||
|
Getting the first 100 entries: ::
|
||||||
|
|
||||||
|
async for entry in guild.audit_logs(limit=100):
|
||||||
|
print('{0.user} did {0.action} to {0.target}'.format(entry))
|
||||||
|
|
||||||
|
Getting entries for a specific action: ::
|
||||||
|
|
||||||
|
async for entry in guild.audit_logs(action=discord.AuditLogAction.ban):
|
||||||
|
print('{0.user} banned {0.target}'.format(entry))
|
||||||
|
|
||||||
|
Getting entries made by a specific user: ::
|
||||||
|
|
||||||
|
entries = await guild.audit_logs(limit=None, user=guild.me).flatten()
|
||||||
|
await guild.default_channel.send('I made {} moderation actions.'.format(len(entries)))
|
||||||
|
"""
|
||||||
|
if user:
|
||||||
|
user = user.id
|
||||||
|
|
||||||
|
if action:
|
||||||
|
action = action.value
|
||||||
|
|
||||||
|
return AuditLogIterator(self, before=before, after=after, limit=limit,
|
||||||
|
reverse=reverse, user_id=user, action_type=action)
|
||||||
|
@ -410,15 +410,20 @@ class HTTPClient:
|
|||||||
|
|
||||||
# Member management
|
# Member management
|
||||||
|
|
||||||
def kick(self, user_id, guild_id):
|
def kick(self, user_id, guild_id, reason=None):
|
||||||
r = Route('DELETE', '/guilds/{guild_id}/members/{user_id}', guild_id=guild_id, user_id=user_id)
|
r = Route('DELETE', '/guilds/{guild_id}/members/{user_id}', guild_id=guild_id, user_id=user_id)
|
||||||
return self.request(r)
|
if reason:
|
||||||
|
return self.request(r, params={'reason': reason })
|
||||||
|
return self.request(r, params=params)
|
||||||
|
|
||||||
def ban(self, user_id, guild_id, delete_message_days=1):
|
def ban(self, user_id, guild_id, delete_message_days=1, reason=None):
|
||||||
r = Route('PUT', '/guilds/{guild_id}/bans/{user_id}', guild_id=guild_id, user_id=user_id)
|
r = Route('PUT', '/guilds/{guild_id}/bans/{user_id}', guild_id=guild_id, user_id=user_id)
|
||||||
params = {
|
params = {
|
||||||
'delete-message-days': delete_message_days
|
'delete-message-days': delete_message_days,
|
||||||
}
|
}
|
||||||
|
if reason:
|
||||||
|
params['reason'] = reason
|
||||||
|
|
||||||
return self.request(r, params=params)
|
return self.request(r, params=params)
|
||||||
|
|
||||||
def unban(self, user_id, guild_id):
|
def unban(self, user_id, guild_id):
|
||||||
@ -557,6 +562,20 @@ class HTTPClient:
|
|||||||
r = Route('PATCH', '/guilds/{guild_id}/emojis/{emoji_id}', guild_id=guild_id, emoji_id=emoji_id)
|
r = Route('PATCH', '/guilds/{guild_id}/emojis/{emoji_id}', guild_id=guild_id, emoji_id=emoji_id)
|
||||||
return self.request(r, json=payload)
|
return self.request(r, json=payload)
|
||||||
|
|
||||||
|
def get_audit_logs(self, guild_id, limit=100, before=None, after=None, user_id=None, action_type=None):
|
||||||
|
params = { 'limit': limit }
|
||||||
|
if before:
|
||||||
|
params['before'] = before
|
||||||
|
if after:
|
||||||
|
params['after'] = after
|
||||||
|
if user_id:
|
||||||
|
params['user_id'] = user_id
|
||||||
|
if action_type:
|
||||||
|
params['action_type'] = action_type
|
||||||
|
|
||||||
|
r = Route('GET', '/guilds/{guild_id}/audit-logs', guild_id=guild_id)
|
||||||
|
return self.request(r, params=params)
|
||||||
|
|
||||||
# Invite management
|
# Invite management
|
||||||
|
|
||||||
def create_invite(self, channel_id, **options):
|
def create_invite(self, channel_id, **options):
|
||||||
|
@ -31,6 +31,7 @@ import datetime
|
|||||||
from .errors import NoMoreItems
|
from .errors import NoMoreItems
|
||||||
from .utils import time_snowflake, maybe_coroutine
|
from .utils import time_snowflake, maybe_coroutine
|
||||||
from .object import Object
|
from .object import Object
|
||||||
|
from .audit_logs import AuditLogEntry
|
||||||
|
|
||||||
PY35 = sys.version_info >= (3, 5)
|
PY35 = sys.version_info >= (3, 5)
|
||||||
|
|
||||||
@ -369,3 +370,111 @@ class HistoryIterator(_AsyncIterator):
|
|||||||
self.around = None
|
self.around = None
|
||||||
return data
|
return data
|
||||||
return []
|
return []
|
||||||
|
|
||||||
|
class AuditLogIterator(_AsyncIterator):
|
||||||
|
def __init__(self, guild, limit=None, before=None, after=None, reverse=None, user_id=None, action_type=None):
|
||||||
|
if isinstance(before, datetime.datetime):
|
||||||
|
before = Object(id=time_snowflake(before, high=False))
|
||||||
|
if isinstance(after, datetime.datetime):
|
||||||
|
after = Object(id=time_snowflake(after, high=True))
|
||||||
|
|
||||||
|
|
||||||
|
self.guild = guild
|
||||||
|
self.loop = guild._state.loop
|
||||||
|
self.request = guild._state.http.get_audit_logs
|
||||||
|
self.limit = limit
|
||||||
|
self.before = before
|
||||||
|
self.user_id = user_id
|
||||||
|
self.action_type = action_type
|
||||||
|
self.after = after
|
||||||
|
self._users = {}
|
||||||
|
self._state = guild._state
|
||||||
|
|
||||||
|
if reverse is None:
|
||||||
|
self.reverse = after is not None
|
||||||
|
else:
|
||||||
|
self.reverse = reverse
|
||||||
|
|
||||||
|
self._filter = None # entry dict -> bool
|
||||||
|
|
||||||
|
self.entries = asyncio.Queue(loop=self.loop)
|
||||||
|
|
||||||
|
if self.before and self.after:
|
||||||
|
if self.reverse:
|
||||||
|
self._strategy = self._after_strategy
|
||||||
|
self._filter = lambda m: int(m['id']) < self.before.id
|
||||||
|
else:
|
||||||
|
self._strategy = self._before_strategy
|
||||||
|
self._filter = lambda m: int(m['id']) > self.after.id
|
||||||
|
elif self.after:
|
||||||
|
self._strategy = self._after_strategy
|
||||||
|
else:
|
||||||
|
self._strategy = self._before_strategy
|
||||||
|
|
||||||
|
@asyncio.coroutine
|
||||||
|
def _before_strategy(self, retrieve):
|
||||||
|
before = self.before.id if self.before else None
|
||||||
|
data = yield from self.request(self.guild.id, limit=retrieve, user_id=self.user_id,
|
||||||
|
action_type=self.action_type, before=before)
|
||||||
|
if len(data):
|
||||||
|
if self.limit is not None:
|
||||||
|
self.limit -= retrieve
|
||||||
|
self.before = Object(id=int(data['audit_log_entries'][-1]['id']))
|
||||||
|
return data
|
||||||
|
|
||||||
|
@asyncio.coroutine
|
||||||
|
def _after_strategy(self, retrieve):
|
||||||
|
after = self.after.id if self.after else None
|
||||||
|
data = yield from self.request(self.guild.id, limit=retrieve, user_id=self.user_id,
|
||||||
|
action_type=self.action_type, after=after)
|
||||||
|
if len(data):
|
||||||
|
if self.limit is not None:
|
||||||
|
self.limit -= retrieve
|
||||||
|
self.after = Object(id=int(data['audit_log_entries'][0]['id']))
|
||||||
|
return data
|
||||||
|
|
||||||
|
@asyncio.coroutine
|
||||||
|
def get(self):
|
||||||
|
if self.entries.empty():
|
||||||
|
yield from self._fill()
|
||||||
|
|
||||||
|
try:
|
||||||
|
return self.entries.get_nowait()
|
||||||
|
except asyncio.QueueEmpty:
|
||||||
|
raise NoMoreItems()
|
||||||
|
|
||||||
|
def _get_retrieve(self):
|
||||||
|
l = self.limit
|
||||||
|
if l is None:
|
||||||
|
r = 100
|
||||||
|
elif l <= 100:
|
||||||
|
r = l
|
||||||
|
else:
|
||||||
|
r = 100
|
||||||
|
|
||||||
|
self.retrieve = r
|
||||||
|
return r > 0
|
||||||
|
|
||||||
|
@asyncio.coroutine
|
||||||
|
def _fill(self):
|
||||||
|
from .user import User
|
||||||
|
|
||||||
|
if self._get_retrieve():
|
||||||
|
data = yield from self._strategy(self.retrieve)
|
||||||
|
users = data.get('users', [])
|
||||||
|
data = data.get('audit_log_entries', [])
|
||||||
|
|
||||||
|
if self.limit is None and len(data) < 100:
|
||||||
|
self.limit = 0 # terminate the infinite loop
|
||||||
|
|
||||||
|
if self.reverse:
|
||||||
|
data = reversed(data)
|
||||||
|
if self._filter:
|
||||||
|
data = filter(self._filter, data)
|
||||||
|
|
||||||
|
for user in users:
|
||||||
|
u = User(data=user, state=self._state)
|
||||||
|
self._users[u.id] = u
|
||||||
|
|
||||||
|
for element in data:
|
||||||
|
yield from self.entries.put(AuditLogEntry(data=element, users=self._users, guild=self.guild))
|
||||||
|
@ -357,12 +357,12 @@ class Member(discord.abc.Messageable):
|
|||||||
yield from self.guild.unban(self)
|
yield from self.guild.unban(self)
|
||||||
|
|
||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
def kick(self):
|
def kick(self, *, reason=None):
|
||||||
"""|coro|
|
"""|coro|
|
||||||
|
|
||||||
Kicks this member. Equivalent to :meth:`Guild.kick`
|
Kicks this member. Equivalent to :meth:`Guild.kick`
|
||||||
"""
|
"""
|
||||||
yield from self.guild.kick(self)
|
yield from self.guild.kick(self, reason=reason)
|
||||||
|
|
||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
def edit(self, **fields):
|
def edit(self, **fields):
|
||||||
|
@ -284,12 +284,12 @@ class Permissions:
|
|||||||
self._set(6, value)
|
self._set(6, value)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def view_audit_log(self):
|
def view_audit_logs(self):
|
||||||
"""Returns True if a user can view the guild's audit log."""
|
"""Returns True if a user can view the guild's audit log."""
|
||||||
return self._bit(7)
|
return self._bit(7)
|
||||||
|
|
||||||
@view_audit_log.setter
|
@view_audit_logs.setter
|
||||||
def view_audit_log(self, value):
|
def view_audit_logs(self, value):
|
||||||
self._set(7, value)
|
self._set(7, value)
|
||||||
|
|
||||||
# 2 unused
|
# 2 unused
|
||||||
|
685
docs/api.rst
685
docs/api.rst
@ -709,6 +709,400 @@ All enumerations are subclasses of `enum`_.
|
|||||||
|
|
||||||
You have sent a friend request to this user.
|
You have sent a friend request to this user.
|
||||||
|
|
||||||
|
|
||||||
|
.. class:: AuditLogAction
|
||||||
|
|
||||||
|
Represents the type of action being done for a :class:`AuditLogEntry`\,
|
||||||
|
which is retrievable via :meth:`Guild.audit_log`.
|
||||||
|
|
||||||
|
.. attribute:: guild_update
|
||||||
|
|
||||||
|
The guild has updated. Things that trigger this include:
|
||||||
|
|
||||||
|
- Changing the guild vanity URL
|
||||||
|
- Changing the guild invite splash
|
||||||
|
- Changing the guild AFK channel or timeout
|
||||||
|
- Changing the guild voice server region
|
||||||
|
- Changing the guild icon
|
||||||
|
- Changing the guild moderation settings
|
||||||
|
- Changing things related to the guild widget
|
||||||
|
|
||||||
|
When this is the action, the type of :attr:`~AuditLogEntry.target` is
|
||||||
|
the :class:`Guild`.
|
||||||
|
|
||||||
|
Possible attributes for :class:`AuditLogDiff`:
|
||||||
|
|
||||||
|
- :attr:`~AuditLogDiff.afk_channel`
|
||||||
|
- :attr:`~AuditLogDiff.afk_timeout`
|
||||||
|
- :attr:`~AuditLogDiff.default_message_notifications`
|
||||||
|
- :attr:`~AuditLogDiff.explicit_content_filter`
|
||||||
|
- :attr:`~AuditLogDiff.mfa_level`
|
||||||
|
- :attr:`~AuditLogDiff.name`
|
||||||
|
- :attr:`~AuditLogDiff.owner`
|
||||||
|
- :attr:`~AuditLogDiff.splash`
|
||||||
|
- :attr:`~AuditLogDiff.vanity_url_code`
|
||||||
|
|
||||||
|
.. attribute:: channel_create
|
||||||
|
|
||||||
|
A new channel was created.
|
||||||
|
|
||||||
|
When this is the action, the type of :attr:`~AuditLogEntry.target` is
|
||||||
|
either a :class:`abc.GuildChannel` or :class:`Object` with an ID.
|
||||||
|
|
||||||
|
A more filled out object in the :class:`Object` case can be found
|
||||||
|
by using :attr:`~AuditLogEntry.after`.
|
||||||
|
|
||||||
|
Possible attributes for :class:`AuditLogDiff`:
|
||||||
|
|
||||||
|
- :attr:`~AuditLogDiff.name`
|
||||||
|
- :attr:`~AuditLogDiff.type`
|
||||||
|
- :attr:`~AuditLogDiff.overwrites`
|
||||||
|
|
||||||
|
.. attribute:: channel_update
|
||||||
|
|
||||||
|
A channel was updated. Things that trigger this include:
|
||||||
|
|
||||||
|
- The channel name or topic was changed
|
||||||
|
- The channel bitrate was changed
|
||||||
|
|
||||||
|
When this is the action, the type of :attr:`~AuditLogEntry.target` is
|
||||||
|
the :class:`abc.GuildChannel` or :class:`Object` with an ID.
|
||||||
|
|
||||||
|
A more filled out object in the :class:`Object` case can be found
|
||||||
|
by using :attr:`~AuditLogEntry.after` or :attr:`~AuditLogEntry.before`.
|
||||||
|
|
||||||
|
Possible attributes for :class:`AuditLogDiff`:
|
||||||
|
|
||||||
|
- :attr:`~AuditLogDiff.name`
|
||||||
|
- :attr:`~AuditLogDiff.type`
|
||||||
|
- :attr:`~AuditLogDiff.position`
|
||||||
|
- :attr:`~AuditLogDiff.overwrites`
|
||||||
|
- :attr:`~AuditLogDiff.topic`
|
||||||
|
- :attr:`~AuditLogDiff.bitrate`
|
||||||
|
|
||||||
|
.. attribute:: channel_delete
|
||||||
|
|
||||||
|
A channel was deleted.
|
||||||
|
|
||||||
|
When this is the action, the type of :attr:`~AuditLogEntry.target` is
|
||||||
|
an :class:`Object` with an ID.
|
||||||
|
|
||||||
|
A more filled out object can be found by using the
|
||||||
|
:attr:`~AuditLogEntry.before` object.
|
||||||
|
|
||||||
|
Possible attributes for :class:`AuditLogDiff`:
|
||||||
|
|
||||||
|
- :attr:`~AuditLogDiff.name`
|
||||||
|
- :attr:`~AuditLogDiff.type`
|
||||||
|
- :attr:`~AuditLogDiff.overwrites`
|
||||||
|
|
||||||
|
.. attribute:: overwrite_create
|
||||||
|
|
||||||
|
A channel permission overwrite was created.
|
||||||
|
|
||||||
|
When this is the action, the type of :attr:`~AuditLogEntry.target` is
|
||||||
|
the :class:`abc.GuildChannel` or :class:`Object` with an ID.
|
||||||
|
|
||||||
|
When this is the action, the type of :attr:`~AuditLogEntry.extra` is
|
||||||
|
either a :class:`Role` or :class:`Member`. If the object is not found
|
||||||
|
then it is a :class:`Object` with an ID being filled, a name, and a
|
||||||
|
``type`` attribute set to either ``'role'`` or ``'member'`` to help
|
||||||
|
dictate what type of ID it is.
|
||||||
|
|
||||||
|
Possible attributes for :class:`AuditLogDiff`:
|
||||||
|
|
||||||
|
- :attr:`~AuditLogDiff.deny`
|
||||||
|
- :attr:`~AuditLogDiff.allow`
|
||||||
|
- :attr:`~AuditLogDiff.id`
|
||||||
|
- :attr:`~AuditLogDiff.type`
|
||||||
|
|
||||||
|
.. attribute:: overwrite_update
|
||||||
|
|
||||||
|
A channel permission overwrite was changed, this is typically
|
||||||
|
when the permission values change.
|
||||||
|
|
||||||
|
See :attr:`overwrite_create` for more information on how the
|
||||||
|
:attr:`~AuditLogEntry.target` and :attr:`~AuditLogEntry.extra` fields
|
||||||
|
are set.
|
||||||
|
|
||||||
|
Possible attributes for :class:`AuditLogDiff`:
|
||||||
|
|
||||||
|
- :attr:`~AuditLogDiff.deny`
|
||||||
|
- :attr:`~AuditLogDiff.allow`
|
||||||
|
- :attr:`~AuditLogDiff.id`
|
||||||
|
- :attr:`~AuditLogDiff.type`
|
||||||
|
|
||||||
|
.. attribute:: overwrite_delete
|
||||||
|
|
||||||
|
A channel permission overwrite was deleted.
|
||||||
|
|
||||||
|
See :attr:`overwrite_create` for more information on how the
|
||||||
|
:attr:`~AuditLogEntry.target` and :attr:`~AuditLogEntry.extra` fields
|
||||||
|
are set.
|
||||||
|
|
||||||
|
Possible attributes for :class:`AuditLogDiff`:
|
||||||
|
|
||||||
|
- :attr:`~AuditLogDiff.deny`
|
||||||
|
- :attr:`~AuditLogDiff.allow`
|
||||||
|
- :attr:`~AuditLogDiff.id`
|
||||||
|
- :attr:`~AuditLogDiff.type`
|
||||||
|
|
||||||
|
.. attribute:: kick
|
||||||
|
|
||||||
|
A member was kicked.
|
||||||
|
|
||||||
|
When this is the action, the type of :attr:`~AuditLogEntry.target` is
|
||||||
|
the :class:`User` who got kicked.
|
||||||
|
|
||||||
|
When this is the action, :attr:`~AuditLogEntry.changes` is empty.
|
||||||
|
|
||||||
|
.. attribute:: member_prune
|
||||||
|
|
||||||
|
A member prune was triggered.
|
||||||
|
|
||||||
|
When this is the action, the type of :attr:`~AuditLogEntry.target` is
|
||||||
|
set to `None`.
|
||||||
|
|
||||||
|
When this is the action, the type of :attr:`~AuditLogEntry.extra` is
|
||||||
|
set to an unspecified proxy object with two attributes:
|
||||||
|
|
||||||
|
- ``delete_members_days``: An integer specifying how far the prune was.
|
||||||
|
- ``members_removed``: An integer specifying how many members were removed.
|
||||||
|
|
||||||
|
When this is the action, :attr:`~AuditLogEntry.changes` is empty.
|
||||||
|
|
||||||
|
.. attribute:: ban
|
||||||
|
|
||||||
|
A member was banned.
|
||||||
|
|
||||||
|
When this is the action, the type of :attr:`~AuditLogEntry.target` is
|
||||||
|
the :class:`User` who got banned.
|
||||||
|
|
||||||
|
When this is the action, :attr:`~AuditLogEntry.changes` is empty.
|
||||||
|
|
||||||
|
.. attribute:: unban
|
||||||
|
|
||||||
|
A member was unbanned.
|
||||||
|
|
||||||
|
When this is the action, the type of :attr:`~AuditLogEntry.target` is
|
||||||
|
the :class:`User` who got unbanned.
|
||||||
|
|
||||||
|
When this is the action, :attr:`~AuditLogEntry.changes` is empty.
|
||||||
|
|
||||||
|
.. attribute:: member_update
|
||||||
|
|
||||||
|
A member has updated. This triggers in the following situations:
|
||||||
|
|
||||||
|
- A nickname was changed
|
||||||
|
- They were server muted or deafened (or it was undo'd)
|
||||||
|
|
||||||
|
When this is the action, the type of :attr:`~AuditLogEntry.target` is
|
||||||
|
the :class:`Member` or :class:`User` who got updated.
|
||||||
|
|
||||||
|
Possible attributes for :class:`AuditLogDiff`:
|
||||||
|
|
||||||
|
- :attr:`~AuditLogDiff.nick`
|
||||||
|
- :attr:`~AuditLogDiff.mute`
|
||||||
|
- :attr:`~AuditLogDiff.deaf`
|
||||||
|
|
||||||
|
.. attribute:: member_role_update
|
||||||
|
|
||||||
|
A member's role has been updated. This triggers when a member
|
||||||
|
either gains a role or losses a role.
|
||||||
|
|
||||||
|
When this is the action, the type of :attr:`~AuditLogEntry.target` is
|
||||||
|
the :class:`Member` or :class:`User` who got the role.
|
||||||
|
|
||||||
|
Possible attributes for :class:`AuditLogDiff`:
|
||||||
|
|
||||||
|
- :attr:`~AuditLogDiff.role`
|
||||||
|
|
||||||
|
.. attribute:: role_create
|
||||||
|
|
||||||
|
A new role was created.
|
||||||
|
|
||||||
|
When this is the action, the type of :attr:`~AuditLogEntry.target` is
|
||||||
|
the :class:`Role` or a :class:`Object` with the ID.
|
||||||
|
|
||||||
|
Possible attributes for :class:`AuditLogDiff`:
|
||||||
|
|
||||||
|
- :attr:`~AuditLogDiff.colour`
|
||||||
|
- :attr:`~AuditLogDiff.mentionable`
|
||||||
|
- :attr:`~AuditLogDiff.hoist`
|
||||||
|
- :attr:`~AuditLogDiff.name`
|
||||||
|
- :attr:`~AuditLogDiff.permissions`
|
||||||
|
|
||||||
|
.. attribute:: role_update
|
||||||
|
|
||||||
|
A role was updated. This triggers in the following situations:
|
||||||
|
|
||||||
|
- The name has changed
|
||||||
|
- The permissions have changed
|
||||||
|
- The colour has changed
|
||||||
|
- Its hoist/mentionable state has changed
|
||||||
|
|
||||||
|
When this is the action, the type of :attr:`~AuditLogEntry.target` is
|
||||||
|
the :class:`Role` or a :class:`Object` with the ID.
|
||||||
|
|
||||||
|
Possible attributes for :class:`AuditLogDiff`:
|
||||||
|
|
||||||
|
- :attr:`~AuditLogDiff.colour`
|
||||||
|
- :attr:`~AuditLogDiff.mentionable`
|
||||||
|
- :attr:`~AuditLogDiff.hoist`
|
||||||
|
- :attr:`~AuditLogDiff.name`
|
||||||
|
- :attr:`~AuditLogDiff.permissions`
|
||||||
|
|
||||||
|
.. attribute:: role_delete
|
||||||
|
|
||||||
|
A role was deleted.
|
||||||
|
|
||||||
|
When this is the action, the type of :attr:`~AuditLogEntry.target` is
|
||||||
|
the :class:`Role` or a :class:`Object` with the ID.
|
||||||
|
|
||||||
|
Possible attributes for :class:`AuditLogDiff`:
|
||||||
|
|
||||||
|
- :attr:`~AuditLogDiff.colour`
|
||||||
|
- :attr:`~AuditLogDiff.mentionable`
|
||||||
|
- :attr:`~AuditLogDiff.hoist`
|
||||||
|
- :attr:`~AuditLogDiff.name`
|
||||||
|
- :attr:`~AuditLogDiff.permissions`
|
||||||
|
|
||||||
|
.. attribute:: invite_create
|
||||||
|
|
||||||
|
An invite was created.
|
||||||
|
|
||||||
|
When this is the action, the type of :attr:`~AuditLogEntry.target` is
|
||||||
|
the :class:`Invite` that was created.
|
||||||
|
|
||||||
|
Possible attributes for :class:`AuditLogDiff`:
|
||||||
|
|
||||||
|
- :attr:`~AuditLogDiff.max_age`
|
||||||
|
- :attr:`~AuditLogDiff.code`
|
||||||
|
- :attr:`~AuditLogDiff.temporary`
|
||||||
|
- :attr:`~AuditLogDiff.inviter`
|
||||||
|
- :attr:`~AuditLogDiff.channel`
|
||||||
|
- :attr:`~AuditLogDiff.uses`
|
||||||
|
- :attr:`~AuditLogDiff.max_uses`
|
||||||
|
|
||||||
|
.. attribute:: invite_update
|
||||||
|
|
||||||
|
An invite was updated.
|
||||||
|
|
||||||
|
When this is the action, the type of :attr:`~AuditLogEntry.target` is
|
||||||
|
the :class:`Invite` that was updated.
|
||||||
|
|
||||||
|
.. attribute:: invite_delete
|
||||||
|
|
||||||
|
An invite was deleted.
|
||||||
|
|
||||||
|
When this is the action, the type of :attr:`~AuditLogEntry.target` is
|
||||||
|
the :class:`Invite` that was deleted.
|
||||||
|
|
||||||
|
Possible attributes for :class:`AuditLogDiff`:
|
||||||
|
|
||||||
|
- :attr:`~AuditLogDiff.max_age`
|
||||||
|
- :attr:`~AuditLogDiff.code`
|
||||||
|
- :attr:`~AuditLogDiff.temporary`
|
||||||
|
- :attr:`~AuditLogDiff.inviter`
|
||||||
|
- :attr:`~AuditLogDiff.channel`
|
||||||
|
- :attr:`~AuditLogDiff.uses`
|
||||||
|
- :attr:`~AuditLogDiff.max_uses`
|
||||||
|
|
||||||
|
.. attribute:: webhook_create
|
||||||
|
|
||||||
|
A webhook was created.
|
||||||
|
|
||||||
|
When this is the action, the type of :attr:`~AuditLogEntry.target` is
|
||||||
|
the :class:`Object` with the webhook ID.
|
||||||
|
|
||||||
|
Possible attributes for :class:`AuditLogDiff`:
|
||||||
|
|
||||||
|
- :attr:`~AuditLogDiff.channel`
|
||||||
|
- :attr:`~AuditLogDiff.name`
|
||||||
|
- :attr:`~AuditLogDiff.type` (always set to ``1`` if so)
|
||||||
|
|
||||||
|
.. attribute:: webhook_update
|
||||||
|
|
||||||
|
A webhook was updated. This trigger in the following situations:
|
||||||
|
|
||||||
|
- The webhook name changed
|
||||||
|
- The webhook channel changed
|
||||||
|
|
||||||
|
When this is the action, the type of :attr:`~AuditLogEntry.target` is
|
||||||
|
the :class:`Object` with the webhook ID.
|
||||||
|
|
||||||
|
Possible attributes for :class:`AuditLogDiff`:
|
||||||
|
|
||||||
|
- :attr:`~AuditLogDiff.channel`
|
||||||
|
- :attr:`~AuditLogDiff.name`
|
||||||
|
|
||||||
|
.. attribute:: webhook_delete
|
||||||
|
|
||||||
|
A webhook was deleted.
|
||||||
|
|
||||||
|
When this is the action, the type of :attr:`~AuditLogEntry.target` is
|
||||||
|
the :class:`Object` with the webhook ID.
|
||||||
|
|
||||||
|
Possible attributes for :class:`AuditLogDiff`:
|
||||||
|
|
||||||
|
- :attr:`~AuditLogDiff.channel`
|
||||||
|
- :attr:`~AuditLogDiff.name`
|
||||||
|
- :attr:`~AuditLogDiff.type` (always set to ``1`` if so)
|
||||||
|
|
||||||
|
.. attribute:: emoji_create
|
||||||
|
|
||||||
|
An emoji was created.
|
||||||
|
|
||||||
|
When this is the action, the type of :attr:`~AuditLogEntry.target` is
|
||||||
|
the :class:`Emoji` or :class:`Object` with the emoji ID.
|
||||||
|
|
||||||
|
Possible attributes for :class:`AuditLogDiff`:
|
||||||
|
|
||||||
|
- :attr:`~AuditLogDiff.name`
|
||||||
|
|
||||||
|
.. attribute:: emoji_update
|
||||||
|
|
||||||
|
An emoji was updated. This triggers when the name has changed.
|
||||||
|
|
||||||
|
When this is the action, the type of :attr:`~AuditLogEntry.target` is
|
||||||
|
the :class:`Emoji` or :class:`Object` with the emoji ID.
|
||||||
|
|
||||||
|
Possible attributes for :class:`AuditLogDiff`:
|
||||||
|
|
||||||
|
- :attr:`~AuditLogDiff.name`
|
||||||
|
|
||||||
|
.. attribute:: emoji_delete
|
||||||
|
|
||||||
|
An emoji was deleted.
|
||||||
|
|
||||||
|
When this is the action, the type of :attr:`~AuditLogEntry.target` is
|
||||||
|
the :class:`Object` with the emoji ID.
|
||||||
|
|
||||||
|
Possible attributes for :class:`AuditLogDiff`:
|
||||||
|
|
||||||
|
- :attr:`~AuditLogDiff.name`
|
||||||
|
|
||||||
|
|
||||||
|
.. class:: AuditLogActionCategory
|
||||||
|
|
||||||
|
Represents the category that the :class:`AuditLogAction` belongs to.
|
||||||
|
|
||||||
|
This can be retrieved via :attr:`AuditLogEntry.category`.
|
||||||
|
|
||||||
|
.. attribute:: create
|
||||||
|
|
||||||
|
The action is the creation of something.
|
||||||
|
|
||||||
|
.. attribute:: delete
|
||||||
|
|
||||||
|
The action is the deletion of something.
|
||||||
|
|
||||||
|
.. attribute:: update
|
||||||
|
|
||||||
|
The action is the update of something.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Async Iterator
|
Async Iterator
|
||||||
----------------
|
----------------
|
||||||
|
|
||||||
@ -785,6 +1179,297 @@ Certain utilities make working with async iterators easier, detailed below.
|
|||||||
:param predicate: The predicate to call on every element. Could be a coroutine.
|
:param predicate: The predicate to call on every element. Could be a coroutine.
|
||||||
:return: An async iterator.
|
:return: An async iterator.
|
||||||
|
|
||||||
|
|
||||||
|
Audit Log Data
|
||||||
|
----------------
|
||||||
|
|
||||||
|
Working with :meth:`Guild.audit_logs` is a complicated process with a lot of machinery
|
||||||
|
involved. The library attempts to make it easy to use and friendly. In order to accomplish
|
||||||
|
this goal, it must make use of a couple of data classes that aid in this goal.
|
||||||
|
|
||||||
|
.. autoclass:: AuditLogEntry
|
||||||
|
:members:
|
||||||
|
|
||||||
|
.. class:: AuditLogChanges
|
||||||
|
|
||||||
|
An audit log change set.
|
||||||
|
|
||||||
|
.. attribute:: before
|
||||||
|
|
||||||
|
The old value. The attribute has the type of :class:`AuditLogDiff`.
|
||||||
|
|
||||||
|
Depending on the :class:`AuditLogActionCategory` retrieved by
|
||||||
|
:attr:`~AuditLogEntry.category`\, the data retrieved by this
|
||||||
|
attribute differs:
|
||||||
|
|
||||||
|
+----------------------------------------+---------------------------------------------------+
|
||||||
|
| Category | Description |
|
||||||
|
+----------------------------------------+---------------------------------------------------+
|
||||||
|
| :attr:`~AuditLogActionCategory.create` | All attributes are set to ``None``. |
|
||||||
|
+----------------------------------------+---------------------------------------------------+
|
||||||
|
| :attr:`~AuditLogActionCategory.delete` | All attributes are set the value before deletion. |
|
||||||
|
+----------------------------------------+---------------------------------------------------+
|
||||||
|
| :attr:`~AuditLogActionCategory.update` | All attributes are set the value before updating. |
|
||||||
|
+----------------------------------------+---------------------------------------------------+
|
||||||
|
| ``None`` | No attributes are set. |
|
||||||
|
+----------------------------------------+---------------------------------------------------+
|
||||||
|
|
||||||
|
.. attribute:: after
|
||||||
|
|
||||||
|
The new value. The attribute has the type of :class:`AuditLogDiff`.
|
||||||
|
|
||||||
|
Depending on the :class:`AuditLogActionCategory` retrieved by
|
||||||
|
:attr:`~AuditLogEntry.category`\, the data retrieved by this
|
||||||
|
attribute differs:
|
||||||
|
|
||||||
|
+----------------------------------------+--------------------------------------------------+
|
||||||
|
| Category | Description |
|
||||||
|
+----------------------------------------+--------------------------------------------------+
|
||||||
|
| :attr:`~AuditLogActionCategory.create` | All attributes are set to the created value |
|
||||||
|
+----------------------------------------+--------------------------------------------------+
|
||||||
|
| :attr:`~AuditLogActionCategory.delete` | All attributes are set to ``None`` |
|
||||||
|
+----------------------------------------+--------------------------------------------------+
|
||||||
|
| :attr:`~AuditLogActionCategory.update` | All attributes are set the value after updating. |
|
||||||
|
+----------------------------------------+--------------------------------------------------+
|
||||||
|
| ``None`` | No attributes are set. |
|
||||||
|
+----------------------------------------+--------------------------------------------------+
|
||||||
|
|
||||||
|
.. class:: AuditLogDiff
|
||||||
|
|
||||||
|
Represents an audit log "change" object. A change object has dynamic
|
||||||
|
attributes that depend on the type of action being done. Certain actions
|
||||||
|
map to certain attributes being set.
|
||||||
|
|
||||||
|
Note that accessing an attribute that does not match the specified action
|
||||||
|
will lead to an attribute error.
|
||||||
|
|
||||||
|
To get a list of attributes that have been set, you can iterate over
|
||||||
|
them. To see a list of all possible attributes that could be set based
|
||||||
|
on the action being done, check the documentation for :class:`AuditLogAction`,
|
||||||
|
otherwise check the documentation below for all attributes that are possible.
|
||||||
|
|
||||||
|
.. describe:: iter(diff)
|
||||||
|
|
||||||
|
Return an iterator over (attribute, value) tuple of this diff.
|
||||||
|
|
||||||
|
.. attribute:: name
|
||||||
|
|
||||||
|
*str* – A name of something.
|
||||||
|
|
||||||
|
.. attribute:: icon
|
||||||
|
|
||||||
|
*str* – A guild's icon hash. See also :attr:`Guild.icon`.
|
||||||
|
|
||||||
|
.. attribute:: splash
|
||||||
|
|
||||||
|
*str* – The guild's invite splash hash. See also :attr:`Guild.splash`.
|
||||||
|
|
||||||
|
.. attribute:: owner
|
||||||
|
|
||||||
|
*Union[:class:`Member`, :class:`User`]`* – The guild's owner. See also :attr:`Guild.owner`
|
||||||
|
|
||||||
|
.. attribute:: region
|
||||||
|
|
||||||
|
*:class:`GuildRegion`* – The guild's voice region. See also :attr:`Guild.region`.
|
||||||
|
|
||||||
|
.. attribute:: afk_channel
|
||||||
|
|
||||||
|
*Union[:class:`VoiceChannel`, :class:`Object`]* – The guild's AFK channel.
|
||||||
|
|
||||||
|
If this could not be found, then it falls back to a :class:`Object`
|
||||||
|
with the ID being set.
|
||||||
|
|
||||||
|
See :attr:`Guild.afk_channel`.
|
||||||
|
|
||||||
|
.. attribute:: afk_timeout
|
||||||
|
|
||||||
|
*int* – The guild's AFK timeout. See :attr:`Guild.afk_timeout`.
|
||||||
|
|
||||||
|
.. attribute:: mfa_level
|
||||||
|
|
||||||
|
*int* - The guild's MFA level. See :attr:`Guild.mfa_level`.
|
||||||
|
|
||||||
|
.. attribute:: widget_enabled
|
||||||
|
|
||||||
|
*bool* – The guild's widget has been enabled or disabled.
|
||||||
|
|
||||||
|
.. attribute:: widget_channel
|
||||||
|
|
||||||
|
*Union[:class:`TextChannel`, :class:`Object`]* – The widget's channel.
|
||||||
|
|
||||||
|
If this could not be found then it falls back to a :class:`Object`
|
||||||
|
with the ID being set.
|
||||||
|
|
||||||
|
.. attribute:: verification_level
|
||||||
|
|
||||||
|
*:class:`VerificationLevel`* – The guild's verification level.
|
||||||
|
|
||||||
|
See also :attr:`Guild.verification_level`.
|
||||||
|
|
||||||
|
.. attribute:: explicit_content_filter
|
||||||
|
|
||||||
|
*:class:`ContentFilter`* – The guild's content filter.
|
||||||
|
|
||||||
|
See also :attr:`Guild.explicit_content_filter`.
|
||||||
|
|
||||||
|
.. attribute:: default_message_notifications
|
||||||
|
|
||||||
|
*int* – The guild's default message notification setting.
|
||||||
|
|
||||||
|
.. attribute:: vanity_url_code
|
||||||
|
|
||||||
|
*str* – The guild's vanity URL.
|
||||||
|
|
||||||
|
.. attribute:: position
|
||||||
|
|
||||||
|
*int* – The position of a :class:`Role` or :class:`abc.GuildChannel`.
|
||||||
|
|
||||||
|
.. attribute:: type
|
||||||
|
|
||||||
|
*Union[int, str]* – The type of channel or channel permission overwrite.
|
||||||
|
|
||||||
|
If the type is an ``int``, then it is a type of channel which can be either
|
||||||
|
``0`` to indicate a text channel or ``1`` to indicate a voice channel.
|
||||||
|
|
||||||
|
If the type is a ``str``, then it is a type of permission overwrite which
|
||||||
|
can be either ``'role'`` or ``'member'``.
|
||||||
|
|
||||||
|
.. attribute:: topic
|
||||||
|
|
||||||
|
*str* – The topic of a :class:`TextChannel`.
|
||||||
|
|
||||||
|
See also :attr:`TextChannel.topic`.
|
||||||
|
|
||||||
|
.. attribute:: bitrate
|
||||||
|
|
||||||
|
*int* – The bitrate of a :class:`VoiceChannel`.
|
||||||
|
|
||||||
|
See also :attr:`VoiceChannel.bitrate`.
|
||||||
|
|
||||||
|
.. attribute:: overwrites
|
||||||
|
|
||||||
|
*List[Tuple[target, :class:`PermissionOverwrite`]]* – A list of
|
||||||
|
permission overwrite tuples that represents a target and a
|
||||||
|
:class:`PermissionOverwrite` for said target.
|
||||||
|
|
||||||
|
The first element is the object being targeted, which can either
|
||||||
|
be a :class:`Member` or :class:`User` or :class:`Role`. If this object
|
||||||
|
is not found then it is a :class:`Object` with an ID being filled and
|
||||||
|
a ``type`` attribute set to either ``'role'`` or ``'member'`` to help
|
||||||
|
decide what type of ID it is.
|
||||||
|
|
||||||
|
.. attribute:: role
|
||||||
|
|
||||||
|
*Union[:class:`Role`, :class:`Object`]* – A role being added or removed
|
||||||
|
from a member.
|
||||||
|
|
||||||
|
If the role is not found then it is a :class:`Object` with the ID being
|
||||||
|
filled in.
|
||||||
|
|
||||||
|
.. attribute:: nick
|
||||||
|
|
||||||
|
*Optional[str]* – The nickname of a member.
|
||||||
|
|
||||||
|
See also :attr:`Member.nick`
|
||||||
|
|
||||||
|
.. attribute:: deaf
|
||||||
|
|
||||||
|
*bool* – Whether the member is being server deafened.
|
||||||
|
|
||||||
|
See also :attr:`VoiceState.deaf`.
|
||||||
|
|
||||||
|
.. attribute:: mute
|
||||||
|
|
||||||
|
*bool* – Whether the member is being server muted.
|
||||||
|
|
||||||
|
See also :attr:`VoiceState.mute`.
|
||||||
|
|
||||||
|
.. attribute:: permissions
|
||||||
|
|
||||||
|
*:class:`Permissions`* – The permissions of a role.
|
||||||
|
|
||||||
|
See also :attr:`Role.permissions`.
|
||||||
|
|
||||||
|
.. attribute:: colour
|
||||||
|
color
|
||||||
|
|
||||||
|
*:class:`Colour`* – The colour of a role.
|
||||||
|
|
||||||
|
See also :attr:`Role.colour`
|
||||||
|
|
||||||
|
.. attribute:: hoist
|
||||||
|
|
||||||
|
*bool* – Whether the role is being hoisted or not.
|
||||||
|
|
||||||
|
See also :attr:`Role.hoist`
|
||||||
|
|
||||||
|
.. attribute:: mentionable
|
||||||
|
|
||||||
|
*bool* – Whether the role is mentionable or not.
|
||||||
|
|
||||||
|
See also :attr:`Role.mentionable`
|
||||||
|
|
||||||
|
.. attribute:: code
|
||||||
|
|
||||||
|
*str* – The invite's code.
|
||||||
|
|
||||||
|
See also :attr:`Invite.code`
|
||||||
|
|
||||||
|
.. attribute:: channel
|
||||||
|
|
||||||
|
*Union[:class:`abc.GuildChannel`, :class:`Object`]* – A guild channel.
|
||||||
|
|
||||||
|
If the channel is not found then it is a :class:`Object` with the ID
|
||||||
|
being set. In some cases the channel name is also set.
|
||||||
|
|
||||||
|
.. attribute:: inviter
|
||||||
|
|
||||||
|
*:class:`User`* – The user who created the invite.
|
||||||
|
|
||||||
|
See also :attr:`Invite.inviter`.
|
||||||
|
|
||||||
|
.. attribute:: max_uses
|
||||||
|
|
||||||
|
*int* – The invite's max uses.
|
||||||
|
|
||||||
|
See also :attr:`Invite.max_uses`.
|
||||||
|
|
||||||
|
.. attribute:: uses
|
||||||
|
|
||||||
|
*int* – The invite's current uses.
|
||||||
|
|
||||||
|
See also :attr:`Invite.uses`.
|
||||||
|
|
||||||
|
.. attribute:: max_age
|
||||||
|
|
||||||
|
*int* – The invite's max age in seconds.
|
||||||
|
|
||||||
|
See also :attr:`Invite.max_age`.
|
||||||
|
|
||||||
|
.. attribute:: temporary
|
||||||
|
|
||||||
|
*bool* – If the invite is a temporary invite.
|
||||||
|
|
||||||
|
See also :attr:`Invite.temporary`.
|
||||||
|
|
||||||
|
.. attribute:: allow
|
||||||
|
deny
|
||||||
|
|
||||||
|
*:class:`Permissions`* – The permissions being allowed or denied.
|
||||||
|
|
||||||
|
.. attribute:: id
|
||||||
|
|
||||||
|
*int* – The ID of the object being changed.
|
||||||
|
|
||||||
|
.. attribute:: avatar
|
||||||
|
|
||||||
|
*str* – The avatar hash of a member.
|
||||||
|
|
||||||
|
See also :attr:`User.avatar`.
|
||||||
|
|
||||||
|
.. this is currently missing the following keys: reason and application_id
|
||||||
|
I'm not sure how to about porting these
|
||||||
|
|
||||||
.. _discord_api_data:
|
.. _discord_api_data:
|
||||||
|
|
||||||
Data Classes
|
Data Classes
|
||||||
|
Loading…
x
Reference in New Issue
Block a user