use indexmap::IndexMap; use lazy_static::lazy_static; use serde_derive::Deserialize; use serde_json::{json, Value}; use std::collections::HashMap; use std::sync::Mutex; use crate::data_source::{DataSource, Stat}; use crate::ChampInfo; use crate::Champion; #[derive(Deserialize, Debug)] struct BuildResponse { lol: Lol, } #[derive(Deserialize, Debug)] struct Lol { #[serde(rename = "championsReport")] champions_report: Vec, } #[derive(Deserialize, Debug)] struct ChampionReport { champion_id: u32, role: String, patch: String, stats: Stats, } #[derive(Deserialize, Debug)] struct Stats { starting_items: Build, core_builds: Build, big_item_builds: Build, skills: Build, most_common_starting_items: Build, most_common_core_builds: Build, most_common_big_item_builds: Build, most_common_skills: Build, } #[derive(Deserialize, Debug)] struct Build { build: Vec, win_rate: f64, games: u32, } #[derive(Deserialize, Debug, Clone)] struct ChampStat { champion_id: Option, stats: Option, matchups: Option>, #[serde(rename = "winRate")] win_rate: Option, games: Option, kda: Option, } #[derive(Deserialize, Debug, Clone)] struct Info { id: String, } lazy_static! { static ref CHAMPIONS_REPORT: Mutex> = Mutex::new(Vec::new()); static ref CHAMPIONS_STATS: Mutex> = Mutex::new(HashMap::new()); } pub struct CGGDataSource; impl CGGDataSource { fn make_item_set(&self, build: &Build, label: &str) -> Value { json!({ "items": build.build.iter().map(|x| json!({"id": x.to_string(), "count": 1})).collect::>(), "type": format!("{} ({:.2}% - {} games)", label, build.win_rate * 100., build.games) }) } fn make_item_set_from_list(&self, build: &Build, label: &str, skills: &Build) -> Value { let key_order = skills .build .iter() .map(|x| x.to_string()) .collect::>() .join(""); self.make_item_set(build, [label, &key_order.as_str()].join(" ").as_str()) } } fn extract_json(pattern: &str, page: &str) -> String { let json = page[page.find(pattern).unwrap() + pattern.len()..].to_owned(); json[..json.find("};").unwrap() + 1].replace("undefined", "null") } impl DataSource for CGGDataSource { fn get_alias(&self) -> &str { "CGG" } fn get_timeout(&self) -> u64 { 0 } fn get_champs_with_positions( &self, client: &ureq::Agent, _champion: &Champion, ) -> IndexMap> { let req = client.get("https://champion.gg").call().unwrap(); let page = &req.into_string().unwrap(); let datas: BuildResponse = serde_json::from_str(&extract_json("window.__PRELOADED_STATE__ = ", &page)).unwrap(); let champs_stats: HashMap = serde_json::from_str(&extract_json("window.__FLASH_CMS_APOLLO_STATE__ = ", &page)) .unwrap(); for entry in champs_stats.iter() { CHAMPIONS_STATS .lock() .unwrap() .insert(entry.0.to_owned(), entry.1.to_owned()); } let mut champions: IndexMap> = IndexMap::new(); for champ in &datas.lol.champions_report { let id = champ.champion_id; if champions.contains_key(&id) { let mut roles = champions.get(&id).unwrap().to_owned(); roles.push(champ.role.to_owned()); champions.insert(id, roles); } else { champions.insert(id, vec![champ.role.to_owned()]); } } for report in datas.lol.champions_report { CHAMPIONS_REPORT.lock().unwrap().push(report); } champions } fn get_champ_data_with_win_pourcentage( &self, champ: &ChampInfo, positions: &[String], _client: &ureq::Agent, ) -> Vec<(String, Vec, Stat)> { let mut data = vec![]; for position in positions { let mut some_champ: Option<&ChampionReport> = None; let reports = CHAMPIONS_REPORT.lock().unwrap(); for champion in reports.iter() { if champion.champion_id.to_string() == champ.key && champion.role == *position { some_champ = Some(champion); break; } } if let Some(champ) = some_champ { let mut blocks = vec![]; blocks.push(self.make_item_set_from_list( &champ.stats.starting_items, "Highest % Win Starting Items | Skills: ", &champ.stats.skills, )); blocks.push( self.make_item_set(&champ.stats.core_builds, "Highest % Win Core Build Path:"), ); blocks.push( self.make_item_set(&champ.stats.big_item_builds, "Highest % Win Big Items:"), ); blocks.push(self.make_item_set_from_list( &champ.stats.most_common_starting_items, "Most Frequent Starting Items | Skills: ", &champ.stats.most_common_skills, )); blocks.push(self.make_item_set( &champ.stats.most_common_core_builds, "Most Frequent Build Path", )); blocks.push(self.make_item_set( &champ.stats.most_common_big_item_builds, "Most Frequent Big Items:", )); let mut key: String = String::new(); let champs_stats = CHAMPIONS_STATS.lock().unwrap(); for val in champs_stats.values() { if val.champion_id.is_some() && val.champion_id.unwrap() == champ.champion_id { key = val.stats.as_ref().unwrap().id.to_owned(); } } let stat = champs_stats.get(&key).unwrap(); data.push(( position.to_owned(), blocks, Stat { win_rate: stat.win_rate.unwrap(), games: stat.games.unwrap(), kda: stat.kda.unwrap(), patch: champ.patch.to_owned(), }, )); } } data } }