diff options
author | gennyble <gen@nyble.dev> | 2025-08-15 17:53:53 -0500 |
---|---|---|
committer | gennyble <gen@nyble.dev> | 2025-08-15 17:53:53 -0500 |
commit | db62e4ab9c54a370e15ad6944963b2ceb8d2638b (patch) | |
tree | 24b7d59356a117b1c684a237bf7ecfb6477a50e7 /src/command | |
parent | d997d8a3d640cf36b0e705db249e1a8865f0b54f (diff) | |
download | leaberblord-db62e4ab9c54a370e15ad6944963b2ceb8d2638b.tar.gz leaberblord-db62e4ab9c54a370e15ad6944963b2ceb8d2638b.zip |
refactor add_point and revise
Diffstat (limited to 'src/command')
-rw-r--r-- | src/command/add_points.rs | 90 | ||||
-rw-r--r-- | src/command/mod.rs | 138 | ||||
-rw-r--r-- | src/command/revise.rs | 131 |
3 files changed, 189 insertions, 170 deletions
diff --git a/src/command/add_points.rs b/src/command/add_points.rs index 453be2d..97e3981 100644 --- a/src/command/add_points.rs +++ b/src/command/add_points.rs @@ -1,21 +1,17 @@ use std::sync::Arc; use twilight_model::{ - application::interaction::application_command::{CommandData, CommandOptionValue}, + application::interaction::application_command::CommandData, gateway::payload::incoming::InteractionCreate, http::interaction::InteractionResponseData, - id::{ - Id, - marker::{GuildMarker, UserMarker}, - }, + id::{Id, marker::GuildMarker}, }; use twilight_util::builder::{InteractionResponseDataBuilder, embed::EmbedBuilder}; use crate::{ - bail, brain::Brain, - database::{BoardRow, Error as DbError, PermissionSetting}, - success, util, + command::{check_command_permissions, parse_points_command_data}, + database::{BoardRow, Error as DbError}, }; pub async fn add_points( @@ -24,71 +20,28 @@ pub async fn add_points( create: &InteractionCreate, data: &CommandData, ) -> InteractionResponseData { - let mut points = None; - let mut users: Vec<Id<UserMarker>> = vec![]; - - for opt in &data.options { - match opt.name.as_str() { - "points" => match opt.value { - CommandOptionValue::Integer(num) => points = Some(num), - _ => unreachable!(), - }, - "users" => match &opt.value { - CommandOptionValue::String(raw) => { - let mentions = util::extract_user_mentions(&raw); - users = mentions; - } - _ => unreachable!(), - }, - _ => unreachable!(), - } - } - - if users.len() == 0 { - bail!("No users mentioned! Who do we add points to?") - } - - let (points, points_display, points_verb) = match points { - Some(p) if p > 0 => (p, p, "added to"), - Some(p) if p < 0 => (p, -p, "removed from"), - Some(0) => { - return success!("adding 0 points is a no-operation! I won't do anything :)"); - } - Some(_) | None => unreachable!(), + let point_data = match parse_points_command_data(data) { + Err(e) => return e, + Ok(pcd) => pcd, }; brain.create_leaderboard_if_not_exists(guild); - - let settings = brain.db.get_leaderboard_settings(guild.get()).unwrap(); - if let PermissionSetting::RoleRequired = settings.permission { - if let Some(role) = settings.role { - let member = create.member.clone().unwrap(); - let found_role = member.roles.iter().find(|vrole| vrole.get() == role); - - if found_role.is_none() { - bail!("You do not have the right permissions to change the score"); - } - } else { - // Seeing as the role is a required input on the subcommand, this - // would only happen with direct database fiddling or like, some - // really weird arcane bullshit? - bail!( - "Permissions set to Role Required, but no role is set. \ - This shouldn't be able to happen. \ - Maybe try to set the permissions again?" - ); - } + if let Some(err) = check_command_permissions(&brain, guild, create.member.clone().unwrap()) { + return err; } - for user in &users { - match brain.db.give_user_points(guild.get(), user.get(), points) { + for user in &point_data.users { + match brain + .db + .give_user_points(guild.get(), user.get(), point_data.points) + { Err(DbError::UserNotExist) => { let member = brain.guild_member(guild, *user).await; let row = BoardRow { user_id: user.get(), user_handle: member.handle, user_nickname: member.nick.or(member.global_name), - points, + points: point_data.points, }; brain.db.add_user_to_leaderboard(guild.get(), row).unwrap(); } @@ -96,13 +49,14 @@ pub async fn add_points( } } - let users_string = users - .into_iter() - .map(|u| format!("<@{u}>")) - .collect::<Vec<String>>() - .join(", "); + let users_string = point_data.user_string(); + let points_verb = point_data.points_verb; + + let msg = format!( + "{} points {points_verb} {users_string}", + point_data.points.abs() + ); - let msg = format!("{points_display} points {points_verb} {users_string}"); let embed = EmbedBuilder::new().description(msg).build(); InteractionResponseDataBuilder::new() .embeds([embed]) diff --git a/src/command/mod.rs b/src/command/mod.rs index 73b292a..c3dc7a9 100644 --- a/src/command/mod.rs +++ b/src/command/mod.rs @@ -5,6 +5,7 @@ mod revise; pub use add_points::add_points; pub use leaderboard::leaderboard; pub use revise::revise; +use time::{Date, PrimitiveDateTime, Time, format_description}; use std::sync::Arc; @@ -14,8 +15,12 @@ use twilight_model::{ interaction::application_command::{CommandData, CommandOptionValue}, }, gateway::payload::incoming::InteractionCreate, + guild::PartialMember, http::interaction::InteractionResponseData, - id::{Id, marker::GuildMarker}, + id::{ + Id, + marker::{GuildMarker, UserMarker}, + }, }; use twilight_util::builder::{ InteractionResponseDataBuilder, @@ -23,7 +28,7 @@ use twilight_util::builder::{ embed::{EmbedBuilder, EmbedFieldBuilder}, }; -use crate::{bail, brain::Brain, database::PermissionSetting, fail, import, success}; +use crate::{bail, brain::Brain, database::PermissionSetting, fail, import, success, util}; pub enum Commands { About, @@ -231,3 +236,132 @@ pub async fn import( import::emboard_direct::import(brain, guild, create).await; } } + +/// Checks the settings of the leaderboard associated with the provided guild. +/// +/// Returns `None` if: +/// - The guild does not have permissions set +/// - The guild member has the appropriate permissions +/// +/// Returns `Some` with InteractionResponseData if: +/// - The guild member does not have proper permissions +/// - The guild is set to role required, but no role is set, which should be +/// impossible to happen, but. +pub fn check_command_permissions( + brain: &Brain, + guild: Id<GuildMarker>, + member: PartialMember, +) -> Option<InteractionResponseData> { + let settings = brain.db.get_leaderboard_settings(guild.get()).unwrap(); + if let PermissionSetting::RoleRequired = settings.permission { + if let Some(role) = settings.role { + let member = member; + let found_role = member.roles.iter().find(|vrole| vrole.get() == role); + + if found_role.is_none() { + Some(fail!( + "You do not have the right permissions to change the score" + )) + } else { + None + } + } else { + // Seeing as the role is a required input on the subcommand, this + // would only happen with direct database fiddling or like, some + // really weird arcane bullshit? + Some(fail!( + "Permissions set to Role Required, but no role is set. \ + This shouldn't be able to happen. \ + Maybe try to set the permissions again?" + )) + } + } else { + None + } +} + +pub struct PointsCommandData { + points: i64, + points_verb: &'static str, + users: Vec<Id<UserMarker>>, + date: Result<PrimitiveDateTime, InteractionResponseData>, +} + +impl PointsCommandData { + pub fn user_string(&self) -> String { + self.users + .iter() + .map(|u| format!("<@{u}>")) + .collect::<Vec<String>>() + .join(", ") + } +} + +pub fn parse_points_command_data( + data: &CommandData, +) -> Result<PointsCommandData, InteractionResponseData> { + let mut points = None; + let mut users: Vec<Id<UserMarker>> = vec![]; + let mut date_string = None; + + for opt in &data.options { + match opt.name.as_str() { + "points" => match opt.value { + CommandOptionValue::Integer(num) => points = Some(num), + _ => unreachable!(), + }, + "users" => match &opt.value { + CommandOptionValue::String(raw) => { + let mentions = util::extract_user_mentions(&raw); + users = mentions; + } + _ => unreachable!(), + }, + "date" => match &opt.value { + CommandOptionValue::String(raw) => { + date_string = Some(raw); + } + _ => unreachable!(), + }, + _ => unreachable!(), + } + } + + if users.len() == 0 { + return Err(fail!("No users mentioned! Who do we add points to?")); + } + + let (points, _points_display, points_verb) = match points { + Some(p) if p >= 0 => (p, p, "added to"), + Some(p) if p < 0 => (p, -p, "removed from"), + Some(_) | None => unreachable!(), + }; + + let date_fmt = format_description::parse("[year]-[month]-[day]").unwrap(); + let datetime_fmt = + format_description::parse("[year]-[month]-[day] [hour]:[minute]:[second]").unwrap(); + + let date = match date_string { + None => Err(fail!("What date were these points earned?")), + Some(str) if str.is_empty() => Err(fail!("You must specify a date!")), + Some(date) => match Date::parse(date.trim(), &date_fmt) { + Err(_e) => match PrimitiveDateTime::parse(date.trim(), &datetime_fmt) { + Err(_e) => Err(fail!( + "Cannot parse the given date/time. Did you write it like \"2025-04-13\" or, with time, \"2025-04-13 02:33\"?" + )), + Ok(datetime) => Ok(datetime), + }, + Ok(date) => Ok(PrimitiveDateTime::new( + date, + Time::from_hms(12, 0, 0).unwrap(), + )), + }, + }; + + Ok(PointsCommandData { + points, + points_verb, + users, + date, + }) +} diff --git a/src/command/revise.rs b/src/command/revise.rs index ecb97d8..50b8c4e 100644 --- a/src/command/revise.rs +++ b/src/command/revise.rs @@ -1,24 +1,18 @@ use std::sync::Arc; -use time::{ - Date, OffsetDateTime, PrimitiveDateTime, Time, format_description, macros::format_description, -}; +use time::format_description; use twilight_model::{ - application::interaction::application_command::{CommandData, CommandOptionValue}, + application::interaction::application_command::CommandData, gateway::payload::incoming::InteractionCreate, http::interaction::InteractionResponseData, - id::{ - Id, - marker::{GuildMarker, UserMarker}, - }, + id::{Id, marker::GuildMarker}, }; use twilight_util::builder::{InteractionResponseDataBuilder, embed::EmbedBuilder}; use crate::{ - bail, brain::Brain, - database::{BoardRow, Error as DbError, PermissionSetting}, - success, util, + command::{check_command_permissions, parse_points_command_data}, + database::{BoardRow, Error as DbError}, }; pub async fn revise( @@ -27,100 +21,31 @@ pub async fn revise( create: &InteractionCreate, data: &CommandData, ) -> InteractionResponseData { - let mut points = None; - let mut users: Vec<Id<UserMarker>> = vec![]; - let mut date_string = None; - - for opt in &data.options { - match opt.name.as_str() { - "points" => match opt.value { - CommandOptionValue::Integer(num) => points = Some(num), - _ => unreachable!(), - }, - "users" => match &opt.value { - CommandOptionValue::String(raw) => { - let mentions = util::extract_user_mentions(&raw); - users = mentions; - } - _ => unreachable!(), - }, - "date" => match &opt.value { - CommandOptionValue::String(raw) => { - date_string = Some(raw); - } - _ => unreachable!(), - }, - _ => unreachable!(), - } - } - - let date_fmt = format_description::parse("[year]-[month]-[day]").unwrap(); - let datetime_fmt = - format_description::parse("[year]-[month]-[day] [hour]:[minute]:[second]").unwrap(); - - if users.len() == 0 { - bail!("No users mentioned! Who do we add points to?") - } - - if let Some(date) = date_string { - println!("date = \"{date}\""); - } - - let date = match date_string { - None => bail!("What date were these points earned?"), - Some(str) if str.is_empty() => bail!("You must specify a date!"), - Some(date) => match Date::parse(date.trim(), &date_fmt) { - Err(_e) => match PrimitiveDateTime::parse(date.trim(), &datetime_fmt) { - Err(_e) => bail!( - "Cannot parse the given date/time. Did you write it like \"2025-04-13\" or, with time, \"2025-04-13 02:33\"?" - ), - Ok(datetime) => datetime, - }, - Ok(date) => PrimitiveDateTime::new(date, Time::from_hms(12, 0, 0).unwrap()), + let (point_data, date) = match parse_points_command_data(data) { + Err(e) => return e, + Ok(pcd) => match pcd.date { + Err(e) => return e, + Ok(date) => (pcd, date), }, }; - let (points, points_display, points_verb) = match points { - Some(p) if p > 0 => (p, p, "added to"), - Some(p) if p < 0 => (p, -p, "removed from"), - Some(0) => { - return success!("adding 0 points is a no-operation! I won't do anything :)"); - } - Some(_) | None => unreachable!(), - }; - brain.create_leaderboard_if_not_exists(guild); - - let settings = brain.db.get_leaderboard_settings(guild.get()).unwrap(); - if let PermissionSetting::RoleRequired = settings.permission { - if let Some(role) = settings.role { - let member = create.member.clone().unwrap(); - let found_role = member.roles.iter().find(|vrole| vrole.get() == role); - - if found_role.is_none() { - bail!("You do not have the right permissions to change the score"); - } - } else { - // Seeing as the role is a required input on the subcommand, this - // would only happen with direct database fiddling or like, some - // really weird arcane bullshit? - bail!( - "Permissions set to Role Required, but no role is set. \ - This shouldn't be able to happen. \ - Maybe try to set the permissions again?" - ); - } + if let Some(err) = check_command_permissions(&brain, guild, create.member.clone().unwrap()) { + return err; } - for user in &users { - match brain.db.give_user_points(guild.get(), user.get(), points) { + for user in &point_data.users { + match brain + .db + .give_user_points(guild.get(), user.get(), point_data.points) + { Err(DbError::UserNotExist) => { let member = brain.guild_member(guild, *user).await; let row = BoardRow { user_id: user.get(), user_handle: member.handle, user_nickname: member.nick.or(member.global_name), - points, + points: point_data.points, }; brain.db.add_user_to_leaderboard(guild.get(), row).unwrap(); } @@ -130,18 +55,24 @@ pub async fn revise( //FIXME: gen 2025-08-14: do not assume UTC! brain .db - .revise_last_history_date(guild.get(), user.get(), points, date.assume_utc()) + .revise_last_history_date( + guild.get(), + user.get(), + point_data.points, + date.assume_utc(), + ) .unwrap(); } - let users_string = users - .into_iter() - .map(|u| format!("<@{u}>")) - .collect::<Vec<String>>() - .join(", "); + let datetime_fmt = + format_description::parse("[year]-[month]-[day] [hour]:[minute]:[second]").unwrap(); + + let points_verb = point_data.points_verb; + let users_string = point_data.user_string(); let msg = format!( - "{points_display} points {points_verb} {users_string} on date {}", + "{} points {points_verb} {users_string} on date {}", + point_data.points.abs(), date.format(&datetime_fmt).unwrap() ); let embed = EmbedBuilder::new().description(msg).build(); |