Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(model, http): Add support for message forwarding #2340

Merged
merged 4 commits into from
Jul 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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