Skip to content

Commit c9811ba

Browse files
authored
Deserialize components properly on MessageUpdateEvent (#3316)
1 parent 29451cd commit c9811ba

File tree

4 files changed

+63
-50
lines changed

4 files changed

+63
-50
lines changed

src/model/channel/message.rs

Lines changed: 1 addition & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ use crate::gateway::ShardMessenger;
2222
#[cfg(feature = "model")]
2323
use crate::http::{CacheHttp, Http};
2424
use crate::model::prelude::*;
25-
use crate::model::utils::{deserialize_val, discord_colours, StrOrInt};
25+
use crate::model::utils::{deserialize_components, discord_colours, StrOrInt};
2626
#[cfg(all(feature = "model", feature = "cache"))]
2727
use crate::utils;
2828

@@ -151,54 +151,6 @@ pub struct Message {
151151
pub poll: Option<Box<Poll>>,
152152
}
153153

154-
// Custom deserialize function to deserialize components safely without knocking the whole message
155-
// out when new components are found but not supported.
156-
fn deserialize_components<'de, D>(deserializer: D) -> Result<Vec<ActionRow>, D::Error>
157-
where
158-
D: Deserializer<'de>,
159-
{
160-
struct ComponentsVisitor;
161-
162-
impl<'de> Visitor<'de> for ComponentsVisitor {
163-
type Value = Vec<ActionRow>;
164-
165-
fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
166-
formatter.write_str("a sequence of ActionRow elements")
167-
}
168-
169-
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
170-
where
171-
A: serde::de::SeqAccess<'de>,
172-
{
173-
let mut components = Vec::with_capacity(seq.size_hint().unwrap_or_default());
174-
175-
while let Some(map) = seq.next_element::<JsonMap>()? {
176-
// We deserialize only the `kind` field to determine the component type.
177-
// We later use this to check if its a supported component before deserializing the
178-
// entire payload.
179-
let raw_kind =
180-
map.get("type").ok_or_else(|| DeError::missing_field("type"))?.clone();
181-
let kind: i64 = deserialize_val(raw_kind)?;
182-
183-
// Action rows are the only top level component supported in serenity at this time.
184-
if kind == 1 {
185-
let value = Value::from(map);
186-
components
187-
.push(ActionRow::deserialize(value).map_err(serde::de::Error::custom)?);
188-
} else {
189-
// Top level component is not an action row and cannot be supported on
190-
// serenity@current without breaking changes, so we skip them.
191-
tracing::debug!("Skipping component with unsupported kind: {kind}");
192-
}
193-
}
194-
195-
Ok(components)
196-
}
197-
}
198-
199-
deserializer.deserialize_seq(ComponentsVisitor)
200-
}
201-
202154
#[cfg(feature = "model")]
203155
impl Message {
204156
/// Crossposts this message.

src/model/event.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ use crate::model::utils::{
1515
deserialize_val,
1616
emojis,
1717
members,
18+
optional_deserialize_components,
1819
remove_from_map,
1920
remove_from_map_opt,
2021
stickers,
@@ -531,6 +532,7 @@ pub struct MessageUpdateEvent {
531532
pub interaction_metadata: Option<Option<Box<MessageInteractionMetadata>>>,
532533
#[serde(default, deserialize_with = "deserialize_some")]
533534
pub thread: Option<Option<GuildChannel>>,
535+
#[serde(default, deserialize_with = "optional_deserialize_components")]
534536
pub components: Option<Vec<ActionRow>>,
535537
pub sticker_items: Option<Vec<StickerItem>>,
536538
pub position: Option<Option<u64>>,

src/model/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ pub use self::timestamp::Timestamp;
6262
pub mod prelude {
6363
pub(crate) use std::collections::HashMap;
6464

65-
pub(crate) use serde::de::{Error as DeError, Visitor};
65+
pub(crate) use serde::de::Visitor;
6666
pub(crate) use serde::{Deserialize, Deserializer};
6767

6868
pub use super::guild::automod::EventType as AutomodEventType;

src/model/utils.rs

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -450,3 +450,62 @@ where
450450
})
451451
.collect()
452452
}
453+
454+
// A function used for deserializing components within a MessageUpdateEvent.
455+
// Due to discord now sending the whole message payload, we don't need to distinguish between None
456+
// and empty, as such we always return Some.
457+
pub fn optional_deserialize_components<'de, D>(
458+
deserializer: D,
459+
) -> Result<Option<Vec<ActionRow>>, D::Error>
460+
where
461+
D: Deserializer<'de>,
462+
{
463+
deserialize_components(deserializer).map(Some)
464+
}
465+
466+
// Custom deserialize function to deserialize components safely without knocking the whole message
467+
// out when new components are found but not supported.
468+
pub fn deserialize_components<'de, D>(deserializer: D) -> Result<Vec<ActionRow>, D::Error>
469+
where
470+
D: Deserializer<'de>,
471+
{
472+
struct ComponentsVisitor;
473+
474+
impl<'de> Visitor<'de> for ComponentsVisitor {
475+
type Value = Vec<ActionRow>;
476+
477+
fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
478+
formatter.write_str("a sequence of ActionRow elements")
479+
}
480+
481+
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
482+
where
483+
A: serde::de::SeqAccess<'de>,
484+
{
485+
let mut components = Vec::with_capacity(seq.size_hint().unwrap_or_default());
486+
487+
while let Some(map) = seq.next_element::<JsonMap>()? {
488+
// We deserialize only the `kind` field to determine the component type.
489+
// We later use this to check if its a supported component before deserializing the
490+
// entire payload.
491+
let raw_kind =
492+
map.get("type").ok_or_else(|| DeError::missing_field("type"))?.clone();
493+
let kind: i64 = deserialize_val(raw_kind)?;
494+
495+
// Action rows are the only top level component supported in serenity at this time.
496+
if kind == 1 {
497+
let value = Value::from(map);
498+
components.push(ActionRow::deserialize(value).map_err(DeError::custom)?);
499+
} else {
500+
// Top level component is not an action row and cannot be supported on
501+
// serenity@current without breaking changes, so we skip them.
502+
tracing::debug!("Skipping component with unsupported kind: {kind}");
503+
}
504+
}
505+
506+
Ok(components)
507+
}
508+
}
509+
510+
deserializer.deserialize_seq(ComponentsVisitor)
511+
}

0 commit comments

Comments
 (0)