CGGItemSets/src/kb_data_source.rs

409 lines
11 KiB
Rust
Raw Normal View History

2021-03-14 14:25:37 +01:00
use crate::data_source::{Build, DataSource, Item, Stat};
use crate::Champion as ChampionLoL;
2021-03-14 11:21:49 +01:00
use crate::ChampInfo;
2021-03-17 09:13:19 +01:00
use crate::USER_AGENT_VALUE;
use indexmap::IndexMap;
2021-03-16 09:12:30 +01:00
use lazy_static::lazy_static;
use serde_derive::Deserialize;
use serde_json::{json, Value};
2021-03-17 09:07:01 +01:00
use std::time::Duration;
2021-03-17 09:07:01 +01:00
pub struct KBDataSource {
token: Option<String>,
}
#[derive(Deserialize, Debug)]
struct ChampionResponse {
patches: Vec<Patch>,
champions: Vec<Champion>,
}
#[derive(Deserialize, Debug)]
struct Patch {
enabled: bool,
#[serde(rename = "patchVersion")]
patch_version: String,
patchid: u32,
start: String,
}
#[derive(Deserialize, Debug)]
struct Champion {
id: u32,
name: String,
#[serde(rename = "className")]
classname: String,
builds: Option<Position>,
}
#[derive(Deserialize, Debug)]
struct Position {
bot: u16,
jungle: u16,
mid: u16,
support: u16,
top: u16,
}
#[derive(Deserialize, Debug)]
struct BuildResponse {
builds2: Vec<KBBuild>,
}
#[derive(Deserialize, Debug)]
struct KBBuild {
2021-03-15 23:00:20 +01:00
position: String,
item0: KBItem,
item1: KBItem,
item2: KBItem,
item3: KBItem,
item4: KBItem,
item5: KBItem,
item6: KBItem,
#[serde(rename = "startItem0")]
start_item0: KBItem,
#[serde(rename = "startItem1")]
start_item1: KBItem,
#[serde(rename = "startItem2")]
start_item2: KBItem,
#[serde(rename = "startItem3")]
start_item3: KBItem,
#[serde(rename = "startItem4")]
start_item4: KBItem,
#[serde(rename = "startItem5")]
start_item5: KBItem,
#[serde(rename = "skillOrder")]
skill_order: String,
wins: f64,
2021-03-16 11:13:54 +01:00
games: u32,
summoner: Summoner,
2021-03-16 11:13:54 +01:00
patch: Patch,
}
#[derive(Deserialize, Debug)]
struct KBItem {
#[serde(rename = "itemId")]
item_id: u32,
name: String,
}
#[derive(Deserialize, Debug)]
struct Summoner {
name: String,
}
2021-03-17 09:07:01 +01:00
// It will be better to use Result...
fn get_auth_token() -> Option<String> {
let client = ureq::AgentBuilder::new()
.user_agent(USER_AGENT_VALUE)
.timeout(Duration::from_secs(10))
.build();
let mut bundle = match client.get("https://koreanbuilds.net/bundle.js").call() {
Ok(resp) => match resp.into_string() {
Ok(val) => val,
Err(_) => return None,
},
Err(_) => return None,
};
let auth_position = match bundle.find("Authorization") {
Some(position) => position,
None => return None,
};
bundle = (&bundle[(auth_position + 13)..]).to_string();
let q_position = match bundle.find('"') {
Some(position) => position,
None => return None,
};
bundle = (&bundle[(q_position + 1)..]).to_string();
match bundle.find('"') {
Some(position) => Some((&bundle[..position]).to_string()),
None => None,
}
2021-03-16 09:12:30 +01:00
}
2021-03-16 09:12:30 +01:00
impl KBDataSource {
2021-03-17 09:07:01 +01:00
pub fn new() -> &'static KBDataSource {
lazy_static! {
static ref DATASOURCE: KBDataSource = KBDataSource {
token: get_auth_token(),
};
2021-03-14 17:17:29 +01:00
}
2021-03-17 09:07:01 +01:00
&DATASOURCE
2021-03-14 18:10:51 +01:00
}
2021-03-10 12:22:00 +01:00
fn get_champion_response(&self, client: &ureq::Agent) -> Option<ChampionResponse> {
2021-03-17 09:07:01 +01:00
if let Some(token) = &self.token {
2021-03-16 09:12:30 +01:00
return match client
.get("https://api.koreanbuilds.net/champions?patchid=-1")
.set("Accept", "application/json")
.set("Authorization", token.as_str())
.call()
{
Ok(resp) => match resp.into_json() {
Ok(val) => val,
Err(_) => None,
},
2021-03-11 00:00:04 +01:00
Err(_) => None,
2021-03-16 09:12:30 +01:00
};
2021-03-11 00:00:04 +01:00
}
2021-03-16 09:12:30 +01:00
None
}
fn get_positions(position: Option<Position>) -> Vec<String> {
let mut positions = Vec::new();
2021-03-11 00:00:04 +01:00
if let Some(pos) = position {
if pos.top > 0 {
positions.push("TOP".to_owned());
}
if pos.jungle > 0 {
positions.push("JUNGLE".to_owned());
}
if pos.mid > 0 {
positions.push("MID".to_owned());
}
if pos.bot > 0 {
positions.push("BOT".to_owned());
}
if pos.support > 0 {
positions.push("SUPPORT".to_owned());
}
}
// TODO: find better solution, activate all positions for retrieve older builds
if positions.is_empty() {
positions.push("TOP".to_owned());
positions.push("JUNGLE".to_owned());
positions.push("MID".to_owned());
positions.push("BOT".to_owned());
positions.push("SUPPORT".to_owned());
}
positions
}
2021-03-15 23:00:20 +01:00
fn get_build(&self, build: &KBBuild) -> (String, Vec<Value>, Stat) {
let mut starting_items: Vec<Item> = vec![];
let mut blocks = vec![];
if build.start_item0.item_id != 0 {
starting_items.push(Item {
id: build.start_item0.item_id.to_string(),
count: 1,
})
}
if build.start_item1.item_id != 0 {
starting_items.push(Item {
id: build.start_item1.item_id.to_string(),
count: 1,
})
}
if build.start_item2.item_id != 0 {
starting_items.push(Item {
id: build.start_item2.item_id.to_string(),
count: 1,
})
}
if build.start_item3.item_id != 0 {
starting_items.push(Item {
id: build.start_item3.item_id.to_string(),
count: 1,
})
}
if build.start_item4.item_id != 0 {
starting_items.push(Item {
id: build.start_item4.item_id.to_string(),
count: 1,
})
}
if build.start_item5.item_id != 0 {
starting_items.push(Item {
id: build.start_item5.item_id.to_string(),
count: 1,
})
}
blocks.push(json!(Build {
type_: format!("Early game items | skillOrder : {}", build.skill_order),
items: starting_items
}));
let mut final_items: Vec<Item> = vec![];
if build.item0.item_id != 0 {
final_items.push(Item {
id: build.item0.item_id.to_string(),
count: 1,
})
}
if build.item1.item_id != 0 {
final_items.push(Item {
id: build.item1.item_id.to_string(),
count: 1,
})
}
if build.item2.item_id != 0 {
final_items.push(Item {
id: build.item2.item_id.to_string(),
count: 1,
})
}
if build.item3.item_id != 0 {
final_items.push(Item {
id: build.item3.item_id.to_string(),
count: 1,
})
}
if build.item4.item_id != 0 {
final_items.push(Item {
id: build.item4.item_id.to_string(),
count: 1,
})
}
if build.item5.item_id != 0 {
final_items.push(Item {
id: build.item5.item_id.to_string(),
count: 1,
})
}
if build.item6.item_id != 0 {
final_items.push(Item {
id: build.item6.item_id.to_string(),
count: 1,
})
}
blocks.push(json!(Build {
type_: format!(
"Item order by time finished | Summoner : {}",
build.summoner.name
),
items: final_items
}));
(
build.position.to_owned().to_uppercase(),
blocks,
Stat {
2021-03-16 11:13:54 +01:00
win_rate: (build.wins / build.games as f64) * 100.,
games: build.games,
2021-03-15 23:00:20 +01:00
kda: 0.0,
2021-03-16 11:13:54 +01:00
patch: build.patch.patch_version.to_owned(),
2021-03-15 23:00:20 +01:00
},
)
}
}
impl DataSource for KBDataSource {
fn get_alias(&self) -> &str {
"KB"
}
2021-03-14 09:49:18 +01:00
fn get_timeout(&self) -> u64 {
300
}
fn get_champs_with_positions(&self, client: &ureq::Agent, _champion: &ChampionLoL) -> IndexMap<u32, Vec<String>> {
let mut champions = IndexMap::new();
let data: ChampionResponse = match self.get_champion_response(client) {
Some(val) => val,
None => {
2021-03-16 11:13:54 +01:00
return champions;
}
};
for champ in data.champions {
2021-03-16 10:53:16 +01:00
champions.insert(champ.id, KBDataSource::get_positions(champ.builds));
}
2021-03-16 11:13:54 +01:00
champions
}
fn get_champ_data_with_win_pourcentage(
&self,
2021-03-13 23:02:26 +01:00
champ: &ChampInfo,
2021-03-15 23:11:40 +01:00
position: &[String],
2021-03-10 12:22:00 +01:00
client: &ureq::Agent,
2021-03-15 23:00:20 +01:00
) -> Vec<(String, Vec<Value>, Stat)> {
let mut champ_data = vec![];
2021-03-17 09:07:01 +01:00
if let Some(token) = &self.token {
2021-03-16 11:13:54 +01:00
let data: BuildResponse = match client
.get(&format!(
"https://api.koreanbuilds.net/builds?chmpname={}&patchid=-2&position=COMPOSITE",
champ.id
))
.set("Accept", "application/json")
.set("Authorization", token.as_str())
.call()
{
Ok(resp) => match resp.into_json() {
Ok(val) => val,
2021-03-15 23:00:20 +01:00
Err(_) => {
return vec![];
}
2021-03-16 11:13:54 +01:00
},
Err(_) => {
return vec![];
}
};
2021-03-15 23:00:20 +01:00
2021-03-16 11:13:54 +01:00
for pos in position {
let mut build: Option<&KBBuild> = None;
2021-03-15 23:00:20 +01:00
2021-03-16 11:13:54 +01:00
for b in &data.builds2 {
if b.position.to_uppercase() == *pos {
build = Some(b);
break;
2021-03-15 23:00:20 +01:00
}
2021-03-16 11:13:54 +01:00
}
2021-03-15 23:00:20 +01:00
2021-03-16 11:13:54 +01:00
if let Some(b) = build {
champ_data.push(self.get_build(&b));
}
2021-03-16 11:13:54 +01:00
}
2021-03-16 09:12:30 +01:00
}
2021-03-15 23:00:20 +01:00
champ_data
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_get_auth_token() {
2021-03-17 09:07:01 +01:00
match get_auth_token() {
Some(token) => assert!(token.len() > 0),
None => assert!(false),
};
}
#[test]
fn test_get_champs_with_positions_and_patch() {
2021-03-10 12:22:00 +01:00
let client = ureq::AgentBuilder::new()
2021-03-17 09:07:01 +01:00
.user_agent(USER_AGENT_VALUE)
.timeout(Duration::from_secs(10))
2021-03-10 12:22:00 +01:00
.build();
2021-03-17 09:07:01 +01:00
let datasource = KBDataSource::new();
let champion = ChampionLoL {
data: IndexMap::new()
};
let champs_with_positions = datasource.get_champs_with_positions(&client, &champion);
2021-03-16 11:13:54 +01:00
assert!(champs_with_positions.len() > 0);
}
#[test]
fn test_get_champ_data_with_win_pourcentage() {
2021-03-10 12:22:00 +01:00
let client = ureq::AgentBuilder::new()
2021-03-17 09:07:01 +01:00
.user_agent(USER_AGENT_VALUE)
.timeout(Duration::from_secs(10))
2021-03-10 12:22:00 +01:00
.build();
2021-03-17 09:07:01 +01:00
let datasource = KBDataSource::new();
2021-03-14 11:21:49 +01:00
let champ = ChampInfo {
2021-03-16 10:53:16 +01:00
id: String::from("Annie"),
name: String::from("Annie"),
2021-03-14 11:21:49 +01:00
key: String::from("1"),
};
2021-03-15 23:00:20 +01:00
let result = datasource.get_champ_data_with_win_pourcentage(
&champ,
2021-03-16 10:53:16 +01:00
&vec!["MID".to_string()],
2021-03-15 23:00:20 +01:00
&client,
);
assert!(!result.is_empty());
assert!(!result[0].1.is_empty());
assert!(result[0].2.win_rate > 0.);
}
}