This commit is contained in:
Evann Regnault 2023-05-02 08:02:38 +02:00
commit 23ffc50659
16 changed files with 3112 additions and 0 deletions

8
.idea/.gitignore vendored Normal file
View file

@ -0,0 +1,8 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

8
.idea/modules.xml Normal file
View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/obsessed-yanqing.iml" filepath="$PROJECT_DIR$/.idea/obsessed-yanqing.iml" />
</modules>
</component>
</project>

View file

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="CPP_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
<excludeFolder url="file://$MODULE_DIR$/target" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

6
.idea/vcs.xml Normal file
View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>

2016
Cargo.lock generated Normal file

File diff suppressed because it is too large Load diff

252
src/commands/character.rs Normal file
View file

@ -0,0 +1,252 @@
use std::any::Any;
use std::borrow::Cow;
use std::fmt::format;
use std::future::Future;
use std::ops::{Deref, DerefMut};
use std::pin::Pin;
use std::sync::Arc;
use std::time::Duration;
use async_recursion::async_recursion;
use poise::{BoxFuture, ReplyHandle};
use poise::futures_util::FutureExt;
use serenity::builder::{CreateButton, CreateComponents, CreateEmbed, CreateEmbedFooter, CreateInteractionResponseData};
use serenity::model::application::component::ButtonStyle;
use serenity::model::application::interaction::InteractionResponseType;
use serenity::model::application::interaction::message_component::MessageComponentInteraction;
use serenity::model::channel::Message;
use serenity::utils::{Color, MessageBuilder};
use crate::data::{Context, Error};
use crate::data::allcharacters::{Characters, get_nearest_characters};
use crate::data::character::{Character, Description, get_character_data, Review};
use crate::data::description::{Content2, get_all_texts};
use crate::data::proscons::get_proscons_texts;
use crate::utils::color_manager::get_element_color;
use crate::utils::emote_manager::{get_element_emote, get_path_emote};
enum CharacterTab {
Home,
Review,
}
async fn create_menu(ctx: Context<'_>, chars: Vec<Characters>) -> ReplyHandle {
ctx.send(|a| {
a.content("Select the right character").components(|c| {
c.create_action_row(|row| {
row.create_select_menu(|menu| {
menu.custom_id("select_character");
menu.placeholder("Character");
menu.options(|f| {
chars.into_iter().for_each(|char| {
f.create_option(|o| o.label(char.name).value(char.slug));
});
f
})
})
})
}).ephemeral(true)
}).await.unwrap()
}
async fn get_character_home(character: &Character) -> Option<CreateEmbed> {
let desc_data = &character.description;
let desc = match desc_data {
None => { "".to_string() }
Some(data) => {
let all_texts: Vec<String> = get_all_texts(&data).unwrap();
let a = all_texts.get(0).cloned();
match a {
None => { "".to_string() }
Some(b) => { b }
}
}
};
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{}\n\n{}",
":star:".repeat(character.rarity.parse().unwrap_or(4)),
character.default_role,
desc))
.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()
)
}
async fn get_character_review(character: &Character) -> Option<CreateEmbed> {
let cons_data = &character.cons;
let cons = match cons_data {
None => { "".to_string() }
Some(data) => {
let mut all_texts: Vec<String> = get_proscons_texts(&data).unwrap_or(vec![]).into_iter().map(|x| format!("{}", x)).collect();
all_texts.join("\n")
}
};
let pros_data = &character.pros;
let pros = match pros_data {
None => { "".to_string() }
Some(data) => {
let mut all_texts: Vec<String> = get_proscons_texts(&data).unwrap_or(vec![]).into_iter().map(|x| format!("{}", x)).collect();
all_texts.join("\n")
}
};
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(":green_circle: Pros", pros, false)
.field(":red_circle: Cons", cons, 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();
// Home Button
{
match current_tab {
CharacterTab::Home => { home_button.style(ButtonStyle::Success); }
_ => { home_button.style(ButtonStyle::Primary); }
};
home_button.label("Home");
home_button.custom_id(CharacterTab::Home.to_button_id());
all_buttons.push(home_button);
}
// Review Button
{
match current_tab {
CharacterTab::Review => { review_button.style(ButtonStyle::Success); }
_ => { review_button.style(ButtonStyle::Primary); }
};
match &char.review {
None => { review_button.disabled(true); }
Some(_) => { review_button.disabled(false); }
};
review_button.label("Review");
review_button.custom_id(CharacterTab::Review.to_button_id());
all_buttons.push(review_button);
}
f.create_action_row(|r| {
all_buttons.into_iter().for_each(|b| {
r.add_button(b);
});
r
})
}
async fn menu_handler(ctx: Context<'_>, interaction: Arc<MessageComponentInteraction>, character: Character, mut tab: CharacterTab) {
let mut looping = false;
loop {
let character_embed = match tab {
CharacterTab::Home => {
get_character_home(&character).await
}
CharacterTab::Review => {
get_character_review(&character).await
}
};
match looping {
true => {
interaction
.edit_original_interaction_response(&ctx, |d| {
match character_embed {
None => {}
Some(_) => { d.set_embed(character_embed.unwrap()); }
}
d.content("");
d.components(|f| create_character_tabs_button(&mut *f, &character, &tab))
}).await.unwrap();
}
false => {
interaction
.create_interaction_response(&ctx, |r| {
r.interaction_response_data(|d| {
match character_embed {
None => {}
Some(_) => { d.set_embed(character_embed.unwrap()); }
}
d.content("");
d.ephemeral(true);
d.components(|f| create_character_tabs_button(&mut *f, &character, &tab))
})
}).await.unwrap();
}
}
let x = interaction.get_interaction_response(&ctx).await.unwrap().await_component_interaction(&ctx).timeout(Duration::from_secs(10 * 60)).await;
match x {
None => {}
Some(x) => {
looping = true;
tab = match x.data.custom_id.as_str() {
"charactertab_home" => CharacterTab::Home,
"charactertab_review" => CharacterTab::Review,
_ => CharacterTab::Home
};
x.defer(ctx).await.expect("TODO: panic message");
}
}
}
}
async fn choice_interaction_handler(ctx: Context<'_>, message: &ReplyHandle<'_>, tab: CharacterTab) {
let message = message.clone().into_message().await.unwrap();
let interaction =
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;
}
};
let character_string = &interaction.data.values[0];
let character = get_character_data(character_string.to_string()).await.unwrap();
menu_handler(ctx, interaction, character, CharacterTab::Home).await;
}
impl CharacterTab {
fn to_button_id(&self) -> &'static str {
match self {
CharacterTab::Home => "charactertab_home",
CharacterTab::Review => "charactertab_review"
}
}
}
/// Displays your or another user's account creation date
#[poise::command(slash_command, prefix_command)]
pub async fn character(
ctx: Context<'_>,
#[description = "Character to Search"] user: String,
) -> Result<(), Error> {
match get_nearest_characters(user).await {
None => { ctx.say(format!("Error occured")).await? }
Some(characters) => {
let handler = create_menu(ctx, characters).await;
choice_interaction_handler(ctx, &handler, CharacterTab::Home).await;
handler
}
};
Ok(())
}

