Repository: serenity-rs/serenity (language: rust)

Issue:
`GuildAuditLogEntryCreate` has `Unknown` changes field when action is `Member(RoleUpdate)`
Serenity version: `0.12.0`

Rust version (`rustc -V`): `rustc 1.74.0 (79e9716c9 2023-11-13)`

This bug can be replicated by implementing this event handler:
```rs
#[async_trait]
impl EventHandler for Handler {
    async fn guild_audit_log_entry_create(&self, ctx: Context, entry: AuditLogEntry, guild_id: GuildId) {
        println!("{:#?}", entry);
    }
}
```
for a basic bot with administrator privilege and all gateway intents (including all privileged intents) enabled.
Assigning someone a role results in this entry being printed:
```
AuditLogEntry {
    target_id: Some(
        GenericId(
            940020670115512390,
        ),
    ),
    action: Member(
        RoleUpdate,
    ),
    reason: None,
    user_id: UserId(
        940020670115512390,
    ),
    changes: Some(
        [
            Unknown,
        ],
    ),
    id: AuditLogEntryId(
        1188383715492102316,
    ),
    options: None,
}
```

In this case, we have information about the who, but no information about what role, or whether it was assigned or removed. 


Discussion / hints:

Fix patch:
diff --git a/src/model/guild/audit_log/change.rs b/src/model/guild/audit_log/change.rs
index b08bf39f822..c57ab2bee29 100644
--- a/src/model/guild/audit_log/change.rs
+++ b/src/model/guild/audit_log/change.rs
@@ -56,6 +56,29 @@ macro_rules! generate_change {
                 },
             )*
 
+            /* These changes are special because their variant names do not match their keys. */
+
+            /// Role was added to a member.
+            #[serde(rename = "$add")]
+            RolesAdded {
+                #[serde(skip_serializing_if = "Option::is_none")]
+                #[serde(rename = "old_value")]
+                old: Option<Vec<AffectedRole>>,
+                #[serde(skip_serializing_if = "Option::is_none")]
+                #[serde(rename = "new_value")]
+                new: Option<Vec<AffectedRole>>,
+            },
+            /// Role was removed to a member.
+            #[serde(rename = "$remove")]
+            RolesRemove {
+                #[serde(skip_serializing_if = "Option::is_none")]
+                #[serde(rename = "old_value")]
+                old: Option<Vec<AffectedRole>>,
+                #[serde(skip_serializing_if = "Option::is_none")]
+                #[serde(rename = "new_value")]
+                new: Option<Vec<AffectedRole>>,
+            },
+
             /// Unknown key was changed.
             Other {
                 name: String,
@@ -77,6 +100,8 @@ macro_rules! generate_change {
             pub fn key(&self) -> &str {
                 match self {
                     $( Self::$name { .. } => $key, )*
+                    Self::RolesAdded { .. } => "$add",
+                    Self::RolesRemove { .. } => "$remove",
                     Self::Other { name, .. } => name,
                     Self::Unknown => "unknown",
                 }
@@ -200,10 +225,6 @@ generate_change! {
     "rate_limit_per_user" => RateLimitPerUser(u16),
     /// Region of a guild was changed.
     "region" => Region(String),
-    /// Role was added to a member.
-    "add" => RolesAdded(Vec<AffectedRole>),
-    /// Role was removed to a member.
-    "remove" => RolesRemove(Vec<AffectedRole>),
     /// ID of the rules channel was changed.
     "rules_channel_id" => RulesChannelId(ChannelId),
     /// Invite splash page artwork was changed.


Test patch: