Browse Source

Implement initial command permissions system

After our initial discussion on the matter (see #172) this is the system
that we all seem to have agreed on. As a result, I have implemented a
simple system which effectively implements permissions, while being
extensible and tweakable so bot devs can decide what they want to do for
permissions.

As for default 'permissions', I'm not sure what the best approach would be
here; bot devs are likely to implement their own permissions 'levels' and
use those. I think the most we could do for now is add attributes to
require certain users (by id) and certain roles (by id and possibly by
name?) This would probably be the best option for now as it requires less
work from us, nor do we know the *exact* approach bot devs want to take
with permissions.
tags/1.0-rc
Finite Reality 8 years ago
parent
commit
772fd97080
4 changed files with 53 additions and 0 deletions
  1. +12
    -0
      src/Discord.Net.Commands/Attributes/FilterAttribute.cs
  2. +15
    -0
      src/Discord.Net.Commands/Command.cs
  3. +1
    -0
      src/Discord.Net.Commands/CommandError.cs
  4. +25
    -0
      src/Discord.Net.Commands/Context/CommandExecutionContext.cs

+ 12
- 0
src/Discord.Net.Commands/Attributes/FilterAttribute.cs View File

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

namespace Discord.Commands
{
public abstract class FilterAttribute : Attribute
{
public abstract void OnCommandExecuting(CommandExecutionContext context);
}
}

+ 15
- 0
src/Discord.Net.Commands/Command.cs View File

@@ -19,6 +19,7 @@ namespace Discord.Commands
public string Text { get; } public string Text { get; }
public Module Module { get; } public Module Module { get; }
public IReadOnlyList<CommandParameter> Parameters { get; } public IReadOnlyList<CommandParameter> Parameters { get; }
public IReadOnlyList<FilterAttribute> Filters { get; }
internal Command(Module module, object instance, CommandAttribute attribute, MethodInfo methodInfo, string groupPrefix) internal Command(Module module, object instance, CommandAttribute attribute, MethodInfo methodInfo, string groupPrefix)
{ {
@@ -37,6 +38,7 @@ namespace Discord.Commands
Synopsis = synopsis.Text; Synopsis = synopsis.Text;


Parameters = BuildParameters(methodInfo); Parameters = BuildParameters(methodInfo);
Filters = BuildFilters(methodInfo);
_action = BuildAction(methodInfo); _action = BuildAction(methodInfo);
} }


@@ -52,6 +54,14 @@ namespace Discord.Commands
if (!parseResult.IsSuccess) if (!parseResult.IsSuccess)
return ExecuteResult.FromError(parseResult); return ExecuteResult.FromError(parseResult);


var context = new CommandExecutionContext(this, parseResult, msg);
foreach (FilterAttribute filter in Filters)
{
filter.OnCommandExecuting(context);
if (context.Handled)
return ExecuteResult.FromError(CommandError.InvalidPermissions, $"Permission check for {filter.GetType().FullName} failed");
}

try try
{ {
await _action.Invoke(msg, parseResult.Values);//Note: This code may need context await _action.Invoke(msg, parseResult.Values);//Note: This code may need context
@@ -63,6 +73,11 @@ namespace Discord.Commands
} }
} }


private IReadOnlyList<FilterAttribute> BuildFilters(MethodInfo methodInfo)
{
return methodInfo.GetCustomAttributes<FilterAttribute>().ToImmutableArray();
}

private IReadOnlyList<CommandParameter> BuildParameters(MethodInfo methodInfo) private IReadOnlyList<CommandParameter> BuildParameters(MethodInfo methodInfo)
{ {
var parameters = methodInfo.GetParameters(); var parameters = methodInfo.GetParameters();


+ 1
- 0
src/Discord.Net.Commands/CommandError.cs View File

@@ -16,5 +16,6 @@


//Execute //Execute
Exception, Exception,
InvalidPermissions
} }
} }

+ 25
- 0
src/Discord.Net.Commands/Context/CommandExecutionContext.cs View File

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

namespace Discord.Commands
{
public class CommandExecutionContext
{
public Command ExecutingCommand { get; internal set; }
public ParseResult ParseResult { get; internal set; }
public IMessage Message { get; internal set; }

public bool Handled { get; set; }

internal CommandExecutionContext(Command command, ParseResult parseResult, IMessage message)
{
ExecutingCommand = command;
ParseResult = parseResult;
Message = message;

Handled = false;
}
}
}

Loading…
Cancel
Save