From 018d62905ef51764c992c52c064d024ebf796d18 Mon Sep 17 00:00:00 2001 From: James Whaley Date: Sat, 4 Jan 2020 13:38:53 +0000 Subject: [PATCH 01/44] Split Emoji and PartialEmoji into separate files --- src/model/guild/emoji/mod.rs | 126 ++++++++++++++++ .../guild/{emoji.rs => emoji/partial.rs} | 138 ++++-------------- 2 files changed, 153 insertions(+), 111 deletions(-) create mode 100644 src/model/guild/emoji/mod.rs rename src/model/guild/{emoji.rs => emoji/partial.rs} (71%) diff --git a/src/model/guild/emoji/mod.rs b/src/model/guild/emoji/mod.rs new file mode 100644 index 0000000..f10e6cb --- /dev/null +++ b/src/model/guild/emoji/mod.rs @@ -0,0 +1,126 @@ +mod partial; + +use std::fmt::{self, Display}; + +use serde::{Deserialize, Serialize}; + +use crate::model::user::User; + +pub use self::partial::PartialEmoji; + +/// An emoji. +#[non_exhaustive] +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct Emoji { + /// The ID of the emoji. + #[serde(flatten)] + pub emoji: PartialEmoji, + // TODO: roles + /// The user that created the emoji. + pub user: Option, + /// Whether the name requires colons to be used by a client. + #[serde(default)] + pub require_colons: bool, + /// Whether the emoji was created by an integration service. + #[serde(default)] + pub managed: bool, +} + +impl Display for Emoji { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.emoji.fmt(f) + } +} + +#[cfg(test)] +mod tests { + use serde_json::json; + + use crate::model::id::UserId; + use crate::model::user::Discriminator; + + use super::*; + + macro_rules! assert_eq_fields { + ($left:expr, $right:expr, [$($field:ident),* $(,)*]) => {$( + assert_eq!($left.$field, $right.$field); + )*}; + } + + #[test] + fn test_deserialize_emoji() { + let value = json!({ + "id": "41771983429993937", + "name": "LUL", + "roles": ["41771983429993000", "41771983429993111"], + "user": { + "username": "Luigi", + "discriminator": "0002", + "id": "96008815106887111", + "avatar": "5500909a3274e1812beb4e8de6631111", + }, + "require_colons": true, + "managed": false, + "animated": false, + }); + let emoji = Emoji { + emoji: PartialEmoji::custom(41771983429993937, "LUL", false), + user: Some(User { + id: UserId::from(96008815106887111), + name: "Luigi".to_owned(), + discriminator: Discriminator::new(2).unwrap(), + avatar: Some("5500909a3274e1812beb4e8de6631111".to_owned()), + bot: false, + system: false, + }), + require_colons: true, + managed: false, + }; + + let deserialized: Emoji = serde_json::from_value(value).unwrap(); + + assert_eq_fields!(emoji, deserialized, [emoji, require_colons, managed]); + assert_eq_fields!( + emoji.user.as_ref().unwrap(), + deserialized.user.as_ref().unwrap(), + [id, name, discriminator, avatar, bot, system] + ); + } + + // TODO: Enable test when `roles` is added to `Emoji`. + #[test] + #[ignore] + fn test_serialize_emoji() { + let value = json!({ + "id": "41771983429993937", + "name": "LUL", + "roles": ["41771983429993000", "41771983429993111"], + "user": { + "username": "Luigi", + "discriminator": "0002", + "id": "96008815106887111", + "avatar": null, + "bot": false, + "system": false, + }, + "require_colons": true, + "managed": false, + "animated": true, + }); + let emoji = Emoji { + emoji: PartialEmoji::custom(41771983429993937, "LUL", true), + user: Some(User { + id: UserId::from(96008815106887111), + name: "Luigi".to_owned(), + discriminator: Discriminator::new(2).unwrap(), + avatar: None, + bot: false, + system: false, + }), + require_colons: true, + managed: false, + }; + + assert_eq!(value, serde_json::to_value(emoji).unwrap()); + } +} diff --git a/src/model/guild/emoji.rs b/src/model/guild/emoji/partial.rs similarity index 71% rename from src/model/guild/emoji.rs rename to src/model/guild/emoji/partial.rs index 54505cc..5ce74ba 100644 --- a/src/model/guild/emoji.rs +++ b/src/model/guild/emoji/partial.rs @@ -1,11 +1,11 @@ use std::fmt::{self, Display}; use std::hash::{Hash, Hasher}; +use std::str::FromStr; use serde::de; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use crate::model::id::EmojiId; -use crate::model::user::User; /// An emoji, with partial information. #[derive(Clone, Debug)] @@ -129,6 +129,32 @@ impl Hash for PartialEmoji { } } +impl From for PartialEmoji { + fn from(ch: char) -> Self { + PartialEmoji::Standard(ch.to_string()) + } +} + +impl From for PartialEmoji { + fn from(s: String) -> Self { + PartialEmoji::Standard(s) + } +} + +impl<'a> From<&'a str> for PartialEmoji { + fn from(s: &'a str) -> Self { + PartialEmoji::Standard(s.to_owned()) + } +} + +impl FromStr for PartialEmoji { + type Err = std::convert::Infallible; + + fn from_str(s: &str) -> Result { + Ok(PartialEmoji::from(s)) + } +} + impl Serialize for PartialEmoji { fn serialize(&self, serializer: S) -> Result where @@ -227,45 +253,12 @@ impl<'de> Deserialize<'de> for PartialEmoji { } } -/// An emoji. -#[non_exhaustive] -#[derive(Clone, Debug, Deserialize, Serialize)] -pub struct Emoji { - /// The ID of the emoji. - #[serde(flatten)] - pub emoji: PartialEmoji, - // TODO: roles - /// The user that created the emoji. - pub user: Option, - /// Whether the name requires colons to be used by a client. - #[serde(default)] - pub require_colons: bool, - /// Whether the emoji was created by an integration service. - #[serde(default)] - pub managed: bool, -} - -impl Display for Emoji { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - self.emoji.fmt(f) - } -} - #[cfg(test)] mod tests { use serde_json::json; - use crate::model::id::UserId; - use crate::model::user::Discriminator; - use super::*; - macro_rules! assert_eq_fields { - ($left:expr, $right:expr, [$($field:ident),* $(,)*]) => {$( - assert_eq!($left.$field, $right.$field); - )*}; - } - #[test] fn test_deserialize_standard() { let value = json!({ @@ -310,81 +303,4 @@ mod tests { assert_eq!(value, serde_json::to_value(emoji).unwrap()); } - - #[test] - fn test_deserialize_emoji() { - let value = json!({ - "id": "41771983429993937", - "name": "LUL", - "roles": ["41771983429993000", "41771983429993111"], - "user": { - "username": "Luigi", - "discriminator": "0002", - "id": "96008815106887111", - "avatar": "5500909a3274e1812beb4e8de6631111", - }, - "require_colons": true, - "managed": false, - "animated": false, - }); - let emoji = Emoji { - emoji: PartialEmoji::custom(41771983429993937, "LUL", false), - user: Some(User { - id: UserId::from(96008815106887111), - name: "Luigi".to_owned(), - discriminator: Discriminator::new(2).unwrap(), - avatar: Some("5500909a3274e1812beb4e8de6631111".to_owned()), - bot: false, - system: false, - }), - require_colons: true, - managed: false, - }; - - let deserialized: Emoji = serde_json::from_value(value).unwrap(); - - assert_eq_fields!(emoji, deserialized, [emoji, require_colons, managed]); - assert_eq_fields!( - emoji.user.as_ref().unwrap(), - deserialized.user.as_ref().unwrap(), - [id, name, discriminator, avatar, bot, system] - ); - } - - // TODO: Enable test when `roles` is added to `Emoji`. - #[test] - #[ignore] - fn test_serialize_emoji() { - let value = json!({ - "id": "41771983429993937", - "name": "LUL", - "roles": ["41771983429993000", "41771983429993111"], - "user": { - "username": "Luigi", - "discriminator": "0002", - "id": "96008815106887111", - "avatar": null, - "bot": false, - "system": false, - }, - "require_colons": true, - "managed": false, - "animated": true, - }); - let emoji = Emoji { - emoji: PartialEmoji::custom(41771983429993937, "LUL", true), - user: Some(User { - id: UserId::from(96008815106887111), - name: "Luigi".to_owned(), - discriminator: Discriminator::new(2).unwrap(), - avatar: None, - bot: false, - system: false, - }), - require_colons: true, - managed: false, - }; - - assert_eq!(value, serde_json::to_value(emoji).unwrap()); - } } From 6ef7b8196cbef16c00f6b12fbd6ca4ce851d0398 Mon Sep 17 00:00:00 2001 From: James Whaley Date: Sat, 4 Jan 2020 16:32:44 +0000 Subject: [PATCH 02/44] Start work on Channel models --- src/model/channel/dm_channel.rs | 9 +++++++++ src/model/channel/mod.rs | 29 +++++++++++++++++++++++++++-- 2 files changed, 36 insertions(+), 2 deletions(-) create mode 100644 src/model/channel/dm_channel.rs diff --git a/src/model/channel/dm_channel.rs b/src/model/channel/dm_channel.rs new file mode 100644 index 0000000..6086d59 --- /dev/null +++ b/src/model/channel/dm_channel.rs @@ -0,0 +1,9 @@ +use serde::{Deserialize, Serialize}; + +/// A direct message channel between the [`ClientUser`] and another [`User`]. +/// +/// [`ClientUser`]: ../../user/struct.ClientUser.html +/// [`User`]: ../../user/struct.User.html +#[non_exhaustive] +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct DMChannel {} diff --git a/src/model/channel/mod.rs b/src/model/channel/mod.rs index 4e33812..6399967 100644 --- a/src/model/channel/mod.rs +++ b/src/model/channel/mod.rs @@ -1,6 +1,7 @@ //! Models related to channels. mod attachment; +mod dm_channel; mod embed; mod message; mod permission_overwrite; @@ -11,6 +12,7 @@ use serde::{Deserialize, Serialize}; use crate::model::guild::PartialEmoji; pub use self::attachment::Attachment; +pub use self::dm_channel::DMChannel; pub use self::embed::{ Embed, EmbedAuthor, EmbedField, EmbedFooter, EmbedImage, EmbedProvider, EmbedThumbnail, EmbedType, EmbedVideo, @@ -21,6 +23,29 @@ pub use self::message::{ pub use self::permission_overwrite::{OverwriteId, PermissionOverwrite}; pub use self::rich_presence::{MessageActivity, MessageActivityType, MessageApplication}; +/// A channel in Discord. +#[non_exhaustive] +#[remain::sorted] +#[derive(Clone, Debug)] +pub enum Channel { + /// A direct message channel between the [`ClientUser`] and another + /// [`User`]. + /// + /// [`ClientUser`]: ../user/struct.ClientUser.html + /// [`User`]: ../user/struct.User.html + DM(DMChannel), + /// A group message channel between multiple [`User`]s. + /// + /// [`User`]: ../user/struct.User.html + Group, // TODO: Add GroupChannel. + /// A channel within a [`Guild`]. + /// + /// [`Guild`]: TODO + Guild, // TODO: Add GuildChannel. +} + +// TODO: Implement Deserialize and Serialize for Channel based in ChannelType. + /// The type of a channel. #[non_exhaustive] #[int_enum::int_enum(u8)] @@ -28,11 +53,11 @@ pub use self::rich_presence::{MessageActivity, MessageActivityType, MessageAppli pub enum ChannelType { /// A text channel in a guild. Text = 0, - /// A private message channel between 2 users. + /// A direct message channel between the client user and another user. Private = 1, /// A voice channel in a guild. Voice = 2, - /// A group private message channel between multiple users. + /// A group message channel between multiple users. Group = 3, /// An organizational category that contains non-category channels. Category = 4, From 1be521d7a7b0891ccf6ecded0d1b9a6ca4cc8318 Mon Sep 17 00:00:00 2001 From: James Whaley Date: Sun, 5 Jan 2020 16:15:08 +0000 Subject: [PATCH 03/44] Add DMChannel model --- src/model/channel/dm_channel.rs | 145 +++++++++++++++++++++++++++++++- 1 file changed, 144 insertions(+), 1 deletion(-) diff --git a/src/model/channel/dm_channel.rs b/src/model/channel/dm_channel.rs index 6086d59..b4d374a 100644 --- a/src/model/channel/dm_channel.rs +++ b/src/model/channel/dm_channel.rs @@ -1,9 +1,152 @@ +use chrono::{DateTime, FixedOffset}; use serde::{Deserialize, Serialize}; +use crate::model::channel::ChannelType; +use crate::model::id::{ChannelId, MessageId}; +use crate::model::user::User; + /// A direct message channel between the [`ClientUser`] and another [`User`]. /// /// [`ClientUser`]: ../../user/struct.ClientUser.html /// [`User`]: ../../user/struct.User.html #[non_exhaustive] #[derive(Clone, Debug, Deserialize, Serialize)] -pub struct DMChannel {} +pub struct DMChannel { + /// The ID of the channel. + pub id: ChannelId, + /// The type of the channel. + #[serde(rename = "type")] + pub kind: ChannelType, + /// The recipient to the direct message channel. + #[serde(rename = "recipients", with = "serde_recipient")] + pub recipient: User, + /// The ID of the last message sent to the channel. + pub last_message_id: Option, + /// When the last message with pinned. + pub last_pin_timestamp: Option>, +} + +mod serde_recipient { + use serde::ser::SerializeTuple; + use serde::{Deserialize, Deserializer, Serializer}; + + use crate::model::user::User; + + pub fn deserialize<'de, D>(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let [recipient] = <[User; 1]>::deserialize(deserializer)?; + Ok(recipient) + } + + pub fn serialize(recipient: &User, serializer: S) -> Result + where + S: Serializer, + { + let mut tuple = serializer.serialize_tuple(1)?; + tuple.serialize_element(recipient)?; + tuple.end() + } +} + +#[cfg(test)] +mod tests { + use chrono::TimeZone; + use serde_json::json; + + use crate::model::id::UserId; + use crate::model::user::Discriminator; + + use super::*; + + macro_rules! assert_eq_fields { + ($left:expr, $right:expr, [$($field:ident),* $(,)*]) => {$( + assert_eq!($left.$field, $right.$field); + )*}; + } + + #[test] + fn test_deserialize() { + let value = json!({ + "last_message_id": "3343820033257021450", + "type": 1, + "id": "319674150115610528", + "recipients": [ + { + "username": "test", + "discriminator": "9999", + "id": "82198898841029460", + "avatar": "33ecab261d4681afa4d85a04691c4a01" + } + ] + }); + let channel = DMChannel { + id: ChannelId::from(319674150115610528), + kind: ChannelType::Private, + recipient: User { + id: UserId::from(82198898841029460), + name: "test".to_owned(), + discriminator: Discriminator::new(9999).unwrap(), + avatar: Some("33ecab261d4681afa4d85a04691c4a01".to_owned()), + bot: false, + system: false, + }, + last_message_id: Some(MessageId::from(3343820033257021450)), + last_pin_timestamp: None, + }; + + let deserialized: DMChannel = serde_json::from_value(value).unwrap(); + + assert_eq_fields!( + channel, + deserialized, + [id, kind, last_message_id, last_pin_timestamp] + ); + assert_eq_fields!( + channel.recipient, + deserialized.recipient, + [id, name, discriminator, avatar, bot, system] + ); + } + + #[test] + fn test_serialize() { + let value = json!({ + "id": "550383196278358041", + "last_message_id": "663411909835620352", + "last_pin_timestamp": "2020-01-05T16:02:04.179+00:00", + "type": 1, + "recipients": [ + { + "id": "225336713231204353", + "username": "Juici", + "avatar": "a_e8b3a198dab6af59aacd1072bbedb255", + "discriminator": "0001", + "bot": false, + "system": false + } + ] + }); + let channel = DMChannel { + id: ChannelId::from(550383196278358041), + kind: ChannelType::Private, + recipient: User { + id: UserId::from(225336713231204353), + name: "Juici".to_owned(), + discriminator: Discriminator::new(1).unwrap(), + avatar: Some("a_e8b3a198dab6af59aacd1072bbedb255".to_owned()), + bot: false, + system: false, + }, + last_message_id: Some(MessageId::from(663411909835620352)), + last_pin_timestamp: Some( + FixedOffset::east(0) + .ymd(2020, 1, 5) + .and_hms_milli(16, 2, 4, 179), + ), + }; + + assert_eq!(value, serde_json::to_value(&channel).unwrap()); + } +} From 312af16fc1519e32a4c49b6948c4ffd5768d83b7 Mon Sep 17 00:00:00 2001 From: James Whaley Date: Sun, 5 Jan 2020 16:22:52 +0000 Subject: [PATCH 04/44] Start work on GroupChannel model --- src/model/channel/group_channel.rs | 8 ++++++++ src/model/channel/mod.rs | 4 +++- 2 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 src/model/channel/group_channel.rs diff --git a/src/model/channel/group_channel.rs b/src/model/channel/group_channel.rs new file mode 100644 index 0000000..c812100 --- /dev/null +++ b/src/model/channel/group_channel.rs @@ -0,0 +1,8 @@ +use serde::{Deserialize, Serialize}; + +/// A group message channel between multiple [`User`]s. +/// +/// [`User`]: ../../user/struct.User.html +#[non_exhaustive] +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct GroupChannel {} diff --git a/src/model/channel/mod.rs b/src/model/channel/mod.rs index 6399967..8a7891a 100644 --- a/src/model/channel/mod.rs +++ b/src/model/channel/mod.rs @@ -3,6 +3,7 @@ mod attachment; mod dm_channel; mod embed; +mod group_channel; mod message; mod permission_overwrite; mod rich_presence; @@ -17,6 +18,7 @@ pub use self::embed::{ Embed, EmbedAuthor, EmbedField, EmbedFooter, EmbedImage, EmbedProvider, EmbedThumbnail, EmbedType, EmbedVideo, }; +pub use self::group_channel::GroupChannel; pub use self::message::{ MentionedChannel, MentionedUser, Message, MessageFlags, MessageReference, MessageType, }; @@ -37,7 +39,7 @@ pub enum Channel { /// A group message channel between multiple [`User`]s. /// /// [`User`]: ../user/struct.User.html - Group, // TODO: Add GroupChannel. + Group(GroupChannel), /// A channel within a [`Guild`]. /// /// [`Guild`]: TODO From c9264eb8eacaf6e851f7ca9c3d177c3bac1fdaf0 Mon Sep 17 00:00:00 2001 From: James Whaley Date: Sun, 5 Jan 2020 19:52:23 +0000 Subject: [PATCH 05/44] Improve docs for DMChannel --- src/model/channel/dm_channel.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/model/channel/dm_channel.rs b/src/model/channel/dm_channel.rs index b4d374a..8be8ea2 100644 --- a/src/model/channel/dm_channel.rs +++ b/src/model/channel/dm_channel.rs @@ -15,6 +15,10 @@ pub struct DMChannel { /// The ID of the channel. pub id: ChannelId, /// The type of the channel. + /// + /// This should always be [`ChannelType::Private`]. + /// + /// [`ChannelType::Private`]: ../enum.ChannelType.html#variant.Private #[serde(rename = "type")] pub kind: ChannelType, /// The recipient to the direct message channel. From c11715acf15f80e2e6544f6c60a9e33a7fb4b2dc Mon Sep 17 00:00:00 2001 From: James Whaley Date: Mon, 6 Jan 2020 17:42:38 +0000 Subject: [PATCH 06/44] Clean up duplicate assert_eq_fields macros --- src/internal/macros.rs | 7 +++++++ src/model/channel/dm_channel.rs | 6 ------ src/model/guild/emoji/mod.rs | 6 ------ src/model/user/mod.rs | 6 ------ 4 files changed, 7 insertions(+), 18 deletions(-) diff --git a/src/internal/macros.rs b/src/internal/macros.rs index bc6e408..3217b78 100644 --- a/src/internal/macros.rs +++ b/src/internal/macros.rs @@ -114,6 +114,13 @@ macro_rules! wrap { }; } +#[cfg(test)] +macro_rules! assert_eq_fields { + ($left:expr, $right:expr, [$($field:ident),* $(,)*]) => {$( + assert_eq!($left.$field, $right.$field); + )*}; +} + #[cfg(test)] mod tests { const ID: u64 = 80351110224678912; diff --git a/src/model/channel/dm_channel.rs b/src/model/channel/dm_channel.rs index 8be8ea2..172605f 100644 --- a/src/model/channel/dm_channel.rs +++ b/src/model/channel/dm_channel.rs @@ -64,12 +64,6 @@ mod tests { use super::*; - macro_rules! assert_eq_fields { - ($left:expr, $right:expr, [$($field:ident),* $(,)*]) => {$( - assert_eq!($left.$field, $right.$field); - )*}; - } - #[test] fn test_deserialize() { let value = json!({ diff --git a/src/model/guild/emoji/mod.rs b/src/model/guild/emoji/mod.rs index f10e6cb..c354094 100644 --- a/src/model/guild/emoji/mod.rs +++ b/src/model/guild/emoji/mod.rs @@ -41,12 +41,6 @@ mod tests { use super::*; - macro_rules! assert_eq_fields { - ($left:expr, $right:expr, [$($field:ident),* $(,)*]) => {$( - assert_eq!($left.$field, $right.$field); - )*}; - } - #[test] fn test_deserialize_emoji() { let value = json!({ diff --git a/src/model/user/mod.rs b/src/model/user/mod.rs index baffbdf..730309e 100644 --- a/src/model/user/mod.rs +++ b/src/model/user/mod.rs @@ -79,12 +79,6 @@ mod tests { use super::*; - macro_rules! assert_eq_fields { - ($left:expr, $right:expr, [$($field:ident),* $(,)*]) => {$( - assert_eq!($left.$field, $right.$field); - )*}; - } - #[test] fn test_deserialize_user() { let value = json!({ From f2a22ddd15a8b93cab1bd5f6dfaac78935d755bf Mon Sep 17 00:00:00 2001 From: James Whaley Date: Mon, 6 Jan 2020 17:43:02 +0000 Subject: [PATCH 07/44] Add GroupChannel model --- src/model/channel/group_channel.rs | 271 ++++++++++++++++++++++++++++- 1 file changed, 270 insertions(+), 1 deletion(-) diff --git a/src/model/channel/group_channel.rs b/src/model/channel/group_channel.rs index c812100..03ee442 100644 --- a/src/model/channel/group_channel.rs +++ b/src/model/channel/group_channel.rs @@ -1,8 +1,277 @@ +use std::collections::HashMap; + +use chrono::{DateTime, FixedOffset}; use serde::{Deserialize, Serialize}; +use crate::model::channel::ChannelType; +use crate::model::id::{ChannelId, MessageId, UserId}; +use crate::model::user::User; + /// A group message channel between multiple [`User`]s. /// /// [`User`]: ../../user/struct.User.html #[non_exhaustive] #[derive(Clone, Debug, Deserialize, Serialize)] -pub struct GroupChannel {} +pub struct GroupChannel { + /// The ID of the group channel. + pub id: ChannelId, + /// The type of the channel. + /// + /// This should always be [`ChannelType::Group`]. + /// + /// [`ChannelType::Group`]: ../enum.ChannelType.html#variant.Group + #[serde(rename = "type")] + pub kind: ChannelType, + /// The name of the group. + pub name: Option, + /// The group icon hash. + pub icon: Option, + /// The users in the group. + #[serde(default, with = "serde_recipients")] + pub recipients: HashMap, + /// The ID of the group owner. + pub owner_id: UserId, + /// The ID of the last message sent to the channel. + pub last_message_id: Option, + /// When the last message with pinned. + pub last_pin_timestamp: Option>, +} + +impl GroupChannel { + /// Returns a reference to the owner of the group. + /// + /// # Notes + /// + /// If there is no user in `recipients` with the same ID as `owner_id`, the + /// group is malformed and this function will return `None`. + pub fn owner(&self) -> Option<&User> { + self.recipients.get(&self.owner_id) + } +} + +mod serde_recipients { + use std::collections::HashMap; + use std::fmt; + + use serde::de; + use serde::{Deserializer, Serializer}; + + use crate::model::id::UserId; + use crate::model::user::User; + + pub fn deserialize<'de, D>(deserializer: D) -> Result, D::Error> + where + D: Deserializer<'de>, + { + struct Visitor; + + impl<'de> de::Visitor<'de> for Visitor { + type Value = HashMap; + + fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_str("a sequence of users") + } + + fn visit_seq(self, mut seq: A) -> Result + where + A: de::SeqAccess<'de>, + { + let mut map = HashMap::with_capacity(seq.size_hint().unwrap_or_default()); + + while let Some(user) = seq.next_element::()? { + if let Some(existing) = map.insert(user.id, user) { + return Err(de::Error::custom(format_args!( + "duplicate recipient user: {}", + existing.id + ))); + } + } + + Ok(map) + } + } + + deserializer.deserialize_seq(Visitor) + } + + pub fn serialize( + recipients: &HashMap, + serializer: S, + ) -> Result + where + S: Serializer, + { + serializer.collect_seq(recipients.values()) + } +} + +#[cfg(test)] +mod tests { + use std::iter::FromIterator; + + use serde_json::json; + + use crate::model::user::Discriminator; + + use super::*; + + #[test] + fn test_deserialize() { + let value = json!({ + "name": "Some test channel", + "icon": null, + "recipients": [ + { + "username": "test", + "discriminator": "9999", + "id": "82198898841029460", + "avatar": "33ecab261d4681afa4d85a04691c4a01" + }, + { + "username": "test2", + "discriminator": "9999", + "id": "53908099506183680", + "avatar": "a_bab14f271d565501444b2ca3be944b25" + } + ], + "last_message_id": "3343820033257021450", + "type": 3, + "id": "319674150115710528", + "owner_id": "82198810841029460" + }); + let channel = GroupChannel { + id: ChannelId::from(319674150115710528), + kind: ChannelType::Group, + name: Some("Some test channel".to_owned()), + icon: None, + recipients: HashMap::from_iter(vec![ + ( + UserId::from(82198898841029460), + User { + id: UserId::from(82198898841029460), + name: "test".to_string(), + discriminator: Discriminator::new(9999).unwrap(), + avatar: Some("33ecab261d4681afa4d85a04691c4a01".to_owned()), + bot: false, + system: false, + }, + ), + ( + UserId::from(53908099506183680), + User { + id: UserId::from(53908099506183680), + name: "test2".to_string(), + discriminator: Discriminator::new(9999).unwrap(), + avatar: Some("a_bab14f271d565501444b2ca3be944b25".to_owned()), + bot: false, + system: false, + }, + ), + ]), + owner_id: UserId::from(82198810841029460), + last_message_id: Some(MessageId::from(3343820033257021450)), + last_pin_timestamp: None, + }; + + let deserialized: GroupChannel = serde_json::from_value(value).unwrap(); + + assert_eq_fields!( + channel, + deserialized, + [ + id, + kind, + name, + icon, + owner_id, + last_message_id, + last_pin_timestamp + ] + ); + + assert_eq!(channel.recipients.len(), deserialized.recipients.len()); + for (id, user) in channel.recipients.iter() { + let de_user = deserialized.recipients.get(id).unwrap(); + assert_eq_fields!( + user, + de_user, + [id, name, discriminator, avatar, bot, system] + ); + } + } + + #[test] + fn test_serialize() { + let mut value = json!({ + "name": "Some test channel", + "icon": null, + "recipients": [ + { + "username": "test", + "discriminator": "9999", + "id": "82198898841029460", + "avatar": "33ecab261d4681afa4d85a04691c4a01", + "bot": false, + "system": false + }, + { + "username": "test2", + "discriminator": "9999", + "id": "53908099506183680", + "avatar": "a_bab14f271d565501444b2ca3be944b25", + "bot": false, + "system": false + } + ], + "last_message_id": "3343820033257021450", + "last_pin_timestamp": null, + "type": 3, + "id": "319674150115710528", + "owner_id": "53908099506183680" + }); + let channel = GroupChannel { + id: ChannelId::from(319674150115710528), + kind: ChannelType::Group, + name: Some("Some test channel".to_owned()), + icon: None, + recipients: HashMap::from_iter(vec![ + ( + UserId::from(53908099506183680), + User { + id: UserId::from(53908099506183680), + name: "test2".to_string(), + discriminator: Discriminator::new(9999).unwrap(), + avatar: Some("a_bab14f271d565501444b2ca3be944b25".to_owned()), + bot: false, + system: false, + }, + ), + ( + UserId::from(82198898841029460), + User { + id: UserId::from(82198898841029460), + name: "test".to_string(), + discriminator: Discriminator::new(9999).unwrap(), + avatar: Some("33ecab261d4681afa4d85a04691c4a01".to_owned()), + bot: false, + system: false, + }, + ), + ]), + owner_id: UserId::from(53908099506183680), + last_message_id: Some(MessageId::from(3343820033257021450)), + last_pin_timestamp: None, + }; + + let mut serialized = serde_json::to_value(&channel).unwrap(); + + // Stable sort of recipients for `assert_eq`. + fn sort_recipients(vec: &mut Vec) { + vec.sort_by(|a, b| a["id"].as_str().unwrap().cmp(&b["id"].as_str().unwrap())) + } + sort_recipients(value["recipients"].as_array_mut().unwrap()); + sort_recipients(serialized["recipients"].as_array_mut().unwrap()); + + assert_eq!(value, serialized); + } +} From 942b5f5acf704b7d9b61f2d93ab6c017577e37ed Mon Sep 17 00:00:00 2001 From: James Whaley Date: Tue, 7 Jan 2020 16:47:23 +0000 Subject: [PATCH 08/44] Clean up assert_eq_fields code using traits Fixes #3. --- src/internal/macros.rs | 7 -- src/internal/mod.rs | 4 + src/internal/test.rs | 156 +++++++++++++++++++++++++++++ src/model/channel/dm_channel.rs | 13 +-- src/model/channel/group_channel.rs | 34 ++----- src/model/guild/emoji/mod.rs | 9 +- src/model/user/mod.rs | 43 ++------ 7 files changed, 183 insertions(+), 83 deletions(-) create mode 100644 src/internal/test.rs diff --git a/src/internal/macros.rs b/src/internal/macros.rs index 3217b78..bc6e408 100644 --- a/src/internal/macros.rs +++ b/src/internal/macros.rs @@ -114,13 +114,6 @@ macro_rules! wrap { }; } -#[cfg(test)] -macro_rules! assert_eq_fields { - ($left:expr, $right:expr, [$($field:ident),* $(,)*]) => {$( - assert_eq!($left.$field, $right.$field); - )*}; -} - #[cfg(test)] mod tests { const ID: u64 = 80351110224678912; diff --git a/src/internal/mod.rs b/src/internal/mod.rs index 4f90afa..77bb946 100644 --- a/src/internal/mod.rs +++ b/src/internal/mod.rs @@ -3,6 +3,10 @@ mod macros; pub mod prelude; +#[macro_use] +#[doc(hidden)] +pub mod test; + /// From core::fmt::num [impl_Display] macro. /// /// [impl_Display]: https://doc.rust-lang.org/src/core/fmt/num.rs.html#192-238 diff --git a/src/internal/test.rs b/src/internal/test.rs new file mode 100644 index 0000000..c68d512 --- /dev/null +++ b/src/internal/test.rs @@ -0,0 +1,156 @@ +#[cfg(test)] +#[macro_use] +mod inner { + pub trait DelegateEqFields { + fn eq_fields(&self, other: &Self); + } + + impl DelegateEqFields for T + where + T: std::fmt::Debug + PartialEq, + { + fn eq_fields(&self, other: &Self) { + assert_eq!(self, other); + } + } + + pub trait EqFields: std::fmt::Debug + where + Rhs: std::fmt::Debug, + { + fn eq_fields(&self, other: &Rhs); + } + + impl EqFields<&B> for &A + where + A: EqFields, + B: std::fmt::Debug, + { + fn eq_fields(&self, other: &&B) { + EqFields::eq_fields(*self, *other); + } + } + + impl EqFields<&mut B> for &mut A + where + A: EqFields, + B: std::fmt::Debug, + { + fn eq_fields(&self, other: &&mut B) { + EqFields::eq_fields(*self, *other); + } + } + + impl EqFields<&B> for &mut A + where + A: EqFields, + B: std::fmt::Debug, + { + fn eq_fields(&self, other: &&B) { + EqFields::eq_fields(*self, *other); + } + } + + impl EqFields<&mut B> for &A + where + A: EqFields, + B: std::fmt::Debug, + { + fn eq_fields(&self, other: &&mut B) { + EqFields::eq_fields(*self, *other); + } + } + + impl EqFields> for Option + where + A: EqFields, + B: std::fmt::Debug, + { + fn eq_fields(&self, other: &Option) { + match (self, other) { + (Some(left_val), Some(right_val)) => EqFields::eq_fields(left_val, right_val), + (left_val, right_val) => panic!( + r#"assertion failed: `(left == right) by fields` + left: `{:?}`, + right: `{:?}`"#, + &*left_val, &*right_val + ), + } + } + } + + impl EqFields> for Result + where + A: EqFields, + B: std::fmt::Debug, + Err: std::fmt::Debug, + { + fn eq_fields(&self, other: &Result) { + match (self, other) { + (Ok(left_val), Ok(right_val)) => EqFields::eq_fields(left_val, right_val), + (left_val, right_val) => panic!( + r#"assertion failed: `(left == right) by fields` + left: `{:?}`, + right: `{:?}`"#, + &*left_val, &*right_val + ), + } + } + } + + macro_rules! assert_eq_fields { + ($left:expr, $right:expr) => { + match (&$left, &$right) { + (left_val, right_val) => { + // Use a hack with trait method resolution to allow pseudo-specialization. + #[allow(unused_imports)] + use $crate::internal::test::{EqFields, DelegateEqFields}; + + left_val.eq_fields(right_val); + } + } + }; + ($left:expr, $right:expr, [$($field:ident),* $(,)*]) => { + $( + assert_eq_fields!($left.$field, $right.$field); + )* + }; + } +} + +#[cfg(test)] +#[doc(hidden)] +pub use self::inner::*; + +#[doc(hidden)] +macro_rules! __impl_eq_fields { + ($name:ident: ($a:ident, $b:ident) => { $($inner:tt)* }) => { + #[cfg(test)] + const _: () = { + use $crate::internal::test::EqFields; + + impl EqFields for $name { + fn eq_fields(&self, other: &Self) { + match (self, other) { + ($a, $b) => { $($inner)* } + } + } + } + }; + }; +} + +macro_rules! impl_eq_fields { + ($name:ident: ($a:ident, $b:ident) => { $($inner:tt)* }) => { + __impl_eq_fields!($name: ($a, $b) => { + $($inner)* + }); + }; + ($name:ident: [$($field:ident),* $(,)*]) => { + __impl_eq_fields!($name: (a, b) => { + $( + assert_eq_fields!(a.$field, b.$field); + )* + }); + } +} diff --git a/src/model/channel/dm_channel.rs b/src/model/channel/dm_channel.rs index 172605f..2974010 100644 --- a/src/model/channel/dm_channel.rs +++ b/src/model/channel/dm_channel.rs @@ -54,6 +54,8 @@ mod serde_recipient { } } +impl_eq_fields!(DMChannel: [id, kind, recipient, last_message_id, last_pin_timestamp]); + #[cfg(test)] mod tests { use chrono::TimeZone; @@ -96,16 +98,7 @@ mod tests { let deserialized: DMChannel = serde_json::from_value(value).unwrap(); - assert_eq_fields!( - channel, - deserialized, - [id, kind, last_message_id, last_pin_timestamp] - ); - assert_eq_fields!( - channel.recipient, - deserialized.recipient, - [id, name, discriminator, avatar, bot, system] - ); + assert_eq_fields!(channel, deserialized); } #[test] diff --git a/src/model/channel/group_channel.rs b/src/model/channel/group_channel.rs index 03ee442..b379e19 100644 --- a/src/model/channel/group_channel.rs +++ b/src/model/channel/group_channel.rs @@ -105,6 +105,16 @@ mod serde_recipients { } } +impl_eq_fields!(GroupChannel: (a, b) => { + assert_eq_fields!(a, b, [id, kind, name, icon, owner_id, last_message_id, last_pin_timestamp]); + + assert_eq!(a.recipients.len(), b.recipients.len()); + for (id, a_user) in a.recipients.iter() { + let b_user = b.recipients.get(id).expect(&format!("missing user with id: {}", id)); + assert_eq_fields!(a_user, b_user); + } +}); + #[cfg(test)] mod tests { use std::iter::FromIterator; @@ -175,29 +185,7 @@ mod tests { let deserialized: GroupChannel = serde_json::from_value(value).unwrap(); - assert_eq_fields!( - channel, - deserialized, - [ - id, - kind, - name, - icon, - owner_id, - last_message_id, - last_pin_timestamp - ] - ); - - assert_eq!(channel.recipients.len(), deserialized.recipients.len()); - for (id, user) in channel.recipients.iter() { - let de_user = deserialized.recipients.get(id).unwrap(); - assert_eq_fields!( - user, - de_user, - [id, name, discriminator, avatar, bot, system] - ); - } + assert_eq_fields!(channel, deserialized); } #[test] diff --git a/src/model/guild/emoji/mod.rs b/src/model/guild/emoji/mod.rs index c354094..3216a66 100644 --- a/src/model/guild/emoji/mod.rs +++ b/src/model/guild/emoji/mod.rs @@ -32,6 +32,8 @@ impl Display for Emoji { } } +impl_eq_fields!(Emoji: [emoji, user, require_colons, managed]); + #[cfg(test)] mod tests { use serde_json::json; @@ -73,12 +75,7 @@ mod tests { let deserialized: Emoji = serde_json::from_value(value).unwrap(); - assert_eq_fields!(emoji, deserialized, [emoji, require_colons, managed]); - assert_eq_fields!( - emoji.user.as_ref().unwrap(), - deserialized.user.as_ref().unwrap(), - [id, name, discriminator, avatar, bot, system] - ); + assert_eq_fields!(emoji, deserialized); } // TODO: Enable test when `roles` is added to `Emoji`. diff --git a/src/model/user/mod.rs b/src/model/user/mod.rs index 730309e..55b9363 100644 --- a/src/model/user/mod.rs +++ b/src/model/user/mod.rs @@ -73,6 +73,9 @@ pub enum PremiumType { Nitro = 2, } +impl_eq_fields!(ClientUser: [user, mfa_enabled, locale, verified, email, flags, premium_type]); +impl_eq_fields!(User: [id, name, discriminator, avatar, bot, system]); + #[cfg(test)] mod tests { use serde_json::json; @@ -98,7 +101,7 @@ mod tests { let user2: User = serde_json::from_value(value.clone()).unwrap(); - assert_eq_fields!(user, user2, [id, name, discriminator, avatar, bot, system]); + assert_eq_fields!(user, user2); } #[test] @@ -159,24 +162,7 @@ mod tests { let user2: ClientUser = serde_json::from_value(value.clone()).unwrap(); - assert_eq_fields!( - user, - user2, - [ - id, - name, - discriminator, - avatar, - bot, - system, - mfa_enabled, - locale, - verified, - email, - flags, - premium_type, - ] - ); + assert_eq_fields!(user, user2); } #[test] @@ -212,24 +198,7 @@ mod tests { let user2: ClientUser = serde_json::from_value(value.clone()).unwrap(); - assert_eq_fields!( - user, - user2, - [ - id, - name, - discriminator, - avatar, - bot, - system, - mfa_enabled, - locale, - verified, - email, - flags, - premium_type, - ] - ); + assert_eq_fields!(user, user2); } #[test] From 513bc1dee33e273d8b67f2d77fe1cdaeee5ab30b Mon Sep 17 00:00:00 2001 From: James Whaley Date: Wed, 8 Jan 2020 23:19:22 +0000 Subject: [PATCH 09/44] Refactor `recipients` serde modules into `utils` Refactor `serde_recipient` and `serde_recipients` into internal `utils` module, to prevent duplicating code when implementing GuildChannel. --- src/model/channel/dm_channel.rs | 25 +-------- src/model/channel/group_channel.rs | 57 +-------------------- src/model/channel/mod.rs | 3 ++ src/model/channel/utils.rs | 82 ++++++++++++++++++++++++++++++ 4 files changed, 87 insertions(+), 80 deletions(-) create mode 100644 src/model/channel/utils.rs diff --git a/src/model/channel/dm_channel.rs b/src/model/channel/dm_channel.rs index 2974010..2c71701 100644 --- a/src/model/channel/dm_channel.rs +++ b/src/model/channel/dm_channel.rs @@ -1,6 +1,7 @@ use chrono::{DateTime, FixedOffset}; use serde::{Deserialize, Serialize}; +use crate::model::channel::utils::serde_recipient; use crate::model::channel::ChannelType; use crate::model::id::{ChannelId, MessageId}; use crate::model::user::User; @@ -30,30 +31,6 @@ pub struct DMChannel { pub last_pin_timestamp: Option>, } -mod serde_recipient { - use serde::ser::SerializeTuple; - use serde::{Deserialize, Deserializer, Serializer}; - - use crate::model::user::User; - - pub fn deserialize<'de, D>(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - let [recipient] = <[User; 1]>::deserialize(deserializer)?; - Ok(recipient) - } - - pub fn serialize(recipient: &User, serializer: S) -> Result - where - S: Serializer, - { - let mut tuple = serializer.serialize_tuple(1)?; - tuple.serialize_element(recipient)?; - tuple.end() - } -} - impl_eq_fields!(DMChannel: [id, kind, recipient, last_message_id, last_pin_timestamp]); #[cfg(test)] diff --git a/src/model/channel/group_channel.rs b/src/model/channel/group_channel.rs index b379e19..1aedf14 100644 --- a/src/model/channel/group_channel.rs +++ b/src/model/channel/group_channel.rs @@ -3,6 +3,7 @@ use std::collections::HashMap; use chrono::{DateTime, FixedOffset}; use serde::{Deserialize, Serialize}; +use crate::model::channel::utils::serde_recipients; use crate::model::channel::ChannelType; use crate::model::id::{ChannelId, MessageId, UserId}; use crate::model::user::User; @@ -49,62 +50,6 @@ impl GroupChannel { } } -mod serde_recipients { - use std::collections::HashMap; - use std::fmt; - - use serde::de; - use serde::{Deserializer, Serializer}; - - use crate::model::id::UserId; - use crate::model::user::User; - - pub fn deserialize<'de, D>(deserializer: D) -> Result, D::Error> - where - D: Deserializer<'de>, - { - struct Visitor; - - impl<'de> de::Visitor<'de> for Visitor { - type Value = HashMap; - - fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.write_str("a sequence of users") - } - - fn visit_seq(self, mut seq: A) -> Result - where - A: de::SeqAccess<'de>, - { - let mut map = HashMap::with_capacity(seq.size_hint().unwrap_or_default()); - - while let Some(user) = seq.next_element::()? { - if let Some(existing) = map.insert(user.id, user) { - return Err(de::Error::custom(format_args!( - "duplicate recipient user: {}", - existing.id - ))); - } - } - - Ok(map) - } - } - - deserializer.deserialize_seq(Visitor) - } - - pub fn serialize( - recipients: &HashMap, - serializer: S, - ) -> Result - where - S: Serializer, - { - serializer.collect_seq(recipients.values()) - } -} - impl_eq_fields!(GroupChannel: (a, b) => { assert_eq_fields!(a, b, [id, kind, name, icon, owner_id, last_message_id, last_pin_timestamp]); diff --git a/src/model/channel/mod.rs b/src/model/channel/mod.rs index 8a7891a..b38e4c4 100644 --- a/src/model/channel/mod.rs +++ b/src/model/channel/mod.rs @@ -1,5 +1,8 @@ //! Models related to channels. +// Internal utility module. +mod utils; + mod attachment; mod dm_channel; mod embed; diff --git a/src/model/channel/utils.rs b/src/model/channel/utils.rs new file mode 100644 index 0000000..cb28ed5 --- /dev/null +++ b/src/model/channel/utils.rs @@ -0,0 +1,82 @@ +/// Serde mapping functions between `User` sequence (with a known single entry) +/// and `User`. +pub mod serde_recipient { + use serde::ser::SerializeTuple; + use serde::{Deserialize, Deserializer, Serializer}; + + use crate::model::user::User; + + pub fn deserialize<'de, D>(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let [recipient] = <[User; 1]>::deserialize(deserializer)?; + Ok(recipient) + } + + pub fn serialize(recipient: &User, serializer: S) -> Result + where + S: Serializer, + { + let mut tuple = serializer.serialize_tuple(1)?; + tuple.serialize_element(recipient)?; + tuple.end() + } +} + +/// Serde mapping functions between `User` sequence and `HashMap`. +pub mod serde_recipients { + use std::collections::HashMap; + use std::fmt; + + use serde::de; + use serde::{Deserializer, Serializer}; + + use crate::model::id::UserId; + use crate::model::user::User; + + pub fn deserialize<'de, D>(deserializer: D) -> Result, D::Error> + where + D: Deserializer<'de>, + { + struct Visitor; + + impl<'de> de::Visitor<'de> for Visitor { + type Value = HashMap; + + fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_str("a sequence of users") + } + + fn visit_seq(self, mut seq: A) -> Result + where + A: de::SeqAccess<'de>, + { + let mut map = HashMap::with_capacity(seq.size_hint().unwrap_or_default()); + + while let Some(user) = seq.next_element::()? { + if let Some(existing) = map.insert(user.id, user) { + return Err(de::Error::custom(format_args!( + "duplicate recipient user: {}", + existing.id + ))); + } + } + + Ok(map) + } + } + + deserializer.deserialize_seq(Visitor) + } + + pub fn serialize( + recipients: &HashMap, + serializer: S, + ) -> Result + where + S: Serializer, + { + serializer.collect_seq(recipients.values()) + } +} From 5e85fee4f7032e206737985b68101c11fd1b851a Mon Sep 17 00:00:00 2001 From: James Whaley Date: Thu, 9 Jan 2020 10:57:31 +0000 Subject: [PATCH 10/44] Remove use of ref keyword from strife --- src/model/guild/emoji/partial.rs | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/model/guild/emoji/partial.rs b/src/model/guild/emoji/partial.rs index 5ce74ba..8168334 100644 --- a/src/model/guild/emoji/partial.rs +++ b/src/model/guild/emoji/partial.rs @@ -85,14 +85,10 @@ impl Display for PartialEmoji { /// Work around this issue by substituting unknown names with `_`. const UNKNOWN_NAME: &str = "_"; - match *self { - PartialEmoji::Standard(ref name) => f.write_str(name), - PartialEmoji::Custom { - id, - ref name, - animated, - } => { - if animated { + match self { + PartialEmoji::Standard(name) => f.write_str(name), + PartialEmoji::Custom { id, name, animated } => { + if *animated { f.write_str(" Date: Thu, 9 Jan 2020 13:44:56 +0000 Subject: [PATCH 11/44] Implement Deserialize and Serialize for Channel Start work on GuildChannel model. --- src/internal/test.rs | 77 +++++---- src/model/channel/guild_channel/mod.rs | 82 +++++++++ src/model/channel/mod.rs | 230 ++++++++++++++++++++++--- 3 files changed, 326 insertions(+), 63 deletions(-) create mode 100644 src/model/channel/guild_channel/mod.rs diff --git a/src/internal/test.rs b/src/internal/test.rs index c68d512..af28701 100644 --- a/src/internal/test.rs +++ b/src/internal/test.rs @@ -1,17 +1,36 @@ #[cfg(test)] #[macro_use] mod inner { - pub trait DelegateEqFields { - fn eq_fields(&self, other: &Self); + macro_rules! assert_eq_fields { + ($left:expr, $right:expr) => { + match (&$left, &$right) { + (left_val, right_val) => { + // Use a hack with trait method resolution to allow pseudo-specialization. + #[allow(unused_imports)] + use $crate::internal::test::{EqFields, DelegateEqFields}; + + left_val.eq_fields(right_val); + } + } + }; + ($left:expr, $right:expr, [$($field:ident),* $(,)*]) => { + $( + assert_eq_fields!($left.$field, $right.$field); + )* + }; } - impl DelegateEqFields for T - where - T: std::fmt::Debug + PartialEq, - { - fn eq_fields(&self, other: &Self) { - assert_eq!(self, other); - } + macro_rules! panic_ne_fields { + ($left:expr, $right:expr) => { + match (&$left, &$right) { + (left_val, right_val) => panic!( + r#"assertion failed: `(left == right) by fields` + left: `{:?}`, + right: `{:?}`"#, + &*left_val, &*right_val + ), + } + }; } pub trait EqFields: std::fmt::Debug @@ -69,12 +88,7 @@ mod inner { fn eq_fields(&self, other: &Option) { match (self, other) { (Some(left_val), Some(right_val)) => EqFields::eq_fields(left_val, right_val), - (left_val, right_val) => panic!( - r#"assertion failed: `(left == right) by fields` - left: `{:?}`, - right: `{:?}`"#, - &*left_val, &*right_val - ), + (left_val, right_val) => panic_ne_fields!(left_val, right_val), } } } @@ -88,33 +102,22 @@ mod inner { fn eq_fields(&self, other: &Result) { match (self, other) { (Ok(left_val), Ok(right_val)) => EqFields::eq_fields(left_val, right_val), - (left_val, right_val) => panic!( - r#"assertion failed: `(left == right) by fields` - left: `{:?}`, - right: `{:?}`"#, - &*left_val, &*right_val - ), + (left_val, right_val) => panic_ne_fields!(left_val, right_val), } } } - macro_rules! assert_eq_fields { - ($left:expr, $right:expr) => { - match (&$left, &$right) { - (left_val, right_val) => { - // Use a hack with trait method resolution to allow pseudo-specialization. - #[allow(unused_imports)] - use $crate::internal::test::{EqFields, DelegateEqFields}; + pub trait DelegateEqFields { + fn eq_fields(&self, other: &Self); + } - left_val.eq_fields(right_val); - } - } - }; - ($left:expr, $right:expr, [$($field:ident),* $(,)*]) => { - $( - assert_eq_fields!($left.$field, $right.$field); - )* - }; + impl DelegateEqFields for T + where + T: std::fmt::Debug + PartialEq, + { + fn eq_fields(&self, other: &Self) { + assert_eq!(self, other); + } } } diff --git a/src/model/channel/guild_channel/mod.rs b/src/model/channel/guild_channel/mod.rs new file mode 100644 index 0000000..315a281 --- /dev/null +++ b/src/model/channel/guild_channel/mod.rs @@ -0,0 +1,82 @@ +use serde::de; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; + +use crate::model::channel::ChannelType; + +/// A channel in a [`Guild`]. +/// +/// [`Guild`]: TODO +#[non_exhaustive] +#[derive(Clone, Debug)] +pub enum GuildChannel { + /// A text channel in a [`Guild`]. + /// + /// [`Guild`]: TODO + Text, // TODO: Add TextChannel. + /// A voice channel in a [`Guild`]. + /// + /// [`Guild`]: TODO + Voice, // TODO: Add VoiceChannel. + /// An organizational category that contains non-category channels. + Category, // TODO: Add Category. + /// A channel that users can follow and crosspost into another [`Guild`]. + /// + /// [`Guild`]: TODO + News, // TODO: Add NewsChannel. + /// A channel in which game developers can sell games on Discord. + Store, // TODO: Add StoreChannel. +} + +impl GuildChannel { + pub(crate) fn from_value( + kind: ChannelType, + _value: serde_json::Value, + ) -> Result + where + E: de::Error, + { + match kind { + ChannelType::Category => todo!(), + ChannelType::News => todo!(), + ChannelType::Store => todo!(), + ChannelType::Text => todo!(), + ChannelType::Voice => todo!(), + kind => Err(E::custom(format_args!( + "invalid channel type for guild channel: {:?}", + kind + ))), + } + } +} + +impl Serialize for GuildChannel { + fn serialize(&self, _serializer: S) -> Result + where + S: Serializer, + { + match self { + GuildChannel::Category => todo!(), + GuildChannel::News => todo!(), + GuildChannel::Store => todo!(), + GuildChannel::Text => todo!(), + GuildChannel::Voice => todo!(), + } + } +} + +impl<'de> Deserialize<'de> for GuildChannel { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + use serde_json::{Map, Value}; + + let map: Map = Map::deserialize(deserializer)?; + let kind = ChannelType::from_map(&map)?; + + let value = Value::Object(map); + GuildChannel::from_value(kind, value) + } +} + +impl_eq_fields!(GuildChannel: (_a, _b) => { todo!() }); diff --git a/src/model/channel/mod.rs b/src/model/channel/mod.rs index b38e4c4..64ff22e 100644 --- a/src/model/channel/mod.rs +++ b/src/model/channel/mod.rs @@ -7,11 +7,13 @@ mod attachment; mod dm_channel; mod embed; mod group_channel; +mod guild_channel; mod message; mod permission_overwrite; mod rich_presence; -use serde::{Deserialize, Serialize}; +use serde::de; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; use crate::model::guild::PartialEmoji; @@ -22,39 +24,17 @@ pub use self::embed::{ EmbedType, EmbedVideo, }; pub use self::group_channel::GroupChannel; +pub use self::guild_channel::GuildChannel; pub use self::message::{ MentionedChannel, MentionedUser, Message, MessageFlags, MessageReference, MessageType, }; pub use self::permission_overwrite::{OverwriteId, PermissionOverwrite}; pub use self::rich_presence::{MessageActivity, MessageActivityType, MessageApplication}; -/// A channel in Discord. -#[non_exhaustive] -#[remain::sorted] -#[derive(Clone, Debug)] -pub enum Channel { - /// A direct message channel between the [`ClientUser`] and another - /// [`User`]. - /// - /// [`ClientUser`]: ../user/struct.ClientUser.html - /// [`User`]: ../user/struct.User.html - DM(DMChannel), - /// A group message channel between multiple [`User`]s. - /// - /// [`User`]: ../user/struct.User.html - Group(GroupChannel), - /// A channel within a [`Guild`]. - /// - /// [`Guild`]: TODO - Guild, // TODO: Add GuildChannel. -} - -// TODO: Implement Deserialize and Serialize for Channel based in ChannelType. - /// The type of a channel. #[non_exhaustive] #[int_enum::int_enum(u8)] -#[derive(Clone, Copy, Debug, Eq, PartialEq)] +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub enum ChannelType { /// A text channel in a guild. Text = 0, @@ -66,12 +46,29 @@ pub enum ChannelType { Group = 3, /// An organizational category that contains non-category channels. Category = 4, - /// A channel that users can follow and crosspost into another server. + /// A channel that users can follow and crosspost into another guild. News = 5, /// A channel in which game developers can sell games on Discord. Store = 6, } +impl ChannelType { + pub(crate) fn from_map( + map: &serde_json::Map, + ) -> Result + where + E: de::Error, + { + match map.get("type") { + Some(kind) => match Deserialize::deserialize(kind) { + Ok(kind) => Ok(kind), + Err(_) => return Err(E::custom(format_args!("unknown channel type: {}", kind))), + }, + None => return Err(E::missing_field("type")), + } + } +} + /// A reaction to a [`Message`]. /// /// [`Message`]: struct.Message.html @@ -85,3 +82,184 @@ pub struct Reaction { /// The partial emoji information for the reaction. pub emoji: PartialEmoji, } + +/// A channel in Discord. +#[non_exhaustive] +#[remain::sorted] +#[derive(Clone, Debug)] +pub enum Channel { + /// A direct message channel between the [`ClientUser`] and another + /// [`User`]. + /// + /// [`ClientUser`]: ../user/struct.ClientUser.html + /// [`User`]: ../user/struct.User.html + DM(DMChannel), + /// A group message channel between multiple [`User`]s. + /// + /// [`User`]: ../user/struct.User.html + Group(GroupChannel), + /// A channel within a [`Guild`]. + /// + /// [`Guild`]: TODO + Guild(GuildChannel), +} + +impl Serialize for Channel { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + match self { + Channel::DM(channel) => channel.serialize(serializer), + Channel::Group(channel) => channel.serialize(serializer), + Channel::Guild(channel) => channel.serialize(serializer), + } + } +} + +impl<'de> Deserialize<'de> for Channel { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + use serde_json::{Map, Value}; + + let map: Map = Map::deserialize(deserializer)?; + let kind = ChannelType::from_map(&map)?; + + let value = Value::Object(map); + match kind { + ChannelType::Text + | ChannelType::Voice + | ChannelType::Category + | ChannelType::News + | ChannelType::Store => GuildChannel::from_value(kind, value).map(Channel::Guild), + ChannelType::Private => DMChannel::deserialize(value) + .map(Channel::DM) + .map_err(de::Error::custom), + ChannelType::Group => GroupChannel::deserialize(value) + .map(Channel::Group) + .map_err(de::Error::custom), + } + } +} + +impl_eq_fields!(Channel: (a, b) => { + match (a, b) { + (Channel::DM(a), Channel::DM(b)) => assert_eq_fields!(a, b), + (Channel::Group(a), Channel::Group(b)) => assert_eq_fields!(a, b), + (Channel::Guild(a), Channel::Guild(b)) => assert_eq_fields!(a, b), + (a, b) => panic_ne_fields!(a, b), + } +}); + +#[cfg(test)] +mod tests { + use std::collections::HashMap; + use std::iter::FromIterator; + + use serde_json::json; + + use crate::model::id::{ChannelId, MessageId, UserId}; + use crate::model::user::{Discriminator, User}; + + use super::*; + + #[test] + fn test_deserialize_dm() { + let value = json!({ + "last_message_id": "3343820033257021450", + "type": 1, + "id": "319674150115610528", + "recipients": [ + { + "username": "test", + "discriminator": "9999", + "id": "82198898841029460", + "avatar": "33ecab261d4681afa4d85a04691c4a01" + } + ] + }); + let channel = Channel::DM(DMChannel { + id: ChannelId::from(319674150115610528), + kind: ChannelType::Private, + recipient: User { + id: UserId::from(82198898841029460), + name: "test".to_owned(), + discriminator: Discriminator::new(9999).unwrap(), + avatar: Some("33ecab261d4681afa4d85a04691c4a01".to_owned()), + bot: false, + system: false, + }, + last_message_id: Some(MessageId::from(3343820033257021450)), + last_pin_timestamp: None, + }); + + let deserialized: Channel = serde_json::from_value(value).unwrap(); + + assert_eq_fields!(channel, deserialized); + } + + #[test] + fn test_deserialize_group() { + let value = json!({ + "name": "Some test channel", + "icon": null, + "recipients": [ + { + "username": "test", + "discriminator": "9999", + "id": "82198898841029460", + "avatar": "33ecab261d4681afa4d85a04691c4a01" + }, + { + "username": "test2", + "discriminator": "9999", + "id": "53908099506183680", + "avatar": "a_bab14f271d565501444b2ca3be944b25" + } + ], + "last_message_id": "3343820033257021450", + "type": 3, + "id": "319674150115710528", + "owner_id": "82198810841029460" + }); + let channel = Channel::Group(GroupChannel { + id: ChannelId::from(319674150115710528), + kind: ChannelType::Group, + name: Some("Some test channel".to_owned()), + icon: None, + recipients: HashMap::from_iter(vec![ + ( + UserId::from(82198898841029460), + User { + id: UserId::from(82198898841029460), + name: "test".to_string(), + discriminator: Discriminator::new(9999).unwrap(), + avatar: Some("33ecab261d4681afa4d85a04691c4a01".to_owned()), + bot: false, + system: false, + }, + ), + ( + UserId::from(53908099506183680), + User { + id: UserId::from(53908099506183680), + name: "test2".to_string(), + discriminator: Discriminator::new(9999).unwrap(), + avatar: Some("a_bab14f271d565501444b2ca3be944b25".to_owned()), + bot: false, + system: false, + }, + ), + ]), + owner_id: UserId::from(82198810841029460), + last_message_id: Some(MessageId::from(3343820033257021450)), + last_pin_timestamp: None, + }); + + let deserialized: Channel = serde_json::from_value(value).unwrap(); + + assert_eq_fields!(channel, deserialized); + } +} From 85b166a29f299da3fa5456fbe476b4b427c09ad1 Mon Sep 17 00:00:00 2001 From: James Whaley Date: Thu, 9 Jan 2020 13:46:29 +0000 Subject: [PATCH 12/44] Rename GroupChannel to Group --- src/model/channel/group_channel.rs | 14 +++++++------- src/model/channel/mod.rs | 8 ++++---- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/model/channel/group_channel.rs b/src/model/channel/group_channel.rs index 1aedf14..66bde85 100644 --- a/src/model/channel/group_channel.rs +++ b/src/model/channel/group_channel.rs @@ -13,7 +13,7 @@ use crate::model::user::User; /// [`User`]: ../../user/struct.User.html #[non_exhaustive] #[derive(Clone, Debug, Deserialize, Serialize)] -pub struct GroupChannel { +pub struct Group { /// The ID of the group channel. pub id: ChannelId, /// The type of the channel. @@ -32,13 +32,13 @@ pub struct GroupChannel { pub recipients: HashMap, /// The ID of the group owner. pub owner_id: UserId, - /// The ID of the last message sent to the channel. + /// The ID of the last message sent to the group. pub last_message_id: Option, /// When the last message with pinned. pub last_pin_timestamp: Option>, } -impl GroupChannel { +impl Group { /// Returns a reference to the owner of the group. /// /// # Notes @@ -50,7 +50,7 @@ impl GroupChannel { } } -impl_eq_fields!(GroupChannel: (a, b) => { +impl_eq_fields!(Group: (a, b) => { assert_eq_fields!(a, b, [id, kind, name, icon, owner_id, last_message_id, last_pin_timestamp]); assert_eq!(a.recipients.len(), b.recipients.len()); @@ -94,7 +94,7 @@ mod tests { "id": "319674150115710528", "owner_id": "82198810841029460" }); - let channel = GroupChannel { + let channel = Group { id: ChannelId::from(319674150115710528), kind: ChannelType::Group, name: Some("Some test channel".to_owned()), @@ -128,7 +128,7 @@ mod tests { last_pin_timestamp: None, }; - let deserialized: GroupChannel = serde_json::from_value(value).unwrap(); + let deserialized: Group = serde_json::from_value(value).unwrap(); assert_eq_fields!(channel, deserialized); } @@ -162,7 +162,7 @@ mod tests { "id": "319674150115710528", "owner_id": "53908099506183680" }); - let channel = GroupChannel { + let channel = Group { id: ChannelId::from(319674150115710528), kind: ChannelType::Group, name: Some("Some test channel".to_owned()), diff --git a/src/model/channel/mod.rs b/src/model/channel/mod.rs index 64ff22e..ee102c3 100644 --- a/src/model/channel/mod.rs +++ b/src/model/channel/mod.rs @@ -23,7 +23,7 @@ pub use self::embed::{ Embed, EmbedAuthor, EmbedField, EmbedFooter, EmbedImage, EmbedProvider, EmbedThumbnail, EmbedType, EmbedVideo, }; -pub use self::group_channel::GroupChannel; +pub use self::group_channel::Group; pub use self::guild_channel::GuildChannel; pub use self::message::{ MentionedChannel, MentionedUser, Message, MessageFlags, MessageReference, MessageType, @@ -97,7 +97,7 @@ pub enum Channel { /// A group message channel between multiple [`User`]s. /// /// [`User`]: ../user/struct.User.html - Group(GroupChannel), + Group(Group), /// A channel within a [`Guild`]. /// /// [`Guild`]: TODO @@ -137,7 +137,7 @@ impl<'de> Deserialize<'de> for Channel { ChannelType::Private => DMChannel::deserialize(value) .map(Channel::DM) .map_err(de::Error::custom), - ChannelType::Group => GroupChannel::deserialize(value) + ChannelType::Group => Group::deserialize(value) .map(Channel::Group) .map_err(de::Error::custom), } @@ -224,7 +224,7 @@ mod tests { "id": "319674150115710528", "owner_id": "82198810841029460" }); - let channel = Channel::Group(GroupChannel { + let channel = Channel::Group(Group { id: ChannelId::from(319674150115710528), kind: ChannelType::Group, name: Some("Some test channel".to_owned()), From b6b86432760369dd2a73004c90e5cdc7d6ba657e Mon Sep 17 00:00:00 2001 From: James Whaley Date: Thu, 9 Jan 2020 13:58:18 +0000 Subject: [PATCH 13/44] Remove `kind` from public API for DM and Group Remove `kind` field from public API for DMChannel and Group since the value should be fixed. --- src/model/channel/dm_channel.rs | 2 +- src/model/channel/group_channel.rs | 2 +- src/model/channel/guild_channel/mod.rs | 13 +++++++++++++ src/model/channel/mod.rs | 11 +++++++++++ 4 files changed, 26 insertions(+), 2 deletions(-) diff --git a/src/model/channel/dm_channel.rs b/src/model/channel/dm_channel.rs index 2c71701..366f152 100644 --- a/src/model/channel/dm_channel.rs +++ b/src/model/channel/dm_channel.rs @@ -21,7 +21,7 @@ pub struct DMChannel { /// /// [`ChannelType::Private`]: ../enum.ChannelType.html#variant.Private #[serde(rename = "type")] - pub kind: ChannelType, + pub(crate) kind: ChannelType, /// The recipient to the direct message channel. #[serde(rename = "recipients", with = "serde_recipient")] pub recipient: User, diff --git a/src/model/channel/group_channel.rs b/src/model/channel/group_channel.rs index 66bde85..a7d7fa9 100644 --- a/src/model/channel/group_channel.rs +++ b/src/model/channel/group_channel.rs @@ -22,7 +22,7 @@ pub struct Group { /// /// [`ChannelType::Group`]: ../enum.ChannelType.html#variant.Group #[serde(rename = "type")] - pub kind: ChannelType, + pub(crate) kind: ChannelType, /// The name of the group. pub name: Option, /// The group icon hash. diff --git a/src/model/channel/guild_channel/mod.rs b/src/model/channel/guild_channel/mod.rs index 315a281..7bd8443 100644 --- a/src/model/channel/guild_channel/mod.rs +++ b/src/model/channel/guild_channel/mod.rs @@ -27,6 +27,19 @@ pub enum GuildChannel { Store, // TODO: Add StoreChannel. } +impl GuildChannel { + /// The type of the channel. + pub fn kind(&self) -> ChannelType { + match self { + GuildChannel::Text => ChannelType::Text, + GuildChannel::Voice => ChannelType::Voice, + GuildChannel::Category => ChannelType::Category, + GuildChannel::News => ChannelType::News, + GuildChannel::Store => ChannelType::Store, + } + } +} + impl GuildChannel { pub(crate) fn from_value( kind: ChannelType, diff --git a/src/model/channel/mod.rs b/src/model/channel/mod.rs index ee102c3..4840914 100644 --- a/src/model/channel/mod.rs +++ b/src/model/channel/mod.rs @@ -104,6 +104,17 @@ pub enum Channel { Guild(GuildChannel), } +impl Channel { + /// The type of the channel. + pub fn kind(&self) -> ChannelType { + match self { + Channel::DM(_) => ChannelType::Private, + Channel::Group(_) => ChannelType::Group, + Channel::Guild(channel) => channel.kind(), + } + } +} + impl Serialize for Channel { fn serialize(&self, serializer: S) -> Result where From 2ec8e9490710e27107bc83b2ccd38247f6fdb2c9 Mon Sep 17 00:00:00 2001 From: James Whaley Date: Thu, 9 Jan 2020 18:32:42 +0000 Subject: [PATCH 14/44] Clean up PermissionOverwrite serde --- src/model/channel/permission_overwrite.rs | 32 +++++++++-------------- 1 file changed, 13 insertions(+), 19 deletions(-) diff --git a/src/model/channel/permission_overwrite.rs b/src/model/channel/permission_overwrite.rs index 9363c86..da3c20d 100644 --- a/src/model/channel/permission_overwrite.rs +++ b/src/model/channel/permission_overwrite.rs @@ -1,6 +1,6 @@ use std::fmt::{self, Display}; -use serde::{Serialize, Serializer}; +use serde::{Deserialize, Serialize}; use crate::model::id::{RoleId, UserId}; use crate::model::permissions::Permissions; @@ -9,11 +9,14 @@ use crate::model::permissions::Permissions; /// /// [`PermissionOverwrite`]: struct.PermissionOverwrite.html #[non_exhaustive] -#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Deserialize, Serialize)] +#[serde(tag = "type", content = "id")] pub enum OverwriteId { /// A role with permission overwrites being edited. + #[serde(rename = "role")] Role(RoleId), /// A user with permission overwrites being edited. + #[serde(rename = "member")] User(UserId), } @@ -40,10 +43,10 @@ impl From for OverwriteId { /// Channel-specific permission overwrites for a role or user. #[non_exhaustive] -#[derive(Clone, Debug, Eq, PartialEq, Serialize)] +#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)] pub struct PermissionOverwrite { /// The ID of the role or user. - #[serde(rename = "type", serialize_with = "serialize_type")] + #[serde(flatten)] pub id: OverwriteId, /// The set of permissions being allowed. pub allow: Permissions, @@ -75,17 +78,6 @@ impl PermissionOverwrite { } } -fn serialize_type(id: &OverwriteId, serializer: S) -> Result -where - S: Serializer, -{ - let r#type = match id { - OverwriteId::Role(_) => "role", - OverwriteId::User(_) => "member", - }; - serializer.serialize_str(r#type) -} - #[cfg(test)] mod tests { use serde_json::json; @@ -99,12 +91,13 @@ mod tests { #[test] fn test_serialize_role() { - let id = OverwriteId::from(RoleId::from(ID)); let allow = Permissions::from_bits(ALLOW_BITS).expect("valid permissions"); let deny = Permissions::from_bits(DENY_BITS).expect("valid permissions"); - let overwrites = PermissionOverwrite::new(id, allow, deny); + + let overwrites = PermissionOverwrite::new(RoleId::from(ID), allow, deny); let expected = json!({ + "id": "80351110224678912", "type": "role", "allow": 104188992, "deny": 135168, @@ -116,12 +109,13 @@ mod tests { #[test] fn test_serialize_user() { - let id = OverwriteId::from(UserId::from(ID)); let allow = Permissions::from_bits(ALLOW_BITS).expect("valid permissions"); let deny = Permissions::from_bits(DENY_BITS).expect("valid permissions"); - let overwrites = PermissionOverwrite::new(id, allow, deny); + + let overwrites = PermissionOverwrite::new(UserId::from(ID), allow, deny); let expected = json!({ + "id": "80351110224678912", "type": "member", "allow": 104188992, "deny": 135168, From f1eb27ab08dbe901c70050868d95a507666250d1 Mon Sep 17 00:00:00 2001 From: James Whaley Date: Thu, 9 Jan 2020 18:38:41 +0000 Subject: [PATCH 15/44] Add application_id to Group model and update docs --- src/model/channel/group_channel.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/model/channel/group_channel.rs b/src/model/channel/group_channel.rs index a7d7fa9..fc2718e 100644 --- a/src/model/channel/group_channel.rs +++ b/src/model/channel/group_channel.rs @@ -5,7 +5,7 @@ use serde::{Deserialize, Serialize}; use crate::model::channel::utils::serde_recipients; use crate::model::channel::ChannelType; -use crate::model::id::{ChannelId, MessageId, UserId}; +use crate::model::id::{ApplicationId, ChannelId, MessageId, UserId}; use crate::model::user::User; /// A group message channel between multiple [`User`]s. @@ -30,8 +30,10 @@ pub struct Group { /// The users in the group. #[serde(default, with = "serde_recipients")] pub recipients: HashMap, - /// The ID of the group owner. + /// The ID of the group creator. pub owner_id: UserId, + /// The application ID of the group creator, if it is bot-created. + pub application_id: Option, /// The ID of the last message sent to the group. pub last_message_id: Option, /// When the last message with pinned. From 3203e40d8a5396795fd972c63f44dd78b3869c30 Mon Sep 17 00:00:00 2001 From: James Whaley Date: Thu, 9 Jan 2020 20:48:16 +0000 Subject: [PATCH 16/44] Refactor channel modules to reduce bloated exports --- channels.json | 11240 ++++++++++++++++ src/http/routing.rs | 2 +- src/model/channel/{dm_channel.rs => dm.rs} | 2 +- .../channel/{group_channel.rs => group.rs} | 7 +- .../channel/{guild_channel => guild}/mod.rs | 37 +- src/model/channel/guild/text_channel.rs | 52 + src/model/channel/{ => message}/attachment.rs | 0 src/model/channel/{ => message}/embed.rs | 3 + .../channel/{message.rs => message/mod.rs} | 21 +- .../channel/{ => message}/rich_presence.rs | 0 src/model/channel/mod.rs | 34 +- ...permission_overwrite.rs => permissions.rs} | 2 + 12 files changed, 11356 insertions(+), 44 deletions(-) create mode 100644 channels.json rename src/model/channel/{dm_channel.rs => dm.rs} (98%) rename src/model/channel/{group_channel.rs => group.rs} (97%) rename src/model/channel/{guild_channel => guild}/mod.rs (75%) create mode 100644 src/model/channel/guild/text_channel.rs rename src/model/channel/{ => message}/attachment.rs (100%) rename src/model/channel/{ => message}/embed.rs (98%) rename src/model/channel/{message.rs => message/mod.rs} (96%) rename src/model/channel/{ => message}/rich_presence.rs (100%) rename src/model/channel/{permission_overwrite.rs => permissions.rs} (98%) diff --git a/channels.json b/channels.json new file mode 100644 index 0000000..1e1598d --- /dev/null +++ b/channels.json @@ -0,0 +1,11240 @@ +[ + { + "id": "162706186272112640", + "last_message_id": "664890258189582337", + "last_pin_timestamp": "2020-01-01T22:37:59.513000+00:00", + "type": 0, + "name": "general", + "position": 24, + "parent_id": "381910837293613057", + "topic": "Chat with fellow guardians. Links ARE allowed. No NSFW allowed. Keep LFG requests in the #lfg-requests channel.", + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 1, + "deny": 131072 + }, + { + "id": "463520715254267916", + "type": "role", + "allow": 0, + "deny": 3072 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 131072, + "deny": 0 + }, + { + "id": "530919353999163414", + "type": "role", + "allow": 0, + "deny": 2112 + }, + { + "id": "538477449130803212", + "type": "role", + "allow": 0, + "deny": 131072 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "381910672256008196", + "type": 2, + "name": "AFK", + "position": 23, + "parent_id": "382125435376631820", + "bitrate": 64000, + "user_limit": 0, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 0 + }, + { + "id": "419272087132307467", + "type": "member", + "allow": 0, + "deny": 1024 + }, + { + "id": "463520715254267916", + "type": "role", + "allow": 0, + "deny": 1048576 + }, + { + "id": "530919353999163414", + "type": "role", + "allow": 0, + "deny": 1048576 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 1048576 + } + ], + "nsfw": false + }, + { + "id": "381910837293613057", + "type": 4, + "name": "Textual Chat", + "position": 4, + "parent_id": null, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 131072 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 131072, + "deny": 0 + }, + { + "id": "463520715254267916", + "type": "role", + "allow": 0, + "deny": 3072 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 131072, + "deny": 0 + }, + { + "id": "530919353999163414", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 131072, + "deny": 0 + } + ], + "nsfw": false + }, + { + "id": "381910983444135957", + "last_message_id": "664884899244802051", + "last_pin_timestamp": "2018-10-29T03:27:06.909000+00:00", + "type": 0, + "name": "welcome", + "position": 4, + "parent_id": null, + "topic": "", + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 15360 + }, + { + "id": "381911719901134850", + "type": "role", + "allow": 0, + "deny": 71680 + }, + { + "id": "382118099341541379", + "type": "role", + "allow": 1024, + "deny": 6144 + }, + { + "id": "439951550358487040", + "type": "role", + "allow": 0, + "deny": 15360 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 3072, + "deny": 0 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 1024, + "deny": 6144 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "381911060493500416", + "type": 4, + "name": "Server Info", + "position": 0, + "parent_id": null, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 1024, + "deny": 145472 + }, + { + "id": "463520715254267916", + "type": "role", + "allow": 0, + "deny": 3072 + } + ], + "nsfw": false + }, + { + "id": "382114317857849344", + "type": 4, + "name": "Staff channels", + "position": 18, + "parent_id": null, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1025 + }, + { + "id": "463520715254267916", + "type": "role", + "allow": 0, + "deny": 3072 + } + ], + "nsfw": false + }, + { + "id": "382125277066690562", + "type": 2, + "name": "Ninja A", + "position": 35, + "parent_id": "510476994476113930", + "bitrate": 64000, + "user_limit": 0, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1049600 + }, + { + "id": "382118099341541379", + "type": "role", + "allow": 1049600, + "deny": 0 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 1049600, + "deny": 0 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 17826816, + "deny": 0 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 1050112, + "deny": 0 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 1048576 + } + ], + "nsfw": false + }, + { + "id": "382125435376631820", + "type": 4, + "name": "Other", + "position": 13, + "parent_id": null, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "463520715254267916", + "type": "role", + "allow": 0, + "deny": 3148800 + } + ], + "nsfw": false + }, + { + "id": "382558620639952906", + "last_message_id": "664556483433725952", + "last_pin_timestamp": "2020-01-02T17:11:24.932000+00:00", + "type": 0, + "name": "trial-moderator-chat", + "position": 54, + "parent_id": "382114317857849344", + "topic": "", + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "382118099341541379", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "384886471531692033", + "last_message_id": "664885574011715585", + "last_pin_timestamp": "2019-10-27T19:48:16.002000+00:00", + "type": 0, + "name": "lfg-requests", + "position": 25, + "parent_id": "381910837293613057", + "topic": "Find Fireteams here.\n\nPlease refrain from using @ everyone here! Ping @sherpa for help with raids, @pve-guide for pve, and @crucible guide for crucible.\n\nRight click #lfg-request to mute channel\nAny off topic conversation will result in the message(s) being deleted", + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 1, + "deny": 49216 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 49216, + "deny": 0 + }, + { + "id": "463520715254267916", + "type": "role", + "allow": 0, + "deny": 3072 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 49216, + "deny": 0 + }, + { + "id": "530919353999163414", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 49216, + "deny": 0 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "399740243781222401", + "last_message_id": "470260894153310208", + "type": 0, + "name": "faq-old-old", + "position": 97, + "parent_id": "449088445617405962", + "topic": "", + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1051648 + }, + { + "id": "382117075558203392", + "type": "role", + "allow": 1049600, + "deny": 0 + }, + { + "id": "382118099341541379", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "409197306370326539", + "last_message_id": "664890767151464459", + "last_pin_timestamp": "2019-08-12T01:29:41.721000+00:00", + "type": 0, + "name": "bot-channel", + "position": 28, + "parent_id": "381910837293613057", + "topic": "Use this channel only for any bot commands. Check pins for more info.", + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 131072 + }, + { + "id": "441751906428256277", + "type": "member", + "allow": 0, + "deny": 3072 + }, + { + "id": "463520715254267916", + "type": "role", + "allow": 0, + "deny": 3072 + }, + { + "id": "491769129318088714", + "type": "member", + "allow": 0, + "deny": 3072 + }, + { + "id": "530919353999163414", + "type": "role", + "allow": 131072, + "deny": 2048 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 5 + }, + { + "id": "429506865097998350", + "last_message_id": "664661027912155146", + "type": 0, + "name": "announcements", + "position": 1, + "parent_id": null, + "topic": "Clan and server news, including activity check results and surveys about the state of the Discord server.", + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 1024, + "deny": 268580944 + }, + { + "id": "382116615614889984", + "type": "role", + "allow": 0, + "deny": 0 + }, + { + "id": "382117075558203392", + "type": "role", + "allow": 131072, + "deny": 0 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 134208, + "deny": 0 + }, + { + "id": "463520715254267916", + "type": "role", + "allow": 0, + "deny": 3072 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 134144, + "deny": 0 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "440336757146255391", + "last_message_id": "599321185226063882", + "last_pin_timestamp": "2018-05-28T06:46:30.876000+00:00", + "type": 0, + "name": "trial-moderator-applications", + "position": 110, + "parent_id": "449088445617405962", + "topic": "Apply for staff positions. Read the channel pins for more information.", + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1244225 + }, + { + "id": "530919353999163414", + "type": "role", + "allow": 0, + "deny": 3072 + }, + { + "id": "538477449130803212", + "type": "role", + "allow": 65536, + "deny": 3072 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "441331852528320512", + "last_message_id": "664886455297900589", + "last_pin_timestamp": "2019-12-30T14:56:36.260000+00:00", + "type": 0, + "name": "apply-to-clan", + "position": 13, + "parent_id": "479849608558936064", + "topic": "Request clan admission or missing ranks (e.g. Clan Member ranks) here.", + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 68608, + "deny": 442432 + }, + { + "id": "323177364848902145", + "type": "member", + "allow": 0, + "deny": 0 + }, + { + "id": "382118099341541379", + "type": "role", + "allow": 312384, + "deny": 0 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 312384, + "deny": 0 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 312384, + "deny": 0 + }, + { + "id": "530919353999163414", + "type": "role", + "allow": 0, + "deny": 2112 + }, + { + "id": "538477449130803212", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 312384, + "deny": 0 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "444243773254270996", + "last_message_id": "502319247230238722", + "last_pin_timestamp": "2018-05-10T21:07:07.374000+00:00", + "type": 0, + "name": "spire-of-stars-lfg", + "position": 89, + "parent_id": "449088445617405962", + "topic": "This is a temporary channel that will be removed the weekend of the 12th of May. Its purpose is to find a group for the opening weekend of the Spire of Stars raid lair, launching Friday, May 11th. Do not use this channel for any purpose other than finding a fire team.", + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1051648 + }, + { + "id": "382117075558203392", + "type": "role", + "allow": 1049600, + "deny": 0 + }, + { + "id": "382118099341541379", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "445696353503281154", + "last_message_id": "664889772812271617", + "last_pin_timestamp": "2019-11-06T01:10:46.678000+00:00", + "type": 0, + "name": "bot-logs", + "position": 62, + "parent_id": "485882029158826019", + "topic": "", + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 3072 + }, + { + "id": "382116615614889984", + "type": "role", + "allow": 0, + "deny": 0 + }, + { + "id": "382117075558203392", + "type": "role", + "allow": 3072, + "deny": 0 + }, + { + "id": "382118099341541379", + "type": "role", + "allow": 0, + "deny": 3072 + }, + { + "id": "442060343162699776", + "type": "role", + "allow": 3072, + "deny": 0 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 3072, + "deny": 0 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 0, + "deny": 3072 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 7168, + "deny": 0 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "449088445617405962", + "type": 4, + "name": "Archive", + "position": 23, + "parent_id": null, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1051648 + }, + { + "id": "382117075558203392", + "type": "role", + "allow": 1049600, + "deny": 0 + }, + { + "id": "382118099341541379", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 1024, + "deny": 0 + } + ], + "nsfw": false + }, + { + "id": "449542580330430464", + "last_message_id": "664245989422530571", + "last_pin_timestamp": "2019-11-13T02:52:34.300000+00:00", + "type": 0, + "name": "admin-chat", + "position": 58, + "parent_id": "382114317857849344", + "topic": null, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 3072 + }, + { + "id": "382116615614889984", + "type": "role", + "allow": 0, + "deny": 0 + }, + { + "id": "382117075558203392", + "type": "role", + "allow": 0, + "deny": 0 + }, + { + "id": "382118099341541379", + "type": "role", + "allow": 0, + "deny": 3072 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "452875714891481144", + "last_message_id": "664890491350810635", + "last_pin_timestamp": "2018-07-30T00:13:57.199000+00:00", + "type": 0, + "name": "song-requests", + "position": 33, + "parent_id": "382125435376631820", + "topic": "Use \";;play song name\" to request a track. This channel is only visible to users in the Club Collective voice channel for song requests.", + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 3072, + "deny": 0 + }, + { + "id": "463520715254267916", + "type": "role", + "allow": 3072, + "deny": 3145728 + }, + { + "id": "530919353999163414", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "538477449130803212", + "type": "role", + "allow": 3072, + "deny": 0 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "453945344586416128", + "last_message_id": "664889895722024971", + "type": 0, + "name": "charlemagne-feed", + "position": 11, + "parent_id": "479849608558936064", + "topic": null, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "409197133946683402", + "type": "role", + "allow": 2048, + "deny": 0 + }, + { + "id": "463520715254267916", + "type": "role", + "allow": 0, + "deny": 3072 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "459913518146125846", + "last_message_id": "663518485808152614", + "last_pin_timestamp": "2019-06-02T01:47:49.675000+00:00", + "type": 0, + "name": "programmer-chat", + "position": 64, + "parent_id": "485882029158826019", + "topic": null, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "382118099341541379", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "653357372466659349", + "type": "role", + "allow": 3072, + "deny": 0 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "463518519556833280", + "type": 2, + "name": "Ask the Admins!", + "position": 1, + "parent_id": null, + "bitrate": 100000, + "user_limit": 0, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 3146752 + }, + { + "id": "382117075558203392", + "type": "role", + "allow": 256, + "deny": 0 + }, + { + "id": "382128710779928576", + "type": "role", + "allow": 3146752, + "deny": 0 + }, + { + "id": "463520715254267916", + "type": "role", + "allow": 3146752, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 1048576 + } + ], + "nsfw": false + }, + { + "id": "466401774094516245", + "last_message_id": "626974055483965451", + "last_pin_timestamp": "2019-05-24T02:46:48.857000+00:00", + "type": 0, + "name": "stormbot", + "position": 63, + "parent_id": "485882029158826019", + "topic": "For Testing Purposes | Apparently the only spot to use ?activity", + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "382118099341541379", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "567781687874945028", + "type": "role", + "allow": 0, + "deny": 0 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "653357372466659349", + "type": "role", + "allow": 3072, + "deny": 0 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "470403009936097291", + "last_message_id": "572545790606573588", + "type": 0, + "name": "sherpa-program", + "position": 122, + "parent_id": "619529638699335685", + "topic": "Information about the Sherpa Program", + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1051713 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "482625494886645781", + "type": "role", + "allow": 0, + "deny": 268446736 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 3072, + "deny": 0 + }, + { + "id": "565734337819967489", + "type": "role", + "allow": 0, + "deny": 268446720 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 3072 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "470408603724021770", + "last_message_id": "627299350069903370", + "type": 0, + "name": "rules", + "position": 7, + "parent_id": "381911060493500416", + "topic": null, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 1024, + "deny": 145472 + }, + { + "id": "463520715254267916", + "type": "role", + "allow": 0, + "deny": 3072 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "470424640100237312", + "last_message_id": "502692186509410304", + "last_pin_timestamp": "2018-07-25T21:51:59.290000+00:00", + "type": 0, + "name": "sherpa-notes", + "position": 84, + "parent_id": "449088445617405962", + "topic": "Log notes on sherpa performance here. Keep discussion to a minimum for organization.", + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1051648 + }, + { + "id": "382117075558203392", + "type": "role", + "allow": 1049600, + "deny": 0 + }, + { + "id": "382118099341541379", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "470809196695126027", + "last_message_id": "663192067815637011", + "last_pin_timestamp": "2019-08-03T22:04:31.830000+00:00", + "type": 0, + "name": "staff-announcements", + "position": 49, + "parent_id": "382114317857849344", + "topic": "", + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 3072 + }, + { + "id": "382118099341541379", + "type": "role", + "allow": 1024, + "deny": 2048 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 3072, + "deny": 0 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 1024, + "deny": 2048 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 3072, + "deny": 0 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "472506648766971934", + "last_message_id": "502319831031480330", + "last_pin_timestamp": "2018-10-18T03:19:40.358000+00:00", + "type": 0, + "name": "edit-channel", + "position": 106, + "parent_id": "449088445617405962", + "topic": null, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1051648 + }, + { + "id": "382117075558203392", + "type": "role", + "allow": 1049600, + "deny": 0 + }, + { + "id": "382118099341541379", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "477857643734761475", + "last_message_id": "655850429124444162", + "last_pin_timestamp": "2019-05-06T11:45:40.437000+00:00", + "type": 0, + "name": "game-night-announcements", + "position": 18, + "parent_id": "485961711535390720", + "topic": "", + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 1024, + "deny": 2048 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 3148800, + "deny": 0 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 3072, + "deny": 0 + }, + { + "id": "497267428460462080", + "type": "role", + "allow": 1024, + "deny": 2048 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 3072, + "deny": 0 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "478428670332239873", + "type": 2, + "name": "Ninja B", + "position": 36, + "parent_id": "510476994476113930", + "bitrate": 64000, + "user_limit": 0, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1049600 + }, + { + "id": "382118099341541379", + "type": "role", + "allow": 1049600, + "deny": 0 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 1049600, + "deny": 0 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 17826816, + "deny": 0 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 1049600, + "deny": 0 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 1048576 + } + ], + "nsfw": false + }, + { + "id": "478711974734069770", + "last_message_id": "478712512028868630", + "type": 0, + "name": "faq-old", + "position": 114, + "parent_id": "449088445617405962", + "topic": "", + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 146496 + }, + { + "id": "463520715254267916", + "type": "role", + "allow": 0, + "deny": 3072 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "479068822079275031", + "last_message_id": "506207223572070402", + "last_pin_timestamp": "2018-09-09T02:37:38.661000+00:00", + "type": 0, + "name": "role-management", + "position": 92, + "parent_id": "449088445617405962", + "topic": "Discuss granting of member roles such as sherpa, frequenter, veteran.", + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1051648 + }, + { + "id": "382117075558203392", + "type": "role", + "allow": 1049600, + "deny": 0 + }, + { + "id": "382118099341541379", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "479849608558936064", + "type": 4, + "name": "Clan Area", + "position": 1, + "parent_id": null, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1051713 + }, + { + "id": "538477449130803212", + "type": "role", + "allow": 1024, + "deny": 0 + } + ], + "nsfw": false + }, + { + "id": "480081996245237770", + "last_message_id": "502319210547118080", + "last_pin_timestamp": "2018-09-10T17:14:48.935000+00:00", + "type": 0, + "name": "forsaken-spoiler-discussion", + "position": 91, + "parent_id": "449088445617405962", + "topic": "", + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1051648 + }, + { + "id": "382117075558203392", + "type": "role", + "allow": 1049600, + "deny": 0 + }, + { + "id": "382118099341541379", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "482277741366870017", + "last_message_id": "532260802607054863", + "last_pin_timestamp": "2018-11-23T04:15:10.787000+00:00", + "type": 0, + "name": "clan-website", + "position": 73, + "parent_id": "449088445617405962", + "topic": null, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1051648 + }, + { + "id": "382117075558203392", + "type": "role", + "allow": 1049600, + "deny": 0 + }, + { + "id": "382118099341541379", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "484389015949148198", + "type": 2, + "name": "Dev Network", + "position": 41, + "parent_id": "485882029158826019", + "bitrate": 64000, + "user_limit": 0, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 1048576 + } + ], + "nsfw": false + }, + { + "id": "485135215459696655", + "last_message_id": "664847553245872130", + "last_pin_timestamp": "2019-11-16T22:36:58.344000+00:00", + "type": 0, + "name": "bot-commands", + "position": 60, + "parent_id": "485882029158826019", + "topic": null, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "241950106125860865", + "type": "member", + "allow": 117760, + "deny": 0 + }, + { + "id": "432533456807919639", + "type": "member", + "allow": 3072, + "deny": 0 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "495497885652090892", + "type": "member", + "allow": 1024, + "deny": 0 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "545333050251411457", + "type": "member", + "allow": 3072, + "deny": 0 + }, + { + "id": "548453282960375859", + "type": "member", + "allow": 388160, + "deny": 0 + }, + { + "id": "554080307226345482", + "type": "member", + "allow": 3072, + "deny": 0 + }, + { + "id": "578669719343857704", + "type": "member", + "allow": 3072, + "deny": 0 + }, + { + "id": "579680071028310027", + "type": "member", + "allow": 117760, + "deny": 0 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "485882029158826019", + "type": 4, + "name": "dev channels", + "position": 20, + "parent_id": null, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "382118099341541379", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "578669719343857704", + "type": "member", + "allow": 0, + "deny": 0 + } + ], + "nsfw": false + }, + { + "id": "485961711535390720", + "type": 4, + "name": "Events", + "position": 3, + "parent_id": null, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 3146752, + "deny": 0 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 1024, + "deny": 0 + } + ], + "nsfw": false + }, + { + "id": "486328170128211971", + "type": 4, + "name": "QuickPlay", + "position": 11, + "parent_id": null, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 1, + "deny": 0 + }, + { + "id": "463520715254267916", + "type": "role", + "allow": 0, + "deny": 1048576 + }, + { + "id": "530919353999163414", + "type": "role", + "allow": 0, + "deny": 1048576 + } + ], + "nsfw": false + }, + { + "id": "489162012547612682", + "last_message_id": "489162440773206044", + "type": 0, + "name": "absence-form", + "position": 96, + "parent_id": "449088445617405962", + "topic": null, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1051648 + }, + { + "id": "382117075558203392", + "type": "role", + "allow": 1049600, + "deny": 0 + }, + { + "id": "382118099341541379", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "489690763450384384", + "last_message_id": "502319294605033472", + "last_pin_timestamp": "2018-09-26T22:55:22.514000+00:00", + "type": 0, + "name": "last-wish-raid-lfg", + "position": 87, + "parent_id": "449088445617405962", + "topic": "LFG for and discuss the Last Wish raid in Forsaken", + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1051648 + }, + { + "id": "382117075558203392", + "type": "role", + "allow": 1049600, + "deny": 0 + }, + { + "id": "382118099341541379", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "492500180768260096", + "last_message_id": "664863758354612244", + "last_pin_timestamp": "2019-12-10T23:24:53.504000+00:00", + "type": 0, + "name": "moderation", + "position": 52, + "parent_id": "382114317857849344", + "topic": "Evidence collection and discussion of member moderation.", + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "382118099341541379", + "type": "role", + "allow": 3072, + "deny": 0 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 3072, + "deny": 0 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 3072, + "deny": 0 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 3072, + "deny": 0 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "493519397143445504", + "last_message_id": "664840787242254339", + "type": 0, + "name": "leave", + "position": 5, + "parent_id": null, + "topic": null, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 805829713 + }, + { + "id": "382116615614889984", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "382117075558203392", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 66560, + "deny": 0 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "497261738866114560", + "last_message_id": "497265773711065088", + "type": 0, + "name": "role-assignments", + "position": 78, + "parent_id": "449088445617405962", + "topic": "", + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1051648 + }, + { + "id": "382117075558203392", + "type": "role", + "allow": 1049600, + "deny": 0 + }, + { + "id": "382118099341541379", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "497542874821296138", + "last_message_id": "664867825156096031", + "last_pin_timestamp": "2019-12-28T17:11:04.937000+00:00", + "type": 0, + "name": "pve-talk", + "position": 26, + "parent_id": "381910837293613057", + "topic": "Place to discuss meta guns, your best loadouts and strats! Great place to ask for advice from our PvE guides.", + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 135168 + }, + { + "id": "463520715254267916", + "type": "role", + "allow": 0, + "deny": 3072 + }, + { + "id": "530919353999163414", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "498156410551402497", + "last_message_id": "664874749377380363", + "last_pin_timestamp": "2020-01-09T16:54:53.018000+00:00", + "type": 0, + "name": "weekly-information", + "position": 12, + "parent_id": "479849608558936064", + "topic": "Information on all vendors and content resetting every Tuesday at 1PM EST", + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 1024, + "deny": 145472 + }, + { + "id": "382118099341541379", + "type": "role", + "allow": 1024, + "deny": 2048 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 11264, + "deny": 0 + }, + { + "id": "463520715254267916", + "type": "role", + "allow": 0, + "deny": 3072 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 11264, + "deny": 0 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 3072, + "deny": 0 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "498336203675009034", + "last_message_id": "517067567584509953", + "type": 0, + "name": "clan-application-old", + "position": 88, + "parent_id": "449088445617405962", + "topic": "", + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1051648 + }, + { + "id": "382117075558203392", + "type": "role", + "allow": 1049600, + "deny": 0 + }, + { + "id": "382118099341541379", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "501072657865900062", + "last_message_id": "548740255088377857", + "type": 0, + "name": "analytics", + "position": 72, + "parent_id": "449088445617405962", + "topic": "", + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1051648 + }, + { + "id": "382117075558203392", + "type": "role", + "allow": 1049600, + "deny": 0 + }, + { + "id": "382118099341541379", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "501218568151498754", + "type": 2, + "name": "Sherpa Head/Lead", + "position": 31, + "parent_id": "526617610519838741", + "bitrate": 96000, + "user_limit": 0, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "469631935753355264", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "565734337819967489", + "type": "role", + "allow": 1049600, + "deny": 0 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 1048576 + } + ], + "nsfw": false + }, + { + "id": "501514955103928320", + "last_message_id": "655039849056698368", + "type": 0, + "name": "game-night-votes", + "position": 19, + "parent_id": "485961711535390720", + "topic": null, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 1024, + "deny": 2112 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 3148864, + "deny": 0 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "502647609362612244", + "last_message_id": "502931307836473364", + "last_pin_timestamp": "2018-10-19T01:22:24.420000+00:00", + "type": 0, + "name": "clan-stealing-advert", + "position": 115, + "parent_id": "449088445617405962", + "topic": "We're tryin to grow by bringing in the herd", + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "382116615614889984", + "type": "role", + "allow": 0, + "deny": 0 + }, + { + "id": "382117075558203392", + "type": "role", + "allow": 0, + "deny": 0 + }, + { + "id": "382118099341541379", + "type": "role", + "allow": 0, + "deny": 0 + }, + { + "id": "463520715254267916", + "type": "role", + "allow": 0, + "deny": 3072 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "503639019138056202", + "last_message_id": "509809017090998316", + "type": 0, + "name": "advert", + "position": 107, + "parent_id": "449088445617405962", + "topic": null, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1051648 + }, + { + "id": "382117075558203392", + "type": "role", + "allow": 1049600, + "deny": 0 + }, + { + "id": "382118099341541379", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "505807467637112842", + "last_message_id": "507989702754828319", + "type": 0, + "name": "landing-page-work", + "position": 81, + "parent_id": "449088445617405962", + "topic": null, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1051648 + }, + { + "id": "382117075558203392", + "type": "role", + "allow": 1049600, + "deny": 0 + }, + { + "id": "382118099341541379", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "505816855303421953", + "last_message_id": "563177965253296139", + "last_pin_timestamp": "2018-10-27T21:18:11.025000+00:00", + "type": 0, + "name": "pvp-guides", + "position": 112, + "parent_id": "449088445617405962", + "topic": "Make nominations for the PVP Guide role here as well as any PVP-related discussion between guides.", + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "336630009546407937", + "type": "member", + "allow": 805829713, + "deny": 0 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 0, + "deny": 0 + }, + { + "id": "502964473674465300", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "505982623441616917", + "last_message_id": "516749475239428098", + "type": 0, + "name": "pvp-council", + "position": 109, + "parent_id": "449088445617405962", + "topic": "Vote on nominations for PVP Guide and privately discuss anything related to PVP Guides", + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1051648 + }, + { + "id": "382117075558203392", + "type": "role", + "allow": 1049600, + "deny": 0 + }, + { + "id": "382118099341541379", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 1040, + "deny": 2048 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 1040, + "deny": 2048 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "505983940809719808", + "last_message_id": "658097394386206731", + "last_pin_timestamp": "2019-04-25T01:55:20.717000+00:00", + "type": 0, + "name": "sherpa-lounge", + "position": 44, + "parent_id": "526617610519838741", + "topic": "Feel free to ping @sherpa if you need another pair of hands!", + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1049600 + }, + { + "id": "382116615614889984", + "type": "role", + "allow": 1049600, + "deny": 0 + }, + { + "id": "382117075558203392", + "type": "role", + "allow": 1049600, + "deny": 0 + }, + { + "id": "382559698513166348", + "type": "role", + "allow": 1049600, + "deny": 0 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 1051648, + "deny": 0 + }, + { + "id": "469631935753355264", + "type": "role", + "allow": 1049600, + "deny": 0 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "482625494886645781", + "type": "role", + "allow": 1049600, + "deny": 0 + }, + { + "id": "502964473674465300", + "type": "role", + "allow": 1048576, + "deny": 1024 + }, + { + "id": "565734337819967489", + "type": "role", + "allow": 11264, + "deny": 0 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "505984010053222401", + "last_message_id": "654121008671752222", + "last_pin_timestamp": "2019-01-07T16:20:14.596000+00:00", + "type": 0, + "name": "sherpa-admin", + "position": 45, + "parent_id": "526617610519838741", + "topic": "", + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1049600 + }, + { + "id": "382559698513166348", + "type": "role", + "allow": 1048576, + "deny": 1024 + }, + { + "id": "469631935753355264", + "type": "role", + "allow": 1048576, + "deny": 1024 + }, + { + "id": "502964473674465300", + "type": "role", + "allow": 1048576, + "deny": 1024 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 3072, + "deny": 0 + }, + { + "id": "565734337819967489", + "type": "role", + "allow": 3072, + "deny": 0 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "506204441733627936", + "last_message_id": "506205120024018946", + "type": 0, + "name": "goal-list", + "position": 117, + "parent_id": "449088445617405962", + "topic": null, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "382116615614889984", + "type": "role", + "allow": 0, + "deny": 0 + }, + { + "id": "382117075558203392", + "type": "role", + "allow": 0, + "deny": 0 + }, + { + "id": "382118099341541379", + "type": "role", + "allow": 0, + "deny": 3072 + }, + { + "id": "463520715254267916", + "type": "role", + "allow": 0, + "deny": 3072 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "506231343919923220", + "last_message_id": "518470848793935882", + "last_pin_timestamp": "2018-12-01T02:56:46.084000+00:00", + "type": 0, + "name": "logo-challenge-channel", + "position": 90, + "parent_id": "449088445617405962", + "topic": null, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1051648 + }, + { + "id": "382117075558203392", + "type": "role", + "allow": 1049600, + "deny": 0 + }, + { + "id": "382118099341541379", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "509065623997906944", + "last_message_id": "509121968562241536", + "type": 0, + "name": "advert-text", + "position": 116, + "parent_id": "449088445617405962", + "topic": null, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "382117075558203392", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "509878104899190807", + "type": 2, + "name": "Badmins", + "position": 30, + "parent_id": "510476994476113930", + "bitrate": 64000, + "user_limit": 0, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "382116615614889984", + "type": "role", + "allow": 0, + "deny": 0 + }, + { + "id": "382117075558203392", + "type": "role", + "allow": 0, + "deny": 0 + }, + { + "id": "382118099341541379", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "463520715254267916", + "type": "role", + "allow": 0, + "deny": 3072 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 1048576 + } + ], + "nsfw": false + }, + { + "id": "510141207062511617", + "last_message_id": "510691810163687444", + "type": 0, + "name": "staff-changes", + "position": 94, + "parent_id": "449088445617405962", + "topic": null, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1051648 + }, + { + "id": "382117075558203392", + "type": "role", + "allow": 1049600, + "deny": 0 + }, + { + "id": "382118099341541379", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "510194688335282207", + "last_message_id": "645150989686669323", + "type": 0, + "name": "landing", + "position": 0, + "parent_id": null, + "topic": null, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 1025, + "deny": 264256 + }, + { + "id": "336630009546407937", + "type": "member", + "allow": 3072, + "deny": 0 + }, + { + "id": "381911719901134850", + "type": "role", + "allow": 1025, + "deny": 2048 + }, + { + "id": "382116615614889984", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "382117075558203392", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "382118099341541379", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 1025, + "deny": 2048 + }, + { + "id": "482625494886645781", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "510476994476113930", + "type": 4, + "name": "Staff Voice", + "position": 19, + "parent_id": null, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1049600 + }, + { + "id": "382118099341541379", + "type": "role", + "allow": 1049600, + "deny": 0 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 1049600, + "deny": 0 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 17826816, + "deny": 0 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 1049600, + "deny": 0 + } + ], + "nsfw": false + }, + { + "id": "511564679945125908", + "last_message_id": "512008649628057610", + "type": 0, + "name": "faq-talking-points", + "position": 83, + "parent_id": "449088445617405962", + "topic": null, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1051648 + }, + { + "id": "382117075558203392", + "type": "role", + "allow": 1049600, + "deny": 0 + }, + { + "id": "382118099341541379", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "513094096445767681", + "last_message_id": "663811969744109591", + "last_pin_timestamp": "2019-12-27T22:28:23.586000+00:00", + "type": 0, + "name": "leader-chat", + "position": 56, + "parent_id": "382114317857849344", + "topic": "", + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "382116615614889984", + "type": "role", + "allow": 0, + "deny": 0 + }, + { + "id": "382117075558203392", + "type": "role", + "allow": 0, + "deny": 0 + }, + { + "id": "382118099341541379", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 3136, + "deny": 0 + }, + { + "id": "463520715254267916", + "type": "role", + "allow": 0, + "deny": 3072 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 3136, + "deny": 0 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "513506633259679746", + "last_message_id": "516054238514774042", + "last_pin_timestamp": "2018-11-24T22:00:33.955000+00:00", + "type": 0, + "name": "scrims", + "position": 99, + "parent_id": "449088445617405962", + "topic": null, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1051648 + }, + { + "id": "382117075558203392", + "type": "role", + "allow": 1049600, + "deny": 0 + }, + { + "id": "382118099341541379", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "517002345096413185", + "last_message_id": "524718054521962508", + "last_pin_timestamp": "2018-12-14T05:34:15.359000+00:00", + "type": 0, + "name": "black-armory-discussion", + "position": 85, + "parent_id": "449088445617405962", + "topic": "A chat for discussion about black armory! Please keep all expansion discussion here", + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1051648 + }, + { + "id": "382117075558203392", + "type": "role", + "allow": 1049600, + "deny": 0 + }, + { + "id": "382118099341541379", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "517009920500039719", + "last_message_id": "517452242526273578", + "last_pin_timestamp": "2018-11-27T16:16:51.108000+00:00", + "type": 0, + "name": "ow-prep", + "position": 93, + "parent_id": "449088445617405962", + "topic": "", + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1051648 + }, + { + "id": "382117075558203392", + "type": "role", + "allow": 1049600, + "deny": 0 + }, + { + "id": "382118099341541379", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "517781981489004544", + "last_message_id": "551859163286863902", + "type": 0, + "name": "clan-info", + "position": 80, + "parent_id": "449088445617405962", + "topic": "", + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1051648 + }, + { + "id": "382117075558203392", + "type": "role", + "allow": 1049600, + "deny": 0 + }, + { + "id": "382118099341541379", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "518264327158104091", + "last_message_id": "536599840742834186", + "type": 0, + "name": "lore-presentation", + "position": 101, + "parent_id": "449088445617405962", + "topic": "Curated lore-based content written by our lore Chroniclers.", + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1051648 + }, + { + "id": "382117075558203392", + "type": "role", + "allow": 1049600, + "deny": 0 + }, + { + "id": "382118099341541379", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "518267216408346624", + "last_message_id": "664860385119174657", + "last_pin_timestamp": "2019-11-29T00:31:52.550000+00:00", + "type": 0, + "name": "talk-to-staff", + "position": 9, + "parent_id": "381911060493500416", + "topic": "Ask questions or request assistance from server and clan staff. Please keep the channel professional and check pinned messages.", + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 131137 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 64, + "deny": 0 + }, + { + "id": "463520715254267916", + "type": "role", + "allow": 0, + "deny": 3072 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 64, + "deny": 0 + }, + { + "id": "530919353999163414", + "type": "role", + "allow": 2048, + "deny": 0 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 64, + "deny": 0 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "518267455659966465", + "last_message_id": "518272480293683201", + "type": 0, + "name": "what-we-define-as-active", + "position": 95, + "parent_id": "449088445617405962", + "topic": null, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1051648 + }, + { + "id": "382117075558203392", + "type": "role", + "allow": 1049600, + "deny": 0 + }, + { + "id": "382118099341541379", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "518871349125447701", + "type": 4, + "name": "Applications", + "position": 2, + "parent_id": null, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "530919353999163414", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false + }, + { + "id": "519019593092825088", + "last_message_id": "657044122556956673", + "type": 0, + "name": "staff-links-resources", + "position": 50, + "parent_id": "382114317857849344", + "topic": "Cool beans. The coolest beans.", + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1089 + }, + { + "id": "382118099341541379", + "type": "role", + "allow": 1024, + "deny": 2048 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 3072, + "deny": 0 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 1024, + "deny": 2048 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 3072, + "deny": 0 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "519243467298177035", + "last_message_id": "539168206288322579", + "type": 0, + "name": "faq-old", + "position": 105, + "parent_id": "449088445617405962", + "topic": "", + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1051648 + }, + { + "id": "382117075558203392", + "type": "role", + "allow": 1049600, + "deny": 0 + }, + { + "id": "382118099341541379", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "519246529295286275", + "last_message_id": "554403043093905458", + "type": 0, + "name": "admin-coco-accounts", + "position": 59, + "parent_id": "382114317857849344", + "topic": null, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1025 + }, + { + "id": "382116615614889984", + "type": "role", + "allow": 0, + "deny": 0 + }, + { + "id": "382117075558203392", + "type": "role", + "allow": 0, + "deny": 0 + }, + { + "id": "382118099341541379", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "463520715254267916", + "type": "role", + "allow": 0, + "deny": 3072 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "519592861776478228", + "last_message_id": "523675676419555328", + "type": 0, + "name": "forge-requests", + "position": 86, + "parent_id": "449088445617405962", + "topic": null, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1051648 + }, + { + "id": "382117075558203392", + "type": "role", + "allow": 1049600, + "deny": 0 + }, + { + "id": "382118099341541379", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "520339423829950484", + "last_message_id": "522213071847030785", + "type": 0, + "name": "peer-edits", + "position": 79, + "parent_id": "449088445617405962", + "topic": null, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1051648 + }, + { + "id": "382117075558203392", + "type": "role", + "allow": 1049600, + "deny": 0 + }, + { + "id": "382118099341541379", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "520399992318590996", + "last_message_id": "523675436853624832", + "last_pin_timestamp": "2018-12-12T00:18:35.037000+00:00", + "type": 0, + "name": "scourge-of-the-past-lfg", + "position": 100, + "parent_id": "449088445617405962", + "topic": null, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1051648 + }, + { + "id": "382117075558203392", + "type": "role", + "allow": 1049600, + "deny": 0 + }, + { + "id": "382118099341541379", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "522227695577333770", + "last_message_id": "534116355126067201", + "type": 0, + "name": "chronicler-chat", + "position": 102, + "parent_id": "449088445617405962", + "topic": null, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "336630009546407937", + "type": "member", + "allow": 0, + "deny": 0 + }, + { + "id": "382116615614889984", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "382117075558203392", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "530919353999163414", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "526617610519838741", + "type": 4, + "name": "Sherpa Channels", + "position": 16, + "parent_id": null, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "469631935753355264", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 1024, + "deny": 0 + } + ], + "nsfw": false + }, + { + "id": "530986285712670729", + "last_message_id": "570314690459336704", + "type": 0, + "name": "pve-guide-applications-old", + "position": 70, + "parent_id": "449088445617405962", + "topic": "", + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1051648 + }, + { + "id": "382117075558203392", + "type": "role", + "allow": 1049600, + "deny": 0 + }, + { + "id": "382118099341541379", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "531000532589084692", + "last_message_id": "664299552744013824", + "last_pin_timestamp": "2019-12-05T16:43:16.751000+00:00", + "type": 0, + "name": "moderator-chat", + "position": 55, + "parent_id": "382114317857849344", + "topic": "", + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1025 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "531647598700920832", + "type": 2, + "name": "Ninja C", + "position": 37, + "parent_id": "510476994476113930", + "bitrate": 64000, + "user_limit": 0, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1049600 + }, + { + "id": "382118099341541379", + "type": "role", + "allow": 1049600, + "deny": 0 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 1049600, + "deny": 0 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 17826816, + "deny": 0 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 1049600, + "deny": 0 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 1048576 + } + ], + "nsfw": false + }, + { + "id": "531663092862418965", + "last_message_id": "538274610060460043", + "type": 0, + "name": "analytics-old", + "position": 98, + "parent_id": "449088445617405962", + "topic": "", + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1051648 + }, + { + "id": "382117075558203392", + "type": "role", + "allow": 1049600, + "deny": 0 + }, + { + "id": "382118099341541379", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "533482437863276555", + "type": 2, + "name": "Moderator Voice", + "position": 38, + "parent_id": "510476994476113930", + "bitrate": 64000, + "user_limit": 0, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1049600 + }, + { + "id": "382118099341541379", + "type": "role", + "allow": 0, + "deny": 1049600 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 1049600, + "deny": 0 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 1049600, + "deny": 0 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 1049600, + "deny": 0 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 1048576 + } + ], + "nsfw": false + }, + { + "id": "533493079970152463", + "last_message_id": "551876572118777882", + "last_pin_timestamp": "2019-02-25T17:04:02.281000+00:00", + "type": 0, + "name": "guide-requests", + "position": 76, + "parent_id": "449088445617405962", + "topic": "Request help from Raid Sherpas, PvE Guides, and PvP Guides here.", + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1051648 + }, + { + "id": "382117075558203392", + "type": "role", + "allow": 1049600, + "deny": 0 + }, + { + "id": "382118099341541379", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "536713209160269824", + "last_message_id": "664890932616888340", + "last_pin_timestamp": "2020-01-03T04:39:19.212000+00:00", + "type": 0, + "name": "staff-general", + "position": 53, + "parent_id": "382114317857849344", + "topic": "Basically just memes, use #trial-moderator-chat for serious stuff.", + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 11264 + }, + { + "id": "382118099341541379", + "type": "role", + "allow": 519232, + "deny": 0 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 519232, + "deny": 0 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 11264, + "deny": 0 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 519232, + "deny": 0 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "536998280358264847", + "last_message_id": "553570653999988737", + "type": 0, + "name": "hub-member-code", + "position": 71, + "parent_id": "449088445617405962", + "topic": "", + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1051648 + }, + { + "id": "382117075558203392", + "type": "role", + "allow": 1049600, + "deny": 0 + }, + { + "id": "382118099341541379", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "539634611886489630", + "last_message_id": "552946844733538304", + "type": 0, + "name": "faq-4-21-19", + "position": 68, + "parent_id": "449088445617405962", + "topic": "", + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1051648 + }, + { + "id": "382117075558203392", + "type": "role", + "allow": 1049600, + "deny": 0 + }, + { + "id": "382118099341541379", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "539637181669572609", + "last_message_id": "664765445890506752", + "last_pin_timestamp": "2019-10-24T08:18:24.221000+00:00", + "type": 0, + "name": "raid-scheduling-bot", + "position": 30, + "parent_id": "381910837293613057", + "topic": "Use this channel for scheduling a FUTURE raid ONLY. If you wish to LFG for a raid NOW, please use #lfg-requests.", + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 3072, + "deny": 0 + }, + { + "id": "463520715254267916", + "type": "role", + "allow": 0, + "deny": 3072 + }, + { + "id": "530919353999163414", + "type": "role", + "allow": 0, + "deny": 2099200 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "539639035795210246", + "last_message_id": "662512213873000479", + "last_pin_timestamp": "2019-08-01T15:10:27.755000+00:00", + "type": 0, + "name": "legend-voting", + "position": 21, + "parent_id": "485961711535390720", + "topic": "", + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 3136 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 3148864, + "deny": 0 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "538477449130803212", + "type": "role", + "allow": 1024, + "deny": 2112 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 1088, + "deny": 0 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "544594107197161482", + "last_message_id": "664889744831807538", + "last_pin_timestamp": "2020-01-02T17:07:34.185000+00:00", + "type": 0, + "name": "pvp-talk", + "position": 27, + "parent_id": "381910837293613057", + "topic": "Place to discuss meta guns, best loadouts, strats etc, and to ask for advice from our PvP guides. NOT a place to ask for carries. Please do not use @here in this chat!", + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 131072 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 131072, + "deny": 0 + }, + { + "id": "463520715254267916", + "type": "role", + "allow": 0, + "deny": 3072 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 131072, + "deny": 0 + }, + { + "id": "530919353999163414", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 131072, + "deny": 0 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "544600171913412638", + "last_message_id": "551543634642862110", + "last_pin_timestamp": "2019-02-11T19:44:40.158000+00:00", + "type": 0, + "name": "fanart-channel", + "position": 77, + "parent_id": "449088445617405962", + "topic": null, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1051648 + }, + { + "id": "382117075558203392", + "type": "role", + "allow": 1049600, + "deny": 0 + }, + { + "id": "382118099341541379", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "544600393011822602", + "last_message_id": "627666417550098464", + "type": 0, + "name": "giveaways", + "position": 20, + "parent_id": "485961711535390720", + "topic": null, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 64, + "deny": 1051649 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 3072, + "deny": 0 + }, + { + "id": "538477449130803212", + "type": "role", + "allow": 1024, + "deny": 2049 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 3072 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "544607799393517569", + "last_message_id": "664634987324375042", + "type": 0, + "name": "support-your-community", + "position": 3, + "parent_id": null, + "topic": "For YouTube or Twitch streams", + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 64, + "deny": 2048 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "550739041293697024", + "last_message_id": "557282142938202114", + "last_pin_timestamp": "2019-03-15T18:29:04.585000+00:00", + "type": 0, + "name": "jokers-wild-discussion", + "position": 74, + "parent_id": "449088445617405962", + "topic": "A chat for discussion about Jokers Wild! Please keep all expansion discussion here", + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1051648 + }, + { + "id": "382117075558203392", + "type": "role", + "allow": 1049600, + "deny": 0 + }, + { + "id": "382118099341541379", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "551504554437509120", + "last_message_id": "656325286144507904", + "type": 0, + "name": "feedback-bot-responses", + "position": 61, + "parent_id": "485882029158826019", + "topic": "Feedback from the community.", + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 3072 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 3072, + "deny": 0 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 1024, + "deny": 2048 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 3072, + "deny": 0 + }, + { + "id": "548453282960375859", + "type": "member", + "allow": 388160, + "deny": 0 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "551599760675438595", + "last_message_id": "640303771536195584", + "type": 0, + "name": "score-submission", + "position": 40, + "parent_id": "552220299521818674", + "topic": null, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "382118099341541379", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "530919353999163414", + "type": "role", + "allow": 0, + "deny": 1050624 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "551597061955387403", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "551600678816972815", + "last_message_id": "644349377271431179", + "last_pin_timestamp": "2019-10-29T13:59:32.759000+00:00", + "type": 0, + "name": "tournament-lfg", + "position": 39, + "parent_id": "552220299521818674", + "topic": "", + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 132096 + }, + { + "id": "382118099341541379", + "type": "role", + "allow": 1024, + "deny": 131072 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "502964473674465300", + "type": "role", + "allow": 3072, + "deny": 0 + }, + { + "id": "530919353999163414", + "type": "role", + "allow": 0, + "deny": 1051648 + }, + { + "id": "538477449130803212", + "type": "role", + "allow": 2048, + "deny": 1024 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "551597061955387403", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "564516531082362880", + "type": "role", + "allow": 131072, + "deny": 0 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 3072 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "551600808387543061", + "last_message_id": "640249183517016065", + "last_pin_timestamp": "2019-06-15T21:09:22.317000+00:00", + "type": 0, + "name": "teams", + "position": 37, + "parent_id": "552220299521818674", + "topic": "Determine the teams here. Post RNG team pics.", + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 2048, + "deny": 1024 + }, + { + "id": "382118099341541379", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "530919353999163414", + "type": "role", + "allow": 0, + "deny": 1050624 + }, + { + "id": "538477449130803212", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "551597061955387403", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "551825969346773022", + "type": 2, + "name": "Game Night 1", + "position": 1, + "parent_id": "485961711535390720", + "bitrate": 202000, + "user_limit": 12, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 3146752, + "deny": 0 + }, + { + "id": "463520715254267916", + "type": "role", + "allow": 3146752, + "deny": 0 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 1049600, + "deny": 0 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 1048576 + } + ], + "nsfw": false + }, + { + "id": "551866300754952215", + "type": 4, + "name": "Strikes", + "position": 7, + "parent_id": null, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "463520715254267916", + "type": "role", + "allow": 0, + "deny": 1050624 + }, + { + "id": "530919353999163414", + "type": "role", + "allow": 0, + "deny": 1048576 + } + ], + "nsfw": false + }, + { + "id": "551868604350464011", + "type": 4, + "name": "Gambit", + "position": 10, + "parent_id": null, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "463520715254267916", + "type": "role", + "allow": 0, + "deny": 1048576 + }, + { + "id": "530919353999163414", + "type": "role", + "allow": 0, + "deny": 1048576 + } + ], + "nsfw": false + }, + { + "id": "551883047021183020", + "last_message_id": "662515150582186004", + "last_pin_timestamp": "2019-12-08T18:13:46.292000+00:00", + "type": 0, + "name": "game-night-text", + "position": 23, + "parent_id": "485961711535390720", + "topic": null, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 3148800, + "deny": 0 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 3072, + "deny": 0 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 3072, + "deny": 0 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "552220299521818674", + "type": 4, + "name": "Scrims", + "position": 15, + "parent_id": null, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "382118099341541379", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "530919353999163414", + "type": "role", + "allow": 0, + "deny": 1050624 + }, + { + "id": "538477449130803212", + "type": "role", + "allow": 3072, + "deny": 0 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "551597061955387403", + "type": "role", + "allow": 0, + "deny": 1024 + } + ], + "nsfw": false + }, + { + "id": "552509209133056000", + "last_message_id": "557260938722672666", + "type": 0, + "name": "gambit-prime-requests", + "position": 82, + "parent_id": "449088445617405962", + "topic": "A place for people to find gambit prime and reckoning groups. Not for general discussion.", + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1051648 + }, + { + "id": "382117075558203392", + "type": "role", + "allow": 1049600, + "deny": 0 + }, + { + "id": "382118099341541379", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "552662715768897536", + "last_message_id": "556668222296948741", + "last_pin_timestamp": "2019-03-13T22:34:51.930000+00:00", + "type": 0, + "name": "screenshot-chat", + "position": 113, + "parent_id": "449088445617405962", + "topic": "Send here screenshots of your destiny gameplay, or just funny stuff you saw happen in destiny with you and your friend. Screenshots for coco website only please.", + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 3072 + }, + { + "id": "463520715254267916", + "type": "role", + "allow": 0, + "deny": 3072 + }, + { + "id": "530919353999163414", + "type": "role", + "allow": 0, + "deny": 2099200 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "555713073290149888", + "type": 2, + "name": "Orbit", + "position": 3, + "parent_id": "579868728410505276", + "bitrate": 64000, + "user_limit": 0, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 0 + }, + { + "id": "463520715254267916", + "type": "role", + "allow": 0, + "deny": 1048576 + }, + { + "id": "530919353999163414", + "type": "role", + "allow": 0, + "deny": 1048576 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 1048576 + } + ], + "nsfw": false + }, + { + "id": "556681307447492609", + "type": 2, + "name": "Leader Voice", + "position": 39, + "parent_id": "510476994476113930", + "bitrate": 64000, + "user_limit": 0, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1049600 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 1049600, + "deny": 0 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 1049600, + "deny": 0 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 1048576 + } + ], + "nsfw": false + }, + { + "id": "559205358103691304", + "last_message_id": "576463460552933406", + "last_pin_timestamp": "2019-03-28T23:09:00.391000+00:00", + "type": 0, + "name": "dnd-text", + "position": 103, + "parent_id": "449088445617405962", + "topic": null, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1051648 + }, + { + "id": "382117075558203392", + "type": "role", + "allow": 1049600, + "deny": 0 + }, + { + "id": "382118099341541379", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "560973436558508052", + "last_message_id": "562113337870123008", + "last_pin_timestamp": "2019-03-31T01:51:07.700000+00:00", + "type": 0, + "name": "dnd-resources", + "position": 108, + "parent_id": "449088445617405962", + "topic": null, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "382118099341541379", + "type": "role", + "allow": 0, + "deny": 3072 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 0, + "deny": 3072 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "559215320435523617", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "561750420242825216", + "last_message_id": "561906338654060564", + "type": 0, + "name": "dnd-suggestions", + "position": 104, + "parent_id": "449088445617405962", + "topic": null, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1051648 + }, + { + "id": "382117075558203392", + "type": "role", + "allow": 1049600, + "deny": 0 + }, + { + "id": "382118099341541379", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "562017811556204554", + "last_message_id": "652141817181110282", + "type": 0, + "name": "crucible-coach-application", + "position": 67, + "parent_id": "619529638699335685", + "topic": "", + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 134208 + }, + { + "id": "381911719901134850", + "type": "role", + "allow": 65536, + "deny": 3072 + }, + { + "id": "382118099341541379", + "type": "role", + "allow": 3072, + "deny": 0 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 3072, + "deny": 0 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 133120, + "deny": 1024 + }, + { + "id": "530919353999163414", + "type": "role", + "allow": 0, + "deny": 2112 + }, + { + "id": "538477449130803212", + "type": "role", + "allow": 0, + "deny": 3072 + }, + { + "id": "562036125590618117", + "type": "role", + "allow": 0, + "deny": 0 + }, + { + "id": "564516531082362880", + "type": "role", + "allow": 126016, + "deny": 0 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "562041698650161153", + "type": 4, + "name": "Guide Channels", + "position": 17, + "parent_id": null, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1049600 + }, + { + "id": "382118099341541379", + "type": "role", + "allow": 1049600, + "deny": 0 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 1049600, + "deny": 0 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 1049600, + "deny": 0 + }, + { + "id": "502964473674465300", + "type": "role", + "allow": 1049600, + "deny": 0 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 1049600, + "deny": 0 + }, + { + "id": "562036125590618117", + "type": "role", + "allow": 1049600, + "deny": 0 + }, + { + "id": "562038106166001714", + "type": "role", + "allow": 1049600, + "deny": 0 + } + ], + "nsfw": false + }, + { + "id": "562041750559129643", + "last_message_id": "664810765433307136", + "last_pin_timestamp": "2019-11-14T00:37:44.532000+00:00", + "type": 0, + "name": "crucible-coach-chat", + "position": 48, + "parent_id": "562041698650161153", + "topic": "", + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1049600 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 1048576, + "deny": 0 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 0, + "deny": 3072 + }, + { + "id": "502964473674465300", + "type": "role", + "allow": 1051648, + "deny": 0 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 1049600, + "deny": 0 + }, + { + "id": "562036125590618117", + "type": "role", + "allow": 1048576, + "deny": 3072 + }, + { + "id": "562038106166001714", + "type": "role", + "allow": 1048576, + "deny": 3088 + }, + { + "id": "564516531082362880", + "type": "role", + "allow": 268438544, + "deny": 0 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "562042488735399966", + "type": 2, + "name": "Crucible Guide Voice", + "position": 33, + "parent_id": "562041698650161153", + "bitrate": 64000, + "user_limit": 0, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 2097152, + "deny": 1049600 + }, + { + "id": "382118099341541379", + "type": "role", + "allow": 0, + "deny": 1049600 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 1048576, + "deny": 1024 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 0, + "deny": 1049600 + }, + { + "id": "502964473674465300", + "type": "role", + "allow": 0, + "deny": 1049600 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 1049600, + "deny": 0 + }, + { + "id": "562036125590618117", + "type": "role", + "allow": 1049600, + "deny": 0 + }, + { + "id": "562038106166001714", + "type": "role", + "allow": 573572096, + "deny": 297795601 + }, + { + "id": "564516531082362880", + "type": "role", + "allow": 334496785, + "deny": 0 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 1048576 + } + ], + "nsfw": false + }, + { + "id": "562100175460630538", + "last_message_id": "664886245410996283", + "last_pin_timestamp": "2020-01-03T16:48:08.703000+00:00", + "type": 0, + "name": "crucible-guide-chat", + "position": 47, + "parent_id": "562041698650161153", + "topic": "", + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1049600 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 1049600, + "deny": 8192 + }, + { + "id": "502964473674465300", + "type": "role", + "allow": 1048576, + "deny": 1024 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 1049600, + "deny": 0 + }, + { + "id": "562036125590618117", + "type": "role", + "allow": 1048576, + "deny": 1024 + }, + { + "id": "562038106166001714", + "type": "role", + "allow": 1051648, + "deny": 8192 + }, + { + "id": "564516531082362880", + "type": "role", + "allow": 142352, + "deny": 0 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "562109664133644299", + "type": 2, + "name": "Crucible Coach Voice", + "position": 34, + "parent_id": "562041698650161153", + "bitrate": 64000, + "user_limit": 0, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1049600 + }, + { + "id": "381911719901134850", + "type": "role", + "allow": 0, + "deny": 3146752 + }, + { + "id": "382118099341541379", + "type": "role", + "allow": 0, + "deny": 1049600 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 1049600, + "deny": 0 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 0, + "deny": 1049600 + }, + { + "id": "502964473674465300", + "type": "role", + "allow": 3146752, + "deny": 0 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 1049600, + "deny": 0 + }, + { + "id": "562036125590618117", + "type": "role", + "allow": 0, + "deny": 1049600 + }, + { + "id": "562038106166001714", + "type": "role", + "allow": 0, + "deny": 3146768 + }, + { + "id": "564516531082362880", + "type": "role", + "allow": 271582224, + "deny": 0 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 1048576 + } + ], + "nsfw": false + }, + { + "id": "562443185989615617", + "last_message_id": "574333285975588884", + "last_pin_timestamp": "2019-04-11T17:27:15.625000+00:00", + "type": 0, + "name": "lore-discussion", + "position": 111, + "parent_id": "449088445617405962", + "topic": "A channel dedicated to those who take it upon themselves to learn the lore of Destiny.", + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "463520715254267916", + "type": "role", + "allow": 0, + "deny": 3072 + }, + { + "id": "530919353999163414", + "type": "role", + "allow": 0, + "deny": 2099200 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 10 + }, + { + "id": "563290422907109376", + "type": 2, + "name": "Sherpa Voice", + "position": 30, + "parent_id": "526617610519838741", + "bitrate": 64000, + "user_limit": 0, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "469631935753355264", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 1048576 + } + ], + "nsfw": false + }, + { + "id": "563820866358476839", + "last_message_id": "592502089482240039", + "last_pin_timestamp": "2019-04-05T20:28:54.348000+00:00", + "type": 0, + "name": "stormbot-dev", + "position": 66, + "parent_id": "485882029158826019", + "topic": "Only for members working on StormBot", + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 3072 + }, + { + "id": "442060343162699776", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "563821431079829535", + "type": "role", + "allow": 3072, + "deny": 0 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "564512049229463611", + "type": 4, + "name": "Streamer Channels", + "position": 26, + "parent_id": null, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1049600 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 1049600, + "deny": 0 + }, + { + "id": "564512695156736010", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "564512855790059545", + "type": "role", + "allow": 1024, + "deny": 0 + } + ], + "nsfw": false + }, + { + "id": "564512421990105109", + "type": 2, + "name": "Streamer Voice", + "position": 48, + "parent_id": "564512049229463611", + "bitrate": 64000, + "user_limit": 0, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1049600 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 1049600, + "deny": 0 + }, + { + "id": "564512695156736010", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "564512855790059545", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 1048576 + } + ], + "nsfw": false + }, + { + "id": "564512544614514699", + "last_message_id": "564882343513554964", + "last_pin_timestamp": "2019-04-08T17:31:43.789000+00:00", + "type": 0, + "name": "streamer-chat", + "position": 131, + "parent_id": "564512049229463611", + "topic": "General discussion for the clan Twitch streamers.", + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1049600 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 1049600, + "deny": 0 + }, + { + "id": "564512695156736010", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "564512855790059545", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "564512595789217811", + "last_message_id": "601202301280976901", + "type": 0, + "name": "streamer-resources", + "position": 132, + "parent_id": "564512049229463611", + "topic": "A collection of resources for the clan Twitch streamers to use when streaming.", + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "565942508216123393", + "last_message_id": "664879870383489035", + "last_pin_timestamp": "2019-12-13T22:09:31.780000+00:00", + "type": 0, + "name": "talk-with-ai", + "position": 29, + "parent_id": "381910837293613057", + "topic": "Deliberately trying to rate limit, or bug the bot will result in your permissions to this channel being revoked.", + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 0 + }, + { + "id": "279738762559225856", + "type": "member", + "allow": 0, + "deny": 3072 + }, + { + "id": "382117075558203392", + "type": "role", + "allow": 3072, + "deny": 0 + }, + { + "id": "382118099341541379", + "type": "role", + "allow": 3072, + "deny": 0 + }, + { + "id": "382128710779928576", + "type": "role", + "allow": 3072, + "deny": 0 + }, + { + "id": "442060343162699776", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 3072, + "deny": 0 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 3072, + "deny": 0 + }, + { + "id": "530919353999163414", + "type": "role", + "allow": 0, + "deny": 2112 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 3072, + "deny": 0 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 15 + }, + { + "id": "566694214000312338", + "type": 2, + "name": "Tournament 1", + "position": 31, + "parent_id": "552220299521818674", + "bitrate": 64000, + "user_limit": 3, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "382118099341541379", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "530919353999163414", + "type": "role", + "allow": 0, + "deny": 1050624 + }, + { + "id": "538477449130803212", + "type": "role", + "allow": 2048, + "deny": 1024 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "551597061955387403", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 1048576 + } + ], + "nsfw": false + }, + { + "id": "566849660531113984", + "type": 4, + "name": "Gambit Tournament", + "position": 25, + "parent_id": null, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "382118099341541379", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 1024, + "deny": 0 + } + ], + "nsfw": false + }, + { + "id": "566850260756987904", + "last_message_id": "572452154489438210", + "type": 0, + "name": "tournament-talk", + "position": 127, + "parent_id": "566849660531113984", + "topic": "Discuss about the gambit tournament. Ask questions, talk about gambit loadouts, tips. Everything!", + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "382118099341541379", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "566850292843282443", + "last_message_id": "572193214484971520", + "type": 0, + "name": "gambit-tournament-find-a-team-only", + "position": 129, + "parent_id": "566849660531113984", + "topic": "Find a team for the gambit tournament.\nPlease don't use @everyone in here.", + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "382118099341541379", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "566850480190521344", + "type": 2, + "name": "Waiting Room", + "position": 46, + "parent_id": "566849660531113984", + "bitrate": 64000, + "user_limit": 0, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "382118099341541379", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 1048576 + } + ], + "nsfw": false + }, + { + "id": "566851142823444496", + "last_message_id": "569129323009933312", + "type": 0, + "name": "tournament-rules", + "position": 130, + "parent_id": "566849660531113984", + "topic": "Tournament rules.", + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "382118099341541379", + "type": "role", + "allow": 0, + "deny": 3072 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 3072, + "deny": 0 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 0, + "deny": 3072 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 3072, + "deny": 0 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "567012072538505216", + "last_message_id": "567059408069197825", + "type": 0, + "name": "score-submission", + "position": 128, + "parent_id": "566849660531113984", + "topic": "Gambit team captains will send proof of winning / losing a match in here.", + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "382118099341541379", + "type": "role", + "allow": 0, + "deny": 3072 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 0, + "deny": 3072 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "567496802434154537", + "type": 2, + "name": "PvE Guide Voice", + "position": 32, + "parent_id": "562041698650161153", + "bitrate": 64000, + "user_limit": 0, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1049600 + }, + { + "id": "314778031631892492", + "type": "member", + "allow": 1049600, + "deny": 0 + }, + { + "id": "361990149648678913", + "type": "member", + "allow": 1024, + "deny": 0 + }, + { + "id": "382118099341541379", + "type": "role", + "allow": 1048576, + "deny": 1024 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 1048576, + "deny": 1024 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 1048576, + "deny": 1024 + }, + { + "id": "531004642612477964", + "type": "role", + "allow": 3146752, + "deny": 0 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 1048576, + "deny": 1024 + }, + { + "id": "564601276500017153", + "type": "role", + "allow": 3146752, + "deny": 0 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 1048576 + } + ], + "nsfw": false + }, + { + "id": "567501194508107808", + "last_message_id": "662042299118911518", + "last_pin_timestamp": "2019-11-26T14:22:42.218000+00:00", + "type": 0, + "name": "pve-guide-chat", + "position": 46, + "parent_id": "562041698650161153", + "topic": null, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "382118099341541379", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 3072, + "deny": 0 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 3072, + "deny": 0 + }, + { + "id": "531004642612477964", + "type": "role", + "allow": 3072, + "deny": 0 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "564601276500017153", + "type": "role", + "allow": 11264, + "deny": 0 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "567802274035007518", + "type": 2, + "name": "Gambit Team 1", + "position": 47, + "parent_id": "566849660531113984", + "bitrate": 64000, + "user_limit": 0, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "382118099341541379", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 1048576 + } + ], + "nsfw": false + }, + { + "id": "569563584221282318", + "last_message_id": "585609965897777163", + "type": 0, + "name": "faq", + "position": 8, + "parent_id": "381911060493500416", + "topic": null, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 1024, + "deny": 14336 + }, + { + "id": "382117075558203392", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "382118099341541379", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 11264, + "deny": 0 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 11264, + "deny": 0 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "569710520974966794", + "last_message_id": "649480824479285250", + "type": 0, + "name": "sherpa-application", + "position": 15, + "parent_id": "518871349125447701", + "topic": "Pre-Requisites: Have 10+ clears on 3 raids, 1 of those being the most recent raid", + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 3072, + "deny": 131136 + }, + { + "id": "209401759787909120", + "type": "member", + "allow": 0, + "deny": 2048 + }, + { + "id": "530919353999163414", + "type": "role", + "allow": 131072, + "deny": 2048 + }, + { + "id": "565734337819967489", + "type": "role", + "allow": 8256, + "deny": 0 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "570782335755223067", + "last_message_id": "575677156055580694", + "type": 0, + "name": "sherpa-resources", + "position": 43, + "parent_id": "526617610519838741", + "topic": null, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 1024, + "deny": 2048 + }, + { + "id": "469631935753355264", + "type": "role", + "allow": 1024, + "deny": 2048 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 1024, + "deny": 2048 + }, + { + "id": "482625494886645781", + "type": "role", + "allow": 3072, + "deny": 0 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 1024, + "deny": 2048 + }, + { + "id": "565734337819967489", + "type": "role", + "allow": 3072, + "deny": 0 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "570786215087833108", + "last_message_id": "657663197406429215", + "type": 0, + "name": "sherpa-announcements", + "position": 41, + "parent_id": "526617610519838741", + "topic": null, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 1024, + "deny": 2048 + }, + { + "id": "469631935753355264", + "type": "role", + "allow": 1024, + "deny": 2048 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 1024, + "deny": 2048 + }, + { + "id": "482625494886645781", + "type": "role", + "allow": 3072, + "deny": 0 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 1024, + "deny": 2048 + }, + { + "id": "565734337819967489", + "type": "role", + "allow": 3072, + "deny": 0 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "573199920455090176", + "last_message_id": "662031124800864256", + "last_pin_timestamp": "2019-11-20T02:52:21.664000+00:00", + "type": 0, + "name": "bot-admin-commands", + "position": 65, + "parent_id": "485882029158826019", + "topic": null, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "382118099341541379", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "567781687874945028", + "type": "role", + "allow": 52224, + "deny": 0 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "573880802685747201", + "type": 2, + "name": "Protocol / Well Chat", + "position": 4, + "parent_id": "579868728410505276", + "bitrate": 64000, + "user_limit": 9, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 0 + }, + { + "id": "463520715254267916", + "type": "role", + "allow": 0, + "deny": 1048576 + }, + { + "id": "530919353999163414", + "type": "role", + "allow": 0, + "deny": 1048576 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 1048576 + } + ], + "nsfw": false + }, + { + "id": "573885121485340682", + "last_message_id": "664384742434799618", + "type": 0, + "name": "pve-guide-application", + "position": 14, + "parent_id": "518871349125447701", + "topic": "", + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 3072, + "deny": 131072 + }, + { + "id": "530919353999163414", + "type": "role", + "allow": 0, + "deny": 2112 + }, + { + "id": "564601276500017153", + "type": "role", + "allow": 8192, + "deny": 0 + }, + { + "id": "576790479392342016", + "type": "role", + "allow": 0, + "deny": 0 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "576134290509660170", + "last_message_id": "664576172142624769", + "last_pin_timestamp": "2019-05-09T19:54:44.928000+00:00", + "type": 0, + "name": "activity-check-notifications", + "position": 2, + "parent_id": null, + "topic": null, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "156533924716216320", + "type": "member", + "allow": 134144, + "deny": 0 + }, + { + "id": "162706186272112640", + "type": "role", + "allow": 66560, + "deny": 805763152 + }, + { + "id": "376219751476887562", + "type": "member", + "allow": 2048, + "deny": 0 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 3072, + "deny": 0 + }, + { + "id": "538477449130803212", + "type": "role", + "allow": 1024, + "deny": 805763152 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 3072, + "deny": 0 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "578978120334442506", + "last_message_id": "583695880893497355", + "type": 0, + "name": "clan-links-5-30-19", + "position": 75, + "parent_id": "449088445617405962", + "topic": "", + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1051648 + }, + { + "id": "382117075558203392", + "type": "role", + "allow": 1049600, + "deny": 0 + }, + { + "id": "382118099341541379", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "579715674151321611", + "type": 2, + "name": "Game Night 2", + "position": 2, + "parent_id": "485961711535390720", + "bitrate": 64000, + "user_limit": 10, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1049600 + }, + { + "id": "381911719901134850", + "type": "role", + "allow": 0, + "deny": 0 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 3146752, + "deny": 0 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 1049600, + "deny": 0 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 1048576 + } + ], + "nsfw": false + }, + { + "id": "579868209835147281", + "type": 4, + "name": "Raid", + "position": 6, + "parent_id": null, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "463520715254267916", + "type": "role", + "allow": 0, + "deny": 1048576 + }, + { + "id": "530919353999163414", + "type": "role", + "allow": 0, + "deny": 1048576 + } + ], + "nsfw": false + }, + { + "id": "579868728410505276", + "type": 4, + "name": "FireTeam Chats", + "position": 5, + "parent_id": null, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "463520715254267916", + "type": "role", + "allow": 0, + "deny": 1048576 + }, + { + "id": "530919353999163414", + "type": "role", + "allow": 0, + "deny": 1048576 + } + ], + "nsfw": false + }, + { + "id": "583416988152627212", + "last_message_id": "592543240952086578", + "type": 0, + "name": "weekly-clan-advertisement", + "position": 69, + "parent_id": "449088445617405962", + "topic": null, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1088 + }, + { + "id": "382117075558203392", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "538477449130803212", + "type": "role", + "allow": 0, + "deny": 3136 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "585549159705346120", + "type": 2, + "name": "Chronic Corner", + "position": 22, + "parent_id": "382125435376631820", + "bitrate": 111000, + "user_limit": 0, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "463520715254267916", + "type": "role", + "allow": 0, + "deny": 3148800 + }, + { + "id": "530919353999163414", + "type": "role", + "allow": 0, + "deny": 1048576 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 1048576 + } + ], + "nsfw": false + }, + { + "id": "586324559062630420", + "type": 4, + "name": "Menagerie", + "position": 9, + "parent_id": null, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "463520715254267916", + "type": "role", + "allow": 0, + "deny": 1048576 + }, + { + "id": "530919353999163414", + "type": "role", + "allow": 0, + "deny": 1048576 + }, + { + "id": "579865256327643141", + "type": "role", + "allow": 268436496, + "deny": 0 + } + ], + "nsfw": false + }, + { + "id": "587034851031384065", + "last_message_id": "634087295531614208", + "last_pin_timestamp": "2019-10-16T14:57:10.943000+00:00", + "type": 0, + "name": "shadowkeep-spoilers", + "position": 120, + "parent_id": "619529638699335685", + "topic": "*Spoiler Alert* @JeBaited#1342 with any questions. Repeat post = delete | Spoilers outside go as Warning ---> Chat ban", + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1051648 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 1049600, + "deny": 0 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "590564776057241610", + "type": 4, + "name": "Iron Banner", + "position": 22, + "parent_id": null, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "530919353999163414", + "type": "role", + "allow": 0, + "deny": 1049600 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "579865256327643141", + "type": "role", + "allow": 16, + "deny": 0 + } + ], + "nsfw": false + }, + { + "id": "597659463272628244", + "last_message_id": "664288725546565642", + "last_pin_timestamp": "2019-12-07T03:31:21.512000+00:00", + "type": 0, + "name": "adverts", + "position": 51, + "parent_id": "382114317857849344", + "topic": null, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "382116615614889984", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "382117075558203392", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "382118099341541379", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "598679640122327051", + "last_message_id": "618147052584566960", + "type": 0, + "name": "clan-links-9-4-19", + "position": 124, + "parent_id": "619529638699335685", + "topic": "", + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 3072 + }, + { + "id": "181383334457376769", + "type": "member", + "allow": 3072, + "deny": 0 + }, + { + "id": "382117075558203392", + "type": "role", + "allow": 3072, + "deny": 0 + }, + { + "id": "382118099341541379", + "type": "role", + "allow": 0, + "deny": 3072 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 3072, + "deny": 0 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 0, + "deny": 3072 + }, + { + "id": "538477449130803212", + "type": "role", + "allow": 0, + "deny": 3072 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "599341089194770443", + "last_message_id": "663405680836739082", + "type": 0, + "name": "trial-moderator-applications", + "position": 16, + "parent_id": "518871349125447701", + "topic": null, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 805825617 + }, + { + "id": "382117075558203392", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "538477449130803212", + "type": "role", + "allow": 68608, + "deny": 1 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "601945058215723038", + "last_message_id": "653385643552145431", + "last_pin_timestamp": "2019-07-20T01:21:56.751000+00:00", + "type": 0, + "name": "ask-the-admins", + "position": 6, + "parent_id": null, + "topic": null, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 3072 + }, + { + "id": "463520715254267916", + "type": "role", + "allow": 0, + "deny": 3148800 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "601951874295267349", + "type": 2, + "name": "Club Collective (Music)", + "position": 21, + "parent_id": "382125435376631820", + "bitrate": 384000, + "user_limit": 0, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "463520715254267916", + "type": "role", + "allow": 3146752, + "deny": 2048 + }, + { + "id": "530919353999163414", + "type": "role", + "allow": 0, + "deny": 1048576 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 1048576 + } + ], + "nsfw": false + }, + { + "id": "602695133107978250", + "last_message_id": "602842254217969668", + "type": 0, + "name": "━━━━━━━━", + "position": 22, + "parent_id": "485961711535390720", + "topic": "", + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 3072 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 3146752, + "deny": 0 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "602695431935492116", + "type": 2, + "name": "━━━━━━━━", + "position": 0, + "parent_id": "485961711535390720", + "bitrate": 64000, + "user_limit": 0, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 3146752 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 1048576 + } + ], + "nsfw": false + }, + { + "id": "607742611821821952", + "last_message_id": "646537997612875799", + "last_pin_timestamp": "2019-09-20T01:42:08.444000+00:00", + "type": 0, + "name": "officer-chat", + "position": 57, + "parent_id": "382114317857849344", + "topic": null, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 3072 + }, + { + "id": "382116615614889984", + "type": "role", + "allow": 3072, + "deny": 0 + }, + { + "id": "382117075558203392", + "type": "role", + "allow": 3072, + "deny": 0 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 3072, + "deny": 0 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "609073241704759306", + "type": 4, + "name": "📊 Server Stats 📊", + "position": 21, + "parent_id": null, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 871890257 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "482625494886645781", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "531004642612477964", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "538477449130803212", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "539618386037047317", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "565734337819967489", + "type": "role", + "allow": 0, + "deny": 1024 + } + ], + "nsfw": false + }, + { + "id": "609073244103770124", + "type": 2, + "name": "Member Count: 9639", + "position": 42, + "parent_id": "609073241704759306", + "bitrate": 64000, + "user_limit": 0, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1049600 + }, + { + "id": "382118099341541379", + "type": "role", + "allow": 1024, + "deny": 1048576 + }, + { + "id": "432533456807919639", + "type": "member", + "allow": 1049600, + "deny": 0 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 269485056, + "deny": 0 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 1024, + "deny": 1048576 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 1048576 + } + ], + "nsfw": false + }, + { + "id": "609073245840343065", + "type": 2, + "name": "Role Count: 93", + "position": 43, + "parent_id": "609073241704759306", + "bitrate": 64000, + "user_limit": 0, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1049600 + }, + { + "id": "432533456807919639", + "type": "member", + "allow": 1049600, + "deny": 0 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 269485056, + "deny": 0 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 1048576 + } + ], + "nsfw": false + }, + { + "id": "609073247341903873", + "type": 2, + "name": "Bot Count: 19", + "position": 44, + "parent_id": "609073241704759306", + "bitrate": 64000, + "user_limit": 0, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1049600 + }, + { + "id": "432533456807919639", + "type": "member", + "allow": 1049600, + "deny": 0 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 269485056, + "deny": 0 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 1048576 + } + ], + "nsfw": false + }, + { + "id": "609073250512797699", + "type": 2, + "name": "Channel Count: 187", + "position": 45, + "parent_id": "609073241704759306", + "bitrate": 64000, + "user_limit": 0, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1049600 + }, + { + "id": "432533456807919639", + "type": "member", + "allow": 1049600, + "deny": 0 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 269485056, + "deny": 0 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 1048576 + } + ], + "nsfw": false + }, + { + "id": "609073252442046495", + "type": 2, + "name": "User Count: 9620", + "position": 46, + "parent_id": "609073241704759306", + "bitrate": 64000, + "user_limit": 0, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1049600 + }, + { + "id": "382118099341541379", + "type": "role", + "allow": 1024, + "deny": 1048576 + }, + { + "id": "432533456807919639", + "type": "member", + "allow": 1049600, + "deny": 0 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 269485056, + "deny": 0 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 1024, + "deny": 1048576 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 1048576 + } + ], + "nsfw": false + }, + { + "id": "611755273308209163", + "last_message_id": null, + "type": 0, + "name": "━━━━━━━━", + "position": 38, + "parent_id": "552220299521818674", + "topic": null, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "382118099341541379", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "530919353999163414", + "type": "role", + "allow": 0, + "deny": 1050624 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "551597061955387403", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "611755385929334794", + "last_message_id": "616436715967610906", + "type": 0, + "name": "scrim-rules", + "position": 36, + "parent_id": "552220299521818674", + "topic": null, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 3072 + }, + { + "id": "382118099341541379", + "type": "role", + "allow": 1024, + "deny": 2048 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 1024, + "deny": 2048 + }, + { + "id": "538477449130803212", + "type": "role", + "allow": 0, + "deny": 0 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 3072, + "deny": 0 + }, + { + "id": "551597061955387403", + "type": "role", + "allow": 0, + "deny": 3072 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "611755724095094790", + "type": 2, + "name": "━━━━━━━━", + "position": 32, + "parent_id": "552220299521818674", + "bitrate": 64000, + "user_limit": 0, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "382118099341541379", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "551597061955387403", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 1048576 + } + ], + "nsfw": false + }, + { + "id": "612041075867254784", + "type": 2, + "name": "Lobby", + "position": 24, + "parent_id": "552220299521818674", + "bitrate": 64000, + "user_limit": 69, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "382118099341541379", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "530919353999163414", + "type": "role", + "allow": 0, + "deny": 1050624 + }, + { + "id": "538477449130803212", + "type": "role", + "allow": 2048, + "deny": 1024 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "551597061955387403", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 1048576 + } + ], + "nsfw": false + }, + { + "id": "612041393640308746", + "type": 2, + "name": "Alpha", + "position": 25, + "parent_id": "552220299521818674", + "bitrate": 64000, + "user_limit": 3, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "382118099341541379", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "530919353999163414", + "type": "role", + "allow": 0, + "deny": 1050624 + }, + { + "id": "538477449130803212", + "type": "role", + "allow": 2048, + "deny": 1024 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "551597061955387403", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 1048576 + } + ], + "nsfw": false + }, + { + "id": "612041414724943872", + "type": 2, + "name": "Bravo", + "position": 26, + "parent_id": "552220299521818674", + "bitrate": 64000, + "user_limit": 3, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "382118099341541379", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "530919353999163414", + "type": "role", + "allow": 0, + "deny": 1050624 + }, + { + "id": "538477449130803212", + "type": "role", + "allow": 2048, + "deny": 1024 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "551597061955387403", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 1048576 + } + ], + "nsfw": false + }, + { + "id": "612041458693832728", + "type": 2, + "name": "Charlie", + "position": 27, + "parent_id": "552220299521818674", + "bitrate": 64000, + "user_limit": 3, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "382118099341541379", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "530919353999163414", + "type": "role", + "allow": 0, + "deny": 1050624 + }, + { + "id": "538477449130803212", + "type": "role", + "allow": 2048, + "deny": 1024 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "551597061955387403", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 1048576 + } + ], + "nsfw": false + }, + { + "id": "612041492822884360", + "type": 2, + "name": "Delta", + "position": 28, + "parent_id": "552220299521818674", + "bitrate": 64000, + "user_limit": 3, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "382118099341541379", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "530919353999163414", + "type": "role", + "allow": 0, + "deny": 1050624 + }, + { + "id": "538477449130803212", + "type": "role", + "allow": 2048, + "deny": 1024 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "551597061955387403", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 1048576 + } + ], + "nsfw": false + }, + { + "id": "612041608505851951", + "type": 2, + "name": "Echo", + "position": 29, + "parent_id": "552220299521818674", + "bitrate": 64000, + "user_limit": 3, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "382118099341541379", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "530919353999163414", + "type": "role", + "allow": 0, + "deny": 1050624 + }, + { + "id": "538477449130803212", + "type": "role", + "allow": 2048, + "deny": 1024 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "551597061955387403", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 1048576 + } + ], + "nsfw": false + }, + { + "id": "612041641414361088", + "type": 2, + "name": "Foxtrot", + "position": 30, + "parent_id": "552220299521818674", + "bitrate": 64000, + "user_limit": 3, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "382118099341541379", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "530919353999163414", + "type": "role", + "allow": 0, + "deny": 1050624 + }, + { + "id": "538477449130803212", + "type": "role", + "allow": 2048, + "deny": 1024 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "551597061955387403", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 1048576 + } + ], + "nsfw": false + }, + { + "id": "613863292426911910", + "last_message_id": "613863859186434048", + "type": 0, + "name": "cross-save-faq", + "position": 125, + "parent_id": "619529638699335685", + "topic": null, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 146496 + }, + { + "id": "463520715254267916", + "type": "role", + "allow": 0, + "deny": 7168 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 7168 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 7168 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "614213169182932992", + "type": 2, + "name": "Fireteam 1", + "position": 5, + "parent_id": "579868728410505276", + "bitrate": 64000, + "user_limit": 3, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "463520715254267916", + "type": "role", + "allow": 0, + "deny": 1048576 + }, + { + "id": "530919353999163414", + "type": "role", + "allow": 0, + "deny": 1048576 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 1048576 + } + ], + "nsfw": false + }, + { + "id": "614213300577894400", + "type": 2, + "name": "Raid 1", + "position": 8, + "parent_id": "579868209835147281", + "bitrate": 64000, + "user_limit": 6, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "463520715254267916", + "type": "role", + "allow": 0, + "deny": 1048576 + }, + { + "id": "530919353999163414", + "type": "role", + "allow": 0, + "deny": 1048576 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 1048576 + } + ], + "nsfw": false + }, + { + "id": "614213534787698915", + "type": 2, + "name": "Strike 1", + "position": 11, + "parent_id": "551866300754952215", + "bitrate": 64000, + "user_limit": 3, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "463520715254267916", + "type": "role", + "allow": 0, + "deny": 1050624 + }, + { + "id": "530919353999163414", + "type": "role", + "allow": 0, + "deny": 1048576 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 1048576 + } + ], + "nsfw": false + }, + { + "id": "614213665654046730", + "type": 2, + "name": "Gambit 1", + "position": 16, + "parent_id": "551868604350464011", + "bitrate": 64000, + "user_limit": 4, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "463520715254267916", + "type": "role", + "allow": 0, + "deny": 1048576 + }, + { + "id": "530919353999163414", + "type": "role", + "allow": 0, + "deny": 1048576 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 1048576 + } + ], + "nsfw": false + }, + { + "id": "614214069452406853", + "type": 2, + "name": "Quickplay 1", + "position": 17, + "parent_id": "486328170128211971", + "bitrate": 64000, + "user_limit": 6, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 1, + "deny": 0 + }, + { + "id": "463520715254267916", + "type": "role", + "allow": 0, + "deny": 1048576 + }, + { + "id": "530919353999163414", + "type": "role", + "allow": 0, + "deny": 1048576 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 1048576 + } + ], + "nsfw": false + }, + { + "id": "614214970540032004", + "type": 2, + "name": "Menagerie 1", + "position": 15, + "parent_id": "586324559062630420", + "bitrate": 64000, + "user_limit": 6, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "463520715254267916", + "type": "role", + "allow": 0, + "deny": 1048576 + }, + { + "id": "530919353999163414", + "type": "role", + "allow": 0, + "deny": 1048576 + }, + { + "id": "579865256327643141", + "type": "role", + "allow": 268436496, + "deny": 0 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 1048576 + } + ], + "nsfw": false + }, + { + "id": "617774763678629936", + "last_message_id": "618855778014724141", + "type": 0, + "name": "clan-links-9-21-19", + "position": 123, + "parent_id": "619529638699335685", + "topic": "", + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 3072 + }, + { + "id": "382116615614889984", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "382117075558203392", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 3072, + "deny": 0 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 3072, + "deny": 0 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "619529638699335685", + "type": 4, + "name": "Archive 2", + "position": 24, + "parent_id": null, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1049600 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 1049600, + "deny": 0 + } + ], + "nsfw": false + }, + { + "id": "623277342713839646", + "last_message_id": "664860062422007814", + "last_pin_timestamp": "2019-12-20T20:20:59.822000+00:00", + "type": 0, + "name": "memes", + "position": 32, + "parent_id": "381910837293613057", + "topic": "No Racism, Sexualization of people (Especially Women and Minors.), No porn, no anti-semetic stuff, No Gore, And No degradation of **ANYONE**. NO EXCEPTIONS! if you can’t handle being compliant with the rules, we suggest you not use this channel.", + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 3072, + "deny": 0 + }, + { + "id": "382117075558203392", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "382118099341541379", + "type": "role", + "allow": 0, + "deny": 0 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 0, + "deny": 0 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 0, + "deny": 0 + }, + { + "id": "530919353999163414", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 0, + "deny": 0 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "624378694466666499", + "last_message_id": "638161665724121098", + "type": 0, + "name": "clan-links-nov-2019", + "position": 126, + "parent_id": "619529638699335685", + "topic": "", + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1051713 + }, + { + "id": "538477449130803212", + "type": "role", + "allow": 0, + "deny": 3072 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "627919829491515394", + "last_message_id": "634098653275095069", + "last_pin_timestamp": "2019-09-29T17:42:37.488000+00:00", + "type": 0, + "name": "gos-lfg", + "position": 121, + "parent_id": "619529638699335685", + "topic": "Please use this channel for finding GoS Teams! Have at it, guardians! @here is allowed!", + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 133120, + "deny": 1024 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 132096, + "deny": 0 + }, + { + "id": "463520715254267916", + "type": "role", + "allow": 0, + "deny": 3072 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 131072, + "deny": 0 + }, + { + "id": "530919353999163414", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 131072, + "deny": 0 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "631646526921244702", + "type": 4, + "name": "Temp Channels", + "position": 14, + "parent_id": null, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "241950106125860865", + "type": "member", + "allow": 0, + "deny": 3072 + }, + { + "id": "530919353999163414", + "type": "role", + "allow": 0, + "deny": 2100224 + }, + { + "id": "564512855790059545", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "625165812881424404", + "type": "role", + "allow": 871890769, + "deny": 0 + } + ], + "nsfw": false + }, + { + "id": "632038066831360034", + "last_message_id": "664730689446477825", + "last_pin_timestamp": "2019-10-11T02:57:04.526000+00:00", + "type": 0, + "name": "create-channel", + "position": 34, + "parent_id": "631646526921244702", + "topic": null, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 3072, + "deny": 268627984 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "625165812881424404", + "type": "role", + "allow": 3072, + "deny": 0 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "633066562663874571", + "type": 4, + "name": "Competitive", + "position": 12, + "parent_id": null, + "guild_id": "162706186272112640", + "permission_overwrites": [], + "nsfw": false + }, + { + "id": "633066676975435806", + "type": 2, + "name": "Competitive 1", + "position": 19, + "parent_id": "633066562663874571", + "bitrate": 64000, + "user_limit": 3, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 1048576 + } + ], + "nsfw": false + }, + { + "id": "634143533640581125", + "type": 2, + "name": "Iron Banner 1", + "position": 45, + "parent_id": "590564776057241610", + "bitrate": 64000, + "user_limit": 6, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "530919353999163414", + "type": "role", + "allow": 0, + "deny": 1049600 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "579865256327643141", + "type": "role", + "allow": 16, + "deny": 0 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 1048576 + } + ], + "nsfw": false + }, + { + "id": "634501759024365569", + "last_message_id": "662123864658280458", + "last_pin_timestamp": "2019-11-24T22:06:00.080000+00:00", + "type": 0, + "name": "recognition", + "position": 31, + "parent_id": "381910837293613057", + "topic": "Abusing this channel in any way will result in your permissions being taken away to view this channel. Please look at the pinned format for how to go about posting a message here!", + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 3072, + "deny": 0 + }, + { + "id": "382117075558203392", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 268435456, + "deny": 0 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 268435456, + "deny": 0 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 268435456, + "deny": 0 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "635625512319975426", + "type": 4, + "name": "Seasonal Activity", + "position": 8, + "parent_id": null, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 3146752, + "deny": 0 + }, + { + "id": "382117075558203392", + "type": "role", + "allow": 1049600, + "deny": 0 + } + ], + "nsfw": false + }, + { + "id": "637279033146736641", + "last_message_id": "637804501151252480", + "type": 0, + "name": "game-night-voting", + "position": 119, + "parent_id": "619529638699335685", + "topic": null, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 3072 + }, + { + "id": "382117075558203392", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 3072, + "deny": 0 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "638537842120851464", + "last_message_id": "640310212619665410", + "type": 0, + "name": "scrim-announcements", + "position": 35, + "parent_id": "552220299521818674", + "topic": null, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "382117075558203392", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 3072, + "deny": 0 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 3072, + "deny": 0 + }, + { + "id": "502964473674465300", + "type": "role", + "allow": 3072, + "deny": 0 + }, + { + "id": "538477449130803212", + "type": "role", + "allow": 1024, + "deny": 2048 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 3072, + "deny": 0 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 3072 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "641678205329080325", + "type": 2, + "name": "Fireteam 2", + "position": 6, + "parent_id": "579868728410505276", + "bitrate": 64000, + "user_limit": 3, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "463520715254267916", + "type": "role", + "allow": 0, + "deny": 1048576 + }, + { + "id": "530919353999163414", + "type": "role", + "allow": 0, + "deny": 1048576 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 1048576 + } + ], + "nsfw": false + }, + { + "id": "644693082440335360", + "last_message_id": "660224642883256353", + "type": 0, + "name": "clan-links", + "position": 10, + "parent_id": "479849608558936064", + "topic": "", + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 1024, + "deny": 2048 + }, + { + "id": "382117075558203392", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "441751906428256277", + "type": "member", + "allow": 3136, + "deny": 0 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 3072, + "deny": 0 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "652529652631601192", + "last_message_id": "658143232655491083", + "type": 0, + "name": "sherpa-night-tracker", + "position": 42, + "parent_id": "526617610519838741", + "topic": null, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "382117075558203392", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "469631935753355264", + "type": "role", + "allow": 3072, + "deny": 0 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "565734337819967489", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "652893148732850197", + "type": 2, + "name": "Competitive 2", + "position": 20, + "parent_id": "633066562663874571", + "bitrate": 64000, + "user_limit": 3, + "guild_id": "162706186272112640", + "permission_overwrites": [], + "nsfw": false + }, + { + "id": "652923485076717578", + "type": 2, + "name": "Quickplay 2", + "position": 18, + "parent_id": "486328170128211971", + "bitrate": 64000, + "user_limit": 6, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 1, + "deny": 0 + }, + { + "id": "463520715254267916", + "type": "role", + "allow": 0, + "deny": 1048576 + }, + { + "id": "530919353999163414", + "type": "role", + "allow": 0, + "deny": 1048576 + } + ], + "nsfw": false + }, + { + "id": "654049594480525322", + "last_message_id": "658121836738904079", + "last_pin_timestamp": "2019-12-21T18:29:45.766000+00:00", + "type": 0, + "name": "season-of-dawn-discussion", + "position": 118, + "parent_id": "619529638699335685", + "topic": "*Spoiler Alert* @JeBaited#1342 with any questions. Try to not repeat post | Spoilers outside of this channel go as Warning then Chat ban.", + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 0, + "deny": 3072 + }, + { + "id": "382116615614889984", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "382117075558203392", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "382118099341541379", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "475476028349874178", + "type": "role", + "allow": 0, + "deny": 1024 + }, + { + "id": "538477449130803212", + "type": "role", + "allow": 0, + "deny": 3072 + }, + { + "id": "539605106790760468", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "654052427988729877", + "type": 2, + "name": "Sundial 1", + "position": 13, + "parent_id": "635625512319975426", + "bitrate": 64000, + "user_limit": 6, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 3146752, + "deny": 0 + }, + { + "id": "382117075558203392", + "type": "role", + "allow": 1049600, + "deny": 0 + } + ], + "nsfw": false + }, + { + "id": "654165747999703070", + "last_message_id": "664168444618604554", + "type": 0, + "name": "syc-application", + "position": 17, + "parent_id": "518871349125447701", + "topic": "Application form for #support-your-community (Streaming and YouTube videos.)", + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 68608, + "deny": 262208 + }, + { + "id": "530919353999163414", + "type": "role", + "allow": 0, + "deny": 2048 + }, + { + "id": "646540079375843347", + "type": "role", + "allow": 1024, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "657037991297941514", + "type": 2, + "name": "Strike 2", + "position": 12, + "parent_id": "551866300754952215", + "bitrate": 64000, + "user_limit": 0, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "463520715254267916", + "type": "role", + "allow": 0, + "deny": 1050624 + }, + { + "id": "530919353999163414", + "type": "role", + "allow": 0, + "deny": 1048576 + } + ], + "nsfw": false + }, + { + "id": "657038231388291073", + "type": 2, + "name": "Sundial 2", + "position": 14, + "parent_id": "635625512319975426", + "bitrate": 64000, + "user_limit": 6, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 3146752, + "deny": 0 + }, + { + "id": "382117075558203392", + "type": "role", + "allow": 1049600, + "deny": 0 + } + ], + "nsfw": false + }, + { + "id": "664637199861678091", + "last_message_id": "664823212156256287", + "type": 0, + "name": "crucible-guide-applications", + "position": 133, + "parent_id": "518871349125447701", + "topic": null, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "162706186272112640", + "type": "role", + "allow": 3072, + "deny": 0 + }, + { + "id": "382117075558203392", + "type": "role", + "allow": 1024, + "deny": 0 + }, + { + "id": "442416690631016469", + "type": "role", + "allow": 3072, + "deny": 0 + }, + { + "id": "623678304683294720", + "type": "role", + "allow": 0, + "deny": 2048 + } + ], + "nsfw": false, + "rate_limit_per_user": 0 + }, + { + "id": "664879639814078464", + "type": 2, + "name": "Menagerie 2", + "position": 49, + "parent_id": "586324559062630420", + "bitrate": 64000, + "user_limit": 6, + "guild_id": "162706186272112640", + "permission_overwrites": [ + { + "id": "463520715254267916", + "type": "role", + "allow": 0, + "deny": 1048576 + }, + { + "id": "530919353999163414", + "type": "role", + "allow": 0, + "deny": 1048576 + }, + { + "id": "579865256327643141", + "type": "role", + "allow": 268436496, + "deny": 0 + } + ], + "nsfw": false + } +] diff --git a/src/http/routing.rs b/src/http/routing.rs index 79a9885..7c92752 100644 --- a/src/http/routing.rs +++ b/src/http/routing.rs @@ -7,7 +7,7 @@ use std::fmt::Write; use hyper::Method as HttpMethod; -use crate::model::channel::OverwriteId; +use crate::model::channel::permissions::OverwriteId; use crate::model::guild::{AuditLogEvent, Emoji}; use crate::model::id::*; diff --git a/src/model/channel/dm_channel.rs b/src/model/channel/dm.rs similarity index 98% rename from src/model/channel/dm_channel.rs rename to src/model/channel/dm.rs index 366f152..fa13f4b 100644 --- a/src/model/channel/dm_channel.rs +++ b/src/model/channel/dm.rs @@ -27,7 +27,7 @@ pub struct DMChannel { pub recipient: User, /// The ID of the last message sent to the channel. pub last_message_id: Option, - /// When the last message with pinned. + /// When the last message was pinned. pub last_pin_timestamp: Option>, } diff --git a/src/model/channel/group_channel.rs b/src/model/channel/group.rs similarity index 97% rename from src/model/channel/group_channel.rs rename to src/model/channel/group.rs index fc2718e..04b4c22 100644 --- a/src/model/channel/group_channel.rs +++ b/src/model/channel/group.rs @@ -36,7 +36,7 @@ pub struct Group { pub application_id: Option, /// The ID of the last message sent to the group. pub last_message_id: Option, - /// When the last message with pinned. + /// When the last message was pinned. pub last_pin_timestamp: Option>, } @@ -126,6 +126,7 @@ mod tests { ), ]), owner_id: UserId::from(82198810841029460), + application_id: None, last_message_id: Some(MessageId::from(3343820033257021450)), last_pin_timestamp: None, }; @@ -162,7 +163,8 @@ mod tests { "last_pin_timestamp": null, "type": 3, "id": "319674150115710528", - "owner_id": "53908099506183680" + "owner_id": "53908099506183680", + "application_id": null }); let channel = Group { id: ChannelId::from(319674150115710528), @@ -194,6 +196,7 @@ mod tests { ), ]), owner_id: UserId::from(53908099506183680), + application_id: None, last_message_id: Some(MessageId::from(3343820033257021450)), last_pin_timestamp: None, }; diff --git a/src/model/channel/guild_channel/mod.rs b/src/model/channel/guild/mod.rs similarity index 75% rename from src/model/channel/guild_channel/mod.rs rename to src/model/channel/guild/mod.rs index 7bd8443..71afa37 100644 --- a/src/model/channel/guild_channel/mod.rs +++ b/src/model/channel/guild/mod.rs @@ -1,8 +1,14 @@ +//! Guild channel models. + +mod text_channel; + use serde::de; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use crate::model::channel::ChannelType; +pub use self::text_channel::TextChannel; + /// A channel in a [`Guild`]. /// /// [`Guild`]: TODO @@ -12,7 +18,7 @@ pub enum GuildChannel { /// A text channel in a [`Guild`]. /// /// [`Guild`]: TODO - Text, // TODO: Add TextChannel. + Text(TextChannel), /// A voice channel in a [`Guild`]. /// /// [`Guild`]: TODO @@ -31,7 +37,7 @@ impl GuildChannel { /// The type of the channel. pub fn kind(&self) -> ChannelType { match self { - GuildChannel::Text => ChannelType::Text, + GuildChannel::Text(_) => ChannelType::Text, GuildChannel::Voice => ChannelType::Voice, GuildChannel::Category => ChannelType::Category, GuildChannel::News => ChannelType::News, @@ -43,36 +49,39 @@ impl GuildChannel { impl GuildChannel { pub(crate) fn from_value( kind: ChannelType, - _value: serde_json::Value, + value: serde_json::Value, ) -> Result where E: de::Error, { - match kind { + let result = match kind { + ChannelType::Text => TextChannel::deserialize(value).map(GuildChannel::Text), + ChannelType::Voice => todo!(), ChannelType::Category => todo!(), ChannelType::News => todo!(), ChannelType::Store => todo!(), - ChannelType::Text => todo!(), - ChannelType::Voice => todo!(), - kind => Err(E::custom(format_args!( - "invalid channel type for guild channel: {:?}", - kind - ))), - } + kind => { + return Err(E::custom(format_args!( + "invalid channel type for guild channel: {:?}", + kind + ))) + } + }; + result.map_err(E::custom) } } impl Serialize for GuildChannel { - fn serialize(&self, _serializer: S) -> Result + fn serialize(&self, serializer: S) -> Result where S: Serializer, { match self { + GuildChannel::Text(channel) => channel.serialize(serializer), + GuildChannel::Voice => todo!(), GuildChannel::Category => todo!(), GuildChannel::News => todo!(), GuildChannel::Store => todo!(), - GuildChannel::Text => todo!(), - GuildChannel::Voice => todo!(), } } } diff --git a/src/model/channel/guild/text_channel.rs b/src/model/channel/guild/text_channel.rs new file mode 100644 index 0000000..c6a5a8a --- /dev/null +++ b/src/model/channel/guild/text_channel.rs @@ -0,0 +1,52 @@ +use chrono::{DateTime, FixedOffset}; +use serde::{Deserialize, Serialize}; + +use crate::model::channel::permissions::PermissionOverwrite; +use crate::model::channel::ChannelType; +use crate::model::id::{ChannelId, GuildId, MessageId}; + +/// A text channel in a [`Guild`]. +/// +/// [`Guild`]: TODO +#[non_exhaustive] +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct TextChannel { + /// The ID of the channel. + pub id: ChannelId, + /// The type of the channel. + /// + /// This should always be [`ChannelType::Text`]. + /// + /// [`ChannelType::Text`]: ../enum.ChannelType.html#variant.Text + #[serde(rename = "type")] + pub(crate) kind: ChannelType, + /// The ID of the guild. + pub guild_id: GuildId, + /// The sorting position of the chanel. + pub position: u64, + /// A collection of explicit permission overwrites for members and roles. + #[serde(default)] + pub permission_overwrites: Vec, + /// The name of the channel. + pub name: String, + /// The topic of the channel. + pub topic: Option, + /// Whether the channel is NSFW. + #[serde(default)] + pub nsfw: bool, + /// The ID of the last message sent to the group. + pub last_message_id: Option, + /// The amount of seconds a user has to wait before sending another message + /// (0 - 216000s). + /// + /// Bots as well as users with the permission [`MANAGE_MESSAGES`] or + /// [`MANAGE_CHANNEL`], are unaffected. + #[doc = "\n[`MANAGE_MESSAGES`]: ../permissions/struct.Permissions.html#associatedconstant.MANAGE_MESSAGES"] + #[doc = "\n[`MANAGE_CHANNEL`]: ../permissions/struct.Permissions.html#associatedconstant.MANAGE_CHANNEL"] + #[serde(default, rename = "rate_limit_per_user")] + pub rate_limit: u16, + /// The ID of the parent category of the channel. + pub parent_id: Option, + /// When the last message was pinned. + pub last_pin_timestamp: Option>, +} diff --git a/src/model/channel/attachment.rs b/src/model/channel/message/attachment.rs similarity index 100% rename from src/model/channel/attachment.rs rename to src/model/channel/message/attachment.rs diff --git a/src/model/channel/embed.rs b/src/model/channel/message/embed.rs similarity index 98% rename from src/model/channel/embed.rs rename to src/model/channel/message/embed.rs index 7e62682..ca9653b 100644 --- a/src/model/channel/embed.rs +++ b/src/model/channel/message/embed.rs @@ -1,3 +1,5 @@ +//! Message embed models. + use chrono::{DateTime, FixedOffset}; use serde::{Deserialize, Serialize}; @@ -72,6 +74,7 @@ pub enum EmbedType { } impl Default for EmbedType { + /// Embed type defaults to rich embed. fn default() -> Self { EmbedType::Rich } diff --git a/src/model/channel/message.rs b/src/model/channel/message/mod.rs similarity index 96% rename from src/model/channel/message.rs rename to src/model/channel/message/mod.rs index 9ed20a2..e73a0d8 100644 --- a/src/model/channel/message.rs +++ b/src/model/channel/message/mod.rs @@ -1,16 +1,26 @@ +//! Message related models. + +pub mod embed; + +mod attachment; +mod rich_presence; + use bitflags::bitflags; use chrono::{DateTime, FixedOffset}; use serde::de; use serde::{Deserialize, Deserializer, Serialize, Serializer}; -use crate::model::channel::{ - Attachment, ChannelType, Embed, MessageActivity, MessageApplication, Reaction, -}; +use crate::model::channel::{ChannelType, Reaction}; use crate::model::guild::PartialMember; use crate::model::id::{ChannelId, GuildId, MessageId, RoleId, WebhookId}; use crate::model::user::User; use crate::model::utils::U8Visitor; +use self::embed::Embed; + +pub use self::attachment::Attachment; +pub use self::rich_presence::{MessageActivity, MessageActivityType, MessageApplication}; + /// A message sent in a text channel. #[non_exhaustive] #[derive(Clone, Debug, Deserialize, Serialize)] @@ -18,8 +28,12 @@ pub struct Message { /// The ID of the message. pub id: MessageId, /// The ID of the [`Channel`] the message was sent in. + /// + /// [`Channel`]: ../enum.Channel.html pub channel_id: ChannelId, /// The ID of the [`Guild`] the message was sent in. + /// + /// [`Guild`]: TODO pub guild_id: Option, /// The author of the message. /// @@ -68,7 +82,6 @@ pub struct Message { /// The reactions to the message. #[serde(default)] pub reactions: Vec, - // TODO: Add `nonce`. /// Whether the message is pinned. pub pinned: bool, /// The webhook ID that generated the message, if the message was generated diff --git a/src/model/channel/rich_presence.rs b/src/model/channel/message/rich_presence.rs similarity index 100% rename from src/model/channel/rich_presence.rs rename to src/model/channel/message/rich_presence.rs diff --git a/src/model/channel/mod.rs b/src/model/channel/mod.rs index 4840914..0ed1245 100644 --- a/src/model/channel/mod.rs +++ b/src/model/channel/mod.rs @@ -3,33 +3,22 @@ // Internal utility module. mod utils; -mod attachment; -mod dm_channel; -mod embed; -mod group_channel; -mod guild_channel; -mod message; -mod permission_overwrite; -mod rich_presence; +mod dm; +mod group; + +pub mod guild; +pub mod message; +pub mod permissions; use serde::de; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use crate::model::guild::PartialEmoji; -pub use self::attachment::Attachment; -pub use self::dm_channel::DMChannel; -pub use self::embed::{ - Embed, EmbedAuthor, EmbedField, EmbedFooter, EmbedImage, EmbedProvider, EmbedThumbnail, - EmbedType, EmbedVideo, -}; -pub use self::group_channel::Group; -pub use self::guild_channel::GuildChannel; -pub use self::message::{ - MentionedChannel, MentionedUser, Message, MessageFlags, MessageReference, MessageType, -}; -pub use self::permission_overwrite::{OverwriteId, PermissionOverwrite}; -pub use self::rich_presence::{MessageActivity, MessageActivityType, MessageApplication}; +pub use self::dm::DMChannel; +pub use self::group::Group; +pub use self::guild::GuildChannel; +pub use self::message::Message; /// The type of a channel. #[non_exhaustive] @@ -233,7 +222,7 @@ mod tests { "last_message_id": "3343820033257021450", "type": 3, "id": "319674150115710528", - "owner_id": "82198810841029460" + "owner_id": "82198810841029460", }); let channel = Channel::Group(Group { id: ChannelId::from(319674150115710528), @@ -265,6 +254,7 @@ mod tests { ), ]), owner_id: UserId::from(82198810841029460), + application_id: None, last_message_id: Some(MessageId::from(3343820033257021450)), last_pin_timestamp: None, }); diff --git a/src/model/channel/permission_overwrite.rs b/src/model/channel/permissions.rs similarity index 98% rename from src/model/channel/permission_overwrite.rs rename to src/model/channel/permissions.rs index da3c20d..3ec3e2f 100644 --- a/src/model/channel/permission_overwrite.rs +++ b/src/model/channel/permissions.rs @@ -1,3 +1,5 @@ +//! Channel permission overwrite models. + use std::fmt::{self, Display}; use serde::{Deserialize, Serialize}; From cc8dfea9326307e241d35f02696d63e30b4f0a7b Mon Sep 17 00:00:00 2001 From: James Whaley Date: Fri, 10 Jan 2020 11:15:39 +0000 Subject: [PATCH 17/44] Add Github sponsors support In no way should people feel an obligation to sponsor my work, this is merely to allow people to sponsor my work if they want to. --- .github/FUNDING.yml | 1 + 1 file changed, 1 insertion(+) create mode 100644 .github/FUNDING.yml diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..6cbd699 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +github: Juici From 75b87fd2eec5bb8e73783f541035c8cd98335211 Mon Sep 17 00:00:00 2001 From: James Whaley Date: Fri, 10 Jan 2020 11:34:26 +0000 Subject: [PATCH 18/44] Add TextChannel tests --- src/internal/test.rs | 4 +- src/model/channel/guild/text_channel.rs | 91 +++++++++++++++++++++++++ 2 files changed, 93 insertions(+), 2 deletions(-) diff --git a/src/internal/test.rs b/src/internal/test.rs index af28701..324c5a9 100644 --- a/src/internal/test.rs +++ b/src/internal/test.rs @@ -150,9 +150,9 @@ macro_rules! impl_eq_fields { }); }; ($name:ident: [$($field:ident),* $(,)*]) => { - __impl_eq_fields!($name: (a, b) => { + __impl_eq_fields!($name: (_a, _b) => { $( - assert_eq_fields!(a.$field, b.$field); + assert_eq_fields!(_a.$field, _b.$field); )* }); } diff --git a/src/model/channel/guild/text_channel.rs b/src/model/channel/guild/text_channel.rs index c6a5a8a..7dcd8c7 100644 --- a/src/model/channel/guild/text_channel.rs +++ b/src/model/channel/guild/text_channel.rs @@ -50,3 +50,94 @@ pub struct TextChannel { /// When the last message was pinned. pub last_pin_timestamp: Option>, } + +impl_eq_fields!(TextChannel: [ + id, + kind, + guild_id, + position, + permission_overwrites, + name, + topic, + nsfw, + last_message_id, + rate_limit, + parent_id, + last_pin_timestamp, +]); + +#[cfg(test)] +mod tests { + use serde_json::json; + + use super::*; + + #[test] + fn test_deserialize() { + let value = json!({ + "id": "41771983423143937", + "guild_id": "41771983423143937", + "name": "general", + "type": 0, + "position": 6, + "permission_overwrites": [], + "rate_limit_per_user": 2, + "nsfw": true, + "topic": "24/7 chat about how to gank Mike #2", + "last_message_id": "155117677105512449", + "parent_id": "399942396007890945" + }); + let channel = TextChannel { + id: ChannelId::from(41771983423143937), + kind: ChannelType::Text, + guild_id: GuildId::from(41771983423143937), + position: 6, + permission_overwrites: vec![], + name: "general".to_owned(), + topic: Some("24/7 chat about how to gank Mike #2".to_owned()), + nsfw: true, + last_message_id: Some(MessageId::from(155117677105512449)), + rate_limit: 2, + parent_id: Some(ChannelId::from(399942396007890945)), + last_pin_timestamp: None, + }; + + let deserialized: TextChannel = serde_json::from_value(value).unwrap(); + + assert_eq_fields!(channel, deserialized); + } + + #[test] + fn test_serialize() { + let value = json!({ + "id": "41771983423143937", + "guild_id": "41771983423143937", + "name": "general", + "type": 0, + "position": 6, + "permission_overwrites": [], + "rate_limit_per_user": 2, + "nsfw": true, + "topic": "24/7 chat about how to gank Mike #2", + "last_message_id": "155117677105512449", + "parent_id": "399942396007890945", + "last_pin_timestamp": null + }); + let channel = TextChannel { + id: ChannelId::from(41771983423143937), + kind: ChannelType::Text, + guild_id: GuildId::from(41771983423143937), + position: 6, + permission_overwrites: vec![], + name: "general".to_owned(), + topic: Some("24/7 chat about how to gank Mike #2".to_owned()), + nsfw: true, + last_message_id: Some(MessageId::from(155117677105512449)), + rate_limit: 2, + parent_id: Some(ChannelId::from(399942396007890945)), + last_pin_timestamp: None, + }; + + assert_eq!(value, serde_json::to_value(&channel).unwrap()); + } +} From 08ea308a923a764a77d687b4f5e2abebb026b05c Mon Sep 17 00:00:00 2001 From: James Whaley Date: Fri, 10 Jan 2020 11:59:58 +0000 Subject: [PATCH 19/44] Move Reaction to message and clean up tests --- src/model/channel/dm.rs | 6 +- src/model/channel/group.rs | 6 +- src/model/channel/guild/mod.rs | 11 +- src/model/channel/guild/text_channel.rs | 7 +- src/model/channel/message/mod.rs | 17 +++- src/model/channel/mod.rs | 130 +----------------------- src/model/guild/emoji/mod.rs | 1 - src/model/user/mod.rs | 11 +- 8 files changed, 46 insertions(+), 143 deletions(-) diff --git a/src/model/channel/dm.rs b/src/model/channel/dm.rs index fa13f4b..5477e36 100644 --- a/src/model/channel/dm.rs +++ b/src/model/channel/dm.rs @@ -38,6 +38,7 @@ mod tests { use chrono::TimeZone; use serde_json::json; + use crate::model::channel::Channel; use crate::model::id::UserId; use crate::model::user::Discriminator; @@ -73,8 +74,11 @@ mod tests { last_pin_timestamp: None, }; - let deserialized: DMChannel = serde_json::from_value(value).unwrap(); + let deserialized = DMChannel::deserialize(&value).unwrap(); + assert_eq_fields!(channel, deserialized); + let channel = Channel::DM(channel); + let deserialized = Channel::deserialize(&value).unwrap(); assert_eq_fields!(channel, deserialized); } diff --git a/src/model/channel/group.rs b/src/model/channel/group.rs index 04b4c22..8d190f6 100644 --- a/src/model/channel/group.rs +++ b/src/model/channel/group.rs @@ -68,6 +68,7 @@ mod tests { use serde_json::json; + use crate::model::channel::Channel; use crate::model::user::Discriminator; use super::*; @@ -131,8 +132,11 @@ mod tests { last_pin_timestamp: None, }; - let deserialized: Group = serde_json::from_value(value).unwrap(); + let deserialized = Group::deserialize(&value).unwrap(); + assert_eq_fields!(channel, deserialized); + let channel = Channel::Group(channel); + let deserialized = Channel::deserialize(&value).unwrap(); assert_eq_fields!(channel, deserialized); } diff --git a/src/model/channel/guild/mod.rs b/src/model/channel/guild/mod.rs index 71afa37..cc75e0b 100644 --- a/src/model/channel/guild/mod.rs +++ b/src/model/channel/guild/mod.rs @@ -101,4 +101,13 @@ impl<'de> Deserialize<'de> for GuildChannel { } } -impl_eq_fields!(GuildChannel: (_a, _b) => { todo!() }); +impl_eq_fields!(GuildChannel: (a, b) => { + match (a, b) { + (GuildChannel::Text(a), GuildChannel::Text(b)) => assert_eq_fields!(a, b), + (GuildChannel::Voice, GuildChannel::Voice) => todo!(), + (GuildChannel::Category, GuildChannel::Category) => todo!(), + (GuildChannel::News, GuildChannel::News) => todo!(), + (GuildChannel::Store, GuildChannel::Store) => todo!(), + (a, b) => panic_ne_fields!(a, b), + } +}); diff --git a/src/model/channel/guild/text_channel.rs b/src/model/channel/guild/text_channel.rs index 7dcd8c7..819cdd8 100644 --- a/src/model/channel/guild/text_channel.rs +++ b/src/model/channel/guild/text_channel.rs @@ -70,6 +70,8 @@ impl_eq_fields!(TextChannel: [ mod tests { use serde_json::json; + use crate::model::channel::{Channel, GuildChannel}; + use super::*; #[test] @@ -102,8 +104,11 @@ mod tests { last_pin_timestamp: None, }; - let deserialized: TextChannel = serde_json::from_value(value).unwrap(); + let deserialized = TextChannel::deserialize(&value).unwrap(); + assert_eq_fields!(channel, deserialized); + let channel = Channel::Guild(GuildChannel::Text(channel)); + let deserialized = Channel::deserialize(&value).unwrap(); assert_eq_fields!(channel, deserialized); } diff --git a/src/model/channel/message/mod.rs b/src/model/channel/message/mod.rs index e73a0d8..beae003 100644 --- a/src/model/channel/message/mod.rs +++ b/src/model/channel/message/mod.rs @@ -10,7 +10,8 @@ use chrono::{DateTime, FixedOffset}; use serde::de; use serde::{Deserialize, Deserializer, Serialize, Serializer}; -use crate::model::channel::{ChannelType, Reaction}; +use crate::model::channel::ChannelType; +use crate::model::guild::PartialEmoji; use crate::model::guild::PartialMember; use crate::model::id::{ChannelId, GuildId, MessageId, RoleId, WebhookId}; use crate::model::user::User; @@ -216,6 +217,20 @@ impl<'de> Deserialize<'de> for MessageFlags { } } +/// A reaction to a [`Message`]. +/// +/// [`Message`]: struct.Message.html +#[non_exhaustive] +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct Reaction { + /// The number of reactions with this emoji. + pub count: u64, + /// Whether the current user reacted with this emoji. + pub me: bool, + /// The partial emoji information for the reaction. + pub emoji: PartialEmoji, +} + #[cfg(test)] mod tests { use serde_json::json; diff --git a/src/model/channel/mod.rs b/src/model/channel/mod.rs index 0ed1245..356adb9 100644 --- a/src/model/channel/mod.rs +++ b/src/model/channel/mod.rs @@ -13,8 +13,6 @@ pub mod permissions; use serde::de; use serde::{Deserialize, Deserializer, Serialize, Serializer}; -use crate::model::guild::PartialEmoji; - pub use self::dm::DMChannel; pub use self::group::Group; pub use self::guild::GuildChannel; @@ -58,20 +56,6 @@ impl ChannelType { } } -/// A reaction to a [`Message`]. -/// -/// [`Message`]: struct.Message.html -#[non_exhaustive] -#[derive(Clone, Debug, Deserialize, Serialize)] -pub struct Reaction { - /// The number of reactions with this emoji. - pub count: u64, - /// Whether the current user reacted with this emoji. - pub me: bool, - /// The partial emoji information for the reaction. - pub emoji: PartialEmoji, -} - /// A channel in Discord. #[non_exhaustive] #[remain::sorted] @@ -124,6 +108,8 @@ impl<'de> Deserialize<'de> for Channel { { use serde_json::{Map, Value}; + // TODO: Run benchmarks and maybe write a custom approach if warranted. + let map: Map = Map::deserialize(deserializer)?; let kind = ChannelType::from_map(&map)?; @@ -152,115 +138,3 @@ impl_eq_fields!(Channel: (a, b) => { (a, b) => panic_ne_fields!(a, b), } }); - -#[cfg(test)] -mod tests { - use std::collections::HashMap; - use std::iter::FromIterator; - - use serde_json::json; - - use crate::model::id::{ChannelId, MessageId, UserId}; - use crate::model::user::{Discriminator, User}; - - use super::*; - - #[test] - fn test_deserialize_dm() { - let value = json!({ - "last_message_id": "3343820033257021450", - "type": 1, - "id": "319674150115610528", - "recipients": [ - { - "username": "test", - "discriminator": "9999", - "id": "82198898841029460", - "avatar": "33ecab261d4681afa4d85a04691c4a01" - } - ] - }); - let channel = Channel::DM(DMChannel { - id: ChannelId::from(319674150115610528), - kind: ChannelType::Private, - recipient: User { - id: UserId::from(82198898841029460), - name: "test".to_owned(), - discriminator: Discriminator::new(9999).unwrap(), - avatar: Some("33ecab261d4681afa4d85a04691c4a01".to_owned()), - bot: false, - system: false, - }, - last_message_id: Some(MessageId::from(3343820033257021450)), - last_pin_timestamp: None, - }); - - let deserialized: Channel = serde_json::from_value(value).unwrap(); - - assert_eq_fields!(channel, deserialized); - } - - #[test] - fn test_deserialize_group() { - let value = json!({ - "name": "Some test channel", - "icon": null, - "recipients": [ - { - "username": "test", - "discriminator": "9999", - "id": "82198898841029460", - "avatar": "33ecab261d4681afa4d85a04691c4a01" - }, - { - "username": "test2", - "discriminator": "9999", - "id": "53908099506183680", - "avatar": "a_bab14f271d565501444b2ca3be944b25" - } - ], - "last_message_id": "3343820033257021450", - "type": 3, - "id": "319674150115710528", - "owner_id": "82198810841029460", - }); - let channel = Channel::Group(Group { - id: ChannelId::from(319674150115710528), - kind: ChannelType::Group, - name: Some("Some test channel".to_owned()), - icon: None, - recipients: HashMap::from_iter(vec![ - ( - UserId::from(82198898841029460), - User { - id: UserId::from(82198898841029460), - name: "test".to_string(), - discriminator: Discriminator::new(9999).unwrap(), - avatar: Some("33ecab261d4681afa4d85a04691c4a01".to_owned()), - bot: false, - system: false, - }, - ), - ( - UserId::from(53908099506183680), - User { - id: UserId::from(53908099506183680), - name: "test2".to_string(), - discriminator: Discriminator::new(9999).unwrap(), - avatar: Some("a_bab14f271d565501444b2ca3be944b25".to_owned()), - bot: false, - system: false, - }, - ), - ]), - owner_id: UserId::from(82198810841029460), - application_id: None, - last_message_id: Some(MessageId::from(3343820033257021450)), - last_pin_timestamp: None, - }); - - let deserialized: Channel = serde_json::from_value(value).unwrap(); - - assert_eq_fields!(channel, deserialized); - } -} diff --git a/src/model/guild/emoji/mod.rs b/src/model/guild/emoji/mod.rs index 3216a66..66af52c 100644 --- a/src/model/guild/emoji/mod.rs +++ b/src/model/guild/emoji/mod.rs @@ -74,7 +74,6 @@ mod tests { }; let deserialized: Emoji = serde_json::from_value(value).unwrap(); - assert_eq_fields!(emoji, deserialized); } diff --git a/src/model/user/mod.rs b/src/model/user/mod.rs index 55b9363..6a3abbf 100644 --- a/src/model/user/mod.rs +++ b/src/model/user/mod.rs @@ -100,7 +100,6 @@ mod tests { }; let user2: User = serde_json::from_value(value.clone()).unwrap(); - assert_eq_fields!(user, user2); } @@ -123,9 +122,7 @@ mod tests { system: false, }; - let value2 = serde_json::to_value(user).unwrap(); - - assert_eq!(value, value2); + assert_eq!(value, serde_json::to_value(user).unwrap()); } #[test] @@ -161,7 +158,6 @@ mod tests { }; let user2: ClientUser = serde_json::from_value(value.clone()).unwrap(); - assert_eq_fields!(user, user2); } @@ -197,7 +193,6 @@ mod tests { }; let user2: ClientUser = serde_json::from_value(value.clone()).unwrap(); - assert_eq_fields!(user, user2); } @@ -234,8 +229,6 @@ mod tests { premium_type: None, }; - let value2 = serde_json::to_value(user).unwrap(); - - assert_eq!(value, value2); + assert_eq!(value, serde_json::to_value(user).unwrap()); } } From 49ca528e4a9e06cfe7bde68ed91652a0c045f5dd Mon Sep 17 00:00:00 2001 From: James Whaley Date: Fri, 10 Jan 2020 12:34:01 +0000 Subject: [PATCH 20/44] Add VoiceChannel model --- src/model/channel/guild/mod.rs | 12 ++- src/model/channel/guild/text_channel.rs | 2 +- src/model/channel/guild/voice_channel.rs | 118 +++++++++++++++++++++++ 3 files changed, 126 insertions(+), 6 deletions(-) create mode 100644 src/model/channel/guild/voice_channel.rs diff --git a/src/model/channel/guild/mod.rs b/src/model/channel/guild/mod.rs index cc75e0b..442b558 100644 --- a/src/model/channel/guild/mod.rs +++ b/src/model/channel/guild/mod.rs @@ -1,6 +1,7 @@ //! Guild channel models. mod text_channel; +mod voice_channel; use serde::de; use serde::{Deserialize, Deserializer, Serialize, Serializer}; @@ -8,6 +9,7 @@ use serde::{Deserialize, Deserializer, Serialize, Serializer}; use crate::model::channel::ChannelType; pub use self::text_channel::TextChannel; +pub use self::voice_channel::VoiceChannel; /// A channel in a [`Guild`]. /// @@ -22,7 +24,7 @@ pub enum GuildChannel { /// A voice channel in a [`Guild`]. /// /// [`Guild`]: TODO - Voice, // TODO: Add VoiceChannel. + Voice(VoiceChannel), /// An organizational category that contains non-category channels. Category, // TODO: Add Category. /// A channel that users can follow and crosspost into another [`Guild`]. @@ -38,7 +40,7 @@ impl GuildChannel { pub fn kind(&self) -> ChannelType { match self { GuildChannel::Text(_) => ChannelType::Text, - GuildChannel::Voice => ChannelType::Voice, + GuildChannel::Voice(_) => ChannelType::Voice, GuildChannel::Category => ChannelType::Category, GuildChannel::News => ChannelType::News, GuildChannel::Store => ChannelType::Store, @@ -56,7 +58,7 @@ impl GuildChannel { { let result = match kind { ChannelType::Text => TextChannel::deserialize(value).map(GuildChannel::Text), - ChannelType::Voice => todo!(), + ChannelType::Voice => VoiceChannel::deserialize(value).map(GuildChannel::Voice), ChannelType::Category => todo!(), ChannelType::News => todo!(), ChannelType::Store => todo!(), @@ -78,7 +80,7 @@ impl Serialize for GuildChannel { { match self { GuildChannel::Text(channel) => channel.serialize(serializer), - GuildChannel::Voice => todo!(), + GuildChannel::Voice(channel) => channel.serialize(serializer), GuildChannel::Category => todo!(), GuildChannel::News => todo!(), GuildChannel::Store => todo!(), @@ -104,7 +106,7 @@ impl<'de> Deserialize<'de> for GuildChannel { impl_eq_fields!(GuildChannel: (a, b) => { match (a, b) { (GuildChannel::Text(a), GuildChannel::Text(b)) => assert_eq_fields!(a, b), - (GuildChannel::Voice, GuildChannel::Voice) => todo!(), + (GuildChannel::Voice(a), GuildChannel::Voice(b)) => assert_eq_fields!(a, b), (GuildChannel::Category, GuildChannel::Category) => todo!(), (GuildChannel::News, GuildChannel::News) => todo!(), (GuildChannel::Store, GuildChannel::Store) => todo!(), diff --git a/src/model/channel/guild/text_channel.rs b/src/model/channel/guild/text_channel.rs index 819cdd8..8dc804c 100644 --- a/src/model/channel/guild/text_channel.rs +++ b/src/model/channel/guild/text_channel.rs @@ -23,7 +23,7 @@ pub struct TextChannel { /// The ID of the guild. pub guild_id: GuildId, /// The sorting position of the chanel. - pub position: u64, + pub position: usize, /// A collection of explicit permission overwrites for members and roles. #[serde(default)] pub permission_overwrites: Vec, diff --git a/src/model/channel/guild/voice_channel.rs b/src/model/channel/guild/voice_channel.rs new file mode 100644 index 0000000..1f856d3 --- /dev/null +++ b/src/model/channel/guild/voice_channel.rs @@ -0,0 +1,118 @@ +use serde::{Deserialize, Serialize}; + +use crate::model::channel::permissions::PermissionOverwrite; +use crate::model::channel::ChannelType; +use crate::model::id::{ChannelId, GuildId}; + +/// A voice channel in a [`Guild`]. +/// +/// [`Guild`]: TODO +#[non_exhaustive] +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct VoiceChannel { + /// The ID of the channel. + pub id: ChannelId, + /// The type of the channel. + /// + /// This should always be [`ChannelType::Voice`]. + /// + /// [`ChannelType::Voice`]: ../enum.ChannelType.html#variant.Voice + #[serde(rename = "type")] + pub(crate) kind: ChannelType, + /// The ID of the guild. + pub guild_id: GuildId, + /// The sorting position of the chanel. + pub position: usize, + /// A collection of explicit permission overwrites for members and roles. + #[serde(default)] + pub permission_overwrites: Vec, + /// The name of the channel. + pub name: String, + /// The bitrate (in bits) of the voice channel. + pub bitrate: u32, + /// The user limit of the channel voice channel, a limit of `0` is + /// unlimited. + pub user_limit: u8, + /// The ID of the parent category of the channel. + pub parent_id: Option, +} + +impl_eq_fields!(VoiceChannel: [ + id, + kind, + guild_id, + position, + permission_overwrites, + name, +]); + +#[cfg(test)] +mod tests { + use serde_json::json; + + use crate::model::channel::{Channel, GuildChannel}; + + use super::*; + + #[test] + fn test_deserialize() { + let value = json!({ + "id": "155101607195836416", + "guild_id": "41771983423143937", + "name": "ROCKET CHEESE", + "type": 2, + "nsfw": false, + "position": 5, + "permission_overwrites": [], + "bitrate": 64000, + "user_limit": 0, + "parent_id": null + }); + let channel = VoiceChannel { + id: ChannelId::from(155101607195836416), + kind: ChannelType::Voice, + guild_id: GuildId::from(41771983423143937), + position: 5, + permission_overwrites: vec![], + name: "ROCKET CHEESE".to_owned(), + bitrate: 64000, + user_limit: 0, + parent_id: None, + }; + + let deserialized = VoiceChannel::deserialize(&value).unwrap(); + assert_eq_fields!(channel, deserialized); + + let channel = Channel::Guild(GuildChannel::Voice(channel)); + let deserialized = Channel::deserialize(&value).unwrap(); + assert_eq_fields!(channel, deserialized); + } + + #[test] + fn test_serialize() { + let value = json!({ + "id": "155101607195836416", + "guild_id": "41771983423143937", + "name": "ROCKET CHEESE", + "type": 2, + "position": 5, + "permission_overwrites": [], + "bitrate": 64000, + "user_limit": 0, + "parent_id": null + }); + let channel = VoiceChannel { + id: ChannelId::from(155101607195836416), + kind: ChannelType::Voice, + guild_id: GuildId::from(41771983423143937), + position: 5, + permission_overwrites: vec![], + name: "ROCKET CHEESE".to_owned(), + bitrate: 64000, + user_limit: 0, + parent_id: None, + }; + + assert_eq!(value, serde_json::to_value(&channel).unwrap()); + } +} From 77c41de071342238d67201ced9c3982998a6a47e Mon Sep 17 00:00:00 2001 From: James Whaley Date: Fri, 10 Jan 2020 12:50:31 +0000 Subject: [PATCH 21/44] Add Category model --- src/model/channel/{dm.rs => dm_channel.rs} | 0 src/model/channel/guild/category.rs | 98 ++++++++++++++++++++++ src/model/channel/guild/mod.rs | 12 +-- src/model/channel/guild/voice_channel.rs | 3 + src/model/channel/mod.rs | 4 +- 5 files changed, 110 insertions(+), 7 deletions(-) rename src/model/channel/{dm.rs => dm_channel.rs} (100%) create mode 100644 src/model/channel/guild/category.rs diff --git a/src/model/channel/dm.rs b/src/model/channel/dm_channel.rs similarity index 100% rename from src/model/channel/dm.rs rename to src/model/channel/dm_channel.rs diff --git a/src/model/channel/guild/category.rs b/src/model/channel/guild/category.rs new file mode 100644 index 0000000..5078cd4 --- /dev/null +++ b/src/model/channel/guild/category.rs @@ -0,0 +1,98 @@ +use serde::{Deserialize, Serialize}; + +use crate::model::channel::permissions::PermissionOverwrite; +use crate::model::channel::ChannelType; +use crate::model::id::{ChannelId, GuildId}; + +/// An organizational category that contains non-category channels. +#[non_exhaustive] +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct Category { + /// The ID of the channel. + pub id: ChannelId, + /// The type of the channel. + /// + /// This should always be [`ChannelType::Category`]. + /// + /// [`ChannelType::Category`]: ../enum.ChannelType.html#variant.Category + #[serde(rename = "type")] + pub(crate) kind: ChannelType, + /// The ID of the guild. + pub guild_id: GuildId, + /// The sorting position of the chanel. + pub position: usize, + /// A collection of explicit permission overwrites for members and roles. + #[serde(default)] + pub permission_overwrites: Vec, + /// The name of the channel. + pub name: String, +} + +impl_eq_fields!(Category: [ + id, + kind, + guild_id, + position, + permission_overwrites, + name, +]); + +#[cfg(test)] +mod tests { + use serde_json::json; + + use crate::model::channel::{Channel, GuildChannel}; + + use super::*; + + #[test] + fn test_deserialize() { + let value = json!({ + "permission_overwrites": [], + "name": "Test", + "parent_id": null, + "nsfw": false, + "position": 0, + "guild_id": "290926798629997250", + "type": 4, + "id": "399942396007890945" + }); + let channel = Category { + id: ChannelId::from(399942396007890945), + kind: ChannelType::Category, + guild_id: GuildId::from(290926798629997250), + position: 0, + permission_overwrites: vec![], + name: "Test".to_owned(), + }; + + let deserialized = Category::deserialize(&value).unwrap(); + assert_eq_fields!(channel, deserialized); + + let channel = Channel::Guild(GuildChannel::Category(channel)); + let deserialized = Channel::deserialize(&value).unwrap(); + assert_eq_fields!(channel, deserialized); + } + + #[test] + fn test_serialize() { + let value = json!({ + "permission_overwrites": [], + "name": "Test", + "position": 0, + "guild_id": "290926798629997250", + "type": 4, + "id": "399942396007890945" + }); + let channel = Category { + id: ChannelId::from(399942396007890945), + kind: ChannelType::Category, + guild_id: GuildId::from(290926798629997250), + position: 0, + permission_overwrites: vec![], + name: "Test".to_owned(), + }; + + assert_eq!(value, serde_json::to_value(&channel).unwrap()); + } +} diff --git a/src/model/channel/guild/mod.rs b/src/model/channel/guild/mod.rs index 442b558..dea9f79 100644 --- a/src/model/channel/guild/mod.rs +++ b/src/model/channel/guild/mod.rs @@ -1,5 +1,6 @@ //! Guild channel models. +mod category; mod text_channel; mod voice_channel; @@ -8,6 +9,7 @@ use serde::{Deserialize, Deserializer, Serialize, Serializer}; use crate::model::channel::ChannelType; +pub use self::category::Category; pub use self::text_channel::TextChannel; pub use self::voice_channel::VoiceChannel; @@ -26,7 +28,7 @@ pub enum GuildChannel { /// [`Guild`]: TODO Voice(VoiceChannel), /// An organizational category that contains non-category channels. - Category, // TODO: Add Category. + Category(Category), /// A channel that users can follow and crosspost into another [`Guild`]. /// /// [`Guild`]: TODO @@ -41,7 +43,7 @@ impl GuildChannel { match self { GuildChannel::Text(_) => ChannelType::Text, GuildChannel::Voice(_) => ChannelType::Voice, - GuildChannel::Category => ChannelType::Category, + GuildChannel::Category(_) => ChannelType::Category, GuildChannel::News => ChannelType::News, GuildChannel::Store => ChannelType::Store, } @@ -59,7 +61,7 @@ impl GuildChannel { let result = match kind { ChannelType::Text => TextChannel::deserialize(value).map(GuildChannel::Text), ChannelType::Voice => VoiceChannel::deserialize(value).map(GuildChannel::Voice), - ChannelType::Category => todo!(), + ChannelType::Category => Category::deserialize(value).map(GuildChannel::Category), ChannelType::News => todo!(), ChannelType::Store => todo!(), kind => { @@ -81,7 +83,7 @@ impl Serialize for GuildChannel { match self { GuildChannel::Text(channel) => channel.serialize(serializer), GuildChannel::Voice(channel) => channel.serialize(serializer), - GuildChannel::Category => todo!(), + GuildChannel::Category(channel) => channel.serialize(serializer), GuildChannel::News => todo!(), GuildChannel::Store => todo!(), } @@ -107,7 +109,7 @@ impl_eq_fields!(GuildChannel: (a, b) => { match (a, b) { (GuildChannel::Text(a), GuildChannel::Text(b)) => assert_eq_fields!(a, b), (GuildChannel::Voice(a), GuildChannel::Voice(b)) => assert_eq_fields!(a, b), - (GuildChannel::Category, GuildChannel::Category) => todo!(), + (GuildChannel::Category(a), GuildChannel::Category(b)) => assert_eq_fields!(a, b), (GuildChannel::News, GuildChannel::News) => todo!(), (GuildChannel::Store, GuildChannel::Store) => todo!(), (a, b) => panic_ne_fields!(a, b), diff --git a/src/model/channel/guild/voice_channel.rs b/src/model/channel/guild/voice_channel.rs index 1f856d3..fc030e3 100644 --- a/src/model/channel/guild/voice_channel.rs +++ b/src/model/channel/guild/voice_channel.rs @@ -44,6 +44,9 @@ impl_eq_fields!(VoiceChannel: [ position, permission_overwrites, name, + bitrate, + user_limit, + parent_id, ]); #[cfg(test)] diff --git a/src/model/channel/mod.rs b/src/model/channel/mod.rs index 356adb9..7c189c4 100644 --- a/src/model/channel/mod.rs +++ b/src/model/channel/mod.rs @@ -3,7 +3,7 @@ // Internal utility module. mod utils; -mod dm; +mod dm_channel; mod group; pub mod guild; @@ -13,7 +13,7 @@ pub mod permissions; use serde::de; use serde::{Deserialize, Deserializer, Serialize, Serializer}; -pub use self::dm::DMChannel; +pub use self::dm_channel::DMChannel; pub use self::group::Group; pub use self::guild::GuildChannel; pub use self::message::Message; From 503aeb4b1e42228c10f5828ca28c235003dfddbf Mon Sep 17 00:00:00 2001 From: James Whaley Date: Fri, 10 Jan 2020 13:00:34 +0000 Subject: [PATCH 22/44] Add NewsChannel model --- src/model/channel/guild/mod.rs | 12 ++- src/model/channel/guild/news_channel.rs | 134 ++++++++++++++++++++++++ 2 files changed, 141 insertions(+), 5 deletions(-) create mode 100644 src/model/channel/guild/news_channel.rs diff --git a/src/model/channel/guild/mod.rs b/src/model/channel/guild/mod.rs index dea9f79..addb5d7 100644 --- a/src/model/channel/guild/mod.rs +++ b/src/model/channel/guild/mod.rs @@ -1,6 +1,7 @@ //! Guild channel models. mod category; +mod news_channel; mod text_channel; mod voice_channel; @@ -10,6 +11,7 @@ use serde::{Deserialize, Deserializer, Serialize, Serializer}; use crate::model::channel::ChannelType; pub use self::category::Category; +pub use self::news_channel::NewsChannel; pub use self::text_channel::TextChannel; pub use self::voice_channel::VoiceChannel; @@ -32,7 +34,7 @@ pub enum GuildChannel { /// A channel that users can follow and crosspost into another [`Guild`]. /// /// [`Guild`]: TODO - News, // TODO: Add NewsChannel. + News(NewsChannel), /// A channel in which game developers can sell games on Discord. Store, // TODO: Add StoreChannel. } @@ -44,7 +46,7 @@ impl GuildChannel { GuildChannel::Text(_) => ChannelType::Text, GuildChannel::Voice(_) => ChannelType::Voice, GuildChannel::Category(_) => ChannelType::Category, - GuildChannel::News => ChannelType::News, + GuildChannel::News(_) => ChannelType::News, GuildChannel::Store => ChannelType::Store, } } @@ -62,7 +64,7 @@ impl GuildChannel { ChannelType::Text => TextChannel::deserialize(value).map(GuildChannel::Text), ChannelType::Voice => VoiceChannel::deserialize(value).map(GuildChannel::Voice), ChannelType::Category => Category::deserialize(value).map(GuildChannel::Category), - ChannelType::News => todo!(), + ChannelType::News => NewsChannel::deserialize(value).map(GuildChannel::News), ChannelType::Store => todo!(), kind => { return Err(E::custom(format_args!( @@ -84,7 +86,7 @@ impl Serialize for GuildChannel { GuildChannel::Text(channel) => channel.serialize(serializer), GuildChannel::Voice(channel) => channel.serialize(serializer), GuildChannel::Category(channel) => channel.serialize(serializer), - GuildChannel::News => todo!(), + GuildChannel::News(channel) => channel.serialize(serializer), GuildChannel::Store => todo!(), } } @@ -110,7 +112,7 @@ impl_eq_fields!(GuildChannel: (a, b) => { (GuildChannel::Text(a), GuildChannel::Text(b)) => assert_eq_fields!(a, b), (GuildChannel::Voice(a), GuildChannel::Voice(b)) => assert_eq_fields!(a, b), (GuildChannel::Category(a), GuildChannel::Category(b)) => assert_eq_fields!(a, b), - (GuildChannel::News, GuildChannel::News) => todo!(), + (GuildChannel::News(a), GuildChannel::News(b)) => assert_eq_fields!(a, b), (GuildChannel::Store, GuildChannel::Store) => todo!(), (a, b) => panic_ne_fields!(a, b), } diff --git a/src/model/channel/guild/news_channel.rs b/src/model/channel/guild/news_channel.rs new file mode 100644 index 0000000..fe775ae --- /dev/null +++ b/src/model/channel/guild/news_channel.rs @@ -0,0 +1,134 @@ +use chrono::{DateTime, FixedOffset}; +use serde::{Deserialize, Serialize}; + +use crate::model::channel::permissions::PermissionOverwrite; +use crate::model::channel::ChannelType; +use crate::model::id::{ChannelId, GuildId, MessageId}; + +/// A text channel in a [`Guild`]. +/// +/// [`Guild`]: TODO +#[non_exhaustive] +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct NewsChannel { + /// The ID of the channel. + pub id: ChannelId, + /// The type of the channel. + /// + /// This should always be [`ChannelType::Text`]. + /// + /// [`ChannelType::Text`]: ../enum.ChannelType.html#variant.Text + #[serde(rename = "type")] + pub(crate) kind: ChannelType, + /// The ID of the guild. + pub guild_id: GuildId, + /// The sorting position of the chanel. + pub position: usize, + /// A collection of explicit permission overwrites for members and roles. + #[serde(default)] + pub permission_overwrites: Vec, + /// The name of the channel. + pub name: String, + /// The topic of the channel. + pub topic: Option, + /// Whether the channel is NSFW. + #[serde(default)] + pub nsfw: bool, + /// The ID of the last message sent to the group. + pub last_message_id: Option, + /// The ID of the parent category of the channel. + pub parent_id: Option, + /// When the last message was pinned. + pub last_pin_timestamp: Option>, +} + +impl_eq_fields!(NewsChannel: [ + id, + kind, + guild_id, + position, + permission_overwrites, + name, + topic, + nsfw, + last_message_id, + parent_id, + last_pin_timestamp, +]); + +#[cfg(test)] +mod tests { + use serde_json::json; + + use crate::model::channel::{Channel, GuildChannel}; + + use super::*; + + #[test] + fn test_deserialize() { + let value = json!({ + "id": "41771983423143937", + "guild_id": "41771983423143937", + "name": "important-news", + "type": 5, + "position": 6, + "permission_overwrites": [], + "nsfw": true, + "topic": "Rumors about Half Life 3", + "last_message_id": "155117677105512449", + "parent_id": "399942396007890945" + }); + let channel = NewsChannel { + id: ChannelId::from(41771983423143937), + kind: ChannelType::News, + guild_id: GuildId::from(41771983423143937), + position: 6, + permission_overwrites: vec![], + name: "important-news".to_owned(), + topic: Some("Rumors about Half Life 3".to_owned()), + nsfw: true, + last_message_id: Some(MessageId::from(155117677105512449)), + parent_id: Some(ChannelId::from(399942396007890945)), + last_pin_timestamp: None, + }; + + let deserialized = NewsChannel::deserialize(&value).unwrap(); + assert_eq_fields!(channel, deserialized); + + let channel = Channel::Guild(GuildChannel::News(channel)); + let deserialized = Channel::deserialize(&value).unwrap(); + assert_eq_fields!(channel, deserialized); + } + + #[test] + fn test_serialize() { + let value = json!({ + "id": "41771983423143937", + "guild_id": "41771983423143937", + "name": "important-news", + "type": 5, + "position": 6, + "permission_overwrites": [], + "nsfw": true, + "topic": "Rumors about Half Life 3", + "last_message_id": "155117677105512449", + "parent_id": "399942396007890945", + "last_pin_timestamp": null + }); + let channel = NewsChannel { + id: ChannelId::from(41771983423143937), + kind: ChannelType::News, + guild_id: GuildId::from(41771983423143937), + position: 6, + permission_overwrites: vec![], + name: "important-news".to_owned(), + topic: Some("Rumors about Half Life 3".to_owned()), + nsfw: true, + last_message_id: Some(MessageId::from(155117677105512449)), + parent_id: Some(ChannelId::from(399942396007890945)), + last_pin_timestamp: None, + }; + + assert_eq!(value, serde_json::to_value(&channel).unwrap()); + } +} From adacf227551f47e95db9a005e446bf5d706f63c1 Mon Sep 17 00:00:00 2001 From: James Whaley Date: Fri, 10 Jan 2020 13:12:20 +0000 Subject: [PATCH 23/44] Add StoreChannel model --- src/model/channel/guild/mod.rs | 12 ++- src/model/channel/guild/news_channel.rs | 4 +- src/model/channel/guild/store_channel.rs | 111 +++++++++++++++++++++++ 3 files changed, 120 insertions(+), 7 deletions(-) create mode 100644 src/model/channel/guild/store_channel.rs diff --git a/src/model/channel/guild/mod.rs b/src/model/channel/guild/mod.rs index addb5d7..03eab6a 100644 --- a/src/model/channel/guild/mod.rs +++ b/src/model/channel/guild/mod.rs @@ -2,6 +2,7 @@ mod category; mod news_channel; +mod store_channel; mod text_channel; mod voice_channel; @@ -12,6 +13,7 @@ use crate::model::channel::ChannelType; pub use self::category::Category; pub use self::news_channel::NewsChannel; +pub use self::store_channel::StoreChannel; pub use self::text_channel::TextChannel; pub use self::voice_channel::VoiceChannel; @@ -36,7 +38,7 @@ pub enum GuildChannel { /// [`Guild`]: TODO News(NewsChannel), /// A channel in which game developers can sell games on Discord. - Store, // TODO: Add StoreChannel. + Store(StoreChannel), } impl GuildChannel { @@ -47,7 +49,7 @@ impl GuildChannel { GuildChannel::Voice(_) => ChannelType::Voice, GuildChannel::Category(_) => ChannelType::Category, GuildChannel::News(_) => ChannelType::News, - GuildChannel::Store => ChannelType::Store, + GuildChannel::Store(_) => ChannelType::Store, } } } @@ -65,7 +67,7 @@ impl GuildChannel { ChannelType::Voice => VoiceChannel::deserialize(value).map(GuildChannel::Voice), ChannelType::Category => Category::deserialize(value).map(GuildChannel::Category), ChannelType::News => NewsChannel::deserialize(value).map(GuildChannel::News), - ChannelType::Store => todo!(), + ChannelType::Store => StoreChannel::deserialize(value).map(GuildChannel::Store), kind => { return Err(E::custom(format_args!( "invalid channel type for guild channel: {:?}", @@ -87,7 +89,7 @@ impl Serialize for GuildChannel { GuildChannel::Voice(channel) => channel.serialize(serializer), GuildChannel::Category(channel) => channel.serialize(serializer), GuildChannel::News(channel) => channel.serialize(serializer), - GuildChannel::Store => todo!(), + GuildChannel::Store(channel) => channel.serialize(serializer), } } } @@ -113,7 +115,7 @@ impl_eq_fields!(GuildChannel: (a, b) => { (GuildChannel::Voice(a), GuildChannel::Voice(b)) => assert_eq_fields!(a, b), (GuildChannel::Category(a), GuildChannel::Category(b)) => assert_eq_fields!(a, b), (GuildChannel::News(a), GuildChannel::News(b)) => assert_eq_fields!(a, b), - (GuildChannel::Store, GuildChannel::Store) => todo!(), + (GuildChannel::Store(a), GuildChannel::Store(b)) => assert_eq_fields!(a, b), (a, b) => panic_ne_fields!(a, b), } }); diff --git a/src/model/channel/guild/news_channel.rs b/src/model/channel/guild/news_channel.rs index fe775ae..e39e3fa 100644 --- a/src/model/channel/guild/news_channel.rs +++ b/src/model/channel/guild/news_channel.rs @@ -15,9 +15,9 @@ pub struct NewsChannel { pub id: ChannelId, /// The type of the channel. /// - /// This should always be [`ChannelType::Text`]. + /// This should always be [`ChannelType::News`]. /// - /// [`ChannelType::Text`]: ../enum.ChannelType.html#variant.Text + /// [`ChannelType::News`]: ../enum.ChannelType.html#variant.News #[serde(rename = "type")] pub(crate) kind: ChannelType, /// The ID of the guild. diff --git a/src/model/channel/guild/store_channel.rs b/src/model/channel/guild/store_channel.rs new file mode 100644 index 0000000..64c196b --- /dev/null +++ b/src/model/channel/guild/store_channel.rs @@ -0,0 +1,111 @@ +use serde::{Deserialize, Serialize}; + +use crate::model::channel::permissions::PermissionOverwrite; +use crate::model::channel::ChannelType; +use crate::model::id::{ChannelId, GuildId}; + +/// A channel in which game developers can sell games on Discord. +#[non_exhaustive] +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct StoreChannel { + /// The ID of the channel. + pub id: ChannelId, + /// The type of the channel. + /// + /// This should always be [`ChannelType::Store`]. + /// + /// [`ChannelType::Store`]: ../enum.ChannelType.html#variant.Store + #[serde(rename = "type")] + pub(crate) kind: ChannelType, + /// The ID of the guild. + pub guild_id: GuildId, + /// The sorting position of the chanel. + pub position: usize, + /// A collection of explicit permission overwrites for members and roles. + #[serde(default)] + pub permission_overwrites: Vec, + /// The name of the channel. + pub name: String, + /// Whether the channel is NSFW. + #[serde(default)] + pub nsfw: bool, + /// The ID of the parent category of the channel. + pub parent_id: Option, +} + +impl_eq_fields!(StoreChannel: [ + id, + kind, + guild_id, + position, + permission_overwrites, + name, + nsfw, + parent_id, +]); + +#[cfg(test)] +mod tests { + use serde_json::json; + + use crate::model::channel::{Channel, GuildChannel}; + + use super::*; + + #[test] + fn test_deserialize() { + let value = json!({ + "id": "41771983423143937", + "guild_id": "41771983423143937", + "name": "buy dota-2", + "type": 6, + "position": 0, + "permission_overwrites": [], + "nsfw": false, + "parent_id": null + }); + let channel = StoreChannel { + id: ChannelId::from(41771983423143937), + kind: ChannelType::Store, + guild_id: GuildId::from(41771983423143937), + position: 0, + permission_overwrites: vec![], + name: "buy dota-2".to_owned(), + nsfw: false, + parent_id: None, + }; + + let deserialized = StoreChannel::deserialize(&value).unwrap(); + assert_eq_fields!(channel, deserialized); + + let channel = Channel::Guild(GuildChannel::Store(channel)); + let deserialized = Channel::deserialize(&value).unwrap(); + assert_eq_fields!(channel, deserialized); + } + + #[test] + fn test_serialize() { + let value = json!({ + "id": "41771983423143937", + "guild_id": "41771983423143937", + "name": "buy dota-2", + "type": 6, + "position": 0, + "permission_overwrites": [], + "nsfw": false, + "parent_id": null + }); + let channel = StoreChannel { + id: ChannelId::from(41771983423143937), + kind: ChannelType::Store, + guild_id: GuildId::from(41771983423143937), + position: 0, + permission_overwrites: vec![], + name: "buy dota-2".to_owned(), + nsfw: false, + parent_id: None, + }; + + assert_eq!(value, serde_json::to_value(&channel).unwrap()); + } +} From 79f8d6e919a9012ce7f5c6b450234116f9f0ef70 Mon Sep 17 00:00:00 2001 From: James Whaley Date: Fri, 10 Jan 2020 14:01:52 +0000 Subject: [PATCH 24/44] Remove channels.json file Remove a test file that was accidentally committed. --- channels.json | 11240 ------------------------------------------------ 1 file changed, 11240 deletions(-) delete mode 100644 channels.json diff --git a/channels.json b/channels.json deleted file mode 100644 index 1e1598d..0000000 --- a/channels.json +++ /dev/null @@ -1,11240 +0,0 @@ -[ - { - "id": "162706186272112640", - "last_message_id": "664890258189582337", - "last_pin_timestamp": "2020-01-01T22:37:59.513000+00:00", - "type": 0, - "name": "general", - "position": 24, - "parent_id": "381910837293613057", - "topic": "Chat with fellow guardians. Links ARE allowed. No NSFW allowed. Keep LFG requests in the #lfg-requests channel.", - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 1, - "deny": 131072 - }, - { - "id": "463520715254267916", - "type": "role", - "allow": 0, - "deny": 3072 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 131072, - "deny": 0 - }, - { - "id": "530919353999163414", - "type": "role", - "allow": 0, - "deny": 2112 - }, - { - "id": "538477449130803212", - "type": "role", - "allow": 0, - "deny": 131072 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "381910672256008196", - "type": 2, - "name": "AFK", - "position": 23, - "parent_id": "382125435376631820", - "bitrate": 64000, - "user_limit": 0, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 0 - }, - { - "id": "419272087132307467", - "type": "member", - "allow": 0, - "deny": 1024 - }, - { - "id": "463520715254267916", - "type": "role", - "allow": 0, - "deny": 1048576 - }, - { - "id": "530919353999163414", - "type": "role", - "allow": 0, - "deny": 1048576 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 1048576 - } - ], - "nsfw": false - }, - { - "id": "381910837293613057", - "type": 4, - "name": "Textual Chat", - "position": 4, - "parent_id": null, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 131072 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 131072, - "deny": 0 - }, - { - "id": "463520715254267916", - "type": "role", - "allow": 0, - "deny": 3072 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 131072, - "deny": 0 - }, - { - "id": "530919353999163414", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 131072, - "deny": 0 - } - ], - "nsfw": false - }, - { - "id": "381910983444135957", - "last_message_id": "664884899244802051", - "last_pin_timestamp": "2018-10-29T03:27:06.909000+00:00", - "type": 0, - "name": "welcome", - "position": 4, - "parent_id": null, - "topic": "", - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 15360 - }, - { - "id": "381911719901134850", - "type": "role", - "allow": 0, - "deny": 71680 - }, - { - "id": "382118099341541379", - "type": "role", - "allow": 1024, - "deny": 6144 - }, - { - "id": "439951550358487040", - "type": "role", - "allow": 0, - "deny": 15360 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 3072, - "deny": 0 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 1024, - "deny": 6144 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "381911060493500416", - "type": 4, - "name": "Server Info", - "position": 0, - "parent_id": null, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 1024, - "deny": 145472 - }, - { - "id": "463520715254267916", - "type": "role", - "allow": 0, - "deny": 3072 - } - ], - "nsfw": false - }, - { - "id": "382114317857849344", - "type": 4, - "name": "Staff channels", - "position": 18, - "parent_id": null, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1025 - }, - { - "id": "463520715254267916", - "type": "role", - "allow": 0, - "deny": 3072 - } - ], - "nsfw": false - }, - { - "id": "382125277066690562", - "type": 2, - "name": "Ninja A", - "position": 35, - "parent_id": "510476994476113930", - "bitrate": 64000, - "user_limit": 0, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1049600 - }, - { - "id": "382118099341541379", - "type": "role", - "allow": 1049600, - "deny": 0 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 1049600, - "deny": 0 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 17826816, - "deny": 0 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 1050112, - "deny": 0 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 1048576 - } - ], - "nsfw": false - }, - { - "id": "382125435376631820", - "type": 4, - "name": "Other", - "position": 13, - "parent_id": null, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "463520715254267916", - "type": "role", - "allow": 0, - "deny": 3148800 - } - ], - "nsfw": false - }, - { - "id": "382558620639952906", - "last_message_id": "664556483433725952", - "last_pin_timestamp": "2020-01-02T17:11:24.932000+00:00", - "type": 0, - "name": "trial-moderator-chat", - "position": 54, - "parent_id": "382114317857849344", - "topic": "", - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "382118099341541379", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "384886471531692033", - "last_message_id": "664885574011715585", - "last_pin_timestamp": "2019-10-27T19:48:16.002000+00:00", - "type": 0, - "name": "lfg-requests", - "position": 25, - "parent_id": "381910837293613057", - "topic": "Find Fireteams here.\n\nPlease refrain from using @ everyone here! Ping @sherpa for help with raids, @pve-guide for pve, and @crucible guide for crucible.\n\nRight click #lfg-request to mute channel\nAny off topic conversation will result in the message(s) being deleted", - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 1, - "deny": 49216 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 49216, - "deny": 0 - }, - { - "id": "463520715254267916", - "type": "role", - "allow": 0, - "deny": 3072 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 49216, - "deny": 0 - }, - { - "id": "530919353999163414", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 49216, - "deny": 0 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "399740243781222401", - "last_message_id": "470260894153310208", - "type": 0, - "name": "faq-old-old", - "position": 97, - "parent_id": "449088445617405962", - "topic": "", - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1051648 - }, - { - "id": "382117075558203392", - "type": "role", - "allow": 1049600, - "deny": 0 - }, - { - "id": "382118099341541379", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "409197306370326539", - "last_message_id": "664890767151464459", - "last_pin_timestamp": "2019-08-12T01:29:41.721000+00:00", - "type": 0, - "name": "bot-channel", - "position": 28, - "parent_id": "381910837293613057", - "topic": "Use this channel only for any bot commands. Check pins for more info.", - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 131072 - }, - { - "id": "441751906428256277", - "type": "member", - "allow": 0, - "deny": 3072 - }, - { - "id": "463520715254267916", - "type": "role", - "allow": 0, - "deny": 3072 - }, - { - "id": "491769129318088714", - "type": "member", - "allow": 0, - "deny": 3072 - }, - { - "id": "530919353999163414", - "type": "role", - "allow": 131072, - "deny": 2048 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 5 - }, - { - "id": "429506865097998350", - "last_message_id": "664661027912155146", - "type": 0, - "name": "announcements", - "position": 1, - "parent_id": null, - "topic": "Clan and server news, including activity check results and surveys about the state of the Discord server.", - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 1024, - "deny": 268580944 - }, - { - "id": "382116615614889984", - "type": "role", - "allow": 0, - "deny": 0 - }, - { - "id": "382117075558203392", - "type": "role", - "allow": 131072, - "deny": 0 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 134208, - "deny": 0 - }, - { - "id": "463520715254267916", - "type": "role", - "allow": 0, - "deny": 3072 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 134144, - "deny": 0 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "440336757146255391", - "last_message_id": "599321185226063882", - "last_pin_timestamp": "2018-05-28T06:46:30.876000+00:00", - "type": 0, - "name": "trial-moderator-applications", - "position": 110, - "parent_id": "449088445617405962", - "topic": "Apply for staff positions. Read the channel pins for more information.", - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1244225 - }, - { - "id": "530919353999163414", - "type": "role", - "allow": 0, - "deny": 3072 - }, - { - "id": "538477449130803212", - "type": "role", - "allow": 65536, - "deny": 3072 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "441331852528320512", - "last_message_id": "664886455297900589", - "last_pin_timestamp": "2019-12-30T14:56:36.260000+00:00", - "type": 0, - "name": "apply-to-clan", - "position": 13, - "parent_id": "479849608558936064", - "topic": "Request clan admission or missing ranks (e.g. Clan Member ranks) here.", - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 68608, - "deny": 442432 - }, - { - "id": "323177364848902145", - "type": "member", - "allow": 0, - "deny": 0 - }, - { - "id": "382118099341541379", - "type": "role", - "allow": 312384, - "deny": 0 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 312384, - "deny": 0 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 312384, - "deny": 0 - }, - { - "id": "530919353999163414", - "type": "role", - "allow": 0, - "deny": 2112 - }, - { - "id": "538477449130803212", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 312384, - "deny": 0 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "444243773254270996", - "last_message_id": "502319247230238722", - "last_pin_timestamp": "2018-05-10T21:07:07.374000+00:00", - "type": 0, - "name": "spire-of-stars-lfg", - "position": 89, - "parent_id": "449088445617405962", - "topic": "This is a temporary channel that will be removed the weekend of the 12th of May. Its purpose is to find a group for the opening weekend of the Spire of Stars raid lair, launching Friday, May 11th. Do not use this channel for any purpose other than finding a fire team.", - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1051648 - }, - { - "id": "382117075558203392", - "type": "role", - "allow": 1049600, - "deny": 0 - }, - { - "id": "382118099341541379", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "445696353503281154", - "last_message_id": "664889772812271617", - "last_pin_timestamp": "2019-11-06T01:10:46.678000+00:00", - "type": 0, - "name": "bot-logs", - "position": 62, - "parent_id": "485882029158826019", - "topic": "", - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 3072 - }, - { - "id": "382116615614889984", - "type": "role", - "allow": 0, - "deny": 0 - }, - { - "id": "382117075558203392", - "type": "role", - "allow": 3072, - "deny": 0 - }, - { - "id": "382118099341541379", - "type": "role", - "allow": 0, - "deny": 3072 - }, - { - "id": "442060343162699776", - "type": "role", - "allow": 3072, - "deny": 0 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 3072, - "deny": 0 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 0, - "deny": 3072 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 7168, - "deny": 0 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "449088445617405962", - "type": 4, - "name": "Archive", - "position": 23, - "parent_id": null, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1051648 - }, - { - "id": "382117075558203392", - "type": "role", - "allow": 1049600, - "deny": 0 - }, - { - "id": "382118099341541379", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 1024, - "deny": 0 - } - ], - "nsfw": false - }, - { - "id": "449542580330430464", - "last_message_id": "664245989422530571", - "last_pin_timestamp": "2019-11-13T02:52:34.300000+00:00", - "type": 0, - "name": "admin-chat", - "position": 58, - "parent_id": "382114317857849344", - "topic": null, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 3072 - }, - { - "id": "382116615614889984", - "type": "role", - "allow": 0, - "deny": 0 - }, - { - "id": "382117075558203392", - "type": "role", - "allow": 0, - "deny": 0 - }, - { - "id": "382118099341541379", - "type": "role", - "allow": 0, - "deny": 3072 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "452875714891481144", - "last_message_id": "664890491350810635", - "last_pin_timestamp": "2018-07-30T00:13:57.199000+00:00", - "type": 0, - "name": "song-requests", - "position": 33, - "parent_id": "382125435376631820", - "topic": "Use \";;play song name\" to request a track. This channel is only visible to users in the Club Collective voice channel for song requests.", - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 3072, - "deny": 0 - }, - { - "id": "463520715254267916", - "type": "role", - "allow": 3072, - "deny": 3145728 - }, - { - "id": "530919353999163414", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "538477449130803212", - "type": "role", - "allow": 3072, - "deny": 0 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "453945344586416128", - "last_message_id": "664889895722024971", - "type": 0, - "name": "charlemagne-feed", - "position": 11, - "parent_id": "479849608558936064", - "topic": null, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "409197133946683402", - "type": "role", - "allow": 2048, - "deny": 0 - }, - { - "id": "463520715254267916", - "type": "role", - "allow": 0, - "deny": 3072 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "459913518146125846", - "last_message_id": "663518485808152614", - "last_pin_timestamp": "2019-06-02T01:47:49.675000+00:00", - "type": 0, - "name": "programmer-chat", - "position": 64, - "parent_id": "485882029158826019", - "topic": null, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "382118099341541379", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "653357372466659349", - "type": "role", - "allow": 3072, - "deny": 0 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "463518519556833280", - "type": 2, - "name": "Ask the Admins!", - "position": 1, - "parent_id": null, - "bitrate": 100000, - "user_limit": 0, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 3146752 - }, - { - "id": "382117075558203392", - "type": "role", - "allow": 256, - "deny": 0 - }, - { - "id": "382128710779928576", - "type": "role", - "allow": 3146752, - "deny": 0 - }, - { - "id": "463520715254267916", - "type": "role", - "allow": 3146752, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 1048576 - } - ], - "nsfw": false - }, - { - "id": "466401774094516245", - "last_message_id": "626974055483965451", - "last_pin_timestamp": "2019-05-24T02:46:48.857000+00:00", - "type": 0, - "name": "stormbot", - "position": 63, - "parent_id": "485882029158826019", - "topic": "For Testing Purposes | Apparently the only spot to use ?activity", - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "382118099341541379", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "567781687874945028", - "type": "role", - "allow": 0, - "deny": 0 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "653357372466659349", - "type": "role", - "allow": 3072, - "deny": 0 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "470403009936097291", - "last_message_id": "572545790606573588", - "type": 0, - "name": "sherpa-program", - "position": 122, - "parent_id": "619529638699335685", - "topic": "Information about the Sherpa Program", - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1051713 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "482625494886645781", - "type": "role", - "allow": 0, - "deny": 268446736 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 3072, - "deny": 0 - }, - { - "id": "565734337819967489", - "type": "role", - "allow": 0, - "deny": 268446720 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 3072 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "470408603724021770", - "last_message_id": "627299350069903370", - "type": 0, - "name": "rules", - "position": 7, - "parent_id": "381911060493500416", - "topic": null, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 1024, - "deny": 145472 - }, - { - "id": "463520715254267916", - "type": "role", - "allow": 0, - "deny": 3072 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "470424640100237312", - "last_message_id": "502692186509410304", - "last_pin_timestamp": "2018-07-25T21:51:59.290000+00:00", - "type": 0, - "name": "sherpa-notes", - "position": 84, - "parent_id": "449088445617405962", - "topic": "Log notes on sherpa performance here. Keep discussion to a minimum for organization.", - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1051648 - }, - { - "id": "382117075558203392", - "type": "role", - "allow": 1049600, - "deny": 0 - }, - { - "id": "382118099341541379", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "470809196695126027", - "last_message_id": "663192067815637011", - "last_pin_timestamp": "2019-08-03T22:04:31.830000+00:00", - "type": 0, - "name": "staff-announcements", - "position": 49, - "parent_id": "382114317857849344", - "topic": "", - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 3072 - }, - { - "id": "382118099341541379", - "type": "role", - "allow": 1024, - "deny": 2048 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 3072, - "deny": 0 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 1024, - "deny": 2048 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 3072, - "deny": 0 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "472506648766971934", - "last_message_id": "502319831031480330", - "last_pin_timestamp": "2018-10-18T03:19:40.358000+00:00", - "type": 0, - "name": "edit-channel", - "position": 106, - "parent_id": "449088445617405962", - "topic": null, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1051648 - }, - { - "id": "382117075558203392", - "type": "role", - "allow": 1049600, - "deny": 0 - }, - { - "id": "382118099341541379", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "477857643734761475", - "last_message_id": "655850429124444162", - "last_pin_timestamp": "2019-05-06T11:45:40.437000+00:00", - "type": 0, - "name": "game-night-announcements", - "position": 18, - "parent_id": "485961711535390720", - "topic": "", - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 1024, - "deny": 2048 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 3148800, - "deny": 0 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 3072, - "deny": 0 - }, - { - "id": "497267428460462080", - "type": "role", - "allow": 1024, - "deny": 2048 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 3072, - "deny": 0 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "478428670332239873", - "type": 2, - "name": "Ninja B", - "position": 36, - "parent_id": "510476994476113930", - "bitrate": 64000, - "user_limit": 0, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1049600 - }, - { - "id": "382118099341541379", - "type": "role", - "allow": 1049600, - "deny": 0 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 1049600, - "deny": 0 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 17826816, - "deny": 0 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 1049600, - "deny": 0 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 1048576 - } - ], - "nsfw": false - }, - { - "id": "478711974734069770", - "last_message_id": "478712512028868630", - "type": 0, - "name": "faq-old", - "position": 114, - "parent_id": "449088445617405962", - "topic": "", - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 146496 - }, - { - "id": "463520715254267916", - "type": "role", - "allow": 0, - "deny": 3072 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "479068822079275031", - "last_message_id": "506207223572070402", - "last_pin_timestamp": "2018-09-09T02:37:38.661000+00:00", - "type": 0, - "name": "role-management", - "position": 92, - "parent_id": "449088445617405962", - "topic": "Discuss granting of member roles such as sherpa, frequenter, veteran.", - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1051648 - }, - { - "id": "382117075558203392", - "type": "role", - "allow": 1049600, - "deny": 0 - }, - { - "id": "382118099341541379", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "479849608558936064", - "type": 4, - "name": "Clan Area", - "position": 1, - "parent_id": null, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1051713 - }, - { - "id": "538477449130803212", - "type": "role", - "allow": 1024, - "deny": 0 - } - ], - "nsfw": false - }, - { - "id": "480081996245237770", - "last_message_id": "502319210547118080", - "last_pin_timestamp": "2018-09-10T17:14:48.935000+00:00", - "type": 0, - "name": "forsaken-spoiler-discussion", - "position": 91, - "parent_id": "449088445617405962", - "topic": "", - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1051648 - }, - { - "id": "382117075558203392", - "type": "role", - "allow": 1049600, - "deny": 0 - }, - { - "id": "382118099341541379", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "482277741366870017", - "last_message_id": "532260802607054863", - "last_pin_timestamp": "2018-11-23T04:15:10.787000+00:00", - "type": 0, - "name": "clan-website", - "position": 73, - "parent_id": "449088445617405962", - "topic": null, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1051648 - }, - { - "id": "382117075558203392", - "type": "role", - "allow": 1049600, - "deny": 0 - }, - { - "id": "382118099341541379", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "484389015949148198", - "type": 2, - "name": "Dev Network", - "position": 41, - "parent_id": "485882029158826019", - "bitrate": 64000, - "user_limit": 0, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 1048576 - } - ], - "nsfw": false - }, - { - "id": "485135215459696655", - "last_message_id": "664847553245872130", - "last_pin_timestamp": "2019-11-16T22:36:58.344000+00:00", - "type": 0, - "name": "bot-commands", - "position": 60, - "parent_id": "485882029158826019", - "topic": null, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "241950106125860865", - "type": "member", - "allow": 117760, - "deny": 0 - }, - { - "id": "432533456807919639", - "type": "member", - "allow": 3072, - "deny": 0 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "495497885652090892", - "type": "member", - "allow": 1024, - "deny": 0 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "545333050251411457", - "type": "member", - "allow": 3072, - "deny": 0 - }, - { - "id": "548453282960375859", - "type": "member", - "allow": 388160, - "deny": 0 - }, - { - "id": "554080307226345482", - "type": "member", - "allow": 3072, - "deny": 0 - }, - { - "id": "578669719343857704", - "type": "member", - "allow": 3072, - "deny": 0 - }, - { - "id": "579680071028310027", - "type": "member", - "allow": 117760, - "deny": 0 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "485882029158826019", - "type": 4, - "name": "dev channels", - "position": 20, - "parent_id": null, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "382118099341541379", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "578669719343857704", - "type": "member", - "allow": 0, - "deny": 0 - } - ], - "nsfw": false - }, - { - "id": "485961711535390720", - "type": 4, - "name": "Events", - "position": 3, - "parent_id": null, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 3146752, - "deny": 0 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 1024, - "deny": 0 - } - ], - "nsfw": false - }, - { - "id": "486328170128211971", - "type": 4, - "name": "QuickPlay", - "position": 11, - "parent_id": null, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 1, - "deny": 0 - }, - { - "id": "463520715254267916", - "type": "role", - "allow": 0, - "deny": 1048576 - }, - { - "id": "530919353999163414", - "type": "role", - "allow": 0, - "deny": 1048576 - } - ], - "nsfw": false - }, - { - "id": "489162012547612682", - "last_message_id": "489162440773206044", - "type": 0, - "name": "absence-form", - "position": 96, - "parent_id": "449088445617405962", - "topic": null, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1051648 - }, - { - "id": "382117075558203392", - "type": "role", - "allow": 1049600, - "deny": 0 - }, - { - "id": "382118099341541379", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "489690763450384384", - "last_message_id": "502319294605033472", - "last_pin_timestamp": "2018-09-26T22:55:22.514000+00:00", - "type": 0, - "name": "last-wish-raid-lfg", - "position": 87, - "parent_id": "449088445617405962", - "topic": "LFG for and discuss the Last Wish raid in Forsaken", - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1051648 - }, - { - "id": "382117075558203392", - "type": "role", - "allow": 1049600, - "deny": 0 - }, - { - "id": "382118099341541379", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "492500180768260096", - "last_message_id": "664863758354612244", - "last_pin_timestamp": "2019-12-10T23:24:53.504000+00:00", - "type": 0, - "name": "moderation", - "position": 52, - "parent_id": "382114317857849344", - "topic": "Evidence collection and discussion of member moderation.", - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "382118099341541379", - "type": "role", - "allow": 3072, - "deny": 0 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 3072, - "deny": 0 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 3072, - "deny": 0 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 3072, - "deny": 0 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "493519397143445504", - "last_message_id": "664840787242254339", - "type": 0, - "name": "leave", - "position": 5, - "parent_id": null, - "topic": null, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 805829713 - }, - { - "id": "382116615614889984", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "382117075558203392", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 66560, - "deny": 0 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "497261738866114560", - "last_message_id": "497265773711065088", - "type": 0, - "name": "role-assignments", - "position": 78, - "parent_id": "449088445617405962", - "topic": "", - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1051648 - }, - { - "id": "382117075558203392", - "type": "role", - "allow": 1049600, - "deny": 0 - }, - { - "id": "382118099341541379", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "497542874821296138", - "last_message_id": "664867825156096031", - "last_pin_timestamp": "2019-12-28T17:11:04.937000+00:00", - "type": 0, - "name": "pve-talk", - "position": 26, - "parent_id": "381910837293613057", - "topic": "Place to discuss meta guns, your best loadouts and strats! Great place to ask for advice from our PvE guides.", - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 135168 - }, - { - "id": "463520715254267916", - "type": "role", - "allow": 0, - "deny": 3072 - }, - { - "id": "530919353999163414", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "498156410551402497", - "last_message_id": "664874749377380363", - "last_pin_timestamp": "2020-01-09T16:54:53.018000+00:00", - "type": 0, - "name": "weekly-information", - "position": 12, - "parent_id": "479849608558936064", - "topic": "Information on all vendors and content resetting every Tuesday at 1PM EST", - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 1024, - "deny": 145472 - }, - { - "id": "382118099341541379", - "type": "role", - "allow": 1024, - "deny": 2048 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 11264, - "deny": 0 - }, - { - "id": "463520715254267916", - "type": "role", - "allow": 0, - "deny": 3072 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 11264, - "deny": 0 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 3072, - "deny": 0 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "498336203675009034", - "last_message_id": "517067567584509953", - "type": 0, - "name": "clan-application-old", - "position": 88, - "parent_id": "449088445617405962", - "topic": "", - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1051648 - }, - { - "id": "382117075558203392", - "type": "role", - "allow": 1049600, - "deny": 0 - }, - { - "id": "382118099341541379", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "501072657865900062", - "last_message_id": "548740255088377857", - "type": 0, - "name": "analytics", - "position": 72, - "parent_id": "449088445617405962", - "topic": "", - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1051648 - }, - { - "id": "382117075558203392", - "type": "role", - "allow": 1049600, - "deny": 0 - }, - { - "id": "382118099341541379", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "501218568151498754", - "type": 2, - "name": "Sherpa Head/Lead", - "position": 31, - "parent_id": "526617610519838741", - "bitrate": 96000, - "user_limit": 0, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "469631935753355264", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "565734337819967489", - "type": "role", - "allow": 1049600, - "deny": 0 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 1048576 - } - ], - "nsfw": false - }, - { - "id": "501514955103928320", - "last_message_id": "655039849056698368", - "type": 0, - "name": "game-night-votes", - "position": 19, - "parent_id": "485961711535390720", - "topic": null, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 1024, - "deny": 2112 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 3148864, - "deny": 0 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "502647609362612244", - "last_message_id": "502931307836473364", - "last_pin_timestamp": "2018-10-19T01:22:24.420000+00:00", - "type": 0, - "name": "clan-stealing-advert", - "position": 115, - "parent_id": "449088445617405962", - "topic": "We're tryin to grow by bringing in the herd", - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "382116615614889984", - "type": "role", - "allow": 0, - "deny": 0 - }, - { - "id": "382117075558203392", - "type": "role", - "allow": 0, - "deny": 0 - }, - { - "id": "382118099341541379", - "type": "role", - "allow": 0, - "deny": 0 - }, - { - "id": "463520715254267916", - "type": "role", - "allow": 0, - "deny": 3072 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "503639019138056202", - "last_message_id": "509809017090998316", - "type": 0, - "name": "advert", - "position": 107, - "parent_id": "449088445617405962", - "topic": null, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1051648 - }, - { - "id": "382117075558203392", - "type": "role", - "allow": 1049600, - "deny": 0 - }, - { - "id": "382118099341541379", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "505807467637112842", - "last_message_id": "507989702754828319", - "type": 0, - "name": "landing-page-work", - "position": 81, - "parent_id": "449088445617405962", - "topic": null, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1051648 - }, - { - "id": "382117075558203392", - "type": "role", - "allow": 1049600, - "deny": 0 - }, - { - "id": "382118099341541379", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "505816855303421953", - "last_message_id": "563177965253296139", - "last_pin_timestamp": "2018-10-27T21:18:11.025000+00:00", - "type": 0, - "name": "pvp-guides", - "position": 112, - "parent_id": "449088445617405962", - "topic": "Make nominations for the PVP Guide role here as well as any PVP-related discussion between guides.", - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "336630009546407937", - "type": "member", - "allow": 805829713, - "deny": 0 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 0, - "deny": 0 - }, - { - "id": "502964473674465300", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "505982623441616917", - "last_message_id": "516749475239428098", - "type": 0, - "name": "pvp-council", - "position": 109, - "parent_id": "449088445617405962", - "topic": "Vote on nominations for PVP Guide and privately discuss anything related to PVP Guides", - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1051648 - }, - { - "id": "382117075558203392", - "type": "role", - "allow": 1049600, - "deny": 0 - }, - { - "id": "382118099341541379", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 1040, - "deny": 2048 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 1040, - "deny": 2048 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "505983940809719808", - "last_message_id": "658097394386206731", - "last_pin_timestamp": "2019-04-25T01:55:20.717000+00:00", - "type": 0, - "name": "sherpa-lounge", - "position": 44, - "parent_id": "526617610519838741", - "topic": "Feel free to ping @sherpa if you need another pair of hands!", - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1049600 - }, - { - "id": "382116615614889984", - "type": "role", - "allow": 1049600, - "deny": 0 - }, - { - "id": "382117075558203392", - "type": "role", - "allow": 1049600, - "deny": 0 - }, - { - "id": "382559698513166348", - "type": "role", - "allow": 1049600, - "deny": 0 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 1051648, - "deny": 0 - }, - { - "id": "469631935753355264", - "type": "role", - "allow": 1049600, - "deny": 0 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "482625494886645781", - "type": "role", - "allow": 1049600, - "deny": 0 - }, - { - "id": "502964473674465300", - "type": "role", - "allow": 1048576, - "deny": 1024 - }, - { - "id": "565734337819967489", - "type": "role", - "allow": 11264, - "deny": 0 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "505984010053222401", - "last_message_id": "654121008671752222", - "last_pin_timestamp": "2019-01-07T16:20:14.596000+00:00", - "type": 0, - "name": "sherpa-admin", - "position": 45, - "parent_id": "526617610519838741", - "topic": "", - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1049600 - }, - { - "id": "382559698513166348", - "type": "role", - "allow": 1048576, - "deny": 1024 - }, - { - "id": "469631935753355264", - "type": "role", - "allow": 1048576, - "deny": 1024 - }, - { - "id": "502964473674465300", - "type": "role", - "allow": 1048576, - "deny": 1024 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 3072, - "deny": 0 - }, - { - "id": "565734337819967489", - "type": "role", - "allow": 3072, - "deny": 0 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "506204441733627936", - "last_message_id": "506205120024018946", - "type": 0, - "name": "goal-list", - "position": 117, - "parent_id": "449088445617405962", - "topic": null, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "382116615614889984", - "type": "role", - "allow": 0, - "deny": 0 - }, - { - "id": "382117075558203392", - "type": "role", - "allow": 0, - "deny": 0 - }, - { - "id": "382118099341541379", - "type": "role", - "allow": 0, - "deny": 3072 - }, - { - "id": "463520715254267916", - "type": "role", - "allow": 0, - "deny": 3072 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "506231343919923220", - "last_message_id": "518470848793935882", - "last_pin_timestamp": "2018-12-01T02:56:46.084000+00:00", - "type": 0, - "name": "logo-challenge-channel", - "position": 90, - "parent_id": "449088445617405962", - "topic": null, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1051648 - }, - { - "id": "382117075558203392", - "type": "role", - "allow": 1049600, - "deny": 0 - }, - { - "id": "382118099341541379", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "509065623997906944", - "last_message_id": "509121968562241536", - "type": 0, - "name": "advert-text", - "position": 116, - "parent_id": "449088445617405962", - "topic": null, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "382117075558203392", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "509878104899190807", - "type": 2, - "name": "Badmins", - "position": 30, - "parent_id": "510476994476113930", - "bitrate": 64000, - "user_limit": 0, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "382116615614889984", - "type": "role", - "allow": 0, - "deny": 0 - }, - { - "id": "382117075558203392", - "type": "role", - "allow": 0, - "deny": 0 - }, - { - "id": "382118099341541379", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "463520715254267916", - "type": "role", - "allow": 0, - "deny": 3072 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 1048576 - } - ], - "nsfw": false - }, - { - "id": "510141207062511617", - "last_message_id": "510691810163687444", - "type": 0, - "name": "staff-changes", - "position": 94, - "parent_id": "449088445617405962", - "topic": null, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1051648 - }, - { - "id": "382117075558203392", - "type": "role", - "allow": 1049600, - "deny": 0 - }, - { - "id": "382118099341541379", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "510194688335282207", - "last_message_id": "645150989686669323", - "type": 0, - "name": "landing", - "position": 0, - "parent_id": null, - "topic": null, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 1025, - "deny": 264256 - }, - { - "id": "336630009546407937", - "type": "member", - "allow": 3072, - "deny": 0 - }, - { - "id": "381911719901134850", - "type": "role", - "allow": 1025, - "deny": 2048 - }, - { - "id": "382116615614889984", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "382117075558203392", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "382118099341541379", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 1025, - "deny": 2048 - }, - { - "id": "482625494886645781", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "510476994476113930", - "type": 4, - "name": "Staff Voice", - "position": 19, - "parent_id": null, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1049600 - }, - { - "id": "382118099341541379", - "type": "role", - "allow": 1049600, - "deny": 0 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 1049600, - "deny": 0 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 17826816, - "deny": 0 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 1049600, - "deny": 0 - } - ], - "nsfw": false - }, - { - "id": "511564679945125908", - "last_message_id": "512008649628057610", - "type": 0, - "name": "faq-talking-points", - "position": 83, - "parent_id": "449088445617405962", - "topic": null, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1051648 - }, - { - "id": "382117075558203392", - "type": "role", - "allow": 1049600, - "deny": 0 - }, - { - "id": "382118099341541379", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "513094096445767681", - "last_message_id": "663811969744109591", - "last_pin_timestamp": "2019-12-27T22:28:23.586000+00:00", - "type": 0, - "name": "leader-chat", - "position": 56, - "parent_id": "382114317857849344", - "topic": "", - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "382116615614889984", - "type": "role", - "allow": 0, - "deny": 0 - }, - { - "id": "382117075558203392", - "type": "role", - "allow": 0, - "deny": 0 - }, - { - "id": "382118099341541379", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 3136, - "deny": 0 - }, - { - "id": "463520715254267916", - "type": "role", - "allow": 0, - "deny": 3072 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 3136, - "deny": 0 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "513506633259679746", - "last_message_id": "516054238514774042", - "last_pin_timestamp": "2018-11-24T22:00:33.955000+00:00", - "type": 0, - "name": "scrims", - "position": 99, - "parent_id": "449088445617405962", - "topic": null, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1051648 - }, - { - "id": "382117075558203392", - "type": "role", - "allow": 1049600, - "deny": 0 - }, - { - "id": "382118099341541379", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "517002345096413185", - "last_message_id": "524718054521962508", - "last_pin_timestamp": "2018-12-14T05:34:15.359000+00:00", - "type": 0, - "name": "black-armory-discussion", - "position": 85, - "parent_id": "449088445617405962", - "topic": "A chat for discussion about black armory! Please keep all expansion discussion here", - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1051648 - }, - { - "id": "382117075558203392", - "type": "role", - "allow": 1049600, - "deny": 0 - }, - { - "id": "382118099341541379", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "517009920500039719", - "last_message_id": "517452242526273578", - "last_pin_timestamp": "2018-11-27T16:16:51.108000+00:00", - "type": 0, - "name": "ow-prep", - "position": 93, - "parent_id": "449088445617405962", - "topic": "", - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1051648 - }, - { - "id": "382117075558203392", - "type": "role", - "allow": 1049600, - "deny": 0 - }, - { - "id": "382118099341541379", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "517781981489004544", - "last_message_id": "551859163286863902", - "type": 0, - "name": "clan-info", - "position": 80, - "parent_id": "449088445617405962", - "topic": "", - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1051648 - }, - { - "id": "382117075558203392", - "type": "role", - "allow": 1049600, - "deny": 0 - }, - { - "id": "382118099341541379", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "518264327158104091", - "last_message_id": "536599840742834186", - "type": 0, - "name": "lore-presentation", - "position": 101, - "parent_id": "449088445617405962", - "topic": "Curated lore-based content written by our lore Chroniclers.", - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1051648 - }, - { - "id": "382117075558203392", - "type": "role", - "allow": 1049600, - "deny": 0 - }, - { - "id": "382118099341541379", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "518267216408346624", - "last_message_id": "664860385119174657", - "last_pin_timestamp": "2019-11-29T00:31:52.550000+00:00", - "type": 0, - "name": "talk-to-staff", - "position": 9, - "parent_id": "381911060493500416", - "topic": "Ask questions or request assistance from server and clan staff. Please keep the channel professional and check pinned messages.", - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 131137 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 64, - "deny": 0 - }, - { - "id": "463520715254267916", - "type": "role", - "allow": 0, - "deny": 3072 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 64, - "deny": 0 - }, - { - "id": "530919353999163414", - "type": "role", - "allow": 2048, - "deny": 0 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 64, - "deny": 0 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "518267455659966465", - "last_message_id": "518272480293683201", - "type": 0, - "name": "what-we-define-as-active", - "position": 95, - "parent_id": "449088445617405962", - "topic": null, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1051648 - }, - { - "id": "382117075558203392", - "type": "role", - "allow": 1049600, - "deny": 0 - }, - { - "id": "382118099341541379", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "518871349125447701", - "type": 4, - "name": "Applications", - "position": 2, - "parent_id": null, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "530919353999163414", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false - }, - { - "id": "519019593092825088", - "last_message_id": "657044122556956673", - "type": 0, - "name": "staff-links-resources", - "position": 50, - "parent_id": "382114317857849344", - "topic": "Cool beans. The coolest beans.", - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1089 - }, - { - "id": "382118099341541379", - "type": "role", - "allow": 1024, - "deny": 2048 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 3072, - "deny": 0 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 1024, - "deny": 2048 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 3072, - "deny": 0 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "519243467298177035", - "last_message_id": "539168206288322579", - "type": 0, - "name": "faq-old", - "position": 105, - "parent_id": "449088445617405962", - "topic": "", - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1051648 - }, - { - "id": "382117075558203392", - "type": "role", - "allow": 1049600, - "deny": 0 - }, - { - "id": "382118099341541379", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "519246529295286275", - "last_message_id": "554403043093905458", - "type": 0, - "name": "admin-coco-accounts", - "position": 59, - "parent_id": "382114317857849344", - "topic": null, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1025 - }, - { - "id": "382116615614889984", - "type": "role", - "allow": 0, - "deny": 0 - }, - { - "id": "382117075558203392", - "type": "role", - "allow": 0, - "deny": 0 - }, - { - "id": "382118099341541379", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "463520715254267916", - "type": "role", - "allow": 0, - "deny": 3072 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "519592861776478228", - "last_message_id": "523675676419555328", - "type": 0, - "name": "forge-requests", - "position": 86, - "parent_id": "449088445617405962", - "topic": null, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1051648 - }, - { - "id": "382117075558203392", - "type": "role", - "allow": 1049600, - "deny": 0 - }, - { - "id": "382118099341541379", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "520339423829950484", - "last_message_id": "522213071847030785", - "type": 0, - "name": "peer-edits", - "position": 79, - "parent_id": "449088445617405962", - "topic": null, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1051648 - }, - { - "id": "382117075558203392", - "type": "role", - "allow": 1049600, - "deny": 0 - }, - { - "id": "382118099341541379", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "520399992318590996", - "last_message_id": "523675436853624832", - "last_pin_timestamp": "2018-12-12T00:18:35.037000+00:00", - "type": 0, - "name": "scourge-of-the-past-lfg", - "position": 100, - "parent_id": "449088445617405962", - "topic": null, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1051648 - }, - { - "id": "382117075558203392", - "type": "role", - "allow": 1049600, - "deny": 0 - }, - { - "id": "382118099341541379", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "522227695577333770", - "last_message_id": "534116355126067201", - "type": 0, - "name": "chronicler-chat", - "position": 102, - "parent_id": "449088445617405962", - "topic": null, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "336630009546407937", - "type": "member", - "allow": 0, - "deny": 0 - }, - { - "id": "382116615614889984", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "382117075558203392", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "530919353999163414", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "526617610519838741", - "type": 4, - "name": "Sherpa Channels", - "position": 16, - "parent_id": null, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "469631935753355264", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 1024, - "deny": 0 - } - ], - "nsfw": false - }, - { - "id": "530986285712670729", - "last_message_id": "570314690459336704", - "type": 0, - "name": "pve-guide-applications-old", - "position": 70, - "parent_id": "449088445617405962", - "topic": "", - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1051648 - }, - { - "id": "382117075558203392", - "type": "role", - "allow": 1049600, - "deny": 0 - }, - { - "id": "382118099341541379", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "531000532589084692", - "last_message_id": "664299552744013824", - "last_pin_timestamp": "2019-12-05T16:43:16.751000+00:00", - "type": 0, - "name": "moderator-chat", - "position": 55, - "parent_id": "382114317857849344", - "topic": "", - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1025 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "531647598700920832", - "type": 2, - "name": "Ninja C", - "position": 37, - "parent_id": "510476994476113930", - "bitrate": 64000, - "user_limit": 0, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1049600 - }, - { - "id": "382118099341541379", - "type": "role", - "allow": 1049600, - "deny": 0 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 1049600, - "deny": 0 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 17826816, - "deny": 0 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 1049600, - "deny": 0 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 1048576 - } - ], - "nsfw": false - }, - { - "id": "531663092862418965", - "last_message_id": "538274610060460043", - "type": 0, - "name": "analytics-old", - "position": 98, - "parent_id": "449088445617405962", - "topic": "", - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1051648 - }, - { - "id": "382117075558203392", - "type": "role", - "allow": 1049600, - "deny": 0 - }, - { - "id": "382118099341541379", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "533482437863276555", - "type": 2, - "name": "Moderator Voice", - "position": 38, - "parent_id": "510476994476113930", - "bitrate": 64000, - "user_limit": 0, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1049600 - }, - { - "id": "382118099341541379", - "type": "role", - "allow": 0, - "deny": 1049600 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 1049600, - "deny": 0 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 1049600, - "deny": 0 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 1049600, - "deny": 0 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 1048576 - } - ], - "nsfw": false - }, - { - "id": "533493079970152463", - "last_message_id": "551876572118777882", - "last_pin_timestamp": "2019-02-25T17:04:02.281000+00:00", - "type": 0, - "name": "guide-requests", - "position": 76, - "parent_id": "449088445617405962", - "topic": "Request help from Raid Sherpas, PvE Guides, and PvP Guides here.", - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1051648 - }, - { - "id": "382117075558203392", - "type": "role", - "allow": 1049600, - "deny": 0 - }, - { - "id": "382118099341541379", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "536713209160269824", - "last_message_id": "664890932616888340", - "last_pin_timestamp": "2020-01-03T04:39:19.212000+00:00", - "type": 0, - "name": "staff-general", - "position": 53, - "parent_id": "382114317857849344", - "topic": "Basically just memes, use #trial-moderator-chat for serious stuff.", - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 11264 - }, - { - "id": "382118099341541379", - "type": "role", - "allow": 519232, - "deny": 0 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 519232, - "deny": 0 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 11264, - "deny": 0 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 519232, - "deny": 0 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "536998280358264847", - "last_message_id": "553570653999988737", - "type": 0, - "name": "hub-member-code", - "position": 71, - "parent_id": "449088445617405962", - "topic": "", - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1051648 - }, - { - "id": "382117075558203392", - "type": "role", - "allow": 1049600, - "deny": 0 - }, - { - "id": "382118099341541379", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "539634611886489630", - "last_message_id": "552946844733538304", - "type": 0, - "name": "faq-4-21-19", - "position": 68, - "parent_id": "449088445617405962", - "topic": "", - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1051648 - }, - { - "id": "382117075558203392", - "type": "role", - "allow": 1049600, - "deny": 0 - }, - { - "id": "382118099341541379", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "539637181669572609", - "last_message_id": "664765445890506752", - "last_pin_timestamp": "2019-10-24T08:18:24.221000+00:00", - "type": 0, - "name": "raid-scheduling-bot", - "position": 30, - "parent_id": "381910837293613057", - "topic": "Use this channel for scheduling a FUTURE raid ONLY. If you wish to LFG for a raid NOW, please use #lfg-requests.", - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 3072, - "deny": 0 - }, - { - "id": "463520715254267916", - "type": "role", - "allow": 0, - "deny": 3072 - }, - { - "id": "530919353999163414", - "type": "role", - "allow": 0, - "deny": 2099200 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "539639035795210246", - "last_message_id": "662512213873000479", - "last_pin_timestamp": "2019-08-01T15:10:27.755000+00:00", - "type": 0, - "name": "legend-voting", - "position": 21, - "parent_id": "485961711535390720", - "topic": "", - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 3136 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 3148864, - "deny": 0 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "538477449130803212", - "type": "role", - "allow": 1024, - "deny": 2112 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 1088, - "deny": 0 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "544594107197161482", - "last_message_id": "664889744831807538", - "last_pin_timestamp": "2020-01-02T17:07:34.185000+00:00", - "type": 0, - "name": "pvp-talk", - "position": 27, - "parent_id": "381910837293613057", - "topic": "Place to discuss meta guns, best loadouts, strats etc, and to ask for advice from our PvP guides. NOT a place to ask for carries. Please do not use @here in this chat!", - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 131072 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 131072, - "deny": 0 - }, - { - "id": "463520715254267916", - "type": "role", - "allow": 0, - "deny": 3072 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 131072, - "deny": 0 - }, - { - "id": "530919353999163414", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 131072, - "deny": 0 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "544600171913412638", - "last_message_id": "551543634642862110", - "last_pin_timestamp": "2019-02-11T19:44:40.158000+00:00", - "type": 0, - "name": "fanart-channel", - "position": 77, - "parent_id": "449088445617405962", - "topic": null, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1051648 - }, - { - "id": "382117075558203392", - "type": "role", - "allow": 1049600, - "deny": 0 - }, - { - "id": "382118099341541379", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "544600393011822602", - "last_message_id": "627666417550098464", - "type": 0, - "name": "giveaways", - "position": 20, - "parent_id": "485961711535390720", - "topic": null, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 64, - "deny": 1051649 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 3072, - "deny": 0 - }, - { - "id": "538477449130803212", - "type": "role", - "allow": 1024, - "deny": 2049 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 3072 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "544607799393517569", - "last_message_id": "664634987324375042", - "type": 0, - "name": "support-your-community", - "position": 3, - "parent_id": null, - "topic": "For YouTube or Twitch streams", - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 64, - "deny": 2048 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "550739041293697024", - "last_message_id": "557282142938202114", - "last_pin_timestamp": "2019-03-15T18:29:04.585000+00:00", - "type": 0, - "name": "jokers-wild-discussion", - "position": 74, - "parent_id": "449088445617405962", - "topic": "A chat for discussion about Jokers Wild! Please keep all expansion discussion here", - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1051648 - }, - { - "id": "382117075558203392", - "type": "role", - "allow": 1049600, - "deny": 0 - }, - { - "id": "382118099341541379", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "551504554437509120", - "last_message_id": "656325286144507904", - "type": 0, - "name": "feedback-bot-responses", - "position": 61, - "parent_id": "485882029158826019", - "topic": "Feedback from the community.", - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 3072 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 3072, - "deny": 0 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 1024, - "deny": 2048 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 3072, - "deny": 0 - }, - { - "id": "548453282960375859", - "type": "member", - "allow": 388160, - "deny": 0 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "551599760675438595", - "last_message_id": "640303771536195584", - "type": 0, - "name": "score-submission", - "position": 40, - "parent_id": "552220299521818674", - "topic": null, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "382118099341541379", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "530919353999163414", - "type": "role", - "allow": 0, - "deny": 1050624 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "551597061955387403", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "551600678816972815", - "last_message_id": "644349377271431179", - "last_pin_timestamp": "2019-10-29T13:59:32.759000+00:00", - "type": 0, - "name": "tournament-lfg", - "position": 39, - "parent_id": "552220299521818674", - "topic": "", - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 132096 - }, - { - "id": "382118099341541379", - "type": "role", - "allow": 1024, - "deny": 131072 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "502964473674465300", - "type": "role", - "allow": 3072, - "deny": 0 - }, - { - "id": "530919353999163414", - "type": "role", - "allow": 0, - "deny": 1051648 - }, - { - "id": "538477449130803212", - "type": "role", - "allow": 2048, - "deny": 1024 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "551597061955387403", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "564516531082362880", - "type": "role", - "allow": 131072, - "deny": 0 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 3072 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "551600808387543061", - "last_message_id": "640249183517016065", - "last_pin_timestamp": "2019-06-15T21:09:22.317000+00:00", - "type": 0, - "name": "teams", - "position": 37, - "parent_id": "552220299521818674", - "topic": "Determine the teams here. Post RNG team pics.", - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 2048, - "deny": 1024 - }, - { - "id": "382118099341541379", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "530919353999163414", - "type": "role", - "allow": 0, - "deny": 1050624 - }, - { - "id": "538477449130803212", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "551597061955387403", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "551825969346773022", - "type": 2, - "name": "Game Night 1", - "position": 1, - "parent_id": "485961711535390720", - "bitrate": 202000, - "user_limit": 12, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 3146752, - "deny": 0 - }, - { - "id": "463520715254267916", - "type": "role", - "allow": 3146752, - "deny": 0 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 1049600, - "deny": 0 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 1048576 - } - ], - "nsfw": false - }, - { - "id": "551866300754952215", - "type": 4, - "name": "Strikes", - "position": 7, - "parent_id": null, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "463520715254267916", - "type": "role", - "allow": 0, - "deny": 1050624 - }, - { - "id": "530919353999163414", - "type": "role", - "allow": 0, - "deny": 1048576 - } - ], - "nsfw": false - }, - { - "id": "551868604350464011", - "type": 4, - "name": "Gambit", - "position": 10, - "parent_id": null, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "463520715254267916", - "type": "role", - "allow": 0, - "deny": 1048576 - }, - { - "id": "530919353999163414", - "type": "role", - "allow": 0, - "deny": 1048576 - } - ], - "nsfw": false - }, - { - "id": "551883047021183020", - "last_message_id": "662515150582186004", - "last_pin_timestamp": "2019-12-08T18:13:46.292000+00:00", - "type": 0, - "name": "game-night-text", - "position": 23, - "parent_id": "485961711535390720", - "topic": null, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 3148800, - "deny": 0 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 3072, - "deny": 0 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 3072, - "deny": 0 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "552220299521818674", - "type": 4, - "name": "Scrims", - "position": 15, - "parent_id": null, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "382118099341541379", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "530919353999163414", - "type": "role", - "allow": 0, - "deny": 1050624 - }, - { - "id": "538477449130803212", - "type": "role", - "allow": 3072, - "deny": 0 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "551597061955387403", - "type": "role", - "allow": 0, - "deny": 1024 - } - ], - "nsfw": false - }, - { - "id": "552509209133056000", - "last_message_id": "557260938722672666", - "type": 0, - "name": "gambit-prime-requests", - "position": 82, - "parent_id": "449088445617405962", - "topic": "A place for people to find gambit prime and reckoning groups. Not for general discussion.", - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1051648 - }, - { - "id": "382117075558203392", - "type": "role", - "allow": 1049600, - "deny": 0 - }, - { - "id": "382118099341541379", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "552662715768897536", - "last_message_id": "556668222296948741", - "last_pin_timestamp": "2019-03-13T22:34:51.930000+00:00", - "type": 0, - "name": "screenshot-chat", - "position": 113, - "parent_id": "449088445617405962", - "topic": "Send here screenshots of your destiny gameplay, or just funny stuff you saw happen in destiny with you and your friend. Screenshots for coco website only please.", - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 3072 - }, - { - "id": "463520715254267916", - "type": "role", - "allow": 0, - "deny": 3072 - }, - { - "id": "530919353999163414", - "type": "role", - "allow": 0, - "deny": 2099200 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "555713073290149888", - "type": 2, - "name": "Orbit", - "position": 3, - "parent_id": "579868728410505276", - "bitrate": 64000, - "user_limit": 0, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 0 - }, - { - "id": "463520715254267916", - "type": "role", - "allow": 0, - "deny": 1048576 - }, - { - "id": "530919353999163414", - "type": "role", - "allow": 0, - "deny": 1048576 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 1048576 - } - ], - "nsfw": false - }, - { - "id": "556681307447492609", - "type": 2, - "name": "Leader Voice", - "position": 39, - "parent_id": "510476994476113930", - "bitrate": 64000, - "user_limit": 0, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1049600 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 1049600, - "deny": 0 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 1049600, - "deny": 0 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 1048576 - } - ], - "nsfw": false - }, - { - "id": "559205358103691304", - "last_message_id": "576463460552933406", - "last_pin_timestamp": "2019-03-28T23:09:00.391000+00:00", - "type": 0, - "name": "dnd-text", - "position": 103, - "parent_id": "449088445617405962", - "topic": null, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1051648 - }, - { - "id": "382117075558203392", - "type": "role", - "allow": 1049600, - "deny": 0 - }, - { - "id": "382118099341541379", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "560973436558508052", - "last_message_id": "562113337870123008", - "last_pin_timestamp": "2019-03-31T01:51:07.700000+00:00", - "type": 0, - "name": "dnd-resources", - "position": 108, - "parent_id": "449088445617405962", - "topic": null, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "382118099341541379", - "type": "role", - "allow": 0, - "deny": 3072 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 0, - "deny": 3072 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "559215320435523617", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "561750420242825216", - "last_message_id": "561906338654060564", - "type": 0, - "name": "dnd-suggestions", - "position": 104, - "parent_id": "449088445617405962", - "topic": null, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1051648 - }, - { - "id": "382117075558203392", - "type": "role", - "allow": 1049600, - "deny": 0 - }, - { - "id": "382118099341541379", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "562017811556204554", - "last_message_id": "652141817181110282", - "type": 0, - "name": "crucible-coach-application", - "position": 67, - "parent_id": "619529638699335685", - "topic": "", - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 134208 - }, - { - "id": "381911719901134850", - "type": "role", - "allow": 65536, - "deny": 3072 - }, - { - "id": "382118099341541379", - "type": "role", - "allow": 3072, - "deny": 0 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 3072, - "deny": 0 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 133120, - "deny": 1024 - }, - { - "id": "530919353999163414", - "type": "role", - "allow": 0, - "deny": 2112 - }, - { - "id": "538477449130803212", - "type": "role", - "allow": 0, - "deny": 3072 - }, - { - "id": "562036125590618117", - "type": "role", - "allow": 0, - "deny": 0 - }, - { - "id": "564516531082362880", - "type": "role", - "allow": 126016, - "deny": 0 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "562041698650161153", - "type": 4, - "name": "Guide Channels", - "position": 17, - "parent_id": null, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1049600 - }, - { - "id": "382118099341541379", - "type": "role", - "allow": 1049600, - "deny": 0 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 1049600, - "deny": 0 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 1049600, - "deny": 0 - }, - { - "id": "502964473674465300", - "type": "role", - "allow": 1049600, - "deny": 0 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 1049600, - "deny": 0 - }, - { - "id": "562036125590618117", - "type": "role", - "allow": 1049600, - "deny": 0 - }, - { - "id": "562038106166001714", - "type": "role", - "allow": 1049600, - "deny": 0 - } - ], - "nsfw": false - }, - { - "id": "562041750559129643", - "last_message_id": "664810765433307136", - "last_pin_timestamp": "2019-11-14T00:37:44.532000+00:00", - "type": 0, - "name": "crucible-coach-chat", - "position": 48, - "parent_id": "562041698650161153", - "topic": "", - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1049600 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 1048576, - "deny": 0 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 0, - "deny": 3072 - }, - { - "id": "502964473674465300", - "type": "role", - "allow": 1051648, - "deny": 0 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 1049600, - "deny": 0 - }, - { - "id": "562036125590618117", - "type": "role", - "allow": 1048576, - "deny": 3072 - }, - { - "id": "562038106166001714", - "type": "role", - "allow": 1048576, - "deny": 3088 - }, - { - "id": "564516531082362880", - "type": "role", - "allow": 268438544, - "deny": 0 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "562042488735399966", - "type": 2, - "name": "Crucible Guide Voice", - "position": 33, - "parent_id": "562041698650161153", - "bitrate": 64000, - "user_limit": 0, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 2097152, - "deny": 1049600 - }, - { - "id": "382118099341541379", - "type": "role", - "allow": 0, - "deny": 1049600 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 1048576, - "deny": 1024 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 0, - "deny": 1049600 - }, - { - "id": "502964473674465300", - "type": "role", - "allow": 0, - "deny": 1049600 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 1049600, - "deny": 0 - }, - { - "id": "562036125590618117", - "type": "role", - "allow": 1049600, - "deny": 0 - }, - { - "id": "562038106166001714", - "type": "role", - "allow": 573572096, - "deny": 297795601 - }, - { - "id": "564516531082362880", - "type": "role", - "allow": 334496785, - "deny": 0 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 1048576 - } - ], - "nsfw": false - }, - { - "id": "562100175460630538", - "last_message_id": "664886245410996283", - "last_pin_timestamp": "2020-01-03T16:48:08.703000+00:00", - "type": 0, - "name": "crucible-guide-chat", - "position": 47, - "parent_id": "562041698650161153", - "topic": "", - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1049600 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 1049600, - "deny": 8192 - }, - { - "id": "502964473674465300", - "type": "role", - "allow": 1048576, - "deny": 1024 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 1049600, - "deny": 0 - }, - { - "id": "562036125590618117", - "type": "role", - "allow": 1048576, - "deny": 1024 - }, - { - "id": "562038106166001714", - "type": "role", - "allow": 1051648, - "deny": 8192 - }, - { - "id": "564516531082362880", - "type": "role", - "allow": 142352, - "deny": 0 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "562109664133644299", - "type": 2, - "name": "Crucible Coach Voice", - "position": 34, - "parent_id": "562041698650161153", - "bitrate": 64000, - "user_limit": 0, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1049600 - }, - { - "id": "381911719901134850", - "type": "role", - "allow": 0, - "deny": 3146752 - }, - { - "id": "382118099341541379", - "type": "role", - "allow": 0, - "deny": 1049600 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 1049600, - "deny": 0 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 0, - "deny": 1049600 - }, - { - "id": "502964473674465300", - "type": "role", - "allow": 3146752, - "deny": 0 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 1049600, - "deny": 0 - }, - { - "id": "562036125590618117", - "type": "role", - "allow": 0, - "deny": 1049600 - }, - { - "id": "562038106166001714", - "type": "role", - "allow": 0, - "deny": 3146768 - }, - { - "id": "564516531082362880", - "type": "role", - "allow": 271582224, - "deny": 0 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 1048576 - } - ], - "nsfw": false - }, - { - "id": "562443185989615617", - "last_message_id": "574333285975588884", - "last_pin_timestamp": "2019-04-11T17:27:15.625000+00:00", - "type": 0, - "name": "lore-discussion", - "position": 111, - "parent_id": "449088445617405962", - "topic": "A channel dedicated to those who take it upon themselves to learn the lore of Destiny.", - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "463520715254267916", - "type": "role", - "allow": 0, - "deny": 3072 - }, - { - "id": "530919353999163414", - "type": "role", - "allow": 0, - "deny": 2099200 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 10 - }, - { - "id": "563290422907109376", - "type": 2, - "name": "Sherpa Voice", - "position": 30, - "parent_id": "526617610519838741", - "bitrate": 64000, - "user_limit": 0, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "469631935753355264", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 1048576 - } - ], - "nsfw": false - }, - { - "id": "563820866358476839", - "last_message_id": "592502089482240039", - "last_pin_timestamp": "2019-04-05T20:28:54.348000+00:00", - "type": 0, - "name": "stormbot-dev", - "position": 66, - "parent_id": "485882029158826019", - "topic": "Only for members working on StormBot", - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 3072 - }, - { - "id": "442060343162699776", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "563821431079829535", - "type": "role", - "allow": 3072, - "deny": 0 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "564512049229463611", - "type": 4, - "name": "Streamer Channels", - "position": 26, - "parent_id": null, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1049600 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 1049600, - "deny": 0 - }, - { - "id": "564512695156736010", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "564512855790059545", - "type": "role", - "allow": 1024, - "deny": 0 - } - ], - "nsfw": false - }, - { - "id": "564512421990105109", - "type": 2, - "name": "Streamer Voice", - "position": 48, - "parent_id": "564512049229463611", - "bitrate": 64000, - "user_limit": 0, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1049600 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 1049600, - "deny": 0 - }, - { - "id": "564512695156736010", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "564512855790059545", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 1048576 - } - ], - "nsfw": false - }, - { - "id": "564512544614514699", - "last_message_id": "564882343513554964", - "last_pin_timestamp": "2019-04-08T17:31:43.789000+00:00", - "type": 0, - "name": "streamer-chat", - "position": 131, - "parent_id": "564512049229463611", - "topic": "General discussion for the clan Twitch streamers.", - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1049600 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 1049600, - "deny": 0 - }, - { - "id": "564512695156736010", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "564512855790059545", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "564512595789217811", - "last_message_id": "601202301280976901", - "type": 0, - "name": "streamer-resources", - "position": 132, - "parent_id": "564512049229463611", - "topic": "A collection of resources for the clan Twitch streamers to use when streaming.", - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "565942508216123393", - "last_message_id": "664879870383489035", - "last_pin_timestamp": "2019-12-13T22:09:31.780000+00:00", - "type": 0, - "name": "talk-with-ai", - "position": 29, - "parent_id": "381910837293613057", - "topic": "Deliberately trying to rate limit, or bug the bot will result in your permissions to this channel being revoked.", - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 0 - }, - { - "id": "279738762559225856", - "type": "member", - "allow": 0, - "deny": 3072 - }, - { - "id": "382117075558203392", - "type": "role", - "allow": 3072, - "deny": 0 - }, - { - "id": "382118099341541379", - "type": "role", - "allow": 3072, - "deny": 0 - }, - { - "id": "382128710779928576", - "type": "role", - "allow": 3072, - "deny": 0 - }, - { - "id": "442060343162699776", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 3072, - "deny": 0 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 3072, - "deny": 0 - }, - { - "id": "530919353999163414", - "type": "role", - "allow": 0, - "deny": 2112 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 3072, - "deny": 0 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 15 - }, - { - "id": "566694214000312338", - "type": 2, - "name": "Tournament 1", - "position": 31, - "parent_id": "552220299521818674", - "bitrate": 64000, - "user_limit": 3, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "382118099341541379", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "530919353999163414", - "type": "role", - "allow": 0, - "deny": 1050624 - }, - { - "id": "538477449130803212", - "type": "role", - "allow": 2048, - "deny": 1024 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "551597061955387403", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 1048576 - } - ], - "nsfw": false - }, - { - "id": "566849660531113984", - "type": 4, - "name": "Gambit Tournament", - "position": 25, - "parent_id": null, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "382118099341541379", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 1024, - "deny": 0 - } - ], - "nsfw": false - }, - { - "id": "566850260756987904", - "last_message_id": "572452154489438210", - "type": 0, - "name": "tournament-talk", - "position": 127, - "parent_id": "566849660531113984", - "topic": "Discuss about the gambit tournament. Ask questions, talk about gambit loadouts, tips. Everything!", - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "382118099341541379", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "566850292843282443", - "last_message_id": "572193214484971520", - "type": 0, - "name": "gambit-tournament-find-a-team-only", - "position": 129, - "parent_id": "566849660531113984", - "topic": "Find a team for the gambit tournament.\nPlease don't use @everyone in here.", - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "382118099341541379", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "566850480190521344", - "type": 2, - "name": "Waiting Room", - "position": 46, - "parent_id": "566849660531113984", - "bitrate": 64000, - "user_limit": 0, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "382118099341541379", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 1048576 - } - ], - "nsfw": false - }, - { - "id": "566851142823444496", - "last_message_id": "569129323009933312", - "type": 0, - "name": "tournament-rules", - "position": 130, - "parent_id": "566849660531113984", - "topic": "Tournament rules.", - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "382118099341541379", - "type": "role", - "allow": 0, - "deny": 3072 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 3072, - "deny": 0 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 0, - "deny": 3072 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 3072, - "deny": 0 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "567012072538505216", - "last_message_id": "567059408069197825", - "type": 0, - "name": "score-submission", - "position": 128, - "parent_id": "566849660531113984", - "topic": "Gambit team captains will send proof of winning / losing a match in here.", - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "382118099341541379", - "type": "role", - "allow": 0, - "deny": 3072 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 0, - "deny": 3072 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "567496802434154537", - "type": 2, - "name": "PvE Guide Voice", - "position": 32, - "parent_id": "562041698650161153", - "bitrate": 64000, - "user_limit": 0, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1049600 - }, - { - "id": "314778031631892492", - "type": "member", - "allow": 1049600, - "deny": 0 - }, - { - "id": "361990149648678913", - "type": "member", - "allow": 1024, - "deny": 0 - }, - { - "id": "382118099341541379", - "type": "role", - "allow": 1048576, - "deny": 1024 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 1048576, - "deny": 1024 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 1048576, - "deny": 1024 - }, - { - "id": "531004642612477964", - "type": "role", - "allow": 3146752, - "deny": 0 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 1048576, - "deny": 1024 - }, - { - "id": "564601276500017153", - "type": "role", - "allow": 3146752, - "deny": 0 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 1048576 - } - ], - "nsfw": false - }, - { - "id": "567501194508107808", - "last_message_id": "662042299118911518", - "last_pin_timestamp": "2019-11-26T14:22:42.218000+00:00", - "type": 0, - "name": "pve-guide-chat", - "position": 46, - "parent_id": "562041698650161153", - "topic": null, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "382118099341541379", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 3072, - "deny": 0 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 3072, - "deny": 0 - }, - { - "id": "531004642612477964", - "type": "role", - "allow": 3072, - "deny": 0 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "564601276500017153", - "type": "role", - "allow": 11264, - "deny": 0 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "567802274035007518", - "type": 2, - "name": "Gambit Team 1", - "position": 47, - "parent_id": "566849660531113984", - "bitrate": 64000, - "user_limit": 0, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "382118099341541379", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 1048576 - } - ], - "nsfw": false - }, - { - "id": "569563584221282318", - "last_message_id": "585609965897777163", - "type": 0, - "name": "faq", - "position": 8, - "parent_id": "381911060493500416", - "topic": null, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 1024, - "deny": 14336 - }, - { - "id": "382117075558203392", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "382118099341541379", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 11264, - "deny": 0 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 11264, - "deny": 0 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "569710520974966794", - "last_message_id": "649480824479285250", - "type": 0, - "name": "sherpa-application", - "position": 15, - "parent_id": "518871349125447701", - "topic": "Pre-Requisites: Have 10+ clears on 3 raids, 1 of those being the most recent raid", - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 3072, - "deny": 131136 - }, - { - "id": "209401759787909120", - "type": "member", - "allow": 0, - "deny": 2048 - }, - { - "id": "530919353999163414", - "type": "role", - "allow": 131072, - "deny": 2048 - }, - { - "id": "565734337819967489", - "type": "role", - "allow": 8256, - "deny": 0 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "570782335755223067", - "last_message_id": "575677156055580694", - "type": 0, - "name": "sherpa-resources", - "position": 43, - "parent_id": "526617610519838741", - "topic": null, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 1024, - "deny": 2048 - }, - { - "id": "469631935753355264", - "type": "role", - "allow": 1024, - "deny": 2048 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 1024, - "deny": 2048 - }, - { - "id": "482625494886645781", - "type": "role", - "allow": 3072, - "deny": 0 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 1024, - "deny": 2048 - }, - { - "id": "565734337819967489", - "type": "role", - "allow": 3072, - "deny": 0 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "570786215087833108", - "last_message_id": "657663197406429215", - "type": 0, - "name": "sherpa-announcements", - "position": 41, - "parent_id": "526617610519838741", - "topic": null, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 1024, - "deny": 2048 - }, - { - "id": "469631935753355264", - "type": "role", - "allow": 1024, - "deny": 2048 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 1024, - "deny": 2048 - }, - { - "id": "482625494886645781", - "type": "role", - "allow": 3072, - "deny": 0 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 1024, - "deny": 2048 - }, - { - "id": "565734337819967489", - "type": "role", - "allow": 3072, - "deny": 0 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "573199920455090176", - "last_message_id": "662031124800864256", - "last_pin_timestamp": "2019-11-20T02:52:21.664000+00:00", - "type": 0, - "name": "bot-admin-commands", - "position": 65, - "parent_id": "485882029158826019", - "topic": null, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "382118099341541379", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "567781687874945028", - "type": "role", - "allow": 52224, - "deny": 0 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "573880802685747201", - "type": 2, - "name": "Protocol / Well Chat", - "position": 4, - "parent_id": "579868728410505276", - "bitrate": 64000, - "user_limit": 9, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 0 - }, - { - "id": "463520715254267916", - "type": "role", - "allow": 0, - "deny": 1048576 - }, - { - "id": "530919353999163414", - "type": "role", - "allow": 0, - "deny": 1048576 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 1048576 - } - ], - "nsfw": false - }, - { - "id": "573885121485340682", - "last_message_id": "664384742434799618", - "type": 0, - "name": "pve-guide-application", - "position": 14, - "parent_id": "518871349125447701", - "topic": "", - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 3072, - "deny": 131072 - }, - { - "id": "530919353999163414", - "type": "role", - "allow": 0, - "deny": 2112 - }, - { - "id": "564601276500017153", - "type": "role", - "allow": 8192, - "deny": 0 - }, - { - "id": "576790479392342016", - "type": "role", - "allow": 0, - "deny": 0 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "576134290509660170", - "last_message_id": "664576172142624769", - "last_pin_timestamp": "2019-05-09T19:54:44.928000+00:00", - "type": 0, - "name": "activity-check-notifications", - "position": 2, - "parent_id": null, - "topic": null, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "156533924716216320", - "type": "member", - "allow": 134144, - "deny": 0 - }, - { - "id": "162706186272112640", - "type": "role", - "allow": 66560, - "deny": 805763152 - }, - { - "id": "376219751476887562", - "type": "member", - "allow": 2048, - "deny": 0 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 3072, - "deny": 0 - }, - { - "id": "538477449130803212", - "type": "role", - "allow": 1024, - "deny": 805763152 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 3072, - "deny": 0 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "578978120334442506", - "last_message_id": "583695880893497355", - "type": 0, - "name": "clan-links-5-30-19", - "position": 75, - "parent_id": "449088445617405962", - "topic": "", - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1051648 - }, - { - "id": "382117075558203392", - "type": "role", - "allow": 1049600, - "deny": 0 - }, - { - "id": "382118099341541379", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "579715674151321611", - "type": 2, - "name": "Game Night 2", - "position": 2, - "parent_id": "485961711535390720", - "bitrate": 64000, - "user_limit": 10, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1049600 - }, - { - "id": "381911719901134850", - "type": "role", - "allow": 0, - "deny": 0 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 3146752, - "deny": 0 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 1049600, - "deny": 0 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 1048576 - } - ], - "nsfw": false - }, - { - "id": "579868209835147281", - "type": 4, - "name": "Raid", - "position": 6, - "parent_id": null, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "463520715254267916", - "type": "role", - "allow": 0, - "deny": 1048576 - }, - { - "id": "530919353999163414", - "type": "role", - "allow": 0, - "deny": 1048576 - } - ], - "nsfw": false - }, - { - "id": "579868728410505276", - "type": 4, - "name": "FireTeam Chats", - "position": 5, - "parent_id": null, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "463520715254267916", - "type": "role", - "allow": 0, - "deny": 1048576 - }, - { - "id": "530919353999163414", - "type": "role", - "allow": 0, - "deny": 1048576 - } - ], - "nsfw": false - }, - { - "id": "583416988152627212", - "last_message_id": "592543240952086578", - "type": 0, - "name": "weekly-clan-advertisement", - "position": 69, - "parent_id": "449088445617405962", - "topic": null, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1088 - }, - { - "id": "382117075558203392", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "538477449130803212", - "type": "role", - "allow": 0, - "deny": 3136 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "585549159705346120", - "type": 2, - "name": "Chronic Corner", - "position": 22, - "parent_id": "382125435376631820", - "bitrate": 111000, - "user_limit": 0, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "463520715254267916", - "type": "role", - "allow": 0, - "deny": 3148800 - }, - { - "id": "530919353999163414", - "type": "role", - "allow": 0, - "deny": 1048576 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 1048576 - } - ], - "nsfw": false - }, - { - "id": "586324559062630420", - "type": 4, - "name": "Menagerie", - "position": 9, - "parent_id": null, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "463520715254267916", - "type": "role", - "allow": 0, - "deny": 1048576 - }, - { - "id": "530919353999163414", - "type": "role", - "allow": 0, - "deny": 1048576 - }, - { - "id": "579865256327643141", - "type": "role", - "allow": 268436496, - "deny": 0 - } - ], - "nsfw": false - }, - { - "id": "587034851031384065", - "last_message_id": "634087295531614208", - "last_pin_timestamp": "2019-10-16T14:57:10.943000+00:00", - "type": 0, - "name": "shadowkeep-spoilers", - "position": 120, - "parent_id": "619529638699335685", - "topic": "*Spoiler Alert* @JeBaited#1342 with any questions. Repeat post = delete | Spoilers outside go as Warning ---> Chat ban", - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1051648 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 1049600, - "deny": 0 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "590564776057241610", - "type": 4, - "name": "Iron Banner", - "position": 22, - "parent_id": null, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "530919353999163414", - "type": "role", - "allow": 0, - "deny": 1049600 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "579865256327643141", - "type": "role", - "allow": 16, - "deny": 0 - } - ], - "nsfw": false - }, - { - "id": "597659463272628244", - "last_message_id": "664288725546565642", - "last_pin_timestamp": "2019-12-07T03:31:21.512000+00:00", - "type": 0, - "name": "adverts", - "position": 51, - "parent_id": "382114317857849344", - "topic": null, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "382116615614889984", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "382117075558203392", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "382118099341541379", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "598679640122327051", - "last_message_id": "618147052584566960", - "type": 0, - "name": "clan-links-9-4-19", - "position": 124, - "parent_id": "619529638699335685", - "topic": "", - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 3072 - }, - { - "id": "181383334457376769", - "type": "member", - "allow": 3072, - "deny": 0 - }, - { - "id": "382117075558203392", - "type": "role", - "allow": 3072, - "deny": 0 - }, - { - "id": "382118099341541379", - "type": "role", - "allow": 0, - "deny": 3072 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 3072, - "deny": 0 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 0, - "deny": 3072 - }, - { - "id": "538477449130803212", - "type": "role", - "allow": 0, - "deny": 3072 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "599341089194770443", - "last_message_id": "663405680836739082", - "type": 0, - "name": "trial-moderator-applications", - "position": 16, - "parent_id": "518871349125447701", - "topic": null, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 805825617 - }, - { - "id": "382117075558203392", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "538477449130803212", - "type": "role", - "allow": 68608, - "deny": 1 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "601945058215723038", - "last_message_id": "653385643552145431", - "last_pin_timestamp": "2019-07-20T01:21:56.751000+00:00", - "type": 0, - "name": "ask-the-admins", - "position": 6, - "parent_id": null, - "topic": null, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 3072 - }, - { - "id": "463520715254267916", - "type": "role", - "allow": 0, - "deny": 3148800 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "601951874295267349", - "type": 2, - "name": "Club Collective (Music)", - "position": 21, - "parent_id": "382125435376631820", - "bitrate": 384000, - "user_limit": 0, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "463520715254267916", - "type": "role", - "allow": 3146752, - "deny": 2048 - }, - { - "id": "530919353999163414", - "type": "role", - "allow": 0, - "deny": 1048576 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 1048576 - } - ], - "nsfw": false - }, - { - "id": "602695133107978250", - "last_message_id": "602842254217969668", - "type": 0, - "name": "━━━━━━━━", - "position": 22, - "parent_id": "485961711535390720", - "topic": "", - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 3072 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 3146752, - "deny": 0 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "602695431935492116", - "type": 2, - "name": "━━━━━━━━", - "position": 0, - "parent_id": "485961711535390720", - "bitrate": 64000, - "user_limit": 0, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 3146752 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 1048576 - } - ], - "nsfw": false - }, - { - "id": "607742611821821952", - "last_message_id": "646537997612875799", - "last_pin_timestamp": "2019-09-20T01:42:08.444000+00:00", - "type": 0, - "name": "officer-chat", - "position": 57, - "parent_id": "382114317857849344", - "topic": null, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 3072 - }, - { - "id": "382116615614889984", - "type": "role", - "allow": 3072, - "deny": 0 - }, - { - "id": "382117075558203392", - "type": "role", - "allow": 3072, - "deny": 0 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 3072, - "deny": 0 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "609073241704759306", - "type": 4, - "name": "📊 Server Stats 📊", - "position": 21, - "parent_id": null, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 871890257 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "482625494886645781", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "531004642612477964", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "538477449130803212", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "539618386037047317", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "565734337819967489", - "type": "role", - "allow": 0, - "deny": 1024 - } - ], - "nsfw": false - }, - { - "id": "609073244103770124", - "type": 2, - "name": "Member Count: 9639", - "position": 42, - "parent_id": "609073241704759306", - "bitrate": 64000, - "user_limit": 0, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1049600 - }, - { - "id": "382118099341541379", - "type": "role", - "allow": 1024, - "deny": 1048576 - }, - { - "id": "432533456807919639", - "type": "member", - "allow": 1049600, - "deny": 0 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 269485056, - "deny": 0 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 1024, - "deny": 1048576 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 1048576 - } - ], - "nsfw": false - }, - { - "id": "609073245840343065", - "type": 2, - "name": "Role Count: 93", - "position": 43, - "parent_id": "609073241704759306", - "bitrate": 64000, - "user_limit": 0, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1049600 - }, - { - "id": "432533456807919639", - "type": "member", - "allow": 1049600, - "deny": 0 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 269485056, - "deny": 0 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 1048576 - } - ], - "nsfw": false - }, - { - "id": "609073247341903873", - "type": 2, - "name": "Bot Count: 19", - "position": 44, - "parent_id": "609073241704759306", - "bitrate": 64000, - "user_limit": 0, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1049600 - }, - { - "id": "432533456807919639", - "type": "member", - "allow": 1049600, - "deny": 0 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 269485056, - "deny": 0 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 1048576 - } - ], - "nsfw": false - }, - { - "id": "609073250512797699", - "type": 2, - "name": "Channel Count: 187", - "position": 45, - "parent_id": "609073241704759306", - "bitrate": 64000, - "user_limit": 0, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1049600 - }, - { - "id": "432533456807919639", - "type": "member", - "allow": 1049600, - "deny": 0 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 269485056, - "deny": 0 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 1048576 - } - ], - "nsfw": false - }, - { - "id": "609073252442046495", - "type": 2, - "name": "User Count: 9620", - "position": 46, - "parent_id": "609073241704759306", - "bitrate": 64000, - "user_limit": 0, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1049600 - }, - { - "id": "382118099341541379", - "type": "role", - "allow": 1024, - "deny": 1048576 - }, - { - "id": "432533456807919639", - "type": "member", - "allow": 1049600, - "deny": 0 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 269485056, - "deny": 0 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 1024, - "deny": 1048576 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 1048576 - } - ], - "nsfw": false - }, - { - "id": "611755273308209163", - "last_message_id": null, - "type": 0, - "name": "━━━━━━━━", - "position": 38, - "parent_id": "552220299521818674", - "topic": null, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "382118099341541379", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "530919353999163414", - "type": "role", - "allow": 0, - "deny": 1050624 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "551597061955387403", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "611755385929334794", - "last_message_id": "616436715967610906", - "type": 0, - "name": "scrim-rules", - "position": 36, - "parent_id": "552220299521818674", - "topic": null, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 3072 - }, - { - "id": "382118099341541379", - "type": "role", - "allow": 1024, - "deny": 2048 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 1024, - "deny": 2048 - }, - { - "id": "538477449130803212", - "type": "role", - "allow": 0, - "deny": 0 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 3072, - "deny": 0 - }, - { - "id": "551597061955387403", - "type": "role", - "allow": 0, - "deny": 3072 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "611755724095094790", - "type": 2, - "name": "━━━━━━━━", - "position": 32, - "parent_id": "552220299521818674", - "bitrate": 64000, - "user_limit": 0, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "382118099341541379", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "551597061955387403", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 1048576 - } - ], - "nsfw": false - }, - { - "id": "612041075867254784", - "type": 2, - "name": "Lobby", - "position": 24, - "parent_id": "552220299521818674", - "bitrate": 64000, - "user_limit": 69, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "382118099341541379", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "530919353999163414", - "type": "role", - "allow": 0, - "deny": 1050624 - }, - { - "id": "538477449130803212", - "type": "role", - "allow": 2048, - "deny": 1024 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "551597061955387403", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 1048576 - } - ], - "nsfw": false - }, - { - "id": "612041393640308746", - "type": 2, - "name": "Alpha", - "position": 25, - "parent_id": "552220299521818674", - "bitrate": 64000, - "user_limit": 3, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "382118099341541379", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "530919353999163414", - "type": "role", - "allow": 0, - "deny": 1050624 - }, - { - "id": "538477449130803212", - "type": "role", - "allow": 2048, - "deny": 1024 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "551597061955387403", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 1048576 - } - ], - "nsfw": false - }, - { - "id": "612041414724943872", - "type": 2, - "name": "Bravo", - "position": 26, - "parent_id": "552220299521818674", - "bitrate": 64000, - "user_limit": 3, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "382118099341541379", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "530919353999163414", - "type": "role", - "allow": 0, - "deny": 1050624 - }, - { - "id": "538477449130803212", - "type": "role", - "allow": 2048, - "deny": 1024 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "551597061955387403", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 1048576 - } - ], - "nsfw": false - }, - { - "id": "612041458693832728", - "type": 2, - "name": "Charlie", - "position": 27, - "parent_id": "552220299521818674", - "bitrate": 64000, - "user_limit": 3, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "382118099341541379", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "530919353999163414", - "type": "role", - "allow": 0, - "deny": 1050624 - }, - { - "id": "538477449130803212", - "type": "role", - "allow": 2048, - "deny": 1024 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "551597061955387403", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 1048576 - } - ], - "nsfw": false - }, - { - "id": "612041492822884360", - "type": 2, - "name": "Delta", - "position": 28, - "parent_id": "552220299521818674", - "bitrate": 64000, - "user_limit": 3, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "382118099341541379", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "530919353999163414", - "type": "role", - "allow": 0, - "deny": 1050624 - }, - { - "id": "538477449130803212", - "type": "role", - "allow": 2048, - "deny": 1024 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "551597061955387403", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 1048576 - } - ], - "nsfw": false - }, - { - "id": "612041608505851951", - "type": 2, - "name": "Echo", - "position": 29, - "parent_id": "552220299521818674", - "bitrate": 64000, - "user_limit": 3, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "382118099341541379", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "530919353999163414", - "type": "role", - "allow": 0, - "deny": 1050624 - }, - { - "id": "538477449130803212", - "type": "role", - "allow": 2048, - "deny": 1024 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "551597061955387403", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 1048576 - } - ], - "nsfw": false - }, - { - "id": "612041641414361088", - "type": 2, - "name": "Foxtrot", - "position": 30, - "parent_id": "552220299521818674", - "bitrate": 64000, - "user_limit": 3, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "382118099341541379", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "530919353999163414", - "type": "role", - "allow": 0, - "deny": 1050624 - }, - { - "id": "538477449130803212", - "type": "role", - "allow": 2048, - "deny": 1024 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "551597061955387403", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 1048576 - } - ], - "nsfw": false - }, - { - "id": "613863292426911910", - "last_message_id": "613863859186434048", - "type": 0, - "name": "cross-save-faq", - "position": 125, - "parent_id": "619529638699335685", - "topic": null, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 146496 - }, - { - "id": "463520715254267916", - "type": "role", - "allow": 0, - "deny": 7168 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 7168 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 7168 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "614213169182932992", - "type": 2, - "name": "Fireteam 1", - "position": 5, - "parent_id": "579868728410505276", - "bitrate": 64000, - "user_limit": 3, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "463520715254267916", - "type": "role", - "allow": 0, - "deny": 1048576 - }, - { - "id": "530919353999163414", - "type": "role", - "allow": 0, - "deny": 1048576 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 1048576 - } - ], - "nsfw": false - }, - { - "id": "614213300577894400", - "type": 2, - "name": "Raid 1", - "position": 8, - "parent_id": "579868209835147281", - "bitrate": 64000, - "user_limit": 6, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "463520715254267916", - "type": "role", - "allow": 0, - "deny": 1048576 - }, - { - "id": "530919353999163414", - "type": "role", - "allow": 0, - "deny": 1048576 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 1048576 - } - ], - "nsfw": false - }, - { - "id": "614213534787698915", - "type": 2, - "name": "Strike 1", - "position": 11, - "parent_id": "551866300754952215", - "bitrate": 64000, - "user_limit": 3, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "463520715254267916", - "type": "role", - "allow": 0, - "deny": 1050624 - }, - { - "id": "530919353999163414", - "type": "role", - "allow": 0, - "deny": 1048576 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 1048576 - } - ], - "nsfw": false - }, - { - "id": "614213665654046730", - "type": 2, - "name": "Gambit 1", - "position": 16, - "parent_id": "551868604350464011", - "bitrate": 64000, - "user_limit": 4, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "463520715254267916", - "type": "role", - "allow": 0, - "deny": 1048576 - }, - { - "id": "530919353999163414", - "type": "role", - "allow": 0, - "deny": 1048576 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 1048576 - } - ], - "nsfw": false - }, - { - "id": "614214069452406853", - "type": 2, - "name": "Quickplay 1", - "position": 17, - "parent_id": "486328170128211971", - "bitrate": 64000, - "user_limit": 6, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 1, - "deny": 0 - }, - { - "id": "463520715254267916", - "type": "role", - "allow": 0, - "deny": 1048576 - }, - { - "id": "530919353999163414", - "type": "role", - "allow": 0, - "deny": 1048576 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 1048576 - } - ], - "nsfw": false - }, - { - "id": "614214970540032004", - "type": 2, - "name": "Menagerie 1", - "position": 15, - "parent_id": "586324559062630420", - "bitrate": 64000, - "user_limit": 6, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "463520715254267916", - "type": "role", - "allow": 0, - "deny": 1048576 - }, - { - "id": "530919353999163414", - "type": "role", - "allow": 0, - "deny": 1048576 - }, - { - "id": "579865256327643141", - "type": "role", - "allow": 268436496, - "deny": 0 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 1048576 - } - ], - "nsfw": false - }, - { - "id": "617774763678629936", - "last_message_id": "618855778014724141", - "type": 0, - "name": "clan-links-9-21-19", - "position": 123, - "parent_id": "619529638699335685", - "topic": "", - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 3072 - }, - { - "id": "382116615614889984", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "382117075558203392", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 3072, - "deny": 0 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 3072, - "deny": 0 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "619529638699335685", - "type": 4, - "name": "Archive 2", - "position": 24, - "parent_id": null, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1049600 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 1049600, - "deny": 0 - } - ], - "nsfw": false - }, - { - "id": "623277342713839646", - "last_message_id": "664860062422007814", - "last_pin_timestamp": "2019-12-20T20:20:59.822000+00:00", - "type": 0, - "name": "memes", - "position": 32, - "parent_id": "381910837293613057", - "topic": "No Racism, Sexualization of people (Especially Women and Minors.), No porn, no anti-semetic stuff, No Gore, And No degradation of **ANYONE**. NO EXCEPTIONS! if you can’t handle being compliant with the rules, we suggest you not use this channel.", - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 3072, - "deny": 0 - }, - { - "id": "382117075558203392", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "382118099341541379", - "type": "role", - "allow": 0, - "deny": 0 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 0, - "deny": 0 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 0, - "deny": 0 - }, - { - "id": "530919353999163414", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 0, - "deny": 0 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "624378694466666499", - "last_message_id": "638161665724121098", - "type": 0, - "name": "clan-links-nov-2019", - "position": 126, - "parent_id": "619529638699335685", - "topic": "", - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1051713 - }, - { - "id": "538477449130803212", - "type": "role", - "allow": 0, - "deny": 3072 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "627919829491515394", - "last_message_id": "634098653275095069", - "last_pin_timestamp": "2019-09-29T17:42:37.488000+00:00", - "type": 0, - "name": "gos-lfg", - "position": 121, - "parent_id": "619529638699335685", - "topic": "Please use this channel for finding GoS Teams! Have at it, guardians! @here is allowed!", - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 133120, - "deny": 1024 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 132096, - "deny": 0 - }, - { - "id": "463520715254267916", - "type": "role", - "allow": 0, - "deny": 3072 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 131072, - "deny": 0 - }, - { - "id": "530919353999163414", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 131072, - "deny": 0 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "631646526921244702", - "type": 4, - "name": "Temp Channels", - "position": 14, - "parent_id": null, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "241950106125860865", - "type": "member", - "allow": 0, - "deny": 3072 - }, - { - "id": "530919353999163414", - "type": "role", - "allow": 0, - "deny": 2100224 - }, - { - "id": "564512855790059545", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "625165812881424404", - "type": "role", - "allow": 871890769, - "deny": 0 - } - ], - "nsfw": false - }, - { - "id": "632038066831360034", - "last_message_id": "664730689446477825", - "last_pin_timestamp": "2019-10-11T02:57:04.526000+00:00", - "type": 0, - "name": "create-channel", - "position": 34, - "parent_id": "631646526921244702", - "topic": null, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 3072, - "deny": 268627984 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "625165812881424404", - "type": "role", - "allow": 3072, - "deny": 0 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "633066562663874571", - "type": 4, - "name": "Competitive", - "position": 12, - "parent_id": null, - "guild_id": "162706186272112640", - "permission_overwrites": [], - "nsfw": false - }, - { - "id": "633066676975435806", - "type": 2, - "name": "Competitive 1", - "position": 19, - "parent_id": "633066562663874571", - "bitrate": 64000, - "user_limit": 3, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 1048576 - } - ], - "nsfw": false - }, - { - "id": "634143533640581125", - "type": 2, - "name": "Iron Banner 1", - "position": 45, - "parent_id": "590564776057241610", - "bitrate": 64000, - "user_limit": 6, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "530919353999163414", - "type": "role", - "allow": 0, - "deny": 1049600 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "579865256327643141", - "type": "role", - "allow": 16, - "deny": 0 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 1048576 - } - ], - "nsfw": false - }, - { - "id": "634501759024365569", - "last_message_id": "662123864658280458", - "last_pin_timestamp": "2019-11-24T22:06:00.080000+00:00", - "type": 0, - "name": "recognition", - "position": 31, - "parent_id": "381910837293613057", - "topic": "Abusing this channel in any way will result in your permissions being taken away to view this channel. Please look at the pinned format for how to go about posting a message here!", - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 3072, - "deny": 0 - }, - { - "id": "382117075558203392", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 268435456, - "deny": 0 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 268435456, - "deny": 0 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 268435456, - "deny": 0 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "635625512319975426", - "type": 4, - "name": "Seasonal Activity", - "position": 8, - "parent_id": null, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 3146752, - "deny": 0 - }, - { - "id": "382117075558203392", - "type": "role", - "allow": 1049600, - "deny": 0 - } - ], - "nsfw": false - }, - { - "id": "637279033146736641", - "last_message_id": "637804501151252480", - "type": 0, - "name": "game-night-voting", - "position": 119, - "parent_id": "619529638699335685", - "topic": null, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 3072 - }, - { - "id": "382117075558203392", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 3072, - "deny": 0 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "638537842120851464", - "last_message_id": "640310212619665410", - "type": 0, - "name": "scrim-announcements", - "position": 35, - "parent_id": "552220299521818674", - "topic": null, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "382117075558203392", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 3072, - "deny": 0 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 3072, - "deny": 0 - }, - { - "id": "502964473674465300", - "type": "role", - "allow": 3072, - "deny": 0 - }, - { - "id": "538477449130803212", - "type": "role", - "allow": 1024, - "deny": 2048 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 3072, - "deny": 0 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 3072 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "641678205329080325", - "type": 2, - "name": "Fireteam 2", - "position": 6, - "parent_id": "579868728410505276", - "bitrate": 64000, - "user_limit": 3, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "463520715254267916", - "type": "role", - "allow": 0, - "deny": 1048576 - }, - { - "id": "530919353999163414", - "type": "role", - "allow": 0, - "deny": 1048576 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 1048576 - } - ], - "nsfw": false - }, - { - "id": "644693082440335360", - "last_message_id": "660224642883256353", - "type": 0, - "name": "clan-links", - "position": 10, - "parent_id": "479849608558936064", - "topic": "", - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 1024, - "deny": 2048 - }, - { - "id": "382117075558203392", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "441751906428256277", - "type": "member", - "allow": 3136, - "deny": 0 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 3072, - "deny": 0 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "652529652631601192", - "last_message_id": "658143232655491083", - "type": 0, - "name": "sherpa-night-tracker", - "position": 42, - "parent_id": "526617610519838741", - "topic": null, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "382117075558203392", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "469631935753355264", - "type": "role", - "allow": 3072, - "deny": 0 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "565734337819967489", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "652893148732850197", - "type": 2, - "name": "Competitive 2", - "position": 20, - "parent_id": "633066562663874571", - "bitrate": 64000, - "user_limit": 3, - "guild_id": "162706186272112640", - "permission_overwrites": [], - "nsfw": false - }, - { - "id": "652923485076717578", - "type": 2, - "name": "Quickplay 2", - "position": 18, - "parent_id": "486328170128211971", - "bitrate": 64000, - "user_limit": 6, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 1, - "deny": 0 - }, - { - "id": "463520715254267916", - "type": "role", - "allow": 0, - "deny": 1048576 - }, - { - "id": "530919353999163414", - "type": "role", - "allow": 0, - "deny": 1048576 - } - ], - "nsfw": false - }, - { - "id": "654049594480525322", - "last_message_id": "658121836738904079", - "last_pin_timestamp": "2019-12-21T18:29:45.766000+00:00", - "type": 0, - "name": "season-of-dawn-discussion", - "position": 118, - "parent_id": "619529638699335685", - "topic": "*Spoiler Alert* @JeBaited#1342 with any questions. Try to not repeat post | Spoilers outside of this channel go as Warning then Chat ban.", - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 0, - "deny": 3072 - }, - { - "id": "382116615614889984", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "382117075558203392", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "382118099341541379", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "475476028349874178", - "type": "role", - "allow": 0, - "deny": 1024 - }, - { - "id": "538477449130803212", - "type": "role", - "allow": 0, - "deny": 3072 - }, - { - "id": "539605106790760468", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "654052427988729877", - "type": 2, - "name": "Sundial 1", - "position": 13, - "parent_id": "635625512319975426", - "bitrate": 64000, - "user_limit": 6, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 3146752, - "deny": 0 - }, - { - "id": "382117075558203392", - "type": "role", - "allow": 1049600, - "deny": 0 - } - ], - "nsfw": false - }, - { - "id": "654165747999703070", - "last_message_id": "664168444618604554", - "type": 0, - "name": "syc-application", - "position": 17, - "parent_id": "518871349125447701", - "topic": "Application form for #support-your-community (Streaming and YouTube videos.)", - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 68608, - "deny": 262208 - }, - { - "id": "530919353999163414", - "type": "role", - "allow": 0, - "deny": 2048 - }, - { - "id": "646540079375843347", - "type": "role", - "allow": 1024, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "657037991297941514", - "type": 2, - "name": "Strike 2", - "position": 12, - "parent_id": "551866300754952215", - "bitrate": 64000, - "user_limit": 0, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "463520715254267916", - "type": "role", - "allow": 0, - "deny": 1050624 - }, - { - "id": "530919353999163414", - "type": "role", - "allow": 0, - "deny": 1048576 - } - ], - "nsfw": false - }, - { - "id": "657038231388291073", - "type": 2, - "name": "Sundial 2", - "position": 14, - "parent_id": "635625512319975426", - "bitrate": 64000, - "user_limit": 6, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 3146752, - "deny": 0 - }, - { - "id": "382117075558203392", - "type": "role", - "allow": 1049600, - "deny": 0 - } - ], - "nsfw": false - }, - { - "id": "664637199861678091", - "last_message_id": "664823212156256287", - "type": 0, - "name": "crucible-guide-applications", - "position": 133, - "parent_id": "518871349125447701", - "topic": null, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "162706186272112640", - "type": "role", - "allow": 3072, - "deny": 0 - }, - { - "id": "382117075558203392", - "type": "role", - "allow": 1024, - "deny": 0 - }, - { - "id": "442416690631016469", - "type": "role", - "allow": 3072, - "deny": 0 - }, - { - "id": "623678304683294720", - "type": "role", - "allow": 0, - "deny": 2048 - } - ], - "nsfw": false, - "rate_limit_per_user": 0 - }, - { - "id": "664879639814078464", - "type": 2, - "name": "Menagerie 2", - "position": 49, - "parent_id": "586324559062630420", - "bitrate": 64000, - "user_limit": 6, - "guild_id": "162706186272112640", - "permission_overwrites": [ - { - "id": "463520715254267916", - "type": "role", - "allow": 0, - "deny": 1048576 - }, - { - "id": "530919353999163414", - "type": "role", - "allow": 0, - "deny": 1048576 - }, - { - "id": "579865256327643141", - "type": "role", - "allow": 268436496, - "deny": 0 - } - ], - "nsfw": false - } -] From c22d358ff8cb169970d269994eaeeeab50c99100 Mon Sep 17 00:00:00 2001 From: James Whaley Date: Fri, 10 Jan 2020 17:48:22 +0000 Subject: [PATCH 25/44] Add Role model and roles field to Emoji --- src/model/guild/emoji/mod.rs | 21 +++++-- src/model/guild/mod.rs | 2 + src/model/guild/role.rs | 106 +++++++++++++++++++++++++++++++++++ 3 files changed, 123 insertions(+), 6 deletions(-) create mode 100644 src/model/guild/role.rs diff --git a/src/model/guild/emoji/mod.rs b/src/model/guild/emoji/mod.rs index 66af52c..bfa706e 100644 --- a/src/model/guild/emoji/mod.rs +++ b/src/model/guild/emoji/mod.rs @@ -4,6 +4,7 @@ use std::fmt::{self, Display}; use serde::{Deserialize, Serialize}; +use crate::model::id::RoleId; use crate::model::user::User; pub use self::partial::PartialEmoji; @@ -15,7 +16,9 @@ pub struct Emoji { /// The ID of the emoji. #[serde(flatten)] pub emoji: PartialEmoji, - // TODO: roles + /// A set of roles the emoji is whitelisted to. + #[serde(default)] + pub roles: Vec, /// The user that created the emoji. pub user: Option, /// Whether the name requires colons to be used by a client. @@ -32,7 +35,7 @@ impl Display for Emoji { } } -impl_eq_fields!(Emoji: [emoji, user, require_colons, managed]); +impl_eq_fields!(Emoji: [emoji, roles, user, require_colons, managed]); #[cfg(test)] mod tests { @@ -61,6 +64,10 @@ mod tests { }); let emoji = Emoji { emoji: PartialEmoji::custom(41771983429993937, "LUL", false), + roles: vec![ + RoleId::from(41771983429993000), + RoleId::from(41771983429993111), + ], user: Some(User { id: UserId::from(96008815106887111), name: "Luigi".to_owned(), @@ -73,13 +80,11 @@ mod tests { managed: false, }; - let deserialized: Emoji = serde_json::from_value(value).unwrap(); + let deserialized = Emoji::deserialize(&value).unwrap(); assert_eq_fields!(emoji, deserialized); } - // TODO: Enable test when `roles` is added to `Emoji`. #[test] - #[ignore] fn test_serialize_emoji() { let value = json!({ "id": "41771983429993937", @@ -99,6 +104,10 @@ mod tests { }); let emoji = Emoji { emoji: PartialEmoji::custom(41771983429993937, "LUL", true), + roles: vec![ + RoleId::from(41771983429993000), + RoleId::from(41771983429993111), + ], user: Some(User { id: UserId::from(96008815106887111), name: "Luigi".to_owned(), @@ -111,6 +120,6 @@ mod tests { managed: false, }; - assert_eq!(value, serde_json::to_value(emoji).unwrap()); + assert_eq!(value, serde_json::to_value(&emoji).unwrap()); } } diff --git a/src/model/guild/mod.rs b/src/model/guild/mod.rs index ac9c592..0551205 100644 --- a/src/model/guild/mod.rs +++ b/src/model/guild/mod.rs @@ -3,7 +3,9 @@ mod audit_log; mod emoji; mod member; +mod role; pub use self::audit_log::AuditLogEvent; pub use self::emoji::{Emoji, PartialEmoji}; pub use self::member::{Member, PartialMember}; +pub use self::role::Role; diff --git a/src/model/guild/role.rs b/src/model/guild/role.rs new file mode 100644 index 0000000..c16c12a --- /dev/null +++ b/src/model/guild/role.rs @@ -0,0 +1,106 @@ +use serde::{Deserialize, Serialize}; + +use crate::model::color::Color; +use crate::model::id::RoleId; +use crate::model::permissions::Permissions; + +/// Represents a set of permissions attached to a group of users. +/// +/// Roles have unique names, colors, and can be pinned to the side bar, causing +/// their members to be listed separately. Roles are unique per guild, and can +/// have separate permission profiles for the global context (guild) and channel +/// context. +/// +/// The `@everyone` role has the same ID as the guild it belongs to. +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct Role { + /// The ID of the role. + pub id: RoleId, + /// The name of the role. + pub name: String, + /// The color of the role. + #[serde(default, alias = "colour")] + pub color: Color, + /// Whether the role is pinned in the user listing. + #[serde(rename = "hoist")] + pub pinned: bool, + /// The position of the role. + pub position: usize, + /// The set of permissions for the users with the role. + pub permissions: Permissions, + /// Whether the role is managed by an integration. + pub managed: bool, + /// Whether the role is mentionable. + pub mentionable: bool, +} + +impl_eq_fields!(Role: [ + id, + name, + color, + pinned, + position, + permissions, + managed, + mentionable, +]); + +#[cfg(test)] +mod tests { + use serde_json::json; + + use super::*; + + #[test] + fn test_deserialize() { + let value = json!({ + "id": "41771983423143936", + "name": "WE DEM BOYZZ!!!!!!", + "color": 3447003, + "hoist": true, + "position": 1, + "permissions": 66321471, + "managed": false, + "mentionable": false + }); + let role = Role { + id: RoleId::from(41771983423143936), + name: "WE DEM BOYZZ!!!!!!".to_owned(), + color: Color::new(3447003), + pinned: true, + position: 1, + permissions: Permissions::from_bits(66321471).unwrap(), + managed: false, + mentionable: false, + }; + + let deserialized = Role::deserialize(&value).unwrap(); + assert_eq_fields!(role, deserialized); + } + + #[test] + fn test_serialize() { + let value = json!({ + "id": "41771983423143936", + "name": "WE DEM BOYZZ!!!!!!", + "color": 3447003, + "hoist": true, + "position": 1, + "permissions": 66321471, + "managed": false, + "mentionable": false + }); + let role = Role { + id: RoleId::from(41771983423143936), + name: "WE DEM BOYZZ!!!!!!".to_owned(), + color: Color::new(3447003), + pinned: true, + position: 1, + permissions: Permissions::from_bits(66321471).unwrap(), + managed: false, + mentionable: false, + }; + + assert_eq!(value, serde_json::to_value(role).unwrap()); + } +} From 31c97b00a25d726dfbf6c756ba8da00f45a38f91 Mon Sep 17 00:00:00 2001 From: James Whaley Date: Fri, 10 Jan 2020 17:52:44 +0000 Subject: [PATCH 26/44] Improve impl_eq_fields for Group --- src/model/channel/group.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/model/channel/group.rs b/src/model/channel/group.rs index 8d190f6..d5ebd18 100644 --- a/src/model/channel/group.rs +++ b/src/model/channel/group.rs @@ -57,7 +57,11 @@ impl_eq_fields!(Group: (a, b) => { assert_eq!(a.recipients.len(), b.recipients.len()); for (id, a_user) in a.recipients.iter() { - let b_user = b.recipients.get(id).expect(&format!("missing user with id: {}", id)); + let b_user = match b.recipients.get(id) { + Some(user) => user, + #[cold] + None => panic!("missing user with id: {}", id), + }; assert_eq_fields!(a_user, b_user); } }); From ec4ab45a765f72d1550485b0ac5c7e24926d8636 Mon Sep 17 00:00:00 2001 From: James Whaley Date: Sat, 11 Jan 2020 00:12:57 +0000 Subject: [PATCH 27/44] Improve tests and add Message tests --- src/internal/test.rs | 20 +++- src/model/channel/message/attachment.rs | 2 + src/model/channel/message/embed.rs | 16 +-- src/model/channel/message/mod.rs | 132 ++++++++++++++++++++- src/model/channel/message/rich_presence.rs | 2 +- src/model/channel/permissions.rs | 35 +++--- src/model/guild/emoji/partial.rs | 8 +- src/model/guild/member.rs | 3 + src/model/guild/role.rs | 2 +- src/model/permissions.rs | 4 +- src/model/snowflake.rs | 12 +- src/model/user/discriminator.rs | 8 +- src/model/user/mod.rs | 16 +-- 13 files changed, 194 insertions(+), 66 deletions(-) diff --git a/src/internal/test.rs b/src/internal/test.rs index 324c5a9..1603cc2 100644 --- a/src/internal/test.rs +++ b/src/internal/test.rs @@ -88,25 +88,39 @@ mod inner { fn eq_fields(&self, other: &Option) { match (self, other) { (Some(left_val), Some(right_val)) => EqFields::eq_fields(left_val, right_val), + (None, None) => {} (left_val, right_val) => panic_ne_fields!(left_val, right_val), } } } - impl EqFields> for Result + impl EqFields> for Result where A: EqFields, B: std::fmt::Debug, - Err: std::fmt::Debug, + E: EqFields, { - fn eq_fields(&self, other: &Result) { + fn eq_fields(&self, other: &Result) { match (self, other) { (Ok(left_val), Ok(right_val)) => EqFields::eq_fields(left_val, right_val), + (Err(left_val), Err(right_val)) => EqFields::eq_fields(left_val, right_val), (left_val, right_val) => panic_ne_fields!(left_val, right_val), } } } + impl EqFields> for Vec + where + A: EqFields, + B: std::fmt::Debug, + { + fn eq_fields(&self, other: &Vec) { + for (a, b) in self.iter().zip(other.iter()) { + assert_eq_fields!(a, b); + } + } + } + pub trait DelegateEqFields { fn eq_fields(&self, other: &Self); } diff --git a/src/model/channel/message/attachment.rs b/src/model/channel/message/attachment.rs index b3ebe38..354d00d 100644 --- a/src/model/channel/message/attachment.rs +++ b/src/model/channel/message/attachment.rs @@ -23,3 +23,5 @@ pub struct Attachment { /// The width of the image, if the file is an image. pub width: Option, } + +impl_eq_fields!(Attachment: [id, filename, size, url, proxy_url, height, width]); diff --git a/src/model/channel/message/embed.rs b/src/model/channel/message/embed.rs index ca9653b..45cd023 100644 --- a/src/model/channel/message/embed.rs +++ b/src/model/channel/message/embed.rs @@ -9,7 +9,7 @@ use crate::model::color::Color; /// /// [`Message`]: ../struct.Message.html #[non_exhaustive] -#[derive(Clone, Debug, Deserialize, Serialize)] +#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)] pub struct Embed { /// The title of the embed. pub title: Option, @@ -84,7 +84,7 @@ impl Default for EmbedType { /// /// [`Embed`]: struct.Embed.html #[non_exhaustive] -#[derive(Clone, Debug, Deserialize, Serialize)] +#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)] pub struct EmbedFooter { /// The footer text. pub text: String, @@ -100,7 +100,7 @@ pub struct EmbedFooter { /// /// [`Embed`]: struct.Embed.html #[non_exhaustive] -#[derive(Clone, Debug, Deserialize, Serialize)] +#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)] pub struct EmbedImage { /// The URL of the image. /// @@ -118,7 +118,7 @@ pub struct EmbedImage { /// /// [`Embed`]: struct.Embed.html #[non_exhaustive] -#[derive(Clone, Debug, Deserialize, Serialize)] +#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)] pub struct EmbedThumbnail { /// The URL of the thumbnail. /// @@ -136,7 +136,7 @@ pub struct EmbedThumbnail { /// /// [`Embed`]: struct.Embed.html #[non_exhaustive] -#[derive(Clone, Debug, Deserialize, Serialize)] +#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)] pub struct EmbedVideo { /// The URL of the video. pub url: String, @@ -150,7 +150,7 @@ pub struct EmbedVideo { /// /// [`Embed`]: struct.Embed.html #[non_exhaustive] -#[derive(Clone, Debug, Deserialize, Serialize)] +#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)] pub struct EmbedProvider { /// The name of the provider. pub name: String, @@ -162,7 +162,7 @@ pub struct EmbedProvider { /// /// [`Embed`]: struct.Embed.html #[non_exhaustive] -#[derive(Clone, Debug, Deserialize, Serialize)] +#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)] pub struct EmbedAuthor { /// The author name. pub name: String, @@ -180,7 +180,7 @@ pub struct EmbedAuthor { /// /// [`Embed`]: struct.Embed.html #[non_exhaustive] -#[derive(Clone, Debug, Deserialize, Serialize)] +#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)] pub struct EmbedField { /// The name of the field. pub name: String, diff --git a/src/model/channel/message/mod.rs b/src/model/channel/message/mod.rs index beae003..260490a 100644 --- a/src/model/channel/message/mod.rs +++ b/src/model/channel/message/mod.rs @@ -125,6 +125,12 @@ pub enum MessageType { ChannelFollowAdd = 12, } +impl Default for MessageType { + fn default() -> Self { + MessageType::Default + } +} + /// Reference data send with a crossposted [`Message`] /// /// [`Message`]: struct.Message.html @@ -231,13 +237,44 @@ pub struct Reaction { pub emoji: PartialEmoji, } +impl_eq_fields!(Message: [ + id, + channel_id, + guild_id, + author, + member, + content, + timestamp, + edited_timestamp, + tts, + mention_everyone, + mentions, + mention_roles, + mention_channels, + attachments, + embeds, + reactions, + pinned, + webhook_id, + kind, + activity, + application, + message_reference, + flags, +]); +impl_eq_fields!(MentionedUser: [user, member]); +impl_eq_fields!(MentionedChannel: [id, guild_id, kind, name]); +impl_eq_fields!(Reaction: [count, me, emoji]); + #[cfg(test)] mod tests { use serde_json::json; + use crate::model::id::UserId; + use super::*; - // TODO: Improve `Message` serde tests. + // TODO: Add serialize message serde tests. #[test] fn test_deserialize_message() { @@ -272,8 +309,45 @@ mod tests { "mentions": [], "type": 0 }); + let message = Message { + id: MessageId::from(334385199974967042), + channel_id: ChannelId::from(290926798999357250), + guild_id: None, + author: User { + id: UserId::from(53908099506183680), + name: "Mason".to_owned(), + discriminator: "9999".parse().unwrap(), + avatar: Some("a_bab14f271d565501444b2ca3be944b25".to_owned()), + bot: false, + system: false, + }, + member: None, + content: "Supa Hot".to_owned(), + timestamp: "2017-07-11T17:27:07.299000+00:00".parse().unwrap(), + edited_timestamp: None, + tts: false, + mention_everyone: false, + mentions: vec![], + mention_roles: vec![], + mention_channels: vec![], + attachments: vec![], + embeds: vec![], + reactions: vec![Reaction { + count: 1, + me: false, + emoji: PartialEmoji::standard("🔥"), + }], + pinned: false, + webhook_id: None, + kind: MessageType::Default, + activity: None, + application: None, + message_reference: None, + flags: MessageFlags::default(), + }; - let _: Message = serde_json::from_value(value).unwrap(); + let deserialized = Message::deserialize(&value).unwrap(); + assert_eq_fields!(message, deserialized); } #[test] @@ -323,16 +397,64 @@ mod tests { "message_id": "306588351130107906" } }); + let message = Message { + id: MessageId::from(334385199974967042), + channel_id: ChannelId::from(290926798999357250), + guild_id: None, + author: User { + id: UserId::from(53908099506183680), + name: "Mason".to_owned(), + discriminator: "9999".parse().unwrap(), + avatar: Some("a_bab14f271d565501444b2ca3be944b25".to_owned()), + bot: false, + system: false, + }, + member: None, + content: "Big news! In this <#278325129692446722> channel!".to_owned(), + timestamp: "2017-07-11T17:27:07.299000+00:00".parse().unwrap(), + edited_timestamp: None, + tts: false, + mention_everyone: false, + mentions: vec![], + mention_roles: vec![], + mention_channels: vec![], + attachments: vec![], + embeds: vec![], + reactions: vec![Reaction { + count: 1, + me: false, + emoji: PartialEmoji::standard("🔥"), + }], + pinned: false, + webhook_id: None, + kind: MessageType::Default, + activity: None, + application: None, + message_reference: Some(MessageReference { + message_id: Some(MessageId::from(306588351130107906)), + channel_id: ChannelId::from(278325129692446722), + guild_id: Some(GuildId::from(278325129692446720)), + }), + flags: MessageFlags::IS_CROSSPOST, + }; + + let deserialized = Message::deserialize(&value).unwrap(); + assert_eq_fields!(message, deserialized); + } + + #[test] + fn test_deserialize_message_flags() { + let value = json!(2); + let flags = MessageFlags::IS_CROSSPOST; - let _: Message = serde_json::from_value(value).unwrap(); + assert_eq!(flags, MessageFlags::deserialize(&value).unwrap()); } #[test] - fn test_message_flags() { + fn test_serialize_message_flags() { let value = json!(2); let flags = MessageFlags::IS_CROSSPOST; assert_eq!(value, serde_json::to_value(&flags).unwrap()); - assert_eq!(flags, serde_json::from_value(value).unwrap()); } } diff --git a/src/model/channel/message/rich_presence.rs b/src/model/channel/message/rich_presence.rs index fe9af8a..b4326d1 100644 --- a/src/model/channel/message/rich_presence.rs +++ b/src/model/channel/message/rich_presence.rs @@ -28,7 +28,7 @@ pub enum MessageActivityType { /// Rich Presence application information. #[non_exhaustive] -#[derive(Clone, Debug, Deserialize, Serialize)] +#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)] pub struct MessageApplication { /// The ID of the application. pub id: ApplicationId, diff --git a/src/model/channel/permissions.rs b/src/model/channel/permissions.rs index 3ec3e2f..0be025e 100644 --- a/src/model/channel/permissions.rs +++ b/src/model/channel/permissions.rs @@ -86,44 +86,37 @@ mod tests { use super::*; - const ID: u64 = 80351110224678912; - - const ALLOW_BITS: u64 = 104188992; - const DENY_BITS: u64 = 135168; - #[test] fn test_serialize_role() { - let allow = Permissions::from_bits(ALLOW_BITS).expect("valid permissions"); - let deny = Permissions::from_bits(DENY_BITS).expect("valid permissions"); - - let overwrites = PermissionOverwrite::new(RoleId::from(ID), allow, deny); - - let expected = json!({ + let value = json!({ "id": "80351110224678912", "type": "role", "allow": 104188992, "deny": 135168, }); + let overwrites = PermissionOverwrite::new( + RoleId::from(80351110224678912), + Permissions::from_bits(104188992).expect("valid permissions"), + Permissions::from_bits(135168).expect("valid permissions"), + ); - let v = serde_json::to_value(overwrites).unwrap(); - assert_eq!(v, expected); + assert_eq!(value, serde_json::to_value(&overwrites).unwrap()); } #[test] fn test_serialize_user() { - let allow = Permissions::from_bits(ALLOW_BITS).expect("valid permissions"); - let deny = Permissions::from_bits(DENY_BITS).expect("valid permissions"); - - let overwrites = PermissionOverwrite::new(UserId::from(ID), allow, deny); - - let expected = json!({ + let value = json!({ "id": "80351110224678912", "type": "member", "allow": 104188992, "deny": 135168, }); + let overwrites = PermissionOverwrite::new( + UserId::from(80351110224678912), + Permissions::from_bits(104188992).expect("valid permissions"), + Permissions::from_bits(135168).expect("valid permissions"), + ); - let v = serde_json::to_value(overwrites).unwrap(); - assert_eq!(v, expected); + assert_eq!(value, serde_json::to_value(&overwrites).unwrap()); } } diff --git a/src/model/guild/emoji/partial.rs b/src/model/guild/emoji/partial.rs index 8168334..a0898a3 100644 --- a/src/model/guild/emoji/partial.rs +++ b/src/model/guild/emoji/partial.rs @@ -263,7 +263,7 @@ mod tests { }); let emoji = PartialEmoji::standard("🔥"); - assert_eq!(emoji, serde_json::from_value(value).unwrap()); + assert_eq!(emoji, PartialEmoji::deserialize(&value).unwrap()); } #[test] @@ -274,7 +274,7 @@ mod tests { }); let emoji = PartialEmoji::standard("🔥"); - assert_eq!(value, serde_json::to_value(emoji).unwrap()); + assert_eq!(value, serde_json::to_value(&emoji).unwrap()); } #[test] @@ -285,7 +285,7 @@ mod tests { }); let emoji = PartialEmoji::custom(41771983429993937, "LUL", false); - assert_eq!(emoji, serde_json::from_value(value).unwrap()); + assert_eq!(emoji, PartialEmoji::deserialize(&value).unwrap()); } #[test] @@ -297,6 +297,6 @@ mod tests { }); let emoji = PartialEmoji::custom(41771983429993937, "LUL", true); - assert_eq!(value, serde_json::to_value(emoji).unwrap()); + assert_eq!(value, serde_json::to_value(&emoji).unwrap()); } } diff --git a/src/model/guild/member.rs b/src/model/guild/member.rs index f6ca283..6455c12 100644 --- a/src/model/guild/member.rs +++ b/src/model/guild/member.rs @@ -34,3 +34,6 @@ pub struct PartialMember { /// Whether the user in muted in voice channels. pub mute: bool, } + +impl_eq_fields!(Member: [member, user, nick, premium_since]); +impl_eq_fields!(PartialMember: [roles, joined_at, deaf, mute]); diff --git a/src/model/guild/role.rs b/src/model/guild/role.rs index c16c12a..e01e3e8 100644 --- a/src/model/guild/role.rs +++ b/src/model/guild/role.rs @@ -101,6 +101,6 @@ mod tests { mentionable: false, }; - assert_eq!(value, serde_json::to_value(role).unwrap()); + assert_eq!(value, serde_json::to_value(&role).unwrap()); } } diff --git a/src/model/permissions.rs b/src/model/permissions.rs index 0de6b95..04ac09e 100644 --- a/src/model/permissions.rs +++ b/src/model/permissions.rs @@ -142,13 +142,13 @@ mod tests { | Permissions::READ_MESSAGE_HISTORY | Permissions::SEND_MESSAGES; - assert_eq!(perms, serde_json::from_value(value).unwrap()); + assert_eq!(perms, Permissions::deserialize(&value).unwrap()); } #[test] fn test_deserialize_invalid() { let value = serde_json::json!(0x00080000); - let err = serde_json::from_value::(value); + let err = Permissions::deserialize(&value); assert!(err.is_err()); assert!(err.unwrap_err().is_data()); diff --git a/src/model/snowflake.rs b/src/model/snowflake.rs index 89327d2..5e609f3 100644 --- a/src/model/snowflake.rs +++ b/src/model/snowflake.rs @@ -166,7 +166,7 @@ mod tests { fn test_serialize() { let value = json!("80351110224678912"); let snowflake = Snowflake::from(80351110224678912); - assert_eq!(value, serde_json::to_value(snowflake).unwrap()); + assert_eq!(value, serde_json::to_value(&snowflake).unwrap()); } #[test] @@ -174,15 +174,9 @@ mod tests { let snowflake = Snowflake::from(80351110224678912); let value = json!(80351110224678912u64); - assert_eq!( - snowflake, - serde_json::from_value::(value).unwrap() - ); + assert_eq!(snowflake, Snowflake::deserialize(&value).unwrap()); let value = json!("80351110224678912"); - assert_eq!( - snowflake, - serde_json::from_value::(value).unwrap() - ); + assert_eq!(snowflake, Snowflake::deserialize(&value).unwrap()); } } diff --git a/src/model/user/discriminator.rs b/src/model/user/discriminator.rs index ab6f46b..bed8e9e 100644 --- a/src/model/user/discriminator.rs +++ b/src/model/user/discriminator.rs @@ -187,17 +187,17 @@ mod tests { #[test] fn test_serialize() { - let v = json!("0001"); + let value = json!("0001"); let d = Discriminator::new(1).unwrap(); - assert_eq!(v, serde_json::to_value(&d).unwrap()); + assert_eq!(value, serde_json::to_value(&d).unwrap()); } #[test] fn test_deserialize() { - let v = json!("0001"); + let value = json!("0001"); let d = Discriminator::new(1).unwrap(); - assert_eq!(d, serde_json::from_value(v).unwrap()); + assert_eq!(d, Discriminator::deserialize(&value).unwrap()); } } diff --git a/src/model/user/mod.rs b/src/model/user/mod.rs index 6a3abbf..26b1d8e 100644 --- a/src/model/user/mod.rs +++ b/src/model/user/mod.rs @@ -99,8 +99,8 @@ mod tests { system: Default::default(), }; - let user2: User = serde_json::from_value(value.clone()).unwrap(); - assert_eq_fields!(user, user2); + let deserialized = User::deserialize(&value).unwrap(); + assert_eq_fields!(user, deserialized); } #[test] @@ -122,7 +122,7 @@ mod tests { system: false, }; - assert_eq!(value, serde_json::to_value(user).unwrap()); + assert_eq!(value, serde_json::to_value(&user).unwrap()); } #[test] @@ -157,8 +157,8 @@ mod tests { premium_type: Some(PremiumType::NitroClassic), }; - let user2: ClientUser = serde_json::from_value(value.clone()).unwrap(); - assert_eq_fields!(user, user2); + let deserialized = ClientUser::deserialize(&value).unwrap(); + assert_eq_fields!(user, deserialized); } #[test] @@ -192,8 +192,8 @@ mod tests { premium_type: None, }; - let user2: ClientUser = serde_json::from_value(value.clone()).unwrap(); - assert_eq_fields!(user, user2); + let deserialized = ClientUser::deserialize(&value).unwrap(); + assert_eq_fields!(user, deserialized); } #[test] @@ -229,6 +229,6 @@ mod tests { premium_type: None, }; - assert_eq!(value, serde_json::to_value(user).unwrap()); + assert_eq!(value, serde_json::to_value(&user).unwrap()); } } From 1d362ad6dd5a85007ecf005e272417f41f9f6655 Mon Sep 17 00:00:00 2001 From: James Whaley Date: Sun, 12 Jan 2020 15:03:30 +0000 Subject: [PATCH 28/44] Add `/users/@me/guilds` route --- src/http/routing.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/http/routing.rs b/src/http/routing.rs index 7c92752..3462c2b 100644 --- a/src/http/routing.rs +++ b/src/http/routing.rs @@ -249,6 +249,11 @@ pub enum Bucket { UsersMeChannels, /// Route: /// ```text + /// /users/@me/guilds + /// ``` + UsersMeGuilds, + /// Route: + /// ```text /// /users/@me/guilds/{guild.id} /// ``` UsersMeGuildsId(GuildId), @@ -469,6 +474,7 @@ pub enum Route<'a> { channel_id: ChannelId, }, GetCurrentUser, + GetCurrentUserGuilds, GetEmoji { guild_id: GuildId, emoji_id: EmojiId, @@ -652,6 +658,7 @@ impl<'a> Route<'a> { GetChannels { .. } => Method::Get, GetChannelWebhooks { .. } => Method::Get, GetCurrentUser => Method::Get, + GetCurrentUserGuilds => Method::Get, GetEmoji { .. } => Method::Get, GetGateway => Method::Get, GetGuild { .. } => Method::Get, @@ -816,6 +823,8 @@ impl<'a> Route<'a> { GetCurrentUser | EditCurrentUser | GetUser { .. } => Bucket::UsersId, + GetCurrentUserGuilds => Bucket::UsersMeGuilds, + LeaveGuild { guild_id } => Bucket::UsersMeGuildsId(guild_id), CreatePrivateChannel => Bucket::UsersMeChannels, @@ -1131,6 +1140,8 @@ impl<'a> Route<'a> { GetCurrentUser | EditCurrentUser => Cow::from(api!("/users/@me")), + GetCurrentUserGuilds => Cow::from(api!("/users/@me/guilds")), + GetUser { user_id } => Cow::from(api!("/users/{}", user_id)), LeaveGuild { guild_id } => Cow::from(api!("/user/@me/guilds/{}", guild_id)), From 079758d5044c8256b8ebc846d627121940cba6c7 Mon Sep 17 00:00:00 2001 From: James Whaley Date: Sun, 12 Jan 2020 15:36:55 +0000 Subject: [PATCH 29/44] Start work on Guild models --- src/model/guild/mod.rs | 72 ++++++++++++++++++++++++++++++++++++++++++ src/model/mod.rs | 1 + src/model/utils.rs | 5 +++ src/model/voice.rs | 62 ++++++++++++++++++++++++++++++++++++ 4 files changed, 140 insertions(+) create mode 100644 src/model/voice.rs diff --git a/src/model/guild/mod.rs b/src/model/guild/mod.rs index 0551205..84c7a55 100644 --- a/src/model/guild/mod.rs +++ b/src/model/guild/mod.rs @@ -5,7 +5,79 @@ mod emoji; mod member; mod role; +use serde::{Deserialize, Serialize}; + +use crate::model::id::{ChannelId, GuildId, UserId}; +use crate::model::utils::is_false; +use crate::model::voice::VoiceRegionId; + pub use self::audit_log::AuditLogEvent; pub use self::emoji::{Emoji, PartialEmoji}; pub use self::member::{Member, PartialMember}; pub use self::role::Role; +use crate::model::permissions::Permissions; + +/// The required level of criteria a user must meet, prior to being able to send +/// messages in a [`Guild`]. +/// +/// [`Guild`}: struct.Guild.html +#[non_exhaustive] +#[int_enum::int_enum(u8)] +#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub enum VerificationLevel { + /// Does not require any verification. + None = 0, + /// Must have a verified email on the user's Discord account. + Low = 1, + /// Must also be a registered user on Discord for longer than 5 minutes. + Medium = 2, + /// Must also be a member of the guild for longer than 10 minutes. + High = 3, + /// Must have a verified phone on the user's Discord account. + Higher = 4, +} + +/// A guild with partial information. +#[non_exhaustive] +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct PartialGuild { + /// The ID of the guild. + pub id: GuildId, + /// The name of the guild. + pub name: String, + /// The hash of the guild icon. + pub icon: Option, + /// Whether the client user is the owner of the guild. + pub owner: bool, + /// The set of permissions for the client user in the guild (excluding + /// channel overrides). + pub permissions: Option, +} + +/// A guild in Discord represents an isolated collection of users and channels, +/// and are often referred to as "servers" in the UI. +#[non_exhaustive] +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct Guild { + #[serde(flatten)] + guild: PartialGuild, + /// The hash of the guild splash. + pub splash: Option, + /// The ID of the owner of the guild. + pub owner_id: UserId, + /// The ID of the guild voice region. + pub region: VoiceRegionId, + /// The ID of the AFK channel. + pub afk_channel_id: Option, + /// The AFK timeout in seconds. + pub afk_timeout: u64, + /// Whether the guild is embeddable (eg. widget). + #[serde(default, skip_serializing_if = "is_false")] + pub embed_enabled: bool, + /// The ID of the channel that the embed widget will generate in invite to. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub embed_channel_id: Option, + /// The verification level required for the guild. + pub verification_level: VerificationLevel, +} +wrap!(Guild => mut guild: PartialGuild); diff --git a/src/model/mod.rs b/src/model/mod.rs index 33b3b41..d283be7 100644 --- a/src/model/mod.rs +++ b/src/model/mod.rs @@ -9,3 +9,4 @@ pub mod id; pub mod permissions; pub mod snowflake; pub mod user; +pub mod voice; diff --git a/src/model/utils.rs b/src/model/utils.rs index 121cb6e..7dceccc 100644 --- a/src/model/utils.rs +++ b/src/model/utils.rs @@ -1,3 +1,8 @@ +/// Used in serde `skip_serializing_if` attribute. +pub fn is_false(b: &bool) -> bool { + !b +} + macro_rules! int_visitor { ($vis:vis $name:ident : $type:ty) => { #[derive(Debug)] diff --git a/src/model/voice.rs b/src/model/voice.rs new file mode 100644 index 0000000..a75bdc1 --- /dev/null +++ b/src/model/voice.rs @@ -0,0 +1,62 @@ +//! Models relating voice information. + +use std::borrow::Cow; +use std::fmt::{self, Display}; +use std::ops::Deref; + +use serde::{Deserialize, Serialize}; + +/// The ID of a [`VoiceRegion`]. +/// +/// [`VoiceRegion`]: struct.VoiceRegion.html +#[derive(Clone, Debug, Eq, Hash, PartialEq, Deserialize, Serialize)] +#[serde(transparent)] +pub struct VoiceRegionId(pub(crate) Cow<'static, str>); + +impl Display for VoiceRegionId { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.0.fmt(f) + } +} + +impl Deref for VoiceRegionId { + type Target = str; + + fn deref(&self) -> &Self::Target { + self.0.deref() + } +} + +impl AsRef for VoiceRegionId { + fn as_ref(&self) -> &str { + self.0.as_ref() + } +} + +impl From for String { + fn from(id: VoiceRegionId) -> Self { + id.0.to_string() + } +} + +/// A voice region that can be used. +#[non_exhaustive] +#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)] +pub struct VoiceRegion { + /// The unique ID for the region. + pub id: VoiceRegionId, + /// The name of the region. + pub name: String, + /// Whether the region is VIP-only. + pub vip: bool, + /// Whether the region is the optimal region for the client user. + /// + /// This is defined as the region closest to the client user. + pub optimal: bool, + /// Whether the region is a deprecated voice region. + /// + /// Deprecated regions should be avoided. + pub deprecated: bool, + /// Whether the region is a custom voice region (used for events, etc.). + pub custom: bool, +} From 7e26bbebb1c405abddfd872a6b31431d7ee651ce Mon Sep 17 00:00:00 2001 From: James Whaley Date: Mon, 13 Jan 2020 17:19:28 +0000 Subject: [PATCH 30/44] Add ToSnowflake and ToSnowflakeId traits Clean up Snowflake related code. Add serde_id_map module for generic conversion between Vec and HashMap. --- src/internal/macros.rs | 45 +++--- src/model/channel/dm_channel.rs | 27 +++- src/model/channel/group.rs | 4 +- src/model/channel/mod.rs | 3 - src/model/channel/permissions.rs | 31 +++- src/model/channel/utils.rs | 82 ----------- src/model/guild/emoji/mod.rs | 11 +- src/model/guild/emoji/partial.rs | 179 +++++++++++++---------- src/model/guild/mod.rs | 35 ++++- src/model/id.rs | 244 ++++++++++++++++++------------- src/model/snowflake.rs | 23 +++ src/model/utils.rs | 81 +++++++++- 12 files changed, 467 insertions(+), 298 deletions(-) delete mode 100644 src/model/channel/utils.rs diff --git a/src/internal/macros.rs b/src/internal/macros.rs index bc6e408..60918e8 100644 --- a/src/internal/macros.rs +++ b/src/internal/macros.rs @@ -19,16 +19,12 @@ macro_rules! pkg_repo { } #[doc(hidden)] -macro_rules! api_base { +macro_rules! __api { () => { "https://discordapp.com/api/v6" }; -} - -#[doc(hidden)] -macro_rules! __api { (@s $s:expr) => { - concat!(api_base!(), $s) + concat!(__api!(), $s) }; (@s $s:expr; @a $($arg:expr),*) => { format!(__api!(@s $s), $($arg),*) @@ -61,6 +57,9 @@ macro_rules! __api { } macro_rules! api { + () => { + __api!() + }; ($s:expr) => { __api!(@s $s) }; @@ -114,24 +113,37 @@ macro_rules! wrap { }; } +macro_rules! impl_to_snowflake { + ($T:ident: |$_self:ident| $($map:tt)*) => { + impl $crate::model::snowflake::ToSnowflake for $T { + fn snowflake(&self) -> $crate::model::snowflake::Snowflake { + match self { + $_self => { $($map)* } + } + } + } + + impl $crate::model::snowflake::private::Sealed for $T {} + }; +} + #[cfg(test)] mod tests { const ID: u64 = 80351110224678912; #[test] fn test_basic() { - assert_eq!(api_base!(), api!("")); - assert_eq!(concat!(api_base!(), "/guilds"), api!("/guilds")); + assert_eq!(concat!(api!(), "/guilds"), api!("/guilds")); } #[test] fn test_arg() { assert_eq!( - format!("{}/guilds/{}/audit-logs", api_base!(), ID), + format!("{}/guilds/{}/audit-logs", api!(), ID), api!("/guilds/{}/audit-logs", ID) ); assert_eq!( - format!("{}/guilds/{}/audit-logs", api_base!(), ID), + format!("{}/guilds/{}/audit-logs", api!(), ID), api!("/guilds/{}/audit-logs", ID; []) ); } @@ -139,12 +151,7 @@ mod tests { #[test] fn test_query_basic() { let user_id: u64 = 123; - let url = format!( - "{}/guilds/{}/audit-logs?&user_id={}", - api_base!(), - ID, - user_id - ); + let url = format!("{}/guilds/{}/audit-logs?&user_id={}", api!(), ID, user_id); assert_eq!( url, @@ -157,7 +164,7 @@ mod tests { #[test] fn test_query_none() { let user_id: Option = None; - let url = format!("{}/guilds/{}/audit-logs?", api_base!(), ID); + let url = format!("{}/guilds/{}/audit-logs?", api!(), ID); assert_eq!( url, @@ -172,7 +179,7 @@ mod tests { let user_id: Option = Some(456); let url = format!( "{}/guilds/{}/audit-logs?&user_id={}", - api_base!(), + api!(), ID, user_id.unwrap() ); @@ -194,7 +201,7 @@ mod tests { let url = format!( "{}/guilds/{}/audit-logs?&user_id={}&limit={}&action_type={}", - api_base!(), + api!(), ID, user_id, limit, diff --git a/src/model/channel/dm_channel.rs b/src/model/channel/dm_channel.rs index 5477e36..0aaf964 100644 --- a/src/model/channel/dm_channel.rs +++ b/src/model/channel/dm_channel.rs @@ -1,7 +1,6 @@ use chrono::{DateTime, FixedOffset}; use serde::{Deserialize, Serialize}; -use crate::model::channel::utils::serde_recipient; use crate::model::channel::ChannelType; use crate::model::id::{ChannelId, MessageId}; use crate::model::user::User; @@ -31,6 +30,32 @@ pub struct DMChannel { pub last_pin_timestamp: Option>, } +/// Serde mapping functions between `User` sequence (with a known single entry) +/// and `User`. +mod serde_recipient { + use serde::ser::SerializeTuple; + use serde::{Deserialize, Deserializer, Serializer}; + + use crate::model::user::User; + + pub fn deserialize<'de, D>(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let [recipient] = <[User; 1]>::deserialize(deserializer)?; + Ok(recipient) + } + + pub fn serialize(recipient: &User, serializer: S) -> Result + where + S: Serializer, + { + let mut tuple = serializer.serialize_tuple(1)?; + tuple.serialize_element(recipient)?; + tuple.end() + } +} + impl_eq_fields!(DMChannel: [id, kind, recipient, last_message_id, last_pin_timestamp]); #[cfg(test)] diff --git a/src/model/channel/group.rs b/src/model/channel/group.rs index d5ebd18..72fb889 100644 --- a/src/model/channel/group.rs +++ b/src/model/channel/group.rs @@ -3,10 +3,10 @@ use std::collections::HashMap; use chrono::{DateTime, FixedOffset}; use serde::{Deserialize, Serialize}; -use crate::model::channel::utils::serde_recipients; use crate::model::channel::ChannelType; use crate::model::id::{ApplicationId, ChannelId, MessageId, UserId}; use crate::model::user::User; +use crate::model::utils::serde_id_map; /// A group message channel between multiple [`User`]s. /// @@ -28,7 +28,7 @@ pub struct Group { /// The group icon hash. pub icon: Option, /// The users in the group. - #[serde(default, with = "serde_recipients")] + #[serde(default, with = "serde_id_map")] pub recipients: HashMap, /// The ID of the group creator. pub owner_id: UserId, diff --git a/src/model/channel/mod.rs b/src/model/channel/mod.rs index 7c189c4..1e9acfd 100644 --- a/src/model/channel/mod.rs +++ b/src/model/channel/mod.rs @@ -1,8 +1,5 @@ //! Models related to channels. -// Internal utility module. -mod utils; - mod dm_channel; mod group; diff --git a/src/model/channel/permissions.rs b/src/model/channel/permissions.rs index 0be025e..4435730 100644 --- a/src/model/channel/permissions.rs +++ b/src/model/channel/permissions.rs @@ -4,8 +4,9 @@ use std::fmt::{self, Display}; use serde::{Deserialize, Serialize}; -use crate::model::id::{RoleId, UserId}; +use crate::model::id::{RoleId, ToSnowflakeId, UserId}; use crate::model::permissions::Permissions; +use crate::model::snowflake::{Snowflake, ToSnowflake}; /// The ID of a [`PermissionOverwrite`]. /// @@ -80,6 +81,34 @@ impl PermissionOverwrite { } } +impl crate::model::id::private::Sealed for PermissionOverwrite {} + +impl ToSnowflakeId for PermissionOverwrite { + type Id = OverwriteId; + + fn id(&self) -> Self::Id { + self.id + } +} + +impl crate::model::snowflake::private::Sealed for OverwriteId {} +impl crate::model::snowflake::private::Sealed for PermissionOverwrite {} + +impl ToSnowflake for OverwriteId { + fn snowflake(&self) -> Snowflake { + match self { + OverwriteId::Role(id) => id.snowflake(), + OverwriteId::User(id) => id.snowflake(), + } + } +} + +impl ToSnowflake for PermissionOverwrite { + fn snowflake(&self) -> Snowflake { + self.id.snowflake() + } +} + #[cfg(test)] mod tests { use serde_json::json; diff --git a/src/model/channel/utils.rs b/src/model/channel/utils.rs deleted file mode 100644 index cb28ed5..0000000 --- a/src/model/channel/utils.rs +++ /dev/null @@ -1,82 +0,0 @@ -/// Serde mapping functions between `User` sequence (with a known single entry) -/// and `User`. -pub mod serde_recipient { - use serde::ser::SerializeTuple; - use serde::{Deserialize, Deserializer, Serializer}; - - use crate::model::user::User; - - pub fn deserialize<'de, D>(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - let [recipient] = <[User; 1]>::deserialize(deserializer)?; - Ok(recipient) - } - - pub fn serialize(recipient: &User, serializer: S) -> Result - where - S: Serializer, - { - let mut tuple = serializer.serialize_tuple(1)?; - tuple.serialize_element(recipient)?; - tuple.end() - } -} - -/// Serde mapping functions between `User` sequence and `HashMap`. -pub mod serde_recipients { - use std::collections::HashMap; - use std::fmt; - - use serde::de; - use serde::{Deserializer, Serializer}; - - use crate::model::id::UserId; - use crate::model::user::User; - - pub fn deserialize<'de, D>(deserializer: D) -> Result, D::Error> - where - D: Deserializer<'de>, - { - struct Visitor; - - impl<'de> de::Visitor<'de> for Visitor { - type Value = HashMap; - - fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.write_str("a sequence of users") - } - - fn visit_seq(self, mut seq: A) -> Result - where - A: de::SeqAccess<'de>, - { - let mut map = HashMap::with_capacity(seq.size_hint().unwrap_or_default()); - - while let Some(user) = seq.next_element::()? { - if let Some(existing) = map.insert(user.id, user) { - return Err(de::Error::custom(format_args!( - "duplicate recipient user: {}", - existing.id - ))); - } - } - - Ok(map) - } - } - - deserializer.deserialize_seq(Visitor) - } - - pub fn serialize( - recipients: &HashMap, - serializer: S, - ) -> Result - where - S: Serializer, - { - serializer.collect_seq(recipients.values()) - } -} diff --git a/src/model/guild/emoji/mod.rs b/src/model/guild/emoji/mod.rs index bfa706e..28f6787 100644 --- a/src/model/guild/emoji/mod.rs +++ b/src/model/guild/emoji/mod.rs @@ -7,15 +7,15 @@ use serde::{Deserialize, Serialize}; use crate::model::id::RoleId; use crate::model::user::User; -pub use self::partial::PartialEmoji; +pub use self::partial::{CustomEmoji, PartialEmoji}; -/// An emoji. +/// A guild emoji. #[non_exhaustive] #[derive(Clone, Debug, Deserialize, Serialize)] pub struct Emoji { /// The ID of the emoji. #[serde(flatten)] - pub emoji: PartialEmoji, + emoji: CustomEmoji, /// A set of roles the emoji is whitelisted to. #[serde(default)] pub roles: Vec, @@ -28,6 +28,7 @@ pub struct Emoji { #[serde(default)] pub managed: bool, } +wrap!(Emoji => mut emoji: CustomEmoji); impl Display for Emoji { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { @@ -63,7 +64,7 @@ mod tests { "animated": false, }); let emoji = Emoji { - emoji: PartialEmoji::custom(41771983429993937, "LUL", false), + emoji: CustomEmoji::new(41771983429993937, "LUL", false), roles: vec![ RoleId::from(41771983429993000), RoleId::from(41771983429993111), @@ -103,7 +104,7 @@ mod tests { "animated": true, }); let emoji = Emoji { - emoji: PartialEmoji::custom(41771983429993937, "LUL", true), + emoji: CustomEmoji::new(41771983429993937, "LUL", true), roles: vec![ RoleId::from(41771983429993000), RoleId::from(41771983429993111), diff --git a/src/model/guild/emoji/partial.rs b/src/model/guild/emoji/partial.rs index a0898a3..64febc2 100644 --- a/src/model/guild/emoji/partial.rs +++ b/src/model/guild/emoji/partial.rs @@ -1,3 +1,4 @@ +use std::borrow::{Borrow, Cow}; use std::fmt::{self, Display}; use std::hash::{Hash, Hasher}; use std::str::FromStr; @@ -7,32 +8,87 @@ use serde::{Deserialize, Deserializer, Serialize, Serializer}; use crate::model::id::EmojiId; +/// A custom guild emoji with partial information. +#[non_exhaustive] +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct CustomEmoji { + /// The ID of the custom emoji. + pub id: EmojiId, + /// The name of the custom emoji. + /// + /// # Notes + /// + /// In `MESSAGE_REACTION_ADD` and `MESSAGE_REACTION_REMOVE` gateway + /// events `name` may be `None` when custom emoji data is not + /// available (for example, if it was deleted from the guild). + pub name: Option>, + /// Whether the custom emoji is animated. + pub animated: bool, +} + +impl CustomEmoji { + /// Creates a custom guild emoji. + pub fn new(id: I, name: S, animated: bool) -> CustomEmoji + where + I: Into, + S: Into>, + { + CustomEmoji { + id: id.into(), + name: Some(name.into()), + animated, + } + } +} + +impl PartialEq for CustomEmoji { + fn eq(&self, other: &Self) -> bool { + self.id == other.id + } +} + +impl Eq for CustomEmoji {} + +impl Hash for CustomEmoji { + fn hash(&self, state: &mut H) { + self.id.hash(state); + } +} + +impl Display for CustomEmoji { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + /// Rendering a custom emoji doesn't require knowing the name, however + /// if no name is provided the emoji will not render. + /// + /// Work around this issue by substituting unknown names with `_`. + const UNKNOWN_NAME: &str = "_"; + + if self.animated { + f.write_str("", name, self.id) + } +} + /// An emoji, with partial information. -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Eq, PartialEq)] pub enum PartialEmoji { /// A standard unicode emoji. - Standard(String), + Standard(Cow<'static, str>), /// A custom guild emoji. - #[non_exhaustive] - Custom { - /// The ID of the custom emoji. - id: EmojiId, - /// The name of the custom emoji. - /// - /// # Notes - /// - /// In `MESSAGE_REACTION_ADD` and `MESSAGE_REACTION_REMOVE` gateway - /// events `name` may be `None` when custom emoji data is not - /// available (for example, if it was deleted from the guild). - name: Option, - /// Whether the custom emoji is animated. - animated: bool, - }, + Custom(CustomEmoji), } impl PartialEmoji { /// Creates a standard unicode emoji. - pub fn standard>(emoji: S) -> PartialEmoji { + pub fn standard(emoji: S) -> PartialEmoji + where + S: Into>, + { PartialEmoji::Standard(emoji.into()) } @@ -40,20 +96,16 @@ impl PartialEmoji { pub fn custom(id: I, name: S, animated: bool) -> PartialEmoji where I: Into, - S: Into, + S: Into>, { - PartialEmoji::Custom { - id: id.into(), - name: Some(name.into()), - animated, - } + PartialEmoji::Custom(CustomEmoji::new(id, name, animated)) } /// Returns the ID of the emoji. pub fn id(&self) -> Option { - match *self { + match self { PartialEmoji::Standard(_) => None, - PartialEmoji::Custom { id, .. } => Some(id), + PartialEmoji::Custom(emoji) => Some(emoji.id), } } @@ -64,82 +116,58 @@ impl PartialEmoji { pub fn name(&self) -> Option<&str> { match self { PartialEmoji::Standard(name) => Some(name), - PartialEmoji::Custom { name, .. } => name.as_deref(), + PartialEmoji::Custom(emoji) => emoji.name.as_deref(), } } /// Returns whether the emoji is animated. pub fn animated(&self) -> bool { - match *self { + match self { PartialEmoji::Standard(_) => false, - PartialEmoji::Custom { animated, .. } => animated, + PartialEmoji::Custom(emoji) => emoji.animated, } } } impl Display for PartialEmoji { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - /// Rendering a custom emoji doesn't require knowing the name, however - /// if no name is provided the emoji will not render. - /// - /// Work around this issue by substituting unknown names with `_`. - const UNKNOWN_NAME: &str = "_"; - match self { PartialEmoji::Standard(name) => f.write_str(name), - PartialEmoji::Custom { id, name, animated } => { - if *animated { - f.write_str("", name.as_deref().unwrap_or(UNKNOWN_NAME), id) - } - } - } -} - -impl PartialEq for PartialEmoji { - fn eq(&self, other: &Self) -> bool { - match self { - PartialEmoji::Standard(name) => match other { - PartialEmoji::Standard(other_name) => name == other_name, - PartialEmoji::Custom { .. } => false, - }, - PartialEmoji::Custom { id, .. } => match other { - PartialEmoji::Standard(_) => false, - PartialEmoji::Custom { id: other_id, .. } => id == other_id, - }, + PartialEmoji::Custom(emoji) => emoji.fmt(f), } } } -impl Eq for PartialEmoji {} - impl Hash for PartialEmoji { fn hash(&self, state: &mut H) { match self { PartialEmoji::Standard(name) => name.hash(state), - PartialEmoji::Custom { id, .. } => id.hash(state), + PartialEmoji::Custom(emoji) => emoji.hash(state), } } } +impl From for PartialEmoji { + fn from(emoji: CustomEmoji) -> Self { + PartialEmoji::Custom(emoji) + } +} + impl From for PartialEmoji { fn from(ch: char) -> Self { - PartialEmoji::Standard(ch.to_string()) + PartialEmoji::standard(ch.to_string()) } } impl From for PartialEmoji { fn from(s: String) -> Self { - PartialEmoji::Standard(s) + PartialEmoji::standard(s) } } -impl<'a> From<&'a str> for PartialEmoji { - fn from(s: &'a str) -> Self { - PartialEmoji::Standard(s.to_owned()) +impl From<&'static str> for PartialEmoji { + fn from(s: &'static str) -> Self { + PartialEmoji::standard(s) } } @@ -147,7 +175,7 @@ impl FromStr for PartialEmoji { type Err = std::convert::Infallible; fn from_str(s: &str) -> Result { - Ok(PartialEmoji::from(s)) + Ok(PartialEmoji::standard(s.to_owned())) } } @@ -163,19 +191,15 @@ impl Serialize for PartialEmoji { PartialEmoji::Custom { .. } => 3, }; - let mut map = serializer.serialize_map(Some(len))?; match self { PartialEmoji::Standard(name) => { + let mut map = serializer.serialize_map(Some(len))?; map.serialize_entry("id", &None::)?; map.serialize_entry("name", name)?; + map.end() } - PartialEmoji::Custom { id, name, animated } => { - map.serialize_entry("id", id)?; - map.serialize_entry("name", name)?; - map.serialize_entry("animated", animated)?; - } - }; - map.end() + PartialEmoji::Custom(emoji) => emoji.serialize(serializer), + } } } @@ -234,12 +258,13 @@ impl<'de> Deserialize<'de> for PartialEmoji { Ok(match id { Some(id) => { + let name = name.map(Cow::Owned); let animated = animated.unwrap_or_default(); - PartialEmoji::Custom { id, name, animated } + PartialEmoji::Custom(CustomEmoji { id, name, animated }) } None => { let name = name.ok_or(de::Error::missing_field("name"))?; - PartialEmoji::Standard(name) + PartialEmoji::standard(name) } }) } diff --git a/src/model/guild/mod.rs b/src/model/guild/mod.rs index 84c7a55..0f831a2 100644 --- a/src/model/guild/mod.rs +++ b/src/model/guild/mod.rs @@ -12,7 +12,7 @@ use crate::model::utils::is_false; use crate::model::voice::VoiceRegionId; pub use self::audit_log::AuditLogEvent; -pub use self::emoji::{Emoji, PartialEmoji}; +pub use self::emoji::{CustomEmoji, Emoji, PartialEmoji}; pub use self::member::{Member, PartialMember}; pub use self::role::Role; use crate::model::permissions::Permissions; @@ -37,6 +37,32 @@ pub enum VerificationLevel { Higher = 4, } +/// The default level of message notifications in a guild. +#[non_exhaustive] +#[int_enum::int_enum(u8)] +#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub enum MessageNotificationLevel { + /// All messages will send notifications. + AllMessages = 0, + /// Only messages that mention a user or a user's role will send + /// notifications. + OnlyMentions = 1, +} + +/// The level of filter to apply to users that send messages containing explicit +/// content. +#[non_exhaustive] +#[int_enum::int_enum(u8)] +#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub enum ExplicitContentFilterLevel { + /// No filter will be applied. + Disabled = 0, + /// Only members with roles will be able to send explicit content. + MembersWithoutRoles = 1, + /// All members will have explicit content filtered from messages they send. + AllMembers = 2, +} + /// A guild with partial information. #[non_exhaustive] #[derive(Clone, Debug, Deserialize, Serialize)] @@ -48,9 +74,11 @@ pub struct PartialGuild { /// The hash of the guild icon. pub icon: Option, /// Whether the client user is the owner of the guild. + #[serde(default, skip_serializing_if = "is_false")] pub owner: bool, /// The set of permissions for the client user in the guild (excluding /// channel overrides). + #[serde(default, skip_serializing_if = "Option::is_none")] pub permissions: Option, } @@ -79,5 +107,10 @@ pub struct Guild { pub embed_channel_id: Option, /// The verification level required for the guild. pub verification_level: VerificationLevel, + /// The default message notification level in the guild. + #[serde(rename = "default_message_notifications")] + pub message_notifications: MessageNotificationLevel, + /// The level at which explicit content will be filtered. + pub explicit_content_filter: ExplicitContentFilterLevel, } wrap!(Guild => mut guild: PartialGuild); diff --git a/src/model/id.rs b/src/model/id.rs index 65b59ba..e64a93b 100644 --- a/src/model/id.rs +++ b/src/model/id.rs @@ -1,130 +1,170 @@ //! Strongly-typed snowflake IDs. -use std::fmt::{self, Display}; +use std::fmt::{self, Debug, Display}; +use std::hash::Hash; use std::ops::Deref; use serde::{Deserialize, Serialize}; -use crate::model::snowflake::Snowflake; - -/// The ID of an [`Application`]. -/// -/// [`Application`]: TODO -#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Deserialize, Serialize)] -pub struct ApplicationId(Snowflake); - -/// The ID of an [`Attachment`]. -/// -/// [`Attachment`]: ../channel/struct.Attachment.html -#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Deserialize, Serialize)] -pub struct AttachmentId(Snowflake); - -/// The ID of an [`AuditLogEntry`]. -/// -/// [`AuditLogEntry`]: TODO -#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Deserialize, Serialize)] -pub struct AuditLogEntryId(Snowflake); - -/// The ID of a [`Channel`]. -/// -/// [`Channel`]: TODO -#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Deserialize, Serialize)] -pub struct ChannelId(Snowflake); - -/// The ID of an [`Emoji`]. -/// -/// [`Emoji`]: ../guild/struct.Emoji.html -#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Deserialize, Serialize)] -pub struct EmojiId(Snowflake); - -/// The ID of a [`Guild`]. -/// -/// [`Guild`]: TODO -#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Deserialize, Serialize)] -pub struct GuildId(Snowflake); - -/// The ID of an [`Integration`]. -/// -/// [`Integration`]: TODO -#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Deserialize, Serialize)] -pub struct IntegrationId(Snowflake); - -/// The ID of a [`Message`]. -/// -/// [`Message`]: ../channel/struct.Message.html -#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Deserialize, Serialize)] -pub struct MessageId(Snowflake); - -/// The ID of a [`Role`]. -/// -/// [`Role`]: TODO -#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Deserialize, Serialize)] -pub struct RoleId(Snowflake); - -/// The ID of a [`User`]. -/// -/// [`User`]: ../user/struct.User.html -#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Deserialize, Serialize)] -pub struct UserId(Snowflake); - -/// The ID of a [`Webhook`]. -/// -/// [`Webhook`]: TODO -#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Deserialize, Serialize)] -pub struct WebhookId(Snowflake); - -macro_rules! impl_id { - ($($name:ident,)*) => {$( - impl From for $name { +use crate::model::channel::message::Attachment; +use crate::model::channel::Message; +use crate::model::guild::{CustomEmoji, Emoji, Guild, PartialGuild, Role}; +use crate::model::snowflake::{Snowflake, ToSnowflake}; +use crate::model::user::{ClientUser, User}; + +macro_rules! id_type { + ($( + $(#[$attr:meta])* + $Id:ident; + )*) => {$( + $(#[$attr])* + #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Deserialize, Serialize)] + pub struct $Id(Snowflake); + + impl Display for $Id { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + Display::fmt(&self.0, f) + } + } + + impl Deref for $Id { + type Target = Snowflake; + + fn deref(&self) -> &Self::Target { + &self.0 + } + } + + impl AsRef for $Id { + fn as_ref(&self) -> &Snowflake { + &self.0 + } + } + + impl From for $Id { fn from(n: u64) -> Self { Self(Snowflake::from(n)) } } - impl From for $name { + impl From for $Id { fn from(snowflake: Snowflake) -> Self { Self(snowflake) } } - impl From<$name> for Snowflake { - fn from(id: $name) -> Self { + impl From<$Id> for Snowflake { + fn from(id: $Id) -> Self { id.0 } } - impl Deref for $name { - type Target = Snowflake; + impl_to_snowflake!($Id: |id| id.0); + )*}; +} - fn deref(&self) -> &Self::Target { - &self.0 - } - } +id_type! { + /// The ID of an [`Application`]. + /// + /// [`Application`]: TODO + ApplicationId; + + /// The ID of an [`Attachment`]. + /// + /// [`Attachment`]: ../channel/struct.Attachment.html + AttachmentId; + + /// The ID of an [`AuditLogEntry`]. + /// + /// [`AuditLogEntry`]: TODO + AuditLogEntryId; + + /// The ID of a [`Channel`]. + /// + /// [`Channel`]: ../channel/enum.Channel.html + ChannelId; + + /// The ID of an [`Emoji`]. + /// + /// [`Emoji`]: ../guild/struct.Emoji.html + EmojiId; + + /// The ID of a [`Guild`]. + /// + /// [`Guild`]: ../guild/struct.Guild.html + GuildId; + + /// The ID of an [`Integration`]. + /// + /// [`Integration`]: TODO + IntegrationId; + + /// The ID of a [`Message`]. + /// + /// [`Message`]: ../channel/struct.Message.html + MessageId; + + /// The ID of a [`Role`]. + /// + /// [`Role`]: ../guild/struct.Role.html + RoleId; + + /// The ID of a [`User`]. + /// + /// [`User`]: ../user/struct.User.html + UserId; + + /// The ID of a [`Webhook`]. + /// + /// [`Webhook`]: TODO + WebhookId; +} - impl AsRef for $name { - fn as_ref(&self) -> &Snowflake { - &self.0 - } - } +pub(crate) mod private { + pub trait Sealed {} +} - impl Display for $name { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - Display::fmt(&self.0, f) +/// A trait that have a strongly-typed Snowflake ID. +pub trait ToSnowflakeId: private::Sealed { + /// The strongly-typed Snowflake ID type. + type Id: ToSnowflake + Copy + Debug + Display + Eq + Hash; + + /// Returns the Snowflake ID. + fn id(&self) -> Self::Id; +} + +macro_rules! impl_to_id { + ($Parent:ident => $field:ident: $Id:ident) => { + impl private::Sealed for $Parent {} + + impl ToSnowflakeId for $Parent { + type Id = $Id; + + fn id(&self) -> Self::Id { + self.$field } } - )*} + + impl_to_snowflake!($Parent: |parent| parent.id().snowflake()); + }; + ($( + $Parent:ident => $field:ident: $Id:ident; + )*) => {$( + impl_to_id!($Parent => $field: $Id); + )*}; } -impl_id! { - ApplicationId, - AttachmentId, - AuditLogEntryId, - ChannelId, - EmojiId, - GuildId, - IntegrationId, - MessageId, - RoleId, - UserId, - WebhookId, +impl_to_id! { + Attachment => id: AttachmentId; + Emoji => id: EmojiId; + CustomEmoji => id: EmojiId; + Guild => id: GuildId; + PartialGuild => id: GuildId; + Message => id: MessageId; + Role => id: RoleId; + User => id: UserId; + ClientUser => id: UserId; } + +// TODO: Implement ToSnowflakeId for Channel. +// TODO: Implement ToSnowflakeId for other types. diff --git a/src/model/snowflake.rs b/src/model/snowflake.rs index 5e609f3..df25086 100644 --- a/src/model/snowflake.rs +++ b/src/model/snowflake.rs @@ -17,6 +17,29 @@ use crate::model::utils::U64Visitor; /// Discord epoch is the first second of 2015. const DISCORD_EPOCH: u64 = 1_420_070_400_000; +pub(crate) mod private { + pub trait Sealed {} + + impl Sealed for super::Snowflake {} +} + +/// A trait for types that have a Snowflake ID. +pub trait ToSnowflake: private::Sealed { + /// Returns the Snowflake ID. + fn snowflake(&self) -> Snowflake; + + /// Gets the timestamp that the snowflake was created at. + fn created_at(&self) -> DateTime { + self.snowflake().created_at() + } +} + +impl ToSnowflake for Snowflake { + fn snowflake(&self) -> Snowflake { + *self + } +} + /// A [`Snowflake`] is a 64 bit unique ID. /// /// [`Snowflake`]: https://discordapp.com/developers/docs/reference#snowflakes diff --git a/src/model/utils.rs b/src/model/utils.rs index 7dceccc..12ffce1 100644 --- a/src/model/utils.rs +++ b/src/model/utils.rs @@ -4,9 +4,9 @@ pub fn is_false(b: &bool) -> bool { } macro_rules! int_visitor { - ($vis:vis $name:ident : $type:ty) => { + (($($vis:tt)*) $name:ident: $type:ty) => { #[derive(Debug)] - $vis struct $name; + $($vis)* struct $name; impl<'de> ::serde::de::Visitor<'de> for $name { type Value = $type; @@ -37,9 +37,12 @@ macro_rules! int_visitor { } } }; - ($($vis:vis $name:ident : $type:ty ;)*) => {$( - int_visitor!($vis $name: $type); - )*} + ($(pub $name:ident: $type:ty;)*) => {$( + int_visitor!((pub) $name: $type); + )*}; + ($(pub(crate) $name:ident: $type:ty;)*) => {$( + int_visitor!((pub(crate)) $name: $type); + )*}; } int_visitor! { @@ -47,3 +50,71 @@ int_visitor! { pub U16Visitor: u16; pub U64Visitor: u64; } + +/// Serde mappings of sequences of objects with Snowflake IDs to HashMaps keyed +/// with by the Snowflake IDs. +pub mod serde_id_map { + use std::collections::HashMap; + use std::fmt; + use std::marker::PhantomData; + + use serde::{de, Deserialize, Serialize}; + use serde::{Deserializer, Serializer}; + + use crate::model::id::ToSnowflakeId; + + pub fn deserialize<'de, D, V: 'de>(deserializer: D) -> Result, D::Error> + where + D: Deserializer<'de>, + V: ToSnowflakeId + Deserialize<'de>, + { + struct Visitor<'de, V: 'de> + where + V: ToSnowflakeId + Deserialize<'de>, + { + _value: &'de PhantomData, + } + + impl<'de, V: 'de> de::Visitor<'de> for Visitor<'de, V> + where + V: ToSnowflakeId + Deserialize<'de>, + { + type Value = HashMap; + + fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_str("a sequence of objects with snowflake ids") + } + + fn visit_seq(self, mut seq: A) -> Result + where + A: de::SeqAccess<'de>, + { + let mut map: HashMap = + HashMap::with_capacity(seq.size_hint().unwrap_or_default()); + + while let Some(value) = seq.next_element::()? { + if let Some(existing) = map.insert(::id(&value), value) { + return Err(de::Error::custom(format_args!( + "duplicate snowflake id: {}", + ::id(&existing), + ))); + } + } + + Ok(map) + } + } + + deserializer.deserialize_seq(Visitor { + _value: &PhantomData, + }) + } + + pub fn serialize(map: &HashMap, serializer: S) -> Result + where + S: Serializer, + V: ToSnowflakeId + Serialize, + { + serializer.collect_seq(map.values()) + } +} From 8f2be946e12aa49099d6e7854787ecebbd3097c8 Mon Sep 17 00:00:00 2001 From: James Whaley Date: Wed, 15 Jan 2020 16:52:52 +0000 Subject: [PATCH 31/44] Add Guild and related models --- Cargo.toml | 1 + src/internal/macros.rs | 5 +- src/internal/test.rs | 15 ++ src/model/channel/group.rs | 10 +- src/model/channel/guild/mod.rs | 21 ++ src/model/channel/mod.rs | 19 ++ src/model/gateway.rs | 1 + src/model/guild/emoji/mod.rs | 1 + src/model/guild/member.rs | 16 +- src/model/guild/mod.rs | 385 ++++++++++++++++++++++++++++++++- src/model/id.rs | 17 +- src/model/mod.rs | 1 + src/model/utils.rs | 62 +++--- src/model/voice.rs | 167 ++++++++++++++ 14 files changed, 670 insertions(+), 51 deletions(-) create mode 100644 src/model/gateway.rs diff --git a/Cargo.toml b/Cargo.toml index 0082f90..35a48b3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,6 +25,7 @@ hyper = "0.13" hyper-tls = "0.4" int-enum = { version = "0.3", features = ["serialize", "convert"] } log = "0.4" +num-traits = "0.2" remain = "0.2" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" diff --git a/src/internal/macros.rs b/src/internal/macros.rs index 60918e8..c7f8931 100644 --- a/src/internal/macros.rs +++ b/src/internal/macros.rs @@ -115,6 +115,9 @@ macro_rules! wrap { macro_rules! impl_to_snowflake { ($T:ident: |$_self:ident| $($map:tt)*) => { + #[doc(hidden)] + impl $crate::model::snowflake::private::Sealed for $T {} + impl $crate::model::snowflake::ToSnowflake for $T { fn snowflake(&self) -> $crate::model::snowflake::Snowflake { match self { @@ -122,8 +125,6 @@ macro_rules! impl_to_snowflake { } } } - - impl $crate::model::snowflake::private::Sealed for $T {} }; } diff --git a/src/internal/test.rs b/src/internal/test.rs index 1603cc2..b9289bb 100644 --- a/src/internal/test.rs +++ b/src/internal/test.rs @@ -18,6 +18,21 @@ mod inner { assert_eq_fields!($left.$field, $right.$field); )* }; + (map => $left:expr, $right:expr) => { + match (&$left, &$right) { + (left_map, right_map) => { + assert_eq!(left_map.len(), right_map.len()); + for (key, left_val) in left_map.iter() { + let right_val = match right_map.get(key) { + Some(right_val) => right_val, + #[cold] + None => panic!("missing key: {}", key), + }; + assert_eq_fields!(left_val, right_val); + } + } + } + }; } macro_rules! panic_ne_fields { diff --git a/src/model/channel/group.rs b/src/model/channel/group.rs index 72fb889..f681d9b 100644 --- a/src/model/channel/group.rs +++ b/src/model/channel/group.rs @@ -55,15 +55,7 @@ impl Group { impl_eq_fields!(Group: (a, b) => { assert_eq_fields!(a, b, [id, kind, name, icon, owner_id, last_message_id, last_pin_timestamp]); - assert_eq!(a.recipients.len(), b.recipients.len()); - for (id, a_user) in a.recipients.iter() { - let b_user = match b.recipients.get(id) { - Some(user) => user, - #[cold] - None => panic!("missing user with id: {}", id), - }; - assert_eq_fields!(a_user, b_user); - } + assert_eq_fields!(map => a.recipients, b.recipients); }); #[cfg(test)] diff --git a/src/model/channel/guild/mod.rs b/src/model/channel/guild/mod.rs index 03eab6a..d91a5ad 100644 --- a/src/model/channel/guild/mod.rs +++ b/src/model/channel/guild/mod.rs @@ -16,6 +16,7 @@ pub use self::news_channel::NewsChannel; pub use self::store_channel::StoreChannel; pub use self::text_channel::TextChannel; pub use self::voice_channel::VoiceChannel; +use crate::model::id::{ChannelId, ToSnowflakeId}; /// A channel in a [`Guild`]. /// @@ -54,6 +55,26 @@ impl GuildChannel { } } +#[doc(hidden)] +impl crate::model::id::private::Sealed for GuildChannel {} + +impl ToSnowflakeId for GuildChannel { + type Id = ChannelId; + + /// The ID of the channel. + fn id(&self) -> Self::Id { + match self { + GuildChannel::Text(channel) => channel.id, + GuildChannel::Voice(channel) => channel.id, + GuildChannel::Category(channel) => channel.id, + GuildChannel::News(channel) => channel.id, + GuildChannel::Store(channel) => channel.id, + } + } +} + +impl_to_snowflake!(GuildChannel: |channel| channel.id().snowflake()); + impl GuildChannel { pub(crate) fn from_value( kind: ChannelType, diff --git a/src/model/channel/mod.rs b/src/model/channel/mod.rs index 1e9acfd..28fef3e 100644 --- a/src/model/channel/mod.rs +++ b/src/model/channel/mod.rs @@ -14,6 +14,7 @@ pub use self::dm_channel::DMChannel; pub use self::group::Group; pub use self::guild::GuildChannel; pub use self::message::Message; +use crate::model::id::{ChannelId, ToSnowflakeId}; /// The type of a channel. #[non_exhaustive] @@ -85,6 +86,24 @@ impl Channel { } } +#[doc(hidden)] +impl crate::model::id::private::Sealed for Channel {} + +impl ToSnowflakeId for Channel { + type Id = ChannelId; + + /// The ID of the channel. + fn id(&self) -> Self::Id { + match self { + Channel::DM(channel) => channel.id, + Channel::Group(channel) => channel.id, + Channel::Guild(channel) => channel.id(), + } + } +} + +impl_to_snowflake!(Channel: |channel| channel.id().snowflake()); + impl Serialize for Channel { fn serialize(&self, serializer: S) -> Result where diff --git a/src/model/gateway.rs b/src/model/gateway.rs new file mode 100644 index 0000000..65c7892 --- /dev/null +++ b/src/model/gateway.rs @@ -0,0 +1 @@ +//! Models related to the gateway. diff --git a/src/model/guild/emoji/mod.rs b/src/model/guild/emoji/mod.rs index 28f6787..ec7e2e4 100644 --- a/src/model/guild/emoji/mod.rs +++ b/src/model/guild/emoji/mod.rs @@ -20,6 +20,7 @@ pub struct Emoji { #[serde(default)] pub roles: Vec, /// The user that created the emoji. + #[serde(skip_serializing_if = "Option::is_none")] pub user: Option, /// Whether the name requires colons to be used by a client. #[serde(default)] diff --git a/src/model/guild/member.rs b/src/model/guild/member.rs index 6455c12..721d681 100644 --- a/src/model/guild/member.rs +++ b/src/model/guild/member.rs @@ -1,7 +1,7 @@ use chrono::{DateTime, FixedOffset}; use serde::{Deserialize, Serialize}; -use crate::model::id::RoleId; +use crate::model::id::{RoleId, ToSnowflakeId, UserId}; use crate::model::user::User; // TODO: Add `guild_id` field, injected by the http `Client` API. @@ -21,6 +21,20 @@ pub struct Member { } wrap!(Member => mut member: PartialMember); +#[doc(hidden)] +impl crate::model::id::private::Sealed for Member {} + +impl ToSnowflakeId for Member { + type Id = UserId; + + /// The ID of the channel. + fn id(&self) -> Self::Id { + self.user.id + } +} + +impl_to_snowflake!(Member: |member| member.id().snowflake()); + /// A member of a guild, with partial information. #[non_exhaustive] #[derive(Clone, Debug, Deserialize, Serialize)] diff --git a/src/model/guild/mod.rs b/src/model/guild/mod.rs index 0f831a2..4fe0498 100644 --- a/src/model/guild/mod.rs +++ b/src/model/guild/mod.rs @@ -5,22 +5,30 @@ mod emoji; mod member; mod role; +use std::borrow::Cow; +use std::collections::{HashMap, HashSet}; +use std::fmt::{self, Display}; +use std::ops::Deref; + +use chrono::{DateTime, FixedOffset}; +use num_traits::Zero; use serde::{Deserialize, Serialize}; -use crate::model::id::{ChannelId, GuildId, UserId}; -use crate::model::utils::is_false; -use crate::model::voice::VoiceRegionId; +use crate::model::channel::GuildChannel; +use crate::model::id::{ApplicationId, ChannelId, EmojiId, GuildId, RoleId, UserId}; +use crate::model::permissions::Permissions; +use crate::model::utils::{is_false, serde_id_map}; +use crate::model::voice::{VoiceRegionId, VoiceState}; pub use self::audit_log::AuditLogEvent; pub use self::emoji::{CustomEmoji, Emoji, PartialEmoji}; pub use self::member::{Member, PartialMember}; pub use self::role::Role; -use crate::model::permissions::Permissions; /// The required level of criteria a user must meet, prior to being able to send /// messages in a [`Guild`]. /// -/// [`Guild`}: struct.Guild.html +/// [`Guild`]: struct.Guild.html #[non_exhaustive] #[int_enum::int_enum(u8)] #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] @@ -37,6 +45,12 @@ pub enum VerificationLevel { Higher = 4, } +impl Default for VerificationLevel { + fn default() -> Self { + VerificationLevel::None + } +} + /// The default level of message notifications in a guild. #[non_exhaustive] #[int_enum::int_enum(u8)] @@ -49,6 +63,12 @@ pub enum MessageNotificationLevel { OnlyMentions = 1, } +impl Default for MessageNotificationLevel { + fn default() -> Self { + MessageNotificationLevel::AllMessages + } +} + /// The level of filter to apply to users that send messages containing explicit /// content. #[non_exhaustive] @@ -63,6 +83,171 @@ pub enum ExplicitContentFilterLevel { AllMembers = 2, } +impl Default for ExplicitContentFilterLevel { + fn default() -> Self { + ExplicitContentFilterLevel::Disabled + } +} + +/// A feature enabled for a guild. +#[non_exhaustive] +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Deserialize, Serialize)] +#[serde(rename_all = "SCREAMING_SNAKE_CASE")] +pub enum GuildFeature { + /// The guild has access to set an invite splash background. + InviteSplash, + /// The guild has to set 384kbps bitrate in voice. + VipRegions, + /// The guild has access to set a vanity URL. + VanityUrl, + /// The guild is verified. + Verified, + /// The guild is partnered. + Partnered, + /// The guild is public. + Public, + /// The guild has access to use commerce features (i.e. create store + /// channels). + Commerce, + /// The guild has access to create news channels. + News, + /// The guild is able to be discovered in the directory. + Discoverable, + /// The guild is able to be featured in the directory. + Featurable, + /// The guild has access to set an animated guild icon. + AnimatedIcon, + /// The guild has access to set a guild banner image. + Banner, +} + +/// The required level of multi-factor authentication required for a guild. +#[non_exhaustive] +#[int_enum::int_enum(u8)] +#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub enum MfaLevel { + /// Multi-factor authentication is not required. + None = 0, + /// Multi-factor authentication is required for members to take moderation + /// actions (eg. kick, ban, delete messages). + Elevated = 1, +} + +impl Default for MfaLevel { + fn default() -> Self { + MfaLevel::None + } +} + +/// The tier of premium for a guild, provided by nitro boosts. +#[non_exhaustive] +#[int_enum::int_enum(u8)] +#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub enum PremiumTier { + /// Not premium. + None = 0, + /// Tier 1. + Tier1 = 1, + /// Tier 2. + Tier2 = 2, + /// Tier 3. + Tier3 = 3, +} + +impl Default for PremiumTier { + fn default() -> Self { + PremiumTier::None + } +} + +/// A locale. +#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Deserialize, Serialize)] +#[serde(transparent)] +pub struct Locale(pub(crate) Cow<'static, str>); + +impl Locale { + /// Locale defaults to `en-US`. + pub const DEFAULT: Locale = Locale::from_static("en-US"); + + /// Creates a voice region ID from a static string. + pub const fn from_static(id: &'static str) -> Locale { + Locale(Cow::Borrowed(id)) + } + + /// Creates a voice region ID from a string. + pub fn from_string>(id: S) -> Locale { + Locale(Cow::Owned(id.into())) + } +} + +impl Default for Locale { + fn default() -> Self { + Locale::DEFAULT + } +} + +impl From<&'static str> for Locale { + fn from(s: &'static str) -> Self { + Locale(Cow::Borrowed(s)) + } +} + +impl From for Locale { + fn from(s: String) -> Self { + Locale(Cow::Owned(s)) + } +} + +impl Display for Locale { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.0.fmt(f) + } +} + +impl Deref for Locale { + type Target = str; + + fn deref(&self) -> &Self::Target { + self.0.deref() + } +} + +impl AsRef for Locale { + fn as_ref(&self) -> &str { + self.0.as_ref() + } +} + +impl From for String { + fn from(id: Locale) -> Self { + id.0.to_string() + } +} + +impl From for Cow<'static, str> { + fn from(id: Locale) -> Self { + id.0 + } +} + +impl PartialEq for Locale { + fn eq(&self, other: &str) -> bool { + self.0 == other + } +} + +impl PartialEq for Locale { + fn eq(&self, other: &String) -> bool { + self.0 == &other[..] + } +} + +impl<'a> PartialEq> for Locale { + fn eq(&self, other: &Cow<'a, str>) -> bool { + &self.0[..] == &other[..] + } +} + /// A guild with partial information. #[non_exhaustive] #[derive(Clone, Debug, Deserialize, Serialize)] @@ -77,7 +262,7 @@ pub struct PartialGuild { #[serde(default, skip_serializing_if = "is_false")] pub owner: bool, /// The set of permissions for the client user in the guild (excluding - /// channel overrides). + /// channel permission overwrites). #[serde(default, skip_serializing_if = "Option::is_none")] pub permissions: Option, } @@ -112,5 +297,193 @@ pub struct Guild { pub message_notifications: MessageNotificationLevel, /// The level at which explicit content will be filtered. pub explicit_content_filter: ExplicitContentFilterLevel, + /// The roles in the guild. + #[serde(with = "serde_id_map")] + pub roles: HashMap, + /// The roles in the guild. + #[serde(with = "serde_id_map")] + pub emojis: HashMap, + /// The features enabled for the guild. + pub features: HashSet, + /// The required MFA level for the guild. + pub mfa_level: MfaLevel, + /// The application ID of the guild creator, if the guild is bot-created. + pub application_id: Option, + /// Whether the server widget is enabled. + #[serde(default, skip_serializing_if = "is_false")] + pub widget_enabled: bool, + /// The ID of the channel for the server widget. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub widget_channel_id: Option, + /// The ID of the channel to which system messages are sent. + pub system_channel_id: Option, + /// When the guild was joined. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub joined_at: Option>, + /// Whether the guild is considered a large guild. + #[serde(default, skip_serializing_if = "is_false")] + pub large: bool, + /// Whether the guild is unavailable. + #[serde(default, skip_serializing_if = "is_false")] + pub unavailable: bool, + /// The total number of members in the guild. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub member_count: Option, + /// The states of all current voice connections in the guild. + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub voice_states: Vec, + /// The users in the guild. + #[serde(with = "serde_id_map")] + #[serde(default, skip_serializing_if = "HashMap::is_empty")] + pub members: HashMap, + /// The channels in the guild. + #[serde(with = "serde_id_map")] + #[serde(default, skip_serializing_if = "HashMap::is_empty")] + pub channels: HashMap, + // TODO: Add `presences` field. + /// The maximum amount of members for the guild. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub max_members: Option, + /// The vanity URL of the guild. + pub vanity_url_code: Option, + /// The description of the guild. + pub description: Option, + /// The hash of the guild banner. + pub banner: Option, + /// The tier of premium for the guild, provided by nitro boosts. + #[serde(default)] + pub premium_tier: PremiumTier, + /// The total number of users currently boosting the guild. + #[serde(rename = "premium_subscription_count")] + #[serde(default, skip_serializing_if = "Zero::is_zero")] + pub boost_count: u64, + /// The preferred locale of the guild. + /// + /// Defaults to `en-US` if the guild does not have the [`Discoverable`] + /// feature enabled. + /// + /// [`Discoverable`]: enum.GuildFeature.html#variant.Discoverable + #[serde(default)] + pub preferred_locale: Locale, } wrap!(Guild => mut guild: PartialGuild); + +impl_eq_fields!(PartialGuild: [id, name, icon, owner, permissions]); +impl_eq_fields!(Guild: (a, b) => { + assert_eq_fields!(a, b, [ + guild, + splash, + owner_id, + region, + afk_channel_id, + afk_timeout, + embed_enabled, + embed_channel_id, + verification_level, + message_notifications, + explicit_content_filter, + features, + mfa_level, + application_id, + widget_enabled, + widget_channel_id, + system_channel_id, + joined_at, + large, + unavailable, + member_count, + voice_states, + max_members, + vanity_url_code, + description, + banner, + premium_tier, + boost_count, + preferred_locale, + ]); + + assert_eq_fields!(map => a.roles, b.roles); + assert_eq_fields!(map => a.emojis, b.emojis); + assert_eq_fields!(map => a.members, b.members); + assert_eq_fields!(map => a.channels, b.channels); +}); + +#[cfg(test)] +mod tests { + use std::iter::FromIterator; + + use serde_json::json; + + use super::*; + + #[test] + fn test_deserialize_guild() { + let value = json!({ + "id": "41771983423143937", + "application_id": null, + "name": "Discord Developers", + "icon": "86e39f7ae3307e811784e2ffd11a7310", + "splash": null, + "owner_id": "80351110224678912", + "region": "us-east", + "afk_channel_id": "42072017402331136", + "afk_timeout": 300, + "embed_enabled": true, + "embed_channel_id": "41771983444115456", + "verification_level": 1, + "default_message_notifications": 0, + "explicit_content_filter": 0, + "mfa_level": 0, + "widget_enabled": false, + "widget_channel_id": "41771983423143937", + "roles": [], + "emojis": [], + "features": ["INVITE_SPLASH"], + "unavailable": false + }); + let guild = Guild { + guild: PartialGuild { + id: GuildId::from(41771983423143937), + name: "Discord Developers".to_owned(), + icon: Some("86e39f7ae3307e811784e2ffd11a7310".to_owned()), + owner: false, + permissions: None, + }, + splash: None, + owner_id: UserId::from(80351110224678912), + region: VoiceRegionId::US_EAST, + afk_channel_id: Some(ChannelId::from(42072017402331136)), + afk_timeout: 300, + embed_enabled: true, + embed_channel_id: Some(ChannelId::from(41771983444115456)), + verification_level: VerificationLevel::Low, + message_notifications: MessageNotificationLevel::AllMessages, + explicit_content_filter: ExplicitContentFilterLevel::Disabled, + roles: HashMap::default(), + emojis: HashMap::default(), + features: HashSet::from_iter(vec![GuildFeature::InviteSplash]), + mfa_level: MfaLevel::None, + application_id: None, + widget_enabled: false, + widget_channel_id: Some(ChannelId::from(41771983423143937)), + system_channel_id: None, + joined_at: None, + large: false, + unavailable: false, + member_count: None, + voice_states: vec![], + members: HashMap::default(), + channels: HashMap::default(), + max_members: None, + vanity_url_code: None, + description: None, + banner: None, + premium_tier: PremiumTier::None, + boost_count: 0, + preferred_locale: Locale::DEFAULT, + }; + + let deserialized = Guild::deserialize(&value).unwrap(); + assert_eq_fields!(guild, deserialized); + } +} diff --git a/src/model/id.rs b/src/model/id.rs index e64a93b..b8f1dff 100644 --- a/src/model/id.rs +++ b/src/model/id.rs @@ -6,8 +6,11 @@ use std::ops::Deref; use serde::{Deserialize, Serialize}; +use crate::model::channel::guild::{ + Category, NewsChannel, StoreChannel, TextChannel, VoiceChannel, +}; use crate::model::channel::message::Attachment; -use crate::model::channel::Message; +use crate::model::channel::{DMChannel, Group, Message}; use crate::model::guild::{CustomEmoji, Emoji, Guild, PartialGuild, Role}; use crate::model::snowflake::{Snowflake, ToSnowflake}; use crate::model::user::{ClientUser, User}; @@ -135,6 +138,7 @@ pub trait ToSnowflakeId: private::Sealed { macro_rules! impl_to_id { ($Parent:ident => $field:ident: $Id:ident) => { + #[doc(hidden)] impl private::Sealed for $Parent {} impl ToSnowflakeId for $Parent { @@ -166,5 +170,14 @@ impl_to_id! { ClientUser => id: UserId; } -// TODO: Implement ToSnowflakeId for Channel. +impl_to_id! { + Category => id: ChannelId; + DMChannel => id: ChannelId; + Group => id: ChannelId; + NewsChannel => id: ChannelId; + StoreChannel => id: ChannelId; + TextChannel => id: ChannelId; + VoiceChannel => id: ChannelId; +} + // TODO: Implement ToSnowflakeId for other types. diff --git a/src/model/mod.rs b/src/model/mod.rs index d283be7..e715e3f 100644 --- a/src/model/mod.rs +++ b/src/model/mod.rs @@ -4,6 +4,7 @@ mod utils; pub mod channel; pub mod color; +pub mod gateway; pub mod guild; pub mod id; pub mod permissions; diff --git a/src/model/utils.rs b/src/model/utils.rs index 12ffce1..407ff1d 100644 --- a/src/model/utils.rs +++ b/src/model/utils.rs @@ -63,48 +63,48 @@ pub mod serde_id_map { use crate::model::id::ToSnowflakeId; - pub fn deserialize<'de, D, V: 'de>(deserializer: D) -> Result, D::Error> + struct Visitor<'de, V: 'de> where - D: Deserializer<'de>, V: ToSnowflakeId + Deserialize<'de>, { - struct Visitor<'de, V: 'de> - where - V: ToSnowflakeId + Deserialize<'de>, - { - _value: &'de PhantomData, + _value: &'de PhantomData, + } + + impl<'de, V: 'de> de::Visitor<'de> for Visitor<'de, V> + where + V: ToSnowflakeId + Deserialize<'de>, + { + type Value = HashMap; + + fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_str("a sequence of objects with snowflake ids") } - impl<'de, V: 'de> de::Visitor<'de> for Visitor<'de, V> + fn visit_seq(self, mut seq: A) -> Result where - V: ToSnowflakeId + Deserialize<'de>, + A: de::SeqAccess<'de>, { - type Value = HashMap; - - fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.write_str("a sequence of objects with snowflake ids") - } - - fn visit_seq(self, mut seq: A) -> Result - where - A: de::SeqAccess<'de>, - { - let mut map: HashMap = - HashMap::with_capacity(seq.size_hint().unwrap_or_default()); - - while let Some(value) = seq.next_element::()? { - if let Some(existing) = map.insert(::id(&value), value) { - return Err(de::Error::custom(format_args!( - "duplicate snowflake id: {}", - ::id(&existing), - ))); - } + let mut map: HashMap = + HashMap::with_capacity(seq.size_hint().unwrap_or_default()); + + while let Some(value) = seq.next_element::()? { + if let Some(existing) = map.insert(::id(&value), value) { + return Err(de::Error::custom(format_args!( + "duplicate snowflake id: {}", + ::id(&existing), + ))); } - - Ok(map) } + + Ok(map) } + } + pub fn deserialize<'de, D, V: 'de>(deserializer: D) -> Result, D::Error> + where + D: Deserializer<'de>, + V: ToSnowflakeId + Deserialize<'de>, + { deserializer.deserialize_seq(Visitor { _value: &PhantomData, }) diff --git a/src/model/voice.rs b/src/model/voice.rs index a75bdc1..665cf29 100644 --- a/src/model/voice.rs +++ b/src/model/voice.rs @@ -6,6 +6,10 @@ use std::ops::Deref; use serde::{Deserialize, Serialize}; +use crate::model::guild::Member; +use crate::model::id::{ChannelId, GuildId, UserId}; +use crate::model::utils::is_false; + /// The ID of a [`VoiceRegion`]. /// /// [`VoiceRegion`]: struct.VoiceRegion.html @@ -13,6 +17,30 @@ use serde::{Deserialize, Serialize}; #[serde(transparent)] pub struct VoiceRegionId(pub(crate) Cow<'static, str>); +impl VoiceRegionId { + /// Creates a voice region ID from a static string. + pub const fn from_static(id: &'static str) -> VoiceRegionId { + VoiceRegionId(Cow::Borrowed(id)) + } + + /// Creates a voice region ID from a string. + pub fn from_string>(id: S) -> VoiceRegionId { + VoiceRegionId(Cow::Owned(id.into())) + } +} + +impl From<&'static str> for VoiceRegionId { + fn from(s: &'static str) -> Self { + VoiceRegionId(Cow::Borrowed(s)) + } +} + +impl From for VoiceRegionId { + fn from(s: String) -> Self { + VoiceRegionId(Cow::Owned(s)) + } +} + impl Display for VoiceRegionId { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.0.fmt(f) @@ -39,6 +67,92 @@ impl From for String { } } +impl From for Cow<'static, str> { + fn from(id: VoiceRegionId) -> Self { + id.0 + } +} + +impl PartialEq for VoiceRegionId { + fn eq(&self, other: &str) -> bool { + self.0 == other + } +} + +impl PartialEq for VoiceRegionId { + fn eq(&self, other: &String) -> bool { + self.0 == &other[..] + } +} + +impl<'a> PartialEq> for VoiceRegionId { + fn eq(&self, other: &Cow<'a, str>) -> bool { + &self.0[..] == &other[..] + } +} + +macro_rules! region_id { + ($( + $(#[$attr:meta])* + const $name:ident = $id:expr; + )*) => { + #[allow(missing_docs)] + impl VoiceRegionId { + $( + $(#[$attr])* + pub const $name: VoiceRegionId = VoiceRegionId::from_static($id); + )* + } + }; +} + +region_id! { + /// Amsterdam. + #[deprecated] + const AMSTERDAM = "amsterdam"; + /// Brazil. + const BRAZIL = "brazil"; + /// Dubai. + #[deprecated] + const DUBAI = "dubai"; + /// Central Europe. + #[deprecated] + const EU_CENTRAL = "eu-central"; + /// Western Europe. + #[deprecated] + const EU_WEST = "eu-west"; + /// Europe. + const EUROPE = "europe"; + /// Frankfurt. + #[deprecated] + const FRANKFURT = "frankfurt"; + /// Hong Kong. + const HONG_KONG = "hongkong"; + /// India. + const INDIA = "india"; + /// Japan. + const JAPAN = "japan"; + /// London. + #[deprecated] + const LONDON = "london"; + /// Russia. + const RUSSIA = "russia"; + /// Singapore. + const SINGAPORE = "singapore"; + /// South Africa. + const SOUTH_AFRICA = "southafrica"; + /// Sydney. + const SYDNEY = "sydney"; + /// US Central. + const US_CENTRAL = "us-central"; + /// US East. + const US_EAST = "us-east"; + /// US South. + const US_SOUTH = "us-south"; + /// US West. + const US_WEST = "us-west"; +} + /// A voice region that can be used. #[non_exhaustive] #[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)] @@ -60,3 +174,56 @@ pub struct VoiceRegion { /// Whether the region is a custom voice region (used for events, etc.). pub custom: bool, } + +/// The voice connection state of a [`User`]. +/// +/// [`User`]: ../user/struct.User.html +#[non_exhaustive] +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct VoiceState { + /// The ID of the [`Guild`] the voice state is for. + /// + /// [`Guild`]: ../guild/struct.Guild.html + #[serde(default, skip_serializing_if = "Option::is_none")] + pub guild_id: Option, + /// The ID of the [`Channel`] the user is connected to. + /// + /// [`Channel`]: ../channel/enum.Channel.html + pub channel_id: Option, + /// The ID of the [`User`]. + /// + /// [`User`]: ../user/struct.User.html + pub user_id: UserId, + /// The guild member the voice state is for. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub member: Option, + /// The ID of the voice session. + pub session_id: String, + /// Whether the user is deafened by the server. + pub deaf: bool, + /// Whether the user is muted by the server. + pub mute: bool, + /// Whether the user is locally deafened. + pub self_deaf: bool, + /// Whether the user is locally muted. + pub self_mute: bool, + /// Whether the user is streaming using "Go Live". + #[serde(default, skip_serializing_if = "is_false")] + pub self_stream: bool, + /// Whether the user is muted by the client user. + pub suppress: bool, +} + +impl_eq_fields!(VoiceState: [ + guild_id, + channel_id, + user_id, + member, + session_id, + deaf, + mute, + self_deaf, + self_mute, + self_stream, + suppress, +]); From e153e7efe50d131aa71beb56dc6eb529da80c3ce Mon Sep 17 00:00:00 2001 From: James Whaley Date: Thu, 16 Jan 2020 14:10:51 +0000 Subject: [PATCH 32/44] Clean up use statements in model::utils --- src/model/utils.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/model/utils.rs b/src/model/utils.rs index 407ff1d..0e55609 100644 --- a/src/model/utils.rs +++ b/src/model/utils.rs @@ -58,8 +58,8 @@ pub mod serde_id_map { use std::fmt; use std::marker::PhantomData; - use serde::{de, Deserialize, Serialize}; - use serde::{Deserializer, Serializer}; + use serde::de; + use serde::{Deserialize, Deserializer, Serialize, Serializer}; use crate::model::id::ToSnowflakeId; From 7fbcb995135286009f31012ad025343c5f442fc2 Mon Sep 17 00:00:00 2001 From: James Whaley Date: Thu, 16 Jan 2020 14:42:34 +0000 Subject: [PATCH 33/44] Add gateway Activity models --- src/model/gateway/activity.rs | 439 +++++++++++++++++++++++ src/model/{gateway.rs => gateway/mod.rs} | 2 + src/model/utils.rs | 29 ++ 3 files changed, 470 insertions(+) create mode 100644 src/model/gateway/activity.rs rename src/model/{gateway.rs => gateway/mod.rs} (64%) diff --git a/src/model/gateway/activity.rs b/src/model/gateway/activity.rs new file mode 100644 index 0000000..05a197b --- /dev/null +++ b/src/model/gateway/activity.rs @@ -0,0 +1,439 @@ +//! Models related to activities in Rich Presence. + +use bitflags::bitflags; +use chrono::{DateTime, Utc}; +use serde::de; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; + +use crate::model::id::{ApplicationId, EmojiId}; +use crate::model::utils::{is_false, serde_option_timestamp, U8Visitor}; + +/// Timestamps for start and end of an [`Activity`]. +/// +/// [`Activity`]: struct.Activity.html +#[non_exhaustive] +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct ActivityTimestamps { + /// When the activity started. + #[serde(with = "serde_option_timestamp")] + #[serde(default, skip_serializing_if = "Option::is_none")] + pub start: Option>, + /// When the activity ends. + #[serde(with = "serde_option_timestamp")] + #[serde(default, skip_serializing_if = "Option::is_none")] + pub end: Option>, +} + +/// The type of an [`Activity`]. +/// +/// [`Activity`]: struct.Activity.html +#[non_exhaustive] +#[int_enum::int_enum(u8)] +#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub enum ActivityType { + /// Format: `Playing {`[`name`]`}`. + /// + /// [`name`]: struct.Activity.html#structfield.name + Game = 0, + /// Format: `Streaming {`[`details`]`}`. + /// + /// [`details`]: struct.Activity.html#structfield.details + Streaming = 1, + /// Format: `Listening to {`[`name`]`}`. + /// + /// [`name`]: struct.Activity.html#structfield.name + Listening = 2, + /// Format: `{`[`emoji`]`} {`[`name`]`}`. + /// + /// [`emoji`]: struct.Activity.html#structfield.emoji + /// [`name`]: struct.Activity.html#structfield.name + Custom = 3, +} + +/// An emoji in a [`Custom`] status for an [`Activity`] +/// +/// [`Custom`]: enum.ActivityType.html#variant.Custom +/// [`Activity`]: struct.Activity.html +#[non_exhaustive] +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct ActivityEmoji { + /// The name of the emoji. + pub name: String, + /// The ID of the emoji. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub id: Option, + /// Whether the emoji is animated. + #[serde(default, skip_serializing_if = "is_false")] + pub animated: bool, +} + +/// An in-game party in an [`Activity`]. +/// +/// [`Activity`]: struct.Activity.html +#[non_exhaustive] +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct ActivityParty { + /// The ID of the party. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub id: Option, + /// The current size and max size of the party. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub size: Option<[u64; 2]>, +} + +impl ActivityParty { + /// The current size of the party. + pub fn current_size(&self) -> Option { + self.size.as_ref().map(|size| size[0]) + } + + /// The maximum size of the party. + pub fn max_size(&self) -> Option { + self.size.as_ref().map(|size| size[1]) + } +} + +/// Images and accompanying hover texts for an [`Activity`]. +/// +/// [`Activity`]: struct.Activity.html +#[non_exhaustive] +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct ActivityAssets { + /// The ID for a large asset of the activity, usually a snowflake. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub large_image: Option, + /// The text displayed when hovering over the [large image] of the activity. + /// + /// [large image]: #structfield.large_image + #[serde(default, skip_serializing_if = "Option::is_none")] + pub large_text: Option, + /// The ID for a small asset of the activity, usually a snowflake. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub small_image: Option, + /// The text displayed when hovering over the [small image] of the activity. + /// + /// [small image]: #structfield.small_image + #[serde(default, skip_serializing_if = "Option::is_none")] + pub small_text: Option, +} + +/// Secrets for joining or spectating an [`Activity`]. +/// +/// [`Activity`]: struct.Activity.html +#[non_exhaustive] +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct ActivitySecrets { + /// The secret for joining a party. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub join: Option, + /// The secret for spectating a game. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub spectate: Option, + /// The secret for a specific instanced match. + #[serde(rename = "match")] + #[serde(default, skip_serializing_if = "Option::is_none")] + pub game: Option, +} + +bitflags! { + /// A set of flags describing an activity. + #[derive(Default)] + pub struct ActivityFlags: u8 { + #[allow(missing_docs)] + const INSTANCE = 1 << 0; + #[allow(missing_docs)] + const JOIN = 1 << 1; + #[allow(missing_docs)] + const SPECTATE = 1 << 2; + #[allow(missing_docs)] + const JOIN_REQUEST = 1 << 3; + #[allow(missing_docs)] + const SYNC = 1 << 4; + #[allow(missing_docs)] + const PLAY = 1 << 5; + } +} + +impl Serialize for ActivityFlags { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_u8(self.bits()) + } +} + +impl<'de> Deserialize<'de> for ActivityFlags { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let bits = deserializer.deserialize_any(U8Visitor)?; + match ActivityFlags::from_bits(bits) { + Some(perms) => Ok(perms), + None => { + let unknown: u8 = bits & !ActivityFlags::all().bits(); + Err(de::Error::custom(format!( + "unknown permissions bits {:b} in {:b}", + unknown, bits + ))) + } + } + } +} + +/// An activity. +#[non_exhaustive] +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct Activity { + /// The name of the activity. + pub name: String, + /// The type of activity. + #[serde(rename = "type")] + pub kind: ActivityType, + /// The URL of the stream. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub url: Option, + // TODO: Look into `created_at` field. + /// Timestamps for when the game started or ends. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub timestamps: Option, + /// The application ID of the game. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub application_id: Option, + /// What the user is currently doing. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub details: Option, + /// The current party status of the user. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub state: Option, + /// The emoji used for a custom status. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub emoji: Option, + /// The current party of the user. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub party: Option, + /// Images and accompanying hover texts for the activity. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub assets: Option, + /// The secrets for joining or spectating the activity. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub secrets: Option, + /// Whether the activity is an instanced game session. + #[serde(default, skip_serializing_if = "is_false")] + pub instance: bool, + /// A set of flags describing the activity. + #[serde(default, skip_serializing_if = "ActivityFlags::is_empty")] + pub flags: ActivityFlags, +} + +impl_eq_fields!(ActivityTimestamps: [start, end]); +impl_eq_fields!(ActivityEmoji: [name, id, animated]); +impl_eq_fields!(ActivityParty: [id, size]); +impl_eq_fields!(ActivityAssets: [large_image, large_text, small_image, small_text]); +impl_eq_fields!(ActivitySecrets: [join, spectate, game]); +impl_eq_fields!(Activity: [ + name, + kind, + url, + timestamps, + application_id, + details, + state, + emoji, + party, + assets, + secrets, + instance, + flags, +]); + +#[cfg(test)] +mod tests { + use serde_json::json; + + use super::*; + use chrono::TimeZone; + + #[test] + fn test_deserialize_activity() { + let value = json!({ + "details": "24H RL Stream for Charity", + "state": "Rocket League", + "name": "Twitch", + "type": 1, + "url": "https://www.twitch.tv/discordapp" + }); + let activity = Activity { + name: "Twitch".to_owned(), + kind: ActivityType::Streaming, + url: Some("https://www.twitch.tv/discordapp".to_owned()), + timestamps: None, + application_id: None, + details: Some("24H RL Stream for Charity".to_owned()), + state: Some("Rocket League".to_owned()), + emoji: None, + party: None, + assets: None, + secrets: None, + instance: false, + flags: ActivityFlags::default(), + }; + + let deserialized = Activity::deserialize(&value).unwrap(); + + assert_eq_fields!(activity, deserialized); + } + + #[test] + fn test_serialize_activity() { + let value = json!({ + "details": "24H RL Stream for Charity", + "state": "Rocket League", + "name": "Twitch", + "type": 1, + "url": "https://www.twitch.tv/discordapp" + }); + let activity = Activity { + name: "Twitch".to_owned(), + kind: ActivityType::Streaming, + url: Some("https://www.twitch.tv/discordapp".to_owned()), + timestamps: None, + application_id: None, + details: Some("24H RL Stream for Charity".to_owned()), + state: Some("Rocket League".to_owned()), + emoji: None, + party: None, + assets: None, + secrets: None, + instance: false, + flags: ActivityFlags::default(), + }; + + assert_eq!(value, serde_json::to_value(&activity).unwrap()); + } + + #[test] + fn test_deserialize_activity_rich() { + let value = json!({ + "name": "Rocket League", + "type": 0, + "application_id": "379286085710381999", + "state": "In a Match", + "details": "Ranked Duos: 2-1", + "timestamps": { + "start": 15112000660000i64 + }, + "party": { + "id": "9dd6594e-81b3-49f6-a6b5-a679e6a060d3", + "size": [2, 2] + }, + "assets": { + "large_image": "351371005538729000", + "large_text": "DFH Stadium", + "small_image": "351371005538729111", + "small_text": "Silver III" + }, + "secrets": { + "join": "025ed05c71f639de8bfaa0d679d7c94b2fdce12f", + "spectate": "e7eb30d2ee025ed05c71ea495f770b76454ee4e0", + "match": "4b2fdce12f639de8bfa7e3591b71a0d679d7c93f" + } + }); + let activity = Activity { + name: "Rocket League".to_owned(), + kind: ActivityType::Game, + url: None, + timestamps: Some(ActivityTimestamps { + start: Some(Utc.timestamp_millis(15112000660000)), + end: None, + }), + application_id: Some(ApplicationId::from(379286085710381999)), + details: Some("Ranked Duos: 2-1".to_owned()), + state: Some("In a Match".to_owned()), + emoji: None, + party: Some(ActivityParty { + id: Some("9dd6594e-81b3-49f6-a6b5-a679e6a060d3".to_owned()), + size: Some([2, 2]), + }), + assets: Some(ActivityAssets { + large_image: Some("351371005538729000".to_owned()), + large_text: Some("DFH Stadium".to_owned()), + small_image: Some("351371005538729111".to_owned()), + small_text: Some("Silver III".to_owned()), + }), + secrets: Some(ActivitySecrets { + join: Some("025ed05c71f639de8bfaa0d679d7c94b2fdce12f".to_owned()), + spectate: Some("e7eb30d2ee025ed05c71ea495f770b76454ee4e0".to_owned()), + game: Some("4b2fdce12f639de8bfa7e3591b71a0d679d7c93f".to_owned()), + }), + instance: false, + flags: ActivityFlags::default(), + }; + + let deserialized = Activity::deserialize(&value).unwrap(); + + assert_eq_fields!(activity, deserialized); + } + + #[test] + fn test_serialize_activity_rich() { + let value = json!({ + "name": "Rocket League", + "type": 0, + "application_id": "379286085710381999", + "state": "In a Match", + "details": "Ranked Duos: 2-1", + "timestamps": { + "start": 15112000660000i64 + }, + "party": { + "id": "9dd6594e-81b3-49f6-a6b5-a679e6a060d3", + "size": [2, 2] + }, + "assets": { + "large_image": "351371005538729000", + "large_text": "DFH Stadium", + "small_image": "351371005538729111", + "small_text": "Silver III" + }, + "secrets": { + "join": "025ed05c71f639de8bfaa0d679d7c94b2fdce12f", + "spectate": "e7eb30d2ee025ed05c71ea495f770b76454ee4e0", + "match": "4b2fdce12f639de8bfa7e3591b71a0d679d7c93f" + } + }); + let activity = Activity { + name: "Rocket League".to_owned(), + kind: ActivityType::Game, + url: None, + timestamps: Some(ActivityTimestamps { + start: Some(Utc.timestamp_millis(15112000660000)), + end: None, + }), + application_id: Some(ApplicationId::from(379286085710381999)), + details: Some("Ranked Duos: 2-1".to_owned()), + state: Some("In a Match".to_owned()), + emoji: None, + party: Some(ActivityParty { + id: Some("9dd6594e-81b3-49f6-a6b5-a679e6a060d3".to_owned()), + size: Some([2, 2]), + }), + assets: Some(ActivityAssets { + large_image: Some("351371005538729000".to_owned()), + large_text: Some("DFH Stadium".to_owned()), + small_image: Some("351371005538729111".to_owned()), + small_text: Some("Silver III".to_owned()), + }), + secrets: Some(ActivitySecrets { + join: Some("025ed05c71f639de8bfaa0d679d7c94b2fdce12f".to_owned()), + spectate: Some("e7eb30d2ee025ed05c71ea495f770b76454ee4e0".to_owned()), + game: Some("4b2fdce12f639de8bfa7e3591b71a0d679d7c93f".to_owned()), + }), + instance: false, + flags: ActivityFlags::default(), + }; + + assert_eq!(value, serde_json::to_value(&activity).unwrap()); + } +} diff --git a/src/model/gateway.rs b/src/model/gateway/mod.rs similarity index 64% rename from src/model/gateway.rs rename to src/model/gateway/mod.rs index 65c7892..70d65e9 100644 --- a/src/model/gateway.rs +++ b/src/model/gateway/mod.rs @@ -1 +1,3 @@ //! Models related to the gateway. + +pub mod activity; diff --git a/src/model/utils.rs b/src/model/utils.rs index 0e55609..3c0afef 100644 --- a/src/model/utils.rs +++ b/src/model/utils.rs @@ -118,3 +118,32 @@ pub mod serde_id_map { serializer.collect_seq(map.values()) } } + +pub mod serde_option_timestamp { + use chrono::serde::ts_milliseconds; + use chrono::{DateTime, Utc}; + use serde::{Deserialize, Deserializer, Serializer}; + + pub fn deserialize<'de, D>(deserializer: D) -> Result>, D::Error> + where + D: Deserializer<'de>, + { + #[derive(Deserialize)] + struct OptionWrapper( + #[serde(deserialize_with = "ts_milliseconds::deserialize")] DateTime, + ); + + let v = Option::deserialize(deserializer)?; + Ok(v.map(|OptionWrapper(dt)| dt)) + } + + pub fn serialize(dt: &Option>, serializer: S) -> Result + where + S: Serializer, + { + match dt { + Some(dt) => ts_milliseconds::serialize(dt, serializer), + None => serializer.serialize_none(), + } + } +} From bac5217a71bfa71f8b17a9ee91398224e16f301b Mon Sep 17 00:00:00 2001 From: James Whaley Date: Thu, 16 Jan 2020 15:25:04 +0000 Subject: [PATCH 34/44] Start work on Presence model --- src/model/gateway/activity.rs | 12 ++-- src/model/gateway/mod.rs | 109 ++++++++++++++++++++++++++++++++++ 2 files changed, 115 insertions(+), 6 deletions(-) diff --git a/src/model/gateway/activity.rs b/src/model/gateway/activity.rs index 05a197b..f03d6a8 100644 --- a/src/model/gateway/activity.rs +++ b/src/model/gateway/activity.rs @@ -139,17 +139,17 @@ bitflags! { /// A set of flags describing an activity. #[derive(Default)] pub struct ActivityFlags: u8 { - #[allow(missing_docs)] + /// The activity is an instance activity. const INSTANCE = 1 << 0; - #[allow(missing_docs)] + /// The activity is joinable. const JOIN = 1 << 1; - #[allow(missing_docs)] + /// The activity can be spectated. const SPECTATE = 1 << 2; - #[allow(missing_docs)] + /// A request can be sent to join the activity party. const JOIN_REQUEST = 1 << 3; - #[allow(missing_docs)] + /// The activity can be synced. const SYNC = 1 << 4; - #[allow(missing_docs)] + /// The activity can be played. const PLAY = 1 << 5; } } diff --git a/src/model/gateway/mod.rs b/src/model/gateway/mod.rs index 70d65e9..81bda12 100644 --- a/src/model/gateway/mod.rs +++ b/src/model/gateway/mod.rs @@ -1,3 +1,112 @@ //! Models related to the gateway. +use serde::{Deserialize, Serialize}; + +use crate::model::id::UserId; +use crate::model::user::User; + pub mod activity; + +/// A user with possibly only partial information. +#[derive(Clone, Debug, Deserialize, Serialize)] +#[serde(untagged)] +pub enum PartialUser { + /// A full user object. + Full(User), + /// A partial user object with only the `id` field. + #[non_exhaustive] + Partial { + /// The ID of the user. + id: UserId, + }, +} + +//#[derive(Clone, Debug, Deserialize, Serialize)] +// pub struct Presence { +// pub user: PartialUser, +//} + +impl_eq_fields!(PartialUser: (a, b) => { + match (a, b) { + (PartialUser::Full(a), PartialUser::Full(b)) => assert_eq_fields!(a, b), + (PartialUser::Partial { id: id_a }, PartialUser::Partial { id: id_b }) => assert_eq_fields!(id_a, id_b), + (a, b) => panic_ne_fields!(a, b), + } +}); + +#[cfg(test)] +mod tests { + use serde_json::json; + + use crate::model::user::Discriminator; + + use super::*; + + #[test] + fn test_deserialize_partial_user() { + let value = json!({ + "id": "80351110224678912" + }); + let user = PartialUser::Partial { + id: UserId::from(80351110224678912), + }; + + let deserialized = PartialUser::deserialize(&value).unwrap(); + assert_eq_fields!(user, deserialized); + } + + #[test] + fn test_serialize_partial_user() { + let value = json!({ + "id": "80351110224678912" + }); + let user = PartialUser::Partial { + id: UserId::from(80351110224678912), + }; + + assert_eq!(value, serde_json::to_value(&user).unwrap()); + } + + #[test] + fn test_deserialize_full_user() { + let value = json!({ + "id": "80351110224678912", + "username": "Nelly", + "discriminator": "1337", + "avatar": "8342729096ea3675442027381ff50dfe", + }); + let user = PartialUser::Full(User { + id: UserId::from(80351110224678912), + name: "Nelly".to_owned(), + discriminator: Discriminator::new(1337).unwrap(), + avatar: Some("8342729096ea3675442027381ff50dfe".to_owned()), + bot: false, + system: false, + }); + + let deserialized = PartialUser::deserialize(&value).unwrap(); + assert_eq_fields!(user, deserialized); + } + + #[test] + fn test_serialize_full_user() { + let value = json!({ + "id": "80351110224678912", + "username": "Nelly", + "discriminator": "1337", + "avatar": "8342729096ea3675442027381ff50dfe", + "bot": false, + "system": false + }); + let user = PartialUser::Full(User { + id: UserId::from(80351110224678912), + name: "Nelly".to_owned(), + discriminator: Discriminator::new(1337).unwrap(), + avatar: Some("8342729096ea3675442027381ff50dfe".to_owned()), + bot: false, + system: false, + }); + + assert_eq!(value, serde_json::to_value(&user).unwrap()); + } +} From c617dacd565bd353dee38db806494496bebe745e Mon Sep 17 00:00:00 2001 From: James Whaley Date: Thu, 16 Jan 2020 16:19:23 +0000 Subject: [PATCH 35/44] Add Presence model --- src/model/gateway/mod.rs | 96 +++++++++++++++++++++++++++++++++++++--- src/model/guild/mod.rs | 4 +- 2 files changed, 93 insertions(+), 7 deletions(-) diff --git a/src/model/gateway/mod.rs b/src/model/gateway/mod.rs index 81bda12..60a1ebc 100644 --- a/src/model/gateway/mod.rs +++ b/src/model/gateway/mod.rs @@ -1,8 +1,10 @@ //! Models related to the gateway. +use chrono::{DateTime, FixedOffset}; use serde::{Deserialize, Serialize}; -use crate::model::id::UserId; +use crate::model::gateway::activity::Activity; +use crate::model::id::{GuildId, RoleId, UserId}; use crate::model::user::User; pub mod activity; @@ -21,10 +23,83 @@ pub enum PartialUser { }, } -//#[derive(Clone, Debug, Deserialize, Serialize)] -// pub struct Presence { -// pub user: PartialUser, -//} +/// The online status of a [`User`] in a [`Presence`]. +/// +/// [`User`]: ../user/struct.User.html +/// [`Presence`]: struct.Presence.html +#[derive(Clone, Copy, Debug, Eq, PartialEq, Deserialize, Serialize)] +pub enum OnlineStatus { + /// Idle. + #[serde(rename = "idle")] + Idle, + /// Do not disturb. + #[serde(rename = "dnd")] + DoNotDisturb, + /// Online. + #[serde(rename = "online")] + Online, + /// Offline or invisible. + #[serde(rename = "offline")] + Offline, +} + +impl OnlineStatus { + fn is_offline(&self) -> bool { + *self == OnlineStatus::Offline + } +} + +impl Default for OnlineStatus { + fn default() -> Self { + OnlineStatus::Offline + } +} + +/// Statuses of the active sessions for each platform for a [`User`]. +/// +/// [`User`]: ../user/struct.User.html +#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)] +pub struct ClientStatus { + /// The status set for an active desktop (Windows, Linux, Mac) application + /// session. + #[serde(default, skip_serializing_if = "OnlineStatus::is_offline")] + pub desktop: OnlineStatus, + /// The status set for an active mobile (iOS, Android) application session. + #[serde(default, skip_serializing_if = "OnlineStatus::is_offline")] + pub mobile: OnlineStatus, + /// The status set for an active web (browser, bot account) application + /// session. + #[serde(default, skip_serializing_if = "OnlineStatus::is_offline")] + pub web: OnlineStatus, +} + +/// The presence state of a [`User`] in a [`Guild`]. +/// +/// [`User`]: ../user/struct.User.html +/// [`Guild`]: ../guild/struct.Guild.html +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct Presence { + /// The user the presence relates to. + pub user: PartialUser, + /// The roles of the user. + pub roles: Vec, + /// The current activity of the user. + pub game: Option, + /// The ID of the guild. + pub guild_id: GuildId, + /// The online status of the user. + pub status: OnlineStatus, + /// The current activities of the user. + pub activities: Vec, + /// The platform dependent status of the user. + pub client_status: ClientStatus, + /// When the user Nitro boosted the guild. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub premium_since: Option>, + /// The nickname of the user in the guild, if set. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub nick: Option, +} impl_eq_fields!(PartialUser: (a, b) => { match (a, b) { @@ -33,6 +108,17 @@ impl_eq_fields!(PartialUser: (a, b) => { (a, b) => panic_ne_fields!(a, b), } }); +impl_eq_fields!(Presence: [ + user, + roles, + game, + guild_id, + status, + activities, + client_status, + premium_since, + nick +]); #[cfg(test)] mod tests { diff --git a/src/model/guild/mod.rs b/src/model/guild/mod.rs index 4fe0498..554d752 100644 --- a/src/model/guild/mod.rs +++ b/src/model/guild/mod.rs @@ -139,7 +139,7 @@ impl Default for MfaLevel { } } -/// The tier of premium for a guild, provided by nitro boosts. +/// The tier of premium for a guild, provided by Nitro boosts. #[non_exhaustive] #[int_enum::int_enum(u8)] #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] @@ -350,7 +350,7 @@ pub struct Guild { pub description: Option, /// The hash of the guild banner. pub banner: Option, - /// The tier of premium for the guild, provided by nitro boosts. + /// The tier of premium for the guild, provided by Nitro boosts. #[serde(default)] pub premium_tier: PremiumTier, /// The total number of users currently boosting the guild. From 15d17d915009751ccc888527f4883d505244cd24 Mon Sep 17 00:00:00 2001 From: James Whaley Date: Thu, 16 Jan 2020 16:22:25 +0000 Subject: [PATCH 36/44] Refactor presence models into presence module --- src/model/gateway/mod.rs | 196 +--------------------------------- src/model/gateway/presence.rs | 196 ++++++++++++++++++++++++++++++++++ 2 files changed, 197 insertions(+), 195 deletions(-) create mode 100644 src/model/gateway/presence.rs diff --git a/src/model/gateway/mod.rs b/src/model/gateway/mod.rs index 60a1ebc..139749b 100644 --- a/src/model/gateway/mod.rs +++ b/src/model/gateway/mod.rs @@ -1,198 +1,4 @@ //! Models related to the gateway. -use chrono::{DateTime, FixedOffset}; -use serde::{Deserialize, Serialize}; - -use crate::model::gateway::activity::Activity; -use crate::model::id::{GuildId, RoleId, UserId}; -use crate::model::user::User; - pub mod activity; - -/// A user with possibly only partial information. -#[derive(Clone, Debug, Deserialize, Serialize)] -#[serde(untagged)] -pub enum PartialUser { - /// A full user object. - Full(User), - /// A partial user object with only the `id` field. - #[non_exhaustive] - Partial { - /// The ID of the user. - id: UserId, - }, -} - -/// The online status of a [`User`] in a [`Presence`]. -/// -/// [`User`]: ../user/struct.User.html -/// [`Presence`]: struct.Presence.html -#[derive(Clone, Copy, Debug, Eq, PartialEq, Deserialize, Serialize)] -pub enum OnlineStatus { - /// Idle. - #[serde(rename = "idle")] - Idle, - /// Do not disturb. - #[serde(rename = "dnd")] - DoNotDisturb, - /// Online. - #[serde(rename = "online")] - Online, - /// Offline or invisible. - #[serde(rename = "offline")] - Offline, -} - -impl OnlineStatus { - fn is_offline(&self) -> bool { - *self == OnlineStatus::Offline - } -} - -impl Default for OnlineStatus { - fn default() -> Self { - OnlineStatus::Offline - } -} - -/// Statuses of the active sessions for each platform for a [`User`]. -/// -/// [`User`]: ../user/struct.User.html -#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)] -pub struct ClientStatus { - /// The status set for an active desktop (Windows, Linux, Mac) application - /// session. - #[serde(default, skip_serializing_if = "OnlineStatus::is_offline")] - pub desktop: OnlineStatus, - /// The status set for an active mobile (iOS, Android) application session. - #[serde(default, skip_serializing_if = "OnlineStatus::is_offline")] - pub mobile: OnlineStatus, - /// The status set for an active web (browser, bot account) application - /// session. - #[serde(default, skip_serializing_if = "OnlineStatus::is_offline")] - pub web: OnlineStatus, -} - -/// The presence state of a [`User`] in a [`Guild`]. -/// -/// [`User`]: ../user/struct.User.html -/// [`Guild`]: ../guild/struct.Guild.html -#[derive(Clone, Debug, Deserialize, Serialize)] -pub struct Presence { - /// The user the presence relates to. - pub user: PartialUser, - /// The roles of the user. - pub roles: Vec, - /// The current activity of the user. - pub game: Option, - /// The ID of the guild. - pub guild_id: GuildId, - /// The online status of the user. - pub status: OnlineStatus, - /// The current activities of the user. - pub activities: Vec, - /// The platform dependent status of the user. - pub client_status: ClientStatus, - /// When the user Nitro boosted the guild. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub premium_since: Option>, - /// The nickname of the user in the guild, if set. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub nick: Option, -} - -impl_eq_fields!(PartialUser: (a, b) => { - match (a, b) { - (PartialUser::Full(a), PartialUser::Full(b)) => assert_eq_fields!(a, b), - (PartialUser::Partial { id: id_a }, PartialUser::Partial { id: id_b }) => assert_eq_fields!(id_a, id_b), - (a, b) => panic_ne_fields!(a, b), - } -}); -impl_eq_fields!(Presence: [ - user, - roles, - game, - guild_id, - status, - activities, - client_status, - premium_since, - nick -]); - -#[cfg(test)] -mod tests { - use serde_json::json; - - use crate::model::user::Discriminator; - - use super::*; - - #[test] - fn test_deserialize_partial_user() { - let value = json!({ - "id": "80351110224678912" - }); - let user = PartialUser::Partial { - id: UserId::from(80351110224678912), - }; - - let deserialized = PartialUser::deserialize(&value).unwrap(); - assert_eq_fields!(user, deserialized); - } - - #[test] - fn test_serialize_partial_user() { - let value = json!({ - "id": "80351110224678912" - }); - let user = PartialUser::Partial { - id: UserId::from(80351110224678912), - }; - - assert_eq!(value, serde_json::to_value(&user).unwrap()); - } - - #[test] - fn test_deserialize_full_user() { - let value = json!({ - "id": "80351110224678912", - "username": "Nelly", - "discriminator": "1337", - "avatar": "8342729096ea3675442027381ff50dfe", - }); - let user = PartialUser::Full(User { - id: UserId::from(80351110224678912), - name: "Nelly".to_owned(), - discriminator: Discriminator::new(1337).unwrap(), - avatar: Some("8342729096ea3675442027381ff50dfe".to_owned()), - bot: false, - system: false, - }); - - let deserialized = PartialUser::deserialize(&value).unwrap(); - assert_eq_fields!(user, deserialized); - } - - #[test] - fn test_serialize_full_user() { - let value = json!({ - "id": "80351110224678912", - "username": "Nelly", - "discriminator": "1337", - "avatar": "8342729096ea3675442027381ff50dfe", - "bot": false, - "system": false - }); - let user = PartialUser::Full(User { - id: UserId::from(80351110224678912), - name: "Nelly".to_owned(), - discriminator: Discriminator::new(1337).unwrap(), - avatar: Some("8342729096ea3675442027381ff50dfe".to_owned()), - bot: false, - system: false, - }); - - assert_eq!(value, serde_json::to_value(&user).unwrap()); - } -} +pub mod presence; diff --git a/src/model/gateway/presence.rs b/src/model/gateway/presence.rs new file mode 100644 index 0000000..18509bf --- /dev/null +++ b/src/model/gateway/presence.rs @@ -0,0 +1,196 @@ +//! Models related to Rich Presence. + +use chrono::{DateTime, FixedOffset}; +use serde::{Deserialize, Serialize}; + +use crate::model::gateway::activity::Activity; +use crate::model::id::{GuildId, RoleId, UserId}; +use crate::model::user::User; + +/// A user with possibly only partial information. +#[derive(Clone, Debug, Deserialize, Serialize)] +#[serde(untagged)] +pub enum PartialUser { + /// A full user object. + Full(User), + /// A partial user object with only the `id` field. + #[non_exhaustive] + Partial { + /// The ID of the user. + id: UserId, + }, +} + +/// The online status of a [`User`] in a [`Presence`]. +/// +/// [`User`]: ../../user/struct.User.html +/// [`Presence`]: struct.Presence.html +#[derive(Clone, Copy, Debug, Eq, PartialEq, Deserialize, Serialize)] +pub enum OnlineStatus { + /// Idle. + #[serde(rename = "idle")] + Idle, + /// Do not disturb. + #[serde(rename = "dnd")] + DoNotDisturb, + /// Online. + #[serde(rename = "online")] + Online, + /// Offline or invisible. + #[serde(rename = "offline")] + Offline, +} + +impl OnlineStatus { + fn is_offline(&self) -> bool { + *self == OnlineStatus::Offline + } +} + +impl Default for OnlineStatus { + fn default() -> Self { + OnlineStatus::Offline + } +} + +/// Statuses of the active sessions for each platform for a [`User`]. +/// +/// [`User`]: ../../user/struct.User.html +#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)] +pub struct ClientStatus { + /// The status set for an active desktop (Windows, Linux, Mac) application + /// session. + #[serde(default, skip_serializing_if = "OnlineStatus::is_offline")] + pub desktop: OnlineStatus, + /// The status set for an active mobile (iOS, Android) application session. + #[serde(default, skip_serializing_if = "OnlineStatus::is_offline")] + pub mobile: OnlineStatus, + /// The status set for an active web (browser, bot account) application + /// session. + #[serde(default, skip_serializing_if = "OnlineStatus::is_offline")] + pub web: OnlineStatus, +} + +/// The presence state of a [`User`] in a [`Guild`]. +/// +/// [`User`]: ../../user/struct.User.html +/// [`Guild`]: ../../guild/struct.Guild.html +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct Presence { + /// The user the presence relates to. + pub user: PartialUser, + /// The roles of the user. + pub roles: Vec, + /// The current activity of the user. + pub game: Option, + /// The ID of the guild. + pub guild_id: GuildId, + /// The online status of the user. + pub status: OnlineStatus, + /// The current activities of the user. + pub activities: Vec, + /// The platform dependent status of the user. + pub client_status: ClientStatus, + /// When the user Nitro boosted the guild. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub premium_since: Option>, + /// The nickname of the user in the guild, if set. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub nick: Option, +} + +impl_eq_fields!(PartialUser: (a, b) => { + match (a, b) { + (PartialUser::Full(a), PartialUser::Full(b)) => assert_eq_fields!(a, b), + (PartialUser::Partial { id: id_a }, PartialUser::Partial { id: id_b }) => assert_eq_fields!(id_a, id_b), + (a, b) => panic_ne_fields!(a, b), + } +}); +impl_eq_fields!(Presence: [ + user, + roles, + game, + guild_id, + status, + activities, + client_status, + premium_since, + nick +]); + +#[cfg(test)] +mod tests { + use serde_json::json; + + use crate::model::user::Discriminator; + + use super::*; + + #[test] + fn test_deserialize_partial_user() { + let value = json!({ + "id": "80351110224678912" + }); + let user = PartialUser::Partial { + id: UserId::from(80351110224678912), + }; + + let deserialized = PartialUser::deserialize(&value).unwrap(); + assert_eq_fields!(user, deserialized); + } + + #[test] + fn test_serialize_partial_user() { + let value = json!({ + "id": "80351110224678912" + }); + let user = PartialUser::Partial { + id: UserId::from(80351110224678912), + }; + + assert_eq!(value, serde_json::to_value(&user).unwrap()); + } + + #[test] + fn test_deserialize_full_user() { + let value = json!({ + "id": "80351110224678912", + "username": "Nelly", + "discriminator": "1337", + "avatar": "8342729096ea3675442027381ff50dfe", + }); + let user = PartialUser::Full(User { + id: UserId::from(80351110224678912), + name: "Nelly".to_owned(), + discriminator: Discriminator::new(1337).unwrap(), + avatar: Some("8342729096ea3675442027381ff50dfe".to_owned()), + bot: false, + system: false, + }); + + let deserialized = PartialUser::deserialize(&value).unwrap(); + assert_eq_fields!(user, deserialized); + } + + #[test] + fn test_serialize_full_user() { + let value = json!({ + "id": "80351110224678912", + "username": "Nelly", + "discriminator": "1337", + "avatar": "8342729096ea3675442027381ff50dfe", + "bot": false, + "system": false + }); + let user = PartialUser::Full(User { + id: UserId::from(80351110224678912), + name: "Nelly".to_owned(), + discriminator: Discriminator::new(1337).unwrap(), + avatar: Some("8342729096ea3675442027381ff50dfe".to_owned()), + bot: false, + system: false, + }); + + assert_eq!(value, serde_json::to_value(&user).unwrap()); + } +} From 1520076646199633eecf79b25b6c33768310afeb Mon Sep 17 00:00:00 2001 From: James Whaley Date: Thu, 16 Jan 2020 16:35:13 +0000 Subject: [PATCH 37/44] Add presences to Guild model --- src/model/gateway/presence.rs | 29 ++++++++++++++++++++++++++++- src/model/guild/mod.rs | 8 +++++++- 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/src/model/gateway/presence.rs b/src/model/gateway/presence.rs index 18509bf..b49d9a0 100644 --- a/src/model/gateway/presence.rs +++ b/src/model/gateway/presence.rs @@ -4,7 +4,7 @@ use chrono::{DateTime, FixedOffset}; use serde::{Deserialize, Serialize}; use crate::model::gateway::activity::Activity; -use crate::model::id::{GuildId, RoleId, UserId}; +use crate::model::id::{GuildId, RoleId, ToSnowflakeId, UserId}; use crate::model::user::User; /// A user with possibly only partial information. @@ -21,6 +21,21 @@ pub enum PartialUser { }, } +impl crate::model::id::private::Sealed for PartialUser {} + +impl ToSnowflakeId for PartialUser { + type Id = UserId; + + fn id(&self) -> Self::Id { + match self { + PartialUser::Full(user) => user.id, + PartialUser::Partial { id } => *id, + } + } +} + +impl_to_snowflake!(PartialUser: |user| user.id().snowflake()); + /// The online status of a [`User`] in a [`Presence`]. /// /// [`User`]: ../../user/struct.User.html @@ -99,6 +114,18 @@ pub struct Presence { pub nick: Option, } +impl crate::model::id::private::Sealed for Presence {} + +impl ToSnowflakeId for Presence { + type Id = ::Id; + + fn id(&self) -> Self::Id { + self.user.id() + } +} + +impl_to_snowflake!(Presence: |presence| presence.id().snowflake()); + impl_eq_fields!(PartialUser: (a, b) => { match (a, b) { (PartialUser::Full(a), PartialUser::Full(b)) => assert_eq_fields!(a, b), diff --git a/src/model/guild/mod.rs b/src/model/guild/mod.rs index 554d752..07d72de 100644 --- a/src/model/guild/mod.rs +++ b/src/model/guild/mod.rs @@ -24,6 +24,7 @@ pub use self::audit_log::AuditLogEvent; pub use self::emoji::{CustomEmoji, Emoji, PartialEmoji}; pub use self::member::{Member, PartialMember}; pub use self::role::Role; +use crate::model::gateway::presence::Presence; /// The required level of criteria a user must meet, prior to being able to send /// messages in a [`Guild`]. @@ -340,7 +341,10 @@ pub struct Guild { #[serde(with = "serde_id_map")] #[serde(default, skip_serializing_if = "HashMap::is_empty")] pub channels: HashMap, - // TODO: Add `presences` field. + /// The presences of the users in the guild. + #[serde(with = "serde_id_map")] + #[serde(default, skip_serializing_if = "HashMap::is_empty")] + pub presences: HashMap, /// The maximum amount of members for the guild. #[serde(default, skip_serializing_if = "Option::is_none")] pub max_members: Option, @@ -406,6 +410,7 @@ impl_eq_fields!(Guild: (a, b) => { assert_eq_fields!(map => a.emojis, b.emojis); assert_eq_fields!(map => a.members, b.members); assert_eq_fields!(map => a.channels, b.channels); + assert_eq_fields!(map => a.presences, b.presences); }); #[cfg(test)] @@ -474,6 +479,7 @@ mod tests { voice_states: vec![], members: HashMap::default(), channels: HashMap::default(), + presences: HashMap::default(), max_members: None, vanity_url_code: None, description: None, From 2a1043c20fc17ac9dc102b086f1abf8f74999c54 Mon Sep 17 00:00:00 2001 From: James Whaley Date: Thu, 16 Jan 2020 17:37:09 +0000 Subject: [PATCH 38/44] Fix bug in token used in authorization headers --- src/http/client.rs | 10 +++++++++- src/http/ratelimit.rs | 7 +++++-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/http/client.rs b/src/http/client.rs index e1f2119..54bedee 100644 --- a/src/http/client.rs +++ b/src/http/client.rs @@ -1,5 +1,6 @@ use async_std::sync::Arc; +use bytes::Bytes; use serde::de::DeserializeOwned; use crate::internal::prelude::*; @@ -17,7 +18,14 @@ pub struct Http { impl Http { /// Creates a new HTTP client with the given API token. pub fn new>(token: S) -> Http { - let token = token.as_ref(); + // Trim whitespace from token. + let token = token.as_ref().trim(); + // Add "Bot " prefix to token if necessary. + let token = if token.starts_with("Bot ") { + Bytes::copy_from_slice(token.as_bytes()) + } else { + Bytes::from(format!("Bot {}", token)) + }; let client = hyper::Client::builder().build(HttpsConnector::new()); let client = Arc::new(client); diff --git a/src/http/ratelimit.rs b/src/http/ratelimit.rs index 52c76a0..ff1a981 100644 --- a/src/http/ratelimit.rs +++ b/src/http/ratelimit.rs @@ -33,9 +33,12 @@ pub struct RateLimiter { impl RateLimiter { /// Creates a new rate limit manager. - pub fn new>(client: Arc, token: S) -> RateLimiter { + pub fn new(client: Arc, token: T) -> RateLimiter + where + T: Into, + { RateLimiter { - token: Bytes::copy_from_slice(token.as_ref().as_bytes()), + token: token.into(), client, global: Default::default(), routes: Default::default(), From 150380a032cc872823574c047200ee69aa74656b Mon Sep 17 00:00:00 2001 From: James Whaley Date: Thu, 16 Jan 2020 17:44:22 +0000 Subject: [PATCH 39/44] Remove unnecessary token trims from Client --- src/client/mod.rs | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/client/mod.rs b/src/client/mod.rs index b71a155..47580cf 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -43,15 +43,6 @@ impl Client { S: AsRef, H: EventHandler + Send + Sync + 'static, { - let token = token.as_ref().trim(); - - // Prepend "Bot " to token if required. - let token = if token.starts_with("Bot ") { - token.to_string() - } else { - format!("Bot {}", token) - }; - let _http = Http::new(token); // TODO: thread pool From 4482e63edafbe1cde526548f9a49fcbab01290fe Mon Sep 17 00:00:00 2001 From: James Whaley Date: Thu, 16 Jan 2020 17:44:50 +0000 Subject: [PATCH 40/44] Add examples Add current_user example. --- .gitignore | 4 ++++ Cargo.toml | 10 +++++++--- examples/current_user.rs | 26 ++++++++++++++++++++++++++ 3 files changed, 37 insertions(+), 3 deletions(-) create mode 100644 examples/current_user.rs diff --git a/.gitignore b/.gitignore index 6936990..53653fb 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,7 @@ +# Rust/Cargo /target **/*.rs.bk Cargo.lock + +# Discord Token +examples/.token diff --git a/Cargo.toml b/Cargo.toml index 35a48b3..a609f92 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,6 +15,10 @@ keywords = ["discord", "api", "async"] edition = "2018" +[features] +default = [] +systime_ratelimits = [] + [dependencies] async-std = "1.3" bitflags = "1.2" @@ -31,6 +35,6 @@ serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" thiserror = "1.0" -[features] -default = [] -systime_ratelimits = [] +[dev-dependencies] +pretty_env_logger = "0.3" +tokio = { version = "0.2", features = ["full"] } diff --git a/examples/current_user.rs b/examples/current_user.rs new file mode 100644 index 0000000..a757917 --- /dev/null +++ b/examples/current_user.rs @@ -0,0 +1,26 @@ +use strife::http::unstable::{Request, Route}; +use strife::model::user::ClientUser; +use strife::{Error, Http}; + +const DISCORD_TOKEN: &str = include_str!(".token"); + +#[tokio::main] +async fn main() -> Result<(), Error> { + // Initialize pretty logging. + pretty_env_logger::init(); + + // Create an http client using our token. + let http = Http::new(DISCORD_TOKEN); + + // Request information on the current user. + let user: ClientUser = http.request(Request::new(Route::GetCurrentUser)).await?; + + println!( + "Client User: {name}#{discriminator} ({id})", + name = user.name, + discriminator = user.discriminator, + id = user.id + ); + + Ok(()) +} From 98b992bb1e4f5f04b0bb9c072852f65d49416b39 Mon Sep 17 00:00:00 2001 From: James Whaley Date: Thu, 16 Jan 2020 20:42:04 +0000 Subject: [PATCH 41/44] Fix current_user example Fix current_user to not fail when run on CI. --- examples/current_user.rs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/examples/current_user.rs b/examples/current_user.rs index a757917..5508cdd 100644 --- a/examples/current_user.rs +++ b/examples/current_user.rs @@ -1,16 +1,22 @@ +use std::env; +use std::fs; + use strife::http::unstable::{Request, Route}; use strife::model::user::ClientUser; use strife::{Error, Http}; -const DISCORD_TOKEN: &str = include_str!(".token"); - #[tokio::main] async fn main() -> Result<(), Error> { // Initialize pretty logging. pretty_env_logger::init(); + // Get token from `DISCORD_TOKEN` environment variable or `.token` file. + let token = env::var("DISCORD_TOKEN") + .or_else(|_| fs::read_to_string(".token")) + .expect("missing discord token"); + // Create an http client using our token. - let http = Http::new(DISCORD_TOKEN); + let http = Http::new(token); // Request information on the current user. let user: ClientUser = http.request(Request::new(Route::GetCurrentUser)).await?; From e9ecdc40832819d43683927d3a7ba876e89b435f Mon Sep 17 00:00:00 2001 From: James Whaley Date: Thu, 16 Jan 2020 21:02:15 +0000 Subject: [PATCH 42/44] Add skip_serializing_if serde attributes Update tests to reflect changes in serialization. Fixes #4. --- src/internal/test.rs | 7 + src/model/channel/dm_channel.rs | 6 +- src/model/channel/group.rs | 20 +-- src/model/channel/guild/mod.rs | 10 +- src/model/channel/guild/news_channel.rs | 9 +- src/model/channel/guild/store_channel.rs | 1 + src/model/channel/guild/text_channel.rs | 13 +- src/model/channel/guild/voice_channel.rs | 3 +- src/model/channel/message/embed.rs | 21 ++- src/model/channel/message/mod.rs | 199 ++++++++++++++++++++++- src/model/channel/mod.rs | 2 +- src/model/gateway/presence.rs | 4 +- src/model/guild/emoji/mod.rs | 7 +- src/model/guild/emoji/partial.rs | 1 + src/model/guild/member.rs | 2 + src/model/guild/mod.rs | 94 +---------- src/model/misc.rs | 95 +++++++++++ src/model/mod.rs | 1 + src/model/user/mod.rs | 36 ++-- src/model/utils.rs | 5 + 20 files changed, 385 insertions(+), 151 deletions(-) create mode 100644 src/model/misc.rs diff --git a/src/internal/test.rs b/src/internal/test.rs index b9289bb..c04c753 100644 --- a/src/internal/test.rs +++ b/src/internal/test.rs @@ -130,6 +130,13 @@ mod inner { B: std::fmt::Debug, { fn eq_fields(&self, other: &Vec) { + assert_eq!( + self.len(), + other.len(), + "different length vec: {:?} and {:?}", + self, + other + ); for (a, b) in self.iter().zip(other.iter()) { assert_eq_fields!(a, b); } diff --git a/src/model/channel/dm_channel.rs b/src/model/channel/dm_channel.rs index 0aaf964..88e188e 100644 --- a/src/model/channel/dm_channel.rs +++ b/src/model/channel/dm_channel.rs @@ -25,8 +25,10 @@ pub struct DMChannel { #[serde(rename = "recipients", with = "serde_recipient")] pub recipient: User, /// The ID of the last message sent to the channel. + #[serde(default)] pub last_message_id: Option, /// When the last message was pinned. + #[serde(default, skip_serializing_if = "Option::is_none")] pub last_pin_timestamp: Option>, } @@ -119,9 +121,7 @@ mod tests { "id": "225336713231204353", "username": "Juici", "avatar": "a_e8b3a198dab6af59aacd1072bbedb255", - "discriminator": "0001", - "bot": false, - "system": false + "discriminator": "0001" } ] }); diff --git a/src/model/channel/group.rs b/src/model/channel/group.rs index f681d9b..f9492e4 100644 --- a/src/model/channel/group.rs +++ b/src/model/channel/group.rs @@ -24,19 +24,25 @@ pub struct Group { #[serde(rename = "type")] pub(crate) kind: ChannelType, /// The name of the group. + #[serde(default, skip_serializing_if = "Option::is_none")] pub name: Option, /// The group icon hash. + #[serde(default)] pub icon: Option, /// The users in the group. - #[serde(default, with = "serde_id_map")] + #[serde(with = "serde_id_map")] + #[serde(default, skip_serializing_if = "HashMap::is_empty")] pub recipients: HashMap, /// The ID of the group creator. pub owner_id: UserId, /// The application ID of the group creator, if it is bot-created. + #[serde(default, skip_serializing_if = "Option::is_none")] pub application_id: Option, /// The ID of the last message sent to the group. + #[serde(default)] pub last_message_id: Option, /// When the last message was pinned. + #[serde(default, skip_serializing_if = "Option::is_none")] pub last_pin_timestamp: Option>, } @@ -146,25 +152,19 @@ mod tests { "username": "test", "discriminator": "9999", "id": "82198898841029460", - "avatar": "33ecab261d4681afa4d85a04691c4a01", - "bot": false, - "system": false + "avatar": "33ecab261d4681afa4d85a04691c4a01" }, { "username": "test2", "discriminator": "9999", "id": "53908099506183680", - "avatar": "a_bab14f271d565501444b2ca3be944b25", - "bot": false, - "system": false + "avatar": "a_bab14f271d565501444b2ca3be944b25" } ], "last_message_id": "3343820033257021450", - "last_pin_timestamp": null, "type": 3, "id": "319674150115710528", - "owner_id": "53908099506183680", - "application_id": null + "owner_id": "53908099506183680" }); let channel = Group { id: ChannelId::from(319674150115710528), diff --git a/src/model/channel/guild/mod.rs b/src/model/channel/guild/mod.rs index d91a5ad..bea0431 100644 --- a/src/model/channel/guild/mod.rs +++ b/src/model/channel/guild/mod.rs @@ -10,33 +10,33 @@ use serde::de; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use crate::model::channel::ChannelType; +use crate::model::id::{ChannelId, ToSnowflakeId}; pub use self::category::Category; pub use self::news_channel::NewsChannel; pub use self::store_channel::StoreChannel; pub use self::text_channel::TextChannel; pub use self::voice_channel::VoiceChannel; -use crate::model::id::{ChannelId, ToSnowflakeId}; /// A channel in a [`Guild`]. /// -/// [`Guild`]: TODO +/// [`Guild`]: ../../guild/struct.Guild.html #[non_exhaustive] #[derive(Clone, Debug)] pub enum GuildChannel { /// A text channel in a [`Guild`]. /// - /// [`Guild`]: TODO + /// [`Guild`]: ../../guild/struct.Guild.html Text(TextChannel), /// A voice channel in a [`Guild`]. /// - /// [`Guild`]: TODO + /// [`Guild`]: ../../guild/struct.Guild.html Voice(VoiceChannel), /// An organizational category that contains non-category channels. Category(Category), /// A channel that users can follow and crosspost into another [`Guild`]. /// - /// [`Guild`]: TODO + /// [`Guild`]: ../../guild/struct.Guild.html News(NewsChannel), /// A channel in which game developers can sell games on Discord. Store(StoreChannel), diff --git a/src/model/channel/guild/news_channel.rs b/src/model/channel/guild/news_channel.rs index e39e3fa..7c18513 100644 --- a/src/model/channel/guild/news_channel.rs +++ b/src/model/channel/guild/news_channel.rs @@ -7,7 +7,7 @@ use crate::model::id::{ChannelId, GuildId, MessageId}; /// A text channel in a [`Guild`]. /// -/// [`Guild`]: TODO +/// [`Guild`]: ../../guild/struct.Guild.html #[non_exhaustive] #[derive(Clone, Debug, Deserialize, Serialize)] pub struct NewsChannel { @@ -30,15 +30,19 @@ pub struct NewsChannel { /// The name of the channel. pub name: String, /// The topic of the channel. + #[serde(default)] pub topic: Option, /// Whether the channel is NSFW. #[serde(default)] pub nsfw: bool, /// The ID of the last message sent to the group. + #[serde(default)] pub last_message_id: Option, /// The ID of the parent category of the channel. + #[serde(default)] pub parent_id: Option, /// When the last message was pinned. + #[serde(default, skip_serializing_if = "Option::is_none")] pub last_pin_timestamp: Option>, } @@ -112,8 +116,7 @@ mod tests { "nsfw": true, "topic": "Rumors about Half Life 3", "last_message_id": "155117677105512449", - "parent_id": "399942396007890945", - "last_pin_timestamp": null + "parent_id": "399942396007890945" }); let channel = NewsChannel { id: ChannelId::from(41771983423143937), diff --git a/src/model/channel/guild/store_channel.rs b/src/model/channel/guild/store_channel.rs index 64c196b..4bb0aff 100644 --- a/src/model/channel/guild/store_channel.rs +++ b/src/model/channel/guild/store_channel.rs @@ -30,6 +30,7 @@ pub struct StoreChannel { #[serde(default)] pub nsfw: bool, /// The ID of the parent category of the channel. + #[serde(default)] pub parent_id: Option, } diff --git a/src/model/channel/guild/text_channel.rs b/src/model/channel/guild/text_channel.rs index 8dc804c..d27320b 100644 --- a/src/model/channel/guild/text_channel.rs +++ b/src/model/channel/guild/text_channel.rs @@ -1,4 +1,5 @@ use chrono::{DateTime, FixedOffset}; +use num_traits::Zero; use serde::{Deserialize, Serialize}; use crate::model::channel::permissions::PermissionOverwrite; @@ -7,7 +8,7 @@ use crate::model::id::{ChannelId, GuildId, MessageId}; /// A text channel in a [`Guild`]. /// -/// [`Guild`]: TODO +/// [`Guild`]: ../../guild/struct.Guild.html #[non_exhaustive] #[derive(Clone, Debug, Deserialize, Serialize)] pub struct TextChannel { @@ -30,11 +31,13 @@ pub struct TextChannel { /// The name of the channel. pub name: String, /// The topic of the channel. + #[serde(default)] pub topic: Option, /// Whether the channel is NSFW. #[serde(default)] pub nsfw: bool, /// The ID of the last message sent to the group. + #[serde(default)] pub last_message_id: Option, /// The amount of seconds a user has to wait before sending another message /// (0 - 216000s). @@ -43,11 +46,14 @@ pub struct TextChannel { /// [`MANAGE_CHANNEL`], are unaffected. #[doc = "\n[`MANAGE_MESSAGES`]: ../permissions/struct.Permissions.html#associatedconstant.MANAGE_MESSAGES"] #[doc = "\n[`MANAGE_CHANNEL`]: ../permissions/struct.Permissions.html#associatedconstant.MANAGE_CHANNEL"] - #[serde(default, rename = "rate_limit_per_user")] + #[serde(rename = "rate_limit_per_user")] + #[serde(default, skip_serializing_if = "Zero::is_zero")] pub rate_limit: u16, /// The ID of the parent category of the channel. + #[serde(default)] pub parent_id: Option, /// When the last message was pinned. + #[serde(default, skip_serializing_if = "Option::is_none")] pub last_pin_timestamp: Option>, } @@ -125,8 +131,7 @@ mod tests { "nsfw": true, "topic": "24/7 chat about how to gank Mike #2", "last_message_id": "155117677105512449", - "parent_id": "399942396007890945", - "last_pin_timestamp": null + "parent_id": "399942396007890945" }); let channel = TextChannel { id: ChannelId::from(41771983423143937), diff --git a/src/model/channel/guild/voice_channel.rs b/src/model/channel/guild/voice_channel.rs index fc030e3..22ae2f0 100644 --- a/src/model/channel/guild/voice_channel.rs +++ b/src/model/channel/guild/voice_channel.rs @@ -6,7 +6,7 @@ use crate::model::id::{ChannelId, GuildId}; /// A voice channel in a [`Guild`]. /// -/// [`Guild`]: TODO +/// [`Guild`]: ../../guild/struct.Guild.html #[non_exhaustive] #[derive(Clone, Debug, Deserialize, Serialize)] pub struct VoiceChannel { @@ -34,6 +34,7 @@ pub struct VoiceChannel { /// unlimited. pub user_limit: u8, /// The ID of the parent category of the channel. + #[serde(default)] pub parent_id: Option, } diff --git a/src/model/channel/message/embed.rs b/src/model/channel/message/embed.rs index 45cd023..04dca04 100644 --- a/src/model/channel/message/embed.rs +++ b/src/model/channel/message/embed.rs @@ -4,6 +4,7 @@ use chrono::{DateTime, FixedOffset}; use serde::{Deserialize, Serialize}; use crate::model::color::Color; +use crate::model::utils::is_false; /// Embedded content in a [`Message`]. /// @@ -12,6 +13,7 @@ use crate::model::color::Color; #[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)] pub struct Embed { /// The title of the embed. + #[serde(default, skip_serializing_if = "Option::is_none")] pub title: Option, /// The type of the embed. /// @@ -21,28 +23,37 @@ pub struct Embed { #[serde(rename = "type")] pub kind: EmbedType, /// The description of the embed. + #[serde(default, skip_serializing_if = "Option::is_none")] pub description: Option, /// The URL of the embed. + #[serde(default, skip_serializing_if = "Option::is_none")] pub url: Option, /// The timestamp of the embed. + #[serde(default, skip_serializing_if = "Option::is_none")] pub timestamp: Option>, /// The color code of the embed. #[serde(default, alias = "colour")] pub color: Color, /// The footer information of the embed. + #[serde(default, skip_serializing_if = "Option::is_none")] pub footer: Option, /// The image information of the embed. + #[serde(default, skip_serializing_if = "Option::is_none")] pub image: Option, /// The thumbnail information of the embed. + #[serde(default, skip_serializing_if = "Option::is_none")] pub thumbnail: Option, /// The video information of the embed. + #[serde(default, skip_serializing_if = "Option::is_none")] pub video: Option, /// The provider information of the embed. + #[serde(default, skip_serializing_if = "Option::is_none")] pub provider: Option, /// The author information of the embed. + #[serde(default, skip_serializing_if = "Option::is_none")] pub author: Option, /// The additional fields of the embed. - #[serde(default)] + #[serde(default, skip_serializing_if = "Vec::is_empty")] pub fields: Vec, } @@ -91,8 +102,10 @@ pub struct EmbedFooter { /// The URL of the footer icon. /// /// Only supports HTTP(S). + #[serde(default, skip_serializing_if = "Option::is_none")] pub icon_url: Option, /// The proxied URL of the footer icon. + #[serde(default, skip_serializing_if = "Option::is_none")] pub proxy_icon_url: Option, } @@ -155,6 +168,7 @@ pub struct EmbedProvider { /// The name of the provider. pub name: String, /// The URL of the provider. + #[serde(default, skip_serializing_if = "Option::is_none")] pub url: Option, } @@ -167,12 +181,15 @@ pub struct EmbedAuthor { /// The author name. pub name: String, /// The URL of the author. + #[serde(default, skip_serializing_if = "Option::is_none")] pub url: Option, /// The URL of the author icon. /// /// Only supports HTTP(S). + #[serde(default, skip_serializing_if = "Option::is_none")] pub icon_url: Option, /// The proxied URL of the author icon. + #[serde(default, skip_serializing_if = "Option::is_none")] pub proxy_icon_url: Option, } @@ -187,6 +204,6 @@ pub struct EmbedField { /// The value of the embed. pub value: String, /// Whether the field should be displayed inline. - #[serde(default)] + #[serde(default, skip_serializing_if = "is_false")] pub inline: bool, } diff --git a/src/model/channel/message/mod.rs b/src/model/channel/message/mod.rs index 260490a..2aa47bb 100644 --- a/src/model/channel/message/mod.rs +++ b/src/model/channel/message/mod.rs @@ -34,7 +34,8 @@ pub struct Message { pub channel_id: ChannelId, /// The ID of the [`Guild`] the message was sent in. /// - /// [`Guild`]: TODO + /// [`Guild`]: ../../guild/struct.Guild.html + #[serde(default, skip_serializing_if = "Option::is_none")] pub guild_id: Option, /// The author of the message. /// @@ -53,20 +54,24 @@ pub struct Message { /// sent in a guild. /// /// [`author`]: #structfield.author + #[serde(default, skip_serializing_if = "Option::is_none")] pub member: Option, /// The content of the message. pub content: String, /// When the message was sent. pub timestamp: DateTime, /// When the message was edited, if the message has been edited. + #[serde(default)] pub edited_timestamp: Option>, /// Whether the message was sent as a TTS (Text-To-Speech) message. pub tts: bool, /// Whether the message mentions everyone. pub mention_everyone: bool, /// The users specifically mentioned in the message. + #[serde(default)] pub mentions: Vec, /// The roles specifically mentioned in the message. + #[serde(default)] pub mention_roles: Vec, /// The channels specifically mentioned in the message. /// @@ -87,24 +92,29 @@ pub struct Message { pub pinned: bool, /// The webhook ID that generated the message, if the message was generated /// by a webhook. + #[serde(default, skip_serializing_if = "Option::is_none")] pub webhook_id: Option, /// The type of message. #[serde(rename = "type")] pub kind: MessageType, /// The Rich Presence activity information sent with related embeds. + #[serde(default, skip_serializing_if = "Option::is_none")] pub activity: Option, /// The Rich Presence application information sent with related embeds. + #[serde(default, skip_serializing_if = "Option::is_none")] pub application: Option, /// The reference data sent with a crossposted message. + #[serde(default, skip_serializing_if = "Option::is_none")] pub message_reference: Option, /// Flags describing extra features of the message. - #[serde(default)] + #[serde(default, skip_serializing_if = "MessageFlags::is_empty")] pub flags: MessageFlags, } /// Type of a [`Message`]. /// /// [`Message`]: struct.Message.html +// TODO: Add docs. #[allow(missing_docs)] #[int_enum::int_enum(u8)] #[non_exhaustive] @@ -138,10 +148,12 @@ impl Default for MessageType { #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Deserialize, Serialize)] pub struct MessageReference { /// The ID of the originating message. + #[serde(default, skip_serializing_if = "Option::is_none")] pub message_id: Option, /// The ID of the originating channel. pub channel_id: ChannelId, /// The ID of the originating guild. + #[serde(default, skip_serializing_if = "Option::is_none")] pub guild_id: Option, } @@ -274,8 +286,6 @@ mod tests { use super::*; - // TODO: Add serialize message serde tests. - #[test] fn test_deserialize_message() { let value = json!({ @@ -350,6 +360,80 @@ mod tests { assert_eq_fields!(message, deserialized); } + #[test] + fn test_serialize_message() { + let value = json!({ + "reactions": [ + { + "count": 1, + "me": false, + "emoji": { + "id": null, + "name": "🔥" + } + } + ], + "attachments": [], + "tts": false, + "embeds": [], + "timestamp": "2017-07-11T17:27:07.299+00:00", + "mention_channels": [], + "mention_everyone": false, + "id": "334385199974967042", + "pinned": false, + "edited_timestamp": null, + "author": { + "username": "Mason", + "discriminator": "9999", + "id": "53908099506183680", + "avatar": "a_bab14f271d565501444b2ca3be944b25" + }, + "mention_roles": [], + "content": "Supa Hot", + "channel_id": "290926798999357250", + "mentions": [], + "type": 0 + }); + let message = Message { + id: MessageId::from(334385199974967042), + channel_id: ChannelId::from(290926798999357250), + guild_id: None, + author: User { + id: UserId::from(53908099506183680), + name: "Mason".to_owned(), + discriminator: "9999".parse().unwrap(), + avatar: Some("a_bab14f271d565501444b2ca3be944b25".to_owned()), + bot: false, + system: false, + }, + member: None, + content: "Supa Hot".to_owned(), + timestamp: "2017-07-11T17:27:07.299+00:00".parse().unwrap(), + edited_timestamp: None, + tts: false, + mention_everyone: false, + mentions: vec![], + mention_roles: vec![], + mention_channels: vec![], + attachments: vec![], + embeds: vec![], + reactions: vec![Reaction { + count: 1, + me: false, + emoji: PartialEmoji::standard("🔥"), + }], + pinned: false, + webhook_id: None, + kind: MessageType::Default, + activity: None, + application: None, + message_reference: None, + flags: MessageFlags::default(), + }; + + assert_eq!(value, serde_json::to_value(&message).unwrap()); + } + #[test] fn test_deserialize_crossposted_message() { let value = json!({ @@ -366,7 +450,7 @@ mod tests { "attachments": [], "tts": false, "embeds": [], - "timestamp": "2017-07-11T17:27:07.299000+00:00", + "timestamp": "2017-07-11T17:27:07.299+00:00", "mention_everyone": false, "id": "334385199974967042", "pinned": false, @@ -411,13 +495,18 @@ mod tests { }, member: None, content: "Big news! In this <#278325129692446722> channel!".to_owned(), - timestamp: "2017-07-11T17:27:07.299000+00:00".parse().unwrap(), + timestamp: "2017-07-11T17:27:07.299+00:00".parse().unwrap(), edited_timestamp: None, tts: false, mention_everyone: false, mentions: vec![], mention_roles: vec![], - mention_channels: vec![], + mention_channels: vec![MentionedChannel { + id: ChannelId::from(278325129692446722), + guild_id: GuildId::from(278325129692446720), + kind: ChannelType::News, + name: "big-news".to_owned(), + }], attachments: vec![], embeds: vec![], reactions: vec![Reaction { @@ -442,6 +531,102 @@ mod tests { assert_eq_fields!(message, deserialized); } + #[test] + fn test_serialize_crossposted_message() { + let value = json!({ + "reactions": [ + { + "count": 1, + "me": false, + "emoji": { + "id": null, + "name": "🔥" + } + } + ], + "attachments": [], + "tts": false, + "embeds": [], + "timestamp": "2017-07-11T17:27:07.299+00:00", + "mention_everyone": false, + "id": "334385199974967042", + "pinned": false, + "edited_timestamp": null, + "author": { + "username": "Mason", + "discriminator": "9999", + "id": "53908099506183680", + "avatar": "a_bab14f271d565501444b2ca3be944b25" + }, + "mention_roles": [], + "mention_channels": [ + { + "id": "278325129692446722", + "guild_id": "278325129692446720", + "name": "big-news", + "type": 5 + } + ], + "content": "Big news! In this <#278325129692446722> channel!", + "channel_id": "290926798999357250", + "mentions": [], + "type": 0, + "flags": 2, + "message_reference": { + "channel_id": "278325129692446722", + "guild_id": "278325129692446720", + "message_id": "306588351130107906" + } + }); + let message = Message { + id: MessageId::from(334385199974967042), + channel_id: ChannelId::from(290926798999357250), + guild_id: None, + author: User { + id: UserId::from(53908099506183680), + name: "Mason".to_owned(), + discriminator: "9999".parse().unwrap(), + avatar: Some("a_bab14f271d565501444b2ca3be944b25".to_owned()), + bot: false, + system: false, + }, + member: None, + content: "Big news! In this <#278325129692446722> channel!".to_owned(), + timestamp: "2017-07-11T17:27:07.299+00:00".parse().unwrap(), + edited_timestamp: None, + tts: false, + mention_everyone: false, + mentions: vec![], + mention_roles: vec![], + mention_channels: vec![MentionedChannel { + id: ChannelId::from(278325129692446722), + guild_id: GuildId::from(278325129692446720), + kind: ChannelType::News, + name: "big-news".to_owned(), + }], + attachments: vec![], + embeds: vec![], + reactions: vec![Reaction { + count: 1, + me: false, + emoji: PartialEmoji::standard("🔥"), + }], + pinned: false, + webhook_id: None, + kind: MessageType::Default, + activity: None, + application: None, + message_reference: Some(MessageReference { + message_id: Some(MessageId::from(306588351130107906)), + channel_id: ChannelId::from(278325129692446722), + guild_id: Some(GuildId::from(278325129692446720)), + }), + flags: MessageFlags::IS_CROSSPOST, + }; + + assert_eq!(value, serde_json::to_value(&message).unwrap()); + } + #[test] fn test_deserialize_message_flags() { let value = json!(2); diff --git a/src/model/channel/mod.rs b/src/model/channel/mod.rs index 28fef3e..eb0af95 100644 --- a/src/model/channel/mod.rs +++ b/src/model/channel/mod.rs @@ -71,7 +71,7 @@ pub enum Channel { Group(Group), /// A channel within a [`Guild`]. /// - /// [`Guild`]: TODO + /// [`Guild`]: ../guild/struct.Guild.html Guild(GuildChannel), } diff --git a/src/model/gateway/presence.rs b/src/model/gateway/presence.rs index b49d9a0..7709144 100644 --- a/src/model/gateway/presence.rs +++ b/src/model/gateway/presence.rs @@ -205,9 +205,7 @@ mod tests { "id": "80351110224678912", "username": "Nelly", "discriminator": "1337", - "avatar": "8342729096ea3675442027381ff50dfe", - "bot": false, - "system": false + "avatar": "8342729096ea3675442027381ff50dfe" }); let user = PartialUser::Full(User { id: UserId::from(80351110224678912), diff --git a/src/model/guild/emoji/mod.rs b/src/model/guild/emoji/mod.rs index ec7e2e4..7edf274 100644 --- a/src/model/guild/emoji/mod.rs +++ b/src/model/guild/emoji/mod.rs @@ -6,6 +6,7 @@ use serde::{Deserialize, Serialize}; use crate::model::id::RoleId; use crate::model::user::User; +use crate::model::utils::default_true; pub use self::partial::{CustomEmoji, PartialEmoji}; @@ -23,7 +24,7 @@ pub struct Emoji { #[serde(skip_serializing_if = "Option::is_none")] pub user: Option, /// Whether the name requires colons to be used by a client. - #[serde(default)] + #[serde(default = "default_true")] pub require_colons: bool, /// Whether the emoji was created by an integration service. #[serde(default)] @@ -96,9 +97,7 @@ mod tests { "username": "Luigi", "discriminator": "0002", "id": "96008815106887111", - "avatar": null, - "bot": false, - "system": false, + "avatar": null }, "require_colons": true, "managed": false, diff --git a/src/model/guild/emoji/partial.rs b/src/model/guild/emoji/partial.rs index 64febc2..aafd21a 100644 --- a/src/model/guild/emoji/partial.rs +++ b/src/model/guild/emoji/partial.rs @@ -23,6 +23,7 @@ pub struct CustomEmoji { /// available (for example, if it was deleted from the guild). pub name: Option>, /// Whether the custom emoji is animated. + #[serde(default)] pub animated: bool, } diff --git a/src/model/guild/member.rs b/src/model/guild/member.rs index 721d681..b212909 100644 --- a/src/model/guild/member.rs +++ b/src/model/guild/member.rs @@ -15,8 +15,10 @@ pub struct Member { /// The user the member represents. pub user: User, /// The nickname of the user, if one is set. + #[serde(default, skip_serializing_if = "Option::is_none")] pub nick: Option, /// When the user used their Nitro boost on the guild. + #[serde(default, skip_serializing_if = "Option::is_none")] pub premium_since: Option>, } wrap!(Member => mut member: PartialMember); diff --git a/src/model/guild/mod.rs b/src/model/guild/mod.rs index 07d72de..7a9fea0 100644 --- a/src/model/guild/mod.rs +++ b/src/model/guild/mod.rs @@ -5,17 +5,16 @@ mod emoji; mod member; mod role; -use std::borrow::Cow; use std::collections::{HashMap, HashSet}; -use std::fmt::{self, Display}; -use std::ops::Deref; use chrono::{DateTime, FixedOffset}; use num_traits::Zero; use serde::{Deserialize, Serialize}; use crate::model::channel::GuildChannel; +use crate::model::gateway::presence::Presence; use crate::model::id::{ApplicationId, ChannelId, EmojiId, GuildId, RoleId, UserId}; +use crate::model::misc::Locale; use crate::model::permissions::Permissions; use crate::model::utils::{is_false, serde_id_map}; use crate::model::voice::{VoiceRegionId, VoiceState}; @@ -24,7 +23,6 @@ pub use self::audit_log::AuditLogEvent; pub use self::emoji::{CustomEmoji, Emoji, PartialEmoji}; pub use self::member::{Member, PartialMember}; pub use self::role::Role; -use crate::model::gateway::presence::Presence; /// The required level of criteria a user must meet, prior to being able to send /// messages in a [`Guild`]. @@ -161,94 +159,6 @@ impl Default for PremiumTier { } } -/// A locale. -#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Deserialize, Serialize)] -#[serde(transparent)] -pub struct Locale(pub(crate) Cow<'static, str>); - -impl Locale { - /// Locale defaults to `en-US`. - pub const DEFAULT: Locale = Locale::from_static("en-US"); - - /// Creates a voice region ID from a static string. - pub const fn from_static(id: &'static str) -> Locale { - Locale(Cow::Borrowed(id)) - } - - /// Creates a voice region ID from a string. - pub fn from_string>(id: S) -> Locale { - Locale(Cow::Owned(id.into())) - } -} - -impl Default for Locale { - fn default() -> Self { - Locale::DEFAULT - } -} - -impl From<&'static str> for Locale { - fn from(s: &'static str) -> Self { - Locale(Cow::Borrowed(s)) - } -} - -impl From for Locale { - fn from(s: String) -> Self { - Locale(Cow::Owned(s)) - } -} - -impl Display for Locale { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - self.0.fmt(f) - } -} - -impl Deref for Locale { - type Target = str; - - fn deref(&self) -> &Self::Target { - self.0.deref() - } -} - -impl AsRef for Locale { - fn as_ref(&self) -> &str { - self.0.as_ref() - } -} - -impl From for String { - fn from(id: Locale) -> Self { - id.0.to_string() - } -} - -impl From for Cow<'static, str> { - fn from(id: Locale) -> Self { - id.0 - } -} - -impl PartialEq for Locale { - fn eq(&self, other: &str) -> bool { - self.0 == other - } -} - -impl PartialEq for Locale { - fn eq(&self, other: &String) -> bool { - self.0 == &other[..] - } -} - -impl<'a> PartialEq> for Locale { - fn eq(&self, other: &Cow<'a, str>) -> bool { - &self.0[..] == &other[..] - } -} - /// A guild with partial information. #[non_exhaustive] #[derive(Clone, Debug, Deserialize, Serialize)] diff --git a/src/model/misc.rs b/src/model/misc.rs new file mode 100644 index 0000000..177e835 --- /dev/null +++ b/src/model/misc.rs @@ -0,0 +1,95 @@ +//! Miscellaneous models. + +use std::borrow::Cow; +use std::fmt::{self, Display}; +use std::ops::Deref; + +use serde::{Deserialize, Serialize}; + +/// A locale. +#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Deserialize, Serialize)] +#[serde(transparent)] +pub struct Locale(pub(crate) Cow<'static, str>); + +impl Locale { + /// Locale defaults to `en-US`. + pub const DEFAULT: Locale = Locale::from_static("en-US"); + + /// Creates a voice region ID from a static string. + pub const fn from_static(id: &'static str) -> Locale { + Locale(Cow::Borrowed(id)) + } + + /// Creates a voice region ID from a string. + pub fn from_string>(id: S) -> Locale { + Locale(Cow::Owned(id.into())) + } +} + +impl Default for Locale { + fn default() -> Self { + Locale::DEFAULT + } +} + +impl From<&'static str> for Locale { + fn from(s: &'static str) -> Self { + Locale(Cow::Borrowed(s)) + } +} + +impl From for Locale { + fn from(s: String) -> Self { + Locale(Cow::Owned(s)) + } +} + +impl Display for Locale { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.0.fmt(f) + } +} + +impl Deref for Locale { + type Target = str; + + fn deref(&self) -> &Self::Target { + self.0.deref() + } +} + +impl AsRef for Locale { + fn as_ref(&self) -> &str { + self.0.as_ref() + } +} + +impl From for String { + fn from(id: Locale) -> Self { + id.0.to_string() + } +} + +impl From for Cow<'static, str> { + fn from(id: Locale) -> Self { + id.0 + } +} + +impl PartialEq for Locale { + fn eq(&self, other: &str) -> bool { + self.0 == other + } +} + +impl PartialEq for Locale { + fn eq(&self, other: &String) -> bool { + self.0 == &other[..] + } +} + +impl<'a> PartialEq> for Locale { + fn eq(&self, other: &Cow<'a, str>) -> bool { + &self.0[..] == &other[..] + } +} diff --git a/src/model/mod.rs b/src/model/mod.rs index e715e3f..0fa161e 100644 --- a/src/model/mod.rs +++ b/src/model/mod.rs @@ -7,6 +7,7 @@ pub mod color; pub mod gateway; pub mod guild; pub mod id; +pub mod misc; pub mod permissions; pub mod snowflake; pub mod user; diff --git a/src/model/user/mod.rs b/src/model/user/mod.rs index 26b1d8e..5034d36 100644 --- a/src/model/user/mod.rs +++ b/src/model/user/mod.rs @@ -8,6 +8,8 @@ mod flags; use serde::{Deserialize, Serialize}; use crate::model::id::UserId; +use crate::model::misc::Locale; +use crate::model::utils::is_false; pub use self::discriminator::{Discriminator, DiscriminatorParseError}; pub use self::flags::UserFlags; @@ -20,20 +22,26 @@ pub struct ClientUser { user: User, /// Whether the user has multi-factor authentication enabled on their /// account. + #[serde(default)] pub mfa_enabled: bool, /// The chosen language of the user. - pub locale: String, + #[serde(default)] + pub locale: Locale, /// Whether the email on the user account is verified. + #[serde(default)] pub verified: bool, /// The email of the user. + #[serde(default)] pub email: Option, /// The [flags] on the user account. /// /// [flags]: struct.UserFlags.html + #[serde(default)] pub flags: UserFlags, /// The [type of Nitro subscription][type] on the user account. /// /// [type]: struct.PremiumType.html + #[serde(default, skip_serializing_if = "Option::is_none")] pub premium_type: Option, } wrap!(ClientUser => mut user: User); @@ -52,11 +60,11 @@ pub struct User { /// The avatar hash of the user. pub avatar: Option, /// Whether the user is a bot. - #[serde(default)] + #[serde(default, skip_serializing_if = "is_false")] pub bot: bool, /// Whether the user is an Official Discord System user (part of the urgent /// message system). - #[serde(default)] + #[serde(default, skip_serializing_if = "is_false")] pub system: bool, } @@ -95,8 +103,8 @@ mod tests { name: "Nelly".to_owned(), discriminator: "1337".parse().unwrap(), avatar: Some("8342729096ea3675442027381ff50dfe".to_owned()), - bot: Default::default(), - system: Default::default(), + bot: bool::default(), + system: bool::default(), }; let deserialized = User::deserialize(&value).unwrap(); @@ -109,9 +117,7 @@ mod tests { "id": "225336713231204353", "username": "Juici", "avatar": "a_e8b3a198dab6af59aacd1072bbedb255", - "discriminator": "0001", - "bot": false, - "system": false, + "discriminator": "0001" }); let user = User { id: UserId::from(225336713231204353), @@ -147,10 +153,10 @@ mod tests { discriminator: "9999".parse().unwrap(), avatar: Some("33ecab261d4681afa4d85a04691c4a01".to_owned()), bot: false, - system: Default::default(), + system: bool::default(), }, mfa_enabled: true, - locale: "en-US".to_string(), + locale: Locale::from_static("en-US"), verified: true, email: Some("test@example.com".to_owned()), flags: UserFlags::from_bits(64).unwrap(), @@ -182,10 +188,10 @@ mod tests { discriminator: "0369".parse().unwrap(), avatar: None, bot: true, - system: Default::default(), + system: bool::default(), }, mfa_enabled: true, - locale: "en-US".to_string(), + locale: Locale::from_static("en-US"), verified: true, email: None, flags: UserFlags::NONE, @@ -204,13 +210,11 @@ mod tests { "discriminator": "0369", "avatar": null, "bot": true, - "system": false, "mfa_enabled": true, "locale": "en-US", "verified": true, "email": null, - "premium_type": null, - "flags": 0, + "flags": 0 }); let user = ClientUser { user: User { @@ -222,7 +226,7 @@ mod tests { system: false, }, mfa_enabled: true, - locale: "en-US".to_string(), + locale: Locale::from_static("en-US"), verified: true, email: None, flags: UserFlags::NONE, diff --git a/src/model/utils.rs b/src/model/utils.rs index 3c0afef..c630b1c 100644 --- a/src/model/utils.rs +++ b/src/model/utils.rs @@ -3,6 +3,11 @@ pub fn is_false(b: &bool) -> bool { !b } +/// Used in serde `default` attribute. +pub fn default_true() -> bool { + true +} + macro_rules! int_visitor { (($($vis:tt)*) $name:ident: $type:ty) => { #[derive(Debug)] From 134330f7fa9ebfecd81f53eac9e03a737ae9b573 Mon Sep 17 00:00:00 2001 From: James Whaley Date: Thu, 16 Jan 2020 21:07:29 +0000 Subject: [PATCH 43/44] Fix order of enum attributes Fixes #5. --- src/constants.rs | 2 +- src/model/channel/message/mod.rs | 2 +- src/model/channel/message/rich_presence.rs | 2 +- src/model/guild/audit_log.rs | 2 +- src/model/user/mod.rs | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/constants.rs b/src/constants.rs index cf25084..1e1748b 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -12,8 +12,8 @@ pub const MESSAGE_MAX_LENGTH: usize = 2000; pub const USER_AGENT: &str = concat!("DiscordBot (", pkg_repo!(), ", ", pkg_version!(), ")"); /// Gateway opcodes. -#[int_enum::int_enum(u8)] #[non_exhaustive] +#[int_enum::int_enum(u8)] #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub enum OpCode { /// Dispatches an event. diff --git a/src/model/channel/message/mod.rs b/src/model/channel/message/mod.rs index 2aa47bb..584a2ce 100644 --- a/src/model/channel/message/mod.rs +++ b/src/model/channel/message/mod.rs @@ -116,8 +116,8 @@ pub struct Message { /// [`Message`]: struct.Message.html // TODO: Add docs. #[allow(missing_docs)] -#[int_enum::int_enum(u8)] #[non_exhaustive] +#[int_enum::int_enum(u8)] #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub enum MessageType { Default = 0, diff --git a/src/model/channel/message/rich_presence.rs b/src/model/channel/message/rich_presence.rs index b4326d1..236e00f 100644 --- a/src/model/channel/message/rich_presence.rs +++ b/src/model/channel/message/rich_presence.rs @@ -16,8 +16,8 @@ pub struct MessageActivity { /// /// [`Message`]: struct.MessageActivity.html #[allow(missing_docs)] -#[int_enum::int_enum(u8)] #[non_exhaustive] +#[int_enum::int_enum(u8)] #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub enum MessageActivityType { Join = 1, diff --git a/src/model/guild/audit_log.rs b/src/model/guild/audit_log.rs index 5d2e842..462ada8 100644 --- a/src/model/guild/audit_log.rs +++ b/src/model/guild/audit_log.rs @@ -1,8 +1,8 @@ /// The [type of action] that occurred in an [`AuditLogEntry`]. /// /// [type of action]: https://discordapp.com/developers/docs/resources/audit-log#audit-log-entry-object-audit-log-events -#[int_enum::int_enum(u8)] #[non_exhaustive] +#[int_enum::int_enum(u8)] #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub enum AuditLogEvent { /// The guild settings were updated. diff --git a/src/model/user/mod.rs b/src/model/user/mod.rs index 5034d36..52a9865 100644 --- a/src/model/user/mod.rs +++ b/src/model/user/mod.rs @@ -71,8 +71,8 @@ pub struct User { /// The level of premium a [`User`] has. /// /// [`User`]: struct.User.html -#[int_enum::int_enum(u8)] #[non_exhaustive] +#[int_enum::int_enum(u8)] #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub enum PremiumType { /// Nitro Classic. From 16294b366309b764dab659218e17bb943b4d07a0 Mon Sep 17 00:00:00 2001 From: James Whaley Date: Thu, 16 Jan 2020 21:45:54 +0000 Subject: [PATCH 44/44] Bump version to 0.2.0 Skip version 0.1.0, since it was previously yanked. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index a609f92..13fe2a8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "strife" -version = "0.0.1" +version = "0.2.0" authors = ["James Whaley "] description = "A lightweight library for the Discord API." license = "MIT"