2023-08-27 04:32:55 -04:00
|
|
|
use cynic::{GraphQlResponse, QueryBuilder};
|
|
|
|
use serde::{Deserialize, Serialize};
|
2023-09-30 04:37:10 -04:00
|
|
|
use std::fmt::Debug;
|
2023-09-02 01:47:52 -04:00
|
|
|
use std::path::Path;
|
2023-10-01 14:55:15 -04:00
|
|
|
use std::thread::sleep;
|
|
|
|
use std::time::Duration;
|
2023-08-26 23:48:13 -04:00
|
|
|
|
2023-08-27 03:02:18 -04:00
|
|
|
pub mod search_games;
|
2023-08-28 00:57:20 -04:00
|
|
|
pub use search_games::*;
|
2023-09-30 18:16:00 -04:00
|
|
|
pub mod tournament_events;
|
|
|
|
pub use tournament_events::*;
|
|
|
|
pub mod event_sets;
|
|
|
|
pub use event_sets::*;
|
2023-09-02 20:46:23 -04:00
|
|
|
pub mod player_info;
|
|
|
|
pub use player_info::*;
|
2023-08-27 03:11:23 -04:00
|
|
|
|
2023-10-03 01:26:25 -04:00
|
|
|
use crate::error;
|
2023-08-27 03:11:23 -04:00
|
|
|
use schema::schema;
|
|
|
|
|
2023-09-02 01:47:52 -04:00
|
|
|
// Auth key
|
|
|
|
|
2023-09-30 01:43:33 -04:00
|
|
|
pub fn get_auth_token(config_dir: &Path) -> Option<String> {
|
2023-09-02 01:47:52 -04:00
|
|
|
use std::env::{var, VarError};
|
|
|
|
use std::fs::read_to_string;
|
|
|
|
|
2023-09-23 03:37:22 -04:00
|
|
|
match var("AUTH_TOKEN") {
|
2023-09-02 01:47:52 -04:00
|
|
|
Ok(key) => Some(key),
|
2023-10-03 01:26:25 -04:00
|
|
|
Err(VarError::NotUnicode(_)) => error("Invalid authorization key", 2),
|
2023-09-02 01:47:52 -04:00
|
|
|
Err(VarError::NotPresent) => {
|
|
|
|
let mut auth_file = config_dir.to_owned();
|
2023-10-03 23:37:51 -04:00
|
|
|
auth_file.push("startrnr");
|
2023-09-02 01:47:52 -04:00
|
|
|
auth_file.push("auth.txt");
|
|
|
|
read_to_string(auth_file).ok().and_then(|s| {
|
|
|
|
let trimmed = s.trim();
|
|
|
|
if trimmed.is_empty() {
|
|
|
|
None
|
|
|
|
} else {
|
|
|
|
Some(trimmed.to_owned())
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-09-02 19:48:47 -04:00
|
|
|
// Types
|
|
|
|
|
|
|
|
// HACK: Unfortunately, start.gg seems to use integers for its ID type, whereas
|
|
|
|
// cynic always assumes that IDs are strings. To get around that, we define new
|
|
|
|
// scalar types that deserialize to u64.
|
|
|
|
|
2023-10-05 22:10:24 -04:00
|
|
|
#[derive(cynic::Scalar, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
2023-09-02 19:48:47 -04:00
|
|
|
#[cynic(graphql_type = "ID")]
|
|
|
|
pub struct VideogameId(pub u64);
|
|
|
|
|
2023-10-05 22:10:24 -04:00
|
|
|
#[derive(cynic::Scalar, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
2023-09-30 18:16:00 -04:00
|
|
|
#[cynic(graphql_type = "ID")]
|
|
|
|
pub struct EventId(pub u64);
|
|
|
|
|
2023-10-05 22:10:24 -04:00
|
|
|
#[derive(cynic::Scalar, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
2023-09-02 19:48:47 -04:00
|
|
|
#[cynic(graphql_type = "ID")]
|
|
|
|
pub struct EntrantId(pub u64);
|
|
|
|
|
2023-10-05 22:10:24 -04:00
|
|
|
#[derive(cynic::Scalar, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
2023-09-02 19:48:47 -04:00
|
|
|
#[cynic(graphql_type = "ID")]
|
|
|
|
pub struct PlayerId(pub u64);
|
|
|
|
|
2023-10-05 22:10:24 -04:00
|
|
|
#[derive(cynic::Scalar, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
2023-09-02 19:48:47 -04:00
|
|
|
pub struct Timestamp(pub u64);
|
|
|
|
|
2023-08-27 05:33:03 -04:00
|
|
|
// Query machinery
|
2023-08-27 03:52:38 -04:00
|
|
|
|
2023-08-27 16:44:18 -04:00
|
|
|
pub trait QueryUnwrap<Vars>: 'static + QueryBuilder<Vars> {
|
2023-08-27 04:32:55 -04:00
|
|
|
type Unwrapped;
|
2023-08-27 03:52:38 -04:00
|
|
|
|
2023-08-27 04:32:55 -04:00
|
|
|
fn unwrap_response(response: GraphQlResponse<Self>) -> Option<Self::Unwrapped>;
|
|
|
|
}
|
2023-08-27 03:52:38 -04:00
|
|
|
|
2023-08-27 04:32:55 -04:00
|
|
|
// Generic function for running start.gg queries
|
2023-09-30 01:43:33 -04:00
|
|
|
pub fn run_query<Builder, Vars>(vars: Vars, auth_token: &str) -> Option<Builder::Unwrapped>
|
2023-08-27 04:32:55 -04:00
|
|
|
where
|
2023-10-03 01:26:25 -04:00
|
|
|
Vars: Copy + Serialize,
|
2023-08-27 04:32:55 -04:00
|
|
|
Builder: QueryUnwrap<Vars>,
|
|
|
|
for<'de> Builder: Deserialize<'de>,
|
2023-08-27 03:52:38 -04:00
|
|
|
{
|
2023-09-02 01:35:36 -04:00
|
|
|
use cynic::http::ReqwestBlockingExt;
|
2023-08-27 03:52:38 -04:00
|
|
|
|
2023-10-01 14:55:15 -04:00
|
|
|
let mut response = reqwest::blocking::Client::new()
|
2023-09-02 01:35:36 -04:00
|
|
|
.post("https://api.start.gg/gql/alpha")
|
2023-09-30 01:43:33 -04:00
|
|
|
.header("Authorization", String::from("Bearer ") + auth_token)
|
2023-10-02 20:34:54 -04:00
|
|
|
.run_graphql(Builder::build(vars));
|
2023-10-01 14:55:15 -04:00
|
|
|
|
2023-10-04 17:58:54 -04:00
|
|
|
// If query fails to reach server, retry up to 10 times
|
2023-10-01 14:55:15 -04:00
|
|
|
for _ in 1..10 {
|
2023-10-02 20:39:32 -04:00
|
|
|
if response.is_ok() {
|
|
|
|
break;
|
|
|
|
}
|
2023-10-01 14:55:15 -04:00
|
|
|
sleep(Duration::from_secs(2));
|
|
|
|
response = reqwest::blocking::Client::new()
|
|
|
|
.post("https://api.start.gg/gql/alpha")
|
|
|
|
.header("Authorization", String::from("Bearer ") + auth_token)
|
2023-10-02 20:34:54 -04:00
|
|
|
.run_graphql(Builder::build(vars));
|
2023-10-01 14:55:15 -04:00
|
|
|
}
|
2023-08-27 03:52:38 -04:00
|
|
|
|
2023-10-01 14:55:15 -04:00
|
|
|
Builder::unwrap_response(response.ok()?)
|
2023-08-27 03:52:38 -04:00
|
|
|
}
|