Skip to content

Commit

Permalink
feat(model, http): Add support for message forwarding (twilight-rs#2340)
Browse files Browse the repository at this point in the history
  • Loading branch information
suneettipirneni authored Jul 18, 2024
1 parent dab3dbc commit d767129
Show file tree
Hide file tree
Showing 11 changed files with 297 additions and 7 deletions.
1 change: 1 addition & 0 deletions twilight-cache-inmemory/src/event/interaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,7 @@ mod tests {
mention_everyone: false,
mention_roles: Vec::new(),
mentions: Vec::new(),
message_snapshots: Vec::new(),
pinned: false,
poll: None,
reactions: Vec::new(),
Expand Down
1 change: 1 addition & 0 deletions twilight-cache-inmemory/src/event/message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@ mod tests {
mention_everyone: false,
mention_roles: Vec::new(),
mentions: Vec::new(),
message_snapshots: Vec::new(),
pinned: false,
poll: None,
reactions: Vec::new(),
Expand Down
5 changes: 4 additions & 1 deletion twilight-cache-inmemory/src/model/message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use twilight_model::{
message::{
sticker::MessageSticker, Component, Embed, Message, MessageActivity,
MessageApplication, MessageCall, MessageFlags, MessageInteraction, MessageReference,
MessageType, Reaction, RoleSubscriptionData,
MessageSnapshot, MessageType, Reaction, RoleSubscriptionData,
},
Attachment, ChannelMention,
},
Expand Down Expand Up @@ -115,6 +115,7 @@ pub struct CachedMessage {
pub(crate) mention_everyone: bool,
pub(crate) mention_roles: Vec<Id<RoleMarker>>,
pub(crate) mentions: Vec<Id<UserMarker>>,
pub(crate) message_snapshots: Vec<MessageSnapshot>,
pub(crate) pinned: bool,
pub(crate) poll: Option<Poll>,
pub(crate) reactions: Vec<Reaction>,
Expand Down Expand Up @@ -323,6 +324,7 @@ impl From<Message> for CachedMessage {
mention_everyone,
mention_roles,
mentions,
message_snapshots,
pinned,
poll,
reactions,
Expand Down Expand Up @@ -358,6 +360,7 @@ impl From<Message> for CachedMessage {
mention_everyone,
mention_roles,
mentions: mentions.into_iter().map(|mention| mention.id).collect(),
message_snapshots,
pinned,
poll,
reactions,
Expand Down
1 change: 1 addition & 0 deletions twilight-cache-inmemory/src/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ pub fn cache_with_message_and_reactions() -> DefaultInMemoryCache {
mention_everyone: false,
mention_roles: Vec::new(),
mentions: Vec::new(),
message_snapshots: Vec::new(),
pinned: false,
poll: None,
reactions: Vec::new(),
Expand Down
32 changes: 32 additions & 0 deletions twilight-http/src/request/channel/message/create_message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ use std::future::IntoFuture;
use twilight_model::{
channel::message::{
AllowedMentions, Component, Embed, Message, MessageFlags, MessageReference,
MessageReferenceType,
},
http::attachment::Attachment,
id::{
Expand Down Expand Up @@ -244,6 +245,7 @@ impl<'a> CreateMessage<'a> {
reference.fail_if_not_exists = Some(fail_if_not_exists);
} else {
fields.message_reference = Some(MessageReference {
kind: MessageReferenceType::default(),
channel_id: None,
guild_id: None,
message_id: None,
Expand Down Expand Up @@ -312,6 +314,36 @@ impl<'a> CreateMessage<'a> {
}
} else {
MessageReference {
kind: MessageReferenceType::Default,
channel_id: Some(channel_id),
guild_id: None,
message_id: Some(other),
fail_if_not_exists: None,
}
};

fields.message_reference = Some(reference);

fields
});

self
}

/// Specify the ID of another message to forward.
pub fn forward(mut self, other: Id<MessageMarker>) -> Self {
self.fields = self.fields.map(|mut fields| {
let channel_id = self.channel_id;

let reference = if let Some(reference) = fields.message_reference {
MessageReference {
channel_id: Some(channel_id),
message_id: Some(other),
..reference
}
} else {
MessageReference {
kind: MessageReferenceType::Forward,
channel_id: Some(channel_id),
guild_id: None,
message_id: Some(other),
Expand Down
1 change: 1 addition & 0 deletions twilight-model/src/application/interaction/resolved.rs
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,7 @@ mod tests {
mention_everyone: false,
mention_roles: Vec::new(),
mentions: Vec::new(),
message_snapshots: Vec::new(),
pinned: false,
poll: None,
reactions: Vec::new(),
Expand Down
19 changes: 16 additions & 3 deletions twilight-model/src/channel/message/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ mod mention;
mod reaction;
mod reaction_type;
mod reference;
mod reference_type;
mod role_subscription_data;
mod snapshot;

pub use self::{
activity::{MessageActivity, MessageActivityType},
Expand All @@ -32,11 +34,12 @@ pub use self::{
reaction::{EmojiReactionType, Reaction, ReactionCountDetails},
reaction_type::ReactionType,
reference::MessageReference,
reference_type::MessageReferenceType,
role_subscription_data::RoleSubscriptionData,
sticker::Sticker,
snapshot::MessageSnapshot,
sticker::{MessageSticker, Sticker},
};

use self::sticker::MessageSticker;
use crate::{
channel::{Attachment, Channel, ChannelMention},
guild::PartialMember,
Expand Down Expand Up @@ -164,6 +167,10 @@ pub struct Message {
pub mention_roles: Vec<Id<RoleMarker>>,
/// Users mentioned in the message.
pub mentions: Vec<Mention>,
/// The message associated with the [`MessageReference`]. This is a minimal subset
/// of fields in a message (e.g. author is excluded.).
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub message_snapshots: Vec<MessageSnapshot>,
/// Whether the message is pinned.
pub pinned: bool,
/// The poll associated with the message.
Expand Down Expand Up @@ -207,6 +214,7 @@ pub struct Message {
mod tests {
use super::{
reaction::ReactionCountDetails,
reference_type::MessageReferenceType,
sticker::{MessageSticker, StickerFormatType},
EmojiReactionType, Message, MessageActivity, MessageActivityType, MessageApplication,
MessageCall, MessageFlags, MessageReference, MessageType, Reaction,
Expand Down Expand Up @@ -285,6 +293,7 @@ mod tests {
mention_everyone: false,
mention_roles: Vec::new(),
mentions: Vec::new(),
message_snapshots: Vec::new(),
pinned: false,
poll: None,
reactions: Vec::new(),
Expand Down Expand Up @@ -510,6 +519,7 @@ mod tests {
mention_everyone: false,
mention_roles: Vec::new(),
mentions: Vec::new(),
message_snapshots: Vec::new(),
pinned: false,
poll: None,
reactions: vec![Reaction {
Expand All @@ -528,6 +538,7 @@ mod tests {
reference: Some(MessageReference {
channel_id: Some(Id::new(1)),
guild_id: None,
kind: MessageReferenceType::Default,
message_id: None,
fail_if_not_exists: None,
}),
Expand Down Expand Up @@ -734,12 +745,14 @@ mod tests {
Token::Some,
Token::Struct {
name: "MessageReference",
len: 1,
len: 2,
},
Token::Str("channel_id"),
Token::Some,
Token::NewtypeStruct { name: "Id" },
Token::Str("1"),
Token::Str("type"),
Token::U8(0),
Token::StructEnd,
Token::Str("sticker_items"),
Token::Seq { len: Some(1) },
Expand Down
19 changes: 16 additions & 3 deletions twilight-model/src/channel/message/reference.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ use crate::id::{
};
use serde::{Deserialize, Serialize};

use super::reference_type::MessageReferenceType;

/// Message reference struct.
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
pub struct MessageReference {
Expand All @@ -16,6 +18,11 @@ pub struct MessageReference {
/// Originating message's guild ID.
#[serde(skip_serializing_if = "Option::is_none")]
pub guild_id: Option<Id<GuildMarker>>,
/// The type of reference.
///
/// Defaults to [`MessageReferenceType::Default`].
#[serde(default, rename = "type")]
pub kind: MessageReferenceType,
/// Originating message's ID.
#[serde(skip_serializing_if = "Option::is_none")]
pub message_id: Option<Id<MessageMarker>>,
Expand All @@ -30,12 +37,13 @@ pub struct MessageReference {
#[cfg(test)]
mod tests {
use super::MessageReference;
use crate::id::Id;
use crate::{channel::message::reference_type::MessageReferenceType, id::Id};
use serde_test::Token;

#[test]
fn minimal() {
let value = MessageReference {
kind: MessageReferenceType::Default,
channel_id: Some(Id::new(1)),
guild_id: None,
message_id: None,
Expand All @@ -47,12 +55,14 @@ mod tests {
&[
Token::Struct {
name: "MessageReference",
len: 1,
len: 2,
},
Token::Str("channel_id"),
Token::Some,
Token::NewtypeStruct { name: "Id" },
Token::Str("1"),
Token::Str("type"),
Token::U8(0),
Token::StructEnd,
],
);
Expand All @@ -63,6 +73,7 @@ mod tests {
let value = MessageReference {
channel_id: Some(Id::new(1)),
guild_id: Some(Id::new(2)),
kind: MessageReferenceType::Default,
message_id: Some(Id::new(3)),
fail_if_not_exists: Some(false),
};
Expand All @@ -72,7 +83,7 @@ mod tests {
&[
Token::Struct {
name: "MessageReference",
len: 4,
len: 5,
},
Token::Str("channel_id"),
Token::Some,
Expand All @@ -82,6 +93,8 @@ mod tests {
Token::Some,
Token::NewtypeStruct { name: "Id" },
Token::Str("2"),
Token::Str("type"),
Token::U8(0),
Token::Str("message_id"),
Token::Some,
Token::NewtypeStruct { name: "Id" },
Expand Down
90 changes: 90 additions & 0 deletions twilight-model/src/channel/message/reference_type.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
use serde::{Deserialize, Serialize};

/// The type of reference for a message.
#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, Hash, PartialEq, Serialize)]
#[non_exhaustive]
#[serde(from = "u8", into = "u8")]
pub enum MessageReferenceType {
/// A standard reference used by replies.
#[default]
Default,
/// Reference used to point to a message at a point in time.
Forward,
/// An unknown message reference type.
Unknown(u8),
}

impl From<u8> for MessageReferenceType {
fn from(value: u8) -> Self {
match value {
0 => MessageReferenceType::Default,
1 => MessageReferenceType::Forward,
other => MessageReferenceType::Unknown(other),
}
}
}

impl From<MessageReferenceType> for u8 {
fn from(value: MessageReferenceType) -> Self {
match value {
MessageReferenceType::Default => 0,
MessageReferenceType::Forward => 1,
MessageReferenceType::Unknown(other) => other,
}
}
}

impl MessageReferenceType {
/// Return a string representation of the type.
pub const fn name(&self) -> &str {
match self {
Self::Default => "Default",
Self::Forward => "Forward",
Self::Unknown(_) => "Unknown",
}
}
}

#[cfg(test)]
mod tests {
use super::MessageReferenceType;
use serde::{Deserialize, Serialize};
use serde_test::Token;
use static_assertions::assert_impl_all;
use std::{fmt::Debug, hash::Hash};

assert_impl_all!(
MessageReferenceType: Clone,
Copy,
Debug,
Deserialize<'static>,
Eq,
Hash,
PartialEq,
Serialize,
Send,
Sync,
);

#[test]
fn variants() {
serde_test::assert_tokens(&MessageReferenceType::Default, &[Token::U8(0)]);
serde_test::assert_tokens(&MessageReferenceType::Forward, &[Token::U8(1)]);
serde_test::assert_tokens(&MessageReferenceType::Unknown(99), &[Token::U8(99)]);
}

#[test]
fn names() {
assert_eq!(MessageReferenceType::Default.name(), "Default");
assert_eq!(MessageReferenceType::Forward.name(), "Forward");
assert_eq!(MessageReferenceType::Unknown(99).name(), "Unknown");
}

#[test]
fn default() {
assert_eq!(
MessageReferenceType::Default,
MessageReferenceType::default()
);
}
}
Loading

0 comments on commit d767129

Please sign in to comment.