1
src/commands/mod.rs Normal file
View file

@ -0,0 +1 @@
pub mod character;

63
src/data/allcharacters.rs Normal file
View file

@ -0,0 +1,63 @@
use std::cmp::Ordering;
use levenshtein::levenshtein;
use crate::data::core::{PrydwenCompatible, PrydwenResponse};
use serde_derive::{Deserialize, Serialize};
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
struct PrydwenAllCharacters {
pub all_characters: AllCharacters,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct AllCharacters {
pub nodes: Vec<Characters>,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Characters {
pub id: String,
pub unit_id: i64,
pub slug: String,
pub name: String,
pub rarity: String,
pub element: String,
pub path: String,
}
impl PrydwenCompatible for PrydwenAllCharacters{}
pub async fn get_nearest_characters(name: String) -> Option<Vec<Characters>> {
let mut characters = get_all_characters().await?;
characters.nodes.sort_by(|a, b | {
match a.name.to_lowercase().contains(name.to_lowercase().as_str()) {
true => {
match b.name.to_lowercase().contains(name.to_lowercase().as_str()) {
true => return Ordering::Equal,
false => return Ordering::Less
}
}
false => {
match b.name.to_lowercase().contains(name.to_lowercase().as_str()){
true => return Ordering::Greater,
false => return levenshtein(a.name.to_lowercase().as_str(), name.to_lowercase().as_str()).partial_cmp(&levenshtein(b.name.to_lowercase().as_str(), name.to_lowercase().as_str())).unwrap()
}
}
}
});
characters.nodes.truncate(5);
Some(characters.nodes)
}
async fn get_all_characters() -> Option<AllCharacters> {
let data = reqwest::get("https://www.prydwen.gg/page-data/star-rail/characters/page-data.json").await.ok()?.json::<PrydwenResponse<PrydwenAllCharacters>>().await;
match data {
Ok(d) => {
Some(d.result.data.all_characters)
}
Err(_) => None
}
}

492
src/data/character.rs Normal file
View file

@ -0,0 +1,492 @@
use serde_derive::Deserialize;
use serde_derive::Serialize;
use serde_json::Value;
use crate::data::core::{PrydwenCompatible, PrydwenResponse};
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PrydwenCharacter {
pub current_unit: CharacterWrapper,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CharacterWrapper {
pub nodes: Vec<Character>,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Character {
pub id: String,
pub updated_at: String,
pub created_at: String,
pub unit_id: i64,
pub name: String,
pub slug: String,
pub small_image: SmallImage,
pub full_image: FullImage,
pub description: Option<Description>,
pub default_role: String,
pub affiliation: String,
pub rarity: String,
pub element: String,
pub path: String,
pub stats: Stats,
pub ascension_materials: AscensionMaterials,
pub skills: Vec<Skill>,
pub eidolon: Vec<Eidolon>,
pub traces: Vec<Trace>,
pub traces_small: Vec<TracesSmall>,
pub review: Option<Review>,
pub pros: Option<Pros>,
pub cons: Option<Cons>,
pub voice_actors: VoiceActors,
pub ratings: Ratings,
pub energy_ultimate: String,
pub build_data: Option<Vec<BuildDaum>>,
pub videos: Value,
pub teams: Option<Vec<Team>>,
pub character_builder_info: CharacterBuilderInfo,
pub tier_category: String,
pub release_date: Option<String>,
pub is_released: bool,
pub is_new: Value,
pub is_updated: Value,
pub hide_skills: bool,
pub is_review_pending: bool,
pub available_in_cbt3: Option<bool>,
pub show_build: Option<bool>,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SmallImage {
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 FullImage {
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 Description {
pub raw: String,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Stats {
#[serde(rename = "hp_base")]
pub hp_base: f64,
#[serde(rename = "def_base")]
pub def_base: f64,
#[serde(rename = "atk_base")]
pub atk_base: f64,
#[serde(rename = "speed_base")]
pub speed_base: i64,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct AscensionMaterials {
#[serde(rename = "mats_1")]
pub mats_1: String,
#[serde(rename = "mats_2")]
pub mats_2: String,
#[serde(rename = "mats_3")]
pub mats_3: String,
#[serde(rename = "mats_4")]
pub mats_4: String,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Skill {
pub skill_id: String,
pub name: String,
#[serde(rename = "type")]
pub type_field: String,
pub enhanced_name: Value,
pub tags: Option<String>,
pub description_level1: DescriptionLevel1,
#[serde(rename = "descriptionLevel1Enhanced")]
pub description_level1enhanced: Value,
pub has_enhanced_version: Value,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct DescriptionLevel1 {
pub raw: String,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Eidolon {
pub eidolon_id: String,
pub name: String,
#[serde(rename = "upgrade1Name")]
pub upgrade1name: String,
#[serde(rename = "upgrade1Desc")]
pub upgrade1desc: Upgrade1Desc,
#[serde(rename = "upgrade2Name")]
pub upgrade2name: String,
#[serde(rename = "upgrade2Desc")]
pub upgrade2desc: Upgrade2Desc,
#[serde(rename = "upgrade3Name")]
pub upgrade3name: String,
#[serde(rename = "upgrade3Desc")]
pub upgrade3desc: Upgrade3Desc,
#[serde(rename = "upgrade4Name")]
pub upgrade4name: String,
#[serde(rename = "upgrade4Desc")]
pub upgrade4desc: Upgrade4Desc,
#[serde(rename = "upgrade5Name")]
pub upgrade5name: String,
#[serde(rename = "upgrade5Desc")]
pub upgrade5desc: Upgrade5Desc,
#[serde(rename = "upgrade6Name")]
pub upgrade6name: String,
#[serde(rename = "upgrade6Desc")]
pub upgrade6desc: Upgrade6Desc,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Upgrade1Desc {
pub raw: String,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Upgrade2Desc {
pub raw: String,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Upgrade3Desc {
pub raw: String,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Upgrade4Desc {
pub raw: String,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Upgrade5Desc {
pub raw: String,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Upgrade6Desc {
pub raw: String,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Trace {
pub req: String,
pub desc: String,
pub name: String,
#[serde(rename = "sub_nodes")]
pub sub_nodes: Vec<SubNode>,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SubNode {
pub req: String,
pub stat: String,
pub value: String,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct TracesSmall {
pub req: String,
pub stat: String,
pub value: String,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Review {
pub raw: String,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Pros {
pub raw: String,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Cons {
pub raw: String,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct VoiceActors {
pub en: String,
pub kr: String,
pub jpn: String,
pub cn: String,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Ratings {
#[serde(rename = "story_early")]
pub story_early: String,
#[serde(rename = "story_late")]
pub story_late: String,
pub sim: String,
pub bosses: String,
pub farming: String,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct BuildDaum {
#[serde(rename = "relic_1")]
pub relic_1: String,
#[serde(rename = "relic_2")]
pub relic_2: String,
pub planar: String,
#[serde(rename = "cone_1")]
pub cone_1: String,
#[serde(rename = "cone_2")]
pub cone_2: String,
#[serde(rename = "cone_3")]
pub cone_3: String,
#[serde(rename = "cone_4")]
pub cone_4: String,
#[serde(rename = "cone_5")]
pub cone_5: String,
#[serde(rename = "cone_6")]
pub cone_6: Option<String>,
pub body: Vec<Body>,
pub feet: Vec<Foot>,
pub rope: Vec<Rope>,
pub sphere: Vec<Sphere>,
pub comments: String,
pub substats: String,
pub name: String,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Body {
pub stat: String,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Foot {
pub stat: String,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Rope {
pub stat: String,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Sphere {
pub stat: String,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Team {
pub name: String,
#[serde(rename = "member_1")]
pub member_1: String,
#[serde(rename = "member_2")]
pub member_2: String,
#[serde(rename = "member_3")]
pub member_3: String,
#[serde(rename = "member_4")]
pub member_4: String,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CharacterBuilderInfo {
pub trace_stat1: TraceStat1,
pub trace_stat2: TraceStat2,
pub trace_stat3: TraceStat3,
pub trace_stat_major1: TraceStatMajor1,
pub trace_stat_major2: TraceStatMajor2,
pub trace_stat_major3: TraceStatMajor3,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct TraceStat1 {
pub stat: String,
pub value: f64,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct TraceStat2 {
pub stat: String,
pub value: f64,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct TraceStat3 {
pub stat: String,
pub value: f64,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct TraceStatMajor1 {
pub stat: String,
pub value: i64,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct TraceStatMajor2 {
pub stat: String,
pub value: i64,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct TraceStatMajor3 {
pub stat: String,
pub value: i64,
}
impl PrydwenCompatible for PrydwenCharacter {}
pub async fn get_character_data(name: String) -> Option<Character> {
let data_response = reqwest::get(format!("https://www.prydwen.gg/page-data/star-rail/characters/{}/page-data.json", name)).await.ok()?;
let d = data_response.text().await.ok()?;
let js = &mut serde_json::Deserializer::from_str(d.as_str());
let data : Result<PrydwenResponse<PrydwenCharacter>, _> = serde_path_to_error::deserialize(js);
match data {
Ok(d) => {
Some(d.result.data.current_unit.nodes.get(0).unwrap().clone())
}
Err(err) => {
let path = err.path().to_string();
println!("{}", path);
None
}
}
}

26
src/data/core.rs Normal file
View file

@ -0,0 +1,26 @@
use serde_derive::{Deserialize, Serialize};
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PrydwenResponse<T: PrydwenCompatible> {
pub component_chunk_name: String,
pub path: String,
pub result: Result<T>,
pub static_query_hashes: Vec<String>,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Result<T: PrydwenCompatible> {
pub data: T,
pub page_context: PageContext
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PageContext {
contentfulId: Option<String>
}
pub trait PrydwenCompatible {
}

77
src/data/description.rs Normal file
View file

@ -0,0 +1,77 @@
use serde_derive::Deserialize;
use serde_derive::Serialize;
use serde_json::Value;
use crate::data::character::{Cons, Description, Pros, Review};
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Root {
pub data: Data,
pub content: Vec<Content>,
pub node_type: String,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Data {
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Content {
pub data: Data,
pub content: Vec<Content2>,
pub node_type: String,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Content2 {
pub data: Data,
pub marks: Vec<Value>,
pub value: String,
pub node_type: String,
}
pub trait RawText {
fn get_raw(&self) -> &str;
}
impl RawText for &Description {
fn get_raw(&self) -> &str {
self.raw.as_str()
}
}
impl RawText for &Review {
fn get_raw(&self) -> &str {
self.raw.as_str()
}
}
impl RawText for &Cons {
fn get_raw(&self) -> &str {
self.raw.as_str()
}
}
impl RawText for &Pros {
fn get_raw(&self) -> &str {
self.raw.as_str()
}
}
pub fn get_all_texts<T:RawText>(desc: &T) -> Option<Vec<String>> {
let js = &mut serde_json::Deserializer::from_str(desc.get_raw());
let data : Result<Root, _> = serde_path_to_error::deserialize(js);
match data {
Ok(d) => {
Some(d.content.into_iter().map(|x| x.content.into_iter().map(|y| y.value)).flatten().collect())
}
Err(err) => {
let path = err.path().to_string();
println!("{}", path);
None
}
}
}

10
src/data/mod.rs Normal file
View file

@ -0,0 +1,10 @@
pub struct Data {} // User data, which is stored and accessible in all command invocations
pub type Error = Box<dyn std::error::Error + Send + Sync>;
pub type Context<'a> = poise::Context<'a, Data, Error>;
pub mod core;
pub mod allcharacters;
pub mod character;
pub mod description;
pub mod proscons;

87
src/data/proscons.rs Normal file
View file

@ -0,0 +1,87 @@
use serde_derive::Deserialize;
use serde_derive::Serialize;
use serde_json::Value;
use crate::data::character::{Cons, Pros};
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Root {
pub data: Data,
pub content: Vec<Content>,
pub node_type: String,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Data {
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Content {
pub data: Data,
pub content: Vec<Content2>,
pub node_type: String,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Content2 {
pub data: Data,
#[serde(default)]
pub marks: Vec<Value>,
pub value: Option<String>,
pub node_type: String,
#[serde(default)]
pub content: Vec<Content3>,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Content3 {
pub data: Data,
pub content: Vec<Content4>,
pub node_type: String,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Content4 {
pub data: Data,
pub marks: Vec<Value>,
pub value: String,
pub node_type: String,
}
pub trait ProsCons {
fn get_raw(&self) -> &str;
}
impl ProsCons for &Cons {
fn get_raw(&self) -> &str {
self.raw.as_str()
}
}
impl ProsCons for &Pros {
fn get_raw(&self) -> &str {
self.raw.as_str()
}
}
pub fn get_proscons_texts<T:ProsCons>(desc: &T) -> Option<Vec<String>> {
let js = &mut serde_json::Deserializer::from_str(desc.get_raw());
let data : Result<Root, _> = serde_path_to_error::deserialize(js);
match data {
Ok(d) => {
Some(d.content.into_iter().map(|x| x.content.into_iter().map(|y| y.content.into_iter().map(|z| z.content.into_iter().map(|w| w.value)).flatten()).flatten()).flatten().collect())
}
Err(err) => {
let path = err.path().to_string();
println!("{}", path);
None
}
}
}

View file

@ -0,0 +1,27 @@
use std::collections::HashMap;
use serenity::utils::Color;
pub fn get_element_color(name: &String) -> Color {
let mut color_map = HashMap::new();
color_map.insert("Physical", Color::new(9934743));
color_map.insert("Fire", Color::new(15615805));
color_map.insert("Ice", Color::new(2527955));
color_map.insert("Lightning", Color::new(12999390));
color_map.insert("Wind", Color::new(6410131));
color_map.insert("Quantum", Color::new(6313929));
color_map.insert("Imaginary", Color::new(15982903));
color_map.get(name.as_str()).unwrap_or(&Color::new(0)).to_owned()
}
pub fn get_path_color(name: &String) -> Color {
let mut color_map = HashMap::new();
color_map.insert("Abundance", Color::new(6410131));
color_map.insert("Destruction", Color::new(9934743));
color_map.insert("Erudition", Color::new(12999390));
color_map.insert("Harmony", Color::new(2527955));
color_map.insert("Hunt", Color::new(15615805));
color_map.insert("Nihility", Color::new(6313929));
color_map.insert("Preservation", Color::new(15982903));
color_map.get(name.as_str()).unwrap_or(&Color::new(0)).to_owned()
}

View file

@ -0,0 +1,25 @@
use std::collections::HashMap;
pub fn get_element_emote(name: &String) -> &'static str {
let mut emote_map = HashMap::new();
emote_map.insert("Physical", "<:ele_physical:1102553011928186901>");
emote_map.insert("Fire", "<:ele_fire:1102553084523196470>");
emote_map.insert("Ice", "<:ele_ice:1102553257009750046>");
emote_map.insert("Lightning", "<:ele_lightning:1102553255692738640>");
emote_map.insert("Wind", "<:ele_wind:1102553253733998643>");
emote_map.insert("Quantum", "<:ele_quantum:1102553252278583326>");
emote_map.insert("Imaginary", "<:ele_imaginary:1102551584057069649>");
emote_map.get(name.as_str()).unwrap_or(&"").to_owned()
}
pub fn get_path_emote(name: &String) -> &'static str {
let mut emote_map = HashMap::new();
emote_map.insert("Abundance", "<:path_abundance:1102554507986088038>");
emote_map.insert("Destruction", "<:path_destruction:1102554505511448646>");
emote_map.insert("Erudition", "<:path_erudition:1102554503527530597>");
emote_map.insert("Harmony","<:path_harmony:1102554501518471249>");
emote_map.insert("Hunt", "<:path_hunt:1102554500352458812>");
emote_map.insert("Nihility", "<:path_nihility:1102554499085778964>");
emote_map.insert("Preservation", "<:path_preservation:1102554496757927968>");
emote_map.get(name.as_str()).unwrap_or(&"").to_owned()
}

3
src/utils/mod.rs Normal file
View file

@ -0,0 +1,3 @@
pub mod color_manager;
pub mod emote_manager;