|
|
@@ -506,82 +506,26 @@ namespace Discord.Commands |
|
|
|
services = services ?? EmptyServiceProvider.Instance; |
|
|
|
|
|
|
|
var searchResult = Search(input); |
|
|
|
if (!searchResult.IsSuccess) |
|
|
|
{ |
|
|
|
await _commandExecutedEvent.InvokeAsync(Optional.Create<CommandInfo>(), context, searchResult).ConfigureAwait(false); |
|
|
|
return searchResult; |
|
|
|
} |
|
|
|
|
|
|
|
var commands = searchResult.Commands; |
|
|
|
var preconditionResults = new Dictionary<CommandMatch, PreconditionResult>(); |
|
|
|
|
|
|
|
foreach (var match in commands) |
|
|
|
{ |
|
|
|
preconditionResults[match] = await match.Command.CheckPreconditionsAsync(context, services).ConfigureAwait(false); |
|
|
|
} |
|
|
|
|
|
|
|
var successfulPreconditions = preconditionResults |
|
|
|
.Where(x => x.Value.IsSuccess) |
|
|
|
.ToArray(); |
|
|
|
//Since ValidateAndGetBestMatch is deterministic on the return type, we can use pattern matching on the type for infering the code flow. |
|
|
|
var (validationResult, commandMatch) = await ValidateAndGetBestMatch(searchResult, context, services, multiMatchHandling); |
|
|
|
|
|
|
|
if (successfulPreconditions.Length == 0) |
|
|
|
if(validationResult is SearchResult) |
|
|
|
{ |
|
|
|
//All preconditions failed, return the one from the highest priority command |
|
|
|
var bestCandidate = preconditionResults |
|
|
|
.OrderByDescending(x => x.Key.Command.Priority) |
|
|
|
.FirstOrDefault(x => !x.Value.IsSuccess); |
|
|
|
|
|
|
|
await _commandExecutedEvent.InvokeAsync(bestCandidate.Key.Command, context, bestCandidate.Value).ConfigureAwait(false); |
|
|
|
return bestCandidate.Value; |
|
|
|
await _commandExecutedEvent.InvokeAsync(Optional.Create<CommandInfo>(), context, searchResult).ConfigureAwait(false); |
|
|
|
} |
|
|
|
|
|
|
|
//If we get this far, at least one precondition was successful. |
|
|
|
|
|
|
|
var parseResultsDict = new Dictionary<CommandMatch, ParseResult>(); |
|
|
|
foreach (var pair in successfulPreconditions) |
|
|
|
else if(validationResult is not ParseResult parseResult) |
|
|
|
{ |
|
|
|
var parseResult = await pair.Key.ParseAsync(context, searchResult, pair.Value, services).ConfigureAwait(false); |
|
|
|
|
|
|
|
if (parseResult.Error == CommandError.MultipleMatches) |
|
|
|
{ |
|
|
|
IReadOnlyList<TypeReaderValue> 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; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
parseResultsDict[pair.Key] = parseResult; |
|
|
|
await _commandExecutedEvent.InvokeAsync(commandMatch.Value.Command,context,validationResult).ConfigureAwait(false); |
|
|
|
} |
|
|
|
|
|
|
|
//Order the parse results by their score so that we choose the most likely result to execute |
|
|
|
var parseResults = parseResultsDict |
|
|
|
.OrderByDescending(x => CalculateScore(x.Key, x.Value)); |
|
|
|
|
|
|
|
var successfulParses = parseResults |
|
|
|
.Where(x => x.Value.IsSuccess) |
|
|
|
.ToArray(); |
|
|
|
|
|
|
|
if (successfulParses.Length == 0) |
|
|
|
else |
|
|
|
{ |
|
|
|
//All parses failed, return the one from the highest priority command, using score as a tie breaker |
|
|
|
var bestMatch = parseResults |
|
|
|
.FirstOrDefault(x => !x.Value.IsSuccess); |
|
|
|
|
|
|
|
await _commandExecutedEvent.InvokeAsync(bestMatch.Key.Command, context, bestMatch.Value).ConfigureAwait(false); |
|
|
|
return bestMatch.Value; |
|
|
|
var result = await commandMatch.Value.Command.ExecuteAsync(context, parseResult, services).ConfigureAwait(false); |
|
|
|
if (!result.IsSuccess && !(result is RuntimeResult || result is ExecuteResult)) // succesful results raise the event in CommandInfo#ExecuteInternalAsync (have to raise it there b/c deffered execution) |
|
|
|
await _commandExecutedEvent.InvokeAsync(commandMatch.Value.Command, context, result); |
|
|
|
return result; |
|
|
|
} |
|
|
|
|
|
|
|
//If we get this far, at least one parse was successful. Execute the most likely overload. |
|
|
|
var chosenOverload = successfulParses[0]; |
|
|
|
var result = await chosenOverload.Key.ExecuteAsync(context, chosenOverload.Value, services).ConfigureAwait(false); |
|
|
|
if (!result.IsSuccess && !(result is RuntimeResult || result is ExecuteResult)) // succesful results raise the event in CommandInfo#ExecuteInternalAsync (have to raise it there b/c deffered execution) |
|
|
|
await _commandExecutedEvent.InvokeAsync(chosenOverload.Key.Command, context, result); |
|
|
|
return result; |
|
|
|
return validationResult; |
|
|
|
} |
|
|
|
|
|
|
|
// Calculates the 'score' of a command given a parse result |
|
|
|