scioly_bot/commands/
pc.rs

1use crate::utils::{Context, Error};
2
3use poise::{
4    serenity_prelude::{
5        self as serenity, ButtonStyle, Color, CreateActionRow, CreateButton, CreateEmbed,
6        CreateEmbedFooter,
7    },
8    CreateReply,
9};
10
11#[derive(Debug, serde::Serialize, serde::Deserialize, Default)]
12pub struct ProgressCheck {
13    event: String,
14    duration: String,
15    progress: String,
16    improvements: String,
17    timestamp: String,
18    team: String,
19    other: String,
20}
21impl ProgressCheck {
22    pub fn event(&mut self, event: String) {
23        self.event = event;
24    }
25    pub fn duration(&mut self, duration: String) {
26        self.duration = duration;
27    }
28    pub fn progress(&mut self, progress: String) {
29        self.progress = progress;
30    }
31    pub fn improvements(&mut self, improvements: String) {
32        self.improvements = improvements;
33    }
34    pub fn timestamp(&mut self, timestamp: String) {
35        self.timestamp = timestamp;
36    }
37    pub fn team(&mut self, team: char) {
38        self.team = team.to_string();
39    }
40    pub fn other(&mut self, other: String) {
41        self.other = other;
42    }
43    pub fn to_vec_values(&self) -> Result<Vec<serde_json::Value>, crate::utils::Error> {
44        Ok(vec![
45            serde_json::to_value(self.event.clone())?,
46            serde_json::to_value(self.team.clone())?,
47            serde_json::to_value(self.duration.clone())?,
48            serde_json::to_value(self.progress.clone())?,
49            serde_json::to_value(self.improvements.clone())?,
50            serde_json::to_value(self.timestamp.clone())?,
51            serde_json::to_value(self.other.clone())?,
52        ])
53    }
54}
55
56pub async fn pc_start_embed(
57    ctx: Context<'_>,
58    event_list_ids: &Vec<(String, String)>,
59    abort_id: &String,
60) -> Result<(), Error> {
61    ctx.channel_id().broadcast_typing(ctx).await?;
62    let mut buttons = Vec::new();
63
64    for (event, event_id) in event_list_ids {
65        let button = CreateButton::new(event_id)
66            .label(event)
67            .style(ButtonStyle::Primary)
68            .emoji('📝');
69        buttons.push(button);
70    }
71
72    buttons.push(
73        CreateButton::new(abort_id)
74            .label("Abort")
75            .style(ButtonStyle::Danger)
76            .emoji('❌'),
77    );
78
79    let embed = CreateEmbed::default()
80        .title("Pick an event to start!")
81        .footer(CreateEmbedFooter::new("run !help for help!"));
82    let reply = CreateReply::default()
83        .embed(embed)
84        .components(vec![CreateActionRow::Buttons(buttons)]);
85
86    ctx.send(reply).await?;
87
88    Ok(())
89}
90pub async fn pc_abort_embed(
91    ctx: Context<'_>,
92    press: &serenity::ComponentInteraction,
93) -> Result<(), Error> {
94    let abort_embed = CreateEmbed::default()
95        .color(Color::RED)
96        .title("Progress Check Aborted")
97        .description("Your progress check has been aborted.");
98
99    let abort_components = vec![CreateActionRow::Buttons(vec![CreateButton::new(
100        "whatthefrik",
101    )
102    .emoji('❌')
103    .style(ButtonStyle::Danger)
104    .label("Aborted")
105    .disabled(true)])];
106
107    let builder = serenity::CreateInteractionResponse::UpdateMessage(
108        serenity::CreateInteractionResponseMessage::new()
109            .embed(abort_embed.clone())
110            .components(abort_components),
111    );
112
113    press.create_response(ctx, builder).await?;
114
115    Ok(())
116}
117
118pub async fn pc_event_handling(ctx: Context<'_>, event: &String) -> Result<ProgressCheck, Error> {
119    ctx.channel_id().broadcast_typing(ctx).await?;
120    let mut prog_check = ProgressCheck::default();
121    prog_check.event(event.clone());
122    let replyhandle = ctx.say("Starting progress check...").await?;
123
124    prog_check.duration(
125        send_questions(
126            &ctx,
127            &replyhandle,
128            "How long did you meet with your partner for".to_string(),
129            event,
130        )
131        .await?,
132    );
133
134    prog_check.progress(
135        send_questions(
136            &ctx,
137            &replyhandle,
138            "What progress did you make on".to_string(),
139            event,
140        )
141        .await?,
142    );
143
144    prog_check.improvements(
145        send_questions(
146            &ctx,
147            &replyhandle,
148            "What improvements can you make for".to_string(),
149            event,
150        )
151        .await?,
152    );
153
154    prog_check.other(
155        send_questions(
156            &ctx,
157            &replyhandle,
158            "Anything else you'd like to let us know?".to_string(),
159            &"".to_string(),
160        )
161        .await?,
162    );
163
164    prog_check.timestamp(
165        chrono::offset::Local::now()
166            .format("%B %d, %Y at %I:%M %P")
167            .to_string(),
168    );
169    println!("{}", prog_check.timestamp);
170
171    let confirmation_embed = CreateEmbed::default()
172        .title(format!("Confirm that your Progress Check for {} is correct: ", event))
173        .description(format!(
174            "1. Duration: {}\n 2. Progress: {}\n 3. Improvements: {}\n, 4. Notes: {}\n",
175            prog_check.duration, prog_check.progress, prog_check.improvements, prog_check.other
176        ))
177        .color(poise::serenity_prelude::Color::DARK_ORANGE)
178        .footer(CreateEmbedFooter::new(
179            "If you would like to change any of the above answers, type the number of the question you would like to change after pressing ❌'.",
180        ));
181
182    let reply = CreateReply::default()
183        .embed(confirmation_embed.clone())
184        .components(vec![CreateActionRow::Buttons(vec![
185            CreateButton::new("whatthefriksubmitit")
186                .emoji('✅')
187                .style(ButtonStyle::Success)
188                .label("Confirm"),
189            CreateButton::new("whatthefrikiswrong")
190                .emoji('❌')
191                .style(ButtonStyle::Danger)
192                .label("Nope"),
193        ])]);
194
195    ctx.send(reply).await?;
196
197    while let Some(press) = serenity::collector::ComponentInteractionCollector::new(ctx)
198        .author_id(ctx.author().id)
199        .filter(move |press| press.data.custom_id.starts_with("whatthefrik"))
200        .timeout(std::time::Duration::from_secs(100))
201        .await
202    {
203        press.defer(ctx).await?;
204        let final_embed = CreateEmbed::default()
205            .title("Progress Check Submitted")
206            .description("Your progress check has been submitted.")
207            .color(poise::serenity_prelude::Color::DARK_GREEN);
208        let final_response = serenity::EditInteractionResponse::default()
209            .embed(final_embed)
210            .components(vec![CreateActionRow::Buttons(vec![
211                CreateButton::new("whatthefriksubmitit")
212                    .emoji('✅')
213                    .style(ButtonStyle::Success)
214                    .label("Confirm")
215                    .disabled(true),
216                CreateButton::new("whatthefrikiswrong")
217                    .emoji('❌')
218                    .style(ButtonStyle::Danger)
219                    .label("Nope")
220                    .disabled(true),
221            ])]);
222
223        if press.data.custom_id == "whatthefrikiswrong" {
224            println!("time to fix xd");
225            edit_answers(&ctx, &press, &replyhandle, &mut prog_check, event).await?;
226            break;
227        } else if press.data.custom_id == "whatthefriksubmitit" {
228            println!("{:?}", press.data.custom_id);
229            press.edit_response(ctx, final_response).await?;
230            break;
231        }
232    }
233
234    Ok(prog_check)
235}
236
237async fn edit_answers(
238    ctx: &Context<'_>,
239    press: &serenity::ComponentInteraction,
240    replyhandle: &poise::ReplyHandle<'_>,
241    prog_check: &mut ProgressCheck,
242    event: &str,
243) -> Result<(), Error> {
244    let interaction_handler = async {
245        while let Some(press) = serenity::collector::ComponentInteractionCollector::new(ctx)
246            .author_id(ctx.author().id)
247            .filter(move |press| press.data.custom_id.starts_with("whatthefrik"))
248            .timeout(std::time::Duration::from_secs(100))
249            .await
250        {
251            let _ = press.defer(ctx).await;
252            if press.data.custom_id == "whatthefriksubmitit" {
253                let final_embed = CreateEmbed::default()
254                    .title("Progress Check Submitted")
255                    .description("Your progress check has been submitted.")
256                    .color(poise::serenity_prelude::Color::DARK_GREEN);
257                let final_response = serenity::EditInteractionResponse::default()
258                    .embed(final_embed)
259                    .components(vec![CreateActionRow::Buttons(vec![CreateButton::new(
260                        "whatthefriksubmitit",
261                    )
262                    .emoji('✅')
263                    .style(ButtonStyle::Success)
264                    .label("Confirm")
265                    .disabled(true)])]);
266                let _ = press.edit_response(ctx, final_response).await;
267                return;
268            }
269        }
270    };
271
272    let other_task = async {
273        while let Some(messages) = ctx
274            .channel_id()
275            .await_reply(ctx)
276            .author_id(ctx.author().id)
277            .timeout(std::time::Duration::from_secs(60))
278            .await
279        {
280            let answer = messages.content;
281            println!("{}", answer);
282            if answer == "1" {
283                prog_check.duration(
284                    send_questions(
285                        ctx,
286                        replyhandle,
287                        "How long did you meet with your partner for".to_string(),
288                        &event.to_string(),
289                    )
290                    .await
291                    .unwrap(),
292                );
293                let _ = send_confirmation(ctx, press, prog_check).await;
294            } else if answer == "2" {
295                prog_check.progress(
296                    send_questions(
297                        ctx,
298                        replyhandle,
299                        "What progress did you make on".to_string(),
300                        &event.to_string(),
301                    )
302                    .await
303                    .unwrap(),
304                );
305                let _ = send_confirmation(ctx, press, prog_check).await;
306            } else if answer == "3" {
307                prog_check.improvements(
308                    send_questions(
309                        ctx,
310                        replyhandle,
311                        "What improvements can you make for".to_string(),
312                        &event.to_string(),
313                    )
314                    .await
315                    .unwrap(),
316                );
317                let _ = send_confirmation(ctx, press, prog_check).await;
318            } else if answer == "4" {
319                prog_check.other(
320                    send_questions(
321                        ctx,
322                        replyhandle,
323                        "Anything else you'd like to let us know?".to_string(),
324                        &"".to_string(),
325                    )
326                    .await
327                    .unwrap(),
328                );
329                let _ = send_confirmation(ctx, press, prog_check).await;
330            } else {
331                let _ = ctx.say(
332                "Invalid input. Please type the number of the question you would like to change.",
333            )
334            .await;
335            }
336        }
337    };
338    tokio::select!(
339        _ = interaction_handler => {},
340        _ = other_task => {}
341    );
342    Ok(())
343}
344
345async fn send_confirmation(
346    ctx: &Context<'_>,
347    press: &serenity::ComponentInteraction,
348    prog_check: &ProgressCheck,
349) -> Result<(), Error> {
350    let confirmation_embed = CreateEmbed::default()
351        .title(format!("Confirm that your Progress Check for {} is correct: ", prog_check.event))
352        .description(format!(
353            "1. Duration: {}\n 2. Progress: {}\n 3. Improvements: {}\n, 4. Notes: {}\n",
354            prog_check.duration, prog_check.progress, prog_check.improvements, prog_check.other
355        ))
356        .color(poise::serenity_prelude::Color::DARK_ORANGE)
357        .footer(CreateEmbedFooter::new(
358            "If you would like to change any of the above answers, type the number of the question you would like to change!'.",
359        ));
360
361    let reply = CreateReply::default()
362        .embed(confirmation_embed.clone())
363        .components(vec![CreateActionRow::Buttons(vec![CreateButton::new(
364            "whatthefriksubmitit",
365        )
366        .emoji('✅')
367        .style(ButtonStyle::Success)
368        .label("Confirm")])]);
369
370    ctx.send(reply).await?;
371
372    Ok(())
373}
374
375async fn send_questions<'a>(
376    ctx: &Context<'a>,
377    replyhandle: &poise::ReplyHandle<'a>,
378    question: String,
379    event: &String,
380) -> Result<String, Error> {
381    replyhandle
382        .message()
383        .await?
384        .reply(ctx, format!("{} {}?", &question, &event))
385        .await?;
386    let mut answer = String::new();
387    if let Some(messages) = ctx
388        .channel_id()
389        .await_reply(ctx)
390        .author_id(ctx.author().id)
391        .timeout(std::time::Duration::from_secs(60))
392        .await
393    {
394        answer = messages.content;
395    } else {
396        return Err(Error::from("No answer provided"));
397    }
398
399    Ok(answer)
400}