From 104083aac17de5dd3521b685d4cd34c6018ab5e7 Mon Sep 17 00:00:00 2001 From: dusk Date: Fri, 26 Sep 2025 14:58:59 +0000 Subject: [PATCH] feat: implement system front commands --- PluralKit.Bot/CommandMeta/CommandTree.cs | 16 +++------------- PluralKit.Bot/Commands/SystemFront.cs | 13 ++++--------- crates/command_definitions/src/system.rs | 13 +++++++++++++ crates/command_parser/src/lib.rs | 19 ++++++++++++++----- crates/command_parser/src/token.rs | 2 +- 5 files changed, 35 insertions(+), 28 deletions(-) diff --git a/PluralKit.Bot/CommandMeta/CommandTree.cs b/PluralKit.Bot/CommandMeta/CommandTree.cs index d2891529..0aad03f6 100644 --- a/PluralKit.Bot/CommandMeta/CommandTree.cs +++ b/PluralKit.Bot/CommandMeta/CommandTree.cs @@ -159,6 +159,9 @@ public partial class CommandTree Commands.SwitchEditOut(_, _) => ctx.Execute(SwitchEditOut, m => m.SwitchEditOut(ctx)), Commands.SwitchDelete(var param, var flags) => ctx.Execute(SwitchDelete, m => m.SwitchDelete(ctx, flags.all)), Commands.SwitchCopy(var param, var flags) => ctx.Execute(SwitchCopy, m => m.SwitchEdit(ctx, param.targets, true, flags.first, flags.remove, flags.append, flags.prepend)), + Commands.SystemFronter(var param, var flags) => ctx.Execute(SystemFronter, m => m.Fronter(ctx, param.target)), + Commands.SystemFronterHistory(var param, var flags) => ctx.Execute(SystemFrontHistory, m => m.FrontHistory(ctx, param.target, flags.clear)), + Commands.SystemFronterPercent(var param, var flags) => ctx.Execute(SystemFrontPercent, m => m.FrontPercent(ctx, param.target, flags.duration, flags.fronters_only, flags.flat)), _ => // this should only ever occur when deving if commands are not implemented... ctx.Reply( @@ -407,19 +410,6 @@ public partial class CommandTree await ctx.CheckSystem(target).Execute(SystemList, m => m.MemberList(ctx, target)); else if (ctx.Match("find", "search", "query", "fd", "s")) await ctx.CheckSystem(target).Execute(SystemFind, m => m.MemberList(ctx, target)); - else if (ctx.Match("f", "front", "fronter", "fronters")) - { - if (ctx.Match("h", "history")) - await ctx.CheckSystem(target).Execute(SystemFrontHistory, m => m.SystemFrontHistory(ctx, target)); - else if (ctx.Match("p", "percent", "%")) - await ctx.CheckSystem(target).Execute(SystemFrontPercent, m => m.FrontPercent(ctx, system: target)); - else - await ctx.CheckSystem(target).Execute(SystemFronter, m => m.SystemFronter(ctx, target)); - } - else if (ctx.Match("fh", "fronthistory", "history", "switches")) - await ctx.CheckSystem(target).Execute(SystemFrontHistory, m => m.SystemFrontHistory(ctx, target)); - else if (ctx.Match("fp", "frontpercent", "front%", "frontbreakdown")) - await ctx.CheckSystem(target).Execute(SystemFrontPercent, m => m.FrontPercent(ctx, system: target)); else if (ctx.Match("groups", "gs")) await ctx.CheckSystem(target).Execute(GroupList, g => g.ListSystemGroups(ctx, target)); else if (ctx.Match("id")) diff --git a/PluralKit.Bot/Commands/SystemFront.cs b/PluralKit.Bot/Commands/SystemFront.cs index 8139d5e4..6d9cf049 100644 --- a/PluralKit.Bot/Commands/SystemFront.cs +++ b/PluralKit.Bot/Commands/SystemFront.cs @@ -15,7 +15,7 @@ public class SystemFront _embeds = embeds; } - public async Task SystemFronter(Context ctx, PKSystem system) + public async Task Fronter(Context ctx, PKSystem system) { if (system == null) throw Errors.NoSystemError(ctx.DefaultPrefix); ctx.CheckSystemPrivacy(system.Id, system.FrontPrivacy); @@ -26,9 +26,9 @@ public class SystemFront await ctx.Reply(embed: await _embeds.CreateFronterEmbed(sw, ctx.Zone, ctx.LookupContextFor(system.Id))); } - public async Task SystemFrontHistory(Context ctx, PKSystem system) + public async Task FrontHistory(Context ctx, PKSystem system, bool clear = false) { - if (ctx.MatchFlag("clear", "c") || ctx.PeekArgument() == "clear") + if (clear) { await new Switch().SwitchDelete(ctx, true); return; @@ -106,7 +106,7 @@ public class SystemFront ); } - public async Task FrontPercent(Context ctx, PKSystem? system = null, PKGroup? group = null) + public async Task FrontPercent(Context ctx, PKSystem? system = null, string durationStr = "30d", bool ignoreNoFronters = false, bool showFlat = false, PKGroup? group = null) { if (system == null && group == null) throw Errors.NoSystemError(ctx.DefaultPrefix); if (system == null) system = await GetGroupSystem(ctx, group); @@ -116,11 +116,6 @@ public class SystemFront var totalSwitches = await ctx.Repository.GetSwitchCount(system.Id); if (totalSwitches == 0) throw Errors.NoRegisteredSwitches; - var ignoreNoFronters = ctx.MatchFlag("fo", "fronters-only"); - var showFlat = ctx.MatchFlag("flat"); - - var durationStr = ctx.RemainderOrNull() ?? "30d"; - // Picked the UNIX epoch as a random date // even though we don't store switch timestamps in UNIX time // I assume most people won't have switches logged previously to that (?) diff --git a/crates/command_definitions/src/system.rs b/crates/command_definitions/src/system.rs index a4fe0037..7de7e274 100644 --- a/crates/command_definitions/src/system.rs +++ b/crates/command_definitions/src/system.rs @@ -223,6 +223,18 @@ pub fn edit() -> impl Iterator { .help("Changes a specific privacy setting for your system"), ].into_iter(); + let system_front = tokens!(system_target, ("front", ["fronter", "fronters", "f"])); + let system_front_cmd = [ + command!(system_front => "system_fronter"), + command!(system_front, ("history", ["h"]) => "system_fronter_history") + .flag(("clear", ["c"])), + command!(system_front, ("percent", ["p", "%"]) => "system_fronter_percent") + .flag(("duration", OpaqueString)) + .flag(("fronters-only", ["fo"])) + .flag("flat"), + ] + .into_iter(); + system_new_cmd .chain(system_name_self_cmd) .chain(system_server_name_self_cmd) @@ -248,4 +260,5 @@ pub fn edit() -> impl Iterator { .chain(system_server_avatar_cmd) .chain(system_banner_cmd) .chain(system_info_cmd) + .chain(system_front_cmd) } diff --git a/crates/command_parser/src/lib.rs b/crates/command_parser/src/lib.rs index 34614f2d..c02fc463 100644 --- a/crates/command_parser/src/lib.rs +++ b/crates/command_parser/src/lib.rs @@ -58,10 +58,14 @@ pub fn parse_command( match &result { // todo: better error messages for these? TokenMatchResult::MissingParameter { name } => { - return Err(format!("Expected parameter `{name}` in command `{prefix}{input} {found_token}`.")); + return Err(format!( + "Expected parameter `{name}` in command `{prefix}{input} {found_token}`." + )); } TokenMatchResult::ParameterMatchError { input: raw, msg } => { - return Err(format!("Parameter `{raw}` in command `{prefix}{input}` could not be parsed: {msg}.")); + return Err(format!( + "Parameter `{raw}` in command `{prefix}{input}` could not be parsed: {msg}." + )); } // don't use a catch-all here, we want to make sure compiler errors when new errors are added TokenMatchResult::MatchedParameter { .. } | TokenMatchResult::MatchedValue => {} @@ -109,7 +113,9 @@ pub fn parse_command( raw_flags.push((current_token_idx, matched_flag)); } // if we have a command, stop parsing and return it (only if there is no remaining input) - if current_pos >= input.len() && let Some(command) = local_tree.command() { + if current_pos >= input.len() + && let Some(command) = local_tree.command() + { // match the flags against this commands flags let mut flags: HashMap> = HashMap::new(); let mut misplaced_flags: Vec = Vec::new(); @@ -182,8 +188,11 @@ pub fn parse_command( write!( &mut error, " {} seem to be applicable in this command (`{prefix}{command}`).", - (invalid_flags.len() > 1).then_some("don't").unwrap_or("doesn't") - ).expect("oom"); + (invalid_flags.len() > 1) + .then_some("don't") + .unwrap_or("doesn't") + ) + .expect("oom"); return Err(error); } println!("{} {flags:?} {params:?}", command.cb); diff --git a/crates/command_parser/src/token.rs b/crates/command_parser/src/token.rs index 863cb936..653b8a65 100644 --- a/crates/command_parser/src/token.rs +++ b/crates/command_parser/src/token.rs @@ -76,7 +76,7 @@ impl Token { msg: err, } } - }, + } }), // don't add a _ match here! }