diff --git a/src/main.rs b/src/main.rs index f35ec4f..be10a6f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,7 +8,7 @@ extern crate select; extern crate regex; #[macro_use] extern crate lazy_static; -use std::collections::HashMap; +use std::collections::BTreeMap; use std::{fs, thread, time}; use std::path::{Path, PathBuf}; use reqwest::header::{Headers, UserAgent}; @@ -16,6 +16,7 @@ use select::document::Document; use select::predicate::{Class, Name}; use regex::Regex; use serde_json::Value; +use time::Duration; #[derive(Deserialize)] struct Realm { @@ -24,12 +25,12 @@ struct Realm { #[derive(Deserialize)] struct Champion { - data: HashMap + data: BTreeMap } #[derive(Deserialize)] struct ChampInfo { - id: String + /*id: String*/ } #[derive(Serialize, Deserialize)] @@ -46,14 +47,16 @@ struct ItemSet { const USER_AGENT: &str = "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:60.0) Gecko/20100101 Firefox/60.0"; const LOL_CHAMPS_DIR: &str = ".\\champs"; +const CONSUMABLES: [u32; 9] = [2003, 2004, 2055, 2031, 2032, 2033, 2138, 2140, 2139]; +const TRINKETS: [u32; 3] = [3340, 3364, 3363]; lazy_static! { - static ref ITEM_TYPES: HashMap<&'static str, [&'static str; 2]> = { - let mut m = HashMap::new(); - m.insert("Most Frequent Starters", ["firstItems", "mostGames"]); - m.insert("Highest Win % Starters", ["firstItems", "highestWinPercent"]); - m.insert("Most Frequent Core Build", ["items", "mostGames"]); - m.insert("Highest Win % Core Build", ["firstItems", "highestWinPercent"]); + static ref ITEM_TYPES: BTreeMap<&'static str, [&'static str; 2]> = { + let mut m = BTreeMap::new(); + m.insert("1. Most Frequent Starters", ["firstItems", "mostGames"]); + m.insert("2. Highest Win % Starters", ["firstItems", "highestWinPercent"]); + m.insert("3. Most Frequent Core Build", ["items", "mostGames"]); + m.insert("4. Highest Win % Core Build", ["firstItems", "highestWinPercent"]); m }; } @@ -66,7 +69,10 @@ fn main() { let mut headers = Headers::new(); headers.set(UserAgent::new(USER_AGENT)); - let client = reqwest::Client::builder().default_headers(headers).build().unwrap(); + let client = reqwest::Client::builder() + .default_headers(headers) + .timeout(Duration::from_secs(10)) + .build().unwrap(); let realm: Realm = client.get("https://ddragon.leagueoflegends.com/realms/euw.json").send().unwrap().json().unwrap(); info!("LoL version: {}", realm.v); @@ -85,7 +91,7 @@ fn main() { fs::create_dir_all(&path).unwrap(); for pos in positions { write_item_set(&id, &pos, &patch, &path, &client); - thread::sleep(time::Duration::from_millis(300)); + thread::sleep(Duration::from_millis(300)); } } else { info!("{} not found in LoL champs", &id); @@ -93,13 +99,13 @@ fn main() { } } -fn get_champs_with_positions_and_patch(client: &reqwest::Client) -> (HashMap>, String) { +fn get_champs_with_positions_and_patch(client: &reqwest::Client) -> (BTreeMap>, String) { let page = client.get("https://champion.gg").send().unwrap().text().unwrap(); let document = Document::from(&*page); let patch = document.find(Class("analysis-holder")).next().unwrap().find(Name("strong")).next().unwrap().text(); - let mut champions = HashMap::new(); + let mut champions = BTreeMap::new(); for node in document.find(Class("champ-height")) { let id = node.find(Class("home-champion")).next().unwrap().attr("class").unwrap().split(' ').last().unwrap().to_string(); let positions = node.find(Name("a")) @@ -112,9 +118,13 @@ fn get_champs_with_positions_and_patch(client: &reqwest::Client) -> (HashMap Value { - let page = client.get(&format!("https://champion.gg/champion/{}/{}?league=", id, position)).send().unwrap().text().unwrap(); - serde_json::from_str(&find_champ_data(&page)).unwrap() +fn get_champ_data(id: &str, position: &str, client: &reqwest::Client) -> Option { + let mut req = client.get(&format!("https://champion.gg/champion/{}/{}?league=", id, position)).send().unwrap(); + if req.status() == reqwest::StatusCode::Ok { + serde_json::from_str(&find_champ_data(&req.text().unwrap())).unwrap() + } else { + None + } } fn make_item_set(data: &Value, label: &str) -> Value { @@ -124,26 +134,45 @@ fn make_item_set(data: &Value, label: &str) -> Value { }) } +fn make_item_set_from_list(list: Vec, label: &str, key: &str, data: &Value) -> Value { + json!({ + "items": list.iter().map(|x| json!({"id": x.to_string(), "count": 1})).collect::>(), + "type": format!("{} {}", label, data["skills"][key]["order"].as_array().unwrap().iter().map(|x| data["skills"]["skillInfo"].as_array().unwrap()[x.as_str().unwrap().parse::().unwrap()-1]["key"].as_str().unwrap()).collect::>().join(".")) + }) +} + fn write_item_set(id: &str, pos: &str, ver: &str, path: &PathBuf, client: &reqwest::Client) { info!("Retrieving data for {} at {}", id, pos); let data = get_champ_data(id, pos, client); - let mut item_set = ItemSet { - title: format!("CGG {} {} - {:.2}%", pos, ver, data["stats"]["winRate"].as_f64().unwrap() * 100.), - type_: "custom".to_string(), - map: "any".to_string(), - mode: "any".to_string(), - priority: false, - sortrank: 0, - blocks: vec![] - }; + match data { + Some(data) => { + let mut item_set = ItemSet { + title: format!("CGG {} {} - {:.2}%", pos, ver, data["stats"]["winRate"].as_f64().unwrap() * 100.), + type_: "custom".to_string(), + map: "any".to_string(), + mode: "any".to_string(), + priority: false, + sortrank: 0, + blocks: vec![] + }; - for (label, path) in ITEM_TYPES.iter() { - item_set.blocks.push(make_item_set(&data[&path[0]][&path[1]], label)); + for (label, path) in ITEM_TYPES.iter() { + if !data[&path[0]].get(&path[1]).is_none() { + item_set.blocks.push(make_item_set(&data[&path[0]][&path[1]], label)); + } + } + + item_set.blocks.push(make_item_set_from_list(CONSUMABLES.to_vec(), "Consumables | Frequent:", "mostGames", &data)); + item_set.blocks.push(make_item_set_from_list(TRINKETS.to_vec(), "Trinkets | Wins:", "highestWinPercent", &data)); + + info!("Writing item set for {} at {}", id, pos); + fs::write(path.join(format!("CGG_{}_{}.json", id, pos)), serde_json::to_string_pretty(&item_set).unwrap()).unwrap(); + }, + None => { + error!("Can't get data for {} at {}", id, pos); + } } - - info!("Writing item set for {} at {}", id, pos); - fs::write(path.join(format!("CGG_{}_{}.json", id, pos)), serde_json::to_string_pretty(&item_set).unwrap()).unwrap(); } fn find_champ_data(text: &str) -> String {