serenity/model/guild/audit_log/
change.rs

1use crate::json::Value;
2use crate::model::channel::PermissionOverwrite;
3use crate::model::guild::automod::{Action, EventType, TriggerMetadata, TriggerType};
4use crate::model::guild::{
5    AfkTimeout,
6    DefaultMessageNotificationLevel,
7    ExplicitContentFilter,
8    MfaLevel,
9    SystemChannelFlags,
10    VerificationLevel,
11};
12use crate::model::id::{ApplicationId, ChannelId, GenericId, GuildId, RoleId, UserId};
13use crate::model::misc::ImageHash;
14use crate::model::sticker::StickerFormatType;
15use crate::model::utils::StrOrInt;
16use crate::model::{Permissions, Timestamp};
17
18#[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
19#[derive(Debug, PartialEq, Eq, Deserialize, Serialize, Clone)]
20#[non_exhaustive]
21pub struct AffectedRole {
22    pub id: RoleId,
23    pub name: String,
24}
25
26#[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
27#[derive(Debug, PartialEq, Eq, Serialize, Clone)]
28#[serde(untagged)]
29#[non_exhaustive]
30pub enum EntityType {
31    Int(u64),
32    Str(String),
33}
34
35impl<'de> serde::Deserialize<'de> for EntityType {
36    fn deserialize<D: serde::de::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
37        Ok(StrOrInt::deserialize(deserializer)?.into_enum(Self::Str, Self::Int))
38    }
39}
40
41macro_rules! generate_change {
42    ( $(
43        $( #[doc = $doc:literal] )?
44        $key:literal => $name:ident ($type:ty),
45    )* ) => {
46        #[cfg_attr(not(feature = "simd_json"), allow(clippy::derive_partial_eq_without_eq))]
47        #[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
48        #[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
49        // serde_json's Value impls Eq, simd-json's Value doesn't
50        #[cfg_attr(not(feature = "simd_json"), derive(Eq))]
51        #[non_exhaustive]
52        #[serde(tag = "key")]
53        #[serde(rename_all = "snake_case")]
54        pub enum Change {
55            $(
56                $( #[doc = $doc] )?
57                $name {
58                    #[serde(skip_serializing_if = "Option::is_none")]
59                    #[serde(rename = "old_value")]
60                    old: Option<$type>,
61                    #[serde(skip_serializing_if = "Option::is_none")]
62                    #[serde(rename = "new_value")]
63                    new: Option<$type>,
64                },
65            )*
66
67            /* These changes are special because their variant names do not match their keys. */
68
69            /// Role was added to a member.
70            #[serde(rename = "$add")]
71            RolesAdded {
72                #[serde(skip_serializing_if = "Option::is_none")]
73                #[serde(rename = "old_value")]
74                old: Option<Vec<AffectedRole>>,
75                #[serde(skip_serializing_if = "Option::is_none")]
76                #[serde(rename = "new_value")]
77                new: Option<Vec<AffectedRole>>,
78            },
79            /// Role was removed to a member.
80            #[serde(rename = "$remove")]
81            RolesRemove {
82                #[serde(skip_serializing_if = "Option::is_none")]
83                #[serde(rename = "old_value")]
84                old: Option<Vec<AffectedRole>>,
85                #[serde(skip_serializing_if = "Option::is_none")]
86                #[serde(rename = "new_value")]
87                new: Option<Vec<AffectedRole>>,
88            },
89
90            /// Unknown key was changed.
91            Other {
92                name: String,
93                #[serde(skip_serializing_if = "Option::is_none")]
94                #[serde(rename = "old_value")]
95                old_value: Option<Value>,
96                #[serde(skip_serializing_if = "Option::is_none")]
97                #[serde(rename = "new_value")]
98                new_value: Option<Value>,
99            },
100
101            /// Unknown key was changed and was invalid
102            #[serde(other)]
103            Unknown
104        }
105
106        impl Change {
107            #[must_use]
108            pub fn key(&self) -> &str {
109                match self {
110                    $( Self::$name { .. } => $key, )*
111                    Self::RolesAdded { .. } => "$add",
112                    Self::RolesRemove { .. } => "$remove",
113                    Self::Other { name, .. } => name,
114                    Self::Unknown => "unknown",
115                }
116            }
117        }
118    };
119}
120
121generate_change! {
122    "actions" => Actions(Vec<Action>),
123    /// AFK channel was changed.
124    "afk_channel_id" => AfkChannelId(ChannelId),
125    /// AFK timeout duration was changed.
126    "afk_timeout" => AfkTimeout(AfkTimeout),
127    /// Permission on a text or voice channel was allowed for a role.
128    "allow" => Allow(Permissions),
129    /// Application ID of the added or removed webhook or bot.
130    "application_id" => ApplicationId(ApplicationId),
131    /// Thread is now archived/unarchived.
132    "archived" => Archived(bool),
133    "asset" => Asset(String),
134    /// Auto archive duration of a thread was changed.
135    "auto_archive_duration" => AutoArchiveDuration(u16),
136    /// Availability of a sticker was changed.
137    "available" => Available(bool),
138    /// User avatar was changed.
139    "avatar_hash" => AvatarHash(ImageHash),
140    /// Guild banner was changed.
141    "banner_hash" => BannerHash(ImageHash),
142    /// Voice channel bitrate was changed.
143    "bitrate" => Bitrate(u32),
144    /// Channel for invite code or guild scheduled event was changed.
145    "channel_id" => ChannelId(ChannelId),
146    /// Invite code was changed.
147    "code" => Code(String),
148    /// Role color was changed.
149    "color" => Color(u32),
150    /// Member timeout state was changed.
151    "communication_disabled_until" => CommunicationDisabledUntil(Timestamp),
152    /// User was server deafened/undeafened.
153    "deaf" => Deaf(bool),
154    /// Default auto archive duration for newly created threads was changed.
155    "default_auto_archive_duration" => DefaultAutoArchiveDuration(u16),
156    /// Default message notification level for a server was changed.
157    "default_message_notifications" => DefaultMessageNotifications(DefaultMessageNotificationLevel),
158    /// Permission on a text or voice channel was denied for a role.
159    "deny" => Deny(Permissions),
160    /// Description for guild, sticker, or guild scheduled event was changed.
161    "description" => Description(String),
162    /// Guild's discovery splash was changed.
163    "discovery_splash_hash" => DiscoverySplashHash(ImageHash),
164    "enabled" => Enabled(bool),
165    /// Integration emoticons was enabled/disabled.
166    "enable_emoticons" => EnableEmoticons(bool),
167    /// Entity type of guild scheduled event was changed.
168    "entity_type" => EntityType(u64),
169    "event_type" => EventType(EventType),
170    "exempt_channels" => ExemptChannels(Vec<ChannelId>),
171    "exempt_roles" => ExemptRoles(Vec<RoleId>),
172    /// Behavior of the expiration of an integration was changed.
173    "expire_behavior" => ExpireBehavior(u64),
174    /// Grace period of the expiration of an integration was changed.
175    "expire_grace_period" => ExpireGracePeriod(u64),
176    /// Explicit content filter level of a guild was changed.
177    "explicit_content_filter" => ExplicitContentFilter(ExplicitContentFilter),
178    /// Unknown but sent by discord
179    "flags" => Flags(u64),
180    /// Format type of a sticker was changed.
181    "format_type" => FormatType(StickerFormatType),
182    /// Guild a sticker is in was changed.
183    "guild_id" => GuildId(GuildId),
184    /// Role is now displayed/no longer displayed separate from online users.
185    "hoist" => Hoist(bool),
186    /// Guild icon was changed.
187    "icon_hash" => IconHash(ImageHash),
188    /// Guild scheduled event cover image was changed.
189    "id" => Id(GenericId),
190    /// ID of the changed entity.
191    "image_hash" => ImageHash(ImageHash),
192    /// Private thread's invitable state was changed.
193    "invitable" => Invitable(bool),
194    /// ID of the user who created the invite.
195    "inviter_id" => InviterId(UserId),
196    /// Location for a guild scheduled event was changed.
197    "location" => Location(String),
198    /// Thread was locked/unlocked.
199    "locked" => Locked(bool),
200    /// How long invite code lasts was changed.
201    "max_age" => MaxAge(u32),
202    /// Maximum uses of an invite was changed.
203    "max_uses" => MaxUses(u8),
204    /// Whether a role can be mentioned in a message was changed.
205    "mentionable" => Mentionable(bool),
206    /// Multi-factor authentication requirement was changed.
207    "mfa_level" => MfaLevel(MfaLevel),
208    /// User was server muted/unmuted.
209    "mute" => Mute(bool),
210    /// Name of an entity was changed.
211    "name" => Name(String),
212    /// Nickname of a member was changed.
213    "nick" => Nick(String),
214    /// Channel NSFW restriction was changed.
215    "nsfw" => Nsfw(bool),
216    /// Owner of a guild was changed.
217    "owner_id" => OwnerId(UserId),
218    /// Permissions on a channel were changed.
219    "permission_overwrites" => PermissionOverwrites(Vec<PermissionOverwrite>),
220    /// Permissions for a role were changed.
221    "permissions" => Permissions(Permissions),
222    /// Channel or role position was changed.
223    "position" => Position(u32),
224    /// Preferred locale of a guild was changed.
225    "preferred_locale" => PreferredLocale(String),
226    /// Privacy level of the stage instance was changed.
227    "privacy_level" => PrivacyLevel(u64),
228    /// Number of days after which inactive and role-unassigned members are kicked was changed.
229    "prune_delete_days" => PruneDeleteDays(u64),
230    /// ID of the public updates channel was changed.
231    "public_updates_channel_id" => PublicUpdatesChannelId(ChannelId),
232    /// Ratelimit per user in a text channel was changed.
233    "rate_limit_per_user" => RateLimitPerUser(u16),
234    /// Region of a guild was changed.
235    "region" => Region(String),
236    /// ID of the rules channel was changed.
237    "rules_channel_id" => RulesChannelId(ChannelId),
238    /// Invite splash page artwork was changed.
239    "splash_hash" => SplashHash(ImageHash),
240    /// Status of guild scheduled event was changed.
241    "status" => Status(u64),
242    /// System channel settings were changed.
243    "system_channel_flags" => SystemChannelFlags(SystemChannelFlags),
244    /// ID of the system channel was changed.
245    "system_channel_id" => SystemChannelId(ChannelId),
246    /// Related emoji of a sticker was changed.
247    "tags" => Tags(String),
248    /// Whether an invite is temporary or never expires was changed.
249    "temporary" => Temporary(bool),
250    /// Topic of a text channel or stage instance was changed.
251    "topic" => Topic(String),
252    "trigger_metadata" => TriggerMetadata(TriggerMetadata),
253    "trigger_type" => TriggerType(TriggerType),
254    /// Type of a created entity.
255    "type" => Type(EntityType),
256    /// Unicode emoji of a role icon was changed.
257    "unicode_emoji" => UnicodeEmoji(String),
258    /// Maximum number of users in a voice channel was changed.
259    "user_limit" => UserLimit(u64),
260    /// Number of uses of an invite was changed.
261    "uses" => Uses(u64),
262    /// Guild invite vanity url was changed.
263    "vanity_url_code" => VanityUrlCode(String),
264    /// Required verification level for new members was changed.
265    "verification_level" => VerificationLevel(VerificationLevel),
266    /// Channel of the server widget was changed.
267    "widget_channel_id" => WidgetChannelId(ChannelId),
268    /// Whether a widget is enabled or not was changed.
269    "widget_enabled" => WidgetEnabled(bool),
270}
271
272#[cfg(test)]
273mod tests {
274    use super::*;
275    use crate::json::{assert_json, json};
276
277    #[test]
278    fn afk_channel_id_variant() {
279        let value = Change::AfkChannelId {
280            old: Some(ChannelId::new(1)),
281            new: Some(ChannelId::new(2)),
282        };
283        assert_json(&value, json!({"key": "afk_channel_id", "old_value": "1", "new_value": "2"}));
284    }
285
286    #[test]
287    fn skip_serializing_if_none() {
288        let value = Change::AfkChannelId {
289            old: None,
290            new: Some(ChannelId::new(2)),
291        };
292        assert_json(&value, json!({"key": "afk_channel_id", "new_value": "2"}));
293        let value = Change::AfkChannelId {
294            old: Some(ChannelId::new(1)),
295            new: None,
296        };
297        assert_json(&value, json!({"key": "afk_channel_id", "old_value": "1"}));
298    }
299
300    #[test]
301    fn entity_type_variant() {
302        let value = Change::Type {
303            old: Some(EntityType::Int(123)),
304            new: Some(EntityType::Str("discord".into())),
305        };
306        assert_json(&value, json!({"key": "type", "old_value": 123, "new_value": "discord"}));
307    }
308
309    #[test]
310    fn permissions_variant() {
311        let value = Change::Permissions {
312            old: Some(Permissions::default()),
313            new: Some(Permissions::MANAGE_GUILD),
314        };
315        assert_json(&value, json!({"key": "permissions", "old_value": "0", "new_value": "32"}));
316    }
317
318    #[test]
319    fn system_channels() {
320        let value = Change::SystemChannelFlags {
321            old: Some(
322                SystemChannelFlags::SUPPRESS_GUILD_REMINDER_NOTIFICATIONS
323                    | SystemChannelFlags::SUPPRESS_JOIN_NOTIFICATION_REPLIES,
324            ),
325            new: Some(
326                SystemChannelFlags::SUPPRESS_GUILD_REMINDER_NOTIFICATIONS
327                    | SystemChannelFlags::SUPPRESS_JOIN_NOTIFICATION_REPLIES
328                    | SystemChannelFlags::SUPPRESS_JOIN_NOTIFICATIONS,
329            ),
330        };
331        assert_json(
332            &value,
333            json!({"key": "system_channel_flags", "old_value": 12, "new_value": 13 }),
334        );
335    }
336}