aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authoracdenisSK <[email protected]>2017-07-20 03:47:13 +0200
committeracdenisSK <[email protected]>2017-07-20 03:48:28 +0200
commit6a101c4a409ae3abe4038f96dcd51f0788d4c0e4 (patch)
tree505c7dc6e4735c82dc9d01c0b83146b30c75da50 /src
parentFix tests (diff)
downloadserenity-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.rs10
-rw-r--r--src/http/ratelimiting.rs5
-rw-r--r--src/model/guild/audit_log.rs230
-rw-r--r--src/model/guild/guild_id.rs6
-rw-r--r--src/model/guild/mod.rs10
-rw-r--r--src/model/mod.rs2
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.