mirror of
https://github.com/PluralKit/PluralKit.git
synced 2026-02-09 15:27:54 +00:00
feat: implement proper ("static") parameters handling command parser -> bot
feat: handle few more commands bot side fix(commands): handle missing parameters and return error refactor(commands): use ordermap instead of relying on a sort function to sort tokens
This commit is contained in:
parent
1a781014bd
commit
eec9f64026
16 changed files with 358 additions and 502 deletions
|
|
@ -50,21 +50,6 @@ public class Context
|
|||
DefaultPrefix = prefixes[0];
|
||||
Rest = provider.Resolve<DiscordApiClient>();
|
||||
Cluster = provider.Resolve<Cluster>();
|
||||
|
||||
try
|
||||
{
|
||||
Parameters = new ParametersFFI(message.Content?.Substring(commandParseOffset));
|
||||
}
|
||||
catch (PKError e)
|
||||
{
|
||||
// don't send an "invalid command" response if the guild has those turned off
|
||||
if (!(GuildConfig != null && GuildConfig!.InvalidCommandResponseEnabled != true))
|
||||
{
|
||||
// todo: not this
|
||||
Reply($"{Emojis.Error} {e.Message}");
|
||||
}
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public readonly IDiscordCache Cache;
|
||||
|
|
@ -90,7 +75,6 @@ public class Context
|
|||
|
||||
public readonly string CommandPrefix;
|
||||
public readonly string DefaultPrefix;
|
||||
public readonly ParametersFFI Parameters;
|
||||
|
||||
internal readonly IDatabase Database;
|
||||
internal readonly ModelRepository Repository;
|
||||
|
|
|
|||
|
|
@ -8,20 +8,15 @@ namespace PluralKit.Bot;
|
|||
|
||||
public static class ContextArgumentsExt
|
||||
{
|
||||
public static string PopArgument(this Context ctx) =>
|
||||
ctx.Parameters.Pop();
|
||||
public static string PopArgument(this Context ctx) => throw new PKError("todo: PopArgument");
|
||||
|
||||
public static string PeekArgument(this Context ctx) =>
|
||||
ctx.Parameters.Peek();
|
||||
public static string PeekArgument(this Context ctx) => throw new PKError("todo: PeekArgument");
|
||||
|
||||
public static string RemainderOrNull(this Context ctx, bool skipFlags = true) =>
|
||||
ctx.Parameters.Remainder(skipFlags).Length == 0 ? null : ctx.Parameters.Remainder(skipFlags);
|
||||
public static string RemainderOrNull(this Context ctx, bool skipFlags = true) => throw new PKError("todo: RemainderOrNull");
|
||||
|
||||
public static bool HasNext(this Context ctx, bool skipFlags = true) =>
|
||||
ctx.RemainderOrNull(skipFlags) != null;
|
||||
public static bool HasNext(this Context ctx, bool skipFlags = true) => throw new PKError("todo: HasNext");
|
||||
|
||||
public static string FullCommand(this Context ctx) =>
|
||||
ctx.Parameters.FullCommand;
|
||||
public static string FullCommand(this Context ctx) => throw new PKError("todo: FullCommand");
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the next parameter is equal to one of the given keywords and pops it from the stack. Case-insensitive.
|
||||
|
|
@ -53,12 +48,7 @@ public static class ContextArgumentsExt
|
|||
/// </summary>
|
||||
public static bool PeekMatch(this Context ctx, ref int ptr, string[] potentialMatches)
|
||||
{
|
||||
var arg = ctx.Parameters.PeekWithPtr(ref ptr);
|
||||
foreach (var match in potentialMatches)
|
||||
if (arg.Equals(match, StringComparison.InvariantCultureIgnoreCase))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
throw new PKError("todo: PeekMatch");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -69,23 +59,14 @@ public static class ContextArgumentsExt
|
|||
/// </summary>
|
||||
public static bool MatchMultiple(this Context ctx, params string[][] potentialParametersMatches)
|
||||
{
|
||||
int ptr = ctx.Parameters._ptr;
|
||||
|
||||
foreach (var param in potentialParametersMatches)
|
||||
if (!ctx.PeekMatch(ref ptr, param)) return false;
|
||||
|
||||
ctx.Parameters._ptr = ptr;
|
||||
|
||||
return true;
|
||||
throw new PKError("todo: MatchMultiple");
|
||||
}
|
||||
|
||||
public static bool MatchFlag(this Context ctx, params string[] potentialMatches)
|
||||
{
|
||||
// Flags are *ALWAYS PARSED LOWERCASE*. This means we skip out on a "ToLower" call here.
|
||||
// Can assume the caller array only contains lowercase *and* the set below only contains lowercase
|
||||
|
||||
var flags = ctx.Parameters.Flags();
|
||||
return potentialMatches.Any(potentialMatch => flags.Contains(potentialMatch));
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public static bool MatchClear(this Context ctx)
|
||||
|
|
@ -100,11 +81,7 @@ public static class ContextArgumentsExt
|
|||
|
||||
public static ReplyFormat PeekMatchFormat(this Context ctx)
|
||||
{
|
||||
int ptr1 = ctx.Parameters._ptr;
|
||||
int ptr2 = ctx.Parameters._ptr;
|
||||
if (ctx.PeekMatch(ref ptr1, new[] { "r", "raw" }) || ctx.MatchFlag("r", "raw")) return ReplyFormat.Raw;
|
||||
if (ctx.PeekMatch(ref ptr2, new[] { "pt", "plaintext" }) || ctx.MatchFlag("pt", "plaintext")) return ReplyFormat.Plaintext;
|
||||
return ReplyFormat.Standard;
|
||||
throw new PKError("todo: PeekMatchFormat");
|
||||
}
|
||||
|
||||
public static bool MatchToggle(this Context ctx, bool? defaultValue = null)
|
||||
|
|
@ -153,49 +130,12 @@ public static class ContextArgumentsExt
|
|||
|
||||
public static async Task<List<PKMember>> ParseMemberList(this Context ctx, SystemId? restrictToSystem)
|
||||
{
|
||||
var members = new List<PKMember>();
|
||||
|
||||
// Loop through all the given arguments
|
||||
while (ctx.HasNext())
|
||||
{
|
||||
// and attempt to match a member
|
||||
var member = await ctx.MatchMember(restrictToSystem);
|
||||
|
||||
if (member == null)
|
||||
// if we can't, big error. Every member name must be valid.
|
||||
throw new PKError(ctx.CreateNotFoundError("Member", ctx.PopArgument()));
|
||||
|
||||
members.Add(member); // Then add to the final output list
|
||||
}
|
||||
|
||||
if (members.Count == 0) throw new PKSyntaxError("You must input at least one member.");
|
||||
|
||||
return members;
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public static async Task<List<PKGroup>> ParseGroupList(this Context ctx, SystemId? restrictToSystem)
|
||||
{
|
||||
var groups = new List<PKGroup>();
|
||||
|
||||
// Loop through all the given arguments
|
||||
while (ctx.HasNext())
|
||||
{
|
||||
// and attempt to match a group
|
||||
var group = await ctx.MatchGroup(restrictToSystem);
|
||||
if (group == null)
|
||||
// if we can't, big error. Every group name must be valid.
|
||||
throw new PKError(ctx.CreateNotFoundError("Group", ctx.PopArgument()));
|
||||
|
||||
// todo: remove this, the database query enforces the restriction
|
||||
if (restrictToSystem != null && group.System != restrictToSystem)
|
||||
throw Errors.NotOwnGroupError; // TODO: name *which* group?
|
||||
|
||||
groups.Add(group); // Then add to the final output list
|
||||
}
|
||||
|
||||
if (groups.Count == 0) throw new PKSyntaxError("You must input at least one group.");
|
||||
|
||||
return groups;
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -34,19 +34,15 @@ public static class ContextEntityArgumentsExt
|
|||
return id != 0;
|
||||
}
|
||||
|
||||
public static Task<PKSystem> PeekSystem(this Context ctx) => ctx.MatchSystemInner();
|
||||
public static Task<PKSystem> PeekSystem(this Context ctx) => throw new NotImplementedException();
|
||||
|
||||
public static async Task<PKSystem> MatchSystem(this Context ctx)
|
||||
{
|
||||
var system = await ctx.MatchSystemInner();
|
||||
if (system != null) ctx.PopArgument();
|
||||
return system;
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
private static async Task<PKSystem> MatchSystemInner(this Context ctx)
|
||||
public static async Task<PKSystem> ParseSystem(this Context ctx, string input)
|
||||
{
|
||||
var input = ctx.PeekArgument();
|
||||
|
||||
// System references can take three forms:
|
||||
// - The direct user ID of an account connected to the system
|
||||
// - A @mention of an account connected to the system (<@uid>)
|
||||
|
|
@ -63,10 +59,8 @@ public static class ContextEntityArgumentsExt
|
|||
return null;
|
||||
}
|
||||
|
||||
public static async Task<PKMember> PeekMember(this Context ctx, SystemId? restrictToSystem = null)
|
||||
public static async Task<PKMember> ParseMember(this Context ctx, Parameters parameters, string input, SystemId? restrictToSystem = null)
|
||||
{
|
||||
var input = ctx.PeekArgument();
|
||||
|
||||
// Member references can have one of three forms, depending on
|
||||
// whether you're in a system or not:
|
||||
// - A member hid
|
||||
|
|
@ -75,7 +69,7 @@ public static class ContextEntityArgumentsExt
|
|||
|
||||
// Skip name / display name matching if the user does not have a system
|
||||
// or if they specifically request by-HID matching
|
||||
if (ctx.System != null && !ctx.MatchFlag("id", "by-id"))
|
||||
if (ctx.System != null && !parameters.HasFlag("id", "by-id"))
|
||||
{
|
||||
// First, try finding by member name in system
|
||||
if (await ctx.Repository.GetMemberByName(ctx.System.Id, input) is PKMember memberByName)
|
||||
|
|
@ -124,6 +118,11 @@ public static class ContextEntityArgumentsExt
|
|||
return null;
|
||||
}
|
||||
|
||||
public static async Task<PKMember> PeekMember(this Context ctx, SystemId? restrictToSystem = null)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to pop a member descriptor from the stack, returning it if present. If a member could not be
|
||||
/// resolved by the next word in the argument stack, does *not* touch the stack, and returns null.
|
||||
|
|
@ -170,9 +169,9 @@ public static class ContextEntityArgumentsExt
|
|||
return group;
|
||||
}
|
||||
|
||||
public static string CreateNotFoundError(this Context ctx, string entity, string input)
|
||||
public static string CreateNotFoundError(this Context ctx, Parameters parameters, string entity, string input)
|
||||
{
|
||||
var isIDOnlyQuery = ctx.System == null || ctx.MatchFlag("id", "by-id");
|
||||
var isIDOnlyQuery = ctx.System == null || parameters.HasFlag("id", "by-id");
|
||||
var inputIsHid = HidUtils.ParseHid(input) != null;
|
||||
|
||||
if (isIDOnlyQuery)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue