diff --git a/PluralKit.Bot/CommandMeta/CommandTree.cs b/PluralKit.Bot/CommandMeta/CommandTree.cs index 1f546baa..b542a87a 100644 --- a/PluralKit.Bot/CommandMeta/CommandTree.cs +++ b/PluralKit.Bot/CommandMeta/CommandTree.cs @@ -162,6 +162,15 @@ public partial class CommandTree 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)), + Commands.RandomSelf(_, var flags) => + flags.group + ? ctx.Execute(GroupRandom, m => m.Group(ctx, ctx.System, flags.all, flags.show_embed)) + : ctx.Execute(MemberRandom, m => m.Member(ctx, ctx.System, flags.all, flags.show_embed)), + Commands.SystemRandom(var param, var flags) => + flags.group + ? ctx.Execute(GroupRandom, m => m.Group(ctx, param.target, flags.all, flags.show_embed)) + : ctx.Execute(MemberRandom, m => m.Member(ctx, param.target, flags.all, flags.show_embed)), + Commands.GroupRandomMember(var param, var flags) => ctx.Execute(GroupMemberRandom, m => m.GroupMember(ctx, param.target, flags.all, flags.show_embed)), _ => // this should only ever occur when deving if commands are not implemented... ctx.Reply( @@ -252,11 +261,6 @@ public partial class CommandTree return HandleDebugCommand(ctx); if (ctx.Match("admin")) return HandleAdminCommand(ctx); - if (ctx.Match("random", "rand", "r")) - if (ctx.Match("group", "g") || ctx.MatchFlag("group", "g")) - return ctx.Execute(GroupRandom, r => r.Group(ctx, ctx.System)); - else - return ctx.Execute(MemberRandom, m => m.Member(ctx, ctx.System)); if (ctx.Match("dashboard", "dash")) return ctx.Execute(Dashboard, m => m.Dashboard(ctx)); } @@ -416,11 +420,6 @@ public partial class CommandTree await ctx.CheckSystem(target).Execute(GroupList, g => g.ListSystemGroups(ctx, target)); else if (ctx.Match("id")) await ctx.CheckSystem(target).Execute(SystemId, m => m.DisplayId(ctx, target)); - else if (ctx.Match("random", "rand", "r")) - if (ctx.Match("group", "g") || ctx.MatchFlag("group", "g")) - await ctx.CheckSystem(target).Execute(GroupRandom, r => r.Group(ctx, target)); - else - await ctx.CheckSystem(target).Execute(MemberRandom, m => m.Member(ctx, target)); } private async Task HandleMemberCommand(Context ctx) diff --git a/PluralKit.Bot/CommandSystem/Context/ContextEntityArgumentsExt.cs b/PluralKit.Bot/CommandSystem/Context/ContextEntityArgumentsExt.cs index b1cddf17..bf0fe27b 100644 --- a/PluralKit.Bot/CommandSystem/Context/ContextEntityArgumentsExt.cs +++ b/PluralKit.Bot/CommandSystem/Context/ContextEntityArgumentsExt.cs @@ -147,13 +147,9 @@ public static class ContextEntityArgumentsExt return member; } - public static async Task PeekGroup(this Context ctx, SystemId? restrictToSystem = null) + public static async Task ParseGroup(this Context ctx, string input, bool byId, SystemId? restrictToSystem = null) { - var input = ctx.PeekArgument(); - - // see PeekMember for an explanation of the logic used here - - if (ctx.System != null && !ctx.MatchFlag("id", "by-id")) + if (ctx.System != null && !byId) { if (await ctx.Repository.GetGroupByName(ctx.System.Id, input) is { } byName) return byName; @@ -170,6 +166,11 @@ public static class ContextEntityArgumentsExt return null; } + public static async Task PeekGroup(this Context ctx, SystemId? restrictToSystem = null) + { + throw new NotImplementedException(); + } + public static async Task MatchGroup(this Context ctx, SystemId? restrictToSystem = null) { var group = await ctx.PeekGroup(restrictToSystem); diff --git a/PluralKit.Bot/CommandSystem/Context/ContextParametersExt.cs b/PluralKit.Bot/CommandSystem/Context/ContextParametersExt.cs index 9f0c3f22..13b0ce99 100644 --- a/PluralKit.Bot/CommandSystem/Context/ContextParametersExt.cs +++ b/PluralKit.Bot/CommandSystem/Context/ContextParametersExt.cs @@ -28,6 +28,14 @@ public static class ContextParametersExt ); } + public static async Task ParamResolveGroup(this Context ctx, string param_name) + { + return await ctx.Parameters.ResolveParameter( + ctx, param_name, + param => (param as Parameter.GroupRef)?.group + ); + } + public static async Task ParamResolveSystem(this Context ctx, string param_name) { return await ctx.Parameters.ResolveParameter( diff --git a/PluralKit.Bot/CommandSystem/Parameters.cs b/PluralKit.Bot/CommandSystem/Parameters.cs index 2c753fde..d3331de1 100644 --- a/PluralKit.Bot/CommandSystem/Parameters.cs +++ b/PluralKit.Bot/CommandSystem/Parameters.cs @@ -10,6 +10,7 @@ public abstract record Parameter() { public record MemberRef(PKMember member): Parameter; public record MemberRefs(List members): Parameter; + public record GroupRef(PKGroup group): Parameter; public record SystemRef(PKSystem system): Parameter; public record GuildRef(Guild guild): Parameter; public record MemberPrivacyTarget(MemberPrivacySubject target): Parameter; @@ -73,6 +74,11 @@ public class Parameters ?? throw new PKError(ctx.CreateNotFoundError("Member", m, byId)) ).ToListAsync() ); + case uniffi.commands.Parameter.GroupRef groupRef: + return new Parameter.GroupRef( + await ctx.ParseGroup(groupRef.group, byId) + ?? throw new PKError(ctx.CreateNotFoundError("Group", groupRef.group)) + ); case uniffi.commands.Parameter.SystemRef systemRef: // todo: do we need byId here? return new Parameter.SystemRef( diff --git a/PluralKit.Bot/Commands/Random.cs b/PluralKit.Bot/Commands/Random.cs index b16fff6e..7c451afa 100644 --- a/PluralKit.Bot/Commands/Random.cs +++ b/PluralKit.Bot/Commands/Random.cs @@ -15,7 +15,7 @@ public class Random // todo: get postgresql to return one random member/group instead of querying all members/groups - public async Task Member(Context ctx, PKSystem target, bool showEmbed = false) + public async Task Member(Context ctx, PKSystem target, bool all, bool showEmbed = false) { if (target == null) throw Errors.NoSystemError(ctx.DefaultPrefix); @@ -24,7 +24,7 @@ public class Random var members = await ctx.Repository.GetSystemMembers(target.Id).ToListAsync(); - if (!ctx.MatchFlag("all", "a")) + if (!all) members = members.Where(m => m.MemberVisibility == PrivacyLevel.Public).ToList(); else ctx.CheckOwnSystem(target); @@ -49,7 +49,7 @@ public class Random components: await _embeds.CreateMemberMessageComponents(target, members[randInt], ctx.Guild, ctx.Config, ctx.LookupContextFor(target.Id), ctx.Zone)); } - public async Task Group(Context ctx, PKSystem target, bool showEmbed = false) + public async Task Group(Context ctx, PKSystem target, bool all, bool showEmbed = false) { if (target == null) throw Errors.NoSystemError(ctx.DefaultPrefix); @@ -57,7 +57,7 @@ public class Random ctx.CheckSystemPrivacy(target.Id, target.GroupListPrivacy); var groups = await ctx.Repository.GetSystemGroups(target.Id).ToListAsync(); - if (!ctx.MatchFlag("all", "a")) + if (!all) groups = groups.Where(g => g.Visibility == PrivacyLevel.Public).ToList(); else ctx.CheckOwnSystem(target); @@ -82,7 +82,7 @@ public class Random components: await _embeds.CreateGroupMessageComponents(ctx, target, groups.ToArray()[randInt])); } - public async Task GroupMember(Context ctx, PKGroup group, bool showEmbed = false) + public async Task GroupMember(Context ctx, PKGroup group, bool all, bool showEmbed = false) { ctx.CheckSystemPrivacy(group.System, group.ListPrivacy); @@ -96,7 +96,7 @@ public class Random "This group has no members!" + (ctx.System?.Id == group.System ? " Please add at least one member to this group before using this command." : "")); - if (!ctx.MatchFlag("all", "a")) + if (!all) members = members.Where(g => g.MemberVisibility == PrivacyLevel.Public); else ctx.CheckOwnGroup(group); diff --git a/crates/command_definitions/src/group.rs b/crates/command_definitions/src/group.rs index 8b137891..44847ee6 100644 --- a/crates/command_definitions/src/group.rs +++ b/crates/command_definitions/src/group.rs @@ -1 +1,11 @@ +use command_parser::token::TokensIterator; +use super::*; + +pub fn group() -> (&'static str, [&'static str; 1]) { + ("group", ["g"]) +} + +pub fn targeted() -> TokensIterator { + tokens!(group(), GroupRef) +} diff --git a/crates/command_definitions/src/lib.rs b/crates/command_definitions/src/lib.rs index 500499de..f8deb48b 100644 --- a/crates/command_definitions/src/lib.rs +++ b/crates/command_definitions/src/lib.rs @@ -27,6 +27,7 @@ pub fn all() -> impl Iterator { .chain(config::cmds()) .chain(fun::cmds()) .chain(switch::cmds()) + .chain(random::cmds()) .map(|cmd| { cmd.flag(("plaintext", ["pt"])) .flag(("raw", ["r"])) diff --git a/crates/command_definitions/src/random.rs b/crates/command_definitions/src/random.rs index 8b137891..2f48c9f0 100644 --- a/crates/command_definitions/src/random.rs +++ b/crates/command_definitions/src/random.rs @@ -1 +1,14 @@ +use super::*; +pub fn cmds() -> impl Iterator { + let random = ("random", ["rand"]); + let group = group::group(); + + [ + command!(random => "random_self").flag(group), + command!(system::targeted(), random => "system_random").flag(group), + command!(group::targeted(), random => "group_random_member"), + ] + .into_iter() + .map(|cmd| cmd.flag(("all", ["a"]))) +} diff --git a/crates/command_definitions/src/system.rs b/crates/command_definitions/src/system.rs index 7de7e274..e6fd5b66 100644 --- a/crates/command_definitions/src/system.rs +++ b/crates/command_definitions/src/system.rs @@ -1,12 +1,22 @@ +use command_parser::token::TokensIterator; + use super::*; pub fn cmds() -> impl Iterator { edit() } +pub fn system() -> (&'static str, [&'static str; 1]) { + ("system", ["s"]) +} + +pub fn targeted() -> TokensIterator { + tokens!(system(), SystemRef) +} + pub fn edit() -> impl Iterator { - let system = ("system", ["s"]); - let system_target = tokens!(system, SystemRef); + let system = system(); + let system_target = targeted(); let system_new = tokens!(system, ("new", ["n"])); let system_new_cmd = [ diff --git a/crates/command_parser/src/parameter.rs b/crates/command_parser/src/parameter.rs index 08971d99..0c38f532 100644 --- a/crates/command_parser/src/parameter.rs +++ b/crates/command_parser/src/parameter.rs @@ -12,6 +12,7 @@ pub enum ParameterValue { OpaqueString(String), MemberRef(String), MemberRefs(Vec), + GroupRef(String), SystemRef(String), GuildRef(String), MemberPrivacyTarget(String), @@ -48,6 +49,7 @@ impl Display for Parameter { } ParameterKind::MemberRef => write!(f, ""), ParameterKind::MemberRefs => write!(f, " ..."), + ParameterKind::GroupRef => write!(f, ""), ParameterKind::SystemRef => write!(f, ""), ParameterKind::GuildRef => write!(f, ""), ParameterKind::MemberPrivacyTarget => write!(f, ""), @@ -83,6 +85,7 @@ pub enum ParameterKind { OpaqueStringRemainder, MemberRef, MemberRefs, + GroupRef, SystemRef, GuildRef, MemberPrivacyTarget, @@ -99,6 +102,7 @@ impl ParameterKind { ParameterKind::OpaqueStringRemainder => "string", ParameterKind::MemberRef => "target", ParameterKind::MemberRefs => "targets", + ParameterKind::GroupRef => "target", ParameterKind::SystemRef => "target", ParameterKind::GuildRef => "target", ParameterKind::MemberPrivacyTarget => "member_privacy_target", @@ -122,6 +126,7 @@ impl ParameterKind { ParameterKind::OpaqueString | ParameterKind::OpaqueStringRemainder => { Ok(ParameterValue::OpaqueString(input.into())) } + ParameterKind::GroupRef => Ok(ParameterValue::GroupRef(input.into())), ParameterKind::MemberRef => Ok(ParameterValue::MemberRef(input.into())), ParameterKind::MemberRefs => Ok(ParameterValue::MemberRefs( input.split(' ').map(|s| s.trim().to_string()).collect(), diff --git a/crates/commands/src/bin/write_cs_glue.rs b/crates/commands/src/bin/write_cs_glue.rs index 0559de88..1d0e61df 100644 --- a/crates/commands/src/bin/write_cs_glue.rs +++ b/crates/commands/src/bin/write_cs_glue.rs @@ -168,6 +168,7 @@ fn get_param_ty(kind: ParameterKind) -> &'static str { ParameterKind::OpaqueString | ParameterKind::OpaqueStringRemainder => "string", ParameterKind::MemberRef => "PKMember", ParameterKind::MemberRefs => "List", + ParameterKind::GroupRef => "PKGroup", ParameterKind::SystemRef => "PKSystem", ParameterKind::MemberPrivacyTarget => "MemberPrivacySubject", ParameterKind::SystemPrivacyTarget => "SystemPrivacySubject", @@ -183,6 +184,7 @@ fn get_param_param_ty(kind: ParameterKind) -> &'static str { ParameterKind::OpaqueString | ParameterKind::OpaqueStringRemainder => "Opaque", ParameterKind::MemberRef => "Member", ParameterKind::MemberRefs => "Members", + ParameterKind::GroupRef => "Group", ParameterKind::SystemRef => "System", ParameterKind::MemberPrivacyTarget => "MemberPrivacyTarget", ParameterKind::SystemPrivacyTarget => "SystemPrivacyTarget", diff --git a/crates/commands/src/commands.udl b/crates/commands/src/commands.udl index 1a8da927..15e9849c 100644 --- a/crates/commands/src/commands.udl +++ b/crates/commands/src/commands.udl @@ -10,6 +10,7 @@ interface CommandResult { interface Parameter { MemberRef(string member); MemberRefs(sequence members); + GroupRef(string group); SystemRef(string system); GuildRef(string guild); MemberPrivacyTarget(string target); diff --git a/crates/commands/src/lib.rs b/crates/commands/src/lib.rs index bc79e0ca..368cb81f 100644 --- a/crates/commands/src/lib.rs +++ b/crates/commands/src/lib.rs @@ -24,6 +24,7 @@ pub enum CommandResult { pub enum Parameter { MemberRef { member: String }, MemberRefs { members: Vec }, + GroupRef { group: String }, SystemRef { system: String }, GuildRef { guild: String }, MemberPrivacyTarget { target: String }, @@ -39,6 +40,7 @@ impl From for Parameter { match value { ParameterValue::MemberRef(member) => Self::MemberRef { member }, ParameterValue::MemberRefs(members) => Self::MemberRefs { members }, + ParameterValue::GroupRef(group) => Self::GroupRef { group }, ParameterValue::SystemRef(system) => Self::SystemRef { system }, ParameterValue::MemberPrivacyTarget(target) => Self::MemberPrivacyTarget { target }, ParameterValue::SystemPrivacyTarget(target) => Self::SystemPrivacyTarget { target },