Created
February 15, 2024 14:58
-
-
Save HurricanKai/b96c5b4fd822e34d59f90ca3660d47ea to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
struct RiotApiClient<T: RegionOrPlatform> { | |
inner: Arc<reqwest::Client>, | |
ratelimits: tokio::sync::Mutex<Vec<Ratelimit>>, | |
region_or_platform: T, | |
} | |
#[derive(Debug)] | |
struct Ratelimit { | |
pub current: u32, | |
pub limit: u32, | |
pub per_seconds: u32, | |
pub first_time: DateTime<Utc>, | |
} | |
fn region_to_str(region: Region) -> &'static str { | |
match region { | |
Region::BR1 => "BR1", | |
Region::EUN1 => "EUN1", | |
Region::EUW1 => "EUW1", | |
Region::JP1 => "JP1", | |
Region::KR => "KR", | |
Region::LA1 => "LA1", | |
Region::LA2 => "LA2", | |
Region::NA1 => "NA1", | |
Region::OC1 => "OC1", | |
Region::TR1 => "TR1", | |
Region::RU => "RU", | |
Region::PH2 => "PH2", | |
Region::SG2 => "SG2", | |
Region::TH2 => "TH2", | |
Region::TW2 => "TW2", | |
Region::VN2 => "VN2", | |
} | |
} | |
fn platform_to_str(platform: Platform) -> &'static str { | |
match platform { | |
Platform::Americas => "Americas", | |
Platform::Asia => "Asia", | |
Platform::Europe => "Europe", | |
Platform::Sea => "Sea", | |
} | |
} | |
impl<T: RegionOrPlatform> RiotApiClient<T> { | |
async fn make_request<E: RiotEndpoint<T>, P: serde::Serialize + ?Sized>( | |
self: &Self, | |
endpoint: &E, | |
method: reqwest::Method, | |
path: &str, | |
params: Option<&P>, | |
) -> anyhow::Result<Option<reqwest::Response>> { | |
'outer: loop { | |
let app_ratelimits = &self.ratelimits; | |
let endpoint_ratelimits = endpoint.ratelimits(); | |
loop { | |
let mut endpoint_ratelimits = endpoint_ratelimits.lock().await; | |
if let Some(time) = check_ratelimits(&mut endpoint_ratelimits, Utc::now()) { | |
let r = time.signed_duration_since(Utc::now()); | |
if r > Duration::zero() { | |
println!("Waiting on Endpoint Ratelimit for {r} {endpoint_ratelimits:#?}"); | |
tokio::time::sleep(r.abs().to_std()?).await; | |
} | |
continue; | |
} | |
let mut app_ratelimits = app_ratelimits.lock().await; | |
loop { | |
if let Some(time) = check_ratelimits(&mut app_ratelimits, Utc::now()) { | |
let r = time.signed_duration_since(Utc::now()); | |
if r > Duration::zero() { | |
println!("Waiting on App Ratelimit for {r} {app_ratelimits:#?}"); | |
tokio::time::sleep(r.abs().to_std()?).await; | |
} | |
continue; | |
} | |
// ready to make the request now! | |
let mut url = String::new(); | |
url.push_str(self.region_or_platform.to_url()); | |
url.push_str(path); | |
println!("Making request to {url}"); | |
let mut builder = self.inner.request(method.clone(), url); | |
if let Some(params) = params { | |
builder = builder.query(params); | |
} | |
builder = builder.header("X-Riot-Token", API_KEY); | |
let request = builder.build()?; | |
let response = self.inner.execute(request).await?; | |
let response_date = response | |
.headers() | |
.get("Date") | |
.and_then(|d| d.to_str().ok()) | |
.and_then(|d| DateTime::parse_from_rfc2822(d).ok()) | |
.map(|d| d.with_timezone(&Utc)) | |
.unwrap(); | |
if response.status() == StatusCode::TOO_MANY_REQUESTS { | |
// Handle overflow | |
let seconds = response | |
.headers() | |
.get("Retry-After") | |
.and_then(|h| h.to_str().ok()) | |
.and_then(|h| h.parse().ok()); | |
if let Some(seconds) = seconds { | |
println!("Waiting on Overflow for {seconds}"); | |
tokio::time::sleep( | |
(Duration::seconds(seconds) | |
- Utc::now().signed_duration_since(response_date)) | |
.to_std()?, | |
) | |
.await; | |
// if an overflow happens & we got a retry-after, it's due to a desync. Drop all current ratelimits. | |
endpoint_ratelimits.clear(); | |
app_ratelimits.clear(); | |
continue 'outer; | |
} | |
} | |
// parse headers & update ratelimits. | |
match ( | |
response | |
.headers() | |
.get("X-App-Rate-Limit") | |
.and_then(|h| h.to_str().ok()), | |
response | |
.headers() | |
.get("X-App-Rate-Limit-Count") | |
.and_then(|h| h.to_str().ok()), | |
) { | |
(Some(l), Some(r)) => { | |
update_ratelimits(&mut app_ratelimits, l, r, response_date)? | |
} | |
_ => {} | |
} | |
match ( | |
response | |
.headers() | |
.get("X-Method-Rate-Limit") | |
.and_then(|h| h.to_str().ok()), | |
response | |
.headers() | |
.get("X-Method-Rate-Limit-Count") | |
.and_then(|h| h.to_str().ok()), | |
) { | |
(Some(l), Some(r)) => { | |
update_ratelimits(&mut endpoint_ratelimits, l, r, response_date)? | |
} | |
_ => {} | |
} | |
if response.status() == StatusCode::NOT_FOUND { | |
return Ok(None); | |
} | |
// TODO: Improve | |
if response.status() != StatusCode::OK { | |
println!("Request Error: {response:#?}"); | |
return Err(anyhow!("Request Error!!")); | |
} | |
return Ok(Some(response)); | |
} | |
#[allow(unreachable_code)] | |
{ | |
unreachable!("Above loop should never break or complete!"); | |
} | |
} | |
} | |
} | |
} | |
fn check_ratelimits( | |
limits: &mut Vec<Ratelimit>, | |
relative_to: DateTime<Utc>, | |
) -> Option<DateTime<Utc>> { | |
for ratelimit in limits.iter_mut() { | |
let diff = (relative_to.signed_duration_since(ratelimit.first_time)).abs(); | |
let diffs = diff.num_seconds(); | |
// only consider ratelimits that are still relevant. | |
if diffs <= ratelimit.per_seconds.into() { | |
if ratelimit.current >= ratelimit.limit { | |
let d = ratelimit | |
.first_time | |
.checked_add_signed(Duration::seconds(ratelimit.per_seconds.into())) | |
.unwrap(); | |
return Some(d); | |
} | |
} | |
} | |
return None; | |
} | |
fn update_ratelimits( | |
limits: &mut Vec<Ratelimit>, | |
real_limit: &str, | |
real_count: &str, | |
relative_to: DateTime<Utc>, | |
) -> anyhow::Result<()> { | |
let mut map_counts = HashMap::new(); | |
let mut map_limits = HashMap::new(); | |
fn parse_list(map: &mut HashMap<u32, u32>, str: &str) { | |
for l in str.split(',') { | |
match { | |
let mut s = l.split(':'); | |
( | |
s.next().and_then(|s| s.parse().ok()), | |
s.next().and_then(|s| s.parse().ok()), | |
) | |
} { | |
(Some(limit), Some(time)) => { | |
map.insert(time, limit); | |
} | |
_ => {} | |
} | |
} | |
} | |
parse_list(&mut map_counts, real_count); | |
parse_list(&mut map_limits, real_limit); | |
let mut new_limits = Vec::new(); | |
for (time, count) in map_counts { | |
let limit = map_limits.get(&time).map_or_else( | |
|| Err(anyhow!("? Riot didn't return complete lists")), | |
|o| Ok(*o), | |
)?; | |
let mut first_time = limits | |
.iter() | |
.find(|l| l.per_seconds == time) | |
.map_or(relative_to, |r| r.first_time); | |
// TODO: Also check first time to be in range?? | |
if count == 0 || first_time + Duration::seconds(time.into()) < relative_to { | |
first_time = relative_to; | |
} | |
new_limits.push(Ratelimit { | |
current: count, | |
limit: limit, | |
per_seconds: time, | |
first_time: first_time, | |
}); | |
} | |
*limits = new_limits; | |
Ok(()) | |
} | |
trait RiotEndpoint<T: RegionOrPlatform> { | |
fn ratelimits(&self) -> &tokio::sync::Mutex<Vec<Ratelimit>>; | |
} | |
trait RegionOrPlatform { | |
fn to_url(&self) -> &'static str; | |
} | |
impl RegionOrPlatform for Platform { | |
fn to_url(&self) -> &'static str { | |
match self { | |
Platform::Americas => "https://americas.api.riotgames.com", | |
Platform::Asia => "https://asia.api.riotgames.com", | |
Platform::Europe => "https://europe.api.riotgames.com", | |
Platform::Sea => "https://sea.api.riotgames.com", | |
} | |
} | |
} | |
impl RegionOrPlatform for Region { | |
fn to_url(&self) -> &'static str { | |
match self { | |
Region::BR1 => "https://br1.api.riotgames.com", | |
Region::EUN1 => "https://eun1.api.riotgames.com", | |
Region::EUW1 => "https://euw1.api.riotgames.com", | |
Region::JP1 => "https://jp1.api.riotgames.com", | |
Region::KR => "https://kr.api.riotgames.com", | |
Region::LA1 => "https://la1.api.riotgames.com", | |
Region::LA2 => "https://la2.api.riotgames.com", | |
Region::NA1 => "https://na1.api.riotgames.com", | |
Region::OC1 => "https://oc1.api.riotgames.com", | |
Region::TR1 => "https://tr1.api.riotgames.com", | |
Region::RU => "https://ru.api.riotgames.com", | |
Region::PH2 => "https://ph2.api.riotgames.com", | |
Region::SG2 => "https://sg2.api.riotgames.com", | |
Region::TH2 => "https://th2.api.riotgames.com", | |
Region::TW2 => "https://tw2.api.riotgames.com", | |
Region::VN2 => "https://vn2.api.riotgames.com", | |
} | |
} | |
} | |
const ALL_REGIONS: [Region; 16] = [ | |
Region::BR1, | |
Region::EUN1, | |
Region::EUW1, | |
Region::JP1, | |
Region::KR, | |
Region::LA1, | |
Region::LA2, | |
Region::NA1, | |
Region::OC1, | |
Region::TR1, | |
Region::RU, | |
Region::PH2, | |
Region::SG2, | |
Region::TH2, | |
Region::TW2, | |
Region::VN2, | |
]; | |
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)] | |
enum Region { | |
BR1, | |
EUN1, | |
EUW1, | |
JP1, | |
KR, | |
LA1, | |
LA2, | |
NA1, | |
OC1, | |
TR1, | |
RU, | |
PH2, | |
SG2, | |
TH2, | |
TW2, | |
VN2, | |
} | |
#[derive(Debug)] | |
enum RegionParseError { | |
UnknownRegion, | |
} | |
impl<'a> TryFrom<&'a str> for Region { | |
type Error = RegionParseError; | |
fn try_from(value: &'a str) -> Result<Self, Self::Error> { | |
match value { | |
"BR1" => Ok(Region::BR1), | |
"EUN1" => Ok(Region::EUN1), | |
"EUW1" => Ok(Region::EUW1), | |
"JP1" => Ok(Region::JP1), | |
"KR" => Ok(Region::KR), | |
"LA1" => Ok(Region::LA1), | |
"LA2" => Ok(Region::LA2), | |
"NA1" => Ok(Region::NA1), | |
"OC1" => Ok(Region::OC1), | |
"TR1" => Ok(Region::TR1), | |
"RU" => Ok(Region::RU), | |
"PH2" => Ok(Region::PH2), | |
"SG2" => Ok(Region::SG2), | |
"TH2" => Ok(Region::TH2), | |
"TW2" => Ok(Region::TW2), | |
"VN2" => Ok(Region::VN2), | |
_ => Err(Self::Error::UnknownRegion), | |
} | |
} | |
} | |
const ALL_PLATFORMS: [Platform; 4] = [ | |
Platform::Americas, | |
Platform::Asia, | |
Platform::Europe, | |
Platform::Sea, | |
]; | |
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)] | |
enum Platform { | |
Americas, | |
Asia, | |
Europe, | |
Sea, | |
} | |
impl<'a> TryFrom<&'a str> for Platform { | |
type Error = RegionParseError; | |
fn try_from(value: &'a str) -> Result<Self, Self::Error> { | |
match value { | |
"Americas" => Ok(Platform::Americas), | |
"Asia" => Ok(Platform::Asia), | |
"Europe" => Ok(Platform::Europe), | |
"Sea" => Ok(Platform::Sea), | |
_ => Err(Self::Error::UnknownRegion), | |
} | |
} | |
} | |
fn region_to_platform(region: Region) -> Platform { | |
match region { | |
Region::NA1 => Platform::Americas, | |
Region::BR1 => Platform::Americas, | |
Region::LA1 => Platform::Americas, | |
Region::LA2 => Platform::Americas, | |
Region::KR => Platform::Asia, | |
Region::JP1 => Platform::Asia, | |
Region::EUN1 => Platform::Europe, | |
Region::EUW1 => Platform::Europe, | |
Region::TR1 => Platform::Europe, | |
Region::RU => Platform::Europe, | |
Region::OC1 => Platform::Sea, | |
Region::PH2 => Platform::Sea, | |
Region::SG2 => Platform::Sea, | |
Region::TH2 => Platform::Sea, | |
Region::TW2 => Platform::Sea, | |
Region::VN2 => Platform::Sea, | |
} | |
} | |
fn region_partition_offset(region: Region) -> u32 { | |
match region { | |
Region::NA1 => 0, | |
Region::BR1 => 1, | |
Region::LA1 => 2, | |
Region::LA2 => 3, | |
Region::KR => 4, | |
Region::JP1 => 5, | |
Region::EUN1 => 6, | |
Region::EUW1 => 7, | |
Region::TR1 => 8, | |
Region::RU => 9, | |
Region::OC1 => 10, | |
Region::PH2 => 11, | |
Region::SG2 => 12, | |
Region::TH2 => 13, | |
Region::TW2 => 14, | |
Region::VN2 => 15, | |
} | |
} | |
// Platform ONLY | |
struct Matchv5 { | |
ratelimits: tokio::sync::Mutex<Vec<Ratelimit>>, | |
base: Arc<RiotApiClient<Platform>>, | |
} | |
impl Matchv5 { | |
fn new(base: Arc<RiotApiClient<Platform>>) -> Self { | |
return Self { | |
ratelimits: Mutex::new(Vec::new()), | |
base, | |
}; | |
} | |
async fn get_match_list_by_puuid( | |
&self, | |
puuid: &Puuid, | |
start_time: Option<chrono::DateTime<Utc>>, | |
end_time: Option<chrono::DateTime<Utc>>, | |
queue: Option<u32>, | |
match_type: Option<String>, | |
start: Option<u32>, | |
count: Option<u32>, | |
) -> anyhow::Result<Vec<MatchId>> { | |
let mut params = Vec::new(); | |
if let Some(start_time) = start_time { | |
params.push(("startTime", start_time.timestamp().to_string())) | |
} | |
if let Some(end_time) = end_time { | |
params.push(("endTime", end_time.timestamp().to_string())) | |
} | |
if let Some(queue) = queue { | |
params.push(("queue", queue.to_string())) | |
} | |
if let Some(match_type) = match_type { | |
params.push(("type", match_type.to_string())) | |
} | |
if let Some(start) = start { | |
params.push(("start", start.to_string())) | |
} | |
params.push(("count", count.unwrap_or(100).to_string())); | |
let response = self | |
.base | |
.make_request::<_, Vec<(&str, String)>>( | |
self, | |
Method::GET, | |
&("/lol/match/v5/matches/by-puuid/".to_owned() + puuid.to_str() + "/ids"), | |
Some(¶ms), | |
) | |
.await?; | |
return Ok(response | |
.expect("puuids should not go missing") | |
.json::<Vec<String>>() | |
.await? | |
.into_iter() | |
.map(|e| e.into()) | |
.collect()); | |
} | |
async fn get_match_by_id( | |
&self, | |
match_id: &MatchId, | |
) -> anyhow::Result<Option<serde_json::Value>> { | |
let response = self | |
.base | |
.make_request::<_, [String; 0]>( | |
self, | |
Method::GET, | |
&("/lol/match/v5/matches/".to_owned() + match_id.to_str()), | |
None, | |
) | |
.await?; | |
if let Some(response) = response { | |
return Ok(response.json().await?); | |
} | |
return Ok(None); | |
} | |
} | |
impl<'a> RiotEndpoint<Platform> for Matchv5 { | |
fn ratelimits(&self) -> &tokio::sync::Mutex<Vec<Ratelimit>> { | |
return &self.ratelimits; | |
} | |
} | |
#[derive(Debug)] | |
struct Puuid { | |
inner: String, | |
} | |
impl Puuid { | |
fn to_str(&self) -> &str { | |
return &self.inner; | |
} | |
} | |
impl From<String> for Puuid { | |
fn from(value: String) -> Self { | |
Self { inner: value } | |
} | |
} | |
#[derive(Debug)] | |
struct AccountId { | |
inner: String, | |
} | |
impl AccountId { | |
fn to_str(&self) -> &str { | |
return &self.inner; | |
} | |
} | |
impl From<String> for AccountId { | |
fn from(value: String) -> Self { | |
Self { inner: value } | |
} | |
} | |
#[derive(Debug)] | |
struct SummonerId { | |
inner: String, | |
} | |
impl SummonerId { | |
fn to_str(&self) -> &str { | |
return &self.inner; | |
} | |
} | |
impl From<String> for SummonerId { | |
fn from(value: String) -> Self { | |
Self { inner: value } | |
} | |
} | |
#[derive(Debug)] | |
struct MatchId { | |
inner: String, | |
} | |
impl MatchId { | |
fn to_str(&self) -> &str { | |
return &self.inner; | |
} | |
fn into_parts(&self) -> (Region, u64) { | |
let mut it = self.to_str().split('_'); | |
( | |
it.next() | |
.expect("matchid format should be <region>_<id>") | |
.try_into() | |
.expect("first part of matchid should always be real region"), | |
it.next() | |
.expect("matchid format should be <region>_<id>") | |
.parse() | |
.expect("second part of matchid should always be a number"), | |
) | |
} | |
fn from_parts(region: Region, id: u64) -> Self { | |
return Self { | |
inner: region_to_str(region).to_owned() + "_" + &id.to_string(), | |
}; | |
} | |
} | |
impl From<String> for MatchId { | |
fn from(value: String) -> Self { | |
Self { inner: value } | |
} | |
} | |
#[derive(Debug)] | |
struct RiotId { | |
pub game_name: String, | |
pub tagline: String, | |
} | |
struct LeagueV4 { | |
ratelimits: tokio::sync::Mutex<Vec<Ratelimit>>, | |
base: Arc<RiotApiClient<Region>>, | |
} | |
impl LeagueV4 { | |
fn new(base: Arc<RiotApiClient<Region>>) -> Self { | |
Self { | |
ratelimits: Mutex::new(Vec::new()), | |
base, | |
} | |
} | |
async fn league_entries( | |
&self, | |
queue: &str, | |
tier: &str, | |
division: &str, | |
page: u32, | |
) -> anyhow::Result<Vec<SummonerId>> { | |
// TODO: Return object instead of summonerId array | |
let response = self | |
.base | |
.make_request( | |
self, | |
Method::GET, | |
&("/lol/league/v4/entries/".to_owned() + queue + "/" + tier + "/" + division), | |
Some(&[("page", page.to_string())]), | |
) | |
.await?; | |
let body = response | |
.expect("league should not go missing") | |
.json::<serde_json::Value>() | |
.await?; | |
// TODO: Fix parsing to error instead of panic | |
Ok(body | |
.as_array() | |
.map(|a| { | |
a.into_iter() | |
.map(|v| { | |
v.as_object() | |
.unwrap() | |
.get("summonerId") | |
.unwrap() | |
.as_str() | |
.unwrap() | |
.to_owned() | |
.into() | |
}) | |
.collect() | |
}) | |
.unwrap()) | |
} | |
} | |
impl RiotEndpoint<Region> for LeagueV4 { | |
fn ratelimits(&self) -> &tokio::sync::Mutex<Vec<Ratelimit>> { | |
return &self.ratelimits; | |
} | |
} | |
#[derive(Debug)] | |
struct AccountInfo { | |
puuid: Puuid, | |
riotid: RiotId, | |
} | |
// PLATFORM ONLY | |
struct AccountV1 { | |
ratelimits: tokio::sync::Mutex<Vec<Ratelimit>>, | |
base: Arc<RiotApiClient<Platform>>, | |
} | |
impl AccountV1 { | |
fn new(base: Arc<RiotApiClient<Platform>>) -> Self { | |
return Self { | |
ratelimits: Mutex::new(Vec::new()), | |
base, | |
}; | |
} | |
async fn puuid_from_riotid(&self, riotid: &RiotId) -> anyhow::Result<AccountInfo> { | |
let response = self | |
.base | |
.make_request::<_, [String; 0]>( | |
self, | |
Method::GET, | |
&("/riot/account/v1/accounts/by-riot-id/".to_owned() | |
+ &riotid.game_name | |
+ "/" | |
+ &riotid.tagline), | |
None, | |
) | |
.await?; | |
let body = response | |
.expect("accounts should not go missing") | |
.json::<serde_json::Value>() | |
.await?; | |
body.as_object() | |
.and_then(|o| o.get("puuid").and_then(|x| x.as_str()).map(|p| (o, p))) | |
.and_then(|(o, p)| { | |
o.get("gameName") | |
.and_then(|x| x.as_str()) | |
.map(|g| (o, p, g)) | |
}) | |
.and_then(|(o, p, g)| o.get("tagLine").and_then(|x| x.as_str()).map(|t| (p, g, t))) | |
.map(|(p, g, t)| AccountInfo { | |
puuid: p.to_owned().into(), | |
riotid: RiotId { | |
game_name: g.to_owned(), | |
tagline: t.to_owned(), | |
}, | |
}) | |
.map_or_else(|| Err(anyhow!("Could not parse account info!")), |e| Ok(e)) | |
} | |
} | |
impl RiotEndpoint<Platform> for AccountV1 { | |
fn ratelimits(&self) -> &tokio::sync::Mutex<Vec<Ratelimit>> { | |
return &self.ratelimits; | |
} | |
} | |
struct SummonerV4 { | |
ratelimits: tokio::sync::Mutex<Vec<Ratelimit>>, | |
base: Arc<RiotApiClient<Region>>, | |
} | |
impl SummonerV4 { | |
fn new(base: Arc<RiotApiClient<Region>>) -> Self { | |
return Self { | |
ratelimits: Mutex::new(Vec::new()), | |
base, | |
}; | |
} | |
fn parse_body(body: serde_json::Value) -> anyhow::Result<SummonerInfo> { | |
body.as_object() | |
.map(|o| { | |
match ( | |
o.get("accountId") | |
.and_then(|e| e.as_str()) | |
.map(|e| e.to_owned().into()), | |
o.get("profileIconId") | |
.and_then(|e| e.as_i64()) | |
.and_then(|e| e.try_into().ok()), | |
o.get("revisionDate") | |
.and_then(|e| e.as_i64()) | |
.map(|e| chrono::DateTime::UNIX_EPOCH + chrono::Duration::milliseconds(e)), | |
o.get("name").and_then(|e| e.as_str()).map(|e| e.to_owned()), | |
o.get("id") | |
.and_then(|e| e.as_str()) | |
.map(|e| e.to_owned().into()), | |
o.get("puuid") | |
.and_then(|e| e.as_str()) | |
.map(|e| e.to_owned().into()), | |
o.get("summonerLevel") | |
.and_then(|e| e.as_i64()) | |
.and_then(|e| e.try_into().ok()), | |
) { | |
( | |
Some(account_id), | |
Some(profile_icon_id), | |
Some(revision_date), | |
Some(name), | |
Some(id), | |
Some(puuid), | |
Some(summoner_level), | |
) => Ok(SummonerInfo { | |
account_id, | |
profile_icon_id, | |
revision_date, | |
name, | |
id, | |
puuid, | |
summoner_level, | |
}), | |
_ => Err(anyhow!("Failed to Parse SummonerInfo!")), | |
} | |
}) | |
.unwrap() | |
} | |
async fn summoner_from_id(&self, summoner_id: &SummonerId) -> anyhow::Result<SummonerInfo> { | |
let response = self | |
.base | |
.make_request::<_, [String; 0]>( | |
self, | |
Method::GET, | |
&("/lol/summoner/v4/summoners/".to_owned() + &summoner_id.to_str()), | |
None, | |
) | |
.await?; | |
let body = response | |
.expect("summonerids should not go missing") | |
.json::<serde_json::Value>() | |
.await?; | |
Self::parse_body(body) | |
} | |
async fn summoner_from_puuid(&self, puuid: &Puuid) -> anyhow::Result<SummonerInfo> { | |
let response = self | |
.base | |
.make_request::<_, [String; 0]>( | |
self, | |
Method::GET, | |
&("/lol/summoner/v4/summoners/by-puuid/".to_owned() + &puuid.to_str()), | |
None, | |
) | |
.await?; | |
let body = response | |
.expect("puuids should not go missing") | |
.json::<serde_json::Value>() | |
.await?; | |
Self::parse_body(body) | |
} | |
} | |
impl<'a> RiotEndpoint<Region> for SummonerV4 { | |
fn ratelimits(&self) -> &tokio::sync::Mutex<Vec<Ratelimit>> { | |
return &self.ratelimits; | |
} | |
} | |
#[derive(Debug)] | |
struct SummonerInfo { | |
account_id: AccountId, | |
profile_icon_id: u32, | |
revision_date: chrono::DateTime<Utc>, | |
name: String, | |
id: SummonerId, | |
puuid: Puuid, | |
summoner_level: u64, | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment