refactor: centralize HTTP client creation with dynamic user agent
All checks were successful
ci/woodpecker/push/test Pipeline was successful
ci/woodpecker/push/linux Pipeline was successful
ci/woodpecker/push/mingw Pipeline was successful

This commit is contained in:
nyyu 2025-06-01 13:08:45 +02:00
parent 1e1f89ddc0
commit 87afdc4e24
4 changed files with 57 additions and 59 deletions

1
Cargo.lock generated
View file

@ -75,6 +75,7 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
name = "cggitem_sets" name = "cggitem_sets"
version = "1.0.0" version = "1.0.0"
dependencies = [ dependencies = [
"chrono",
"indexmap", "indexmap",
"log", "log",
"logsy", "logsy",

View file

@ -6,6 +6,7 @@ version = "1.0.0"
include = ["src/**/*"] include = ["src/**/*"]
[dependencies] [dependencies]
chrono = {version = "0.4.41", features = ["std"], default-features = false}
indexmap = {version = "2.2", features = ["serde", "rayon"]} indexmap = {version = "2.2", features = ["serde", "rayon"]}
log = "0.4" log = "0.4"
logsy = "1.0.1" logsy = "1.0.1"

View file

@ -1,13 +1,10 @@
use crate::ChampInfo; use crate::ChampInfo;
use crate::Champion as ChampionLoL; use crate::Champion as ChampionLoL;
use crate::USER_AGENT_VALUE;
use crate::data_source::{Build, DataSource, Item, Stat}; use crate::data_source::{Build, DataSource, Item, Stat};
use indexmap::IndexMap; use indexmap::IndexMap;
use log::error; use log::error;
use serde_derive::Deserialize; use serde_derive::Deserialize;
use serde_json::{Value, json}; use serde_json::{Value, json};
use std::time::Duration;
use ureq::Agent;
pub struct KBDataSource { pub struct KBDataSource {
token: Option<String>, token: Option<String>,
@ -118,12 +115,7 @@ struct Summoner {
} }
// It will be better to use Result... // It will be better to use Result...
fn get_auth_token() -> Option<String> { fn get_auth_token(client: &ureq::Agent) -> Option<String> {
let client: Agent = Agent::config_builder()
.user_agent(USER_AGENT_VALUE)
.timeout_global(Some(Duration::from_secs(10)))
.build()
.into();
let mut bundle = match client.get("https://koreanbuilds.net/bundle.js").call() { let mut bundle = match client.get("https://koreanbuilds.net/bundle.js").call() {
Ok(mut resp) => match resp.body_mut().read_to_string() { Ok(mut resp) => match resp.body_mut().read_to_string() {
Ok(val) => val, Ok(val) => val,
@ -141,9 +133,9 @@ fn get_auth_token() -> Option<String> {
} }
impl KBDataSource { impl KBDataSource {
pub fn new() -> KBDataSource { pub fn new(client: &ureq::Agent) -> KBDataSource {
Self { Self {
token: get_auth_token(), token: get_auth_token(client),
} }
} }
@ -371,10 +363,15 @@ impl DataSource for KBDataSource {
mod tests { mod tests {
use super::*; use super::*;
use crate::create_http_client;
use std::sync::LazyLock;
static CLIENT: LazyLock<ureq::Agent> = LazyLock::new(|| create_http_client());
static DATASOURCE: LazyLock<KBDataSource> = LazyLock::new(|| KBDataSource::new(&CLIENT));
#[test] #[test]
fn test_get_auth_token() { fn test_get_auth_token() {
match get_auth_token() { match get_auth_token(&CLIENT) {
Some(token) => assert!(token.len() > 0), Some(token) => assert!(token.len() > 0),
None => assert!(false), None => assert!(false),
}; };
@ -382,36 +379,24 @@ mod tests {
#[test] #[test]
fn test_get_champs_with_positions_and_patch() { 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 { let champion = ChampionLoL {
data: IndexMap::new(), 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); assert!(champs_with_positions.len() > 0);
} }
#[test] #[test]
fn test_get_champ_data_with_win_pourcentage() { 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 { let champ = ChampInfo {
id: String::from("Annie"), id: String::from("Annie"),
name: String::from("Annie"), name: String::from("Annie"),
key: String::from("1"), key: String::from("1"),
}; };
let result = datasource.get_champ_data_with_win_pourcentage( let result = DATASOURCE.get_champ_data_with_win_pourcentage(
&champ, &champ,
&vec!["MID".to_string()], &vec!["MID".to_string()],
&client, &CLIENT,
); );
assert!(!result.is_empty()); assert!(!result.is_empty());
assert!(!result[0].1.is_empty()); assert!(!result[0].1.is_empty());

View file

@ -1,3 +1,4 @@
use chrono::{Datelike, Local, NaiveDate};
use indexmap::IndexMap; use indexmap::IndexMap;
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
use log::debug; use log::debug;
@ -10,7 +11,6 @@ use std::io;
use std::io::Error; use std::io::Error;
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
use std::io::ErrorKind; use std::io::ErrorKind;
use std::ops::Deref;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::time::Instant; use std::time::Instant;
use std::{fs, thread, time}; use std::{fs, thread, time};
@ -44,8 +44,6 @@ pub struct ChampInfo {
key: String, 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"; const DEFAULT_LOL_CHAMPS_DIR: &str = "./champs";
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
const REG_KEY_LOL_RADS: &str = r"SOFTWARE\WOW6432Node\Riot Games\RADS"; const REG_KEY_LOL_RADS: &str = r"SOFTWARE\WOW6432Node\Riot Games\RADS";
@ -76,11 +74,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
}; };
info!("LoL Champs Folder: {}", lol_champs_dir.display()); info!("LoL Champs Folder: {}", lol_champs_dir.display());
let client: Agent = Agent::config_builder() let client: Agent = create_http_client();
.user_agent(USER_AGENT_VALUE)
.timeout_global(Some(Duration::from_secs(10)))
.build()
.into();
let realm: Realm = client let realm: Realm = client
.get("https://ddragon.leagueoflegends.com/realms/euw.json") .get("https://ddragon.leagueoflegends.com/realms/euw.json")
@ -99,10 +93,10 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
info!("LoL numbers of champs: {}", champion.data.len()); info!("LoL numbers of champs: {}", champion.data.len());
let data_sources: Vec<Box<dyn DataSource + Sync + Send>> = let data_sources: Vec<Box<dyn DataSource + Sync + Send>> =
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| { data_sources.par_iter().for_each(|data_source| {
let init = Instant::now(); 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!( info!(
"{}: done in {}s", "{}: done in {}s",
data_source.get_alias(), data_source.get_alias(),
@ -135,30 +129,25 @@ fn execute_data_source(
champs.len() champs.len()
); );
if data_source.get_timeout() == 0 { let process = |(id, positions): (&u32, &Vec<String>)| {
champs.par_iter().for_each(|(id, positions)| { get_and_write_item_set(
get_and_write_item_set( data_source,
data_source, client,
client, champion,
champion, lol_champs_dir,
lol_champs_dir, *id,
*id, positions,
positions, );
); if data_source.get_timeout() > 0 {
});
} else {
champs.iter().for_each(|(id, positions)| {
get_and_write_item_set(
data_source,
client,
champion,
lol_champs_dir,
*id,
positions,
);
thread::sleep(Duration::from_millis(data_source.get_timeout())); 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( 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")] #[cfg(target_os = "windows")]
fn lol_champ_dir() -> Result<PathBuf, Error> { fn lol_champ_dir() -> Result<PathBuf, Error> {
let hklm = RegKey::predef(winreg::enums::HKEY_LOCAL_MACHINE); let hklm = RegKey::predef(winreg::enums::HKEY_LOCAL_MACHINE);