twilight_http/client/
interaction.rs

1use crate::{
2    request::application::{
3        command::{
4            CreateGlobalCommand, CreateGuildCommand, DeleteGlobalCommand, DeleteGuildCommand,
5            GetCommandPermissions, GetGlobalCommand, GetGlobalCommands, GetGuildCommand,
6            GetGuildCommandPermissions, GetGuildCommands, SetGlobalCommands, SetGuildCommands,
7            UpdateCommandPermissions, UpdateGlobalCommand, UpdateGuildCommand,
8        },
9        interaction::{
10            CreateFollowup, CreateResponse, DeleteFollowup, DeleteResponse, GetFollowup,
11            GetResponse, UpdateFollowup, UpdateResponse,
12        },
13    },
14    Client,
15};
16use twilight_model::{
17    application::command::{permissions::CommandPermission, Command},
18    http::interaction::InteractionResponse,
19    id::{
20        marker::{ApplicationMarker, CommandMarker, GuildMarker, InteractionMarker, MessageMarker},
21        Id,
22    },
23};
24
25/// Client interface for using interactions.
26///
27/// # Examples
28///
29/// Retrieve the application ID and then use an interaction request:
30///
31/// ```no_run
32/// # #[tokio::main]
33/// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
34/// use std::env;
35/// use twilight_http::Client;
36/// use twilight_model::id::Id;
37///
38/// let client = Client::new(env::var("DISCORD_TOKEN")?);
39/// let application_id = Id::new(123);
40///
41/// let interaction_client = client.interaction(application_id);
42///
43/// let commands = interaction_client.global_commands().await?.models().await?;
44///
45/// println!("there are {} global commands", commands.len());
46/// # Ok(()) }
47/// ```
48#[derive(Debug)]
49pub struct InteractionClient<'a> {
50    application_id: Id<ApplicationMarker>,
51    client: &'a Client,
52}
53
54impl<'a> InteractionClient<'a> {
55    /// Create a new interface for using interactions.
56    pub(super) const fn new(client: &'a Client, application_id: Id<ApplicationMarker>) -> Self {
57        Self {
58            application_id,
59            client,
60        }
61    }
62
63    /// Respond to an interaction, by its ID and token.
64    ///
65    /// For variants of [`InteractionResponse`] that contain
66    /// [`InteractionResponseData`], there is an [associated builder] in the
67    /// [`twilight-util`] crate.
68    ///
69    /// This endpoint is not bound to the application's global rate limit.
70    ///
71    /// [`InteractionResponseData`]: twilight_model::http::interaction::InteractionResponseData
72    /// [`twilight-util`]: https://docs.rs/twilight-util/latest/index.html
73    /// [associated builder]: https://docs.rs/twilight-util/latest/twilight_util/builder/struct.InteractionResponseDataBuilder.html
74    pub const fn create_response(
75        &'a self,
76        interaction_id: Id<InteractionMarker>,
77        interaction_token: &'a str,
78        response: &'a InteractionResponse,
79    ) -> CreateResponse<'a> {
80        CreateResponse::new(self.client, interaction_id, interaction_token, response)
81    }
82
83    /// Delete the original message, by its token.
84    ///
85    /// This endpoint is not bound to the application's global rate limit.
86    pub const fn delete_response(&'a self, interaction_token: &'a str) -> DeleteResponse<'a> {
87        DeleteResponse::new(self.client, self.application_id, interaction_token)
88    }
89
90    /// Get the original message, by its token.
91    ///
92    /// This endpoint is not bound to the application's global rate limit.
93    pub const fn response(&'a self, interaction_token: &'a str) -> GetResponse<'a> {
94        GetResponse::new(self.client, self.application_id, interaction_token)
95    }
96
97    /// Edit the original message, by its token.
98    ///
99    /// The update must include at least one of [`attachments`], [`components`],
100    /// [`content`] or [`embeds`].
101    ///
102    /// This endpoint is not bound to the application's global rate limit.
103    ///
104    /// [`attachments`]: CreateFollowup::attachments
105    /// [`components`]: CreateFollowup::components
106    /// [`content`]: CreateFollowup::content
107    /// [`embeds`]: CreateFollowup::embeds
108    pub const fn update_response(&'a self, interaction_token: &'a str) -> UpdateResponse<'a> {
109        UpdateResponse::new(self.client, self.application_id, interaction_token)
110    }
111
112    /// Create a followup message to an interaction, by its token.
113    ///
114    /// The message must include at least one of [`attachments`], [`components`]
115    /// [`content`] or [`embeds`].
116    ///
117    /// This endpoint is not bound to the application's global rate limit.
118    ///
119    /// # Examples
120    ///
121    /// ```no_run
122    /// # #[tokio::main] async fn main() -> Result<(), Box<dyn std::error::Error>> {
123    /// use std::env;
124    /// use twilight_http::Client;
125    /// use twilight_model::id::Id;
126    ///
127    /// let client = Client::new(env::var("DISCORD_TOKEN")?);
128    /// let application_id = Id::new(1);
129    ///
130    /// client
131    ///     .interaction(application_id)
132    ///     .create_followup("webhook token")
133    ///     .content("Pinkie...")
134    ///     .await?;
135    /// # Ok(()) }
136    /// ```
137    ///
138    /// [`attachments`]: CreateFollowup::attachments
139    /// [`components`]: CreateFollowup::components
140    /// [`content`]: CreateFollowup::content
141    /// [`embeds`]: CreateFollowup::embeds
142    pub const fn create_followup(&'a self, interaction_token: &'a str) -> CreateFollowup<'a> {
143        CreateFollowup::new(self.client, self.application_id, interaction_token)
144    }
145
146    /// Delete a followup message to an interaction, by its token and message
147    /// ID.
148    ///
149    /// This endpoint is not bound to the application's global rate limit.
150    pub const fn delete_followup(
151        &'a self,
152        interaction_token: &'a str,
153        message_id: Id<MessageMarker>,
154    ) -> DeleteFollowup<'a> {
155        DeleteFollowup::new(
156            self.client,
157            self.application_id,
158            interaction_token,
159            message_id,
160        )
161    }
162
163    /// Get a followup message of an interaction, by its token and the message
164    /// ID.
165    ///
166    /// This endpoint is not bound to the application's global rate limit.
167    pub const fn followup(
168        &'a self,
169        interaction_token: &'a str,
170        message_id: Id<MessageMarker>,
171    ) -> GetFollowup<'a> {
172        GetFollowup::new(
173            self.client,
174            self.application_id,
175            interaction_token,
176            message_id,
177        )
178    }
179
180    /// Edit a followup message of an interaction, by its token and the message
181    /// ID.
182    ///
183    /// This endpoint is not bound to the application's global rate limit.
184    pub const fn update_followup(
185        &'a self,
186        interaction_token: &'a str,
187        message_id: Id<MessageMarker>,
188    ) -> UpdateFollowup<'a> {
189        UpdateFollowup::new(
190            self.client,
191            self.application_id,
192            interaction_token,
193            message_id,
194        )
195    }
196
197    /// Create a new global command.
198    pub const fn create_global_command(&'a self) -> CreateGlobalCommand<'a> {
199        CreateGlobalCommand::new(self.client, self.application_id)
200    }
201
202    /// Delete a global command, by ID.
203    pub const fn delete_global_command(
204        &self,
205        command_id: Id<CommandMarker>,
206    ) -> DeleteGlobalCommand<'_> {
207        DeleteGlobalCommand::new(self.client, self.application_id, command_id)
208    }
209
210    /// Fetch a global command for your application.
211    pub const fn global_command(&self, command_id: Id<CommandMarker>) -> GetGlobalCommand<'_> {
212        GetGlobalCommand::new(self.client, self.application_id, command_id)
213    }
214
215    /// Fetch all global commands for your application.
216    pub const fn global_commands(&self) -> GetGlobalCommands<'_> {
217        GetGlobalCommands::new(self.client, self.application_id)
218    }
219
220    /// Set global commands.
221    ///
222    /// This method is idempotent: it can be used on every start, without being
223    /// ratelimited if there aren't changes to the commands.
224    ///
225    /// The [`Command`] struct has an [associated builder] in the
226    /// [`twilight-util`] crate.
227    ///
228    /// [`twilight-util`]: https://docs.rs/twilight-util/latest/index.html
229    /// [associated builder]: https://docs.rs/twilight-util/latest/twilight_util/builder/command/struct.CommandBuilder.html
230    pub const fn set_global_commands(&'a self, commands: &'a [Command]) -> SetGlobalCommands<'a> {
231        SetGlobalCommands::new(self.client, self.application_id, commands)
232    }
233
234    /// Edit a global command, by ID.
235    ///
236    /// You must specify a name and description. See
237    /// [Discord Docs/Edit Global Application Command].
238    ///
239    /// [Discord Docs/Edit Global Application Command]: https://discord.com/developers/docs/interactions/application-commands#edit-global-application-command
240    pub const fn update_global_command(
241        &self,
242        command_id: Id<CommandMarker>,
243    ) -> UpdateGlobalCommand<'_> {
244        UpdateGlobalCommand::new(self.client, self.application_id, command_id)
245    }
246
247    /// Create a new command in a guild.
248    pub const fn create_guild_command(
249        &'a self,
250        guild_id: Id<GuildMarker>,
251    ) -> CreateGuildCommand<'a> {
252        CreateGuildCommand::new(self.client, self.application_id, guild_id)
253    }
254
255    /// Delete a command in a guild, by ID.
256    pub const fn delete_guild_command(
257        &self,
258        guild_id: Id<GuildMarker>,
259        command_id: Id<CommandMarker>,
260    ) -> DeleteGuildCommand<'_> {
261        DeleteGuildCommand::new(self.client, self.application_id, guild_id, command_id)
262    }
263
264    /// Fetch a guild command for your application.
265    pub const fn guild_command(
266        &self,
267        guild_id: Id<GuildMarker>,
268        command_id: Id<CommandMarker>,
269    ) -> GetGuildCommand<'_> {
270        GetGuildCommand::new(self.client, self.application_id, guild_id, command_id)
271    }
272
273    /// Fetch all commands for a guild, by ID.
274    pub const fn guild_commands(&self, guild_id: Id<GuildMarker>) -> GetGuildCommands<'_> {
275        GetGuildCommands::new(self.client, self.application_id, guild_id)
276    }
277
278    /// Set a guild's commands.
279    ///
280    /// This method is idempotent: it can be used on every start, without being
281    /// ratelimited if there aren't changes to the commands.
282    ///
283    /// The [`Command`] struct has an [associated builder] in the
284    /// [`twilight-util`] crate.
285    ///
286    /// [`twilight-util`]: https://docs.rs/twilight_util/index.html
287    /// [associated builder]: https://docs.rs/twilight-util/latest/twilight_util/builder/command/struct.CommandBuilder.html
288    pub const fn set_guild_commands(
289        &'a self,
290        guild_id: Id<GuildMarker>,
291        commands: &'a [Command],
292    ) -> SetGuildCommands<'a> {
293        SetGuildCommands::new(self.client, self.application_id, guild_id, commands)
294    }
295
296    /// Edit a command in a guild, by ID.
297    ///
298    /// You must specify a name and description. See
299    /// [Discord Docs/Edit Guild Application Command].
300    ///
301    /// [Discord Docs/Edit Guild Application Command]: https://discord.com/developers/docs/interactions/application-commands#edit-guild-application-command
302    pub const fn update_guild_command(
303        &self,
304        guild_id: Id<GuildMarker>,
305        command_id: Id<CommandMarker>,
306    ) -> UpdateGuildCommand<'_> {
307        UpdateGuildCommand::new(self.client, self.application_id, guild_id, command_id)
308    }
309
310    /// Fetch command permissions for a command from the current application
311    /// in a guild.
312    pub const fn command_permissions(
313        &self,
314        guild_id: Id<GuildMarker>,
315        command_id: Id<CommandMarker>,
316    ) -> GetCommandPermissions<'_> {
317        GetCommandPermissions::new(self.client, self.application_id, guild_id, command_id)
318    }
319
320    /// Fetch command permissions for all commands from the current
321    /// application in a guild.
322    pub const fn guild_command_permissions(
323        &self,
324        guild_id: Id<GuildMarker>,
325    ) -> GetGuildCommandPermissions<'_> {
326        GetGuildCommandPermissions::new(self.client, self.application_id, guild_id)
327    }
328
329    /// Update command permissions for a single command in a guild.
330    ///
331    /// This overwrites the command permissions so the full set of permissions
332    /// have to be sent every time.
333    ///
334    /// This request requires that the client was configured with an OAuth2 Bearer
335    /// token.
336    ///
337    /// # Errors
338    ///
339    /// Returns an error of type [`PermissionsCountInvalid`] if the permissions
340    /// are invalid.
341    ///
342    /// [`PermissionsCountInvalid`]: twilight_validate::command::CommandValidationErrorType::PermissionsCountInvalid
343    pub fn update_command_permissions(
344        &'a self,
345        guild_id: Id<GuildMarker>,
346        command_id: Id<CommandMarker>,
347        permissions: &'a [CommandPermission],
348    ) -> UpdateCommandPermissions<'a> {
349        UpdateCommandPermissions::new(
350            self.client,
351            self.application_id,
352            guild_id,
353            command_id,
354            permissions,
355        )
356    }
357}
358
359#[cfg(test)]
360mod tests {
361    use super::InteractionClient;
362    use static_assertions::assert_impl_all;
363    use std::fmt::Debug;
364
365    assert_impl_all!(InteractionClient<'_>: Debug, Send, Sync);
366}