serenity/utils/
quick_modal.rs1use crate::builder::{
2 Builder as _,
3 CreateActionRow,
4 CreateInputText,
5 CreateInteractionResponse,
6 CreateModal,
7};
8use crate::client::Context;
9use crate::collector::ModalInteractionCollector;
10use crate::model::prelude::*;
11
12#[cfg(feature = "collector")]
13pub struct QuickModalResponse {
14 pub interaction: ModalInteraction,
15 pub inputs: Vec<String>,
16}
17
18#[cfg(feature = "collector")]
35#[must_use]
36pub struct CreateQuickModal {
37 title: String,
38 timeout: Option<std::time::Duration>,
39 input_texts: Vec<CreateInputText>,
40}
41
42#[cfg(feature = "collector")]
43impl CreateQuickModal {
44 pub fn new(title: impl Into<String>) -> Self {
45 Self {
46 title: title.into(),
47 timeout: None,
48 input_texts: Vec::new(),
49 }
50 }
51
52 pub fn timeout(mut self, timeout: std::time::Duration) -> Self {
57 self.timeout = Some(timeout);
58 self
59 }
60
61 pub fn field(mut self, input_text: CreateInputText) -> Self {
66 self.input_texts.push(input_text);
67 self
68 }
69
70 pub fn short_field(self, label: impl Into<String>) -> Self {
74 self.field(CreateInputText::new(InputTextStyle::Short, label, ""))
75 }
76
77 pub fn paragraph_field(self, label: impl Into<String>) -> Self {
81 self.field(CreateInputText::new(InputTextStyle::Paragraph, label, ""))
82 }
83
84 pub async fn execute(
88 self,
89 ctx: &Context,
90 interaction_id: InteractionId,
91 token: &str,
92 ) -> Result<Option<QuickModalResponse>, crate::Error> {
93 let modal_custom_id = interaction_id.get().to_string();
94 let builder = CreateInteractionResponse::Modal(
95 CreateModal::new(&modal_custom_id, self.title).components(
96 self.input_texts
97 .into_iter()
98 .enumerate()
99 .map(|(i, input_text)| {
100 CreateActionRow::InputText(input_text.custom_id(i.to_string()))
101 })
102 .collect(),
103 ),
104 );
105 builder.execute(ctx, (interaction_id, token)).await?;
106
107 let collector =
108 ModalInteractionCollector::new(&ctx.shard).custom_ids(vec![modal_custom_id]);
109
110 let collector = match self.timeout {
111 Some(timeout) => collector.timeout(timeout),
112 None => collector,
113 };
114
115 let modal_interaction = collector.next().await;
116
117 let Some(modal_interaction) = modal_interaction else { return Ok(None) };
118
119 let inputs = modal_interaction
120 .data
121 .components
122 .iter()
123 .filter_map(|row| match row.components.first() {
124 Some(ActionRowComponent::InputText(text)) => {
125 if let Some(value) = &text.value {
126 Some(value.clone())
127 } else {
128 tracing::warn!("input text value was empty in modal response");
129 None
130 }
131 },
132 Some(other) => {
133 tracing::warn!("expected input text in modal response, got {:?}", other);
134 None
135 },
136 None => {
137 tracing::warn!("empty action row");
138 None
139 },
140 })
141 .collect();
142
143 Ok(Some(QuickModalResponse {
144 inputs,
145 interaction: modal_interaction,
146 }))
147 }
148}