use indexmap::IndexMap; use lazy_static::lazy_static; use regex::Regex; use select::document::Document; use select::predicate::{Class, Name}; use serde_json::{json, Value}; use crate::data_source::DataSource; const CONSUMABLES: [u32; 9] = [2003, 2004, 2055, 2031, 2032, 2033, 2138, 2140, 2139]; const TRINKETS: [u32; 3] = [3340, 3364, 3363]; const ITEM_TYPES: &'static [(&str, [&str; 2]); 4] = &[ ("Most Frequent Starters", ["firstItems", "mostGames"]), ( "Highest Win % Starters", ["firstItems", "highestWinPercent"], ), ("Most Frequent Core Build", ["items", "mostGames"]), ("Highest Win % Core Build", ["items", "highestWinPercent"]), ]; pub struct CGGDataSource; impl CGGDataSource { fn make_item_set(&self, data: &Value, label: &str) -> Value { json!({ "items": data["items"].as_array().unwrap().iter().map(|x| json!({"id": x["id"].as_str(), "count": 1})).collect::>(), "type": format!("{} ({:.2}% - {} games)", label, data["winPercent"].as_f64().unwrap() * 100., data["games"].as_u64().unwrap()) }) } fn make_item_set_from_list( &self, list: &Vec, label: &str, key: &str, data: &Value, ) -> Value { let mut key_order = String::new(); if !data["skills"].get("skillInfo").is_none() { key_order = 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("."); } json!({ "items": list.iter().map(|x| json!({"id": x.to_string(), "count": 1})).collect::>(), "type": format!("{} {}", label, key_order) }) } } impl DataSource for CGGDataSource { fn get_alias(&self) -> &str { "CGG" } fn get_champs_with_positions_and_patch( &self, client: &ureq::Agent, ) -> (IndexMap>, String) { let page = client .get("https://champion.gg") .call() .unwrap() .into_string() .unwrap(); let document = Document::from(&*page); let patch = document .find(Class("analysis-holder")) .next() .unwrap() .find(Name("strong")) .next() .unwrap() .text(); let mut champions = IndexMap::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")) .skip(1) .map(|x| { x.attr("href") .unwrap() .split('/') .last() .unwrap() .to_string() }) .collect(); champions.insert(id, positions); } (champions, patch) } fn get_champ_data_with_win_pourcentage( &self, id: &str, position: &str, client: &ureq::Agent, ) -> Option<(Vec, f64)> { let req = client .get(&format!( "https://champion.gg/champion/{}/{}?league=", id, position )) .call() .unwrap(); let response_status = req.status(); if 300 > response_status && response_status >= 200 { lazy_static! { static ref RE: Regex = Regex::new(r"(?m)^\s+matchupData\.championData = (.*)$").unwrap(); } let data: Value = serde_json::from_str(&RE.captures(&req.into_string().unwrap())?[1]).unwrap(); let mut blocks = vec![]; for (label, path) in ITEM_TYPES.iter() { if !data[&path[0]].get(&path[1]).is_none() { blocks.push(self.make_item_set(&data[&path[0]][&path[1]], label)); } } blocks.push(self.make_item_set_from_list( &CONSUMABLES.to_vec(), "Consumables | Frequent:", "mostGames", &data, )); blocks.push(self.make_item_set_from_list( &TRINKETS.to_vec(), "Trinkets | Wins:", "highestWinPercent", &data, )); Some((blocks, data["stats"]["winRate"].as_f64().unwrap() * 100.)) } else { None } } }