stat_summoner/module/askingforflex/
interaction_flex_buttons.rs

1use crate::models::data::EmojiId;
2use crate::models::error::Error;
3use crate::utils::get_emoji;
4use mongodb::Collection;
5use poise::serenity_prelude::CacheHttp;
6use poise::serenity_prelude::{ComponentInteraction, CreateEmbed};
7use std::str::FromStr;
8
9/// Handles flex interactions with buttons in Discord messages.
10///
11/// This function processes button clicks in Discord related to the flex feature, specifically those with a custom_id
12/// starting with `flex_user:`. It identifies the user and the selected role, updates the embed message to reflect the user's choice,
13/// and manages role assignments based on the presence of existing users.
14///
15/// # Parameters:
16/// - ctx: The context of the interaction, providing access to the bot and its utilities.
17/// - message_component_interaction: The interaction object representing the button click event.
18/// - ctx_data: The application's shared data, containing the MongoDB client and other configuration details.
19///
20/// # Returns:
21/// - Result<(), Error>: Returns `Ok(())` if the interaction is processed successfully; otherwise, returns an `Error`.
22///
23/// # ⚠️ Notes:
24/// - The function checks if the `custom_id` starts with `flex_user:` to identify flex actions.
25/// - The `custom_id` must contain the role as a string after the prefix to parse the selected role.
26/// - Updates the Discord embed fields based on user selections and ensures only one user is assigned per role.
27/// - Handles setting and resetting user mentions in the embed fields dynamically.
28///
29/// # Example:
30///
31/// ```rust
32/// handle_interaction_button_flex(ctx, message_component_interaction, ctx_data).await?;
33/// ```
34///
35/// When a user clicks a "Flex" button for a specific role, the following actions occur:
36/// 1. The selected role is parsed from the `custom_id`.
37/// 2. The corresponding embed field is updated with the user's mention if it was previously empty.
38/// 3. If the user was already set in another role, that field is reset to `TBD`.
39/// 4. If the user is already in the selected role, the field is reset to `TBD`.
40/// 5. The modified embed is then updated in the original Discord message.
41///
42/// # Errors:
43/// - If the `custom_id` is not formatted correctly or the role is unrecognized, the function exits silently.
44/// - If any database operation with MongoDB fails, an error is returned.
45/// - If the message cannot be edited on Discord, an error is returned.
46///
47/// # See Also:
48/// - `get_emoji`: Retrieves emojis from the MongoDB collection for use in embed fields.
49/// - `Role`: Enum representing the different roles available in the flex feature.
50///
51/// # Related Structures:
52/// - `EmojiId`: Represents the structure for storing emoji identifiers in the database.
53/// - `Role`: Enum defining Top, Jungle, Mid, ADC, and Support roles.
54///
55/// # Dependencies:
56/// - Relies on MongoDB for storing and retrieving emoji data.
57/// - Uses `poise::serenity_prelude` for interacting with Discord messages and embeds.
58/// - Parses roles from button `custom_id` strings using `FromStr` trait.
59///
60/// ```
61pub async fn handle_interaction_button_flex(
62    ctx: poise::serenity_prelude::Context,
63    message_component_interaction: ComponentInteraction,
64    ctx_data: &crate::models::data::Data,
65) -> Result<(), Error> {
66    let custom_id = &message_component_interaction.data.custom_id;
67    let channel_id = message_component_interaction.message.channel_id;
68    let message_id = message_component_interaction.message.id;
69    let embed = message_component_interaction.message.embeds.get(0);
70    let user_mention = format!("<@{}>", message_component_interaction.user.id);
71    let mongo_client = ctx_data.mongo_client.clone();
72    let collection = mongo_client
73        .database("stat-summoner")
74        .collection::<EmojiId>("emojis_id");
75
76    if let Some(data) = custom_id.strip_prefix("flex_user:") {
77        if let Ok(role) = Role::from_str(data) {
78            if let Some(embed) = embed {
79                let mut new_embed = embed.clone();
80                let mut user_already_set = false;
81                for field in &new_embed.fields {
82                    if field.value == user_mention {
83                        user_already_set = true;
84                        break;
85                    }
86                }
87
88                for field in &mut new_embed.fields {
89                    if field.name == role.field_name(&collection).await? {
90                        if field.value == user_mention {
91                            field.value = "TBD".to_string();
92                        } else if field.value == "TBD" {
93                            field.value = user_mention.clone();
94                        }
95                    } else if user_already_set && field.value == user_mention {
96                        // If the user is already set in another field, set that field to "TBD"
97                        field.value = "TBD".to_string();
98                    }
99                }
100
101                let c_embed = CreateEmbed::from(new_embed);
102                channel_id
103                    .edit_message(
104                        &ctx.http(),
105                        message_id,
106                        poise::serenity_prelude::EditMessage::default().embed(c_embed),
107                    )
108                    .await?;
109                return Ok(());
110            }
111        }
112    }
113    Ok(())
114}
115
116enum Role {
117    Top,
118    Jungle,
119    Mid,
120    ADC,
121    Support,
122}
123
124impl FromStr for Role {
125    type Err = ();
126
127    fn from_str(input: &str) -> Result<Role, Self::Err> {
128        match input {
129            "top" => Ok(Role::Top),
130            "jungle" => Ok(Role::Jungle),
131            "mid" => Ok(Role::Mid),
132            "adc" => Ok(Role::ADC),
133            "support" => Ok(Role::Support),
134            _ => Err(()),
135        }
136    }
137}
138
139impl Role {
140    async fn field_name(&self, collection_emojis: &Collection<EmojiId>) -> Result<String, Error> {
141        match self {
142            Role::Top => {
143                let emoji = get_emoji(collection_emojis.clone(), "position", "TOP").await?;
144                Ok(format!("{}:", emoji))
145            }
146            Role::Jungle => {
147                let emoji = get_emoji(collection_emojis.clone(), "position", "JUNGLE").await?;
148                Ok(format!("{}:", emoji))
149            }
150            Role::Mid => {
151                let emoji = get_emoji(collection_emojis.clone(), "position", "MIDDLE").await?;
152                Ok(format!("{}:", emoji))
153            }
154            Role::ADC => {
155                let emoji = get_emoji(collection_emojis.clone(), "position", "BOTTOM").await?;
156                Ok(format!("{}:", emoji))
157            }
158            Role::Support => {
159                let emoji = get_emoji(collection_emojis.clone(), "position", "SUPPORT").await?;
160                Ok(format!("{}:", emoji))
161            }
162        }
163    }
164}