diff options
| author | Zeyla Hellyer <[email protected]> | 2017-04-15 22:37:49 -0700 |
|---|---|---|
| committer | Zeyla Hellyer <[email protected]> | 2017-04-19 14:53:32 -0700 |
| commit | e1079e9a03473f9ec67414628d5b84e7ea1b5b38 (patch) | |
| tree | c872eced06b3a1dfce04e876f1d7c381ee926f29 /src | |
| parent | Add a test suite for event deserialization (diff) | |
| download | serenity-e1079e9a03473f9ec67414628d5b84e7ea1b5b38.tar.xz serenity-e1079e9a03473f9ec67414628d5b84e7ea1b5b38.zip | |
Check for embed lengths on message sends
Due to the maximum length of the textual content of an embed being 4000,
this should be checked prior to message sending.
Embeds have a fairly new limit of only being 4000 character long, at
maximum. The length of these fields - where present - should be summed,
so that bad requests are not made with embeds that are too large in
text.
The fields that count as "textual" includes:
- author name
- description
- `name` and `value` of each field
- footer text
- title
If this exceeds the limit - currently at 4000 unicode codepoints - then
a `ClientError::EmbedTooLarge` will be returned, including the number
of codepoints overflowing.
Diffstat (limited to 'src')
| -rw-r--r-- | src/client/error.rs | 3 | ||||
| -rw-r--r-- | src/constants.rs | 4 | ||||
| -rw-r--r-- | src/model/channel/channel_id.rs | 9 | ||||
| -rw-r--r-- | src/model/channel/message.rs | 65 | ||||
| -rw-r--r-- | src/utils/macros.rs | 6 |
5 files changed, 75 insertions, 12 deletions
diff --git a/src/client/error.rs b/src/client/error.rs index 6a18b7a..225f2d3 100644 --- a/src/client/error.rs +++ b/src/client/error.rs @@ -57,6 +57,9 @@ pub enum Error { /// When attempting to delete a number of days' worth of messages that is /// not allowed. DeleteMessageDaysAmount(u8), + /// Indicates that the textual content of an embed exceeds the maximum + /// length. + EmbedTooLarge(u64), /// When there is an error from Discord for a specific action, such as /// [`ErrorCode::EditByOtherAuthor`]. This is a friendlier representation of /// the numerical error codes Discord provides. diff --git a/src/constants.rs b/src/constants.rs index 0ec973c..de670c9 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -1,5 +1,5 @@ -use std::result::Result as StdResult; - +/// The maximum length of the textual size of an embed. +pub const EMBED_MAX_LENGTH: u16 = 4000; /// The gateway version used by the library. The gateway URI is retrieved via /// the REST API. pub const GATEWAY_VERSION: u8 = 6; diff --git a/src/model/channel/channel_id.rs b/src/model/channel/channel_id.rs index 5b238b0..79a6fa4 100644 --- a/src/model/channel/channel_id.rs +++ b/src/model/channel/channel_id.rs @@ -418,13 +418,8 @@ impl ChannelId { where F: FnOnce(CreateMessage) -> CreateMessage { let map = f(CreateMessage::default()).0; - if let Some(content) = map.get(&"content".to_owned()) { - if let Value::String(ref content) = *content { - if let Some(length_over) = Message::overflow_length(content) { - return Err(Error::Client(ClientError::MessageTooLong(length_over))); - } - } - } + Message::check_content_length(&map)?; + Message::check_embed_length(&map)?; rest::send_message(self.0, &Value::Object(map)) } diff --git a/src/model/channel/message.rs b/src/model/channel/message.rs index 6bbb1a2..11855b8 100644 --- a/src/model/channel/message.rs +++ b/src/model/channel/message.rs @@ -431,6 +431,71 @@ impl Message { -> Result<Vec<User>> where R: Into<ReactionType>, U: Into<UserId> { self.reaction_users(reaction_type, limit, after) } + + #[doc(hidden)] + pub fn check_content_length(map: &JsonMap) -> Result<()> { + if let Some(content) = map.get("content") { + if let Value::String(ref content) = *content { + if let Some(length_over) = Message::overflow_length(content) { + return Err(Error::Client(ClientError::MessageTooLong(length_over))); + } + } + } + + Ok(()) + } + + #[doc(hidden)] + pub fn check_embed_length(map: &JsonMap) -> Result<()> { + let embed = match map.get("embed") { + Some(&Value::Object(ref value)) => value, + _ => return Ok(()), + }; + + let mut total: usize = 0; + + if let Some(&Value::Object(ref author)) = embed.get("author") { + if let Some(&Value::Object(ref name)) = author.get("name") { + total += name.len(); + } + } + + if let Some(&Value::String(ref description)) = embed.get("description") { + total += description.len(); + } + + if let Some(&Value::Array(ref fields)) = embed.get("fields") { + for field_as_value in fields { + if let Value::Object(ref field) = *field_as_value { + if let Some(&Value::String(ref field_name)) = field.get("name") { + total += field_name.len(); + } + + if let Some(&Value::String(ref field_value)) = field.get("value") { + total += field_value.len(); + } + } + } + } + + if let Some(&Value::Object(ref footer)) = embed.get("footer") { + if let Some(&Value::String(ref text)) = footer.get("text") { + total += text.len(); + } + } + + if let Some(&Value::String(ref title)) = embed.get("title") { + total += title.len(); + } + + if total > constants::EMBED_MAX_LENGTH as usize { + Ok(()) + } else { + let overflow = constants::EMBED_MAX_LENGTH as u64 - total as u64; + + Err(Error::Client(ClientError::EmbedTooLarge(overflow))) + } + } } impl From<Message> for MessageId { diff --git a/src/utils/macros.rs b/src/utils/macros.rs index be94046..27b3a9b 100644 --- a/src/utils/macros.rs +++ b/src/utils/macros.rs @@ -128,7 +128,7 @@ macro_rules! enum_number { } impl ::serde::Serialize for $name { - fn serialize<S>(&self, serializer: S) -> StdResult<S::Ok, S::Error> + fn serialize<S>(&self, serializer: S) -> ::std::result::Result<S::Ok, S::Error> where S: ::serde::Serializer { // Serialize the enum as a u64. @@ -137,7 +137,7 @@ macro_rules! enum_number { } impl ::serde::Deserialize for $name { - fn deserialize<D>(deserializer: D) -> StdResult<Self, D::Error> + fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error> where D: ::serde::Deserializer { struct Visitor; @@ -149,7 +149,7 @@ macro_rules! enum_number { formatter.write_str("positive integer") } - fn visit_u64<E>(self, value: u64) -> StdResult<$name, E> + fn visit_u64<E>(self, value: u64) -> ::std::result::Result<$name, E> where E: ::serde::de::Error { // Rust does not come with a simple way of converting a |