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}