From 7de38097eb2f317b0c0fce87cb69b870f5f05628 Mon Sep 17 00:00:00 2001 From: evannregnault Date: Sat, 15 Jul 2023 10:35:09 +0200 Subject: [PATCH] Build Tab --- Cargo.lock | 2 +- Cargo.toml | 2 +- src/commands/character.rs | 99 ++++++++++++++- src/data/cones.rs | 251 ++++++++++++++++++++++++++++++++++++++ src/data/mod.rs | 1 + 5 files changed, 349 insertions(+), 6 deletions(-) create mode 100644 src/data/cones.rs diff --git a/Cargo.lock b/Cargo.lock index 4204293..7df72be 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -800,7 +800,7 @@ dependencies = [ [[package]] name = "obsessed-yanqing" -version = "1.0.3" +version = "1.0.4" dependencies = [ "levenshtein", "poise", diff --git a/Cargo.toml b/Cargo.toml index f4d3259..bfb66f9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "obsessed-yanqing" -version = "1.0.4" +version = "1.1.0" edition = "2021" authors = ["Evann Regnault"] license = "MIT" diff --git a/src/commands/character.rs b/src/commands/character.rs index 84795ef..3f56f90 100644 --- a/src/commands/character.rs +++ b/src/commands/character.rs @@ -2,11 +2,13 @@ use std::sync::Arc; use std::time::Duration; use poise::ReplyHandle; use serenity::builder::{CreateButton, CreateComponents, CreateEmbed, CreateEmbedFooter}; +use serenity::futures::future::join_all; use serenity::model::application::component::ButtonStyle; use serenity::model::application::interaction::message_component::MessageComponentInteraction; use crate::data::{Context, Error}; use crate::data::allcharacters::{Characters, get_nearest_characters}; use crate::data::character::{Character, get_character_data}; +use crate::data::cones::get_light_cone; use crate::data::description::get_all_texts; use crate::data::proscons::get_proscons_texts; use crate::utils::color_manager::get_element_color; @@ -15,6 +17,7 @@ use crate::utils::emote_manager::{get_element_emote, get_path_emote}; enum CharacterTab { Home, Review, + Gear(usize), } async fn create_menu(ctx: Context<'_>, chars: Vec) -> ReplyHandle { @@ -97,10 +100,58 @@ async fn get_character_review(character: &Character) -> Option { ) } +async fn get_character_build(character: &Character, index: usize) -> Option { + let chosen_build = character.build_data.as_ref().expect("No builds").get(index).expect("Build doesnt exist"); + + let clone = chosen_build.clone(); + let light_cones = join_all(clone.cones.into_iter().map(|c| async { get_light_cone(c.cone).await.expect("Cone")})).await; + + Some( + CreateEmbed::default() + .title(format!("{} {} {}", get_element_emote(&character.element), character.name, get_path_emote(&character.path))) + .set_footer(CreateEmbedFooter::default().text("Data from https://www.prydwen.gg/").to_owned()) + .description(format!("{}\n{}", + ":star:".repeat(character.rarity.parse().unwrap_or(4)), + character.default_role)) + .field(&chosen_build.name.as_str(), &chosen_build.comments.as_str(), false) + + + // STUFF + .field("Relic Sets", chosen_build.relics.iter().enumerate().map(|(i, f)| match f.relic_2.len() { + 0 => format!("{}. {}", i+1, f.relic), + _ => format!("{}. {} + {}", i+1, f.relic, f.relic_2) + }).collect::>().join("\n"), false) + + .field("Planetary Sets", chosen_build.planars.iter().enumerate().map(|(i, f)| format!("{}. {}", i+1, f.planar)).collect::>().join("\n"), false) + + .field("Light Cones", light_cones.iter().enumerate().map(|(i, c)| format!("{}. {}", i+1, c.name)).collect::>().join("\n"), false) + + + // STATS + .field("Body", chosen_build.body.iter().map(|f| format!("{}",f.stat)).collect::>().join(" / "), true) + .field("Feet", chosen_build.feet.iter().map(|f| format!("{}",f.stat)).collect::>().join(" / "), true) + .field("\u{200B}", "\u{200B}", true) + .field("Planar Sphere", chosen_build.sphere.iter().map(|f| format!("{}",f.stat)).collect::>().join(" / "), true) + .field("Link Rope", chosen_build.rope.iter().map(|f| format!("{}",f.stat)).collect::>().join(" / "), true) + .field("\u{200B}", "\u{200B}", true) + + .field("Substats", &chosen_build.substats, false) + + // TRACES + .field("Skill Priority", &chosen_build.skill_priority, false) + .field("Major Traces Priority", &chosen_build.traces_priority, false) + + .color(get_element_color(&character.element)) + .thumbnail(format!("https://www.prydwen.gg{}", character.small_image.local_file.child_image_sharp.gatsby_image_data.images.fallback.src)) + .to_owned() + ) +} + fn create_character_tabs_button<'a>(f: &'a mut CreateComponents, char: &Character, current_tab: &CharacterTab) -> &'a mut CreateComponents { let mut all_buttons: Vec = vec![]; let mut home_button: CreateButton = CreateButton::default(); let mut review_button: CreateButton = CreateButton::default(); + let mut gear_button: CreateButton = CreateButton::default(); // Home Button { @@ -129,6 +180,33 @@ fn create_character_tabs_button<'a>(f: &'a mut CreateComponents, char: &Characte all_buttons.push(review_button); } + // Gear Button + { + match current_tab { + CharacterTab::Gear(_) => { gear_button.style(ButtonStyle::Success); } + _ => { gear_button.style(ButtonStyle::Primary); } + }; + + match &char.build_data { + None => { + gear_button.label("Gear"); + gear_button.disabled(true); + } + Some(_) => { + gear_button.disabled(false); + match current_tab { + CharacterTab::Gear(n) => { + gear_button.label(format!("Gear {}/{}", n+1, char.build_data.as_ref().expect("").len())); + } + _ => {gear_button.label("Gear");} + } + } + } + + gear_button.custom_id(CharacterTab::Gear(1).to_button_id()); + all_buttons.push(gear_button); + } + f.create_action_row(|r| { all_buttons.into_iter().for_each(|b| { @@ -149,6 +227,12 @@ async fn menu_handler(ctx: Context<'_>, interaction: Arc { get_character_review(&character).await } + CharacterTab::Gear(n) => { + let builds = character.build_data.as_ref().expect("No builds"); + let n : usize = n % builds.len(); + tab = CharacterTab::Gear(n); + get_character_build(&character, n).await + } }; match looping { true => { @@ -178,14 +262,14 @@ async fn menu_handler(ctx: Context<'_>, interaction: Arc {interaction.delete_followup_message(&ctx, interaction.message.id).await.unwrap();} + Ok(_) => { interaction.delete_followup_message(&ctx, interaction.message.id).await.unwrap(); } Err(_) => {} } } } let x = match interaction.get_interaction_response(&ctx).await { - Ok(x) => {x} + Ok(x) => { x } Err(_) => return }; @@ -198,6 +282,12 @@ async fn menu_handler(ctx: Context<'_>, interaction: Arc CharacterTab::Home, "charactertab_review" => CharacterTab::Review, + "charactertab_gear" => { + match tab { + CharacterTab::Gear(n) => CharacterTab::Gear(n+1), + _ => CharacterTab::Gear(0) + } + }, _ => CharacterTab::Home }; x.defer(ctx).await.expect("TODO: panic message"); @@ -212,7 +302,7 @@ async fn choice_interaction_handler(ctx: Context<'_>, message: &ReplyHandle<'_>) match message.await_component_interaction(&ctx).timeout(Duration::from_secs(60 * 3)).await { Some(x) => { x - }, + } None => { message.reply(&ctx, "Timed out").await.unwrap(); return; @@ -227,7 +317,8 @@ impl CharacterTab { fn to_button_id(&self) -> &'static str { match self { CharacterTab::Home => "charactertab_home", - CharacterTab::Review => "charactertab_review" + CharacterTab::Review => "charactertab_review", + CharacterTab::Gear(_) => "charactertab_gear" } } } diff --git a/src/data/cones.rs b/src/data/cones.rs new file mode 100644 index 0000000..b78be6d --- /dev/null +++ b/src/data/cones.rs @@ -0,0 +1,251 @@ +use serde_derive::Deserialize; +use serde_derive::Serialize; +use crate::data::core::{PrydwenCompatible, PrydwenResponse}; + +#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct PrydwenCone { + pub all_characters: AllCharacters, +} + +#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct AllCharacters { + pub nodes: Vec, +} + +#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct Cone { + pub id: String, + pub updated_at: String, + pub created_at: String, + pub cone_id: i64, + pub name: String, + pub slug: String, + pub image: Option, + pub small_image: SmallImage, + pub rarity: String, + #[serde(default)] + pub source: Option>, + pub release_date: String, + pub path: String, + pub stats: Stats, + pub character_builder_cone_info: CharacterBuilderConeInfo, + pub skill_name: String, + pub skill_description: SkillDescription, +} + +#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct Image { + pub local_file: LocalFile, +} + +#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct LocalFile { + pub child_image_sharp: ChildImageSharp, +} + +#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct ChildImageSharp { + pub gatsby_image_data: GatsbyImageData, +} + +#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct GatsbyImageData { + pub layout: String, + pub background_color: String, + pub images: Images, + pub width: i64, + pub height: i64, +} + +#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct Images { + pub fallback: Fallback, + pub sources: Vec, +} + +#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct Fallback { + pub src: String, + pub src_set: String, + pub sizes: String, +} + +#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct Source { + pub src_set: String, + #[serde(rename = "type")] + pub type_field: String, + pub sizes: String, +} + +#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct SmallImage { + pub local_file: LocalFile2, +} + +#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct LocalFile2 { + pub child_image_sharp: ChildImageSharp2, +} + +#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct ChildImageSharp2 { + pub gatsby_image_data: GatsbyImageData2, +} + +#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct GatsbyImageData2 { + pub layout: String, + pub background_color: String, + pub images: Images2, + pub width: i64, + pub height: i64, +} + +#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct Images2 { + pub fallback: Fallback2, + pub sources: Vec, +} + +#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct Fallback2 { + pub src: String, + pub src_set: String, + pub sizes: String, +} + +#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct Source2 { + pub src_set: String, + #[serde(rename = "type")] + pub type_field: String, + pub sizes: String, +} + +#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct Stats { + pub hp: Hp, + pub atk: Atk, + pub def: Def, +} + +#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct Hp { + #[serde(rename = "value_level_1")] + pub value_level_1: String, + #[serde(rename = "value_level_max")] + pub value_level_max: String, +} + +#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct Atk { + #[serde(rename = "value_level_1")] + pub value_level_1: String, + #[serde(rename = "value_level_max")] + pub value_level_max: String, +} + +#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct Def { + #[serde(rename = "value_level_1")] + pub value_level_1: String, + #[serde(rename = "value_level_max")] + pub value_level_max: String, +} + +#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct CharacterBuilderConeInfo { + pub cone_custom_stat1: ConeCustomStat1, + pub cone_custom_stat2: ConeCustomStat2, + pub cone_custom_stat3: ConeCustomStat3, + pub cone_custom_stat4: ConeCustomStat4, + pub comment: String, +} + +#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct ConeCustomStat1 { + pub stat: String, + #[serde(rename = "value_1")] + pub value_1: i64, + #[serde(rename = "value_5")] + pub value_5: i64, +} + +#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct ConeCustomStat2 { + pub stat: String, + #[serde(rename = "value_1")] + pub value_1: i64, + #[serde(rename = "value_5")] + pub value_5: i64, +} + +#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct ConeCustomStat3 { + pub stat: String, + #[serde(rename = "value_1")] + pub value_1: i64, + #[serde(rename = "value_5")] + pub value_5: i64, +} + +#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct ConeCustomStat4 { + pub stat: String, + #[serde(rename = "value_1")] + pub value_1: i64, + #[serde(rename = "value_5")] + pub value_5: i64, +} + +#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct SkillDescription { + pub raw: String, +} + +impl PrydwenCompatible for PrydwenCone {} + +pub async fn get_light_cone(name: String) -> Option { + let data_response = reqwest::get("https://www.prydwen.gg/page-data/star-rail/light-cones/page-data.json").await.ok()?; + let d = data_response.text().await.ok()?; + let js = &mut serde_json::Deserializer::from_str(d.as_str()); + let data : Result, _> = serde_path_to_error::deserialize(js); + match data { + Ok(d) => { + Some(d.result.data.all_characters.nodes.into_iter().filter(|f| f.slug.eq(name.as_str())).collect::>().get(0).expect(format!("Cannot find {}", name).as_str()).clone()) + } + Err(err) => { + let path = err.path().to_string(); + println!("{}", path); + None + } + } +} \ No newline at end of file diff --git a/src/data/mod.rs b/src/data/mod.rs index 10ff5b8..a539d96 100644 --- a/src/data/mod.rs +++ b/src/data/mod.rs @@ -7,4 +7,5 @@ pub mod allcharacters; pub mod character; pub mod description; pub mod proscons; +pub mod cones;