diff --git a/src/logging.rs b/src/logging.rs new file mode 100644 index 0000000000000000000000000000000000000000..72a85127fc105475aa10e32082aa8bce6e248104 --- /dev/null +++ b/src/logging.rs @@ -0,0 +1,70 @@ +use core::fmt::Display; +use serenity::{ + builder::EditMember, + http::{client::Http, CacheHttp}, + model::{ + channel::{Message, Reaction, ReactionType}, + guild::Member, + id::{ChannelId, RoleId}, + }, +}; + +pub trait LoggingReaction { + fn try_delete(&self, cache_http: impl CacheHttp); +} + +impl LoggingReaction for Reaction { + fn try_delete(&self, cache_http: impl CacheHttp) { + self.delete(cache_http) + .unwrap_or_else(|e| error!("Unable to delete react: {:?}", e)); + } +} + +pub trait LoggingMessage { + fn try_react<R: Into<ReactionType>>(&self, cache_http: impl CacheHttp, reaction_type: R); + fn try_delete(&self, cache_http: impl CacheHttp); +} + +impl LoggingMessage for Message { + fn try_react<R: Into<ReactionType>>(&self, cache_http: impl CacheHttp, reaction_type: R) { + self.react(cache_http, reaction_type) + .unwrap_or_else(|e| error!("Unable to add reaction, {:?}", e)); + } + fn try_delete(&self, cache_http: impl CacheHttp) { + self.delete(cache_http) + .unwrap_or_else(|e| error!("Unable to delete message: {:?}", e)); + } +} + +pub trait LoggingChannelId { + fn try_say(self, http: impl AsRef<Http>, content: impl Display); +} + +impl LoggingChannelId for ChannelId { + fn try_say(self, http: impl AsRef<Http>, content: impl Display) { + if let Err(e) = self.say(http, content) { + error!("Error sending message {:?}", e) + } + } +} + +pub trait LoggingMember { + fn try_edit<F: FnOnce(&mut EditMember) -> &mut EditMember>(&self, http: impl AsRef<Http>, f: F); + fn try_remove_role<R: Into<RoleId>>(&mut self, http: impl AsRef<Http>, role_id: R); +} + +impl LoggingMember for Member { + fn try_edit<F: FnOnce(&mut EditMember) -> &mut EditMember>( + &self, + http: impl AsRef<Http>, + f: F, + ) { + self.edit(http, f) + .unwrap_or_else(|e| error!("Unable to edit member: {:?}", e)); + } + fn try_remove_role<R: Into<RoleId>>(&mut self, http: impl AsRef<Http>, role_id: R) { + self.remove_role(http, role_id) + .unwrap_or_else(|e| error!("Unable to remove role: {:?}", e)); + } +} + diff --git a/src/main.rs b/src/main.rs index 2207e5ffd6f9b8e360898d2db279bae27000f3d4..67da86fc226b2b73668cc8c05fd04c595715536b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -18,6 +18,7 @@ use serenity::{ }; mod config; +mod logging; mod reaction_roles; mod token_management; mod user_management; @@ -25,18 +26,10 @@ mod util; mod voting; use config::CONFIG; +use logging::*; use reaction_roles::{add_role_by_reaction, remove_role_by_reaction}; use util::get_string_from_react; -macro_rules! e { - ($error: literal, $x:expr) => { - match $x { - Ok(_) => (), - Err(why) => error!($error, why), - } - }; -} - struct Handler; impl EventHandler for Handler { @@ -69,12 +62,9 @@ impl EventHandler for Handler { voting::Commands::cowsay(ctx, msg.clone(), message_content[1]); } "logreact" => { - e!("Error deleting logreact prompt: {:?}", msg.delete(&ctx)); - e!( - "Error sending message {:?}", - msg.channel_id - .say(&ctx.http, "React to this to log the ID (for the next 5min)") - ) + msg.try_delete(&ctx); + msg.channel_id + .try_say(&ctx.http, "React to this to log the ID (for the next 5min)") } "help" => { let mut message = MessageBuilder::new(); @@ -86,20 +76,12 @@ impl EventHandler for Handler { "Use {}poll <proposal> to see what people think about something", &CONFIG.command_prefix )); - e!( - "Error sending message: {:?}", - msg.channel_id.say(&ctx.http, message.build()) - ); - } - _ => { - e!( - "Error sending message: {:?}", - msg.channel_id.say( - &ctx.http, - format!("Unrecognised command. Try {}help", &CONFIG.command_prefix) - ) - ); + msg.channel_id.try_say(&ctx.http, message.build()) } + _ => msg.channel_id.try_say( + &ctx.http, + format!("Unrecognised command. Try {}help", &CONFIG.command_prefix), + ), } } @@ -141,10 +123,7 @@ impl EventHandler for Handler { add_reaction.emoji, )); msg.push_mono(react_as_string); - e!( - "Error sending message: {:?}", - message.channel_id.say(&ctx.http, msg.build()) - ); + message.channel_id.try_say(&ctx.http, msg.build()) } _ => {} } diff --git a/src/reaction_roles.rs b/src/reaction_roles.rs index 3c4ba9e91c46c7aee1102c645b269f6482239a38..d498ee8bf671414d6d4e7c10ca37c77032672f65 100644 --- a/src/reaction_roles.rs +++ b/src/reaction_roles.rs @@ -1,4 +1,5 @@ use crate::config::{ReactRoleMap, CONFIG}; +use crate::logging::*; use crate::util::{get_react_from_string, get_string_from_react}; use rayon::prelude::*; use serenity::{ @@ -8,15 +9,6 @@ use serenity::{ use std::collections::{HashMap, HashSet}; use std::iter::FromIterator; -macro_rules! e { - ($error: literal, $x:expr) => { - match $x { - Ok(_) => (), - Err(why) => error!($error, why), - } - }; -} - pub fn add_role_by_reaction(ctx: &Context, msg: Message, added_reaction: Reaction) { let user = added_reaction .user_id @@ -48,7 +40,7 @@ pub fn add_role_by_reaction(ctx: &Context, msg: Message, added_reaction: Reactio .ok(); } else { warn!("{} provided invalid react for role", user.name); - e!("Unable to delete react: {:?}", added_reaction.delete(ctx)); + added_reaction.try_delete(ctx); } } @@ -131,10 +123,7 @@ pub fn sync_all_role_reactions(ctx: &Context) { // ensure bot has reacted if !reactor_ids.contains(&UserId::from(CONFIG.bot_id)) { - e!( - "Unable to add reaction, {:?}", - message.react(ctx, reaction_type) - ); + message.try_react(ctx, reaction_type); } for member in all_members.clone() { diff --git a/src/user_management.rs b/src/user_management.rs index 68f6aa94e4fe826ec08982372be5e2932aeb18a0..826fc287411c7d6aa1ee43e231568a4e2ce18ce9 100644 --- a/src/user_management.rs +++ b/src/user_management.rs @@ -6,6 +6,7 @@ use serenity::{ }; use crate::config::CONFIG; +use crate::logging::*; use crate::token_management::*; macro_rules! e { @@ -24,16 +25,11 @@ pub fn new_member(ctx: &Context, mut new_member: Member) { message.push_line("! Would you care to introduce yourself?"); message.push_line("If you're not sure where to start, perhaps you could tell us about your projects, your first computer…"); message.push_line("You should also know that we follow the Freenode Channel Guidelines: https://freenode.net/changuide, and try to avoid defamatory content"); - if let Err(why) = CONFIG.welcome_channel.say(&ctx, message.build()) { - error!("Error sending message: {:?}", why); - } - + CONFIG.welcome_channel.try_say(&ctx, message.build()); let mut message = MessageBuilder::new(); message.push(format!("Say hi to {} in ", new_member.display_name())); message.mention(&CONFIG.welcome_channel); - if let Err(why) = CONFIG.main_channel.say(&ctx, message.build()) { - error!("Error sending message: {:?}", why); - } + CONFIG.main_channel.try_say(&ctx, message.build()); if let Err(why) = new_member.add_role(&ctx.http, CONFIG.unregistered_member_role) { error!("Error adding user role: {:?}", why); @@ -44,20 +40,20 @@ pub struct Commands; impl Commands { pub fn register(ctx: Context, msg: Message, account_name: &str) { if account_name.is_empty() { - e!( - "Error sending message: {:?}", - msg.channel_id - .say(&ctx.http, "Usage: !register <ucc username>") - ); + msg.channel_id + .try_say(&ctx.http, "Usage: !register <ucc username>"); return; } - e!( - "Error sending message: {:?}", - // TODO convert to email - msg.channel_id - .say(&ctx.http, format!("Hey {} here's that token you ordered: {}\nIf this wasn't you just ignore this.", account_name, generate_token(&msg.author, account_name))) + // TODO convert to email + msg.channel_id.try_say( + &ctx.http, + format!( + "Hey {} here's that token you ordered: {}\nIf this wasn't you just ignore this.", + account_name, + generate_token(&msg.author, account_name) + ), ); - e!("Error deleting register message: {:?}", msg.delete(ctx)); + msg.try_delete(ctx); } pub fn verify(ctx: Context, msg: Message, token: &str) { match parse_token(&msg.author, token) { @@ -71,42 +67,34 @@ impl Commands { "Unable to remove role: {:?}", member.remove_role(&ctx.http, CONFIG.unregistered_member_role) ); - e!( - "Unable to edit nickname: {:?}", - member.edit(&ctx.http, |m| { - let mut rng = rand::thread_rng(); - m.nickname(format!( - "{}, {}", - name, - [ - "The Big Cheese", - "The One and Only", - "The Exalted One", - "not to be trusted", - "The Scoundrel", - "A big fish in a small pond", - ][rng.gen_range(0, 5)] - )); - m - }) - ); + member.try_edit(&ctx.http, |m| { + let mut rng = rand::thread_rng(); + m.nickname(format!( + "{}, {}", + name, + [ + "The Big Cheese", + "The One and Only", + "The Exalted One", + "not to be trusted", + "The Scoundrel", + "A big fish in a small pond", + ][rng.gen_range(0, 5)] + )); + m + }); let new_msg = msg .channel_id .say(&ctx.http, "Verification succesful") .expect("Error sending message"); - e!( - "Error deleting register message: {:?}", - new_msg.delete(&ctx) - ); + new_msg.try_delete(&ctx); }) ); } - Err(reason) => e!( - "Error sending message: {:?}", - msg.channel_id - .say(&ctx.http, format!("Verification error: {:?}", reason)) - ), + Err(reason) => msg + .channel_id + .try_say(&ctx.http, format!("Verification error: {:?}", reason)), } - e!("Error deleting register message: {:?}", msg.delete(&ctx)); + msg.try_delete(&ctx); } } diff --git a/src/voting.rs b/src/voting.rs index ebb3e592a3662e0e5e556b8e69e6c1ca3e467b06..a2faf1e581a09f5663f62340fa9aa5177269fe66 100644 --- a/src/voting.rs +++ b/src/voting.rs @@ -7,18 +7,11 @@ use std::collections::HashMap; use std::sync::Mutex; use crate::config::CONFIG; +use crate::logging::*; use crate::util::get_string_from_react; -macro_rules! e { - ($error: literal, $x:expr) => { - match $x { - Ok(_) => (), - Err(why) => error!($error, why), - } - }; -} - pub struct Commands; + impl Commands { pub fn move_something(ctx: Context, msg: Message, content: &str) { let motion = content; @@ -26,20 +19,16 @@ impl Commands { create_motion(&ctx, &msg, motion); return; } - e!( - "Error sending message: {:?}", - msg.channel_id.say( - &ctx.http, - "If there's something you want to motion, put it after the !move keyword", - ) + msg.channel_id.try_say( + &ctx.http, + "If there's something you want to motion, put it after the !move keyword", ); } pub fn motion(ctx: Context, msg: Message, _content: &str) { - e!("Error sending message: {:?}", - msg.channel_id.say( - &ctx.http, - "I hope you're not having a motion. You may have wanted to !move something instead." - )); + msg.channel_id.try_say( + &ctx.http, + "I hope you're not having a motion. You may have wanted to !move something instead.", + ); } pub fn poll(ctx: Context, msg: Message, content: &str) { let topic = content; @@ -47,12 +36,9 @@ impl Commands { create_poll(&ctx, &msg, topic); return; } - e!( - "Error sending message: {:?}", - msg.channel_id.say( - &ctx.http, - "If there's something you want to motion, put it after the !move keyword", - ) + msg.channel_id.try_say( + &ctx.http, + "If there's something you want to motion, put it after the !move keyword", ); } pub fn cowsay(ctx: Context, msg: Message, content: &str) { @@ -78,10 +64,7 @@ impl Commands { String::from_utf8(output.stdout).expect("unable to parse stdout to String"), None, ); - e!( - "Error sending message: {:?}", - msg.channel_id.say(&ctx.http, message.build()) - ); + msg.channel_id.try_say(&ctx.http, message.build()); } } @@ -284,9 +267,9 @@ fn update_motion( message.push(" is now "); message.push_bold(status); message.push_italic(format!(" (was {})", last_status)); - if let Err(why) = CONFIG.announcement_channel.say(&ctx.http, message.build()) { - error!("Error sending message: {:?}", why); - }; + CONFIG + .announcement_channel + .try_say(&ctx.http, message.build()); } };