From e4ea1836f578a02a53556815b23124cb4873473e Mon Sep 17 00:00:00 2001 From: Gianluca Randazzo Date: Sun, 24 Sep 2023 07:07:38 +0200 Subject: [PATCH 01/18] Error handling in Npc...something's working --- Cargo.lock | 9 +-- Cargo.toml | 1 + src/xddmod/Cargo.toml | 1 + src/xddmod/src/handlers.rs | 15 ++++ src/xddmod/src/handlers/npc/core.rs | 52 +++++--------- src/xddmod/src/handlers/persistence.rs | 94 ++++++++++++++++++++++++-- src/xddmod/src/main.rs | 4 +- 7 files changed, 130 insertions(+), 46 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index bbdee01..7b75da4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2202,18 +2202,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.40" +version = "1.0.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac" +checksum = "9d6d7a740b8a666a7e828dd00da9c0dc290dff53154ea77ac109281de90589b7" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.40" +version = "1.0.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" +checksum = "49922ecae66cc8a249b77e68d1d0623c1b2c514f0060c27cdc68bd62a1219d35" dependencies = [ "proc-macro2", "quote", @@ -2933,6 +2933,7 @@ dependencies = [ "serde", "serde_json", "sqlx", + "thiserror", "timeago", "tokio", "tower", diff --git a/Cargo.toml b/Cargo.toml index fc66a89..224aba9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,5 +22,6 @@ sqlx = { version = "0.6.3", features = [ "offline", "migrate", ] } +thiserror = { version = "1.0.48" } tokio = { version = "1.32.0", features = ["full"] } url = { version = "2.4.1", features = ["serde"] } diff --git a/src/xddmod/Cargo.toml b/src/xddmod/Cargo.toml index 6d06570..898d1fb 100644 --- a/src/xddmod/Cargo.toml +++ b/src/xddmod/Cargo.toml @@ -26,6 +26,7 @@ serde = { workspace = true } serde_json = { workspace = true } sqlx = { workspace = true } timeago = "0.4.2" +thiserror = { workspace = true } tokio = { workspace = true } tower = "0.4.13" twitch_api = { version = "0.7.0-rc.7", features = [ diff --git a/src/xddmod/src/handlers.rs b/src/xddmod/src/handlers.rs index 753c7d9..7934965 100644 --- a/src/xddmod/src/handlers.rs +++ b/src/xddmod/src/handlers.rs @@ -1,3 +1,6 @@ +use twitch_irc::login::LoginCredentials; +use twitch_irc::transport::Transport; + pub mod gamba_time; pub mod gg; pub mod npc; @@ -5,3 +8,15 @@ pub mod persistence; pub mod rip_bozo; pub mod sniffa; pub mod the_grind; + +#[derive(thiserror::Error, Debug)] +pub enum HandlerError { + #[error(transparent)] + Persistence(#[from] persistence::PersistenceError), + #[error(transparent)] + Rendering(#[from] persistence::RenderingError), + #[error(transparent)] + Twitch(#[from] twitch_irc::Error), + #[error(transparent)] + Generic(#[from] anyhow::Error), +} diff --git a/src/xddmod/src/handlers/npc/core.rs b/src/xddmod/src/handlers/npc/core.rs index b0f5750..fa65834 100644 --- a/src/xddmod/src/handlers/npc/core.rs +++ b/src/xddmod/src/handlers/npc/core.rs @@ -1,60 +1,42 @@ use minijinja::value::Value; use minijinja::Environment; use sqlx::SqlitePool; +use twitch_irc::login::LoginCredentials; use twitch_irc::message::PrivmsgMessage; use twitch_irc::message::ServerMessage; +use twitch_irc::transport::Transport; +use twitch_irc::TwitchIRCClient; -use crate::auth::IRCClient; use crate::handlers::persistence::Handler; use crate::handlers::persistence::Reply; +use crate::handlers::HandlerError; use crate::poor_man_throttling; -pub struct Npc<'a> { - pub irc_client: IRCClient, +pub struct Npc<'a, T: Transport, L: LoginCredentials> { + pub irc_client: TwitchIRCClient, pub db_pool: SqlitePool, pub templates_env: Environment<'a>, } -impl<'a> Npc<'a> { +impl<'a, T: Transport, L: LoginCredentials> Npc<'a, T, L> { pub fn handler(&self) -> Handler { Handler::Npc } } -impl<'a> Npc<'a> { - pub async fn handle(&self, server_message: &ServerMessage) { +impl<'a, T: Transport, L: LoginCredentials> Npc<'a, T, L> { + pub async fn handle(&self, server_message: &ServerMessage) -> Result<(), HandlerError> { if let ServerMessage::Privmsg(message @ PrivmsgMessage { is_action: false, .. }) = server_message { - match Reply::matching(self.handler(), message, &self.db_pool).await.as_slice() { - [reply] => { - // FIXME: poor man throttling - match poor_man_throttling::should_throttle(message, reply) { - Ok(false) => (), - Ok(true) => { - eprintln!( - "Skip reply: message {:?}, sender {:?}, reply {:?}", - message.message_text, message.sender, reply.template - ); - return; - } - Err(error) => { - eprintln!("Error throttling, error: {:?}", error); - } - } + let (reply, _) = Reply::first_matching(self.handler(), message, &self.db_pool).await?; - match reply.render_template::(&self.templates_env, None) { - Ok(rendered_reply) if rendered_reply.is_empty() => { - eprintln!("Rendered reply template empty: {:?}", reply) - } - Ok(rendered_reply) => self.irc_client.say_in_reply_to(message, rendered_reply).await.unwrap(), - Err(e) => eprintln!("Error rendering reply template, error: {:?}, {:?}.", reply, e), - } - } - [] => {} - multiple_matching_replies => eprintln!( - "Multiple matching replies for message: {:?}, {:?}.", - multiple_matching_replies, server_message - ), + // FIXME: poor man throttling + if poor_man_throttling::should_throttle(message, &reply)? { + return Ok(()); } + + let rendered_reply = reply.render_template::(&self.templates_env, None)?; + self.irc_client.say_in_reply_to(message, rendered_reply).await?; } + Ok(()) } } diff --git a/src/xddmod/src/handlers/persistence.rs b/src/xddmod/src/handlers/persistence.rs index 205cddb..f2cf477 100644 --- a/src/xddmod/src/handlers/persistence.rs +++ b/src/xddmod/src/handlers/persistence.rs @@ -9,6 +9,30 @@ use sqlx::types::chrono::Utc; use sqlx::types::Json; use twitch_irc::message::PrivmsgMessage; +#[derive(thiserror::Error, Debug)] +pub enum PersistenceError { + #[error("No reply matching message {message:?}, regex errors {regex_errors:?}")] + NoMatchingReply { + message: String, + regex_errors: Vec, + }, + #[error("Multiple replies matching message {message:?}, regex errors {regex_errors:?}")] + MultipleMatchingReplies { + message: String, + regex_errors: Vec, + }, + #[error(transparent)] + Db(#[from] sqlx::Error), +} + +#[derive(thiserror::Error, Debug)] +pub enum RenderingError { + #[error("Empty rendered reply {reply:?} with template env {template_env:?}")] + Empty { reply: Reply, template_env: String }, + #[error(transparent)] + Templating(#[from] minijinja::Error), +} + #[derive(Debug, Clone)] pub struct Reply { pub id: i64, @@ -47,6 +71,58 @@ impl MatchableMessage for PrivmsgMessage { } impl Reply { + pub async fn first_matching<'a>( + handler: Handler, + matchable_message: &impl MatchableMessage, + executor: impl SqliteExecutor<'a>, + ) -> Result<(Reply, Vec), PersistenceError> { + let (replies, regex_errors) = Self::matching_2(handler, matchable_message, executor).await?; + + if let [reply] = replies.as_slice() { + return Ok((reply.clone(), regex_errors)); + } + + let message = matchable_message.text(); + + if replies.first().is_none() { + return Err(PersistenceError::NoMatchingReply { + message: message.into(), + regex_errors, + }); + } + + Err(PersistenceError::MultipleMatchingReplies { + message: message.into(), + regex_errors, + }) + } + + pub async fn matching_2<'a>( + handler: Handler, + matchable_message: &impl MatchableMessage, + executor: impl SqliteExecutor<'a>, + ) -> Result<(Vec, Vec), sqlx::Error> { + let matchable_message_text = matchable_message.text(); + + let (matching_replies, re_errors) = Self::all(handler, matchable_message.channel(), executor) + .await? + .into_iter() + .fold((vec![], vec![]), |(mut matching_replies, mut re_errors), reply| { + match RegexBuilder::new(&reply.pattern) + .case_insensitive(reply.case_insensitive) + .build() + { + Ok(re) if re.is_match(matchable_message_text) => matching_replies.push(reply), + Ok(_) => (), + Err(re_error) => re_errors.push(re_error), + }; + + (matching_replies, re_errors) + }); + + Ok((matching_replies, re_errors)) + } + pub async fn matching<'a>( handler: Handler, matchable_message: &impl MatchableMessage, @@ -77,12 +153,18 @@ impl Reply { &self, template_env: &Environment, ctx: Option<&S>, - ) -> Result { - let ctx = match ctx { - Some(ctx) => minijinja::value::Value::from_serializable(ctx), - None => context!(), - }; - template_env.render_str(&self.template, ctx).map(|s| s.trim().into()) + ) -> Result { + let ctx = ctx.map_or_else(|| context!(), |ctx| minijinja::value::Value::from_serializable(ctx)); + let rendered_reply: String = template_env.render_str(&self.template, ctx).map(|s| s.trim().into())?; + + if rendered_reply.is_empty() { + return Err(RenderingError::Empty { + reply: self.clone(), + template_env: format!("{:?}", template_env), + }); + } + + Ok(rendered_reply) } async fn all<'a>( diff --git a/src/xddmod/src/main.rs b/src/xddmod/src/main.rs index 693e828..985ebb4 100644 --- a/src/xddmod/src/main.rs +++ b/src/xddmod/src/main.rs @@ -73,7 +73,9 @@ async fn main() { if let Ok(true) = rip_bozo_g.handle(&server_message).await { return; } - npc.handle(&server_message).await; + if let Err(e) = npc.handle(&server_message).await { + eprintln!("Npc error {:?}", e); + }; gg.handle(&server_message).await; sniffa.handle(&server_message).await; the_grind.handle(&server_message).await; From 6d2efca2ee39ed381c9f1ff8f03fa134ddd0a840 Mon Sep 17 00:00:00 2001 From: Gianluca Randazzo Date: Sun, 24 Sep 2023 09:01:30 +0200 Subject: [PATCH 02/18] Extract rendering.rs from persistence.rs --- src/xddmod/src/handlers.rs | 3 ++- src/xddmod/src/handlers/persistence.rs | 28 ---------------------- src/xddmod/src/handlers/rendering.rs | 33 ++++++++++++++++++++++++++ 3 files changed, 35 insertions(+), 29 deletions(-) create mode 100644 src/xddmod/src/handlers/rendering.rs diff --git a/src/xddmod/src/handlers.rs b/src/xddmod/src/handlers.rs index 7934965..d9a030d 100644 --- a/src/xddmod/src/handlers.rs +++ b/src/xddmod/src/handlers.rs @@ -5,6 +5,7 @@ pub mod gamba_time; pub mod gg; pub mod npc; pub mod persistence; +pub mod rendering; pub mod rip_bozo; pub mod sniffa; pub mod the_grind; @@ -14,7 +15,7 @@ pub enum HandlerError { #[error(transparent)] Persistence(#[from] persistence::PersistenceError), #[error(transparent)] - Rendering(#[from] persistence::RenderingError), + Rendering(#[from] rendering::RenderingError), #[error(transparent)] Twitch(#[from] twitch_irc::Error), #[error(transparent)] diff --git a/src/xddmod/src/handlers/persistence.rs b/src/xddmod/src/handlers/persistence.rs index f2cf477..620f891 100644 --- a/src/xddmod/src/handlers/persistence.rs +++ b/src/xddmod/src/handlers/persistence.rs @@ -1,5 +1,3 @@ -use minijinja::context; -use minijinja::Environment; use regex::RegexBuilder; use serde::Deserialize; use serde::Serialize; @@ -25,14 +23,6 @@ pub enum PersistenceError { Db(#[from] sqlx::Error), } -#[derive(thiserror::Error, Debug)] -pub enum RenderingError { - #[error("Empty rendered reply {reply:?} with template env {template_env:?}")] - Empty { reply: Reply, template_env: String }, - #[error(transparent)] - Templating(#[from] minijinja::Error), -} - #[derive(Debug, Clone)] pub struct Reply { pub id: i64, @@ -149,24 +139,6 @@ impl Reply { .collect() } - pub fn render_template( - &self, - template_env: &Environment, - ctx: Option<&S>, - ) -> Result { - let ctx = ctx.map_or_else(|| context!(), |ctx| minijinja::value::Value::from_serializable(ctx)); - let rendered_reply: String = template_env.render_str(&self.template, ctx).map(|s| s.trim().into())?; - - if rendered_reply.is_empty() { - return Err(RenderingError::Empty { - reply: self.clone(), - template_env: format!("{:?}", template_env), - }); - } - - Ok(rendered_reply) - } - async fn all<'a>( handler: Handler, channel: &str, diff --git a/src/xddmod/src/handlers/rendering.rs b/src/xddmod/src/handlers/rendering.rs new file mode 100644 index 0000000..a79b0a1 --- /dev/null +++ b/src/xddmod/src/handlers/rendering.rs @@ -0,0 +1,33 @@ +use minijinja::context; +use minijinja::Environment; +use serde::Serialize; + +use crate::handlers::persistence::Reply; + +#[derive(thiserror::Error, Debug)] +pub enum RenderingError { + #[error("Empty rendered reply {reply:?} with template env {template_env:?}")] + EmptyRenderedReply { reply: Reply, template_env: String }, + #[error(transparent)] + Templating(#[from] minijinja::Error), +} + +impl Reply { + pub fn render_template( + &self, + template_env: &Environment, + ctx: Option<&S>, + ) -> Result { + let ctx = ctx.map_or_else(|| context!(), |ctx| minijinja::value::Value::from_serializable(ctx)); + let rendered_reply: String = template_env.render_str(&self.template, ctx).map(|s| s.trim().into())?; + + if rendered_reply.is_empty() { + return Err(RenderingError::EmptyRenderedReply { + reply: self.clone(), + template_env: format!("{:?}", template_env), + }); + } + + Ok(rendered_reply) + } +} From 4df04692d52b59aeaa66f038926c479992571a51 Mon Sep 17 00:00:00 2001 From: Gianluca Randazzo Date: Sun, 24 Sep 2023 09:17:11 +0200 Subject: [PATCH 03/18] Add Helix Twitch error...not sure about this though --- src/xddmod/src/handlers.rs | 12 ++++++++++-- src/xddmod/src/handlers/npc/core.rs | 12 ++++++++++-- src/xddmod/src/main.rs | 2 +- 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/src/xddmod/src/handlers.rs b/src/xddmod/src/handlers.rs index d9a030d..e2b4a57 100644 --- a/src/xddmod/src/handlers.rs +++ b/src/xddmod/src/handlers.rs @@ -11,13 +11,21 @@ pub mod sniffa; pub mod the_grind; #[derive(thiserror::Error, Debug)] -pub enum HandlerError { +pub enum HandlerError { #[error(transparent)] Persistence(#[from] persistence::PersistenceError), #[error(transparent)] Rendering(#[from] rendering::RenderingError), #[error(transparent)] - Twitch(#[from] twitch_irc::Error), + Twitch(#[from] TwitchError), #[error(transparent)] Generic(#[from] anyhow::Error), } + +#[derive(thiserror::Error, Debug)] +pub enum TwitchError { + #[error(transparent)] + Irc(#[from] twitch_irc::Error), + #[error(transparent)] + Api(#[from] twitch_api::helix::ClientRequestError), +} diff --git a/src/xddmod/src/handlers/npc/core.rs b/src/xddmod/src/handlers/npc/core.rs index fa65834..de68530 100644 --- a/src/xddmod/src/handlers/npc/core.rs +++ b/src/xddmod/src/handlers/npc/core.rs @@ -10,6 +10,7 @@ use twitch_irc::TwitchIRCClient; use crate::handlers::persistence::Handler; use crate::handlers::persistence::Reply; use crate::handlers::HandlerError; +use crate::handlers::TwitchError; use crate::poor_man_throttling; pub struct Npc<'a, T: Transport, L: LoginCredentials> { @@ -25,7 +26,10 @@ impl<'a, T: Transport, L: LoginCredentials> Npc<'a, T, L> { } impl<'a, T: Transport, L: LoginCredentials> Npc<'a, T, L> { - pub async fn handle(&self, server_message: &ServerMessage) -> Result<(), HandlerError> { + pub async fn handle( + &self, + server_message: &ServerMessage, + ) -> Result<(), HandlerError> { if let ServerMessage::Privmsg(message @ PrivmsgMessage { is_action: false, .. }) = server_message { let (reply, _) = Reply::first_matching(self.handler(), message, &self.db_pool).await?; @@ -35,7 +39,11 @@ impl<'a, T: Transport, L: LoginCredentials> Npc<'a, T, L> { } let rendered_reply = reply.render_template::(&self.templates_env, None)?; - self.irc_client.say_in_reply_to(message, rendered_reply).await?; + + self.irc_client + .say_in_reply_to(message, rendered_reply) + .await + .map_err(TwitchError::from)?; } Ok(()) } diff --git a/src/xddmod/src/main.rs b/src/xddmod/src/main.rs index 985ebb4..8cef6d6 100644 --- a/src/xddmod/src/main.rs +++ b/src/xddmod/src/main.rs @@ -73,7 +73,7 @@ async fn main() { if let Ok(true) = rip_bozo_g.handle(&server_message).await { return; } - if let Err(e) = npc.handle(&server_message).await { + if let Err(e) = npc.handle::(&server_message).await { eprintln!("Npc error {:?}", e); }; gg.handle(&server_message).await; From fad9c315bea87fceaa4d70492739e8a3c9d7e7ff Mon Sep 17 00:00:00 2001 From: Gianluca Randazzo Date: Sun, 24 Sep 2023 10:19:15 +0200 Subject: [PATCH 04/18] Generalize RipBozo helix_client --- src/xddmod/src/handlers/rip_bozo/core.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/xddmod/src/handlers/rip_bozo/core.rs b/src/xddmod/src/handlers/rip_bozo/core.rs index f15129a..8c49bd9 100644 --- a/src/xddmod/src/handlers/rip_bozo/core.rs +++ b/src/xddmod/src/handlers/rip_bozo/core.rs @@ -3,9 +3,11 @@ use lazy_static::lazy_static; use regex::Captures; use regex::Regex; use sqlx::SqlitePool; +use twitch_api::twitch_oauth2::client::Client as TwitchOauth2Client; use twitch_api::twitch_oauth2::TwitchToken; use twitch_api::twitch_oauth2::UserToken; use twitch_api::HelixClient; +use twitch_api::HttpClient as TwitchHttpClient; use twitch_irc::message::PrivmsgMessage; use twitch_irc::message::ServerMessage; use twitch_types::UserId; @@ -19,20 +21,20 @@ lazy_static! { static ref MENTION_REGEX: Regex = Regex::new(r"(@(\w+))").unwrap(); } -pub struct RipBozo<'a> { +pub struct RipBozo<'a, C: TwitchHttpClient + TwitchOauth2Client> { pub broadcaster_id: UserId, pub token: UserToken, - pub helix_client: HelixClient<'a, reqwest::Client>, + pub helix_client: HelixClient<'a, C>, pub db_pool: SqlitePool, } -impl<'a> RipBozo<'a> { +impl<'a, C: TwitchHttpClient + TwitchOauth2Client> RipBozo<'a, C> { pub fn handler(&self) -> Handler { Handler::RipBozo } } -impl<'a> RipBozo<'a> { +impl<'a, C: TwitchHttpClient + TwitchOauth2Client> RipBozo<'a, C> { pub async fn handle(&mut self, server_message: &ServerMessage) -> anyhow::Result { if let ServerMessage::Privmsg(message @ PrivmsgMessage { is_action: false, .. }) = server_message { if twitch::helpers::is_from_streamer_or_mod(message) { From 17dac48721c27c67dbb64f0bcbfc000c54d54b68 Mon Sep 17 00:00:00 2001 From: Gianluca Randazzo Date: Sun, 24 Sep 2023 10:32:31 +0200 Subject: [PATCH 05/18] Extract TwitchApi wrapper traits --- src/xddmod/src/handlers.rs | 15 ++++++++++----- src/xddmod/src/handlers/npc/core.rs | 3 ++- src/xddmod/src/handlers/rip_bozo/core.rs | 9 ++++----- 3 files changed, 16 insertions(+), 11 deletions(-) diff --git a/src/xddmod/src/handlers.rs b/src/xddmod/src/handlers.rs index e2b4a57..2facb15 100644 --- a/src/xddmod/src/handlers.rs +++ b/src/xddmod/src/handlers.rs @@ -1,6 +1,3 @@ -use twitch_irc::login::LoginCredentials; -use twitch_irc::transport::Transport; - pub mod gamba_time; pub mod gg; pub mod npc; @@ -10,8 +7,16 @@ pub mod rip_bozo; pub mod sniffa; pub mod the_grind; +pub trait TwitchApiClient: twitch_api::HttpClient + twitch_api::twitch_oauth2::client::Client {} + +impl TwitchApiClient for T {} + +pub trait TwitchApiError: std::error::Error + Send + Sync + 'static {} + +impl TwitchApiError for T {} + #[derive(thiserror::Error, Debug)] -pub enum HandlerError { +pub enum HandlerError { #[error(transparent)] Persistence(#[from] persistence::PersistenceError), #[error(transparent)] @@ -23,7 +28,7 @@ pub enum HandlerError { +pub enum TwitchError { #[error(transparent)] Irc(#[from] twitch_irc::Error), #[error(transparent)] diff --git a/src/xddmod/src/handlers/npc/core.rs b/src/xddmod/src/handlers/npc/core.rs index de68530..6d2552e 100644 --- a/src/xddmod/src/handlers/npc/core.rs +++ b/src/xddmod/src/handlers/npc/core.rs @@ -10,6 +10,7 @@ use twitch_irc::TwitchIRCClient; use crate::handlers::persistence::Handler; use crate::handlers::persistence::Reply; use crate::handlers::HandlerError; +use crate::handlers::TwitchApiError; use crate::handlers::TwitchError; use crate::poor_man_throttling; @@ -26,7 +27,7 @@ impl<'a, T: Transport, L: LoginCredentials> Npc<'a, T, L> { } impl<'a, T: Transport, L: LoginCredentials> Npc<'a, T, L> { - pub async fn handle( + pub async fn handle( &self, server_message: &ServerMessage, ) -> Result<(), HandlerError> { diff --git a/src/xddmod/src/handlers/rip_bozo/core.rs b/src/xddmod/src/handlers/rip_bozo/core.rs index 8c49bd9..a160d1c 100644 --- a/src/xddmod/src/handlers/rip_bozo/core.rs +++ b/src/xddmod/src/handlers/rip_bozo/core.rs @@ -3,11 +3,9 @@ use lazy_static::lazy_static; use regex::Captures; use regex::Regex; use sqlx::SqlitePool; -use twitch_api::twitch_oauth2::client::Client as TwitchOauth2Client; use twitch_api::twitch_oauth2::TwitchToken; use twitch_api::twitch_oauth2::UserToken; use twitch_api::HelixClient; -use twitch_api::HttpClient as TwitchHttpClient; use twitch_irc::message::PrivmsgMessage; use twitch_irc::message::ServerMessage; use twitch_types::UserId; @@ -15,26 +13,27 @@ use unicode_segmentation::UnicodeSegmentation; use crate::apis::twitch; use crate::handlers::persistence::Handler; +use crate::handlers::TwitchApiClient; lazy_static! { static ref EMOJI_REGEX: Regex = Regex::new(r"\p{Emoji}").unwrap(); static ref MENTION_REGEX: Regex = Regex::new(r"(@(\w+))").unwrap(); } -pub struct RipBozo<'a, C: TwitchHttpClient + TwitchOauth2Client> { +pub struct RipBozo<'a, C: TwitchApiClient> { pub broadcaster_id: UserId, pub token: UserToken, pub helix_client: HelixClient<'a, C>, pub db_pool: SqlitePool, } -impl<'a, C: TwitchHttpClient + TwitchOauth2Client> RipBozo<'a, C> { +impl<'a, C: TwitchApiClient> RipBozo<'a, C> { pub fn handler(&self) -> Handler { Handler::RipBozo } } -impl<'a, C: TwitchHttpClient + TwitchOauth2Client> RipBozo<'a, C> { +impl<'a, C: TwitchApiClient> RipBozo<'a, C> { pub async fn handle(&mut self, server_message: &ServerMessage) -> anyhow::Result { if let ServerMessage::Privmsg(message @ PrivmsgMessage { is_action: false, .. }) = server_message { if twitch::helpers::is_from_streamer_or_mod(message) { From 6c8895d509af7db3181996f7b50b65ae86d9b311 Mon Sep 17 00:00:00 2001 From: Gianluca Randazzo Date: Sat, 30 Sep 2023 08:23:37 +0200 Subject: [PATCH 06/18] Don't know what I am doing but I'm having fun --- Cargo.lock | 7 ++ Cargo.toml | 1 + src/xddmod/Cargo.toml | 1 + src/xddmod/src/handlers/npc/core.rs | 15 +++- src/xddmod/src/handlers/persistence.rs | 94 +++++++++++++++++--------- 5 files changed, 82 insertions(+), 36 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7b75da4..7a89a24 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2591,6 +2591,12 @@ version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" +[[package]] +name = "vec1" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bda7c41ca331fe9a1c278a9e7ee055f4be7f5eb1c2b72f079b4ff8b5fce9d5c" + [[package]] name = "version_check" version = "0.9.4" @@ -2942,6 +2948,7 @@ dependencies = [ "twitch_types", "unicode-segmentation", "url", + "vec1", "webbrowser", ] diff --git a/Cargo.toml b/Cargo.toml index 224aba9..3aa4646 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,3 +25,4 @@ sqlx = { version = "0.6.3", features = [ thiserror = { version = "1.0.48" } tokio = { version = "1.32.0", features = ["full"] } url = { version = "2.4.1", features = ["serde"] } +vec1 = { version = "1.10.1" } diff --git a/src/xddmod/Cargo.toml b/src/xddmod/Cargo.toml index 898d1fb..0d2705d 100644 --- a/src/xddmod/Cargo.toml +++ b/src/xddmod/Cargo.toml @@ -42,4 +42,5 @@ twitch-irc = { git = "https://github.com/Retoon/twitch-irc-rs", "branch" = "repl ] } unicode-segmentation = "1.9.0" url = { workspace = true } +vec1 = { workspace = true } webbrowser = "0.8.11" diff --git a/src/xddmod/src/handlers/npc/core.rs b/src/xddmod/src/handlers/npc/core.rs index 6d2552e..1112a40 100644 --- a/src/xddmod/src/handlers/npc/core.rs +++ b/src/xddmod/src/handlers/npc/core.rs @@ -8,6 +8,7 @@ use twitch_irc::transport::Transport; use twitch_irc::TwitchIRCClient; use crate::handlers::persistence::Handler; +use crate::handlers::persistence::PersistenceError; use crate::handlers::persistence::Reply; use crate::handlers::HandlerError; use crate::handlers::TwitchApiError; @@ -32,19 +33,27 @@ impl<'a, T: Transport, L: LoginCredentials> Npc<'a, T, L> { server_message: &ServerMessage, ) -> Result<(), HandlerError> { if let ServerMessage::Privmsg(message @ PrivmsgMessage { is_action: false, .. }) = server_message { - let (reply, _) = Reply::first_matching(self.handler(), message, &self.db_pool).await?; + let Some(first_matching) = Reply::first_matching(self.handler(), message, &self.db_pool).await? else { + return Ok(()); + }; // FIXME: poor man throttling - if poor_man_throttling::should_throttle(message, &reply)? { + if poor_man_throttling::should_throttle(message, &first_matching.reply)? { return Ok(()); } - let rendered_reply = reply.render_template::(&self.templates_env, None)?; + let rendered_reply = first_matching + .reply + .render_template::(&self.templates_env, None)?; self.irc_client .say_in_reply_to(message, rendered_reply) .await .map_err(TwitchError::from)?; + + if let Ok(error) = PersistenceError::try_from(first_matching) { + return Err(error.into()); + }; } Ok(()) } diff --git a/src/xddmod/src/handlers/persistence.rs b/src/xddmod/src/handlers/persistence.rs index 620f891..304238a 100644 --- a/src/xddmod/src/handlers/persistence.rs +++ b/src/xddmod/src/handlers/persistence.rs @@ -6,16 +6,24 @@ use sqlx::types::chrono::DateTime; use sqlx::types::chrono::Utc; use sqlx::types::Json; use twitch_irc::message::PrivmsgMessage; +use vec1::Vec1; #[derive(thiserror::Error, Debug)] pub enum PersistenceError { + #[error("Single reply {reply:?} matching message {message:?}, regex errors {regex_errors:?}")] + SingleReplyAndErrors { + reply: Reply, + message: String, + regex_errors: Vec1, + }, #[error("No reply matching message {message:?}, regex errors {regex_errors:?}")] - NoMatchingReply { + NoReplyAndErrors { message: String, - regex_errors: Vec, + regex_errors: Vec1, }, - #[error("Multiple replies matching message {message:?}, regex errors {regex_errors:?}")] - MultipleMatchingReplies { + #[error("Multiple replies {replies:?} matching message {message:?}, regex errors {regex_errors:?}")] + MultipleReplies { + replies: Vec, message: String, regex_errors: Vec, }, @@ -23,21 +31,6 @@ pub enum PersistenceError { Db(#[from] sqlx::Error), } -#[derive(Debug, Clone)] -pub struct Reply { - pub id: i64, - pub handler: Option, - pub pattern: String, - pub case_insensitive: bool, - pub template: String, - pub channel: Option, - pub enabled: bool, - pub additional_inputs: Option>, - pub created_by: String, - pub created_at: DateTime, - pub updated_at: DateTime, -} - pub trait MatchableMessage { fn channel(&self) -> &str; fn text(&self) -> &str; @@ -60,31 +53,48 @@ impl MatchableMessage for PrivmsgMessage { } } +#[derive(Debug, Clone)] +pub struct Reply { + pub id: i64, + pub handler: Option, + pub pattern: String, + pub case_insensitive: bool, + pub template: String, + pub channel: Option, + pub enabled: bool, + pub additional_inputs: Option>, + pub created_by: String, + pub created_at: DateTime, + pub updated_at: DateTime, +} + impl Reply { pub async fn first_matching<'a>( handler: Handler, matchable_message: &impl MatchableMessage, executor: impl SqliteExecutor<'a>, - ) -> Result<(Reply, Vec), PersistenceError> { + ) -> Result, PersistenceError> { let (replies, regex_errors) = Self::matching_2(handler, matchable_message, executor).await?; - if let [reply] = replies.as_slice() { - return Ok((reply.clone(), regex_errors)); - } - let message = matchable_message.text(); - if replies.first().is_none() { - return Err(PersistenceError::NoMatchingReply { + match (replies.as_slice(), regex_errors.as_slice()) { + ([], []) => Ok(None), + ([reply], regex_errors) => Ok(Some(FirstMatching { + reply: reply.to_owned(), + message: message.to_owned(), + regex_errors: regex_errors.to_owned(), + })), + ([], regex_errors) => Err(PersistenceError::NoReplyAndErrors { message: message.into(), - regex_errors, - }); + regex_errors: regex_errors.try_into().unwrap(), + }), + (replies, regex_errors) => Err(PersistenceError::MultipleReplies { + replies: replies.to_owned(), + message: message.into(), + regex_errors: regex_errors.try_into().unwrap(), + }), } - - Err(PersistenceError::MultipleMatchingReplies { - message: message.into(), - regex_errors, - }) } pub async fn matching_2<'a>( @@ -171,6 +181,24 @@ impl Reply { } } +pub struct FirstMatching { + pub reply: Reply, + message: String, + regex_errors: Vec, +} + +impl TryFrom for PersistenceError { + type Error = vec1::Size0Error; + + fn try_from(value: FirstMatching) -> Result { + Ok(Self::SingleReplyAndErrors { + reply: value.reply, + message: value.message, + regex_errors: value.regex_errors.try_into()?, + }) + } +} + #[derive(Debug, Clone, Copy, Serialize, Deserialize, sqlx::Type)] #[sqlx(type_name = "TEXT")] pub enum Handler { From b7413c45747cb9a951401c2034fd0014e2d7735a Mon Sep 17 00:00:00 2001 From: Gianluca Randazzo Date: Sat, 30 Sep 2023 10:57:11 +0200 Subject: [PATCH 07/18] Try to extend "dummy" error handling to Gg --- src/xddmod/src/handlers/gg/core.rs | 136 ++++++++++++------------- src/xddmod/src/handlers/persistence.rs | 8 +- src/xddmod/src/handlers/rendering.rs | 2 +- src/xddmod/src/main.rs | 4 +- 4 files changed, 73 insertions(+), 77 deletions(-) diff --git a/src/xddmod/src/handlers/gg/core.rs b/src/xddmod/src/handlers/gg/core.rs index ab5750f..eb03eac 100644 --- a/src/xddmod/src/handlers/gg/core.rs +++ b/src/xddmod/src/handlers/gg/core.rs @@ -4,109 +4,101 @@ use minijinja::Environment; use serde::Deserialize; use serde::Serialize; use sqlx::SqlitePool; +use twitch_irc::login::LoginCredentials; use twitch_irc::message::PrivmsgMessage; use twitch_irc::message::ServerMessage; +use twitch_irc::transport::Transport; +use twitch_irc::TwitchIRCClient; use crate::apis::ddragon::champion::Champion; use crate::apis::op_gg; use crate::apis::op_gg::games::Game; use crate::apis::op_gg::Region; -use crate::auth::IRCClient; use crate::handlers::persistence::Handler; +use crate::handlers::persistence::PersistenceError; use crate::handlers::persistence::Reply; +use crate::handlers::HandlerError; +use crate::handlers::TwitchApiError; +use crate::handlers::TwitchError; use crate::poor_man_throttling; -pub struct Gg<'a> { - pub irc_client: IRCClient, +pub struct Gg<'a, T: Transport, L: LoginCredentials> { + pub irc_client: TwitchIRCClient, pub db_pool: SqlitePool, pub templates_env: Environment<'a>, } -impl<'a> Gg<'a> { +impl<'a, T: Transport, L: LoginCredentials> Gg<'a, T, L> { pub fn handler(&self) -> Handler { Handler::Gg } } -impl<'a> Gg<'a> { - pub async fn handle(&self, server_message: &ServerMessage) { +impl<'a, T: Transport, L: LoginCredentials> Gg<'a, T, L> { + pub async fn handle( + &self, + server_message: &ServerMessage, + ) -> Result<(), HandlerError> { if let ServerMessage::Privmsg(message @ PrivmsgMessage { is_action: false, .. }) = server_message { - match Reply::matching(self.handler(), message, &self.db_pool).await.as_slice() { - [reply @ Reply { - additional_inputs: Some(additional_inputs), - .. - }] => { - match poor_man_throttling::should_throttle(message, reply) { - Ok(false) => (), - Ok(true) => { - eprintln!( - "Skip reply: message {:?}, sender {:?}, reply {:?}", - message.message_text, message.sender, reply.template - ); - return; - } - Err(error) => { - eprintln!("Error throttling, error: {:?}", error); - } - } + let Some(first_matching) = Reply::first_matching(self.handler(), message, &self.db_pool).await? else { + return Ok(()); + }; + + let Some(additional_inputs) = first_matching.reply.additional_inputs.as_ref() else { + return Err(PersistenceError::MissingAdditionalInputs { + reply: first_matching.reply.clone(), + } + .into()); + }; + + // FIXME: poor man throttling + if poor_man_throttling::should_throttle(message, &first_matching.reply)? { + return Ok(()); + } - match serde_json::from_value::(additional_inputs.0.clone()) { - Ok(additional_inputs) => { - let summoner = op_gg::summoners::get_summoner( - additional_inputs.region, - &additional_inputs.summoner_name, - ) + match serde_json::from_value::(additional_inputs.0.clone()) { + Ok(additional_inputs) => { + let summoner = + op_gg::summoners::get_summoner(additional_inputs.region, &additional_inputs.summoner_name) .await .unwrap(); - if let Some(game) = - op_gg::games::get_last_game(additional_inputs.region, &summoner.common.summoner_id) - .await - .unwrap() - { - let template_inputs = TemplateInputs { - champion: Champion::by_key(game.my_data.champion_key.into(), &self.db_pool) - .await - .unwrap(), - game, - }; + if let Some(game) = + op_gg::games::get_last_game(additional_inputs.region, &summoner.common.summoner_id) + .await + .unwrap() + { + let template_inputs = TemplateInputs { + champion: Champion::by_key(game.my_data.champion_key.into(), &self.db_pool) + .await + .unwrap(), + game, + }; + + let rendered_reply = first_matching.reply.render_template::( + &self.templates_env, + Some(&Value::from_serializable(&template_inputs)), + )?; - match reply.render_template( - &self.templates_env, - Some(&Value::from_serializable(&template_inputs)), - ) { - Ok(rendered_reply) if rendered_reply.is_empty() => { - eprintln!("Rendered reply template empty: {:?}.", reply) - } - Ok(rendered_reply) => { - self.irc_client.say_in_reply_to(message, rendered_reply).await.unwrap() - } - Err(e) => eprintln!("Error rendering reply template, error: {:?}, {:?}.", reply, e), - } - } else { - eprintln!("No games returned for reply: {:?}.", reply) - } - } - Err(error) => eprintln!( - "Error deserializing AdditionalInputs from Reply for ServerMessage: {:?}, {:?}, {:?}.", - error, server_message, reply - ), + self.irc_client + .say_in_reply_to(message, rendered_reply) + .await + .map_err(TwitchError::from)?; + + if let Ok(error) = PersistenceError::try_from(first_matching) { + return Err(error.into()); + }; + } else { + eprintln!("No games returned for reply: {:?}.", first_matching.reply) } } - [reply @ Reply { - additional_inputs: None, - .. - }] => eprintln!( - "Reply for ServerMessage with missing AdditionalInputs: {:?}, {:?}.", - server_message, reply - ), - [] => {} - multiple_matching_replies => eprintln!( - "Multiple matching replies for message: {:?}, {:?}.", - multiple_matching_replies, server_message + Err(error) => eprintln!( + "Error deserializing AdditionalInputs from Reply for ServerMessage: {:?}, {:?}, {:?}.", + error, server_message, first_matching.reply ), } } + Ok(()) } } diff --git a/src/xddmod/src/handlers/persistence.rs b/src/xddmod/src/handlers/persistence.rs index 304238a..a0d8128 100644 --- a/src/xddmod/src/handlers/persistence.rs +++ b/src/xddmod/src/handlers/persistence.rs @@ -10,23 +10,25 @@ use vec1::Vec1; #[derive(thiserror::Error, Debug)] pub enum PersistenceError { - #[error("Single reply {reply:?} matching message {message:?}, regex errors {regex_errors:?}")] + #[error("single reply {reply:?} matching message {message:?}, regex errors {regex_errors:?}")] SingleReplyAndErrors { reply: Reply, message: String, regex_errors: Vec1, }, - #[error("No reply matching message {message:?}, regex errors {regex_errors:?}")] + #[error("no reply matching message {message:?}, regex errors {regex_errors:?}")] NoReplyAndErrors { message: String, regex_errors: Vec1, }, - #[error("Multiple replies {replies:?} matching message {message:?}, regex errors {regex_errors:?}")] + #[error("multiple replies {replies:?} matching message {message:?}, regex errors {regex_errors:?}")] MultipleReplies { replies: Vec, message: String, regex_errors: Vec, }, + #[error("missing additional inputs in {reply:?}")] + MissingAdditionalInputs { reply: Reply }, #[error(transparent)] Db(#[from] sqlx::Error), } diff --git a/src/xddmod/src/handlers/rendering.rs b/src/xddmod/src/handlers/rendering.rs index a79b0a1..8250f05 100644 --- a/src/xddmod/src/handlers/rendering.rs +++ b/src/xddmod/src/handlers/rendering.rs @@ -6,7 +6,7 @@ use crate::handlers::persistence::Reply; #[derive(thiserror::Error, Debug)] pub enum RenderingError { - #[error("Empty rendered reply {reply:?} with template env {template_env:?}")] + #[error("empty rendered reply {reply:?} with template env {template_env:?}")] EmptyRenderedReply { reply: Reply, template_env: String }, #[error(transparent)] Templating(#[from] minijinja::Error), diff --git a/src/xddmod/src/main.rs b/src/xddmod/src/main.rs index 8cef6d6..d7ec9dd 100644 --- a/src/xddmod/src/main.rs +++ b/src/xddmod/src/main.rs @@ -76,7 +76,9 @@ async fn main() { if let Err(e) = npc.handle::(&server_message).await { eprintln!("Npc error {:?}", e); }; - gg.handle(&server_message).await; + if let Err(e) = gg.handle::(&server_message).await { + eprintln!("Gg error {:?}", e); + }; sniffa.handle(&server_message).await; the_grind.handle(&server_message).await; }); From 0931528feb3b353d3f856d84d095b3c92535080b Mon Sep 17 00:00:00 2001 From: Gianluca Randazzo Date: Sat, 30 Sep 2023 12:13:35 +0200 Subject: [PATCH 08/18] Try to extend "dummy" error handling to Sniffa --- src/xddmod/src/handlers/sniffa/core.rs | 125 ++++++++++++------------- src/xddmod/src/main.rs | 4 +- 2 files changed, 61 insertions(+), 68 deletions(-) diff --git a/src/xddmod/src/handlers/sniffa/core.rs b/src/xddmod/src/handlers/sniffa/core.rs index d4fa209..3ccae1a 100644 --- a/src/xddmod/src/handlers/sniffa/core.rs +++ b/src/xddmod/src/handlers/sniffa/core.rs @@ -4,105 +4,96 @@ use minijinja::Environment; use serde::Deserialize; use serde::Serialize; use sqlx::SqlitePool; +use twitch_irc::login::LoginCredentials; use twitch_irc::message::PrivmsgMessage; use twitch_irc::message::ServerMessage; +use twitch_irc::transport::Transport; +use twitch_irc::TwitchIRCClient; use crate::apis::op_gg; use crate::apis::op_gg::spectate::get_spectate_status; use crate::apis::op_gg::spectate::SpectateStatus; use crate::apis::op_gg::summoners::Summoner; use crate::apis::op_gg::Region; -use crate::auth::IRCClient; use crate::handlers::persistence::Handler; +use crate::handlers::persistence::PersistenceError; use crate::handlers::persistence::Reply; +use crate::handlers::HandlerError; +use crate::handlers::TwitchApiError; +use crate::handlers::TwitchError; use crate::poor_man_throttling; -pub struct Sniffa<'a> { - pub irc_client: IRCClient, +pub struct Sniffa<'a, T: Transport, L: LoginCredentials> { + pub irc_client: TwitchIRCClient, pub db_pool: SqlitePool, pub templates_env: Environment<'a>, } -impl<'a> Sniffa<'a> { +impl<'a, T: Transport, L: LoginCredentials> Sniffa<'a, T, L> { pub fn handler(&self) -> Handler { Handler::Sniffa } } -impl<'a> Sniffa<'a> { - pub async fn handle(&self, server_message: &ServerMessage) { +impl<'a, T: Transport, L: LoginCredentials> Sniffa<'a, T, L> { + pub async fn handle( + &self, + server_message: &ServerMessage, + ) -> Result<(), HandlerError> { if let ServerMessage::Privmsg(message @ PrivmsgMessage { is_action: false, .. }) = server_message { - match Reply::matching(self.handler(), message, &self.db_pool).await.as_slice() { - [reply @ Reply { - additional_inputs: Some(additional_inputs), - .. - }] => { - match poor_man_throttling::should_throttle(message, reply) { - Ok(false) => (), - Ok(true) => { - eprintln!( - "Skip reply: message {:?}, sender {:?}, reply {:?}", - message.message_text, message.sender, reply.template - ); - return; - } - Err(error) => { - eprintln!("Error throttling, error: {:?}", error); - } - } + let Some(first_matching) = Reply::first_matching(self.handler(), message, &self.db_pool).await? else { + return Ok(()); + }; - match serde_json::from_value::(additional_inputs.0.clone()) { - Ok(additional_inputs) => { - let summoner = op_gg::summoners::get_summoner( - additional_inputs.region, - &additional_inputs.summoner_name, - ) + let Some(additional_inputs) = first_matching.reply.additional_inputs.as_ref() else { + return Err(PersistenceError::MissingAdditionalInputs { + reply: first_matching.reply.clone(), + } + .into()); + }; + + // FIXME: poor man throttling + if poor_man_throttling::should_throttle(message, &first_matching.reply)? { + return Ok(()); + } + + match serde_json::from_value::(additional_inputs.0.clone()) { + Ok(additional_inputs) => { + let summoner = + op_gg::summoners::get_summoner(additional_inputs.region, &additional_inputs.summoner_name) .await .unwrap(); - let spectate_status = - get_spectate_status(additional_inputs.region, &summoner.common.summoner_id) - .await - .unwrap(); + let spectate_status = get_spectate_status(additional_inputs.region, &summoner.common.summoner_id) + .await + .unwrap(); - let template_inputs = TemplateInputs { - summoner, - spectate_status, - }; + let template_inputs = TemplateInputs { + summoner, + spectate_status, + }; - match reply - .render_template(&self.templates_env, Some(&Value::from_serializable(&template_inputs))) - { - Ok(rendered_reply) if rendered_reply.is_empty() => { - eprintln!("Rendered reply template empty: {:?}.", reply) - } - Ok(rendered_reply) => { - self.irc_client.say_in_reply_to(message, rendered_reply).await.unwrap() - } - Err(e) => eprintln!("Error rendering reply template, error: {:?}, {:?}.", reply, e), - } - } - Err(error) => eprintln!( - "Error deserializing AdditionalInputs from Reply for ServerMessage: {:?}, {:?}, {:?}.", - error, server_message, reply - ), - } - } + let rendered_reply = first_matching.reply.render_template::( + &self.templates_env, + Some(&Value::from_serializable(&template_inputs)), + )?; - [reply @ Reply { - additional_inputs: None, - .. - }] => eprintln!( - "Reply for ServerMessage with missing AdditionalInputs: {:?}, {:?}.", - server_message, reply - ), - [] => {} - multiple_matching_replies => eprintln!( - "Multiple matching replies for message: {:?}, {:?}.", - multiple_matching_replies, server_message + self.irc_client + .say_in_reply_to(message, rendered_reply) + .await + .map_err(TwitchError::from)?; + + if let Ok(error) = PersistenceError::try_from(first_matching) { + return Err(error.into()); + }; + } + Err(error) => eprintln!( + "Error deserializing AdditionalInputs from Reply for ServerMessage: {:?}, {:?}, {:?}.", + error, server_message, first_matching.reply ), } } + Ok(()) } } diff --git a/src/xddmod/src/main.rs b/src/xddmod/src/main.rs index d7ec9dd..c10123f 100644 --- a/src/xddmod/src/main.rs +++ b/src/xddmod/src/main.rs @@ -79,7 +79,9 @@ async fn main() { if let Err(e) = gg.handle::(&server_message).await { eprintln!("Gg error {:?}", e); }; - sniffa.handle(&server_message).await; + if let Err(e) = sniffa.handle::(&server_message).await { + eprintln!("Sniffa error {:?}", e); + }; the_grind.handle(&server_message).await; }); } From eac25350df7ea961ed31d66e81bbc022751b1528 Mon Sep 17 00:00:00 2001 From: Gianluca Randazzo Date: Sat, 30 Sep 2023 13:32:27 +0200 Subject: [PATCH 09/18] Sneaky unwraps --- src/xddmod/src/handlers/gg/core.rs | 7 ++----- src/xddmod/src/handlers/sniffa/core.rs | 8 +++----- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/src/xddmod/src/handlers/gg/core.rs b/src/xddmod/src/handlers/gg/core.rs index eb03eac..d372708 100644 --- a/src/xddmod/src/handlers/gg/core.rs +++ b/src/xddmod/src/handlers/gg/core.rs @@ -60,13 +60,10 @@ impl<'a, T: Transport, L: LoginCredentials> Gg<'a, T, L> { Ok(additional_inputs) => { let summoner = op_gg::summoners::get_summoner(additional_inputs.region, &additional_inputs.summoner_name) - .await - .unwrap(); + .await?; if let Some(game) = - op_gg::games::get_last_game(additional_inputs.region, &summoner.common.summoner_id) - .await - .unwrap() + op_gg::games::get_last_game(additional_inputs.region, &summoner.common.summoner_id).await? { let template_inputs = TemplateInputs { champion: Champion::by_key(game.my_data.champion_key.into(), &self.db_pool) diff --git a/src/xddmod/src/handlers/sniffa/core.rs b/src/xddmod/src/handlers/sniffa/core.rs index 3ccae1a..76a6478 100644 --- a/src/xddmod/src/handlers/sniffa/core.rs +++ b/src/xddmod/src/handlers/sniffa/core.rs @@ -61,12 +61,10 @@ impl<'a, T: Transport, L: LoginCredentials> Sniffa<'a, T, L> { Ok(additional_inputs) => { let summoner = op_gg::summoners::get_summoner(additional_inputs.region, &additional_inputs.summoner_name) - .await - .unwrap(); + .await?; - let spectate_status = get_spectate_status(additional_inputs.region, &summoner.common.summoner_id) - .await - .unwrap(); + let spectate_status = + get_spectate_status(additional_inputs.region, &summoner.common.summoner_id).await?; let template_inputs = TemplateInputs { summoner, From 25ece35964be729534f159fff2692d2438cc3adb Mon Sep 17 00:00:00 2001 From: Gianluca Randazzo Date: Sat, 30 Sep 2023 15:28:32 +0200 Subject: [PATCH 10/18] Try to extend "dummy" error handling to TheGrind --- src/xddmod/src/handlers/the_grind/core.rs | 114 ++++++++++------------ src/xddmod/src/main.rs | 4 +- 2 files changed, 56 insertions(+), 62 deletions(-) diff --git a/src/xddmod/src/handlers/the_grind/core.rs b/src/xddmod/src/handlers/the_grind/core.rs index 4e5226a..f065542 100644 --- a/src/xddmod/src/handlers/the_grind/core.rs +++ b/src/xddmod/src/handlers/the_grind/core.rs @@ -4,95 +4,87 @@ use minijinja::Environment; use serde::Deserialize; use serde::Serialize; use sqlx::SqlitePool; +use twitch_irc::login::LoginCredentials; use twitch_irc::message::PrivmsgMessage; use twitch_irc::message::ServerMessage; +use twitch_irc::transport::Transport; +use twitch_irc::TwitchIRCClient; use crate::apis::op_gg; use crate::apis::op_gg::summoners::LpHistory; use crate::apis::op_gg::summoners::SummonerJson; use crate::apis::op_gg::Region; -use crate::auth::IRCClient; use crate::handlers::persistence::Handler; +use crate::handlers::persistence::PersistenceError; use crate::handlers::persistence::Reply; +use crate::handlers::HandlerError; +use crate::handlers::TwitchApiError; +use crate::handlers::TwitchError; use crate::poor_man_throttling; -pub struct TheGrind<'a> { - pub irc_client: IRCClient, +pub struct TheGrind<'a, T: Transport, L: LoginCredentials> { + pub irc_client: TwitchIRCClient, pub db_pool: SqlitePool, pub templates_env: Environment<'a>, } -impl<'a> TheGrind<'a> { +impl<'a, T: Transport, L: LoginCredentials> TheGrind<'a, T, L> { pub fn handler(&self) -> Handler { Handler::TheGrind } } -impl<'a> TheGrind<'a> { - pub async fn handle(&self, server_message: &ServerMessage) { +impl<'a, T: Transport, L: LoginCredentials> TheGrind<'a, T, L> { + pub async fn handle( + &self, + server_message: &ServerMessage, + ) -> Result<(), HandlerError> { if let ServerMessage::Privmsg(message @ PrivmsgMessage { is_action: false, .. }) = server_message { - match Reply::matching(self.handler(), message, &self.db_pool).await.as_slice() { - [reply @ Reply { - additional_inputs: Some(additional_inputs), - .. - }] => { - match poor_man_throttling::should_throttle(message, reply) { - Ok(false) => (), - Ok(true) => { - eprintln!( - "Skip reply: message {:?}, sender {:?}, reply {:?}", - message.message_text, message.sender, reply.template - ); - return; - } - Err(error) => { - eprintln!("Error throttling, error: {:?}", error); - } - } + let Some(first_matching) = Reply::first_matching(self.handler(), message, &self.db_pool).await? else { + return Ok(()); + }; - match serde_json::from_value::(additional_inputs.0.clone()) { - Ok(additional_inputs) => { - let summoner_json = op_gg::summoners::get_summoner_json( - additional_inputs.region, - &additional_inputs.summoner_name, - ) - .await - .unwrap(); + let Some(additional_inputs) = first_matching.reply.additional_inputs.as_ref() else { + return Err(PersistenceError::MissingAdditionalInputs { + reply: first_matching.reply.clone(), + } + .into()); + }; + + // FIXME: poor man throttling + if poor_man_throttling::should_throttle(message, &first_matching.reply)? { + return Ok(()); + } + + match serde_json::from_value::(additional_inputs.0.clone()) { + Ok(additional_inputs) => { + let summoner_json = + op_gg::summoners::get_summoner_json(additional_inputs.region, &additional_inputs.summoner_name) + .await?; - let template_inputs = TemplateInputs::from(summoner_json); + let template_inputs = TemplateInputs::from(summoner_json); - match reply - .render_template(&self.templates_env, Some(&Value::from_serializable(&template_inputs))) - { - Ok(rendered_reply) if rendered_reply.is_empty() => { - eprintln!("Rendered reply template empty: {:?}.", reply) - } - Ok(rendered_reply) => { - self.irc_client.say_in_reply_to(message, rendered_reply).await.unwrap() - } - Err(e) => eprintln!("Error rendering reply template, error: {:?}, {:?}.", reply, e), - } - } - Err(error) => eprintln!( - "Error deserializing AdditionalInputs from Reply for ServerMessage: {:?}, {:?}, {:?}.", - error, server_message, reply - ), - } + let rendered_reply = first_matching.reply.render_template::( + &self.templates_env, + Some(&Value::from_serializable(&template_inputs)), + )?; + + self.irc_client + .say_in_reply_to(message, rendered_reply) + .await + .map_err(TwitchError::from)?; + + if let Ok(error) = PersistenceError::try_from(first_matching) { + return Err(error.into()); + }; } - [reply @ Reply { - additional_inputs: None, - .. - }] => eprintln!( - "Reply for ServerMessage with missing AdditionalInputs: {:?}, {:?}.", - server_message, reply - ), - [] => {} - multiple_matching_replies => eprintln!( - "Multiple matching replies for message: {:?}, {:?}.", - multiple_matching_replies, server_message + Err(error) => eprintln!( + "Error deserializing AdditionalInputs from Reply for ServerMessage: {:?}, {:?}, {:?}.", + error, server_message, first_matching.reply ), } } + Ok(()) } } diff --git a/src/xddmod/src/main.rs b/src/xddmod/src/main.rs index c10123f..f29b27b 100644 --- a/src/xddmod/src/main.rs +++ b/src/xddmod/src/main.rs @@ -82,7 +82,9 @@ async fn main() { if let Err(e) = sniffa.handle::(&server_message).await { eprintln!("Sniffa error {:?}", e); }; - the_grind.handle(&server_message).await; + if let Err(e) = the_grind.handle::(&server_message).await { + eprintln!("TheGrind error {:?}", e); + }; }); } }) From 29b7d8384a251cecc4435e5698ebe788a711f070 Mon Sep 17 00:00:00 2001 From: Gianluca Randazzo Date: Sat, 30 Sep 2023 15:35:12 +0200 Subject: [PATCH 11/18] Strum all the things --- Cargo.lock | 21 +++++++++++++++++++++ src/xddmod/Cargo.toml | 2 ++ src/xddmod/src/handlers/persistence.rs | 2 +- src/xddmod/src/main.rs | 8 ++++---- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7a89a24..0737738 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2150,6 +2150,25 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +[[package]] +name = "strum" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125" + +[[package]] +name = "strum_macros" +version = "0.25.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad8d03b598d3d0fff69bf533ee3ef19b8eeb342729596df84bcc7e1f96ec4059" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "rustversion", + "syn 2.0.28", +] + [[package]] name = "syn" version = "1.0.109" @@ -2939,6 +2958,8 @@ dependencies = [ "serde", "serde_json", "sqlx", + "strum", + "strum_macros", "thiserror", "timeago", "tokio", diff --git a/src/xddmod/Cargo.toml b/src/xddmod/Cargo.toml index 0d2705d..c970eab 100644 --- a/src/xddmod/Cargo.toml +++ b/src/xddmod/Cargo.toml @@ -25,6 +25,8 @@ reqwest = { workspace = true } serde = { workspace = true } serde_json = { workspace = true } sqlx = { workspace = true } +strum = { version = "0.25" } +strum_macros = { version = "0.25" } timeago = "0.4.2" thiserror = { workspace = true } tokio = { workspace = true } diff --git a/src/xddmod/src/handlers/persistence.rs b/src/xddmod/src/handlers/persistence.rs index a0d8128..c1c7958 100644 --- a/src/xddmod/src/handlers/persistence.rs +++ b/src/xddmod/src/handlers/persistence.rs @@ -201,7 +201,7 @@ impl TryFrom for PersistenceError { } } -#[derive(Debug, Clone, Copy, Serialize, Deserialize, sqlx::Type)] +#[derive(Debug, Clone, Copy, Serialize, Deserialize, sqlx::Type, strum_macros::Display)] #[sqlx(type_name = "TEXT")] pub enum Handler { Gamba, diff --git a/src/xddmod/src/main.rs b/src/xddmod/src/main.rs index f29b27b..90fb7f3 100644 --- a/src/xddmod/src/main.rs +++ b/src/xddmod/src/main.rs @@ -74,16 +74,16 @@ async fn main() { return; } if let Err(e) = npc.handle::(&server_message).await { - eprintln!("Npc error {:?}", e); + eprintln!("{} error {:?}", npc.handler(), e); }; if let Err(e) = gg.handle::(&server_message).await { - eprintln!("Gg error {:?}", e); + eprintln!("{} error {:?}", gg.handler(), e); }; if let Err(e) = sniffa.handle::(&server_message).await { - eprintln!("Sniffa error {:?}", e); + eprintln!("{} error {:?}", sniffa.handler(), e); }; if let Err(e) = the_grind.handle::(&server_message).await { - eprintln!("TheGrind error {:?}", e); + eprintln!("{} error {:?}", the_grind.handler(), e); }; }); } From 8354c0a562246fc0a749bf0f9c03a75f9c2e5eb3 Mon Sep 17 00:00:00 2001 From: Gianluca Randazzo Date: Sat, 30 Sep 2023 15:39:48 +0200 Subject: [PATCH 12/18] Utc is utc --- src/xddmod/src/poor_man_throttling.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/xddmod/src/poor_man_throttling.rs b/src/xddmod/src/poor_man_throttling.rs index 6aed27e..45ed03a 100644 --- a/src/xddmod/src/poor_man_throttling.rs +++ b/src/xddmod/src/poor_man_throttling.rs @@ -1,3 +1,4 @@ +use sqlx::types::chrono::Utc; use twitch_irc::message::PrivmsgMessage; use crate::apis::twitch; @@ -22,13 +23,13 @@ pub fn should_throttle(message: &PrivmsgMessage, reply: &Reply) -> anyhow::Resul let throttling = throttle .get(&reply.id) .map(|last_reply_date_time| { - let time_passed = sqlx::types::chrono::Utc::now() - *last_reply_date_time; + let time_passed = Utc::now() - *last_reply_date_time; time_passed < chrono::Duration::seconds(20) }) .unwrap_or_default(); if !throttling { - throttle.insert(reply.id, sqlx::types::chrono::Utc::now()); + throttle.insert(reply.id, Utc::now()); } Ok(throttling) From 73c49e99f0c2bc200eb4a3154dc7cb6a1d6c7777 Mon Sep 17 00:00:00 2001 From: Gianluca Randazzo Date: Sun, 1 Oct 2023 05:48:54 +0200 Subject: [PATCH 13/18] Goodbye HandlerError...anyhow ftw --- src/xddmod/src/handlers.rs | 24 --------- src/xddmod/src/handlers/gg/core.rs | 63 +++++++++-------------- src/xddmod/src/handlers/npc/core.rs | 13 +---- src/xddmod/src/handlers/sniffa/core.rs | 52 ++++++------------- src/xddmod/src/handlers/the_grind/core.rs | 43 +++++----------- src/xddmod/src/main.rs | 8 +-- 6 files changed, 59 insertions(+), 144 deletions(-) diff --git a/src/xddmod/src/handlers.rs b/src/xddmod/src/handlers.rs index 2facb15..0d3aa81 100644 --- a/src/xddmod/src/handlers.rs +++ b/src/xddmod/src/handlers.rs @@ -10,27 +10,3 @@ pub mod the_grind; pub trait TwitchApiClient: twitch_api::HttpClient + twitch_api::twitch_oauth2::client::Client {} impl TwitchApiClient for T {} - -pub trait TwitchApiError: std::error::Error + Send + Sync + 'static {} - -impl TwitchApiError for T {} - -#[derive(thiserror::Error, Debug)] -pub enum HandlerError { - #[error(transparent)] - Persistence(#[from] persistence::PersistenceError), - #[error(transparent)] - Rendering(#[from] rendering::RenderingError), - #[error(transparent)] - Twitch(#[from] TwitchError), - #[error(transparent)] - Generic(#[from] anyhow::Error), -} - -#[derive(thiserror::Error, Debug)] -pub enum TwitchError { - #[error(transparent)] - Irc(#[from] twitch_irc::Error), - #[error(transparent)] - Api(#[from] twitch_api::helix::ClientRequestError), -} diff --git a/src/xddmod/src/handlers/gg/core.rs b/src/xddmod/src/handlers/gg/core.rs index d372708..54e801e 100644 --- a/src/xddmod/src/handlers/gg/core.rs +++ b/src/xddmod/src/handlers/gg/core.rs @@ -17,9 +17,6 @@ use crate::apis::op_gg::Region; use crate::handlers::persistence::Handler; use crate::handlers::persistence::PersistenceError; use crate::handlers::persistence::Reply; -use crate::handlers::HandlerError; -use crate::handlers::TwitchApiError; -use crate::handlers::TwitchError; use crate::poor_man_throttling; pub struct Gg<'a, T: Transport, L: LoginCredentials> { @@ -35,10 +32,7 @@ impl<'a, T: Transport, L: LoginCredentials> Gg<'a, T, L> { } impl<'a, T: Transport, L: LoginCredentials> Gg<'a, T, L> { - pub async fn handle( - &self, - server_message: &ServerMessage, - ) -> Result<(), HandlerError> { + pub async fn handle(&self, server_message: &ServerMessage) -> anyhow::Result<()> { if let ServerMessage::Privmsg(message @ PrivmsgMessage { is_action: false, .. }) = server_message { let Some(first_matching) = Reply::first_matching(self.handler(), message, &self.db_pool).await? else { return Ok(()); @@ -56,43 +50,32 @@ impl<'a, T: Transport, L: LoginCredentials> Gg<'a, T, L> { return Ok(()); } - match serde_json::from_value::(additional_inputs.0.clone()) { - Ok(additional_inputs) => { - let summoner = - op_gg::summoners::get_summoner(additional_inputs.region, &additional_inputs.summoner_name) - .await?; + let additional_inputs = serde_json::from_value::(additional_inputs.0.clone())?; - if let Some(game) = - op_gg::games::get_last_game(additional_inputs.region, &summoner.common.summoner_id).await? - { - let template_inputs = TemplateInputs { - champion: Champion::by_key(game.my_data.champion_key.into(), &self.db_pool) - .await - .unwrap(), - game, - }; + let summoner = + op_gg::summoners::get_summoner(additional_inputs.region, &additional_inputs.summoner_name).await?; - let rendered_reply = first_matching.reply.render_template::( - &self.templates_env, - Some(&Value::from_serializable(&template_inputs)), - )?; + if let Some(game) = + op_gg::games::get_last_game(additional_inputs.region, &summoner.common.summoner_id).await? + { + let template_inputs = TemplateInputs { + champion: Champion::by_key(game.my_data.champion_key.into(), &self.db_pool) + .await + .unwrap(), + game, + }; - self.irc_client - .say_in_reply_to(message, rendered_reply) - .await - .map_err(TwitchError::from)?; + let rendered_reply = first_matching + .reply + .render_template::(&self.templates_env, Some(&Value::from_serializable(&template_inputs)))?; - if let Ok(error) = PersistenceError::try_from(first_matching) { - return Err(error.into()); - }; - } else { - eprintln!("No games returned for reply: {:?}.", first_matching.reply) - } - } - Err(error) => eprintln!( - "Error deserializing AdditionalInputs from Reply for ServerMessage: {:?}, {:?}, {:?}.", - error, server_message, first_matching.reply - ), + self.irc_client.say_in_reply_to(message, rendered_reply).await?; + + if let Ok(error) = PersistenceError::try_from(first_matching) { + return Err(error.into()); + }; + } else { + eprintln!("No games returned for reply: {:?}.", first_matching.reply) } } Ok(()) diff --git a/src/xddmod/src/handlers/npc/core.rs b/src/xddmod/src/handlers/npc/core.rs index 1112a40..32bf447 100644 --- a/src/xddmod/src/handlers/npc/core.rs +++ b/src/xddmod/src/handlers/npc/core.rs @@ -10,9 +10,6 @@ use twitch_irc::TwitchIRCClient; use crate::handlers::persistence::Handler; use crate::handlers::persistence::PersistenceError; use crate::handlers::persistence::Reply; -use crate::handlers::HandlerError; -use crate::handlers::TwitchApiError; -use crate::handlers::TwitchError; use crate::poor_man_throttling; pub struct Npc<'a, T: Transport, L: LoginCredentials> { @@ -28,10 +25,7 @@ impl<'a, T: Transport, L: LoginCredentials> Npc<'a, T, L> { } impl<'a, T: Transport, L: LoginCredentials> Npc<'a, T, L> { - pub async fn handle( - &self, - server_message: &ServerMessage, - ) -> Result<(), HandlerError> { + pub async fn handle(&self, server_message: &ServerMessage) -> anyhow::Result<()> { if let ServerMessage::Privmsg(message @ PrivmsgMessage { is_action: false, .. }) = server_message { let Some(first_matching) = Reply::first_matching(self.handler(), message, &self.db_pool).await? else { return Ok(()); @@ -46,10 +40,7 @@ impl<'a, T: Transport, L: LoginCredentials> Npc<'a, T, L> { .reply .render_template::(&self.templates_env, None)?; - self.irc_client - .say_in_reply_to(message, rendered_reply) - .await - .map_err(TwitchError::from)?; + self.irc_client.say_in_reply_to(message, rendered_reply).await?; if let Ok(error) = PersistenceError::try_from(first_matching) { return Err(error.into()); diff --git a/src/xddmod/src/handlers/sniffa/core.rs b/src/xddmod/src/handlers/sniffa/core.rs index 76a6478..2e15469 100644 --- a/src/xddmod/src/handlers/sniffa/core.rs +++ b/src/xddmod/src/handlers/sniffa/core.rs @@ -18,9 +18,6 @@ use crate::apis::op_gg::Region; use crate::handlers::persistence::Handler; use crate::handlers::persistence::PersistenceError; use crate::handlers::persistence::Reply; -use crate::handlers::HandlerError; -use crate::handlers::TwitchApiError; -use crate::handlers::TwitchError; use crate::poor_man_throttling; pub struct Sniffa<'a, T: Transport, L: LoginCredentials> { @@ -36,10 +33,7 @@ impl<'a, T: Transport, L: LoginCredentials> Sniffa<'a, T, L> { } impl<'a, T: Transport, L: LoginCredentials> Sniffa<'a, T, L> { - pub async fn handle( - &self, - server_message: &ServerMessage, - ) -> Result<(), HandlerError> { + pub async fn handle(&self, server_message: &ServerMessage) -> anyhow::Result<()> { if let ServerMessage::Privmsg(message @ PrivmsgMessage { is_action: false, .. }) = server_message { let Some(first_matching) = Reply::first_matching(self.handler(), message, &self.db_pool).await? else { return Ok(()); @@ -57,39 +51,27 @@ impl<'a, T: Transport, L: LoginCredentials> Sniffa<'a, T, L> { return Ok(()); } - match serde_json::from_value::(additional_inputs.0.clone()) { - Ok(additional_inputs) => { - let summoner = - op_gg::summoners::get_summoner(additional_inputs.region, &additional_inputs.summoner_name) - .await?; + let additional_inputs = serde_json::from_value::(additional_inputs.0.clone())?; - let spectate_status = - get_spectate_status(additional_inputs.region, &summoner.common.summoner_id).await?; + let summoner = + op_gg::summoners::get_summoner(additional_inputs.region, &additional_inputs.summoner_name).await?; - let template_inputs = TemplateInputs { - summoner, - spectate_status, - }; + let spectate_status = get_spectate_status(additional_inputs.region, &summoner.common.summoner_id).await?; - let rendered_reply = first_matching.reply.render_template::( - &self.templates_env, - Some(&Value::from_serializable(&template_inputs)), - )?; + let template_inputs = TemplateInputs { + summoner, + spectate_status, + }; - self.irc_client - .say_in_reply_to(message, rendered_reply) - .await - .map_err(TwitchError::from)?; + let rendered_reply = first_matching + .reply + .render_template::(&self.templates_env, Some(&Value::from_serializable(&template_inputs)))?; - if let Ok(error) = PersistenceError::try_from(first_matching) { - return Err(error.into()); - }; - } - Err(error) => eprintln!( - "Error deserializing AdditionalInputs from Reply for ServerMessage: {:?}, {:?}, {:?}.", - error, server_message, first_matching.reply - ), - } + self.irc_client.say_in_reply_to(message, rendered_reply).await?; + + if let Ok(error) = PersistenceError::try_from(first_matching) { + return Err(error.into()); + }; } Ok(()) } diff --git a/src/xddmod/src/handlers/the_grind/core.rs b/src/xddmod/src/handlers/the_grind/core.rs index f065542..532bcfe 100644 --- a/src/xddmod/src/handlers/the_grind/core.rs +++ b/src/xddmod/src/handlers/the_grind/core.rs @@ -17,9 +17,6 @@ use crate::apis::op_gg::Region; use crate::handlers::persistence::Handler; use crate::handlers::persistence::PersistenceError; use crate::handlers::persistence::Reply; -use crate::handlers::HandlerError; -use crate::handlers::TwitchApiError; -use crate::handlers::TwitchError; use crate::poor_man_throttling; pub struct TheGrind<'a, T: Transport, L: LoginCredentials> { @@ -35,10 +32,7 @@ impl<'a, T: Transport, L: LoginCredentials> TheGrind<'a, T, L> { } impl<'a, T: Transport, L: LoginCredentials> TheGrind<'a, T, L> { - pub async fn handle( - &self, - server_message: &ServerMessage, - ) -> Result<(), HandlerError> { + pub async fn handle(&self, server_message: &ServerMessage) -> anyhow::Result<()> { if let ServerMessage::Privmsg(message @ PrivmsgMessage { is_action: false, .. }) = server_message { let Some(first_matching) = Reply::first_matching(self.handler(), message, &self.db_pool).await? else { return Ok(()); @@ -56,33 +50,22 @@ impl<'a, T: Transport, L: LoginCredentials> TheGrind<'a, T, L> { return Ok(()); } - match serde_json::from_value::(additional_inputs.0.clone()) { - Ok(additional_inputs) => { - let summoner_json = - op_gg::summoners::get_summoner_json(additional_inputs.region, &additional_inputs.summoner_name) - .await?; + let additional_inputs = serde_json::from_value::(additional_inputs.0.clone())?; - let template_inputs = TemplateInputs::from(summoner_json); + let summoner_json = + op_gg::summoners::get_summoner_json(additional_inputs.region, &additional_inputs.summoner_name).await?; - let rendered_reply = first_matching.reply.render_template::( - &self.templates_env, - Some(&Value::from_serializable(&template_inputs)), - )?; + let template_inputs = TemplateInputs::from(summoner_json); - self.irc_client - .say_in_reply_to(message, rendered_reply) - .await - .map_err(TwitchError::from)?; + let rendered_reply = first_matching + .reply + .render_template::(&self.templates_env, Some(&Value::from_serializable(&template_inputs)))?; - if let Ok(error) = PersistenceError::try_from(first_matching) { - return Err(error.into()); - }; - } - Err(error) => eprintln!( - "Error deserializing AdditionalInputs from Reply for ServerMessage: {:?}, {:?}, {:?}.", - error, server_message, first_matching.reply - ), - } + self.irc_client.say_in_reply_to(message, rendered_reply).await?; + + if let Ok(error) = PersistenceError::try_from(first_matching) { + return Err(error.into()); + }; } Ok(()) } diff --git a/src/xddmod/src/main.rs b/src/xddmod/src/main.rs index 90fb7f3..839719d 100644 --- a/src/xddmod/src/main.rs +++ b/src/xddmod/src/main.rs @@ -73,16 +73,16 @@ async fn main() { if let Ok(true) = rip_bozo_g.handle(&server_message).await { return; } - if let Err(e) = npc.handle::(&server_message).await { + if let Err(e) = npc.handle(&server_message).await { eprintln!("{} error {:?}", npc.handler(), e); }; - if let Err(e) = gg.handle::(&server_message).await { + if let Err(e) = gg.handle(&server_message).await { eprintln!("{} error {:?}", gg.handler(), e); }; - if let Err(e) = sniffa.handle::(&server_message).await { + if let Err(e) = sniffa.handle(&server_message).await { eprintln!("{} error {:?}", sniffa.handler(), e); }; - if let Err(e) = the_grind.handle::(&server_message).await { + if let Err(e) = the_grind.handle(&server_message).await { eprintln!("{} error {:?}", the_grind.handler(), e); }; }); From 6a51aa83fa8abc7c1b07901fbc399aff27dc62fb Mon Sep 17 00:00:00 2001 From: Gianluca Randazzo Date: Sun, 1 Oct 2023 06:03:57 +0200 Subject: [PATCH 14/18] rip_bozo_g? --- src/xddmod/src/main.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/xddmod/src/main.rs b/src/xddmod/src/main.rs index 839719d..48885dd 100644 --- a/src/xddmod/src/main.rs +++ b/src/xddmod/src/main.rs @@ -69,8 +69,8 @@ async fn main() { let the_grind = the_grind.clone(); tokio::spawn(async move { - let mut rip_bozo_g = rip_bozo.lock().await; - if let Ok(true) = rip_bozo_g.handle(&server_message).await { + let mut rip_bozo_guard = rip_bozo.lock().await; + if let Ok(true) = rip_bozo_guard.handle(&server_message).await { return; } if let Err(e) = npc.handle(&server_message).await { From 6d4d3eb2b5f7242db4a942490e57f43f88fbd77e Mon Sep 17 00:00:00 2001 From: Gianluca Randazzo Date: Sun, 1 Oct 2023 06:10:48 +0200 Subject: [PATCH 15/18] Moar anyhow! --- src/xddmod/src/main.rs | 26 ++++++++++++++++-------- src/xtask/src/import_ddragon_champion.rs | 10 ++++----- 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/src/xddmod/src/main.rs b/src/xddmod/src/main.rs index 48885dd..be360db 100644 --- a/src/xddmod/src/main.rs +++ b/src/xddmod/src/main.rs @@ -12,11 +12,13 @@ use xddmod::handlers::sniffa::core::Sniffa; use xddmod::handlers::the_grind::core::TheGrind; #[tokio::main] -async fn main() { +async fn main() -> anyhow::Result<()> { let app_config = AppConfig::init(); - let channel = std::env::args().nth(1).unwrap(); + let channel = std::env::args() + .nth(1) + .ok_or_else(|| anyhow::anyhow!("missing 1st CLI arg, `channel`"))?; - let db_pool = SqlitePool::connect(app_config.db_url.as_ref()).await.unwrap(); + let db_pool = SqlitePool::connect(app_config.db_url.as_ref()).await?; let (mut incoming_messages, irc_client, user_token) = auth::authenticate(app_config.clone()).await; @@ -24,11 +26,16 @@ async fn main() { let broadcaster = helix_client .get_user_from_login(&channel, &user_token) - .await - .unwrap() - .unwrap(); + .await? + .ok_or_else(|| { + anyhow::anyhow!( + "no broacaster found for `channel` {} with `user_token` {:?}", + channel, + user_token + ) + })?; - irc_client.join(channel).unwrap(); + irc_client.join(channel)?; let templates_env = xddmod::templates_env::build_global_templates_env(); @@ -88,6 +95,7 @@ async fn main() { }); } }) - .await - .unwrap(); + .await?; + + Ok(()) } diff --git a/src/xtask/src/import_ddragon_champion.rs b/src/xtask/src/import_ddragon_champion.rs index fdf8c01..fd1a239 100644 --- a/src/xtask/src/import_ddragon_champion.rs +++ b/src/xtask/src/import_ddragon_champion.rs @@ -19,19 +19,19 @@ pub struct ImportDdragonChampion { impl ImportDdragonChampion { pub async fn run(self) -> anyhow::Result<()> { - let db_pool = SqlitePool::connect(self.db_url.as_ref()).await.unwrap(); + let db_pool = SqlitePool::connect(self.db_url.as_ref()).await?; let api_response: ApiResponse = reqwest::get(format!("{}/champion.json", self.ddragon_api_base_url)) .await? .json() .await?; - let mut tx = db_pool.begin().await.unwrap(); - Champion::truncate(&mut tx).await.unwrap(); + let mut tx = db_pool.begin().await?; + Champion::truncate(&mut tx).await?; for champion in api_response.data.into_values() { - champion.insert(&mut tx).await.unwrap(); + champion.insert(&mut tx).await?; } - tx.commit().await.unwrap(); + tx.commit().await?; Ok(()) } From 671188f3d7af2ad9bcab432810fb55124a392e93 Mon Sep 17 00:00:00 2001 From: Gianluca Randazzo Date: Sun, 1 Oct 2023 06:28:22 +0200 Subject: [PATCH 16/18] anyhow GambaTime --- src/xddmod/src/handlers/gamba_time/core.rs | 89 +++++++++------------- src/xddmod/src/main.rs | 5 +- src/xddmod/src/poor_man_throttling.rs | 3 +- 3 files changed, 43 insertions(+), 54 deletions(-) diff --git a/src/xddmod/src/handlers/gamba_time/core.rs b/src/xddmod/src/handlers/gamba_time/core.rs index 08ef6c5..ad7d00e 100644 --- a/src/xddmod/src/handlers/gamba_time/core.rs +++ b/src/xddmod/src/handlers/gamba_time/core.rs @@ -19,76 +19,63 @@ use twitch_api::types::PredictionOutcome; use twitch_api::types::PredictionStatus; use twitch_api::types::UserId; use twitch_api::HelixClient; +use twitch_irc::login::LoginCredentials; use twitch_irc::message::PrivmsgMessage; use twitch_irc::message::ServerMessage; +use twitch_irc::transport::Transport; +use twitch_irc::TwitchIRCClient; -use crate::auth::IRCClient; use crate::handlers::persistence::Handler; use crate::handlers::persistence::Reply; +use crate::handlers::TwitchApiClient; -pub struct GambaTime<'a> { +pub struct GambaTime<'a, C: TwitchApiClient, T: Transport, L: LoginCredentials> { pub token: UserToken, pub broadcaster_id: UserId, - pub helix_client: HelixClient<'a, reqwest::Client>, - pub irc_client: IRCClient, + pub helix_client: HelixClient<'a, C>, + pub irc_client: TwitchIRCClient, pub db_pool: SqlitePool, pub templates_env: Environment<'a>, } -impl<'a> GambaTime<'a> { +impl<'a, C: TwitchApiClient, T: Transport, L: LoginCredentials> GambaTime<'a, C, T, L> { pub fn handler(&self) -> Handler { Handler::Gamba } } -impl<'a> GambaTime<'a> { - pub async fn handle(&self, server_message: &ServerMessage) { +impl<'a, C: TwitchApiClient, T: Transport, L: LoginCredentials> GambaTime<'a, C, T, L> { + pub async fn handle(&self, server_message: &ServerMessage) -> anyhow::Result<()> { if let ServerMessage::Privmsg(message @ PrivmsgMessage { is_action: false, .. }) = server_message { - match Reply::matching(self.handler(), message, &self.db_pool).await.as_slice() { - [reply] => { - let prediction_request = GetPredictionsRequest::builder() - .broadcaster_id(self.broadcaster_id.clone()) - .first(Some(1)) - .build(); - - let predictions: Vec = self - .helix_client - .req_get(prediction_request.clone(), &self.token) - .await - .unwrap() - .data; - - match predictions.first() { - Some(prediction) => match Gamba::try_from(prediction.clone()) { - Ok(gamba) => { - match reply.render_template( - &self.templates_env, - Some(&minijinja::value::Value::from_serializable(&gamba)), - ) { - Ok(rendered_reply) if rendered_reply.is_empty() => { - eprintln!("Rendered reply template empty: {:?}.", reply) - } - Ok(rendered_reply) => { - self.irc_client.say_in_reply_to(message, rendered_reply).await.unwrap() - } - Err(e) => eprintln!("Error rendering reply template, error: {:?}, {:?}.", reply, e), - } - } - Err(e) => eprintln!( - "Error building GambaData for Prediction {:?}, error: {:?}.", - prediction, e - ), - }, - None => eprintln!("No Predictions found for request {:?}.", prediction_request), - } - } - [] => {} - multiple_matching_replies => eprintln!( - "Multiple matching replies for message: {:?}, {:?}.", - multiple_matching_replies, server_message - ), - } + let Some(first_matching) = Reply::first_matching(self.handler(), message, &self.db_pool).await? else { + return Ok(()); + }; + + let prediction_request = GetPredictionsRequest::builder() + .broadcaster_id(self.broadcaster_id.clone()) + .first(Some(1)) + .build(); + + let predictions: Vec = self + .helix_client + .req_get(prediction_request.clone(), &self.token) + .await? + .data; + + let prediction = predictions + .first() + .ok_or_else(|| anyhow!("no prediction found for request {:?}", prediction_request))?; + + let gamba = Gamba::try_from(prediction.clone())?; + + let rendered_reply = first_matching.reply.render_template( + &self.templates_env, + Some(&minijinja::value::Value::from_serializable(&gamba)), + )?; + + self.irc_client.say_in_reply_to(message, rendered_reply).await?; } + Ok(()) } } diff --git a/src/xddmod/src/main.rs b/src/xddmod/src/main.rs index be360db..ac0316c 100644 --- a/src/xddmod/src/main.rs +++ b/src/xddmod/src/main.rs @@ -1,5 +1,6 @@ use std::sync::Arc; +use anyhow::anyhow; use sqlx::SqlitePool; use tokio::sync::Mutex; use twitch_api::HelixClient; @@ -16,7 +17,7 @@ async fn main() -> anyhow::Result<()> { let app_config = AppConfig::init(); let channel = std::env::args() .nth(1) - .ok_or_else(|| anyhow::anyhow!("missing 1st CLI arg, `channel`"))?; + .ok_or_else(|| anyhow!("missing 1st CLI arg, `channel`"))?; let db_pool = SqlitePool::connect(app_config.db_url.as_ref()).await?; @@ -28,7 +29,7 @@ async fn main() -> anyhow::Result<()> { .get_user_from_login(&channel, &user_token) .await? .ok_or_else(|| { - anyhow::anyhow!( + anyhow!( "no broacaster found for `channel` {} with `user_token` {:?}", channel, user_token diff --git a/src/xddmod/src/poor_man_throttling.rs b/src/xddmod/src/poor_man_throttling.rs index 45ed03a..731450f 100644 --- a/src/xddmod/src/poor_man_throttling.rs +++ b/src/xddmod/src/poor_man_throttling.rs @@ -1,3 +1,4 @@ +use anyhow::anyhow; use sqlx::types::chrono::Utc; use twitch_irc::message::PrivmsgMessage; @@ -18,7 +19,7 @@ pub fn should_throttle(message: &PrivmsgMessage, reply: &Reply) -> anyhow::Resul let mut throttle = THROTTLE .lock() - .map_err(|error| anyhow::anyhow!("Cannot get THROTTLE Lock, error: {:?}", error))?; + .map_err(|error| anyhow!("Cannot get THROTTLE Lock, error: {:?}", error))?; let throttling = throttle .get(&reply.id) From fa211ecefec75a13a721b709a9b4a0e5d41ff9c4 Mon Sep 17 00:00:00 2001 From: Gianluca Randazzo Date: Sun, 1 Oct 2023 11:39:32 +0200 Subject: [PATCH 17/18] =?UTF-8?q?THIS=20IS=20AWESOME=20=E2=9D=A4=EF=B8=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .cargo/config.toml | 2 + Cargo.lock | 265 ++++++++++++++++++++++++++++++++++++++++- Cargo.toml | 2 +- src/xddmod/Cargo.toml | 1 + src/xddmod/src/main.rs | 2 + 5 files changed, 268 insertions(+), 4 deletions(-) create mode 100644 .cargo/config.toml diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 0000000..bff29e6 --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,2 @@ +[build] +rustflags = ["--cfg", "tokio_unstable"] diff --git a/Cargo.lock b/Cargo.lock index 0737738..d2bf50f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -160,6 +160,28 @@ dependencies = [ "syn 2.0.28", ] +[[package]] +name = "async-stream" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51" +dependencies = [ + "async-stream-impl", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-stream-impl" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.28", +] + [[package]] name = "async-trait" version = "0.1.68" @@ -435,6 +457,43 @@ dependencies = [ "yaml-rust", ] +[[package]] +name = "console-api" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd326812b3fd01da5bb1af7d340d0d555fd3d4b641e7f1dfcf5962a902952787" +dependencies = [ + "futures-core", + "prost", + "prost-types", + "tonic", + "tracing-core", +] + +[[package]] +name = "console-subscriber" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7481d4c57092cd1c19dd541b92bdce883de840df30aa5d03fd48a3935c01842e" +dependencies = [ + "console-api", + "crossbeam-channel", + "crossbeam-utils", + "futures-task", + "hdrhistogram", + "humantime", + "prost-types", + "serde", + "serde_json", + "thread_local", + "tokio", + "tokio-stream", + "tonic", + "tracing", + "tracing-core", + "tracing-subscriber", +] + [[package]] name = "core-foundation" version = "0.9.3" @@ -475,6 +534,25 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9cace84e55f07e7301bae1c519df89cdad8cc3cd868413d3fdbdeca9ff3db484" +[[package]] +name = "crc32fast" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + [[package]] name = "crossbeam-queue" version = "0.3.8" @@ -726,6 +804,16 @@ dependencies = [ "instant", ] +[[package]] +name = "flate2" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6c98ee8095e9d1dcbf2fcc6d95acccb90d1c81db1e44725c6a984b1dbdfb010" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + [[package]] name = "flume" version = "0.10.14" @@ -932,6 +1020,19 @@ dependencies = [ "hashbrown", ] +[[package]] +name = "hdrhistogram" +version = "7.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f19b9f54f7c7f55e31401bb647626ce0cf0f67b0004982ce815b3ee72a02aa8" +dependencies = [ + "base64 0.13.1", + "byteorder", + "flate2", + "nom", + "num-traits", +] + [[package]] name = "heck" version = "0.4.1" @@ -1005,6 +1106,12 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + [[package]] name = "hyper" version = "0.14.27" @@ -1029,6 +1136,18 @@ dependencies = [ "want", ] +[[package]] +name = "hyper-timeout" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbb958482e8c7be4bc3cf272a766a2b0bf1a6755e7a6ae777f017a31d11b13b1" +dependencies = [ + "hyper", + "pin-project-lite", + "tokio", + "tokio-io-timeout", +] + [[package]] name = "hyper-tls" version = "0.5.0" @@ -1256,6 +1375,15 @@ dependencies = [ "libc", ] +[[package]] +name = "matchers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +dependencies = [ + "regex-automata 0.1.10", +] + [[package]] name = "matchit" version = "0.7.0" @@ -1675,6 +1803,38 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "prost" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4fdd22f3b9c31b53c060df4a0613a1c7f062d4115a2b984dd15b1858f7e340d" +dependencies = [ + "bytes", + "prost-derive", +] + +[[package]] +name = "prost-derive" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "265baba7fabd416cf5078179f7d2cbeca4ce7a9041111900675ea7c4cb8a4c32" +dependencies = [ + "anyhow", + "itertools", + "proc-macro2", + "quote", + "syn 2.0.28", +] + +[[package]] +name = "prost-types" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e081b29f63d83a4bc75cfc9f3fe424f9156cf92d8a4f0c9407cce9a1b67327cf" +dependencies = [ + "prost", +] + [[package]] name = "quote" version = "1.0.29" @@ -1746,8 +1906,17 @@ checksum = "697061221ea1b4a94a624f67d0ae2bfe4e22b8a17b6a192afb11046542cc8c47" dependencies = [ "aho-corasick", "memchr", - "regex-automata", - "regex-syntax", + "regex-automata 0.3.8", + "regex-syntax 0.7.5", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax 0.6.29", ] [[package]] @@ -1758,9 +1927,15 @@ checksum = "c2f401f4955220693b56f8ec66ee9c78abffd8d1c4f23dc41a23839eb88f0795" dependencies = [ "aho-corasick", "memchr", - "regex-syntax", + "regex-syntax 0.7.5", ] +[[package]] +name = "regex-syntax" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + [[package]] name = "regex-syntax" version = "0.7.5" @@ -1973,6 +2148,15 @@ dependencies = [ "digest", ] +[[package]] +name = "sharded-slab" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1b21f559e07218024e7e9f90f96f601825397de0e25420135f7f952453fed0b" +dependencies = [ + "lazy_static", +] + [[package]] name = "signal-hook-registry" version = "1.4.1" @@ -2239,6 +2423,16 @@ dependencies = [ "syn 2.0.28", ] +[[package]] +name = "thread_local" +version = "1.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" +dependencies = [ + "cfg-if", + "once_cell", +] + [[package]] name = "time" version = "0.3.20" @@ -2307,9 +2501,20 @@ dependencies = [ "signal-hook-registry", "socket2 0.5.3", "tokio-macros", + "tracing", "windows-sys 0.48.0", ] +[[package]] +name = "tokio-io-timeout" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30b74022ada614a1b4834de765f9bb43877f910cc8ce4be40e89042c9223a8bf" +dependencies = [ + "pin-project-lite", + "tokio", +] + [[package]] name = "tokio-macros" version = "2.1.0" @@ -2365,6 +2570,33 @@ dependencies = [ "serde", ] +[[package]] +name = "tonic" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d560933a0de61cf715926b9cac824d4c883c2c43142f787595e48280c40a1d0e" +dependencies = [ + "async-stream", + "async-trait", + "axum", + "base64 0.21.4", + "bytes", + "h2", + "http", + "http-body", + "hyper", + "hyper-timeout", + "percent-encoding", + "pin-project", + "prost", + "tokio", + "tokio-stream", + "tower", + "tower-layer", + "tower-service", + "tracing", +] + [[package]] name = "tower" version = "0.4.13" @@ -2373,9 +2605,13 @@ checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" dependencies = [ "futures-core", "futures-util", + "indexmap", "pin-project", "pin-project-lite", + "rand", + "slab", "tokio", + "tokio-util", "tower-layer", "tower-service", "tracing", @@ -2424,6 +2660,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" dependencies = [ "once_cell", + "valuable", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30a651bc37f915e81f087d86e62a18eec5f79550c7faff886f7090b4ea757c77" +dependencies = [ + "matchers", + "once_cell", + "regex", + "sharded-slab", + "thread_local", + "tracing", + "tracing-core", ] [[package]] @@ -2604,6 +2856,12 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4dad5567ad0cf5b760e5665964bec1b47dfd077ba8a2544b513f3556d3d239a2" +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + [[package]] name = "vcpkg" version = "0.2.15" @@ -2948,6 +3206,7 @@ dependencies = [ "chrono", "chrono-tz", "config", + "console-subscriber", "fake", "hyper", "lazy_static", diff --git a/Cargo.toml b/Cargo.toml index 3aa4646..1191454 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,6 +23,6 @@ sqlx = { version = "0.6.3", features = [ "migrate", ] } thiserror = { version = "1.0.48" } -tokio = { version = "1.32.0", features = ["full"] } +tokio = { version = "1.32.0", features = ["full", "tracing"] } url = { version = "2.4.1", features = ["serde"] } vec1 = { version = "1.10.1" } diff --git a/src/xddmod/Cargo.toml b/src/xddmod/Cargo.toml index c970eab..307fd3c 100644 --- a/src/xddmod/Cargo.toml +++ b/src/xddmod/Cargo.toml @@ -15,6 +15,7 @@ axum = "0.6.20" chrono = { workspace = true } chrono-tz = "0.8.3" config = "0.13.3" +console-subscriber = { version = "0.2.0" } fake = { workspace = true } hyper = { version = "0.14.27", features = ["full"] } lazy_static = { workspace = true } diff --git a/src/xddmod/src/main.rs b/src/xddmod/src/main.rs index ac0316c..71868fb 100644 --- a/src/xddmod/src/main.rs +++ b/src/xddmod/src/main.rs @@ -14,6 +14,8 @@ use xddmod::handlers::the_grind::core::TheGrind; #[tokio::main] async fn main() -> anyhow::Result<()> { + console_subscriber::init(); + let app_config = AppConfig::init(); let channel = std::env::args() .nth(1) From 083a27a13f4ff2a221aebec141043eb001b5ac8f Mon Sep 17 00:00:00 2001 From: Gianluca Randazzo Date: Sun, 8 Oct 2023 16:00:49 +0200 Subject: [PATCH 18/18] Up dankcontent --- src/dankcontent | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dankcontent b/src/dankcontent index 3dfb2e0..1635556 160000 --- a/src/dankcontent +++ b/src/dankcontent @@ -1 +1 @@ -Subproject commit 3dfb2e078ad885c09b6a1762d0336de6b72bb2b7 +Subproject commit 16355562a3453c0a62b0794dee8a7275370ba3a2