Build Tab

This commit is contained in:
Evann Regnault 2023-07-15 10:35:09 +02:00
parent 1414d8b6f7
commit 7de38097eb
5 changed files with 349 additions and 6 deletions

2
Cargo.lock generated
View file

@ -800,7 +800,7 @@ dependencies = [
[[package]]
name = "obsessed-yanqing"
version = "1.0.3"
version = "1.0.4"
dependencies = [
"levenshtein",
"poise",

View file

@ -1,6 +1,6 @@
[package]
name = "obsessed-yanqing"
version = "1.0.4"
version = "1.1.0"
edition = "2021"
authors = ["Evann Regnault"]
license = "MIT"

View file

@ -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<Characters>) -> ReplyHandle {
@ -97,10 +100,58 @@ async fn get_character_review(character: &Character) -> Option<CreateEmbed> {
)
}
async fn get_character_build(character: &Character, index: usize) -> Option<CreateEmbed> {
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::<Vec<String>>().join("\n"), false)
.field("Planetary Sets", chosen_build.planars.iter().enumerate().map(|(i, f)| format!("{}. {}", i+1, f.planar)).collect::<Vec<String>>().join("\n"), false)
.field("Light Cones", light_cones.iter().enumerate().map(|(i, c)| format!("{}. {}", i+1, c.name)).collect::<Vec<String>>().join("\n"), false)
// STATS
.field("Body", chosen_build.body.iter().map(|f| format!("{}",f.stat)).collect::<Vec<String>>().join(" / "), true)
.field("Feet", chosen_build.feet.iter().map(|f| format!("{}",f.stat)).collect::<Vec<String>>().join(" / "), true)
.field("\u{200B}", "\u{200B}", true)
.field("Planar Sphere", chosen_build.sphere.iter().map(|f| format!("{}",f.stat)).collect::<Vec<String>>().join(" / "), true)
.field("Link Rope", chosen_build.rope.iter().map(|f| format!("{}",f.stat)).collect::<Vec<String>>().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<CreateButton> = 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<MessageComponentInterac
CharacterTab::Review => {
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 => {
@ -198,6 +282,12 @@ async fn menu_handler(ctx: Context<'_>, interaction: Arc<MessageComponentInterac
tab = match x.data.custom_id.as_str() {
"charactertab_home" => 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"
}
}
}

251
src/data/cones.rs Normal file
View file

@ -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<Cone>,
}
#[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<Image>,
pub small_image: SmallImage,
pub rarity: String,
#[serde(default)]
pub source: Option<Vec<String>>,
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<Source>,
}
#[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<Source2>,
}
#[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<Cone> {
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<PrydwenResponse<PrydwenCone>, _> = 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::<Vec<Cone>>().get(0).expect(format!("Cannot find {}", name).as_str()).clone())
}
Err(err) => {
let path = err.path().to_string();
println!("{}", path);
None
}
}
}

View file

@ -7,4 +7,5 @@ pub mod allcharacters;
pub mod character;
pub mod description;
pub mod proscons;
pub mod cones;