diff --git a/src/Discord.Net.Commands/CommandService.cs b/src/Discord.Net.Commands/CommandService.cs
index 15f0c866d..be83b955f 100644
--- a/src/Discord.Net.Commands/CommandService.cs
+++ b/src/Discord.Net.Commands/CommandService.cs
@@ -602,6 +602,83 @@ namespace Discord.Commands
return match.Command.Priority + totalArgsScore * 0.99f;
}
+ ///
+ /// Validates and gets the best from a specified
+ ///
+ /// The SearchResult.
+ /// The context of the command.
+ /// The service provider to be used on the command's dependency injection.
+ /// The handling mode when multiple command matches are found.
+ /// A task that represents the asynchronous validation operation. The task result contains the result of the
+ /// command validation and the command matched, if a match was found.
+ public async Task<(IResult, Optional)> ValidateAndGetBestMatch(SearchResult matches, ICommandContext context, IServiceProvider provider, MultiMatchHandling multiMatchHandling = MultiMatchHandling.Exception)
+ {
+ if (!matches.IsSuccess)
+ return (matches, Optional.Create());
+
+ var commands = matches.Commands;
+ var preconditionResults = new Dictionary();
+
+ foreach (var command in commands)
+ {
+ preconditionResults[command] = await command.CheckPreconditionsAsync(context, provider);
+ }
+
+ var successfulPreconditions = preconditionResults
+ .Where(x => x.Value.IsSuccess)
+ .ToArray();
+
+ if (successfulPreconditions.Length == 0)
+ {
+ var bestCandidate = preconditionResults
+ .OrderByDescending(x => x.Key.Command.Priority)
+ .FirstOrDefault(x => !x.Value.IsSuccess);
+
+ return (bestCandidate.Value, bestCandidate.Key);
+ }
+
+ var parseResults = new Dictionary();
+
+ foreach (var pair in successfulPreconditions)
+ {
+ var parseResult = await pair.Key.ParseAsync(context, matches, pair.Value, provider).ConfigureAwait(false);
+
+ if (parseResult.Error == CommandError.MultipleMatches)
+ {
+ IReadOnlyList argList, paramList;
+ switch (multiMatchHandling)
+ {
+ case MultiMatchHandling.Best:
+ argList = parseResult.ArgValues.Select(x => x.Values.OrderByDescending(y => y.Score).First()).ToImmutableArray();
+ paramList = parseResult.ParamValues.Select(x => x.Values.OrderByDescending(y => y.Score).First()).ToImmutableArray();
+ parseResult = ParseResult.FromSuccess(argList, paramList);
+ break;
+ }
+ }
+
+ parseResults[pair.Key] = parseResult;
+ }
+
+ var weightedParseResults = parseResults
+ .OrderByDescending(x => CalculateScore(x.Key, x.Value));
+
+ var successfulParses = weightedParseResults
+ .Where(x => x.Value.IsSuccess)
+ .ToArray();
+
+ if(successfulParses.Length == 0)
+ {
+ var bestMatch = parseResults
+ .FirstOrDefault(x => !x.Value.IsSuccess);
+
+ return (bestMatch.Value, bestMatch.Key);
+ }
+
+ var chosenOverload = successfulParses[0];
+
+ return (chosenOverload.Value, chosenOverload.Key);
+ }
+
protected virtual void Dispose(bool disposing)
{
if (!_isDisposed)