Browse Source

Complete Preconditions implementation

tags/1.0-rc
Finite Reality 8 years ago
parent
commit
0e920da21f
10 changed files with 108 additions and 50 deletions
  1. +1
    -0
      .gitignore
  2. +1
    -1
      src/Discord.Net.Commands/Attributes/PreconditionAttribute.cs
  3. +5
    -3
      src/Discord.Net.Commands/Attributes/Preconditions/RequireDMAttribute.cs
  4. +5
    -3
      src/Discord.Net.Commands/Attributes/Preconditions/RequireGuildAttribute.cs
  5. +45
    -0
      src/Discord.Net.Commands/Attributes/Preconditions/RequireRoleAttribute.cs
  6. +12
    -13
      src/Discord.Net.Commands/Command.cs
  7. +10
    -7
      src/Discord.Net.Commands/CommandService.cs
  8. +0
    -23
      src/Discord.Net.Commands/Context/PreconditionContext.cs
  9. +2
    -0
      src/Discord.Net.Commands/Results/ExecuteResult.cs
  10. +27
    -0
      src/Discord.Net.Commands/Results/PreconditionResult.cs

+ 1
- 0
.gitignore View File

@@ -200,3 +200,4 @@ project.lock.json
/test/Discord.Net.Tests/config.json
/docs/_build
*.pyc
/.editorconfig

+ 1
- 1
src/Discord.Net.Commands/Attributes/PreconditionAttribute.cs View File

@@ -7,6 +7,6 @@ namespace Discord.Commands
{
public abstract class PreconditionAttribute : Attribute
{
public abstract void CheckPermissions(PreconditionContext context);
public abstract Task<PreconditionResult> CheckPermissions(IMessage context);
}
}

+ 5
- 3
src/Discord.Net.Commands/Attributes/Preconditions/RequireDMAttribute.cs View File

@@ -7,10 +7,12 @@ namespace Discord.Commands
{
public class RequireDMAttribute : PreconditionAttribute
{
public override void CheckPermissions(PreconditionContext context)
public override Task<PreconditionResult> CheckPermissions(IMessage context)
{
if (context.Message.Channel is IGuildChannel)
context.Handled = true;
if (context.Channel is IGuildChannel)
return Task.FromResult(PreconditionResult.FromError("Command must be used in a DM"));

return Task.FromResult(PreconditionResult.FromSuccess());
}
}
}

+ 5
- 3
src/Discord.Net.Commands/Attributes/Preconditions/RequireGuildAttribute.cs View File

@@ -7,10 +7,12 @@ namespace Discord.Commands
{
public class RequireGuildAttribute : PreconditionAttribute
{
public override void CheckPermissions(PreconditionContext context)
public override Task<PreconditionResult> CheckPermissions(IMessage context)
{
if (!(context.Message.Channel is IGuildChannel))
context.Handled = true;
if (!(context.Channel is IGuildChannel))
return Task.FromResult(PreconditionResult.FromError("Command must be used in a guild"));

return Task.FromResult(PreconditionResult.FromSuccess());
}
}
}

+ 45
- 0
src/Discord.Net.Commands/Attributes/Preconditions/RequireRoleAttribute.cs View File

@@ -0,0 +1,45 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Discord.Commands
{
public class RequireRoleAttribute : RequireGuildAttribute
{
public string Role { get; set; }
public StringComparer Comparer { get; set; }

public RequireRoleAttribute(string roleName)
{
Role = roleName;
Comparer = StringComparer.Ordinal;
}

public RequireRoleAttribute(string roleName, StringComparer comparer)
{
Role = roleName;
Comparer = comparer;
}

public override async Task<PreconditionResult> CheckPermissions(IMessage context)
{
var result = await base.CheckPermissions(context).ConfigureAwait(false);

if (!result.IsSuccess)
return result;

var author = (context.Author as IGuildUser);

if (author != null)
{
var hasRole = author.Roles.Any(x => Comparer.Compare(x.Name, Role) == 0);

if (!hasRole)
return PreconditionResult.FromError($"User does not have the '{Role}' role.");
}

return PreconditionResult.FromSuccess();
}
}
}

+ 12
- 13
src/Discord.Net.Commands/Command.cs View File

@@ -19,7 +19,7 @@ namespace Discord.Commands
public string Text { get; }
public Module Module { get; }
public IReadOnlyList<CommandParameter> Parameters { get; }
public IReadOnlyList<PreconditionAttribute> Permissions { get; }
public IReadOnlyList<PreconditionAttribute> Preconditions { get; }

internal Command(Module module, object instance, CommandAttribute attribute, MethodInfo methodInfo, string groupPrefix)
{
@@ -38,22 +38,20 @@ namespace Discord.Commands
Synopsis = synopsis.Text;

Parameters = BuildParameters(methodInfo);
Permissions = BuildPermissions(methodInfo);
Preconditions = BuildPreconditions(methodInfo);
_action = BuildAction(methodInfo);
}

