twilight_model/channel/message/
reaction.rs

1use crate::{
2    id::{marker::EmojiMarker, Id},
3    util::HexColor,
4};
5use serde::{Deserialize, Serialize};
6
7/// Reaction below a message.
8#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
9pub struct Reaction {
10    /// HEX colors used for super reaction.
11    pub burst_colors: Vec<HexColor>,
12    /// Amount of reactions this emoji has.
13    pub count: u64,
14    /// Reaction count details for each type of reaction.
15    pub count_details: ReactionCountDetails,
16    /// Emoji of this reaction.
17    pub emoji: EmojiReactionType,
18    /// Whether the current user has reacted with this emoji.
19    pub me: bool,
20    /// Whether the current user super-reacted using this emoji
21    pub me_burst: bool,
22}
23
24/// Type of emoji in a [`Reaction`].
25#[derive(Clone, Debug, Eq, Hash, PartialEq, Deserialize, Serialize)]
26#[serde(untagged)]
27pub enum EmojiReactionType {
28    /// Custom [`Emoji`].
29    ///
30    /// [`Emoji`]: crate::guild::Emoji
31    Custom {
32        /// Whether the emoji is animated.
33        #[serde(default)]
34        animated: bool,
35        /// Emoji identifier.
36        // Even though it says that the id can be nil in the docs,
37        // it is a bit misleading as that should only happen when
38        // the reaction is a unicode emoji and then it is caught by
39        // the other variant.
40        id: Id<EmojiMarker>,
41        /// Emoji name.
42        // Name is nil if the emoji data is no longer available, for
43        // example if the emoji have been deleted off the guild.
44        name: Option<String>,
45    },
46    /// Standard [Unicode] emoji value.
47    ///
48    /// Unicode reactions must be specified by their unicode value, and *not*
49    /// their Discord display name. Instead of using `:arrow_right:`, use "➡️".
50    ///
51    /// [Unicode]: https://unicode.org/emoji/
52    Unicode {
53        /// Unicode name identifier.
54        name: String,
55    },
56}
57
58/// Breakdown of normal and super reaction counts for the associated emoji.
59#[derive(Clone, Debug, Eq, Hash, PartialEq, Deserialize, Serialize)]
60pub struct ReactionCountDetails {
61    /// Count of super reactions.
62    pub burst: u64,
63    /// Count of normal reactions.
64    pub normal: u64,
65}
66
67#[cfg(test)]
68mod tests {
69    use super::{EmojiReactionType, Reaction, ReactionCountDetails};
70    use crate::{id::Id, util::HexColor};
71    use serde_test::Token;
72
73    #[test]
74    fn message_reaction_unicode() {
75        let value = Reaction {
76            burst_colors: Vec::from([HexColor(255, 255, 255)]),
77            count: 7,
78            count_details: ReactionCountDetails {
79                burst: 0,
80                normal: 7,
81            },
82            emoji: EmojiReactionType::Unicode {
83                name: "a".to_owned(),
84            },
85            me: true,
86            me_burst: false,
87        };
88
89        serde_test::assert_tokens(
90            &value,
91            &[
92                Token::Struct {
93                    name: "Reaction",
94                    len: 6,
95                },
96                Token::Str("burst_colors"),
97                Token::Seq { len: Some(1) },
98                Token::Str("#FFFFFF"),
99                Token::SeqEnd,
100                Token::Str("count"),
101                Token::U64(7),
102                Token::Str("count_details"),
103                Token::Struct {
104                    name: "ReactionCountDetails",
105                    len: 2,
106                },
107                Token::Str("burst"),
108                Token::U64(0),
109                Token::Str("normal"),
110                Token::U64(7),
111                Token::StructEnd,
112                Token::Str("emoji"),
113                Token::Struct {
114                    name: "EmojiReactionType",
115                    len: 1,
116                },
117                Token::Str("name"),
118                Token::Str("a"),
119                Token::StructEnd,
120                Token::Str("me"),
121                Token::Bool(true),
122                Token::Str("me_burst"),
123                Token::Bool(false),
124                Token::StructEnd,
125            ],
126        );
127    }
128
129    #[test]
130    fn custom() {
131        let value = EmojiReactionType::Custom {
132            animated: false,
133            id: Id::new(1337),
134            name: Some("foo".to_owned()),
135        };
136
137        serde_test::assert_tokens(
138            &value,
139            &[
140                Token::Struct {
141                    name: "EmojiReactionType",
142                    len: 3,
143                },
144                Token::Str("animated"),
145                Token::Bool(false),
146                Token::Str("id"),
147                Token::NewtypeStruct { name: "Id" },
148                Token::Str("1337"),
149                Token::Str("name"),
150                Token::Some,
151                Token::Str("foo"),
152                Token::StructEnd,
153            ],
154        );
155
156        // When `animated` isn't present in the payload it should default to
157        // `false`.
158        serde_test::assert_de_tokens(
159            &value,
160            &[
161                Token::Struct {
162                    name: "ReactionType",
163                    len: 2,
164                },
165                Token::Str("id"),
166                Token::NewtypeStruct { name: "Id" },
167                Token::Str("1337"),
168                Token::Str("name"),
169                Token::Some,
170                Token::Str("foo"),
171                Token::StructEnd,
172            ],
173        );
174    }
175
176    #[test]
177    fn unicode() {
178        let value = EmojiReactionType::Unicode {
179            name: "\u{1f643}".to_owned(),
180        };
181
182        serde_test::assert_tokens(
183            &value,
184            &[
185                Token::Struct {
186                    name: "EmojiReactionType",
187                    len: 1,
188                },
189                Token::Str("name"),
190                Token::Str("\u{1f643}"),
191                Token::StructEnd,
192            ],
193        );
194    }
195}