titanium_model/
interaction.rs

1//! Interactions (Slash Commands, Components, Modals).
2
3use crate::command::CommandType;
4use crate::component::ComponentType;
5use crate::member::GuildMember;
6use crate::snowflake::Snowflake;
7use crate::Message;
8use crate::TitanString;
9use crate::User;
10use serde::{Deserialize, Serialize};
11
12/// Incoming Interaction.
13#[derive(Debug, Clone, Serialize, Deserialize)]
14pub struct Interaction<'a> {
15    /// Interaction ID.
16    pub id: Snowflake,
17    /// Application ID.
18    pub application_id: Snowflake,
19    /// Type of interaction.
20    #[serde(rename = "type")]
21    pub interaction_type: InteractionType,
22    /// Data payload.
23    #[serde(default)]
24    pub data: Option<InteractionData<'a>>,
25    /// Guild ID.
26    #[serde(default)]
27    pub guild_id: Option<Snowflake>,
28    /// Channel ID.
29    #[serde(default)]
30    pub channel_id: Option<Snowflake>,
31    /// Member (if in guild).
32    #[serde(default)]
33    pub member: Option<GuildMember<'a>>,
34    /// User (if in DM).
35    #[serde(default)]
36    pub user: Option<User<'a>>,
37    /// Token for responding.
38    pub token: String,
39    /// Protocol version.
40    pub version: u8,
41    /// Message (primary for components).
42    #[serde(default)]
43    pub message: Option<Box<Message<'a>>>,
44    /// Locale.
45    #[serde(default)]
46    pub locale: Option<TitanString<'a>>,
47    /// Guild locale.
48    #[serde(default)]
49    pub guild_locale: Option<TitanString<'a>>,
50}
51
52/// Interaction Type.
53#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
54#[serde(from = "u8", into = "u8")]
55pub enum InteractionType {
56    /// Ping (HTTP only).
57    Ping = 1,
58    /// Application Command (Slash Command).
59    ApplicationCommand = 2,
60    /// Component (Button/Select).
61    MessageComponent = 3,
62    /// Autocomplete.
63    ApplicationCommandAutocomplete = 4,
64    /// Modal Submit.
65    ModalSubmit = 5,
66}
67
68impl From<u8> for InteractionType {
69    fn from(value: u8) -> Self {
70        match value {
71            1 => InteractionType::Ping,
72
73            3 => InteractionType::MessageComponent,
74            4 => InteractionType::ApplicationCommandAutocomplete,
75            5 => InteractionType::ModalSubmit,
76            _ => InteractionType::ApplicationCommand,
77        }
78    }
79}
80
81impl From<InteractionType> for u8 {
82    fn from(value: InteractionType) -> Self {
83        value as u8
84    }
85}
86
87/// Data payload for an interaction.
88#[derive(Debug, Clone, Serialize, Deserialize)]
89pub struct InteractionData<'a> {
90    /// ID of the invoked command/component.
91    #[serde(default)]
92    pub id: Option<Snowflake>,
93    /// Name of the invoked command.
94    #[serde(default)]
95    pub name: Option<TitanString<'a>>,
96    /// Command type.
97    #[serde(default, rename = "type")]
98    pub command_type: Option<CommandType>,
99    /// Resolved data (users/roles/channels).
100    #[serde(default)]
101    pub resolved: Option<ResolvedData<'a>>,
102    /// Parameters/Options.
103    #[serde(default)]
104    pub options: Vec<InteractionDataOption<'a>>,
105    /// Custom ID (for components/modals).
106    #[serde(default)]
107    pub custom_id: Option<TitanString<'a>>,
108    /// Component type (for components).
109    #[serde(default)]
110    pub component_type: Option<ComponentType>,
111    /// Selected values (for select menus).
112    #[serde(default)]
113    pub values: Vec<String>,
114    /// Target ID (for context menus).
115    #[serde(default)]
116    pub target_id: Option<Snowflake>,
117    /// Components (for modals).
118    #[serde(default)]
119    pub components: Vec<crate::json::Value>,
120}
121
122/// Resolved/Hydrated data.
123#[derive(Debug, Clone, Serialize, Deserialize)]
124pub struct ResolvedData<'a> {
125    /// Users map.
126    #[serde(default)]
127    pub users: ahash::AHashMap<Snowflake, User<'a>>,
128    /// Members map.
129    #[serde(default)]
130    pub members: ahash::AHashMap<Snowflake, GuildMember<'a>>,
131    /// Roles map.
132    #[serde(default)]
133    pub roles: ahash::AHashMap<Snowflake, crate::member::Role<'a>>,
134    /// Channels map.
135    #[serde(default)]
136    pub channels: ahash::AHashMap<Snowflake, crate::Channel<'a>>,
137    /// Messages map.
138    #[serde(default)]
139    pub messages: ahash::AHashMap<Snowflake, Message<'a>>,
140    /// Attachments map.
141    #[serde(default)]
142    pub attachments: ahash::AHashMap<Snowflake, crate::Attachment<'a>>,
143}
144
145/// Option received in Interaction Data.
146#[derive(Debug, Clone, Serialize, Deserialize)]
147pub struct InteractionDataOption<'a> {
148    /// Name of the parameter.
149    pub name: TitanString<'a>,
150    /// Value type.
151    #[serde(rename = "type")]
152    pub option_type: crate::command::OptionType,
153    /// Value (can be string, int, double, bool).
154    #[serde(default)]
155    pub value: Option<crate::json::Value>,
156    /// Sub-options.
157    #[serde(default)]
158    pub options: Vec<InteractionDataOption<'a>>,
159    /// Whether focused (autocomplete).
160    #[serde(default)]
161    pub focused: bool,
162}
163
164/// Response to an interaction.
165#[derive(Debug, Clone, Serialize, Deserialize)]
166pub struct InteractionResponse<'a> {
167    /// Response type.
168    #[serde(rename = "type")]
169    pub response_type: InteractionCallbackType,
170    /// Response data.
171    #[serde(skip_serializing_if = "Option::is_none")]
172    pub data: Option<InteractionCallbackData<'a>>,
173}
174
175impl<'a> InteractionResponse<'a> {
176    /// Create a builder for an `InteractionResponse`.
177    pub fn builder() -> crate::builder::InteractionResponseBuilder<'a> {
178        crate::builder::InteractionResponseBuilder::new()
179    }
180
181    /// Shortcut to create a simple reply.
182    pub fn reply(
183        content: impl Into<TitanString<'a>>,
184    ) -> crate::builder::InteractionResponseBuilder<'a> {
185        Self::builder().content(content)
186    }
187
188    /// Shortcut for a deferred response (think "loading...").
189    pub fn deferred() -> crate::builder::InteractionResponseBuilder<'a> {
190        Self::builder().kind(InteractionCallbackType::DeferredChannelMessageWithSource)
191    }
192}
193
194/// Callback Type.
195#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
196#[serde(from = "u8", into = "u8")]
197pub enum InteractionCallbackType {
198    /// Pong (HTTP).
199    Pong = 1,
200    /// Channel Message With Source.
201    ChannelMessageWithSource = 4,
202    /// Deferred Channel Message With Source.
203    DeferredChannelMessageWithSource = 5,
204    /// Deferred Update Message (Components).
205    DeferredUpdateMessage = 6,
206    /// Update Message (Components).
207    UpdateMessage = 7,
208    /// Autocomplete Result.
209    ApplicationCommandAutocompleteResult = 8,
210    /// Modal.
211    Modal = 9,
212}
213
214impl From<u8> for InteractionCallbackType {
215    fn from(value: u8) -> Self {
216        match value {
217            1 => InteractionCallbackType::Pong,
218
219            5 => InteractionCallbackType::DeferredChannelMessageWithSource,
220            6 => InteractionCallbackType::DeferredUpdateMessage,
221            7 => InteractionCallbackType::UpdateMessage,
222            8 => InteractionCallbackType::ApplicationCommandAutocompleteResult,
223            9 => InteractionCallbackType::Modal,
224            _ => InteractionCallbackType::ChannelMessageWithSource,
225        }
226    }
227}
228
229impl From<InteractionCallbackType> for u8 {
230    fn from(value: InteractionCallbackType) -> Self {
231        value as u8
232    }
233}
234
235/// Callback Data.
236#[derive(Debug, Clone, Default, Serialize, Deserialize)]
237pub struct InteractionCallbackData<'a> {
238    /// TTS.
239    #[serde(default)]
240    pub tts: bool,
241    /// Message content.
242    #[serde(skip_serializing_if = "Option::is_none")]
243    pub content: Option<TitanString<'a>>,
244    /// Embeds.
245    #[serde(skip_serializing_if = "Vec::is_empty")]
246    pub embeds: Vec<crate::Embed<'a>>,
247    /// Allowed mentions.
248    #[serde(skip_serializing_if = "Option::is_none")]
249    pub allowed_mentions: Option<crate::json::Value>,
250    /// Message flags (e.g. EPHEMERAL).
251    #[serde(skip_serializing_if = "Option::is_none")]
252    pub flags: Option<u64>,
253    /// Components.
254    #[serde(skip_serializing_if = "Vec::is_empty")]
255    pub components: Vec<crate::Component<'a>>,
256    /// Autocomplete choices.
257    #[serde(skip_serializing_if = "Option::is_none")]
258    pub choices: Option<Vec<InteractionDataOption<'a>>>,
259    /// Attachments.
260    #[serde(default)]
261    pub attachments: Vec<crate::Attachment<'a>>,
262    /// Custom ID (for Modals).
263    #[serde(skip_serializing_if = "Option::is_none")]
264    pub custom_id: Option<TitanString<'a>>,
265    /// Title (for Modals).
266    #[serde(skip_serializing_if = "Option::is_none")]
267    pub title: Option<TitanString<'a>>,
268}
269
270/// Modal Payload.
271#[derive(Debug, Clone, Serialize, Deserialize)]
272pub struct Modal<'a> {
273    pub custom_id: TitanString<'a>,
274    pub title: TitanString<'a>,
275    pub components: Vec<crate::Component<'a>>,
276}