public bool MeetsPreconditions(IMessage message)
public async Task<PreconditionResult> CheckPreconditions(IMessage context)
{
var context = new PreconditionContext(this, message);

foreach (PreconditionAttribute permission in Permissions)
foreach (PreconditionAttribute permission in Preconditions)
{
permission.CheckPermissions(context);
if (context.Handled)
return false;
var result = await permission.CheckPermissions(context).ConfigureAwait(false);
if (!result.IsSuccess)
return result;
}

return true;
return PreconditionResult.FromSuccess();
}

public async Task<ParseResult> Parse(IMessage msg, SearchResult searchResult)
@@ -68,8 +66,9 @@ namespace Discord.Commands
if (!parseResult.IsSuccess)
return ExecuteResult.FromError(parseResult);

if (!MeetsPreconditions(msg)) // TODO: should we have to check this here, or leave it entirely to the bot dev?
return ExecuteResult.FromError(CommandError.UnmetPrecondition, "Permissions check failed");
var precondition = await CheckPreconditions(msg).ConfigureAwait(false);
if (!precondition.IsSuccess) // TODO: should we have to check this here, or leave it entirely to the bot dev?
return ExecuteResult.FromError(precondition);

try
{
@@ -82,7 +81,7 @@ namespace Discord.Commands
}
}

private IReadOnlyList<PreconditionAttribute> BuildPermissions(MethodInfo methodInfo)
private IReadOnlyList<PreconditionAttribute> BuildPreconditions(MethodInfo methodInfo)
{
return methodInfo.GetCustomAttributes<PreconditionAttribute>().ToImmutableArray();
}


+ 10
- 7
src/Discord.Net.Commands/CommandService.cs View File

@@ -208,16 +208,19 @@ namespace Discord.Commands
if (!searchResult.IsSuccess)
return searchResult;

// TODO: this logic is for users who don't manually search/execute: should we keep it?
IReadOnlyList<Command> commands = searchResult.Commands
.Where(x => x.MeetsPreconditions(message)).ToImmutableArray();

if (commands.Count == 0 && searchResult.Commands.Count > 0)
return ParseResult.FromError(CommandError.UnmetPrecondition, "Unmet precondition");
var commands = searchResult.Commands;

for (int i = commands.Count - 1; i >= 0; i--)
{
var preconditionResult = await commands[i].CheckPreconditions(message);
if (!preconditionResult.IsSuccess)
{
if (commands.Count == 1)
return preconditionResult;
else
continue;
}

var parseResult = await commands[i].Parse(message, searchResult);
if (!parseResult.IsSuccess)
{


+ 0
- 23
src/Discord.Net.Commands/Context/PreconditionContext.cs View File

@@ -1,23 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Discord.Commands
{
public class PreconditionContext
{
public Command Command { get; internal set; }
public IMessage Message { get; internal set; }

public bool Handled { get; set; }

internal PreconditionContext(Command command, IMessage message)
{
Command = command;
Message = message;

Handled = false;
}
}
}

+ 2
- 0
src/Discord.Net.Commands/Results/ExecuteResult.cs View File

@@ -28,6 +28,8 @@ namespace Discord.Commands
=> new ExecuteResult(ex, CommandError.Exception, ex.Message);
internal static ExecuteResult FromError(ParseResult result)
=> new ExecuteResult(null, result.Error, result.ErrorReason);
internal static ExecuteResult FromError(PreconditionResult result)
=> new ExecuteResult(null, result.Error, result.ErrorReason);
public override string ToString() => IsSuccess ? "Success" : $"{Error}: {ErrorReason}";
private string DebuggerDisplay => IsSuccess ? "Success" : $"{Error}: {ErrorReason}";


+ 27
- 0
src/Discord.Net.Commands/Results/PreconditionResult.cs View File

@@ -0,0 +1,27 @@
using System.Diagnostics;

namespace Discord.Commands
{
[DebuggerDisplay(@"{DebuggerDisplay,nq}")]
public struct PreconditionResult : IResult
{
public CommandError? Error { get; }
public string ErrorReason { get; }

public bool IsSuccess => !Error.HasValue;

private PreconditionResult(CommandError? error, string errorReason)
{
Error = error;
ErrorReason = errorReason;
}

internal static PreconditionResult FromSuccess()
=> new PreconditionResult(null, null);
internal static PreconditionResult FromError(string reason)
=> new PreconditionResult(CommandError.UnmetPrecondition, reason);

public override string ToString() => IsSuccess ? "Success" : $"{Error}: {ErrorReason}";
private string DebuggerDisplay => IsSuccess ? "Success" : $"{Error}: {ErrorReason}";
}
}

Loading…
Cancel
Save