@@ -6,7 +6,7 @@ namespace Discord.Commands | |||||
public class CommandAttribute : Attribute | public class CommandAttribute : Attribute | ||||
{ | { | ||||
public string Text { get; } | public string Text { get; } | ||||
public RunMode RunMode { get; set; } = RunMode.Sync; | |||||
public RunMode RunMode { get; set; } = RunMode.Default; | |||||
public CommandAttribute() | public CommandAttribute() | ||||
{ | { | ||||
@@ -115,7 +115,7 @@ namespace Discord.Commands | |||||
private static void BuildCommand(CommandBuilder builder, TypeInfo typeInfo, MethodInfo method, CommandService service) | private static void BuildCommand(CommandBuilder builder, TypeInfo typeInfo, MethodInfo method, CommandService service) | ||||
{ | { | ||||
var attributes = method.GetCustomAttributes(); | var attributes = method.GetCustomAttributes(); | ||||
foreach (var attribute in attributes) | foreach (var attribute in attributes) | ||||
{ | { | ||||
// TODO: C#7 type switch | // TODO: C#7 type switch | ||||
@@ -140,6 +140,9 @@ namespace Discord.Commands | |||||
builder.AddPrecondition(attribute as PreconditionAttribute); | builder.AddPrecondition(attribute as PreconditionAttribute); | ||||
} | } | ||||
if (builder.Name == null) | |||||
builder.Name = method.Name; | |||||
var parameters = method.GetParameters(); | var parameters = method.GetParameters(); | ||||
int pos = 0, count = parameters.Length; | int pos = 0, count = parameters.Length; | ||||
foreach (var paramInfo in parameters) | foreach (var paramInfo in parameters) | ||||
@@ -19,10 +19,13 @@ namespace Discord.Commands | |||||
private readonly ConcurrentBag<ModuleInfo> _moduleDefs; | private readonly ConcurrentBag<ModuleInfo> _moduleDefs; | ||||
private readonly CommandMap _map; | private readonly CommandMap _map; | ||||
public IEnumerable<ModuleInfo> Modules => _typedModuleDefs.Select(x => x.Value); | |||||
public IEnumerable<CommandInfo> Commands => _typedModuleDefs.SelectMany(x => x.Value.Commands); | |||||
internal readonly RunMode _defaultRunMode; | |||||
public CommandService() | |||||
public IEnumerable<ModuleInfo> Modules => _moduleDefs.Select(x => x); | |||||
public IEnumerable<CommandInfo> Commands => _moduleDefs.SelectMany(x => x.Commands); | |||||
public CommandService() : this(new CommandServiceConfig()) { } | |||||
public CommandService(CommandServiceConfig config) | |||||
{ | { | ||||
_moduleLock = new SemaphoreSlim(1, 1); | _moduleLock = new SemaphoreSlim(1, 1); | ||||
_typedModuleDefs = new ConcurrentDictionary<Type, ModuleInfo>(); | _typedModuleDefs = new ConcurrentDictionary<Type, ModuleInfo>(); | ||||
@@ -64,6 +67,7 @@ namespace Discord.Commands | |||||
[typeof(IGroupUser)] = new UserTypeReader<IGroupUser>(), | [typeof(IGroupUser)] = new UserTypeReader<IGroupUser>(), | ||||
[typeof(IGuildUser)] = new UserTypeReader<IGuildUser>(), | [typeof(IGuildUser)] = new UserTypeReader<IGuildUser>(), | ||||
}; | }; | ||||
_defaultRunMode = config.DefaultRunMode; | |||||
} | } | ||||
//Modules | //Modules | ||||
@@ -0,0 +1,8 @@ | |||||
namespace Discord.Commands | |||||
{ | |||||
public class CommandServiceConfig | |||||
{ | |||||
/// <summary> The default RunMode commands should have, if one is not specified on the Command attribute or builder. </summary> | |||||
public RunMode DefaultRunMode { get; set; } = RunMode.Mixed; | |||||
} | |||||
} |
@@ -37,13 +37,21 @@ namespace Discord.Commands | |||||
Summary = builder.Summary; | Summary = builder.Summary; | ||||
Remarks = builder.Remarks; | Remarks = builder.Remarks; | ||||
RunMode = builder.RunMode; | |||||
RunMode = (builder.RunMode == RunMode.Default ? service._defaultRunMode : builder.RunMode); | |||||
Priority = builder.Priority; | Priority = builder.Priority; | ||||
if (module.Aliases.Count != 0) | |||||
Aliases = module.Aliases.Permutate(builder.Aliases, (first, second) => first + " " + second).ToImmutableArray(); | |||||
else | |||||
// both command and module provide aliases | |||||
if (module.Aliases.Count > 0 && builder.Aliases.Count > 0) | |||||
Aliases = module.Aliases.Permutate(builder.Aliases, (first, second) => second != null ? first + " " + second : first).ToImmutableArray(); | |||||
// only module provides aliases | |||||
else if (module.Aliases.Count > 0) | |||||
Aliases = module.Aliases.ToImmutableArray(); | |||||
// only command provides aliases | |||||
else if (builder.Aliases.Count > 0) | |||||
Aliases = builder.Aliases.ToImmutableArray(); | Aliases = builder.Aliases.ToImmutableArray(); | ||||
// neither provide aliases | |||||
else | |||||
throw new InvalidOperationException("Cannot build a command without any aliases"); | |||||
Preconditions = builder.Preconditions.ToImmutableArray(); | Preconditions = builder.Preconditions.ToImmutableArray(); | ||||
@@ -49,15 +49,21 @@ namespace Discord.Commands | |||||
while (builderStack.Count() > 0) | while (builderStack.Count() > 0) | ||||
{ | { | ||||
ModuleBuilder level = builderStack.Pop(); // get the topmost builder | |||||
ModuleBuilder level = builderStack.Pop(); //get the topmost builder | |||||
if (result == null) | if (result == null) | ||||
result = level.Aliases.ToList(); // create a shallow copy so we don't overwrite the builder unexpectedly | |||||
{ | |||||
if (level.Aliases.Count > 0) | |||||
result = level.Aliases.ToList(); //create a shallow copy so we don't overwrite the builder unexpectedly | |||||
} | |||||
else if (result.Count() > level.Aliases.Count) | else if (result.Count() > level.Aliases.Count) | ||||
result = result.Permutate(level.Aliases, (first, second) => first + " " + second); | result = result.Permutate(level.Aliases, (first, second) => first + " " + second); | ||||
else | else | ||||
result = level.Aliases.Permutate(result, (second, first) => first + " " + second); | result = level.Aliases.Permutate(result, (second, first) => first + " " + second); | ||||
} | } | ||||
if (result == null) //there were no aliases; default to an empty list | |||||
result = new List<string>(); | |||||
return result; | return result; | ||||
} | } | ||||
@@ -2,6 +2,7 @@ | |||||
{ | { | ||||
public enum RunMode | public enum RunMode | ||||
{ | { | ||||
Default, | |||||
Sync, | Sync, | ||||
Mixed, | Mixed, | ||||
Async | Async | ||||
@@ -498,14 +498,15 @@ namespace Discord.API | |||||
break; | break; | ||||
} | } | ||||
} | } | ||||
public async Task<Message> ModifyMessageAsync(ulong channelId, ulong messageId, ModifyMessageParams args, RequestOptions options = null) | |||||
public async Task<Message> ModifyMessageAsync(ulong channelId, ulong messageId, Rest.ModifyMessageParams args, RequestOptions options = null) | |||||
{ | { | ||||
Preconditions.NotEqual(channelId, 0, nameof(channelId)); | Preconditions.NotEqual(channelId, 0, nameof(channelId)); | ||||
Preconditions.NotEqual(messageId, 0, nameof(messageId)); | Preconditions.NotEqual(messageId, 0, nameof(messageId)); | ||||
Preconditions.NotNull(args, nameof(args)); | Preconditions.NotNull(args, nameof(args)); | ||||
if (args.Content.IsSpecified) | if (args.Content.IsSpecified) | ||||
{ | { | ||||
Preconditions.NotNullOrEmpty(args.Content, nameof(args.Content)); | |||||
if (!args.Embed.IsSpecified) | |||||
Preconditions.NotNullOrEmpty(args.Content, nameof(args.Content)); | |||||
if (args.Content.Value.Length > DiscordConfig.MaxMessageSize) | if (args.Content.Value.Length > DiscordConfig.MaxMessageSize) | ||||
throw new ArgumentOutOfRangeException($"Message content is too long, length must be less or equal to {DiscordConfig.MaxMessageSize}.", nameof(args.Content)); | throw new ArgumentOutOfRangeException($"Message content is too long, length must be less or equal to {DiscordConfig.MaxMessageSize}.", nameof(args.Content)); | ||||
} | } | ||||
@@ -8,5 +8,7 @@ namespace Discord.API.Rest | |||||
{ | { | ||||
[JsonProperty("content")] | [JsonProperty("content")] | ||||
public Optional<string> Content { get; set; } | public Optional<string> Content { get; set; } | ||||
[JsonProperty("embed")] | |||||
public Optional<Embed> Embed { get; set; } | |||||
} | } | ||||
} | } |
@@ -1,5 +1,4 @@ | |||||
using Discord.API.Rest; | |||||
using System; | |||||
using System; | |||||
using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
namespace Discord | namespace Discord | ||||
@@ -0,0 +1,12 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
namespace Discord | |||||
{ | |||||
public class ModifyMessageParams | |||||
{ | |||||
public Optional<string> Content { get; set; } | |||||
public Optional<EmbedBuilder> Embed { get; set; } | |||||
} | |||||
} |
@@ -32,6 +32,12 @@ namespace Discord | |||||
} | } | ||||
public Color(float r, float g, float b) | public Color(float r, float g, float b) | ||||
{ | { | ||||
if (r < 0.0f || r > 1.0f) | |||||
throw new ArgumentOutOfRangeException(nameof(r), "A float value must be within [0,1]"); | |||||
if (g < 0.0f || g > 1.0f) | |||||
throw new ArgumentOutOfRangeException(nameof(g), "A float value must be within [0,1]"); | |||||
if (b < 0.0f || b > 1.0f) | |||||
throw new ArgumentOutOfRangeException(nameof(b), "A float value must be within [0,1]"); | |||||
RawValue = | RawValue = | ||||
((uint)(r * 255.0f) << 16) | | ((uint)(r * 255.0f) << 16) | | ||||
((uint)(g * 255.0f) << 8) | | ((uint)(g * 255.0f) << 8) | | ||||
@@ -15,7 +15,12 @@ namespace Discord.Rest | |||||
{ | { | ||||
var args = new ModifyMessageParams(); | var args = new ModifyMessageParams(); | ||||
func(args); | func(args); | ||||
return await client.ApiClient.ModifyMessageAsync(msg.Channel.Id, msg.Id, args, options).ConfigureAwait(false); | |||||
var apiArgs = new API.Rest.ModifyMessageParams | |||||
{ | |||||
Content = args.Content, | |||||
Embed = args.Embed.IsSpecified ? args.Embed.Value.Build() : Optional.Create<API.Embed>() | |||||
}; | |||||
return await client.ApiClient.ModifyMessageAsync(msg.Channel.Id, msg.Id, apiArgs, options).ConfigureAwait(false); | |||||
} | } | ||||
public static async Task DeleteAsync(IMessage msg, BaseDiscordClient client, | public static async Task DeleteAsync(IMessage msg, BaseDiscordClient client, | ||||
RequestOptions options) | RequestOptions options) | ||||