diff --git a/PluralKit.Bot/CommandMeta/CommandTree.cs b/PluralKit.Bot/CommandMeta/CommandTree.cs index 72a43288..9420b80a 100644 --- a/PluralKit.Bot/CommandMeta/CommandTree.cs +++ b/PluralKit.Bot/CommandMeta/CommandTree.cs @@ -283,7 +283,8 @@ public partial class CommandTree Commands.MessageAuthor(var param, var flags) => ctx.Execute(Message, m => m.GetMessage(ctx, param.target.MessageId, flags.GetReplyFormat(), false, true)), Commands.MessageDelete(var param, var flags) => ctx.Execute(Message, m => m.GetMessage(ctx, param.target.MessageId, flags.GetReplyFormat(), true, false)), Commands.MessageEdit(var param, var flags) => ctx.Execute(MessageEdit, m => m.EditMessage(ctx, param.target.MessageId, param.new_content, flags.regex, flags.mutate_space, flags.append, flags.prepend, flags.clear_embeds, flags.clear_attachments)), - Commands.MessageReproxy(var param, _) => ctx.Execute(MessageReproxy, m => m.ReproxyMessage(ctx, param.msg.MessageId, param.member)), + Commands.MessageReproxySpecified(var param, _) => ctx.Execute(MessageReproxy, m => m.ReproxyMessage(ctx, param.msg.MessageId, param.member)), + Commands.MessageReproxy(var param, _) => ctx.Execute(MessageReproxy, m => m.ReproxyMessage(ctx, null, param.member)), Commands.Import(var param, var flags) => ctx.Execute(Import, m => m.Import(ctx, param.url, flags.yes)), Commands.Export(_, _) => ctx.Execute(Export, m => m.Export(ctx)), Commands.ServerConfigShow => ctx.Execute(null, m => m.ShowConfig(ctx)), diff --git a/crates/command_definitions/src/member.rs b/crates/command_definitions/src/member.rs index e18a04e3..f35bb3d8 100644 --- a/crates/command_definitions/src/member.rs +++ b/crates/command_definitions/src/member.rs @@ -183,15 +183,15 @@ pub fn cmds() -> impl Iterator { [ command!(member_keep_proxy => "member_keepproxy_show") .help("Shows a member's keep-proxy setting"), - command!(member_keep_proxy, Skip(("value", Toggle)) => "member_keepproxy_update") + command!(member_keep_proxy, ("value", Toggle) => "member_keepproxy_update") .help("Changes a member's keep-proxy setting"), command!(member_server_keep_proxy => "member_server_keepproxy_show") .help("Shows a member's server-specific keep-proxy setting"), - command!(member_server_keep_proxy, Skip(("value", Toggle)) => "member_server_keepproxy_update") - .help("Changes a member's server-specific keep-proxy setting"), command!(member_server_keep_proxy, CLEAR => "member_server_keepproxy_clear") .flag(YES) .help("Clears a member's server-specific keep-proxy setting"), + command!(member_server_keep_proxy, ("value", Toggle) => "member_server_keepproxy_update") + .help("Changes a member's server-specific keep-proxy setting"), ].into_iter() }; diff --git a/crates/command_definitions/src/message.rs b/crates/command_definitions/src/message.rs index e36969c2..06a5c0ca 100644 --- a/crates/command_definitions/src/message.rs +++ b/crates/command_definitions/src/message.rs @@ -3,6 +3,10 @@ use super::*; pub fn cmds() -> impl Iterator { let message = tokens!(("message", ["msg", "messageinfo"]), MessageRef); + let author = ("author", ["sender", "a"]); + let delete = ("delete", ["del", "d"]); + let reproxy = ("reproxy", ["rp", "crimes", "crime"]); + let edit = tokens!(("edit", ["e"]), ("new_content", OpaqueStringRemainder)); let apply_edit = |cmd: Command| { cmd.flag(("append", ["a"])) @@ -16,16 +20,16 @@ pub fn cmds() -> impl Iterator { [ command!(message => "message_info") - .flag(("delete", ["d"])) - .flag(("author", ["a"])) + .flag(delete) + .flag(author) .help("Shows information about a proxied message"), - command!(message, ("author", ["sender"]) => "message_author") - .help("Shows the author of a proxied message"), - command!(message, ("delete", ["del"]) => "message_delete") - .help("Deletes a proxied message"), + command!(message, author => "message_author").help("Shows the author of a proxied message"), + command!(message, delete => "message_delete").help("Deletes a proxied message"), apply_edit(command!(message, edit => "message_edit")), apply_edit(command!(edit => "message_edit")), - command!(("reproxy", ["rp", "crimes", "crime"]), ("msg", MessageRef), ("member", MemberRef) => "message_reproxy") + command!(reproxy, ("member", MemberRef) => "message_reproxy") + .help("Reproxies a message with a different member"), + command!(reproxy, ("msg", MessageRef), ("member", MemberRef) => "message_reproxy_specified") .help("Reproxies a message with a different member"), ] .into_iter() diff --git a/crates/command_parser/src/lib.rs b/crates/command_parser/src/lib.rs index 1d2d0549..f33021cf 100644 --- a/crates/command_parser/src/lib.rs +++ b/crates/command_parser/src/lib.rs @@ -47,14 +47,25 @@ pub fn parse_command( let mut params: HashMap = HashMap::new(); let mut raw_flags: Vec<(usize, MatchedFlag)> = Vec::new(); + let mut matched_tokens: Vec<(Tree, (Token, TokenMatchResult, usize))> = Vec::new(); + let mut filtered_tokens: Vec = Vec::new(); loop { println!( "possible: {:?}", - local_tree.possible_tokens().collect::>() + local_tree + .possible_tokens() + .filter(|t| filtered_tokens.contains(t)) + .collect::>() + ); + let next = next_token( + local_tree + .possible_tokens() + .filter(|t| !filtered_tokens.contains(t)), + &input, + current_pos, ); - let next = next_token(local_tree.possible_tokens(), &input, current_pos); println!("next: {:?}", next); - match next { + match &next { Some((found_token, result, new_pos)) => { match &result { // todo: better error messages for these? @@ -74,21 +85,37 @@ pub fn parse_command( // add parameter if any if let TokenMatchResult::MatchedParameter { name, value } = result { - params.insert(name.to_string(), value); + params.insert(name.to_string(), value.clone()); } // move to the next branch if let Some(next_tree) = local_tree.get_branch(&found_token) { + matched_tokens.push(( + local_tree.clone(), + (found_token.clone(), result.clone(), *new_pos), + )); + filtered_tokens.clear(); // new branch, new tokens local_tree = next_tree.clone(); } else { panic!("found token {found_token:?} could not match tree, at {input}"); } // advance our position on the input - current_pos = new_pos; + current_pos = *new_pos; current_token_idx += 1; } None => { + // redo the previous branches if we didnt match on a parameter + // this is a bit of a hack, but its necessary for making parameters on the same depth work + if let Some((match_tree, match_next)) = matched_tokens + .pop() + .and_then(|m| matches!(m.1, (Token::Parameter(_), _, _)).then_some(m)) + { + local_tree = match_tree; + filtered_tokens.push(match_next.0); + continue; + } + let mut error = format!("Unknown command `{prefix}{input}`."); let possible_commands = @@ -239,7 +266,7 @@ fn next_token<'a>( possible_tokens: impl Iterator, input: &str, current_pos: usize, -) -> Option<(&'a Token, TokenMatchResult, usize)> { +) -> Option<(Token, TokenMatchResult, usize)> { // get next parameter, matching quotes let matched = string::next_param(&input, current_pos); println!("matched: {matched:?}\n---"); @@ -267,7 +294,7 @@ fn next_token<'a>( match token.try_match(input_to_match) { Some(result) => { //println!("matched token: {}", token); - return Some((token, result, next_pos)); + return Some((token.clone(), result, next_pos)); } None => {} // continue matching until we exhaust all tokens } diff --git a/crates/command_parser/src/token.rs b/crates/command_parser/src/token.rs index e87c702a..99fc6fb3 100644 --- a/crates/command_parser/src/token.rs +++ b/crates/command_parser/src/token.rs @@ -16,7 +16,7 @@ pub enum Token { Parameter(Parameter), } -#[derive(Debug)] +#[derive(Clone, Debug)] pub enum TokenMatchResult { MatchedValue, MatchedParameter { @@ -36,6 +36,7 @@ pub enum TokenMatchResult { // a: because we want to differentiate between no match and match failure (it matched with an error) // "no match" has a different charecteristic because we want to continue matching other tokens... // ...while "match failure" means we should stop matching and return the error +// Option fits this better (and it makes some code look a bit nicer) pub type TryMatchResult = Option; impl Token {