From 87afdc4e2440a80786c3e7aca420b4d4b2d43f57 Mon Sep 17 00:00:00 2001 From: nyyu Date: Sun, 1 Jun 2025 13:08:45 +0200 Subject: [PATCH] refactor: centralize HTTP client creation with dynamic user agent --- Cargo.lock | 1 + Cargo.toml | 1 + src/kb_data_source.rs | 39 +++++++--------------- src/main.rs | 75 +++++++++++++++++++++++++------------------ 4 files changed, 57 insertions(+), 59 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3340033..3778444 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -75,6 +75,7 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" name = "cggitem_sets" version = "1.0.0" dependencies = [ + "chrono", "indexmap", "log", "logsy", diff --git a/Cargo.toml b/Cargo.toml index 60a948e..a90dc1f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,7 @@ version = "1.0.0" include = ["src/**/*"] [dependencies] +chrono = {version = "0.4.41", features = ["std"], default-features = false} indexmap = {version = "2.2", features = ["serde", "rayon"]} log = "0.4" logsy = "1.0.1" diff --git a/src/kb_data_source.rs b/src/kb_data_source.rs index 8921b9f..510242f 100644 --- a/src/kb_data_source.rs +++ b/src/kb_data_source.rs @@ -1,13 +1,10 @@ use crate::ChampInfo; use crate::Champion as ChampionLoL; -use crate::USER_AGENT_VALUE; use crate::data_source::{Build, DataSource, Item, Stat}; use indexmap::IndexMap; use log::error; use serde_derive::Deserialize; use serde_json::{Value, json}; -use std::time::Duration; -use ureq::Agent; pub struct KBDataSource { token: Option, @@ -118,12 +115,7 @@ struct Summoner { } // It will be better to use Result... -fn get_auth_token() -> Option { - let client: Agent = Agent::config_builder() - .user_agent(USER_AGENT_VALUE) - .timeout_global(Some(Duration::from_secs(10))) - .build() - .into(); +fn get_auth_token(client: &ureq::Agent) -> Option { let mut bundle = match client.get("https://koreanbuilds.net/bundle.js").call() { Ok(mut resp) => match resp.body_mut().read_to_string() { Ok(val) => val, @@ -141,9 +133,9 @@ fn get_auth_token() -> Option { } impl KBDataSource { - pub fn new() -> KBDataSource { + pub fn new(client: &ureq::Agent) -> KBDataSource { Self { - token: get_auth_token(), + token: get_auth_token(client), } } @@ -371,10 +363,15 @@ impl DataSource for KBDataSource { mod tests { use super::*; + use crate::create_http_client; + use std::sync::LazyLock; + + static CLIENT: LazyLock = LazyLock::new(|| create_http_client()); + static DATASOURCE: LazyLock = LazyLock::new(|| KBDataSource::new(&CLIENT)); #[test] fn test_get_auth_token() { - match get_auth_token() { + match get_auth_token(&CLIENT) { Some(token) => assert!(token.len() > 0), None => assert!(false), }; @@ -382,36 +379,24 @@ mod tests { #[test] fn test_get_champs_with_positions_and_patch() { - let client = ureq::Agent::config_builder() - .user_agent(USER_AGENT_VALUE) - .timeout_global(Some(Duration::from_secs(10))) - .build() - .into(); - let datasource = KBDataSource::new(); let champion = ChampionLoL { data: IndexMap::new(), }; - let champs_with_positions = datasource.get_champs_with_positions(&client, &champion); + let champs_with_positions = DATASOURCE.get_champs_with_positions(&CLIENT, &champion); assert!(champs_with_positions.len() > 0); } #[test] fn test_get_champ_data_with_win_pourcentage() { - let client = ureq::Agent::config_builder() - .user_agent(USER_AGENT_VALUE) - .timeout_global(Some(Duration::from_secs(10))) - .build() - .into(); - let datasource = KBDataSource::new(); let champ = ChampInfo { id: String::from("Annie"), name: String::from("Annie"), key: String::from("1"), }; - let result = datasource.get_champ_data_with_win_pourcentage( + let result = DATASOURCE.get_champ_data_with_win_pourcentage( &champ, &vec!["MID".to_string()], - &client, + &CLIENT, ); assert!(!result.is_empty()); assert!(!result[0].1.is_empty()); diff --git a/src/main.rs b/src/main.rs index d72ac1c..8fad247 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,4 @@ +use chrono::{Datelike, Local, NaiveDate}; use indexmap::IndexMap; #[cfg(target_os = "windows")] use log::debug; @@ -10,7 +11,6 @@ use std::io; use std::io::Error; #[cfg(target_os = "windows")] use std::io::ErrorKind; -use std::ops::Deref; use std::path::{Path, PathBuf}; use std::time::Instant; use std::{fs, thread, time}; @@ -44,8 +44,6 @@ pub struct ChampInfo { key: String, } -const USER_AGENT_VALUE: &str = - "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:139.0) Gecko/20100101 Firefox/139.0"; const DEFAULT_LOL_CHAMPS_DIR: &str = "./champs"; #[cfg(target_os = "windows")] const REG_KEY_LOL_RADS: &str = r"SOFTWARE\WOW6432Node\Riot Games\RADS"; @@ -76,11 +74,7 @@ fn main() -> Result<(), Box> { }; info!("LoL Champs Folder: {}", lol_champs_dir.display()); - let client: Agent = Agent::config_builder() - .user_agent(USER_AGENT_VALUE) - .timeout_global(Some(Duration::from_secs(10))) - .build() - .into(); + let client: Agent = create_http_client(); let realm: Realm = client .get("https://ddragon.leagueoflegends.com/realms/euw.json") @@ -99,10 +93,10 @@ fn main() -> Result<(), Box> { info!("LoL numbers of champs: {}", champion.data.len()); let data_sources: Vec> = - vec![Box::new(KBDataSource::new()), Box::new(MSDataSource)]; + vec![Box::new(KBDataSource::new(&client)), Box::new(MSDataSource)]; data_sources.par_iter().for_each(|data_source| { let init = Instant::now(); - execute_data_source(data_source.deref(), &client, &champion, &lol_champs_dir); + execute_data_source(&**data_source, &client, &champion, &lol_champs_dir); info!( "{}: done in {}s", data_source.get_alias(), @@ -135,30 +129,25 @@ fn execute_data_source( champs.len() ); - if data_source.get_timeout() == 0 { - champs.par_iter().for_each(|(id, positions)| { - get_and_write_item_set( - data_source, - client, - champion, - lol_champs_dir, - *id, - positions, - ); - }); - } else { - champs.iter().for_each(|(id, positions)| { - get_and_write_item_set( - data_source, - client, - champion, - lol_champs_dir, - *id, - positions, - ); + let process = |(id, positions): (&u32, &Vec)| { + get_and_write_item_set( + data_source, + client, + champion, + lol_champs_dir, + *id, + positions, + ); + if data_source.get_timeout() > 0 { thread::sleep(Duration::from_millis(data_source.get_timeout())); - }); + } }; + + if data_source.get_timeout() == 0 { + champs.par_iter().for_each(process); + } else { + champs.iter().for_each(process); + } } fn get_and_write_item_set( @@ -197,6 +186,28 @@ fn get_and_write_item_set( } } +fn create_http_client() -> Agent { + Agent::config_builder() + .user_agent(get_browser_user_agent()) + .timeout_global(Some(Duration::from_secs(10))) + .build() + .into() +} + +fn get_browser_user_agent() -> String { + let base_version = 125; + let start_date = NaiveDate::from_ymd_opt(2024, 4, 16).unwrap(); + let now = Local::now().naive_local().date(); + + let months_between = + (now.year() - start_date.year()) * 12 + (now.month() as i32 - start_date.month() as i32); + let version = base_version + months_between; + + format!( + "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:{version}.0) Gecko/20100101 Firefox/{version}.0" + ) +} + #[cfg(target_os = "windows")] fn lol_champ_dir() -> Result { let hklm = RegKey::predef(winreg::enums::HKEY_LOCAL_MACHINE);