use std::collections::HashMap;
use std::sync::Arc;
use chrono::{DateTime, Duration, Utc};
use serde::{Deserialize, Serialize};
use tokio::task;
use crate::config::Config;
use crate::traits::EventHandler;
static mut C_STREAMING: Vec<String> = Vec::new();
#[derive(Serialize, Deserialize, PartialEq, Clone)]
pub struct Streamer {
pub name: String
}
#[derive(Serialize, Deserialize, Debug)]
pub(crate) struct StreamsRes {
pub data: Vec<StreamData>,
pub pagination: Pagination
}
#[derive(Serialize, Deserialize, Debug)]
pub struct StreamData {
pub id: String,
pub user_id: String,
pub user_login: String,
pub user_name: String,
pub game_id: String,
pub game_name: String,
#[serde(rename = "type")]
pub stream_type: String,
pub title: String,
pub viewer_count: u32,
pub started_at: DateTime<Utc>,
pub language: String,
pub thumbnail_url: String,
pub tags_ids: Option<Vec<String>>,
pub tags: Option<Vec<String>>,
pub is_mature: bool
}
#[derive(Serialize, Deserialize, Debug)]
pub(crate) struct Pagination {
pub cursor: String
}
#[derive(Clone)]
pub struct Client {
pub client_id: String,
pub token: String,
event_handler: Option<Arc<dyn EventHandler>>,
config: Config,
currently_streaming: Vec<String>,
delay: tokio::time::Duration
}
impl Client {
pub async fn new() -> Client {
let c = crate::config::read_config().await;
if c.user_id.clone().is_none() {
panic!("Missing User ID in Config File!")
}
if c.token.clone().is_none() {
panic!("Missing User Token in Config File!")
}
let mut d = c.delay.clone().unwrap() as u64;
if d < 80u64 {
d = 80u64;
}
Client {
client_id: c.user_id.clone().unwrap(),
token: c.token.clone().unwrap(),
event_handler: None,
config: c.clone(),
currently_streaming: Vec::new(),
delay: tokio::time::Duration::from_millis(d)
}
}
pub fn event_handler<H: EventHandler + 'static>(mut self, event_handler: H) -> Self {
self.event_handler = Some(Arc::new(event_handler));
self
}
pub async fn run(self) -> Result<(), crate::error::Error> {
if self.event_handler.is_none() {
panic!("No Event Handler Set");
}
let mut recent: HashMap<String, DateTime<Utc>> = HashMap::new();
let mut running = true;
while running {
let mut local_client: Client = self.clone();
tokio::time::sleep(self.delay.clone()).await;
let streamers: Vec<String> = local_client.config.streamers.clone();
if streamers.is_empty() {
running = false;
}
for streamer in streamers {
if let Some(time) = recent.get(streamer.as_str()) {
let difference: Duration = Utc::now() - *time;
if 30 > difference.num_seconds() {
continue;
}
else {
recent.remove(streamer.as_str());
}
}
recent.insert(streamer.clone(), Utc::now());
let handler = local_client.event_handler.clone().unwrap();
let t_string = local_client.token.clone();
let u_string = local_client.client_id.clone();
tokio::spawn(async move {
let client = reqwest::Client::new();
let res = client.get(format!("https://api.twitch.tv/helix/streams?user_login={0}", streamer.clone()))
.bearer_auth(t_string.clone()).header("Client-Id", u_string.clone()).send().await.expect("Error Occurred");
let rjson = res.json::<StreamsRes>().await;
match rjson {
Ok(json) => unsafe {
if json.data.is_empty() {
return;
}
let info = json.data.first().expect("Missing Info");
if C_STREAMING.contains(&info.user_id) {
return;
}
C_STREAMING.push(info.user_id.clone());
handler.on_stream(&streamer, info).await;
},
Err(e) => unsafe {
if e.is_timeout() {
handler.on_error(crate::error::Error::new("An error occurred due to timing out...", 1u16)).await;
} else if e.is_connect() {
handler.on_error(crate::error::Error::new("An error occurred when trying to connect...", 2u16)).await;
} else if e.is_status() {
handler.on_error(crate::error::Error::new("Status returned as an Error...", 3u16)).await;
} else if e.is_redirect() {
handler.on_error(crate::error::Error::new("An error occurred due to an attempted redirect...", 4u16)).await;
} else if e.is_request() {
handler.on_error(crate::error::Error::new("An error occurred due to the request...", 5u16)).await;
} else if e.is_body() {
handler.on_error(crate::error::Error::new("An error occurred with the request or response body...", 6u16)).await;
} else if e.is_builder() {
handler.on_error(crate::error::Error::new("An error occurred with the type builder...", 7u16)).await;
} else {
if C_STREAMING.contains(&streamer) {
C_STREAMING.retain(|x | x.to_string() != streamer)
}
}
}
}
task::yield_now().await;
}).await.expect("TODO: panic message");
tokio::time::sleep(tokio::time::Duration::from_millis(80)).await;
}
};
Ok(())
}
}