diff options
| author | acdenisSK <[email protected]> | 2017-07-20 03:47:13 +0200 |
|---|---|---|
| committer | acdenisSK <[email protected]> | 2017-07-20 03:48:28 +0200 |
| commit | 6a101c4a409ae3abe4038f96dcd51f0788d4c0e4 (patch) | |
| tree | 505c7dc6e4735c82dc9d01c0b83146b30c75da50 /src | |
| parent | Fix tests (diff) | |
| download | serenity-6a101c4a409ae3abe4038f96dcd51f0788d4c0e4.tar.xz serenity-6a101c4a409ae3abe4038f96dcd51f0788d4c0e4.zip | |
Add an actual way to fetch audit log entries from a guild
Diffstat (limited to 'src')
| -rw-r--r-- | src/http/mod.rs | 10 | ||||
| -rw-r--r-- | src/http/ratelimiting.rs | 5 | ||||
| -rw-r--r-- | src/model/guild/audit_log.rs | 230 | ||||
| -rw-r--r-- | src/model/guild/guild_id.rs | 6 | ||||
| -rw-r--r-- | src/model/guild/mod.rs | 10 | ||||
| -rw-r--r-- | src/model/mod.rs | 2 |
6 files changed, 263 insertions, 0 deletions
diff --git a/src/http/mod.rs b/src/http/mod.rs index 781e64a..850f800 100644 --- a/src/http/mod.rs +++ b/src/http/mod.rs @@ -887,6 +887,16 @@ pub fn get_bans(guild_id: u64) -> Result<Vec<Ban>> { serde_json::from_reader::<HyperResponse, Vec<Ban>>(response).map_err(From::from) } +/// Gets all audit logs in a specific guild. +pub fn get_audit_logs(guild_id: u64) -> Result<AuditLogs> { + let response = request!(Route::GuildsIdAuditLogs(guild_id), + get, + "/guilds/{}/audit-logs", + guild_id); + + serde_json::from_reader::<HyperResponse, AuditLogs>(response).map_err(From::from) +} + /// Gets current bot gateway. pub fn get_bot_gateway() -> Result<BotGateway> { let response = request!(Route::GatewayBot, get, "/gateway/bot"); diff --git a/src/http/ratelimiting.rs b/src/http/ratelimiting.rs index a674537..a5b7032 100644 --- a/src/http/ratelimiting.rs +++ b/src/http/ratelimiting.rs @@ -207,6 +207,11 @@ pub enum Route { /// /// [`GuildId`]: struct.GuildId.html GuildsIdBans(u64), + /// Route for the `/guilds/:guild_id/audit-logs` path. + /// The data is the relevant [`GuildId`]. + /// + /// [`GuildId`]: struct.GuildId.html + GuildsIdAuditLogs(u64), /// Route for the `/guilds/:guild_id/bans/:user_id` path. /// /// The data is the relevant [`GuildId`]. diff --git a/src/model/guild/audit_log.rs b/src/model/guild/audit_log.rs new file mode 100644 index 0000000..43e4b16 --- /dev/null +++ b/src/model/guild/audit_log.rs @@ -0,0 +1,230 @@ +use super::super::{UserId, AuditLogEntryId}; +use serde::de::{self, Deserialize, Deserializer, Visitor, MapAccess}; +use std::fmt; +use std::collections::HashMap; + +/// Determines to what entity an action was used on. +pub enum Target { + Guild, + Channel, + User, + Role, + Invite, + Webhook, + Emoji, +} + +/// Determines what the action was done on a target. +pub enum Action { + GuildUpdate, + ChannelCreate, + ChannelUpdate, + ChannelDelete, + ChannelOverwriteCreate, + ChannelOverwriteUpdate, + ChannelOverwriteDelete, + MemberKick, + MemberPrune, + MemberBanAdd, + MemberBanRemove, + MemberUpdate, + MemberRoleUpdate, + RoleCreate, + RoleUpdate, + RoleDelete, + InviteCreate, + InviteUpdate, + InviteDelete, + WebhookCreate, + WebhookUpdate, + WebhookDelete, + EmojiCreate, + EmojiUpdate, + EmojiDelete, +} + +#[derive(Deserialize)] +pub struct Change { + #[serde(rename="key")] + pub name: String, + #[serde(rename="old_value")] + pub old: String, + #[serde(rename="new_value")] + pub new: String, +} + +pub struct AuditLogs { + pub entries: HashMap<AuditLogEntryId, AuditLogEntry>, +} + +#[derive(Deserialize)] +pub struct AuditLogEntry { + /// Determines to what entity an [`action`] was used on. + /// + /// [`action`]: #structfield.action + #[serde(deserialize_with="deserialize_target", rename="target_type")] + pub target: Target, + /// Determines what action was done on a [`target`] + /// + /// [`target`]: #structfield.target + #[serde(deserialize_with="deserialize_action", rename="action_type")] + pub action: Action, + /// What was the reasoning by doing an action on a target? If there was one. + pub reason: Option<String>, + /// The user that did this action on a target. + pub user_id: UserId, + /// What changes were made. + pub changes: Vec<Change>, + /// The id of this entry. + pub id: AuditLogEntryId, + +} + +fn deserialize_target<'de, D: Deserializer<'de>>(de: D) -> Result<Target, D::Error> { + struct TargetVisitor; + + impl<'de> Visitor<'de> for TargetVisitor { + type Value = Target; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("an integer between 0 to 70") + } + + fn visit_i32<E: de::Error>(self, value: i32) -> Result<Target, E> { + Ok(if value < 10 { + Target::Guild + } else if value < 20 { + Target::Channel + } else if value < 30 { + Target::User + } else if value < 40 { + Target::Role + } else if value < 50 { + Target::Invite + } else if value < 60 { + Target::Webhook + } else if value < 70 { + Target::Emoji + } else { + return Err(E::custom(format!("Unexpected target number: {}", value))); + }) + } + } + + de.deserialize_i32(TargetVisitor) +} + +fn deserialize_action<'de, D: Deserializer<'de>>(de: D) -> Result<Action, D::Error> { + struct ActionVisitor; + + impl<'de> Visitor<'de> for ActionVisitor { + type Value = Action; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("an integer between 1 to 62") + } + + fn visit_i32<E: de::Error>(self, value: i32) -> Result<Action, E> { + // todo: improve this + Ok(if value == 1 { + Action::GuildUpdate + } else if value == 10 { + Action::ChannelCreate + } else if value == 11 { + Action::ChannelUpdate + } else if value == 12 { + Action::ChannelDelete + } else if value == 13 { + Action::ChannelOverwriteCreate + } else if value == 14 { + Action::ChannelOverwriteUpdate + } else if value == 15 { + Action::ChannelOverwriteDelete + } else if value == 20 { + Action::MemberKick + } else if value == 21 { + Action::MemberPrune + } else if value == 22 { + Action::MemberBanAdd + } else if value == 23 { + Action::MemberBanRemove + } else if value == 24 { + Action::MemberUpdate + } else if value == 25 { + Action::MemberRoleUpdate + } else if value == 30 { + Action::RoleCreate + } else if value == 31 { + Action::RoleUpdate + } else if value == 32 { + Action::RoleDelete + } else if value == 40 { + Action::InviteCreate + } else if value == 41 { + Action::InviteUpdate + } else if value == 42 { + Action::InviteDelete + } else if value == 50 { + Action::WebhookCreate + } else if value == 51 { + Action::WebhookUpdate + } else if value == 52 { + Action::WebhookDelete + } else if value == 60 { + Action::EmojiCreate + } else if value == 61 { + Action::EmojiUpdate + } else if value == 62 { + Action::EmojiDelete + } else { + return Err(E::custom(format!("Unexpected action number: {}", value))); + }) + } + } + + de.deserialize_i32(ActionVisitor) +} + +impl<'de> Deserialize<'de> for AuditLogs { + fn deserialize<D: Deserializer<'de>>(de: D) -> Result<Self, D::Error> { + #[derive(Deserialize)] + #[serde(field_identifier)] + enum Field { + #[serde(rename="audit_log_entries")] + AuditLogEntries + } + + struct EntriesVisitor; + + impl<'de> Visitor<'de> for EntriesVisitor { + type Value = AuditLogs; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("audit log entries") + } + + fn visit_map<V: MapAccess<'de>>(self, mut map: V) -> Result<AuditLogs, V::Error> { + let mut audit_log_entries = None; + + while let Some(key) = map.next_key()? { + match key { + Field::AuditLogEntries => { + if audit_log_entries.is_some() { + return Err(de::Error::duplicate_field("audit_log_entries")); + } + + audit_log_entries = Some(map.next_value()?); + } + } + } + + let entries: Vec<AuditLogEntry> = audit_log_entries.ok_or_else(|| de::Error::missing_field("audit_log_entries"))?; + + Ok(AuditLogs { entries: entries.into_iter().map(|entry| (entry.id, entry)).collect() }) + } + } + + const FIELD: &'static [&'static str] = &["audit_log_entries"]; + de.deserialize_struct("AuditLogs", FIELD, EntriesVisitor) + } +}
\ No newline at end of file diff --git a/src/model/guild/guild_id.rs b/src/model/guild/guild_id.rs index 5d318a5..6fbaddf 100644 --- a/src/model/guild/guild_id.rs +++ b/src/model/guild/guild_id.rs @@ -72,6 +72,12 @@ impl GuildId { http::get_bans(self.0) } + /// Gets a list of the guild's audit log entries + #[inline] + pub fn audit_logs(&self) -> Result<AuditLogs> { + http::get_audit_logs(self.0) + } + /// Gets all of the guild's channels over the REST API. /// /// [`Guild`]: struct.Guild.html diff --git a/src/model/guild/mod.rs b/src/model/guild/mod.rs index fb91654..17d88e6 100644 --- a/src/model/guild/mod.rs +++ b/src/model/guild/mod.rs @@ -5,6 +5,7 @@ mod integration; mod member; mod partial_guild; mod role; +mod audit_log; pub use self::emoji::*; pub use self::feature::*; @@ -13,6 +14,7 @@ pub use self::integration::*; pub use self::member::*; pub use self::partial_guild::*; pub use self::role::*; +pub use self::audit_log::*; use chrono::{DateTime, FixedOffset}; use serde::de::Error as DeError; @@ -206,6 +208,14 @@ impl Guild { self.id.bans() } + /// Retrieves a list of [`AuditLogs`] for the guild. + /// + /// [`AuditLogs`]: audit_log/struct.AuditLogs.html + #[inline] + pub fn audit_logs(&self) -> Result<AuditLogs> { + self.id.audit_logs() + } + /// Gets all of the guild's channels over the REST API. /// /// [`Guild`]: struct.Guild.html diff --git a/src/model/mod.rs b/src/model/mod.rs index 92e533e..84cd7bf 100644 --- a/src/model/mod.rs +++ b/src/model/mod.rs @@ -105,6 +105,8 @@ id_u64! { UserId; /// An identifier for a [`Webhook`](struct.Webhook.html). WebhookId; + /// An identifier for an audit log entry. + AuditLogEntryId; } /// A container for guilds. |