diff --git a/Cargo.lock b/Cargo.lock index bf814ce..a662d46 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -213,6 +213,55 @@ dependencies = [ "thiserror", ] +[[package]] +name = "axum" +version = "0.6.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b829e4e32b91e643de6eafe82b1d90675f5874230191a4ffbc1b336dec4d6bf" +dependencies = [ + "async-trait", + "axum-core", + "bitflags", + "bytes", + "futures-util", + "http", + "http-body", + "hyper", + "itoa", + "matchit", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "rustversion", + "serde", + "serde_json", + "serde_path_to_error", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tower", + "tower-layer", + "tower-service", +] + +[[package]] +name = "axum-core" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "759fa577a247914fd3f7f76d62972792636412fbfd634cd452f6a385a74d2d2c" +dependencies = [ + "async-trait", + "bytes", + "futures-util", + "http", + "http-body", + "mime", + "rustversion", + "tower-layer", + "tower-service", +] + [[package]] name = "backtrace" version = "0.3.69" @@ -665,8 +714,10 @@ version = "0.1.0" dependencies = [ "arc-swap", "aws-creds 0.36.0", + "axum", "chrono", "clap", + "once_cell", "prometheus", "reqwest", "rust-s3", @@ -919,6 +970,12 @@ version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" +[[package]] +name = "matchit" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" + [[package]] name = "maybe-async" version = "0.2.7" @@ -1076,6 +1133,26 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" +[[package]] +name = "pin-project" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fda4ed1c6c173e3fc7a83629421152e01d7b1f9b7f65fb301e490e8cfc656422" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.38", +] + [[package]] name = "pin-project-lite" version = "0.2.13" @@ -1431,6 +1508,12 @@ dependencies = [ "untrusted 0.9.0", ] +[[package]] +name = "rustversion" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" + [[package]] name = "ryu" version = "1.0.15" @@ -1494,6 +1577,16 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_path_to_error" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4beec8bce849d58d06238cb50db2e1c417cfeafa4c63f692b15c82b7c80f8335" +dependencies = [ + "itoa", + "serde", +] + [[package]] name = "serde_urlencoded" version = "0.7.1" @@ -1659,6 +1752,12 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "sync_wrapper" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" + [[package]] name = "system-configuration" version = "0.5.1" @@ -1836,6 +1935,28 @@ dependencies = [ "tracing", ] +[[package]] +name = "tower" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +dependencies = [ + "futures-core", + "futures-util", + "pin-project", + "pin-project-lite", + "tokio", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-layer" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" + [[package]] name = "tower-service" version = "0.3.2" diff --git a/Cargo.toml b/Cargo.toml index 17709d4..5c443f6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,3 +20,5 @@ prometheus = { version = "0.13.3" } aws-creds = { version = "0.36.0", default_features = false, features = ["http-credentials", "rustls-tls"]} rust-s3 = { version = "0.33.0", default_features = false, features = [ "tokio", "tokio-rustls-tls", "no-verify-ssl"] } clap = { version = "4.4.7", features = ["derive"] } +axum = "0.6.20" +once_cell = "1.18.0" diff --git a/src/collector.rs b/src/collector.rs index bbc1af1..eb5c4a3 100644 --- a/src/collector.rs +++ b/src/collector.rs @@ -328,12 +328,16 @@ impl Client { } #[tracing::instrument(skip(client, clan_season_stats))] -pub async fn update_names(client: &Client, clan: &ClanTag, clan_season_stats: &mut ClanStorage) { +pub async fn update_names( + client: &Client, + clan: &ClanTag, + clan_season_stats: &mut ClanStorage, +) -> Result<(), ()> { let info = match client.clan_info(clan).await { Ok(i) => i, Err(e) => { tracing::error!("Failed to load Clan Information {:?}", e); - return; + return Err(()); } }; @@ -343,15 +347,17 @@ pub async fn update_names(client: &Client, clan: &ClanTag, clan_season_stats: &m .player_names .insert(member.tag, member.name); } + + Ok(()) } #[tracing::instrument(skip(client, storage))] -pub async fn update_cwl(client: &Client, clan: &ClanTag, storage: &mut Storage) { +pub async fn update_cwl(client: &Client, clan: &ClanTag, storage: &mut Storage) -> Result<(), ()> { let w = match client.clan_war_league_group(clan).await { Ok(w) => w, Err(e) => { tracing::error!("Loading Clan War League Group: {:?}", e); - return; + return Err(()); } }; @@ -410,21 +416,27 @@ pub async fn update_cwl(client: &Client, clan: &ClanTag, storage: &mut Storage) } } } + + Ok(()) } #[tracing::instrument(skip(client, storage))] -pub async fn update_war(client: &Client, clan_tag: &ClanTag, storage: &mut Storage) { +pub async fn update_war( + client: &Client, + clan_tag: &ClanTag, + storage: &mut Storage, +) -> Result<(), ()> { let war = match client.war().current(&clan_tag).await { Ok(w) => w, Err(e) => { tracing::error!("Error loading War: {:?}", e); - return; + return Err(()); } }; if !matches!(war.state, CurrentWarState::InWar) { tracing::info!("WAR: Not in War currently {:?}", war.state); - return; + return Ok(()); } let clan = war.clan; @@ -432,7 +444,7 @@ pub async fn update_war(client: &Client, clan_tag: &ClanTag, storage: &mut Stora Some(m) => m, None => { tracing::error!("Current War Clan is missing Members"); - return; + return Err(()); } }; @@ -440,7 +452,7 @@ pub async fn update_war(client: &Client, clan_tag: &ClanTag, storage: &mut Stora Some(t) => t, None => { tracing::error!("Current War missing Start Time"); - return; + return Err(()); } }; @@ -475,15 +487,21 @@ pub async fn update_war(client: &Client, clan_tag: &ClanTag, storage: &mut Stora .collect(), }; clan_season_stats.wars.insert(start_time, war_stats); + + Ok(()) } #[tracing::instrument(skip(client, storage))] -pub async fn update_clan_games(client: &Client, clan_tag: &ClanTag, storage: &mut Storage) { +pub async fn update_clan_games( + client: &Client, + clan_tag: &ClanTag, + storage: &mut Storage, +) -> Result<(), ()> { let clan = match client.clan_info(&clan_tag).await { Ok(clan) => clan, Err(e) => { tracing::error!("Loading Clan Information: {:?}", e); - return; + return Err(()); } }; @@ -526,4 +544,6 @@ pub async fn update_clan_games(client: &Client, clan_tag: &ClanTag, storage: &mu } } } + + Ok(()) } diff --git a/src/main.rs b/src/main.rs index 367be93..25d77df 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,15 +6,12 @@ use std::env; use std::sync::Arc; use std::time::{Duration, SystemTime, UNIX_EPOCH}; -use awscreds::time::format_description::modifier::Padding; -use awscreds::time::serde::timestamp; use gold_pass_bot::{ ClanTag, ExcelStats, PlayerSummary, RaidMember, RaidWeekendStats, Season, Storage, }; use serenity::async_trait; use serenity::framework::standard::macros::{command, group}; use serenity::framework::standard::{CommandResult, StandardFramework}; -use serenity::futures::io::LineWriter; use serenity::model::channel::Message; use serenity::model::prelude::AttachmentType; use serenity::prelude::*; @@ -36,6 +33,9 @@ impl TypeMapKey for ClanStates { #[async_trait] impl EventHandler for Handler {} +static REGISTRY: once_cell::sync::Lazy = + once_cell::sync::Lazy::new(|| prometheus::Registry::new()); + #[tokio::main(flavor = "current_thread")] async fn main() { let layers = tracing_subscriber::registry() @@ -107,6 +107,14 @@ async fn main() { let mut storage = storage; storage.register_clan(ClanTag(alfie_tag.to_string())); + let error_counter = + prometheus::Counter::new("api_errors", "The Number of errors returned by the API") + .unwrap(); + + once_cell::sync::Lazy::force(®ISTRY) + .register(Box::new(error_counter.clone())) + .unwrap(); + loop { let season = Season::current(); @@ -128,13 +136,23 @@ async fn main() { } }; - gold_pass_bot::update_names(&client, &tag, clan_season_stats).await; + if let Err(e) = gold_pass_bot::update_names(&client, &tag, clan_season_stats).await + { + error_counter.inc(); + } - gold_pass_bot::update_war(&client, &tag, &mut storage).await; + if let Err(e) = gold_pass_bot::update_war(&client, &tag, &mut storage).await { + error_counter.inc(); + } - gold_pass_bot::update_cwl(&client, &tag, &mut storage).await; + if let Err(e) = gold_pass_bot::update_cwl(&client, &tag, &mut storage).await { + error_counter.inc(); + } - gold_pass_bot::update_clan_games(&client, &tag, &mut storage).await; + if let Err(e) = gold_pass_bot::update_clan_games(&client, &tag, &mut storage).await + { + error_counter.inc(); + } match client.captial_raid_seasons(&tag).await { Ok(raid_res) => { @@ -176,6 +194,8 @@ async fn main() { } } Err(e) => { + error_counter.inc(); + tracing::error!("Error loading Capital Raid Seasons: {:?}", e); } }; @@ -199,6 +219,15 @@ async fn main() { } }); + tokio::spawn(async move { + let app = axum::Router::new().route("/metrics", axum::routing::get(metrics)); + + axum::Server::bind(&"0.0.0.0:8080".parse().unwrap()) + .serve(app.into_make_service()) + .await + .unwrap(); + }); + // start listening for events by starting a single shard if let Err(why) = client.start_autosharded().await { println!("An error occurred while running the client: {:?}", why); @@ -380,3 +409,13 @@ async fn export(ctx: &Context, msg: &Message) -> CommandResult { Ok(()) } + +async fn metrics() -> String { + let reg = once_cell::sync::Lazy::force(®ISTRY); + + let encoder = prometheus::TextEncoder::new(); + let metrics_family = reg.gather(); + let encoded = encoder.encode_to_string(&metrics_family).unwrap(); + + encoded +}