twilight_util/builder/
interaction_response_data.rs

1use twilight_model::{
2    application::command::CommandOptionChoice,
3    channel::message::{AllowedMentions, Component, Embed, MessageFlags},
4    http::{attachment::Attachment, interaction::InteractionResponseData},
5    poll::Poll,
6};
7
8/// Create an [`InteractionResponseData`] with a builder.
9///
10/// # Example
11/// ```
12/// use twilight_model::channel::message::{
13///     component::{ActionRow, Button, ButtonStyle, Component},
14///     MessageFlags,
15/// };
16/// use twilight_util::builder::InteractionResponseDataBuilder;
17///
18/// let component = Component::ActionRow(ActionRow {
19///     id: None,
20///     components: Vec::from([Component::Button(Button {
21///         id: None,
22///         style: ButtonStyle::Primary,
23///         emoji: None,
24///         label: Some("Button label".to_string()),
25///         custom_id: Some("button_id".to_string()),
26///         url: None,
27///         disabled: false,
28///         sku_id: None,
29///     })]),
30/// });
31///
32/// let interaction_response_data = InteractionResponseDataBuilder::new()
33///     .content("Callback message")
34///     .flags(MessageFlags::EPHEMERAL)
35///     .components([component.clone()])
36///     .build();
37///
38/// assert_eq!(interaction_response_data.components, Some(vec![component]));
39/// ```
40#[derive(Clone, Debug)]
41#[must_use = "builders have no effect if unused"]
42pub struct InteractionResponseDataBuilder(InteractionResponseData);
43
44impl InteractionResponseDataBuilder {
45    /// Create a new builder to construct an [`InteractionResponseData`].
46    pub const fn new() -> Self {
47        Self(InteractionResponseData {
48            allowed_mentions: None,
49            attachments: None,
50            choices: None,
51            components: None,
52            content: None,
53            custom_id: None,
54            embeds: None,
55            flags: None,
56            title: None,
57            tts: None,
58            poll: None,
59        })
60    }
61
62    /// Consume the builder, returning an [`InteractionResponseData`].
63    #[allow(clippy::missing_const_for_fn)]
64    #[must_use = "builders have no effect if unused"]
65    pub fn build(self) -> InteractionResponseData {
66        self.0
67    }
68
69    /// Set the [`AllowedMentions`] of the callback.
70    ///
71    /// Defaults to [`None`].
72    #[allow(clippy::missing_const_for_fn)]
73    pub fn allowed_mentions(mut self, allowed_mentions: AllowedMentions) -> Self {
74        self.0.allowed_mentions = Some(allowed_mentions);
75
76        self
77    }
78
79    /// Set the attachments of the message.
80    ///
81    /// Defaults to [`None`].
82    pub fn attachments(mut self, attachments: impl IntoIterator<Item = Attachment>) -> Self {
83        self.0.attachments = Some(attachments.into_iter().collect());
84
85        self
86    }
87
88    /// Set the autocomplete choices of the response.
89    ///
90    /// Only valid when the type of the interaction is
91    /// [`ApplicationCommandAutocompleteResult`].
92    ///
93    /// [`ApplicationCommandAutocompleteResult`]: twilight_model::http::interaction::InteractionResponseType::ApplicationCommandAutocompleteResult
94    pub fn choices(mut self, choices: impl IntoIterator<Item = CommandOptionChoice>) -> Self {
95        self.0.choices = Some(choices.into_iter().collect());
96
97        self
98    }
99
100    /// Set the message [`Component`]s of the callback.
101    ///
102    /// Defaults to [`None`].
103    pub fn components(mut self, components: impl IntoIterator<Item = Component>) -> Self {
104        self.0.components = Some(components.into_iter().collect());
105
106        self
107    }
108
109    /// Set the message content of the callback.
110    ///
111    /// Defaults to [`None`].
112    pub fn content(mut self, content: impl Into<String>) -> Self {
113        self.0.content = Some(content.into());
114
115        self
116    }
117
118    /// Set the custom ID of the callback.
119    ///
120    /// Defaults to [`None`].
121    pub fn custom_id(mut self, custom_id: impl Into<String>) -> Self {
122        self.0.custom_id = Some(custom_id.into());
123
124        self
125    }
126
127    /// Set the [`Embed`]s of the callback.
128    ///
129    /// Defaults to an empty list.
130    pub fn embeds(mut self, embeds: impl IntoIterator<Item = Embed>) -> Self {
131        self.0.embeds = Some(embeds.into_iter().collect());
132
133        self
134    }
135
136    /// Set the [`MessageFlags`].
137    ///
138    /// The only supported flags are [`EPHEMERAL`] and [`SUPPRESS_EMBEDS`].
139    ///
140    /// Defaults to [`None`].
141    ///
142    /// [`EPHEMERAL`]: twilight_model::channel::message::MessageFlags::EPHEMERAL
143    /// [`SUPPRESS_EMBEDS`]: twilight_model::channel::message::MessageFlags::SUPPRESS_EMBEDS
144    pub const fn flags(mut self, flags: MessageFlags) -> Self {
145        self.0.flags = Some(flags);
146
147        self
148    }
149
150    /// Set the title of the callback.
151    ///
152    /// Defaults to [`None`].
153    pub fn title(mut self, title: impl Into<String>) -> Self {
154        self.0.title = Some(title.into());
155
156        self
157    }
158
159    /// Set whether the response has text-to-speech enabled.
160    ///
161    /// Defaults to [`None`].
162    pub const fn tts(mut self, value: bool) -> Self {
163        self.0.tts = Some(value);
164
165        self
166    }
167
168    /// Set the poll of the callback.
169    pub fn poll(mut self, poll: Poll) -> Self {
170        self.0.poll = Some(poll);
171
172        self
173    }
174}
175
176impl Default for InteractionResponseDataBuilder {
177    fn default() -> Self {
178        Self::new()
179    }
180}
181
182#[cfg(test)]
183mod tests {
184    use super::*;
185    use static_assertions::assert_impl_all;
186    use std::fmt::Debug;
187    use twilight_model::{
188        channel::message::{
189            component::{Button, ButtonStyle},
190            MentionType,
191        },
192        poll::{PollLayoutType, PollMedia},
193        util::Timestamp,
194    };
195
196    assert_impl_all!(
197        InteractionResponseDataBuilder: Clone,
198        Debug,
199        Default,
200        Send,
201        Sync
202    );
203
204    #[test]
205    fn callback_data_builder() {
206        let allowed_mentions = AllowedMentions {
207            parse: Vec::from([MentionType::Everyone]),
208            ..Default::default()
209        };
210
211        let component = Component::Button(Button {
212            style: ButtonStyle::Primary,
213            emoji: None,
214            label: Some("test label".into()),
215            custom_id: Some("test custom id".into()),
216            url: None,
217            disabled: false,
218            sku_id: None,
219            id: None,
220        });
221
222        let embed = Embed {
223            author: None,
224            color: Some(123),
225            description: Some("a description".to_owned()),
226            fields: Vec::new(),
227            footer: None,
228            image: None,
229            kind: "rich".to_owned(),
230            provider: None,
231            thumbnail: None,
232            timestamp: Some(Timestamp::from_secs(1_580_608_922).unwrap()),
233            title: Some("a title".to_owned()),
234            url: Some("https://example.com".to_owned()),
235            video: None,
236        };
237
238        let poll = Poll {
239            answers: vec![],
240            allow_multiselect: false,
241            expiry: None,
242            layout_type: PollLayoutType::Default,
243            question: PollMedia {
244                emoji: None,
245                text: Some("lorem ipsum".to_owned()),
246            },
247            results: None,
248        };
249
250        let value = InteractionResponseDataBuilder::new()
251            .allowed_mentions(allowed_mentions.clone())
252            .components([component.clone()])
253            .content("a content")
254            .embeds([embed.clone()])
255            .flags(MessageFlags::empty())
256            .tts(false)
257            .poll(poll.clone())
258            .build();
259
260        let expected = InteractionResponseData {
261            allowed_mentions: Some(allowed_mentions),
262            attachments: None,
263            choices: None,
264            components: Some(vec![component]),
265            content: Some("a content".to_owned()),
266            custom_id: None,
267            embeds: Some(vec![embed]),
268            flags: Some(MessageFlags::empty()),
269            title: None,
270            tts: Some(false),
271            poll: Some(poll),
272        };
273
274        assert_eq!(value, expected);
275    }
276}