serenity/model/guild/audit_log/
mod.rs

1//! Audit log types for administrative actions within guilds.
2
3use std::mem::transmute;
4
5use serde::ser::{Serialize, Serializer};
6
7mod change;
8mod utils;
9
10pub use change::{AffectedRole, Change, EntityType};
11use utils::{optional_string, users, webhooks};
12
13use crate::model::prelude::*;
14
15/// Determines the action that was done on a target.
16///
17/// [Discord docs](https://discord.com/developers/docs/resources/audit-log#audit-log-entry-object-audit-log-events).
18#[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
19#[derive(Clone, Copy, Debug)]
20#[non_exhaustive]
21pub enum Action {
22    GuildUpdate,
23    Channel(ChannelAction),
24    ChannelOverwrite(ChannelOverwriteAction),
25    Member(MemberAction),
26    Role(RoleAction),
27    Invite(InviteAction),
28    Webhook(WebhookAction),
29    Emoji(EmojiAction),
30    Message(MessageAction),
31    Integration(IntegrationAction),
32    StageInstance(StageInstanceAction),
33    Sticker(StickerAction),
34    ScheduledEvent(ScheduledEventAction),
35    Thread(ThreadAction),
36    AutoMod(AutoModAction),
37    CreatorMonetization(CreatorMonetizationAction),
38    VoiceChannelStatus(VoiceChannelStatusAction),
39    Unknown(u8),
40}
41
42impl Action {
43    #[must_use]
44    pub const fn num(self) -> u8 {
45        match self {
46            Self::GuildUpdate => 1,
47            Self::Channel(x) => x as u8,
48            Self::ChannelOverwrite(x) => x as u8,
49            Self::Member(x) => x as u8,
50            Self::Role(x) => x as u8,
51            Self::Invite(x) => x as u8,
52            Self::Webhook(x) => x as u8,
53            Self::Emoji(x) => x as u8,
54            Self::Message(x) => x as u8,
55            Self::Integration(x) => x as u8,
56            Self::StageInstance(x) => x as u8,
57            Self::Sticker(x) => x as u8,
58            Self::ScheduledEvent(x) => x as u8,
59            Self::Thread(x) => x as u8,
60            Self::AutoMod(x) => x as u8,
61            Self::CreatorMonetization(x) => x as u8,
62            Self::VoiceChannelStatus(x) => x as u8,
63            Self::Unknown(x) => x,
64        }
65    }
66
67    #[must_use]
68    #[allow(unknown_lints, clippy::missing_transmute_annotations)]
69    pub fn from_value(value: u8) -> Action {
70        match value {
71            1 => Action::GuildUpdate,
72            10..=12 => Action::Channel(unsafe { transmute(value) }),
73            13..=15 => Action::ChannelOverwrite(unsafe { transmute(value) }),
74            20..=28 => Action::Member(unsafe { transmute(value) }),
75            30..=32 => Action::Role(unsafe { transmute(value) }),
76            40..=42 => Action::Invite(unsafe { transmute(value) }),
77            50..=52 => Action::Webhook(unsafe { transmute(value) }),
78            60..=62 => Action::Emoji(unsafe { transmute(value) }),
79            72..=75 => Action::Message(unsafe { transmute(value) }),
80            80..=82 => Action::Integration(unsafe { transmute(value) }),
81            83..=85 => Action::StageInstance(unsafe { transmute(value) }),
82            90..=92 => Action::Sticker(unsafe { transmute(value) }),
83            100..=102 => Action::ScheduledEvent(unsafe { transmute(value) }),
84            110..=112 => Action::Thread(unsafe { transmute(value) }),
85            140..=145 => Action::AutoMod(unsafe { transmute(value) }),
86            150..=151 => Action::CreatorMonetization(unsafe { transmute(value) }),
87            192..=193 => Action::VoiceChannelStatus(unsafe { transmute(value) }),
88            _ => Action::Unknown(value),
89        }
90    }
91}
92
93// Manual impl needed to emulate integer enum tags
94impl<'de> Deserialize<'de> for Action {
95    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> StdResult<Self, D::Error> {
96        let value = u8::deserialize(deserializer)?;
97        Ok(Action::from_value(value))
98    }
99}
100
101impl Serialize for Action {
102    fn serialize<S: Serializer>(&self, serializer: S) -> StdResult<S::Ok, S::Error> {
103        serializer.serialize_u8(self.num())
104    }
105}
106
107/// [Discord docs](https://discord.com/developers/docs/resources/audit-log#audit-log-entry-object-audit-log-events).
108#[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
109#[derive(Clone, Copy, Debug)]
110#[non_exhaustive]
111#[repr(u8)]
112pub enum ChannelAction {
113    Create = 10,
114    Update = 11,
115    Delete = 12,
116}
117
118/// [Discord docs](https://discord.com/developers/docs/resources/audit-log#audit-log-entry-object-audit-log-events).
119#[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
120#[derive(Clone, Copy, Debug)]
121#[non_exhaustive]
122#[repr(u8)]
123pub enum ChannelOverwriteAction {
124    Create = 13,
125    Update = 14,
126    Delete = 15,
127}
128
129/// [Discord docs](https://discord.com/developers/docs/resources/audit-log#audit-log-entry-object-audit-log-events).
130#[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
131#[derive(Clone, Copy, Debug)]
132#[non_exhaustive]
133#[repr(u8)]
134pub enum MemberAction {
135    Kick = 20,
136    Prune = 21,
137    BanAdd = 22,
138    BanRemove = 23,
139    Update = 24,
140    RoleUpdate = 25,
141    MemberMove = 26,
142    MemberDisconnect = 27,
143    BotAdd = 28,
144}
145
146/// [Discord docs](https://discord.com/developers/docs/resources/audit-log#audit-log-entry-object-audit-log-events).
147#[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
148#[derive(Clone, Copy, Debug)]
149#[non_exhaustive]
150#[repr(u8)]
151pub enum RoleAction {
152    Create = 30,
153    Update = 31,
154    Delete = 32,
155}
156
157/// [Discord docs](https://discord.com/developers/docs/resources/audit-log#audit-log-entry-object-audit-log-events).
158#[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
159#[derive(Clone, Copy, Debug)]
160#[non_exhaustive]
161#[repr(u8)]
162pub enum InviteAction {
163    Create = 40,
164    Update = 41,
165    Delete = 42,
166}
167
168/// [Discord docs](https://discord.com/developers/docs/resources/audit-log#audit-log-entry-object-audit-log-events).
169#[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
170#[derive(Clone, Copy, Debug)]
171#[non_exhaustive]
172#[repr(u8)]
173pub enum WebhookAction {
174    Create = 50,
175    Update = 51,
176    Delete = 52,
177}
178
179/// [Discord docs](https://discord.com/developers/docs/resources/audit-log#audit-log-entry-object-audit-log-events).
180#[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
181#[derive(Clone, Copy, Debug)]
182#[non_exhaustive]
183#[repr(u8)]
184pub enum EmojiAction {
185    Create = 60,
186    Update = 61,
187    Delete = 62,
188}
189
190/// [Discord docs](https://discord.com/developers/docs/resources/audit-log#audit-log-entry-object-audit-log-events).
191#[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
192#[derive(Clone, Copy, Debug)]
193#[non_exhaustive]
194#[repr(u8)]
195pub enum MessageAction {
196    Delete = 72,
197    BulkDelete = 73,
198    Pin = 74,
199    Unpin = 75,
200}
201
202/// [Discord docs](https://discord.com/developers/docs/resources/audit-log#audit-log-entry-object-audit-log-events).
203#[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
204#[derive(Clone, Copy, Debug)]
205#[non_exhaustive]
206#[repr(u8)]
207pub enum IntegrationAction {
208    Create = 80,
209    Update = 81,
210    Delete = 82,
211}
212
213/// [Discord docs](https://discord.com/developers/docs/resources/audit-log#audit-log-entry-object-audit-log-events).
214#[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
215#[derive(Clone, Copy, Debug)]
216#[non_exhaustive]
217#[repr(u8)]
218pub enum StageInstanceAction {
219    Create = 83,
220    Update = 84,
221    Delete = 85,
222}
223
224/// [Discord docs](https://discord.com/developers/docs/resources/audit-log#audit-log-entry-object-audit-log-events).
225#[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
226#[derive(Clone, Copy, Debug)]
227#[non_exhaustive]
228#[repr(u8)]
229pub enum StickerAction {
230    Create = 90,
231    Update = 91,
232    Delete = 92,
233}
234
235/// [Discord docs](https://discord.com/developers/docs/resources/audit-log#audit-log-entry-object-audit-log-events).
236#[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
237#[derive(Clone, Copy, Debug)]
238#[non_exhaustive]
239#[repr(u8)]
240pub enum ScheduledEventAction {
241    Create = 100,
242    Update = 101,
243    Delete = 102,
244}
245
246/// [Discord docs](https://discord.com/developers/docs/resources/audit-log#audit-log-entry-object-audit-log-events).
247#[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
248#[derive(Clone, Copy, Debug)]
249#[non_exhaustive]
250#[repr(u8)]
251pub enum ThreadAction {
252    Create = 110,
253    Update = 111,
254    Delete = 112,
255}
256
257/// [Discord docs](https://discord.com/developers/docs/resources/audit-log#audit-log-entry-object-audit-log-events).
258#[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
259#[derive(Copy, Clone, Debug)]
260#[non_exhaustive]
261#[repr(u8)]
262pub enum AutoModAction {
263    RuleCreate = 140,
264    RuleUpdate = 141,
265    RuleDelete = 142,
266    BlockMessage = 143,
267    FlagToChannel = 144,
268    UserCommunicationDisabled = 145,
269}
270
271/// [Discord docs](https://discord.com/developers/docs/resources/audit-log#audit-log-entry-object-audit-log-events).
272#[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
273#[derive(Copy, Clone, Debug)]
274#[non_exhaustive]
275#[repr(u8)]
276pub enum CreatorMonetizationAction {
277    RequestCreated = 150,
278    TermsAccepted = 151,
279}
280
281/// [Incomplete documentation](https://github.com/discord/discord-api-docs/pull/6398)
282#[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
283#[derive(Copy, Clone, Debug)]
284#[non_exhaustive]
285#[repr(u8)]
286pub enum VoiceChannelStatusAction {
287    StatusUpdate = 192,
288    StatusDelete = 193,
289}
290
291/// [Discord docs](https://discord.com/developers/docs/resources/audit-log#audit-log-object).
292#[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
293#[derive(Debug, Deserialize, Serialize)]
294#[non_exhaustive]
295pub struct AuditLogs {
296    /// List of audit log entries, sorted from most to least recent.
297    #[serde(rename = "audit_log_entries")]
298    pub entries: Vec<AuditLogEntry>,
299    /// List of auto moderation rules referenced in the audit log.
300    pub auto_moderation_rules: Vec<Rule>,
301    /// List of application commands referenced in the audit log.
302    pub application_commands: Vec<Command>,
303    /// List of guild scheduled events referenced in the audit log.
304    pub guild_scheduled_events: Vec<ScheduledEvent>,
305    /// List of partial integration objects.
306    pub integrations: Vec<PartialIntegration>,
307    /// List of threads referenced in the audit log.
308    ///
309    /// Threads referenced in THREAD_CREATE and THREAD_UPDATE events are included in the threads
310    /// map since archived threads might not be kept in memory by clients.
311    pub threads: Vec<GuildChannel>,
312    /// List of users referenced in the audit log.
313    #[serde(with = "users")]
314    pub users: HashMap<UserId, User>,
315    /// List of webhooks referenced in the audit log.
316    #[serde(with = "webhooks")]
317    pub webhooks: HashMap<WebhookId, Webhook>,
318}
319
320/// Partial version of [`Integration`], used in [`AuditLogs::integrations`].
321///
322/// [Discord docs](https://discord.com/developers/docs/resources/audit-log#audit-log-object-example-partial-integration-object).
323#[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
324#[derive(Clone, Debug, Deserialize, Serialize)]
325#[non_exhaustive]
326pub struct PartialIntegration {
327    pub id: IntegrationId,
328    pub name: String,
329    #[serde(rename = "type")]
330    pub kind: String,
331    pub account: IntegrationAccount,
332    pub application: Option<IntegrationApplication>,
333}
334
335/// [Discord docs](https://discord.com/developers/docs/resources/audit-log#audit-log-entry-object).
336#[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
337#[derive(Debug, Deserialize, Serialize, Clone)]
338#[non_exhaustive]
339pub struct AuditLogEntry {
340    /// Determines to what entity an [`Self::action`] was used on.
341    pub target_id: Option<GenericId>,
342    /// Determines what action was done on a [`Self::target_id`]
343    #[serde(rename = "action_type")]
344    pub action: Action,
345    /// What was the reasoning by doing an action on a target? If there was one.
346    pub reason: Option<String>,
347    /// The user that did this action on a target.
348    pub user_id: UserId,
349    /// What changes were made.
350    pub changes: Option<Vec<Change>>,
351    /// The id of this entry.
352    pub id: AuditLogEntryId,
353    /// Some optional data associated with this entry.
354    pub options: Option<Options>,
355}
356
357/// [Discord docs](https://discord.com/developers/docs/resources/audit-log#audit-log-entry-object-optional-audit-entry-info).
358// TODO: should be renamed to a less ambiguous name
359#[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
360#[derive(Debug, Deserialize, Serialize, Clone)]
361#[non_exhaustive]
362pub struct Options {
363    /// Name of the Auto Moderation rule that was triggered.
364    pub auto_moderation_rule_name: Option<String>,
365    /// Trigger type of the Auto Moderation rule that was triggered.
366    pub auto_moderation_rule_trigger_type: Option<String>,
367    /// ID of the app whose permissions were targeted.
368    pub application_id: Option<ApplicationId>,
369    /// Number of days after which inactive members were kicked.
370    #[serde(default, with = "optional_string")]
371    pub delete_member_days: Option<u64>,
372    /// Number of members removed by the prune
373    #[serde(default, with = "optional_string")]
374    pub members_removed: Option<u64>,
375    /// Channel in which the messages were deleted
376    #[serde(default)]
377    pub channel_id: Option<ChannelId>,
378    /// Number of deleted messages.
379    #[serde(default, with = "optional_string")]
380    pub count: Option<u64>,
381    /// Id of the overwritten entity
382    #[serde(default)]
383    pub id: Option<GenericId>,
384    /// Type of overwritten entity ("member" or "role").
385    #[serde(default, rename = "type")]
386    pub kind: Option<String>,
387    /// Message that was pinned or unpinned.
388    #[serde(default)]
389    pub message_id: Option<MessageId>,
390    /// Name of the role if type is "role"
391    #[serde(default)]
392    pub role_name: Option<String>,
393    /// The status of a voice channel when set.
394    #[serde(default)]
395    pub status: Option<String>,
396}
397
398#[cfg(test)]
399mod tests {
400    use super::*;
401
402    #[test]
403    fn action_value() {
404        macro_rules! assert_action {
405            ($action:pat, $num:literal) => {{
406                let a = Action::from_value($num);
407                assert!(matches!(a, $action), "{:?} didn't match the variant", a);
408                assert_eq!(a.num(), $num);
409            }};
410        }
411
412        assert_action!(Action::GuildUpdate, 1);
413        assert_action!(Action::Channel(ChannelAction::Create), 10);
414        assert_action!(Action::Channel(ChannelAction::Update), 11);
415        assert_action!(Action::Channel(ChannelAction::Delete), 12);
416        assert_action!(Action::ChannelOverwrite(ChannelOverwriteAction::Create), 13);
417        assert_action!(Action::ChannelOverwrite(ChannelOverwriteAction::Update), 14);
418        assert_action!(Action::ChannelOverwrite(ChannelOverwriteAction::Delete), 15);
419        assert_action!(Action::Member(MemberAction::Kick), 20);
420        assert_action!(Action::Member(MemberAction::Prune), 21);
421        assert_action!(Action::Member(MemberAction::BanAdd), 22);
422        assert_action!(Action::Member(MemberAction::BanRemove), 23);
423        assert_action!(Action::Member(MemberAction::Update), 24);
424        assert_action!(Action::Member(MemberAction::RoleUpdate), 25);
425        assert_action!(Action::Member(MemberAction::MemberMove), 26);
426        assert_action!(Action::Member(MemberAction::MemberDisconnect), 27);
427        assert_action!(Action::Member(MemberAction::BotAdd), 28);
428        assert_action!(Action::Role(RoleAction::Create), 30);
429        assert_action!(Action::Role(RoleAction::Update), 31);
430        assert_action!(Action::Role(RoleAction::Delete), 32);
431        assert_action!(Action::Invite(InviteAction::Create), 40);
432        assert_action!(Action::Invite(InviteAction::Update), 41);
433        assert_action!(Action::Invite(InviteAction::Delete), 42);
434        assert_action!(Action::Webhook(WebhookAction::Create), 50);
435        assert_action!(Action::Webhook(WebhookAction::Update), 51);
436        assert_action!(Action::Webhook(WebhookAction::Delete), 52);
437        assert_action!(Action::Emoji(EmojiAction::Create), 60);
438        assert_action!(Action::Emoji(EmojiAction::Update), 61);
439        assert_action!(Action::Emoji(EmojiAction::Delete), 62);
440        assert_action!(Action::Message(MessageAction::Delete), 72);
441        assert_action!(Action::Message(MessageAction::BulkDelete), 73);
442        assert_action!(Action::Message(MessageAction::Pin), 74);
443        assert_action!(Action::Message(MessageAction::Unpin), 75);
444        assert_action!(Action::Integration(IntegrationAction::Create), 80);
445        assert_action!(Action::Integration(IntegrationAction::Update), 81);
446        assert_action!(Action::Integration(IntegrationAction::Delete), 82);
447        assert_action!(Action::StageInstance(StageInstanceAction::Create), 83);
448        assert_action!(Action::StageInstance(StageInstanceAction::Update), 84);
449        assert_action!(Action::StageInstance(StageInstanceAction::Delete), 85);
450        assert_action!(Action::Sticker(StickerAction::Create), 90);
451        assert_action!(Action::Sticker(StickerAction::Update), 91);
452        assert_action!(Action::Sticker(StickerAction::Delete), 92);
453        assert_action!(Action::ScheduledEvent(ScheduledEventAction::Create), 100);
454        assert_action!(Action::ScheduledEvent(ScheduledEventAction::Update), 101);
455        assert_action!(Action::ScheduledEvent(ScheduledEventAction::Delete), 102);
456        assert_action!(Action::Thread(ThreadAction::Create), 110);
457        assert_action!(Action::Thread(ThreadAction::Update), 111);
458        assert_action!(Action::Thread(ThreadAction::Delete), 112);
459        assert_action!(Action::AutoMod(AutoModAction::RuleCreate), 140);
460        assert_action!(Action::AutoMod(AutoModAction::RuleUpdate), 141);
461        assert_action!(Action::AutoMod(AutoModAction::RuleDelete), 142);
462        assert_action!(Action::AutoMod(AutoModAction::BlockMessage), 143);
463        assert_action!(Action::AutoMod(AutoModAction::FlagToChannel), 144);
464        assert_action!(Action::AutoMod(AutoModAction::UserCommunicationDisabled), 145);
465        assert_action!(Action::CreatorMonetization(CreatorMonetizationAction::RequestCreated), 150);
466        assert_action!(Action::CreatorMonetization(CreatorMonetizationAction::TermsAccepted), 151);
467        assert_action!(Action::VoiceChannelStatus(VoiceChannelStatusAction::StatusUpdate), 192);
468        assert_action!(Action::VoiceChannelStatus(VoiceChannelStatusAction::StatusDelete), 193);
469        assert_action!(Action::Unknown(234), 234);
470    }
471
472    #[test]
473    fn action_serde() {
474        use crate::json::{self, json};
475
476        #[derive(Debug, Deserialize, Serialize)]
477        struct T {
478            action: Action,
479        }
480
481        let value = json!({
482            "action": 234,
483        });
484
485        let value = json::from_value::<T>(value).unwrap();
486        assert_eq!(value.action.num(), 234);
487
488        assert!(matches!(value.action, Action::Unknown(234)));
489    }
490}