1use twilight_http::{response::marker::EmptyBody, Response};
2use twilight_model::{
3 channel::{
4 message::{AllowedMentions, Component, Embed, MessageFlags},
5 Message,
6 },
7 http::{attachment::Attachment, interaction::InteractionResponseData},
8 id::{
9 marker::{ChannelMarker, MessageMarker, StickerMarker, UserMarker, WebhookMarker},
10 Id,
11 },
12};
13
14use crate::error::Error;
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq)]
18pub enum MissingMessageReferenceHandleMethod {
19 Fail,
21 Ignore,
23}
24
25#[derive(Debug)]
27pub enum ExecuteWebhookResponse {
28 EmptyBody(Response<EmptyBody>),
30 Message(Response<Message>),
32}
33
34impl ExecuteWebhookResponse {
35 #[allow(clippy::missing_const_for_fn)]
38 pub fn message(self) -> Option<Response<Message>> {
39 if let Self::Message(response) = self {
40 Some(response)
41 } else {
42 None
43 }
44 }
45}
46
47#[derive(Clone, Debug, PartialEq, Eq)]
59pub struct Reply {
60 pub content: String,
62 pub embeds: Vec<Embed>,
64 pub components: Vec<Component>,
66 pub attachments: Vec<Attachment>,
68 pub flags: MessageFlags,
70 #[allow(clippy::option_option)]
75 pub allowed_mentions: Option<Option<AllowedMentions>>,
76 pub tts: bool,
78 pub update_last: bool,
80 pub sticker_ids: Vec<Id<StickerMarker>>,
82 pub message_reference: Option<Id<MessageMarker>>,
84 pub missing_message_reference_handle_method: MissingMessageReferenceHandleMethod,
86 pub nonce: Option<u64>,
88 pub username: Option<String>,
90 pub avatar_url: Option<String>,
92 pub thread_id: Option<Id<ChannelMarker>>,
94 pub thread_name: Option<String>,
96 pub wait: bool,
98}
99
100impl Default for Reply {
101 fn default() -> Self {
102 Self::new()
103 }
104}
105
106impl From<Reply> for InteractionResponseData {
107 fn from(reply: Reply) -> Self {
108 Self {
109 content: Some(reply.content),
110 embeds: Some(reply.embeds),
111 components: Some(reply.components),
112 attachments: Some(reply.attachments),
113 flags: Some(reply.flags),
114 tts: Some(reply.tts),
115 allowed_mentions: reply.allowed_mentions.flatten(),
116 choices: None,
117 custom_id: None,
118 title: None,
119 }
120 }
121}
122
123impl Reply {
124 #[must_use]
132 pub const fn new() -> Self {
133 Self {
134 content: String::new(),
135 embeds: vec![],
136 components: vec![],
137 attachments: vec![],
138 flags: MessageFlags::empty(),
139 allowed_mentions: None,
140 tts: false,
141 update_last: false,
142 sticker_ids: vec![],
143 message_reference: None,
144 nonce: None,
145 missing_message_reference_handle_method: MissingMessageReferenceHandleMethod::Fail,
146 username: None,
147 avatar_url: None,
148 thread_id: None,
149 thread_name: None,
150 wait: false,
151 }
152 }
153
154 #[must_use]
158 pub fn content(mut self, content: impl Into<String>) -> Self {
159 self.content = content.into();
160 self
161 }
162
163 #[must_use]
165 pub fn embed(mut self, embed: Embed) -> Self {
166 self.embeds.push(embed);
167 self
168 }
169
170 #[must_use]
172 pub fn component(mut self, component: Component) -> Self {
173 self.components.push(component);
174 self
175 }
176
177 #[must_use]
179 pub fn attachment(mut self, attachment: Attachment) -> Self {
180 self.attachments.push(attachment);
181 self
182 }
183
184 #[must_use]
190 pub const fn flags(mut self, flags: MessageFlags) -> Self {
191 self.flags = flags;
192 self
193 }
194
195 #[must_use]
199 #[allow(clippy::missing_const_for_fn)]
200 pub fn allowed_mentions(mut self, allowed_mentions: Option<AllowedMentions>) -> Self {
201 self.allowed_mentions = Some(allowed_mentions);
202 self
203 }
204
205 #[must_use]
207 pub const fn tts(mut self) -> Self {
208 self.tts = true;
209 self
210 }
211
212 #[must_use]
218 pub const fn update_last(mut self) -> Self {
219 self.update_last = true;
220 self
221 }
222
223 #[must_use]
227 pub const fn ephemeral(mut self) -> Self {
228 self.flags = self.flags.union(MessageFlags::EPHEMERAL);
229 self
230 }
231
232 #[must_use]
236 pub fn sticker(mut self, sticker_id: Id<StickerMarker>) -> Self {
237 self.sticker_ids.push(sticker_id);
238 self
239 }
240
241 #[must_use]
246 pub const fn message_reference(
247 mut self,
248 message_id: Id<MessageMarker>,
249 missing_handle_method: MissingMessageReferenceHandleMethod,
250 ) -> Self {
251 self.message_reference = Some(message_id);
252 self.missing_message_reference_handle_method = missing_handle_method;
253 self
254 }
255
256 #[must_use]
260 pub const fn nonce(mut self, nonce: u64) -> Self {
261 self.nonce = Some(nonce);
262 self
263 }
264
265 #[must_use]
269 #[allow(clippy::missing_const_for_fn)]
270 pub fn username(mut self, username: impl Into<String>) -> Self {
271 self.username = Some(username.into());
272 self
273 }
274
275 #[must_use]
279 #[allow(clippy::missing_const_for_fn)]
280 pub fn avatar_url(mut self, avatar_url: impl Into<String>) -> Self {
281 self.avatar_url = Some(avatar_url.into());
282 self
283 }
284
285 #[must_use]
289 pub const fn thread_id(mut self, thread_id: Id<ChannelMarker>) -> Self {
290 self.thread_id = Some(thread_id);
291 self
292 }
293
294 #[must_use]
299 #[allow(clippy::missing_const_for_fn)]
300 pub fn thread_name(mut self, thread_name: impl Into<String>) -> Self {
301 self.thread_name = Some(thread_name.into());
302 self
303 }
304
305 #[must_use]
309 pub const fn wait(mut self) -> Self {
310 self.wait = true;
311 self
312 }
313
314 pub async fn create_message(
323 &self,
324 http: &twilight_http::Client,
325 channel_id: Id<ChannelMarker>,
326 ) -> Result<Response<Message>, Error> {
327 let mut create_message = http.create_message(channel_id);
328
329 if let Some(message_reference) = self.message_reference {
330 create_message = create_message.reply(message_reference);
331 }
332 if let Some(allowed_mentions) = self.allowed_mentions.as_ref() {
333 create_message = create_message.allowed_mentions(allowed_mentions.as_ref());
334 }
335 if let Some(nonce) = self.nonce {
336 create_message = create_message.nonce(nonce);
337 }
338
339 Ok(create_message
340 .content(&self.content)?
341 .embeds(&self.embeds)?
342 .components(&self.components)?
343 .attachments(&self.attachments)?
344 .sticker_ids(&self.sticker_ids)?
345 .flags(self.flags)
346 .tts(self.tts)
347 .fail_if_not_exists(
348 self.missing_message_reference_handle_method
349 == MissingMessageReferenceHandleMethod::Fail,
350 )
351 .await?)
352 }
353
354 pub async fn update_message(
365 &self,
366 http: &twilight_http::Client,
367 channel_id: Id<ChannelMarker>,
368 message_id: Id<MessageMarker>,
369 ) -> Result<Response<Message>, Error> {
370 let mut update_message = http.update_message(channel_id, message_id);
371
372 if let Some(allowed_mentions) = self.allowed_mentions.as_ref() {
373 update_message = update_message.allowed_mentions(allowed_mentions.as_ref());
374 }
375
376 Ok(update_message
377 .content(Some(&self.content))?
378 .embeds(Some(&self.embeds))?
379 .components(Some(&self.components))?
380 .attachments(&self.attachments)?
381 .flags(self.flags)
382 .await?)
383 }
384
385 pub async fn create_private_message(
395 &self,
396 http: &twilight_http::Client,
397 user_id: Id<UserMarker>,
398 ) -> Result<Response<Message>, Error> {
399 let channel_id = http
400 .create_private_channel(user_id)
401 .await?
402 .model()
403 .await?
404 .id;
405
406 self.create_message(http, channel_id).await
407 }
408
409 pub async fn update_private_message(
421 &self,
422 http: &twilight_http::Client,
423 user_id: Id<UserMarker>,
424 message_id: Id<MessageMarker>,
425 ) -> Result<Response<Message>, Error> {
426 let channel_id = http
427 .create_private_channel(user_id)
428 .await?
429 .model()
430 .await?
431 .id;
432
433 self.update_message(http, channel_id, message_id).await
434 }
435
436 pub async fn execute_webhook(
449 &self,
450 http: &twilight_http::Client,
451 webhook_id: Id<WebhookMarker>,
452 token: &str,
453 ) -> Result<ExecuteWebhookResponse, Error> {
454 let mut execute_webhook = http.execute_webhook(webhook_id, token);
455
456 if let Some(username) = self.username.as_ref() {
457 execute_webhook = execute_webhook.username(username)?;
458 }
459 if let Some(avatar_url) = self.avatar_url.as_ref() {
460 execute_webhook = execute_webhook.avatar_url(avatar_url);
461 }
462 if let Some(thread_id) = self.thread_id {
463 execute_webhook = execute_webhook.thread_id(thread_id);
464 }
465 if let Some(thread_name) = self.thread_name.as_ref() {
466 execute_webhook = execute_webhook.thread_name(thread_name);
467 }
468 if let Some(allowed_mentions) = self.allowed_mentions.as_ref() {
469 execute_webhook = execute_webhook.allowed_mentions(allowed_mentions.as_ref());
470 }
471
472 execute_webhook = execute_webhook
473 .content(&self.content)?
474 .embeds(&self.embeds)?
475 .components(&self.components)?
476 .attachments(&self.attachments)?
477 .flags(self.flags)
478 .tts(self.tts);
479
480 if self.wait {
481 Ok(ExecuteWebhookResponse::Message(
482 execute_webhook.wait().await?,
483 ))
484 } else {
485 Ok(ExecuteWebhookResponse::EmptyBody(execute_webhook.await?))
486 }
487 }
488
489 pub async fn update_webhook_message(
500 &self,
501 http: &twilight_http::Client,
502 webhook_id: Id<WebhookMarker>,
503 token: &str,
504 message_id: Id<MessageMarker>,
505 ) -> Result<Response<Message>, Error> {
506 let mut update_webhook_message = http.update_webhook_message(webhook_id, token, message_id);
507
508 if let Some(thread_id) = self.thread_id {
509 update_webhook_message = update_webhook_message.thread_id(thread_id);
510 }
511 if let Some(allowed_mentions) = self.allowed_mentions.as_ref() {
512 update_webhook_message =
513 update_webhook_message.allowed_mentions(allowed_mentions.as_ref());
514 }
515
516 Ok(update_webhook_message
517 .content(Some(&self.content))?
518 .embeds(Some(&self.embeds))?
519 .components(Some(&self.components))?
520 .attachments(&self.attachments)?
521 .await?)
522 }
523}